From a097412f8eadfb8c7d0ce5ac29c433ed4ec6543d Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Thu, 27 Aug 2026 22:09:37 -0700 Subject: [PATCH 01/31] Generate the TypeScript FHIR Schemas from the Implementation Guides --- .github/workflows/build-and-test.yml | 181 +- .github/workflows/fhir-conformance.yml | 173 + .gitignore | 6 + .prettierignore | 5 + README.md | 2 +- package-lock.json | 69 + .../docs/docs/docs/technology-stack.md | 4 +- packages/fhir/README.md | 623 +- .../catalog/measurement-capabilities.json | 1863 + .../measurement-capabilities.json.license} | 0 packages/fhir/eslint.config.mjs | 55 +- .../fhir/fixtures/conformance/manifest.json | 414 + .../conformance/manifest.json.license} | 0 .../resources/mobile-active-energy.json | 252 + .../mobile-active-energy.json.license} | 0 .../resources/mobile-basal-energy.json | 252 + .../mobile-basal-energy.json.license} | 0 ...le-blood-glucose-unspecified-specimen.json | 244 + ...glucose-unspecified-specimen.json.license} | 0 .../resources/mobile-blood-pressure.json | 262 + .../mobile-blood-pressure.json.license} | 0 .../resources/mobile-body-fat-percentage.json | 244 + .../mobile-body-fat-percentage.json.license} | 0 .../resources/mobile-body-height.json | 244 + .../mobile-body-height.json.license} | 2 - .../resources/mobile-body-temperature.json | 244 + .../mobile-body-temperature.json.license} | 2 - .../resources/mobile-body-water-mass.json | 244 + .../mobile-body-water-mass.json.license} | 2 - .../resources/mobile-body-weight.json | 244 + .../mobile-body-weight.json.license} | 2 - .../resources/mobile-bone-mass.json | 244 + .../resources/mobile-bone-mass.json.license | 3 + .../resources/mobile-deep-sleep-duration.json | 257 + .../mobile-deep-sleep-duration.json.license | 3 + .../resources/mobile-distance.json | 247 + .../resources/mobile-distance.json.license | 3 + .../mobile-electrodermal-activity.json | 249 + ...mobile-electrodermal-activity.json.license | 3 + .../resources/mobile-flights-climbed.json | 247 + .../mobile-flights-climbed.json.license | 3 + .../mobile-heart-rate-variability-rmssd.json | 249 + ...-heart-rate-variability-rmssd.json.license | 3 + .../mobile-heart-rate-variability-sdnn.json | 244 + ...e-heart-rate-variability-sdnn.json.license | 3 + .../resources/mobile-heart-rate.json | 244 + .../resources/mobile-heart-rate.json.license | 3 + .../resources/mobile-lean-body-mass.json | 244 + .../mobile-lean-body-mass.json.license | 3 + .../mobile-light-sleep-duration.json | 257 + .../mobile-light-sleep-duration.json.license | 3 + ...obile-oxygen-saturation-daily-average.json | 257 + ...ygen-saturation-daily-average.json.license | 3 + .../resources/mobile-oxygen-saturation.json | 244 + .../mobile-oxygen-saturation.json.license | 3 + .../resources/mobile-rem-sleep-duration.json | 257 + .../mobile-rem-sleep-duration.json.license | 3 + .../mobile-respiratory-rate-average.json | 255 + ...bile-respiratory-rate-average.json.license | 3 + .../resources/mobile-resting-heart-rate.json | 262 + .../mobile-resting-heart-rate.json.license | 3 + .../resources/mobile-skin-temperature.json | 244 + .../mobile-skin-temperature.json.license | 3 + .../mobile-sleep-awake-duration.json | 257 + .../mobile-sleep-awake-duration.json.license | 3 + .../resources/mobile-sleep-duration.json | 247 + .../mobile-sleep-duration.json.license | 3 + .../resources/mobile-sleep-heart-rate.json | 260 + .../mobile-sleep-heart-rate.json.license | 3 + .../resources/mobile-step-count.json | 252 + .../resources/mobile-step-count.json.license | 3 + .../conformance/resources/mobile-vo2-max.json | 249 + .../resources/mobile-vo2-max.json.license | 3 + .../conformance/resources/mobile-workout.json | 254 + .../resources/mobile-workout.json.license | 3 + .../provider-oura-daily-activity.json | 415 + .../provider-oura-daily-activity.json.license | 3 + .../providers-oura-daily-activity.json | 415 + ...providers-oura-daily-activity.json.license | 3 + .../resources/questionnaire-response.json | 44 + .../questionnaire-response.json.license | 3 + .../conformance/resources/questionnaire.json | 47 + .../resources/questionnaire.json.license | 3 + ...ecording-google-health-api-heart-rate.json | 236 + ...-google-health-api-heart-rate.json.license | 3 + .../resources/recording-oura-heartrate.json | 236 + .../recording-oura-heartrate.json.license | 3 + .../recording-withings-activityintraday.json | 236 + ...ing-withings-activityintraday.json.license | 3 + .../recording-withings-sleepintraday.json | 236 + ...ording-withings-sleepintraday.json.license | 3 + .../google-health-active-energy.json | 33 + .../google-health-active-energy.json.license | 3 + .../google-health-heart-rate-recording.json | 32 + ...e-health-heart-rate-recording.json.license | 3 + .../normalized/oura-daily-activity.json | 51 + .../oura-daily-activity.json.license | 3 + .../normalized/oura-heart-rate-recording.json | 32 + .../oura-heart-rate-recording.json.license | 3 + .../normalized/oura-sleep-duration.json | 33 + .../oura-sleep-duration.json.license | 3 + .../withings-activity-intraday-recording.json | 32 + ...s-activity-intraday-recording.json.license | 3 + .../normalized/withings-body-weight.json | 32 + .../withings-body-weight.json.license | 3 + .../withings-sleep-intraday-recording.json | 32 + ...ings-sleep-intraday-recording.json.license | 3 + packages/fhir/grove-fhir.json | 4 + packages/fhir/grove-fhir.json.license | 3 + packages/fhir/jest.config.js | 19 +- packages/fhir/package.json | 58 +- packages/fhir/scripts/browser-smoke.mjs | 287 + packages/fhir/scripts/fetch-grove-fhir.mjs | 73 + .../scripts/generate-conformance-fixtures.mjs | 547 + .../scripts/generate-measurement-catalog.mjs | 1188 + .../fhir/scripts/generate-zod-schemas.d.mts | 29 + .../fhir/scripts/generate-zod-schemas.mjs | 1048 + packages/fhir/scripts/package-smoke.mjs | 96 + .../fhir/scripts/validate-conformance.mjs | 183 + packages/fhir/src/core/freeze.ts | 32 + packages/fhir/src/core/index.ts | 47 + packages/fhir/src/core/primitives.ts | 369 + packages/fhir/src/core/quantity.ts | 78 + packages/fhir/src/core/result.ts | 83 + packages/fhir/src/elements/anyValueSchema.ts | 129 - packages/fhir/src/elements/backBoneElement.ts | 19 - .../fhir/src/elements/dataTypes/address.ts | 48 - .../fhir/src/elements/dataTypes/annotation.ts | 37 - .../fhir/src/elements/dataTypes/attachment.ts | 47 - .../src/elements/dataTypes/codeableConcept.ts | 31 - .../elements/dataTypes/codeableReference.ts | 29 - .../fhir/src/elements/dataTypes/coding.ts | 40 - .../src/elements/dataTypes/contactPoint.ts | 47 - .../fhir/src/elements/dataTypes/humanName.ts | 49 - .../fhir/src/elements/dataTypes/identifier.ts | 45 - .../src/elements/dataTypes/marketingStatus.ts | 35 - packages/fhir/src/elements/dataTypes/money.ts | 28 - .../fhir/src/elements/dataTypes/narrative.ts | 36 - .../fhir/src/elements/dataTypes/period.ts | 29 - .../src/elements/dataTypes/primitiveTypes.ts | 133 - .../fhir/src/elements/dataTypes/quantity.ts | 41 - packages/fhir/src/elements/dataTypes/range.ts | 27 - packages/fhir/src/elements/dataTypes/ratio.ts | 27 - .../fhir/src/elements/dataTypes/ratioRange.ts | 28 - .../fhir/src/elements/dataTypes/reference.ts | 35 - .../src/elements/dataTypes/sampledData.ts | 39 - .../fhir/src/elements/dataTypes/signature.ts | 43 - .../fhir/src/elements/dataTypes/timing.ts | 84 - packages/fhir/src/elements/domainResource.ts | 28 - packages/fhir/src/elements/dosage.ts | 56 - packages/fhir/src/elements/element.ts | 24 - packages/fhir/src/elements/extension.ts | 139 - packages/fhir/src/elements/index.ts | 45 - packages/fhir/src/elements/meta.ts | 40 - .../elements/metaDataTypes/contactDetail.ts | 30 - .../src/elements/metaDataTypes/contributor.ts | 38 - .../elements/metaDataTypes/dataRequirement.ts | 73 - .../src/elements/metaDataTypes/expression.ts | 40 - .../metaDataTypes/parameterDefinition.ts | 45 - .../elements/metaDataTypes/relatedArtifact.ts | 51 - .../metaDataTypes/triggerDefinition.ts | 61 - .../elements/metaDataTypes/usageContext.ts | 36 - packages/fhir/src/elements/resource.ts | 31 - packages/fhir/src/extract/careplan.ts | 50 + packages/fhir/src/extract/coding.ts | 74 + packages/fhir/src/extract/condition.ts | 94 + packages/fhir/src/extract/encode.ts | 105 + packages/fhir/src/extract/identifier.ts | 67 + packages/fhir/src/extract/immunization.ts | 78 + packages/fhir/src/extract/index.ts | 20 + packages/fhir/src/extract/internal.ts | 28 + packages/fhir/src/extract/observation.ts | 127 + packages/fhir/src/extract/patient.ts | 87 + packages/fhir/src/extract/period.ts | 93 + packages/fhir/src/extract/person.ts | 100 + packages/fhir/src/extract/resource.ts | 138 + packages/fhir/src/extract/scheduling.ts | 85 + packages/fhir/src/index.ts | 249 +- packages/fhir/src/mobile/identity.ts | 114 + packages/fhir/src/mobile/index.ts | 48 + .../mobile/measurement-catalog.generated.ts | 2927 ++ packages/fhir/src/mobile/time.ts | 111 + packages/fhir/src/mobile/types.ts | 186 + .../claimResponse.ts => provenance/index.ts} | 8 +- packages/fhir/src/providers/builder.ts | 568 + .../fhir/src/providers/contract.generated.ts | 6471 +++ packages/fhir/src/providers/graph.ts | 139 + packages/fhir/src/providers/identity.ts | 197 + packages/fhir/src/providers/index.ts | 74 + packages/fhir/src/providers/privacy.ts | 41 + packages/fhir/src/providers/profiles.ts | 76 + packages/fhir/src/providers/provider.ts | 827 + packages/fhir/src/providers/recording.ts | 686 + packages/fhir/src/providers/types.ts | 266 + packages/fhir/src/questionnaire/builder.ts | 313 + .../src/questionnaire/contract.generated.ts | 35 + packages/fhir/src/questionnaire/contract.ts | 1319 + packages/fhir/src/questionnaire/index.ts | 42 + packages/fhir/src/questionnaire/parse.ts | 293 + packages/fhir/src/questionnaire/preflight.ts | 1176 + .../src/questionnaire/r4-resource-types.ts | 163 + packages/fhir/src/questionnaire/schemas.ts | 582 + packages/fhir/src/questionnaire/temporal.ts | 183 + packages/fhir/src/questionnaire/types.ts | 220 + packages/fhir/src/r4/index.ts | 62 + packages/fhir/src/r4/parse.ts | 78 + packages/fhir/src/r4/schemas.ts | 156 + packages/fhir/src/r4/types.ts | 92 + packages/fhir/src/resources/account.ts | 147 - .../fhir/src/resources/activityDefinition.ts | 294 - .../administrableProductDefinition.ts | 170 - packages/fhir/src/resources/adverseEvent.ts | 192 - .../fhir/src/resources/allergyIntolerance.ts | 256 - packages/fhir/src/resources/appointment.ts | 247 - .../fhir/src/resources/appointmentResponse.ts | 149 - packages/fhir/src/resources/auditEvent.ts | 157 - packages/fhir/src/resources/basic.ts | 103 - packages/fhir/src/resources/binary.ts | 76 - .../resources/biologicallyDerivedProduct.ts | 169 - packages/fhir/src/resources/bodyStructure.ts | 117 - packages/fhir/src/resources/bundle.ts | 254 - .../fhir/src/resources/capabilityStatement.ts | 296 - packages/fhir/src/resources/carePlan.ts | 244 - packages/fhir/src/resources/careTeam.ts | 154 - packages/fhir/src/resources/catalogEntry.ts | 133 - packages/fhir/src/resources/chargeItem.ts | 153 - .../src/resources/chargeItemDefinition.ts | 182 - packages/fhir/src/resources/citation.ts | 396 - packages/fhir/src/resources/claim.ts | 311 - packages/fhir/src/resources/claimResponse.ts | 266 - .../fhir/src/resources/clinicalImpression.ts | 174 - .../src/resources/clinicalUseDefinition.ts | 186 - packages/fhir/src/resources/codeSystem.ts | 230 - packages/fhir/src/resources/communication.ts | 172 - .../src/resources/communicationRequest.ts | 164 - .../src/resources/compartmentDefinition.ts | 100 - packages/fhir/src/resources/composition.ts | 138 - packages/fhir/src/resources/conceptMap.ts | 164 - packages/fhir/src/resources/condition.ts | 296 - packages/fhir/src/resources/consent.ts | 206 - packages/fhir/src/resources/contract.ts | 420 - packages/fhir/src/resources/coverage.ts | 173 - .../resources/coverageEligibilityRequest.ts | 168 - .../resources/coverageEligibilityResponse.ts | 184 - packages/fhir/src/resources/detectedIssue.ts | 153 - packages/fhir/src/resources/device.ts | 197 - .../fhir/src/resources/deviceDefinition.ts | 220 - packages/fhir/src/resources/deviceMetric.ts | 134 - packages/fhir/src/resources/deviceRequest.ts | 217 - .../fhir/src/resources/deviceUseStatement.ts | 176 - .../fhir/src/resources/diagnosticReport.ts | 133 - .../fhir/src/resources/documentManifest.ts | 128 - .../fhir/src/resources/documentReference.ts | 187 - packages/fhir/src/resources/encounter.ts | 250 - packages/fhir/src/resources/endpoint.ts | 118 - .../fhir/src/resources/enrollmentRequest.ts | 107 - .../fhir/src/resources/enrollmentResponse.ts | 114 - packages/fhir/src/resources/episodeOfCare.ts | 146 - .../fhir/src/resources/eventDefinition.ts | 153 - packages/fhir/src/resources/evidence.ts | 257 - packages/fhir/src/resources/evidenceReport.ts | 148 - .../fhir/src/resources/evidenceVariable.ts | 206 - .../fhir/src/resources/exampleScenario.ts | 248 - .../src/resources/explanationOfBenefit.ts | 473 - .../fhir/src/resources/familyMemberHistory.ts | 189 - .../fhir/src/resources/fhirDomainResource.ts | 636 - packages/fhir/src/resources/fhirResource.ts | 300 - packages/fhir/src/resources/flag.ts | 124 - packages/fhir/src/resources/goal.ts | 181 - .../fhir/src/resources/graphDefinition.ts | 137 - packages/fhir/src/resources/group.ts | 139 - .../fhir/src/resources/guidanceResponse.ts | 124 - .../fhir/src/resources/healthcareService.ts | 218 - packages/fhir/src/resources/imagingStudy.ts | 223 - packages/fhir/src/resources/immunization.ts | 274 - .../src/resources/immunizationEvaluation.ts | 124 - .../resources/immunizationRecommendation.ts | 185 - .../fhir/src/resources/implementationGuide.ts | 228 - packages/fhir/src/resources/ingredient.ts | 117 - packages/fhir/src/resources/insurancePlan.ts | 191 - packages/fhir/src/resources/invoice.ts | 183 - packages/fhir/src/resources/library.ts | 151 - packages/fhir/src/resources/linkage.ts | 61 - packages/fhir/src/resources/list.ts | 143 - packages/fhir/src/resources/location.ts | 175 - .../resources/manufacturedItemDefinition.ts | 129 - packages/fhir/src/resources/measure.ts | 225 - packages/fhir/src/resources/measureReport.ts | 202 - packages/fhir/src/resources/media.ts | 166 - packages/fhir/src/resources/medication.ts | 153 - .../src/resources/medicationAdministration.ts | 201 - .../fhir/src/resources/medicationDispense.ts | 221 - .../fhir/src/resources/medicationKnowledge.ts | 278 - .../fhir/src/resources/medicationRequest.ts | 191 - .../fhir/src/resources/medicationStatement.ts | 153 - .../resources/medicinalProductDefinition.ts | 210 - .../fhir/src/resources/messageDefinition.ts | 132 - packages/fhir/src/resources/messageHeader.ts | 106 - .../fhir/src/resources/molecularSequence.ts | 239 - packages/fhir/src/resources/namingSystem.ts | 95 - packages/fhir/src/resources/nutritionOrder.ts | 162 - .../fhir/src/resources/nutritionProduct.ts | 119 - packages/fhir/src/resources/observation.ts | 373 - .../src/resources/observationDefinition.ts | 159 - .../fhir/src/resources/operationDefinition.ts | 170 - .../fhir/src/resources/operationOutcome.ts | 68 - packages/fhir/src/resources/organization.ts | 156 - .../src/resources/organizationAffiliation.ts | 115 - .../resources/packagedProductDefinition.ts | 143 - packages/fhir/src/resources/parameters.ts | 63 - packages/fhir/src/resources/patient.ts | 283 - packages/fhir/src/resources/paymentNotice.ts | 116 - .../src/resources/paymentReconciliation.ts | 158 - packages/fhir/src/resources/person.ts | 131 - packages/fhir/src/resources/planDefinition.ts | 302 - packages/fhir/src/resources/practitioner.ts | 160 - .../fhir/src/resources/practitionerRole.ts | 203 - packages/fhir/src/resources/procedure.ts | 255 - packages/fhir/src/resources/provenance.ts | 106 - packages/fhir/src/resources/questionnaire.ts | 269 - .../src/resources/questionnaireResponse.ts | 307 - .../src/resources/regulatedAuthorization.ts | 90 - packages/fhir/src/resources/relatedPerson.ts | 139 - packages/fhir/src/resources/requestGroup.ts | 233 - .../fhir/src/resources/researchDefinition.ts | 162 - .../resources/researchElementDefinition.ts | 212 - packages/fhir/src/resources/researchStudy.ts | 153 - .../fhir/src/resources/researchSubject.ts | 110 - packages/fhir/src/resources/riskAssessment.ts | 168 - packages/fhir/src/resources/schedule.ts | 170 - .../fhir/src/resources/searchParameter.ts | 123 - packages/fhir/src/resources/serviceRequest.ts | 205 - packages/fhir/src/resources/slot.ts | 192 - packages/fhir/src/resources/specimen.ts | 200 - .../fhir/src/resources/specimenDefinition.ts | 118 - .../fhir/src/resources/structureDefinition.ts | 594 - packages/fhir/src/resources/structureMap.ts | 264 - packages/fhir/src/resources/subscription.ts | 81 - .../fhir/src/resources/subscriptionStatus.ts | 84 - .../fhir/src/resources/subscriptionTopic.ts | 222 - packages/fhir/src/resources/substance.ts | 138 - .../fhir/src/resources/substanceDefinition.ts | 266 - packages/fhir/src/resources/supplyDelivery.ts | 148 - packages/fhir/src/resources/supplyRequest.ts | 160 - packages/fhir/src/resources/task.ts | 186 - .../src/resources/terminologyCapabilities.ts | 204 - packages/fhir/src/resources/testReport.ts | 155 - packages/fhir/src/resources/testScript.ts | 327 - packages/fhir/src/resources/valueSet.ts | 254 - .../fhir/src/resources/verificationResult.ts | 116 - .../fhir/src/resources/visionPrescription.ts | 187 - packages/fhir/src/valueSets/account.ts | 27 - .../src/valueSets/actionParticipantType.ts | 26 - .../administrableProductDefinition.ts | 28 - .../src/valueSets/administrativeGender.ts | 26 - packages/fhir/src/valueSets/adverseEvent.ts | 21 - .../fhir/src/valueSets/allergyIntolerance.ts | 78 - packages/fhir/src/valueSets/appointment.ts | 69 - .../fhir/src/valueSets/appointmentResponse.ts | 28 - .../valueSets/biologicallyDerivedProduct.ts | 64 - packages/fhir/src/valueSets/bundle.ts | 68 - .../fhir/src/valueSets/capabilityStatement.ts | 175 - packages/fhir/src/valueSets/carePlan.ts | 91 - packages/fhir/src/valueSets/careTeam.ts | 27 - packages/fhir/src/valueSets/catalogEntry.ts | 43 - packages/fhir/src/valueSets/chargeItem.ts | 29 - .../src/valueSets/chargeItemDefinition.ts | 28 - packages/fhir/src/valueSets/claim.ts | 25 - .../fhir/src/valueSets/clinicalImpression.ts | 27 - .../src/valueSets/clinicalUseDefinition.ts | 29 - packages/fhir/src/valueSets/codeSystem.ts | 90 - packages/fhir/src/valueSets/communication.ts | 30 - .../src/valueSets/communicationRequest.ts | 31 - .../src/valueSets/compartmentDefinition.ts | 21 - packages/fhir/src/valueSets/composition.ts | 64 - packages/fhir/src/valueSets/conceptMap.ts | 50 - packages/fhir/src/valueSets/consent.ts | 57 - packages/fhir/src/valueSets/contract.ts | 67 - packages/fhir/src/valueSets/coverage.ts | 26 - .../fhir/src/valueSets/coverageEligibility.ts | 47 - packages/fhir/src/valueSets/daysOfWeek.ts | 29 - packages/fhir/src/valueSets/detectedIssue.ts | 42 - packages/fhir/src/valueSets/device.ts | 45 - .../fhir/src/valueSets/deviceDefinition.ts | 30 - packages/fhir/src/valueSets/deviceMetric.ts | 104 - packages/fhir/src/valueSets/deviceRequest.ts | 51 - .../fhir/src/valueSets/deviceUseStatement.ts | 30 - .../fhir/src/valueSets/diagnosticReport.ts | 34 - .../fhir/src/valueSets/documentManifest.ts | 27 - .../fhir/src/valueSets/documentReference.ts | 65 - packages/fhir/src/valueSets/encounter.ts | 50 - packages/fhir/src/valueSets/endpoint.ts | 28 - packages/fhir/src/valueSets/episodeOfCare.ts | 29 - packages/fhir/src/valueSets/evidenceReport.ts | 61 - .../fhir/src/valueSets/evidenceVariable.ts | 47 - .../fhir/src/valueSets/exampleScenario.ts | 22 - .../src/valueSets/explanationOfBenefit.ts | 26 - .../fhir/src/valueSets/familyMemberHistory.ts | 28 - packages/fhir/src/valueSets/fhir.ts | 797 - .../fhir/src/valueSets/financialResource.ts | 99 - packages/fhir/src/valueSets/flag.ts | 25 - packages/fhir/src/valueSets/goal.ts | 31 - .../fhir/src/valueSets/graphDefinition.ts | 43 - packages/fhir/src/valueSets/group.ts | 28 - .../fhir/src/valueSets/guidanceResponse.ts | 30 - .../src/valueSets/identityAssuranceLevel.ts | 28 - packages/fhir/src/valueSets/imagingStudy.ts | 27 - packages/fhir/src/valueSets/immunization.ts | 25 - .../src/valueSets/immunizationEvaluation.ts | 26 - .../fhir/src/valueSets/implementationGuide.ts | 49 - packages/fhir/src/valueSets/index.ts | 129 - packages/fhir/src/valueSets/ingredient.ts | 44 - packages/fhir/src/valueSets/invoice.ts | 29 - packages/fhir/src/valueSets/linkage.ts | 25 - packages/fhir/src/valueSets/list.ts | 37 - packages/fhir/src/valueSets/location.ts | 33 - .../valueSets/manufacturedItemDefinition.ts | 28 - packages/fhir/src/valueSets/measure.ts | 77 - packages/fhir/src/valueSets/media.ts | 30 - packages/fhir/src/valueSets/medication.ts | 25 - .../src/valueSets/medicationAdministration.ts | 31 - .../fhir/src/valueSets/medicationDispense.ts | 33 - .../fhir/src/valueSets/medicationRequest.ts | 55 - .../fhir/src/valueSets/medicationStatement.ts | 32 - .../fhir/src/valueSets/messageDefinition.ts | 44 - packages/fhir/src/valueSets/messageHeader.ts | 27 - .../fhir/src/valueSets/molecularSequence.ts | 74 - packages/fhir/src/valueSets/namingSystem.ts | 44 - packages/fhir/src/valueSets/nutritionOrder.ts | 51 - .../fhir/src/valueSets/nutritionProduct.ts | 27 - packages/fhir/src/valueSets/observation.ts | 30 - .../src/valueSets/observationDefinition.ts | 53 - .../fhir/src/valueSets/operationDefinition.ts | 51 - .../fhir/src/valueSets/operationOutcome.ts | 69 - packages/fhir/src/valueSets/patient.ts | 26 - packages/fhir/src/valueSets/planDefinition.ts | 158 - packages/fhir/src/valueSets/procedure.ts | 30 - packages/fhir/src/valueSets/provenance.ts | 27 - .../fhir/src/valueSets/publicationStatus.ts | 26 - packages/fhir/src/valueSets/questionnaire.ts | 91 - .../src/valueSets/questionnaireResponse.ts | 29 - .../src/valueSets/referenceVersionRules.ts | 25 - packages/fhir/src/valueSets/requestGroup.ts | 181 - packages/fhir/src/valueSets/requestIntent.ts | 31 - .../fhir/src/valueSets/requestPriority.ts | 26 - .../fhir/src/valueSets/requestResourceType.ts | 37 - .../valueSets/researchElementDefinition.ts | 43 - packages/fhir/src/valueSets/researchStudy.ts | 72 - .../fhir/src/valueSets/researchSubject.ts | 35 - .../fhir/src/valueSets/searchParameter.ts | 98 - packages/fhir/src/valueSets/serviceRequest.ts | 51 - packages/fhir/src/valueSets/slot.ts | 27 - packages/fhir/src/valueSets/specimen.ts | 26 - .../fhir/src/valueSets/specimenDefinition.ts | 26 - .../fhir/src/valueSets/structureDefinition.ts | 139 - packages/fhir/src/valueSets/structureMap.ts | 134 - packages/fhir/src/valueSets/subscription.ts | 48 - .../fhir/src/valueSets/subscriptionStatus.ts | 46 - .../fhir/src/valueSets/subscriptionTopic.ts | 70 - packages/fhir/src/valueSets/substance.ts | 25 - packages/fhir/src/valueSets/supplyDelivery.ts | 26 - packages/fhir/src/valueSets/supplyRequest.ts | 29 - packages/fhir/src/valueSets/task.ts | 56 - .../src/valueSets/terminologyCapabilities.ts | 21 - packages/fhir/src/valueSets/testReport.ts | 73 - packages/fhir/src/valueSets/testScript.ts | 131 - .../fhir/src/valueSets/verificationResult.ts | 30 - .../fhir/src/valueSets/visionPrescription.ts | 52 - .../riskAssessment.ts => zod/r4/index.ts} | 4 +- packages/fhir/src/zod/r4/schemas.ts | 31881 +++++++++++++++ packages/fhir/src/zod/r4b/index.ts | 9 + packages/fhir/src/zod/r4b/schemas.ts | 32703 ++++++++++++++++ packages/fhir/src/zod/support.ts | 22 + packages/fhir/test/coverage-tsconfig.json | 10 - .../fhir/test/coverage-tsconfig.json.license | 6 - packages/fhir/test/elements/elements.test.ts | 243 - packages/fhir/test/exports.test.ts | 111 + packages/fhir/test/extract-coverage.test.ts | 273 + packages/fhir/test/extract-resources.test.ts | 268 + packages/fhir/test/extract.test.ts | 358 + .../fhir/test/foundation-boundaries.test.ts | 369 + packages/fhir/test/foundation.test.ts | 438 + packages/fhir/test/mobile.test.ts | 208 + packages/fhir/test/providers.test.ts | 1772 + packages/fhir/test/questionnaire.test.ts | 2937 ++ packages/fhir/test/recording.test.ts | 709 + packages/fhir/test/resources/accounts.json | 216 - .../fhir/test/resources/accounts.json.license | 6 - packages/fhir/test/resources/accounts.test.ts | 31 - .../test/resources/activityDefinitions.json | 195 - .../activityDefinitions.json.license | 6 - .../resources/activityDefinitions.test.ts | 38 - .../additionalResourceMethods.test.ts | 235 - .../administrableProductDefinitions.json | 231 - ...ministrableProductDefinitions.json.license | 6 - .../administrableProductDefinitions.test.ts | 39 - .../fhir/test/resources/adverseEvents.json | 175 - .../test/resources/adverseEvents.json.license | 6 - .../fhir/test/resources/adverseEvents.test.ts | 35 - .../test/resources/allergyIntolerances.json | 182 - .../allergyIntolerances.json.license | 6 - .../resources/allergyIntolerances.test.ts | 38 - .../test/resources/appointmentResponses.json | 90 - .../appointmentResponses.json.license | 6 - .../resources/appointmentResponses.test.ts | 38 - .../fhir/test/resources/appointments.json | 196 - .../test/resources/appointments.json.license | 6 - .../fhir/test/resources/appointments.test.ts | 35 - packages/fhir/test/resources/auditEvents.json | 301 - .../test/resources/auditEvents.json.license | 5 - .../fhir/test/resources/auditEvents.test.ts | 35 - packages/fhir/test/resources/basics.json | 88 - .../fhir/test/resources/basics.json.license | 6 - packages/fhir/test/resources/basics.test.ts | 32 - packages/fhir/test/resources/binaries.json | 32 - .../fhir/test/resources/binaries.json.license | 6 - packages/fhir/test/resources/binaries.test.ts | 32 - .../fhir/test/resources/binaryData.test.ts | 145 - .../biologicallyDerivedProducts.json | 148 - .../biologicallyDerivedProducts.json.license | 6 - .../biologicallyDerivedProducts.test.ts | 39 - .../fhir/test/resources/bodyStructures.json | 164 - .../resources/bodyStructures.json.license | 6 - .../test/resources/bodyStructures.test.ts | 35 - .../resources/bundleFindResourcesWith.test.ts | 214 - .../resources/bundleWithNewResources.test.ts | 220 - packages/fhir/test/resources/bundles.json | 766 - .../fhir/test/resources/bundles.json.license | 6 - packages/fhir/test/resources/bundles.test.ts | 42 - .../test/resources/capabilityStatements.json | 82 - .../capabilityStatements.json.license | 5 - .../resources/capabilityStatements.test.ts | 38 - packages/fhir/test/resources/carePlans.json | 92 - .../test/resources/carePlans.json.license | 6 - .../fhir/test/resources/carePlans.test.ts | 32 - packages/fhir/test/resources/careTeams.json | 81 - .../test/resources/careTeams.json.license | 6 - .../fhir/test/resources/careTeams.test.ts | 32 - .../fhir/test/resources/catalogEntries.json | 127 - .../resources/catalogEntries.json.license | 6 - .../test/resources/catalogEntries.test.ts | 35 - .../test/resources/chargeItemDefinitions.json | 154 - .../chargeItemDefinitions.json.license | 6 - .../resources/chargeItemDefinitions.test.ts | 37 - packages/fhir/test/resources/chargeItems.json | 242 - .../test/resources/chargeItems.json.license | 6 - .../fhir/test/resources/chargeItems.test.ts | 34 - packages/fhir/test/resources/citations.json | 123 - .../fhir/test/resources/citations.test.ts | 32 - .../fhir/test/resources/claimResponses.json | 142 - .../resources/claimResponses.json.license | 6 - .../test/resources/claimResponses.test.ts | 34 - packages/fhir/test/resources/claims.json | 201 - .../fhir/test/resources/claims.json.license | 6 - packages/fhir/test/resources/claims.test.ts | 31 - .../test/resources/clinicalImpressions.json | 93 - .../clinicalImpressions.json.license | 6 - .../resources/clinicalImpressions.test.ts | 38 - .../resources/clinicalUseDefinitions.json | 223 - .../clinicalUseDefinitions.json.license | 5 - .../resources/clinicalUseDefinitions.test.ts | 38 - packages/fhir/test/resources/codeSystems.json | 300 - .../fhir/test/resources/codeSystems.test.ts | 35 - .../test/resources/communicationRequests.json | 78 - .../communicationRequests.json.license | 6 - .../resources/communicationRequests.test.ts | 38 - .../fhir/test/resources/communications.json | 89 - .../resources/communications.json.license | 6 - .../test/resources/communications.test.ts | 35 - .../resources/compartmentDefinitions.json | 80 - .../compartmentDefinitions.json.license | 6 - .../resources/compartmentDefinitions.test.ts | 38 - .../fhir/test/resources/compositions.json | 275 - .../test/resources/compositions.json.license | 6 - .../fhir/test/resources/compositions.test.ts | 35 - packages/fhir/test/resources/conceptMaps.json | 275 - .../fhir/test/resources/conceptMaps.test.ts | 35 - packages/fhir/test/resources/conditions.json | 202 - .../test/resources/conditions.json.license | 6 - .../fhir/test/resources/conditions.test.ts | 32 - packages/fhir/test/resources/consents.json | 207 - .../fhir/test/resources/consents.json.license | 5 - packages/fhir/test/resources/consents.test.ts | 32 - packages/fhir/test/resources/contracts.json | 293 - .../test/resources/contracts.json.license | 5 - .../fhir/test/resources/contracts.test.ts | 32 - .../coverageEligibilityRequests.json | 100 - .../coverageEligibilityRequests.json.license | 5 - .../coverageEligibilityRequests.test.ts | 38 - .../coverageEligibilityResponses.json | 111 - .../coverageEligibilityResponses.json.license | 5 - .../coverageEligibilityResponses.test.ts | 38 - packages/fhir/test/resources/coverages.json | 512 - .../test/resources/coverages.json.license | 6 - .../fhir/test/resources/coverages.test.ts | 31 - .../fhir/test/resources/detectedIssues.json | 162 - .../resources/detectedIssues.json.license | 6 - .../test/resources/detectedIssues.test.ts | 35 - .../test/resources/deviceDefinitions.json | 247 - .../resources/deviceDefinitions.json.license | 6 - .../test/resources/deviceDefinitions.test.ts | 38 - .../fhir/test/resources/deviceMetrics.json | 155 - .../test/resources/deviceMetrics.json.license | 6 - .../fhir/test/resources/deviceMetrics.test.ts | 35 - .../fhir/test/resources/deviceRequests.json | 205 - .../resources/deviceRequests.json.license | 6 - .../test/resources/deviceRequests.test.ts | 35 - .../test/resources/deviceUseStatements.json | 169 - .../deviceUseStatements.json.license | 6 - .../resources/deviceUseStatements.test.ts | 38 - packages/fhir/test/resources/devices.json | 179 - .../fhir/test/resources/devices.json.license | 6 - packages/fhir/test/resources/devices.test.ts | 32 - .../test/resources/diagnosticReports.json | 338 - .../resources/diagnosticReports.json.license | 6 - .../test/resources/diagnosticReports.test.ts | 37 - .../test/resources/documentManifests.json | 142 - .../resources/documentManifests.json.license | 6 - .../test/resources/documentManifests.test.ts | 38 - .../test/resources/documentReferences.json | 284 - .../resources/documentReferences.json.license | 6 - .../test/resources/documentReferences.test.ts | 38 - .../resources/domainResourceClass.test.ts | 151 - packages/fhir/test/resources/encounters.json | 251 - .../test/resources/encounters.json.license | 6 - .../fhir/test/resources/encounters.test.ts | 31 - packages/fhir/test/resources/endpoints.json | 106 - .../test/resources/endpoints.json.license | 6 - .../fhir/test/resources/endpoints.test.ts | 32 - .../test/resources/enrollmentRequests.json | 94 - .../resources/enrollmentRequests.json.license | 6 - .../test/resources/enrollmentRequests.test.ts | 37 - .../test/resources/enrollmentResponses.json | 98 - .../enrollmentResponses.json.license | 6 - .../resources/enrollmentResponses.test.ts | 37 - .../fhir/test/resources/episodeOfCares.json | 127 - .../resources/episodeOfCares.json.license | 6 - .../test/resources/episodeOfCares.test.ts | 35 - .../fhir/test/resources/eventDefinitions.json | 153 - .../resources/eventDefinitions.json.license | 6 - .../test/resources/eventDefinitions.test.ts | 38 - .../fhir/test/resources/evidenceReports.json | 207 - .../test/resources/evidenceReports.test.ts | 35 - .../test/resources/evidenceVariables.json | 123 - .../test/resources/evidenceVariables.test.ts | 38 - packages/fhir/test/resources/evidences.json | 193 - .../fhir/test/resources/evidences.test.ts | 32 - .../fhir/test/resources/exampleScenarios.json | 228 - .../resources/exampleScenarios.json.license | 6 - .../test/resources/exampleScenarios.test.ts | 38 - .../test/resources/explanationOfBenefits.json | 196 - .../explanationOfBenefits.json.license | 6 - .../resources/explanationOfBenefits.test.ts | 37 - .../test/resources/familyMemberHistories.json | 241 - .../familyMemberHistories.json.license | 6 - .../resources/familyMemberHistories.test.ts | 38 - packages/fhir/test/resources/flags.json | 93 - .../fhir/test/resources/flags.json.license | 6 - packages/fhir/test/resources/flags.test.ts | 32 - packages/fhir/test/resources/goals.json | 105 - .../fhir/test/resources/goals.json.license | 6 - packages/fhir/test/resources/goals.test.ts | 32 - .../fhir/test/resources/graphDefinitions.json | 113 - .../resources/graphDefinitions.json.license | 6 - .../test/resources/graphDefinitions.test.ts | 38 - packages/fhir/test/resources/groups.json | 162 - .../fhir/test/resources/groups.json.license | 6 - packages/fhir/test/resources/groups.test.ts | 32 - .../test/resources/guidanceResponses.json | 156 - .../resources/guidanceResponses.json.license | 6 - .../test/resources/guidanceResponses.test.ts | 38 - .../test/resources/healthcareServices.json | 122 - .../resources/healthcareServices.json.license | 6 - .../test/resources/healthcareServices.test.ts | 38 - .../fhir/test/resources/imagingStudies.json | 82 - .../resources/imagingStudies.json.license | 6 - .../test/resources/imagingStudies.test.ts | 35 - .../resources/immunizationEvaluations.json | 101 - .../immunizationEvaluations.json.license | 6 - .../resources/immunizationEvaluations.test.ts | 38 - .../immunizationRecommendations.json | 129 - .../immunizationRecommendations.json.license | 6 - .../immunizationRecommendations.test.ts | 58 - .../fhir/test/resources/immunizations.json | 147 - .../test/resources/immunizations.json.license | 6 - .../fhir/test/resources/immunizations.test.ts | 35 - .../test/resources/implementationGuides.json | 126 - .../implementationGuides.json.license | 5 - .../resources/implementationGuides.test.ts | 38 - packages/fhir/test/resources/ingredients.json | 183 - .../test/resources/ingredients.json.license | 6 - .../fhir/test/resources/ingredients.test.ts | 35 - .../fhir/test/resources/insurancePlans.json | 73 - .../resources/insurancePlans.json.license | 6 - .../test/resources/insurancePlans.test.ts | 34 - packages/fhir/test/resources/invoices.json | 89 - .../fhir/test/resources/invoices.json.license | 6 - packages/fhir/test/resources/invoices.test.ts | 31 - packages/fhir/test/resources/libraries.json | 103 - .../test/resources/libraries.json.license | 6 - .../fhir/test/resources/libraries.test.ts | 32 - packages/fhir/test/resources/linkages.json | 101 - .../fhir/test/resources/linkages.json.license | 6 - packages/fhir/test/resources/linkages.test.ts | 32 - packages/fhir/test/resources/lists.json | 173 - .../fhir/test/resources/lists.json.license | 6 - packages/fhir/test/resources/lists.test.ts | 32 - packages/fhir/test/resources/locations.json | 122 - .../test/resources/locations.json.license | 6 - .../fhir/test/resources/locations.test.ts | 32 - .../manufacturedItemDefinitions.json | 205 - .../manufacturedItemDefinitions.json.license | 6 - .../manufacturedItemDefinitions.test.ts | 39 - .../fhir/test/resources/measureReports.json | 324 - .../resources/measureReports.json.license | 6 - .../test/resources/measureReports.test.ts | 35 - packages/fhir/test/resources/measures.json | 327 - .../fhir/test/resources/measures.json.license | 6 - packages/fhir/test/resources/measures.test.ts | 32 - packages/fhir/test/resources/media.json | 240 - .../fhir/test/resources/media.json.license | 6 - packages/fhir/test/resources/media.test.ts | 32 - .../resources/medicationAdministrations.json | 235 - .../medicationAdministrations.json.license | 6 - .../medicationAdministrations.test.ts | 38 - .../test/resources/medicationDispenses.json | 287 - .../medicationDispenses.json.license | 6 - .../resources/medicationDispenses.test.ts | 38 - .../test/resources/medicationKnowledges.json | 175 - .../medicationKnowledges.json.license | 6 - .../resources/medicationKnowledges.test.ts | 38 - .../test/resources/medicationRequests.json | 175 - .../resources/medicationRequests.json.license | 6 - .../test/resources/medicationRequests.test.ts | 38 - .../test/resources/medicationStatements.json | 210 - .../medicationStatements.json.license | 6 - .../resources/medicationStatements.test.ts | 38 - packages/fhir/test/resources/medications.json | 5728 --- .../test/resources/medications.json.license | 6 - .../fhir/test/resources/medications.test.ts | 54 - .../medicinalProductDefinitions.json | 159 - .../medicinalProductDefinitions.json.license | 5 - .../medicinalProductDefinitions.test.ts | 39 - .../test/resources/messageDefinitions.json | 109 - .../resources/messageDefinitions.json.license | 6 - .../test/resources/messageDefinitions.test.ts | 38 - .../fhir/test/resources/messageHeaders.json | 101 - .../resources/messageHeaders.json.license | 6 - .../test/resources/messageHeaders.test.ts | 35 - .../test/resources/molecularSequences.json | 187 - .../resources/molecularSequences.json.license | 6 - .../test/resources/molecularSequences.test.ts | 38 - .../fhir/test/resources/namingSystems.json | 203 - .../fhir/test/resources/namingSystems.test.ts | 35 - .../fhir/test/resources/nutritionOrders.json | 213 - .../test/resources/nutritionOrders.test.ts | 35 - .../test/resources/nutritionProducts.json | 231 - .../test/resources/nutritionProducts.test.ts | 38 - .../resources/observationDefinitions.json | 312 - .../observationDefinitions.json.license | 6 - .../resources/observationDefinitions.test.ts | 38 - .../resources/observationEffective.test.ts | 251 - .../fhir/test/resources/observations.json | 68 - .../test/resources/observations.json.license | 6 - .../fhir/test/resources/observations.test.ts | 35 - .../test/resources/operationDefinitions.json | 176 - .../operationDefinitions.json.license | 6 - .../resources/operationDefinitions.test.ts | 38 - .../test/resources/operationOutcomes.json | 80 - .../resources/operationOutcomes.json.license | 6 - .../test/resources/operationOutcomes.test.ts | 38 - .../resources/organizationAffiliations.json | 196 - .../organizationAffiliations.json.license | 6 - .../organizationAffiliations.test.ts | 38 - .../fhir/test/resources/organizations.json | 154 - .../test/resources/organizations.json.license | 6 - .../fhir/test/resources/organizations.test.ts | 35 - .../resources/packagedProductDefinitions.json | 248 - .../packagedProductDefinitions.json.license | 6 - .../packagedProductDefinitions.test.ts | 39 - packages/fhir/test/resources/parameters.json | 79 - .../test/resources/parameters.json.license | 6 - .../fhir/test/resources/parameters.test.ts | 35 - .../test/resources/patientMethods.test.ts | 171 - packages/fhir/test/resources/patients.json | 205 - .../fhir/test/resources/patients.json.license | 6 - packages/fhir/test/resources/patients.test.ts | 32 - .../fhir/test/resources/paymentNotices.json | 113 - .../resources/paymentNotices.json.license | 6 - .../test/resources/paymentNotices.test.ts | 34 - .../resources/paymentReconciliations.json | 101 - .../paymentReconciliations.json.license | 6 - .../resources/paymentReconciliations.test.ts | 37 - packages/fhir/test/resources/persons.json | 132 - .../fhir/test/resources/persons.json.license | 6 - packages/fhir/test/resources/persons.test.ts | 32 - .../fhir/test/resources/planDefinitions.json | 293 - .../resources/planDefinitions.json.license | 6 - .../test/resources/planDefinitions.test.ts | 35 - .../test/resources/practitionerRoles.json | 156 - .../resources/practitionerRoles.json.license | 6 - .../test/resources/practitionerRoles.test.ts | 38 - .../fhir/test/resources/practitioners.json | 573 - .../test/resources/practitioners.json.license | 6 - .../fhir/test/resources/practitioners.test.ts | 34 - packages/fhir/test/resources/procedures.json | 154 - .../test/resources/procedures.json.license | 6 - .../fhir/test/resources/procedures.test.ts | 32 - packages/fhir/test/resources/provenances.json | 186 - .../test/resources/provenances.json.license | 5 - .../fhir/test/resources/provenances.test.ts | 35 - .../resources/questionnaireResponses.json | 131 - .../questionnaireResponses.json.license | 6 - .../resources/questionnaireResponses.test.ts | 38 - .../fhir/test/resources/questionnaires.json | 7183 ---- .../resources/questionnaires.json.license | 6 - .../test/resources/questionnaires.test.ts | 35 - .../resources/regulatedAuthorizations.json | 190 - .../regulatedAuthorizations.json.license | 6 - .../resources/regulatedAuthorizations.test.ts | 38 - .../fhir/test/resources/relatedPersons.json | 175 - .../resources/relatedPersons.json.license | 6 - .../test/resources/relatedPersons.test.ts | 35 - .../fhir/test/resources/requestGroups.json | 97 - .../test/resources/requestGroups.json.license | 6 - .../fhir/test/resources/requestGroups.test.ts | 35 - .../test/resources/researchDefinitions.json | 95 - .../researchDefinitions.json.license | 5 - .../resources/researchDefinitions.test.ts | 38 - .../resources/researchElementDefinitions.json | 103 - .../researchElementDefinitions.json.license | 5 - .../researchElementDefinitions.test.ts | 39 - .../fhir/test/resources/researchStudies.json | 140 - .../resources/researchStudies.json.license | 5 - .../test/resources/researchStudies.test.ts | 35 - .../fhir/test/resources/researchSubjects.json | 72 - .../resources/researchSubjects.json.license | 5 - .../test/resources/researchSubjects.test.ts | 38 - .../fhir/test/resources/riskAssessments.json | 221 - .../resources/riskAssessments.json.license | 6 - .../test/resources/riskAssessments.test.ts | 35 - packages/fhir/test/resources/schedules.json | 108 - .../test/resources/schedules.json.license | 6 - .../fhir/test/resources/schedules.test.ts | 32 - .../fhir/test/resources/searchParameters.json | 62 - .../resources/searchParameters.json.license | 6 - .../test/resources/searchParameters.test.ts | 38 - .../fhir/test/resources/serviceRequests.json | 308 - .../resources/serviceRequests.json.license | 6 - .../test/resources/serviceRequests.test.ts | 34 - packages/fhir/test/resources/slots.json | 93 - .../fhir/test/resources/slots.json.license | 6 - packages/fhir/test/resources/slots.test.ts | 45 - .../test/resources/specimenDefinitions.json | 345 - .../specimenDefinitions.json.license | 6 - .../resources/specimenDefinitions.test.ts | 38 - packages/fhir/test/resources/specimens.json | 181 - .../test/resources/specimens.json.license | 6 - .../fhir/test/resources/specimens.test.ts | 32 - .../test/resources/structureDefinitions.json | 103 - .../structureDefinitions.json.license | 5 - .../resources/structureDefinitions.test.ts | 38 - .../fhir/test/resources/structureMaps.json | 241 - .../test/resources/structureMaps.json.license | 6 - .../fhir/test/resources/structureMaps.test.ts | 35 - .../test/resources/subscriptionStatuses.json | 78 - .../subscriptionStatuses.json.license | 6 - .../resources/subscriptionStatuses.test.ts | 38 - .../test/resources/subscriptionTopics.json | 115 - .../resources/subscriptionTopics.json.license | 6 - .../test/resources/subscriptionTopics.test.ts | 38 - .../fhir/test/resources/subscriptions.json | 65 - .../test/resources/subscriptions.json.license | 6 - .../fhir/test/resources/subscriptions.test.ts | 35 - .../test/resources/substanceDefinitions.json | 153 - .../substanceDefinitions.json.license | 5 - .../resources/substanceDefinitions.test.ts | 38 - packages/fhir/test/resources/substances.json | 105 - .../test/resources/substances.json.license | 6 - .../fhir/test/resources/substances.test.ts | 32 - .../fhir/test/resources/supplyDeliveries.json | 186 - .../resources/supplyDeliveries.json.license | 5 - .../test/resources/supplyDeliveries.test.ts | 38 - .../fhir/test/resources/supplyRequests.json | 228 - .../resources/supplyRequests.json.license | 5 - .../test/resources/supplyRequests.test.ts | 35 - packages/fhir/test/resources/tasks.json | 111 - .../fhir/test/resources/tasks.json.license | 6 - packages/fhir/test/resources/tasks.test.ts | 32 - .../resources/terminologyCapabilities.json | 267 - .../resources/terminologyCapabilities.test.ts | 38 - packages/fhir/test/resources/testHelpers.ts | 30 - packages/fhir/test/resources/testReports.json | 145 - .../test/resources/testReports.json.license | 6 - .../fhir/test/resources/testReports.test.ts | 35 - packages/fhir/test/resources/testScripts.json | 65 - .../test/resources/testScripts.json.license | 5 - .../fhir/test/resources/testScripts.test.ts | 35 - packages/fhir/test/resources/valueSets.json | 285 - .../fhir/test/resources/valueSets.test.ts | 32 - .../test/resources/verificationResults.json | 293 - .../verificationResults.json.license | 5 - .../resources/verificationResults.test.ts | 38 - .../test/resources/visionPrescriptions.json | 145 - .../visionPrescriptions.json.license | 5 - .../resources/visionPrescriptions.test.ts | 38 - packages/fhir/test/tsconfig.json | 3 - packages/fhir/test/tsconfig.json.license | 3 - .../fhir/test/valueSets/valueSets.test.ts | 67 - packages/fhir/test/zod-fixtures.test.ts | 53 + .../test/zod-generation-invariants.test.ts | 567 + .../fhir/test/zod-official-examples.test.ts | 144 + packages/fhir/test/zod-schemas.test.ts | 318 + packages/fhir/tsconfig.json | 12 +- 914 files changed, 109169 insertions(+), 80837 deletions(-) create mode 100644 .github/workflows/fhir-conformance.yml create mode 100644 packages/fhir/catalog/measurement-capabilities.json rename packages/fhir/{test/resources/codeSystems.json.license => catalog/measurement-capabilities.json.license} (100%) create mode 100644 packages/fhir/fixtures/conformance/manifest.json rename packages/fhir/{test/resources/conceptMaps.json.license => fixtures/conformance/manifest.json.license} (100%) create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-active-energy.json rename packages/fhir/{test/resources/namingSystems.json.license => fixtures/conformance/resources/mobile-active-energy.json.license} (100%) create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json rename packages/fhir/{test/resources/nutritionOrders.json.license => fixtures/conformance/resources/mobile-basal-energy.json.license} (100%) create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json rename packages/fhir/{test/resources/nutritionProducts.json.license => fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json.license} (100%) create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json rename packages/fhir/{test/resources/terminologyCapabilities.json.license => fixtures/conformance/resources/mobile-blood-pressure.json.license} (100%) create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json rename packages/fhir/{test/resources/valueSets.json.license => fixtures/conformance/resources/mobile-body-fat-percentage.json.license} (100%) create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-body-height.json rename packages/fhir/{test/resources/evidenceReports.json.license => fixtures/conformance/resources/mobile-body-height.json.license} (93%) create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json rename packages/fhir/{test/resources/evidenceVariables.json.license => fixtures/conformance/resources/mobile-body-temperature.json.license} (93%) create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json rename packages/fhir/{test/resources/citations.json.license => fixtures/conformance/resources/mobile-body-water-mass.json.license} (93%) create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-body-weight.json rename packages/fhir/{test/resources/evidences.json.license => fixtures/conformance/resources/mobile-body-weight.json.license} (93%) create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-distance.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-distance.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-step-count.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-step-count.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-workout.json create mode 100644 packages/fhir/fixtures/conformance/resources/mobile-workout.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/providers-oura-daily-activity.json create mode 100644 packages/fhir/fixtures/conformance/resources/providers-oura-daily-activity.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/questionnaire-response.json create mode 100644 packages/fhir/fixtures/conformance/resources/questionnaire-response.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/questionnaire.json create mode 100644 packages/fhir/fixtures/conformance/resources/questionnaire.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json create mode 100644 packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json create mode 100644 packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json create mode 100644 packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json create mode 100644 packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json.license create mode 100644 packages/fhir/fixtures/normalized/google-health-active-energy.json create mode 100644 packages/fhir/fixtures/normalized/google-health-active-energy.json.license create mode 100644 packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json create mode 100644 packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json.license create mode 100644 packages/fhir/fixtures/normalized/oura-daily-activity.json create mode 100644 packages/fhir/fixtures/normalized/oura-daily-activity.json.license create mode 100644 packages/fhir/fixtures/normalized/oura-heart-rate-recording.json create mode 100644 packages/fhir/fixtures/normalized/oura-heart-rate-recording.json.license create mode 100644 packages/fhir/fixtures/normalized/oura-sleep-duration.json create mode 100644 packages/fhir/fixtures/normalized/oura-sleep-duration.json.license create mode 100644 packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json create mode 100644 packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json.license create mode 100644 packages/fhir/fixtures/normalized/withings-body-weight.json create mode 100644 packages/fhir/fixtures/normalized/withings-body-weight.json.license create mode 100644 packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json create mode 100644 packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json.license create mode 100644 packages/fhir/grove-fhir.json create mode 100644 packages/fhir/grove-fhir.json.license create mode 100644 packages/fhir/scripts/browser-smoke.mjs create mode 100644 packages/fhir/scripts/fetch-grove-fhir.mjs create mode 100644 packages/fhir/scripts/generate-conformance-fixtures.mjs create mode 100644 packages/fhir/scripts/generate-measurement-catalog.mjs create mode 100644 packages/fhir/scripts/generate-zod-schemas.d.mts create mode 100644 packages/fhir/scripts/generate-zod-schemas.mjs create mode 100644 packages/fhir/scripts/package-smoke.mjs create mode 100644 packages/fhir/scripts/validate-conformance.mjs create mode 100644 packages/fhir/src/core/freeze.ts create mode 100644 packages/fhir/src/core/index.ts create mode 100644 packages/fhir/src/core/primitives.ts create mode 100644 packages/fhir/src/core/quantity.ts create mode 100644 packages/fhir/src/core/result.ts delete mode 100644 packages/fhir/src/elements/anyValueSchema.ts delete mode 100644 packages/fhir/src/elements/backBoneElement.ts delete mode 100644 packages/fhir/src/elements/dataTypes/address.ts delete mode 100644 packages/fhir/src/elements/dataTypes/annotation.ts delete mode 100644 packages/fhir/src/elements/dataTypes/attachment.ts delete mode 100644 packages/fhir/src/elements/dataTypes/codeableConcept.ts delete mode 100644 packages/fhir/src/elements/dataTypes/codeableReference.ts delete mode 100644 packages/fhir/src/elements/dataTypes/coding.ts delete mode 100644 packages/fhir/src/elements/dataTypes/contactPoint.ts delete mode 100644 packages/fhir/src/elements/dataTypes/humanName.ts delete mode 100644 packages/fhir/src/elements/dataTypes/identifier.ts delete mode 100644 packages/fhir/src/elements/dataTypes/marketingStatus.ts delete mode 100644 packages/fhir/src/elements/dataTypes/money.ts delete mode 100644 packages/fhir/src/elements/dataTypes/narrative.ts delete mode 100644 packages/fhir/src/elements/dataTypes/period.ts delete mode 100644 packages/fhir/src/elements/dataTypes/primitiveTypes.ts delete mode 100644 packages/fhir/src/elements/dataTypes/quantity.ts delete mode 100644 packages/fhir/src/elements/dataTypes/range.ts delete mode 100644 packages/fhir/src/elements/dataTypes/ratio.ts delete mode 100644 packages/fhir/src/elements/dataTypes/ratioRange.ts delete mode 100644 packages/fhir/src/elements/dataTypes/reference.ts delete mode 100644 packages/fhir/src/elements/dataTypes/sampledData.ts delete mode 100644 packages/fhir/src/elements/dataTypes/signature.ts delete mode 100644 packages/fhir/src/elements/dataTypes/timing.ts delete mode 100644 packages/fhir/src/elements/domainResource.ts delete mode 100644 packages/fhir/src/elements/dosage.ts delete mode 100644 packages/fhir/src/elements/element.ts delete mode 100644 packages/fhir/src/elements/extension.ts delete mode 100644 packages/fhir/src/elements/index.ts delete mode 100644 packages/fhir/src/elements/meta.ts delete mode 100644 packages/fhir/src/elements/metaDataTypes/contactDetail.ts delete mode 100644 packages/fhir/src/elements/metaDataTypes/contributor.ts delete mode 100644 packages/fhir/src/elements/metaDataTypes/dataRequirement.ts delete mode 100644 packages/fhir/src/elements/metaDataTypes/expression.ts delete mode 100644 packages/fhir/src/elements/metaDataTypes/parameterDefinition.ts delete mode 100644 packages/fhir/src/elements/metaDataTypes/relatedArtifact.ts delete mode 100644 packages/fhir/src/elements/metaDataTypes/triggerDefinition.ts delete mode 100644 packages/fhir/src/elements/metaDataTypes/usageContext.ts delete mode 100644 packages/fhir/src/elements/resource.ts create mode 100644 packages/fhir/src/extract/careplan.ts create mode 100644 packages/fhir/src/extract/coding.ts create mode 100644 packages/fhir/src/extract/condition.ts create mode 100644 packages/fhir/src/extract/encode.ts create mode 100644 packages/fhir/src/extract/identifier.ts create mode 100644 packages/fhir/src/extract/immunization.ts create mode 100644 packages/fhir/src/extract/index.ts create mode 100644 packages/fhir/src/extract/internal.ts create mode 100644 packages/fhir/src/extract/observation.ts create mode 100644 packages/fhir/src/extract/patient.ts create mode 100644 packages/fhir/src/extract/period.ts create mode 100644 packages/fhir/src/extract/person.ts create mode 100644 packages/fhir/src/extract/resource.ts create mode 100644 packages/fhir/src/extract/scheduling.ts create mode 100644 packages/fhir/src/mobile/identity.ts create mode 100644 packages/fhir/src/mobile/index.ts create mode 100644 packages/fhir/src/mobile/measurement-catalog.generated.ts create mode 100644 packages/fhir/src/mobile/time.ts create mode 100644 packages/fhir/src/mobile/types.ts rename packages/fhir/src/{valueSets/claimResponse.ts => provenance/index.ts} (54%) create mode 100644 packages/fhir/src/providers/builder.ts create mode 100644 packages/fhir/src/providers/contract.generated.ts create mode 100644 packages/fhir/src/providers/graph.ts create mode 100644 packages/fhir/src/providers/identity.ts create mode 100644 packages/fhir/src/providers/index.ts create mode 100644 packages/fhir/src/providers/privacy.ts create mode 100644 packages/fhir/src/providers/profiles.ts create mode 100644 packages/fhir/src/providers/provider.ts create mode 100644 packages/fhir/src/providers/recording.ts create mode 100644 packages/fhir/src/providers/types.ts create mode 100644 packages/fhir/src/questionnaire/builder.ts create mode 100644 packages/fhir/src/questionnaire/contract.generated.ts create mode 100644 packages/fhir/src/questionnaire/contract.ts create mode 100644 packages/fhir/src/questionnaire/index.ts create mode 100644 packages/fhir/src/questionnaire/parse.ts create mode 100644 packages/fhir/src/questionnaire/preflight.ts create mode 100644 packages/fhir/src/questionnaire/r4-resource-types.ts create mode 100644 packages/fhir/src/questionnaire/schemas.ts create mode 100644 packages/fhir/src/questionnaire/temporal.ts create mode 100644 packages/fhir/src/questionnaire/types.ts create mode 100644 packages/fhir/src/r4/index.ts create mode 100644 packages/fhir/src/r4/parse.ts create mode 100644 packages/fhir/src/r4/schemas.ts create mode 100644 packages/fhir/src/r4/types.ts delete mode 100644 packages/fhir/src/resources/account.ts delete mode 100644 packages/fhir/src/resources/activityDefinition.ts delete mode 100644 packages/fhir/src/resources/administrableProductDefinition.ts delete mode 100644 packages/fhir/src/resources/adverseEvent.ts delete mode 100644 packages/fhir/src/resources/allergyIntolerance.ts delete mode 100644 packages/fhir/src/resources/appointment.ts delete mode 100644 packages/fhir/src/resources/appointmentResponse.ts delete mode 100644 packages/fhir/src/resources/auditEvent.ts delete mode 100644 packages/fhir/src/resources/basic.ts delete mode 100644 packages/fhir/src/resources/binary.ts delete mode 100644 packages/fhir/src/resources/biologicallyDerivedProduct.ts delete mode 100644 packages/fhir/src/resources/bodyStructure.ts delete mode 100644 packages/fhir/src/resources/bundle.ts delete mode 100644 packages/fhir/src/resources/capabilityStatement.ts delete mode 100644 packages/fhir/src/resources/carePlan.ts delete mode 100644 packages/fhir/src/resources/careTeam.ts delete mode 100644 packages/fhir/src/resources/catalogEntry.ts delete mode 100644 packages/fhir/src/resources/chargeItem.ts delete mode 100644 packages/fhir/src/resources/chargeItemDefinition.ts delete mode 100644 packages/fhir/src/resources/citation.ts delete mode 100644 packages/fhir/src/resources/claim.ts delete mode 100644 packages/fhir/src/resources/claimResponse.ts delete mode 100644 packages/fhir/src/resources/clinicalImpression.ts delete mode 100644 packages/fhir/src/resources/clinicalUseDefinition.ts delete mode 100644 packages/fhir/src/resources/codeSystem.ts delete mode 100644 packages/fhir/src/resources/communication.ts delete mode 100644 packages/fhir/src/resources/communicationRequest.ts delete mode 100644 packages/fhir/src/resources/compartmentDefinition.ts delete mode 100644 packages/fhir/src/resources/composition.ts delete mode 100644 packages/fhir/src/resources/conceptMap.ts delete mode 100644 packages/fhir/src/resources/condition.ts delete mode 100644 packages/fhir/src/resources/consent.ts delete mode 100644 packages/fhir/src/resources/contract.ts delete mode 100644 packages/fhir/src/resources/coverage.ts delete mode 100644 packages/fhir/src/resources/coverageEligibilityRequest.ts delete mode 100644 packages/fhir/src/resources/coverageEligibilityResponse.ts delete mode 100644 packages/fhir/src/resources/detectedIssue.ts delete mode 100644 packages/fhir/src/resources/device.ts delete mode 100644 packages/fhir/src/resources/deviceDefinition.ts delete mode 100644 packages/fhir/src/resources/deviceMetric.ts delete mode 100644 packages/fhir/src/resources/deviceRequest.ts delete mode 100644 packages/fhir/src/resources/deviceUseStatement.ts delete mode 100644 packages/fhir/src/resources/diagnosticReport.ts delete mode 100644 packages/fhir/src/resources/documentManifest.ts delete mode 100644 packages/fhir/src/resources/documentReference.ts delete mode 100644 packages/fhir/src/resources/encounter.ts delete mode 100644 packages/fhir/src/resources/endpoint.ts delete mode 100644 packages/fhir/src/resources/enrollmentRequest.ts delete mode 100644 packages/fhir/src/resources/enrollmentResponse.ts delete mode 100644 packages/fhir/src/resources/episodeOfCare.ts delete mode 100644 packages/fhir/src/resources/eventDefinition.ts delete mode 100644 packages/fhir/src/resources/evidence.ts delete mode 100644 packages/fhir/src/resources/evidenceReport.ts delete mode 100644 packages/fhir/src/resources/evidenceVariable.ts delete mode 100644 packages/fhir/src/resources/exampleScenario.ts delete mode 100644 packages/fhir/src/resources/explanationOfBenefit.ts delete mode 100644 packages/fhir/src/resources/familyMemberHistory.ts delete mode 100644 packages/fhir/src/resources/fhirDomainResource.ts delete mode 100644 packages/fhir/src/resources/fhirResource.ts delete mode 100644 packages/fhir/src/resources/flag.ts delete mode 100644 packages/fhir/src/resources/goal.ts delete mode 100644 packages/fhir/src/resources/graphDefinition.ts delete mode 100644 packages/fhir/src/resources/group.ts delete mode 100644 packages/fhir/src/resources/guidanceResponse.ts delete mode 100644 packages/fhir/src/resources/healthcareService.ts delete mode 100644 packages/fhir/src/resources/imagingStudy.ts delete mode 100644 packages/fhir/src/resources/immunization.ts delete mode 100644 packages/fhir/src/resources/immunizationEvaluation.ts delete mode 100644 packages/fhir/src/resources/immunizationRecommendation.ts delete mode 100644 packages/fhir/src/resources/implementationGuide.ts delete mode 100644 packages/fhir/src/resources/ingredient.ts delete mode 100644 packages/fhir/src/resources/insurancePlan.ts delete mode 100644 packages/fhir/src/resources/invoice.ts delete mode 100644 packages/fhir/src/resources/library.ts delete mode 100644 packages/fhir/src/resources/linkage.ts delete mode 100644 packages/fhir/src/resources/list.ts delete mode 100644 packages/fhir/src/resources/location.ts delete mode 100644 packages/fhir/src/resources/manufacturedItemDefinition.ts delete mode 100644 packages/fhir/src/resources/measure.ts delete mode 100644 packages/fhir/src/resources/measureReport.ts delete mode 100644 packages/fhir/src/resources/media.ts delete mode 100644 packages/fhir/src/resources/medication.ts delete mode 100644 packages/fhir/src/resources/medicationAdministration.ts delete mode 100644 packages/fhir/src/resources/medicationDispense.ts delete mode 100644 packages/fhir/src/resources/medicationKnowledge.ts delete mode 100644 packages/fhir/src/resources/medicationRequest.ts delete mode 100644 packages/fhir/src/resources/medicationStatement.ts delete mode 100644 packages/fhir/src/resources/medicinalProductDefinition.ts delete mode 100644 packages/fhir/src/resources/messageDefinition.ts delete mode 100644 packages/fhir/src/resources/messageHeader.ts delete mode 100644 packages/fhir/src/resources/molecularSequence.ts delete mode 100644 packages/fhir/src/resources/namingSystem.ts delete mode 100644 packages/fhir/src/resources/nutritionOrder.ts delete mode 100644 packages/fhir/src/resources/nutritionProduct.ts delete mode 100644 packages/fhir/src/resources/observation.ts delete mode 100644 packages/fhir/src/resources/observationDefinition.ts delete mode 100644 packages/fhir/src/resources/operationDefinition.ts delete mode 100644 packages/fhir/src/resources/operationOutcome.ts delete mode 100644 packages/fhir/src/resources/organization.ts delete mode 100644 packages/fhir/src/resources/organizationAffiliation.ts delete mode 100644 packages/fhir/src/resources/packagedProductDefinition.ts delete mode 100644 packages/fhir/src/resources/parameters.ts delete mode 100644 packages/fhir/src/resources/patient.ts delete mode 100644 packages/fhir/src/resources/paymentNotice.ts delete mode 100644 packages/fhir/src/resources/paymentReconciliation.ts delete mode 100644 packages/fhir/src/resources/person.ts delete mode 100644 packages/fhir/src/resources/planDefinition.ts delete mode 100644 packages/fhir/src/resources/practitioner.ts delete mode 100644 packages/fhir/src/resources/practitionerRole.ts delete mode 100644 packages/fhir/src/resources/procedure.ts delete mode 100644 packages/fhir/src/resources/provenance.ts delete mode 100644 packages/fhir/src/resources/questionnaire.ts delete mode 100644 packages/fhir/src/resources/questionnaireResponse.ts delete mode 100644 packages/fhir/src/resources/regulatedAuthorization.ts delete mode 100644 packages/fhir/src/resources/relatedPerson.ts delete mode 100644 packages/fhir/src/resources/requestGroup.ts delete mode 100644 packages/fhir/src/resources/researchDefinition.ts delete mode 100644 packages/fhir/src/resources/researchElementDefinition.ts delete mode 100644 packages/fhir/src/resources/researchStudy.ts delete mode 100644 packages/fhir/src/resources/researchSubject.ts delete mode 100644 packages/fhir/src/resources/riskAssessment.ts delete mode 100644 packages/fhir/src/resources/schedule.ts delete mode 100644 packages/fhir/src/resources/searchParameter.ts delete mode 100644 packages/fhir/src/resources/serviceRequest.ts delete mode 100644 packages/fhir/src/resources/slot.ts delete mode 100644 packages/fhir/src/resources/specimen.ts delete mode 100644 packages/fhir/src/resources/specimenDefinition.ts delete mode 100644 packages/fhir/src/resources/structureDefinition.ts delete mode 100644 packages/fhir/src/resources/structureMap.ts delete mode 100644 packages/fhir/src/resources/subscription.ts delete mode 100644 packages/fhir/src/resources/subscriptionStatus.ts delete mode 100644 packages/fhir/src/resources/subscriptionTopic.ts delete mode 100644 packages/fhir/src/resources/substance.ts delete mode 100644 packages/fhir/src/resources/substanceDefinition.ts delete mode 100644 packages/fhir/src/resources/supplyDelivery.ts delete mode 100644 packages/fhir/src/resources/supplyRequest.ts delete mode 100644 packages/fhir/src/resources/task.ts delete mode 100644 packages/fhir/src/resources/terminologyCapabilities.ts delete mode 100644 packages/fhir/src/resources/testReport.ts delete mode 100644 packages/fhir/src/resources/testScript.ts delete mode 100644 packages/fhir/src/resources/valueSet.ts delete mode 100644 packages/fhir/src/resources/verificationResult.ts delete mode 100644 packages/fhir/src/resources/visionPrescription.ts delete mode 100644 packages/fhir/src/valueSets/account.ts delete mode 100644 packages/fhir/src/valueSets/actionParticipantType.ts delete mode 100644 packages/fhir/src/valueSets/administrableProductDefinition.ts delete mode 100644 packages/fhir/src/valueSets/administrativeGender.ts delete mode 100644 packages/fhir/src/valueSets/adverseEvent.ts delete mode 100644 packages/fhir/src/valueSets/allergyIntolerance.ts delete mode 100644 packages/fhir/src/valueSets/appointment.ts delete mode 100644 packages/fhir/src/valueSets/appointmentResponse.ts delete mode 100644 packages/fhir/src/valueSets/biologicallyDerivedProduct.ts delete mode 100644 packages/fhir/src/valueSets/bundle.ts delete mode 100644 packages/fhir/src/valueSets/capabilityStatement.ts delete mode 100644 packages/fhir/src/valueSets/carePlan.ts delete mode 100644 packages/fhir/src/valueSets/careTeam.ts delete mode 100644 packages/fhir/src/valueSets/catalogEntry.ts delete mode 100644 packages/fhir/src/valueSets/chargeItem.ts delete mode 100644 packages/fhir/src/valueSets/chargeItemDefinition.ts delete mode 100644 packages/fhir/src/valueSets/claim.ts delete mode 100644 packages/fhir/src/valueSets/clinicalImpression.ts delete mode 100644 packages/fhir/src/valueSets/clinicalUseDefinition.ts delete mode 100644 packages/fhir/src/valueSets/codeSystem.ts delete mode 100644 packages/fhir/src/valueSets/communication.ts delete mode 100644 packages/fhir/src/valueSets/communicationRequest.ts delete mode 100644 packages/fhir/src/valueSets/compartmentDefinition.ts delete mode 100644 packages/fhir/src/valueSets/composition.ts delete mode 100644 packages/fhir/src/valueSets/conceptMap.ts delete mode 100644 packages/fhir/src/valueSets/consent.ts delete mode 100644 packages/fhir/src/valueSets/contract.ts delete mode 100644 packages/fhir/src/valueSets/coverage.ts delete mode 100644 packages/fhir/src/valueSets/coverageEligibility.ts delete mode 100644 packages/fhir/src/valueSets/daysOfWeek.ts delete mode 100644 packages/fhir/src/valueSets/detectedIssue.ts delete mode 100644 packages/fhir/src/valueSets/device.ts delete mode 100644 packages/fhir/src/valueSets/deviceDefinition.ts delete mode 100644 packages/fhir/src/valueSets/deviceMetric.ts delete mode 100644 packages/fhir/src/valueSets/deviceRequest.ts delete mode 100644 packages/fhir/src/valueSets/deviceUseStatement.ts delete mode 100644 packages/fhir/src/valueSets/diagnosticReport.ts delete mode 100644 packages/fhir/src/valueSets/documentManifest.ts delete mode 100644 packages/fhir/src/valueSets/documentReference.ts delete mode 100644 packages/fhir/src/valueSets/encounter.ts delete mode 100644 packages/fhir/src/valueSets/endpoint.ts delete mode 100644 packages/fhir/src/valueSets/episodeOfCare.ts delete mode 100644 packages/fhir/src/valueSets/evidenceReport.ts delete mode 100644 packages/fhir/src/valueSets/evidenceVariable.ts delete mode 100644 packages/fhir/src/valueSets/exampleScenario.ts delete mode 100644 packages/fhir/src/valueSets/explanationOfBenefit.ts delete mode 100644 packages/fhir/src/valueSets/familyMemberHistory.ts delete mode 100644 packages/fhir/src/valueSets/fhir.ts delete mode 100644 packages/fhir/src/valueSets/financialResource.ts delete mode 100644 packages/fhir/src/valueSets/flag.ts delete mode 100644 packages/fhir/src/valueSets/goal.ts delete mode 100644 packages/fhir/src/valueSets/graphDefinition.ts delete mode 100644 packages/fhir/src/valueSets/group.ts delete mode 100644 packages/fhir/src/valueSets/guidanceResponse.ts delete mode 100644 packages/fhir/src/valueSets/identityAssuranceLevel.ts delete mode 100644 packages/fhir/src/valueSets/imagingStudy.ts delete mode 100644 packages/fhir/src/valueSets/immunization.ts delete mode 100644 packages/fhir/src/valueSets/immunizationEvaluation.ts delete mode 100644 packages/fhir/src/valueSets/implementationGuide.ts delete mode 100644 packages/fhir/src/valueSets/index.ts delete mode 100644 packages/fhir/src/valueSets/ingredient.ts delete mode 100644 packages/fhir/src/valueSets/invoice.ts delete mode 100644 packages/fhir/src/valueSets/linkage.ts delete mode 100644 packages/fhir/src/valueSets/list.ts delete mode 100644 packages/fhir/src/valueSets/location.ts delete mode 100644 packages/fhir/src/valueSets/manufacturedItemDefinition.ts delete mode 100644 packages/fhir/src/valueSets/measure.ts delete mode 100644 packages/fhir/src/valueSets/media.ts delete mode 100644 packages/fhir/src/valueSets/medication.ts delete mode 100644 packages/fhir/src/valueSets/medicationAdministration.ts delete mode 100644 packages/fhir/src/valueSets/medicationDispense.ts delete mode 100644 packages/fhir/src/valueSets/medicationRequest.ts delete mode 100644 packages/fhir/src/valueSets/medicationStatement.ts delete mode 100644 packages/fhir/src/valueSets/messageDefinition.ts delete mode 100644 packages/fhir/src/valueSets/messageHeader.ts delete mode 100644 packages/fhir/src/valueSets/molecularSequence.ts delete mode 100644 packages/fhir/src/valueSets/namingSystem.ts delete mode 100644 packages/fhir/src/valueSets/nutritionOrder.ts delete mode 100644 packages/fhir/src/valueSets/nutritionProduct.ts delete mode 100644 packages/fhir/src/valueSets/observation.ts delete mode 100644 packages/fhir/src/valueSets/observationDefinition.ts delete mode 100644 packages/fhir/src/valueSets/operationDefinition.ts delete mode 100644 packages/fhir/src/valueSets/operationOutcome.ts delete mode 100644 packages/fhir/src/valueSets/patient.ts delete mode 100644 packages/fhir/src/valueSets/planDefinition.ts delete mode 100644 packages/fhir/src/valueSets/procedure.ts delete mode 100644 packages/fhir/src/valueSets/provenance.ts delete mode 100644 packages/fhir/src/valueSets/publicationStatus.ts delete mode 100644 packages/fhir/src/valueSets/questionnaire.ts delete mode 100644 packages/fhir/src/valueSets/questionnaireResponse.ts delete mode 100644 packages/fhir/src/valueSets/referenceVersionRules.ts delete mode 100644 packages/fhir/src/valueSets/requestGroup.ts delete mode 100644 packages/fhir/src/valueSets/requestIntent.ts delete mode 100644 packages/fhir/src/valueSets/requestPriority.ts delete mode 100644 packages/fhir/src/valueSets/requestResourceType.ts delete mode 100644 packages/fhir/src/valueSets/researchElementDefinition.ts delete mode 100644 packages/fhir/src/valueSets/researchStudy.ts delete mode 100644 packages/fhir/src/valueSets/researchSubject.ts delete mode 100644 packages/fhir/src/valueSets/searchParameter.ts delete mode 100644 packages/fhir/src/valueSets/serviceRequest.ts delete mode 100644 packages/fhir/src/valueSets/slot.ts delete mode 100644 packages/fhir/src/valueSets/specimen.ts delete mode 100644 packages/fhir/src/valueSets/specimenDefinition.ts delete mode 100644 packages/fhir/src/valueSets/structureDefinition.ts delete mode 100644 packages/fhir/src/valueSets/structureMap.ts delete mode 100644 packages/fhir/src/valueSets/subscription.ts delete mode 100644 packages/fhir/src/valueSets/subscriptionStatus.ts delete mode 100644 packages/fhir/src/valueSets/subscriptionTopic.ts delete mode 100644 packages/fhir/src/valueSets/substance.ts delete mode 100644 packages/fhir/src/valueSets/supplyDelivery.ts delete mode 100644 packages/fhir/src/valueSets/supplyRequest.ts delete mode 100644 packages/fhir/src/valueSets/task.ts delete mode 100644 packages/fhir/src/valueSets/terminologyCapabilities.ts delete mode 100644 packages/fhir/src/valueSets/testReport.ts delete mode 100644 packages/fhir/src/valueSets/testScript.ts delete mode 100644 packages/fhir/src/valueSets/verificationResult.ts delete mode 100644 packages/fhir/src/valueSets/visionPrescription.ts rename packages/fhir/src/{valueSets/riskAssessment.ts => zod/r4/index.ts} (50%) create mode 100644 packages/fhir/src/zod/r4/schemas.ts create mode 100644 packages/fhir/src/zod/r4b/index.ts create mode 100644 packages/fhir/src/zod/r4b/schemas.ts create mode 100644 packages/fhir/src/zod/support.ts delete mode 100644 packages/fhir/test/coverage-tsconfig.json delete mode 100644 packages/fhir/test/coverage-tsconfig.json.license delete mode 100644 packages/fhir/test/elements/elements.test.ts create mode 100644 packages/fhir/test/exports.test.ts create mode 100644 packages/fhir/test/extract-coverage.test.ts create mode 100644 packages/fhir/test/extract-resources.test.ts create mode 100644 packages/fhir/test/extract.test.ts create mode 100644 packages/fhir/test/foundation-boundaries.test.ts create mode 100644 packages/fhir/test/foundation.test.ts create mode 100644 packages/fhir/test/mobile.test.ts create mode 100644 packages/fhir/test/providers.test.ts create mode 100644 packages/fhir/test/questionnaire.test.ts create mode 100644 packages/fhir/test/recording.test.ts delete mode 100644 packages/fhir/test/resources/accounts.json delete mode 100644 packages/fhir/test/resources/accounts.json.license delete mode 100644 packages/fhir/test/resources/accounts.test.ts delete mode 100644 packages/fhir/test/resources/activityDefinitions.json delete mode 100644 packages/fhir/test/resources/activityDefinitions.json.license delete mode 100644 packages/fhir/test/resources/activityDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/additionalResourceMethods.test.ts delete mode 100644 packages/fhir/test/resources/administrableProductDefinitions.json delete mode 100644 packages/fhir/test/resources/administrableProductDefinitions.json.license delete mode 100644 packages/fhir/test/resources/administrableProductDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/adverseEvents.json delete mode 100644 packages/fhir/test/resources/adverseEvents.json.license delete mode 100644 packages/fhir/test/resources/adverseEvents.test.ts delete mode 100644 packages/fhir/test/resources/allergyIntolerances.json delete mode 100644 packages/fhir/test/resources/allergyIntolerances.json.license delete mode 100644 packages/fhir/test/resources/allergyIntolerances.test.ts delete mode 100644 packages/fhir/test/resources/appointmentResponses.json delete mode 100644 packages/fhir/test/resources/appointmentResponses.json.license delete mode 100644 packages/fhir/test/resources/appointmentResponses.test.ts delete mode 100644 packages/fhir/test/resources/appointments.json delete mode 100644 packages/fhir/test/resources/appointments.json.license delete mode 100644 packages/fhir/test/resources/appointments.test.ts delete mode 100644 packages/fhir/test/resources/auditEvents.json delete mode 100644 packages/fhir/test/resources/auditEvents.json.license delete mode 100644 packages/fhir/test/resources/auditEvents.test.ts delete mode 100644 packages/fhir/test/resources/basics.json delete mode 100644 packages/fhir/test/resources/basics.json.license delete mode 100644 packages/fhir/test/resources/basics.test.ts delete mode 100644 packages/fhir/test/resources/binaries.json delete mode 100644 packages/fhir/test/resources/binaries.json.license delete mode 100644 packages/fhir/test/resources/binaries.test.ts delete mode 100644 packages/fhir/test/resources/binaryData.test.ts delete mode 100644 packages/fhir/test/resources/biologicallyDerivedProducts.json delete mode 100644 packages/fhir/test/resources/biologicallyDerivedProducts.json.license delete mode 100644 packages/fhir/test/resources/biologicallyDerivedProducts.test.ts delete mode 100644 packages/fhir/test/resources/bodyStructures.json delete mode 100644 packages/fhir/test/resources/bodyStructures.json.license delete mode 100644 packages/fhir/test/resources/bodyStructures.test.ts delete mode 100644 packages/fhir/test/resources/bundleFindResourcesWith.test.ts delete mode 100644 packages/fhir/test/resources/bundleWithNewResources.test.ts delete mode 100644 packages/fhir/test/resources/bundles.json delete mode 100644 packages/fhir/test/resources/bundles.json.license delete mode 100644 packages/fhir/test/resources/bundles.test.ts delete mode 100644 packages/fhir/test/resources/capabilityStatements.json delete mode 100644 packages/fhir/test/resources/capabilityStatements.json.license delete mode 100644 packages/fhir/test/resources/capabilityStatements.test.ts delete mode 100644 packages/fhir/test/resources/carePlans.json delete mode 100644 packages/fhir/test/resources/carePlans.json.license delete mode 100644 packages/fhir/test/resources/carePlans.test.ts delete mode 100644 packages/fhir/test/resources/careTeams.json delete mode 100644 packages/fhir/test/resources/careTeams.json.license delete mode 100644 packages/fhir/test/resources/careTeams.test.ts delete mode 100644 packages/fhir/test/resources/catalogEntries.json delete mode 100644 packages/fhir/test/resources/catalogEntries.json.license delete mode 100644 packages/fhir/test/resources/catalogEntries.test.ts delete mode 100644 packages/fhir/test/resources/chargeItemDefinitions.json delete mode 100644 packages/fhir/test/resources/chargeItemDefinitions.json.license delete mode 100644 packages/fhir/test/resources/chargeItemDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/chargeItems.json delete mode 100644 packages/fhir/test/resources/chargeItems.json.license delete mode 100644 packages/fhir/test/resources/chargeItems.test.ts delete mode 100644 packages/fhir/test/resources/citations.json delete mode 100644 packages/fhir/test/resources/citations.test.ts delete mode 100644 packages/fhir/test/resources/claimResponses.json delete mode 100644 packages/fhir/test/resources/claimResponses.json.license delete mode 100644 packages/fhir/test/resources/claimResponses.test.ts delete mode 100644 packages/fhir/test/resources/claims.json delete mode 100644 packages/fhir/test/resources/claims.json.license delete mode 100644 packages/fhir/test/resources/claims.test.ts delete mode 100644 packages/fhir/test/resources/clinicalImpressions.json delete mode 100644 packages/fhir/test/resources/clinicalImpressions.json.license delete mode 100644 packages/fhir/test/resources/clinicalImpressions.test.ts delete mode 100644 packages/fhir/test/resources/clinicalUseDefinitions.json delete mode 100644 packages/fhir/test/resources/clinicalUseDefinitions.json.license delete mode 100644 packages/fhir/test/resources/clinicalUseDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/codeSystems.json delete mode 100644 packages/fhir/test/resources/codeSystems.test.ts delete mode 100644 packages/fhir/test/resources/communicationRequests.json delete mode 100644 packages/fhir/test/resources/communicationRequests.json.license delete mode 100644 packages/fhir/test/resources/communicationRequests.test.ts delete mode 100644 packages/fhir/test/resources/communications.json delete mode 100644 packages/fhir/test/resources/communications.json.license delete mode 100644 packages/fhir/test/resources/communications.test.ts delete mode 100644 packages/fhir/test/resources/compartmentDefinitions.json delete mode 100644 packages/fhir/test/resources/compartmentDefinitions.json.license delete mode 100644 packages/fhir/test/resources/compartmentDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/compositions.json delete mode 100644 packages/fhir/test/resources/compositions.json.license delete mode 100644 packages/fhir/test/resources/compositions.test.ts delete mode 100644 packages/fhir/test/resources/conceptMaps.json delete mode 100644 packages/fhir/test/resources/conceptMaps.test.ts delete mode 100644 packages/fhir/test/resources/conditions.json delete mode 100644 packages/fhir/test/resources/conditions.json.license delete mode 100644 packages/fhir/test/resources/conditions.test.ts delete mode 100644 packages/fhir/test/resources/consents.json delete mode 100644 packages/fhir/test/resources/consents.json.license delete mode 100644 packages/fhir/test/resources/consents.test.ts delete mode 100644 packages/fhir/test/resources/contracts.json delete mode 100644 packages/fhir/test/resources/contracts.json.license delete mode 100644 packages/fhir/test/resources/contracts.test.ts delete mode 100644 packages/fhir/test/resources/coverageEligibilityRequests.json delete mode 100644 packages/fhir/test/resources/coverageEligibilityRequests.json.license delete mode 100644 packages/fhir/test/resources/coverageEligibilityRequests.test.ts delete mode 100644 packages/fhir/test/resources/coverageEligibilityResponses.json delete mode 100644 packages/fhir/test/resources/coverageEligibilityResponses.json.license delete mode 100644 packages/fhir/test/resources/coverageEligibilityResponses.test.ts delete mode 100644 packages/fhir/test/resources/coverages.json delete mode 100644 packages/fhir/test/resources/coverages.json.license delete mode 100644 packages/fhir/test/resources/coverages.test.ts delete mode 100644 packages/fhir/test/resources/detectedIssues.json delete mode 100644 packages/fhir/test/resources/detectedIssues.json.license delete mode 100644 packages/fhir/test/resources/detectedIssues.test.ts delete mode 100644 packages/fhir/test/resources/deviceDefinitions.json delete mode 100644 packages/fhir/test/resources/deviceDefinitions.json.license delete mode 100644 packages/fhir/test/resources/deviceDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/deviceMetrics.json delete mode 100644 packages/fhir/test/resources/deviceMetrics.json.license delete mode 100644 packages/fhir/test/resources/deviceMetrics.test.ts delete mode 100644 packages/fhir/test/resources/deviceRequests.json delete mode 100644 packages/fhir/test/resources/deviceRequests.json.license delete mode 100644 packages/fhir/test/resources/deviceRequests.test.ts delete mode 100644 packages/fhir/test/resources/deviceUseStatements.json delete mode 100644 packages/fhir/test/resources/deviceUseStatements.json.license delete mode 100644 packages/fhir/test/resources/deviceUseStatements.test.ts delete mode 100644 packages/fhir/test/resources/devices.json delete mode 100644 packages/fhir/test/resources/devices.json.license delete mode 100644 packages/fhir/test/resources/devices.test.ts delete mode 100644 packages/fhir/test/resources/diagnosticReports.json delete mode 100644 packages/fhir/test/resources/diagnosticReports.json.license delete mode 100644 packages/fhir/test/resources/diagnosticReports.test.ts delete mode 100644 packages/fhir/test/resources/documentManifests.json delete mode 100644 packages/fhir/test/resources/documentManifests.json.license delete mode 100644 packages/fhir/test/resources/documentManifests.test.ts delete mode 100644 packages/fhir/test/resources/documentReferences.json delete mode 100644 packages/fhir/test/resources/documentReferences.json.license delete mode 100644 packages/fhir/test/resources/documentReferences.test.ts delete mode 100644 packages/fhir/test/resources/domainResourceClass.test.ts delete mode 100644 packages/fhir/test/resources/encounters.json delete mode 100644 packages/fhir/test/resources/encounters.json.license delete mode 100644 packages/fhir/test/resources/encounters.test.ts delete mode 100644 packages/fhir/test/resources/endpoints.json delete mode 100644 packages/fhir/test/resources/endpoints.json.license delete mode 100644 packages/fhir/test/resources/endpoints.test.ts delete mode 100644 packages/fhir/test/resources/enrollmentRequests.json delete mode 100644 packages/fhir/test/resources/enrollmentRequests.json.license delete mode 100644 packages/fhir/test/resources/enrollmentRequests.test.ts delete mode 100644 packages/fhir/test/resources/enrollmentResponses.json delete mode 100644 packages/fhir/test/resources/enrollmentResponses.json.license delete mode 100644 packages/fhir/test/resources/enrollmentResponses.test.ts delete mode 100644 packages/fhir/test/resources/episodeOfCares.json delete mode 100644 packages/fhir/test/resources/episodeOfCares.json.license delete mode 100644 packages/fhir/test/resources/episodeOfCares.test.ts delete mode 100644 packages/fhir/test/resources/eventDefinitions.json delete mode 100644 packages/fhir/test/resources/eventDefinitions.json.license delete mode 100644 packages/fhir/test/resources/eventDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/evidenceReports.json delete mode 100644 packages/fhir/test/resources/evidenceReports.test.ts delete mode 100644 packages/fhir/test/resources/evidenceVariables.json delete mode 100644 packages/fhir/test/resources/evidenceVariables.test.ts delete mode 100644 packages/fhir/test/resources/evidences.json delete mode 100644 packages/fhir/test/resources/evidences.test.ts delete mode 100644 packages/fhir/test/resources/exampleScenarios.json delete mode 100644 packages/fhir/test/resources/exampleScenarios.json.license delete mode 100644 packages/fhir/test/resources/exampleScenarios.test.ts delete mode 100644 packages/fhir/test/resources/explanationOfBenefits.json delete mode 100644 packages/fhir/test/resources/explanationOfBenefits.json.license delete mode 100644 packages/fhir/test/resources/explanationOfBenefits.test.ts delete mode 100644 packages/fhir/test/resources/familyMemberHistories.json delete mode 100644 packages/fhir/test/resources/familyMemberHistories.json.license delete mode 100644 packages/fhir/test/resources/familyMemberHistories.test.ts delete mode 100644 packages/fhir/test/resources/flags.json delete mode 100644 packages/fhir/test/resources/flags.json.license delete mode 100644 packages/fhir/test/resources/flags.test.ts delete mode 100644 packages/fhir/test/resources/goals.json delete mode 100644 packages/fhir/test/resources/goals.json.license delete mode 100644 packages/fhir/test/resources/goals.test.ts delete mode 100644 packages/fhir/test/resources/graphDefinitions.json delete mode 100644 packages/fhir/test/resources/graphDefinitions.json.license delete mode 100644 packages/fhir/test/resources/graphDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/groups.json delete mode 100644 packages/fhir/test/resources/groups.json.license delete mode 100644 packages/fhir/test/resources/groups.test.ts delete mode 100644 packages/fhir/test/resources/guidanceResponses.json delete mode 100644 packages/fhir/test/resources/guidanceResponses.json.license delete mode 100644 packages/fhir/test/resources/guidanceResponses.test.ts delete mode 100644 packages/fhir/test/resources/healthcareServices.json delete mode 100644 packages/fhir/test/resources/healthcareServices.json.license delete mode 100644 packages/fhir/test/resources/healthcareServices.test.ts delete mode 100644 packages/fhir/test/resources/imagingStudies.json delete mode 100644 packages/fhir/test/resources/imagingStudies.json.license delete mode 100644 packages/fhir/test/resources/imagingStudies.test.ts delete mode 100644 packages/fhir/test/resources/immunizationEvaluations.json delete mode 100644 packages/fhir/test/resources/immunizationEvaluations.json.license delete mode 100644 packages/fhir/test/resources/immunizationEvaluations.test.ts delete mode 100644 packages/fhir/test/resources/immunizationRecommendations.json delete mode 100644 packages/fhir/test/resources/immunizationRecommendations.json.license delete mode 100644 packages/fhir/test/resources/immunizationRecommendations.test.ts delete mode 100644 packages/fhir/test/resources/immunizations.json delete mode 100644 packages/fhir/test/resources/immunizations.json.license delete mode 100644 packages/fhir/test/resources/immunizations.test.ts delete mode 100644 packages/fhir/test/resources/implementationGuides.json delete mode 100644 packages/fhir/test/resources/implementationGuides.json.license delete mode 100644 packages/fhir/test/resources/implementationGuides.test.ts delete mode 100644 packages/fhir/test/resources/ingredients.json delete mode 100644 packages/fhir/test/resources/ingredients.json.license delete mode 100644 packages/fhir/test/resources/ingredients.test.ts delete mode 100644 packages/fhir/test/resources/insurancePlans.json delete mode 100644 packages/fhir/test/resources/insurancePlans.json.license delete mode 100644 packages/fhir/test/resources/insurancePlans.test.ts delete mode 100644 packages/fhir/test/resources/invoices.json delete mode 100644 packages/fhir/test/resources/invoices.json.license delete mode 100644 packages/fhir/test/resources/invoices.test.ts delete mode 100644 packages/fhir/test/resources/libraries.json delete mode 100644 packages/fhir/test/resources/libraries.json.license delete mode 100644 packages/fhir/test/resources/libraries.test.ts delete mode 100644 packages/fhir/test/resources/linkages.json delete mode 100644 packages/fhir/test/resources/linkages.json.license delete mode 100644 packages/fhir/test/resources/linkages.test.ts delete mode 100644 packages/fhir/test/resources/lists.json delete mode 100644 packages/fhir/test/resources/lists.json.license delete mode 100644 packages/fhir/test/resources/lists.test.ts delete mode 100644 packages/fhir/test/resources/locations.json delete mode 100644 packages/fhir/test/resources/locations.json.license delete mode 100644 packages/fhir/test/resources/locations.test.ts delete mode 100644 packages/fhir/test/resources/manufacturedItemDefinitions.json delete mode 100644 packages/fhir/test/resources/manufacturedItemDefinitions.json.license delete mode 100644 packages/fhir/test/resources/manufacturedItemDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/measureReports.json delete mode 100644 packages/fhir/test/resources/measureReports.json.license delete mode 100644 packages/fhir/test/resources/measureReports.test.ts delete mode 100644 packages/fhir/test/resources/measures.json delete mode 100644 packages/fhir/test/resources/measures.json.license delete mode 100644 packages/fhir/test/resources/measures.test.ts delete mode 100644 packages/fhir/test/resources/media.json delete mode 100644 packages/fhir/test/resources/media.json.license delete mode 100644 packages/fhir/test/resources/media.test.ts delete mode 100644 packages/fhir/test/resources/medicationAdministrations.json delete mode 100644 packages/fhir/test/resources/medicationAdministrations.json.license delete mode 100644 packages/fhir/test/resources/medicationAdministrations.test.ts delete mode 100644 packages/fhir/test/resources/medicationDispenses.json delete mode 100644 packages/fhir/test/resources/medicationDispenses.json.license delete mode 100644 packages/fhir/test/resources/medicationDispenses.test.ts delete mode 100644 packages/fhir/test/resources/medicationKnowledges.json delete mode 100644 packages/fhir/test/resources/medicationKnowledges.json.license delete mode 100644 packages/fhir/test/resources/medicationKnowledges.test.ts delete mode 100644 packages/fhir/test/resources/medicationRequests.json delete mode 100644 packages/fhir/test/resources/medicationRequests.json.license delete mode 100644 packages/fhir/test/resources/medicationRequests.test.ts delete mode 100644 packages/fhir/test/resources/medicationStatements.json delete mode 100644 packages/fhir/test/resources/medicationStatements.json.license delete mode 100644 packages/fhir/test/resources/medicationStatements.test.ts delete mode 100644 packages/fhir/test/resources/medications.json delete mode 100644 packages/fhir/test/resources/medications.json.license delete mode 100644 packages/fhir/test/resources/medications.test.ts delete mode 100644 packages/fhir/test/resources/medicinalProductDefinitions.json delete mode 100644 packages/fhir/test/resources/medicinalProductDefinitions.json.license delete mode 100644 packages/fhir/test/resources/medicinalProductDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/messageDefinitions.json delete mode 100644 packages/fhir/test/resources/messageDefinitions.json.license delete mode 100644 packages/fhir/test/resources/messageDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/messageHeaders.json delete mode 100644 packages/fhir/test/resources/messageHeaders.json.license delete mode 100644 packages/fhir/test/resources/messageHeaders.test.ts delete mode 100644 packages/fhir/test/resources/molecularSequences.json delete mode 100644 packages/fhir/test/resources/molecularSequences.json.license delete mode 100644 packages/fhir/test/resources/molecularSequences.test.ts delete mode 100644 packages/fhir/test/resources/namingSystems.json delete mode 100644 packages/fhir/test/resources/namingSystems.test.ts delete mode 100644 packages/fhir/test/resources/nutritionOrders.json delete mode 100644 packages/fhir/test/resources/nutritionOrders.test.ts delete mode 100644 packages/fhir/test/resources/nutritionProducts.json delete mode 100644 packages/fhir/test/resources/nutritionProducts.test.ts delete mode 100644 packages/fhir/test/resources/observationDefinitions.json delete mode 100644 packages/fhir/test/resources/observationDefinitions.json.license delete mode 100644 packages/fhir/test/resources/observationDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/observationEffective.test.ts delete mode 100644 packages/fhir/test/resources/observations.json delete mode 100644 packages/fhir/test/resources/observations.json.license delete mode 100644 packages/fhir/test/resources/observations.test.ts delete mode 100644 packages/fhir/test/resources/operationDefinitions.json delete mode 100644 packages/fhir/test/resources/operationDefinitions.json.license delete mode 100644 packages/fhir/test/resources/operationDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/operationOutcomes.json delete mode 100644 packages/fhir/test/resources/operationOutcomes.json.license delete mode 100644 packages/fhir/test/resources/operationOutcomes.test.ts delete mode 100644 packages/fhir/test/resources/organizationAffiliations.json delete mode 100644 packages/fhir/test/resources/organizationAffiliations.json.license delete mode 100644 packages/fhir/test/resources/organizationAffiliations.test.ts delete mode 100644 packages/fhir/test/resources/organizations.json delete mode 100644 packages/fhir/test/resources/organizations.json.license delete mode 100644 packages/fhir/test/resources/organizations.test.ts delete mode 100644 packages/fhir/test/resources/packagedProductDefinitions.json delete mode 100644 packages/fhir/test/resources/packagedProductDefinitions.json.license delete mode 100644 packages/fhir/test/resources/packagedProductDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/parameters.json delete mode 100644 packages/fhir/test/resources/parameters.json.license delete mode 100644 packages/fhir/test/resources/parameters.test.ts delete mode 100644 packages/fhir/test/resources/patientMethods.test.ts delete mode 100644 packages/fhir/test/resources/patients.json delete mode 100644 packages/fhir/test/resources/patients.json.license delete mode 100644 packages/fhir/test/resources/patients.test.ts delete mode 100644 packages/fhir/test/resources/paymentNotices.json delete mode 100644 packages/fhir/test/resources/paymentNotices.json.license delete mode 100644 packages/fhir/test/resources/paymentNotices.test.ts delete mode 100644 packages/fhir/test/resources/paymentReconciliations.json delete mode 100644 packages/fhir/test/resources/paymentReconciliations.json.license delete mode 100644 packages/fhir/test/resources/paymentReconciliations.test.ts delete mode 100644 packages/fhir/test/resources/persons.json delete mode 100644 packages/fhir/test/resources/persons.json.license delete mode 100644 packages/fhir/test/resources/persons.test.ts delete mode 100644 packages/fhir/test/resources/planDefinitions.json delete mode 100644 packages/fhir/test/resources/planDefinitions.json.license delete mode 100644 packages/fhir/test/resources/planDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/practitionerRoles.json delete mode 100644 packages/fhir/test/resources/practitionerRoles.json.license delete mode 100644 packages/fhir/test/resources/practitionerRoles.test.ts delete mode 100644 packages/fhir/test/resources/practitioners.json delete mode 100644 packages/fhir/test/resources/practitioners.json.license delete mode 100644 packages/fhir/test/resources/practitioners.test.ts delete mode 100644 packages/fhir/test/resources/procedures.json delete mode 100644 packages/fhir/test/resources/procedures.json.license delete mode 100644 packages/fhir/test/resources/procedures.test.ts delete mode 100644 packages/fhir/test/resources/provenances.json delete mode 100644 packages/fhir/test/resources/provenances.json.license delete mode 100644 packages/fhir/test/resources/provenances.test.ts delete mode 100644 packages/fhir/test/resources/questionnaireResponses.json delete mode 100644 packages/fhir/test/resources/questionnaireResponses.json.license delete mode 100644 packages/fhir/test/resources/questionnaireResponses.test.ts delete mode 100644 packages/fhir/test/resources/questionnaires.json delete mode 100644 packages/fhir/test/resources/questionnaires.json.license delete mode 100644 packages/fhir/test/resources/questionnaires.test.ts delete mode 100644 packages/fhir/test/resources/regulatedAuthorizations.json delete mode 100644 packages/fhir/test/resources/regulatedAuthorizations.json.license delete mode 100644 packages/fhir/test/resources/regulatedAuthorizations.test.ts delete mode 100644 packages/fhir/test/resources/relatedPersons.json delete mode 100644 packages/fhir/test/resources/relatedPersons.json.license delete mode 100644 packages/fhir/test/resources/relatedPersons.test.ts delete mode 100644 packages/fhir/test/resources/requestGroups.json delete mode 100644 packages/fhir/test/resources/requestGroups.json.license delete mode 100644 packages/fhir/test/resources/requestGroups.test.ts delete mode 100644 packages/fhir/test/resources/researchDefinitions.json delete mode 100644 packages/fhir/test/resources/researchDefinitions.json.license delete mode 100644 packages/fhir/test/resources/researchDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/researchElementDefinitions.json delete mode 100644 packages/fhir/test/resources/researchElementDefinitions.json.license delete mode 100644 packages/fhir/test/resources/researchElementDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/researchStudies.json delete mode 100644 packages/fhir/test/resources/researchStudies.json.license delete mode 100644 packages/fhir/test/resources/researchStudies.test.ts delete mode 100644 packages/fhir/test/resources/researchSubjects.json delete mode 100644 packages/fhir/test/resources/researchSubjects.json.license delete mode 100644 packages/fhir/test/resources/researchSubjects.test.ts delete mode 100644 packages/fhir/test/resources/riskAssessments.json delete mode 100644 packages/fhir/test/resources/riskAssessments.json.license delete mode 100644 packages/fhir/test/resources/riskAssessments.test.ts delete mode 100644 packages/fhir/test/resources/schedules.json delete mode 100644 packages/fhir/test/resources/schedules.json.license delete mode 100644 packages/fhir/test/resources/schedules.test.ts delete mode 100644 packages/fhir/test/resources/searchParameters.json delete mode 100644 packages/fhir/test/resources/searchParameters.json.license delete mode 100644 packages/fhir/test/resources/searchParameters.test.ts delete mode 100644 packages/fhir/test/resources/serviceRequests.json delete mode 100644 packages/fhir/test/resources/serviceRequests.json.license delete mode 100644 packages/fhir/test/resources/serviceRequests.test.ts delete mode 100644 packages/fhir/test/resources/slots.json delete mode 100644 packages/fhir/test/resources/slots.json.license delete mode 100644 packages/fhir/test/resources/slots.test.ts delete mode 100644 packages/fhir/test/resources/specimenDefinitions.json delete mode 100644 packages/fhir/test/resources/specimenDefinitions.json.license delete mode 100644 packages/fhir/test/resources/specimenDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/specimens.json delete mode 100644 packages/fhir/test/resources/specimens.json.license delete mode 100644 packages/fhir/test/resources/specimens.test.ts delete mode 100644 packages/fhir/test/resources/structureDefinitions.json delete mode 100644 packages/fhir/test/resources/structureDefinitions.json.license delete mode 100644 packages/fhir/test/resources/structureDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/structureMaps.json delete mode 100644 packages/fhir/test/resources/structureMaps.json.license delete mode 100644 packages/fhir/test/resources/structureMaps.test.ts delete mode 100644 packages/fhir/test/resources/subscriptionStatuses.json delete mode 100644 packages/fhir/test/resources/subscriptionStatuses.json.license delete mode 100644 packages/fhir/test/resources/subscriptionStatuses.test.ts delete mode 100644 packages/fhir/test/resources/subscriptionTopics.json delete mode 100644 packages/fhir/test/resources/subscriptionTopics.json.license delete mode 100644 packages/fhir/test/resources/subscriptionTopics.test.ts delete mode 100644 packages/fhir/test/resources/subscriptions.json delete mode 100644 packages/fhir/test/resources/subscriptions.json.license delete mode 100644 packages/fhir/test/resources/subscriptions.test.ts delete mode 100644 packages/fhir/test/resources/substanceDefinitions.json delete mode 100644 packages/fhir/test/resources/substanceDefinitions.json.license delete mode 100644 packages/fhir/test/resources/substanceDefinitions.test.ts delete mode 100644 packages/fhir/test/resources/substances.json delete mode 100644 packages/fhir/test/resources/substances.json.license delete mode 100644 packages/fhir/test/resources/substances.test.ts delete mode 100644 packages/fhir/test/resources/supplyDeliveries.json delete mode 100644 packages/fhir/test/resources/supplyDeliveries.json.license delete mode 100644 packages/fhir/test/resources/supplyDeliveries.test.ts delete mode 100644 packages/fhir/test/resources/supplyRequests.json delete mode 100644 packages/fhir/test/resources/supplyRequests.json.license delete mode 100644 packages/fhir/test/resources/supplyRequests.test.ts delete mode 100644 packages/fhir/test/resources/tasks.json delete mode 100644 packages/fhir/test/resources/tasks.json.license delete mode 100644 packages/fhir/test/resources/tasks.test.ts delete mode 100644 packages/fhir/test/resources/terminologyCapabilities.json delete mode 100644 packages/fhir/test/resources/terminologyCapabilities.test.ts delete mode 100644 packages/fhir/test/resources/testHelpers.ts delete mode 100644 packages/fhir/test/resources/testReports.json delete mode 100644 packages/fhir/test/resources/testReports.json.license delete mode 100644 packages/fhir/test/resources/testReports.test.ts delete mode 100644 packages/fhir/test/resources/testScripts.json delete mode 100644 packages/fhir/test/resources/testScripts.json.license delete mode 100644 packages/fhir/test/resources/testScripts.test.ts delete mode 100644 packages/fhir/test/resources/valueSets.json delete mode 100644 packages/fhir/test/resources/valueSets.test.ts delete mode 100644 packages/fhir/test/resources/verificationResults.json delete mode 100644 packages/fhir/test/resources/verificationResults.json.license delete mode 100644 packages/fhir/test/resources/verificationResults.test.ts delete mode 100644 packages/fhir/test/resources/visionPrescriptions.json delete mode 100644 packages/fhir/test/resources/visionPrescriptions.json.license delete mode 100644 packages/fhir/test/resources/visionPrescriptions.test.ts delete mode 100644 packages/fhir/test/valueSets/valueSets.test.ts create mode 100644 packages/fhir/test/zod-fixtures.test.ts create mode 100644 packages/fhir/test/zod-generation-invariants.test.ts create mode 100644 packages/fhir/test/zod-official-examples.test.ts create mode 100644 packages/fhir/test/zod-schemas.test.ts diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index d82a94dc..9fdb2ab3 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -21,6 +21,7 @@ on: branches: - main pull_request: + types: [opened, reopened, synchronize, ready_for_review, converted_to_draft] workflow_dispatch: workflow_call: @@ -34,14 +35,80 @@ jobs: markdown-links: name: Markdown Link Check uses: SchmiedmayerLab/.github/.github/workflows/markdown-links.yml@v0.5 + fhir-draft: + name: Draft FHIR Stack — Scoped Quality and Structural Conformance + if: >- + github.event_name == 'pull_request' && + github.event.pull_request.draft && + startsWith(github.head_ref, 'feature/fhir-') + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout Grove TypeScript + uses: actions/checkout@v7 + with: + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + # The pin lives with the package that reads it, so CI and a local run agree. + - name: Read the pinned Grove FHIR commit + id: grove-fhir + run: >- + echo "sha=$(node -p "const p=require('./packages/fhir/grove-fhir.json');p.ref??p.sha")" + >> "$GITHUB_OUTPUT" + + - name: Checkout the pinned Grove FHIR commit + uses: actions/checkout@v7 + with: + repository: SchmiedmayerLab/grove-fhir + ref: ${{ steps.grove-fhir.outputs.sha }} + path: .build/grove-fhir + persist-credentials: false + - name: Set up Node.js 24 + uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm + - name: Set up npm + run: npm install --global --ignore-scripts npm@12.0.2 + - name: Install dependencies + run: npm ci + - name: Build shared lint configuration + run: npm run build --workspace @schmiedmayerlab/grove-configurations + - name: Check scoped formatting + run: >- + npm exec prettier -- --check + packages/fhir + .github/workflows/build-and-test.yml + .github/workflows/fhir-conformance.yml + - name: Check generated contracts and strict types + run: | + npm run check:catalog --workspace @schmiedmayerlab/grove-fhir + npm run typecheck --workspace @schmiedmayerlab/grove-fhir + - name: Lint and test the FHIR package + run: | + npm run lint:ci --workspace @schmiedmayerlab/grove-fhir + npm test --workspace @schmiedmayerlab/grove-fhir + - name: Check browser and deterministic fixtures + run: | + npm run test:browser --workspace @schmiedmayerlab/grove-fhir + npm run check:fixtures --workspace @schmiedmayerlab/grove-fhir + - name: Run producer-neutral structural conformance + run: >- + npm run conformance:structural --workspace @schmiedmayerlab/grove-fhir -- + --ig "$GITHUB_WORKSPACE/.build/grove-fhir" quality: name: Build and Quality Checks + if: >- + github.event_name != 'pull_request' || + github.event.pull_request.draft == false || + !startsWith(github.head_ref, 'feature/fhir-') runs-on: ubuntu-latest timeout-minutes: 20 steps: - name: Checkout repository uses: actions/checkout@v7 with: + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} persist-credentials: false - name: Set up Node.js uses: actions/setup-node@v7 @@ -72,12 +139,17 @@ jobs: run: npm run audit:signatures node-current: name: Node.js 26 Compatibility + if: >- + github.event_name != 'pull_request' || + github.event.pull_request.draft == false || + !startsWith(github.head_ref, 'feature/fhir-') runs-on: ubuntu-latest timeout-minutes: 20 steps: - name: Checkout repository uses: actions/checkout@v7 with: + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} persist-credentials: false - name: Set up Node.js uses: actions/setup-node@v7 @@ -96,12 +168,17 @@ jobs: run: npm test documentation: name: Build Pages + if: >- + github.event_name != 'pull_request' || + github.event.pull_request.draft == false || + !startsWith(github.head_ref, 'feature/fhir-') runs-on: ubuntu-latest timeout-minutes: 30 steps: - name: Checkout repository uses: actions/checkout@v7 with: + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} persist-credentials: false - name: Set up Node.js uses: actions/setup-node@v7 @@ -127,6 +204,10 @@ jobs: run: npm run pages:build tests: name: Test and Coverage + if: >- + github.event_name != 'pull_request' || + github.event.pull_request.draft == false || + !startsWith(github.head_ref, 'feature/fhir-') uses: SchmiedmayerLab/.github/.github/workflows/npm-test-coverage.yml@v0.5 with: coverage-files: >- @@ -137,6 +218,39 @@ jobs: npmVersion: '12.0.2' secrets: token: ${{ secrets.CODECOV_TOKEN }} + tests-exact-head: + name: Exact PR Head — Test and Coverage + if: >- + github.event_name != 'pull_request' || + github.event.pull_request.draft == false || + !startsWith(github.head_ref, 'feature/fhir-') + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout exact source revision + uses: actions/checkout@v7 + with: + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + - name: Verify the checked-out PR head + if: github.event_name == 'pull_request' + env: + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD_SHA" + - name: Set up Node.js 24 + uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm + - name: Set up npm + run: npm install --global --ignore-scripts npm@12.0.2 + - name: Install dependencies + run: npm ci + - name: Run every package coverage gate on the exact head + run: | + npm run build + npm run test:release + npm run test:coverage dependency-review: name: Dependency Review if: github.event_name == 'pull_request' @@ -150,24 +264,85 @@ jobs: with: fail-on-severity: moderate fail-on-scopes: runtime, development + draft-ci: + name: Draft FHIR Stack — Scoped Checks (Not Release Readiness) + if: >- + always() && + github.event_name == 'pull_request' && + github.event.pull_request.draft && + startsWith(github.head_ref, 'feature/fhir-') + needs: + - actionlint + - dependency-review + - fhir-draft + - markdown-links + - reuse + runs-on: ubuntu-latest + steps: + - name: Check scoped draft jobs + env: + ACTIONLINT_RESULT: ${{ needs.actionlint.result }} + DEPENDENCY_REVIEW_RESULT: ${{ needs.dependency-review.result }} + FHIR_DRAFT_RESULT: ${{ needs.fhir-draft.result }} + MARKDOWN_LINKS_RESULT: ${{ needs.markdown-links.result }} + REUSE_RESULT: ${{ needs.reuse.result }} + run: | + for result in \ + "$ACTIONLINT_RESULT" \ + "$DEPENDENCY_REVIEW_RESULT" \ + "$FHIR_DRAFT_RESULT" \ + "$MARKDOWN_LINKS_RESULT" \ + "$REUSE_RESULT"; do + if [[ "$result" != success ]]; then + exit 1 + fi + done ci: - name: CI + name: CI — Full Release Readiness if: always() needs: - actionlint - dependency-review - documentation + - fhir-draft - markdown-links - node-current - quality - reuse - tests + - tests-exact-head runs-on: ubuntu-latest steps: - name: Check required jobs env: - RESULTS: ${{ join(needs.*.result, ',') }} + ACTIONLINT_RESULT: ${{ needs.actionlint.result }} + DEPENDENCY_REVIEW_RESULT: ${{ needs.dependency-review.result }} + DOCUMENTATION_RESULT: ${{ needs.documentation.result }} + IS_FHIR_DRAFT: ${{ github.event_name == 'pull_request' && github.event.pull_request.draft && startsWith(github.head_ref, 'feature/fhir-') }} + MARKDOWN_LINKS_RESULT: ${{ needs.markdown-links.result }} + NODE_CURRENT_RESULT: ${{ needs.node-current.result }} + QUALITY_RESULT: ${{ needs.quality.result }} + REUSE_RESULT: ${{ needs.reuse.result }} + TESTS_RESULT: ${{ needs.tests.result }} + TESTS_EXACT_HEAD_RESULT: ${{ needs.tests-exact-head.result }} run: | - if [[ "$RESULTS" == *failure* || "$RESULTS" == *cancelled* ]]; then + if [[ "$IS_FHIR_DRAFT" == true ]]; then + echo "Draft-scoped checks do not establish full release readiness." >&2 + exit 1 + fi + for result in \ + "$ACTIONLINT_RESULT" \ + "$DOCUMENTATION_RESULT" \ + "$MARKDOWN_LINKS_RESULT" \ + "$NODE_CURRENT_RESULT" \ + "$QUALITY_RESULT" \ + "$REUSE_RESULT" \ + "$TESTS_RESULT" \ + "$TESTS_EXACT_HEAD_RESULT"; do + if [[ "$result" != success ]]; then + exit 1 + fi + done + if [[ "${{ github.event_name }}" == pull_request && "$DEPENDENCY_REVIEW_RESULT" != success ]]; then exit 1 fi diff --git a/.github/workflows/fhir-conformance.yml b/.github/workflows/fhir-conformance.yml new file mode 100644 index 00000000..c62da95b --- /dev/null +++ b/.github/workflows/fhir-conformance.yml @@ -0,0 +1,173 @@ +# +# This source file is part of the Grove open-source project +# +# SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +# +# SPDX-License-Identifier: MIT +# + +name: FHIR R4 Targeted Conformance + +on: + pull_request: + types: [opened, reopened, synchronize, ready_for_review, converted_to_draft] + paths: + - .github/workflows/build-and-test.yml + - .github/workflows/fhir-conformance.yml + - .nvmrc + - package-lock.json + - package.json + - packages/configurations/** + - packages/fhir/** + push: + branches: [main] + paths: + - .github/workflows/fhir-conformance.yml + - package-lock.json + - package.json + - packages/configurations/** + - packages/fhir/** + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + fhir-r4: + name: ${{ github.event_name == 'pull_request' && github.event.pull_request.draft && 'Draft Scope — Public API and Structural Conformance' || 'Readiness — Public API, Browser, and Official Validator' }} + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Checkout Grove TypeScript + uses: actions/checkout@v7 + with: + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + + - name: Verify the checked-out PR head + if: github.event_name == 'pull_request' + env: + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD_SHA" + + # The pin lives with the package that reads it, so CI and a local run agree. + - name: Read the pinned Grove FHIR commit + id: grove-fhir + run: >- + echo "sha=$(node -p "const p=require('./packages/fhir/grove-fhir.json');p.ref??p.sha")" + >> "$GITHUB_OUTPUT" + + - name: Checkout the pinned Grove FHIR commit + uses: actions/checkout@v7 + with: + repository: SchmiedmayerLab/grove-fhir + ref: ${{ steps.grove-fhir.outputs.sha }} + path: .build/grove-fhir + persist-credentials: false + + - name: Set up Node.js 24 + uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm + cache-dependency-path: | + package-lock.json + .build/grove-fhir/package-lock.json + + - name: Set up npm 12 + run: npm install --global --ignore-scripts npm@12.0.2 + + - name: Set up Java + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + uses: actions/setup-java@v6 + with: + distribution: temurin + java-version: '21.0.10+7.0.LTS' + + - name: Set up Ruby + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3.12' + working-directory: .build/grove-fhir + + - name: Install the IG-locked Bundler + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + run: gem install bundler -v 4.0.16 --no-document + + - name: Cache checksum-pinned FHIR tools and packages + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + uses: actions/cache@v6 + with: + path: | + .build/grove-fhir/.build/bundle + .build/grove-fhir/.build/fhir-tools + .build/grove-fhir/.build/fhir-home/.fhir/packages + key: fhir-r4-${{ runner.os }}-${{ hashFiles('.build/grove-fhir/Gemfile.lock', '.build/grove-fhir/Scripts/download-fhir-tools.sh') }} + + - name: Install TypeScript dependencies + run: npm ci + + - name: Install IG dependencies + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + working-directory: .build/grove-fhir + run: npm ci + + - name: Build shared lint configuration + run: npm run build --workspace @schmiedmayerlab/grove-configurations + + - name: Check targeted formatting + run: npm exec prettier -- --check packages/fhir .github/workflows/fhir-conformance.yml .github/workflows/build-and-test.yml + + - name: Check synchronized catalogs and strict types + run: | + npm run check:catalog --workspace @schmiedmayerlab/grove-fhir + npm run typecheck --workspace @schmiedmayerlab/grove-fhir + + - name: Lint and test the FHIR package + run: | + npm run lint:ci --workspace @schmiedmayerlab/grove-fhir + npm test --workspace @schmiedmayerlab/grove-fhir + + - name: Exercise the public ESM bundle in a browser + run: npm run test:browser --workspace @schmiedmayerlab/grove-fhir + + - name: Check deterministic public-API fixtures + run: npm run check:fixtures --workspace @schmiedmayerlab/grove-fhir + + - name: Run fast structural conformance + run: >- + npm run conformance:structural --workspace @schmiedmayerlab/grove-fhir -- + --ig "$GITHUB_WORKSPACE/.build/grove-fhir" + + - name: Build only the required IG packages + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + working-directory: .build/grove-fhir + run: ./Scripts/build-guides.sh mobile sensor questionnaire providers + + - name: Run official FHIR R4 Validator conformance + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + run: >- + npm run conformance --workspace @schmiedmayerlab/grove-fhir -- + --ig "$GITHUB_WORKSPACE/.build/grove-fhir" + + fhir-readiness: + name: FHIR R4 — Full Release Readiness + if: always() + needs: fhir-r4 + runs-on: ubuntu-latest + steps: + - name: Require the full exact-head Validator lane + env: + FHIR_RESULT: ${{ needs.fhir-r4.result }} + IS_DRAFT: ${{ github.event_name == 'pull_request' && github.event.pull_request.draft }} + run: | + if [[ "$IS_DRAFT" == true ]]; then + echo "Draft structural checks do not establish FHIR release readiness." >&2 + exit 1 + fi + test "$FHIR_RESULT" = success diff --git a/.gitignore b/.gitignore index eac3365c..05bca87d 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,9 @@ eslint_report.json # OS Thumbs.db + +# Grove FHIR catalogs, fetched at the pinned commit rather than vendored. +.grove-fhir/ + +# FHIR core packages the Zod generator downloads; a build cache, not source. +packages/fhir/.fhir-cache/ diff --git a/.prettierignore b/.prettierignore index d5242846..d8de8ef8 100644 --- a/.prettierignore +++ b/.prettierignore @@ -15,5 +15,10 @@ **/*.tsbuildinfo **/.docusaurus packages/design-system/docs/docs/api +packages/fhir/catalog/grove-fhir/*.json +!packages/fhir/catalog/grove-fhir/source-ref.json +packages/fhir/catalog/grove-fhir-conformance/*.json .parcel-cache node_modules + +packages/fhir/.fhir-cache/ diff --git a/README.md b/README.md index 54386ea7..4371e663 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Explore the [live Storybook](https://schmiedmayerlab.github.io/grove-ts/storyboo | [`@schmiedmayerlab/grove-configurations`](./packages/configurations) | Shared ESLint and Prettier configuration for Node and React | | [`@schmiedmayerlab/grove-design-system`](./packages/design-system) | React components, layouts, forms, themes, and utilities | | [`@schmiedmayerlab/grove-firebase-cloud-messaging`](./packages/firebase-cloud-messaging) | Cloud Messaging, device registration, and notification flows | -| [`@schmiedmayerlab/grove-fhir`](./packages/fhir) | FHIR R4B schemas and validation | +| [`@schmiedmayerlab/grove-fhir`](./packages/fhir) | Strict FHIR R4 contracts and Grove resource builders | | [`@schmiedmayerlab/grove-firebase-utils`](./packages/firebase-utils) | Shared Firebase converters and application utilities | Each package README contains focused setup and usage guidance. diff --git a/package-lock.json b/package-lock.json index 0820925d..e328882a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4117,6 +4117,18 @@ "react": ">=18 || >=19.0.0-rc.0" } }, + "node_modules/@noble/hashes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.3.0.tgz", + "integrity": "sha512-oN+QwyX7VSHotibwubG3kpzbwKrfnyR6OOO+3Nk/53ADL7FmgHHz4TgrbaYKvvOw09u6QTx0oiH1cNCIOuN0CQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodable/entities": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.0.0.tgz", @@ -11894,6 +11906,46 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-check": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.9.0.tgz", + "integrity": "sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^8.0.0" + }, + "engines": { + "node": ">=12.17.0" + } + }, + "node_modules/fast-check/node_modules/pure-rand": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.2.tgz", + "integrity": "sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -19481,6 +19533,19 @@ "license": "MIT", "optional": true }, + "node_modules/uuid": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz", + "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -20777,7 +20842,9 @@ "version": "0.0.0", "license": "MIT", "dependencies": { + "@noble/hashes": "2.3.0", "@types/fhir": "^0.0.44", + "uuid": "^14.0.1", "zod": "^4.4.3" }, "devDependencies": { @@ -20787,7 +20854,9 @@ "eslint": "^10.8.0", "eslint-import-resolver-typescript": "^4.4.5", "expect-type": "^1.3.0", + "fast-check": "^4.9.0", "jest": "^30.4.2", + "playwright": "^1.62.1", "prettier": "^3.6.2", "rimraf": "^6.1.2", "ts-jest": "^29.4.12", diff --git a/packages/design-system/docs/docs/docs/technology-stack.md b/packages/design-system/docs/docs/docs/technology-stack.md index 9001464a..94b17251 100644 --- a/packages/design-system/docs/docs/docs/technology-stack.md +++ b/packages/design-system/docs/docs/docs/technology-stack.md @@ -22,7 +22,7 @@ Our choices prioritize simplicity, performance, accessibility, and developer exp - **[Radix UI](https://www.radix-ui.com/)** - Unstyled, accessible component primitives providing robust interaction patterns and WCAG compliance - **[React Hook Form](https://react-hook-form.com/)** - Performant form handling with validation - **[Zod](https://zod.dev/)** - TypeScript-first schema validation -- **[TanStack Table](https://tanstack.com/table)** - Headless table utilities for DataTable component +- **[TanStack Table](https://tanstack.com/table/latest)** - Headless table utilities for DataTable component - **[Firebase](https://firebase.google.com/)** - Authentication utilities and patterns - **[date-fns](https://date-fns.org/)** - Date manipulation and formatting - **[ES-Toolkit](https://es-toolkit.slash.page/)** - Modern utility library for efficient JavaScript operations @@ -89,7 +89,7 @@ At the same time, thanks to the modular structure, you can swap out components w TanStack Table is a headless table library for React. It's a powerful and flexible solution for building data-driven tables. We use it to build our DataTable component. It enables complex features like sorting, filtering, pagination, and more. -[More about TanStack Table](https://tanstack.com/table) +[More about TanStack Table](https://tanstack.com/table/latest) ### Zod diff --git a/packages/fhir/README.md b/packages/fhir/README.md index f8109c16..2dc09e1a 100644 --- a/packages/fhir/README.md +++ b/packages/fhir/README.md @@ -8,226 +8,541 @@ SPDX-License-Identifier: MIT --> -# Grove FHIR +# Grove FHIR for TypeScript -[![Build and Test](https://github.com/SchmiedmayerLab/grove-ts/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/SchmiedmayerLab/grove-ts/actions/workflows/build-and-test.yml) -[![codecov](https://codecov.io/gh/SchmiedmayerLab/grove-ts/graph/badge.svg)](https://codecov.io/gh/SchmiedmayerLab/grove-ts) +`@schmiedmayerlab/grove-fhir` provides strict FHIR R4 contracts and Grove resource builders for TypeScript applications. -Type-safe FHIR R4B resource schemas and utilities for TypeScript applications. This package provides comprehensive [Zod](https://zod.dev) schemas for FHIR (Fast Healthcare Interoperability Resources) data validation, making it easy to work with healthcare data in TypeScript applications, including those built on Firebase Functions and Firestore. +The package targets FHIR R4 4.0.1 only. +It has no Firebase dependency and does not fetch health data, authenticate with providers, persist resources, or define transport behavior. -This package is part of the [Grove](https://github.com/SchmiedmayerLab/grove-ts) project, bringing standardized healthcare data exchange to TypeScript applications. +The contract version exposed by generated metadata is the Grove FHIR implementation-guide version. +The monorepo keeps its `0.0.0` source placeholders until the lockstep release workflow injects the selected npm release version. -## Why Use This Package? +## New to FHIR? -Working with FHIR resources in TypeScript can be challenging due to their complex, nested structures and strict validation requirements. This package solves that by providing: +FHIR is a standard for exchanging health data as JSON documents called resources. +This package builds four of them: an `Observation` for each measurement, a `Device` for the thing that recorded it, a `Provenance` saying where the record came from, and a `Bundle` holding the set together. -- **Type Safety**: Leverage TypeScript's type system with automatically generated types from Zod schemas -- **Runtime Validation**: Validate FHIR resources at runtime to catch data issues early -- **Framework Agnostic**: Plain TypeScript/Zod schemas that work anywhere, including Firebase Functions and Firestore -- **Standards Compliance**: Schemas based on FHIR R4B specification -- **Developer Experience**: Intuitive API with helpful utility methods +If those words are unfamiliar, read the _New to FHIR_ page of the Grove Mobile implementation guide +(`https://grovealliance.org/fhir/mobile`) first. +It explains the resources this package emits, the difference between a server-assigned `id` and a business `identifier`, and why a measurement carries two devices — in about ten minutes, without assuming any FHIR background. -## Installation +The guide is the single place those concepts are explained; this README covers only what is specific to this package. -```bash -npm install @schmiedmayerlab/grove-fhir -``` - -## Features - -- **Comprehensive FHIR Resources**: Schemas for 40+ FHIR resources including Patient, Observation, Medication, Appointment, and more -- **FHIR Elements**: Support for all FHIR data types (CodeableConcept, Quantity, Reference, etc.) -- **Value Sets**: Pre-defined schemas for FHIR value sets and enumerations -- **Helper Methods**: Convenient utilities for working with coded concepts and extensions -- **Full Type Inference**: Get complete TypeScript autocompletion and type checking +## Your first bundle -### Supported FHIR Resources +One measurement, turned into a complete Grove exchange graph: -Patient, Practitioner, Observation, Medication, MedicationRequest, Appointment, AllergyIntolerance, Condition, Procedure, DiagnosticReport, Immunization, Questionnaire, QuestionnaireResponse, and many more. See the [full list](./src/index.ts). - -## Quick Start +```typescript +import { + parseAbsoluteUri, + parseFhirInstant, + parsePatientReference, + parsePositiveInteger, + type Result, +} from '@schmiedmayerlab/grove-fhir' +import { buildProviderMeasurementBundle } from '@schmiedmayerlab/grove-fhir/providers' -### Validating FHIR Resources +// Every parse returns a Result; a real application reports the issues instead of throwing. +const unwrap = (result: Result): Value => { + if (!result.ok) throw new Error(result.issues[0]?.message) + return result.value +} -```typescript -import { FhirPatient } from '@schmiedmayerlab/grove-fhir' +const measuredAt = unwrap(parseFhirInstant('2026-08-19T17:30:00Z')) -// Parse and validate a patient resource -const rawData = { - resourceType: 'Patient', - id: 'patient-123', - name: [ +const result = buildProviderMeasurementBundle({ + subject: unwrap(parsePatientReference('Patient/participant-01')), + measurements: [ { - use: 'official', - family: 'Smith', - given: ['John', 'Michael'], + kind: 'body-weight', + value: 72.5, + effective: { kind: 'date-time', value: measuredAt }, }, ], - gender: 'male', - birthDate: '1980-01-15', + source: { + adapter: { kind: 'providers', provider: 'google-health-api' }, + providerAccountIdentifier: { + system: unwrap( + parseAbsoluteUri( + 'https://mystudy.example.org/fhir/identifiers/provider-accounts', + ), + ), + value: 'pseudonym-participant-01', + assurance: 'deployment-scoped-pseudonym', + }, + sourceType: 'weight', + sourceNativeId: 'ghapi-weight-20260819', + recordingMethod: 'automatically-recorded', + dataOrigin: { + identity: { + identifier: { + system: unwrap( + parseAbsoluteUri('https://mystudy.example.org/data-origins'), + ), + value: 'google-health-api', + }, + }, + name: 'google-health-api', + }, + }, + application: { + identity: { + identifier: { + system: unwrap( + parseAbsoluteUri('https://mystudy.example.org/applications'), + ), + value: 'mystudy', + }, + }, + name: 'MyStudy', + version: '2.1.0', + }, + eventSequence: unwrap(parsePositiveInteger(1)), + issued: unwrap(parseFhirInstant('2026-08-20T12:01:00Z')), + recorded: unwrap(parseFhirInstant('2026-08-20T12:02:00Z')), +}) + +if (!result.ok) { + for (const issue of result.issues) { + console.error(issue.path.join('.'), issue.code, issue.message) + } } +``` + +A success gives you a frozen `collection` Bundle holding the Observation, the two Devices, and the Provenance. + +Four conventions are visible in that call and hold throughout the package: + +- **Nothing throws for bad input.** + Every parser and builder returns a `Result`: either `{ ok: true, value }` or `{ ok: false, issues }`, where each issue carries a stable `code` and a `path` into your input. + Reserve `try`/`catch` for genuine programming errors. +- **Validated strings are branded types.** + An `AbsoluteUri` or a `FhirInstant` can only be produced by its parser, so a malformed timestamp is caught where it enters rather than at the receiving server. + This is why the example parses its literals instead of passing raw strings. +- **You supply identity; the package derives structure.** + The participant reference, the account pseudonym, the source's native id, and the timestamps are yours. + Resource identifiers and the Bundle's internal `urn:uuid` links are derived from them, so the same input always produces the same graph and a re-send deduplicates rather than landing twice. +- **Results are frozen plain JSON.** + Serialize them, store them, send them; there are no classes and no hidden state. + +## Requirements + +- Node.js 24 or newer +- npm 12 for repository development +- TypeScript 6 for repository development + +The published JavaScript uses portable ES2022 and Web Platform APIs. + +## Design + +- FHIR resources are immutable plain JSON values. +- Runtime schemas are strict and reject unknown properties instead of silently removing them. +- The supported R4 closure is intentionally bounded to the resources Grove constructs and validates. +- Expected input failures use a discriminated `Result` with stable issue codes and paths; successful results may carry non-blocking warnings. +- Validated strings use branded types for instants, canonical URLs, FHIR ids, Patient references, and `urn:uuid` full URLs. +- Provider-account pseudonym, source token/native id, durable event sequence, and all times are caller-owned identity inputs. +- Connected source/output/conversion/exchange identifiers and every Bundle full URL are derived internally from the frozen JCS/SHA-256 and UUID-v5 contracts. +- `Resource.id` is omitted unless the caller supplies a repository-assigned id. + +## Provider measurements + +The Grove Mobile catalog defines 84 measurements whose semantics are shared by +at least two evidenced sources; `sharedMobileMeasurementCatalog` exposes them +with their value kind, coded-result vocabulary, method, and member contracts. +Owner-exclusive measurements (HealthKit, Health Connect, and +connected-provider platform exclusives) are deliberately absent from the +shared catalog and are published separately as `adapterMeasurementCatalog`, +keyed by owning adapter, from the `providers` entry point. +This Provider facade exposes a closed provider/source-token/measurement union +for the shared scalar and coded measurements admitted by the frozen Google +Health API, Oura, and Withings inventories. It includes point and period +quantities, closed coded results such as workout classification, and composite +blood pressure. Shared profiles without a semantically exact provider source +remain profiled but are not constructible through this facade, and +owner-exclusive scalar mappings stay contract data outside it. BMI reuses the +standard R4 profile with a HealthKit adapter claim; the four specimen-specific +glucose profiles belong to Health Connect. Neither is incorrectly presented as +a shared Mobile profile. Google glucose without specimen evidence maps only to +the specimen-neutral shared profile, daily vital summaries are admitted only +as their distinct daily-average measurements, and provider stage-duration +summaries map to the per-stage sleep-duration measurements rather than +sleep-stage intervals. There is intentionally no unprofiled fallback builder. + +```typescript +import { + buildProviderMeasurementBundle, + parseProviderMeasurementBundleInput, + parseNormalizedProviderRecord, + type ProviderMeasurementBundleInput, +} from '@schmiedmayerlab/grove-fhir/providers' -// Validate the data and get a typed resource -const patient = FhirPatient.parse(rawData) -console.log(patient.value.name?.[0]?.family) // 'Smith' +import { parsePositiveInteger } from '@schmiedmayerlab/grove-fhir' ``` -### Working with Observations +`parseNormalizedProviderRecord(unknown)` is the strict boundary for one source +record that an application has already mapped from Google Health API, Oura, or +Withings. Its `measurements` tuple is a non-empty, unique subset of the outputs +present in that record and admitted for its exact provider/source token. The +builder emits all supplied outputs atomically with one Provenance that targets +every resulting Observation; a malformed member fails the entire conversion. +Catalog order makes serialization deterministic but carries no FHIR semantic +meaning. For example, one Oura `daily_activity` record may carry any present +subset of step count, active energy, and distance; those outputs remain one +auditable conversion event and share the source's complete midnight-to-midnight +civil-day Period. The same complete-day rule applies to Withings daily activity +rows and permits offset changes across daylight-saving transitions. The strict +schema rejects unknown fields and raw provider responses. +This package contains no provider client, authentication, pagination, webhook, +or fetching behavior. + +`parseProviderMeasurementBundleInput(unknown)` applies the same fail-closed behavior to the +complete graph input. `buildProviderMeasurementBundle` also runs that strict parser itself, +so JavaScript callers cannot bypass the boundary or add fields that would be +silently ignored. Identity strings are validated without trimming or otherwise +changing their digest input. + +Every scalar `effectiveDateTime` and aggregate `effectivePeriod` endpoint is +canonicalized with the generated Mobile IG contract: exact offset-bearing +instants are rounded to the nearest millisecond with ties to even, including +across the Unix epoch, and serialized with the supplied numeric offset. The +source offset is never replaced or invented. `canonicalizeMobileEffectiveInstant` +exposes the same `Result`-typed primitive operation; Sensor and ECG +`SampledData` timing intentionally remains outside this policy. + +The public producer is deliberately closed to the Provider adapter +profile. A future HealthKit, Health Connect, or other adapter must add its own +closed facade and profile claims; callers cannot inject an arbitrary adapter +canonical into this API. + +The four provider-native series that the IG marks `mapped-standard` use the +separate closed `buildProviderRecordingBundle` facade: ```typescript -import { FhirObservation } from '@schmiedmayerlab/grove-fhir' - -const observationData = { - resourceType: 'Observation', - status: 'final', - code: { - coding: [ - { - system: 'http://loinc.org', - code: '29463-7', - display: 'Body Weight', - }, - ], - }, - subject: { - reference: 'Patient/patient-123', - }, - valueQuantity: { - value: 72.5, - unit: 'kg', - system: 'http://unitsofmeasure.org', - code: 'kg', - }, -} +import { + buildProviderRecordingBundle, + encodeRecordingBytes, + parseMediaType, +} from '@schmiedmayerlab/grove-fhir/providers' +``` + +The admitted source tokens are Google Health API `heart-rate`, Oura +`heartrate`, and Withings `activityIntraday` and `sleepIntraday`. The caller +passes an explicitly authorized and minimized encoding of bytes already in its +possession, or an immutable version-specific HTTP(S) URL with required size and +SHA-1/base64 integrity metadata. The builder emits a Sensor + Provider +`DocumentReference`, converter and data-origin application Devices, conversion +Provenance targeting the document, and the enclosing Mobile collection Bundle. +It never fetches the URL or parses provider-native content. Embedded attachment +size and hash are computed from the exact decoded bytes. + +Every attachment declares exactly one payload format from the closed +`groveRecordingFormatRegistry` (for provider API responses, `provider-recording`), +and its `contentType` must match that registry entry; the format is emitted as +the `DocumentReference.content.format` coding. Every attachment also requires +exactly one of the two catalog-derived producer assertions: +`caller-authorized-opaque-payload` or `verified-sanitized-input`. An absent, +ambiguous, or unsupported format or assertion fails closed. The assertion is producer preflight only, is never emitted as +FHIR authorization or consent, and is separate from recording-hardware identity +authorization. + +`sourceNativeId` remains digest-only for raw recordings too. It is rejected if +it leaks through caller-controlled FHIR metadata such as an attachment title, +URL, Device display, business identifier, or repository id. Opaque attachment +bytes are intentionally not inspected or sanitized by Grove; their disclosure +and retention remain an explicit deployment responsibility, not authorization +granted by the digest or Attachment hash. + +The high-level builder accepts a validated `ProviderMeasurementBundleInput` and returns a +`Result`. The collection contains the Observation, converter +and data-origin application Devices, an optional recording Device, an optional +distinct gateway application, and conversion Provenance. Every internal edge +uses a derived `urn:uuid` Bundle full URL. Each entry carries the complete +business Identifier from which that URL was derived. + +FHIR conversion does not make the converter a gateway. The gateway extension +is absent by default and appears only when `GatewayApplicationInput` carries the +explicit `mediated-or-routed-measurement` role assurance. The gateway may be the +converter in that additional role or a separately identified application. +Recording-device identity likewise declares either a deployment scope or an +explicitly authorized hardware scope. Serial numbers are not accepted by the +producer API. + +The source record input contains a deployment-scoped pseudonymous provider +account Identifier, an exact catalog source token, and the provider-native id. +The native id and account pseudonym are digest inputs only and are never emitted +in the FHIR graph. The output discriminator is selected by the generated +catalog—callers cannot provide arbitrary text. The positive durable event +sequence distinguishes conversion/exchange events without changing source or +clinical-output identity. The builder never generates times, event sequences, +or repository ids. + +`createEntryIdentity(identifier, id?)` remains available for deriving the +normative UUID-v5 full URL for caller-owned Device or Specimen business +identifiers. The high-level builder performs that derivation automatically. + +Provider source tokens are lineage and identity inputs; they are never appended +to `Observation.code` as if they were clinically equivalent concepts. A future +adapter that admits sleep-stage intervals may retain a catalog-reviewed, +more-specific source state as the second +`Observation.valueCodeableConcept.coding`; the required shared Grove coding +remains first. + +Study context uses branded `ResearchStudyReference` values. The parser accepts +only `ResearchStudy/{FHIR-id}` or an absolute HTTP(S) resource URL ending in +`/ResearchStudy/{FHIR-id}`. Queries, fragments, history URLs, malformed paths, +and duplicate literal references fail closed. + +The synchronized normative catalog and its resolved development reference live +under `catalog/grove-fhir`. `catalog/measurement-capabilities.json` +distinguishes the 31 constructible Provider scalar and coded measurements, +four constructible native-recording source tokens, the shared profiles not +admitted by this facade, adapter-profiled BMI/glucose outputs, +platform-exclusive adapter measurements, and reviewed future, adapter-only, +Sensor, and out-of-scope candidates. + +## Questionnaires + +The `questionnaire` entry point builds strict, versioned R4 +`Questionnaire`/`QuestionnaireResponse` resources and preflights a pair before +submission: -const observation = FhirObservation.parse(observationData) -console.log(observation.value.valueQuantity?.value) // 72.5 +```typescript +import { + buildQuestionnaire, + buildQuestionnaireResponse, + preflightQuestionnairePair, +} from '@schmiedmayerlab/grove-fhir/questionnaire' ``` -### Using Helper Methods +The builders own the exact Grove profile declarations, Semantic Versioning +algorithm extension, and electronic completion-mode extension. Answer values +are an exactly-one `value[x]` union in TypeScript and in the runtime schema. +The pair preflight checks the exact `url|version`, global link identity, +response nesting, answer types, inline options, repeats, response text, +conditional enablement, and required enabled items for completed or amended +responses. It also enforces text length and decimal-place limits, scalar and +quantity bounds, inline or ValueSet-backed quantity units, attachment MIME and +size limits, repeated-answer occurrence bounds, and exclusive options. + +Terminology is never fetched. For a coded item backed by `answerValueSet`, the +caller supplies a normalized resolved concept list through +`QuestionnairePreflightOptions`; an absent expansion fails closed with an +`external-validation-required` issue. FHIRPath expressions must use +`text/fhirpath` with non-empty source; variables have unique, non-reserved names +within each scope; and target constraints have a globally unique key, severity, +human guidance, and expression. + +For completed or amended responses, unevaluated expression enablement, +calculated output, and error-severity target constraints fail closed so the +application can route the pair through its configured SDC FHIRPath engine. A +warning-severity target constraint returns an `ok: true` result with a +non-empty `warnings` array and must be surfaced or recorded by the caller. An +`initialExpression` population failure alone does not block completion, though +ordinary required-answer rules still apply. Text length is counted in Unicode +code points to match the IG companion validator. + +This package does not extract Questionnaire answers into Observations. That is +a separate, explicitly configured clinical mapping operation. + +## Entry points + +The root entry point contains the source-neutral aggregate: validated +primitives, the bounded R4 closure, the shared Mobile contract, and +Questionnaire support. Provider-specific construction is deliberately absent. ```typescript -import { FhirCondition } from '@schmiedmayerlab/grove-fhir' - -const condition = FhirCondition.parse({ - resourceType: 'Condition', - code: { - coding: [ - { - system: 'http://snomed.info/sct', - code: '73211009', - display: 'Diabetes mellitus', - }, - ], - }, - // ... other fields -}) +import { + parseFhirInstant, + parseObservation, + type Result, +} from '@schmiedmayerlab/grove-fhir' +``` -// Check if condition contains specific coding -const hasDiabetes = condition.containsCoding(condition.value.code, [ - { - system: 'http://snomed.info/sct', - code: '73211009', - }, -]) +Use the Mobile entry point for source-neutral shared semantics, deterministic +entry identity, and effective-time canonicalization: -// Extract codes from CodeableConcept -const codes = condition.codes(condition.value.code, { - system: 'http://snomed.info/sct', -}) -console.log(codes) // ['73211009'] +```typescript +import { + canonicalizeMobileEffectiveInstant, + groveMobilePackageMetadata, + sharedMobileMeasurementCatalog, + type MobileMeasurement, +} from '@schmiedmayerlab/grove-fhir/mobile' ``` -### Firebase Functions Integration +Use the Provider entry point for the closed Google Health API, Oura, +and Withings mapping and graph builders: ```typescript -import { onCall } from 'firebase-functions/v2/https' -import { getFirestore } from 'firebase-admin/firestore' -import { FhirPatient } from '@schmiedmayerlab/grove-fhir' +import { + buildProviderMeasurementBundle, + buildProviderRecordingBundle, + groveProviderPackageMetadata, +} from '@schmiedmayerlab/grove-fhir/providers' +``` -export const createPatient = onCall(async (request) => { - const patientData = request.data +Each generated package-metadata value is bounded to that entry point's +applicable IG package and includes its exact package id, canonical, dependency +list, FHIR release, and IG contract version. The root exposes only the shared +`groveFhirVersion` and `groveFhirContractVersion`; it does not expose the full +internal adapter package graph. - // Validate the patient data - const patient = FhirPatient.parse(patientData) +There is no generic Sensor producer entry point in this release. The admitted +raw-recording facade owns exact Sensor + Provider profile claims and +provider identity, so presenting it as source-neutral would weaken the closed +adapter contract. A future Sensor producer must expose its own evidenced, +profile-closed facade. - // Store in Firestore - const firestore = getFirestore() - await firestore - .collection('patients') - .doc(patient.value.id) - .set(patient.value) +Use the R4 entry point for the bounded resource schemas and types: - return { success: true, id: patient.value.id } -}) +```typescript +import { + parseCollectionBundle, + type CollectionBundle, +} from '@schmiedmayerlab/grove-fhir/r4' ``` -### Firestore Integration +Parsing never returns a partially accepted resource: ```typescript -import { getFirestore } from 'firebase-admin/firestore' -import { FhirObservation } from '@schmiedmayerlab/grove-fhir' +const result = parseObservation(input) -const firestore = getFirestore() +if (!result.ok) { + for (const issue of result.issues) { + console.error(issue.path, issue.code, issue.message) + } +} +``` + +### Reading values back out -// Read and validate from Firestore -const doc = await firestore.collection('observations').doc('obs-123').get() -const observation = FhirObservation.parse(doc.data()) +FHIR states time as a string and a measurement as a `Quantity`, neither of which a consumer can compute with directly. +Two accessors convert them, and both return a `Result` so a malformed value is reported rather than silently becoming `Invalid Date` or `NaN`: -// Write validated data to Firestore -const newObservation = FhirObservation.parse({/* ... */}) -await firestore - .collection('observations') - .doc(newObservation.value.id) - .set(newObservation.value) +```typescript +import { + fhirDateTimeToDate, + fhirQuantityToValue, +} from '@schmiedmayerlab/grove-fhir' + +const recorded = fhirDateTimeToDate(observation.effectiveDateTime) +const measured = fhirQuantityToValue(observation.valueQuantity) ``` -## API Overview +`fhirDateTimeToDate` accepts a `date`, `dateTime`, or `instant`; a value stated to lower precision resolves to the start of the period it names, in UTC, because a FHIR `date` carries no offset to resolve it against. +`fhirQuantityToValue` returns the number with the unit's code where the Quantity states one — only the code is comparable, `unit` alone being a label the source chose — along with the code's system and any comparator, since dropping a comparator would report a bound as a measurement. -### Resource Classes +### Extracting values -All FHIR resources are exported as classes that extend `FhirDomainResource`: +FHIR states a name across five fields, a code across three, and a period as two strings. +Accessors read each as the JavaScript value a consumer works with, and take `unknown` so they can be +applied to a resource before it has been narrowed. ```typescript import { - FhirPatient, - FhirObservation, - FhirMedicationRequest, - FhirAppointment, + codeableConceptDisplay, + observationEffectiveDate, + observationNumericValue, + patientAgeInYears, + periodIsActive, } from '@schmiedmayerlab/grove-fhir' -// Each resource has a parse method -const patient = FhirPatient.parse(rawData) +const when = observationEffectiveDate(observation) +const value = observationNumericValue(observation) +const label = codeableConceptDisplay(observation.code) +``` + +Absence and malformation are both reported as `undefined` rather than as a substitute value. +`codeableConceptDisplay` returns nothing rather than the bare code, which reads as a label but is not +one; `containsCoding` requires a system as well as a code, because matching a code across systems is how +two unrelated terminologies get conflated; and an absent `Period` counts as active but overlaps nothing, +which is how FHIR reads a missing period in each case. + +Dates are computed in UTC throughout. +A FHIR `date` names a calendar day with no offset, so comparing it against the host's local calendar +moves the day for anyone west of UTC — which is a wrong age for one day each year. -// Access the validated value -console.log(patient.value) // Typed FHIR Patient resource +### Validating another release + +The root entry point validates R4, which is the release every Grove guide targets. +Consumers working in R4B import the same shapes from the R4B entry point; both are produced by one generator from their own release's definitions, so neither is a hand-maintained copy of the other. + +```typescript +import { observationSchema } from '@schmiedmayerlab/grove-fhir/zod/r4' +import { observationSchema as r4bObservationSchema } from '@schmiedmayerlab/grove-fhir/zod/r4b' ``` -### Helper Methods +Use these when you need a schema the root entry point does not narrow, or when you are validating a release other than R4. +Prefer `parseObservation` and its siblings for R4: they return a `Result` with typed issues instead of throwing. -The `FhirDomainResource` base class provides useful utilities: +## R4 boundary -- **`codes(concept, filter)`**: Extract code values from a CodeableConcept -- **`containsCoding(concept, filter)`**: Check if a CodeableConcept contains specific codings -- **`getExtension(url)`**: Retrieve extensions by URL +The runtime schemas are generated from a FHIR release's own `StructureDefinition` resources, not written by hand. +Cardinality, choice types, required-binding enumerations, and even the primitive patterns come from the published definitions, so a schema cannot drift from the specification it claims to implement. -### Type Safety +Each schema states the release's own TypeScript type, so TypeScript checks that the schema produces that type rather than trusting a cast. +Primitive metadata such as `_effectiveDateTime` is preserved, and a property the release does not define is rejected rather than silently dropped: FHIR JSON is a closed format, so an unmodelled property is an error and not an extension. -All schemas provide full TypeScript type inference: +A slot that may hold _any_ resource — `contained`, `Bundle.entry.resource`, and `Bundle.entry.response.outcome` — discriminates on `resourceType` and validates the resource against the schema for that type. +A malformed Observation nested in a bundle entry is rejected exactly where a standalone one would be, and the reported path names the element at fault rather than the entry that carries it. +A resource type the package does not model keeps the open shape, because there is nothing better to check it against; rejecting it would refuse a conformant bundle over a gap here rather than a fault in the data. +Every resource the release publishes is modelled — 146 in R4 and 141 in R4B — so a resource arriving contained, bundled, or referenced is checked field by field rather than waved through. +The seed comes from the release's own StructureDefinitions, so a resource cannot be missing because nobody remembered to add it; only a `resourceType` outside the release keeps the open shape. -```typescript -import { type Patient } from 'fhir/r4b.js' +Four FHIR invariants are enforced structurally — `ext-1`, `per-1`, `obs-3`, and `obs-6` — along with cardinality, required-array minimums, and the rule that a choice element carries exactly one alternative when required and at most one otherwise. +Every other invariant the release states is listed in the generated `UNCHECKED_CONSTRAINTS` map, keyed by the path it governs, so the boundary is inspectable rather than implied. +Passing a runtime schema is therefore an application preflight, not a declaration of profile conformance. -// The parsed value is fully typed -const patient = FhirPatient.parse(data) -const name: string | undefined = patient.value.name?.[0]?.family +Normative conformance is established by validating generated fixtures with the official HL7 FHIR Validator and the pinned Grove implementation-guide packages. +Synchronized IG catalogs and the Mobile semantic corpus retain the exact upstream bytes; local generated TypeScript is derived from those immutable inputs. + +### Regenerating + +```sh +npm run generate:zod # rewrite the generated schemas for both releases +npm run check:zod # fail if either is stale +``` + +`check:zod` runs as part of `typecheck`, so a stale schema fails the build the same way a stale catalog does. + +## Development conformance + +`npm run check:fixtures` regenerates the committed conformance corpus from the +public API and fails if any resource has drifted. With a local Grove FHIR +checkout, the fast fail-closed checks run as follows: + +```sh +npm run conformance:structural -- --ig /path/to/grove-fhir ``` +`npm run conformance` additionally requires the checksum-pinned official FHIR +Validator and the Mobile, Sensor dependency, Provider, and Questionnaire +packages built in that checkout. It validates all 10 admitted Mobile +measurement graphs, all four admitted native-recording graphs, and the +Questionnaire/Response pair. Every admitted scalar fixture is generated from +the IG's versioned Mobile semantic corpus and bound through an exact RFC 6901 +resource pointer, so the generic producer kit compares the emitted profile, +clinical code, effective time, and result projection—not only the resource +shape. + +During coordinated 0.2 development, CI consumes +`feature/fhir-v020-adapters`. `catalog/grove-fhir/source-ref.json` records the +resolved review snapshot for traceability; SHA drift is reported but is not by +itself a failure. Catalog or emitted-resource drift is a failure. Before merge, +the consumer branch must refresh the catalogs and recorded snapshot to the +reviewed IG head, be marked ready for review, and pass the repository's full +readiness CI. Draft pull requests run explicitly labeled scoped FHIR and fast +standards checks; the release-readiness aggregate stays fail-closed. Marking a +pull request ready reruns the complete repository suite and official Validator +workflow. + ## License -This project is licensed under the MIT License. See [Licenses](https://github.com/SchmiedmayerLab/grove-ts/tree/main/LICENSES) for more information. +This project is licensed under the MIT License. +See [Licenses](https://github.com/SchmiedmayerLab/grove-ts/tree/main/LICENSES) for more information. ## Contributors -This project is developed as part of the Schmiedmayer Lab at Stanford University. -See the repository's [CONTRIBUTORS.md](../../CONTRIBUTORS.md). +This package is developed by the Schmiedmayer Lab at Stanford University. +See [CONTRIBUTORS.md](../../CONTRIBUTORS.md). diff --git a/packages/fhir/catalog/measurement-capabilities.json b/packages/fhir/catalog/measurement-capabilities.json new file mode 100644 index 00000000..f78ab726 --- /dev/null +++ b/packages/fhir/catalog/measurement-capabilities.json @@ -0,0 +1,1863 @@ +{ + "schemaVersion": 1, + "igVersion": "0.5.0", + "canonicalBase": "https://grovealliance.org/fhir", + "recordConversion": { + "input": "One connected-provider source record with a non-empty unique subset of the catalog-admitted outputs actually present.", + "atomicity": "Every present output is validated before construction; any failure rejects the whole conversion without a partial graph.", + "provenance": "Exactly one conversion Provenance targets every output emitted for the source record.", + "ordering": "Catalog order is used only for deterministic serialization and has no FHIR semantic meaning.", + "fetchBoundary": "Provider clients, authentication, pagination, webhooks, and raw response models remain outside grove-fhir." + }, + "statuses": { + "implemented": "A closed public builder emits resources conforming to the listed v0.2 profile.", + "implemented-recording": "The closed native-recording builder emits the exact Sensor + Provider DocumentReference graph for this catalogued provider source token.", + "profiled-not-admitted": "The shared profile exists, but this adapter facade has no supported provider source.", + "adapter-profiled": "Version 0.2 defines an adapter-owned or standard-plus-adapter profile claim; it is intentionally not a shared Mobile profile and is outside this Provider facade.", + "planned-shared": "The concept is shared by multiple sources but has no admitted v0.2 Grove builder.", + "adapter-specific": "The semantics are source-specific and must not be collapsed into a shared concept.", + "sensor-ig": "The concept belongs to the Sensor and Waveform contract.", + "not-in-scope": "The source exposes the record, but it is not a mobile measurement admitted by this package.", + "platform-exclusive": "The measurement is owned by a single platform adapter and its adapter-exclusive profile; it is outside the shared Mobile facade and this Provider facade." + }, + "measurements": [ + { + "key": "active-energy", + "status": "implemented", + "profile": "grove-mobile-active-energy", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "active-energy-burned", + "display": "Active energy burned" + }, + "unit": { + "code": "kcal", + "display": "kcal" + }, + "effective": "Period", + "sources": [ + "healthkit", + "health-connect", + "google-health-api", + "oura", + "withings" + ] + }, + { + "key": "basal-body-temperature", + "status": "profiled-not-admitted", + "profile": "grove-mobile-basal-body-temperature", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "basal-body-temperature", + "display": "Basal body temperature" + }, + "unit": { + "code": "Cel", + "display": "°C" + }, + "effective": "dateTime", + "sources": ["healthkit", "health-connect"], + "reason": "None of the Google Health API, Oura, or Withings mappings admits this shared measurement in v0.2." + }, + { + "key": "basal-energy", + "status": "implemented", + "profile": "grove-mobile-basal-energy", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "basal-energy-burned", + "display": "Basal energy burned" + }, + "unit": { + "code": "kcal", + "display": "kcal" + }, + "effective": "Period", + "sources": ["healthkit", "google-health-api"] + }, + { + "key": "blood-glucose-unspecified-specimen", + "status": "implemented", + "profile": "grove-mobile-blood-glucose-unspecified-specimen", + "code": { + "system": "http://loinc.org", + "code": "2339-0", + "display": "Glucose [Mass/volume] in Blood" + }, + "unit": { + "code": "mg/dL", + "display": "mg/dL" + }, + "effective": "dateTime", + "sources": ["healthkit", "google-health-api"] + }, + { + "key": "blood-pressure", + "status": "implemented", + "profile": "grove-mobile-blood-pressure", + "authoritativeProfiles": ["http://hl7.org/fhir/StructureDefinition/bp"], + "code": { + "system": "http://loinc.org", + "code": "85354-9", + "display": "Blood pressure panel" + }, + "unit": { + "code": "mm[Hg]", + "display": "mmHg" + }, + "effective": "dateTime", + "sources": ["healthkit", "health-connect", "withings"] + }, + { + "key": "body-fat-percentage", + "status": "implemented", + "profile": "grove-mobile-body-fat-percentage", + "code": { + "system": "http://loinc.org", + "code": "41982-0", + "display": "Percentage of body fat Measured" + }, + "unit": { + "code": "%", + "display": "%" + }, + "effective": "dateTime", + "sources": [ + "healthkit", + "health-connect", + "google-health-api", + "withings" + ] + }, + { + "key": "body-height", + "status": "implemented", + "profile": "grove-mobile-body-height", + "authoritativeProfiles": [ + "http://hl7.org/fhir/StructureDefinition/bodyheight" + ], + "code": { + "system": "http://loinc.org", + "code": "8302-2", + "display": "Body height" + }, + "unit": { + "code": "cm", + "display": "cm" + }, + "effective": "dateTime", + "sources": [ + "healthkit", + "health-connect", + "google-health-api", + "withings" + ] + }, + { + "key": "body-temperature", + "status": "implemented", + "profile": "grove-mobile-body-temperature", + "authoritativeProfiles": [ + "http://hl7.org/fhir/StructureDefinition/bodytemp" + ], + "code": { + "system": "http://loinc.org", + "code": "8310-5", + "display": "Body temperature" + }, + "unit": { + "code": "Cel", + "display": "°C" + }, + "effective": "dateTime", + "sources": [ + "healthkit", + "health-connect", + "google-health-api", + "withings" + ] + }, + { + "key": "body-water-mass", + "status": "implemented", + "profile": "grove-mobile-body-water-mass", + "code": { + "system": "http://loinc.org", + "code": "101683-1", + "display": "Body water mass" + }, + "unit": { + "code": "kg", + "display": "kg" + }, + "effective": "dateTime", + "sources": ["health-connect", "withings"] + }, + { + "key": "body-weight", + "status": "implemented", + "profile": "grove-mobile-body-weight", + "authoritativeProfiles": [ + "http://hl7.org/fhir/StructureDefinition/bodyweight" + ], + "code": { + "system": "http://loinc.org", + "code": "29463-7", + "display": "Body weight" + }, + "unit": { + "code": "kg", + "display": "kg" + }, + "effective": "dateTime", + "sources": [ + "healthkit", + "health-connect", + "google-health-api", + "withings" + ] + }, + { + "key": "bone-mass", + "status": "implemented", + "profile": "grove-mobile-bone-mass", + "code": { + "system": "http://loinc.org", + "code": "101685-6", + "display": "Body bone mass" + }, + "unit": { + "code": "kg", + "display": "kg" + }, + "effective": "dateTime", + "sources": ["health-connect", "withings"] + }, + { + "key": "cervical-mucus-quality", + "status": "profiled-not-admitted", + "profile": "grove-mobile-cervical-mucus-quality", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "cervical-mucus-quality", + "display": "Cervical mucus quality" + }, + "effective": "dateTime", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "cycling-cadence", + "status": "profiled-not-admitted", + "profile": "grove-mobile-cycling-cadence", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "cycling-cadence", + "display": "Cycling cadence" + }, + "unit": { + "code": "/min", + "display": "revolutions/minute" + }, + "effective": "dateTime", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "deep-sleep-duration", + "status": "implemented", + "profile": "grove-mobile-deep-sleep-duration", + "code": { + "system": "http://loinc.org", + "code": "93831-6", + "display": "Deep sleep duration" + }, + "unit": { + "code": "min", + "display": "min" + }, + "effective": "Period", + "sources": ["google-health-api", "oura", "withings"] + }, + { + "key": "dietary-biotin", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-biotin", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-biotin", + "display": "Dietary biotin" + }, + "unit": { + "code": "ug", + "display": "ug" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-caffeine", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-caffeine", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-caffeine", + "display": "Dietary caffeine" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-calcium", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-calcium", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-calcium", + "display": "Dietary calcium" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-carbohydrates", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-carbohydrates", + "code": { + "system": "http://loinc.org", + "code": "9060-5", + "display": "Carbohydrate intake Measured" + }, + "unit": { + "code": "g", + "display": "g" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-chloride", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-chloride", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-chloride", + "display": "Dietary chloride" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-cholesterol", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-cholesterol", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-cholesterol", + "display": "Dietary cholesterol" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-chromium", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-chromium", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-chromium", + "display": "Dietary chromium" + }, + "unit": { + "code": "ug", + "display": "ug" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-copper", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-copper", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-copper", + "display": "Dietary copper" + }, + "unit": { + "code": "ug", + "display": "ug" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-energy", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-energy", + "code": { + "system": "http://loinc.org", + "code": "9052-2", + "display": "Calorie intake total" + }, + "unit": { + "code": "kcal", + "display": "kcal" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-fat-monounsaturated", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-fat-monounsaturated", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-fat-monounsaturated", + "display": "Dietary monounsaturated fat" + }, + "unit": { + "code": "g", + "display": "g" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-fat-polyunsaturated", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-fat-polyunsaturated", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-fat-polyunsaturated", + "display": "Dietary polyunsaturated fat" + }, + "unit": { + "code": "g", + "display": "g" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-fat-saturated", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-fat-saturated", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-fat-saturated", + "display": "Dietary saturated fat" + }, + "unit": { + "code": "g", + "display": "g" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-fat-total", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-fat-total", + "code": { + "system": "http://loinc.org", + "code": "9067-0", + "display": "Fat intake Measured" + }, + "unit": { + "code": "g", + "display": "g" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-fiber", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-fiber", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-fiber", + "display": "Dietary fiber" + }, + "unit": { + "code": "g", + "display": "g" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-folate", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-folate", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-folate", + "display": "Dietary folate" + }, + "unit": { + "code": "ug", + "display": "ug" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-iodine", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-iodine", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-iodine", + "display": "Dietary iodine" + }, + "unit": { + "code": "ug", + "display": "ug" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-iron", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-iron", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-iron", + "display": "Dietary iron" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-magnesium", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-magnesium", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-magnesium", + "display": "Dietary magnesium" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-manganese", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-manganese", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-manganese", + "display": "Dietary manganese" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-molybdenum", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-molybdenum", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-molybdenum", + "display": "Dietary molybdenum" + }, + "unit": { + "code": "ug", + "display": "ug" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-niacin", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-niacin", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-niacin", + "display": "Dietary niacin" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-pantothenic-acid", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-pantothenic-acid", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-pantothenic-acid", + "display": "Dietary pantothenic acid" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-phosphorus", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-phosphorus", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-phosphorus", + "display": "Dietary phosphorus" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-potassium", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-potassium", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-potassium", + "display": "Dietary potassium" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-protein", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-protein", + "code": { + "system": "http://loinc.org", + "code": "9080-3", + "display": "Protein intake Measured" + }, + "unit": { + "code": "g", + "display": "g" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-riboflavin", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-riboflavin", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-riboflavin", + "display": "Dietary riboflavin" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-selenium", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-selenium", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-selenium", + "display": "Dietary selenium" + }, + "unit": { + "code": "ug", + "display": "ug" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-sodium", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-sodium", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-sodium", + "display": "Dietary sodium" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-sugar", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-sugar", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-sugar", + "display": "Dietary sugar" + }, + "unit": { + "code": "g", + "display": "g" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-thiamin", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-thiamin", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-thiamin", + "display": "Dietary thiamin" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-vitamin-a", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-vitamin-a", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-vitamin-a", + "display": "Dietary vitamin A" + }, + "unit": { + "code": "ug", + "display": "ug" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-vitamin-b12", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-vitamin-b12", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-vitamin-b12", + "display": "Dietary vitamin B12" + }, + "unit": { + "code": "ug", + "display": "ug" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-vitamin-b6", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-vitamin-b6", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-vitamin-b6", + "display": "Dietary vitamin B6" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-vitamin-c", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-vitamin-c", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-vitamin-c", + "display": "Dietary vitamin C" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-vitamin-d", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-vitamin-d", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-vitamin-d", + "display": "Dietary vitamin D" + }, + "unit": { + "code": "ug", + "display": "ug" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-vitamin-e", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-vitamin-e", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-vitamin-e", + "display": "Dietary vitamin E" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-vitamin-k", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-vitamin-k", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-vitamin-k", + "display": "Dietary vitamin K" + }, + "unit": { + "code": "ug", + "display": "ug" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "dietary-zinc", + "status": "profiled-not-admitted", + "profile": "grove-mobile-dietary-zinc", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "dietary-zinc", + "display": "Dietary zinc" + }, + "unit": { + "code": "mg", + "display": "mg" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "distance", + "status": "implemented", + "profile": "grove-mobile-distance", + "code": { + "system": "http://loinc.org", + "code": "103208-5", + "display": "Distance traveled" + }, + "unit": { + "code": "m", + "display": "m" + }, + "effective": "Period", + "sources": [ + "healthkit", + "health-connect", + "google-health-api", + "oura", + "withings" + ] + }, + { + "key": "electrodermal-activity", + "status": "implemented", + "profile": "grove-mobile-electrodermal-activity", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "electrodermal-activity", + "display": "Electrodermal activity" + }, + "unit": { + "code": "uS", + "display": "uS" + }, + "effective": "dateTime", + "sources": ["healthkit", "withings"] + }, + { + "key": "flights-climbed", + "status": "implemented", + "profile": "grove-mobile-flights-climbed", + "code": { + "system": "http://loinc.org", + "code": "100304-5", + "display": "Flights climbed [#] Reporting Period" + }, + "unit": { + "code": "{flights}", + "display": "flights" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect", "google-health-api"] + }, + { + "key": "fluid-intake", + "status": "profiled-not-admitted", + "profile": "grove-mobile-fluid-intake", + "code": { + "system": "http://loinc.org", + "code": "8985-4", + "display": "Fluid intake Measured" + }, + "unit": { + "code": "mL", + "display": "mL" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "heart-rate", + "status": "implemented", + "profile": "grove-mobile-heart-rate", + "authoritativeProfiles": [ + "http://hl7.org/fhir/StructureDefinition/heartrate" + ], + "code": { + "system": "http://loinc.org", + "code": "8867-4", + "display": "Heart rate" + }, + "unit": { + "code": "/min", + "display": "beats/minute" + }, + "effective": "dateTime", + "sources": ["healthkit", "health-connect", "sensorkit", "withings"] + }, + { + "key": "heart-rate-variability-rmssd", + "status": "implemented", + "profile": "grove-mobile-heart-rate-variability-rmssd", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "heart-rate-variability-rmssd", + "display": "Heart rate variability RMSSD" + }, + "unit": { + "code": "ms", + "display": "ms" + }, + "effective": "dateTime", + "sources": ["health-connect", "google-health-api"] + }, + { + "key": "heart-rate-variability-sdnn", + "status": "implemented", + "profile": "grove-mobile-heart-rate-variability-sdnn", + "code": { + "system": "http://loinc.org", + "code": "112429-6", + "display": "Heart rate variability SDNN [Time]" + }, + "unit": { + "code": "ms", + "display": "ms" + }, + "effective": "dateTime", + "sources": ["healthkit", "google-health-api"] + }, + { + "key": "intermenstrual-bleeding", + "status": "profiled-not-admitted", + "profile": "grove-mobile-intermenstrual-bleeding", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "intermenstrual-bleeding", + "display": "Intermenstrual bleeding" + }, + "effective": "dateTime", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "lean-body-mass", + "status": "implemented", + "profile": "grove-mobile-lean-body-mass", + "code": { + "system": "http://loinc.org", + "code": "91557-9", + "display": "Lean body weight" + }, + "unit": { + "code": "kg", + "display": "kg" + }, + "effective": "dateTime", + "sources": ["healthkit", "health-connect", "withings"] + }, + { + "key": "light-sleep-duration", + "status": "implemented", + "profile": "grove-mobile-light-sleep-duration", + "code": { + "system": "http://loinc.org", + "code": "93830-8", + "display": "Light sleep duration" + }, + "unit": { + "code": "min", + "display": "min" + }, + "effective": "Period", + "sources": ["google-health-api", "oura", "withings"] + }, + { + "key": "menstruation-flow", + "status": "profiled-not-admitted", + "profile": "grove-mobile-menstruation-flow", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "menstruation-flow", + "display": "Menstruation flow" + }, + "effective": "dateTime", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "mindfulness-session", + "status": "profiled-not-admitted", + "profile": "grove-mobile-mindfulness-session", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "mindfulness-session-duration", + "display": "Mindfulness session duration" + }, + "unit": { + "code": "min", + "display": "min" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "ovulation-test-result", + "status": "profiled-not-admitted", + "profile": "grove-mobile-ovulation-test-result", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "ovulation-test-result", + "display": "Ovulation test result" + }, + "effective": "dateTime", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "oxygen-saturation", + "status": "implemented", + "profile": "grove-mobile-oxygen-saturation", + "authoritativeProfiles": [ + "http://hl7.org/fhir/StructureDefinition/oxygensat" + ], + "code": { + "system": "http://loinc.org", + "code": "2708-6", + "display": "Oxygen saturation" + }, + "unit": { + "code": "%", + "display": "%" + }, + "effective": "dateTime", + "sources": ["healthkit", "health-connect", "withings"] + }, + { + "key": "oxygen-saturation-daily-average", + "status": "implemented", + "profile": "grove-mobile-oxygen-saturation-daily-average", + "code": { + "system": "http://loinc.org", + "code": "103209-3", + "display": "Mean oxygen saturation" + }, + "unit": { + "code": "%", + "display": "%" + }, + "effective": "Period", + "sources": ["google-health-api", "oura"] + }, + { + "key": "power", + "status": "profiled-not-admitted", + "profile": "grove-mobile-power", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "power", + "display": "Power" + }, + "unit": { + "code": "W", + "display": "W" + }, + "effective": "dateTime", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "rem-sleep-duration", + "status": "implemented", + "profile": "grove-mobile-rem-sleep-duration", + "code": { + "system": "http://loinc.org", + "code": "93829-0", + "display": "REM sleep duration" + }, + "unit": { + "code": "min", + "display": "min" + }, + "effective": "Period", + "sources": ["google-health-api", "oura", "withings"] + }, + { + "key": "respiratory-rate", + "status": "profiled-not-admitted", + "profile": "grove-mobile-respiratory-rate", + "authoritativeProfiles": [ + "http://hl7.org/fhir/StructureDefinition/resprate" + ], + "code": { + "system": "http://loinc.org", + "code": "9279-1", + "display": "Respiratory rate" + }, + "unit": { + "code": "/min", + "display": "breaths/minute" + }, + "effective": "dateTime", + "sources": ["healthkit", "health-connect"], + "reason": "The connected providers expose daily aggregates rather than the point-in-time respiratory-rate semantics required by the shared profile." + }, + { + "key": "respiratory-rate-average", + "status": "implemented", + "profile": "grove-mobile-respiratory-rate-average", + "code": { + "system": "http://loinc.org", + "code": "103217-6", + "display": "Mean respiratory rate" + }, + "unit": { + "code": "/min", + "display": "breaths/minute" + }, + "effective": "Period", + "sources": ["google-health-api", "oura", "withings"] + }, + { + "key": "resting-heart-rate", + "status": "implemented", + "profile": "grove-mobile-resting-heart-rate", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "resting-heart-rate", + "display": "Resting heart rate" + }, + "unit": { + "code": "/min", + "display": "beats/minute" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect", "google-health-api"] + }, + { + "key": "sexual-activity", + "status": "profiled-not-admitted", + "profile": "grove-mobile-sexual-activity", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "sexual-activity", + "display": "Sexual activity" + }, + "effective": "dateTime", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "skin-temperature", + "status": "implemented", + "profile": "grove-mobile-skin-temperature", + "code": { + "system": "http://loinc.org", + "code": "61008-9", + "display": "Body surface temperature" + }, + "unit": { + "code": "Cel", + "display": "Cel" + }, + "effective": "dateTime", + "sources": ["healthkit", "health-connect", "withings"] + }, + { + "key": "sleep-awake-duration", + "status": "implemented", + "profile": "grove-mobile-sleep-awake-duration", + "code": { + "system": "http://loinc.org", + "code": "93828-2", + "display": "Nighttime awakening duration" + }, + "unit": { + "code": "min", + "display": "min" + }, + "effective": "Period", + "sources": ["google-health-api", "oura", "withings"] + }, + { + "key": "sleep-duration", + "status": "implemented", + "profile": "grove-mobile-sleep-duration", + "code": { + "system": "http://loinc.org", + "code": "93832-4", + "display": "Sleep duration" + }, + "unit": { + "code": "h", + "display": "h" + }, + "effective": "Period", + "sources": ["health-connect", "google-health-api", "oura"] + }, + { + "key": "sleep-heart-rate", + "status": "implemented", + "profile": "grove-mobile-sleep-heart-rate", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "sleep-heart-rate", + "display": "Sleep heart rate" + }, + "unit": { + "code": "/min", + "display": "beats/minute" + }, + "effective": "Period", + "sources": ["oura", "withings"] + }, + { + "key": "sleep-stage", + "status": "profiled-not-admitted", + "profile": "grove-mobile-sleep-stage", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "sleep-stage", + "display": "Sleep stage" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "The connected-provider inventories expose stage-duration summaries rather than bounded stage intervals, so the facade does not relabel them." + }, + { + "key": "speed", + "status": "profiled-not-admitted", + "profile": "grove-mobile-speed", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "speed", + "display": "Speed" + }, + "unit": { + "code": "m/s", + "display": "m/s" + }, + "effective": "dateTime", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "step-count", + "status": "implemented", + "profile": "grove-mobile-step-count", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "step-count-total", + "display": "Step count total" + }, + "unit": { + "code": "{steps}", + "display": "steps" + }, + "effective": "Period", + "sources": [ + "healthkit", + "health-connect", + "sensorkit", + "google-health-api", + "oura", + "withings" + ] + }, + { + "key": "vo2-max", + "status": "implemented", + "profile": "grove-mobile-vo2-max", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "vo2-max", + "display": "VO2 max" + }, + "unit": { + "code": "mL/kg/min", + "display": "mL/kg/min" + }, + "effective": "dateTime", + "sources": [ + "healthkit", + "health-connect", + "google-health-api", + "oura", + "withings" + ] + }, + { + "key": "wheelchair-push-count", + "status": "profiled-not-admitted", + "profile": "grove-mobile-wheelchair-push-count", + "code": { + "system": "http://loinc.org", + "code": "96502-0", + "display": "Number of wheelchair pushes per time period" + }, + "unit": { + "code": "{pushes}", + "display": "pushes" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "workout", + "status": "implemented", + "profile": "grove-mobile-workout", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "workout", + "display": "Workout session" + }, + "effective": "Period", + "sources": [ + "healthkit", + "health-connect", + "google-health-api", + "oura", + "withings" + ] + }, + { + "key": "workout-segment", + "status": "profiled-not-admitted", + "profile": "grove-mobile-workout-segment", + "code": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "workout-segment", + "display": "Workout segment" + }, + "effective": "Period", + "sources": ["healthkit", "health-connect"], + "reason": "No connected provider exposes a semantically exact source for this shared measurement, so the Provider facade does not admit it." + }, + { + "key": "body-mass-index", + "status": "adapter-profiled", + "profile": "http://hl7.org/fhir/StructureDefinition/bmi", + "adapterProfile": "https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-observation", + "authoritativeProfiles": ["http://hl7.org/fhir/StructureDefinition/bmi"], + "code": { + "system": "http://loinc.org", + "code": "39156-5", + "display": "Body mass index" + }, + "unit": { + "code": "kg/m2", + "display": "kg/m²" + }, + "effective": "dateTime", + "sources": ["healthkit"], + "reason": "FHIR R4 BMI plus the HealthKit adapter profile is the exact v0.2 claim; a one-source shared Mobile wrapper is prohibited." + }, + { + "key": "blood-glucose", + "status": "adapter-profiled", + "profile": "https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-whole-blood-glucose", + "code": { + "system": "http://loinc.org", + "code": "2339-0", + "display": "Glucose in blood" + }, + "unit": { + "code": "mg/dL", + "display": "mg/dL" + }, + "effective": "dateTime", + "sources": ["health-connect"], + "reason": "The exact whole-blood + specimen contract is Health Connect-owned. The consumed Google Health API shape has no specimen evidence, so the Provider facade fails closed." + }, + { + "key": "capillary-blood-glucose", + "status": "adapter-profiled", + "profile": "https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-capillary-blood-glucose", + "code": { + "system": "http://loinc.org", + "code": "32016-8", + "display": "Glucose in capillary blood" + }, + "unit": { + "code": "mg/dL", + "display": "mg/dL" + }, + "effective": "dateTime", + "sources": ["health-connect"], + "reason": "The exact capillary-blood + specimen contract is Health Connect-owned. The consumed Google Health API shape has no specimen evidence, so the Provider facade fails closed." + }, + { + "key": "serum-plasma-glucose", + "status": "adapter-profiled", + "profile": "https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-serum-plasma-glucose", + "code": { + "system": "http://loinc.org", + "code": "2345-7", + "display": "Glucose in serum or plasma" + }, + "unit": { + "code": "mg/dL", + "display": "mg/dL" + }, + "effective": "dateTime", + "sources": ["health-connect"], + "reason": "The exact serum/plasma + specimen contract is Health Connect-owned. The consumed Google Health API shape cannot supply the required specimen evidence, so the Provider facade fails closed." + }, + { + "key": "interstitial-glucose", + "status": "adapter-profiled", + "profile": "https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-interstitial-glucose", + "code": { + "system": "http://loinc.org", + "code": "99504-3", + "display": "Glucose in interstitial fluid" + }, + "unit": { + "code": "mg/dL", + "display": "mg/dL" + }, + "effective": "dateTime", + "sources": ["health-connect"], + "reason": "The exact interstitial-fluid + specimen contract is Health Connect-owned. The consumed Google Health API shape has no specimen evidence, so the Provider facade fails closed." + }, + { + "key": "google-health-api-heart-rate-recording", + "status": "implemented-recording", + "provider": "google-health-api", + "sourceType": "heart-rate", + "profile": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document", + "sourceNeutralProfile": "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", + "outputDiscriminator": "native-recording", + "sources": ["google-health-api"] + }, + { + "key": "oura-heart-rate-recording", + "status": "implemented-recording", + "provider": "oura", + "sourceType": "heartrate", + "profile": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document", + "sourceNeutralProfile": "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", + "outputDiscriminator": "native-recording", + "sources": ["oura"] + }, + { + "key": "withings-activity-intraday-recording", + "status": "implemented-recording", + "provider": "withings", + "sourceType": "activityIntraday", + "profile": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document", + "sourceNeutralProfile": "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", + "outputDiscriminator": "native-recording", + "sources": ["withings"] + }, + { + "key": "withings-sleep-intraday-recording", + "status": "implemented-recording", + "provider": "withings", + "sourceType": "sleepIntraday", + "profile": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document", + "sourceNeutralProfile": "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", + "outputDiscriminator": "native-recording", + "sources": ["withings"] + }, + { + "key": "running-speed", + "status": "planned-shared", + "sources": ["healthkit", "health-connect", "google-health-api"] + }, + { + "key": "hydration", + "status": "planned-shared", + "sources": ["healthkit", "health-connect", "google-health-api"] + }, + { + "key": "nutrition", + "status": "planned-shared", + "sources": ["healthkit", "health-connect", "google-health-api"] + }, + { + "key": "wheelchair-pushes", + "status": "planned-shared", + "sources": ["healthkit", "health-connect"] + }, + { + "key": "cardiovascular-age", + "status": "adapter-specific", + "sources": ["oura", "withings"], + "reason": "Provider algorithms are not semantically interchangeable without an explicit method." + }, + { + "key": "readiness-score", + "status": "adapter-specific", + "sources": ["oura"], + "reason": "Oura-specific computed score." + }, + { + "key": "electrocardiogram-waveform", + "status": "sensor-ig", + "sources": ["healthkit", "connected-device"] + }, + { + "key": "photoplethysmogram-waveform", + "status": "sensor-ig", + "sources": ["connected-device"] + }, + { + "key": "accelerometer-series", + "status": "sensor-ig", + "sources": ["sensorkit", "connected-device"] + }, + { + "key": "reproductive-health-record", + "status": "not-in-scope", + "sources": ["healthkit", "health-connect"], + "reason": "These clinical event records require a separate reviewed domain contract." + }, + { + "key": "sexual-activity-record", + "status": "not-in-scope", + "sources": ["healthkit", "health-connect"], + "reason": "These event records are not treated as generic mobile measurements." + }, + { + "key": "waist-circumference", + "status": "platform-exclusive", + "profile": "healthkit-waist-circumference", + "code": { + "system": "http://loinc.org", + "code": "8280-0", + "display": "Waist Circumference at umbilicus by Tape measure" + }, + "unit": { + "code": "cm", + "display": "cm" + }, + "effective": "dateTime", + "owner": "healthkit", + "sources": ["healthkit"] + }, + { + "key": "total-energy", + "status": "platform-exclusive", + "profile": "health-connect-total-energy", + "code": { + "system": "https://grovealliance.org/fhir/health-connect/CodeSystem/health-connect-measurement", + "code": "total-energy-burned", + "display": "Total energy burned" + }, + "unit": { + "code": "kcal", + "display": "kcal" + }, + "effective": "Period", + "owner": "health-connect", + "sources": ["health-connect"] + }, + { + "key": "elevation-gained", + "status": "platform-exclusive", + "profile": "health-connect-elevation-gained", + "code": { + "system": "https://grovealliance.org/fhir/health-connect/CodeSystem/health-connect-measurement", + "code": "elevation-gained", + "display": "Elevation gained" + }, + "unit": { + "code": "m", + "display": "m" + }, + "effective": "Period", + "owner": "health-connect", + "sources": ["health-connect"] + }, + { + "key": "walking-speed", + "status": "platform-exclusive", + "profile": "healthkit-walking-speed", + "code": { + "system": "https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement", + "code": "walking-speed", + "display": "Walking speed" + }, + "unit": { + "code": "m/s", + "display": "m/s" + }, + "effective": "dateTime", + "owner": "healthkit", + "sources": ["healthkit"] + }, + { + "key": "step-cadence", + "status": "platform-exclusive", + "profile": "health-connect-step-cadence", + "code": { + "system": "https://grovealliance.org/fhir/health-connect/CodeSystem/health-connect-measurement", + "code": "step-cadence", + "display": "Step cadence" + }, + "unit": { + "code": "{steps}/min", + "display": "steps/minute" + }, + "effective": "dateTime", + "owner": "health-connect", + "sources": ["health-connect"] + }, + { + "key": "six-minute-walk-test-distance", + "status": "platform-exclusive", + "profile": "healthkit-six-minute-walk-test-distance", + "code": { + "system": "http://loinc.org", + "code": "64098-7", + "display": "Six minute walk test" + }, + "unit": { + "code": "m", + "display": "m" + }, + "effective": "Period", + "owner": "healthkit", + "sources": ["healthkit"] + }, + { + "key": "apple-stand-hour", + "status": "platform-exclusive", + "profile": "healthkit-apple-stand-hour", + "code": { + "system": "https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement", + "code": "apple-stand-hour", + "display": "Apple stand hour" + }, + "effective": "Period", + "owner": "healthkit", + "sources": ["healthkit"] + }, + { + "key": "apple-exercise-time", + "status": "platform-exclusive", + "profile": "healthkit-apple-exercise-time", + "code": { + "system": "https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement", + "code": "apple-exercise-time", + "display": "Apple exercise time" + }, + "unit": { + "code": "min", + "display": "min" + }, + "effective": "Period", + "owner": "healthkit", + "sources": ["healthkit"] + }, + { + "key": "body-fat-mass", + "status": "platform-exclusive", + "profile": "provider-body-fat-mass", + "code": { + "system": "http://loinc.org", + "code": "73708-0", + "display": "Body fat [Mass] Calculated" + }, + "unit": { + "code": "kg", + "display": "kg" + }, + "effective": "dateTime", + "owner": "providers", + "sources": ["withings"] + }, + { + "key": "extracellular-water-mass", + "status": "platform-exclusive", + "profile": "provider-extracellular-water-mass", + "code": { + "system": "https://grovealliance.org/fhir/providers/CodeSystem/provider-measurement", + "code": "extracellular-water-mass", + "display": "Extracellular water mass" + }, + "unit": { + "code": "kg", + "display": "kg" + }, + "effective": "dateTime", + "owner": "providers", + "sources": ["withings"] + }, + { + "key": "intracellular-water-mass", + "status": "platform-exclusive", + "profile": "provider-intracellular-water-mass", + "code": { + "system": "https://grovealliance.org/fhir/providers/CodeSystem/provider-measurement", + "code": "intracellular-water-mass", + "display": "Intracellular water mass" + }, + "unit": { + "code": "kg", + "display": "kg" + }, + "effective": "dateTime", + "owner": "providers", + "sources": ["withings"] + }, + { + "key": "muscle-mass", + "status": "platform-exclusive", + "profile": "provider-muscle-mass", + "code": { + "system": "http://loinc.org", + "code": "73964-9", + "display": "Body muscle mass Calculated" + }, + "unit": { + "code": "kg", + "display": "kg" + }, + "effective": "dateTime", + "owner": "providers", + "sources": ["withings"] + }, + { + "key": "sleeping-heart-rate-average", + "status": "platform-exclusive", + "profile": "provider-sleeping-heart-rate-average", + "code": { + "system": "https://grovealliance.org/fhir/providers/CodeSystem/provider-measurement", + "code": "sleeping-heart-rate-average", + "display": "Sleeping heart rate average" + }, + "unit": { + "code": "/min", + "display": "beats/minute" + }, + "effective": "Period", + "owner": "providers", + "sources": ["withings"] + } + ] +} diff --git a/packages/fhir/test/resources/codeSystems.json.license b/packages/fhir/catalog/measurement-capabilities.json.license similarity index 100% rename from packages/fhir/test/resources/codeSystems.json.license rename to packages/fhir/catalog/measurement-capabilities.json.license diff --git a/packages/fhir/eslint.config.mjs b/packages/fhir/eslint.config.mjs index 43ec1bdb..8a617957 100644 --- a/packages/fhir/eslint.config.mjs +++ b/packages/fhir/eslint.config.mjs @@ -18,60 +18,19 @@ export default [ }, }, { - files: [ - 'src/resources/allergyIntolerance.ts', - 'src/resources/condition.ts', - ], + files: ['src/extract/*.ts', 'test/extract*.test.ts'], rules: { - // These values are canonical FHIR system identifiers, not network requests. + // These are canonical FHIR system identifiers, which are `http` URIs by definition rather + // than endpoints anything requests. 'sonarjs/no-clear-text-protocols': 'off', }, }, { - files: ['src/elements/dataTypes/primitiveTypes.ts'], + files: ['scripts/generate-conformance-fixtures.mjs'], rules: { - // These expressions reproduce the normative FHIR lexical formats. - 'sonarjs/concise-regex': 'off', - 'sonarjs/regex-complexity': 'off', - 'sonarjs/single-character-alternation': 'off', - 'sonarjs/slow-regex': 'off', - }, - }, - { - files: [ - 'test/resources/additionalResourceMethods.test.ts', - 'test/resources/bundleFindResourcesWith.test.ts', - 'test/resources/bundleWithNewResources.test.ts', - 'test/resources/patientMethods.test.ts', - ], - rules: { - // These fixtures exercise canonical FHIR HTTP identifiers. - 'sonarjs/no-clear-text-protocols': 'off', - }, - }, - { - files: ['test/resources/testHelpers.ts'], - rules: { - // Resource ordering is deterministic; locale-aware ordering would vary by runner. - 'sonarjs/no-alphabetical-sort': 'off', - }, - }, - { - files: [ - 'test/resources/domainResourceClass.test.ts', - 'test/resources/observationEffective.test.ts', - ], - rules: { - // These conformance cases have distinct assertions despite similar setup. - 'sonarjs/parameterized-tests': 'off', - }, - }, - { - files: ['test/resources/**/*.ts'], - rules: { - // JSON fixtures are untyped input by design; schemas perform the runtime validation. - '@typescript-eslint/no-unsafe-argument': 'off', - '@typescript-eslint/no-unsafe-assignment': 'off', + // The generator validates its output against the package's own built schemas, which lint runs + // before `build` produces. + 'import-x/no-unresolved': 'off', }, }, ] diff --git a/packages/fhir/fixtures/conformance/manifest.json b/packages/fhir/fixtures/conformance/manifest.json new file mode 100644 index 00000000..6027108b --- /dev/null +++ b/packages/fhir/fixtures/conformance/manifest.json @@ -0,0 +1,414 @@ +{ + "schemaVersion": 1, + "fhirVersion": "4.0.1", + "producer": { "name": "Grove TypeScript", "version": "0.0.0" }, + "packages": [ + { + "alias": "mobile", + "packageId": "org.grovealliance.fhir.mobile", + "version": "0.5.0" + }, + { + "alias": "providers", + "packageId": "org.grovealliance.fhir.providers", + "version": "0.5.0" + }, + { + "alias": "sensor", + "packageId": "org.grovealliance.fhir.sensor", + "version": "0.5.0" + }, + { + "alias": "questionnaire", + "packageId": "org.grovealliance.fhir.questionnaire", + "version": "0.5.0" + } + ], + "resources": [ + { + "path": "resources/mobile-active-energy.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-basal-energy.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-blood-glucose-unspecified-specimen.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-blood-pressure.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-body-fat-percentage.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-body-height.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-body-temperature.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-body-water-mass.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-body-weight.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-bone-mass.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-deep-sleep-duration.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-distance.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-electrodermal-activity.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-flights-climbed.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-heart-rate.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-heart-rate-variability-rmssd.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-heart-rate-variability-sdnn.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-lean-body-mass.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-light-sleep-duration.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-oxygen-saturation.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-oxygen-saturation-daily-average.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-rem-sleep-duration.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-respiratory-rate-average.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-resting-heart-rate.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-skin-temperature.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-sleep-awake-duration.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-sleep-duration.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-sleep-heart-rate.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-step-count.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-vo2-max.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/mobile-workout.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-oura-daily-activity.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/recording-google-health-api-heart-rate.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/recording-oura-heartrate.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/recording-withings-activityintraday.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/recording-withings-sleepintraday.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/questionnaire.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire" + ] + }, + { + "path": "resources/questionnaire-response.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + } + ], + "semanticVectors": [ + { + "id": "active-energy", + "path": "resources/mobile-active-energy.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "basal-energy", + "path": "resources/mobile-basal-energy.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "blood-glucose-unspecified-specimen", + "path": "resources/mobile-blood-glucose-unspecified-specimen.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "blood-pressure", + "path": "resources/mobile-blood-pressure.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "body-fat-percentage", + "path": "resources/mobile-body-fat-percentage.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "body-height", + "path": "resources/mobile-body-height.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "body-temperature", + "path": "resources/mobile-body-temperature.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "body-water-mass", + "path": "resources/mobile-body-water-mass.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "body-weight", + "path": "resources/mobile-body-weight.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "bone-mass", + "path": "resources/mobile-bone-mass.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "deep-sleep-duration", + "path": "resources/mobile-deep-sleep-duration.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "distance", + "path": "resources/mobile-distance.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "electrodermal-activity", + "path": "resources/mobile-electrodermal-activity.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "flights-climbed", + "path": "resources/mobile-flights-climbed.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "heart-rate", + "path": "resources/mobile-heart-rate.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "heart-rate-variability-rmssd", + "path": "resources/mobile-heart-rate-variability-rmssd.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "heart-rate-variability-sdnn", + "path": "resources/mobile-heart-rate-variability-sdnn.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "lean-body-mass", + "path": "resources/mobile-lean-body-mass.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "light-sleep-duration", + "path": "resources/mobile-light-sleep-duration.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "oxygen-saturation", + "path": "resources/mobile-oxygen-saturation.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "oxygen-saturation-daily-average", + "path": "resources/mobile-oxygen-saturation-daily-average.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "rem-sleep-duration", + "path": "resources/mobile-rem-sleep-duration.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "respiratory-rate-average", + "path": "resources/mobile-respiratory-rate-average.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "resting-heart-rate", + "path": "resources/mobile-resting-heart-rate.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "skin-temperature", + "path": "resources/mobile-skin-temperature.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "sleep-awake-duration", + "path": "resources/mobile-sleep-awake-duration.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "sleep-duration", + "path": "resources/mobile-sleep-duration.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "sleep-heart-rate", + "path": "resources/mobile-sleep-heart-rate.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "step-count", + "path": "resources/mobile-step-count.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "vo2-max", + "path": "resources/mobile-vo2-max.json", + "resourcePointer": "/entry/0/resource" + }, + { + "id": "workout", + "path": "resources/mobile-workout.json", + "resourcePointer": "/entry/0/resource" + } + ] +} diff --git a/packages/fhir/test/resources/conceptMaps.json.license b/packages/fhir/fixtures/conformance/manifest.json.license similarity index 100% rename from packages/fhir/test/resources/conceptMaps.json.license rename to packages/fhir/fixtures/conformance/manifest.json.license diff --git a/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json b/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json new file mode 100644 index 00000000..4f241454 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json @@ -0,0 +1,252 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|1|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|active-energy-burned|source-active-energy" + } + } + ], + "fullUrl": "urn:uuid:4fa8c437-0d58-5efd-b9da-a69f1b0e4ac3", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-active-energy", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/active-energy-burned" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|active-energy-burned|source-active-energy" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "active-energy-burned" + } + ] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-20T08:00:00.000-07:00", + "end": "2026-08-20T09:00:00.000-07:00" + }, + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 312.5, + "unit": "kcal", + "system": "http://unitsofmeasure.org", + "code": "kcal" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|1|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:31ec9e26-8e34-5117-b7d5-3caf0dc60afc", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:4fa8c437-0d58-5efd-b9da-a69f1b0e4ac3" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|active-energy-burned|source-active-energy" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/test/resources/namingSystems.json.license b/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json.license similarity index 100% rename from packages/fhir/test/resources/namingSystems.json.license rename to packages/fhir/fixtures/conformance/resources/mobile-active-energy.json.license diff --git a/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json b/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json new file mode 100644 index 00000000..c753f9d1 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json @@ -0,0 +1,252 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|2|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|basal-energy-burned|source-basal-energy" + } + } + ], + "fullUrl": "urn:uuid:6ec71145-b100-5578-8823-2ef0091d0c15", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-basal-energy", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/basal-energy-burned" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|basal-energy-burned|source-basal-energy" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "basal-energy-burned" + } + ] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-20T08:00:00.000-07:00", + "end": "2026-08-20T09:00:00.000-07:00" + }, + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 68.4, + "unit": "kcal", + "system": "http://unitsofmeasure.org", + "code": "kcal" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|2|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:a5c908b9-295f-5609-8720-4a413f3212c6", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:6ec71145-b100-5578-8823-2ef0091d0c15" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|basal-energy-burned|source-basal-energy" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/test/resources/nutritionOrders.json.license b/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json.license similarity index 100% rename from packages/fhir/test/resources/nutritionOrders.json.license rename to packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json.license diff --git a/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json b/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json new file mode 100644 index 00000000..6f92a0a2 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json @@ -0,0 +1,244 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|3|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|blood-glucose|source-blood-glucose-unspecified-specimen" + } + } + ], + "fullUrl": "urn:uuid:d22a19d8-ac4e-5b01-a3d8-2e30a4fd4072", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-blood-glucose-unspecified-specimen", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/blood-glucose" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|blood-glucose|source-blood-glucose-unspecified-specimen" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "2339-0" }] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-20T07:45:00.120-07:00", + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 104, + "unit": "mg/dL", + "system": "http://unitsofmeasure.org", + "code": "mg/dL" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|3|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:3fdb1234-47b7-5454-9afb-3c7e88f44bfe", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:d22a19d8-ac4e-5b01-a3d8-2e30a4fd4072" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|blood-glucose|source-blood-glucose-unspecified-specimen" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/test/resources/nutritionProducts.json.license b/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json.license similarity index 100% rename from packages/fhir/test/resources/nutritionProducts.json.license rename to packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json.license diff --git a/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json b/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json new file mode 100644 index 00000000..3d026296 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json @@ -0,0 +1,262 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|4|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:9+10|source-blood-pressure" + } + } + ], + "fullUrl": "urn:uuid:1f8fa372-3649-5278-adb4-8ca22fe44df2", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-blood-pressure", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:9+10" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:9+10|source-blood-pressure" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "vital-signs", + "display": "Vital Signs" + } + ], + "text": "Vital Signs" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "85354-9" }] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-20T08:10:00.000-07:00", + "issued": "2026-08-20T12:01:00Z", + "component": [ + { + "code": { + "coding": [{ "system": "http://loinc.org", "code": "8480-6" }] + }, + "valueQuantity": { + "value": 118, + "unit": "mmHg", + "system": "http://unitsofmeasure.org", + "code": "mm[Hg]" + } + }, + { + "code": { + "coding": [{ "system": "http://loinc.org", "code": "8462-4" }] + }, + "valueQuantity": { + "value": 76, + "unit": "mmHg", + "system": "http://unitsofmeasure.org", + "code": "mm[Hg]" + } + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|4|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:d09ac50b-9a38-523a-aff4-b5ca6491beee", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:1f8fa372-3649-5278-adb4-8ca22fe44df2" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:9+10|source-blood-pressure" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/test/resources/terminologyCapabilities.json.license b/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json.license similarity index 100% rename from packages/fhir/test/resources/terminologyCapabilities.json.license rename to packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json.license diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json b/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json new file mode 100644 index 00000000..57b5e0eb --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json @@ -0,0 +1,244 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|5|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:6|source-body-fat-percentage" + } + } + ], + "fullUrl": "urn:uuid:b9e2bc87-bd00-5e44-9cc8-a0248ee1aa6c", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-fat-percentage", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:6" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:6|source-body-fat-percentage" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "41982-0" }] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-21T07:12:04.000-07:00", + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 23.5, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|5|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:ac2fbd76-4c3e-5a05-bcad-4270e25678fd", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:b9e2bc87-bd00-5e44-9cc8-a0248ee1aa6c" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:6|source-body-fat-percentage" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/test/resources/valueSets.json.license b/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json.license similarity index 100% rename from packages/fhir/test/resources/valueSets.json.license rename to packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json.license diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-height.json b/packages/fhir/fixtures/conformance/resources/mobile-body-height.json new file mode 100644 index 00000000..0aa95f5e --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-height.json @@ -0,0 +1,244 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|6|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:4|source-body-height" + } + } + ], + "fullUrl": "urn:uuid:f6f3a537-b5ff-50a1-b657-b4e968a96adf", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-height", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:4" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:4|source-body-height" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "vital-signs", + "display": "Vital Signs" + } + ], + "text": "Vital Signs" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "8302-2" }] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-20T08:15:00.000-07:00", + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 171.2, + "unit": "cm", + "system": "http://unitsofmeasure.org", + "code": "cm" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|6|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:39b72318-032b-5b02-b66d-629f24b9c48c", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:f6f3a537-b5ff-50a1-b657-b4e968a96adf" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:4|source-body-height" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/test/resources/evidenceReports.json.license b/packages/fhir/fixtures/conformance/resources/mobile-body-height.json.license similarity index 93% rename from packages/fhir/test/resources/evidenceReports.json.license rename to packages/fhir/fixtures/conformance/resources/mobile-body-height.json.license index 3e8d46d1..fa4e8d92 100644 --- a/packages/fhir/test/resources/evidenceReports.json.license +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-height.json.license @@ -1,5 +1,3 @@ - diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json b/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json new file mode 100644 index 00000000..3fdbf90e --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json @@ -0,0 +1,244 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|7|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|core-body-temperature|source-body-temperature" + } + } + ], + "fullUrl": "urn:uuid:e26a6d6a-e280-536b-b7e0-87776416a1bd", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-temperature", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/core-body-temperature" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|core-body-temperature|source-body-temperature" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "vital-signs", + "display": "Vital Signs" + } + ], + "text": "Vital Signs" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "8310-5" }] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-20T08:20:00.000-07:00", + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 37.1, + "unit": "Cel", + "system": "http://unitsofmeasure.org", + "code": "Cel" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|7|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:ae1ec112-8951-516a-a166-069e5b8ea3a9", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:e26a6d6a-e280-536b-b7e0-87776416a1bd" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|core-body-temperature|source-body-temperature" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/test/resources/evidenceVariables.json.license b/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json.license similarity index 93% rename from packages/fhir/test/resources/evidenceVariables.json.license rename to packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json.license index 3e8d46d1..fa4e8d92 100644 --- a/packages/fhir/test/resources/evidenceVariables.json.license +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json.license @@ -1,5 +1,3 @@ - diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json b/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json new file mode 100644 index 00000000..cdf385d7 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json @@ -0,0 +1,244 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|8|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:77|source-body-water-mass" + } + } + ], + "fullUrl": "urn:uuid:e6a0b057-eddb-5412-a920-2eab118ef502", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-water-mass", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:77" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:77|source-body-water-mass" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "101683-1" }] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-21T07:12:04.000-07:00", + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 38.2, + "unit": "kg", + "system": "http://unitsofmeasure.org", + "code": "kg" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|8|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:e450870a-7a09-513a-ae56-8e4c93b6e867", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:e6a0b057-eddb-5412-a920-2eab118ef502" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:77|source-body-water-mass" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/test/resources/citations.json.license b/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json.license similarity index 93% rename from packages/fhir/test/resources/citations.json.license rename to packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json.license index 3e8d46d1..fa4e8d92 100644 --- a/packages/fhir/test/resources/citations.json.license +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json.license @@ -1,5 +1,3 @@ - diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json b/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json new file mode 100644 index 00000000..3727ae93 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json @@ -0,0 +1,244 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|9|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:1|source-body-weight" + } + } + ], + "fullUrl": "urn:uuid:0ed69292-8608-512b-8f9c-322acca73207", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-weight", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:1" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:1|source-body-weight" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "vital-signs", + "display": "Vital Signs" + } + ], + "text": "Vital Signs" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "29463-7" }] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-20T08:25:00.000-07:00", + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 68.4, + "unit": "kg", + "system": "http://unitsofmeasure.org", + "code": "kg" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|9|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:3ca982c7-7eb0-5b00-ba98-1721fa019d72", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:0ed69292-8608-512b-8f9c-322acca73207" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:1|source-body-weight" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/test/resources/evidences.json.license b/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json.license similarity index 93% rename from packages/fhir/test/resources/evidences.json.license rename to packages/fhir/fixtures/conformance/resources/mobile-body-weight.json.license index 3e8d46d1..fa4e8d92 100644 --- a/packages/fhir/test/resources/evidences.json.license +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json.license @@ -1,5 +1,3 @@ - diff --git a/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json b/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json new file mode 100644 index 00000000..e740ab31 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json @@ -0,0 +1,244 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|10|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:88|source-bone-mass" + } + } + ], + "fullUrl": "urn:uuid:f1ac51fd-7ee2-5e4a-85b7-2e290d709def", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-bone-mass", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:88" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:88|source-bone-mass" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "101685-6" }] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-21T07:12:04.000-07:00", + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 3.1, + "unit": "kg", + "system": "http://unitsofmeasure.org", + "code": "kg" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|10|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:660769ce-8d69-50fe-a69c-cb23b89f9482", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:f1ac51fd-7ee2-5e4a-85b7-2e290d709def" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:88|source-bone-mass" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json.license b/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json new file mode 100644 index 00000000..7057ed51 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json @@ -0,0 +1,257 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "oura|11|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-deep-sleep-duration" + } + } + ], + "fullUrl": "urn:uuid:3f88044c-89be-5cee-916e-1fcd1e737688", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-deep-sleep-duration", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "oura" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "oura/sleep" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-deep-sleep-duration" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "93831-6" }] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-19T22:40:00.000-07:00", + "end": "2026-08-20T06:55:00.000-07:00" + }, + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 92, + "unit": "min", + "system": "http://unitsofmeasure.org", + "code": "min" + }, + "method": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-aggregation-method", + "code": "session-total", + "display": "Session total" + } + ], + "text": "Session total" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "oura|11|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:b3e9d1c8-59f7-5e95-a4bd-78850fef03af", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:3f88044c-89be-5cee-916e-1fcd1e737688" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-deep-sleep-duration" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json.license b/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-distance.json b/packages/fhir/fixtures/conformance/resources/mobile-distance.json new file mode 100644 index 00000000..ec8fbf41 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-distance.json @@ -0,0 +1,247 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|12|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|distance|source-distance" + } + } + ], + "fullUrl": "urn:uuid:a13fa60c-3127-59cf-9b97-a5450c67010a", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-distance", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/distance" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|distance|source-distance" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "103208-5" }] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-20T07:00:00.000-07:00", + "end": "2026-08-20T07:30:00.000-07:00" + }, + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 4820.5, + "unit": "m", + "system": "http://unitsofmeasure.org", + "code": "m" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|12|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:c8a6fefb-2618-5988-aa79-2c39298a732b", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:a13fa60c-3127-59cf-9b97-a5450c67010a" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|distance|source-distance" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-distance.json.license b/packages/fhir/fixtures/conformance/resources/mobile-distance.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-distance.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json b/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json new file mode 100644 index 00000000..cd513e99 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json @@ -0,0 +1,249 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|13|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:196|source-electrodermal-activity" + } + } + ], + "fullUrl": "urn:uuid:6fc9d8dd-20dc-574b-a091-d5d6d87504ea", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-electrodermal-activity", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:196" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:196|source-electrodermal-activity" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "electrodermal-activity" + } + ] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-20T10:02:30.000-07:00", + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 2.4, + "unit": "uS", + "system": "http://unitsofmeasure.org", + "code": "uS" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|13|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:9fd254a2-55b3-5c33-ae0a-bc2f9143d38d", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:6fc9d8dd-20dc-574b-a091-d5d6d87504ea" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:196|source-electrodermal-activity" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json.license b/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json b/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json new file mode 100644 index 00000000..089bbbee --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json @@ -0,0 +1,247 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|14|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|floors|source-flights-climbed" + } + } + ], + "fullUrl": "urn:uuid:e038a46f-4731-5083-9688-9d42886b755b", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-flights-climbed", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/floors" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|floors|source-flights-climbed" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "100304-5" }] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-20T08:00:00.000-07:00", + "end": "2026-08-20T09:00:00.000-07:00" + }, + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 6, + "unit": "flights", + "system": "http://unitsofmeasure.org", + "code": "{flights}" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|14|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:6a7a199b-318d-5d03-85cb-2f1ef8e7a76e", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:e038a46f-4731-5083-9688-9d42886b755b" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|floors|source-flights-climbed" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json.license b/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json new file mode 100644 index 00000000..a5d7cf86 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json @@ -0,0 +1,249 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|16|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|heart-rate-variability|source-heart-rate-variability-rmssd" + } + } + ], + "fullUrl": "urn:uuid:64bdd3cb-444f-5795-aa69-646b868dc687", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate-variability-rmssd", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/heart-rate-variability" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|heart-rate-variability|source-heart-rate-variability-rmssd" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "heart-rate-variability-rmssd" + } + ] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-20T06:58:12.500-07:00", + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 38, + "unit": "ms", + "system": "http://unitsofmeasure.org", + "code": "ms" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|16|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:7466a142-fd83-5310-97e9-762516b22cd1", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:64bdd3cb-444f-5795-aa69-646b868dc687" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|heart-rate-variability|source-heart-rate-variability-rmssd" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json.license b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json new file mode 100644 index 00000000..f6a49b1b --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json @@ -0,0 +1,244 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|17|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|heart-rate-variability|source-heart-rate-variability-sdnn" + } + } + ], + "fullUrl": "urn:uuid:c3c2cb69-302f-527d-83bd-28b86cf4b78a", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate-variability-sdnn", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/heart-rate-variability" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|heart-rate-variability|source-heart-rate-variability-sdnn" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "112429-6" }] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-20T03:12:44.000-07:00", + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 54, + "unit": "ms", + "system": "http://unitsofmeasure.org", + "code": "ms" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|17|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:3faae20d-209d-5ab8-a149-480c4763567f", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:c3c2cb69-302f-527d-83bd-28b86cf4b78a" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|heart-rate-variability|source-heart-rate-variability-sdnn" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json.license b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json new file mode 100644 index 00000000..d3046262 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json @@ -0,0 +1,244 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|15|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:11|source-heart-rate" + } + } + ], + "fullUrl": "urn:uuid:f7304f6c-beb8-53aa-9d35-555432f7c9f6", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:11" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:11|source-heart-rate" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "vital-signs", + "display": "Vital Signs" + } + ], + "text": "Vital Signs" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "8867-4" }] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 72, + "unit": "beats/minute", + "system": "http://unitsofmeasure.org", + "code": "/min" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|15|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:d231c7e8-0b4b-58a1-a51b-da727a2b1361", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:f7304f6c-beb8-53aa-9d35-555432f7c9f6" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:11|source-heart-rate" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json.license b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json b/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json new file mode 100644 index 00000000..372841b4 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json @@ -0,0 +1,244 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|18|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:5|source-lean-body-mass" + } + } + ], + "fullUrl": "urn:uuid:7077f001-9cec-5e08-b3c6-a7ba5096025d", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-lean-body-mass", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:5" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:5|source-lean-body-mass" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "91557-9" }] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-21T07:12:04.000-07:00", + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 55.3, + "unit": "kg", + "system": "http://unitsofmeasure.org", + "code": "kg" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|18|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:28c4d1cd-eef9-51fe-a87c-2c7f67f80a27", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:7077f001-9cec-5e08-b3c6-a7ba5096025d" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:5|source-lean-body-mass" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json.license b/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json new file mode 100644 index 00000000..09932528 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json @@ -0,0 +1,257 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|19|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|sleep|source-light-sleep-duration" + } + } + ], + "fullUrl": "urn:uuid:a8efa58e-ebb6-5388-8969-b24a60edd198", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-light-sleep-duration", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/sleep" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|sleep|source-light-sleep-duration" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "93830-8" }] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-19T22:40:00.000-07:00", + "end": "2026-08-20T06:55:00.000-07:00" + }, + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 214, + "unit": "min", + "system": "http://unitsofmeasure.org", + "code": "min" + }, + "method": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-aggregation-method", + "code": "session-total", + "display": "Session total" + } + ], + "text": "Session total" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|19|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:68a25a2a-0ae8-5806-8fba-c0edefe00b03", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:a8efa58e-ebb6-5388-8969-b24a60edd198" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|sleep|source-light-sleep-duration" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json.license b/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json new file mode 100644 index 00000000..03f56643 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json @@ -0,0 +1,257 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|21|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|daily-oxygen-saturation|source-oxygen-saturation-daily-average" + } + } + ], + "fullUrl": "urn:uuid:88490f63-9e39-5af8-a8d0-447fbeaade5e", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-oxygen-saturation-daily-average", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/daily-oxygen-saturation" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|daily-oxygen-saturation|source-oxygen-saturation-daily-average" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "103209-3" }] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-19T22:40:00.000-07:00", + "end": "2026-08-20T06:55:00.000-07:00" + }, + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 96.5, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + }, + "method": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-aggregation-method", + "code": "daily-mean", + "display": "Daily mean" + } + ], + "text": "Daily mean" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|21|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:9530191a-d2b8-5c01-b751-9ddf0fb03ad1", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:88490f63-9e39-5af8-a8d0-447fbeaade5e" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|daily-oxygen-saturation|source-oxygen-saturation-daily-average" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json.license b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json new file mode 100644 index 00000000..98318da5 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json @@ -0,0 +1,244 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|20|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:54|source-oxygen-saturation" + } + } + ], + "fullUrl": "urn:uuid:0b637261-2d92-53f5-b4df-3f27b4a0fb6b", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-oxygen-saturation", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:54" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:54|source-oxygen-saturation" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "vital-signs", + "display": "Vital Signs" + } + ], + "text": "Vital Signs" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "2708-6" }] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-20T08:35:00.000-07:00", + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 98, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|20|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:eecf4b79-25b7-54c6-9338-4a9daf24bd6f", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:0b637261-2d92-53f5-b4df-3f27b4a0fb6b" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:54|source-oxygen-saturation" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json.license b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json new file mode 100644 index 00000000..b4e9a271 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json @@ -0,0 +1,257 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|22|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|sleep|source-rem-sleep-duration" + } + } + ], + "fullUrl": "urn:uuid:d8ad0e63-29d8-5c65-aa7a-41e7cd9a63ac", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-rem-sleep-duration", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/sleep" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|sleep|source-rem-sleep-duration" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "93829-0" }] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-19T22:40:00.000-07:00", + "end": "2026-08-20T06:55:00.000-07:00" + }, + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 104, + "unit": "min", + "system": "http://unitsofmeasure.org", + "code": "min" + }, + "method": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-aggregation-method", + "code": "session-total", + "display": "Session total" + } + ], + "text": "Session total" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|22|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:51dbdcfc-a144-58dd-a2e9-90f331ad6b39", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:d8ad0e63-29d8-5c65-aa7a-41e7cd9a63ac" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|sleep|source-rem-sleep-duration" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json.license b/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json b/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json new file mode 100644 index 00000000..5bae7c6e --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json @@ -0,0 +1,255 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "oura|23|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-respiratory-rate-average" + } + } + ], + "fullUrl": "urn:uuid:c758f721-c73b-51f5-b81d-eadafad45a58", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-respiratory-rate-average", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "oura" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "oura/sleep" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-respiratory-rate-average" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "103217-6" }] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-19T22:40:00.000-07:00", + "end": "2026-08-20T06:55:00.000-07:00" + }, + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 14.8, + "unit": "breaths/minute", + "system": "http://unitsofmeasure.org", + "code": "/min" + }, + "method": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-aggregation-method", + "code": "session-mean" + } + ] + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "oura|23|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:71f210b4-e20d-5f50-92c5-ee2a6e0eb06f", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:c758f721-c73b-51f5-b81d-eadafad45a58" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-respiratory-rate-average" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json.license b/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json b/packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json new file mode 100644 index 00000000..e68baf64 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json @@ -0,0 +1,262 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|24|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|daily-resting-heart-rate|source-resting-heart-rate" + } + } + ], + "fullUrl": "urn:uuid:96ef5a1e-7a8f-5bdf-b120-113f1b0a5ef5", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-resting-heart-rate", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/daily-resting-heart-rate" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|daily-resting-heart-rate|source-resting-heart-rate" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "resting-heart-rate" + } + ] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-19T22:40:00.000-07:00", + "end": "2026-08-20T06:55:00.000-07:00" + }, + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 58, + "unit": "beats/minute", + "system": "http://unitsofmeasure.org", + "code": "/min" + }, + "method": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-aggregation-method", + "code": "daily-mean", + "display": "Daily mean" + } + ], + "text": "Daily mean" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|24|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:f146dbac-bc58-5e09-b014-b0599dfc6a4b", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:96ef5a1e-7a8f-5bdf-b120-113f1b0a5ef5" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|daily-resting-heart-rate|source-resting-heart-rate" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json.license b/packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json b/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json new file mode 100644 index 00000000..394f1f24 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json @@ -0,0 +1,244 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|25|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:73|source-skin-temperature" + } + } + ], + "fullUrl": "urn:uuid:d92d95f4-c8f7-5a2d-9ed2-b64c71e2d17e", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-skin-temperature", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:73" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:73|source-skin-temperature" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "61008-9" }] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-21T03:41:12.000-07:00", + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 34.9, + "unit": "Cel", + "system": "http://unitsofmeasure.org", + "code": "Cel" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|25|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:c74fdd10-b50a-548d-be24-cb20feb4e1ca", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:d92d95f4-c8f7-5a2d-9ed2-b64c71e2d17e" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:73|source-skin-temperature" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json.license b/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json new file mode 100644 index 00000000..eb4ab86a --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json @@ -0,0 +1,257 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "oura|26|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-sleep-awake-duration" + } + } + ], + "fullUrl": "urn:uuid:780fbb29-081f-5df2-a97a-24960267baae", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-sleep-awake-duration", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "oura" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "oura/sleep" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-sleep-awake-duration" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "93828-2" }] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-19T22:40:00.000-07:00", + "end": "2026-08-20T06:55:00.000-07:00" + }, + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 31, + "unit": "min", + "system": "http://unitsofmeasure.org", + "code": "min" + }, + "method": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-aggregation-method", + "code": "session-total", + "display": "Session total" + } + ], + "text": "Session total" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "oura|26|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:c1317ed0-fcdb-5cfc-aaec-f74fef1e80e6", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:780fbb29-081f-5df2-a97a-24960267baae" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-sleep-awake-duration" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json.license b/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json new file mode 100644 index 00000000..166d7a96 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json @@ -0,0 +1,247 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|27|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|sleep|source-sleep-duration" + } + } + ], + "fullUrl": "urn:uuid:2196ef69-d631-5ca9-b4f8-09c36888ddae", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-sleep-duration", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/sleep" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|sleep|source-sleep-duration" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "93832-4" }] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-19T23:00:00.000-07:00", + "end": "2026-08-20T06:30:00.000-07:00" + }, + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 7.5, + "unit": "h", + "system": "http://unitsofmeasure.org", + "code": "h" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|27|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:127ca2f2-f330-50e4-9834-8ad85f792dc2", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:2196ef69-d631-5ca9-b4f8-09c36888ddae" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|sleep|source-sleep-duration" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json.license b/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json b/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json new file mode 100644 index 00000000..165e3ce3 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json @@ -0,0 +1,260 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "oura|28|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-sleep-heart-rate" + } + } + ], + "fullUrl": "urn:uuid:d0725ae0-d782-5e01-8707-3297dccaa124", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-sleep-heart-rate", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "oura" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "oura/sleep" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-sleep-heart-rate" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "sleep-heart-rate" + } + ] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-19T22:40:00.000-07:00", + "end": "2026-08-20T06:55:00.000-07:00" + }, + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 52, + "unit": "beats/minute", + "system": "http://unitsofmeasure.org", + "code": "/min" + }, + "method": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-aggregation-method", + "code": "session-minimum" + } + ] + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "oura|28|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:aa110c46-e856-54b7-8b81-1929ca1696ce", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:d0725ae0-d782-5e01-8707-3297dccaa124" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-sleep-heart-rate" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json.license b/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-step-count.json b/packages/fhir/fixtures/conformance/resources/mobile-step-count.json new file mode 100644 index 00000000..df99a710 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-step-count.json @@ -0,0 +1,252 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|29|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|steps|source-step-count" + } + } + ], + "fullUrl": "urn:uuid:4c2aa01f-1bf4-5a25-9200-6a54f3b9d695", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-step-count", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/steps" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|steps|source-step-count" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "step-count-total" + } + ] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-20T08:00:00.000-07:00", + "end": "2026-08-20T09:00:00.000-07:00" + }, + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 1042, + "unit": "steps", + "system": "http://unitsofmeasure.org", + "code": "{steps}" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|29|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:e1c60032-30a8-56b0-baa1-8e8437a0dc1d", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:4c2aa01f-1bf4-5a25-9200-6a54f3b9d695" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|steps|source-step-count" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-step-count.json.license b/packages/fhir/fixtures/conformance/resources/mobile-step-count.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-step-count.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json b/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json new file mode 100644 index 00000000..ba8a46f3 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json @@ -0,0 +1,249 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|30|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:123|source-vo2-max" + } + } + ], + "fullUrl": "urn:uuid:015be0f5-1929-59b0-9ac6-8023f6af5079", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-vo2-max", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:123" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:123|source-vo2-max" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "vo2-max" + } + ] + }, + "subject": { "reference": "Patient/example" }, + "effectiveDateTime": "2026-08-20T09:15:00.000-07:00", + "issued": "2026-08-20T12:01:00Z", + "valueQuantity": { + "value": 41.2, + "unit": "mL/kg/min", + "system": "http://unitsofmeasure.org", + "code": "mL/kg/min" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|30|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:eada2e49-6c62-51d7-8027-046c4fc13408", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:015be0f5-1929-59b0-9ac6-8023f6af5079" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|getmeas:123|source-vo2-max" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json.license b/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-workout.json b/packages/fhir/fixtures/conformance/resources/mobile-workout.json new file mode 100644 index 00000000..87ffeefc --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-workout.json @@ -0,0 +1,254 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|31|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|exercise|source-workout" + } + } + ], + "fullUrl": "urn:uuid:f0c37ae9-349c-5bae-8a04-d53238902db3", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-workout", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/exercise" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|exercise|source-workout" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "workout" + } + ] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-20T17:00:00.000-07:00", + "end": "2026-08-20T17:45:00.000-07:00" + }, + "issued": "2026-08-20T12:01:00Z", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-activity", + "code": "running" + } + ] + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|31|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:d41f5be1-4cdc-5796-8613-45b96964cc2d", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:f0c37ae9-349c-5bae-8a04-d53238902db3" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|exercise|source-workout" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-workout.json.license b/packages/fhir/fixtures/conformance/resources/mobile-workout.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/mobile-workout.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json b/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json new file mode 100644 index 00000000..e47f515a --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json @@ -0,0 +1,415 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "oura|32|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-21T07:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", + "value": "v1:source-oura-daily-activity|step-count" + } + } + ], + "fullUrl": "urn:uuid:8847e52b-0da0-5755-a027-e33cc367c692", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-step-count", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "oura" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "oura/daily_activity" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-oura-daily-activity" + }, + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", + "value": "v1:source-oura-daily-activity|step-count" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "step-count-total" + } + ] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-20T00:00:00.000-07:00", + "end": "2026-08-21T00:00:00.000-07:00" + }, + "issued": "2026-08-21T07:01:00Z", + "valueQuantity": { + "value": 1042, + "unit": "steps", + "system": "http://unitsofmeasure.org", + "code": "{steps}" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", + "value": "v1:source-oura-daily-activity|active-energy" + } + } + ], + "fullUrl": "urn:uuid:c99ba8fc-e79e-5b15-bf68-bee5a5e5934b", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-active-energy", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "oura" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "oura/daily_activity" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-oura-daily-activity" + }, + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", + "value": "v1:source-oura-daily-activity|active-energy" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "active-energy-burned" + } + ] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-20T00:00:00.000-07:00", + "end": "2026-08-21T00:00:00.000-07:00" + }, + "issued": "2026-08-21T07:01:00Z", + "valueQuantity": { + "value": 312.5, + "unit": "kcal", + "system": "http://unitsofmeasure.org", + "code": "kcal" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", + "value": "v1:source-oura-daily-activity|distance" + } + } + ], + "fullUrl": "urn:uuid:0aa056e7-092f-5cc6-901d-86a9427c89e0", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-distance", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "oura" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "oura/daily_activity" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-oura-daily-activity" + }, + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", + "value": "v1:source-oura-daily-activity|distance" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "103208-5" }] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-20T00:00:00.000-07:00", + "end": "2026-08-21T00:00:00.000-07:00" + }, + "issued": "2026-08-21T07:01:00Z", + "valueQuantity": { + "value": 4820.5, + "unit": "m", + "system": "http://unitsofmeasure.org", + "code": "m" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "oura|32|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:8da79180-b7a2-5851-9af5-75e3b41a0d2f", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:8847e52b-0da0-5755-a027-e33cc367c692" }, + { "reference": "urn:uuid:c99ba8fc-e79e-5b15-bf68-bee5a5e5934b" }, + { "reference": "urn:uuid:0aa056e7-092f-5cc6-901d-86a9427c89e0" } + ], + "recorded": "2026-08-21T07:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "source-oura-daily-activity" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json.license b/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/providers-oura-daily-activity.json b/packages/fhir/fixtures/conformance/resources/providers-oura-daily-activity.json new file mode 100644 index 00000000..3c968702 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/providers-oura-daily-activity.json @@ -0,0 +1,415 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-exchange-id", + "value": "v1:60bb2b3ed72a0994b1b52c43534fa8cbfbfb44c88dabcbb409143a1cad7738e1" + }, + "type": "collection", + "timestamp": "2026-08-21T07:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", + "value": "v1:14fd42a645502a68c3c9b71d35118e0f7ffa0ba41c42277049caadcad4b72ec3" + } + } + ], + "fullUrl": "urn:uuid:3869cc0d-5ae0-5f2f-8cad-3733d3eb979a", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-step-count", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "oura" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "oura/daily_activity" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:7826f42abcd8a2cb292e4cafeb55b072adeae56b36f781265f2cb29f201641fa" + }, + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", + "value": "v1:14fd42a645502a68c3c9b71d35118e0f7ffa0ba41c42277049caadcad4b72ec3" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "step-count-total" + } + ] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-20T00:00:00.000-07:00", + "end": "2026-08-21T00:00:00.000-07:00" + }, + "issued": "2026-08-21T07:01:00Z", + "valueQuantity": { + "value": 1042, + "unit": "steps", + "system": "http://unitsofmeasure.org", + "code": "{steps}" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", + "value": "v1:2904c733b341853749925ef31880f8d374d001e19f29e996c7cbf82bdd9cc389" + } + } + ], + "fullUrl": "urn:uuid:c1692b07-4621-579e-b0b4-b672b34e0101", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-active-energy", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "oura" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "oura/daily_activity" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:7826f42abcd8a2cb292e4cafeb55b072adeae56b36f781265f2cb29f201641fa" + }, + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", + "value": "v1:2904c733b341853749925ef31880f8d374d001e19f29e996c7cbf82bdd9cc389" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", + "code": "active-energy-burned" + } + ] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-20T00:00:00.000-07:00", + "end": "2026-08-21T00:00:00.000-07:00" + }, + "issued": "2026-08-21T07:01:00Z", + "valueQuantity": { + "value": 312.5, + "unit": "kcal", + "system": "http://unitsofmeasure.org", + "code": "kcal" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", + "value": "v1:aeadc4dcb2cbd9685a2668e343c954b65569d1cb67cd80261debe7eed4fff0e0" + } + } + ], + "fullUrl": "urn:uuid:4c76c5cc-cc6e-583e-824b-82ba0b6a8e94", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-distance", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "oura" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "oura/daily_activity" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:7826f42abcd8a2cb292e4cafeb55b072adeae56b36f781265f2cb29f201641fa" + }, + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", + "value": "v1:aeadc4dcb2cbd9685a2668e343c954b65569d1cb67cd80261debe7eed4fff0e0" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "activity", + "display": "Activity" + } + ], + "text": "Activity" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "103208-5" }] + }, + "subject": { "reference": "Patient/example" }, + "effectivePeriod": { + "start": "2026-08-20T00:00:00.000-07:00", + "end": "2026-08-21T00:00:00.000-07:00" + }, + "issued": "2026-08-21T07:01:00Z", + "valueQuantity": { + "value": 4820.5, + "unit": "m", + "system": "http://unitsofmeasure.org", + "code": "m" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:7c06980e-c131-564a-9d90-26a23d9c1264", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:ed2738ad-fee1-53b8-976d-0fd5b9512aa8", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-conversion-id", + "value": "v1:60bb2b3ed72a0994b1b52c43534fa8cbfbfb44c88dabcbb409143a1cad7738e1" + } + } + ], + "fullUrl": "urn:uuid:4eccfa53-5bce-5842-9ce5-cccf46447031", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:3869cc0d-5ae0-5f2f-8cad-3733d3eb979a" }, + { "reference": "urn:uuid:c1692b07-4621-579e-b0b4-b672b34e0101" }, + { "reference": "urn:uuid:4c76c5cc-cc6e-583e-824b-82ba0b6a8e94" } + ], + "recorded": "2026-08-21T07:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:ed2738ad-fee1-53b8-976d-0fd5b9512aa8" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:7826f42abcd8a2cb292e4cafeb55b072adeae56b36f781265f2cb29f201641fa" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:7c06980e-c131-564a-9d90-26a23d9c1264" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/providers-oura-daily-activity.json.license b/packages/fhir/fixtures/conformance/resources/providers-oura-daily-activity.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/providers-oura-daily-activity.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/questionnaire-response.json b/packages/fhir/fixtures/conformance/resources/questionnaire-response.json new file mode 100644 index 00000000..e15d377f --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/questionnaire-response.json @@ -0,0 +1,44 @@ +{ + "resourceType": "QuestionnaireResponse", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://grovealliance.org/fhir/testing/questionnaire-responses", + "value": "grove-ts-conformance-response" + }, + "questionnaire": "https://grovealliance.org/fhir/testing/Questionnaire/grove-ts-conformance|1.0.0", + "status": "completed", + "subject": { "reference": "Patient/example" }, + "authored": "2026-08-20T12:00:00Z", + "item": [ + { + "linkId": "wellbeing", + "text": "How are you feeling?", + "answer": [ + { + "valueCoding": { + "system": "https://grovealliance.org/fhir/testing/CodeSystem/wellbeing", + "code": "well", + "display": "Well" + } + } + ] + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/questionnaire-response.json.license b/packages/fhir/fixtures/conformance/resources/questionnaire-response.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/questionnaire-response.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/questionnaire.json b/packages/fhir/fixtures/conformance/resources/questionnaire.json new file mode 100644 index 00000000..cc194cd8 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/questionnaire.json @@ -0,0 +1,47 @@ +{ + "resourceType": "Questionnaire", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm", + "valueCoding": { + "system": "http://hl7.org/fhir/version-algorithm", + "code": "semver" + } + } + ], + "url": "https://grovealliance.org/fhir/testing/Questionnaire/grove-ts-conformance", + "version": "1.0.0", + "name": "GroveTsConformance", + "title": "Grove TypeScript conformance instrument", + "status": "active", + "subjectType": ["Patient"], + "item": [ + { + "linkId": "wellbeing", + "text": "How are you feeling?", + "type": "choice", + "required": true, + "answerOption": [ + { + "valueCoding": { + "system": "https://grovealliance.org/fhir/testing/CodeSystem/wellbeing", + "code": "well", + "display": "Well" + } + }, + { + "valueCoding": { + "system": "https://grovealliance.org/fhir/testing/CodeSystem/wellbeing", + "code": "unwell", + "display": "Unwell" + } + } + ] + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/questionnaire.json.license b/packages/fhir/fixtures/conformance/resources/questionnaire.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/questionnaire.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json b/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json new file mode 100644 index 00000000..1dbba174 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json @@ -0,0 +1,236 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|32|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|heart-rate|raw-source-google-health-api-1" + } + } + ], + "fullUrl": "urn:uuid:310feab7-2f06-5d67-bbd1-989df3b9081b", + "resource": { + "resourceType": "DocumentReference", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/heart-rate" + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|heart-rate|raw-source-google-health-api-1" + } + ], + "status": "current", + "type": { "text": "Google Health API heart-rate archive" }, + "subject": { "reference": "Patient/example" }, + "date": "2026-08-20T12:01:00Z", + "author": [ + { "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" } + ], + "content": [ + { + "attachment": { + "contentType": "application/vnd.grovealliance.provider+json", + "data": "eyJzeW50aGV0aWMiOnRydWV9", + "size": 18, + "hash": "SHXPL1bWXRn+jEpR9Aw2zmx15qs=", + "title": "Authorized minimized provider recording" + }, + "format": { + "system": "https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format", + "version": "0.5.0", + "code": "provider-recording", + "display": "Provider Recording" + } + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "google-health-api|32|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:c350fe22-82c5-5488-abcc-329047852480", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:310feab7-2f06-5d67-bbd1-989df3b9081b" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:google-health-api|account-google-health-api|heart-rate|raw-source-google-health-api-1" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json.license b/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json b/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json new file mode 100644 index 00000000..5fa64daf --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json @@ -0,0 +1,236 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "oura|33|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "raw-source-oura-2" + } + } + ], + "fullUrl": "urn:uuid:780e25b2-19ee-5f0f-8d41-61d02be16019", + "resource": { + "resourceType": "DocumentReference", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "oura" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "oura/heartrate" + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "raw-source-oura-2" + } + ], + "status": "current", + "type": { "text": "Oura heartrate archive" }, + "subject": { "reference": "Patient/example" }, + "date": "2026-08-20T12:01:00Z", + "author": [ + { "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" } + ], + "content": [ + { + "attachment": { + "contentType": "application/vnd.grovealliance.provider+json", + "data": "eyJzeW50aGV0aWMiOnRydWV9", + "size": 18, + "hash": "SHXPL1bWXRn+jEpR9Aw2zmx15qs=", + "title": "Authorized minimized provider recording" + }, + "format": { + "system": "https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format", + "version": "0.5.0", + "code": "provider-recording", + "display": "Provider Recording" + } + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "oura|33|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:becb8a0c-bec4-50be-a103-c2b155504067", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:780e25b2-19ee-5f0f-8d41-61d02be16019" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "raw-source-oura-2" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json.license b/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json b/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json new file mode 100644 index 00000000..5f8ded11 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json @@ -0,0 +1,236 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|34|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|activityIntraday|raw-source-withings-3" + } + } + ], + "fullUrl": "urn:uuid:47cd8df0-eadc-529c-ad67-dcbe7895752e", + "resource": { + "resourceType": "DocumentReference", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/activityIntraday" + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|activityIntraday|raw-source-withings-3" + } + ], + "status": "current", + "type": { "text": "Withings activityIntraday archive" }, + "subject": { "reference": "Patient/example" }, + "date": "2026-08-20T12:01:00Z", + "author": [ + { "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" } + ], + "content": [ + { + "attachment": { + "contentType": "application/vnd.grovealliance.provider+json", + "data": "eyJzeW50aGV0aWMiOnRydWV9", + "size": 18, + "hash": "SHXPL1bWXRn+jEpR9Aw2zmx15qs=", + "title": "Authorized minimized provider recording" + }, + "format": { + "system": "https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format", + "version": "0.5.0", + "code": "provider-recording", + "display": "Provider Recording" + } + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|34|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:3c83d787-9632-5fff-9282-e76ca3969288", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:47cd8df0-eadc-529c-ad67-dcbe7895752e" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|activityIntraday|raw-source-withings-3" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json.license b/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json b/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json new file mode 100644 index 00000000..44f81c09 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json @@ -0,0 +1,236 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|35|exchange-bundle" + }, + "type": "collection", + "timestamp": "2026-08-20T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|sleepIntraday|raw-source-withings-4" + } + } + ], + "fullUrl": "urn:uuid:8c829d33-1a6d-5aa4-9007-6df3509aa5a1", + "resource": { + "resourceType": "DocumentReference", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/sleepIntraday" + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|sleepIntraday|raw-source-withings-4" + } + ], + "status": "current", + "type": { "text": "Withings sleepIntraday archive" }, + "subject": { "reference": "Patient/example" }, + "date": "2026-08-20T12:01:00Z", + "author": [ + { "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" } + ], + "content": [ + { + "attachment": { + "contentType": "application/vnd.grovealliance.provider+json", + "data": "eyJzeW50aGV0aWMiOnRydWV9", + "size": 18, + "hash": "SHXPL1bWXRn+jEpR9Aw2zmx15qs=", + "title": "Authorized minimized provider recording" + }, + "format": { + "system": "https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format", + "version": "0.5.0", + "code": "provider-recording", + "display": "Provider Recording" + } + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + } + ], + "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/data-origins", + "value": "connected-provider" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + } + ], + "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/applications", + "value": "grove-ts-0.2" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "valueIdentifier": { + "system": "urn:grove:provider-graph:org.grovealliance.example", + "value": "withings|35|conversion-provenance" + } + } + ], + "fullUrl": "urn:uuid:64cdab01-9ce7-528c-9dc3-902eb32aead5", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:8c829d33-1a6d-5aa4-9007-6df3509aa5a1" } + ], + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", + "value": "v1:withings|account-withings|sleepIntraday|raw-source-withings-4" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json.license b/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/normalized/google-health-active-energy.json b/packages/fhir/fixtures/normalized/google-health-active-energy.json new file mode 100644 index 00000000..5820505c --- /dev/null +++ b/packages/fhir/fixtures/normalized/google-health-active-energy.json @@ -0,0 +1,33 @@ +{ + "source": { + "adapter": { "kind": "providers", "provider": "google-health-api" }, + "providerAccountIdentifier": { + "system": "https://example.org/deployments/provider-accounts", + "value": "pseudonym-google-001", + "assurance": "deployment-scoped-pseudonym" + }, + "sourceType": "active-energy-burned", + "sourceNativeId": "synthetic-active-energy-2026-08-20", + "recordingMethod": "automatically-recorded", + "dataOrigin": { + "identity": { + "identifier": { + "system": "https://example.org/data-origins", + "value": "google-health-api" + } + }, + "name": "Google Health" + } + }, + "measurements": [ + { + "kind": "active-energy", + "value": 430.5, + "effective": { + "kind": "period", + "start": "2026-08-20T00:00:00Z", + "end": "2026-08-20T12:00:00Z" + } + } + ] +} diff --git a/packages/fhir/fixtures/normalized/google-health-active-energy.json.license b/packages/fhir/fixtures/normalized/google-health-active-energy.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/normalized/google-health-active-energy.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json b/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json new file mode 100644 index 00000000..379e2fb3 --- /dev/null +++ b/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json @@ -0,0 +1,32 @@ +{ + "source": { + "adapter": { + "kind": "providers", + "provider": "google-health-api" + }, + "providerAccountIdentifier": { + "system": "https://example.org/deployments/provider-accounts", + "value": "pseudonym-google-001", + "assurance": "deployment-scoped-pseudonym" + }, + "sourceType": "heart-rate", + "sourceNativeId": "synthetic-google-heart-rate-series-001", + "dataOrigin": { + "identity": { + "identifier": { + "system": "https://example.org/data-origins", + "value": "google-health-api" + } + }, + "name": "Google Health API" + } + }, + "attachment": { + "kind": "embedded", + "contentType": "application/vnd.grovealliance.provider+json", + "title": "Authorized minimized Google heart-rate recording", + "format": "provider-recording", + "payloadAssertion": "caller-authorized-opaque-payload", + "dataBase64": "eyJzeW50aGV0aWMiOnRydWV9" + } +} diff --git a/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json.license b/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/normalized/oura-daily-activity.json b/packages/fhir/fixtures/normalized/oura-daily-activity.json new file mode 100644 index 00000000..e4f7d744 --- /dev/null +++ b/packages/fhir/fixtures/normalized/oura-daily-activity.json @@ -0,0 +1,51 @@ +{ + "source": { + "adapter": { "kind": "providers", "provider": "oura" }, + "providerAccountIdentifier": { + "system": "https://example.org/deployments/provider-accounts", + "value": "pseudonym-oura-001", + "assurance": "deployment-scoped-pseudonym" + }, + "sourceType": "daily_activity", + "sourceNativeId": "synthetic-daily-activity-2026-08-20", + "recordingMethod": "automatically-recorded", + "dataOrigin": { + "identity": { + "identifier": { + "system": "https://example.org/data-origins", + "value": "oura" + } + }, + "name": "Oura" + } + }, + "measurements": [ + { + "kind": "distance", + "value": 6123, + "effective": { + "kind": "period", + "start": "2026-08-20T00:00:00Z", + "end": "2026-08-21T00:00:00Z" + } + }, + { + "kind": "step-count", + "value": 8234, + "effective": { + "kind": "period", + "start": "2026-08-20T00:00:00Z", + "end": "2026-08-21T00:00:00Z" + } + }, + { + "kind": "active-energy", + "value": 430, + "effective": { + "kind": "period", + "start": "2026-08-20T00:00:00Z", + "end": "2026-08-21T00:00:00Z" + } + } + ] +} diff --git a/packages/fhir/fixtures/normalized/oura-daily-activity.json.license b/packages/fhir/fixtures/normalized/oura-daily-activity.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/normalized/oura-daily-activity.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json b/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json new file mode 100644 index 00000000..2c43764d --- /dev/null +++ b/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json @@ -0,0 +1,32 @@ +{ + "source": { + "adapter": { + "kind": "providers", + "provider": "oura" + }, + "providerAccountIdentifier": { + "system": "https://example.org/deployments/provider-accounts", + "value": "pseudonym-oura-001", + "assurance": "deployment-scoped-pseudonym" + }, + "sourceType": "heartrate", + "sourceNativeId": "synthetic-oura-heart-rate-series-001", + "dataOrigin": { + "identity": { + "identifier": { + "system": "https://example.org/data-origins", + "value": "oura" + } + }, + "name": "Oura" + } + }, + "attachment": { + "kind": "embedded", + "contentType": "application/vnd.grovealliance.provider+json", + "title": "Sanitized Oura heart-rate recording", + "format": "provider-recording", + "payloadAssertion": "verified-sanitized-input", + "dataBase64": "eyJzeW50aGV0aWMiOnRydWV9" + } +} diff --git a/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json.license b/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/normalized/oura-sleep-duration.json b/packages/fhir/fixtures/normalized/oura-sleep-duration.json new file mode 100644 index 00000000..d600337b --- /dev/null +++ b/packages/fhir/fixtures/normalized/oura-sleep-duration.json @@ -0,0 +1,33 @@ +{ + "source": { + "adapter": { "kind": "providers", "provider": "oura" }, + "providerAccountIdentifier": { + "system": "https://example.org/deployments/provider-accounts", + "value": "pseudonym-oura-001", + "assurance": "deployment-scoped-pseudonym" + }, + "sourceType": "sleep", + "sourceNativeId": "synthetic-sleep-2026-08-19", + "recordingMethod": "automatically-recorded", + "dataOrigin": { + "identity": { + "identifier": { + "system": "https://example.org/data-origins", + "value": "oura" + } + }, + "name": "Oura" + } + }, + "measurements": [ + { + "kind": "sleep-duration", + "value": 7.4, + "effective": { + "kind": "period", + "start": "2026-08-19T22:30:00Z", + "end": "2026-08-20T06:20:00Z" + } + } + ] +} diff --git a/packages/fhir/fixtures/normalized/oura-sleep-duration.json.license b/packages/fhir/fixtures/normalized/oura-sleep-duration.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/normalized/oura-sleep-duration.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json b/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json new file mode 100644 index 00000000..fda64ad0 --- /dev/null +++ b/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json @@ -0,0 +1,32 @@ +{ + "source": { + "adapter": { + "kind": "providers", + "provider": "withings" + }, + "providerAccountIdentifier": { + "system": "https://example.org/deployments/provider-accounts", + "value": "pseudonym-withings-001", + "assurance": "deployment-scoped-pseudonym" + }, + "sourceType": "activityIntraday", + "sourceNativeId": "synthetic-withings-activity-series-001", + "dataOrigin": { + "identity": { + "identifier": { + "system": "https://example.org/data-origins", + "value": "withings" + } + }, + "name": "Withings" + } + }, + "attachment": { + "kind": "embedded", + "contentType": "application/vnd.grovealliance.provider+json", + "title": "Authorized minimized Withings activity recording", + "format": "provider-recording", + "payloadAssertion": "caller-authorized-opaque-payload", + "dataBase64": "eyJzeW50aGV0aWMiOnRydWV9" + } +} diff --git a/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json.license b/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/normalized/withings-body-weight.json b/packages/fhir/fixtures/normalized/withings-body-weight.json new file mode 100644 index 00000000..786668a5 --- /dev/null +++ b/packages/fhir/fixtures/normalized/withings-body-weight.json @@ -0,0 +1,32 @@ +{ + "source": { + "adapter": { "kind": "providers", "provider": "withings" }, + "providerAccountIdentifier": { + "system": "https://example.org/deployments/provider-accounts", + "value": "pseudonym-withings-001", + "assurance": "deployment-scoped-pseudonym" + }, + "sourceType": "getmeas:1", + "sourceNativeId": "synthetic-weight-2026-08-20", + "recordingMethod": "automatically-recorded", + "dataOrigin": { + "identity": { + "identifier": { + "system": "https://example.org/data-origins", + "value": "withings" + } + }, + "name": "Withings" + } + }, + "measurements": [ + { + "kind": "body-weight", + "value": 72.5, + "effective": { + "kind": "date-time", + "value": "2026-08-20T07:05:00Z" + } + } + ] +} diff --git a/packages/fhir/fixtures/normalized/withings-body-weight.json.license b/packages/fhir/fixtures/normalized/withings-body-weight.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/normalized/withings-body-weight.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json b/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json new file mode 100644 index 00000000..d87fa9eb --- /dev/null +++ b/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json @@ -0,0 +1,32 @@ +{ + "source": { + "adapter": { + "kind": "providers", + "provider": "withings" + }, + "providerAccountIdentifier": { + "system": "https://example.org/deployments/provider-accounts", + "value": "pseudonym-withings-001", + "assurance": "deployment-scoped-pseudonym" + }, + "sourceType": "sleepIntraday", + "sourceNativeId": "synthetic-withings-sleep-series-001", + "dataOrigin": { + "identity": { + "identifier": { + "system": "https://example.org/data-origins", + "value": "withings" + } + }, + "name": "Withings" + } + }, + "attachment": { + "kind": "embedded", + "contentType": "application/vnd.grovealliance.provider+json", + "title": "Authorized minimized Withings sleep recording", + "format": "provider-recording", + "payloadAssertion": "caller-authorized-opaque-payload", + "dataBase64": "eyJzeW50aGV0aWMiOnRydWV9" + } +} diff --git a/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json.license b/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/grove-fhir.json b/packages/fhir/grove-fhir.json new file mode 100644 index 00000000..c4871d67 --- /dev/null +++ b/packages/fhir/grove-fhir.json @@ -0,0 +1,4 @@ +{ + "repository": "https://github.com/SchmiedmayerLab/grove-fhir", + "ref": "85eb20a6b19a688f42e66e9ad3de96b0d1a5263f" +} diff --git a/packages/fhir/grove-fhir.json.license b/packages/fhir/grove-fhir.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/grove-fhir.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/jest.config.js b/packages/fhir/jest.config.js index 6fa0e600..681fa5f9 100644 --- a/packages/fhir/jest.config.js +++ b/packages/fhir/jest.config.js @@ -23,16 +23,23 @@ const config = { moduleNameMapper: { '^(\\.{1,2}/.*)\\.js$': '$1', }, - collectCoverage: true, - collectCoverageFrom: ['src/**/*.ts'], + collectCoverage: false, + // The generated schemas are excluded: an uncovered branch there is a FHIR type no example + // happened to use, not untested logic. What guards them is regeneration parity (`check:zod`), + // the pinned generation invariants, and validation against both releases' published examples. + collectCoverageFrom: [ + 'src/**/*.ts', + '!src/zod/r4/schemas.ts', + '!src/zod/r4b/schemas.ts', + ], coverageDirectory: 'coverage', coverageReporters: ['text', 'json-summary', 'lcov'], coverageThreshold: { global: { - branches: 28, - functions: 34, - lines: 64, - statements: 63, + branches: 90, + functions: 90, + lines: 90, + statements: 90, }, }, } diff --git a/packages/fhir/package.json b/packages/fhir/package.json index 0466a234..a9b321d0 100644 --- a/packages/fhir/package.json +++ b/packages/fhir/package.json @@ -1,7 +1,7 @@ { "name": "@schmiedmayerlab/grove-fhir", "version": "0.0.0", - "description": "FHIR schemas for Grove applications", + "description": "Strict FHIR R4 contracts and Grove resource builders for TypeScript", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -10,24 +10,66 @@ ".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" + }, + "./r4": { + "types": "./dist/r4/index.d.ts", + "default": "./dist/r4/index.js" + }, + "./mobile": { + "types": "./dist/mobile/index.d.ts", + "default": "./dist/mobile/index.js" + }, + "./providers": { + "types": "./dist/providers/index.d.ts", + "default": "./dist/providers/index.js" + }, + "./questionnaire": { + "types": "./dist/questionnaire/index.d.ts", + "default": "./dist/questionnaire/index.js" + }, + "./provenance": { + "types": "./dist/provenance/index.d.ts", + "default": "./dist/provenance/index.js" + }, + "./zod/r4": { + "types": "./dist/zod/r4/index.d.ts", + "default": "./dist/zod/r4/index.js" + }, + "./zod/r4b": { + "types": "./dist/zod/r4b/index.d.ts", + "default": "./dist/zod/r4b/index.js" } }, "scripts": { + "fetch:grove-fhir": "node scripts/fetch-grove-fhir.mjs", + "generate:catalog": "npm run fetch:grove-fhir && node scripts/generate-measurement-catalog.mjs", + "check:catalog": "npm run fetch:grove-fhir && node scripts/generate-measurement-catalog.mjs --check", + "generate:fixtures": "npm run fetch:grove-fhir && npm run build && node scripts/generate-conformance-fixtures.mjs", + "check:fixtures": "npm run fetch:grove-fhir && npm run build && node scripts/generate-conformance-fixtures.mjs --check", + "conformance": "npm run fetch:grove-fhir && npm run build && node scripts/validate-conformance.mjs", + "conformance:structural": "npm run fetch:grove-fhir && npm run build && node scripts/validate-conformance.mjs --structural-only", "lint": "eslint .", "lint:ci": "eslint --max-warnings 0 .", "lint:fix": "eslint . --fix", "build": "tsc", "watch": "tsc --watch", "clean": "rimraf dist tsconfig.tsbuildinfo", - "test": "NODE_OPTIONS=--experimental-vm-modules jest --coverage", + "test": "npm run test:unit && npm run test:package", + "test:browser": "npm run build && node scripts/browser-smoke.mjs", "test:coverage": "NODE_OPTIONS=--experimental-vm-modules jest --coverage", + "test:package": "npm run build && node scripts/package-smoke.mjs", + "test:unit": "NODE_OPTIONS=--experimental-vm-modules jest", "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch", - "typecheck": "tsc --noEmit && tsc -p test/tsconfig.json", - "prepublishOnly": "npm run clean && npm run build" + "typecheck": "npm run check:catalog && npm run check:zod && tsc --noEmit && tsc -p test/tsconfig.json", + "prepublishOnly": "npm run clean && npm run build", + "generate:zod": "node scripts/generate-zod-schemas.mjs --release r4 && node scripts/generate-zod-schemas.mjs --release r4b", + "check:zod": "node scripts/generate-zod-schemas.mjs --release r4 --check && node scripts/generate-zod-schemas.mjs --release r4b --check" }, "keywords": [ "grove", - "fhir" + "fhir", + "fhir-r4", + "health-data" ], "author": "Schmiedmayer Lab", "license": "MIT", @@ -45,11 +87,15 @@ }, "files": [ "dist", + "catalog", + "fixtures/normalized", "README.md", "LICENSE" ], "dependencies": { + "@noble/hashes": "2.3.0", "@types/fhir": "^0.0.44", + "uuid": "^14.0.1", "zod": "^4.4.3" }, "devDependencies": { @@ -59,7 +105,9 @@ "eslint": "^10.8.0", "eslint-import-resolver-typescript": "^4.4.5", "expect-type": "^1.3.0", + "fast-check": "^4.9.0", "jest": "^30.4.2", + "playwright": "^1.62.1", "prettier": "^3.6.2", "rimraf": "^6.1.2", "ts-jest": "^29.4.12", diff --git a/packages/fhir/scripts/browser-smoke.mjs b/packages/fhir/scripts/browser-smoke.mjs new file mode 100644 index 00000000..ef71a1f7 --- /dev/null +++ b/packages/fhir/scripts/browser-smoke.mjs @@ -0,0 +1,287 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { readFile } from 'node:fs/promises' +import { createServer } from 'node:http' +import { dirname, extname, resolve, sep } from 'node:path' +import { fileURLToPath, URL } from 'node:url' + +import { chromium } from 'playwright' + +const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..') +const groveRoot = resolve(packageRoot, 'dist') +const nobleRoot = resolve(packageRoot, '../../node_modules/@noble/hashes') +const uuidRoot = resolve(packageRoot, '../../node_modules/uuid/dist') +const zodRoot = resolve(packageRoot, '../../node_modules/zod') + +const contentType = (path) => + extname(path) === '.js' ? + 'text/javascript; charset=utf-8' + : 'application/octet-stream' + +const resolveRequest = (url) => { + const parsed = new URL(url, 'http://localhost') + const route = + parsed.pathname.startsWith('/grove/') ? + { root: groveRoot, relative: parsed.pathname.slice('/grove/'.length) } + : parsed.pathname.startsWith('/noble/') ? + { root: nobleRoot, relative: parsed.pathname.slice('/noble/'.length) } + : parsed.pathname.startsWith('/uuid/') ? + { root: uuidRoot, relative: parsed.pathname.slice('/uuid/'.length) } + : parsed.pathname.startsWith('/zod/') ? + { root: zodRoot, relative: parsed.pathname.slice('/zod/'.length) } + : undefined + if (route === undefined) return undefined + + const candidate = resolve(route.root, route.relative) + return candidate.startsWith(`${route.root}${sep}`) ? candidate : undefined +} + +const server = createServer(async (request, response) => { + if (request.url === '/') { + response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' }) + response.end(` + + `) + return + } + + const path = resolveRequest(request.url ?? '') + if (path === undefined) { + response.writeHead(404) + response.end() + return + } + try { + response.writeHead(200, { 'content-type': contentType(path) }) + response.end(await readFile(path)) + } catch { + response.writeHead(404) + response.end() + } +}) + +await new Promise((resolveListen) => { + server.listen(0, '127.0.0.1', resolveListen) +}) +const address = server.address() +if (address === null || typeof address === 'string') { + throw new Error('Browser test server did not bind to a TCP port.') +} +const origin = `http://127.0.0.1:${address.port}` + +const launchBrowser = async () => { + try { + return await chromium.launch({ headless: true }) + } catch (error) { + if ( + error instanceof Error && + error.message.includes("Executable doesn't exist") + ) { + return chromium.launch({ channel: 'chrome', headless: true }) + } + throw error + } +} + +const browser = await launchBrowser() +try { + const page = await browser.newPage() + await page.goto(origin) + const result = await page.evaluate(async (base) => { + const grove = await import(`${base}/grove/index.js`) + const mobile = await import(`${base}/grove/mobile/index.js`) + const provider = await import(`${base}/grove/providers/index.js`) + const questionnaire = await import(`${base}/grove/questionnaire/index.js`) + const absolute = mobile.deriveEntryFullUrl({ + system: 'https://study.example.org/fhir/identifiers/mobile-observation', + value: 'heart-rate-20260820-001', + }) + const measurementGraph = provider.buildProviderMeasurementBundle({ + subject: 'Patient/browser', + measurements: [ + { + kind: 'heart-rate', + value: 64, + effective: { kind: 'date-time', value: '2026-08-20T12:00:00Z' }, + }, + ], + source: { + adapter: { kind: 'providers', provider: 'withings' }, + providerAccountIdentifier: { + system: 'https://example.org/provider-account-pseudonyms', + value: 'browser-account', + assurance: 'deployment-scoped-pseudonym', + }, + sourceType: 'getmeas:11', + sourceNativeId: 'browser-heart-rate', + dataOrigin: { + identity: { + identifier: { + system: 'https://example.org/data-origins', + value: 'withings', + }, + }, + name: 'Withings', + }, + }, + application: { + identity: { + identifier: { + system: 'https://example.org/applications', + value: 'browser-converter', + }, + }, + name: 'Browser converter', + }, + eventSequence: 1, + graphIdentifierSystem: + 'urn:grove:provider-graph:org.grovealliance.example', + issued: '2026-08-20T12:01:00Z', + recorded: '2026-08-20T12:02:00Z', + }) + const recording = provider.buildProviderRecordingBundle({ + source: { + adapter: { kind: 'providers', provider: 'google-health-api' }, + providerAccountIdentifier: { + system: 'https://example.org/provider-account-pseudonyms', + value: 'browser-raw-account', + assurance: 'deployment-scoped-pseudonym', + }, + sourceType: 'heart-rate', + sourceNativeId: 'browser-native-recording-42', + dataOrigin: { + identity: { + identifier: { + system: 'https://example.org/data-origins', + value: 'google-health-api', + }, + }, + name: 'Google Health API', + }, + }, + attachment: { + kind: 'embedded', + contentType: 'application/vnd.grovealliance.provider+json', + title: 'Authorized minimized provider recording', + format: 'provider-recording', + payloadAssertion: 'caller-authorized-opaque-payload', + dataBase64: 'AQID', + }, + subject: 'Patient/browser', + application: { + identity: { + identifier: { + system: 'https://example.org/applications', + value: 'browser-raw-converter', + }, + }, + name: 'Browser raw converter', + }, + eventSequence: 2, + graphIdentifierSystem: + 'urn:grove:provider-graph:org.grovealliance.example', + documentDate: '2026-08-20T12:01:00Z', + recorded: '2026-08-20T12:02:00Z', + }) + const instrument = questionnaire.buildQuestionnaire({ + url: 'https://example.org/Questionnaire/browser', + version: '1.0.0', + status: 'active', + items: [ + { + linkId: 'ready', + text: 'Are you ready?', + type: 'boolean', + required: true, + }, + ], + }) + const response = questionnaire.buildQuestionnaireResponse({ + questionnaire: 'https://example.org/Questionnaire/browser|1.0.0', + identifier: { + system: 'https://example.org/responses', + value: 'browser-1', + }, + status: 'completed', + authored: '2026-08-20T12:00:00Z', + items: [ + { + linkId: 'ready', + text: 'Are you ready?', + answer: [{ valueBoolean: true }], + }, + ], + }) + const pair = + instrument.ok && response.ok ? + questionnaire.preflightQuestionnairePair( + instrument.value, + response.value, + ) + : undefined + return { + fullUrl: absolute.ok ? absolute.value : undefined, + hasNodeProcess: typeof globalThis.process !== 'undefined', + measurementCount: Object.keys(mobile.sharedMobileMeasurementCatalog) + .length, + measurementGraph: measurementGraph.ok, + recordingGraph: recording.ok, + rawSourceCount: Object.values(provider.providerRawMappings).reduce( + (count, mappings) => count + Object.keys(mappings).length, + 0, + ), + scalarMeasurementCount: new Set( + Object.values(provider.providerScalarMappings).flatMap( + (sourceMappings) => + Object.values(sourceMappings).flatMap((mapping) => + Object.keys(mapping), + ), + ), + ).size, + providerApiVisibleFromMobile: + 'buildProviderMeasurementBundle' in mobile || + 'buildProviderRecordingBundle' in mobile, + providerApiVisibleFromRoot: + 'buildProviderMeasurementBundle' in grove || + 'buildProviderRecordingBundle' in grove, + internalGraphVisible: + 'groveFhirPackageGraph' in grove || + 'groveFhirProfileClaims' in grove || + 'groveFhirPackageGraph' in mobile || + 'groveFhirProfileClaims' in mobile, + questionnairePair: pair?.ok, + } + }, origin) + + if ( + result.fullUrl !== 'urn:uuid:72a64652-0bad-517d-8a36-39e3b6adccac' || + result.hasNodeProcess || + result.measurementCount !== 84 || + result.measurementGraph !== true || + result.recordingGraph !== true || + result.rawSourceCount !== 4 || + result.scalarMeasurementCount !== 36 || + result.providerApiVisibleFromMobile || + result.providerApiVisibleFromRoot || + result.internalGraphVisible || + result.questionnairePair !== true + ) { + throw new Error(`Browser contract failed: ${JSON.stringify(result)}`) + } +} finally { + await browser.close() + await new Promise((resolveClose, rejectClose) => { + server.close((error) => { + if (error === undefined) resolveClose() + else rejectClose(error) + }) + }) +} diff --git a/packages/fhir/scripts/fetch-grove-fhir.mjs b/packages/fhir/scripts/fetch-grove-fhir.mjs new file mode 100644 index 00000000..266b77a5 --- /dev/null +++ b/packages/fhir/scripts/fetch-grove-fhir.mjs @@ -0,0 +1,73 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +// Fetch the Grove FHIR catalogs this package generates from. The implementation guide +// owns them, so they are read at the pinned commit rather than copied into this +// repository, where a second copy could drift from the contract it claims to follow. + +import { Buffer } from 'node:buffer' +import { execFile } from 'node:child_process' +import { mkdir, readFile, rm, writeFile } from 'node:fs/promises' +import { dirname, resolve } from 'node:path' +import { stdout } from 'node:process' +import { fileURLToPath } from 'node:url' +import { promisify } from 'node:util' + +const { fetch } = globalThis +const run = promisify(execFile) +const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..') +const destination = resolve(packageRoot, '.grove-fhir') +const marker = resolve(destination, '.ref') + +const pin = JSON.parse( + await readFile(resolve(packageRoot, 'grove-fhir.json'), 'utf8'), +) +// A released version is pinned by its tag so the reference stays readable in a diff; a bare +// commit stays valid for pinning work that predates a release. +const ref = pin.ref ?? pin.sha +if ( + typeof ref !== 'string' || + !/^(?:[\da-f]{40}|\d+\.\d+\.\d+(?:-[\dA-Za-z]+(?:[.-][\dA-Za-z]+)*)?)$/u.test( + ref, + ) +) { + throw new Error( + `grove-fhir.json must pin a release tag or a complete commit SHA, got "${ref}"`, + ) +} + +const present = await readFile(marker, 'utf8').catch(() => undefined) +// Every generator and checker depends on this, so repeated runs are the common case. +if (present !== ref) { + const archive = `${pin.repository}/archive/${ref}.tar.gz` + const response = await fetch(archive) + if (!response.ok) { + throw new Error(`Could not fetch ${archive}: ${response.status}`) + } + + await rm(destination, { recursive: true, force: true }) + await mkdir(destination, { recursive: true }) + const tarball = resolve(destination, 'grove-fhir.tar.gz') + await writeFile(tarball, Buffer.from(await response.arrayBuffer())) + + // The archive root is named for the commit, so strip it and keep only what is read. + await run('tar', [ + '--extract', + '--file', + tarball, + '--directory', + destination, + '--strip-components', + '1', + `grove-fhir-${ref}/catalog`, + `grove-fhir-${ref}/Conformance/corpora/mobile-semantics`, + ]) + await rm(tarball) + await writeFile(marker, ref) + stdout.write(`Fetched Grove FHIR catalogs at ${ref}\n`) +} diff --git a/packages/fhir/scripts/generate-conformance-fixtures.mjs b/packages/fhir/scripts/generate-conformance-fixtures.mjs new file mode 100644 index 00000000..4de44c2d --- /dev/null +++ b/packages/fhir/scripts/generate-conformance-fixtures.mjs @@ -0,0 +1,547 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { Buffer } from 'node:buffer' +import { mkdir, readFile, writeFile } from 'node:fs/promises' +import { dirname, resolve } from 'node:path' +import { argv, stdout } from 'node:process' +import { fileURLToPath } from 'node:url' + +import { format } from 'prettier' + +// These non-literal imports intentionally resolve only after `npm run build`. +const distRoot = '../dist' +const rootApi = await import(`${distRoot}/index.js`) +const providerApi = await import(`${distRoot}/providers/index.js`) +const { + buildQuestionnaire, + buildQuestionnaireResponse, + groveFhirContractVersion, + parseAbsoluteUri, + parseCanonical, + parseFhirInstant, + parsePatientReference, + parsePositiveInteger, + parseSemVer, + sharedMobileMeasurementCatalog, +} = rootApi +const { + buildProviderRecordingBundle, + buildProviderMeasurementBundle, + providerRawMappings, + providerRecordEffectiveRules, + providerScalarMappings, + encodeRecordingBytes, + parseMediaType, +} = providerApi + +const root = resolve(dirname(fileURLToPath(import.meta.url)), '..') +const fixtureRoot = resolve(root, 'fixtures/conformance') +const resourceRoot = resolve(fixtureRoot, 'resources') +const check = argv.includes('--check') +const packageMetadata = JSON.parse( + await readFile(resolve(root, 'package.json'), 'utf8'), +) +const semanticCorpus = JSON.parse( + await readFile( + resolve( + root, + '.grove-fhir/Conformance/corpora/mobile-semantics/corpus.json', + ), + 'utf8', + ), +) +const packageGraph = JSON.parse( + await readFile( + resolve(root, '.grove-fhir/catalog/package-graph.json'), + 'utf8', + ), +) +const producerVersion = packageMetadata.version +if (typeof producerVersion !== 'string' || producerVersion.length === 0) { + throw new Error('The FHIR package must declare a producer version.') +} + +const conformancePackageSources = [ + 'mobile', + 'providers', + 'sensor', + 'questionnaire', +] +const packageBySource = new Map( + packageGraph.packages.map((entry) => [entry.source, entry]), +) +if ( + packageGraph.fhirVersion !== '4.0.1' || + packageGraph.version !== groveFhirContractVersion +) { + throw new Error( + 'The synchronized package graph must match the generated R4 contract version.', + ) +} +const conformancePackages = conformancePackageSources.map((source) => { + const entry = packageBySource.get(source) + if (entry === undefined) { + throw new Error(`The package graph is missing ${source}.`) + } + return { + alias: source, + packageId: entry.packageId, + version: packageGraph.version, + } +}) +const profileCanonical = (source, profile) => { + const entry = packageBySource.get(source) + if (entry === undefined || !entry.profiles.includes(profile)) { + throw new Error(`The package graph is missing ${source}/${profile}.`) + } + return `${entry.canonical}/StructureDefinition/${profile}` +} +const exchangeBundleProfile = profileCanonical( + 'mobile', + 'grove-mobile-exchange-bundle', +) +const questionnaireProfile = profileCanonical( + 'questionnaire', + 'grove-questionnaire', +) +const questionnaireResponseProfile = profileCanonical( + 'questionnaire', + 'grove-questionnaire-response', +) + +const unwrap = (result) => { + if (!result.ok) { + throw new Error(result.issues.map((entry) => entry.message).join('\n')) + } + return result.value +} + +const uri = (value) => unwrap(parseAbsoluteUri(value)) +const instant = (value) => unwrap(parseFhirInstant(value)) +const resourceIdentity = (system, value) => ({ + identifier: { system: uri(system), value }, +}) + +const application = { + identity: resourceIdentity( + 'https://grovealliance.org/fhir/testing/applications', + 'grove-ts-0.2', + ), + name: 'Grove TypeScript conformance producer', + version: producerVersion, +} +const dataOrigin = { + identity: resourceIdentity( + 'https://grovealliance.org/fhir/testing/data-origins', + 'connected-provider', + ), + name: 'Synthetic connected provider', +} +// Owner-exclusive scalar mappings stay outside the shared facade and the corpus. +const admittedMeasurementIds = new Set( + Object.values(providerScalarMappings) + .flatMap((sourceMappings) => + Object.values(sourceMappings).flatMap((mapping) => Object.keys(mapping)), + ) + .filter((id) => Object.hasOwn(sharedMobileMeasurementCatalog, id)), +) +const semanticVectors = semanticCorpus.vectors.filter((vector) => + admittedMeasurementIds.has(vector.id), +) +if (semanticVectors.length !== admittedMeasurementIds.size) { + throw new Error( + 'The semantic corpus must bind every shared Provider scalar measurement exactly once.', + ) +} + +const effectiveFromVector = (vector) => { + if (vector.effective.type === 'dateTime') { + return { kind: 'date-time', value: instant(vector.effective.value) } + } + if (vector.effective.type === 'Period') { + return { + kind: 'period', + start: instant(vector.effective.start), + end: instant(vector.effective.end), + } + } + throw new Error(`Unsupported semantic effective type: ${vector.id}`) +} + +const measurementFromVector = (vector) => { + const effective = effectiveFromVector(vector) + if (vector.id === 'blood-pressure') { + const systolic = vector.result.components.find( + (component) => component.id === 'systolic', + ) + const diastolic = vector.result.components.find( + (component) => component.id === 'diastolic', + ) + if (systolic === undefined || diastolic === undefined) { + throw new Error('Blood-pressure semantic vector is incomplete.') + } + return { + kind: vector.id, + systolic: systolic.value, + diastolic: diastolic.value, + effective, + } + } + if (vector.result.type === 'CodeableConcept') { + return { kind: vector.id, value: vector.result.code, effective } + } + if (vector.result.type !== 'Quantity') { + throw new Error(`Unsupported semantic result type: ${vector.id}`) + } + return { kind: vector.id, value: vector.result.value, effective } +} + +const measurements = semanticVectors.map(measurementFromVector) + +const providerFor = (measurement, index) => { + const providers = Object.entries(providerScalarMappings) + const ordered = [ + ...providers.slice(index % providers.length), + ...providers.slice(0, index % providers.length), + ] + for (const [provider, sourceMappings] of ordered) { + for (const [sourceType, mapping] of Object.entries(sourceMappings)) { + const effectiveRule = providerRecordEffectiveRules[provider]?.[sourceType] + if ( + effectiveRule === undefined && + Object.hasOwn(mapping, measurement.kind) + ) { + return { provider, sourceType } + } + } + } + throw new Error(`No Provider provider admits ${measurement.kind}.`) +} + +const providerMeasurementBundle = (measurement, index) => { + const provider = providerFor(measurement, index) + return unwrap( + buildProviderMeasurementBundle({ + subject: unwrap(parsePatientReference('Patient/example')), + measurements: [measurement], + source: { + adapter: { + kind: 'providers', + provider: provider.provider, + }, + providerAccountIdentifier: { + system: uri( + 'https://example.org/deployments/provider-account-pseudonyms', + ), + value: `account-${provider.provider}`, + assurance: 'deployment-scoped-pseudonym', + }, + sourceType: provider.sourceType, + sourceNativeId: `source-${measurement.kind}`, + recordingMethod: 'automatically-recorded', + dataOrigin, + }, + application, + eventSequence: unwrap(parsePositiveInteger(index + 1)), + graphIdentifierSystem: uri( + 'urn:grove:provider-graph:org.grovealliance.example', + ), + issued: instant('2026-08-20T12:01:00Z'), + recorded: instant('2026-08-20T12:02:00Z'), + }), + ) +} + +const measurementByKind = new Map( + measurements.map((measurement) => [measurement.kind, measurement]), +) +const ouraDailyActivityEffective = { + kind: 'period', + start: instant('2026-08-20T00:00:00-07:00'), + end: instant('2026-08-21T00:00:00-07:00'), +} +const ouraDailyActivityMeasurements = [ + 'step-count', + 'active-energy', + 'distance', +].map((kind) => { + const measurement = measurementByKind.get(kind) + if (measurement === undefined) { + throw new Error(`Missing semantic vector for Oura ${kind}.`) + } + return { ...measurement, effective: ouraDailyActivityEffective } +}) +const ouraDailyActivityBundle = unwrap( + buildProviderMeasurementBundle({ + subject: unwrap(parsePatientReference('Patient/example')), + measurements: ouraDailyActivityMeasurements, + source: { + adapter: { kind: 'providers', provider: 'oura' }, + providerAccountIdentifier: { + system: uri( + 'https://example.org/deployments/provider-account-pseudonyms', + ), + value: 'account-oura-daily-activity', + assurance: 'deployment-scoped-pseudonym', + }, + sourceType: 'daily_activity', + sourceNativeId: 'source-oura-daily-activity', + recordingMethod: 'automatically-recorded', + dataOrigin, + }, + application, + eventSequence: unwrap(parsePositiveInteger(measurements.length + 1)), + graphIdentifierSystem: uri( + 'urn:grove:provider-graph:org.grovealliance.example', + ), + issued: instant('2026-08-21T07:01:00Z'), + recorded: instant('2026-08-21T07:02:00Z'), + }), +) + +const recordingSources = Object.entries(providerRawMappings).flatMap( + ([provider, sources]) => + Object.keys(sources).map((sourceType) => ({ provider, sourceType })), +) + +const recordingPath = ({ provider, sourceType }) => + `resources/recording-${provider}-${sourceType.replaceAll(/[^A-Za-z0-9]+/gu, '-').toLowerCase()}.json` + +const recordingBundle = ({ provider, sourceType }, index) => + unwrap( + buildProviderRecordingBundle({ + source: { + adapter: { kind: 'providers', provider }, + providerAccountIdentifier: { + system: uri( + 'https://example.org/deployments/provider-account-pseudonyms', + ), + value: `account-${provider}`, + assurance: 'deployment-scoped-pseudonym', + }, + sourceType, + sourceNativeId: `raw-source-${provider}-${String(index + 1)}`, + dataOrigin, + }, + attachment: { + kind: 'embedded', + contentType: unwrap( + parseMediaType('application/vnd.grovealliance.provider+json'), + ), + title: 'Authorized minimized provider recording', + format: 'provider-recording', + payloadAssertion: 'caller-authorized-opaque-payload', + dataBase64: unwrap( + encodeRecordingBytes(Buffer.from('{"synthetic":true}', 'utf8')), + ), + }, + subject: unwrap(parsePatientReference('Patient/example')), + application, + eventSequence: unwrap( + parsePositiveInteger(measurements.length + index + 1), + ), + graphIdentifierSystem: uri( + 'urn:grove:provider-graph:org.grovealliance.example', + ), + documentDate: instant('2026-08-20T12:01:00Z'), + recorded: instant('2026-08-20T12:02:00Z'), + }), + ) + +const questionnaireUrl = uri( + 'https://grovealliance.org/fhir/testing/Questionnaire/grove-ts-conformance', +) +const questionnaireVersion = unwrap(parseSemVer('1.0.0')) +const questionnaire = unwrap( + buildQuestionnaire({ + url: questionnaireUrl, + version: questionnaireVersion, + name: 'GroveTsConformance', + title: 'Grove TypeScript conformance instrument', + status: 'active', + subjectTypes: ['Patient'], + items: [ + { + linkId: 'wellbeing', + text: 'How are you feeling?', + type: 'choice', + required: true, + answerOption: [ + { + valueCoding: { + system: uri( + 'https://grovealliance.org/fhir/testing/CodeSystem/wellbeing', + ), + code: 'well', + display: 'Well', + }, + }, + { + valueCoding: { + system: uri( + 'https://grovealliance.org/fhir/testing/CodeSystem/wellbeing', + ), + code: 'unwell', + display: 'Unwell', + }, + }, + ], + }, + ], + }), +) +const questionnaireResponse = unwrap( + buildQuestionnaireResponse({ + questionnaire: unwrap( + parseCanonical(`${questionnaireUrl}|${questionnaireVersion}`), + ), + identifier: { + system: uri( + 'https://grovealliance.org/fhir/testing/questionnaire-responses', + ), + value: 'grove-ts-conformance-response', + }, + status: 'completed', + subject: unwrap(parsePatientReference('Patient/example')), + authored: instant('2026-08-20T12:00:00Z'), + items: [ + { + linkId: 'wellbeing', + text: 'How are you feeling?', + answer: [ + { + valueCoding: { + system: uri( + 'https://grovealliance.org/fhir/testing/CodeSystem/wellbeing', + ), + code: 'well', + display: 'Well', + }, + }, + ], + }, + ], + }), +) + +const resources = new Map( + measurements.map((measurement, index) => [ + `resources/mobile-${measurement.kind}.json`, + providerMeasurementBundle(measurement, index), + ]), +) +const ouraDailyActivityPath = 'resources/provider-oura-daily-activity.json' +resources.set(ouraDailyActivityPath, ouraDailyActivityBundle) +for (const [index, source] of recordingSources.entries()) { + resources.set(recordingPath(source), recordingBundle(source, index)) +} +resources.set('resources/questionnaire.json', questionnaire) +resources.set('resources/questionnaire-response.json', questionnaireResponse) + +const manifest = { + schemaVersion: 1, + fhirVersion: packageGraph.fhirVersion, + producer: { name: 'Grove TypeScript', version: producerVersion }, + packages: conformancePackages, + resources: [ + ...measurements.map((measurement) => ({ + path: `resources/mobile-${measurement.kind}.json`, + requiredProfiles: [exchangeBundleProfile], + })), + { + path: ouraDailyActivityPath, + requiredProfiles: [exchangeBundleProfile], + }, + ...recordingSources.map((source) => ({ + path: recordingPath(source), + requiredProfiles: [exchangeBundleProfile], + })), + { + path: 'resources/questionnaire.json', + requiredProfiles: [questionnaireProfile], + }, + { + path: 'resources/questionnaire-response.json', + requiredProfiles: [questionnaireResponseProfile], + }, + ], + semanticVectors: semanticVectors.map(({ id }) => ({ + id, + path: `resources/mobile-${id}.json`, + resourcePointer: '/entry/0/resource', + })), +} + +const serialized = new Map( + await Promise.all( + [...resources, ['manifest.json', manifest]].map(async ([path, value]) => [ + path, + await format(JSON.stringify(value), { parser: 'json' }), + ]), + ), +) +const license = `SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT +` +for (const relative of [...serialized.keys()]) { + serialized.set(`${relative}.license`, license) +} + +// Validate what we are about to publish, against the release's own model, before it is written. +// The official Validator checks these later in CI; catching a malformed emission here names the +// resource and the element rather than surfacing as a validator report a build later. +const generated = await import('@schmiedmayerlab/grove-fhir/zod/r4') +const schemaFor = (resourceType) => + generated[ + `${resourceType.charAt(0).toLowerCase()}${resourceType.slice(1)}Schema` + ] +const invalid = [] +for (const [relative, resource] of resources) { + const candidates = + resource.resourceType === 'Bundle' ? + (resource.entry ?? []).map((entry) => entry.resource) + : [resource] + for (const candidate of candidates) { + const schema = schemaFor(candidate.resourceType) + if (!schema) continue + const result = schema.safeParse(candidate) + if (!result.success) { + invalid.push( + `${relative} (${candidate.resourceType}): ${result.error.issues + .map( + (issue) => `${issue.path.join('.') || ''} ${issue.message}`, + ) + .join('; ')}`, + ) + } + } +} +if (invalid.length > 0) { + throw new Error( + `Generated fixtures do not satisfy the R4 schemas:\n ${invalid.join('\n ')}`, + ) +} + +await mkdir(resourceRoot, { recursive: true }) +for (const [relative, value] of serialized) { + const path = resolve(fixtureRoot, relative) + if (check) { + const existing = await readFile(path, 'utf8').catch(() => undefined) + if (existing !== value) { + throw new Error(`Conformance fixture is stale: ${relative}`) + } + } else { + await writeFile(path, value) + } +} + +stdout.write( + `${check ? 'Checked' : 'Generated'} ${resources.size} R4 resources from the public API.\n`, +) diff --git a/packages/fhir/scripts/generate-measurement-catalog.mjs b/packages/fhir/scripts/generate-measurement-catalog.mjs new file mode 100644 index 00000000..ec82f372 --- /dev/null +++ b/packages/fhir/scripts/generate-measurement-catalog.mjs @@ -0,0 +1,1188 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { readFile, writeFile } from 'node:fs/promises' +import { dirname, resolve } from 'node:path' +import { argv } from 'node:process' +import { fileURLToPath } from 'node:url' + +import { format } from 'prettier' + +const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..') +const catalogRoot = resolve(packageRoot, '.grove-fhir/catalog') +const capabilityPath = resolve( + packageRoot, + 'catalog/measurement-capabilities.json', +) +const inputPath = resolve(catalogRoot, 'measurement-catalog.json') +const packageGraphPath = resolve(catalogRoot, 'package-graph.json') +const exchangeIdentityPath = resolve(catalogRoot, 'exchange-identity.json') +const profileClaimsPath = resolve(catalogRoot, 'profile-claims.json') +const sensorCatalogPath = resolve(catalogRoot, 'sensor-catalog.json') +const healthConnectAdapterPath = resolve( + catalogRoot, + 'health-connect-adapter.json', +) +const healthConnectIdentityPath = resolve( + catalogRoot, + 'health-connect-identity.json', +) +const healthKitAdapterPath = resolve(catalogRoot, 'healthkit-adapter.json') +const sensorKitAdapterPath = resolve(catalogRoot, 'sensorkit-adapter.json') +const formatRegistryPath = resolve(catalogRoot, 'format-registry.json') +const sourceRefPath = resolve(packageRoot, 'grove-fhir.json') +const semanticCorpusPath = resolve( + packageRoot, + '.grove-fhir/Conformance/corpora/mobile-semantics/corpus.json', +) +const outputPaths = { + mobile: resolve(packageRoot, 'src/mobile/measurement-catalog.generated.ts'), + questionnaire: resolve( + packageRoot, + 'src/questionnaire/contract.generated.ts', + ), + provider: resolve(packageRoot, 'src/providers/contract.generated.ts'), +} + +const adapterOwners = ['healthkit', 'health-connect', 'sensorkit', 'providers'] +const isMobileOwned = (measurement) => + measurement.owner === undefined || measurement.owner === 'mobile' + +const catalog = JSON.parse(await readFile(inputPath, 'utf8')) +const packageGraph = JSON.parse(await readFile(packageGraphPath, 'utf8')) +const exchangeIdentity = JSON.parse( + await readFile(exchangeIdentityPath, 'utf8'), +) +const profileClaims = JSON.parse(await readFile(profileClaimsPath, 'utf8')) +const sensorCatalog = JSON.parse(await readFile(sensorCatalogPath, 'utf8')) +const healthConnectAdapter = JSON.parse( + await readFile(healthConnectAdapterPath, 'utf8'), +) +const healthConnectIdentity = JSON.parse( + await readFile(healthConnectIdentityPath, 'utf8'), +) +const healthKitAdapter = JSON.parse( + await readFile(healthKitAdapterPath, 'utf8'), +) +const sensorKitAdapter = JSON.parse( + await readFile(sensorKitAdapterPath, 'utf8'), +) +const formatRegistry = JSON.parse(await readFile(formatRegistryPath, 'utf8')) +const sourceRef = JSON.parse(await readFile(sourceRefPath, 'utf8')) +const semanticCorpus = JSON.parse(await readFile(semanticCorpusPath, 'utf8')) +const capabilities = JSON.parse(await readFile(capabilityPath, 'utf8')) +const measurements = catalog.measurements +if (!Array.isArray(measurements) || measurements.length === 0) { + throw new Error('Measurement catalog must contain measurements.') +} + +const mobileMeasurements = measurements.filter(isMobileOwned) +if ( + semanticCorpus.schemaVersion !== 1 || + semanticCorpus.fhirVersion !== '4.0.1' || + semanticCorpus.version !== catalog.version || + !Array.isArray(semanticCorpus.vectors) || + semanticCorpus.vectors.length !== mobileMeasurements.length +) { + throw new Error( + 'Mobile semantic corpus must contain one R4 vector per shared measurement.', + ) +} +const semanticVectorIds = semanticCorpus.vectors.map((vector) => vector.id) +if ( + new Set(semanticVectorIds).size !== semanticVectorIds.length || + mobileMeasurements.some( + (measurement) => + !semanticCorpus.vectors.some( + (vector) => + vector.id === measurement.id && + vector.profile === + `https://grovealliance.org/fhir/mobile/StructureDefinition/${measurement.profile}`, + ), + ) +) { + throw new Error( + 'Mobile semantic vector ids and profiles must exactly cover the measurement catalog.', + ) +} + +const effectiveCanonicalization = catalog.effectiveCanonicalization +const semanticEffectiveCanonicalization = + semanticCorpus.effectiveCanonicalization +const expectedEffectiveCanonicalizationVectorIds = [ + 'positive-below-half', + 'positive-half-to-even', + 'positive-half-to-next-even', + 'negative-half-to-even', + 'negative-half-to-previous-even', + 'offset-preserved', +] +if ( + effectiveCanonicalization?.precision !== 'millisecond' || + effectiveCanonicalization.rounding !== 'half-even' || + effectiveCanonicalization.epoch !== '1970-01-01T00:00:00Z' || + !effectiveCanonicalization.offsetPolicy?.includes( + 'Preserve the caller/source numeric UTC offset', + ) || + semanticEffectiveCanonicalization?.precision !== 'millisecond' || + semanticEffectiveCanonicalization.rounding !== 'half-even' || + semanticEffectiveCanonicalization.epoch !== '1970-01-01T00:00:00Z' || + semanticEffectiveCanonicalization.offsetPolicy !== 'preserve-source-offset' || + !Array.isArray(semanticEffectiveCanonicalization.vectors) || + JSON.stringify( + semanticEffectiveCanonicalization.vectors.map((vector) => vector.id), + ) !== JSON.stringify(expectedEffectiveCanonicalizationVectorIds) || + semanticEffectiveCanonicalization.vectors.some( + (vector) => + typeof vector.input !== 'string' || typeof vector.output !== 'string', + ) +) { + throw new Error( + 'Mobile effective-time canonicalization contract is incomplete or changed.', + ) +} + +const keys = new Set() +for (const measurement of measurements) { + if (typeof measurement.id !== 'string' || keys.has(measurement.id)) { + throw new Error( + `Measurement id is missing or duplicated: ${String(measurement.id)}`, + ) + } + keys.add(measurement.id) + + for (const field of ['profile', 'code', 'effective', 'coverage']) { + if (measurement[field] === undefined) { + throw new Error( + `Normative measurement ${measurement.id} is missing ${field}.`, + ) + } + } + if ( + measurement.owner !== undefined && + !adapterOwners.includes(measurement.owner) + ) { + throw new Error( + `Normative measurement ${measurement.id} declares unknown owner ${String(measurement.owner)}.`, + ) + } + if (measurement.valueKind === 'quantity') { + if (measurement.quantity === null) { + throw new Error( + `Normative measurement ${measurement.id} requires a quantity definition.`, + ) + } + } else if (measurement.valueKind === 'codeableConcept') { + if ( + measurement.quantity !== null || + typeof measurement.resultCodeSystem !== 'string' || + typeof measurement.valueSet !== 'string' || + !Array.isArray(measurement.allowedValues) || + measurement.allowedValues.length === 0 + ) { + throw new Error( + `Normative measurement ${measurement.id} requires a closed coded-result contract.`, + ) + } + } else if (measurement.valueKind === 'grouping') { + // A panel states its members rather than a value of its own, so it declares neither a quantity + // nor a coded result; the members are complete Observations that stand alone. + if (measurement.quantity !== null) { + throw new Error( + `Grouping measurement ${measurement.id} must not declare a quantity.`, + ) + } + } else if ( + measurement.valueKind !== 'components' || + measurement.quantity !== null + ) { + throw new Error( + `Normative measurement ${measurement.id} declares unknown value kind ${String(measurement.valueKind)}.`, + ) + } + + const supportedSources = Object.values(measurement.coverage).filter( + (status) => status === 'supported', + ) + if (isMobileOwned(measurement) && supportedSources.length < 2) { + throw new Error( + `Shared Mobile measurement ${measurement.id} requires at least two evidenced supported sources.`, + ) + } + if (supportedSources.length === 0) { + throw new Error( + `Normative measurement ${measurement.id} requires at least one evidenced supported source.`, + ) + } +} + +const implemented = Object.fromEntries( + measurements.map((measurement) => [measurement.id, measurement]), +) + +const bloodPressure = measurements.find( + (measurement) => measurement.id === 'blood-pressure', +) +if ( + bloodPressure?.components?.length !== 2 || + bloodPressure.components[0]?.id !== 'systolic' || + bloodPressure.components[1]?.id !== 'diastolic' +) { + throw new Error( + 'Blood pressure requires ordered systolic and diastolic component contracts.', + ) +} + +for (const component of bloodPressure.components) { + if ( + typeof component.system !== 'string' || + typeof component.code !== 'string' || + typeof component.quantity?.system !== 'string' || + typeof component.quantity.code !== 'string' + ) { + throw new Error('Blood-pressure component coding is incomplete.') + } +} + +const sleepStage = measurements.find( + (measurement) => measurement.id === 'sleep-stage', +) +if ( + typeof sleepStage?.resultCodeSystem !== 'string' || + typeof sleepStage.valueSet !== 'string' || + !Array.isArray(sleepStage.allowedValues) || + sleepStage.allowedValues.join(',') !== + 'awake,in-bed,out-of-bed,asleep-unspecified,light,deep,rem,unknown' +) { + throw new Error('Sleep-stage terminology contract is incomplete.') +} + +if (packageGraph.fhirVersion !== '4.0.1' || catalog.fhirVersion !== '4.0.1') { + throw new Error( + 'The TypeScript package consumes FHIR R4 (4.0.1) catalogs only.', + ) +} + +for (const consumedCatalog of [ + sensorCatalog, + healthConnectAdapter, + healthConnectIdentity, + healthKitAdapter, + sensorKitAdapter, +]) { + if ( + consumedCatalog.fhirVersion !== '4.0.1' || + consumedCatalog.version !== packageGraph.version + ) { + throw new Error( + 'Every consumed IG catalog must target the same FHIR R4 release.', + ) + } +} + +// A released version is pinned by its tag so the reference stays readable; a bare commit stays +// valid for pinning work that predates a release. +const pinnedRef = sourceRef.ref ?? sourceRef.sha +if ( + sourceRef.repository !== 'https://github.com/SchmiedmayerLab/grove-fhir' || + typeof pinnedRef !== 'string' || + !/^(?:[\da-f]{40}|\d+\.\d+\.\d+(?:-[\dA-Za-z]+(?:[.-][\dA-Za-z]+)*)?)$/u.test( + pinnedRef, + ) +) { + throw new Error( + 'The IG release these catalogs were read from must be pinned.', + ) +} + +const packages = Object.fromEntries( + packageGraph.packages.map((entry) => [entry.source, entry]), +) +for (const source of [ + 'mobile', + 'questionnaire', + 'sensor', + 'sensorkit', + 'healthkit', + 'health-connect', + 'providers', +]) { + if (packages[source] === undefined) { + throw new Error(`Package graph is missing ${source}.`) + } +} + +const packageMetadata = (source) => { + const entry = packages[source] + return { + fhirVersion: packageGraph.fhirVersion, + version: packageGraph.version, + packageId: entry.packageId, + canonical: entry.canonical, + dependencies: entry.dependencies, + } +} +const mobilePackageMetadata = packageMetadata('mobile') +const questionnairePackageMetadata = packageMetadata('questionnaire') +const providerPackageMetadata = packageMetadata('providers') + +const profiles = {} +const packageCanonicals = {} +for (const entry of packageGraph.packages) { + packageCanonicals[entry.source] = entry.canonical + for (const profile of entry.profiles) { + if (profiles[profile] !== undefined) { + throw new Error(`Profile id is duplicated: ${profile}.`) + } + profiles[profile] = `${entry.canonical}/StructureDefinition/${profile}` + } +} + +for (const measurement of measurements) { + if (profiles[measurement.profile] === undefined) { + throw new Error( + `Measurement ${measurement.id} refers to unknown profile ${measurement.profile}.`, + ) + } +} + +const selectEntries = (source, keys) => + Object.fromEntries( + keys.map((key) => { + if (!Object.hasOwn(source, key) || source[key] === undefined) { + throw new Error(`Generated contract is missing required key ${key}.`) + } + return [key, source[key]] + }), + ) + +const questionnaireProfiles = selectEntries(profiles, [ + 'grove-questionnaire', + 'grove-questionnaire-response', +]) +const providerProfiles = selectEntries(profiles, [ + ...measurements.map((measurement) => measurement.profile), + 'grove-application-device', + 'grove-mobile-exchange-bundle', + 'grove-recording-device', + 'grove-sensor-recording-document', + 'provider-conversion-provenance', + 'provider-observation', + 'provider-recording-document', +]) +const providerPackageCanonicals = selectEntries(packageCanonicals, [ + 'mobile', + 'sensor', + 'providers', +]) + +if ( + profileClaims.fhirVersion !== '4.0.1' || + profileClaims.observationAdapterClaim?.cardinality !== 2 || + profileClaims.observationAdapterClaim?.inheritedProfilesAreNotDeclared !== + true || + !profileClaims.observationAdapterClaim?.adapterProfiles?.includes( + profiles['provider-observation'], + ) || + !profileClaims.observationAdapterClaim?.forbiddenExplicitProfiles?.includes( + profiles['grove-mobile-observation'], + ) +) { + throw new Error('Observation direct-profile claim is incomplete.') +} + +const expectedHealthConnectGlucoseProfiles = [ + 'health-connect-capillary-blood-glucose', + 'health-connect-interstitial-glucose', + 'health-connect-serum-plasma-glucose', + 'health-connect-whole-blood-glucose', +].map((profile) => profiles[profile]) +const connectedRecordingClaim = profileClaims.providerRecordingDocumentClaim +const connectedProvenanceClaim = + profileClaims.adapterConversionProvenanceClaims?.find( + (claim) => claim.adapter === 'providers', + ) +if ( + profileClaims.healthConnectPlatformExclusiveClaims?.cardinality !== 1 || + expectedHealthConnectGlucoseProfiles.some( + (profile) => + !profileClaims.healthConnectPlatformExclusiveClaims.profiles.includes( + profile, + ), + ) || + connectedRecordingClaim?.cardinality !== 2 || + connectedRecordingClaim.profiles?.[0] !== + profiles['grove-sensor-recording-document'] || + connectedRecordingClaim.profiles?.[1] !== + profiles['provider-recording-document'] || + connectedProvenanceClaim?.profile !== + profiles['provider-conversion-provenance'] +) { + throw new Error('Adapter-specific profile claims are incomplete.') +} + +const expectedSensorContracts = new Map([ + ['sampled-data', profiles['grove-sensor-sampled-data-observation']], + ['ecg', profiles['grove-sensor-ecg-observation']], + ['recording-document', profiles['grove-sensor-recording-document']], + ['conversion-provenance', profiles['grove-sensor-conversion-provenance']], +]) +if ( + sensorCatalog.contracts?.length !== expectedSensorContracts.size || + sensorCatalog.contracts.some( + (contract) => expectedSensorContracts.get(contract.id) !== contract.profile, + ) +) { + throw new Error('Sensor contracts do not match the package graph profiles.') +} +const rawPayloadAssertions = [ + 'caller-authorized-opaque-payload', + 'verified-sanitized-input', +] +const sensorRecordingContract = sensorCatalog.contracts.find( + (contract) => contract.id === 'recording-document', +) +if ( + sensorRecordingContract?.payloadAdmission?.requiredProducerInput !== + 'exactly one explicit caller assertion before emission' || + sensorRecordingContract.payloadAdmission.allowedAssertions?.join(',') !== + rawPayloadAssertions.join(',') +) { + throw new Error('Sensor raw-payload admission assertions are incomplete.') +} + +if ( + healthConnectAdapter.source?.recordTypeCount !== 41 || + healthConnectAdapter.recordTypes?.length !== 41 +) { + throw new Error( + 'Health Connect source inventory must contain all 41 Record types.', + ) +} +const healthConnectProviderSpecificMeasurements = new Set([ + 'blood-glucose', + 'capillary-blood-glucose', + 'interstitial-glucose', + 'serum-plasma-glucose', +]) +for (const recordType of healthConnectAdapter.recordTypes) { + for (const output of recordType.outputs ?? []) { + if ( + implemented[output.measurement] === undefined && + !healthConnectProviderSpecificMeasurements.has(output.measurement) + ) { + throw new Error( + `Health Connect ${recordType.token} refers to unknown measurement ${output.measurement}.`, + ) + } + } +} + +const healthConnectSpecimens = + healthConnectAdapter.contextMappings?.bloodGlucoseSpecimen?.values +if ( + !Array.isArray(healthConnectSpecimens) || + healthConnectSpecimens.filter((entry) => entry.status === 'supported') + .length !== 5 || + !healthConnectSpecimens.some( + (entry) => + entry.id === 'tears' && entry.status === 'intentionally-unsupported', + ) || + !healthConnectSpecimens.some( + (entry) => + entry.id === 'unknown' && entry.status === 'intentionally-unsupported', + ) +) { + throw new Error('Health Connect glucose specimen mapping must fail closed.') +} + +const healthConnectStages = healthConnectAdapter.contextMappings?.sleepStage +if ( + healthConnectStages?.sourceCodeSystem !== + 'https://grovealliance.org/fhir/health-connect/CodeSystem/health-connect-sleep-stage' || + healthConnectStages.values?.length !== 8 || + healthConnectStages.values.some( + (entry) => !sleepStage.allowedValues.includes(entry.shared), + ) +) { + throw new Error('Health Connect sleep-stage mapping is incomplete.') +} + +if ( + healthConnectIdentity.composition?.separator !== '|' || + healthConnectIdentity.composition?.versionPrefix !== 'v1' || + healthConnectIdentity.vectors?.length !== 7 +) { + throw new Error( + 'Health Connect deterministic identity vectors are incomplete.', + ) +} + +const healthKitSourceRowCount = healthKitAdapter.source?.rowCount +const healthKitDerivedAggregateCount = + healthKitAdapter.source?.derivedAggregateCount ?? 0 +const healthKitRows = healthKitAdapter.rows +const healthKitDerivedAggregates = healthKitAdapter.derivedAggregates ?? [] +const healthKitStatusVocabulary = new Set( + Object.keys(healthKitAdapter.statusVocabulary ?? {}), +) +if ( + !Number.isInteger(healthKitSourceRowCount) || + healthKitSourceRowCount <= 0 || + !Array.isArray(healthKitRows) || + healthKitRows.length !== healthKitSourceRowCount || + new Set(healthKitRows.map((entry) => entry.sourceTypeIdentifier)).size !== + healthKitSourceRowCount || + healthKitRows.some( + (entry) => + typeof entry.sourceTypeIdentifier !== 'string' || + entry.sourceTypeIdentifier.trim().length === 0 || + !healthKitStatusVocabulary.has(entry.status) || + !Array.isArray(entry.measurementIDs) || + !Array.isArray(entry.profiles), + ) +) { + throw new Error( + 'HealthKit source rows must exactly match the catalog-owned public source inventory.', + ) +} +if ( + !Number.isInteger(healthKitDerivedAggregateCount) || + healthKitDerivedAggregateCount < 0 || + !Array.isArray(healthKitDerivedAggregates) || + healthKitDerivedAggregates.length !== healthKitDerivedAggregateCount || + new Set(healthKitDerivedAggregates.map((entry) => entry.id)).size !== + healthKitDerivedAggregateCount || + healthKitDerivedAggregates.some( + (entry) => + typeof entry.id !== 'string' || + entry.id.trim().length === 0 || + !healthKitStatusVocabulary.has(entry.status) || + !Array.isArray(entry.sourceTypeIdentifiers) || + entry.sourceTypeIdentifiers.length === 0 || + !Array.isArray(entry.measurementIDs) || + !Array.isArray(entry.profiles), + ) +) { + throw new Error( + 'HealthKit derived aggregates must remain a complete, separate catalog-owned inventory.', + ) +} + +const expectedConnectedProviders = ['google-health-api', 'oura', 'withings'] +const providerAdapterPath = resolve(catalogRoot, 'providers-adapter.json') +const providerAdapter = JSON.parse(await readFile(providerAdapterPath, 'utf8')) +if ( + providerAdapter.fhirVersion !== '4.0.1' || + providerAdapter.version !== packageGraph.version || + providerAdapter.packageId !== 'org.grovealliance.fhir.providers' || + providerAdapter.canonical !== packageCanonicals['providers'] || + providerAdapter.adapterProfile !== profiles['provider-observation'] || + providerAdapter.recordingDocument?.sourceNeutralProfile !== + profiles['grove-sensor-recording-document'] || + providerAdapter.recordingDocument?.adapterProfile !== + profiles['provider-recording-document'] || + providerAdapter.recordingDocument?.outputDiscriminator !== + 'native-recording' || + providerAdapter.rawPayloadAdmission?.allowedAssertions?.join(',') !== + rawPayloadAssertions.join(',') || + providerAdapter.rawPayloadAdmission?.notFHIRAuthorization !== true || + providerAdapter.conversionProvenanceProfile !== + profiles['provider-conversion-provenance'] || + providerAdapter.sourceTypeExtension?.url !== + `${packageCanonicals['providers']}/StructureDefinition/provider-source-type` || + providerAdapter.sourceTypeExtension?.codeSystem !== + `${packageCanonicals['providers']}/CodeSystem/provider-source-type` +) { + throw new Error('Provider adapter catalog metadata is inconsistent.') +} + +if ( + !Array.isArray(providerAdapter.providers) || + providerAdapter.providers.map((provider) => provider.id).join(',') !== + expectedConnectedProviders.join(',') +) { + throw new Error('Provider providers must be the exact closed provider set.') +} + +const providerScalarMappings = {} +const providerRawMappings = {} +const providerRecordEffectiveRules = {} +const completeCivilDayEffectiveRule = + 'the source civil day represented as a complete day Period; midpoint substitution is forbidden' +for (const provider of providerAdapter.providers) { + if ( + provider.sourceTypes?.length !== provider.sourceTypeCount || + new Set(provider.sourceTypes.map((entry) => entry.token)).size !== + provider.sourceTypeCount + ) { + throw new Error(`Provider ${provider.id} source inventory is incomplete.`) + } + + const sourceMappings = {} + const rawSourceMappings = {} + const recordEffectiveRules = {} + for (const sourceType of provider.sourceTypes) { + const supportedElements = (sourceType.elements ?? []).filter( + (element) => element.status === 'supported', + ) + const measurementIds = [ + ...new Set( + (sourceType.elements ?? []) + .filter( + (element) => + element.status === 'supported' && + element.groupedMapping === undefined, + ) + .flatMap((element) => element.measurementIds ?? []), + ), + ] + for (const measurementId of measurementIds) { + if (implemented[measurementId] === undefined) { + throw new Error( + `Provider ${provider.id}/${sourceType.token} refers to unknown measurement ${measurementId}.`, + ) + } + } + if (measurementIds.length > 0) { + sourceMappings[sourceType.token] = Object.fromEntries( + measurementIds.map((measurementId) => [measurementId, measurementId]), + ) + } + const effectiveRules = [ + ...new Set( + supportedElements + .map((element) => element.effective) + .filter((rule) => typeof rule === 'string'), + ), + ] + if (effectiveRules.includes(completeCivilDayEffectiveRule)) { + if ( + effectiveRules.length !== 1 || + supportedElements.some( + (element) => element.effective !== completeCivilDayEffectiveRule, + ) + ) { + throw new Error( + `Provider ${provider.id}/${sourceType.token} mixes complete-civil-day and incompatible effective rules.`, + ) + } + recordEffectiveRules[sourceType.token] = { + kind: 'complete-civil-day-period', + measurementIds, + outputsShareEffective: true, + } + } + const rawElements = (sourceType.elements ?? []).filter( + (element) => element.status === 'mapped-standard', + ) + if (rawElements.length > 0) { + if ( + rawElements.some( + (element) => + element.sensorProfile !== + profiles['grove-sensor-recording-document'], + ) + ) { + throw new Error( + `Provider ${provider.id}/${sourceType.token} has an unreviewed raw mapping.`, + ) + } + rawSourceMappings[sourceType.token] = + providerAdapter.recordingDocument.outputDiscriminator + } + } + for (const grouped of provider.groupedMappings ?? []) { + if ( + grouped.status !== 'supported' || + typeof grouped.token !== 'string' || + typeof grouped.outputDiscriminator !== 'string' || + !Array.isArray(grouped.measurementIds) || + grouped.measurementIds.length === 0 + ) { + throw new Error(`Provider ${provider.id} grouped mapping is incomplete.`) + } + for (const measurementId of grouped.measurementIds) { + if (implemented[measurementId] === undefined) { + throw new Error( + `Provider ${provider.id}/${grouped.token} refers to unknown measurement ${measurementId}.`, + ) + } + } + sourceMappings[grouped.token] = Object.fromEntries( + grouped.measurementIds.map((measurementId) => [ + measurementId, + grouped.outputDiscriminator, + ]), + ) + } + for (const [sourceType, mappings] of Object.entries(sourceMappings)) { + const discriminators = Object.values(mappings) + if (new Set(discriminators).size !== discriminators.length) { + throw new Error( + `Provider ${provider.id}/${sourceType} output discriminators must be unique.`, + ) + } + } + providerScalarMappings[provider.id] = sourceMappings + providerRawMappings[provider.id] = rawSourceMappings + if (Object.keys(recordEffectiveRules).length > 0) { + providerRecordEffectiveRules[provider.id] = recordEffectiveRules + } +} + +if ( + JSON.stringify(providerRecordEffectiveRules) !== + JSON.stringify({ + oura: { + daily_activity: { + kind: 'complete-civil-day-period', + measurementIds: ['step-count', 'active-energy', 'distance'], + outputsShareEffective: true, + }, + }, + withings: { + 'getactivity:steps': { + kind: 'complete-civil-day-period', + measurementIds: ['step-count'], + outputsShareEffective: true, + }, + 'getactivity:distance': { + kind: 'complete-civil-day-period', + measurementIds: ['distance'], + outputsShareEffective: true, + }, + 'getactivity:calories': { + kind: 'complete-civil-day-period', + measurementIds: ['active-energy'], + outputsShareEffective: true, + }, + }, + }) +) { + throw new Error( + 'Provider complete-civil-day record rules changed unexpectedly.', + ) +} + +if ( + Object.values(providerRawMappings).flatMap((mapping) => Object.keys(mapping)) + .length !== 4 +) { + throw new Error('Provider must admit exactly four raw source tokens.') +} + +const providerIdentity = providerAdapter.identity +if ( + providerIdentity?.digest !== undefined || + providerIdentity.sourceRecord?.system !== + 'https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id' || + providerIdentity.output?.system !== + 'https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id' || + providerIdentity.output?.outputDiscriminatorRule + ?.ordinarySupportedMeasurement !== + 'the exact measurementId string from the supported element mapping' || + providerIdentity.output?.outputDiscriminatorRule?.groupedMapping !== + 'the exact outputDiscriminator declared on that groupedMappings row' || + providerIdentity.output?.outputDiscriminatorRule?.mappedStandardRaw !== + 'native-recording' || + providerIdentity.output?.outputDiscriminatorRule?.noFallback !== true || + providerIdentity.vectors?.length !== 9 || + providerIdentity.vectors.filter((vector) => vector.role === 'sourceRecord') + .length !== 3 || + providerIdentity.vectors.filter((vector) => vector.role === 'output') + .length !== 2 || + providerIdentity.vectors.filter((vector) => vector.role === 'contentDerived') + .length !== 1 || + providerIdentity.vectors.filter((vector) => vector.role === 'writerRecord') + .length !== 1 || + providerIdentity.composition?.separator !== '|' || + // Export-created nodes moved to the deployment's namespace, so this guide owns neither. + providerIdentity.conversion !== undefined || + providerIdentity.exchange !== undefined +) { + throw new Error('Provider deterministic identity contract is incomplete.') +} + +if ( + formatRegistry.fhirVersion !== '4.0.1' || + formatRegistry.version !== packageGraph.version || + typeof formatRegistry.codeSystem !== 'string' || + typeof formatRegistry.valueSet !== 'string' || + Object.keys(formatRegistry.formats ?? {}).length === 0 +) { + throw new Error('Recording format registry is incomplete.') +} +// The registry's payload specifications stay in the IG; the TypeScript contract +// carries only what emission and admission need. +const recordingFormatRegistry = { + codeSystem: formatRegistry.codeSystem, + valueSet: formatRegistry.valueSet, + formats: Object.fromEntries( + Object.entries(formatRegistry.formats).map(([code, entry]) => { + if ( + typeof entry.title !== 'string' || + typeof entry.contentType !== 'string' || + entry.status !== 'active' + ) { + throw new Error(`Recording format ${code} is incomplete.`) + } + return [ + code, + { + title: entry.title, + contentType: entry.contentType, + status: entry.status, + }, + ] + }), + ), +} + +const providerAdmittedMeasurements = new Set( + Object.values(providerScalarMappings).flatMap((sourceMappings) => + Object.values(sourceMappings).flatMap((mapping) => Object.keys(mapping)), + ), +) +// The Provider facade emits shared Mobile profiles only; owner-exclusive +// mappings stay in the contract data but are outside the implemented surface. +const sharedAdmittedMeasurements = new Set( + [...providerAdmittedMeasurements].filter((id) => + isMobileOwned(implemented[id]), + ), +) +for (const row of healthKitAdapter.rows) { + for (const measurement of row.measurementIDs) { + const expectedProfiles = + measurement === 'electrocardiogram' ? + [ + profiles['grove-sensor-ecg-observation'], + profiles['healthkit-ecg-observation'], + ] + : measurement === 'body-mass-index' ? + [ + 'http://hl7.org/fhir/StructureDefinition/bmi', + profiles['healthkit-observation'], + ] + : [profiles[implemented[measurement]?.profile]] + if ( + expectedProfiles.some( + (expectedProfile) => !row.profiles.includes(expectedProfile), + ) + ) { + throw new Error( + `HealthKit ${row.sourceTypeIdentifier} refers to an unknown or mismatched measurement ${measurement}.`, + ) + } + } +} + +if ( + exchangeIdentity.profile !== profiles['grove-mobile-exchange-bundle'] || + typeof exchangeIdentity.entryIdentifierExtension !== 'string' || + exchangeIdentity.fullUrlAlgorithm?.name !== 'uuid-v5-composed-identifier-v1' +) { + throw new Error('Exchange identity catalog is incomplete or inconsistent.') +} + +for (const vector of exchangeIdentity.vectors ?? []) { + // A rejection vector states no name and no URN: it exists to record what is refused. + if (vector.input === null && vector.fullUrl === null) { + continue + } + if ( + typeof vector.system !== 'string' || + typeof vector.value !== 'string' || + typeof vector.input !== 'string' || + typeof vector.fullUrl !== 'string' + ) { + throw new Error('Exchange identity vectors must be complete.') + } +} + +const implementedCapabilities = capabilities.measurements?.filter( + (measurement) => measurement.status === 'implemented', +) +const recordingCapabilities = capabilities.measurements?.filter( + (measurement) => measurement.status === 'implemented-recording', +) +const adapterProfiledCapabilities = capabilities.measurements?.filter( + (measurement) => measurement.status === 'adapter-profiled', +) +const capabilityKeys = capabilities.measurements?.map((entry) => entry.key) +const capabilityStatuses = new Set(Object.keys(capabilities.statuses ?? {})) +if (capabilities.igVersion !== catalog.version) { + throw new Error( + `Capability matrix declares IG version ${String(capabilities.igVersion)} against catalog ${String(catalog.version)}.`, + ) +} +if ( + !Array.isArray(capabilityKeys) || + new Set(capabilityKeys).size !== capabilityKeys.length || + capabilities.measurements.some( + (entry) => + !capabilityStatuses.has(entry.status) || + !Array.isArray(entry.sources) || + entry.sources.length === 0, + ) +) { + throw new Error( + 'Capability matrix rows require unique keys, known statuses, and source coverage.', + ) +} +if ( + !Array.isArray(implementedCapabilities) || + implementedCapabilities.length !== sharedAdmittedMeasurements.size +) { + throw new Error( + 'Capability matrix implemented rows must exactly match the Provider facade.', + ) +} +const rawMappingRows = Object.entries(providerRawMappings).flatMap( + ([provider, mappings]) => + Object.entries(mappings).map(([sourceType, outputDiscriminator]) => ({ + provider, + sourceType, + outputDiscriminator, + })), +) +if ( + !Array.isArray(recordingCapabilities) || + recordingCapabilities.length !== rawMappingRows.length || + rawMappingRows.some( + (mapping) => + !recordingCapabilities.some( + (capability) => + capability.provider === mapping.provider && + capability.sourceType === mapping.sourceType && + capability.outputDiscriminator === mapping.outputDiscriminator && + capability.profile === profiles['provider-recording-document'] && + capability.sourceNeutralProfile === + profiles['grove-sensor-recording-document'], + ), + ) +) { + throw new Error( + 'Capability matrix native-recording rows must exactly match the Provider catalog.', + ) +} +const expectedAdapterProfiledCapabilities = new Map([ + ['body-mass-index', 'http://hl7.org/fhir/StructureDefinition/bmi'], + ['blood-glucose', profiles['health-connect-whole-blood-glucose']], + [ + 'capillary-blood-glucose', + profiles['health-connect-capillary-blood-glucose'], + ], + ['serum-plasma-glucose', profiles['health-connect-serum-plasma-glucose']], + ['interstitial-glucose', profiles['health-connect-interstitial-glucose']], +]) +if ( + !Array.isArray(adapterProfiledCapabilities) || + adapterProfiledCapabilities.length !== + expectedAdapterProfiledCapabilities.size || + adapterProfiledCapabilities.some( + (capability) => + expectedAdapterProfiledCapabilities.get(capability.key) !== + capability.profile, + ) +) { + throw new Error( + 'Capability matrix adapter-profiled rows must match the IG profile claims.', + ) +} +for (const normative of measurements) { + const capability = capabilities.measurements.find( + (entry) => entry.key === normative.id, + ) + if (!isMobileOwned(normative)) { + if ( + capability !== undefined && + (capability.status !== 'platform-exclusive' || + capability.profile !== normative.profile) + ) { + throw new Error( + `Owner-exclusive capability ${normative.id} must stay outside the shared facade.`, + ) + } + continue + } + const admitted = sharedAdmittedMeasurements.has(normative.id) + if ( + capability === undefined || + capability.status !== + (admitted ? 'implemented' : 'profiled-not-admitted') || + capability.profile !== normative.profile || + capability.effective !== normative.effective || + capability.code?.system !== normative.code.system || + capability.code?.code !== normative.code.code || + (normative.quantity !== null && + capability.unit?.code !== normative.quantity.code) || + !Array.isArray(capability.sources) + ) { + throw new Error( + `Implemented capability ${String(capability?.key ?? normative.id)} does not match the shared IG catalog.`, + ) + } +} + +const semanticDefinition = (definition) => { + const semantic = { ...definition } + delete semantic.coverage + delete semantic.coverageDetails + delete semantic.generation + return semantic +} + +const sharedMobileSemanticCatalog = Object.fromEntries( + mobileMeasurements.map((measurement) => [ + measurement.id, + semanticDefinition(measurement), + ]), +) + +const adapterMeasurementCatalog = {} +for (const measurement of measurements) { + if (isMobileOwned(measurement)) continue + adapterMeasurementCatalog[measurement.owner] ??= {} + adapterMeasurementCatalog[measurement.owner][measurement.id] = + semanticDefinition(measurement) +} + +const generated = { + sourceRef, + packageGraph, + mobilePackageMetadata, + questionnairePackageMetadata, + providerPackageMetadata, + exchangeIdentity, + profileClaims, + packageCanonicals, + profiles, + measurements: implemented, + effectiveCanonicalization, + effectiveCanonicalizationVectors: semanticEffectiveCanonicalization.vectors, + providerAdapter, + providerScalarMappings, + providerRawMappings, + providerRecordEffectiveRules, + questionnaireProfiles, + providerProfiles, + providerPackageCanonicals, + adapterMeasurementCatalog, + recordingFormatRegistry, +} + +for (const value of Object.values(generated)) { + if (value === undefined) { + throw new Error('Generated catalog values must be defined.') + } +} + +const frozenExport = (name, value) => { + const valueName = `${name}Value` + return `const ${valueName} = ${JSON.stringify(value, null, 2)} as const + +export const ${name}: typeof ${valueName} = deepFreeze(${valueName})` +} + +const header = (disableClearText) => `// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// +// Generated by scripts/generate-measurement-catalog.mjs. Do not edit directly. + +${disableClearText ? '/* eslint-disable sonarjs/no-clear-text-protocols */\n' : ''} + +import { deepFreeze } from '../core/index.js' + +` +const versions = `export const groveFhirContractVersion = ${JSON.stringify(packageGraph.version)} as const + +export const groveFhirVersion = ${JSON.stringify(packageGraph.fhirVersion)} as const +` + +const mobileUnformattedOutput = `${header(true)}${versions} + +${frozenExport('groveMobilePackageMetadata', mobilePackageMetadata)} + +${frozenExport('groveFhirExchangeIdentity', exchangeIdentity)} + +${frozenExport('sharedMobileMeasurementCatalog', sharedMobileSemanticCatalog)} + +${frozenExport('mobileEffectiveCanonicalization', effectiveCanonicalization)} + +${frozenExport('mobileEffectiveCanonicalizationVectors', semanticEffectiveCanonicalization.vectors)} + +export type SharedMobileMeasurementKind = keyof typeof sharedMobileMeasurementCatalog +` + +const questionnaireUnformattedOutput = `${header(false)}${versions} + +${frozenExport('groveQuestionnairePackageMetadata', questionnairePackageMetadata)} + +${frozenExport('groveQuestionnaireProfileCanonicals', questionnaireProfiles)} +` + +const providerUnformattedOutput = `${header(true)}${versions} + +${frozenExport('groveProviderPackageMetadata', providerPackageMetadata)} + +${frozenExport('groveProviderPackageCanonicals', providerPackageCanonicals)} + +${frozenExport('groveProviderProfileCanonicals', providerProfiles)} + +${frozenExport('providerAdapterCatalog', providerAdapter)} + +${frozenExport('adapterMeasurementCatalog', adapterMeasurementCatalog)} + +${frozenExport('providerScalarMappings', providerScalarMappings)} + +${frozenExport('providerRecordEffectiveRules', providerRecordEffectiveRules)} + +${frozenExport('providerRawMappings', providerRawMappings)} + +${frozenExport('groveRecordingFormatRegistry', recordingFormatRegistry)} + +export type AdapterMeasurementCatalog = typeof adapterMeasurementCatalog + +export type ProviderRecordingFormat = + keyof typeof groveRecordingFormatRegistry.formats + +export type ProviderScalarMappings = typeof providerScalarMappings + +export type ProviderRecordEffectiveRules = typeof providerRecordEffectiveRules + +export type ProviderRawMappings = typeof providerRawMappings +` + +const unformattedOutputs = { + mobile: mobileUnformattedOutput, + questionnaire: questionnaireUnformattedOutput, + provider: providerUnformattedOutput, +} +for (const [scope, unformattedOutput] of Object.entries(unformattedOutputs)) { + const output = await format(unformattedOutput, { + parser: 'typescript', + semi: false, + singleQuote: true, + }) + const outputPath = outputPaths[scope] + if (argv.includes('--check')) { + const existing = await readFile(outputPath, 'utf8') + if (existing !== output) { + throw new Error( + `Generated ${scope} catalog is stale. Run npm run generate:catalog.`, + ) + } + } else { + await writeFile(outputPath, output) + } +} diff --git a/packages/fhir/scripts/generate-zod-schemas.d.mts b/packages/fhir/scripts/generate-zod-schemas.d.mts new file mode 100644 index 00000000..a7e7b12a --- /dev/null +++ b/packages/fhir/scripts/generate-zod-schemas.d.mts @@ -0,0 +1,29 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/** A FHIR package the generator reads, pinned by version and content digest. */ +export interface FhirPackageReference { + readonly packageId: string + readonly version: string + /** SHA-512 of the published tarball, verified before extraction. */ + readonly integrity?: string +} + +/** The example corpora each release publishes, for validating against instances we did not author. */ +export declare const EXAMPLE_PACKAGES: Readonly< + Record<'r4' | 'r4b', FhirPackageReference> +> + +/** + * Downloads and unpacks a FHIR package once, then reuses it. + * + * @returns The absolute path of the package's `package` directory. + */ +export declare function fhirPackage( + reference: FhirPackageReference, +): Promise diff --git a/packages/fhir/scripts/generate-zod-schemas.mjs b/packages/fhir/scripts/generate-zod-schemas.mjs new file mode 100644 index 00000000..0cf9e577 --- /dev/null +++ b/packages/fhir/scripts/generate-zod-schemas.mjs @@ -0,0 +1,1048 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/** + * Generate Zod runtime schemas from a FHIR release's own StructureDefinitions. + * + * Nothing here is transcribed by hand. Cardinality, choice types, required-binding enumerations + * and even the primitive regexes come from the published definitions, so a schema cannot drift + * from the specification it claims to implement, and a second release costs one argument rather + * than a second corpus. + * + * Usage: + * node scripts/generate-zod-schemas.mjs --release r4 + * node scripts/generate-zod-schemas.mjs --release r4b --check + */ + +import { createHash } from 'node:crypto' +import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises' +import { existsSync } from 'node:fs' +import { dirname, join, resolve } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' +import { spawnSync } from 'node:child_process' +import { format, resolveConfig } from 'prettier' +import { Buffer } from 'node:buffer' +import process, { argv, stdout } from 'node:process' + +const { fetch } = globalThis + +const HERE = dirname(fileURLToPath(import.meta.url)) +const PACKAGE_ROOT = resolve(HERE, '..') +const CACHE_ROOT = resolve(PACKAGE_ROOT, '.fhir-cache') +const REGISTRY = 'https://packages2.fhir.org/packages' + +export const EXAMPLE_PACKAGES = { + r4: { + packageId: 'hl7.fhir.r4.examples', + version: '4.0.1', + integrity: + '301666e0cba6d960aa20abfdf95aea57a8c1c0b9365b704a80dee3394d238eedda50fcc3bddfe2446a8c7e282b90725f4039bd4adc75c20f29b7fbc140c3d43c', + }, + r4b: { + packageId: 'hl7.fhir.r4b.examples', + version: '4.3.0', + integrity: + 'de84af61ad4ca5aa6b983e306136c639f343077ca358b4bdd82ad0f0864c1b7695b2b13b413326ccdfb821947309813f1672ee23fbcc4433675b41ec1e3a1725', + }, +} + +const RELEASES = { + r4: { + packageId: 'hl7.fhir.r4.core', + version: '4.0.1', + fhirVersion: '4.0.1', + integrity: + '0981e52db3ffd9fede2809059d63c67b2837e5c9d749e5e0f16229c1784f178001616a9af65c34572f2a74c5a287fe3b4f4801297447deb3dacb9ad48b81b584', + }, + r4b: { + packageId: 'hl7.fhir.r4b.core', + version: '4.3.0', + fhirVersion: '4.3.0', + integrity: + '943ac3376cd2fca94d75c26e43f2609c192723d180240b197517c3dcb757cc258c727a6f69cca7f82f7fc7540779e96d1a2eec17ff8048945b79315b01a16933', + }, +} + +/** + * The seed of the schema closure: every concrete resource the release publishes, plus the + * datatypes Grove names directly. + * + * The package validates whole resources, not only the ones Grove itself builds: a resource + * arrives contained, bundled, or referenced beside the ones that point at it, and a consumer + * that meets it should get the same field-level checking as everything else. Seeding from the + * release rather than from a hand-kept list means a resource cannot be missing because nobody + * remembered to add it. + */ +const DATATYPE_ROOTS = [ + 'Attachment', + 'CodeableConcept', + 'Coding', + 'Extension', + 'Identifier', + 'Period', + 'Quantity', + 'Reference', + 'SampledData', +] + +/** Every concrete resource the release publishes. */ +const resourceRoots = (structures) => + [...structures.values()] + .filter( + (structure) => + structure.kind === 'resource' && + structure.abstract !== true && + structure.name !== 'Resource', + ) + .map((structure) => structure.name) + +/** Downloads and unpacks a FHIR core package once, then reuses it. */ +export async function fhirPackage({ packageId, version, integrity }) { + const target = join(CACHE_ROOT, `${packageId}#${version}`) + if (existsSync(join(target, 'package'))) { + return join(target, 'package') + } + await mkdir(target, { recursive: true }) + const url = `${REGISTRY}/${packageId}/${version}` + const response = await fetch(url) + if (!response.ok) { + throw new Error(`Could not fetch ${url}: ${response.status}`) + } + const bytes = Buffer.from(await response.arrayBuffer()) + // A registry tarball is mutable in principle; the digest pins what this generator was built + // against, so a substituted archive fails the build rather than silently changing the output. + const digest = createHash('sha512').update(bytes).digest('hex') + if (integrity !== undefined && digest !== integrity) { + throw new Error( + `${packageId}#${version} does not match its pinned digest.\n expected ${integrity}\n actual ${digest}`, + ) + } + const archive = join(target, 'package.tgz') + await writeFile(archive, bytes) + const result = spawnSync( + 'tar', + ['xzf', archive, '--no-same-owner', '-C', target], + { + stdio: 'inherit', + }, + ) + if (result.status !== 0) { + throw new Error(`Could not unpack ${archive}`) + } + return join(target, 'package') +} + +/** Every StructureDefinition and ValueSet the release publishes, keyed by name/url. */ +async function loadDefinitions(packageDir) { + const structures = new Map() + const valueSets = new Map() + const codeSystems = new Map() + for (const entry of await readdir(packageDir)) { + if (!entry.endsWith('.json')) continue + if (!/^(StructureDefinition|ValueSet|CodeSystem)-/.test(entry)) continue + let resource + try { + resource = JSON.parse(await readFile(join(packageDir, entry), 'utf8')) + } catch { + continue + } + if (resource.resourceType === 'StructureDefinition') { + if (resource.derivation === 'constraint') continue + structures.set(resource.name, resource) + } else if (resource.resourceType === 'ValueSet') + valueSets.set(resource.url, resource) + else if (resource.resourceType === 'CodeSystem') + codeSystems.set(resource.url, resource) + } + return { structures, valueSets, codeSystems } +} + +/** + * Translate an XML Schema regex into JavaScript's dialect. + * + * FHIR publishes its primitive patterns as XSD regexes, where `\s` is exactly `[ \t\n\r]`. + * JavaScript's `\s` also matches vertical tab, form feed and the Unicode space separators, so + * its `\S` wrongly excludes characters XSD counts as non-space — U+00A0 among them, which the + * published examples use in narrative text. Position matters: inside a character class the two + * expand to members, outside it to a class of their own. + */ +const XSD_SPACE = ' \\t\\n\\r' +/** The characters JavaScript calls space and XSD does not, so an XSD `\S` still admits them. */ +const XSD_NON_SPACE_EXTRA = + '\\v\\f\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff' + +function xsdRegexToJs(regex) { + let out = '' + let index = 0 + while (index < regex.length) { + const character = regex[index] + if (character === '\\') { + const next = regex[index + 1] + if (next === 's') out += `[${XSD_SPACE}]` + else if (next === 'S') out += `[^${XSD_SPACE}]` + else out += character + (next ?? '') + index += 2 + continue + } + if (character !== '[') { + out += character + index += 1 + continue + } + // A character class: JavaScript cannot nest one, so `\s` and `\S` are rewritten to members. + let close = index + 1 + if (regex[close] === '^') close += 1 + if (regex[close] === ']') close += 1 + while (close < regex.length && regex[close] !== ']') { + close += regex[close] === '\\' ? 2 : 1 + } + const body = regex.slice(index + 1, close) + out += renderClass(body) + index = close + 1 + } + return out +} + +/** One XSD character class as its JavaScript equivalent, brackets included. */ +function renderClass(body) { + const negated = body.startsWith('^') + const members = negated ? body.slice(1) : body + const hasNonSpace = /\\S/u.test(members) + const listsEveryXsdSpace = [' ', '\\t', '\\n', '\\r'].every((space) => + members.includes(space), + ) + // XSD `\S` is every character except space, tab, newline and return. A class that also lists + // all four therefore matches everything, and saying so plainly beats a union that repeats + // itself — which is what FHIR's `string` and `markdown` patterns are. + if (!negated && hasNonSpace && listsEveryXsdSpace) return '[\\s\\S]' + const rendered = members + .replaceAll('\\S', `\\S${XSD_NON_SPACE_EXTRA}`) + .replaceAll('\\s', XSD_SPACE) + return `[${negated ? '^' : ''}${rendered}]` +} + +/** The regex a primitive constrains its lexical form with, as the release publishes it. */ +function primitiveRegex(structure) { + const value = (structure.snapshot?.element ?? []).find( + (element) => element.path === `${structure.name}.value`, + ) + for (const type of value?.type ?? []) { + for (const extension of type.extension ?? []) { + if (extension.url?.endsWith('/regex')) return extension.valueString + } + } + return undefined +} + +/** The numeric range a primitive states, or inherits from the type it specialises. */ +function numericBounds(structure, { structures }) { + const valueOf = (candidate) => + (candidate?.snapshot?.element ?? []).find( + (element) => element.path === `${candidate.name}.value`, + ) + const own = valueOf(structure) + const baseName = (structure.baseDefinition ?? '').split('/').pop() + const base = baseName ? valueOf(structures.get(baseName)) : undefined + return { + min: own?.minValueInteger ?? base?.minValueInteger, + max: own?.maxValueInteger ?? base?.maxValueInteger, + } +} + +/** The codes a required binding admits, when the release can enumerate them offline. */ +function requiredCodes(element, { valueSets, codeSystems }) { + if (element.binding?.strength !== 'required') return undefined + const url = (element.binding.valueSet ?? '').split('|')[0] + const valueSet = valueSets.get(url) + if (!valueSet) return undefined + const codes = new Set() + // An exclusion would make the enumeration wrong rather than merely incomplete. + if (valueSet.compose?.exclude !== undefined) return undefined + for (const include of valueSet.compose?.include ?? []) { + if (include.filter || include.valueSet) return undefined + if (include.concept) { + for (const concept of include.concept) codes.add(concept.code) + continue + } + const system = codeSystems.get(include.system) + if (!system || system.content !== 'complete') return undefined + const walk = (concepts) => { + for (const concept of concepts ?? []) { + codes.add(concept.code) + walk(concept.concept) + } + } + walk(system.concept) + } + return codes.size > 0 ? [...codes].sort() : undefined +} + +/** The direct children of a path in the snapshot. */ +function childrenOf(elements, path) { + return elements.filter( + (element) => + element.path.startsWith(`${path}.`) && + !element.path.slice(path.length + 1).includes('.'), + ) +} + +const SCHEMA_NAMES = new Map() +const schemaName = (name) => { + if (!SCHEMA_NAMES.has(name)) { + SCHEMA_NAMES.set( + name, + `${name.charAt(0).toLowerCase()}${name.slice(1)}Schema`, + ) + } + return SCHEMA_NAMES.get(name) +} + +/** The bounds a repeating element states, so a 1..* element cannot be satisfied by `[]`. */ +function arrayBounds(element) { + let rendered = '.array()' + if (element.min > 0) rendered += `.min(${element.min})` + const max = Number(element.max) + if (Number.isFinite(max) && max > 1) rendered += `.max(${max})` + return rendered +} + +/** Renders the Zod expression for one element, honouring cardinality and choice types. */ +function renderElement(element, context, emit) { + const { structures } = context + const types = element.type ?? [] + if (types.length === 0) { + if (!element.contentReference) return undefined + const target = element.contentReference.replace('#', '') + if (!target.startsWith(`${element.path.split('.')[0]}.`)) { + throw new Error( + `${element.path}: contentReference ${element.contentReference} points outside this ` + + 'structure, which the generator does not resolve.', + ) + } + const optional = element.min === 0 + const isArray = element.max === '*' || Number(element.max) > 1 + let expression = `z.lazy(() => ${emit.reference(target)})` + if (isArray) expression = `${expression}${arrayBounds(element)}` + if (optional) expression = `${expression}.optional()` + return { expression } + } + + const isChoice = element.path.endsWith('[x]') + const optional = element.min === 0 + const isArray = element.max === '*' || Number(element.max) > 1 + + let sawPrimitive = false + const single = (typeCode, type) => { + if (typeCode === 'BackboneElement' || typeCode === 'Element') { + return emit.backbone(element) + } + // Elements like Resource.id are typed as FHIRPath system types; the release names the FHIR + // primitive they stand for in an extension, so the constraint is not lost. + if (typeCode.startsWith('http://hl7.org/fhirpath/System')) { + const named = (type?.extension ?? []).find((extension) => + extension.url?.endsWith('/structuredefinition-fhir-type'), + )?.valueUrl + if (!named) return 'z.string()' + // Element.id claims the `id` type, but every StructureDefinition the specification + // publishes gives its elements ids like `DataRequirement.subject[x]` and `Foo.bar:slice`, + // which that pattern forbids. Reading it strictly would reject FHIR's own artifacts, so + // an element id stays a plain string; Resource.id, which the rule actually describes, + // keeps the constraint. + if ( + named === 'id' && + element.path?.endsWith('.id') && + !isResourceId(element) + ) { + return 'z.string()' + } + sawPrimitive = true + return emit.primitive(named) + } + const structure = structures.get(typeCode) + if (structure && structure.kind === 'primitive-type') { + sawPrimitive = true + return emit.primitive(typeCode) + } + if (structure?.abstract === true && structure.kind === 'resource') { + // Any resource may appear here, so the slot dispatches on resourceType to the schema for + // that type. The dispatcher is emitted once, after the closure is known, and reached + // through z.lazy because a resource may carry a Bundle that carries it again. + context.abstractResources.add(typeCode) + return `z.lazy(() => ${schemaName(typeCode)})` + } + if ( + structure && + (structure.kind === 'complex-type' || structure.kind === 'resource') + ) { + emit.require(typeCode) + return `z.lazy(() => ${schemaName(typeCode)})` + } + // Nothing here should be reachable for the configured roots. Failing loudly is the point: + // a silent z.unknown() is how an element stops being validated without anyone noticing. + throw new Error( + `${element.path}: the release models no structure for type ${typeCode}. Add it to the ` + + 'generator rather than emitting an unvalidated element.', + ) + } + + if (isChoice) { + // value[x] expands to one concrete property per admitted type; the object requires that at + // most one is present, which is what the specification means by a choice. + const base = element.path.split('.').pop().replace('[x]', '') + return { + choice: types.map((type) => { + sawPrimitive = false + const expression = single(type.code, type) + return { + name: `${base}${type.code.charAt(0).toUpperCase()}${type.code.slice(1)}`, + expression, + primitiveSibling: sawPrimitive, + } + }), + optional, + } + } + + // A binding may sit on a Coding, CodeableConcept or Quantity as well as on a primitive, and + // there it constrains a code *inside* the datatype rather than replacing it: rendering those + // as a bare enum would reject the very object the element is declared to carry. + const BINDABLE_PRIMITIVES = new Set(['code', 'string', 'uri']) + const codes = + BINDABLE_PRIMITIVES.has(types[0].code) ? + requiredCodes(element, context) + : undefined + let expression + if (codes) { + // A bound code is still a primitive, so it keeps its right to carry an extension sibling. + sawPrimitive = true + expression = `z.enum([${codes.map((code) => JSON.stringify(code)).join(', ')}])` + } else { + expression = single(types[0].code, types[0]) + } + if (isArray) expression = `${expression}${arrayBounds(element)}` + if (optional) expression = `${expression}.optional()` + return { + expression, + primitiveSibling: sawPrimitive, + siblingIsArray: isArray, + } +} + +/** True when this `.id` is a resource's own id rather than one on a nested element. */ +const isResourceId = (element) => + (element.base?.path ?? element.path ?? '') === 'Resource.id' + +/** FHIRPath invariants implemented structurally, keyed by the constraint the release states. */ +const IMPLEMENTED_CONSTRAINTS = { + 'ext-1': (schema) => + `${schema}.refine(\n` + + ` (value) =>\n` + + ` ((value as { extension?: unknown[] }).extension?.length ?? 0) > 0 !==\n` + + ` Object.keys(value as object).some(\n` + + ` // A primitive sibling means the value element exists carrying only extensions,\n` + + ` // which ext-1 counts as the value being present.\n` + + ` (key) => key.startsWith('value') || key.startsWith('_value'),\n` + + ` ),\n` + + ` { message: 'ext-1: an extension has either extensions or a value, never both.' },\n` + + ` )`, + 'obs-6': (schema) => + `${schema}.refine(\n` + + ` (value) => {\n` + + ` const observation = value as Record\n` + + ` const hasValue = Object.keys(observation).some(\n` + + ` (key) => key.startsWith('value') && observation[key] !== undefined,\n` + + ` )\n` + + ` return observation.dataAbsentReason === undefined || !hasValue\n` + + ` },\n` + + ` { message: 'obs-6: dataAbsentReason is present only when no value is.' },\n` + + ` )`, + 'obs-3': (schema) => + `${schema}.refine(\n` + + ` (value) => {\n` + + ` const range = value as { low?: unknown; high?: unknown; text?: unknown }\n` + + ` return range.low !== undefined || range.high !== undefined || range.text !== undefined\n` + + ` },\n` + + ` { message: 'obs-3: a reference range states a low, a high, or text.' },\n` + + ` )`, + 'per-1': (schema) => + `${schema}.refine(\n` + + ` (value) => {\n` + + ` const period = value as { start?: string; end?: string }\n` + + ` if (period.start === undefined || period.end === undefined) return true\n` + + ` const start = fhirDateTimeToEpoch(period.start)\n` + + ` const end = fhirDateTimeToEpoch(period.end)\n` + + ` // A malformed endpoint is a malformed date, not an ordering fault; the release says\n` + + ` // as much with start.hasValue(). Reporting both would name the wrong problem.\n` + + ` if (Number.isNaN(start) || Number.isNaN(end)) return true\n` + + ` return start <= end\n` + + ` },\n` + + ` { message: 'Period.start must not be later than Period.end.' },\n` + + ` )`, +} + +/** At most one property of a choice may be present, which is what `[x]` means. */ +function choiceRefinement(groups) { + if (groups.length === 0) return '' + const rendered = groups + .map( + (group) => + `{ names: [${group.names.map((name) => JSON.stringify(name)).join(', ')}], required: ${group.required} }`, + ) + .join(', ') + return ( + `.refine(\n` + + ` (value) =>\n` + + ` [${rendered}].every((group) => {\n` + + ` const present = group.names.filter(\n` + + ` (name) => (value as Record)[name] !== undefined,\n` + + ` ).length\n` + + ` return group.required ? present === 1 : present <= 1\n` + + ` }),\n` + + ` {\n` + + ` message:\n` + + ` 'A choice element carries exactly one alternative when required, and at most one otherwise.',\n` + + ` },\n` + + ` )` + ) +} + +/** + * The constraints that apply to an object, as the release states applicability. + * + * A constraint is declared once on the element that owns it, and every element it governs names + * it in `condition`. Reading both means a rule stated at the resource root still reaches the + * backbone it actually constrains — Observation.component.dataAbsentReason names obs-6 that way. + * + * This is sound only for a constraint whose expression is relative to the object it lands on. + * Every entry in IMPLEMENTED_CONSTRAINTS satisfies that; a constraint that navigates from an + * ancestor must not be added here without restricting it to its declaring path. + */ +const SKIPPED_CONSTRAINTS = new Map() + +/** Generic constraints every element carries; not a gap worth reporting. */ +const UNIVERSAL_CONSTRAINTS = new Set(['ele-1']) + +/** + * Records every constraint a structure states that this module does not check. + * + * Swept over all of a structure's elements, not just the object paths: a constraint declared on + * a leaf — `txt-1` and `txt-2` on `Narrative.div` — governs that element and belongs in the gap + * report even though no object path declares it. + */ +function recordSkippedConstraints(elements) { + for (const element of elements) { + for (const constraint of element.constraint ?? []) { + const key = constraint.key + if (key in IMPLEMENTED_CONSTRAINTS || UNIVERSAL_CONSTRAINTS.has(key)) + continue + const seen = SKIPPED_CONSTRAINTS.get(element.path) ?? new Set() + seen.add(key) + SKIPPED_CONSTRAINTS.set(element.path, seen) + } + } +} + +/** + * The constraints that apply to an object, as the release states applicability. + * + * A constraint is declared once on the element that owns it, and every element it governs names + * it in `condition`. Reading both means a rule stated at the resource root still reaches the + * backbone it actually constrains — Observation.component.dataAbsentReason names obs-6 that way. + * + * Sound only for a constraint whose expression is relative to the object it lands on. Exactly + * one such landing exists today, and it is asserted below rather than left to a comment. + */ +const CONDITION_LANDINGS = new Set(['Observation.component::obs-6']) + +function applicableConstraints(elements, path) { + const own = ( + elements.find((element) => element.path === path)?.constraint ?? [] + ).map((constraint) => constraint.key) + const referenced = childrenOf(elements, path).flatMap( + (child) => child.condition ?? [], + ) + for (const key of referenced) { + if (own.includes(key) || !(key in IMPLEMENTED_CONSTRAINTS)) continue + if (!CONDITION_LANDINGS.has(`${path}::${key}`)) { + throw new Error( + `${path}: ${key} lands here only through a child's condition, and its expression was ` + + 'written against the element that declares it. Add it to CONDITION_LANDINGS only ' + + 'after confirming the expression is relative to this object.', + ) + } + } + return [...new Set([...own, ...referenced])].filter( + (key) => key in IMPLEMENTED_CONSTRAINTS, + ) +} + +/** + * Fails generation when a structure's snapshot names an element the output does not. + * + * The type annotations cannot catch this: a schema that drops an *optional* property still + * satisfies the release's declared type in both directions. This is release-derived and + * involves no type system, and it is what would have caught `id` being dropped from every + * backbone while every test stayed green. + */ +function assertEveryElementEmitted(root, elements, rendered) { + const missing = [] + for (const element of childrenOf(elements, root)) { + const name = element.path.slice(root.length + 1).replace('[x]', '') + if (!new RegExp(`(^|\\s)_?${name}[A-Za-z]*:`, 'm').test(rendered)) { + missing.push(element.path) + } + } + if (missing.length > 0) { + throw new Error( + `${root}: the release names elements the generated schema does not emit: ` + + missing.join(', '), + ) + } +} + +/** Emits one structure — a primitive, complex type, or resource — as a Zod schema. */ +function renderStructure(structure, context, pending) { + const elements = structure.snapshot?.element ?? [] + const root = structure.name + + if (structure.kind === 'primitive-type') { + const regex = primitiveRegex(structure) + const bounds = numericBounds(structure, context) + const base = + ['boolean'].includes(root) ? 'z.boolean()' + : ['integer', 'positiveInt', 'unsignedInt'].includes(root) ? + 'z.number().int()' + : root === 'decimal' ? 'z.number()' + : 'z.string()' + let constrained = base + if (root === 'base64Binary') { + // The published pattern, `(\s*([0-9a-zA-Z+/=]){4}\s*)+`, nests a quantifier inside a + // quantifier. On a multi-megabyte attachment that backtracks until the stack gives out, and + // FHIR ships such an attachment in its own examples. The language it describes is exactly + // "base64 characters and whitespace, a multiple of four characters once whitespace is + // removed", which decides in one linear pass. + constrained = + `${base}.regex(new RegExp(${JSON.stringify(`^[0-9a-zA-Z+/=${XSD_SPACE}]*$`)}), 'Expected a FHIR base64Binary.')` + + `.refine(\n` + + ` (value) => value.replace(/[${XSD_SPACE}]/gu, '').length % 4 === 0,\n` + + ` { message: 'Expected a FHIR base64Binary.' },\n` + + ` )` + } else if (regex && base === 'z.string()') { + constrained = `${base}.regex(new RegExp(${JSON.stringify(`^(?:${xsdRegexToJs(regex)})$`)}), ${JSON.stringify(`Expected a FHIR ${root}.`)})` + } else if (base.startsWith('z.number()')) { + if (bounds.min !== undefined) + constrained = `${constrained}.min(${bounds.min})` + if (bounds.max !== undefined) + constrained = `${constrained}.max(${bounds.max})` + } + if (regex && base.startsWith('z.number()')) { + // positiveInt and unsignedInt state their bound as a lexical pattern; testing the string + // form applies the specification's own constraint rather than a hand-chosen one. + const source = JSON.stringify(`^(?:${regex})$`) + constrained = `${constrained}.refine((value) => new RegExp(${source}).test(String(value)), { + message: ${JSON.stringify(`Expected a valid FHIR ${root}.`)}, +})` + } + const output = + base === 'z.boolean()' ? 'boolean' + : base.startsWith('z.number()') ? 'number' + : 'string' + return `export const ${schemaName(root)}: z.ZodType<${output}> = ${constrained}\n` + } + + // A backbone another element points at by contentReference is hoisted to its own schema: + // inlining a self-referential one would not terminate. + const hoisted = new Map() + const pathSchemaName = (path) => { + const parts = path.split('.') + return `${parts[0].charAt(0).toLowerCase()}${parts[0].slice(1)}${parts + .slice(1) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join('')}Schema` + } + + const lines = [] + const emit = { + require: (name) => pending.add(name), + reference: (path) => { + hoisted.set(path, pathSchemaName(path)) + return pathSchemaName(path) + }, + primitive: (name) => { + pending.add(name) + return schemaName(name) + }, + backbone: (element) => { + const declared = element.path + .split('.') + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join('') + if (context.declaredTypes.has(declared)) { + hoisted.set(element.path, pathSchemaName(element.path)) + return pathSchemaName(element.path) + } + // Anything the release does not name stays inline: giving it a name would invent a type + // the specification does not publish. The same emitter is used so a deeper backbone is + // validated rather than falling back to an open record. + const nestedChoices = [] + const nested = renderProperties( + element.path, + elements, + context, + pending, + emit, + nestedChoices, + ) + return `z.strictObject({\n${nested}\n })${choiceRefinement(nestedChoices)}` + }, + } + + recordSkippedConstraints(elements) + const rootChoices = [] + const body = renderProperties( + root, + elements, + context, + pending, + emit, + rootChoices, + ) + const rootConstraints = applicableConstraints(elements, root) + // Rendering a backbone can discover further ones, so drain until the set stops growing. + const emitted = new Set() + while ([...hoisted.keys()].some((path) => !emitted.has(path))) { + for (const [path, name] of [...hoisted]) { + if (emitted.has(path)) continue + emitted.add(path) + const nestedChoices = [] + const nested = renderProperties( + path, + elements, + context, + pending, + emit, + nestedChoices, + ) + const declared = path + .split('.') + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join('') + const nestedAnnotation = + context.declaredTypes.has(declared) ? + `z.ZodType` + : 'z.ZodTypeAny' + if (context.declaredTypes.has(declared)) context.usedTypes.add(declared) + // A hoisted backbone carries its own choices and its own constraints; applying them only + // at the resource root would leave the named type weaker than the inline form. + const nestedConstraints = applicableConstraints(elements, path) + let nestedClosing = ` })${choiceRefinement(nestedChoices)}` + for (const key of nestedConstraints) { + nestedClosing = IMPLEMENTED_CONSTRAINTS[key](nestedClosing) + } + lines.push(`export const ${name}: ${nestedAnnotation} = z.lazy(() =>`) + lines.push(` z.strictObject({`) + lines.push(nested) + lines.push(`${nestedClosing},`) + lines.push(`)`) + lines.push('') + assertEveryElementEmitted(path, elements, nested) + } + } + const annotation = + context.declaredTypes.has(root) ? `z.ZodType` : 'z.ZodTypeAny' + if (context.declaredTypes.has(root)) context.usedTypes.add(root) + lines.push(`export const ${schemaName(root)}: ${annotation} = z.lazy(() =>`) + lines.push(` z.strictObject({`) + if (structure.kind === 'resource') { + lines.push(` resourceType: z.literal(${JSON.stringify(root)}),`) + } + lines.push(body) + let closing = ` })${choiceRefinement(rootChoices)}` + for (const key of rootConstraints) { + closing = IMPLEMENTED_CONSTRAINTS[key](closing) + } + lines.push(`${closing},`) + lines.push(`)`) + const rendered = lines.join('\n') + '\n' + assertEveryElementEmitted(root, elements, body) + return rendered +} + +/** Renders every direct child of a path as object properties. */ +function renderProperties(path, elements, context, pending, emit, collector) { + const localEmit = emit ?? { + require: (name) => pending.add(name), + primitive: (name) => { + pending.add(name) + return schemaName(name) + }, + backbone: () => 'z.record(z.string(), z.unknown())', + } + const rows = [] + const choiceGroups = [] + for (const child of childrenOf(elements, path)) { + const name = child.path.slice(path.length + 1) + const rendered = renderElement(child, context, localEmit) + if (!rendered) continue + if (rendered.choice) { + choiceGroups.push({ + names: rendered.choice.map((option) => option.name), + required: rendered.optional === false, + }) + for (const option of rendered.choice) { + rows.push(` ${option.name}: ${option.expression}.optional(),`) + if ( + option.primitiveSibling && + !(child.representation ?? []).includes('xmlAttr') + ) { + pending.add('Element') + rows.push( + ` _${option.name}: z.lazy(() => ${schemaName('Element')}).optional(),`, + ) + } + } + continue + } + const key = /^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name) + rows.push(` ${key}: ${rendered.expression},`) + if ( + rendered.primitiveSibling && + !(child.representation ?? []).includes('xmlAttr') + ) { + pending.add('Element') + const sibling = + rendered.siblingIsArray ? + `z.lazy(() => ${schemaName('Element')}).array()` + : `z.lazy(() => ${schemaName('Element')})` + rows.push(` _${name}: ${sibling}.optional(),`) + } + } + if (collector) collector.push(...choiceGroups) + return rows.join('\n') +} + +/** Whether a resource specialises an abstract one, following the release's own base chain. */ +function specialises(name, ancestor, structures) { + let current = structures.get(name) + while (current !== undefined) { + const baseName = (current.baseDefinition ?? '').split('/').pop() + if (!baseName) return false + if (baseName === ancestor) return true + current = structures.get(baseName) + } + return false +} + +/** + * Emits the schema an abstract resource slot resolves to. + * + * `Bundle.entry.resource`, `Bundle.entry.response.outcome` and every `contained` array admit any + * resource, which is not the same as admitting any object. The slot discriminates on the + * `resourceType` every resource carries and validates against the schema for that type; only a + * type this package does not model keeps the open shape, because there is nothing better to + * check it against. + * + * Dispatching rather than unioning is what keeps the recursion finite: a resource may carry a + * Bundle that carries it again, and the members are read at parse time from a table rather than + * expanded into the type of every slot that admits a resource. + */ +function renderResourceDispatch(typeCode, members, context) { + const table = `modelled${typeCode}s` + const output = + context.declaredTypes.has(typeCode) ? + `Fhir${typeCode}` + : '{ resourceType: string }' + if (context.declaredTypes.has(typeCode)) context.usedTypes.add(typeCode) + const annotation = `z.ZodType<${output}>` + const inFlight = `dispatching${typeCode}` + return ( + `const ${table}: Readonly> = {\n` + + members.map((name) => ` ${name}: ${schemaName(name)},`).join('\n') + + `\n}\n\n` + + `const ${inFlight} = new WeakSet()\n\n` + + `export const ${schemaName(typeCode)}: ${annotation} = z\n` + + ` .custom<${output}>(\n` + + ` (value) =>\n` + + ` typeof value === 'object' &&\n` + + ` value !== null &&\n` + + ` typeof (value as { resourceType?: unknown }).resourceType === 'string',\n` + + ` {\n` + + ` message: 'Expected a FHIR resource carrying a resourceType.',\n` + + ` abort: true,\n` + + ` },\n` + + ` )\n` + + ` .superRefine((value, ctx) => {\n` + + ` const modelled = ${table}[value.resourceType]\n` + + ` // A resource type the release defines but this package does not model keeps the open\n` + + ` // shape; reporting it as invalid would reject a conformant bundle.\n` + + ` if (modelled === undefined) return\n` + + ` // JSON cannot describe a resource that contains itself, but an in-memory object can,\n` + + ` // and re-entering one would recur until the stack ran out.\n` + + ` if (${inFlight}.has(value)) return\n` + + ` ${inFlight}.add(value)\n` + + ` try {\n` + + ` const result = modelled.safeParse(value)\n` + + ` if (result.success) return\n` + + ` // Re-raised rather than summarised, so a fault inside the resource still names the\n` + + ` // element that carries it.\n` + + ` for (const issue of result.error.issues) ctx.addIssue({ ...issue })\n` + + ` } finally {\n` + + ` ${inFlight}.delete(value)\n` + + ` }\n` + + ` })\n` + ) +} + +const HEADER = `// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// +// GENERATED FILE. Run \`npm run generate:zod\` after changing the generator or the release pin. +// + +/* eslint-disable sonarjs/regex-complexity, sonarjs/concise-regex, sonarjs/single-char-in-character-classes, sonarjs/single-character-alternation */ + +` + +const outputPathFor = (releaseKey) => + resolve(PACKAGE_ROOT, 'src/zod', releaseKey, 'schemas.ts') + +async function main(argv) { + const releaseKey = argv[argv.indexOf('--release') + 1] + const check = argv.includes('--check') + const release = RELEASES[releaseKey] + if (!release) { + throw new Error( + `--release must be one of ${Object.keys(RELEASES).join(', ')}`, + ) + } + + const packageDir = await fhirPackage(release) + const { structures, valueSets, codeSystems } = + await loadDefinitions(packageDir) + // Which structures the release's TypeScript declarations actually name; only those can be + // annotated, and the generator must not invent an import for one that is absent. + const declarations = await readFile( + resolve( + PACKAGE_ROOT, + '..', + '..', + 'node_modules/@types/fhir', + `${releaseKey}.d.ts`, + ), + 'utf8', + ) + const declaredTypes = new Set( + [...declarations.matchAll(/^export interface (\w+)/gm)].map( + (match) => match[1], + ), + ) + const context = { + structures, + valueSets, + codeSystems, + declaredTypes, + usedTypes: new Set(), + abstractResources: new Set(), + } + + // Close over everything the roots reach, so a consumer never meets an unresolved reference. + const pending = new Set([...resourceRoots(structures), ...DATATYPE_ROOTS]) + const done = new Set() + const chunks = new Map() + while (pending.size > 0) { + const name = [...pending][0] + pending.delete(name) + if (done.has(name)) continue + done.add(name) + const structure = structures.get(name) + if (!structure) continue + if (structure.abstract === true && structure.kind === 'resource') continue + chunks.set(name, renderStructure(structure, context, pending)) + } + + const ordered = [...chunks.keys()].sort() + // Emitted last, so the dispatch table can name every schema it resolves to as an initialised + // binding rather than through another layer of laziness. + const dispatches = [...context.abstractResources].sort().map((typeCode) => { + const members = ordered.filter( + (name) => + structures.get(name)?.kind === 'resource' && + specialises(name, typeCode, structures), + ) + return renderResourceDispatch(typeCode, members, context) + }) + const body = [...ordered.map((name) => chunks.get(name)), ...dispatches].join( + '\n', + ) + // Publish the gap alongside the schemas so a consumer can see exactly which invariants this + // module does not check, and so a release that introduces a new one fails the parity check. + const skipped = [...SKIPPED_CONSTRAINTS.entries()] + .map(([path, keys]) => [path, [...keys].sort()]) + .sort(([a], [b]) => a.localeCompare(b)) + const skippedExport = + `\n/**\n * Invariants the release states for these paths that this module does not check.\n *\n` + + ` * Structural rules are enforced; a rule expressed only as FHIRPath is not evaluated here.\n` + + ` * Normative conformance is established by the official FHIR Validator, not by this module.\n */\n` + + `export const UNCHECKED_CONSTRAINTS: Readonly> = {\n` + + skipped + .map( + ([path, keys]) => + ` ${JSON.stringify(path)}: [${keys.map((key) => JSON.stringify(key)).join(', ')}],`, + ) + .join('\n') + + `\n}\n` + + const imports = [...context.usedTypes].sort() + const typeImport = + imports.length > 0 ? + `import type {\n${imports.map((name) => ` ${name} as Fhir${name},`).join('\n')}\n} from 'fhir/${releaseKey}.js'\n` + : '' + const rendered = `${HEADER}${typeImport}import { z } from 'zod' +import { fhirDateTimeToEpoch } from '../support.js' + +// Generated from ${release.packageId}#${release.version} (FHIR ${release.fhirVersion}). +// ${ordered.length} structures: every type the exposed roots reach. + +${body}${skippedExport}` + + // Formatted exactly as the repository formats everything else, so `eslint --fix` never + // rewrites a generated file out from under its generator and breaks the parity check. + const prettierConfig = await resolveConfig(outputPathFor(releaseKey)) + const formatted = await format(rendered, { + ...prettierConfig, + parser: 'typescript', + }) + const outputDir = resolve(PACKAGE_ROOT, 'src/zod', releaseKey) + const outputPath = join(outputDir, 'schemas.ts') + if (check) { + const current = + existsSync(outputPath) ? await readFile(outputPath, 'utf8') : '' + if (current !== formatted) { + stdout.write(`${`${outputPath} is stale; run npm run generate:zod`}\n`) + process.exitCode = 1 + return + } + stdout.write(`${`${releaseKey}: ${ordered.length} structures current`}\n`) + return + } + await mkdir(outputDir, { recursive: true }) + await writeFile(outputPath, formatted, 'utf8') + stdout.write( + `${releaseKey}: wrote ${ordered.length} structures to ${outputPath}`, + ) +} + +// Only generate when run as a command. The loader is exported for reuse, and importing it +// must not trigger a full generation with the importer's arguments. +if (import.meta.url === pathToFileURL(argv[1] ?? '').href) { + await main(argv.slice(2)) +} diff --git a/packages/fhir/scripts/package-smoke.mjs b/packages/fhir/scripts/package-smoke.mjs new file mode 100644 index 00000000..ed1526aa --- /dev/null +++ b/packages/fhir/scripts/package-smoke.mjs @@ -0,0 +1,96 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import assert from 'node:assert/strict' +import { stdout } from 'node:process' + +const packageName = '@schmiedmayerlab/grove-fhir' +const [root, mobile, provider, provenance, questionnaire, r4] = + await Promise.all([ + import(packageName), + import(`${packageName}/mobile`), + import(`${packageName}/providers`), + import(`${packageName}/provenance`), + import(`${packageName}/questionnaire`), + import(`${packageName}/r4`), + ]) + +const providerExports = [ + 'buildProviderMeasurementBundle', + 'buildProviderRecordingBundle', + 'providerRawMappings', + 'providerScalarMappings', +] +const internalGraphExports = [ + 'groveFhirPackageCanonicals', + 'groveFhirPackageGraph', + 'groveFhirProfileCanonicals', + 'groveFhirProfileClaims', + 'groveFhirSourceRef', +] + +for (const name of providerExports) { + assert.equal( + name in root, + false, + `${name} leaked through the root entry point`, + ) + assert.equal( + name in mobile, + false, + `${name} leaked through the source-neutral Mobile entry point`, + ) + assert.equal( + name in provenance, + false, + `${name} leaked through the source-neutral Provenance entry point`, + ) + assert.equal( + name in provider, + true, + `${name} is missing from the Provider entry point`, + ) +} + +for (const name of internalGraphExports) { + for (const [label, entryPoint] of [ + ['root', root], + ['Mobile', mobile], + ['Provider', provider], + ['Provenance', provenance], + ['Questionnaire', questionnaire], + ]) { + assert.equal( + name in entryPoint, + false, + `${name} leaked through the ${label} entry point`, + ) + } +} + +assert.equal(typeof mobile.canonicalizeMobileEffectiveInstant, 'function') +assert.equal(root.groveFhirVersion, '4.0.1') +assert.equal(root.groveFhirContractVersion, '0.5.0') +assert.equal( + mobile.groveMobilePackageMetadata.packageId, + 'org.grovealliance.fhir.mobile', +) +assert.equal( + provider.groveProviderPackageMetadata.packageId, + 'org.grovealliance.fhir.providers', +) +assert.equal( + questionnaire.groveQuestionnairePackageMetadata.packageId, + 'org.grovealliance.fhir.questionnaire', +) +assert.equal(typeof root.parseFhirInstant, 'function') +assert.equal(typeof r4.parseCollectionBundle, 'function') +assert.equal(typeof provenance.parseProvenance, 'function') +assert.equal(typeof questionnaire.buildQuestionnaire, 'function') + +stdout.write('Verified all Grove FHIR package entry points.\n') diff --git a/packages/fhir/scripts/validate-conformance.mjs b/packages/fhir/scripts/validate-conformance.mjs new file mode 100644 index 00000000..2996ee02 --- /dev/null +++ b/packages/fhir/scripts/validate-conformance.mjs @@ -0,0 +1,183 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { spawnSync } from 'node:child_process' +import { readdir, readFile, stat } from 'node:fs/promises' +import { dirname, resolve } from 'node:path' +import { argv, execPath, stdout } from 'node:process' +import { fileURLToPath } from 'node:url' +import { isDeepStrictEqual } from 'node:util' + +const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..') +const fixtureRoot = resolve(packageRoot, 'fixtures/conformance') +const manifest = resolve(fixtureRoot, 'manifest.json') +const catalogRoot = resolve(packageRoot, '.grove-fhir/catalog') +const semanticCorpus = resolve( + packageRoot, + '.grove-fhir/Conformance/corpora/mobile-semantics/corpus.json', +) + +const argument = (name) => { + const index = argv.indexOf(name) + return index === -1 ? undefined : argv[index + 1] +} + +const rawIg = argument('--ig') +if (rawIg === undefined) { + throw new Error( + 'Usage: validate-conformance.mjs --ig [--structural-only]', + ) +} +const igRoot = resolve(rawIg) +const structuralOnly = argv.includes('--structural-only') +const validator = resolve( + argument('--validator') ?? `${igRoot}/.build/fhir-tools/validator_cli.jar`, +) + +const run = (command, commandArguments, workingDirectory = packageRoot) => { + const result = spawnSync(command, commandArguments, { + cwd: workingDirectory, + stdio: 'inherit', + encoding: 'utf8', + }) + if (result.error !== undefined) throw result.error + if (result.status !== 0) { + throw new Error(`${command} exited with status ${String(result.status)}.`) + } +} + +const readJson = async (path) => JSON.parse(await readFile(path, 'utf8')) + +const requireByteIdentical = async (localPath, upstreamPath, label) => { + const [local, upstream] = await Promise.all([ + readFile(localPath), + readFile(upstreamPath), + ]) + if (!local.equals(upstream)) { + throw new Error(`Consumed IG artifact is not byte-identical: ${label}`) + } +} + +const igStatus = await stat(igRoot).catch(() => undefined) +if (igStatus?.isDirectory() !== true) { + throw new Error(`Grove FHIR checkout is not a directory: ${igRoot}`) +} + +const catalogs = (await readdir(catalogRoot)) + .filter((name) => name.endsWith('.json')) + .sort() +const upstreamCatalogs = (await readdir(resolve(igRoot, 'catalog'))) + .filter((name) => name.endsWith('.json')) + .sort() +if (!isDeepStrictEqual(catalogs, upstreamCatalogs)) { + throw new Error('The consumed IG catalog set is out of sync.') +} +for (const catalog of catalogs) { + await requireByteIdentical( + resolve(catalogRoot, catalog), + resolve(igRoot, 'catalog', catalog), + catalog, + ) +} +await requireByteIdentical( + semanticCorpus, + resolve(igRoot, 'Conformance/corpora/mobile-semantics/corpus.json'), + 'Conformance/corpora/mobile-semantics/corpus.json', +) + +const pin = await readJson(resolve(packageRoot, 'grove-fhir.json')) +const revision = spawnSync('git', ['-C', igRoot, 'rev-parse', 'HEAD'], { + encoding: 'utf8', +}) +if (revision.status !== 0) { + throw new Error('Cannot resolve the Grove FHIR checkout revision.') +} +const resolvedSha = revision.stdout.trim() +const pinnedRef = pin.ref ?? pin.sha +// A tag is resolved to the commit it names, so a moved tag is caught rather than trusted. +const pinnedSha = + /^[\da-f]{40}$/u.test(pinnedRef) ? pinnedRef : ( + spawnSync('git', ['-C', igRoot, 'rev-list', '-n', '1', pinnedRef], { + encoding: 'utf8', + }).stdout.trim() + ) +stdout.write(`Grove FHIR pinned ref: ${pinnedRef}\n`) +stdout.write(`Grove FHIR checkout SHA: ${resolvedSha}\n`) +// The guide checkout and the catalogs this package generated from must be one commit. +if (!structuralOnly && resolvedSha !== pinnedSha) { + throw new Error( + `The Grove FHIR checkout is ${resolvedSha}, but this package is pinned to ${pinnedRef}` + + (pinnedSha && pinnedSha !== pinnedRef ? ` (${pinnedSha})` : '') + + '.', + ) +} + +run(execPath, [ + resolve(packageRoot, 'scripts/generate-conformance-fixtures.mjs'), + '--check', +]) + +const producerArguments = [ + resolve(igRoot, 'Scripts/validate-producer.py'), + '--manifest', + manifest, +] +if (structuralOnly) { + producerArguments.push('--structural-only') +} else { + producerArguments.push( + '--validator', + validator, + '--package', + `mobile=${resolve(igRoot, 'mobile/output/package.tgz')}`, + '--package', + `providers=${resolve(igRoot, 'providers/output/package.tgz')}`, + '--package', + `sensor=${resolve(igRoot, 'sensor/output/package.tgz')}`, + '--package', + `questionnaire=${resolve(igRoot, 'questionnaire/output/package.tgz')}`, + ) +} +run('python3', producerArguments, igRoot) + +const questionnaire = resolve(fixtureRoot, 'resources/questionnaire.json') +const response = resolve(fixtureRoot, 'resources/questionnaire-response.json') +run( + 'python3', + [ + resolve(igRoot, 'Scripts/validate-questionnaire.py'), + '--questionnaire', + questionnaire, + '--response', + response, + ], + igRoot, +) + +if (!structuralOnly) { + run( + 'python3', + [ + resolve(igRoot, 'Scripts/validate-questionnaire-fhir.py'), + '--validator', + validator, + '--package', + resolve(igRoot, 'questionnaire/output/package.tgz'), + '--resource', + questionnaire, + '--resource', + response, + '--allow-example-urls', + ], + igRoot, + ) +} + +stdout.write( + `Grove FHIR ${structuralOnly ? 'structural' : 'official Validator'} conformance passed.\n`, +) diff --git a/packages/fhir/src/core/freeze.ts b/packages/fhir/src/core/freeze.ts new file mode 100644 index 00000000..e9d2921c --- /dev/null +++ b/packages/fhir/src/core/freeze.ts @@ -0,0 +1,32 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +export type ReadonlyDeep = + T extends (...arguments_: never[]) => unknown ? T + : T extends readonly unknown[] ? + Readonly<{ [Index in keyof T]: ReadonlyDeep }> + : T extends object ? { readonly [Key in keyof T]: ReadonlyDeep } + : T + +const isObject = (value: unknown): value is Record => + typeof value === 'object' && value !== null + +export const deepFreeze = (value: T): ReadonlyDeep => { + if (!isObject(value) || Object.isFrozen(value)) { + return value as ReadonlyDeep + } + + // Freeze before descending: the frozen check above is what stops a cycle, so a node that + // reaches itself through its own children must already be frozen by the time it is revisited. + Object.freeze(value) + for (const nested of Object.values(value)) { + deepFreeze(nested) + } + + return value as ReadonlyDeep +} diff --git a/packages/fhir/src/core/index.ts b/packages/fhir/src/core/index.ts new file mode 100644 index 00000000..861588ac --- /dev/null +++ b/packages/fhir/src/core/index.ts @@ -0,0 +1,47 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +export { deepFreeze, type ReadonlyDeep } from './freeze.js' +export { + compareFhirInstants, + fhirDateTimeToDate, + parseAbsoluteUri, + parseCanonical, + parseFhirId, + parseFhirInstant, + parsePatientReference, + parsePositiveInteger, + parseResearchStudyReference, + parseSemVer, + parseUrnUuid, + type AbsoluteUri, + type Canonical, + type FhirId, + type FhirInstant, + type PatientReference, + type PositiveInteger, + type ResearchStudyReference, + type SemVer, + type UrnUuid, +} from './primitives.js' +export { + fhirQuantityToValue, + type QuantityComparator, + type QuantityValue, +} from './quantity.js' +export { + collectResults, + err, + issues, + mapResult, + ok, + type Issue, + type IssueCode, + type IssueSeverity, + type Result, +} from './result.js' diff --git a/packages/fhir/src/core/primitives.ts b/packages/fhir/src/core/primitives.ts new file mode 100644 index 00000000..e9999b8b --- /dev/null +++ b/packages/fhir/src/core/primitives.ts @@ -0,0 +1,369 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { err, ok, type Result } from './result.js' + +declare const brand: unique symbol +declare const numericBrand: unique symbol + +type Branded = string & { readonly [brand]: Name } + +export type AbsoluteUri = Branded<'AbsoluteUri'> +export type Canonical = Branded<'Canonical'> +export type FhirId = Branded<'FhirId'> +export type FhirInstant = Branded<'FhirInstant'> +export type PatientReference = Branded<'PatientReference'> +export type PositiveInteger = number & { + readonly [numericBrand]: 'PositiveInteger' +} +export type ResearchStudyReference = Branded<'ResearchStudyReference'> +export type SemVer = Branded<'SemVer'> +export type UrnUuid = Branded<'UrnUuid'> + +const FHIR_ID_PATTERN = '[A-Za-z0-9\\-.]{1,64}' +const FHIR_ID = new RegExp(`^${FHIR_ID_PATTERN}$`, 'u') +const INSTANT = + /^(?\d{4})-(?\d{2})-(?\d{2})T(?\d{2}):(?\d{2}):(?\d{2})(?:\.(?\d+))?(?Z|[+-]\d{2}:\d{2})$/u +const PARTIAL_DATE = /^(?\d{4})(?:-(?\d{2})(?:-(?\d{2}))?)?$/u +const UUID = + '[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}' +const URN_UUID = new RegExp(`^urn:uuid:${UUID}$`, 'u') +const DECIMAL_IDENTIFIER = /^\d+$/u +const SEMVER_IDENTIFIER = /^[\dA-Za-z-]+$/u + +const isNumericIdentifier = (value: string): boolean => + DECIMAL_IDENTIFIER.test(value) && (value === '0' || !value.startsWith('0')) + +const areValidIdentifiers = ( + value: string | undefined, + numericLeadingZeroAllowed: boolean, +): boolean => + value === undefined || + (value.length > 0 && + value + .split('.') + .every( + (identifier) => + identifier.length > 0 && + SEMVER_IDENTIFIER.test(identifier) && + (numericLeadingZeroAllowed || + !DECIMAL_IDENTIFIER.test(identifier) || + isNumericIdentifier(identifier)), + )) + +const isSemVer = (value: string): boolean => { + const buildSeparator = value.indexOf('+') + if (buildSeparator !== value.lastIndexOf('+')) return false + const version = buildSeparator === -1 ? value : value.slice(0, buildSeparator) + const build = + buildSeparator === -1 ? undefined : value.slice(buildSeparator + 1) + + const prereleaseSeparator = version.indexOf('-') + const core = + prereleaseSeparator === -1 ? version : version.slice(0, prereleaseSeparator) + const prerelease = + prereleaseSeparator === -1 ? undefined : ( + version.slice(prereleaseSeparator + 1) + ) + const coreIdentifiers = core.split('.') + + return ( + coreIdentifiers.length === 3 && + coreIdentifiers.every(isNumericIdentifier) && + areValidIdentifiers(prerelease, false) && + areValidIdentifiers(build, true) + ) +} + +const isAbsoluteUri = (value: string): boolean => { + try { + const parsed = new URL(value) + return parsed.protocol.length > 1 && !/\s/u.test(value) + } catch { + return false + } +} + +const isTypedResourceReference = ( + value: string, + resourceType: 'Patient' | 'ResearchStudy', +): boolean => { + if (new RegExp(`^${resourceType}/${FHIR_ID_PATTERN}$`, 'u').test(value)) { + return true + } + try { + const parsed = new URL(value) + return ( + !/\s/u.test(value) && + ['http:', 'https:'].includes(parsed.protocol) && + parsed.username === '' && + parsed.password === '' && + parsed.search === '' && + parsed.hash === '' && + new RegExp(`/${resourceType}/${FHIR_ID_PATTERN}$`, 'u').test( + parsed.pathname, + ) + ) + } catch { + return false + } +} + +interface InstantParts { + readonly epochSecond: bigint + readonly fraction: string +} + +const isLeapYear = (year: number): boolean => + year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) + +const daysInMonth = (year: number, month: number): number => { + if (month === 2) return isLeapYear(year) ? 29 : 28 + if ([4, 6, 9, 11].includes(month)) return 30 + return 31 +} + +const parseInstantParts = (value: unknown): InstantParts | undefined => { + if (typeof value !== 'string') return undefined + const groups = INSTANT.exec(value)?.groups + if (groups === undefined) return undefined + + const year = Number(groups.year) + const month = Number(groups.month) + const day = Number(groups.day) + const hour = Number(groups.hour) + const minute = Number(groups.minute) + const second = Number(groups.second) + if ( + year === 0 || + month < 1 || + month > 12 || + day < 1 || + day > daysInMonth(year, month) || + hour > 23 || + minute > 59 || + second > 60 + ) { + return undefined + } + + const zone = groups.zone ?? '' + const offsetSign = zone === 'Z' ? undefined : zone.slice(0, 1) + const offsetHour = zone === 'Z' ? 0 : Number(zone.slice(1, 3)) + const offsetMinute = zone === 'Z' ? 0 : Number(zone.slice(4, 6)) + if ( + offsetHour > 14 || + offsetMinute > 59 || + (offsetHour === 14 && offsetMinute !== 0) + ) { + return undefined + } + + const date = new Date(0) + date.setUTCFullYear(year, month - 1, day) + date.setUTCHours(hour, minute, Math.min(second, 59), 0) + if (Number.isNaN(date.getTime())) return undefined + + const offsetDirection = offsetSign === '-' ? -1 : 1 + const offsetSeconds = + offsetSign === undefined ? 0 : ( + offsetDirection * (offsetHour * 60 + offsetMinute) * 60 + ) + const leapSecond = second === 60 ? 1 : 0 + return { + epochSecond: + BigInt(Math.trunc(date.getTime() / 1000)) - + BigInt(offsetSeconds) + + BigInt(leapSecond), + fraction: groups.fraction ?? '', + } +} + +const compareFractions = (left: string, right: string): -1 | 0 | 1 => { + const digits = Math.max(left.length, right.length) + for (let index = 0; index < digits; index += 1) { + const leftDigit = left[index] ?? '0' + const rightDigit = right[index] ?? '0' + if (leftDigit < rightDigit) return -1 + if (leftDigit > rightDigit) return 1 + } + return 0 +} + +export const parseAbsoluteUri = (value: unknown): Result => { + if (typeof value !== 'string' || !isAbsoluteUri(value)) { + return err('invalid-uri', 'Expected an absolute URI.') + } + return ok(value as AbsoluteUri) +} + +export const parseCanonical = (value: unknown): Result => { + if (typeof value !== 'string') { + return err('invalid-uri', 'Expected a canonical URI string.') + } + + const separator = value.indexOf('|') + const uri = separator === -1 ? value : value.slice(0, separator) + const version = separator === -1 ? undefined : value.slice(separator + 1) + if ( + !isAbsoluteUri(uri) || + version === '' || + version?.includes('|') === true + ) { + return err( + 'invalid-uri', + 'Expected an absolute canonical URI with at most one non-empty version.', + ) + } + return ok(value as Canonical) +} + +export const parseFhirId = (value: unknown): Result => { + if (typeof value !== 'string' || !FHIR_ID.test(value)) { + return err( + 'invalid-identifier', + 'Expected a FHIR id containing 1-64 letters, digits, hyphens, or periods.', + ) + } + return ok(value as FhirId) +} + +export const parseFhirInstant = (value: unknown): Result => { + if (typeof value !== 'string' || parseInstantParts(value) === undefined) { + return err( + 'invalid-date-time', + 'Expected an RFC 3339 instant with seconds and an explicit UTC offset.', + ) + } + return ok(value as FhirInstant) +} + +/** Compares two fully validated FHIR instants without losing sub-millisecond precision. */ +export const compareFhirInstants = ( + left: unknown, + right: unknown, +): Result<-1 | 0 | 1> => { + const leftParts = parseInstantParts(left) + const rightParts = parseInstantParts(right) + if (leftParts === undefined || rightParts === undefined) { + return err('invalid-date-time', 'Expected two valid FHIR instants.') + } + if (leftParts.epochSecond < rightParts.epochSecond) return ok(-1) + if (leftParts.epochSecond > rightParts.epochSecond) return ok(1) + return ok(compareFractions(leftParts.fraction, rightParts.fraction)) +} + +export const parsePatientReference = ( + value: unknown, +): Result => { + if ( + typeof value !== 'string' || + !isTypedResourceReference(value, 'Patient') + ) { + return err( + 'invalid-reference', + 'Expected Patient/{id} or an absolute HTTP(S) URL ending in /Patient/{id} without a query or fragment.', + ) + } + return ok(value as PatientReference) +} + +export const parseResearchStudyReference = ( + value: unknown, +): Result => { + if (typeof value !== 'string') { + return err( + 'invalid-reference', + 'Expected a ResearchStudy reference string.', + ) + } + if (!isTypedResourceReference(value, 'ResearchStudy')) { + return err( + 'invalid-reference', + 'Expected ResearchStudy/{id} or an absolute HTTP(S) URL ending in /ResearchStudy/{id} without a query or fragment.', + ) + } + return ok(value as ResearchStudyReference) +} + +export const parsePositiveInteger = ( + value: unknown, +): Result => { + if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0) { + return err( + 'out-of-range', + 'Expected a positive safe integer greater than zero.', + ) + } + return ok(value as PositiveInteger) +} + +export const parseUrnUuid = (value: unknown): Result => { + if (typeof value !== 'string' || !URN_UUID.test(value)) { + return err('invalid-uri', 'Expected a lowercase RFC 4122 urn:uuid fullUrl.') + } + return ok(value as UrnUuid) +} + +export const parseSemVer = (value: unknown): Result => { + if (typeof value !== 'string' || !isSemVer(value)) { + return err('invalid-code', 'Expected a Semantic Versioning 2.0.0 version.') + } + return ok(value as SemVer) +} + +/** + * A FHIR `date`, `dateTime`, or `instant` as the JavaScript instant it names. + * + * The three primitives share one lexical space: a year, a year and month, a calendar day, or a + * full timestamp with an offset. A value stated to lower precision resolves to the start of the + * period it names, in UTC, because a FHIR `date` carries no offset to resolve it against — the + * same reading `fhirDateTimeToEpoch` uses for ordering. + * + * `Date` holds milliseconds, so a value stating more digits is truncated rather than rounded: a + * truncated instant still falls inside the second it was recorded in. + */ +export const fhirDateTimeToDate = (value: unknown): Result => { + const instant = parseInstantParts(value) + if (instant !== undefined) { + const fraction = Number(`${instant.fraction}000`.slice(0, 3)) + return ok(new Date(Number(instant.epochSecond) * 1000 + fraction)) + } + if (typeof value !== 'string') { + return err( + 'invalid-date-time', + 'Expected a FHIR date, dateTime, or instant.', + ) + } + const groups = PARTIAL_DATE.exec(value)?.groups + if (groups === undefined) { + return err( + 'invalid-date-time', + 'Expected a FHIR date, dateTime, or instant.', + ) + } + const year = Number(groups.year) + const month = groups.month === undefined ? 1 : Number(groups.month) + const day = groups.day === undefined ? 1 : Number(groups.day) + if ( + year === 0 || + month < 1 || + month > 12 || + day < 1 || + day > daysInMonth(year, month) + ) { + return err( + 'invalid-date-time', + 'Expected a FHIR date, dateTime, or instant naming a real calendar day.', + ) + } + const date = new Date(0) + date.setUTCFullYear(year, month - 1, day) + date.setUTCHours(0, 0, 0, 0) + return ok(date) +} diff --git a/packages/fhir/src/core/quantity.ts b/packages/fhir/src/core/quantity.ts new file mode 100644 index 00000000..3823d1e0 --- /dev/null +++ b/packages/fhir/src/core/quantity.ts @@ -0,0 +1,78 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { err, ok, type Result } from './result.js' + +/** The bounds a Quantity may state in place of an exact value. */ +export type QuantityComparator = '<' | '<=' | '>=' | '>' + +/** A FHIR Quantity reduced to the number, unit, and bound a consumer has to carry. */ +export interface QuantityValue { + readonly value: number + /** The unit's code when the Quantity states one, and its display unit otherwise. */ + readonly unit: string + /** The system the unit code belongs to, so a UCUM unit is distinguishable from a local one. */ + readonly system?: string + /** Stated only when the Quantity names a bound; dropping it would misreport the measurement. */ + readonly comparator?: QuantityComparator +} + +const COMPARATORS: readonly string[] = ['<', '<=', '>=', '>'] + +/** + * A FHIR Quantity as the value a consumer can compute with. + * + * The code is preferred over the display unit because only the code is comparable; `unit` alone + * is a label the source chose. A Quantity carrying neither, or no numeric value, is reported + * rather than reduced to a number whose meaning is unknown. + */ +export const fhirQuantityToValue = (value: unknown): Result => { + if (typeof value !== 'object' || value === null) { + return err('invalid-type', 'Expected a FHIR Quantity.') + } + const quantity = value as { + readonly value?: unknown + readonly unit?: unknown + readonly code?: unknown + readonly system?: unknown + readonly comparator?: unknown + } + if (typeof quantity.value !== 'number' || !Number.isFinite(quantity.value)) { + return err( + 'missing-required', + 'A Quantity without a finite numeric value carries no measurement.', + ['value'], + ) + } + const unit = typeof quantity.code === 'string' ? quantity.code : quantity.unit + if (typeof unit !== 'string' || unit.length === 0) { + return err( + 'missing-required', + 'A Quantity states either a unit code or a display unit.', + ['code'], + ) + } + if ( + quantity.comparator !== undefined && + !COMPARATORS.includes(quantity.comparator as string) + ) { + return err('invalid-code', 'Expected a comparator of <, <=, >=, or >.', [ + 'comparator', + ]) + } + return ok({ + value: quantity.value, + unit, + ...(typeof quantity.system === 'string' ? + { system: quantity.system } + : undefined), + ...(quantity.comparator === undefined ? + undefined + : { comparator: quantity.comparator as QuantityComparator }), + }) +} diff --git a/packages/fhir/src/core/result.ts b/packages/fhir/src/core/result.ts new file mode 100644 index 00000000..22831aef --- /dev/null +++ b/packages/fhir/src/core/result.ts @@ -0,0 +1,83 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +export type IssueSeverity = 'error' | 'warning' + +export type IssueCode = + | 'duplicate-identifier' + | 'external-validation-required' + | 'invalid-choice' + | 'invalid-code' + | 'invalid-date-time' + | 'invalid-identifier' + | 'invalid-reference' + | 'invalid-type' + | 'invalid-uri' + | 'missing-required' + | 'out-of-range' + | 'schema-invalid' + | 'unsupported-measurement' + | 'value-mismatch' + +export interface Issue { + readonly severity: IssueSeverity + readonly code: IssueCode + readonly path: ReadonlyArray + readonly message: string +} + +export type Result = + | { + readonly ok: true + readonly value: T + /** Non-blocking findings that the caller must still surface or record. */ + readonly warnings?: readonly Issue[] + } + | { readonly ok: false; readonly issues: readonly Issue[] } + +export const ok = (value: T, warnings: readonly Issue[] = []): Result => + warnings.length === 0 ? { ok: true, value } : { ok: true, value, warnings } + +export const err = ( + code: IssueCode, + message: string, + path: ReadonlyArray = [], +): Result => ({ + ok: false, + issues: [{ severity: 'error', code, path, message }], +}) + +export const issues = (entries: readonly Issue[]): Result => ({ + ok: false, + issues: entries, +}) + +export const mapResult = ( + result: Result, + transform: (value: T) => U, +): Result => + result.ok ? ok(transform(result.value), result.warnings) : result + +export const collectResults = ( + results: ReadonlyArray>, +): Result => { + const values: T[] = [] + const failures: Issue[] = [] + const warnings: Issue[] = [] + + for (const result of results) { + if (result.ok) { + values.push(result.value) + warnings.push(...(result.warnings ?? [])) + } else { + failures.push(...result.issues) + } + } + + return failures.length === 0 ? ok(values, warnings) : issues(failures) +} diff --git a/packages/fhir/src/elements/anyValueSchema.ts b/packages/fhir/src/elements/anyValueSchema.ts deleted file mode 100644 index 6c95206d..00000000 --- a/packages/fhir/src/elements/anyValueSchema.ts +++ /dev/null @@ -1,129 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - addressSchema, - annotationSchema, - attachmentSchema, - backboneElementSchema, - base64BinarySchema, - booleanSchema, - canonicalSchema, - codeableConceptSchema, - codeSchema, - codingSchema, - contactDetailSchema, - contactPointSchema, - contributorSchema, - dataRequirementSchema, - dateSchema, - dateTimeSchema, - decimalSchema, - dosageSchema, - elementSchema, - expressionSchema, - humanNameSchema, - identifierSchema, - idSchema, - instantSchema, - intSchema, - markdownSchema, - metaSchema, - moneySchema, - oidSchema, - parameterDefinitionSchema, - periodSchema, - positiveIntSchema, - quantitySchema, - rangeSchema, - ratioSchema, - referenceSchema, - relatedArtifactSchema, - sampledDataSchema, - signatureSchema, - stringSchema, - timeSchema, - timingSchema, - triggerDefinitionSchema, - unsignedIntSchema, - uriSchema, - urlSchema, - usageContextSchema, - uuidSchema, -} from './index.js' - -/** - * Zod schema for FHIR elements with value[x] fields supporting any FHIR data type. - */ -export const anyValueSchema = backboneElementSchema.extend({ - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: elementSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueCanonical: canonicalSchema.optional(), - _valueCanonical: elementSchema.optional(), - valueCode: codeSchema.optional(), - _valueCode: elementSchema.optional(), - valueDate: dateSchema.optional(), - _valueDate: elementSchema.optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: elementSchema.optional(), - valueDecimal: decimalSchema.optional(), - valueId: idSchema.optional(), - _valueId: elementSchema.optional(), - valueInstant: instantSchema.optional(), - _valueInstant: elementSchema.optional(), - valueInteger: intSchema.optional(), - valueMarkdown: markdownSchema.optional(), - _valueMarkdown: elementSchema.optional(), - valueOid: oidSchema.optional(), - _valueOid: elementSchema.optional(), - valuePositiveInt: positiveIntSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueTime: timeSchema.optional(), - _valueTime: elementSchema.optional(), - valueUnsignedInt: unsignedIntSchema.optional(), - valueUri: uriSchema.optional(), - _valueUri: elementSchema.optional(), - valueUrl: urlSchema.optional(), - _valueUrl: elementSchema.optional(), - valueUuid: uuidSchema.optional(), - _valueUuid: elementSchema.optional(), - valueAddress: addressSchema.optional(), - valueAge: quantitySchema.optional(), - valueAnnotation: annotationSchema.optional(), - valueAttachment: attachmentSchema.optional(), - valueCodeableConcept: codeableConceptSchema.optional(), - valueCoding: codingSchema.optional(), - valueContactPoint: contactPointSchema.optional(), - valueCount: quantitySchema.optional(), - valueDistance: quantitySchema.optional(), - valueDuration: quantitySchema.optional(), - valueHumanName: humanNameSchema.optional(), - valueIdentifier: identifierSchema.optional(), - valueMoney: moneySchema.optional(), - valuePeriod: periodSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueRange: rangeSchema.optional(), - valueRatio: ratioSchema.optional(), - valueReference: referenceSchema.optional(), - valueSampledData: sampledDataSchema.optional(), - valueSignature: signatureSchema.optional(), - valueTiming: timingSchema.optional(), - valueContactDetail: contactDetailSchema.optional(), - valueContributor: contributorSchema.optional(), - valueDataRequirement: dataRequirementSchema.optional(), - valueExpression: expressionSchema.optional(), - valueParameterDefinition: parameterDefinitionSchema.optional(), - valueRelatedArtifact: relatedArtifactSchema.optional(), - valueTriggerDefinition: triggerDefinitionSchema.optional(), - valueUsageContext: usageContextSchema.optional(), - valueDosage: dosageSchema.optional(), - valueMeta: metaSchema.optional(), -}) diff --git a/packages/fhir/src/elements/backBoneElement.ts b/packages/fhir/src/elements/backBoneElement.ts deleted file mode 100644 index c8c20557..00000000 --- a/packages/fhir/src/elements/backBoneElement.ts +++ /dev/null @@ -1,19 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type BackboneElement } from 'fhir/r4b.js' -import { type ZodType } from 'zod' -import { elementSchema } from './element.js' -import { extensionSchema } from './extension.js' - -/** - * Zod schema for FHIR BackboneElement data type. - */ -export const backboneElementSchema = elementSchema.extend({ - modifierExtension: extensionSchema.array().optional(), -}) satisfies ZodType diff --git a/packages/fhir/src/elements/dataTypes/address.ts b/packages/fhir/src/elements/dataTypes/address.ts deleted file mode 100644 index 44df2dde..00000000 --- a/packages/fhir/src/elements/dataTypes/address.ts +++ /dev/null @@ -1,48 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Address } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { periodSchema } from './period.js' -import { stringSchema } from './primitiveTypes.js' -import { elementSchema } from '../element.js' - -const addressTypeSchema = z.enum(['postal', 'physical', 'both']) -const addressUseSchema = z.enum(['home', 'work', 'temp', 'old', 'billing']) - -/** - * Zod schema for FHIR Address data type. - */ -export const untypedAddressSchema = z.lazy(() => - elementSchema.extend({ - use: addressUseSchema.optional(), - _use: elementSchema.optional(), - type: addressTypeSchema.optional(), - _type: elementSchema.optional(), - text: stringSchema.optional(), - _text: elementSchema.optional(), - line: stringSchema.array().optional(), - _line: elementSchema.array().optional(), - city: stringSchema.optional(), - _city: elementSchema.optional(), - district: stringSchema.optional(), - _district: elementSchema.optional(), - state: stringSchema.optional(), - _state: elementSchema.optional(), - postalCode: stringSchema.optional(), - _postalCode: elementSchema.optional(), - country: stringSchema.optional(), - _country: elementSchema.optional(), - period: periodSchema.optional(), - }), -) satisfies ZodType
- -/** - * Zod schema for FHIR Address data type. - */ -export const addressSchema: ZodType
= untypedAddressSchema diff --git a/packages/fhir/src/elements/dataTypes/annotation.ts b/packages/fhir/src/elements/dataTypes/annotation.ts deleted file mode 100644 index 6f46afaf..00000000 --- a/packages/fhir/src/elements/dataTypes/annotation.ts +++ /dev/null @@ -1,37 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Annotation } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { - dateTimeSchema, - markdownSchema, - stringSchema, -} from './primitiveTypes.js' -import { referenceSchema } from './reference.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR Annotation data type. - */ -export const untypedAnnotationSchema = z.lazy(() => - elementSchema.extend({ - authorReference: referenceSchema.optional(), - authorString: stringSchema.optional(), - _authorString: elementSchema.optional(), - time: dateTimeSchema.optional(), - _time: elementSchema.optional(), - text: markdownSchema, - _text: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Annotation data type. - */ -export const annotationSchema: ZodType = untypedAnnotationSchema diff --git a/packages/fhir/src/elements/dataTypes/attachment.ts b/packages/fhir/src/elements/dataTypes/attachment.ts deleted file mode 100644 index fdcfde09..00000000 --- a/packages/fhir/src/elements/dataTypes/attachment.ts +++ /dev/null @@ -1,47 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Attachment } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { - base64BinarySchema, - codeSchema, - dateTimeSchema, - stringSchema, - unsignedIntSchema, - urlSchema, -} from './primitiveTypes.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR Attachment data type. - */ -export const untypedAttachmentSchema = z.lazy(() => - elementSchema.extend({ - contentType: codeSchema.optional(), - _contentType: elementSchema.optional(), - language: codeSchema.optional(), - _language: elementSchema.optional(), - data: base64BinarySchema.optional(), - _data: elementSchema.optional(), - url: urlSchema.optional(), - _url: elementSchema.optional(), - size: unsignedIntSchema.optional(), - hash: base64BinarySchema.optional(), - _hash: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - creation: dateTimeSchema.optional(), - _creation: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Attachment data type. - */ -export const attachmentSchema: ZodType = untypedAttachmentSchema diff --git a/packages/fhir/src/elements/dataTypes/codeableConcept.ts b/packages/fhir/src/elements/dataTypes/codeableConcept.ts deleted file mode 100644 index 7f314d91..00000000 --- a/packages/fhir/src/elements/dataTypes/codeableConcept.ts +++ /dev/null @@ -1,31 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type CodeableConcept } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { codingSchema } from './coding.js' -import { stringSchema } from './primitiveTypes.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR CodeableConcept data type. - */ -export const untypedCodeableConceptSchema = z.lazy(() => - elementSchema.extend({ - coding: codingSchema.array().optional(), - _coding: elementSchema.optional(), - text: stringSchema.optional(), - _text: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR CodeableConcept data type. - */ -export const codeableConceptSchema: ZodType = - untypedCodeableConceptSchema diff --git a/packages/fhir/src/elements/dataTypes/codeableReference.ts b/packages/fhir/src/elements/dataTypes/codeableReference.ts deleted file mode 100644 index fa5ecc61..00000000 --- a/packages/fhir/src/elements/dataTypes/codeableReference.ts +++ /dev/null @@ -1,29 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type CodeableReference } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { codeableConceptSchema } from './codeableConcept.js' -import { referenceSchema } from './reference.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR CodeableReference data type. - */ -export const untypedCodeableReferenceSchema = z.lazy(() => - elementSchema.extend({ - concept: codeableConceptSchema.optional(), - reference: referenceSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR CodeableReference data type. - */ -export const codeableReferenceSchema: ZodType = - untypedCodeableReferenceSchema diff --git a/packages/fhir/src/elements/dataTypes/coding.ts b/packages/fhir/src/elements/dataTypes/coding.ts deleted file mode 100644 index f21f4444..00000000 --- a/packages/fhir/src/elements/dataTypes/coding.ts +++ /dev/null @@ -1,40 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { - booleanSchema, - codeSchema, - stringSchema, - uriSchema, -} from './primitiveTypes.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR Coding data type. - */ -export const untypedCodingSchema = z.lazy(() => - elementSchema.extend({ - system: uriSchema.optional(), - _system: elementSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - code: codeSchema.optional(), - _code: elementSchema.optional(), - display: stringSchema.optional(), - _display: elementSchema.optional(), - userSelected: booleanSchema.optional(), - _userSelected: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Coding data type. - */ -export const codingSchema: ZodType = untypedCodingSchema diff --git a/packages/fhir/src/elements/dataTypes/contactPoint.ts b/packages/fhir/src/elements/dataTypes/contactPoint.ts deleted file mode 100644 index 3c00d27b..00000000 --- a/packages/fhir/src/elements/dataTypes/contactPoint.ts +++ /dev/null @@ -1,47 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type ContactPoint } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { periodSchema } from './period.js' -import { positiveIntSchema, stringSchema } from './primitiveTypes.js' -import { elementSchema } from '../element.js' - -const contactPointSystemSchema = z.enum([ - 'phone', - 'fax', - 'email', - 'pager', - 'url', - 'sms', - 'other', -]) - -const contactPointUseSchema = z.enum(['home', 'work', 'temp', 'old', 'mobile']) - -/** - * Zod schema for FHIR ContactPoint data type. - */ -export const untypedContactPointSchema = z.lazy(() => - elementSchema.extend({ - system: contactPointSystemSchema.optional(), - _system: elementSchema.optional(), - value: stringSchema.optional(), - _value: elementSchema.optional(), - use: contactPointUseSchema.optional(), - _use: elementSchema.optional(), - rank: positiveIntSchema.optional(), - period: periodSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ContactPoint data type. - */ -export const contactPointSchema: ZodType = - untypedContactPointSchema diff --git a/packages/fhir/src/elements/dataTypes/humanName.ts b/packages/fhir/src/elements/dataTypes/humanName.ts deleted file mode 100644 index bb1dfa30..00000000 --- a/packages/fhir/src/elements/dataTypes/humanName.ts +++ /dev/null @@ -1,49 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type HumanName } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { periodSchema } from './period.js' -import { stringSchema } from './primitiveTypes.js' -import { elementSchema } from '../element.js' - -const humanNameUseSchema = z.enum([ - 'usual', - 'official', - 'temp', - 'nickname', - 'anonymous', - 'old', - 'maiden', -]) - -/** - * Zod schema for FHIR HumanName data type. - */ -export const untypedHumanNameSchema = z.lazy(() => - elementSchema.extend({ - use: humanNameUseSchema.optional(), - _use: elementSchema.optional(), - text: stringSchema.optional(), - _text: elementSchema.optional(), - family: stringSchema.optional(), - _family: elementSchema.optional(), - given: stringSchema.array().optional(), - _given: elementSchema.array().optional(), - prefix: stringSchema.array().optional(), - _prefix: elementSchema.array().optional(), - suffix: stringSchema.array().optional(), - _suffix: elementSchema.array().optional(), - period: periodSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR HumanName data type. - */ -export const humanNameSchema: ZodType = untypedHumanNameSchema diff --git a/packages/fhir/src/elements/dataTypes/identifier.ts b/packages/fhir/src/elements/dataTypes/identifier.ts deleted file mode 100644 index 8623402a..00000000 --- a/packages/fhir/src/elements/dataTypes/identifier.ts +++ /dev/null @@ -1,45 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Identifier } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { codeableConceptSchema } from './codeableConcept.js' -import { periodSchema } from './period.js' -import { stringSchema, uriSchema } from './primitiveTypes.js' -import { referenceSchema } from './reference.js' -import { elementSchema } from '../element.js' - -const identifierUseSchema = z.enum([ - 'usual', - 'official', - 'temp', - 'secondary', - 'old', -]) - -/** - * Zod schema for FHIR Identifier data type. - */ -export const untypedIdentifierSchema = z.lazy(() => - elementSchema.extend({ - use: identifierUseSchema.optional(), - _use: elementSchema.optional(), - type: codeableConceptSchema.optional(), - system: uriSchema.optional(), - _system: elementSchema.optional(), - value: stringSchema.optional(), - _value: elementSchema.optional(), - period: periodSchema.optional(), - assigner: referenceSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Identifier data type. - */ -export const identifierSchema: ZodType = untypedIdentifierSchema diff --git a/packages/fhir/src/elements/dataTypes/marketingStatus.ts b/packages/fhir/src/elements/dataTypes/marketingStatus.ts deleted file mode 100644 index 64e06215..00000000 --- a/packages/fhir/src/elements/dataTypes/marketingStatus.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type MarketingStatus } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { codeableConceptSchema } from './codeableConcept.js' -import { periodSchema } from './period.js' -import { dateTimeSchema } from './primitiveTypes.js' -import { backboneElementSchema } from '../backBoneElement.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR MarketingStatus data type. - */ -export const untypedMarketingStatusSchema = z.lazy(() => - backboneElementSchema.extend({ - country: codeableConceptSchema.optional(), - dateRange: periodSchema.optional(), - jurisdiction: codeableConceptSchema.optional(), - restoreDate: dateTimeSchema.optional(), - _restoreDate: elementSchema.optional(), - status: codeableConceptSchema, - }), -) satisfies ZodType - -/** - * Zod schema for FHIR MarketingStatus data type. - */ -export const marketingStatusSchema: ZodType = - untypedMarketingStatusSchema diff --git a/packages/fhir/src/elements/dataTypes/money.ts b/packages/fhir/src/elements/dataTypes/money.ts deleted file mode 100644 index 07ae4c66..00000000 --- a/packages/fhir/src/elements/dataTypes/money.ts +++ /dev/null @@ -1,28 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Money } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { codeSchema, decimalSchema } from './primitiveTypes.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR Money data type. - */ -export const untypedMoneySchema = z.lazy(() => - elementSchema.extend({ - value: decimalSchema.optional(), - currency: codeSchema.optional(), - _currency: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Money data type. - */ -export const moneySchema: ZodType = untypedMoneySchema diff --git a/packages/fhir/src/elements/dataTypes/narrative.ts b/packages/fhir/src/elements/dataTypes/narrative.ts deleted file mode 100644 index f4087849..00000000 --- a/packages/fhir/src/elements/dataTypes/narrative.ts +++ /dev/null @@ -1,36 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Narrative } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { xhtmlSchema } from './primitiveTypes.js' -import { elementSchema } from '../element.js' - -const narrativeStatusSchema = z.enum([ - 'generated', - 'extensions', - 'additional', - 'empty', -]) - -/** - * Zod schema for FHIR Narrative data type. - */ -export const untypedNarrativeSchema = z.lazy(() => - elementSchema.extend({ - status: narrativeStatusSchema, - _status: elementSchema.optional(), - div: xhtmlSchema, - _div: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Narrative data type. - */ -export const narrativeSchema: ZodType = untypedNarrativeSchema diff --git a/packages/fhir/src/elements/dataTypes/period.ts b/packages/fhir/src/elements/dataTypes/period.ts deleted file mode 100644 index 60611b48..00000000 --- a/packages/fhir/src/elements/dataTypes/period.ts +++ /dev/null @@ -1,29 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Period } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { dateTimeSchema } from './primitiveTypes.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR Period data type. - */ -export const untypedPeriodSchema = z.lazy(() => - elementSchema.extend({ - start: dateTimeSchema.optional(), - _start: elementSchema.optional(), - end: dateTimeSchema.optional(), - _end: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Period data type. - */ -export const periodSchema: ZodType = untypedPeriodSchema diff --git a/packages/fhir/src/elements/dataTypes/primitiveTypes.ts b/packages/fhir/src/elements/dataTypes/primitiveTypes.ts deleted file mode 100644 index f4987910..00000000 --- a/packages/fhir/src/elements/dataTypes/primitiveTypes.ts +++ /dev/null @@ -1,133 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -// This file contains the primitive types used in FHIR resources, as defined in https://hl7.org/fhir/R4B/datatypes.html. - -/** - * Zod schema for FHIR dateTime primitive type. - */ -export const dateTimeSchema = z - .string() - .regex( - /([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00)))?)?)?/, - ) - -/** - * Zod schema for FHIR date primitive type. - */ -export const dateSchema = z - .string() - .regex( - /([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1]))?)?/, - ) - -/** - * Zod schema for FHIR url primitive type. - */ -export const urlSchema = z.url() - -// FHIR permits absolute and relative RFC 3986 references, including URNs. -/** - * Zod schema for FHIR uri primitive type. - */ -export const uriSchema = z.string().regex(/\S*/) - -/** - * Zod schema for FHIR time primitive type. - */ -export const timeSchema = z - .string() - .regex(/([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\.[0-9]+)?/) - -/** - * Zod schema for FHIR code primitive type. - */ -export const codeSchema = z.string().regex(/[^\s]+(\s[^\s]+)*/) - -/** - * Zod schema for FHIR positiveDecimal primitive type (not in FHIR spec). - */ -export const positiveDecimalSchema = z.number().positive() - -/** - * Zod schema for FHIR decimal primitive type. - */ -export const decimalSchema = z.number() - -/** - * Zod schema for FHIR oid primitive type. - */ -export const oidSchema = z.string().regex(/urn:oid:[0-2](\.(0|[1-9][0-9]*))+/) - -/** - * Zod schema for FHIR id primitive type. - */ -export const idSchema = z.string().regex(/[A-Za-z0-9.-]{1,64}/) - -/** - * Zod schema for FHIR instant primitive type. - */ -export const instantSchema = z - .string() - .regex( - /([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))/, - ) - -/** - * Zod schema for FHIR markdown primitive type. - */ -export const markdownSchema = z.string().regex(/\s*(\S|\s)*/) - -/** - * Zod schema for FHIR integer primitive type. - */ -export const intSchema = z.number().int() - -/** - * Zod schema for FHIR unsignedInt primitive type. - */ -export const unsignedIntSchema = z.number().int().nonnegative() - -/** - * Zod schema for FHIR positiveInt primitive type. - */ -export const positiveIntSchema = z.number().int().positive() - -/** - * Zod schema for FHIR string primitive type. - */ -export const stringSchema = z.string() - -/** - * Zod schema for FHIR boolean primitive type. - */ -export const booleanSchema = z.boolean() - -/** - * Zod schema for FHIR uuid primitive type. - */ -export const uuidSchema = z.uuid() - -/** - * Zod schema for FHIR xhtml primitive type. - */ -export const xhtmlSchema = z.string() - -/** - * Zod schema for FHIR canonical primitive type. - */ -export const canonicalSchema = z.url() - -/** - * Zod schema for FHIR base64Binary primitive type. - */ -export const base64BinarySchema = z - .string() - .regex(/(\s*([0-9a-zA-Z+=]){4}\s*)+/) diff --git a/packages/fhir/src/elements/dataTypes/quantity.ts b/packages/fhir/src/elements/dataTypes/quantity.ts deleted file mode 100644 index f2d706a0..00000000 --- a/packages/fhir/src/elements/dataTypes/quantity.ts +++ /dev/null @@ -1,41 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Quantity } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { - codeSchema, - decimalSchema, - stringSchema, - uriSchema, -} from './primitiveTypes.js' -import { elementSchema } from '../element.js' - -const quantityComparatorSchema = z.enum(['<', '<=', '>=', '>']) - -/** - * Zod schema for FHIR Quantity data type. - */ -export const untypedQuantitySchema = z.lazy(() => - elementSchema.extend({ - value: decimalSchema.optional(), - comparator: quantityComparatorSchema.optional(), - _comparator: elementSchema.optional(), - unit: stringSchema.optional(), - _unit: elementSchema.optional(), - system: uriSchema.optional(), - _system: elementSchema.optional(), - code: codeSchema.optional(), - _code: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Quantity data type. - */ -export const quantitySchema: ZodType = untypedQuantitySchema diff --git a/packages/fhir/src/elements/dataTypes/range.ts b/packages/fhir/src/elements/dataTypes/range.ts deleted file mode 100644 index ed802c76..00000000 --- a/packages/fhir/src/elements/dataTypes/range.ts +++ /dev/null @@ -1,27 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Range } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { quantitySchema } from './quantity.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR Range data type. - */ -export const untypedRangeSchema = z.lazy(() => - elementSchema.extend({ - low: quantitySchema.optional(), - high: quantitySchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Range data type. - */ -export const rangeSchema: ZodType = untypedRangeSchema diff --git a/packages/fhir/src/elements/dataTypes/ratio.ts b/packages/fhir/src/elements/dataTypes/ratio.ts deleted file mode 100644 index f9b12520..00000000 --- a/packages/fhir/src/elements/dataTypes/ratio.ts +++ /dev/null @@ -1,27 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Ratio } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { quantitySchema } from './quantity.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR Ratio data type. - */ -export const untypedRatioSchema = z.lazy(() => - elementSchema.extend({ - numerator: quantitySchema.optional(), - denominator: quantitySchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Ratio data type. - */ -export const ratioSchema: ZodType = untypedRatioSchema diff --git a/packages/fhir/src/elements/dataTypes/ratioRange.ts b/packages/fhir/src/elements/dataTypes/ratioRange.ts deleted file mode 100644 index 8f1ca6ea..00000000 --- a/packages/fhir/src/elements/dataTypes/ratioRange.ts +++ /dev/null @@ -1,28 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type RatioRange } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { quantitySchema } from './quantity.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR RatioRange data type. - */ -export const untypedRatioRangeSchema = z.lazy(() => - elementSchema.extend({ - lowNumerator: quantitySchema.optional(), - highNumerator: quantitySchema.optional(), - denominator: quantitySchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR RatioRange data type. - */ -export const ratioRangeSchema: ZodType = untypedRatioRangeSchema diff --git a/packages/fhir/src/elements/dataTypes/reference.ts b/packages/fhir/src/elements/dataTypes/reference.ts deleted file mode 100644 index 6fe00730..00000000 --- a/packages/fhir/src/elements/dataTypes/reference.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Reference } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { identifierSchema } from './identifier.js' -import { stringSchema, uriSchema } from './primitiveTypes.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR Reference data type. - */ -export const untypedReferenceSchema = z.lazy(() => - elementSchema.extend({ - reference: stringSchema.optional(), - _reference: elementSchema.optional(), - type: uriSchema.optional(), - _type: elementSchema.optional(), - get identifier() { - return identifierSchema.optional() - }, - display: stringSchema.optional(), - _display: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Reference data type. - */ -export const referenceSchema: ZodType = untypedReferenceSchema diff --git a/packages/fhir/src/elements/dataTypes/sampledData.ts b/packages/fhir/src/elements/dataTypes/sampledData.ts deleted file mode 100644 index eff0cef3..00000000 --- a/packages/fhir/src/elements/dataTypes/sampledData.ts +++ /dev/null @@ -1,39 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type SampledData } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { - decimalSchema, - positiveDecimalSchema, - positiveIntSchema, - stringSchema, -} from './primitiveTypes.js' -import { quantitySchema } from './quantity.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR SampledData data type. - */ -export const untypedSampledDataSchema = z.lazy(() => - elementSchema.extend({ - origin: quantitySchema, - period: positiveDecimalSchema, - factor: decimalSchema.optional(), - lowerLimit: decimalSchema.optional(), - upperLimit: decimalSchema.optional(), - dimensions: positiveIntSchema, - data: stringSchema.optional(), - _data: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR SampledData data type. - */ -export const sampledDataSchema: ZodType = untypedSampledDataSchema diff --git a/packages/fhir/src/elements/dataTypes/signature.ts b/packages/fhir/src/elements/dataTypes/signature.ts deleted file mode 100644 index f7625ef1..00000000 --- a/packages/fhir/src/elements/dataTypes/signature.ts +++ /dev/null @@ -1,43 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Signature } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { codingSchema } from './coding.js' -import { - base64BinarySchema, - codeSchema, - instantSchema, -} from './primitiveTypes.js' -import { referenceSchema } from './reference.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR Signature data type. - */ -export const untypedSignatureSchema = z.lazy(() => - elementSchema.extend({ - type: codingSchema.array().min(1), - _type: elementSchema.optional(), - when: instantSchema, - _when: elementSchema.optional(), - who: referenceSchema, - onBehalfOf: referenceSchema.optional(), - targetFormat: codeSchema.optional(), - _targetFormat: elementSchema.optional(), - sigFormat: codeSchema.optional(), - _sigFormat: elementSchema.optional(), - data: base64BinarySchema.optional(), - _data: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Signature data type. - */ -export const signatureSchema: ZodType = untypedSignatureSchema diff --git a/packages/fhir/src/elements/dataTypes/timing.ts b/packages/fhir/src/elements/dataTypes/timing.ts deleted file mode 100644 index cf6f597f..00000000 --- a/packages/fhir/src/elements/dataTypes/timing.ts +++ /dev/null @@ -1,84 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Timing } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { codeableConceptSchema } from './codeableConcept.js' -import { periodSchema } from './period.js' -import { - dateTimeSchema, - decimalSchema, - positiveIntSchema, - timeSchema, -} from './primitiveTypes.js' -import { quantitySchema } from './quantity.js' -import { rangeSchema } from './range.js' -import { eventTimingSchema } from '../../valueSets/index.js' -import { elementSchema } from '../element.js' - -const timingRepeatDayOfWeekSchema = z.enum([ - 'mon', - 'tue', - 'wed', - 'thu', - 'fri', - 'sat', - 'sun', -]) - -const timingRepeatTimeUnitSchema = z.enum([ - 's', - 'min', - 'h', - 'd', - 'wk', - 'mo', - 'a', -]) - -/** - * Zod schema for FHIR Timing data type. - */ -export const untypedTimingSchema = z.lazy(() => - elementSchema.extend({ - event: dateTimeSchema.array().optional(), - _event: elementSchema.array().optional(), - repeat: elementSchema - .extend({ - boundsDuration: quantitySchema.optional(), - boundsRange: rangeSchema.optional(), - boundsPeriod: periodSchema.optional(), - count: positiveIntSchema.optional(), - countMax: positiveIntSchema.optional(), - duration: decimalSchema.optional(), - durationMax: decimalSchema.optional(), - durationUnit: timingRepeatTimeUnitSchema.optional(), - _durationUnit: elementSchema.optional(), - frequency: positiveIntSchema.optional(), - frequencyMax: positiveIntSchema.optional(), - period: decimalSchema.optional(), - periodMax: decimalSchema.optional(), - periodUnit: timingRepeatTimeUnitSchema.optional(), - _periodUnit: elementSchema.optional(), - dayOfWeek: timingRepeatDayOfWeekSchema.array().optional(), - _dayOfWeek: elementSchema.array().optional(), - timeOfDay: timeSchema.array().optional(), - _timeOfDay: elementSchema.array().optional(), - when: eventTimingSchema.array().optional(), - _when: elementSchema.array().optional(), - offset: positiveIntSchema.optional(), - }) - .optional(), - code: codeableConceptSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Timing data type. - */ -export const timingSchema: ZodType = untypedTimingSchema diff --git a/packages/fhir/src/elements/domainResource.ts b/packages/fhir/src/elements/domainResource.ts deleted file mode 100644 index 6f41bf79..00000000 --- a/packages/fhir/src/elements/domainResource.ts +++ /dev/null @@ -1,28 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type DomainResource, type Resource } from 'fhir/r4b.js' -import { type ZodType } from 'zod' -import { narrativeSchema } from './dataTypes/narrative.js' -import { extensionSchema } from './extension.js' -import { resourceSchema } from './resource.js' -import { fhirResourceSchema } from '../resources/fhirResource.js' - -const containedResourceSchema: ZodType = fhirResourceSchema.array() - -/** - * Zod schema for FHIR DomainResource data type. - */ -export const domainResourceSchema = resourceSchema.extend({ - text: narrativeSchema.optional(), - get contained() { - return containedResourceSchema.optional() - }, - extension: extensionSchema.array().optional(), - modifierExtension: extensionSchema.array().optional(), -}) satisfies ZodType diff --git a/packages/fhir/src/elements/dosage.ts b/packages/fhir/src/elements/dosage.ts deleted file mode 100644 index b139153c..00000000 --- a/packages/fhir/src/elements/dosage.ts +++ /dev/null @@ -1,56 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Dosage } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { codeableConceptSchema } from './dataTypes/codeableConcept.js' -import { - booleanSchema, - intSchema, - stringSchema, -} from './dataTypes/primitiveTypes.js' -import { quantitySchema } from './dataTypes/quantity.js' -import { rangeSchema } from './dataTypes/range.js' -import { ratioSchema } from './dataTypes/ratio.js' -import { timingSchema } from './dataTypes/timing.js' -import { elementSchema } from './element.js' - -/** - * Zod schema for FHIR Dosage data type. - */ -export const untypedDosageSchema = z.lazy(() => - elementSchema.extend({ - sequence: intSchema.optional(), - text: stringSchema.optional(), - _text: elementSchema.optional(), - additionalInstruction: codeableConceptSchema.array().optional(), - patientInstruction: stringSchema.optional(), - _patientInstruction: elementSchema.optional(), - timing: timingSchema.optional(), - asNeededBoolean: booleanSchema.optional(), - _asNeededBoolean: elementSchema.optional(), - asNeededCodeableConcept: codeableConceptSchema.optional(), - site: codeableConceptSchema.optional(), - route: codeableConceptSchema.optional(), - method: codeableConceptSchema.optional(), - doseAndRate: elementSchema - .extend({ - type: codeableConceptSchema.optional(), - doseQuantity: quantitySchema.optional(), - rateRatio: ratioSchema.optional(), - rateRange: rangeSchema.optional(), - }) - .array() - .optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Dosage data type. - */ -export const dosageSchema: ZodType = untypedDosageSchema diff --git a/packages/fhir/src/elements/element.ts b/packages/fhir/src/elements/element.ts deleted file mode 100644 index 263bc9dd..00000000 --- a/packages/fhir/src/elements/element.ts +++ /dev/null @@ -1,24 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Element } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { stringSchema } from './dataTypes/primitiveTypes.js' -import { extensionSchema } from './extension.js' - -/** - * Zod schema for FHIR Element data type (base type for all FHIR elements). - */ -export const elementSchema = z.object({ - id: stringSchema.optional(), - get _id() { - const schema = elementSchema as ZodType - return schema.optional() - }, - extension: extensionSchema.array().optional(), -}) satisfies ZodType diff --git a/packages/fhir/src/elements/extension.ts b/packages/fhir/src/elements/extension.ts deleted file mode 100644 index fdb047cc..00000000 --- a/packages/fhir/src/elements/extension.ts +++ /dev/null @@ -1,139 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Extension } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { addressSchema } from './dataTypes/address.js' -import { annotationSchema } from './dataTypes/annotation.js' -import { attachmentSchema } from './dataTypes/attachment.js' -import { codeableConceptSchema } from './dataTypes/codeableConcept.js' -import { codingSchema } from './dataTypes/coding.js' -import { contactPointSchema } from './dataTypes/contactPoint.js' -import { humanNameSchema } from './dataTypes/humanName.js' -import { identifierSchema } from './dataTypes/identifier.js' -import { moneySchema } from './dataTypes/money.js' -import { periodSchema } from './dataTypes/period.js' -import { - base64BinarySchema, - booleanSchema, - canonicalSchema, - codeSchema, - dateSchema, - dateTimeSchema, - decimalSchema, - idSchema, - instantSchema, - intSchema, - markdownSchema, - oidSchema, - positiveIntSchema, - stringSchema, - timeSchema, - unsignedIntSchema, - urlSchema, - uuidSchema, -} from './dataTypes/primitiveTypes.js' -import { quantitySchema } from './dataTypes/quantity.js' -import { rangeSchema } from './dataTypes/range.js' -import { ratioSchema } from './dataTypes/ratio.js' -import { ratioRangeSchema } from './dataTypes/ratioRange.js' -import { referenceSchema } from './dataTypes/reference.js' -import { sampledDataSchema } from './dataTypes/sampledData.js' -import { signatureSchema } from './dataTypes/signature.js' -import { timingSchema } from './dataTypes/timing.js' -import { dosageSchema } from './dosage.js' -import { elementSchema } from './element.js' -import { contactDetailSchema } from './metaDataTypes/contactDetail.js' -import { contributorSchema } from './metaDataTypes/contributor.js' -import { dataRequirementSchema } from './metaDataTypes/dataRequirement.js' -import { expressionSchema } from './metaDataTypes/expression.js' -import { parameterDefinitionSchema } from './metaDataTypes/parameterDefinition.js' -import { relatedArtifactSchema } from './metaDataTypes/relatedArtifact.js' -import { triggerDefinitionSchema } from './metaDataTypes/triggerDefinition.js' -import { usageContextSchema } from './metaDataTypes/usageContext.js' - -/** - * Zod schema for FHIR Extension data type. - */ -export const untypedExtensionSchema = z.lazy(() => - elementSchema.extend({ - url: urlSchema, - _url: elementSchema.optional(), - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: elementSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueCanonical: canonicalSchema.optional(), - _valueCanonical: elementSchema.optional(), - valueCodeableConcept: codeableConceptSchema.optional(), - valueCode: codeSchema.optional(), - _valueCode: elementSchema.optional(), - valueDate: dateSchema.optional(), - _valueDate: elementSchema.optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: elementSchema.optional(), - valueDecimal: decimalSchema.optional(), - valueId: idSchema.optional(), - _valueId: elementSchema.optional(), - valueInstant: instantSchema.optional(), - _valueInstant: elementSchema.optional(), - valueInteger: intSchema.optional(), - valueMarkdown: markdownSchema.optional(), - _valueMarkdown: elementSchema.optional(), - valueOid: oidSchema.optional(), - _valueOid: elementSchema.optional(), - valuePositiveInt: positiveIntSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueTime: timeSchema.optional(), - _valueTime: elementSchema.optional(), - valueUnsignedInt: unsignedIntSchema.optional(), - valueUri: urlSchema.optional(), - _valueUri: elementSchema.optional(), - valueUrl: urlSchema.optional(), - _valueUrl: elementSchema.optional(), - valueUuid: uuidSchema.optional(), - _valueUuid: elementSchema.optional(), - valueAddress: addressSchema.optional(), - valueAge: quantitySchema.optional(), - valueAnnotation: annotationSchema.optional(), - valueAttachment: attachmentSchema.optional(), - valueCodeableReference: codeableConceptSchema.optional(), - valueCoding: codingSchema.optional(), - valueContactPoint: contactPointSchema.optional(), - valueCount: quantitySchema.optional(), - valueDistance: quantitySchema.optional(), - valueDuration: quantitySchema.optional(), - valueHumanName: humanNameSchema.optional(), - valueIdentifier: identifierSchema.optional(), - valueMoney: moneySchema.optional(), - valuePeriod: periodSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueRange: rangeSchema.optional(), - valueRatio: ratioSchema.optional(), - valueRatioRange: ratioRangeSchema.optional(), - valueReference: referenceSchema.optional(), - valueSampledData: sampledDataSchema.optional(), - valueSignature: signatureSchema.optional(), - valueTiming: timingSchema.optional(), - valueContactDetail: contactDetailSchema.optional(), - valueContributor: contributorSchema.optional(), - valueDataRequirement: dataRequirementSchema.optional(), - valueExpression: expressionSchema.optional(), - valueParameterDefinition: parameterDefinitionSchema.optional(), - valueRelatedArtifact: relatedArtifactSchema.optional(), - valueTriggerDefinition: triggerDefinitionSchema.optional(), - valueUsageContext: usageContextSchema.optional(), - valueDosage: dosageSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Extension data type. - */ -export const extensionSchema: ZodType = untypedExtensionSchema diff --git a/packages/fhir/src/elements/index.ts b/packages/fhir/src/elements/index.ts deleted file mode 100644 index 2fea292d..00000000 --- a/packages/fhir/src/elements/index.ts +++ /dev/null @@ -1,45 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -export * from './backBoneElement.js' -export * from './dataTypes/address.js' -export * from './dataTypes/annotation.js' -export * from './dataTypes/attachment.js' -export * from './dataTypes/codeableConcept.js' -export * from './dataTypes/codeableReference.js' -export * from './dataTypes/coding.js' -export * from './dataTypes/contactPoint.js' -export * from './dataTypes/humanName.js' -export * from './dataTypes/identifier.js' -export * from './dataTypes/marketingStatus.js' -export * from './dataTypes/money.js' -export * from './dataTypes/narrative.js' -export * from './dataTypes/period.js' -export * from './dataTypes/primitiveTypes.js' -export * from './dataTypes/quantity.js' -export * from './dataTypes/range.js' -export * from './dataTypes/ratio.js' -export * from './dataTypes/ratioRange.js' -export * from './dataTypes/reference.js' -export * from './dataTypes/sampledData.js' -export * from './dataTypes/signature.js' -export * from './dataTypes/timing.js' -export * from './domainResource.js' -export * from './dosage.js' -export * from './element.js' -export * from './extension.js' -export * from './meta.js' -export * from './metaDataTypes/contactDetail.js' -export * from './metaDataTypes/contributor.js' -export * from './metaDataTypes/dataRequirement.js' -export * from './metaDataTypes/expression.js' -export * from './metaDataTypes/parameterDefinition.js' -export * from './metaDataTypes/relatedArtifact.js' -export * from './metaDataTypes/triggerDefinition.js' -export * from './metaDataTypes/usageContext.js' -export * from './resource.js' diff --git a/packages/fhir/src/elements/meta.ts b/packages/fhir/src/elements/meta.ts deleted file mode 100644 index 53ed19bb..00000000 --- a/packages/fhir/src/elements/meta.ts +++ /dev/null @@ -1,40 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Meta } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { codingSchema } from './dataTypes/coding.js' -import { - idSchema, - instantSchema, - uriSchema, -} from './dataTypes/primitiveTypes.js' -import { elementSchema } from './element.js' - -/** - * Zod schema for FHIR Meta data type. - */ -export const untypedMetaSchema = z.lazy(() => - elementSchema.extend({ - versionId: idSchema.optional(), - _versionId: elementSchema.optional(), - lastUpdated: instantSchema.optional(), - _lastUpdated: elementSchema.optional(), - source: uriSchema.optional(), - _source: elementSchema.optional(), - profile: uriSchema.array().optional(), - _profile: elementSchema.array().optional(), - security: codingSchema.array().optional(), - tag: codingSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Meta data type. - */ -export const metaSchema: ZodType = untypedMetaSchema diff --git a/packages/fhir/src/elements/metaDataTypes/contactDetail.ts b/packages/fhir/src/elements/metaDataTypes/contactDetail.ts deleted file mode 100644 index ebff53e5..00000000 --- a/packages/fhir/src/elements/metaDataTypes/contactDetail.ts +++ /dev/null @@ -1,30 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type ContactDetail } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { contactPointSchema } from '../dataTypes/contactPoint.js' -import { stringSchema } from '../dataTypes/primitiveTypes.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR ContactDetail data type. - */ -export const untypedContactDetailSchema = z.lazy(() => - elementSchema.extend({ - name: stringSchema.optional(), - _name: elementSchema.optional(), - telecom: contactPointSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ContactDetail data type. - */ -export const contactDetailSchema: ZodType = - untypedContactDetailSchema diff --git a/packages/fhir/src/elements/metaDataTypes/contributor.ts b/packages/fhir/src/elements/metaDataTypes/contributor.ts deleted file mode 100644 index 7739c918..00000000 --- a/packages/fhir/src/elements/metaDataTypes/contributor.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Contributor } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { contactDetailSchema } from './contactDetail.js' -import { stringSchema } from '../dataTypes/primitiveTypes.js' -import { elementSchema } from '../element.js' - -const contributorTypeSchema = z.enum([ - 'author', - 'editor', - 'reviewer', - 'endorser', -]) - -/** - * Zod schema for FHIR Contributor data type. - */ -export const untypedContributorSchema = z.lazy(() => - elementSchema.extend({ - type: contributorTypeSchema, - _type: elementSchema.optional(), - name: stringSchema, - _name: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Contributor data type. - */ -export const contributorSchema: ZodType = untypedContributorSchema diff --git a/packages/fhir/src/elements/metaDataTypes/dataRequirement.ts b/packages/fhir/src/elements/metaDataTypes/dataRequirement.ts deleted file mode 100644 index 52644c9a..00000000 --- a/packages/fhir/src/elements/metaDataTypes/dataRequirement.ts +++ /dev/null @@ -1,73 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type DataRequirement } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { fhirTypeSchema } from '../../valueSets/index.js' -import { codeableConceptSchema } from '../dataTypes/codeableConcept.js' -import { codingSchema } from '../dataTypes/coding.js' -import { periodSchema } from '../dataTypes/period.js' -import { - dateTimeSchema, - positiveIntSchema, - stringSchema, -} from '../dataTypes/primitiveTypes.js' -import { referenceSchema } from '../dataTypes/reference.js' -import { elementSchema } from '../element.js' - -const dataRequirementSortDirectionSchema = z.enum(['ascending', 'descending']) - -/** - * Zod schema for FHIR DataRequirement data type. - */ -export const untypedDataRequirementSchema = z.lazy(() => - elementSchema.extend({ - type: fhirTypeSchema, - _type: elementSchema.optional(), - profile: stringSchema.array().optional(), - _profile: elementSchema.array().optional(), - subjectCodeableConcept: codeableConceptSchema.optional(), - subjectReference: referenceSchema.optional(), - mustSupport: stringSchema.array().optional(), - _mustSupport: elementSchema.array().optional(), - codeFilter: elementSchema - .extend({ - path: stringSchema.optional(), - searchParam: stringSchema.optional(), - valueSet: stringSchema.optional(), - code: codingSchema.array().optional(), - }) - .array() - .optional(), - dateFilter: elementSchema - .extend({ - path: stringSchema.optional(), - searchParam: stringSchema.optional(), - valueDateTime: dateTimeSchema.optional(), - valuePeriod: periodSchema.optional(), - }) - .array() - .optional(), - limit: positiveIntSchema.optional(), - sort: elementSchema - .extend({ - direction: dataRequirementSortDirectionSchema, - _direction: elementSchema.optional(), - path: stringSchema, - _path: elementSchema.optional(), - }) - .array() - .optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR DataRequirement data type. - */ -export const dataRequirementSchema: ZodType = - untypedDataRequirementSchema diff --git a/packages/fhir/src/elements/metaDataTypes/expression.ts b/packages/fhir/src/elements/metaDataTypes/expression.ts deleted file mode 100644 index a1d91ba7..00000000 --- a/packages/fhir/src/elements/metaDataTypes/expression.ts +++ /dev/null @@ -1,40 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Expression } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { - codeSchema, - idSchema, - stringSchema, - uriSchema, -} from '../dataTypes/primitiveTypes.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR Expression data type. - */ -export const untypedExpressionSchema = z.lazy(() => - elementSchema.extend({ - description: stringSchema.optional(), - _description: elementSchema.optional(), - name: idSchema.optional(), - _name: elementSchema.optional(), - language: codeSchema, - _language: elementSchema.optional(), - expression: stringSchema.optional(), - _expression: elementSchema.optional(), - reference: uriSchema.optional(), - _reference: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Expression data type. - */ -export const expressionSchema: ZodType = untypedExpressionSchema diff --git a/packages/fhir/src/elements/metaDataTypes/parameterDefinition.ts b/packages/fhir/src/elements/metaDataTypes/parameterDefinition.ts deleted file mode 100644 index bc5e7442..00000000 --- a/packages/fhir/src/elements/metaDataTypes/parameterDefinition.ts +++ /dev/null @@ -1,45 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type ParameterDefinition } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { fhirTypeSchema } from '../../valueSets/index.js' -import { intSchema, stringSchema } from '../dataTypes/primitiveTypes.js' -import { elementSchema } from '../element.js' - -const parameterDefinitionUseSchema = z.enum(['in', 'out']) - -/** - * Zod schema for FHIR ParameterDefinition data type. - */ -export const untypedParameterDefinitionSchema = z.lazy(() => - elementSchema.extend({ - name: stringSchema.optional(), - _name: elementSchema.optional(), - use: parameterDefinitionUseSchema, - _use: elementSchema.optional(), - min: intSchema.optional(), - max: z - .string() - .regex(/^(\d+|\*)$/) - .optional(), - _max: elementSchema.optional(), - documentation: stringSchema.optional(), - _documentation: elementSchema.optional(), - type: fhirTypeSchema, - _type: elementSchema.optional(), - profile: stringSchema.optional(), - _profile: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ParameterDefinition data type. - */ -export const parameterDefinitionSchema: ZodType = - untypedParameterDefinitionSchema diff --git a/packages/fhir/src/elements/metaDataTypes/relatedArtifact.ts b/packages/fhir/src/elements/metaDataTypes/relatedArtifact.ts deleted file mode 100644 index aa47caa9..00000000 --- a/packages/fhir/src/elements/metaDataTypes/relatedArtifact.ts +++ /dev/null @@ -1,51 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type RelatedArtifact } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { attachmentSchema } from '../dataTypes/attachment.js' -import { stringSchema, urlSchema } from '../dataTypes/primitiveTypes.js' -import { elementSchema } from '../element.js' - -const relatedArtifactTypeSchema = z.enum([ - 'documentation', - 'justification', - 'citation', - 'predecessor', - 'successor', - 'derived-from', - 'depends-on', - 'composed-of', -]) - -/** - * Zod schema for FHIR RelatedArtifact data type. - */ -export const untypedRelatedArtifactSchema = z.lazy(() => - elementSchema.extend({ - type: relatedArtifactTypeSchema, - _type: elementSchema.optional(), - label: stringSchema.optional(), - _label: elementSchema.optional(), - display: stringSchema.optional(), - _display: elementSchema.optional(), - citation: stringSchema.optional(), - _citation: elementSchema.optional(), - url: urlSchema.optional(), - _url: elementSchema.optional(), - document: attachmentSchema.optional(), - resource: stringSchema.optional(), - _resource: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR RelatedArtifact data type. - */ -export const relatedArtifactSchema: ZodType = - untypedRelatedArtifactSchema diff --git a/packages/fhir/src/elements/metaDataTypes/triggerDefinition.ts b/packages/fhir/src/elements/metaDataTypes/triggerDefinition.ts deleted file mode 100644 index 357cb5ca..00000000 --- a/packages/fhir/src/elements/metaDataTypes/triggerDefinition.ts +++ /dev/null @@ -1,61 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type TriggerDefinition } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { dataRequirementSchema } from './dataRequirement.js' -import { expressionSchema } from './expression.js' -import { codeableConceptSchema } from '../dataTypes/codeableConcept.js' -import { - dateSchema, - dateTimeSchema, - stringSchema, -} from '../dataTypes/primitiveTypes.js' -import { referenceSchema } from '../dataTypes/reference.js' -import { timingSchema } from '../dataTypes/timing.js' -import { elementSchema } from '../element.js' - -const triggerDefinitionTypeSchema = z.enum([ - 'named-event', - 'periodic', - 'data-changed', - 'data-added', - 'data-modified', - 'data-removed', - 'data-accessed', - 'data-access-ended', -]) - -/** - * Zod schema for FHIR TriggerDefinition data type. - */ -export const untypedTriggerDefinitionSchema = z.lazy(() => - elementSchema.extend({ - type: triggerDefinitionTypeSchema, - _type: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - code: codeableConceptSchema.optional(), - subscriptionTopic: stringSchema.optional(), - _subscriptionTopic: elementSchema.optional(), - timingTiming: timingSchema.optional(), - timingReference: referenceSchema.optional(), - timingDate: dateSchema.optional(), - _timingDate: elementSchema.optional(), - timingDateTime: dateTimeSchema.optional(), - _timingDateTime: elementSchema.optional(), - data: dataRequirementSchema.array().optional(), - condition: expressionSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR TriggerDefinition data type. - */ -export const triggerDefinitionSchema: ZodType = - untypedTriggerDefinitionSchema diff --git a/packages/fhir/src/elements/metaDataTypes/usageContext.ts b/packages/fhir/src/elements/metaDataTypes/usageContext.ts deleted file mode 100644 index 3e379a8b..00000000 --- a/packages/fhir/src/elements/metaDataTypes/usageContext.ts +++ /dev/null @@ -1,36 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type UsageContext } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { codeableConceptSchema } from '../dataTypes/codeableConcept.js' -import { codingSchema } from '../dataTypes/coding.js' -import { quantitySchema } from '../dataTypes/quantity.js' -import { rangeSchema } from '../dataTypes/range.js' -import { referenceSchema } from '../dataTypes/reference.js' -import { elementSchema } from '../element.js' - -/** - * Zod schema for FHIR UsageContext data type. - */ -export const untypedUsageContextSchema = z.lazy(() => - elementSchema.extend({ - code: codingSchema, - _code: elementSchema.optional(), - valueCodeableConcept: codeableConceptSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueRange: rangeSchema.optional(), - valueReference: referenceSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR UsageContext data type. - */ -export const usageContextSchema: ZodType = - untypedUsageContextSchema diff --git a/packages/fhir/src/elements/resource.ts b/packages/fhir/src/elements/resource.ts deleted file mode 100644 index 2bba1225..00000000 --- a/packages/fhir/src/elements/resource.ts +++ /dev/null @@ -1,31 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Resource } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { - codeSchema, - stringSchema, - uriSchema, -} from './dataTypes/primitiveTypes.js' -import { elementSchema } from './element.js' -import { metaSchema } from './meta.js' - -/** - * Zod schema for FHIR Resource data type (base type for all FHIR resources). - */ -export const resourceSchema = z.object({ - resourceType: stringSchema, - id: stringSchema.optional(), - _id: elementSchema.optional(), - meta: metaSchema.optional(), - implicitRules: uriSchema.optional(), - _implicitRules: elementSchema.optional(), - language: codeSchema.optional(), - _language: elementSchema.optional(), -}) satisfies ZodType diff --git a/packages/fhir/src/extract/careplan.ts b/packages/fhir/src/extract/careplan.ts new file mode 100644 index 00000000..979feeaf --- /dev/null +++ b/packages/fhir/src/extract/careplan.ts @@ -0,0 +1,50 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { codeableConceptDisplay } from './coding.js' +import { asDate, field } from './internal.js' +import { fhirPeriodToBounds, periodIsActive } from './period.js' +import { annotationTexts } from './resource.js' + +/** The instant a CarePlan was created. */ +export const carePlanCreatedDate = (carePlan: unknown): Date | undefined => + asDate(field(carePlan, 'created')) + +/** The start of a CarePlan's period. */ +export const carePlanPeriodStart = (carePlan: unknown): Date | undefined => + fhirPeriodToBounds(field(carePlan, 'period')).start + +/** The end of a CarePlan's period. */ +export const carePlanPeriodEnd = (carePlan: unknown): Date | undefined => + fhirPeriodToBounds(field(carePlan, 'period')).end + +/** + * Whether a CarePlan's period covers an instant. + * + * A plan stating no period counts as covering it, the same reading `periodIsActive` gives + * elsewhere: a missing period means unbounded rather than never. + */ +export const carePlanPeriodIsActive = ( + carePlan: unknown, + asOf: Date = new Date(), +): boolean => periodIsActive(field(carePlan, 'period'), asOf) + +/** The displays a CarePlan's categories are named by. */ +export const carePlanCategoryDisplays = ( + carePlan: unknown, +): readonly string[] => { + const categories = field(carePlan, 'category') + if (!Array.isArray(categories)) return [] + return categories + .map((category) => codeableConceptDisplay(category)) + .filter((display): display is string => display !== undefined) +} + +/** The texts a CarePlan's notes carry. */ +export const carePlanNoteTexts = (carePlan: unknown): readonly string[] => + annotationTexts(field(carePlan, 'note')) diff --git a/packages/fhir/src/extract/coding.ts b/packages/fhir/src/extract/coding.ts new file mode 100644 index 00000000..6be4761e --- /dev/null +++ b/packages/fhir/src/extract/coding.ts @@ -0,0 +1,74 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/** A Coding reduced to the fields a consumer matches on. */ +export interface CodingParts { + readonly system?: string + readonly code?: string + readonly display?: string +} + +const codings = (concept: unknown): readonly CodingParts[] => { + if (typeof concept !== 'object' || concept === null) return [] + const value = (concept as { readonly coding?: unknown }).coding + if (!Array.isArray(value)) return [] + return value.filter( + (entry): entry is CodingParts => + typeof entry === 'object' && entry !== null, + ) +} + +const text = (concept: unknown): string | undefined => { + if (typeof concept !== 'object' || concept === null) return undefined + const value = (concept as { readonly text?: unknown }).text + return typeof value === 'string' && value.length > 0 ? value : undefined +} + +/** + * The display a CodeableConcept is best named by. + * + * `text` wins over a coding's display: it is what the source chose to call this concept, where a + * coding's display is the terminology's own label. A concept carrying neither is reported as + * undefined rather than as its bare code, which reads as a label but is not one. + */ +export const codeableConceptDisplay = (concept: unknown): string | undefined => + text(concept) ?? + codings(concept).find( + (coding) => typeof coding.display === 'string' && coding.display.length > 0, + )?.display + +/** Every display the concept's codings carry, in the order the concept states them. */ +export const codeableConceptDisplays = (concept: unknown): readonly string[] => + codings(concept) + .map((coding) => coding.display) + .filter((display): display is string => typeof display === 'string') + +/** The codes the concept states in one system, so a caller can match without scanning. */ +export const codesBySystem = ( + concept: unknown, + system: string, +): readonly string[] => + codings(concept) + .filter((coding) => coding.system === system) + .map((coding) => coding.code) + .filter((code): code is string => typeof code === 'string') + +/** + * Whether the concept states this exact code in this exact system. + * + * Both are required: a bare code matched across systems is how two unrelated terminologies get + * conflated, and that mistake is silent. + */ +export const containsCoding = ( + concept: unknown, + system: string, + code: string, +): boolean => + codings(concept).some( + (coding) => coding.system === system && coding.code === code, + ) diff --git a/packages/fhir/src/extract/condition.ts b/packages/fhir/src/extract/condition.ts new file mode 100644 index 00000000..b5bf09ac --- /dev/null +++ b/packages/fhir/src/extract/condition.ts @@ -0,0 +1,94 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + codeableConceptDisplay, + codesBySystem, + containsCoding, +} from './coding.js' +import { asDate, field } from './internal.js' +import { fhirPeriodToBounds } from './period.js' + +const CLINICAL_STATUS = + 'http://terminology.hl7.org/CodeSystem/condition-clinical' +const VERIFICATION_STATUS = + 'http://terminology.hl7.org/CodeSystem/condition-ver-status' + +/** The clinical statuses FHIR counts as the condition still being present. */ +const PRESENT: readonly string[] = ['active', 'recurrence', 'relapse'] + +/** The instant a Condition began, when it states one as a dateTime or a period. */ +export const conditionOnsetDate = (condition: unknown): Date | undefined => + asDate(field(condition, 'onsetDateTime')) ?? + fhirPeriodToBounds(field(condition, 'onsetPeriod')).start + +/** The instant a Condition resolved, when it states one as a dateTime or a period. */ +export const conditionAbatementDate = (condition: unknown): Date | undefined => + asDate(field(condition, 'abatementDateTime')) ?? + fhirPeriodToBounds(field(condition, 'abatementPeriod')).start + +/** The instant a Condition was recorded. */ +export const conditionRecordedDate = (condition: unknown): Date | undefined => + asDate(field(condition, 'recordedDate')) + +/** The display a Condition's code is best named by. */ +export const conditionCodeDisplay = (condition: unknown): string | undefined => + codeableConceptDisplay(field(condition, 'code')) + +/** The clinical status code a Condition states, when it states one. */ +export const conditionClinicalStatus = ( + condition: unknown, +): string | undefined => + codesBySystem(field(condition, 'clinicalStatus'), CLINICAL_STATUS)[0] + +/** + * Whether a Condition is still present. + * + * `clinicalStatus` decides it when stated, because that is the element FHIR gives the answer in: + * a condition coded `resolved` is resolved whether or not anyone recorded an abatement date. + * Only when no status is stated does an abatement of any kind stand in for one. + */ +export const conditionIsActive = (condition: unknown): boolean => { + const status = conditionClinicalStatus(condition) + if (status !== undefined) return PRESENT.includes(status) + return ![ + 'abatementDateTime', + 'abatementAge', + 'abatementPeriod', + 'abatementRange', + 'abatementString', + 'abatementBoolean', + ].some((name) => field(condition, name) !== undefined) +} + +/** Whether a Condition's verification status is `confirmed`. */ +export const conditionIsConfirmed = (condition: unknown): boolean => + containsCoding( + field(condition, 'verificationStatus'), + VERIFICATION_STATUS, + 'confirmed', + ) + +/** The milliseconds a Condition lasted, stated only when it names both onset and abatement. */ +export const conditionDuration = (condition: unknown): number | undefined => { + const onset = conditionOnsetDate(condition) + const abatement = conditionAbatementDate(condition) + if (onset === undefined || abatement === undefined) return undefined + return abatement.getTime() - onset.getTime() +} + +/** The displays a Condition's categories are named by. */ +export const conditionCategoryDisplays = ( + condition: unknown, +): readonly string[] => { + const categories = field(condition, 'category') + if (!Array.isArray(categories)) return [] + return categories + .map((category) => codeableConceptDisplay(category)) + .filter((display): display is string => display !== undefined) +} diff --git a/packages/fhir/src/extract/encode.ts b/packages/fhir/src/extract/encode.ts new file mode 100644 index 00000000..8c35d3e4 --- /dev/null +++ b/packages/fhir/src/extract/encode.ts @@ -0,0 +1,105 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import type { CodingParts } from './coding.js' +import type { PeriodBounds } from './period.js' +import type { QuantityValue } from '../core/quantity.js' +import { err, ok, type Result } from '../core/result.js' + +/** + * The other direction of the accessors: a JavaScript value as the FHIR shape that carries it. + * + * Every encoder is the inverse of the reader beside it, so a value read out and written back + * states the same fact. They are deliberately not the inverse of the *source* bytes: FHIR admits + * several spellings of one instant, and round-tripping preserves the meaning rather than the + * formatting a producer happened to choose. + */ + +/** A Date as the FHIR `dateTime` it names, in UTC and to millisecond precision. */ +export const dateToFhirDateTime = (value: Date): string => value.toISOString() + +/** + * Two instants as the FHIR Period they bound. + * + * An end the caller leaves undefined is omitted rather than written as null, because FHIR states + * an open period by the element's absence. + */ +export const boundsToFhirPeriod = ( + bounds: PeriodBounds, +): { readonly start?: string; readonly end?: string } => ({ + ...(bounds.start === undefined ? {} : { start: bounds.start.toISOString() }), + ...(bounds.end === undefined ? {} : { end: bounds.end.toISOString() }), +}) + +/** + * A measurement as the FHIR Quantity that carries it. + * + * The unit is written as `code` with its system, and repeated as `unit` for display. A Quantity + * stating only `unit` is not comparable, and a reader that took the code back out would find + * nothing there. + */ +export const valueToFhirQuantity = ( + value: QuantityValue, +): Result<{ + readonly value: number + readonly unit: string + readonly code: string + readonly system?: string + readonly comparator?: string +}> => { + if (!Number.isFinite(value.value)) { + return err('out-of-range', 'A Quantity carries a finite number.', ['value']) + } + if (value.unit.length === 0) { + return err('missing-required', 'A Quantity states a unit.', ['unit']) + } + return ok({ + value: value.value, + unit: value.unit, + code: value.unit, + ...(value.system === undefined ? {} : { system: value.system }), + ...(value.comparator === undefined ? {} : { comparator: value.comparator }), + }) +} + +/** + * A code in a system as the FHIR CodeableConcept that states it. + * + * `text` is set only when the caller gives a display, so a concept never claims a label the + * caller did not state — the same rule `codeableConceptDisplay` reads it back by. + */ +export const codingToCodeableConcept = ( + coding: CodingParts, +): { + readonly coding: readonly CodingParts[] + readonly text?: string +} => ({ + coding: [coding], + ...(coding.display === undefined ? {} : { text: coding.display }), +}) + +/** A resource type and id as the relative reference literal that points at it. */ +export const toFhirReference = ( + resourceType: string, + id: string, +): { readonly reference: string; readonly type: string } => ({ + reference: `${resourceType}/${id}`, + type: resourceType, +}) + +/** + * Bytes as the FHIR `base64Binary` that carries them. + * + * Unpadded and unwrapped: the lexical form admits whitespace, but a value written without it is + * what every producer emits and what `decodeBase64Binary` reads back unchanged. + */ +export const encodeBase64Binary = (bytes: Uint8Array): string => { + let binary = '' + for (const byte of bytes) binary += String.fromCharCode(byte) + return btoa(binary) +} diff --git a/packages/fhir/src/extract/identifier.ts b/packages/fhir/src/extract/identifier.ts new file mode 100644 index 00000000..e34c1e2c --- /dev/null +++ b/packages/fhir/src/extract/identifier.ts @@ -0,0 +1,67 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { containsCoding } from './coding.js' + +/** An Identifier reduced to the fields a consumer selects on. */ +export interface IdentifierParts { + readonly system?: string + readonly value?: string + readonly use?: string + readonly type?: unknown +} + +const identifiers = (resource: unknown): readonly IdentifierParts[] => { + if (typeof resource !== 'object' || resource === null) return [] + const value = (resource as { readonly identifier?: unknown }).identifier + // Most resources carry a list; a few, such as Bundle, carry exactly one. + const list = Array.isArray(value) ? value : [value] + return list.filter( + (entry): entry is IdentifierParts => + typeof entry === 'object' && entry !== null, + ) +} + +/** Every identifier value the resource states in one system. */ +export const identifiersBySystem = ( + resource: unknown, + system: string, +): readonly string[] => + identifiers(resource) + .filter((identifier) => identifier.system === system) + .map((identifier) => identifier.value) + .filter((value): value is string => typeof value === 'string') + +/** + * The single identifier value the resource states in one system. + * + * The first when several are stated, which is the order the resource chose; a caller that must + * distinguish them wants `identifiersBySystem` and its own rule. + */ +export const identifierBySystem = ( + resource: unknown, + system: string, +): string | undefined => identifiersBySystem(resource, system)[0] + +/** Every identifier value whose type states this code, in the given type system. */ +export const identifiersByType = ( + resource: unknown, + system: string, + code: string, +): readonly string[] => + identifiers(resource) + .filter((identifier) => containsCoding(identifier.type, system, code)) + .map((identifier) => identifier.value) + .filter((value): value is string => typeof value === 'string') + +/** The first identifier value whose type states this code. */ +export const identifierByType = ( + resource: unknown, + system: string, + code: string, +): string | undefined => identifiersByType(resource, system, code)[0] diff --git a/packages/fhir/src/extract/immunization.ts b/packages/fhir/src/extract/immunization.ts new file mode 100644 index 00000000..313075da --- /dev/null +++ b/packages/fhir/src/extract/immunization.ts @@ -0,0 +1,78 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { codeableConceptDisplay } from './coding.js' +import { asDate, field } from './internal.js' +import { annotationTexts } from './resource.js' + +/** The instant an Immunization was administered, when stated as a dateTime. */ +export const immunizationOccurrenceDate = ( + immunization: unknown, +): Date | undefined => asDate(field(immunization, 'occurrenceDateTime')) + +/** The instant an Immunization was recorded. */ +export const immunizationRecordedDate = ( + immunization: unknown, +): Date | undefined => asDate(field(immunization, 'recorded')) + +/** The date the administered lot expires. */ +export const immunizationExpirationDate = ( + immunization: unknown, +): Date | undefined => asDate(field(immunization, 'expirationDate')) + +/** The display the administered vaccine is named by. */ +export const immunizationVaccineDisplay = ( + immunization: unknown, +): string | undefined => + codeableConceptDisplay(field(immunization, 'vaccineCode')) + +/** The display the administration site is named by. */ +export const immunizationSiteDisplay = ( + immunization: unknown, +): string | undefined => codeableConceptDisplay(field(immunization, 'site')) + +/** The display the administration route is named by. */ +export const immunizationRouteDisplay = ( + immunization: unknown, +): string | undefined => codeableConceptDisplay(field(immunization, 'route')) + +/** + * Whether the administered lot had expired at an instant. + * + * Undefined rather than false when no expiration date is stated: "not known to have expired" and + * "known not to have expired" are different answers, and a bare false reads as the second. + */ +export const immunizationIsExpired = ( + immunization: unknown, + asOf: Date = new Date(), +): boolean | undefined => { + const expires = immunizationExpirationDate(immunization) + return expires === undefined ? undefined : asOf > expires +} + +/** The texts an Immunization's notes carry. */ +export const immunizationNoteTexts = ( + immunization: unknown, +): readonly string[] => annotationTexts(field(immunization, 'note')) + +/** The displays an Immunization's recorded reactions are named by. */ +export const immunizationReactionDisplays = ( + immunization: unknown, +): readonly string[] => { + const reactions = field(immunization, 'reaction') + if (!Array.isArray(reactions)) return [] + return reactions + .map((reaction) => + codeableConceptDisplay( + typeof reaction === 'object' && reaction !== null ? + (reaction as { readonly detail?: unknown }).detail + : undefined, + ), + ) + .filter((display): display is string => display !== undefined) +} diff --git a/packages/fhir/src/extract/index.ts b/packages/fhir/src/extract/index.ts new file mode 100644 index 00000000..10836fa2 --- /dev/null +++ b/packages/fhir/src/extract/index.ts @@ -0,0 +1,20 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +export * from './careplan.js' +export * from './coding.js' +export * from './condition.js' +export * from './encode.js' +export * from './identifier.js' +export * from './immunization.js' +export * from './observation.js' +export * from './patient.js' +export * from './period.js' +export * from './person.js' +export * from './resource.js' +export * from './scheduling.js' diff --git a/packages/fhir/src/extract/internal.ts b/packages/fhir/src/extract/internal.ts new file mode 100644 index 00000000..dc5412a7 --- /dev/null +++ b/packages/fhir/src/extract/internal.ts @@ -0,0 +1,28 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { fhirDateTimeToDate } from '../core/primitives.js' + +/** One field of a resource the caller has not yet narrowed, or undefined when it has none. */ +export const field = (resource: unknown, name: string): unknown => + typeof resource === 'object' && resource !== null ? + (resource as Record)[name] + : undefined + +/** + * A FHIR date, dateTime, or instant as a Date, dropping a value that does not parse. + * + * Accessors report absence and malformation the same way, as undefined: a caller reading a field + * wants the value or nothing, and an `Invalid Date` would compare false against everything while + * still looking like a Date. + */ +export const asDate = (value: unknown): Date | undefined => { + if (value === undefined) return undefined + const result = fhirDateTimeToDate(value) + return result.ok ? result.value : undefined +} diff --git a/packages/fhir/src/extract/observation.ts b/packages/fhir/src/extract/observation.ts new file mode 100644 index 00000000..e7d18f64 --- /dev/null +++ b/packages/fhir/src/extract/observation.ts @@ -0,0 +1,127 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { codeableConceptDisplay, containsCoding } from './coding.js' +import { asDate, field } from './internal.js' +import { fhirPeriodToBounds, periodOverlaps } from './period.js' +import { annotationTexts } from './resource.js' +import { fhirQuantityToValue, type QuantityValue } from '../core/quantity.js' + +/** + * The instant an Observation applies to. + * + * `effectiveDateTime` when the Observation states one, and the start of `effectivePeriod` + * otherwise: a period's start is when the observation began to apply, which is the instant a + * caller orders by. An Observation stating neither has no effective time to report. + */ +export const observationEffectiveDate = ( + observation: unknown, +): Date | undefined => + asDate(field(observation, 'effectiveDateTime')) ?? + fhirPeriodToBounds(field(observation, 'effectivePeriod')).start + +/** The end of an Observation's effective period, stated only when it names one. */ +export const observationEffectiveEnd = ( + observation: unknown, +): Date | undefined => + fhirPeriodToBounds(field(observation, 'effectivePeriod')).end ?? + asDate(field(observation, 'effectiveDateTime')) + +/** Whether an Observation's effective time falls inside the range, inclusive at both ends. */ +export const observationEffectiveOverlaps = ( + observation: unknown, + rangeStart: Date, + rangeEnd: Date, +): boolean => { + const period = field(observation, 'effectivePeriod') + if (period !== undefined) return periodOverlaps(period, rangeStart, rangeEnd) + const instant = asDate(field(observation, 'effectiveDateTime')) + if (instant === undefined) return false + return instant >= rangeStart && instant <= rangeEnd +} + +/** The instant an Observation was released, as `issued` states it. */ +export const observationIssuedDate = (observation: unknown): Date | undefined => + asDate(field(observation, 'issued')) + +/** The display an Observation's code is best named by. */ +export const observationCodeDisplay = ( + observation: unknown, +): string | undefined => codeableConceptDisplay(field(observation, 'code')) + +/** + * An Observation's value as a measurement. + * + * Only `valueQuantity` reduces to a number with a unit; the other value types are separate + * accessors, because returning a bare number for a value that carried a unit would drop it. + */ +export const observationQuantity = ( + observation: unknown, +): QuantityValue | undefined => { + const value = field(observation, 'valueQuantity') + if (value === undefined) return undefined + const result = fhirQuantityToValue(value) + return result.ok ? result.value : undefined +} + +/** An Observation's numeric value, dropping the unit its Quantity carried. */ +export const observationNumericValue = ( + observation: unknown, +): number | undefined => observationQuantity(observation)?.value + +/** The unit an Observation's Quantity states, preferring the comparable code. */ +export const observationUnit = (observation: unknown): string | undefined => + observationQuantity(observation)?.unit + +/** An Observation's `valueString`. */ +export const observationStringValue = ( + observation: unknown, +): string | undefined => { + const value = field(observation, 'valueString') + return typeof value === 'string' ? value : undefined +} + +/** An Observation's `valueBoolean`. */ +export const observationBooleanValue = ( + observation: unknown, +): boolean | undefined => { + const value = field(observation, 'valueBoolean') + return typeof value === 'boolean' ? value : undefined +} + +/** The display an Observation's coded value is best named by. */ +export const observationValueDisplay = ( + observation: unknown, +): string | undefined => + codeableConceptDisplay(field(observation, 'valueCodeableConcept')) + +/** Whether an Observation states this category code, in the given system. */ +export const observationHasCategory = ( + observation: unknown, + system: string, + code: string, +): boolean => { + const categories = field(observation, 'category') + if (!Array.isArray(categories)) return false + return categories.some((category) => containsCoding(category, system, code)) +} + +/** The displays an Observation's interpretations are named by. */ +export const observationInterpretationDisplays = ( + observation: unknown, +): readonly string[] => { + const value = field(observation, 'interpretation') + if (!Array.isArray(value)) return [] + return value + .map((entry) => codeableConceptDisplay(entry)) + .filter((display): display is string => display !== undefined) +} + +/** The texts an Observation's notes carry. */ +export const observationNoteTexts = (observation: unknown): readonly string[] => + annotationTexts(field(observation, 'note')) diff --git a/packages/fhir/src/extract/patient.ts b/packages/fhir/src/extract/patient.ts new file mode 100644 index 00000000..c634014b --- /dev/null +++ b/packages/fhir/src/extract/patient.ts @@ -0,0 +1,87 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { codeableConceptDisplay } from './coding.js' +import { asDate, field } from './internal.js' +import { contactPointsBySystem, formatHumanName } from './person.js' + +/** A Patient's birth date, as the UTC start of the day it names. */ +export const patientBirthDate = (patient: unknown): Date | undefined => + asDate(field(patient, 'birthDate')) + +/** The instant a Patient is recorded as having died, when stated as a dateTime. */ +export const patientDeceasedDate = (patient: unknown): Date | undefined => + asDate(field(patient, 'deceasedDateTime')) + +/** + * Whether a Patient is recorded as deceased. + * + * A stated `deceasedDateTime` asserts it as surely as `deceasedBoolean` does; a resource that + * states neither is not an assertion that the patient is alive, so this reports false only in + * the sense of "not recorded as deceased". + */ +export const patientIsDeceased = (patient: unknown): boolean => { + const flag = field(patient, 'deceasedBoolean') + if (typeof flag === 'boolean') return flag + return field(patient, 'deceasedDateTime') !== undefined +} + +/** + * A Patient's age in whole years at an instant. + * + * Computed entirely in UTC. A FHIR `date` names a calendar day with no offset, and comparing it + * against the host's local calendar moves the birthday by a day for anyone west of UTC — which + * is a wrong age for one day each year. + * + * A patient recorded as deceased is aged to their death rather than to `asOf`, because a person + * does not keep having birthdays afterwards. + */ +export const patientAgeInYears = ( + patient: unknown, + asOf: Date = new Date(), +): number | undefined => { + const birth = patientBirthDate(patient) + if (birth === undefined) return undefined + const died = patientDeceasedDate(patient) + const at = died !== undefined && died < asOf ? died : asOf + if (at < birth) return undefined + let age = at.getUTCFullYear() - birth.getUTCFullYear() + const months = at.getUTCMonth() - birth.getUTCMonth() + if (months < 0 || (months === 0 && at.getUTCDate() < birth.getUTCDate())) { + age -= 1 + } + return age +} + +/** A Patient's name as one display string, preferring the first name stated. */ +export const patientName = ( + patient: unknown, + options: { + readonly includePrefix?: boolean + readonly includeSuffix?: boolean + } = {}, +): string | undefined => { + const names = field(patient, 'name') + if (!Array.isArray(names)) return undefined + return names + .map((name) => formatHumanName(name, options)) + .find((name): name is string => name !== undefined) +} + +/** Every phone number a Patient states. */ +export const patientPhoneNumbers = (patient: unknown): readonly string[] => + contactPointsBySystem(patient, 'phone') + +/** Every email address a Patient states. */ +export const patientEmailAddresses = (patient: unknown): readonly string[] => + contactPointsBySystem(patient, 'email') + +/** The display a Patient's marital status is named by. */ +export const patientMaritalStatusDisplay = ( + patient: unknown, +): string | undefined => codeableConceptDisplay(field(patient, 'maritalStatus')) diff --git a/packages/fhir/src/extract/period.ts b/packages/fhir/src/extract/period.ts new file mode 100644 index 00000000..2587aaa8 --- /dev/null +++ b/packages/fhir/src/extract/period.ts @@ -0,0 +1,93 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { fhirDateTimeToDate } from '../core/primitives.js' + +/** A Period as the two instants it names, each absent when the Period leaves that end open. */ +export interface PeriodBounds { + readonly start?: Date + readonly end?: Date +} + +const bound = (value: unknown): Date | undefined => { + if (typeof value !== 'string') return undefined + const result = fhirDateTimeToDate(value) + return result.ok ? result.value : undefined +} + +/** + * A FHIR Period as JavaScript instants. + * + * An end the Period does not state is left undefined rather than defaulted to now: an open period + * and one ending at this moment are different facts, and only the caller knows which it wants. + * A bound that does not parse is dropped, so a malformed end never silently truncates the period. + */ +export const fhirPeriodToBounds = (period: unknown): PeriodBounds => { + if (typeof period !== 'object' || period === null) return {} + const value = period as { readonly start?: unknown; readonly end?: unknown } + const start = bound(value.start) + const end = bound(value.end) + return { + ...(start === undefined ? {} : { start }), + ...(end === undefined ? {} : { end }), + } +} + +/** + * Whether the period covers this instant. + * + * An absent Period is active: FHIR uses a missing period to mean "not bounded", not "never". + * Both ends are inclusive, matching the way FHIR reads a Period elsewhere. + */ +export const periodIsActive = ( + period: unknown, + asOf: Date = new Date(), +): boolean => { + if (period === undefined || period === null) return true + const { start, end } = fhirPeriodToBounds(period) + if (start !== undefined && asOf < start) return false + if (end !== undefined && asOf > end) return false + return true +} + +/** + * Whether the period shares any instant with the range. + * + * An absent Period overlaps nothing: unlike the active case there is no instant to compare, and + * reporting an overlap would invent one. + */ +export const periodOverlaps = ( + period: unknown, + rangeStart: Date, + rangeEnd: Date, +): boolean => { + if (period === undefined || period === null) return false + const { start, end } = fhirPeriodToBounds(period) + const from = start ?? new Date(-8_640_000_000_000_000) + const to = end ?? new Date(8_640_000_000_000_000) + return from <= rangeEnd && to >= rangeStart +} + +/** The milliseconds a period spans, stated only when it names both ends. */ +export const periodDuration = (period: unknown): number | undefined => { + const { start, end } = fhirPeriodToBounds(period) + if (start === undefined || end === undefined) return undefined + return end.getTime() - start.getTime() +} + +/** + * A Date as the FHIR `date` it names, in UTC. + * + * UTC, not the host's zone: formatting in local time silently shifts the calendar day either + * side of midnight, which is how a birth date moves by one. + */ +export const dateToFhirDate = (value: Date): string => + value.toISOString().slice(0, 10) + +/** A Date as the FHIR `instant` it names, in UTC and to millisecond precision. */ +export const dateToFhirInstant = (value: Date): string => value.toISOString() diff --git a/packages/fhir/src/extract/person.ts b/packages/fhir/src/extract/person.ts new file mode 100644 index 00000000..eb3b18ba --- /dev/null +++ b/packages/fhir/src/extract/person.ts @@ -0,0 +1,100 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/** Which parts of a HumanName to include; given and family are always included. */ +export interface HumanNameOptions { + readonly includePrefix?: boolean + readonly includeSuffix?: boolean +} + +const strings = (value: unknown): readonly string[] => + Array.isArray(value) ? + value.filter( + (entry): entry is string => typeof entry === 'string' && entry.length > 0, + ) + : [] + +/** + * A HumanName as its ordered parts. + * + * `text` wins outright when the name states it: it is the name as its owner writes it, and + * reassembling the parts instead reorders names whose culture puts the family name first. + */ +export const humanNameParts = ( + name: unknown, + options: HumanNameOptions = {}, +): readonly string[] => { + if (typeof name !== 'object' || name === null) return [] + const value = name as { + readonly text?: unknown + readonly prefix?: unknown + readonly given?: unknown + readonly family?: unknown + readonly suffix?: unknown + } + if (typeof value.text === 'string' && value.text.length > 0) { + return [value.text] + } + const parts: string[] = [] + if (options.includePrefix === true) parts.push(...strings(value.prefix)) + parts.push(...strings(value.given)) + if (typeof value.family === 'string' && value.family.length > 0) { + parts.push(value.family) + } + if (options.includeSuffix === true) parts.push(...strings(value.suffix)) + return parts +} + +/** A HumanName as one display string, or undefined when it names nothing. */ +export const formatHumanName = ( + name: unknown, + options: HumanNameOptions = {}, +): string | undefined => { + const parts = humanNameParts(name, options) + return parts.length > 0 ? parts.join(' ') : undefined +} + +const telecom = ( + resource: unknown, +): ReadonlyArray<{ + readonly system?: string + readonly value?: string + readonly use?: string +}> => { + if (typeof resource !== 'object' || resource === null) return [] + const value = (resource as { readonly telecom?: unknown }).telecom + if (!Array.isArray(value)) return [] + return value.filter( + (entry): entry is { readonly system?: string; readonly value?: string } => + typeof entry === 'object' && entry !== null, + ) +} + +/** Every contact value the resource states for one system, such as `phone` or `email`. */ +export const contactPointsBySystem = ( + resource: unknown, + system: string, +): readonly string[] => + telecom(resource) + .filter((point) => point.system === system) + .map((point) => point.value) + .filter((value): value is string => typeof value === 'string') + +/** The first contact value for one system, optionally narrowed to a use such as `home`. */ +export const contactPointBySystem = ( + resource: unknown, + system: string, + use?: string, +): string | undefined => + telecom(resource) + .filter( + (point) => + point.system === system && (use === undefined || point.use === use), + ) + .map((point) => point.value) + .find((value): value is string => typeof value === 'string') diff --git a/packages/fhir/src/extract/resource.ts b/packages/fhir/src/extract/resource.ts new file mode 100644 index 00000000..2d8b6f3b --- /dev/null +++ b/packages/fhir/src/extract/resource.ts @@ -0,0 +1,138 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { err, ok, type Result } from '../core/result.js' + +const asObject = (value: unknown): Record | undefined => + typeof value === 'object' && value !== null ? + (value as Record) + : undefined + +/** Every extension the resource or element carries under one URL. */ +export const extensionsByUrl = ( + value: unknown, + url: string, +): readonly unknown[] => { + const object = asObject(value) + const extensions = object?.extension + if (!Array.isArray(extensions)) return [] + return extensions.filter((entry) => asObject(entry)?.url === url) +} + +/** The single extension carried under one URL, or undefined when none is. */ +export const extensionByUrl = (value: unknown, url: string): unknown => + extensionsByUrl(value, url)[0] + +/** Every text a list of Annotations carries, in the order stated. */ +export const annotationTexts = (value: unknown): readonly string[] => { + if (!Array.isArray(value)) return [] + return value + .map((entry) => asObject(entry)?.text) + .filter((text): text is string => typeof text === 'string') +} + +/** The contained resources of one type, in the order the resource states them. */ +export const containedResourcesByType = ( + resource: unknown, + resourceType: string, +): readonly unknown[] => { + const contained = asObject(resource)?.contained + if (!Array.isArray(contained)) return [] + return contained.filter( + (entry) => asObject(entry)?.resourceType === resourceType, + ) +} + +/** + * The contained resource a local reference names. + * + * A local reference is `#id`; anything else points outside the resource and cannot be resolved + * from it, so it resolves to undefined rather than to a same-id resource that merely collides. + */ +export const containedResource = ( + resource: unknown, + reference: string, +): unknown => { + if (!reference.startsWith('#') || reference.length < 2) return undefined + const contained = asObject(resource)?.contained + if (!Array.isArray(contained)) return undefined + const id = reference.slice(1) + return contained.find((entry) => asObject(entry)?.id === id) +} + +/** The type a Reference points at, from `type` when stated and from the literal otherwise. */ +export const referenceType = (value: unknown): string | undefined => { + const object = asObject(value) + if (object === undefined) return undefined + if (typeof object.type === 'string' && object.type.length > 0) { + return object.type + } + const literal = object.reference + if (typeof literal !== 'string') return undefined + // `Patient/123`, or the same with a base URL and possibly a version, so read from the right. + const parts = literal.split('/_history/')[0]?.split('/') ?? [] + const type = parts[parts.length - 2] + return type !== undefined && /^[A-Z][A-Za-z]+$/u.test(type) ? type : undefined +} + +/** Whether a Reference points at a resource of this type. */ +export const isReferenceToType = ( + value: unknown, + resourceType: string, +): boolean => referenceType(value) === resourceType + +/** + * The relative literal a resource is referenced by, as `Type/id`. + * + * Reported rather than returned empty: a resource with no id cannot be referenced at all, and a + * caller that builds `Type/undefined` from it produces a link that resolves to nothing. + */ +export const toReference = (resource: unknown): Result => { + const object = asObject(resource) + const resourceType = object?.resourceType + const id = object?.id + if (typeof resourceType !== 'string' || resourceType.length === 0) { + return err('missing-required', 'A reference needs the resource type.', [ + 'resourceType', + ]) + } + if (typeof id !== 'string' || id.length === 0) { + return err( + 'missing-required', + 'A resource without an id cannot be referenced.', + ['id'], + ) + } + return ok(`${resourceType}/${id}`) +} + +/** + * A FHIR base64Binary as its bytes. + * + * Whitespace is legal inside the lexical form and is stripped before decoding, so a value + * wrapped across lines decodes rather than failing. + */ +export const decodeBase64Binary = (value: unknown): Result => { + if (typeof value !== 'string') { + return err('invalid-type', 'Expected a FHIR base64Binary.') + } + const packed = value.replaceAll(/[ \t\n\r]/gu, '') + if (packed.length % 4 !== 0 || /[^0-9A-Za-z+/=]/u.test(packed)) { + return err('schema-invalid', 'Expected a FHIR base64Binary.') + } + try { + const binary = atob(packed) + const bytes = new Uint8Array(binary.length) + for (let index = 0; index < binary.length; index += 1) { + bytes[index] = binary.charCodeAt(index) + } + return ok(bytes) + } catch { + return err('schema-invalid', 'Expected a FHIR base64Binary.') + } +} diff --git a/packages/fhir/src/extract/scheduling.ts b/packages/fhir/src/extract/scheduling.ts new file mode 100644 index 00000000..dffdc825 --- /dev/null +++ b/packages/fhir/src/extract/scheduling.ts @@ -0,0 +1,85 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { codeableConceptDisplay } from './coding.js' +import { asDate, field } from './internal.js' + +/** + * The start of an Appointment or a Slot. + * + * Both state `start` and `end` as instants directly rather than as a Period, so one pair of + * accessors serves them and anything else shaped the same way. + */ +export const scheduledStart = (resource: unknown): Date | undefined => + asDate(field(resource, 'start')) + +/** The end of an Appointment or a Slot. */ +export const scheduledEnd = (resource: unknown): Date | undefined => + asDate(field(resource, 'end')) + +/** The milliseconds a scheduled slot spans, stated only when it names both ends. */ +export const scheduledDuration = (resource: unknown): number | undefined => { + const start = scheduledStart(resource) + const end = scheduledEnd(resource) + if (start === undefined || end === undefined) return undefined + return end.getTime() - start.getTime() +} + +/** + * Whether a scheduled slot has already ended. + * + * The end decides it, not the start: an appointment under way has begun but is not past. One + * stating no end is judged by its start, and one stating neither cannot be placed in time at all. + */ +export const scheduledIsPast = ( + resource: unknown, + asOf: Date = new Date(), +): boolean | undefined => { + const at = scheduledEnd(resource) ?? scheduledStart(resource) + return at === undefined ? undefined : at < asOf +} + +/** Whether a scheduled slot has not started yet. */ +export const scheduledIsUpcoming = ( + resource: unknown, + asOf: Date = new Date(), +): boolean | undefined => { + const start = scheduledStart(resource) + return start === undefined ? undefined : start > asOf +} + +/** Whether a scheduled slot covers an instant, inclusive at both ends. */ +export const scheduledCovers = ( + resource: unknown, + asOf: Date = new Date(), +): boolean => { + const start = scheduledStart(resource) + const end = scheduledEnd(resource) + if (start === undefined || end === undefined) return false + return asOf >= start && asOf <= end +} + +/** The displays a Slot's specialties are named by. */ +export const slotSpecialtyDisplays = (slot: unknown): readonly string[] => { + const specialties = field(slot, 'specialty') + if (!Array.isArray(specialties)) return [] + return specialties + .map((specialty) => codeableConceptDisplay(specialty)) + .filter((display): display is string => display !== undefined) +} + +/** The display an Appointment's service type is named by. */ +export const appointmentServiceTypeDisplay = ( + appointment: unknown, +): string | undefined => { + const types = field(appointment, 'serviceType') + if (!Array.isArray(types)) return undefined + return types + .map((type) => codeableConceptDisplay(type)) + .find((display): display is string => display !== undefined) +} diff --git a/packages/fhir/src/index.ts b/packages/fhir/src/index.ts index 14564e0e..597b4f16 100644 --- a/packages/fhir/src/index.ts +++ b/packages/fhir/src/index.ts @@ -6,148 +6,107 @@ // SPDX-License-Identifier: MIT // -export * from './elements/index.js' -export * from './valueSets/index.js' -export * from './resources/account.js' -export * from './resources/activityDefinition.js' -export * from './resources/administrableProductDefinition.js' -export * from './resources/adverseEvent.js' -export * from './resources/allergyIntolerance.js' -export * from './resources/appointment.js' -export * from './resources/appointmentResponse.js' -export * from './resources/auditEvent.js' -export * from './resources/basic.js' -export * from './resources/binary.js' -export * from './resources/biologicallyDerivedProduct.js' -export * from './resources/bodyStructure.js' -export * from './resources/bundle.js' -export * from './resources/capabilityStatement.js' -export * from './resources/carePlan.js' -export * from './resources/careTeam.js' -export * from './resources/catalogEntry.js' -export * from './resources/chargeItem.js' -export * from './resources/chargeItemDefinition.js' -export * from './resources/citation.js' -export * from './resources/claim.js' -export * from './resources/claimResponse.js' -export * from './resources/clinicalImpression.js' -export * from './resources/clinicalUseDefinition.js' -export * from './resources/codeSystem.js' -export * from './resources/communication.js' -export * from './resources/communicationRequest.js' -export * from './resources/compartmentDefinition.js' -export * from './resources/composition.js' -export * from './resources/conceptMap.js' -export * from './resources/condition.js' -export * from './resources/consent.js' -export * from './resources/contract.js' -export * from './resources/coverage.js' -export * from './resources/coverageEligibilityRequest.js' -export * from './resources/coverageEligibilityResponse.js' -export * from './resources/detectedIssue.js' -export * from './resources/device.js' -export * from './resources/deviceDefinition.js' -export * from './resources/deviceMetric.js' -export * from './resources/deviceRequest.js' -export * from './resources/deviceUseStatement.js' -export * from './resources/diagnosticReport.js' -export * from './resources/documentManifest.js' -export * from './resources/documentReference.js' -export * from './resources/encounter.js' -export * from './resources/endpoint.js' -export * from './resources/enrollmentRequest.js' -export * from './resources/enrollmentResponse.js' -export * from './resources/episodeOfCare.js' -export * from './resources/eventDefinition.js' -export * from './resources/evidence.js' -export * from './resources/evidenceReport.js' -export * from './resources/evidenceVariable.js' -export * from './resources/exampleScenario.js' -export * from './resources/explanationOfBenefit.js' -export * from './resources/familyMemberHistory.js' -export * from './resources/fhirDomainResource.js' -export * from './resources/fhirResource.js' -export * from './resources/flag.js' -export * from './resources/goal.js' -export * from './resources/graphDefinition.js' -export * from './resources/group.js' -export * from './resources/guidanceResponse.js' -export * from './resources/healthcareService.js' -export * from './resources/imagingStudy.js' -export * from './resources/implementationGuide.js' -export * from './resources/immunization.js' -export * from './resources/immunizationEvaluation.js' -export * from './resources/immunizationRecommendation.js' -export * from './resources/ingredient.js' -export * from './resources/insurancePlan.js' -export * from './resources/invoice.js' -export * from './resources/library.js' -export * from './resources/linkage.js' -export * from './resources/list.js' -export * from './resources/location.js' -export * from './resources/manufacturedItemDefinition.js' -export * from './resources/measure.js' -export * from './resources/measureReport.js' -export * from './resources/medication.js' -export * from './resources/medicationAdministration.js' -export * from './resources/medicationDispense.js' -export * from './resources/medicationKnowledge.js' -export * from './resources/medicationRequest.js' -export * from './resources/medicationStatement.js' -export * from './resources/media.js' -export * from './resources/medicinalProductDefinition.js' -export * from './resources/messageDefinition.js' -export * from './resources/messageHeader.js' -export * from './resources/molecularSequence.js' -export * from './resources/namingSystem.js' -export * from './resources/nutritionOrder.js' -export * from './resources/nutritionProduct.js' -export * from './resources/observation.js' -export * from './resources/observationDefinition.js' -export * from './resources/operationDefinition.js' -export * from './resources/operationOutcome.js' -export * from './resources/organization.js' -export * from './resources/organizationAffiliation.js' -export * from './resources/packagedProductDefinition.js' -export * from './resources/parameters.js' -export * from './resources/patient.js' -export * from './resources/paymentNotice.js' -export * from './resources/paymentReconciliation.js' -export * from './resources/person.js' -export * from './resources/planDefinition.js' -export * from './resources/practitioner.js' -export * from './resources/practitionerRole.js' -export * from './resources/procedure.js' -export * from './resources/provenance.js' -export * from './resources/questionnaire.js' -export * from './resources/questionnaireResponse.js' -export * from './resources/regulatedAuthorization.js' -export * from './resources/relatedPerson.js' -export * from './resources/requestGroup.js' -export * from './resources/researchDefinition.js' -export * from './resources/researchElementDefinition.js' -export * from './resources/researchStudy.js' -export * from './resources/researchSubject.js' -export * from './resources/riskAssessment.js' -export * from './resources/schedule.js' -export * from './resources/searchParameter.js' -export * from './resources/serviceRequest.js' -export * from './resources/slot.js' -export * from './resources/specimen.js' -export * from './resources/specimenDefinition.js' -export * from './resources/structureDefinition.js' -export * from './resources/structureMap.js' -export * from './resources/subscription.js' -export * from './resources/subscriptionStatus.js' -export * from './resources/subscriptionTopic.js' -export * from './resources/substance.js' -export * from './resources/substanceDefinition.js' -export * from './resources/supplyDelivery.js' -export * from './resources/supplyRequest.js' -export * from './resources/task.js' -export * from './resources/terminologyCapabilities.js' -export * from './resources/testReport.js' -export * from './resources/testScript.js' -export * from './resources/valueSet.js' -export * from './resources/verificationResult.js' -export * from './resources/visionPrescription.js' +export { + collectResults, + deepFreeze, + err, + fhirDateTimeToDate, + fhirQuantityToValue, + issues, + mapResult, + ok, + parseAbsoluteUri, + parseCanonical, + parseFhirId, + parseFhirInstant, + parsePatientReference, + parsePositiveInteger, + parseResearchStudyReference, + parseSemVer, + parseUrnUuid, + type AbsoluteUri, + type Canonical, + type FhirId, + type FhirInstant, + type Issue, + type IssueCode, + type IssueSeverity, + type PatientReference, + type PositiveInteger, + type QuantityComparator, + type QuantityValue, + type ResearchStudyReference, + type ReadonlyDeep, + type Result, + type SemVer, + type UrnUuid, +} from './core/index.js' + +export * from './extract/index.js' + +export { + parseCollectionBundle, + parseDevice, + parseDocumentReference, + parseObservation, + parseProvenance, + parseSpecimen, + parseSupportedR4Resource, + type CollectionBundle, + type Device, + type DocumentReference, + type Observation, + type Provenance, + type Specimen, + type SupportedR4Resource, +} from './r4/index.js' + +export { + canonicalizeMobileEffectiveInstant, + entryIdentifierName, + createEntryIdentity, + deriveEntryFullUrl, + groveFhirContractVersion, + groveFhirExchangeIdentity, + groveFhirVersion, + mobileEffectiveCanonicalization, + mobileEffectiveCanonicalizationVectors, + sharedMobileMeasurementCatalog, + type ApplicationDeviceInput, + type BloodPressureMeasurement, + type CompleteIdentifierInput, + type GatewayApplicationInput, + type IdentifiedEntryIdentityInput, + type InstantEffectiveTime, + type InstantQuantityMeasurement, + type InstantQuantityMeasurementKind, + type MobileMeasurement, + type PeriodEffectiveTime, + type PeriodQuantityMeasurement, + type PeriodQuantityMeasurementKind, + type RecordingDeviceInput, + type RecordingMethod, + type ResourceIdentityInput, + type SharedMobileMeasurementKind, + type SleepStage, + type SleepStageMeasurement, + type SleepStageSourceCodingInput, +} from './mobile/index.js' + +export { + buildQuestionnaire, + buildQuestionnaireResponse, + parseQuestionnaire, + parseQuestionnaireResponse, + preflightQuestionnairePair, + type GroveQuestionnaire, + type GroveQuestionnaireResponse, + type QuestionnaireInput, + type QuestionnaireItemInput, + type QuestionnairePair, + type QuestionnairePreflightOptions, + type QuestionnaireResponseAnswerInput, + type QuestionnaireResponseInput, + type QuestionnaireResponseItemInput, + type ResolvedQuestionnaireValueSetInput, +} from './questionnaire/index.js' diff --git a/packages/fhir/src/mobile/identity.ts b/packages/fhir/src/mobile/identity.ts new file mode 100644 index 00000000..4b0e0e9d --- /dev/null +++ b/packages/fhir/src/mobile/identity.ts @@ -0,0 +1,114 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { v5 as uuidV5 } from 'uuid' +import { groveFhirExchangeIdentity } from './measurement-catalog.generated.js' +import type { + CompleteIdentifierInput, + IdentifiedEntryIdentityInput, +} from './types.js' +import { + deepFreeze, + err, + ok, + parseAbsoluteUri, + parseFhirId, + parseUrnUuid, + type FhirId, + type Result, + type UrnUuid, +} from '../core/index.js' + +export const containsIsolatedSurrogate = (value: string): boolean => { + for (let index = 0; index < value.length; index += 1) { + const code = value.charCodeAt(index) + if (code >= 0xd800 && code <= 0xdbff) { + const next = value.charCodeAt(index + 1) + if (!Number.isInteger(next) || next < 0xdc00 || next > 0xdfff) return true + index += 1 + } else if (code >= 0xdc00 && code <= 0xdfff) { + return true + } + } + return false +} + +/** + * The UUID-v5 name for one identifier: the system, a vertical bar, then the value. + * + * Only the system is barred from containing a vertical bar, so the name splits at the first one. + * A value may contain them, because a composed identifier is built from them. + */ +export const entryIdentifierName = ( + input: CompleteIdentifierInput, +): Result => { + if (!parseAbsoluteUri(input.system).ok) { + return err('invalid-uri', 'Identifier.system must be an absolute URI.', [ + 'system', + ]) + } + if (input.value.trim() === '') { + return err('invalid-identifier', 'Identifier.value must not be blank.', [ + 'value', + ]) + } + if ( + containsIsolatedSurrogate(input.system) || + containsIsolatedSurrogate(input.value) + ) { + return err( + 'invalid-identifier', + 'Identifier strings must contain Unicode scalar values only.', + ) + } + if (input.system.includes('|')) { + return err( + 'invalid-identifier', + 'Identifier.system must not contain a vertical bar, so the name splits at the first one.', + ['system'], + ) + } + return ok(`${input.system}|${input.value}`) +} + +/** Derives the IG-mandated lowercase UUID-v5 Bundle entry fullUrl. */ +export const deriveEntryFullUrl = ( + input: CompleteIdentifierInput, +): Result => { + const name = entryIdentifierName(input) + if (!name.ok) return name + + return parseUrnUuid( + `urn:uuid:${uuidV5( + name.value, + groveFhirExchangeIdentity.fullUrlAlgorithm.namespace, + )}`, + ) +} + +/** Pairs a business identifier with its deterministic exchange identity. */ +export const createEntryIdentity = ( + identifier: CompleteIdentifierInput, + id?: FhirId, +): Result => { + if (id !== undefined && !parseFhirId(id).ok) { + return err('invalid-identifier', 'Resource.id is not a valid FHIR id.', [ + 'id', + ]) + } + const fullUrl = deriveEntryFullUrl(identifier) + if (!fullUrl.ok) return fullUrl + + return ok( + deepFreeze({ + fullUrl: fullUrl.value, + identifier: { system: identifier.system, value: identifier.value }, + ...(id === undefined ? {} : { id }), + }) as unknown as IdentifiedEntryIdentityInput, + ) +} diff --git a/packages/fhir/src/mobile/index.ts b/packages/fhir/src/mobile/index.ts new file mode 100644 index 00000000..694fef4d --- /dev/null +++ b/packages/fhir/src/mobile/index.ts @@ -0,0 +1,48 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +export { + entryIdentifierName, + createEntryIdentity, + deriveEntryFullUrl, +} from './identity.js' +export { + groveFhirExchangeIdentity, + groveFhirContractVersion, + groveFhirVersion, + groveMobilePackageMetadata, + mobileEffectiveCanonicalization, + mobileEffectiveCanonicalizationVectors, + sharedMobileMeasurementCatalog, + type SharedMobileMeasurementKind, +} from './measurement-catalog.generated.js' +export { canonicalizeMobileEffectiveInstant } from './time.js' +export type { + ApplicationDeviceInput, + BloodPressureMeasurement, + CompleteIdentifierInput, + GatewayApplicationInput, + IdentifiedEntryIdentityInput, + InstantCodedMeasurement, + InstantCodedMeasurementKind, + InstantEffectiveTime, + InstantQuantityMeasurement, + InstantQuantityMeasurementKind, + MobileMeasurement, + PeriodCodedMeasurement, + PeriodCodedMeasurementKind, + PeriodEffectiveTime, + PeriodQuantityMeasurement, + PeriodQuantityMeasurementKind, + RecordingDeviceInput, + RecordingMethod, + ResourceIdentityInput, + SleepStage, + SleepStageMeasurement, + SleepStageSourceCodingInput, +} from './types.js' diff --git a/packages/fhir/src/mobile/measurement-catalog.generated.ts b/packages/fhir/src/mobile/measurement-catalog.generated.ts new file mode 100644 index 00000000..7a6955aa --- /dev/null +++ b/packages/fhir/src/mobile/measurement-catalog.generated.ts @@ -0,0 +1,2927 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// +// Generated by scripts/generate-measurement-catalog.mjs. Do not edit directly. + +/* eslint-disable sonarjs/no-clear-text-protocols */ + +import { deepFreeze } from '../core/index.js' + +export const groveFhirContractVersion = '0.5.0' as const + +export const groveFhirVersion = '4.0.1' as const + +const groveMobilePackageMetadataValue = { + fhirVersion: '4.0.1', + version: '0.5.0', + packageId: 'org.grovealliance.fhir.mobile', + canonical: 'https://grovealliance.org/fhir/mobile', + dependencies: ['hl7.terminology.r4#7.3.0', 'hl7.fhir.uv.extensions.r4#5.3.0'], +} as const + +export const groveMobilePackageMetadata: typeof groveMobilePackageMetadataValue = + deepFreeze(groveMobilePackageMetadataValue) + +const groveFhirExchangeIdentityValue = { + schemaVersion: 1, + version: '0.5.0', + fhirVersion: '4.0.1', + profile: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle', + entryIdentifierExtension: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier', + fullUrlAlgorithm: { + name: 'uuid-v5-composed-identifier-v1', + namespace: 'a9a39cf1-c944-5d15-a3c2-c395969ea101', + input: + 'UTF-8 bytes of the system, one U+007C VERTICAL LINE, then the value', + rejects: + 'The system must not contain a vertical bar, so the input splits unambiguously at the first one. The value may contain vertical bars, because a composed identifier is built from them. An isolated UTF-16 surrogate in either is rejected. Nothing is escaped: an escaping rule is the conformance surface this scheme exists to remove.', + output: 'urn:uuid:', + whyADigestHere: + 'Version 5 turns an identifier into the URN a Bundle fullUrl must be. It is a format conversion, not concealment: the identifier it is built from travels in the clear on the same resource.', + }, + resourceId: { + role: 'repository logical id', + required: false, + sourceIdentityAllowed: false, + }, + internalReferences: { + containedReferencesAllowed: true, + bundleEntryReferences: 'must use an entry fullUrl UUID URN', + unresolvedUuidUrnsAllowed: false, + }, + recordingDeviceIdentity: { + purpose: + "A recording Device deduplicates across every Observation it produced, so one participant's recorder is stored once instead of once per sample.", + subjectParticipates: + 'A wearable belongs to a person, so the subject partitions the key: two participants wearing the same model are two devices, which is what Device means in R4. This is the one identity in the family that admits a subject reference; every other identity derives from source data alone and must not.', + versionsExcluded: + "Firmware and software change over a recorder's life and are therefore not part of its identity. They are not carried on the deduplicated Device either, because a backfill converts samples out of order and would leave an arbitrary value behind. Each Observation states the versions in force when it was recorded.", + failClosed: + 'The key requires a non-empty manufacturer and at least one of model or hardwareVersion. A source that states less has no admitted device identity; the producer emits its per-sample derived identity instead of asserting a shared device.', + prohibitedInputs: [ + 'vendor per-unit identifiers and serial numbers, including a digest of one', + 'firmware, software, Resource.id, repository ids, and processing timestamps', + ], + lexicalRules: { + absentField: + 'An absent source field is the empty string; it is never omitted from the composition.', + normalization: + 'Source strings are used exactly as the platform states them; no trimming, case folding, or Unicode normalization is applied.', + noSeparatorInComponents: + 'No component may contain U+007C VERTICAL LINE. A source value carrying one is rejected rather than escaped.', + }, + vectors: [ + { + case: 'apple-watch', + components: [ + 'Patient/1a2b3c', + 'healthkit', + 'Apple Inc.', + 'Watch', + 'Watch7,12', + ], + value: 'v1:Patient/1a2b3c|healthkit|Apple Inc.|Watch|Watch7,12', + }, + { + case: 'no-hardware-version', + components: [ + 'Patient/1a2b3c', + 'health-connect', + 'Google', + 'Pixel Watch', + '', + ], + value: 'v1:Patient/1a2b3c|health-connect|Google|Pixel Watch|', + }, + { + case: 'different-participant-same-model', + components: [ + 'Patient/9z8y7x', + 'healthkit', + 'Apple Inc.', + 'Watch', + 'Watch7,12', + ], + value: 'v1:Patient/9z8y7x|healthkit|Apple Inc.|Watch|Watch7,12', + }, + ], + composition: [ + 'subjectReference', + 'adapterId', + 'manufacturer', + 'model', + 'hardwareVersion', + ], + form: 'v1:||||', + fixedArity: + 'Exactly five components, so an absent source field is an empty component rather than an omitted one. The arity is fixed, so an empty component is unambiguous.', + }, + vectors: [ + { + case: 'ascii', + system: 'https://study.example.org/fhir/identifiers/mobile-observation', + value: 'heart-rate-20260820-001', + input: + 'https://study.example.org/fhir/identifiers/mobile-observation|heart-rate-20260820-001', + fullUrl: 'urn:uuid:72a64652-0bad-517d-8a36-39e3b6adccac', + }, + { + case: 'non-ascii', + system: 'https://例.example/識別子', + value: 'café-東京', + input: 'https://例.example/識別子|café-東京', + fullUrl: 'urn:uuid:31acad95-5e9a-5b0f-b5b7-4f4627825b6b', + }, + { + case: 'composed-value', + system: + 'https://grovealliance.org/fhir/mobile/NamingSystem/grove-writer-record-id', + value: 'v1:com.withings.wiscale2|17348211', + input: + 'https://grovealliance.org/fhir/mobile/NamingSystem/grove-writer-record-id|v1:com.withings.wiscale2|17348211', + fullUrl: 'urn:uuid:68db0fd4-0146-59c9-86cf-934f00881095', + }, + { + case: 'rejected-separator-in-system', + system: 'https://example.org/a|b', + value: 'x', + input: null, + fullUrl: null, + note: 'A vertical bar in the system would make the split ambiguous, so it is rejected. A bar in the value is ordinary, as the composed-value case shows.', + }, + ], +} as const + +export const groveFhirExchangeIdentity: typeof groveFhirExchangeIdentityValue = + deepFreeze(groveFhirExchangeIdentityValue) + +const sharedMobileMeasurementCatalogValue = { + 'active-energy': { + id: 'active-energy', + profile: 'grove-mobile-active-energy', + standardProfile: null, + title: 'Grove Mobile Active Energy', + description: + 'Activity-related energy expenditure, excluding basal energy, during an exact effective Period and normalized to UCUM kilocalories.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'active-energy-burned', + display: 'Active energy burned', + definition: + 'Energy expended through activity, excluding basal energy, during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kcal', + unit: 'kcal', + example: 620, + dimension: { + g: 1, + m: 2, + s: -2, + }, + }, + effective: 'Period', + }, + 'basal-body-temperature': { + id: 'basal-body-temperature', + profile: 'grove-mobile-basal-body-temperature', + standardProfile: null, + title: 'Grove Mobile Basal Body Temperature', + description: + 'A source-neutral basal body temperature recorded at physiologic rest and normalized to UCUM degrees Celsius. It is not the general body-temperature vital sign.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'basal-body-temperature', + display: 'Basal body temperature', + definition: + 'Body temperature recorded at physiologic rest for fertility-awareness or cycle-tracking use; it is distinct from a general body-temperature vital sign.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'Cel', + unit: 'Cel', + example: 36.4, + dimension: { + K: 1, + }, + }, + effective: 'dateTime', + }, + 'basal-energy': { + id: 'basal-energy', + profile: 'grove-mobile-basal-energy', + standardProfile: null, + title: 'Basal Energy Burned', + description: + 'Resting (basal) energy expenditure accrued during an exact effective Period, normalized to UCUM kilocalories. It is disjoint from active-energy: the two measurements never overlap in meaning and are summed only by consumers.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'basal-energy-burned', + display: 'Basal energy burned', + definition: + 'Resting energy expended by basal metabolism, excluding activity energy, during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kcal', + unit: 'kcal', + example: 1580, + dimension: { + g: 1, + m: 2, + s: -2, + }, + }, + effective: 'Period', + }, + 'blood-glucose-unspecified-specimen': { + id: 'blood-glucose-unspecified-specimen', + profile: 'grove-mobile-blood-glucose-unspecified-specimen', + standardProfile: null, + title: 'Blood Glucose (Unspecified Specimen)', + description: + 'A glucose mass concentration whose source supplies no specimen evidence, normalized to UCUM mg/dL. The profile REQUIRES the Observation specimen coding to be ABSENT, keeping it disjoint from the four specimen-specific Health Connect glucose profiles that require an exact SNOMED specimen coding.', + code: { + system: 'http://loinc.org', + code: '2339-0', + display: 'Glucose [Mass/volume] in Blood', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg/dL', + unit: 'mg/dL', + example: 95, + dimension: { + g: 1, + m: -3, + }, + }, + effective: 'dateTime', + }, + 'blood-pressure': { + id: 'blood-pressure', + profile: 'grove-mobile-blood-pressure', + standardProfile: 'http://hl7.org/fhir/StructureDefinition/bp', + title: 'Grove Mobile Blood Pressure', + description: + 'A source-neutral mobile blood-pressure panel that is required to conform to the authoritative FHIR R4 Blood Pressure profile. Systolic and diastolic components are normalized to UCUM millimetres of mercury.', + code: { + system: 'http://loinc.org', + code: '85354-9', + }, + valueKind: 'components', + components: [ + { + id: 'systolic', + system: 'http://loinc.org', + code: '8480-6', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mm[Hg]', + unit: 'mmHg', + example: 118, + dimension: { + g: 1, + m: -1, + s: -2, + }, + }, + }, + { + id: 'diastolic', + system: 'http://loinc.org', + code: '8462-4', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mm[Hg]', + unit: 'mmHg', + example: 78, + dimension: { + g: 1, + m: -1, + s: -2, + }, + }, + }, + ], + quantity: null, + effective: 'dateTime', + }, + 'body-fat-percentage': { + id: 'body-fat-percentage', + profile: 'grove-mobile-body-fat-percentage', + standardProfile: null, + title: 'Body Fat Percentage', + description: + 'A source-neutral body fat measurement expressed as a percentage of total body mass and normalized to UCUM percent. The measurement is method-neutral: sources include bioimpedance scales, connected providers, and manual entry, so no measurement method is asserted.', + code: { + system: 'http://loinc.org', + code: '41982-0', + display: 'Percentage of body fat Measured', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: '%', + unit: '%', + example: 22, + dimension: {}, + }, + effective: 'dateTime', + }, + 'body-height': { + id: 'body-height', + profile: 'grove-mobile-body-height', + standardProfile: 'http://hl7.org/fhir/StructureDefinition/bodyheight', + title: 'Grove Mobile Body Height', + description: + 'A source-neutral mobile body height that is required to conform to the authoritative FHIR R4 Body Height profile. Values are normalized to UCUM centimetres.', + code: { + system: 'http://loinc.org', + code: '8302-2', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'cm', + unit: 'cm', + example: 175, + dimension: { + m: 1, + }, + }, + effective: 'dateTime', + }, + 'body-temperature': { + id: 'body-temperature', + profile: 'grove-mobile-body-temperature', + standardProfile: 'http://hl7.org/fhir/StructureDefinition/bodytemp', + title: 'Grove Mobile Body Temperature', + description: + 'A source-neutral mobile body temperature that is required to conform to the authoritative FHIR R4 Body Temperature profile. Values are normalized to UCUM degrees Celsius.', + code: { + system: 'http://loinc.org', + code: '8310-5', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'Cel', + unit: 'Cel', + example: 36.8, + dimension: { + K: 1, + }, + }, + effective: 'dateTime', + }, + 'body-water-mass': { + id: 'body-water-mass', + profile: 'grove-mobile-body-water-mass', + standardProfile: null, + title: 'Body Water Mass', + description: + "A source-neutral total body water mass from body-composition analysis, normalized to UCUM kilograms. Despite Withings labeling its measure 'hydration', this is a body-composition mass and is deliberately distinct from dietary water intake.", + code: { + system: 'http://loinc.org', + code: '101683-1', + display: 'Body water mass', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kg', + unit: 'kg', + example: 42, + dimension: { + g: 1, + }, + }, + effective: 'dateTime', + }, + 'body-weight': { + id: 'body-weight', + profile: 'grove-mobile-body-weight', + standardProfile: 'http://hl7.org/fhir/StructureDefinition/bodyweight', + title: 'Grove Mobile Body Weight', + description: + 'A source-neutral mobile body weight that is required to conform to the authoritative FHIR R4 Body Weight profile. Values are normalized to UCUM kilograms.', + code: { + system: 'http://loinc.org', + code: '29463-7', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kg', + unit: 'kg', + example: 72.5, + dimension: { + g: 1, + }, + }, + effective: 'dateTime', + }, + 'bone-mass': { + id: 'bone-mass', + profile: 'grove-mobile-bone-mass', + standardProfile: null, + title: 'Bone Mass', + description: + 'A source-neutral total bone mass from body-composition analysis, normalized to UCUM kilograms. It is an impedance-estimated mass and is deliberately distinct from DXA bone mineral density, which it never claims.', + code: { + system: 'http://loinc.org', + code: '101685-6', + display: 'Body bone mass', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kg', + unit: 'kg', + example: 3.1, + dimension: { + g: 1, + }, + }, + effective: 'dateTime', + }, + 'cervical-mucus-quality': { + id: 'cervical-mucus-quality', + profile: 'grove-mobile-cervical-mucus-quality', + standardProfile: null, + title: 'Cervical Mucus Quality', + description: + "The observed cervical mucus quality for fertility awareness, with the exact platform value retained as a secondary coding. Health Connect's separate sensation axis rides an optional coded component that HealthKit never emits.", + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'cervical-mucus-quality', + display: 'Cervical mucus quality', + definition: 'The observed cervical mucus quality at the stated instant.', + }, + valueKind: 'codeableConcept', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/mobile/ValueSet/grove-cervical-mucus-quality', + resultCodeSystem: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-cervical-mucus-quality', + allowedValues: [ + 'dry', + 'sticky', + 'creamy', + 'watery', + 'egg-white', + 'unusual', + 'unknown', + ], + resultCodes: [ + { + code: 'dry', + display: 'Dry', + definition: 'No cervical mucus observed.', + }, + { + code: 'sticky', + display: 'Sticky', + definition: 'Sticky cervical mucus.', + }, + { + code: 'creamy', + display: 'Creamy', + definition: 'Creamy cervical mucus.', + }, + { + code: 'watery', + display: 'Watery', + definition: 'Watery cervical mucus.', + }, + { + code: 'egg-white', + display: 'Egg white', + definition: 'Stretchy egg-white cervical mucus.', + }, + { + code: 'unusual', + display: 'Unusual', + definition: 'An unusual observation that fits no listed quality.', + }, + { + code: 'unknown', + display: 'Unknown', + definition: 'The quality was recorded without a usable classification.', + }, + ], + components: [ + { + id: 'sensation', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'cervical-mucus-sensation', + cardinality: '0..1', + valueSet: + 'https://grovealliance.org/fhir/mobile/ValueSet/grove-cervical-mucus-sensation', + resultCodes: [ + { + code: 'light', + display: 'Light', + definition: 'A light sensation.', + }, + { + code: 'medium', + display: 'Medium', + definition: 'A medium sensation.', + }, + { + code: 'heavy', + display: 'Heavy', + definition: 'A heavy sensation.', + }, + ], + title: 'Cervical Mucus Sensation', + display: 'Cervical mucus sensation', + definition: + 'The reported cervical mucus sensation accompanying a quality observation.', + }, + ], + effective: 'dateTime', + }, + 'cycling-cadence': { + id: 'cycling-cadence', + profile: 'grove-mobile-cycling-cadence', + standardProfile: null, + title: 'Cycling Cadence', + description: + 'A source-neutral instantaneous cycling pedaling cadence sample, normalized to UCUM per minute and displayed as crank revolutions per minute. It is distinct from step cadence, whose counted event is a step rather than a crank revolution.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'cycling-cadence', + display: 'Cycling cadence', + definition: + 'The rate of crank revolutions the subject pedals at the exact Observation effective instant.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: 'revolutions/minute', + example: 85, + dimension: { + s: -1, + }, + }, + effective: 'dateTime', + }, + 'deep-sleep-duration': { + id: 'deep-sleep-duration', + profile: 'grove-mobile-deep-sleep-duration', + standardProfile: null, + title: 'Deep Sleep Duration', + description: + 'Total time classified as deep sleep within one sleep session or civil-day summary, normalized to UCUM minutes over the exact session Period. It is a stage-total aggregate without per-stage intervals, distinct from the interval sleep-stage Observation, and is implemented by the phase-2 aggregate design.', + code: { + system: 'http://loinc.org', + code: '93831-6', + display: 'Deep sleep duration', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'min', + unit: 'min', + example: 78, + dimension: { + s: 1, + }, + }, + method: { + code: 'session-total', + display: 'Session total', + }, + effective: 'Period', + }, + 'dietary-biotin': { + id: 'dietary-biotin', + profile: 'grove-mobile-dietary-biotin', + standardProfile: null, + title: 'Grove Mobile Dietary Biotin', + description: + 'Biotin (vitamin B7) consumed during an exact effective Period, normalized to UCUM micrograms. Grove-coded; the LOINC B7 intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-biotin', + display: 'Dietary biotin', + definition: + 'Biotin consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ug', + unit: 'ug', + example: 30, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-caffeine': { + id: 'dietary-caffeine', + profile: 'grove-mobile-dietary-caffeine', + standardProfile: null, + title: 'Grove Mobile Dietary Caffeine', + description: + 'Caffeine consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the LOINC caffeine-intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-caffeine', + display: 'Dietary caffeine', + definition: + 'Caffeine consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 95, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-calcium': { + id: 'dietary-calcium', + profile: 'grove-mobile-dietary-calcium', + standardProfile: null, + title: 'Grove Mobile Dietary Calcium', + description: + 'Calcium consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the Pt-timed LOINC calcium intake concepts are substance-amount and the mass concepts are 24-hour rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-calcium', + display: 'Dietary calcium', + definition: + 'Calcium consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 1000, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-carbohydrates': { + id: 'dietary-carbohydrates', + profile: 'grove-mobile-dietary-carbohydrates', + standardProfile: null, + title: 'Grove Mobile Dietary Carbohydrates', + description: + 'Total carbohydrate consumed during an exact effective Period, normalized to UCUM grams.', + code: { + system: 'http://loinc.org', + code: '9060-5', + display: 'Carbohydrate intake Measured', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'g', + unit: 'g', + example: 275, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-chloride': { + id: 'dietary-chloride', + profile: 'grove-mobile-dietary-chloride', + standardProfile: null, + title: 'Grove Mobile Dietary Chloride', + description: + 'Chloride consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the LOINC chloride intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-chloride', + display: 'Dietary chloride', + definition: + 'Chloride consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 2300, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-cholesterol': { + id: 'dietary-cholesterol', + profile: 'grove-mobile-dietary-cholesterol', + standardProfile: null, + title: 'Grove Mobile Dietary Cholesterol', + description: + 'Cholesterol consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the LOINC cholesterol-intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-cholesterol', + display: 'Dietary cholesterol', + definition: + 'Cholesterol consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 250, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-chromium': { + id: 'dietary-chromium', + profile: 'grove-mobile-dietary-chromium', + standardProfile: null, + title: 'Grove Mobile Dietary Chromium', + description: + 'Chromium consumed during an exact effective Period, normalized to UCUM micrograms. Grove-coded; the LOINC chromium intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-chromium', + display: 'Dietary chromium', + definition: + 'Chromium consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ug', + unit: 'ug', + example: 35, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-copper': { + id: 'dietary-copper', + profile: 'grove-mobile-dietary-copper', + standardProfile: null, + title: 'Grove Mobile Dietary Copper', + description: + 'Copper consumed during an exact effective Period, normalized to UCUM micrograms. Grove-coded; the LOINC copper intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-copper', + display: 'Dietary copper', + definition: + 'Copper consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ug', + unit: 'ug', + example: 900, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-energy': { + id: 'dietary-energy', + profile: 'grove-mobile-dietary-energy', + standardProfile: null, + title: 'Grove Mobile Dietary Energy', + description: + 'Energy consumed from food and drink during an exact effective Period, normalized to UCUM kilocalories. Distinct from the active/basal energy-expenditure family; intake and expenditure never share a code.', + code: { + system: 'http://loinc.org', + code: '9052-2', + display: 'Calorie intake total', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kcal', + unit: 'kcal', + example: 2100, + dimension: { + g: 1, + m: 2, + s: -2, + }, + }, + effective: 'Period', + }, + 'dietary-fat-monounsaturated': { + id: 'dietary-fat-monounsaturated', + profile: 'grove-mobile-dietary-fat-monounsaturated', + standardProfile: null, + title: 'Grove Mobile Dietary Monounsaturated Fat', + description: + 'Monounsaturated fat consumed during an exact effective Period, normalized to UCUM grams. Grove-coded; only 24-hour LOINC concepts exist.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-fat-monounsaturated', + display: 'Dietary monounsaturated fat', + definition: + 'Monounsaturated fat consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'g', + unit: 'g', + example: 35, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-fat-polyunsaturated': { + id: 'dietary-fat-polyunsaturated', + profile: 'grove-mobile-dietary-fat-polyunsaturated', + standardProfile: null, + title: 'Grove Mobile Dietary Polyunsaturated Fat', + description: + 'Polyunsaturated fat consumed during an exact effective Period, normalized to UCUM grams. Grove-coded; only 24-hour LOINC concepts exist.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-fat-polyunsaturated', + display: 'Dietary polyunsaturated fat', + definition: + 'Polyunsaturated fat consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'g', + unit: 'g', + example: 22, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-fat-saturated': { + id: 'dietary-fat-saturated', + profile: 'grove-mobile-dietary-fat-saturated', + standardProfile: null, + title: 'Grove Mobile Dietary Saturated Fat', + description: + 'Saturated fat consumed during an exact effective Period, normalized to UCUM grams. Grove-coded; both LOINC saturated-fat intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-fat-saturated', + display: 'Dietary saturated fat', + definition: + 'Saturated fat consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'g', + unit: 'g', + example: 20, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-fat-total': { + id: 'dietary-fat-total', + profile: 'grove-mobile-dietary-fat-total', + standardProfile: null, + title: 'Grove Mobile Dietary Fat', + description: + 'Total fat consumed during an exact effective Period, normalized to UCUM grams.', + code: { + system: 'http://loinc.org', + code: '9067-0', + display: 'Fat intake Measured', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'g', + unit: 'g', + example: 78, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-fiber': { + id: 'dietary-fiber', + profile: 'grove-mobile-dietary-fiber', + standardProfile: null, + title: 'Grove Mobile Dietary Fiber', + description: + 'Dietary fiber consumed during an exact effective Period, normalized to UCUM grams. Grove-coded because the only measured LOINC fiber concept is a 24-hour mass-rate, not a per-event mass.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-fiber', + display: 'Dietary fiber', + definition: + 'Dietary fiber consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'g', + unit: 'g', + example: 28, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-folate': { + id: 'dietary-folate', + profile: 'grove-mobile-dietary-folate', + standardProfile: null, + title: 'Grove Mobile Dietary Folate', + description: + 'Total folate (vitamin B9) consumed during an exact effective Period, normalized to UCUM micrograms. Grove-coded; the LOINC B9 intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-folate', + display: 'Dietary folate', + definition: + 'Total folate consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ug', + unit: 'ug', + example: 400, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-iodine': { + id: 'dietary-iodine', + profile: 'grove-mobile-dietary-iodine', + standardProfile: null, + title: 'Grove Mobile Dietary Iodine', + description: + 'Iodine consumed during an exact effective Period, normalized to UCUM micrograms. Grove-coded; the LOINC iodine intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-iodine', + display: 'Dietary iodine', + definition: + 'Iodine consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ug', + unit: 'ug', + example: 150, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-iron': { + id: 'dietary-iron', + profile: 'grove-mobile-dietary-iron', + standardProfile: null, + title: 'Grove Mobile Dietary Iron', + description: + 'Iron consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the LOINC iron intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-iron', + display: 'Dietary iron', + definition: + 'Iron consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 18, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-magnesium': { + id: 'dietary-magnesium', + profile: 'grove-mobile-dietary-magnesium', + standardProfile: null, + title: 'Grove Mobile Dietary Magnesium', + description: + 'Magnesium consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the LOINC magnesium intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-magnesium', + display: 'Dietary magnesium', + definition: + 'Magnesium consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 400, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-manganese': { + id: 'dietary-manganese', + profile: 'grove-mobile-dietary-manganese', + standardProfile: null, + title: 'Grove Mobile Dietary Manganese', + description: + 'Manganese consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the LOINC manganese intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-manganese', + display: 'Dietary manganese', + definition: + 'Manganese consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 2.3, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-molybdenum': { + id: 'dietary-molybdenum', + profile: 'grove-mobile-dietary-molybdenum', + standardProfile: null, + title: 'Grove Mobile Dietary Molybdenum', + description: + 'Molybdenum consumed during an exact effective Period, normalized to UCUM micrograms. Grove-coded; the LOINC molybdenum intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-molybdenum', + display: 'Dietary molybdenum', + definition: + 'Molybdenum consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ug', + unit: 'ug', + example: 45, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-niacin': { + id: 'dietary-niacin', + profile: 'grove-mobile-dietary-niacin', + standardProfile: null, + title: 'Grove Mobile Dietary Niacin', + description: + 'Niacin (vitamin B3) consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the LOINC B3 intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-niacin', + display: 'Dietary niacin', + definition: + 'Niacin consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 16, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-pantothenic-acid': { + id: 'dietary-pantothenic-acid', + profile: 'grove-mobile-dietary-pantothenic-acid', + standardProfile: null, + title: 'Grove Mobile Dietary Pantothenic Acid', + description: + 'Pantothenic acid (vitamin B5) consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the LOINC B5 intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-pantothenic-acid', + display: 'Dietary pantothenic acid', + definition: + 'Pantothenic acid consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 5, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-phosphorus': { + id: 'dietary-phosphorus', + profile: 'grove-mobile-dietary-phosphorus', + standardProfile: null, + title: 'Grove Mobile Dietary Phosphorus', + description: + 'Phosphorus consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the LOINC phosphorus intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-phosphorus', + display: 'Dietary phosphorus', + definition: + 'Phosphorus consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 700, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-potassium': { + id: 'dietary-potassium', + profile: 'grove-mobile-dietary-potassium', + standardProfile: null, + title: 'Grove Mobile Dietary Potassium', + description: + 'Potassium consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded for the same substance-versus-mass property mismatch as sodium.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-potassium', + display: 'Dietary potassium', + definition: + 'Potassium consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 3400, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-protein': { + id: 'dietary-protein', + profile: 'grove-mobile-dietary-protein', + standardProfile: null, + title: 'Grove Mobile Dietary Protein', + description: + 'Protein consumed during an exact effective Period, normalized to UCUM grams.', + code: { + system: 'http://loinc.org', + code: '9080-3', + display: 'Protein intake Measured', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'g', + unit: 'g', + example: 50, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-riboflavin': { + id: 'dietary-riboflavin', + profile: 'grove-mobile-dietary-riboflavin', + standardProfile: null, + title: 'Grove Mobile Dietary Riboflavin', + description: + 'Riboflavin (vitamin B2) consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the LOINC B2 intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-riboflavin', + display: 'Dietary riboflavin', + definition: + 'Riboflavin consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 1.3, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-selenium': { + id: 'dietary-selenium', + profile: 'grove-mobile-dietary-selenium', + standardProfile: null, + title: 'Grove Mobile Dietary Selenium', + description: + 'Selenium consumed during an exact effective Period, normalized to UCUM micrograms. Grove-coded; the LOINC selenium intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-selenium', + display: 'Dietary selenium', + definition: + 'Selenium consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ug', + unit: 'ug', + example: 55, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-sodium': { + id: 'dietary-sodium', + profile: 'grove-mobile-dietary-sodium', + standardProfile: null, + title: 'Grove Mobile Dietary Sodium', + description: + 'Sodium consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded because the point-timed LOINC sodium-intake concept is substance-amount, not mass.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-sodium', + display: 'Dietary sodium', + definition: + 'Sodium consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 2300, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-sugar': { + id: 'dietary-sugar', + profile: 'grove-mobile-dietary-sugar', + standardProfile: null, + title: 'Grove Mobile Dietary Sugar', + description: + 'Total sugars consumed during an exact effective Period, normalized to UCUM grams. Grove-coded because LOINC has no active point-timed sugar-intake mass concept.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-sugar', + display: 'Dietary sugar', + definition: + 'Total sugars consumed from food and drink during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'g', + unit: 'g', + example: 50, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-thiamin': { + id: 'dietary-thiamin', + profile: 'grove-mobile-dietary-thiamin', + standardProfile: null, + title: 'Grove Mobile Dietary Thiamin', + description: + 'Thiamin (vitamin B1) consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the LOINC B1 intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-thiamin', + display: 'Dietary thiamin', + definition: + 'Thiamin consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 1.2, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-vitamin-a': { + id: 'dietary-vitamin-a', + profile: 'grove-mobile-dietary-vitamin-a', + standardProfile: null, + title: 'Grove Mobile Dietary Vitamin A', + description: + 'Vitamin A consumed during an exact effective Period, normalized to UCUM micrograms. Grove-coded; the only LOINC vitamin A intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-vitamin-a', + display: 'Dietary vitamin A', + definition: + 'Vitamin A consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ug', + unit: 'ug', + example: 900, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-vitamin-b12': { + id: 'dietary-vitamin-b12', + profile: 'grove-mobile-dietary-vitamin-b12', + standardProfile: null, + title: 'Grove Mobile Dietary Vitamin B12', + description: + 'Vitamin B12 consumed during an exact effective Period, normalized to UCUM micrograms. Grove-coded; the LOINC B12 intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-vitamin-b12', + display: 'Dietary vitamin B12', + definition: + 'Vitamin B12 consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ug', + unit: 'ug', + example: 2.4, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-vitamin-b6': { + id: 'dietary-vitamin-b6', + profile: 'grove-mobile-dietary-vitamin-b6', + standardProfile: null, + title: 'Grove Mobile Dietary Vitamin B6', + description: + 'Vitamin B6 consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the LOINC B6 intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-vitamin-b6', + display: 'Dietary vitamin B6', + definition: + 'Vitamin B6 consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 1.7, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-vitamin-c': { + id: 'dietary-vitamin-c', + profile: 'grove-mobile-dietary-vitamin-c', + standardProfile: null, + title: 'Grove Mobile Dietary Vitamin C', + description: + 'Vitamin C consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the LOINC vitamin C intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-vitamin-c', + display: 'Dietary vitamin C', + definition: + 'Vitamin C consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 90, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-vitamin-d': { + id: 'dietary-vitamin-d', + profile: 'grove-mobile-dietary-vitamin-d', + standardProfile: null, + title: 'Grove Mobile Dietary Vitamin D', + description: + 'Vitamin D consumed during an exact effective Period, normalized to UCUM micrograms. Grove-coded; the LOINC vitamin D intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-vitamin-d', + display: 'Dietary vitamin D', + definition: + 'Vitamin D consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ug', + unit: 'ug', + example: 20, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-vitamin-e': { + id: 'dietary-vitamin-e', + profile: 'grove-mobile-dietary-vitamin-e', + standardProfile: null, + title: 'Grove Mobile Dietary Vitamin E', + description: + 'Vitamin E consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the LOINC vitamin E intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-vitamin-e', + display: 'Dietary vitamin E', + definition: + 'Vitamin E consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 15, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-vitamin-k': { + id: 'dietary-vitamin-k', + profile: 'grove-mobile-dietary-vitamin-k', + standardProfile: null, + title: 'Grove Mobile Dietary Vitamin K', + description: + 'Vitamin K consumed during an exact effective Period, normalized to UCUM micrograms. Grove-coded; the LOINC vitamin K intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-vitamin-k', + display: 'Dietary vitamin K', + definition: + 'Vitamin K consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ug', + unit: 'ug', + example: 120, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-zinc': { + id: 'dietary-zinc', + profile: 'grove-mobile-dietary-zinc', + standardProfile: null, + title: 'Grove Mobile Dietary Zinc', + description: + 'Zinc consumed during an exact effective Period, normalized to UCUM milligrams. Grove-coded; the LOINC zinc intake concepts are 24-hour mass-rates.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'dietary-zinc', + display: 'Dietary zinc', + definition: + 'Zinc consumed during the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mg', + unit: 'mg', + example: 11, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + distance: { + id: 'distance', + profile: 'grove-mobile-distance', + standardProfile: null, + title: 'Grove Mobile Distance', + description: + 'Distance traveled during an exact effective Period, normalized to UCUM metres.', + code: { + system: 'http://loinc.org', + code: '103208-5', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'm', + unit: 'm', + example: 5200, + dimension: { + m: 1, + }, + }, + effective: 'Period', + }, + 'electrodermal-activity': { + id: 'electrodermal-activity', + profile: 'grove-mobile-electrodermal-activity', + standardProfile: null, + title: 'Electrodermal Activity', + description: + "Skin electrical conductance normalized to UCUM microsiemens. Two sources evidence the raw scalar — HealthKit's electrodermal activity samples and the Withings Body Scan feet measurement (meastype 196) — and the sensor body site is preserved as a body-site coding rather than collapsed.", + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'electrodermal-activity', + display: 'Electrodermal activity', + definition: + 'The electrical conductance of the skin at the Observation effective instant.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'uS', + unit: 'uS', + example: 0.8, + dimension: { + C: 2, + g: -1, + m: -2, + s: 1, + }, + }, + effective: 'dateTime', + }, + 'flights-climbed': { + id: 'flights-climbed', + profile: 'grove-mobile-flights-climbed', + standardProfile: null, + title: 'Flights Climbed', + description: + "The number of flights of stairs (floors) ascended during an exact effective Period. Health Connect's fractional floors are preserved as a decimal count of the same measurand.", + code: { + system: 'http://loinc.org', + code: '100304-5', + display: 'Flights climbed [#] Reporting Period', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{flights}', + unit: 'flights', + example: 12, + dimension: {}, + }, + effective: 'Period', + }, + 'fluid-intake': { + id: 'fluid-intake', + profile: 'grove-mobile-fluid-intake', + standardProfile: null, + title: 'Grove Mobile Fluid Intake', + description: + 'Water or fluid consumed during an exact effective Period, normalized to UCUM millilitres. Carries HealthKit dietary water and Health Connect hydration under one shared concept; it belongs to the hydration family, not the dietary-nutrient family, hence the id outside the dietary- prefix.', + code: { + system: 'http://loinc.org', + code: '8985-4', + display: 'Fluid intake Measured', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mL', + unit: 'mL', + example: 250, + dimension: { + m: 3, + }, + }, + effective: 'Period', + }, + 'heart-rate': { + id: 'heart-rate', + profile: 'grove-mobile-heart-rate', + standardProfile: 'http://hl7.org/fhir/StructureDefinition/heartrate', + title: 'Grove Mobile Heart Rate', + description: + 'A source-neutral mobile heart rate that is required to conform to the authoritative FHIR R4 Heart Rate profile and adds Grove exchange identity and provenance context. Values are normalized to UCUM beats per minute.', + code: { + system: 'http://loinc.org', + code: '8867-4', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: 'beats/minute', + example: 72, + dimension: { + s: -1, + }, + }, + effective: 'dateTime', + }, + 'heart-rate-variability-rmssd': { + id: 'heart-rate-variability-rmssd', + profile: 'grove-mobile-heart-rate-variability-rmssd', + standardProfile: null, + title: 'Heart Rate Variability RMSSD', + description: + 'The root mean square of successive differences between NN (R-R) intervals, normalized to UCUM milliseconds. RMSSD is never relabeled as SDNN; the two HRV statistics remain separate measurements.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'heart-rate-variability-rmssd', + display: 'Heart rate variability RMSSD', + definition: + 'The root mean square of successive differences between adjacent NN intervals at the Observation effective instant.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ms', + unit: 'ms', + example: 42, + dimension: { + s: 1, + }, + }, + effective: 'dateTime', + }, + 'heart-rate-variability-sdnn': { + id: 'heart-rate-variability-sdnn', + profile: 'grove-mobile-heart-rate-variability-sdnn', + standardProfile: null, + title: 'Heart Rate Variability SDNN', + description: + 'Standard deviation of NN (normal-to-normal R-R) intervals over the source measurement window, normalized to UCUM milliseconds. SDNN is not interchangeable with RMSSD; the two statistics are distinct measurements and never merge.', + code: { + system: 'http://loinc.org', + code: '112429-6', + display: 'Heart rate variability SDNN [Time]', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ms', + unit: 'ms', + example: 58, + dimension: { + s: 1, + }, + }, + effective: 'dateTime', + }, + 'intermenstrual-bleeding': { + id: 'intermenstrual-bleeding', + profile: 'grove-mobile-intermenstrual-bleeding', + standardProfile: null, + title: 'Intermenstrual Bleeding', + description: + 'A logged intermenstrual (spotting) bleeding event; the fixed result states presence.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'intermenstrual-bleeding', + display: 'Intermenstrual bleeding', + definition: 'An observed bleeding event between menstrual periods.', + }, + valueKind: 'codeableConcept', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/mobile/ValueSet/grove-intermenstrual-bleeding', + resultCodeSystem: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-intermenstrual-bleeding', + allowedValues: ['present'], + resultCodes: [ + { + code: 'present', + display: 'Present', + definition: 'Intermenstrual bleeding was observed.', + }, + ], + effective: 'dateTime', + }, + 'lean-body-mass': { + id: 'lean-body-mass', + profile: 'grove-mobile-lean-body-mass', + standardProfile: null, + title: 'Lean Body Mass', + description: + 'A source-neutral lean body mass normalized to UCUM kilograms. Consumer bioimpedance sources report fat-free mass under this label; the lean-body-mass versus fat-free-mass distinction (essential fat) is not recoverable from any source, so Withings fat-free mass joins this measurement with that caveat rather than forming a separate concept.', + code: { + system: 'http://loinc.org', + code: '91557-9', + display: 'Lean body weight', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kg', + unit: 'kg', + example: 56, + dimension: { + g: 1, + }, + }, + effective: 'dateTime', + }, + 'light-sleep-duration': { + id: 'light-sleep-duration', + profile: 'grove-mobile-light-sleep-duration', + standardProfile: null, + title: 'Light Sleep Duration', + description: + 'Total time classified as light sleep within one sleep session or civil-day summary, normalized to UCUM minutes over the exact session Period. It is a stage-total aggregate without per-stage intervals and is implemented by the phase-2 aggregate design.', + code: { + system: 'http://loinc.org', + code: '93830-8', + display: 'Light sleep duration', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'min', + unit: 'min', + example: 244, + dimension: { + s: 1, + }, + }, + method: { + code: 'session-total', + display: 'Session total', + }, + effective: 'Period', + }, + 'menstruation-flow': { + id: 'menstruation-flow', + profile: 'grove-mobile-menstruation-flow', + standardProfile: null, + title: 'Menstruation Flow', + description: + "The observed menstrual flow classification, with the exact platform value retained as a secondary coding. The HealthKit iOS 18 vaginal-bleeding replacement uses the identical case set. HealthKit's mandatory cycle-start metadata is carried as an optional coded component so no source information is dropped.", + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'menstruation-flow', + display: 'Menstruation flow', + definition: + 'The observed menstrual flow classification at the stated instant.', + }, + valueKind: 'codeableConcept', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/mobile/ValueSet/grove-menstruation-flow', + resultCodeSystem: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-menstruation-flow', + allowedValues: ['unspecified', 'light', 'medium', 'heavy', 'none'], + resultCodes: [ + { + code: 'unspecified', + display: 'Unspecified', + definition: 'Flow was recorded without an amount.', + }, + { + code: 'light', + display: 'Light', + definition: 'Light flow.', + }, + { + code: 'medium', + display: 'Medium', + definition: 'Medium flow.', + }, + { + code: 'heavy', + display: 'Heavy', + definition: 'Heavy flow.', + }, + { + code: 'none', + display: 'None', + definition: 'No flow was observed; recorded only by HealthKit.', + }, + ], + effective: 'dateTime', + components: [ + { + id: 'cycleStart', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'menstrual-cycle-start', + display: 'Menstrual cycle start', + definition: + 'Whether the flow observation marks the first day of a menstrual cycle, as the source states it.', + cardinality: '0..1', + valueSet: + 'https://grovealliance.org/fhir/mobile/ValueSet/grove-menstrual-cycle-start', + resultCodes: [ + { + code: 'cycle-start', + display: 'Cycle start', + definition: + 'The observation marks the first day of a menstrual cycle.', + }, + { + code: 'not-cycle-start', + display: 'Not cycle start', + definition: + 'The observation does not mark the first day of a menstrual cycle.', + }, + ], + title: 'Menstrual Cycle Start', + }, + ], + }, + 'mindfulness-session': { + id: 'mindfulness-session', + profile: 'grove-mobile-mindfulness-session', + standardProfile: null, + title: 'Mindfulness Session', + description: + 'The duration of one mindfulness session over its exact effective Period, normalized to UCUM minutes. Source session subtype (meditation, breathing, etc.) is adapter context and does not alter the normalized duration.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'mindfulness-session-duration', + display: 'Mindfulness session duration', + definition: + 'The duration of a single mindfulness session covering the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'min', + unit: 'min', + example: 10, + dimension: { + s: 1, + }, + }, + effective: 'Period', + }, + 'ovulation-test-result': { + id: 'ovulation-test-result', + profile: 'grove-mobile-ovulation-test-result', + standardProfile: null, + title: 'Ovulation Test Result', + description: + "The result of a home ovulation test, with the exact platform value retained as a secondary coding. HealthKit's estrogen-surge case widens to high-fertility with the source coding preserving the distinction.", + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'ovulation-test-result', + display: 'Ovulation test result', + definition: 'The classified result of a home ovulation test.', + }, + valueKind: 'codeableConcept', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/mobile/ValueSet/grove-ovulation-test-result', + resultCodeSystem: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-ovulation-test-result', + allowedValues: [ + 'negative', + 'high-fertility', + 'luteinizing-hormone-surge', + 'indeterminate', + ], + resultCodes: [ + { + code: 'negative', + display: 'Negative', + definition: 'No fertility surge detected.', + }, + { + code: 'high-fertility', + display: 'High fertility', + definition: + 'An elevated fertility indicator short of a confirmed LH surge.', + }, + { + code: 'luteinizing-hormone-surge', + display: 'Luteinizing hormone surge', + definition: 'A confirmed luteinizing hormone surge.', + }, + { + code: 'indeterminate', + display: 'Indeterminate', + definition: 'The test could not be interpreted.', + }, + ], + effective: 'dateTime', + }, + 'oxygen-saturation': { + id: 'oxygen-saturation', + profile: 'grove-mobile-oxygen-saturation', + standardProfile: 'http://hl7.org/fhir/StructureDefinition/oxygensat', + title: 'Grove Mobile Oxygen Saturation', + description: + 'A source-neutral mobile oxygen saturation that is required to conform to the authoritative FHIR R4 Oxygen Saturation profile. Values are normalized to UCUM percent.', + code: { + system: 'http://loinc.org', + code: '2708-6', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: '%', + unit: '%', + example: 97, + dimension: {}, + }, + effective: 'dateTime', + }, + 'oxygen-saturation-daily-average': { + id: 'oxygen-saturation-daily-average', + profile: 'grove-mobile-oxygen-saturation-daily-average', + standardProfile: null, + title: 'Oxygen Saturation Daily Average', + description: + 'The mean oxygen saturation over a civil-date effective Period, normalized to UCUM percent. It is distinct from the point-in-time shared oxygen-saturation measurement and is implemented by the phase-2 aggregate design.', + code: { + system: 'http://loinc.org', + code: '103209-3', + display: 'Mean oxygen saturation', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: '%', + unit: '%', + example: 96, + dimension: {}, + }, + method: { + code: 'daily-mean', + display: 'Daily mean', + }, + effective: 'Period', + }, + power: { + id: 'power', + profile: 'grove-mobile-power', + standardProfile: null, + title: 'Power', + description: + 'A source-neutral instantaneous mechanical power output sample recorded during activity, normalized to UCUM watts. The activity binding (running, cycling) is carried by source context and workout session linkage, not by the code. It is distinct from the cycling functional threshold power estimate, which is a derived capacity value rather than an instantaneous sample.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'power', + display: 'Power', + definition: + 'The mechanical power the subject produces at the exact Observation effective instant, recorded during activity.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'W', + unit: 'W', + example: 180, + dimension: { + g: 1, + m: 2, + s: -3, + }, + }, + effective: 'dateTime', + }, + 'rem-sleep-duration': { + id: 'rem-sleep-duration', + profile: 'grove-mobile-rem-sleep-duration', + standardProfile: null, + title: 'REM Sleep Duration', + description: + 'Total time classified as REM sleep within one sleep session or civil-day summary, normalized to UCUM minutes over the exact session Period. It is a stage-total aggregate without per-stage intervals and is implemented by the phase-2 aggregate design.', + code: { + system: 'http://loinc.org', + code: '93829-0', + display: 'REM sleep duration', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'min', + unit: 'min', + example: 96, + dimension: { + s: 1, + }, + }, + method: { + code: 'session-total', + display: 'Session total', + }, + effective: 'Period', + }, + 'respiratory-rate': { + id: 'respiratory-rate', + profile: 'grove-mobile-respiratory-rate', + standardProfile: 'http://hl7.org/fhir/StructureDefinition/resprate', + title: 'Grove Mobile Respiratory Rate', + description: + 'A source-neutral mobile respiratory rate that is required to conform to the authoritative FHIR R4 Respiratory Rate profile. Values are normalized to breaths per minute.', + code: { + system: 'http://loinc.org', + code: '9279-1', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: 'breaths/minute', + example: 16, + dimension: { + s: -1, + }, + }, + effective: 'dateTime', + }, + 'respiratory-rate-average': { + id: 'respiratory-rate-average', + profile: 'grove-mobile-respiratory-rate-average', + standardProfile: null, + title: 'Respiratory Rate Average', + description: + 'The mean respiratory rate over a windowed effective Period, normalized to UCUM breaths per minute; current source windows are a civil date (Google) and a sleep session (Oura, Withings), and the window is carried entirely by the effectivePeriod. It is distinct from the point-in-time shared respiratory-rate measurement and is implemented by the phase-2 aggregate design.', + code: { + system: 'http://loinc.org', + code: '103217-6', + display: 'Mean respiratory rate', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: 'breaths/minute', + example: 15, + dimension: { + s: -1, + }, + }, + methodChoice: ['daily-mean', 'session-mean'], + effective: 'Period', + }, + 'resting-heart-rate': { + id: 'resting-heart-rate', + profile: 'grove-mobile-resting-heart-rate', + standardProfile: null, + title: 'Resting Heart Rate', + description: + 'A windowed estimate of the heart rate while at rest, normalized to UCUM beats per minute over the exact estimation window. It is semantically distinct from the shared point heart-rate measurement and is implemented by the phase-2 aggregate design. LOINC 40443-4 is deliberately not used: it denotes a heart rate measured at rest, which R4 classifies as a vital sign, whereas this result is a derived daily estimate that must not be surfaced as a measured vital sign.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'resting-heart-rate', + display: 'Resting heart rate', + definition: + 'The estimated heart rate at rest across the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: 'beats/minute', + example: 58, + dimension: { + s: -1, + }, + }, + method: { + code: 'daily-mean', + display: 'Daily mean', + }, + effective: 'Period', + }, + 'sexual-activity': { + id: 'sexual-activity', + profile: 'grove-mobile-sexual-activity', + standardProfile: null, + title: 'Sexual Activity', + description: + 'A logged sexual-activity event whose result states whether protection was used, with the exact platform value retained where a platform enum exists.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'sexual-activity', + display: 'Sexual activity', + definition: + 'A logged sexual-activity event with its protection-used classification.', + }, + valueKind: 'codeableConcept', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/mobile/ValueSet/grove-sexual-activity', + resultCodeSystem: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-sexual-activity', + allowedValues: ['protected', 'unprotected', 'unknown'], + resultCodes: [ + { + code: 'protected', + display: 'Protection used', + definition: 'Protection was reported used.', + }, + { + code: 'unprotected', + display: 'Protection not used', + definition: 'Protection was reported not used.', + }, + { + code: 'unknown', + display: 'Unknown', + definition: 'Protection use was not reported.', + }, + ], + effective: 'dateTime', + }, + 'skin-temperature': { + id: 'skin-temperature', + profile: 'grove-mobile-skin-temperature', + standardProfile: null, + title: 'Skin Temperature', + description: + 'A source-neutral skin (body surface) temperature normalized to UCUM degrees Celsius and carrying a body-site coding (wrist for Apple sleeping wrist temperature; the Health Connect measurement location when present). It is deliberately distinct from the core body-temperature vital sign and is never relabeled as it.', + code: { + system: 'http://loinc.org', + code: '61008-9', + display: 'Body surface temperature', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'Cel', + unit: 'Cel', + example: 33.2, + dimension: { + K: 1, + }, + }, + effective: 'dateTime', + }, + 'sleep-awake-duration': { + id: 'sleep-awake-duration', + profile: 'grove-mobile-sleep-awake-duration', + standardProfile: null, + title: 'Awake Duration During Sleep', + description: + 'Total time awake within one sleep session or civil-day sleep summary, normalized to UCUM minutes over the exact session Period. It is a stage-total aggregate without per-stage intervals and is implemented by the phase-2 aggregate design.', + code: { + system: 'http://loinc.org', + code: '93828-2', + display: 'Nighttime awakening duration', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'min', + unit: 'min', + example: 22, + dimension: { + s: 1, + }, + }, + method: { + code: 'session-total', + display: 'Session total', + }, + effective: 'Period', + }, + 'sleep-duration': { + id: 'sleep-duration', + profile: 'grove-mobile-sleep-duration', + standardProfile: null, + title: 'Grove Mobile Sleep Duration', + description: + 'Total duration classified as sleep during an exact effective Period, using LOINC Sleep duration and normalized to UCUM hours. This summary does not encode sleep stages.', + code: { + system: 'http://loinc.org', + code: '93832-4', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'h', + unit: 'h', + example: 7.5, + dimension: { + s: 1, + }, + }, + effective: 'Period', + hasMember: ['sleep-stage'], + }, + 'sleep-heart-rate': { + id: 'sleep-heart-rate', + profile: 'grove-mobile-sleep-heart-rate', + standardProfile: null, + title: 'Sleep Heart Rate', + description: + "A heart-rate statistic computed over one sleep session (Oura reports the session minimum, Withings the session mean), normalized to UCUM beats per minute over the exact session Period. The statistic is carried by the aggregate design's fixed method coding, never collapsed into the shared point heart-rate measurement.", + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'sleep-heart-rate', + display: 'Sleep heart rate', + definition: + 'A heart-rate statistic aggregated over the exact sleep-session effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: 'beats/minute', + example: 54, + dimension: { + s: -1, + }, + }, + methodChoice: ['session-mean', 'session-minimum'], + effective: 'Period', + }, + 'sleep-stage': { + id: 'sleep-stage', + profile: 'grove-mobile-sleep-stage', + standardProfile: null, + title: 'Grove Mobile Sleep Stage', + description: + 'A source-neutral sleep-stage classification for one exact interval. A session summary links its ordered, non-overlapping stage Observations through hasMember.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'sleep-stage', + display: 'Sleep stage', + definition: + 'The classification assigned to an exact interval within a sleep session.', + }, + valueKind: 'codeableConcept', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/mobile/ValueSet/grove-sleep-stage', + resultCodeSystem: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-sleep-stage', + allowedValues: [ + 'awake', + 'in-bed', + 'out-of-bed', + 'asleep-unspecified', + 'light', + 'deep', + 'rem', + 'unknown', + ], + effective: 'Period', + }, + speed: { + id: 'speed', + profile: 'grove-mobile-speed', + standardProfile: null, + title: 'Speed', + description: + 'A source-neutral instantaneous speed sample recorded during activity, normalized to UCUM metres per second. The activity binding (running, cycling, skiing, paddling, rowing) is carried by source context and workout session linkage, not by the code, mirroring the shipped generic distance measurement. It is distinct from walking speed and the stair speeds, whose gait-health bindings are part of those concepts.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'speed', + display: 'Speed', + definition: + "The subject's instantaneous speed of travel at the exact Observation effective instant, recorded during activity.", + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'm/s', + unit: 'm/s', + example: 3.2, + dimension: { + m: 1, + s: -1, + }, + }, + effective: 'dateTime', + }, + 'step-count': { + id: 'step-count', + profile: 'grove-mobile-step-count', + standardProfile: null, + title: 'Grove Mobile Step Count', + description: + 'The number of steps recorded during an exact effective Period.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'step-count-total', + display: 'Step count total', + definition: + 'The total number of steps attributed to the exact Observation effective Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{steps}', + unit: 'steps', + example: 8432, + dimension: {}, + unitMustSupport: true, + }, + effective: 'Period', + obeys: [ + 'grove-step-count-result-1', + 'grove-step-count-period-1', + 'grove-step-count-value-1', + ], + }, + 'vo2-max': { + id: 'vo2-max', + profile: 'grove-mobile-vo2-max', + standardProfile: null, + title: 'VO2 Max', + description: + 'The maximal oxygen consumption per body mass, normalized to UCUM millilitres per kilogram per minute. Sources deliver estimated or test-derived maxima; the estimation method is adapter context and does not alter the normalized value.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'vo2-max', + display: 'VO2 max', + definition: + 'The maximal oxygen consumption per kilogram of body mass, whether measured or estimated, reported at the Observation effective instant.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mL/kg/min', + unit: 'mL/kg/min', + example: 42.5, + dimension: { + g: -1, + m: 3, + s: -1, + }, + }, + effective: 'dateTime', + }, + 'wheelchair-push-count': { + id: 'wheelchair-push-count', + profile: 'grove-mobile-wheelchair-push-count', + standardProfile: null, + title: 'Wheelchair Push Count', + description: + 'The number of wheelchair pushes recorded during an exact effective Period, using LOINC Number of wheelchair pushes per time period and normalized to the UCUM annotation {pushes}. It is the wheelchair analogue of step-count and never substitutes for it.', + code: { + system: 'http://loinc.org', + code: '96502-0', + display: 'Number of wheelchair pushes per time period', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{pushes}', + unit: 'pushes', + example: 1240, + dimension: {}, + }, + effective: 'Period', + }, + workout: { + id: 'workout', + profile: 'grove-mobile-workout', + standardProfile: null, + title: 'Workout', + description: + 'One recorded workout session: the shared activity classification as the result, the exact session bounds as the effective Period, and optional per-session statistics as components. The exact platform activity token is retained as a secondary coding; segments and laps are linked child Observations, so no session information is dropped.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'workout', + display: 'Workout session', + definition: + 'One recorded workout session over the exact Observation effective Period.', + }, + valueKind: 'codeableConcept', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/mobile/ValueSet/grove-workout-activity', + resultCodeSystem: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-activity', + allowedValues: [ + 'running', + 'walking', + 'cycling', + 'hiking', + 'swimming', + 'strength-training', + 'high-intensity-interval-training', + 'yoga', + 'pilates', + 'rowing', + 'elliptical', + 'stair-climbing', + 'dancing', + 'tennis', + 'table-tennis', + 'badminton', + 'squash', + 'basketball', + 'soccer', + 'american-football', + 'baseball', + 'volleyball', + 'golf', + 'boxing', + 'martial-arts', + 'skiing', + 'snowboarding', + 'other', + ], + components: [ + { + id: 'active-duration', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'active-duration', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 's', + unit: 's', + example: 2700, + dimension: { + s: 1, + }, + }, + }, + { + id: 'distance-sum', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'distance-sum', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'm', + unit: 'm', + example: 8400, + dimension: { + m: 1, + }, + }, + }, + { + id: 'active-energy-sum', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'active-energy-sum', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kcal', + unit: 'kcal', + example: 430, + dimension: { + g: 1, + m: 2, + s: -2, + }, + }, + }, + { + id: 'heart-rate-avg', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'heart-rate-avg', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: '/min', + example: 142, + dimension: { + s: -1, + }, + }, + }, + { + id: 'heart-rate-max', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'heart-rate-max', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: '/min', + example: 171, + dimension: { + s: -1, + }, + }, + }, + { + id: 'heart-rate-min', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'heart-rate-min', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: '/min', + example: 96, + dimension: { + s: -1, + }, + }, + }, + { + id: 'step-count-sum', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'step-count-sum', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{steps}', + unit: '{steps}', + example: 6120, + dimension: {}, + }, + }, + { + id: 'elevation-gain', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'elevation-gain', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'm', + unit: 'm', + example: 145, + dimension: { + m: 1, + }, + }, + }, + { + id: 'flights-climbed-sum', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'flights-climbed-sum', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{flights}', + unit: '{flights}', + example: 8, + dimension: {}, + }, + }, + { + id: 'speed-avg', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'speed-avg', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'm/s', + unit: 'm/s', + example: 2.8, + dimension: { + m: 1, + s: -1, + }, + }, + }, + { + id: 'swimming-stroke-count-sum', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'swimming-stroke-count-sum', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{strokes}', + unit: '{strokes}', + example: 480, + dimension: {}, + }, + }, + { + id: 'pool-lap-count', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'pool-lap-count', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{laps}', + unit: '{laps}', + example: 24, + dimension: {}, + }, + }, + ], + hasMember: ['workout-segment'], + effective: 'Period', + }, + 'workout-segment': { + id: 'workout-segment', + profile: 'grove-mobile-workout-segment', + standardProfile: null, + title: 'Workout Segment', + description: + 'One segment, lap, pause, marker, or event within a workout session, linked from the parent workout through hasMember. The value is the segment classification — an activity code or a structural code — with the exact platform token retained; per-segment statistics ride the open component slicing.', + code: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', + code: 'workout-segment', + display: 'Workout segment', + definition: 'One classified interval or event within a workout session.', + }, + valueKind: 'codeableConcept', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/mobile/ValueSet/grove-workout-segment-type', + resultCodeSystem: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-segment-type', + allowedValues: [ + 'lap', + 'pause', + 'resume', + 'motion-paused', + 'motion-resumed', + 'pause-or-resume-request', + 'marker', + 'segment-generic', + 'rest', + 'other-workout', + 'unknown', + ], + components: [ + { + id: 'repetitions', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'repetitions', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{count}', + unit: 'repetitions', + example: 12, + dimension: {}, + }, + }, + { + id: 'set-weight', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'set-weight', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kg', + unit: 'kg', + example: 20, + dimension: { + g: 1, + }, + }, + }, + { + id: 'set-index', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'set-index', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{count}', + unit: 'set index', + example: 3, + dimension: {}, + }, + }, + { + id: 'rating-of-perceived-exertion', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'rating-of-perceived-exertion', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{score}', + unit: 'RPE 0-10', + example: 7, + dimension: {}, + }, + }, + { + id: 'lap-length', + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-workout-statistic', + code: 'lap-length', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'm', + unit: 'm', + example: 25, + dimension: { + m: 1, + }, + }, + }, + ], + effective: 'Period', + }, +} as const + +export const sharedMobileMeasurementCatalog: typeof sharedMobileMeasurementCatalogValue = + deepFreeze(sharedMobileMeasurementCatalogValue) + +const mobileEffectiveCanonicalizationValue = { + scope: + 'Every Mobile scalar or aggregate effectiveDateTime and effectivePeriod endpoint; Sensor and ECG SampledData timing is excluded.', + precision: 'millisecond', + rounding: 'half-even', + epoch: '1970-01-01T00:00:00Z', + offsetPolicy: + 'Preserve the caller/source numeric UTC offset when it is available; never invent an offset.', +} as const + +export const mobileEffectiveCanonicalization: typeof mobileEffectiveCanonicalizationValue = + deepFreeze(mobileEffectiveCanonicalizationValue) + +const mobileEffectiveCanonicalizationVectorsValue = [ + { + id: 'positive-below-half', + input: '1970-01-01T00:00:00.000499999Z', + output: '1970-01-01T00:00:00.000Z', + }, + { + id: 'positive-half-to-even', + input: '1970-01-01T00:00:00.000500000Z', + output: '1970-01-01T00:00:00.000Z', + }, + { + id: 'positive-half-to-next-even', + input: '1970-01-01T00:00:00.001500000Z', + output: '1970-01-01T00:00:00.002Z', + }, + { + id: 'negative-half-to-even', + input: '1969-12-31T23:59:59.999500000Z', + output: '1970-01-01T00:00:00.000Z', + }, + { + id: 'negative-half-to-previous-even', + input: '1969-12-31T23:59:59.998500000Z', + output: '1969-12-31T23:59:59.998Z', + }, + { + id: 'offset-preserved', + input: '2026-08-20T08:30:00.251499999-07:00', + output: '2026-08-20T08:30:00.251-07:00', + }, +] as const + +export const mobileEffectiveCanonicalizationVectors: typeof mobileEffectiveCanonicalizationVectorsValue = + deepFreeze(mobileEffectiveCanonicalizationVectorsValue) + +export type SharedMobileMeasurementKind = + keyof typeof sharedMobileMeasurementCatalog diff --git a/packages/fhir/src/mobile/time.ts b/packages/fhir/src/mobile/time.ts new file mode 100644 index 00000000..cc0e791c --- /dev/null +++ b/packages/fhir/src/mobile/time.ts @@ -0,0 +1,111 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { mobileEffectiveCanonicalization } from './measurement-catalog.generated.js' +import { + err, + parseFhirInstant, + type FhirInstant, + type Result, +} from '../core/index.js' + +const OFFSET_INSTANT = + /^(?\d{4})-(?\d{2})-(?\d{2})T(?\d{2}):(?\d{2}):(?\d{2})(?:\.(?\d+))?(?Z|[+-]\d{2}:\d{2})$/u +const SUPPORTED_PRECISION_DIGITS = new Map([['millisecond', 3]]) +const SUPPORTED_ROUNDING = new Set(['half-even']) + +const shouldRoundUp = (floorMillisecond: bigint, tail: string): boolean => { + if (tail === '') return false + if (tail.charAt(0) > '5') return true + if (tail.charAt(0) < '5') return false + if (/[^0]/u.test(tail.slice(1))) return true + return floorMillisecond % 2n !== 0n +} + +const offsetMinutes = (zone: string): number => { + if (zone === 'Z') return 0 + const magnitude = Number(zone.slice(1, 3)) * 60 + Number(zone.slice(4, 6)) + return zone.startsWith('-') ? -magnitude : magnitude +} + +/** + * Applies the generated Mobile IG effective-time policy without floating-point + * truncation. Sensor and ECG SampledData timing intentionally does not use this + * function. + */ +export const canonicalizeMobileEffectiveInstant = ( + value: unknown, +): Result => { + const precisionDigits = SUPPORTED_PRECISION_DIGITS.get( + mobileEffectiveCanonicalization.precision, + ) + if ( + precisionDigits === undefined || + !SUPPORTED_ROUNDING.has(mobileEffectiveCanonicalization.rounding) + ) { + return err( + 'invalid-date-time', + 'The generated Mobile effective-time contract is not supported.', + ) + } + + const parsed = parseFhirInstant(value) + if (!parsed.ok || typeof value !== 'string') return parsed + const groups = OFFSET_INSTANT.exec(value)?.groups + if (groups === undefined) { + return err( + 'invalid-date-time', + 'Expected a valid Mobile effective instant.', + ) + } + + const localSecond = new Date(0) + localSecond.setUTCFullYear( + Number(groups.year), + Number(groups.month) - 1, + Number(groups.day), + ) + localSecond.setUTCHours( + Number(groups.hour), + Number(groups.minute), + Number(groups.second), + 0, + ) + const zone = groups.zone ?? 'Z' + const epochSecond = + BigInt(Math.trunc(localSecond.getTime() / 1000)) - + BigInt(offsetMinutes(zone) * 60) + const fraction = groups.fraction ?? '' + const withinSecond = BigInt( + (fraction + '0'.repeat(precisionDigits)).slice(0, precisionDigits), + ) + const floorMillisecond = epochSecond * 1000n + withinSecond + const roundedMillisecond = + floorMillisecond + + (shouldRoundUp(floorMillisecond, fraction.slice(precisionDigits)) ? 1n : 0n) + const localMillisecond = + roundedMillisecond + BigInt(offsetMinutes(zone) * 60_000) + + if ( + localMillisecond > BigInt(Number.MAX_SAFE_INTEGER) || + localMillisecond < BigInt(Number.MIN_SAFE_INTEGER) + ) { + return err( + 'invalid-date-time', + 'Rounded Mobile effective instant is outside the supported FHIR range.', + ) + } + const rendered = new Date(Number(localMillisecond)).toISOString() + if (!/^\d{4}-/u.test(rendered)) { + return err( + 'invalid-date-time', + 'Rounding would move the Mobile effective instant outside four-digit FHIR years.', + ) + } + return parseFhirInstant(`${rendered.slice(0, 23)}${zone}`) +} diff --git a/packages/fhir/src/mobile/types.ts b/packages/fhir/src/mobile/types.ts new file mode 100644 index 00000000..3059f49c --- /dev/null +++ b/packages/fhir/src/mobile/types.ts @@ -0,0 +1,186 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import type { SharedMobileMeasurementKind } from './measurement-catalog.generated.js' +import type { + AbsoluteUri, + FhirId, + FhirInstant, + UrnUuid, +} from '../core/index.js' + +type SharedCatalog = + typeof import('./measurement-catalog.generated.js').sharedMobileMeasurementCatalog + +/** Kinds whose catalog definition declares the given value kind and effective. */ +type MeasurementKindsWhere< + ValueKind extends string, + Effective extends string, +> = { + [Kind in SharedMobileMeasurementKind]: SharedCatalog[Kind] extends ( + { readonly valueKind: ValueKind; readonly effective: Effective } + ) ? + Kind + : never +}[SharedMobileMeasurementKind] + +export type InstantQuantityMeasurementKind = MeasurementKindsWhere< + 'quantity', + 'dateTime' +> + +export type PeriodQuantityMeasurementKind = MeasurementKindsWhere< + 'quantity', + 'Period' +> + +export type InstantCodedMeasurementKind = MeasurementKindsWhere< + 'codeableConcept', + 'dateTime' +> + +/** Sleep-stage keeps its bespoke shape with an optional source-native coding. */ +export type PeriodCodedMeasurementKind = Exclude< + MeasurementKindsWhere<'codeableConcept', 'Period'>, + 'sleep-stage' +> + +export interface InstantEffectiveTime { + readonly kind: 'date-time' + readonly value: FhirInstant +} + +export interface PeriodEffectiveTime { + readonly kind: 'period' + readonly start: FhirInstant + readonly end: FhirInstant +} + +export type InstantQuantityMeasurement = { + readonly [Kind in InstantQuantityMeasurementKind]: { + readonly kind: Kind + /** Value expressed in the catalog's canonical UCUM unit. */ + readonly value: number + readonly effective: InstantEffectiveTime + } +}[InstantQuantityMeasurementKind] + +export type PeriodQuantityMeasurement = { + readonly [Kind in PeriodQuantityMeasurementKind]: { + readonly kind: Kind + /** Value expressed in the catalog's canonical UCUM unit. */ + readonly value: number + readonly effective: PeriodEffectiveTime + } +}[PeriodQuantityMeasurementKind] + +export type InstantCodedMeasurement = { + readonly [Kind in InstantCodedMeasurementKind]: { + readonly kind: Kind + /** Shared code drawn from the catalog's closed allowed-value set. */ + readonly value: SharedCatalog[Kind]['allowedValues'][number] + readonly effective: InstantEffectiveTime + } +}[InstantCodedMeasurementKind] + +export type PeriodCodedMeasurement = { + readonly [Kind in PeriodCodedMeasurementKind]: { + readonly kind: Kind + /** Shared code drawn from the catalog's closed allowed-value set. */ + readonly value: SharedCatalog[Kind]['allowedValues'][number] + readonly effective: PeriodEffectiveTime + } +}[PeriodCodedMeasurementKind] + +export interface BloodPressureMeasurement { + readonly kind: 'blood-pressure' + readonly systolic: number + readonly diastolic: number + readonly effective: InstantEffectiveTime +} + +export type SleepStage = SharedCatalog['sleep-stage']['allowedValues'][number] + +export interface SleepStageSourceCodingInput { + readonly system: AbsoluteUri + readonly code: string + readonly display?: string +} + +export interface SleepStageMeasurement { + readonly kind: 'sleep-stage' + readonly stage: SleepStage + /** Source-native stage retained when it is more precise than the shared stage. */ + readonly sourceStageCoding?: SleepStageSourceCodingInput + readonly effective: PeriodEffectiveTime +} + +/** Complete FHIR business Identifier pair; neither member may be omitted. */ +export interface CompleteIdentifierInput { + readonly system: AbsoluteUri + readonly value: string +} + +/** Caller-owned business identity with an optional repository-assigned Resource.id. */ +export interface ResourceIdentityInput { + readonly identifier: CompleteIdentifierInput + readonly id?: FhirId +} + +/** Closed union of normalized measurements defined by the shared Mobile IG. */ +export type MobileMeasurement = + | BloodPressureMeasurement + | InstantCodedMeasurement + | InstantQuantityMeasurement + | PeriodCodedMeasurement + | PeriodQuantityMeasurement + | SleepStageMeasurement + +/** Derived exchange identity returned by identity helpers, never required as input. */ +export interface IdentifiedEntryIdentityInput extends ResourceIdentityInput { + readonly fullUrl: UrnUuid +} + +export interface ApplicationDeviceInput { + readonly identity: ResourceIdentityInput + readonly name: string + readonly version?: string + readonly manufacturer?: string +} + +interface RecordingDeviceBaseInput { + readonly identity: ResourceIdentityInput + readonly name?: string + readonly manufacturer?: string + readonly modelNumber?: string +} + +/** Recording-device identity must declare its privacy/disclosure scope. */ +export type RecordingDeviceInput = RecordingDeviceBaseInput & + ( + | { readonly identityScope: 'deployment-scoped' } + | { + readonly identityScope: 'authorized-hardware' + readonly disclosureAuthorization: 'authorized-for-exchange' + } + ) + +/** Explicit evidence for an optional application gateway role. */ +export type GatewayApplicationInput = + | { + readonly kind: 'converter-application' + readonly roleAssurance: 'mediated-or-routed-measurement' + } + | { + readonly kind: 'distinct-application' + readonly roleAssurance: 'mediated-or-routed-measurement' + readonly application: ApplicationDeviceInput + } + +export type RecordingMethod = + 'actively-recorded' | 'automatically-recorded' | 'manual-entry' diff --git a/packages/fhir/src/valueSets/claimResponse.ts b/packages/fhir/src/provenance/index.ts similarity index 54% rename from packages/fhir/src/valueSets/claimResponse.ts rename to packages/fhir/src/provenance/index.ts index 7241e5f5..dd789841 100644 --- a/packages/fhir/src/valueSets/claimResponse.ts +++ b/packages/fhir/src/provenance/index.ts @@ -6,10 +6,4 @@ // SPDX-License-Identifier: MIT // -import { z } from 'zod' - -/** - * Zod schema for FHIR NoteType value set. - * The type of note. - */ -export const noteTypeSchema = z.enum(['display', 'print', 'printoper']) +export { parseProvenance, type Provenance } from '../r4/index.js' diff --git a/packages/fhir/src/providers/builder.ts b/packages/fhir/src/providers/builder.ts new file mode 100644 index 00000000..b795dd0a --- /dev/null +++ b/packages/fhir/src/providers/builder.ts @@ -0,0 +1,568 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + groveProviderProfileCanonicals, + providerAdapterCatalog, +} from './contract.generated.js' +import { + assemblerAgent, + coding, + concept, + identifiedEntry, + identifier, + makeApplicationDevice, + makeRecordingDevice, + provenanceActivity, + resourceId, + sourceEntityAgent, +} from './graph.js' +import { deriveProviderIdentities } from './identity.js' +import { EXTENSIONS, PROFILES, SYSTEMS } from './profiles.js' +import { + providerOutputDiscriminator, + parseProviderMeasurementBundleInput, +} from './provider.js' +import type { ProviderMeasurementBundleInput } from './types.js' +import { issues, ok, type Result, type UrnUuid } from '../core/index.js' +import { createEntryIdentity } from '../mobile/identity.js' +import { sharedMobileMeasurementCatalog } from '../mobile/measurement-catalog.generated.js' +import type { + CompleteIdentifierInput, + IdentifiedEntryIdentityInput, + ResourceIdentityInput, +} from '../mobile/types.js' +import { + parseCollectionBundle, + type CodeableConcept, + type CollectionBundle, +} from '../r4/index.js' + +const VITAL_SIGNS = new Set([ + 'blood-pressure', + 'body-height', + 'body-temperature', + 'body-weight', + 'heart-rate', + 'oxygen-saturation', + 'respiratory-rate', +]) + +type ConnectedMeasurement = + ProviderMeasurementBundleInput['measurements'][number] + +const quantity = ( + value: number, + definition: { + readonly system: string + readonly code: string + readonly unit?: string + }, +) => ({ + value, + unit: definition.unit ?? definition.code, + system: definition.system, + code: definition.code, +}) + +interface ResolvedGraphIdentities { + readonly sourceRecord: CompleteIdentifierInput + readonly exchange: CompleteIdentifierInput + readonly observations: readonly IdentifiedEntryIdentityInput[] + readonly provenance: IdentifiedEntryIdentityInput + readonly application: IdentifiedEntryIdentityInput + readonly dataOrigin: IdentifiedEntryIdentityInput + readonly gatewayReference?: UrnUuid + readonly distinctGatewayApplication?: IdentifiedEntryIdentityInput + readonly recordingDevice?: IdentifiedEntryIdentityInput +} + +const categoryFor = (kind: ConnectedMeasurement['kind']) => { + if (VITAL_SIGNS.has(kind)) { + return [concept(SYSTEMS.observationCategory, 'vital-signs', 'Vital Signs')] + } + return [concept(SYSTEMS.observationCategory, 'activity', 'Activity')] +} + +const profileFor = (kind: ConnectedMeasurement['kind']): readonly string[] => { + const definition = sharedMobileMeasurementCatalog[kind] + return [ + groveProviderProfileCanonicals[definition.profile], + PROFILES.providerObservation, + ] +} + +// A measurement with a methodChoice states its aggregation per adapter source element, +// because the same shared meaning is a daily mean for one provider and a session +// statistic for another. +const declaredAggregationMethods = new Map() +for (const provider of providerAdapterCatalog.providers) { + for (const sourceType of provider.sourceTypes) { + for (const element of sourceType.elements) { + if (!('aggregationMethod' in element)) { + continue + } + for (const [kind, method] of Object.entries(element.aggregationMethod)) { + declaredAggregationMethods.set( + `${provider.id}|${sourceType.token}|${kind}`, + method, + ) + } + } + } +} + +/** The aggregation a measurement carries, or `undefined` when the catalog declares none. */ +const aggregationMethodFor = ( + input: ProviderMeasurementBundleInput, + kind: ConnectedMeasurement['kind'], +): CodeableConcept | undefined => { + const definition = sharedMobileMeasurementCatalog[kind] + if ('method' in definition) { + return concept( + SYSTEMS.groveAggregationMethod, + definition.method.code, + definition.method.display, + ) + } + if (!('methodChoice' in definition)) { + return undefined + } + const declared = declaredAggregationMethods.get( + `${input.source.adapter.provider}|${input.source.sourceType}|${kind}`, + ) + return declared === undefined ? undefined : ( + concept(SYSTEMS.groveAggregationMethod, declared) + ) +} + +/** + * A measurement whose catalog entry admits more than one aggregation must have one declared for + * this exact provider and source type, otherwise the emitted Observation would silently omit the + * method its profile requires. + */ +const missingAggregationMethod = ( + input: ProviderMeasurementBundleInput, + kind: ConnectedMeasurement['kind'], +): boolean => + 'methodChoice' in sharedMobileMeasurementCatalog[kind] && + aggregationMethodFor(input, kind) === undefined + +const effectiveFor = (measurement: ConnectedMeasurement) => + measurement.effective.kind === 'date-time' ? + { effectiveDateTime: measurement.effective.value } + : { + effectivePeriod: { + start: measurement.effective.start, + end: measurement.effective.end, + }, + } + +const resultFor = (measurement: ConnectedMeasurement) => { + if (measurement.kind === 'blood-pressure') { + const definition = sharedMobileMeasurementCatalog['blood-pressure'] + const systolic = definition.components[0] + const diastolic = definition.components[1] + return { + component: [ + { + code: concept(systolic.system, systolic.code), + valueQuantity: quantity(measurement.systolic, systolic.quantity), + }, + { + code: concept(diastolic.system, diastolic.code), + valueQuantity: quantity(measurement.diastolic, diastolic.quantity), + }, + ], + } + } + + const definition = sharedMobileMeasurementCatalog[measurement.kind] + + if (typeof measurement.value === 'string') { + const { resultCodeSystem } = definition as { + readonly resultCodeSystem: string + } + return { + valueCodeableConcept: concept(resultCodeSystem, measurement.value), + } + } + const { quantity: unit } = definition as { + readonly quantity: { + readonly system: string + readonly code: string + readonly unit?: string + } + } + return { + valueQuantity: quantity(measurement.value, unit), + } +} + +const makeObservation = ( + input: ProviderMeasurementBundleInput, + measurement: ConnectedMeasurement, + observationIdentity: IdentifiedEntryIdentityInput, + identities: ResolvedGraphIdentities, + method: CodeableConcept | undefined, +) => { + const definition = sharedMobileMeasurementCatalog[measurement.kind] + const category = categoryFor(measurement.kind) + const extensions = [ + { + url: EXTENSIONS.provider, + valueCode: input.source.adapter.provider, + }, + { + url: EXTENSIONS.providerSourceType, + valueCode: `${input.source.adapter.provider}/${input.source.sourceType}`, + }, + ...(identities.gatewayReference === undefined ? + [] + : [ + { + url: EXTENSIONS.gatewayDevice, + valueReference: { reference: identities.gatewayReference }, + }, + ]), + ...(input.source.recordingMethod === undefined ? + [] + : [ + { + url: EXTENSIONS.recordingMethod, + valueCoding: coding( + SYSTEMS.groveRecordingMethod, + input.source.recordingMethod, + ), + }, + ]), + ...(input.researchStudyReferences ?? []).map((reference) => ({ + url: EXTENSIONS.researchStudy, + valueReference: { reference }, + })), + ] + + return { + resourceType: 'Observation' as const, + ...resourceId(observationIdentity), + meta: { profile: profileFor(measurement.kind) }, + extension: extensions, + // A one-to-one conversion identifies its Observation by the source record, so the entry + // identity is that same pair; listing it twice would repeat one identifier on the resource. + identifier: [ + identifier(identities.sourceRecord), + ...(( + observationIdentity.identifier.system === + identities.sourceRecord.system && + observationIdentity.identifier.value === identities.sourceRecord.value + ) ? + [] + : [identifier(observationIdentity.identifier)]), + ], + status: 'final' as const, + category, + code: { + coding: [coding(definition.code.system, definition.code.code)], + }, + subject: { reference: input.subject }, + ...effectiveFor(measurement), + issued: input.issued, + ...resultFor(measurement), + ...(method === undefined ? {} : { method }), + ...(input.source.recordingDevice === undefined ? + {} + : { device: { reference: identities.recordingDevice?.fullUrl } }), + } +} + +const makeProvenance = ( + input: ProviderMeasurementBundleInput, + identities: ResolvedGraphIdentities, +) => { + return { + resourceType: 'Provenance' as const, + ...resourceId(identities.provenance), + meta: { profile: [PROFILES.providerConversionProvenance] }, + target: identities.observations.map(({ fullUrl }) => ({ + reference: fullUrl, + })), + recorded: input.recorded, + activity: provenanceActivity(), + agent: [assemblerAgent(identities.application.fullUrl)], + entity: [ + { + role: 'source' as const, + what: { + identifier: identifier(identities.sourceRecord), + }, + agent: [sourceEntityAgent(identities.dataOrigin.fullUrl)], + }, + ], + } +} + +const optionalEntryIdentity = ( + input: ResourceIdentityInput | undefined, +): Result => + input === undefined ? + ok(undefined) + : createEntryIdentity(input.identifier, input.id) + +type ResolvedGatewayIdentity = Pick< + ResolvedGraphIdentities, + 'distinctGatewayApplication' | 'gatewayReference' +> + +const resolveGatewayIdentity = ( + input: ProviderMeasurementBundleInput['gatewayApplication'], + converter: IdentifiedEntryIdentityInput, +): Result => { + if (input === undefined) return ok({}) + if (input.kind === 'converter-application') { + return ok({ gatewayReference: converter.fullUrl }) + } + const application = createEntryIdentity( + input.application.identity.identifier, + input.application.identity.id, + ) + if (!application.ok) return application + return ok({ + gatewayReference: application.value.fullUrl, + distinctGatewayApplication: application.value, + }) +} + +const resolveGraphIdentities = ( + input: ProviderMeasurementBundleInput, +): Result => { + const outputDiscriminators = input.measurements.map(({ kind }) => + providerOutputDiscriminator( + input.source.adapter.provider, + input.source.sourceType, + kind, + ), + ) + for (const [index, measurement] of input.measurements.entries()) { + if (!missingAggregationMethod(input, measurement.kind)) { + continue + } + return issues([ + { + severity: 'error', + code: 'unsupported-measurement', + path: ['measurements', index], + message: + `${input.source.adapter.provider}/${input.source.sourceType} declares no aggregation ` + + `for ${measurement.kind}, so its Observation would omit the method its profile requires.`, + }, + ]) + } + if (outputDiscriminators.some((value) => value === undefined)) { + return issues([ + { + severity: 'error', + code: 'unsupported-measurement', + path: ['measurements'], + message: 'No catalog-owned Provider output discriminator exists.', + }, + ]) + } + const connected = deriveProviderIdentities({ + provider: input.source.adapter.provider, + providerAccountIdentifier: input.source.providerAccountIdentifier, + sourceType: input.source.sourceType, + sourceNativeId: input.source.sourceNativeId, + outputDiscriminators: outputDiscriminators as [string, ...string[]], + eventSequence: input.eventSequence, + graphIdentifierSystem: input.graphIdentifierSystem, + }) + if (!connected.ok) return connected + + // A one-to-one conversion mints no output identity, so its single Observation is identified by + // the source record itself. + const outputIdentities = + connected.value.outputs.length > 0 ? + connected.value.outputs + : [connected.value.sourceRecord] + const observations: IdentifiedEntryIdentityInput[] = [] + for (const [index, output] of outputIdentities.entries()) { + const measurement = input.measurements[index] + if (measurement === undefined) { + return issues([ + { + severity: 'error', + code: 'value-mismatch', + path: ['measurements'], + message: 'Provider output identities are incomplete.', + }, + ]) + } + const observation = createEntryIdentity( + output, + input.repositoryIds?.observations?.[measurement.kind], + ) + if (!observation.ok) return observation + observations.push(observation.value) + } + const provenance = createEntryIdentity( + connected.value.conversion, + input.repositoryIds?.provenance, + ) + if (!provenance.ok) return provenance + const application = createEntryIdentity( + input.application.identity.identifier, + input.application.identity.id, + ) + if (!application.ok) return application + const gateway = resolveGatewayIdentity( + input.gatewayApplication, + application.value, + ) + if (!gateway.ok) return gateway + const dataOrigin = createEntryIdentity( + input.source.dataOrigin.identity.identifier, + input.source.dataOrigin.identity.id, + ) + if (!dataOrigin.ok) return dataOrigin + + const recordingDevice = optionalEntryIdentity( + input.source.recordingDevice?.identity, + ) + if (!recordingDevice.ok) return recordingDevice + + const resolved: ResolvedGraphIdentities = { + sourceRecord: connected.value.sourceRecord, + exchange: connected.value.exchange, + observations, + provenance: provenance.value, + application: application.value, + dataOrigin: dataOrigin.value, + ...gateway.value, + ...(recordingDevice.value === undefined ? + {} + : { recordingDevice: recordingDevice.value }), + } + const fullUrls = [ + ...resolved.observations.map(({ fullUrl }) => fullUrl), + resolved.provenance.fullUrl, + resolved.application.fullUrl, + resolved.dataOrigin.fullUrl, + resolved.distinctGatewayApplication?.fullUrl, + resolved.recordingDevice?.fullUrl, + ].filter((value) => value !== undefined) + if (new Set(fullUrls).size !== fullUrls.length) { + return issues([ + { + severity: 'error', + code: 'duplicate-identifier', + path: [], + message: 'Every Bundle entry requires a distinct business identifier.', + }, + ]) + } + return { ok: true, value: resolved } +} + +/** + * Builds one deterministic, profile-stamped R4 resource graph for the non-empty + * subset of catalog-admitted outputs present in a connected-provider source + * record. Every output shares one conversion Provenance. Raw native ids are + * used only as digest input and are never emitted. Times, repository ids, and + * the durable event sequence remain caller-owned; malformed inputs return + * structured issues and no partial graph. + */ +export const buildProviderMeasurementBundle = ( + input: ProviderMeasurementBundleInput, +): Result => { + const parsed = parseProviderMeasurementBundleInput(input) + if (!parsed.ok) return parsed + const validatedInput = parsed.value + + const identities = resolveGraphIdentities(validatedInput) + if (!identities.ok) return identities + + const observationEntries = [] + for (const [index, measurement] of validatedInput.measurements.entries()) { + const identity = identities.value.observations[index] + if (identity === undefined) { + return issues([ + { + severity: 'error', + code: 'value-mismatch', + path: ['measurements', index], + message: 'Provider output identities are incomplete.', + }, + ]) + } + observationEntries.push( + identifiedEntry( + identity, + makeObservation( + validatedInput, + measurement, + identity, + identities.value, + aggregationMethodFor(validatedInput, measurement.kind), + ), + ), + ) + } + const application = makeApplicationDevice(validatedInput.application) + const distinctGatewayApplicationInput = + validatedInput.gatewayApplication?.kind === 'distinct-application' ? + validatedInput.gatewayApplication.application + : undefined + const distinctGatewayApplication = + distinctGatewayApplicationInput === undefined ? undefined : ( + makeApplicationDevice(distinctGatewayApplicationInput) + ) + const recordingDeviceInput = validatedInput.source.recordingDevice + const recordingDevice = + recordingDeviceInput === undefined ? undefined : ( + makeRecordingDevice(recordingDeviceInput) + ) + const dataOrigin = makeApplicationDevice(validatedInput.source.dataOrigin) + const provenance = makeProvenance(validatedInput, identities.value) + const recordingDeviceEntries = + ( + recordingDevice === undefined || + identities.value.recordingDevice === undefined + ) ? + [] + : [identifiedEntry(identities.value.recordingDevice, recordingDevice)] + const entry = [ + ...observationEntries, + ...recordingDeviceEntries, + identifiedEntry(identities.value.dataOrigin, dataOrigin), + ...(( + distinctGatewayApplication === undefined || + identities.value.distinctGatewayApplication === undefined + ) ? + [] + : [ + identifiedEntry( + identities.value.distinctGatewayApplication, + distinctGatewayApplication, + ), + ]), + identifiedEntry(identities.value.application, application), + identifiedEntry(identities.value.provenance, provenance), + ] + + return parseCollectionBundle({ + resourceType: 'Bundle', + ...(validatedInput.repositoryIds?.bundle === undefined ? + {} + : { id: validatedInput.repositoryIds.bundle }), + meta: { profile: [PROFILES.mobileBundle] }, + identifier: identifier(identities.value.exchange), + type: 'collection', + timestamp: validatedInput.recorded, + entry, + }) +} diff --git a/packages/fhir/src/providers/contract.generated.ts b/packages/fhir/src/providers/contract.generated.ts new file mode 100644 index 00000000..50ad29e9 --- /dev/null +++ b/packages/fhir/src/providers/contract.generated.ts @@ -0,0 +1,6471 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// +// Generated by scripts/generate-measurement-catalog.mjs. Do not edit directly. + +/* eslint-disable sonarjs/no-clear-text-protocols */ + +import { deepFreeze } from '../core/index.js' + +export const groveFhirContractVersion = '0.5.0' as const + +export const groveFhirVersion = '4.0.1' as const + +const groveProviderPackageMetadataValue = { + fhirVersion: '4.0.1', + version: '0.5.0', + packageId: 'org.grovealliance.fhir.providers', + canonical: 'https://grovealliance.org/fhir/providers', + dependencies: [ + 'org.grovealliance.fhir.mobile#0.5.0', + 'org.grovealliance.fhir.sensor#0.5.0', + ], +} as const + +export const groveProviderPackageMetadata: typeof groveProviderPackageMetadataValue = + deepFreeze(groveProviderPackageMetadataValue) + +const groveProviderPackageCanonicalsValue = { + mobile: 'https://grovealliance.org/fhir/mobile', + sensor: 'https://grovealliance.org/fhir/sensor', + providers: 'https://grovealliance.org/fhir/providers', +} as const + +export const groveProviderPackageCanonicals: typeof groveProviderPackageCanonicalsValue = + deepFreeze(groveProviderPackageCanonicalsValue) + +const groveProviderProfileCanonicalsValue = { + 'grove-mobile-active-energy': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-active-energy', + 'healthkit-apple-exercise-time': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-exercise-time', + 'healthkit-apple-move-time': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-move-time', + 'healthkit-apple-stand-hour': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-stand-hour', + 'healthkit-apple-stand-time': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-stand-time', + 'healthkit-atrial-fibrillation-burden': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-atrial-fibrillation-burden', + 'healthkit-audiogram-panel': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-audiogram-panel', + 'grove-mobile-basal-body-temperature': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-basal-body-temperature', + 'grove-mobile-basal-energy': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-basal-energy', + 'health-connect-basal-metabolic-rate': + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-basal-metabolic-rate', + 'healthkit-bladder-incontinence': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-bladder-incontinence', + 'healthkit-bleeding-after-pregnancy': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-bleeding-after-pregnancy', + 'healthkit-bleeding-during-pregnancy': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-bleeding-during-pregnancy', + 'healthkit-blood-alcohol-content': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-blood-alcohol-content', + 'grove-mobile-blood-glucose-unspecified-specimen': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-blood-glucose-unspecified-specimen', + 'grove-mobile-blood-pressure': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-blood-pressure', + 'healthkit-blood-type': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-blood-type', + 'provider-body-fat-mass': + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-body-fat-mass', + 'grove-mobile-body-fat-percentage': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-fat-percentage', + 'grove-mobile-body-height': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-height', + 'grove-mobile-body-temperature': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-temperature', + 'grove-mobile-body-water-mass': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-water-mass', + 'grove-mobile-body-weight': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-weight', + 'grove-mobile-bone-mass': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-bone-mass', + 'grove-mobile-cervical-mucus-quality': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-cervical-mucus-quality', + 'healthkit-contraceptive-use': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-contraceptive-use', + 'grove-mobile-cycling-cadence': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-cycling-cadence', + 'healthkit-cycling-functional-threshold-power': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-cycling-functional-threshold-power', + 'grove-mobile-deep-sleep-duration': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-deep-sleep-duration', + 'grove-mobile-dietary-biotin': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-biotin', + 'grove-mobile-dietary-caffeine': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-caffeine', + 'grove-mobile-dietary-calcium': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-calcium', + 'grove-mobile-dietary-carbohydrates': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-carbohydrates', + 'grove-mobile-dietary-chloride': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-chloride', + 'grove-mobile-dietary-cholesterol': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-cholesterol', + 'grove-mobile-dietary-chromium': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-chromium', + 'grove-mobile-dietary-copper': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-copper', + 'grove-mobile-dietary-energy': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-energy', + 'health-connect-dietary-energy-from-fat': + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-dietary-energy-from-fat', + 'grove-mobile-dietary-fat-monounsaturated': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-fat-monounsaturated', + 'grove-mobile-dietary-fat-polyunsaturated': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-fat-polyunsaturated', + 'grove-mobile-dietary-fat-saturated': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-fat-saturated', + 'grove-mobile-dietary-fat-total': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-fat-total', + 'health-connect-dietary-fat-trans': + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-dietary-fat-trans', + 'health-connect-dietary-fat-unsaturated': + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-dietary-fat-unsaturated', + 'grove-mobile-dietary-fiber': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-fiber', + 'grove-mobile-dietary-folate': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-folate', + 'health-connect-dietary-folic-acid': + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-dietary-folic-acid', + 'grove-mobile-dietary-iodine': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-iodine', + 'grove-mobile-dietary-iron': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-iron', + 'grove-mobile-dietary-magnesium': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-magnesium', + 'grove-mobile-dietary-manganese': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-manganese', + 'grove-mobile-dietary-molybdenum': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-molybdenum', + 'grove-mobile-dietary-niacin': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-niacin', + 'grove-mobile-dietary-pantothenic-acid': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-pantothenic-acid', + 'grove-mobile-dietary-phosphorus': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-phosphorus', + 'grove-mobile-dietary-potassium': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-potassium', + 'grove-mobile-dietary-protein': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-protein', + 'grove-mobile-dietary-riboflavin': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-riboflavin', + 'grove-mobile-dietary-selenium': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-selenium', + 'grove-mobile-dietary-sodium': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-sodium', + 'grove-mobile-dietary-sugar': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-sugar', + 'grove-mobile-dietary-thiamin': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-thiamin', + 'grove-mobile-dietary-vitamin-a': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-vitamin-a', + 'grove-mobile-dietary-vitamin-b12': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-vitamin-b12', + 'grove-mobile-dietary-vitamin-b6': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-vitamin-b6', + 'grove-mobile-dietary-vitamin-c': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-vitamin-c', + 'grove-mobile-dietary-vitamin-d': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-vitamin-d', + 'grove-mobile-dietary-vitamin-e': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-vitamin-e', + 'grove-mobile-dietary-vitamin-k': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-vitamin-k', + 'grove-mobile-dietary-zinc': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-dietary-zinc', + 'grove-mobile-distance': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-distance', + 'grove-mobile-electrodermal-activity': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-electrodermal-activity', + 'health-connect-elevation-gained': + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-elevation-gained', + 'healthkit-environmental-audio-exposure': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-environmental-audio-exposure', + 'healthkit-environmental-audio-exposure-notification': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-environmental-audio-exposure-notification', + 'healthkit-environmental-sound-reduction': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-environmental-sound-reduction', + 'provider-extracellular-water-mass': + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-extracellular-water-mass', + 'grove-mobile-flights-climbed': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-flights-climbed', + 'grove-mobile-fluid-intake': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-fluid-intake', + 'healthkit-food-correlation': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-food-correlation', + 'healthkit-forced-expiratory-volume-1': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-forced-expiratory-volume-1', + 'healthkit-forced-vital-capacity': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-forced-vital-capacity', + 'healthkit-gad7-assessment': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-gad7-assessment', + 'healthkit-handwashing-session': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-handwashing-session', + 'healthkit-headphone-audio-exposure': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-headphone-audio-exposure', + 'healthkit-headphone-audio-exposure-notification': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-headphone-audio-exposure-notification', + 'grove-mobile-heart-rate': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate', + 'healthkit-heart-rate-recovery-one-minute': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-heart-rate-recovery-one-minute', + 'grove-mobile-heart-rate-variability-rmssd': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate-variability-rmssd', + 'grove-mobile-heart-rate-variability-sdnn': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate-variability-sdnn', + 'healthkit-high-heart-rate-notification': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-high-heart-rate-notification', + 'healthkit-hypertension-notification': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-hypertension-notification', + 'healthkit-infrequent-menstrual-cycles': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-infrequent-menstrual-cycles', + 'healthkit-inhaler-usage': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-inhaler-usage', + 'healthkit-insulin-delivery': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-insulin-delivery', + 'grove-mobile-intermenstrual-bleeding': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-intermenstrual-bleeding', + 'provider-intracellular-water-mass': + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-intracellular-water-mass', + 'healthkit-irregular-heart-rhythm-notification': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-irregular-heart-rhythm-notification', + 'healthkit-irregular-menstrual-cycles': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-irregular-menstrual-cycles', + 'healthkit-lactation-status': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-lactation-status', + 'grove-mobile-lean-body-mass': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-lean-body-mass', + 'grove-mobile-light-sleep-duration': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-light-sleep-duration', + 'healthkit-low-cardio-fitness-notification': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-low-cardio-fitness-notification', + 'healthkit-low-heart-rate-notification': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-low-heart-rate-notification', + 'grove-mobile-menstruation-flow': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-menstruation-flow', + 'health-connect-menstruation-period': + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-menstruation-period', + 'grove-mobile-mindfulness-session': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-mindfulness-session', + 'provider-muscle-mass': + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-muscle-mass', + 'healthkit-number-of-alcoholic-beverages': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-number-of-alcoholic-beverages', + 'healthkit-number-of-times-fallen': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-number-of-times-fallen', + 'grove-mobile-ovulation-test-result': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-ovulation-test-result', + 'grove-mobile-oxygen-saturation': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-oxygen-saturation', + 'grove-mobile-oxygen-saturation-daily-average': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-oxygen-saturation-daily-average', + 'healthkit-peak-expiratory-flow-rate': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-peak-expiratory-flow-rate', + 'healthkit-peripheral-perfusion-index': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-peripheral-perfusion-index', + 'healthkit-persistent-intermenstrual-bleeding': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-persistent-intermenstrual-bleeding', + 'healthkit-phq9-assessment': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-phq9-assessment', + 'healthkit-physical-effort': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-physical-effort', + 'grove-mobile-power': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-power', + 'healthkit-pregnancy-status': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-pregnancy-status', + 'healthkit-pregnancy-test-result': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-pregnancy-test-result', + 'healthkit-progesterone-test-result': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-progesterone-test-result', + 'healthkit-prolonged-menstrual-periods': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-prolonged-menstrual-periods', + 'grove-mobile-rem-sleep-duration': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-rem-sleep-duration', + 'grove-mobile-respiratory-rate': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-respiratory-rate', + 'grove-mobile-respiratory-rate-average': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-respiratory-rate-average', + 'grove-mobile-resting-heart-rate': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-resting-heart-rate', + 'healthkit-running-ground-contact-time': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-running-ground-contact-time', + 'healthkit-running-stride-length': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-running-stride-length', + 'healthkit-running-vertical-oscillation': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-running-vertical-oscillation', + 'grove-mobile-sexual-activity': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-sexual-activity', + 'healthkit-six-minute-walk-test-distance': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-six-minute-walk-test-distance', + 'grove-mobile-skin-temperature': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-skin-temperature', + 'healthkit-sleep-apnea-notification': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-sleep-apnea-notification', + 'grove-mobile-sleep-awake-duration': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-sleep-awake-duration', + 'grove-mobile-sleep-duration': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-sleep-duration', + 'grove-mobile-sleep-heart-rate': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-sleep-heart-rate', + 'grove-mobile-sleep-stage': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-sleep-stage', + 'healthkit-sleeping-breathing-disturbances': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-sleeping-breathing-disturbances', + 'provider-sleeping-heart-rate-average': + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-sleeping-heart-rate-average', + 'grove-mobile-speed': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-speed', + 'healthkit-stair-ascent-speed': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-stair-ascent-speed', + 'healthkit-stair-descent-speed': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-stair-descent-speed', + 'healthkit-state-of-mind': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-state-of-mind', + 'health-connect-step-cadence': + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-step-cadence', + 'grove-mobile-step-count': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-step-count', + 'healthkit-swimming-stroke-count': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-swimming-stroke-count', + 'healthkit-symptom-abdominal-cramps': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-abdominal-cramps', + 'healthkit-symptom-acne': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-acne', + 'healthkit-symptom-appetite-changes': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-appetite-changes', + 'healthkit-symptom-bloating': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-bloating', + 'healthkit-symptom-breast-pain': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-breast-pain', + 'healthkit-symptom-chest-tightness-or-pain': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-chest-tightness-or-pain', + 'healthkit-symptom-chills': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-chills', + 'healthkit-symptom-constipation': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-constipation', + 'healthkit-symptom-coughing': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-coughing', + 'healthkit-symptom-diarrhea': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-diarrhea', + 'healthkit-symptom-dizziness': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-dizziness', + 'healthkit-symptom-dry-skin': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-dry-skin', + 'healthkit-symptom-fainting': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-fainting', + 'healthkit-symptom-fatigue': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-fatigue', + 'healthkit-symptom-fever': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-fever', + 'healthkit-symptom-generalized-body-ache': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-generalized-body-ache', + 'healthkit-symptom-hair-loss': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-hair-loss', + 'healthkit-symptom-headache': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-headache', + 'healthkit-symptom-heartburn': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-heartburn', + 'healthkit-symptom-hot-flashes': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-hot-flashes', + 'healthkit-symptom-loss-of-smell': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-loss-of-smell', + 'healthkit-symptom-loss-of-taste': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-loss-of-taste', + 'healthkit-symptom-lower-back-pain': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-lower-back-pain', + 'healthkit-symptom-memory-lapse': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-memory-lapse', + 'healthkit-symptom-mood-changes': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-mood-changes', + 'healthkit-symptom-nausea': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-nausea', + 'healthkit-symptom-night-sweats': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-night-sweats', + 'healthkit-symptom-pelvic-pain': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-pelvic-pain', + 'healthkit-symptom-rapid-pounding-or-fluttering-heartbeat': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-rapid-pounding-or-fluttering-heartbeat', + 'healthkit-symptom-runny-nose': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-runny-nose', + 'healthkit-symptom-shortness-of-breath': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-shortness-of-breath', + 'healthkit-symptom-sinus-congestion': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-sinus-congestion', + 'healthkit-symptom-skipped-heartbeat': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-skipped-heartbeat', + 'healthkit-symptom-sleep-changes': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-sleep-changes', + 'healthkit-symptom-sore-throat': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-sore-throat', + 'healthkit-symptom-vomiting': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-vomiting', + 'healthkit-symptom-wheezing': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-wheezing', + 'healthkit-time-in-daylight': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-time-in-daylight', + 'healthkit-toothbrushing-session': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-toothbrushing-session', + 'health-connect-total-energy': + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-total-energy', + 'healthkit-underwater-depth': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-underwater-depth', + 'healthkit-uv-exposure': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-uv-exposure', + 'healthkit-vaginal-dryness': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-vaginal-dryness', + 'grove-mobile-vo2-max': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-vo2-max', + 'healthkit-waist-circumference': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-waist-circumference', + 'healthkit-walking-asymmetry': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-asymmetry', + 'healthkit-walking-double-support': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-double-support', + 'healthkit-walking-heart-rate-average': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-heart-rate-average', + 'healthkit-walking-speed': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-speed', + 'healthkit-walking-steadiness': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-steadiness', + 'healthkit-walking-steadiness-notification': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-steadiness-notification', + 'healthkit-walking-step-length': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-step-length', + 'healthkit-water-temperature': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-water-temperature', + 'grove-mobile-wheelchair-push-count': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-wheelchair-push-count', + 'healthkit-wheelchair-use': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-wheelchair-use', + 'grove-mobile-workout': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-workout', + 'healthkit-workout-effort-score': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-workout-effort-score', + 'grove-mobile-workout-segment': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-workout-segment', + 'grove-application-device': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device', + 'grove-mobile-exchange-bundle': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle', + 'grove-recording-device': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-device', + 'grove-sensor-recording-document': + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + 'provider-conversion-provenance': + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance', + 'provider-observation': + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation', + 'provider-recording-document': + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document', +} as const + +export const groveProviderProfileCanonicals: typeof groveProviderProfileCanonicalsValue = + deepFreeze(groveProviderProfileCanonicalsValue) + +const providerAdapterCatalogValue = { + schemaVersion: 1, + fhirVersion: '4.0.1', + version: '0.5.0', + packageId: 'org.grovealliance.fhir.providers', + canonical: 'https://grovealliance.org/fhir/providers', + adapterProfile: + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation', + sourceTypeExtension: { + url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type', + codeSystem: + 'https://grovealliance.org/fhir/providers/CodeSystem/provider-source-type', + r4Element: + 'Observation.extension.valueCode or DocumentReference.extension.valueCode', + cardinality: 'exactly one', + codeRule: + "provider id + '/' + exact source token; the atomic Withings blood-pressure output uses withings/getmeas:9+10", + rule: 'Every admitted output states the exact catalog dispatch token in its own identifier value.', + }, + recordingDocument: { + sourceNeutralProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + adapterProfile: + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document', + claimMode: 'exactly-source-neutral-plus-adapter', + outputDiscriminator: 'native-recording', + encoding: 'caller-supplied exact already-obtained provider bytes', + }, + rawPayloadAdmission: { + contract: + 'https://grovealliance.org/fhir/sensor#recording-document.payloadAdmission', + allowedAssertions: [ + 'caller-authorized-opaque-payload', + 'verified-sanitized-input', + ], + failureRule: + 'The producer fails closed before emission unless the caller supplies exactly one assertion.', + notFHIRAuthorization: true, + }, + conversionProvenanceProfile: + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance', + statusVocabulary: [ + 'supported', + 'mapped-standard', + 'platform-exclusive', + 'unmodeled', + 'deferred', + 'intentionally-unsupported', + ], + statusDefinitions: { + supported: + 'Version 0.5.0 defines an exact conversion to every shared profile listed for the source element.', + 'mapped-standard': + 'Version 0.5.0 admits only the listed source-neutral Sensor recording contract and does not assert a scalar clinical result.', + 'platform-exclusive': + 'Version 0.5.0 defines the listed provider-scoped structured profile because no exact shared profile exists.', + unmodeled: + 'The source element is inventoried, but no shared or provider-scoped profile models it in version 0.5.0 and no output is admitted.', + deferred: + 'A plausible shared mapping is known, but the source evidence, time semantics, or result shape is insufficient for a conformant version 0.5.0 output.', + 'intentionally-unsupported': + 'Version 0.5.0 deliberately refuses a normalized conversion because doing so would be misleading or diagnostic-adjacent.', + }, + sourceEvidence: { + accessed: '2026-08-20', + scope: + 'Exact provider data-type tokens and consumed fields frozen in providers[].sourceTypes; conversion operates only on already-obtained payloads and defines no provider fetching.', + tokenBinding: + 'Each providers[].sourceTypes[].token and elements[].path is part of the exact v0.5.0 consumed source surface.', + providers: [ + { + id: 'google-health-api', + surface: 'Google Health API REST', + version: 'v4', + documentation: [ + 'https://developers.google.com/health/data-types', + 'https://developers.google.com/health/reference/rest/v4/users.dataTypes.dataPoints', + ], + }, + { + id: 'oura', + surface: 'Oura API', + version: '2.0', + documentation: ['https://cloud.ouraring.com/v2/docs'], + }, + { + id: 'withings', + surface: 'Withings Health Data API', + version: '2.0', + documentation: [ + 'https://developer.withings.com/api-reference/', + 'https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/all-available-health-data/', + ], + }, + ], + }, + identity: { + composition: { + form: 'v1:|...', + versionPrefix: 'v1', + separator: '|', + rule: "Components join with a vertical bar in the order the composition lists them, behind the scheme version. No component may be empty or contain a vertical bar. Every component is the source's own text, unaltered: nothing is hashed, truncated, normalized, or re-encoded.", + passThrough: + 'A provider whose native record key is unique across the whole provider carries that key verbatim, with no scheme version and no components joined to it: the system already says which provider and which key space it belongs to. Composition is used only where the key alone would not identify one record, which is why an account-scoped provider carries its account pseudonym.', + }, + identifierScope: { + meaning: + "Whether a provider's native record key is unique across the whole provider, or only within one account. A provider scoped to `account` carries its deployment-scoped account pseudonym as a component, so the identifier still stands alone and a receiver deduplicates by comparing system and value with no side channel.", + default: 'account', + relaxationRule: + "A provider moves to `none` only when the vendor documents that its native record key is unique across all accounts. Inference from the shape of an observed value is not sufficient, because the failure it guards against is silently merging two participants' measurements.", + }, + providerAccountIdentifier: { + requirement: + 'A complete deployment-scoped pseudonymous Identifier pair. It must be stable for the provider-account scope needed for deduplication.', + prohibitedByDefault: [ + 'vendor email address', + 'vendor account id', + 'vendor member id', + 'OAuth subject or token', + ], + exception: + 'A raw vendor account value may be used only when an explicit deployment privacy policy authorizes that disclosure; this package grants no such authorization.', + emission: + "For a provider scoped to `account`, the pseudonymous value is a component of the source-record identifier. It is a deployment-minted pseudonym, never a vendor-side account key, and it discloses nothing the resource's own subject reference does not already carry.", + }, + sourceNativeId: { + requirement: + 'The exact stable native record or group key supplied by the already-obtained provider payload.', + emission: + 'Carried in the identifier value as the provider supplied it. It is a vendor row key, not participant data.', + absent: + 'A provider source type that supplies no native key uses the contentDerived strategy below.', + }, + sourceRecord: { + system: + 'https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id', + composition: [ + 'providerCode', + 'providerAccountPseudonym', + 'sourceType', + 'sourceNativeId', + ], + appliesWhen: 'identifierScope is account', + passThroughWhen: + 'identifierScope is none: the value is sourceNativeId verbatim.', + }, + contentDerived: { + whenApplicable: + 'A provider source type that supplies no stable native record key.', + composition: [ + 'providerCode', + 'providerAccountPseudonym', + 'sourceType', + 'content', + 'startInstant', + 'endInstant', + 'measuredValue', + ], + discriminator: 'content', + accountScopeRequired: true, + accountScopeReason: + 'Two participants can record identical values over the same window, so content alone collides across people. The account pseudonym is what keeps them apart and must never be omitted for this strategy.', + revisionLimitation: + 'A corrected value or a corrected time yields a different identifier, so a correction appears as a new Observation rather than a revision. The `content` discriminator makes that visible to a receiver rather than leaving it to be inferred per provider.', + rationale: + 'The alternative of keying on the time window alone would survive a value correction but silently merge two distinct measurements sharing a window. A missed deduplication is recoverable; a false merge is not.', + }, + writerRecord: { + system: + 'https://grovealliance.org/fhir/mobile/NamingSystem/grove-writer-record-id', + composition: ['providerCode', 'sourceNativeId'], + note: 'Carries no account component on any provider, so it can match the identical measurement arriving through HealthKit or Health Connect. A receiver must not assume the match holds: nothing obliges a vendor to use one identifier space across its app and its API.', + }, + output: { + system: + 'https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id', + composition: ['', 'outputDiscriminator'], + outputDiscriminatorRule: { + ordinarySupportedMeasurement: + 'the exact measurementId string from the supported element mapping', + groupedMapping: + 'the exact outputDiscriminator declared on that groupedMappings row', + mappedStandardRaw: 'native-recording', + noFallback: true, + }, + cardinality: '0..1', + appliesWhen: + 'Only where one source record yields more than one Observation. A one-to-one conversion carries the source-record identifier alone; a second namespace repeating the same value identifies nothing new.', + }, + deploymentOwnedIdentities: { + rule: "The conversion Provenance and the exchange Bundle are created by the export, not read from the provider, so they are identified in the deployment's own namespace. This retires provider-conversion-id and provider-exchange-id.", + roles: ['conversion-provenance', 'exchange-bundle'], + value: '||', + }, + resourceIdPolicy: + 'Resource.id is optional and repository-assigned; no business identifier is copied into Resource.id.', + positiveEventSequence: + 'A durable positive event sequence written as a non-zero ASCII digit followed by zero or more ASCII digits; no sign, no leading zero, no other characters. A byte-identical retry reuses the sequence; changed content allocates a higher value.', + singleConversionRule: + "A conversion Provenance has exactly one source-record pair. When a Bundle carries exactly one conversion event, the exchange event is that conversion's emission and reuses its sequence, so the exchange identifier value equals the conversion identifier value byte for byte and only the NamingSystem distinguishes them.", + vectors: [ + { + role: 'sourceRecord', + scope: 'account', + components: ['withings', 'acct-7f3a9c', 'measure', '17348211'], + identifierValue: 'v1:withings|acct-7f3a9c|measure|17348211', + }, + { + role: 'sourceRecord', + scope: 'none', + components: ['oura', 'sleep', '8f9a5221-639e-4a85-81cb-4065ef23f979'], + identifierValue: 'v1:oura|sleep|8f9a5221-639e-4a85-81cb-4065ef23f979', + }, + { + role: 'sourceRecord', + scope: 'account', + components: [ + 'withings', + 'acct-7f3a9c', + 'heart-rate', + 'hr-2026-08-19-001', + ], + identifierValue: 'v1:withings|acct-7f3a9c|heart-rate|hr-2026-08-19-001', + }, + { + role: 'output', + scope: 'account', + components: [ + 'withings', + 'acct-7f3a9c', + 'measure', + '17348211', + 'body-weight', + ], + identifierValue: 'v1:withings|acct-7f3a9c|measure|17348211|body-weight', + }, + { + role: 'output', + scope: 'account', + components: [ + 'withings', + 'acct-7f3a9c', + 'heart-rate', + 'hr-2026-08-19-001', + 'native-recording', + ], + identifierValue: + 'v1:withings|acct-7f3a9c|heart-rate|hr-2026-08-19-001|native-recording', + }, + { + role: 'contentDerived', + scope: 'account', + components: [ + 'google-health-api', + 'acct-7f3a9c', + 'steps', + 'content', + '2026-08-19T00:00:00Z', + '2026-08-20T00:00:00Z', + '8412', + ], + identifierValue: + 'v1:google-health-api|acct-7f3a9c|steps|content|2026-08-19T00:00:00Z|2026-08-20T00:00:00Z|8412', + }, + { + role: 'writerRecord', + components: ['withings', '17348211'], + identifierValue: 'v1:withings|17348211', + note: "Identical to the value the same measurement carries when it arrives through HealthKit or Health Connect, where the writer slot is the application's own bundle or package name.", + }, + { + role: 'deploymentOwnedExchangeBundle', + components: ['withings', '1', 'exchange-bundle'], + identifierValue: 'withings|1|exchange-bundle', + note: "Minted in the deployment's namespace, so it carries no scheme-version prefix from this guide.", + }, + { + role: 'rejectedSeparatorInComponent', + components: ['withings', 'acct-7f3a9c', 'measure', '173|48211'], + identifierValue: null, + note: 'A native key containing a vertical bar is rejected, never escaped.', + }, + ], + }, + providers: [ + { + id: 'google-health-api', + title: 'Google Health API', + sourceTypeCount: 18, + sourceTypes: [ + { + token: 'steps', + status: 'supported', + elements: [ + { + path: 'steps.count', + status: 'supported', + measurementIds: ['step-count'], + unitConversion: 'integer count to UCUM {steps}', + effective: 'steps.interval', + }, + ], + }, + { + token: 'distance', + status: 'supported', + elements: [ + { + path: 'distance.millimeters', + status: 'supported', + measurementIds: ['distance'], + unitConversion: 'millimetres divided by 1000 to UCUM m', + effective: 'distance.interval', + }, + ], + }, + { + token: 'active-energy-burned', + status: 'supported', + elements: [ + { + path: 'activeEnergyBurned.kcal', + status: 'supported', + measurementIds: ['active-energy'], + unitConversion: 'identity UCUM kcal', + effective: 'activeEnergyBurned.interval', + }, + ], + }, + { + token: 'weight', + status: 'supported', + elements: [ + { + path: 'weight.weightGrams', + status: 'supported', + measurementIds: ['body-weight'], + unitConversion: 'grams divided by 1000 to UCUM kg', + effective: 'weight.sampleTime.physicalTime', + }, + ], + }, + { + token: 'body-fat', + status: 'supported', + elements: [ + { + path: 'bodyFat.percentage', + status: 'supported', + measurementIds: ['body-fat-percentage'], + }, + ], + }, + { + token: 'height', + status: 'supported', + elements: [ + { + path: 'height.heightMillimeters', + status: 'supported', + measurementIds: ['body-height'], + unitConversion: 'millimetres divided by 10 to UCUM cm', + effective: 'height.sampleTime.physicalTime', + }, + ], + }, + { + token: 'vo2-max', + status: 'supported', + elements: [ + { + path: 'vo2Max.vo2Max', + status: 'supported', + measurementIds: ['vo2-max'], + }, + ], + }, + { + token: 'daily-oxygen-saturation', + status: 'supported', + elements: [ + { + path: 'dailyOxygenSaturation.averagePercentage', + status: 'supported', + measurementIds: ['oxygen-saturation-daily-average'], + }, + ], + }, + { + token: 'heart-rate-variability', + status: 'supported', + elements: [ + { + path: 'heartRateVariability.rootMeanSquareOfSuccessiveDifferencesMilliseconds', + status: 'supported', + measurementIds: ['heart-rate-variability-rmssd'], + }, + { + path: 'heartRateVariability.standardDeviationMilliseconds', + status: 'supported', + measurementIds: [ + 'heart-rate-variability-rmssd', + 'heart-rate-variability-sdnn', + ], + }, + ], + }, + { + token: 'daily-resting-heart-rate', + status: 'supported', + elements: [ + { + path: 'dailyRestingHeartRate.beatsPerMinute', + status: 'supported', + measurementIds: ['resting-heart-rate'], + }, + ], + }, + { + token: 'daily-respiratory-rate', + status: 'supported', + elements: [ + { + path: 'dailyRespiratoryRate.breathsPerMinute', + status: 'supported', + measurementIds: ['respiratory-rate-average'], + aggregationMethod: { + 'respiratory-rate-average': 'daily-mean', + }, + }, + ], + }, + { + token: 'blood-glucose', + status: 'supported', + elements: [ + { + path: 'bloodGlucose.bloodGlucoseMilligramsPerDeciliter', + status: 'supported', + measurementIds: ['blood-glucose-unspecified-specimen'], + }, + ], + }, + { + token: 'core-body-temperature', + status: 'supported', + elements: [ + { + path: 'coreBodyTemperature.temperatureCelsius', + status: 'supported', + measurementIds: ['body-temperature'], + unitConversion: 'identity UCUM Cel', + effective: 'coreBodyTemperature.sampleTime.physicalTime', + }, + ], + }, + { + token: 'floors', + status: 'supported', + elements: [ + { + path: 'floors.count', + status: 'supported', + measurementIds: ['flights-climbed'], + }, + ], + }, + { + token: 'basal-energy-burned', + status: 'supported', + elements: [ + { + path: 'basalEnergyBurned.kcal', + status: 'supported', + measurementIds: ['basal-energy'], + }, + ], + }, + { + token: 'sleep', + status: 'supported', + elements: [ + { + path: 'sleep.summary.minutesAsleep', + status: 'supported', + measurementIds: ['sleep-duration'], + unitConversion: 'minutes divided by 60 to UCUM h', + effective: 'sleep.interval', + }, + { + path: 'sleep.summary.stagesSummary[type=DEEP].minutes', + status: 'supported', + measurementIds: ['deep-sleep-duration'], + }, + { + path: 'sleep.summary.stagesSummary[type=REM].minutes', + status: 'supported', + measurementIds: ['rem-sleep-duration'], + }, + { + path: 'sleep.summary.stagesSummary[type=LIGHT].minutes', + status: 'supported', + measurementIds: ['light-sleep-duration'], + }, + { + path: 'sleep.summary.stagesSummary[type=AWAKE].minutes', + status: 'supported', + measurementIds: ['sleep-awake-duration'], + }, + ], + }, + { + token: 'exercise', + status: 'supported', + elements: [ + { + path: 'exercise.interval', + status: 'supported', + measurementIds: ['workout'], + }, + ], + }, + { + token: 'heart-rate', + status: 'mapped-standard', + archiveDataType: 'heartRate', + raw: { + sourceNeutralProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + adapterProfile: + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document', + outputDiscriminator: 'native-recording', + }, + elements: [ + { + path: 'payload', + status: 'mapped-standard', + sensorProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + reason: + 'The source points may be irregular; retain the provider-native recording rather than resample or invent a uniform SampledData period.', + }, + ], + }, + ], + identifierScope: 'account', + identifierScopeReason: + 'The dataPoint segment of the record name is documented as unique only within a user and a dataType, and may be client-provided, so two accounts can hold the same key. Most Google data types carry no record key at all and use the contentDerived strategy.', + }, + { + id: 'oura', + title: 'Oura', + sourceTypeCount: 8, + sourceTypes: [ + { + token: 'daily_activity', + status: 'supported', + elements: [ + { + path: 'steps', + status: 'supported', + measurementIds: ['step-count'], + unitConversion: 'integer count to UCUM {steps}', + effective: + 'the source civil day represented as a complete day Period; midpoint substitution is forbidden', + }, + { + path: 'active_calories', + status: 'supported', + measurementIds: ['active-energy'], + unitConversion: 'identity UCUM kcal', + effective: + 'the source civil day represented as a complete day Period; midpoint substitution is forbidden', + }, + { + path: 'equivalent_walking_distance', + status: 'supported', + measurementIds: ['distance'], + unitConversion: 'identity UCUM m', + effective: + 'the source civil day represented as a complete day Period; midpoint substitution is forbidden', + }, + ], + }, + { + token: 'sleep', + status: 'supported', + elements: [ + { + path: 'total_sleep_duration', + status: 'supported', + measurementIds: ['sleep-duration'], + unitConversion: 'seconds divided by 3600 to UCUM h', + effective: + 'bedtime_start through bedtime_end; output deferred when either boundary is absent', + }, + { + path: 'deep_sleep_duration', + status: 'supported', + measurementIds: ['deep-sleep-duration'], + }, + { + path: 'rem_sleep_duration', + status: 'supported', + measurementIds: ['rem-sleep-duration'], + }, + { + path: 'light_sleep_duration', + status: 'supported', + measurementIds: ['light-sleep-duration'], + }, + { + path: 'awake_time', + status: 'supported', + measurementIds: ['sleep-awake-duration'], + }, + { + path: 'lowest_heart_rate', + status: 'supported', + measurementIds: ['sleep-heart-rate'], + aggregationMethod: { + 'sleep-heart-rate': 'session-minimum', + }, + }, + { + path: 'average_hrv', + status: 'unmodeled', + reason: + 'Oura reports this as an RMSSD average over the sleep session, which the shared heart-rate-variability-rmssd measurement represents. Unmodelled only until the session-window effective rule for it is settled.', + }, + { + path: 'average_breath', + status: 'supported', + measurementIds: ['respiratory-rate-average'], + aggregationMethod: { + 'respiratory-rate-average': 'session-mean', + }, + }, + ], + }, + { + token: 'daily_spo2', + status: 'supported', + elements: [ + { + path: 'spo2_percentage.average', + status: 'supported', + measurementIds: ['oxygen-saturation-daily-average'], + }, + ], + }, + { + token: 'workout', + status: 'supported', + elements: [ + { + path: 'start_datetime/end_datetime', + status: 'supported', + measurementIds: ['workout'], + }, + ], + }, + { + token: 'vO2_max', + status: 'supported', + elements: [ + { + path: 'vo2_max', + status: 'supported', + measurementIds: ['vo2-max'], + }, + ], + }, + { + token: 'daily_cardiovascular_age', + status: 'intentionally-unsupported', + elements: [ + { + path: 'vascular_age', + status: 'intentionally-unsupported', + reason: + "Although two providers report it, each value is the output of an undisclosed proprietary algorithm over different inputs (Withings pulse-wave velocity versus Oura PPG features), so the numbers are not comparable and no shared physiological definition exists; normalizing them under one code would fabricate clinical comparability, and the diagnostic-adjacent 'age' framing makes a shared measurement unsafe. Both provider catalog rows already record this refusal.", + }, + ], + }, + { + token: 'daily_readiness', + status: 'intentionally-unsupported', + elements: [ + { + path: 'score', + status: 'intentionally-unsupported', + reason: + "A proprietary vendor score with no physiologic unit is not a comparable measurement; normalizing it as a dimensionless quantity would misrepresent an undisclosed composite algorithm as an observable. The same verdict applies to Oura's daily sleep and activity scores, which are not yet inventoried in providers-adapter.json.", + }, + ], + }, + { + token: 'heartrate', + status: 'mapped-standard', + archiveDataType: 'heartRate', + raw: { + sourceNeutralProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + adapterProfile: + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document', + outputDiscriminator: 'native-recording', + }, + elements: [ + { + path: 'payload', + status: 'mapped-standard', + sensorProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + reason: + 'Timestamped points may be irregular; retain the provider-native recording rather than resample or invent a uniform SampledData period.', + }, + ], + }, + ], + identifierScope: 'none', + identifierScopeReason: + 'Document ids are UUIDs, unique by construction across all accounts.', + }, + { + id: 'withings', + title: 'Withings', + sourceTypeCount: 42, + groupedMappings: [ + { + token: 'getmeas:9+10', + status: 'supported', + members: ['getmeas:9', 'getmeas:10'], + sourceNativeId: 'the common measure-group grpid', + measurementIds: ['blood-pressure'], + outputDiscriminator: 'blood-pressure-panel', + rule: 'Emit exactly one panel only when one type 9 value and one type 10 value occur in the same measure group; otherwise emit neither component as a normalized Observation. Use getmeas:9+10, not either member token, in the source-record identity preimage.', + }, + ], + sourceTypes: [ + { + token: 'getmeas:1', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['body-weight'], + unitConversion: 'provider SI value to UCUM kg', + effective: 'measure group date', + }, + ], + }, + { + token: 'getmeas:4', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['body-height'], + unitConversion: 'provider SI value to UCUM cm', + effective: 'measure group date', + }, + ], + }, + { + token: 'getmeas:6', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['body-fat-percentage'], + }, + ], + }, + { + token: 'getmeas:9', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['blood-pressure'], + component: 'diastolic', + groupedMapping: 'getmeas:9+10', + unitConversion: 'provider SI value to UCUM mm[Hg]', + effective: 'measure group date', + }, + ], + }, + { + token: 'getmeas:10', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['blood-pressure'], + component: 'systolic', + groupedMapping: 'getmeas:9+10', + unitConversion: 'provider SI value to UCUM mm[Hg]', + effective: 'measure group date', + }, + ], + }, + { + token: 'getmeas:11', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['heart-rate'], + unitConversion: 'provider value to UCUM /min', + effective: 'measure group date', + }, + ], + }, + { + token: 'getmeas:54', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['oxygen-saturation'], + unitConversion: 'provider value to UCUM %', + effective: 'measure group date', + }, + ], + }, + { + token: 'getmeas:71', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['body-temperature'], + unitConversion: 'provider value to UCUM Cel', + effective: 'measure group date', + }, + ], + }, + { + token: 'getmeas:123', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['vo2-max'], + }, + ], + }, + { + token: 'getmeas:155', + status: 'intentionally-unsupported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'intentionally-unsupported', + reason: + "Although two providers report it, each value is the output of an undisclosed proprietary algorithm over different inputs (Withings pulse-wave velocity versus Oura PPG features), so the numbers are not comparable and no shared physiological definition exists; normalizing them under one code would fabricate clinical comparability, and the diagnostic-adjacent 'age' framing makes a shared measurement unsafe. Both provider catalog rows already record this refusal.", + }, + ], + }, + { + token: 'getactivity:steps', + status: 'supported', + elements: [ + { + path: 'steps', + status: 'supported', + measurementIds: ['step-count'], + unitConversion: 'integer count to UCUM {steps}', + effective: + 'the source civil day represented as a complete day Period; midpoint substitution is forbidden', + }, + ], + }, + { + token: 'getactivity:distance', + status: 'supported', + elements: [ + { + path: 'distance', + status: 'supported', + measurementIds: ['distance'], + unitConversion: 'identity UCUM m', + effective: + 'the source civil day represented as a complete day Period; midpoint substitution is forbidden', + }, + ], + }, + { + token: 'getactivity:calories', + status: 'supported', + elements: [ + { + path: 'calories', + status: 'supported', + measurementIds: ['active-energy'], + unitConversion: 'identity UCUM kcal', + effective: + 'the source civil day represented as a complete day Period; midpoint substitution is forbidden', + }, + ], + }, + { + token: 'getsummary:deepsleepduration', + status: 'supported', + elements: [ + { + path: 'data.deepsleepduration', + status: 'supported', + measurementIds: ['deep-sleep-duration'], + }, + ], + }, + { + token: 'getsummary:remsleepduration', + status: 'supported', + elements: [ + { + path: 'data.remsleepduration', + status: 'supported', + measurementIds: ['rem-sleep-duration'], + }, + ], + }, + { + token: 'getsummary:lightsleepduration', + status: 'supported', + elements: [ + { + path: 'data.lightsleepduration', + status: 'supported', + measurementIds: ['light-sleep-duration'], + }, + ], + }, + { + token: 'getsummary:wakeupduration', + status: 'supported', + elements: [ + { + path: 'data.wakeupduration', + status: 'supported', + measurementIds: ['sleep-awake-duration'], + }, + ], + }, + { + token: 'getsummary:hr_average', + status: 'supported', + elements: [ + { + path: 'data.hr_average', + status: 'supported', + measurementIds: [ + 'sleep-heart-rate', + 'sleeping-heart-rate-average', + ], + aggregationMethod: { + 'sleep-heart-rate': 'session-mean', + }, + }, + ], + }, + { + token: 'getsummary:rr_average', + status: 'supported', + elements: [ + { + path: 'data.rr_average', + status: 'supported', + measurementIds: ['respiratory-rate-average'], + aggregationMethod: { + 'respiratory-rate-average': 'session-mean', + }, + }, + ], + }, + { + token: 'getworkouts:interval', + status: 'supported', + elements: [ + { + path: 'startdate/enddate', + status: 'supported', + measurementIds: ['workout'], + }, + ], + }, + { + token: 'activityIntraday', + status: 'mapped-standard', + archiveDataType: 'activityIntraday', + raw: { + sourceNeutralProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + adapterProfile: + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document', + outputDiscriminator: 'native-recording', + }, + elements: [ + { + path: 'steps', + status: 'mapped-standard', + sensorProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + }, + { + path: 'calories', + status: 'mapped-standard', + sensorProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + }, + { + path: 'distance', + status: 'mapped-standard', + sensorProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + }, + { + path: 'elevation', + status: 'mapped-standard', + sensorProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + }, + { + path: 'heart_rate', + status: 'mapped-standard', + sensorProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + }, + { + path: 'spo2_auto', + status: 'mapped-standard', + sensorProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + }, + ], + }, + { + token: 'sleepIntraday', + status: 'mapped-standard', + archiveDataType: 'sleepIntraday', + raw: { + sourceNeutralProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + adapterProfile: + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document', + outputDiscriminator: 'native-recording', + }, + elements: [ + { + path: 'hr', + status: 'mapped-standard', + sensorProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + }, + { + path: 'rr', + status: 'mapped-standard', + sensorProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + }, + { + path: 'snoring', + status: 'mapped-standard', + sensorProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + }, + { + path: 'sdnn_1', + status: 'mapped-standard', + sensorProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + }, + { + path: 'rmssd', + status: 'mapped-standard', + sensorProfile: + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + }, + ], + }, + { + token: 'getmeas:5', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['lean-body-mass'], + }, + ], + }, + { + token: 'getmeas:8', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['body-fat-mass'], + }, + ], + }, + { + token: 'getmeas:73', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['skin-temperature'], + }, + ], + }, + { + token: 'getmeas:76', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['muscle-mass'], + }, + ], + }, + { + token: 'getmeas:77', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['body-water-mass'], + }, + ], + }, + { + token: 'getmeas:88', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['bone-mass'], + }, + ], + }, + { + token: 'getmeas:91', + status: 'intentionally-unsupported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'intentionally-unsupported', + reason: + 'Diagnostic-adjacent cardiovascular assessment: Withings PWV is an aortic-stiffness screening value produced by a proprietary single-vendor scale algorithm (and was subject to regulatory withdrawal and reintroduction), with no second evidencing source and no safe wellness normalization, so admitting it as a normalized measurement would misrepresent a clinical vascular assessment.', + }, + ], + }, + { + token: 'getmeas:130', + status: 'intentionally-unsupported', + elements: [ + { + path: 'measure.value', + status: 'intentionally-unsupported', + reason: + 'An AFib classification is diagnostic-adjacent and is not represented as an untyped scalar Observation.', + }, + ], + }, + { + token: 'getmeas:135', + status: 'intentionally-unsupported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'intentionally-unsupported', + reason: + 'A QRS interval requires an ECG-specific interpretation contract not present in version 0.5.0.', + }, + ], + }, + { + token: 'getmeas:136', + status: 'intentionally-unsupported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'intentionally-unsupported', + reason: + 'A PR interval requires an ECG-specific interpretation contract not present in version 0.5.0.', + }, + ], + }, + { + token: 'getmeas:137', + status: 'intentionally-unsupported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'intentionally-unsupported', + reason: + 'A QT interval requires an ECG-specific interpretation contract not present in version 0.5.0.', + }, + ], + }, + { + token: 'getmeas:138', + status: 'intentionally-unsupported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'intentionally-unsupported', + reason: + 'A corrected QT interval requires an ECG-specific interpretation contract not present in version 0.5.0.', + }, + ], + }, + { + token: 'getmeas:139', + status: 'intentionally-unsupported', + elements: [ + { + path: 'measure.value', + status: 'intentionally-unsupported', + reason: + 'An AFib classification is diagnostic-adjacent and is not represented as an untyped scalar Observation.', + }, + ], + }, + { + token: 'getmeas:167', + status: 'intentionally-unsupported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'intentionally-unsupported', + reason: + 'A proprietary vendor score with no physiologic unit, and additionally diagnostic-adjacent: Withings positions it as small-fiber-neuropathy screening, so an untyped scalar Observation would be misleading.', + }, + ], + }, + { + token: 'getmeas:168', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['extracellular-water-mass'], + }, + ], + }, + { + token: 'getmeas:169', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['intracellular-water-mass'], + }, + ], + }, + { + token: 'getmeas:170', + status: 'intentionally-unsupported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'intentionally-unsupported', + reason: + 'The value is a unitless proprietary rating, not a mass or area; emitting it as a dimensionless UCUM quantity would assert a measurand and comparability the vendor does not define.', + }, + ], + }, + { + token: 'getmeas:174', + status: 'intentionally-unsupported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'intentionally-unsupported', + reason: + 'The consumed source shape (measure.value*10^unit) does not recover which body segment a value belongs to, and the shared model has no body-segment site; emitting a segment mass under whole-body fat-mass or muscle-mass semantics would be wrong, so conversion is refused rather than mislabeled.', + }, + ], + }, + { + token: 'getmeas:175', + status: 'intentionally-unsupported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'intentionally-unsupported', + reason: + 'The consumed source shape (measure.value*10^unit) does not recover which body segment a value belongs to, and the shared model has no body-segment site; emitting a segment mass under whole-body fat-mass or muscle-mass semantics would be wrong, so conversion is refused rather than mislabeled.', + }, + ], + }, + { + token: 'getmeas:196', + status: 'supported', + elements: [ + { + path: 'measure.value*10^unit', + status: 'supported', + measurementIds: ['electrodermal-activity'], + }, + ], + }, + ], + identifierScope: 'account', + identifierScopeReason: + 'grpid is a server-assigned integer that appears to come from a global sequence, but Withings does not document its scope. Scoped by account until they do.', + }, + ], + recordingDeviceIdentity: { + adapterId: 'providers', + note: "A connected provider's device metadata lives on a separate vendor endpoint the adapter does not consume, and its per-unit identifiers are refused outright. Provider Observations therefore carry a per-sample recording Device until that input exists.", + sourceFields: {}, + status: 'unavailable', + }, +} as const + +export const providerAdapterCatalog: typeof providerAdapterCatalogValue = + deepFreeze(providerAdapterCatalogValue) + +const adapterMeasurementCatalogValue = { + healthkit: { + 'apple-exercise-time': { + id: 'apple-exercise-time', + profile: 'healthkit-apple-exercise-time', + standardProfile: null, + title: 'Apple Exercise Time', + description: + 'Minutes credited to the Apple Watch Exercise ring during an exact effective Period, normalized to UCUM minutes. The crediting threshold is an Apple-proprietary brisk-activity heuristic, so this is not generic exercise duration and is never mapped to LOINC 55411-3.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'apple-exercise-time', + display: 'Apple exercise time', + definition: + "Time credited by Apple's proprietary Exercise-ring algorithm during the exact Observation effective Period.", + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'min', + unit: 'min', + example: 32, + dimension: { + s: 1, + }, + }, + effective: 'Period', + }, + 'apple-move-time': { + id: 'apple-move-time', + profile: 'healthkit-apple-move-time', + standardProfile: null, + title: 'Apple Move Time', + description: + 'Minutes credited to the Apple Watch Move ring in its time-based mode during an exact effective Period, normalized to UCUM minutes. It is Apple-proprietary ring credit, distinct from both apple-exercise-time and any energy measurement.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'apple-move-time', + display: 'Apple move time', + definition: + "Time credited by Apple's proprietary time-based Move-ring algorithm during the exact Observation effective Period.", + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'min', + unit: 'min', + example: 45, + dimension: { + s: 1, + }, + }, + effective: 'Period', + }, + 'apple-stand-hour': { + id: 'apple-stand-hour', + profile: 'healthkit-apple-stand-hour', + standardProfile: null, + title: 'Apple Stand Hour', + description: + 'HKCategoryTypeIdentifierAppleStandHour: per-hour classification of whether the user stood (or, for wheelchair users, rolled) and moved for at least one minute, as tracked by the Apple Watch Stand ring. Coded result over the exact hour Period.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'apple-stand-hour', + display: 'Apple stand hour', + definition: + 'Apple-defined per-hour stand/idle classification from the Activity Stand ring.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-apple-stand-hour', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-apple-stand-hour', + allowedValues: ['stood', 'idle'], + resultCodes: [ + { + code: 'stood', + display: 'Stood', + definition: + 'The user stood and moved at least one minute during the hour.', + }, + { + code: 'idle', + display: 'Idle', + definition: + 'The user did not stand and move at least one minute during the hour.', + }, + ], + effective: 'Period', + }, + 'apple-stand-time': { + id: 'apple-stand-time', + profile: 'healthkit-apple-stand-time', + standardProfile: null, + title: 'Apple Stand Time', + description: + 'Minutes the user was standing and moving, as credited by the Apple Watch Stand algorithm, during an exact effective Period and normalized to UCUM minutes.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'apple-stand-time', + display: 'Apple stand time', + definition: + "Standing time credited by Apple's proprietary Stand algorithm during the exact Observation effective Period.", + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'min', + unit: 'min', + example: 12, + dimension: { + s: 1, + }, + }, + effective: 'Period', + }, + 'atrial-fibrillation-burden': { + id: 'atrial-fibrillation-burden', + profile: 'healthkit-atrial-fibrillation-burden', + standardProfile: null, + title: 'Atrial Fibrillation Burden', + description: + 'The estimated percentage of analyzed time showing atrial fibrillation over a multi-day estimation window, normalized to UCUM percent. It is a windowed estimate implemented by the phase-2 aggregate design; the Withings AFib classification scalars remain intentionally unsupported and never join it.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'atrial-fibrillation-burden', + display: 'Atrial fibrillation burden', + definition: + 'The percentage of analyzed heart-rhythm time classified as atrial fibrillation within the exact Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '%', + unit: '%', + example: 2, + dimension: {}, + }, + method: { + code: 'percentage-of-time', + display: 'Percentage of time', + }, + effective: 'Period', + }, + 'audiogram-panel': { + id: 'audiogram-panel', + profile: 'healthkit-audiogram-panel', + standardProfile: null, + title: 'Audiogram Panel', + description: + 'HKAudiogramSample: a hearing test reporting an air conduction threshold per ear at each frequency it measured. The thresholds are parts of one test rather than results that stand alone, so they are components. Every component is optional: a test states the frequencies it measured and no others, and a frequency outside this set is carried by its own component keyed to the frequency rather than forced onto a neighbouring code.', + code: { + system: 'http://loinc.org', + code: '89015-2', + display: 'Pure tone air conduction threshold audiometry panel', + definition: + 'A hearing test reporting air conduction thresholds across frequencies for each ear.', + }, + valueKind: 'components', + owner: 'healthkit', + quantity: null, + effective: 'dateTime', + components: [ + { + id: 'left-250', + system: 'http://loinc.org', + code: '91375-6', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 20, + dimension: {}, + }, + }, + { + id: 'right-250', + system: 'http://loinc.org', + code: '91374-9', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 25, + dimension: {}, + }, + }, + { + id: 'left-500', + system: 'http://loinc.org', + code: '89024-4', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 20, + dimension: {}, + }, + }, + { + id: 'right-500', + system: 'http://loinc.org', + code: '89025-1', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 25, + dimension: {}, + }, + }, + { + id: 'left-750', + system: 'http://loinc.org', + code: '91379-8', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 20, + dimension: {}, + }, + }, + { + id: 'right-750', + system: 'http://loinc.org', + code: '91378-0', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 25, + dimension: {}, + }, + }, + { + id: 'left-1000', + system: 'http://loinc.org', + code: '89016-0', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 20, + dimension: {}, + }, + }, + { + id: 'right-1000', + system: 'http://loinc.org', + code: '89017-8', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 25, + dimension: {}, + }, + }, + { + id: 'left-1500', + system: 'http://loinc.org', + code: '91373-1', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 20, + dimension: {}, + }, + }, + { + id: 'right-1500', + system: 'http://loinc.org', + code: '91372-3', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 25, + dimension: {}, + }, + }, + { + id: 'left-2000', + system: 'http://loinc.org', + code: '89018-6', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 20, + dimension: {}, + }, + }, + { + id: 'right-2000', + system: 'http://loinc.org', + code: '89019-4', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 25, + dimension: {}, + }, + }, + { + id: 'left-3000', + system: 'http://loinc.org', + code: '89020-2', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 20, + dimension: {}, + }, + }, + { + id: 'right-3000', + system: 'http://loinc.org', + code: '89021-0', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 25, + dimension: {}, + }, + }, + { + id: 'left-4000', + system: 'http://loinc.org', + code: '89022-8', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 20, + dimension: {}, + }, + }, + { + id: 'right-4000', + system: 'http://loinc.org', + code: '89023-6', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 25, + dimension: {}, + }, + }, + { + id: 'left-5000', + system: 'http://loinc.org', + code: '91377-2', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 20, + dimension: {}, + }, + }, + { + id: 'right-5000', + system: 'http://loinc.org', + code: '91376-4', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 25, + dimension: {}, + }, + }, + { + id: 'left-6000', + system: 'http://loinc.org', + code: '89026-9', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 20, + dimension: {}, + }, + }, + { + id: 'right-6000', + system: 'http://loinc.org', + code: '89027-7', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 25, + dimension: {}, + }, + }, + { + id: 'left-8000', + system: 'http://loinc.org', + code: '89028-5', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 20, + dimension: {}, + }, + }, + { + id: 'right-8000', + system: 'http://loinc.org', + code: '89029-3', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB', + unit: 'dB', + example: 25, + dimension: {}, + }, + }, + ], + }, + 'bladder-incontinence': { + id: 'bladder-incontinence', + profile: 'healthkit-bladder-incontinence', + standardProfile: null, + title: 'Bladder Incontinence', + description: + 'HKCategoryTypeIdentifierBladderIncontinence: records bladder incontinence as a symptom using HKCategoryValueSeverity (unspecified, notPresent, mild, moderate, severe).', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'bladder-incontinence', + display: 'Bladder incontinence', + definition: 'Logged severity of bladder incontinence.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'dateTime', + }, + 'bleeding-after-pregnancy': { + id: 'bleeding-after-pregnancy', + profile: 'healthkit-bleeding-after-pregnancy', + standardProfile: null, + title: 'Bleeding After Pregnancy', + description: + 'HKCategoryTypeIdentifierBleedingAfterPregnancy: records postpartum bleeding (lochia) using HKCategoryValueVaginalBleeding (unspecified, light, medium, heavy, none). Coded amount result at a point in time.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'bleeding-after-pregnancy', + display: 'Bleeding after pregnancy', + definition: 'Logged vaginal bleeding amount after pregnancy.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-bleeding-after-pregnancy', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-bleeding-after-pregnancy', + allowedValues: ['unspecified', 'light', 'medium', 'heavy', 'none'], + resultCodes: [ + { + code: 'unspecified', + display: 'Unspecified', + definition: 'Bleeding of unspecified amount.', + }, + { + code: 'light', + display: 'Light', + definition: 'Light bleeding.', + }, + { + code: 'medium', + display: 'Medium', + definition: 'Medium bleeding.', + }, + { + code: 'heavy', + display: 'Heavy', + definition: 'Heavy bleeding.', + }, + { + code: 'none', + display: 'None', + definition: 'No bleeding.', + }, + ], + effective: 'dateTime', + }, + 'bleeding-during-pregnancy': { + id: 'bleeding-during-pregnancy', + profile: 'healthkit-bleeding-during-pregnancy', + standardProfile: null, + title: 'Bleeding During Pregnancy', + description: + 'HKCategoryTypeIdentifierBleedingDuringPregnancy: records bleeding during pregnancy as a symptom using HKCategoryValueVaginalBleeding (verified cases: unspecified, light, medium, heavy, none). Coded amount result at a point in time.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'bleeding-during-pregnancy', + display: 'Bleeding during pregnancy', + definition: 'Logged vaginal bleeding amount during pregnancy.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-bleeding-during-pregnancy', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-bleeding-during-pregnancy', + allowedValues: ['unspecified', 'light', 'medium', 'heavy', 'none'], + resultCodes: [ + { + code: 'unspecified', + display: 'Unspecified', + definition: 'Bleeding of unspecified amount.', + }, + { + code: 'light', + display: 'Light', + definition: 'Light bleeding.', + }, + { + code: 'medium', + display: 'Medium', + definition: 'Medium bleeding.', + }, + { + code: 'heavy', + display: 'Heavy', + definition: 'Heavy bleeding.', + }, + { + code: 'none', + display: 'None', + definition: 'No bleeding.', + }, + ], + effective: 'dateTime', + }, + 'blood-alcohol-content': { + id: 'blood-alcohol-content', + profile: 'healthkit-blood-alcohol-content', + standardProfile: null, + title: 'Blood Alcohol Content', + description: + 'Blood alcohol content as the mass-percent concentration figure a breathalyzer or user reports, kept as the plain UCUM percent scalar per the decided contract. It is not converted into a laboratory ethanol mass concentration.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'blood-alcohol-content', + display: 'Blood alcohol content', + definition: + 'The blood alcohol concentration expressed as a percent, as reported by the source at the Observation effective instant.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '%', + unit: '%', + example: 0.04, + dimension: {}, + }, + effective: 'dateTime', + }, + 'blood-type': { + id: 'blood-type', + profile: 'healthkit-blood-type', + standardProfile: null, + title: 'Grove HealthKit Blood Type', + description: + "ABO and Rh group as a coded Observation. LOINC 882-1 'ABO and Rh group [Type] in Blood' is the exact concept (re-verified ACTIVE, PROPERTY LP6886-8 Type, SCALE_TYP LP7750-5 Nom). Value is a Grove-coded ABO/Rh concept with the HKBloodType token retained as secondary coding per the sleep-stage absorption pattern.", + code: { + system: 'http://loinc.org', + code: '882-1', + display: 'ABO and Rh group [Type] in Blood', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-blood-type', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-blood-type', + allowedValues: [ + 'a-positive', + 'a-negative', + 'b-positive', + 'b-negative', + 'ab-positive', + 'ab-negative', + 'o-positive', + 'o-negative', + ], + resultCodes: [ + { + code: 'a-positive', + display: 'A positive', + definition: 'ABO group A, Rh positive.', + }, + { + code: 'a-negative', + display: 'A negative', + definition: 'ABO group A, Rh negative.', + }, + { + code: 'b-positive', + display: 'B positive', + definition: 'ABO group B, Rh positive.', + }, + { + code: 'b-negative', + display: 'B negative', + definition: 'ABO group B, Rh negative.', + }, + { + code: 'ab-positive', + display: 'AB positive', + definition: 'ABO group AB, Rh positive.', + }, + { + code: 'ab-negative', + display: 'AB negative', + definition: 'ABO group AB, Rh negative.', + }, + { + code: 'o-positive', + display: 'O positive', + definition: 'ABO group O, Rh positive.', + }, + { + code: 'o-negative', + display: 'O negative', + definition: 'ABO group O, Rh negative.', + }, + ], + effective: 'dateTime', + }, + 'contraceptive-use': { + id: 'contraceptive-use', + profile: 'healthkit-contraceptive-use', + standardProfile: null, + title: 'Contraceptive Use', + description: + 'HKCategoryTypeIdentifierContraceptive: records use of a contraceptive method over the sample interval. Coded result under LOINC 8659-5 Birth control method - Reported (Type/Nom, ACTIVE) with a Grove method value set absorbing HKCategoryValueContraceptive.', + code: { + system: 'http://loinc.org', + code: '8659-5', + display: 'Birth control method - Reported', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-contraceptive-use', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-contraceptive-use', + allowedValues: [ + 'unspecified', + 'implant', + 'injection', + 'intrauterine-device', + 'intravaginal-ring', + 'oral', + 'patch', + ], + resultCodes: [ + { + code: 'unspecified', + display: 'Unspecified', + definition: 'A contraceptive whose method is not specified.', + }, + { + code: 'implant', + display: 'Implant', + definition: 'A contraceptive implant.', + }, + { + code: 'injection', + display: 'Injection', + definition: 'A contraceptive injection.', + }, + { + code: 'intrauterine-device', + display: 'Intrauterine device', + definition: 'An intrauterine device (IUD).', + }, + { + code: 'intravaginal-ring', + display: 'Intravaginal ring', + definition: 'An intravaginal contraceptive ring.', + }, + { + code: 'oral', + display: 'Oral', + definition: 'An oral contraceptive.', + }, + { + code: 'patch', + display: 'Patch', + definition: 'A contraceptive patch.', + }, + ], + effective: 'Period', + }, + 'cycling-functional-threshold-power': { + id: 'cycling-functional-threshold-power', + profile: 'healthkit-cycling-functional-threshold-power', + standardProfile: null, + title: 'Cycling Functional Threshold Power', + description: + 'The estimated maximum cycling power output the subject can sustain for about an hour, as derived by the source, normalized to UCUM watts. It is a derived capacity estimate, distinct from the shared instantaneous power measurement.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'cycling-functional-threshold-power', + display: 'Cycling functional threshold power', + definition: + 'The source-estimated highest cycling power output the subject can sustain for approximately one hour.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'W', + unit: 'W', + example: 235, + dimension: { + g: 1, + m: 2, + s: -3, + }, + }, + effective: 'dateTime', + }, + 'environmental-audio-exposure': { + id: 'environmental-audio-exposure', + profile: 'healthkit-environmental-audio-exposure', + standardProfile: null, + title: 'Environmental Audio Exposure', + description: + 'The A-weighted equivalent continuous sound pressure level of environmental sound over an exact effective Period, normalized to UCUM decibels sound pressure level per the pinned Grove decibel convention. Only HealthKit evidences this concept.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'environmental-audio-exposure', + display: 'Environmental audio exposure', + definition: + 'The A-weighted equivalent continuous sound pressure level of environmental sound the user was exposed to during the exact Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB[SPL]', + unit: 'dB(SPL)', + example: 68, + dimension: { + g: 1, + m: -1, + s: -2, + }, + }, + effective: 'Period', + }, + 'environmental-audio-exposure-notification': { + id: 'environmental-audio-exposure-notification', + profile: 'healthkit-environmental-audio-exposure-notification', + standardProfile: null, + title: 'Environmental Audio Exposure Notification', + description: + 'HKCategoryTypeIdentifierAudioExposureEvent: raised when environmental sound reaches the momentary limit. The environmental audio exposure quantity remains the measurement surface.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'environmental-audio-exposure-notification', + display: 'Environmental Audio Exposure Notification', + definition: + 'A device notification that environmental sound reached the momentary exposure limit.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-environmental-audio-exposure-notification', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-environmental-audio-exposure-notification', + allowedValues: ['momentary-limit'], + resultCodes: [ + { + code: 'momentary-limit', + display: 'Momentary limit', + definition: + 'HKCategoryValueEnvironmentalAudioExposureEvent.momentaryLimit: the momentary exposure limit was reached.', + }, + ], + effective: 'Period', + }, + 'environmental-sound-reduction': { + id: 'environmental-sound-reduction', + profile: 'healthkit-environmental-sound-reduction', + standardProfile: null, + title: 'Environmental Sound Reduction', + description: + 'The reduction in equivalent continuous sound pressure level provided by noise-reducing headphones over an exact effective Period, expressed in UCUM decibels sound pressure level per the pinned Grove decibel convention. The value is a level difference, not an absolute exposure level.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'environmental-sound-reduction', + display: 'Environmental sound reduction', + definition: + "The difference in equivalent continuous sound pressure level attenuated by the user's noise-reducing headphones during the exact Observation effective Period.", + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB[SPL]', + unit: 'dB(SPL)', + example: 22, + dimension: { + g: 1, + m: -1, + s: -2, + }, + }, + effective: 'Period', + }, + 'food-correlation': { + id: 'food-correlation', + profile: 'healthkit-food-correlation', + standardProfile: null, + title: 'Food Correlation', + description: + 'One eating occasion, whose members are the nutrient Observations recorded for it. The nutrients are independently meaningful and are already modelled one by one, so the occasion groups them rather than restating them as components.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'food-correlation', + display: 'Food correlation', + definition: + 'One eating occasion grouping the nutrient results recorded for it.', + }, + valueKind: 'grouping', + quantity: null, + effective: 'Period', + owner: 'healthkit', + exampleMembers: ['Observation/GroveMobileDietaryEnergyExample'], + }, + 'forced-expiratory-volume-1': { + id: 'forced-expiratory-volume-1', + profile: 'healthkit-forced-expiratory-volume-1', + standardProfile: null, + title: 'Forced Expiratory Volume in 1 Second', + description: + 'The volume of air forcibly exhaled during the first second of a forced exhalation, captured as a point spirometry result and normalized to UCUM litres. Only HealthKit evidences this concept, so it lands in the HealthKit adapter guide.', + code: { + system: 'http://loinc.org', + code: '20150-9', + display: 'FEV1', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'L', + unit: 'L', + example: 3.6, + dimension: { + m: 3, + }, + }, + effective: 'dateTime', + }, + 'forced-vital-capacity': { + id: 'forced-vital-capacity', + profile: 'healthkit-forced-vital-capacity', + standardProfile: null, + title: 'Forced Vital Capacity', + description: + 'The volume of air forcibly exhaled after the deepest possible breath, captured as a point spirometry result and normalized to UCUM litres. Only HealthKit evidences this concept, so it lands in the HealthKit adapter guide.', + code: { + system: 'http://loinc.org', + code: '19868-9', + display: + 'Forced vital capacity [Volume] Respiratory system by Spirometry', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'L', + unit: 'L', + example: 4.5, + dimension: { + m: 3, + }, + }, + effective: 'dateTime', + }, + 'gad7-assessment': { + id: 'gad7-assessment', + profile: 'healthkit-gad7-assessment', + standardProfile: null, + title: 'Grove HealthKit GAD-7 Score', + description: + "HKGAD7Assessment (iOS 18, HKScoredAssessment subclass): score 0-21, answers [HKGAD7Assessment.Answer], risk. Routing recommendation: platform-exclusive score Observation on exact LOINC 70274-6 'Generalized anxiety disorder 7 item (GAD-7) total score [Reported.PHQ]' (re-verified ACTIVE, PROPERTY LP185820-0 Score, SCALE_TYP LP7753-9 Qn). The seven answers are retained as components coded with the LOINC GAD-7 item codes under panel 69737-5 (re-verified), each valued with the standard LOINC answer codes (LA6568-5 'Not at all', LA6569-3 'Several days', LA6570-1 'More than half the days', LA6571-9 'Nearly every day' — all four displays re-verified against LOINC 2.82 via tx.fhir.org) absorbing Answer cases notAtAll, severalDays, moreThanHalfTheDays, nearlyEveryDay; Risk (noneToMinimal, mild, moderate, severe — re-verified) is redundant with the published score bands and is carried only as an Observation.interpretation-style Grove coding, not a value.", + code: { + system: 'http://loinc.org', + code: '70274-6', + display: + 'Generalized anxiety disorder 7 item (GAD-7) total score [Reported.PHQ]', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{score}', + unit: 'score', + example: 6, + dimension: {}, + }, + effective: 'dateTime', + }, + 'handwashing-session': { + id: 'handwashing-session', + profile: 'healthkit-handwashing-session', + standardProfile: null, + title: 'Handwashing Session', + description: + 'HKCategoryTypeIdentifierHandwashingEvent: an interval sample (notApplicable value) recorded automatically by Apple Watch Series 4+ or written by apps; the sample duration is the handwashing duration. Emitted as a duration quantity over the exact Period.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'handwashing-session', + display: 'Handwashing session', + definition: + 'The duration of one handwashing event during the exact Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 's', + unit: 's', + example: 22, + dimension: { + s: 1, + }, + }, + effective: 'Period', + }, + 'headphone-audio-exposure': { + id: 'headphone-audio-exposure', + profile: 'healthkit-headphone-audio-exposure', + standardProfile: null, + title: 'Headphone Audio Exposure', + description: + 'The A-weighted equivalent continuous sound pressure level delivered through headphones over an exact effective Period, normalized to UCUM decibels sound pressure level per the pinned Grove decibel convention. Only HealthKit evidences this concept.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'headphone-audio-exposure', + display: 'Headphone audio exposure', + definition: + 'The A-weighted equivalent continuous sound pressure level of headphone audio the user was exposed to during the exact Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'dB[SPL]', + unit: 'dB(SPL)', + example: 74, + dimension: { + g: 1, + m: -1, + s: -2, + }, + }, + effective: 'Period', + }, + 'headphone-audio-exposure-notification': { + id: 'headphone-audio-exposure-notification', + profile: 'healthkit-headphone-audio-exposure-notification', + standardProfile: null, + title: 'Headphone Audio Exposure Notification', + description: + 'HKCategoryTypeIdentifierHeadphoneAudioExposureEvent: raised when headphone exposure reaches the seven-day limit. The headphone audio exposure quantity remains the measurement surface.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'headphone-audio-exposure-notification', + display: 'Headphone Audio Exposure Notification', + definition: + 'A device notification that headphone audio exposure reached the seven-day limit.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-headphone-audio-exposure-notification', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-headphone-audio-exposure-notification', + allowedValues: ['seven-day-limit'], + resultCodes: [ + { + code: 'seven-day-limit', + display: 'Seven day limit', + definition: + 'HKCategoryValueHeadphoneAudioExposureEvent.sevenDayLimit: the rolling seven-day exposure limit was reached.', + }, + ], + effective: 'Period', + }, + 'heart-rate-recovery-one-minute': { + id: 'heart-rate-recovery-one-minute', + profile: 'healthkit-heart-rate-recovery-one-minute', + standardProfile: null, + title: 'Heart Rate Recovery (One Minute)', + description: + 'The reduction in heart rate from workout peak to exactly one minute after exercise ends, normalized to UCUM beats per minute. The one-minute protocol is part of the meaning, so the value is not a heart rate and never joins the shared heart-rate measurement.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'heart-rate-recovery-one-minute', + display: 'Heart rate recovery one minute', + definition: + 'The decrease in heart rate, in beats per minute, from peak exercise to one minute after the end of exercise.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: 'beats/minute', + example: 24, + dimension: { + s: -1, + }, + }, + effective: 'dateTime', + }, + 'high-heart-rate-notification': { + id: 'high-heart-rate-notification', + profile: 'healthkit-high-heart-rate-notification', + standardProfile: null, + title: 'High Heart Rate Notification', + description: + 'HKCategoryTypeIdentifierHighHeartRateEvent: an Apple Watch notification raised against a user-configurable threshold. This is the notification, not a rhythm finding; the heart-rate quantities remain the measurement surface, and the threshold that raised it is carried as a component so the notification can be interpreted at all.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'high-heart-rate-notification', + display: 'High Heart Rate Notification', + definition: + "A device notification that heart rate stayed above the wearer's configured threshold while apparently inactive.", + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-high-heart-rate-notification', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-high-heart-rate-notification', + allowedValues: ['occurred'], + resultCodes: [ + { + code: 'occurred', + display: 'Occurred', + definition: + 'The device raised this notification over the stated Period. HealthKit carries no further value for it.', + }, + ], + effective: 'Period', + components: [ + { + id: 'threshold', + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'high-heart-rate-threshold', + display: 'Notification threshold', + title: 'Notification threshold', + definition: + 'The wearer-configured heart rate above which the device raises this notification.', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: 'beats/minute', + example: 120, + dimension: { + s: -1, + }, + }, + }, + ], + }, + 'hypertension-notification': { + id: 'hypertension-notification', + profile: 'healthkit-hypertension-notification', + standardProfile: null, + title: 'Hypertension Notification', + description: + 'HKCategoryTypeIdentifierHypertensionEvent: a proprietary screening notification. Blood pressure remains the measurement surface; this records only that the notification was raised, and never asserts a hypertension diagnosis.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'hypertension-notification', + display: 'Hypertension Notification', + definition: + 'A device notification of blood-pressure readings consistent with hypertension.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-hypertension-notification', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-hypertension-notification', + allowedValues: ['occurred'], + resultCodes: [ + { + code: 'occurred', + display: 'Occurred', + definition: + 'The device raised this notification over the stated Period. HealthKit carries no further value for it.', + }, + ], + effective: 'Period', + }, + 'infrequent-menstrual-cycles': { + id: 'infrequent-menstrual-cycles', + profile: 'healthkit-infrequent-menstrual-cycles', + standardProfile: null, + title: 'Infrequent Menstrual Cycles', + description: + "HKCategoryTypeIdentifierInfrequentMenstrualCycles: a deviation derived from the wearer's own logged cycle records over the stated Period.", + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'infrequent-menstrual-cycles', + display: 'Infrequent menstrual cycles', + definition: + "Cycles occurring less often than expected, derived from the participant's logged cycle records. Corresponds to SNOMED CT 52073004 (Oligomenorrhea); the concept is cited rather than bound, because this guide carries no SNOMED dependency.", + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-infrequent-menstrual-cycles', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-infrequent-menstrual-cycles', + allowedValues: ['present', 'not-present'], + resultCodes: [ + { + code: 'present', + display: 'Present', + definition: + "The pattern was detected over the stated Period from the wearer's own logged cycle records.", + }, + { + code: 'not-present', + display: 'Not present', + definition: + 'The pattern was evaluated over the stated Period and not detected.', + }, + ], + effective: 'Period', + }, + 'inhaler-usage': { + id: 'inhaler-usage', + profile: 'healthkit-inhaler-usage', + standardProfile: null, + title: 'Inhaler Usage', + description: + 'The number of inhaler puffs taken during an exact effective Period. The count is a device-usage total, not a medication-administration record, and only HealthKit evidences it.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'inhaler-usage', + display: 'Inhaler usage', + definition: + 'The total number of inhaler puffs the user took during the exact Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{puff}', + unit: 'puffs', + example: 2, + dimension: {}, + }, + effective: 'Period', + }, + 'insulin-delivery': { + id: 'insulin-delivery', + profile: 'healthkit-insulin-delivery', + standardProfile: null, + title: 'Insulin Delivery', + description: + 'The quantity of insulin delivered during the exact effective Period, normalized to UCUM international units, with a REQUIRED delivery-reason component distinguishing basal from bolus delivery per the decided contract. A sample without the HKMetadataKeyInsulinDeliveryReason metadata fails closed.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'insulin-delivery', + display: 'Insulin delivery', + definition: + 'The amount of insulin delivered during the exact Observation effective Period, qualified by a required basal or bolus delivery reason.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '[iU]', + unit: 'IU', + example: 6, + dimension: {}, + }, + effective: 'Period', + }, + 'irregular-heart-rhythm-notification': { + id: 'irregular-heart-rhythm-notification', + profile: 'healthkit-irregular-heart-rhythm-notification', + standardProfile: null, + title: 'Irregular Heart Rhythm Notification', + description: + 'HKCategoryTypeIdentifierIrregularHeartRhythmEvent: an FDA-cleared screening notification from a proprietary algorithm. It is emitted as a notification and never as a rhythm finding: the electrocardiogram and the atrial-fibrillation burden percentage remain the rhythm evidence, and this Observation links to them through derivedFrom.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'irregular-heart-rhythm-notification', + display: 'Irregular Heart Rhythm Notification', + definition: + 'A device notification of a heart rhythm irregularity consistent with atrial fibrillation.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-irregular-heart-rhythm-notification', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-irregular-heart-rhythm-notification', + allowedValues: ['occurred'], + resultCodes: [ + { + code: 'occurred', + display: 'Occurred', + definition: + 'The device raised this notification over the stated Period. HealthKit carries no further value for it.', + }, + ], + effective: 'Period', + }, + 'irregular-menstrual-cycles': { + id: 'irregular-menstrual-cycles', + profile: 'healthkit-irregular-menstrual-cycles', + standardProfile: null, + title: 'Irregular Menstrual Cycles', + description: + "HKCategoryTypeIdentifierIrregularMenstrualCycles: a deviation derived from the wearer's own logged cycle records over the stated Period. This is a finding, not a device notification: nothing was sensed, and the pattern is computed from data the participant entered.", + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'irregular-menstrual-cycles', + display: 'Irregular menstrual cycles', + definition: + "Cycle lengths outside the expected range, derived from the participant's logged cycle records. Corresponds to SNOMED CT 80182007 (Irregular periods); the concept is cited rather than bound, because this guide carries no SNOMED dependency.", + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-irregular-menstrual-cycles', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-irregular-menstrual-cycles', + allowedValues: ['present', 'not-present'], + resultCodes: [ + { + code: 'present', + display: 'Present', + definition: + "The pattern was detected over the stated Period from the wearer's own logged cycle records.", + }, + { + code: 'not-present', + display: 'Not present', + definition: + 'The pattern was evaluated over the stated Period and not detected.', + }, + ], + effective: 'Period', + }, + 'lactation-status': { + id: 'lactation-status', + profile: 'healthkit-lactation-status', + standardProfile: null, + title: 'Lactation Status', + description: + 'HKCategoryTypeIdentifierLactation: an interval flag (notApplicable value) asserting lactation over the sample period. Emitted as a coded Observation under LOINC 63895-7 Breastfeeding status (Find/Nom, ACTIVE) with the fixed Grove result lactating over the exact effective Period.', + code: { + system: 'http://loinc.org', + code: '63895-7', + display: 'Breastfeeding status', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-lactation-status', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-lactation-status', + allowedValues: ['lactating'], + resultCodes: [ + { + code: 'lactating', + display: 'Lactating', + definition: + 'The person was lactating during the exact Observation effective Period.', + }, + ], + effective: 'Period', + }, + 'low-cardio-fitness-notification': { + id: 'low-cardio-fitness-notification', + profile: 'healthkit-low-cardio-fitness-notification', + standardProfile: null, + title: 'Low Cardio Fitness Notification', + description: + 'HKCategoryTypeIdentifierLowCardioFitnessEvent: raised when the VO2 max estimate falls below a threshold. The threshold is carried as a component.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'low-cardio-fitness-notification', + display: 'Low Cardio Fitness Notification', + definition: + 'A device notification that estimated cardio fitness fell below the configured threshold.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-low-cardio-fitness-notification', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-low-cardio-fitness-notification', + allowedValues: ['low-fitness'], + resultCodes: [ + { + code: 'low-fitness', + display: 'Low fitness', + definition: + 'HKCategoryValueLowCardioFitnessEvent.lowFitness: the cardio fitness estimate is below the threshold.', + }, + ], + effective: 'Period', + components: [ + { + id: 'threshold', + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'low-cardio-fitness-threshold', + display: 'Notification threshold', + title: 'Notification threshold', + definition: + 'The cardio fitness estimate below which the device raises this notification.', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'mL/kg/min', + unit: 'mL/kg/min', + example: 30, + dimension: { + g: -1, + m: 3, + s: -1, + }, + }, + }, + ], + }, + 'low-heart-rate-notification': { + id: 'low-heart-rate-notification', + profile: 'healthkit-low-heart-rate-notification', + standardProfile: null, + title: 'Low Heart Rate Notification', + description: + 'HKCategoryTypeIdentifierLowHeartRateEvent: an Apple Watch notification raised against a user-configurable threshold. The threshold is carried as a component; the heart-rate quantities remain the measurement surface.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'low-heart-rate-notification', + display: 'Low Heart Rate Notification', + definition: + "A device notification that heart rate stayed below the wearer's configured threshold while apparently inactive.", + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-low-heart-rate-notification', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-low-heart-rate-notification', + allowedValues: ['occurred'], + resultCodes: [ + { + code: 'occurred', + display: 'Occurred', + definition: + 'The device raised this notification over the stated Period. HealthKit carries no further value for it.', + }, + ], + effective: 'Period', + components: [ + { + id: 'threshold', + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'low-heart-rate-threshold', + display: 'Notification threshold', + title: 'Notification threshold', + definition: + 'The wearer-configured heart rate below which the device raises this notification.', + cardinality: '0..1', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: 'beats/minute', + example: 40, + dimension: { + s: -1, + }, + }, + }, + ], + }, + 'number-of-alcoholic-beverages': { + id: 'number-of-alcoholic-beverages', + profile: 'healthkit-number-of-alcoholic-beverages', + standardProfile: null, + title: 'Number of Alcoholic Beverages', + description: + "The count of standard alcoholic drinks consumed during the exact effective Period, using the annotated dimensionless UCUM unit {drinks}. Counting semantics follow the source's standard-drink definition and are not converted to ethanol mass.", + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'number-of-alcoholic-beverages', + display: 'Number of alcoholic beverages', + definition: + 'The number of standard alcoholic drinks attributed to the exact Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{drinks}', + unit: 'drinks', + example: 2, + dimension: {}, + }, + effective: 'Period', + }, + 'number-of-times-fallen': { + id: 'number-of-times-fallen', + profile: 'healthkit-number-of-times-fallen', + standardProfile: null, + title: 'Number of Times Fallen', + description: + 'The number of falls recorded during an exact effective Period, normalized to the UCUM annotation {falls}. This is a device or user-recorded interval count, not a clinical falls-history assessment.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'number-of-times-fallen', + display: 'Number of times fallen', + definition: + 'The total number of falls attributed to the exact Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{falls}', + unit: 'falls', + example: 1, + dimension: {}, + }, + effective: 'Period', + }, + 'peak-expiratory-flow-rate': { + id: 'peak-expiratory-flow-rate', + profile: 'healthkit-peak-expiratory-flow-rate', + standardProfile: null, + title: 'Peak Expiratory Flow Rate', + description: + 'The maximum expiratory gas flow generated during a forceful exhalation, captured as a point result and normalized to UCUM litres per minute. The method-agnostic LOINC concept is chosen because HealthKit does not constrain the measuring device.', + code: { + system: 'http://loinc.org', + code: '33452-4', + display: 'Maximum expiratory gas flow Respiratory system airway', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'L/min', + unit: 'L/min', + example: 480, + dimension: { + m: 3, + s: -1, + }, + }, + effective: 'dateTime', + }, + 'peripheral-perfusion-index': { + id: 'peripheral-perfusion-index', + profile: 'healthkit-peripheral-perfusion-index', + standardProfile: null, + title: 'Peripheral Perfusion Index', + description: + 'The pulse-oximetry perfusion index — the ratio of pulsatile to non-pulsatile blood flow at the sensor site — normalized to UCUM percent. Only HealthKit evidences it, so it stays in the HealthKit adapter guide.', + code: { + system: 'http://loinc.org', + code: '61006-3', + display: 'Perfusion index Tissue by Pulse oximetry', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '%', + unit: '%', + example: 3.5, + dimension: {}, + }, + effective: 'dateTime', + }, + 'persistent-intermenstrual-bleeding': { + id: 'persistent-intermenstrual-bleeding', + profile: 'healthkit-persistent-intermenstrual-bleeding', + standardProfile: null, + title: 'Persistent Intermenstrual Bleeding', + description: + "HKCategoryTypeIdentifierPersistentIntermenstrualBleeding: a deviation derived from the wearer's own logged cycle records over the stated Period. No verified SNOMED concept is bound; the adapter code system carries the meaning rather than approximating a clinical concept.", + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'persistent-intermenstrual-bleeding', + display: 'Persistent intermenstrual bleeding', + definition: + 'Bleeding between periods persisting across cycles, derived from logged cycle records.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-persistent-intermenstrual-bleeding', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-persistent-intermenstrual-bleeding', + allowedValues: ['present', 'not-present'], + resultCodes: [ + { + code: 'present', + display: 'Present', + definition: + "The pattern was detected over the stated Period from the wearer's own logged cycle records.", + }, + { + code: 'not-present', + display: 'Not present', + definition: + 'The pattern was evaluated over the stated Period and not detected.', + }, + ], + effective: 'Period', + }, + 'phq9-assessment': { + id: 'phq9-assessment', + profile: 'healthkit-phq9-assessment', + standardProfile: null, + title: 'Grove HealthKit PHQ-9 Score', + description: + "HKPHQ9Assessment (iOS 18, HKScoredAssessment subclass): score 0-27, answers [HKPHQ9Assessment.Answer] including preferNotToAnswer, risk. Routing recommendation: platform-exclusive score Observation on exact LOINC 44261-6 'Patient Health Questionnaire 9 item (PHQ-9) total score [Reported]' (re-verified ACTIVE, Score, Qn); item answers retained as components under panel 44249-1 'PHQ-9 quick depression assessment panel [Reported.PHQ]' (re-verified) with the standard LA answer codes; preferNotToAnswer maps to a component with dataAbsentReason asked-declined, which is the semantically exact FHIR mechanism; Risk (noneToMinimal, mild, moderate, moderatelySevere, severe — re-verified) carried as interpretation-style Grove coding only.", + code: { + system: 'http://loinc.org', + code: '44261-6', + display: + 'Patient Health Questionnaire 9 item (PHQ-9) total score [Reported]', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{score}', + unit: 'score', + example: 8, + dimension: {}, + }, + effective: 'dateTime', + }, + 'physical-effort': { + id: 'physical-effort', + profile: 'healthkit-physical-effort', + standardProfile: null, + title: 'Physical Effort', + description: + "The estimated intensity of physical exertion over an exact effective Period, normalized to UCUM kilocalories per kilogram per hour as sourced from Apple's physicalEffort samples. It is an energy-normalized intensity rate, deliberately not converted to the oxygen-based UCUM [MET].", + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'physical-effort', + display: 'Physical effort', + definition: + 'Estimated exertion intensity, in energy per body mass per time, over the exact Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kcal/kg/h', + unit: 'kcal/(kg.h)', + example: 4.5, + dimension: { + m: 2, + s: -3, + }, + }, + effective: 'Period', + }, + 'pregnancy-status': { + id: 'pregnancy-status', + profile: 'healthkit-pregnancy-status', + standardProfile: null, + title: 'Pregnancy Status', + description: + 'HKCategoryTypeIdentifierPregnancy: an interval flag (notApplicable value) asserting pregnancy over the sample period. Emitted as a coded Observation under LOINC 82810-3 Pregnancy status (Find/Nom, ACTIVE) with the fixed Grove result pregnant over the exact effective Period.', + code: { + system: 'http://loinc.org', + code: '82810-3', + display: 'Pregnancy status', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-pregnancy-status', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-pregnancy-status', + allowedValues: ['pregnant'], + resultCodes: [ + { + code: 'pregnant', + display: 'Pregnant', + definition: + 'The person was pregnant during the exact Observation effective Period.', + }, + ], + effective: 'Period', + }, + 'pregnancy-test-result': { + id: 'pregnancy-test-result', + profile: 'healthkit-pregnancy-test-result', + standardProfile: null, + title: 'Pregnancy Test Result', + description: + 'HKCategoryTypeIdentifierPregnancyTestResult: the result of a home urine hCG pregnancy test. Apple documentation states these tests check for hCG in a urine sample, so LOINC 2106-3 (Choriogonadotropin [Presence] in Urine, PrThr/Ord, ACTIVE) is semantically exact.', + code: { + system: 'http://loinc.org', + code: '2106-3', + display: 'Choriogonadotropin [Presence] in Urine', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-pregnancy-test-result', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-pregnancy-test-result', + allowedValues: ['negative', 'positive', 'indeterminate'], + resultCodes: [ + { + code: 'negative', + display: 'Negative', + definition: 'The pregnancy test was negative.', + }, + { + code: 'positive', + display: 'Positive', + definition: 'The pregnancy test was positive.', + }, + { + code: 'indeterminate', + display: 'Indeterminate', + definition: 'The pregnancy test result could not be determined.', + }, + ], + effective: 'dateTime', + }, + 'progesterone-test-result': { + id: 'progesterone-test-result', + profile: 'healthkit-progesterone-test-result', + standardProfile: null, + title: 'Progesterone Test Result', + description: + 'HKCategoryTypeIdentifierProgesteroneTestResult: the result of a home urine test for pregnanediol-3-glucuronide (PdG) confirming ovulation, per Apple documentation. Qualitative positive/negative/indeterminate result.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'progesterone-test-result', + display: 'Progesterone (PdG) test result', + definition: + 'Qualitative result of a home urine pregnanediol-3-glucuronide test.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-progesterone-test-result', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-progesterone-test-result', + allowedValues: ['negative', 'positive', 'indeterminate'], + resultCodes: [ + { + code: 'negative', + display: 'Negative', + definition: 'The PdG test was negative.', + }, + { + code: 'positive', + display: 'Positive', + definition: 'The PdG test was positive.', + }, + { + code: 'indeterminate', + display: 'Indeterminate', + definition: 'The PdG test result could not be determined.', + }, + ], + effective: 'dateTime', + }, + 'prolonged-menstrual-periods': { + id: 'prolonged-menstrual-periods', + profile: 'healthkit-prolonged-menstrual-periods', + standardProfile: null, + title: 'Prolonged Menstrual Periods', + description: + "HKCategoryTypeIdentifierProlongedMenstrualPeriods: a deviation derived from the wearer's own logged cycle records over the stated Period. No verified SNOMED concept is bound; the adapter code system carries the meaning rather than approximating a clinical concept.", + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'prolonged-menstrual-periods', + display: 'Prolonged menstrual periods', + definition: + 'Periods lasting longer than expected, derived from logged cycle records.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-prolonged-menstrual-periods', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-prolonged-menstrual-periods', + allowedValues: ['present', 'not-present'], + resultCodes: [ + { + code: 'present', + display: 'Present', + definition: + "The pattern was detected over the stated Period from the wearer's own logged cycle records.", + }, + { + code: 'not-present', + display: 'Not present', + definition: + 'The pattern was evaluated over the stated Period and not detected.', + }, + ], + effective: 'Period', + }, + 'running-ground-contact-time': { + id: 'running-ground-contact-time', + profile: 'healthkit-running-ground-contact-time', + standardProfile: null, + title: 'Running Ground Contact Time', + description: + 'The time the foot is in contact with the ground per running stride, normalized to UCUM milliseconds. A per-sample running dynamics metric recorded during workouts.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'running-ground-contact-time', + display: 'Running ground contact time', + definition: + 'The ground contact time of one running stride at the sample instant.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ms', + unit: 'ms', + example: 245, + dimension: { + s: 1, + }, + }, + effective: 'dateTime', + }, + 'running-stride-length': { + id: 'running-stride-length', + profile: 'healthkit-running-stride-length', + standardProfile: null, + title: 'Running Stride Length', + description: + 'The distance covered by one running stride, normalized to UCUM metres. A per-sample running dynamics metric recorded during workouts.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'running-stride-length', + display: 'Running stride length', + definition: + 'The distance covered by one running stride at the sample instant.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'm', + unit: 'm', + example: 1.15, + dimension: { + m: 1, + }, + }, + effective: 'dateTime', + }, + 'running-vertical-oscillation': { + id: 'running-vertical-oscillation', + profile: 'healthkit-running-vertical-oscillation', + standardProfile: null, + title: 'Running Vertical Oscillation', + description: + 'The vertical bounce of the torso per running stride, normalized to UCUM centimetres. A per-sample running dynamics metric recorded during workouts.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'running-vertical-oscillation', + display: 'Running vertical oscillation', + definition: + 'The vertical oscillation of the torso during running at the sample instant.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'cm', + unit: 'cm', + example: 8.4, + dimension: { + m: 1, + }, + }, + effective: 'dateTime', + }, + 'six-minute-walk-test-distance': { + id: 'six-minute-walk-test-distance', + profile: 'healthkit-six-minute-walk-test-distance', + standardProfile: null, + title: 'Six-Minute Walk Test Distance', + description: + "The distance walked (or estimated walkable) in six minutes, using LOINC Six minute walk test and normalized to UCUM metres. Apple's samples are predominantly rolling-window mobility estimates rather than administered tests, so the phase-2 aggregate design owns the emission contract.", + code: { + system: 'http://loinc.org', + code: '64098-7', + display: 'Six minute walk test', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'm', + unit: 'm', + example: 540, + dimension: { + m: 1, + }, + }, + method: { + code: 'rolling-mean', + display: 'Rolling mean', + }, + effective: 'Period', + }, + 'sleep-apnea-notification': { + id: 'sleep-apnea-notification', + profile: 'healthkit-sleep-apnea-notification', + standardProfile: null, + title: 'Sleep Apnea Notification', + description: + 'HKCategoryTypeIdentifierSleepApneaEvent: a proprietary screening notification. The sleeping breathing disturbances quantity remains the measurement surface; this records that the notification was raised.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'sleep-apnea-notification', + display: 'Sleep Apnea Notification', + definition: + 'A device notification of breathing disturbances consistent with sleep apnea.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-sleep-apnea-notification', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-sleep-apnea-notification', + allowedValues: ['occurred'], + resultCodes: [ + { + code: 'occurred', + display: 'Occurred', + definition: + 'The device raised this notification over the stated Period. HealthKit carries no further value for it.', + }, + ], + effective: 'Period', + }, + 'sleeping-breathing-disturbances': { + id: 'sleeping-breathing-disturbances', + profile: 'healthkit-sleeping-breathing-disturbances', + standardProfile: null, + title: 'Sleeping Breathing Disturbances', + description: + "A nightly windowed aggregate of accelerometry-detected breathing disturbances during sleep, expressed as disturbance events per hour of sleep over the night's effective Period. It is a proprietary screening metric and is deliberately not coded as a clinical respiratory disturbance or apnea-hypopnea index.", + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'sleeping-breathing-disturbances', + display: 'Sleeping breathing disturbances', + definition: + 'The number of accelerometer-detected breathing disturbance events per hour of sleep during the exact nightly Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/h', + unit: 'events/hour', + example: 4.2, + dimension: { + s: -1, + }, + }, + method: { + code: 'session-rate', + display: 'Session rate', + }, + effective: 'Period', + }, + 'stair-ascent-speed': { + id: 'stair-ascent-speed', + profile: 'healthkit-stair-ascent-speed', + standardProfile: null, + title: 'Stair Ascent Speed', + description: + 'The vertical speed at which the subject climbs stairs, recorded passively by HealthKit and normalized to UCUM metres per second. It is distinct from the shared exercise speed measurement because the stair-climbing binding is part of the concept.', + code: { + system: 'http://loinc.org', + code: '112431-2', + display: 'Stair ascent speed [Velocity]', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'm/s', + unit: 'm/s', + example: 0.42, + dimension: { + m: 1, + s: -1, + }, + }, + effective: 'dateTime', + }, + 'stair-descent-speed': { + id: 'stair-descent-speed', + profile: 'healthkit-stair-descent-speed', + standardProfile: null, + title: 'Stair Descent Speed', + description: + 'The vertical speed at which the subject descends stairs, recorded passively by HealthKit and normalized to UCUM metres per second. It is distinct from the shared exercise speed measurement because the stair-descent binding is part of the concept.', + code: { + system: 'http://loinc.org', + code: '112430-4', + display: 'Stair descent speed [Velocity]', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'm/s', + unit: 'm/s', + example: 0.52, + dimension: { + m: 1, + s: -1, + }, + }, + effective: 'dateTime', + }, + 'state-of-mind': { + id: 'state-of-mind', + profile: 'healthkit-state-of-mind', + standardProfile: null, + title: 'State of Mind', + description: + "HKStateOfMind: a self-reported reflection on how the participant felt. Valence is the one numeric axis and carries the Observation value, reported on HealthKit's closed −1.0 to 1.0 scale; the reflection's kind, its classification, and its labels and associations are coded components. Every axis is the participant's own report, not a measurement.", + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'state-of-mind', + display: 'State of mind', + definition: + 'A self-reported reflection on felt experience, valenced from unpleasant to pleasant.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '1', + unit: 'valence', + example: 0.4, + dimension: {}, + }, + effective: 'Period', + components: [ + { + id: 'kind', + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'kind', + display: 'Reflection kind', + title: 'Reflection kind', + definition: + 'HKStateOfMind.Kind: whether the reflection describes a momentary feeling or a longer period.', + cardinality: '1..1', + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-state-of-mind-kind', + resultCodes: [ + { + code: 'momentary-emotion', + display: 'Momentary emotion', + definition: + 'HKStateOfMind.Kind.momentaryEmotion: how the participant felt at the moment of reflection.', + }, + { + code: 'daily-mood', + display: 'Daily mood', + definition: + 'HKStateOfMind.Kind.dailyMood: how the participant felt over the day.', + }, + ], + }, + { + id: 'valence-classification', + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'valence-classification', + display: 'Valence classification', + title: 'Valence classification', + definition: + 'HKStateOfMind.ValenceClassification: the coarse band the reported valence falls in.', + cardinality: '0..1', + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-state-of-mind-valence-classification', + resultCodes: [ + { + code: 'very-unpleasant', + display: 'Very unpleasant', + definition: 'HKStateOfMind.ValenceClassification.veryUnpleasant.', + }, + { + code: 'unpleasant', + display: 'Unpleasant', + definition: 'HKStateOfMind.ValenceClassification.unpleasant.', + }, + { + code: 'slightly-unpleasant', + display: 'Slightly unpleasant', + definition: + 'HKStateOfMind.ValenceClassification.slightlyUnpleasant.', + }, + { + code: 'neutral', + display: 'Neutral', + definition: 'HKStateOfMind.ValenceClassification.neutral.', + }, + { + code: 'slightly-pleasant', + display: 'Slightly pleasant', + definition: + 'HKStateOfMind.ValenceClassification.slightlyPleasant.', + }, + { + code: 'pleasant', + display: 'Pleasant', + definition: 'HKStateOfMind.ValenceClassification.pleasant.', + }, + { + code: 'very-pleasant', + display: 'Very pleasant', + definition: 'HKStateOfMind.ValenceClassification.veryPleasant.', + }, + ], + }, + { + id: 'label', + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'state-of-mind-label', + display: 'Label', + title: 'Label', + definition: + 'HKStateOfMind.Label: a word the participant chose for the feeling. Repeats once per chosen label.', + cardinality: '0..*', + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-state-of-mind-label', + resultCodes: [ + { + code: 'amazed', + display: 'Amazed', + definition: 'HKStateOfMind.Label.amazed.', + }, + { + code: 'amused', + display: 'Amused', + definition: 'HKStateOfMind.Label.amused.', + }, + { + code: 'angry', + display: 'Angry', + definition: 'HKStateOfMind.Label.angry.', + }, + { + code: 'anxious', + display: 'Anxious', + definition: 'HKStateOfMind.Label.anxious.', + }, + { + code: 'ashamed', + display: 'Ashamed', + definition: 'HKStateOfMind.Label.ashamed.', + }, + { + code: 'brave', + display: 'Brave', + definition: 'HKStateOfMind.Label.brave.', + }, + { + code: 'calm', + display: 'Calm', + definition: 'HKStateOfMind.Label.calm.', + }, + { + code: 'content', + display: 'Content', + definition: 'HKStateOfMind.Label.content.', + }, + { + code: 'disappointed', + display: 'Disappointed', + definition: 'HKStateOfMind.Label.disappointed.', + }, + { + code: 'discouraged', + display: 'Discouraged', + definition: 'HKStateOfMind.Label.discouraged.', + }, + { + code: 'disgusted', + display: 'Disgusted', + definition: 'HKStateOfMind.Label.disgusted.', + }, + { + code: 'embarrassed', + display: 'Embarrassed', + definition: 'HKStateOfMind.Label.embarrassed.', + }, + { + code: 'excited', + display: 'Excited', + definition: 'HKStateOfMind.Label.excited.', + }, + { + code: 'frustrated', + display: 'Frustrated', + definition: 'HKStateOfMind.Label.frustrated.', + }, + { + code: 'grateful', + display: 'Grateful', + definition: 'HKStateOfMind.Label.grateful.', + }, + { + code: 'guilty', + display: 'Guilty', + definition: 'HKStateOfMind.Label.guilty.', + }, + { + code: 'happy', + display: 'Happy', + definition: 'HKStateOfMind.Label.happy.', + }, + { + code: 'hopeless', + display: 'Hopeless', + definition: 'HKStateOfMind.Label.hopeless.', + }, + { + code: 'irritated', + display: 'Irritated', + definition: 'HKStateOfMind.Label.irritated.', + }, + { + code: 'jealous', + display: 'Jealous', + definition: 'HKStateOfMind.Label.jealous.', + }, + { + code: 'joyful', + display: 'Joyful', + definition: 'HKStateOfMind.Label.joyful.', + }, + { + code: 'lonely', + display: 'Lonely', + definition: 'HKStateOfMind.Label.lonely.', + }, + { + code: 'passionate', + display: 'Passionate', + definition: 'HKStateOfMind.Label.passionate.', + }, + { + code: 'peaceful', + display: 'Peaceful', + definition: 'HKStateOfMind.Label.peaceful.', + }, + { + code: 'proud', + display: 'Proud', + definition: 'HKStateOfMind.Label.proud.', + }, + { + code: 'relieved', + display: 'Relieved', + definition: 'HKStateOfMind.Label.relieved.', + }, + { + code: 'sad', + display: 'Sad', + definition: 'HKStateOfMind.Label.sad.', + }, + { + code: 'scared', + display: 'Scared', + definition: 'HKStateOfMind.Label.scared.', + }, + { + code: 'stressed', + display: 'Stressed', + definition: 'HKStateOfMind.Label.stressed.', + }, + { + code: 'surprised', + display: 'Surprised', + definition: 'HKStateOfMind.Label.surprised.', + }, + { + code: 'worried', + display: 'Worried', + definition: 'HKStateOfMind.Label.worried.', + }, + { + code: 'annoyed', + display: 'Annoyed', + definition: 'HKStateOfMind.Label.annoyed.', + }, + { + code: 'confident', + display: 'Confident', + definition: 'HKStateOfMind.Label.confident.', + }, + { + code: 'drained', + display: 'Drained', + definition: 'HKStateOfMind.Label.drained.', + }, + { + code: 'hopeful', + display: 'Hopeful', + definition: 'HKStateOfMind.Label.hopeful.', + }, + { + code: 'indifferent', + display: 'Indifferent', + definition: 'HKStateOfMind.Label.indifferent.', + }, + { + code: 'overwhelmed', + display: 'Overwhelmed', + definition: 'HKStateOfMind.Label.overwhelmed.', + }, + { + code: 'satisfied', + display: 'Satisfied', + definition: 'HKStateOfMind.Label.satisfied.', + }, + ], + }, + { + id: 'association', + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'state-of-mind-association', + display: 'Association', + title: 'Association', + definition: + 'HKStateOfMind.Association: a life area the participant attributed the feeling to. Repeats once per association.', + cardinality: '0..*', + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-state-of-mind-association', + resultCodes: [ + { + code: 'community', + display: 'Community', + definition: 'HKStateOfMind.Association.community.', + }, + { + code: 'current-events', + display: 'Current events', + definition: 'HKStateOfMind.Association.currentEvents.', + }, + { + code: 'dating', + display: 'Dating', + definition: 'HKStateOfMind.Association.dating.', + }, + { + code: 'education', + display: 'Education', + definition: 'HKStateOfMind.Association.education.', + }, + { + code: 'family', + display: 'Family', + definition: 'HKStateOfMind.Association.family.', + }, + { + code: 'fitness', + display: 'Fitness', + definition: 'HKStateOfMind.Association.fitness.', + }, + { + code: 'friends', + display: 'Friends', + definition: 'HKStateOfMind.Association.friends.', + }, + { + code: 'health', + display: 'Health', + definition: 'HKStateOfMind.Association.health.', + }, + { + code: 'hobbies', + display: 'Hobbies', + definition: 'HKStateOfMind.Association.hobbies.', + }, + { + code: 'identity', + display: 'Identity', + definition: 'HKStateOfMind.Association.identity.', + }, + { + code: 'money', + display: 'Money', + definition: 'HKStateOfMind.Association.money.', + }, + { + code: 'partner', + display: 'Partner', + definition: 'HKStateOfMind.Association.partner.', + }, + { + code: 'self-care', + display: 'Self care', + definition: 'HKStateOfMind.Association.selfCare.', + }, + { + code: 'spirituality', + display: 'Spirituality', + definition: 'HKStateOfMind.Association.spirituality.', + }, + { + code: 'tasks', + display: 'Tasks', + definition: 'HKStateOfMind.Association.tasks.', + }, + { + code: 'travel', + display: 'Travel', + definition: 'HKStateOfMind.Association.travel.', + }, + { + code: 'work', + display: 'Work', + definition: 'HKStateOfMind.Association.work.', + }, + { + code: 'weather', + display: 'Weather', + definition: 'HKStateOfMind.Association.weather.', + }, + ], + }, + ], + }, + 'swimming-stroke-count': { + id: 'swimming-stroke-count', + profile: 'healthkit-swimming-stroke-count', + standardProfile: null, + title: 'Swimming Stroke Count', + description: + 'The number of swimming strokes recorded during an exact effective Period, normalized to the UCUM annotation {strokes}.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'swimming-stroke-count', + display: 'Swimming stroke count', + definition: + 'The total number of swimming strokes attributed to the exact Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{strokes}', + unit: 'strokes', + example: 640, + dimension: {}, + }, + effective: 'Period', + }, + 'symptom-abdominal-cramps': { + id: 'symptom-abdominal-cramps', + profile: 'healthkit-symptom-abdominal-cramps', + standardProfile: null, + title: 'Symptom: Abdominal Cramps', + description: + 'User-logged abdominal cramps with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-abdominal-cramps', + display: 'Abdominal cramps', + definition: + 'Presence and severity of abdominal cramps as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-acne': { + id: 'symptom-acne', + profile: 'healthkit-symptom-acne', + standardProfile: null, + title: 'Symptom: Acne', + description: + 'User-logged acne with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-acne', + display: 'Acne', + definition: 'Presence and severity of acne as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-appetite-changes': { + id: 'symptom-appetite-changes', + profile: 'healthkit-symptom-appetite-changes', + standardProfile: null, + title: 'Symptom: Appetite Changes', + description: + 'User-logged change in appetite. Uses its own Grove appetite-change value set, not the severity set: HealthKit types this with HKCategoryValueAppetiteChanges (direction of change), not HKCategoryValueSeverity.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-appetite-changes', + display: 'Appetite changes', + definition: 'Direction of a reported change in appetite.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-symptom-appetite-changes', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-symptom-appetite-changes', + allowedValues: [ + 'no-change', + 'decreased', + 'increased', + 'change-unspecified', + ], + resultCodes: [ + { + code: 'no-change', + display: 'No change', + definition: 'No change in appetite.', + }, + { + code: 'decreased', + display: 'Decreased', + definition: 'Appetite decreased.', + }, + { + code: 'increased', + display: 'Increased', + definition: 'Appetite increased.', + }, + { + code: 'change-unspecified', + display: 'Changed, direction unspecified', + definition: 'Appetite changed; the direction was not specified.', + }, + ], + effective: 'Period', + }, + 'symptom-bloating': { + id: 'symptom-bloating', + profile: 'healthkit-symptom-bloating', + standardProfile: null, + title: 'Symptom: Bloating', + description: + 'User-logged abdominal bloating with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-bloating', + display: 'Bloating', + definition: + 'Presence and severity of abdominal bloating as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-breast-pain': { + id: 'symptom-breast-pain', + profile: 'healthkit-symptom-breast-pain', + standardProfile: null, + title: 'Symptom: Breast Pain', + description: + 'User-logged breast pain with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-breast-pain', + display: 'Breast pain', + definition: + 'Presence and severity of breast pain as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-chest-tightness-or-pain': { + id: 'symptom-chest-tightness-or-pain', + profile: 'healthkit-symptom-chest-tightness-or-pain', + standardProfile: null, + title: 'Symptom: Chest Tightness or Pain', + description: + 'User-logged chest tightness or pain with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-chest-tightness-or-pain', + display: 'Chest tightness or pain', + definition: + 'Presence and severity of chest tightness or pain as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-chills': { + id: 'symptom-chills', + profile: 'healthkit-symptom-chills', + standardProfile: null, + title: 'Symptom: Chills', + description: + 'User-logged chills with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-chills', + display: 'Chills', + definition: 'Presence and severity of chills as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-constipation': { + id: 'symptom-constipation', + profile: 'healthkit-symptom-constipation', + standardProfile: null, + title: 'Symptom: Constipation', + description: + 'User-logged constipation with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-constipation', + display: 'Constipation', + definition: + 'Presence and severity of constipation as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-coughing': { + id: 'symptom-coughing', + profile: 'healthkit-symptom-coughing', + standardProfile: null, + title: 'Symptom: Coughing', + description: + 'User-logged coughing with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-coughing', + display: 'Coughing', + definition: + 'Presence and severity of coughing as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-diarrhea': { + id: 'symptom-diarrhea', + profile: 'healthkit-symptom-diarrhea', + standardProfile: null, + title: 'Symptom: Diarrhea', + description: + 'User-logged diarrhea with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-diarrhea', + display: 'Diarrhea', + definition: + 'Presence and severity of diarrhea as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-dizziness': { + id: 'symptom-dizziness', + profile: 'healthkit-symptom-dizziness', + standardProfile: null, + title: 'Symptom: Dizziness', + description: + 'User-logged dizziness with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-dizziness', + display: 'Dizziness', + definition: + 'Presence and severity of dizziness as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-dry-skin': { + id: 'symptom-dry-skin', + profile: 'healthkit-symptom-dry-skin', + standardProfile: null, + title: 'Symptom: Dry Skin', + description: + 'User-logged dry skin with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-dry-skin', + display: 'Dry skin', + definition: + 'Presence and severity of dry skin as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-fainting': { + id: 'symptom-fainting', + profile: 'healthkit-symptom-fainting', + standardProfile: null, + title: 'Symptom: Fainting', + description: + 'User-logged fainting (syncope) with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-fainting', + display: 'Fainting', + definition: + 'Presence and severity of fainting as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-fatigue': { + id: 'symptom-fatigue', + profile: 'healthkit-symptom-fatigue', + standardProfile: null, + title: 'Symptom: Fatigue', + description: + 'User-logged fatigue with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-fatigue', + display: 'Fatigue', + definition: 'Presence and severity of fatigue as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-fever': { + id: 'symptom-fever', + profile: 'healthkit-symptom-fever', + standardProfile: null, + title: 'Symptom: Fever', + description: + 'User-logged fever with an ordinal severity grade from the shared Grove symptom-severity value set. This is symptom self-report, not a body-temperature quantity; measured temperature stays a separate measurement.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-fever', + display: 'Fever', + definition: 'Presence and severity of fever as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-generalized-body-ache': { + id: 'symptom-generalized-body-ache', + profile: 'healthkit-symptom-generalized-body-ache', + standardProfile: null, + title: 'Symptom: Generalized Body Ache', + description: + 'User-logged generalized body ache with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-generalized-body-ache', + display: 'Generalized body ache', + definition: + 'Presence and severity of generalized body ache as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-hair-loss': { + id: 'symptom-hair-loss', + profile: 'healthkit-symptom-hair-loss', + standardProfile: null, + title: 'Symptom: Hair Loss', + description: + 'User-logged hair loss with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-hair-loss', + display: 'Hair loss', + definition: + 'Presence and severity of hair loss as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-headache': { + id: 'symptom-headache', + profile: 'healthkit-symptom-headache', + standardProfile: null, + title: 'Symptom: Headache', + description: + 'User-logged headache with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-headache', + display: 'Headache', + definition: + 'Presence and severity of headache as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-heartburn': { + id: 'symptom-heartburn', + profile: 'healthkit-symptom-heartburn', + standardProfile: null, + title: 'Symptom: Heartburn', + description: + 'User-logged heartburn with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-heartburn', + display: 'Heartburn', + definition: + 'Presence and severity of heartburn as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-hot-flashes': { + id: 'symptom-hot-flashes', + profile: 'healthkit-symptom-hot-flashes', + standardProfile: null, + title: 'Symptom: Hot Flashes', + description: + 'User-logged hot flashes with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-hot-flashes', + display: 'Hot flashes', + definition: + 'Presence and severity of hot flashes as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-loss-of-smell': { + id: 'symptom-loss-of-smell', + profile: 'healthkit-symptom-loss-of-smell', + standardProfile: null, + title: 'Symptom: Loss of Smell', + description: + 'User-logged loss of smell with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-loss-of-smell', + display: 'Loss of smell', + definition: + 'Presence and severity of loss of smell as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-loss-of-taste': { + id: 'symptom-loss-of-taste', + profile: 'healthkit-symptom-loss-of-taste', + standardProfile: null, + title: 'Symptom: Loss of Taste', + description: + 'User-logged loss of taste with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-loss-of-taste', + display: 'Loss of taste', + definition: + 'Presence and severity of loss of taste as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-lower-back-pain': { + id: 'symptom-lower-back-pain', + profile: 'healthkit-symptom-lower-back-pain', + standardProfile: null, + title: 'Symptom: Lower Back Pain', + description: + 'User-logged lower back pain with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-lower-back-pain', + display: 'Lower back pain', + definition: + 'Presence and severity of lower back pain as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-memory-lapse': { + id: 'symptom-memory-lapse', + profile: 'healthkit-symptom-memory-lapse', + standardProfile: null, + title: 'Symptom: Memory Lapse', + description: + 'User-logged memory lapse with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-memory-lapse', + display: 'Memory lapse', + definition: + 'Presence and severity of memory lapses as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-mood-changes': { + id: 'symptom-mood-changes', + profile: 'healthkit-symptom-mood-changes', + standardProfile: null, + title: 'Symptom: Mood Changes', + description: + 'User-logged mood changes recorded as presence only. HealthKit types this with HKCategoryValuePresence, so it binds the two-code presence subset of the Grove symptom value set, not the severity grades.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-mood-changes', + display: 'Mood changes', + definition: 'Whether the user reported experiencing mood changes.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-presence', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present'], + effective: 'Period', + }, + 'symptom-nausea': { + id: 'symptom-nausea', + profile: 'healthkit-symptom-nausea', + standardProfile: null, + title: 'Symptom: Nausea', + description: + 'User-logged nausea with an ordinal severity grade from the shared Grove symptom-severity value set. Coded with exact LOINC 81660-3 Nausea [Presence], the one generic, non-instrument LOINC symptom code found for this family.', + code: { + system: 'http://loinc.org', + code: '81660-3', + display: 'Nausea [Presence]', + definition: 'Presence and severity of nausea as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-night-sweats': { + id: 'symptom-night-sweats', + profile: 'healthkit-symptom-night-sweats', + standardProfile: null, + title: 'Symptom: Night Sweats', + description: + 'User-logged night sweats with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-night-sweats', + display: 'Night sweats', + definition: + 'Presence and severity of night sweats as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-pelvic-pain': { + id: 'symptom-pelvic-pain', + profile: 'healthkit-symptom-pelvic-pain', + standardProfile: null, + title: 'Symptom: Pelvic Pain', + description: + 'User-logged pelvic pain with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-pelvic-pain', + display: 'Pelvic pain', + definition: + 'Presence and severity of pelvic pain as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-rapid-pounding-or-fluttering-heartbeat': { + id: 'symptom-rapid-pounding-or-fluttering-heartbeat', + profile: 'healthkit-symptom-rapid-pounding-or-fluttering-heartbeat', + standardProfile: null, + title: 'Symptom: Rapid, Pounding, or Fluttering Heartbeat', + description: + 'User-logged palpitations (rapid, pounding, or fluttering heartbeat) with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-rapid-pounding-or-fluttering-heartbeat', + display: 'Rapid, pounding, or fluttering heartbeat', + definition: + 'Presence and severity of a rapid, pounding, or fluttering heartbeat as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-runny-nose': { + id: 'symptom-runny-nose', + profile: 'healthkit-symptom-runny-nose', + standardProfile: null, + title: 'Symptom: Runny Nose', + description: + 'User-logged runny nose with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-runny-nose', + display: 'Runny nose', + definition: + 'Presence and severity of a runny nose as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-shortness-of-breath': { + id: 'symptom-shortness-of-breath', + profile: 'healthkit-symptom-shortness-of-breath', + standardProfile: null, + title: 'Symptom: Shortness of Breath', + description: + 'User-logged shortness of breath with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-shortness-of-breath', + display: 'Shortness of breath', + definition: + 'Presence and severity of shortness of breath as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-sinus-congestion': { + id: 'symptom-sinus-congestion', + profile: 'healthkit-symptom-sinus-congestion', + standardProfile: null, + title: 'Symptom: Sinus Congestion', + description: + 'User-logged sinus congestion with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-sinus-congestion', + display: 'Sinus congestion', + definition: + 'Presence and severity of sinus congestion as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-skipped-heartbeat': { + id: 'symptom-skipped-heartbeat', + profile: 'healthkit-symptom-skipped-heartbeat', + standardProfile: null, + title: 'Symptom: Skipped Heartbeat', + description: + 'User-logged skipped heartbeat with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-skipped-heartbeat', + display: 'Skipped heartbeat', + definition: + 'Presence and severity of a skipped heartbeat sensation as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-sleep-changes': { + id: 'symptom-sleep-changes', + profile: 'healthkit-symptom-sleep-changes', + standardProfile: null, + title: 'Symptom: Sleep Changes', + description: + 'User-logged sleep changes recorded as presence only. HealthKit types this with HKCategoryValuePresence, so it binds the two-code presence subset of the Grove symptom value set, not the severity grades.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-sleep-changes', + display: 'Sleep changes', + definition: 'Whether the user reported changes in their sleep.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-presence', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present'], + effective: 'Period', + }, + 'symptom-sore-throat': { + id: 'symptom-sore-throat', + profile: 'healthkit-symptom-sore-throat', + standardProfile: null, + title: 'Symptom: Sore Throat', + description: + 'User-logged sore throat with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-sore-throat', + display: 'Sore throat', + definition: + 'Presence and severity of a sore throat as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-vomiting': { + id: 'symptom-vomiting', + profile: 'healthkit-symptom-vomiting', + standardProfile: null, + title: 'Symptom: Vomiting', + description: + 'User-logged vomiting with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-vomiting', + display: 'Vomiting', + definition: + 'Presence and severity of vomiting as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'symptom-wheezing': { + id: 'symptom-wheezing', + profile: 'healthkit-symptom-wheezing', + standardProfile: null, + title: 'Symptom: Wheezing', + description: + 'User-logged wheezing with an ordinal severity grade from the shared Grove symptom-severity value set.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'symptom-wheezing', + display: 'Wheezing', + definition: + 'Presence and severity of wheezing as reported by the user.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'Period', + }, + 'time-in-daylight': { + id: 'time-in-daylight', + profile: 'healthkit-time-in-daylight', + standardProfile: null, + title: 'Time in Daylight', + description: + 'The total time the user spent in daylight during an exact effective Period, normalized to UCUM minutes. Only HealthKit evidences this concept.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'time-in-daylight', + display: 'Time in daylight', + definition: + 'The cumulative time the user spent in daylight during the exact Observation effective Period, as measured by the source ambient light sensing.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'min', + unit: 'min', + example: 47, + dimension: { + s: 1, + }, + }, + effective: 'Period', + }, + 'toothbrushing-session': { + id: 'toothbrushing-session', + profile: 'healthkit-toothbrushing-session', + standardProfile: null, + title: 'Toothbrushing Session', + description: + 'HKCategoryTypeIdentifierToothbrushingEvent: an interval sample (notApplicable value) whose start/end dates carry the toothbrushing duration. Emitted as a duration quantity over the exact Period.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'toothbrushing-session', + display: 'Toothbrushing session', + definition: + 'The duration of one toothbrushing event during the exact Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 's', + unit: 's', + example: 120, + dimension: { + s: 1, + }, + }, + effective: 'Period', + }, + 'underwater-depth': { + id: 'underwater-depth', + profile: 'healthkit-underwater-depth', + standardProfile: null, + title: 'Underwater Depth', + description: + "A HealthKit-exclusive measurement of the wearer's depth below the water surface during a submersion, normalized to UCUM metres. It is an environmental position reading exclusive to Apple Watch Ultra submersions.", + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'underwater-depth', + display: 'Underwater depth', + definition: + 'The depth of the wearer below the water surface during a submersion, as sampled by the device.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'm', + unit: 'm', + example: 12, + dimension: { + m: 1, + }, + }, + effective: 'dateTime', + }, + 'uv-exposure': { + id: 'uv-exposure', + profile: 'healthkit-uv-exposure', + standardProfile: null, + title: 'UV Exposure', + description: + 'The dimensionless Global Solar UV Index the user was exposed to during an exact effective Period. Only HealthKit evidences this concept, and values are typically app-contributed rather than sensor-derived.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'uv-exposure', + display: 'UV exposure', + definition: + 'The Global Solar UV Index value the user was exposed to during the exact Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{uvindex}', + unit: 'UV index', + example: 6, + dimension: {}, + }, + effective: 'Period', + }, + 'vaginal-dryness': { + id: 'vaginal-dryness', + profile: 'healthkit-vaginal-dryness', + standardProfile: null, + title: 'Vaginal Dryness', + description: + 'HKCategoryTypeIdentifierVaginalDryness: records vaginal dryness as a symptom using HKCategoryValueSeverity (verified cases: unspecified, notPresent, mild, moderate, severe).', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'vaginal-dryness', + display: 'Vaginal dryness', + definition: 'Logged severity of vaginal dryness.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/grove-symptom-severity', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/grove-symptom-severity', + allowedValues: ['not-present', 'present', 'mild', 'moderate', 'severe'], + effective: 'dateTime', + }, + 'waist-circumference': { + id: 'waist-circumference', + profile: 'healthkit-waist-circumference', + standardProfile: null, + title: 'Waist Circumference', + description: + 'A HealthKit-exclusive waist circumference normalized to UCUM centimetres. The LOINC code asserts the umbilicus site and tape-measure method that HealthKit does not itself state; this is the established consumer-health interoperability mapping and the caveat is documented rather than inventing a Grove code.', + code: { + system: 'http://loinc.org', + code: '8280-0', + display: 'Waist Circumference at umbilicus by Tape measure', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'cm', + unit: 'cm', + example: 84, + dimension: { + m: 1, + }, + }, + effective: 'dateTime', + }, + 'walking-asymmetry': { + id: 'walking-asymmetry', + profile: 'healthkit-walking-asymmetry', + standardProfile: null, + title: 'Walking Asymmetry', + description: + 'The percentage of walking time in which the step timing of one foot differs from the other, recorded passively by HealthKit and normalized to UCUM percent. Higher values indicate a less even gait.', + code: { + system: 'http://loinc.org', + code: '112432-0', + display: 'Walking asymmetry', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '%', + unit: '%', + example: 1.4, + dimension: {}, + }, + effective: 'dateTime', + }, + 'walking-double-support': { + id: 'walking-double-support', + profile: 'healthkit-walking-double-support', + standardProfile: null, + title: 'Walking Double Support', + description: + 'The percentage of walking time in which both feet are in contact with the ground, recorded passively by HealthKit and normalized to UCUM percent.', + code: { + system: 'http://loinc.org', + code: '112434-6', + display: 'Walking double support [Percentile]', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '%', + unit: '%', + example: 27, + dimension: {}, + }, + effective: 'dateTime', + }, + 'walking-heart-rate-average': { + id: 'walking-heart-rate-average', + profile: 'healthkit-walking-heart-rate-average', + standardProfile: null, + title: 'Walking Heart Rate Average', + description: + 'The average heart rate during walking activity over a daily window, normalized to UCUM beats per minute. HealthKit-only evidence today; the phase-2 aggregate design implements the windowing.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'walking-heart-rate-average', + display: 'Walking heart rate average', + definition: + 'The mean heart rate during periods classified as walking within the exact Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: 'beats/minute', + example: 98, + dimension: { + s: -1, + }, + }, + method: { + code: 'daily-mean', + display: 'Daily mean', + }, + effective: 'Period', + }, + 'walking-speed': { + id: 'walking-speed', + profile: 'healthkit-walking-speed', + standardProfile: null, + title: 'Walking Speed', + description: + 'The instantaneous-to-short-bout walking speed HealthKit records passively as a mobility metric, normalized to UCUM metres per second. It is distinct from the shared exercise speed measurement: the walking binding is part of the concept, and it is a gait-health signal rather than a workout series.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'walking-speed', + display: 'Walking speed', + definition: + 'The speed at which the subject walks, sampled by the source during ordinary walking bouts.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'm/s', + unit: 'm/s', + example: 1.32, + dimension: { + m: 1, + s: -1, + }, + }, + effective: 'dateTime', + }, + 'walking-steadiness': { + id: 'walking-steadiness', + profile: 'healthkit-walking-steadiness', + standardProfile: null, + title: 'Walking Steadiness', + description: + "Apple's windowed walking-steadiness score, a rolling multi-day percentage summarizing gait stability, normalized to UCUM percent over the exact aggregation Period. As a windowed aggregate it is implemented by the phase-2 aggregate design rather than as a point measurement.", + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'walking-steadiness', + display: 'Walking steadiness', + definition: + "A windowed percentage score summarizing the stability of the subject's gait over the aggregation period.", + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '%', + unit: '%', + example: 82, + dimension: {}, + }, + method: { + code: 'rolling-mean', + display: 'Rolling mean', + }, + effective: 'Period', + }, + 'walking-steadiness-notification': { + id: 'walking-steadiness-notification', + profile: 'healthkit-walking-steadiness-notification', + standardProfile: null, + title: 'Walking Steadiness Notification', + description: + 'HKCategoryTypeIdentifierAppleWalkingSteadinessEvent: raised when the walking steadiness classification is low or very low, distinguishing a first occurrence from a repeat. The walking steadiness percentage remains the measurement surface.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'walking-steadiness-notification', + display: 'Walking Steadiness Notification', + definition: + 'A device notification that walking steadiness reached a low or very low classification.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-walking-steadiness-notification', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-walking-steadiness-notification', + allowedValues: ['low', 'very-low'], + resultCodes: [ + { + code: 'low', + display: 'Low', + definition: + 'HKCategoryValueAppleWalkingSteadinessEvent low classification.', + }, + { + code: 'very-low', + display: 'Very low', + definition: + 'HKCategoryValueAppleWalkingSteadinessEvent very low classification.', + }, + ], + effective: 'Period', + components: [ + { + id: 'notification-occurrence', + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'walking-steadiness-notification-occurrence', + display: 'Notification occurrence', + title: 'Notification occurrence', + cardinality: '1..1', + definition: + 'Whether this is the first notification at the classification or a repeat.', + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-notification-occurrence', + resultCodes: [ + { + code: 'initial', + display: 'Initial', + definition: + 'HKCategoryValueAppleWalkingSteadinessEvent initial notification.', + }, + { + code: 'repeat', + display: 'Repeat', + definition: + 'HKCategoryValueAppleWalkingSteadinessEvent repeat notification.', + }, + ], + }, + ], + }, + 'walking-step-length': { + id: 'walking-step-length', + profile: 'healthkit-walking-step-length', + standardProfile: null, + title: 'Walking Step Length', + description: + 'The average length of a single step during ordinary walking, recorded passively by HealthKit and normalized to UCUM centimetres.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'walking-step-length', + display: 'Walking step length', + definition: + 'The distance covered by a single step during ordinary walking, as sampled by the source.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'cm', + unit: 'cm', + example: 72, + dimension: { + m: 1, + }, + }, + effective: 'dateTime', + }, + 'water-temperature': { + id: 'water-temperature', + profile: 'healthkit-water-temperature', + standardProfile: null, + title: 'Water Temperature', + description: + 'A HealthKit-exclusive environmental measurement of the water temperature surrounding the wearer during a water activity, normalized to UCUM degrees Celsius. It is an environmental context reading, not a body measurement, and is never mapped to any body-temperature concept.', + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'water-temperature', + display: 'Water temperature', + definition: + 'The temperature of the water surrounding the wearer during a water activity, as sampled by the device.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'Cel', + unit: 'Cel', + example: 24.5, + dimension: { + K: 1, + }, + }, + effective: 'dateTime', + }, + 'wheelchair-use': { + id: 'wheelchair-use', + profile: 'healthkit-wheelchair-use', + standardProfile: null, + title: 'Grove HealthKit Wheelchair Use', + description: + "Wheelchair-use status as a coded Observation. No context-free ACTIVE LOINC exists: 89411-3 'Does the patient use a wheelchair/scooter' is Deprecated, and its ACTIVE successor 95738-1 'Does the patient use a wheelchair/scooter during assessment period [CMS Assessment]' is bound to a CMS assessment-period context and method, which the HealthKit characteristic does not carry — adopting it would fabricate an assessment context. Observation.code is therefore Grove-coded. The status materially changes interpretation of HK distanceWheelchair and pushCount data, which is why platform-exclusive support is defensible rather than intentionally-unsupported.", + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'wheelchair-use', + display: 'Wheelchair use', + definition: + 'Whether the subject uses a wheelchair, as recorded in the HealthKit wheelchair-use characteristic.', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-wheelchair-use', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-wheelchair-use', + allowedValues: ['uses-wheelchair', 'does-not-use-wheelchair'], + resultCodes: [ + { + code: 'uses-wheelchair', + display: 'Uses wheelchair', + definition: 'The subject uses a wheelchair.', + }, + { + code: 'does-not-use-wheelchair', + display: 'Does not use wheelchair', + definition: 'The subject does not use a wheelchair.', + }, + ], + effective: 'dateTime', + }, + 'workout-effort-score': { + id: 'workout-effort-score', + profile: 'healthkit-workout-effort-score', + standardProfile: null, + title: 'Workout Effort Score', + description: + "Apple's 1-10 workout effort rating over the associated workout's exact effective Period, covering both the user-entered WorkoutEffortScore and the system-computed EstimatedWorkoutEffortScore; the rating method (user-entered versus estimated) is a required adapter distinction. The value is normalized to the UCUM annotation {score}.", + code: { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', + code: 'workout-effort-score', + display: 'Workout effort score', + definition: + 'An Apple-proprietary 1-10 rating of perceived or estimated workout effort for the exact associated workout Period.', + }, + valueKind: 'quantity', + owner: 'healthkit', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{score}', + unit: 'score', + example: 7, + dimension: {}, + }, + effective: 'Period', + }, + }, + 'health-connect': { + 'basal-metabolic-rate': { + id: 'basal-metabolic-rate', + profile: 'health-connect-basal-metabolic-rate', + standardProfile: null, + title: 'Basal Metabolic Rate', + description: + 'The basal metabolic rate as a power, normalized to UCUM kilocalories per day. Only Health Connect evidences it (BasalMetabolicRateRecord), so it stays in the Health Connect adapter guide; it is never relabeled as resting metabolic rate or as an energy total.', + code: { + system: + 'https://grovealliance.org/fhir/health-connect/CodeSystem/health-connect-measurement', + code: 'basal-metabolic-rate', + display: 'Basal metabolic rate', + definition: + "The subject's basal metabolic rate, expressed as energy per day, reported at the Observation effective instant.", + }, + valueKind: 'quantity', + owner: 'health-connect', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kcal/d', + unit: 'kcal/day', + example: 1580, + dimension: { + g: 1, + m: 2, + s: -3, + }, + }, + effective: 'dateTime', + }, + 'dietary-energy-from-fat': { + id: 'dietary-energy-from-fat', + profile: 'health-connect-dietary-energy-from-fat', + standardProfile: null, + title: 'Health Connect Dietary Energy from Fat', + description: + 'Health Connect-only energy from fat consumed during an exact effective Period, normalized to UCUM kilocalories; HealthKit has no counterpart identifier.', + code: { + system: + 'https://grovealliance.org/fhir/health-connect/CodeSystem/health-connect-measurement', + code: 'dietary-energy-from-fat', + display: 'Nutrition energy from fat', + definition: + 'Energy-from-fat component of NutritionRecord, distinct from total energy.', + }, + valueKind: 'quantity', + owner: 'health-connect', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kcal', + unit: 'kcal', + example: 700, + dimension: { + g: 1, + m: 2, + s: -2, + }, + }, + effective: 'Period', + }, + 'dietary-fat-trans': { + id: 'dietary-fat-trans', + profile: 'health-connect-dietary-fat-trans', + standardProfile: null, + title: 'Health Connect Dietary Trans Fat', + description: + 'Health Connect-only trans fat mass consumed during an exact effective Period, normalized to UCUM grams; HealthKit has no trans-fat dietary identifier.', + code: { + system: + 'https://grovealliance.org/fhir/health-connect/CodeSystem/health-connect-measurement', + code: 'dietary-fat-trans', + display: 'Nutrition trans fat', + definition: + 'Trans fat component of NutritionRecord; the only LOINC trans-fat intake concepts (81034-1 Estimated, 81035-8 Measured) are 24-hour mass-rates and are rejected for the same axis reason as the shared fat rows.', + }, + valueKind: 'quantity', + owner: 'health-connect', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'g', + unit: 'g', + example: 1, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-fat-unsaturated': { + id: 'dietary-fat-unsaturated', + profile: 'health-connect-dietary-fat-unsaturated', + standardProfile: null, + title: 'Health Connect Dietary Unsaturated Fat', + description: + 'Health Connect-only total unsaturated fat mass consumed during an exact effective Period, normalized to UCUM grams; HealthKit has no combined unsaturated-fat identifier and the value cannot be derived when the mono and poly fields are null.', + code: { + system: + 'https://grovealliance.org/fhir/health-connect/CodeSystem/health-connect-measurement', + code: 'dietary-fat-unsaturated', + display: 'Nutrition unsaturated fat', + definition: 'Total unsaturated fat component of NutritionRecord.', + }, + valueKind: 'quantity', + owner: 'health-connect', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'g', + unit: 'g', + example: 57, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'dietary-folic-acid': { + id: 'dietary-folic-acid', + profile: 'health-connect-dietary-folic-acid', + standardProfile: null, + title: 'Health Connect Dietary Folic Acid', + description: + 'Health Connect-only synthetic folic acid mass consumed during an exact effective Period, normalized to UCUM micrograms; distinct from total folate.', + code: { + system: + 'https://grovealliance.org/fhir/health-connect/CodeSystem/health-connect-measurement', + code: 'dietary-folic-acid', + display: 'Nutrition folic acid', + definition: + 'Synthetic folic acid component of NutritionRecord; LOINC has no folic-acid intake concept separate from total folate (NLM search returns only the vitamin B9 folate pair).', + }, + valueKind: 'quantity', + owner: 'health-connect', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ug', + unit: 'ug', + example: 400, + dimension: { + g: 1, + }, + }, + effective: 'Period', + }, + 'elevation-gained': { + id: 'elevation-gained', + profile: 'health-connect-elevation-gained', + standardProfile: null, + title: 'Elevation Gained', + description: + 'Vertical elevation gained during an exact effective Period, normalized to UCUM metres. Withings intraday elevation series remain in the provider recording document and do not implement this profile.', + code: { + system: + 'https://grovealliance.org/fhir/health-connect/CodeSystem/health-connect-measurement', + code: 'elevation-gained', + display: 'Elevation gained', + definition: + 'The total vertical distance ascended during the exact Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'health-connect', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'm', + unit: 'm', + example: 120, + dimension: { + m: 1, + }, + }, + effective: 'Period', + }, + 'menstruation-period': { + id: 'menstruation-period', + profile: 'health-connect-menstruation-period', + standardProfile: null, + title: 'Menstruation Period', + description: + 'One user-recorded menstruation period interval. Health Connect records the interval directly; HealthKit derives intervals from flow samples and therefore never emits this profile.', + code: { + system: + 'https://grovealliance.org/fhir/health-connect/CodeSystem/health-connect-measurement', + code: 'menstruation-period', + display: 'Menstruation period', + definition: + 'One recorded menstruation period over the exact Observation effective Period.', + }, + valueKind: 'codeableConcept', + owner: 'health-connect', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/health-connect/ValueSet/health-connect-menstruation-period', + resultCodeSystem: + 'https://grovealliance.org/fhir/health-connect/CodeSystem/health-connect-menstruation-period', + allowedValues: ['present'], + resultCodes: [ + { + code: 'present', + display: 'Present', + definition: 'A menstruation period spanned the effective Period.', + }, + ], + effective: 'Period', + }, + 'step-cadence': { + id: 'step-cadence', + profile: 'health-connect-step-cadence', + standardProfile: null, + title: 'Step Cadence', + description: + 'The instantaneous rate of steps taken, normalized to UCUM steps per minute using the allowlisted {steps} annotation. It is distinct from cycling cadence (crank revolutions) and from the shared step-count total (a Period sum, not a rate).', + code: { + system: + 'https://grovealliance.org/fhir/health-connect/CodeSystem/health-connect-measurement', + code: 'step-cadence', + display: 'Step cadence', + definition: + 'The rate of steps the subject takes at the exact Observation effective instant.', + }, + valueKind: 'quantity', + owner: 'health-connect', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{steps}/min', + unit: 'steps/minute', + example: 112, + dimension: { + s: -1, + }, + }, + effective: 'dateTime', + }, + 'total-energy': { + id: 'total-energy', + profile: 'health-connect-total-energy', + standardProfile: null, + title: 'Total Energy Burned', + description: + 'Total energy expenditure - basal plus active - during an exact effective Period, normalized to UCUM kilocalories. It overlaps the shared active-energy and basal-energy measurements by definition and is therefore never emitted alongside a decomposition for the same interval by the same producer.', + code: { + system: + 'https://grovealliance.org/fhir/health-connect/CodeSystem/health-connect-measurement', + code: 'total-energy-burned', + display: 'Total energy burned', + definition: + 'The sum of basal and activity energy expended during the exact Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'health-connect', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kcal', + unit: 'kcal', + example: 2200, + dimension: { + g: 1, + m: 2, + s: -2, + }, + }, + effective: 'Period', + }, + }, + providers: { + 'body-fat-mass': { + id: 'body-fat-mass', + profile: 'provider-body-fat-mass', + standardProfile: null, + title: 'Body Fat Mass', + description: + 'Absolute fat mass computed by a body-composition scale, normalized to UCUM kilograms. Distinct from body-fat percentage; only Withings evidences an absolute fat-mass output, so the profile is provider-scoped.', + code: { + system: 'http://loinc.org', + code: '73708-0', + display: 'Body fat [Mass] Calculated', + }, + valueKind: 'quantity', + owner: 'providers', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kg', + unit: 'kg', + example: 16.4, + dimension: { + g: 1, + }, + }, + effective: 'dateTime', + }, + 'extracellular-water-mass': { + id: 'extracellular-water-mass', + profile: 'provider-extracellular-water-mass', + standardProfile: null, + title: 'Extracellular Water Mass', + description: + 'Mass of extracellular body water estimated by bioelectrical impedance, normalized to UCUM kilograms. Only Withings evidences the compartmentalized value, so the profile is provider-scoped and is distinct from total body-water mass.', + code: { + system: + 'https://grovealliance.org/fhir/providers/CodeSystem/provider-measurement', + code: 'extracellular-water-mass', + display: 'Extracellular water mass', + definition: + 'The mass of body water outside cells as estimated by bioelectrical impedance analysis.', + }, + valueKind: 'quantity', + owner: 'providers', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kg', + unit: 'kg', + example: 17, + dimension: { + g: 1, + }, + }, + effective: 'dateTime', + }, + 'intracellular-water-mass': { + id: 'intracellular-water-mass', + profile: 'provider-intracellular-water-mass', + standardProfile: null, + title: 'Intracellular Water Mass', + description: + 'Mass of intracellular body water estimated by bioelectrical impedance, normalized to UCUM kilograms. Only Withings evidences the compartmentalized value, so the profile is provider-scoped and is distinct from total body-water mass.', + code: { + system: + 'https://grovealliance.org/fhir/providers/CodeSystem/provider-measurement', + code: 'intracellular-water-mass', + display: 'Intracellular water mass', + definition: + 'The mass of body water inside cells as estimated by bioelectrical impedance analysis.', + }, + valueKind: 'quantity', + owner: 'providers', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kg', + unit: 'kg', + example: 25, + dimension: { + g: 1, + }, + }, + effective: 'dateTime', + }, + 'muscle-mass': { + id: 'muscle-mass', + profile: 'provider-muscle-mass', + standardProfile: null, + title: 'Muscle Mass', + description: + 'A providers-exclusive whole-body muscle mass calculated by bioimpedance analysis, normalized to UCUM kilograms. It is distinct from lean body mass (which additionally includes bone, water, and organ mass) and is not folded into that shared measurement.', + code: { + system: 'http://loinc.org', + code: '73964-9', + display: 'Body muscle mass Calculated', + }, + valueKind: 'quantity', + owner: 'providers', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'kg', + unit: 'kg', + example: 31.5, + dimension: { + g: 1, + }, + }, + effective: 'dateTime', + }, + 'sleeping-heart-rate-average': { + id: 'sleeping-heart-rate-average', + profile: 'provider-sleeping-heart-rate-average', + standardProfile: null, + title: 'Sleeping Heart Rate Average', + description: + 'The average heart rate across one sleep session, normalized to UCUM beats per minute. It is a session-windowed average, distinct from both the shared point heart-rate measurement and the daily resting-heart-rate estimate, and is implemented by the phase-2 aggregate design.', + code: { + system: + 'https://grovealliance.org/fhir/providers/CodeSystem/provider-measurement', + code: 'sleeping-heart-rate-average', + display: 'Sleeping heart rate average', + definition: + 'The mean heart rate across the exact sleep-session Observation effective Period.', + }, + valueKind: 'quantity', + owner: 'providers', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: 'beats/minute', + example: 55, + dimension: { + s: -1, + }, + }, + method: { + code: 'session-mean', + display: 'Session mean', + }, + effective: 'Period', + }, + }, +} as const + +export const adapterMeasurementCatalog: typeof adapterMeasurementCatalogValue = + deepFreeze(adapterMeasurementCatalogValue) + +const providerScalarMappingsValue = { + 'google-health-api': { + steps: { + 'step-count': 'step-count', + }, + distance: { + distance: 'distance', + }, + 'active-energy-burned': { + 'active-energy': 'active-energy', + }, + weight: { + 'body-weight': 'body-weight', + }, + 'body-fat': { + 'body-fat-percentage': 'body-fat-percentage', + }, + height: { + 'body-height': 'body-height', + }, + 'vo2-max': { + 'vo2-max': 'vo2-max', + }, + 'daily-oxygen-saturation': { + 'oxygen-saturation-daily-average': 'oxygen-saturation-daily-average', + }, + 'heart-rate-variability': { + 'heart-rate-variability-rmssd': 'heart-rate-variability-rmssd', + 'heart-rate-variability-sdnn': 'heart-rate-variability-sdnn', + }, + 'daily-resting-heart-rate': { + 'resting-heart-rate': 'resting-heart-rate', + }, + 'daily-respiratory-rate': { + 'respiratory-rate-average': 'respiratory-rate-average', + }, + 'blood-glucose': { + 'blood-glucose-unspecified-specimen': + 'blood-glucose-unspecified-specimen', + }, + 'core-body-temperature': { + 'body-temperature': 'body-temperature', + }, + floors: { + 'flights-climbed': 'flights-climbed', + }, + 'basal-energy-burned': { + 'basal-energy': 'basal-energy', + }, + sleep: { + 'sleep-duration': 'sleep-duration', + 'deep-sleep-duration': 'deep-sleep-duration', + 'rem-sleep-duration': 'rem-sleep-duration', + 'light-sleep-duration': 'light-sleep-duration', + 'sleep-awake-duration': 'sleep-awake-duration', + }, + exercise: { + workout: 'workout', + }, + }, + oura: { + daily_activity: { + 'step-count': 'step-count', + 'active-energy': 'active-energy', + distance: 'distance', + }, + sleep: { + 'sleep-duration': 'sleep-duration', + 'deep-sleep-duration': 'deep-sleep-duration', + 'rem-sleep-duration': 'rem-sleep-duration', + 'light-sleep-duration': 'light-sleep-duration', + 'sleep-awake-duration': 'sleep-awake-duration', + 'sleep-heart-rate': 'sleep-heart-rate', + 'respiratory-rate-average': 'respiratory-rate-average', + }, + daily_spo2: { + 'oxygen-saturation-daily-average': 'oxygen-saturation-daily-average', + }, + workout: { + workout: 'workout', + }, + vO2_max: { + 'vo2-max': 'vo2-max', + }, + }, + withings: { + 'getmeas:1': { + 'body-weight': 'body-weight', + }, + 'getmeas:4': { + 'body-height': 'body-height', + }, + 'getmeas:6': { + 'body-fat-percentage': 'body-fat-percentage', + }, + 'getmeas:11': { + 'heart-rate': 'heart-rate', + }, + 'getmeas:54': { + 'oxygen-saturation': 'oxygen-saturation', + }, + 'getmeas:71': { + 'body-temperature': 'body-temperature', + }, + 'getmeas:123': { + 'vo2-max': 'vo2-max', + }, + 'getactivity:steps': { + 'step-count': 'step-count', + }, + 'getactivity:distance': { + distance: 'distance', + }, + 'getactivity:calories': { + 'active-energy': 'active-energy', + }, + 'getsummary:deepsleepduration': { + 'deep-sleep-duration': 'deep-sleep-duration', + }, + 'getsummary:remsleepduration': { + 'rem-sleep-duration': 'rem-sleep-duration', + }, + 'getsummary:lightsleepduration': { + 'light-sleep-duration': 'light-sleep-duration', + }, + 'getsummary:wakeupduration': { + 'sleep-awake-duration': 'sleep-awake-duration', + }, + 'getsummary:hr_average': { + 'sleep-heart-rate': 'sleep-heart-rate', + 'sleeping-heart-rate-average': 'sleeping-heart-rate-average', + }, + 'getsummary:rr_average': { + 'respiratory-rate-average': 'respiratory-rate-average', + }, + 'getworkouts:interval': { + workout: 'workout', + }, + 'getmeas:5': { + 'lean-body-mass': 'lean-body-mass', + }, + 'getmeas:8': { + 'body-fat-mass': 'body-fat-mass', + }, + 'getmeas:73': { + 'skin-temperature': 'skin-temperature', + }, + 'getmeas:76': { + 'muscle-mass': 'muscle-mass', + }, + 'getmeas:77': { + 'body-water-mass': 'body-water-mass', + }, + 'getmeas:88': { + 'bone-mass': 'bone-mass', + }, + 'getmeas:168': { + 'extracellular-water-mass': 'extracellular-water-mass', + }, + 'getmeas:169': { + 'intracellular-water-mass': 'intracellular-water-mass', + }, + 'getmeas:196': { + 'electrodermal-activity': 'electrodermal-activity', + }, + 'getmeas:9+10': { + 'blood-pressure': 'blood-pressure-panel', + }, + }, +} as const + +export const providerScalarMappings: typeof providerScalarMappingsValue = + deepFreeze(providerScalarMappingsValue) + +const providerRecordEffectiveRulesValue = { + oura: { + daily_activity: { + kind: 'complete-civil-day-period', + measurementIds: ['step-count', 'active-energy', 'distance'], + outputsShareEffective: true, + }, + }, + withings: { + 'getactivity:steps': { + kind: 'complete-civil-day-period', + measurementIds: ['step-count'], + outputsShareEffective: true, + }, + 'getactivity:distance': { + kind: 'complete-civil-day-period', + measurementIds: ['distance'], + outputsShareEffective: true, + }, + 'getactivity:calories': { + kind: 'complete-civil-day-period', + measurementIds: ['active-energy'], + outputsShareEffective: true, + }, + }, +} as const + +export const providerRecordEffectiveRules: typeof providerRecordEffectiveRulesValue = + deepFreeze(providerRecordEffectiveRulesValue) + +const providerRawMappingsValue = { + 'google-health-api': { + 'heart-rate': 'native-recording', + }, + oura: { + heartrate: 'native-recording', + }, + withings: { + activityIntraday: 'native-recording', + sleepIntraday: 'native-recording', + }, +} as const + +export const providerRawMappings: typeof providerRawMappingsValue = deepFreeze( + providerRawMappingsValue, +) + +const groveRecordingFormatRegistryValue = { + codeSystem: + 'https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format', + valueSet: + 'https://grovealliance.org/fhir/sensor/ValueSet/grove-recording-format', + formats: { + 'heart-rate-samples': { + title: 'Heart Rate Samples', + contentType: 'text/csv', + status: 'active', + }, + 'triaxial-acceleration-samples': { + title: 'Triaxial Acceleration Samples', + contentType: 'text/csv', + status: 'active', + }, + 'ambient-light-samples': { + title: 'Ambient Light Samples', + contentType: 'text/csv', + status: 'active', + }, + 'ambient-pressure-samples': { + title: 'Ambient Pressure Samples', + contentType: 'text/csv', + status: 'active', + }, + 'pedometer-samples': { + title: 'Pedometer Samples', + contentType: 'text/csv', + status: 'active', + }, + 'wrist-temperature-samples': { + title: 'Wrist Temperature Samples', + contentType: 'text/csv', + status: 'active', + }, + 'triaxial-rotation-samples': { + title: 'Triaxial Rotation Samples', + contentType: 'text/csv', + status: 'active', + }, + 'odometer-samples': { + title: 'Odometer Samples', + contentType: 'text/csv', + status: 'active', + }, + 'beat-interval-series': { + title: 'Beat Interval Series', + contentType: 'text/csv', + status: 'active', + }, + 'location-track-samples': { + title: 'Location Track Samples', + contentType: 'text/csv', + status: 'active', + }, + 'fhir-resource-array': { + title: 'FHIR Resource Array', + contentType: 'application/vnd.grovealliance.fhir-array+json', + status: 'active', + }, + 'fhir-resource': { + title: 'FHIR Resource', + contentType: 'application/fhir+json', + status: 'active', + }, + 'clinical-document': { + title: 'Clinical Document', + contentType: 'application/hl7-cda+xml', + status: 'active', + }, + 'native-recording': { + title: 'Native Recording', + contentType: 'application/vnd.grovealliance.native+json', + status: 'active', + }, + 'provider-recording': { + title: 'Provider Recording', + contentType: 'application/vnd.grovealliance.provider+json', + status: 'active', + }, + 'photoplethysmogram-samples': { + title: 'Photoplethysmogram Samples', + contentType: 'application/vnd.grovealliance.ppg', + status: 'active', + }, + }, +} as const + +export const groveRecordingFormatRegistry: typeof groveRecordingFormatRegistryValue = + deepFreeze(groveRecordingFormatRegistryValue) + +export type AdapterMeasurementCatalog = typeof adapterMeasurementCatalog + +export type ProviderRecordingFormat = + keyof typeof groveRecordingFormatRegistry.formats + +export type ProviderScalarMappings = typeof providerScalarMappings + +export type ProviderRecordEffectiveRules = typeof providerRecordEffectiveRules + +export type ProviderRawMappings = typeof providerRawMappings diff --git a/packages/fhir/src/providers/graph.ts b/packages/fhir/src/providers/graph.ts new file mode 100644 index 00000000..413a2573 --- /dev/null +++ b/packages/fhir/src/providers/graph.ts @@ -0,0 +1,139 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { EXTENSIONS, PROFILES, SYSTEMS } from './profiles.js' +import type { + ApplicationDeviceInput, + CompleteIdentifierInput, + IdentifiedEntryIdentityInput, + RecordingDeviceInput, + ResourceIdentityInput, +} from '../mobile/types.js' +import type { + CodeableConcept, + Coding, + CollectionBundle, + Device, + GraphResource, + Identifier, + Provenance, +} from '../r4/index.js' + +type GraphEntry = CollectionBundle['entry'][number] +type ProvenanceAgent = Provenance['agent'][number] + +export const resourceId = ( + identity: ResourceIdentityInput, +): { readonly id?: string } => + identity.id === undefined ? {} : { id: identity.id } + +export const identifier = (input: CompleteIdentifierInput): Identifier => ({ + system: input.system, + value: input.value, +}) + +export const coding = ( + system: string, + code: string, + display?: string, +): Coding => ({ + system, + code, + ...(display === undefined ? {} : { display }), +}) + +export const concept = ( + system: string, + code: string, + display?: string, +): CodeableConcept => ({ + coding: [coding(system, code, display)], + ...(display === undefined ? {} : { text: display }), +}) + +export const makeApplicationDevice = ( + input: ApplicationDeviceInput, +): Device => ({ + resourceType: 'Device' as const, + ...resourceId(input.identity), + meta: { profile: [PROFILES.applicationDevice] }, + identifier: [identifier(input.identity.identifier)], + status: 'active' as const, + ...(input.manufacturer === undefined ? + {} + : { manufacturer: input.manufacturer }), + deviceName: [{ name: input.name, type: 'user-friendly-name' as const }], + ...(input.version === undefined ? + {} + : { + version: [ + { + type: { + coding: [ + coding( + 'urn:iso:std:iso:11073:10101', + '531975', + 'Software revision', + ), + ], + }, + value: input.version, + }, + ], + }), +}) + +export const makeRecordingDevice = (input: RecordingDeviceInput): Device => ({ + resourceType: 'Device' as const, + ...resourceId(input.identity), + meta: { profile: [PROFILES.recordingDevice] }, + identifier: [identifier(input.identity.identifier)], + status: 'active' as const, + ...(input.name === undefined ? + {} + : { + deviceName: [{ name: input.name, type: 'user-friendly-name' as const }], + }), + ...(input.manufacturer === undefined ? + {} + : { manufacturer: input.manufacturer }), + ...(input.modelNumber === undefined ? + {} + : { modelNumber: input.modelNumber }), +}) + +export const identifiedEntry = ( + identity: IdentifiedEntryIdentityInput, + resource: GraphResource, +): GraphEntry => ({ + fullUrl: identity.fullUrl, + extension: [ + { + url: EXTENSIONS.exchangeEntryIdentifier, + valueIdentifier: identifier(identity.identifier), + }, + ], + resource, +}) + +export const provenanceActivity = (): CodeableConcept => + concept( + SYSTEMS.isoLifecycle, + 'transform', + 'Transform/Translate Record Lifecycle Event', + ) + +export const assemblerAgent = (reference: string): ProvenanceAgent => ({ + type: concept(SYSTEMS.provenanceParticipant, 'assembler', 'Assembler'), + who: { reference }, +}) + +export const sourceEntityAgent = (reference: string): ProvenanceAgent => ({ + type: concept(SYSTEMS.provenanceParticipant, 'enterer', 'Enterer'), + who: { reference }, +}) diff --git a/packages/fhir/src/providers/identity.ts b/packages/fhir/src/providers/identity.ts new file mode 100644 index 00000000..f5338e26 --- /dev/null +++ b/packages/fhir/src/providers/identity.ts @@ -0,0 +1,197 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { providerAdapterCatalog } from './contract.generated.js' +import type { ConnectedProvider } from './types.js' +import { + deepFreeze, + err, + ok, + parseAbsoluteUri, + parseFhirId, + parsePositiveInteger, + type AbsoluteUri, + type PositiveInteger, + type Result, +} from '../core/index.js' +import { + containsIsolatedSurrogate, + createEntryIdentity, +} from '../mobile/identity.js' +import type { + CompleteIdentifierInput, + IdentifiedEntryIdentityInput, +} from '../mobile/types.js' + +const SEPARATOR = '|' + +/** Joins components behind the scheme version. Nothing is hashed, escaped, or re-encoded. */ +const compose = (components: readonly string[]): Result => { + for (const component of components) { + if (component.includes(SEPARATOR)) { + return err( + 'invalid-identifier', + 'An identity component must not contain a vertical bar; such a value is rejected, never escaped.', + ) + } + if (containsIsolatedSurrogate(component)) { + return err( + 'invalid-identifier', + 'Identity components must contain Unicode scalar values only.', + ) + } + } + return ok(`v1:${components.join(SEPARATOR)}`) +} + +const connectedIdentitySystem = ( + role: 'output' | 'sourceRecord', +): AbsoluteUri => providerAdapterCatalog.identity[role].system as AbsoluteUri + +/** + * Whether this provider's native record key is unique across the whole provider, or only within + * one account. An account-scoped provider carries its deployment pseudonym as a component, so the + * identifier still stands alone and a receiver deduplicates by comparing system and value. + */ +const identifierScope = (provider: ConnectedProvider): 'account' | 'none' => { + const entry = providerAdapterCatalog.providers.find( + (candidate) => candidate.id === provider, + ) as { readonly identifierScope?: 'account' | 'none' } | undefined + // Scoped by account unless the vendor documents that its record key is unique across accounts. + return entry?.identifierScope ?? 'account' +} + +export interface ProviderIdentityInput { + readonly provider: ConnectedProvider + readonly providerAccountIdentifier: CompleteIdentifierInput + readonly sourceType: string + readonly sourceNativeId: string + /** Closed catalog selectors; callers do not supply arbitrary values. */ + readonly outputDiscriminators: readonly [string, ...string[]] + readonly eventSequence: PositiveInteger + /** The namespace this deployment owns for graph nodes the export creates. */ + readonly graphIdentifierSystem: AbsoluteUri +} + +export interface ProviderIdentities { + readonly sourceRecord: CompleteIdentifierInput + /** Empty for a one-to-one conversion; one entry per output where a record fans out. */ + readonly outputs: readonly CompleteIdentifierInput[] + readonly conversion: CompleteIdentifierInput + readonly exchange: CompleteIdentifierInput +} + +/** Internal closed-facade derivation of every Provider business id. */ +export const deriveProviderIdentities = ( + input: ProviderIdentityInput, +): Result => { + if (!parseAbsoluteUri(input.providerAccountIdentifier.system).ok) { + return err( + 'invalid-uri', + 'Provider account Identifier.system must be an absolute URI.', + ['providerAccountIdentifier', 'system'], + ) + } + if ( + input.providerAccountIdentifier.value.trim() === '' || + input.sourceType.trim() === '' || + input.sourceNativeId.trim() === '' || + input.outputDiscriminators.some( + (outputDiscriminator) => outputDiscriminator.trim() === '', + ) + ) { + return err( + 'invalid-identifier', + 'Provider identity inputs must not be empty.', + ) + } + if ( + new Set(input.outputDiscriminators).size !== + input.outputDiscriminators.length + ) { + return err( + 'duplicate-identifier', + 'Provider output discriminators must be unique.', + ['outputDiscriminators'], + ) + } + const sequence = parsePositiveInteger(input.eventSequence) + if (!sequence.ok) return sequence + + // A provider whose native key is unique across the whole provider carries that key verbatim: + // the system already says which provider and which key space it belongs to. Only an + // account-scoped provider composes, because its key alone would not identify one record. + const accountScoped = identifierScope(input.provider) === 'account' + const sourceComponents = + accountScoped ? + [ + input.provider, + input.providerAccountIdentifier.value, + input.sourceType, + input.sourceNativeId, + ] + : [input.sourceNativeId] + const sourceValue = + accountScoped ? compose(sourceComponents) : ok(input.sourceNativeId) + if (!sourceValue.ok) return sourceValue + const sourceRecord = { + system: connectedIdentitySystem('sourceRecord'), + value: sourceValue.value, + } + + // A source record yielding one Observation gives it no output identifier: a second namespace + // repeating the source-record value would identify nothing new. Only a fan-out needs to tell + // its outputs apart. + const outputs: CompleteIdentifierInput[] = [] + if (input.outputDiscriminators.length > 1) { + for (const outputDiscriminator of input.outputDiscriminators) { + const outputValue = compose([...sourceComponents, outputDiscriminator]) + if (!outputValue.ok) return outputValue + outputs.push({ + system: connectedIdentitySystem('output'), + value: outputValue.value, + }) + } + } + + // The conversion Provenance and the exchange Bundle record an export event rather than anything + // read from the provider, so they are named in the deployment's own namespace and carry no + // scheme prefix from this guide. + const eventKey = (role: string): string => + [input.provider, String(input.eventSequence), role].join(SEPARATOR) + + return ok( + deepFreeze({ + sourceRecord, + outputs, + conversion: { + system: input.graphIdentifierSystem, + value: eventKey('conversion-provenance'), + }, + exchange: { + system: input.graphIdentifierSystem, + value: eventKey('exchange-bundle'), + }, + }) as unknown as ProviderIdentities, + ) +} + +/** Internal runtime bridge from strict adapter schemas to branded graph identity. */ +export const parseResourceIdentityInput = (input: { + readonly identifier: { readonly system: string; readonly value: string } + readonly id?: string | undefined +}): Result => { + const system = parseAbsoluteUri(input.identifier.system) + if (!system.ok) return system + const id = input.id === undefined ? undefined : parseFhirId(input.id) + if (id !== undefined && !id.ok) return id + return createEntryIdentity( + { system: system.value, value: input.identifier.value }, + id?.value, + ) +} diff --git a/packages/fhir/src/providers/index.ts b/packages/fhir/src/providers/index.ts new file mode 100644 index 00000000..211a9d65 --- /dev/null +++ b/packages/fhir/src/providers/index.ts @@ -0,0 +1,74 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +export { buildProviderMeasurementBundle } from './builder.js' +export { + adapterMeasurementCatalog, + groveRecordingFormatRegistry, + providerAdapterCatalog, + providerRawMappings, + providerRecordEffectiveRules, + providerScalarMappings, + groveProviderPackageMetadata, + groveFhirContractVersion, + groveFhirVersion, + type AdapterMeasurementCatalog, + type ProviderRawMappings, + type ProviderRecordEffectiveRules, + type ProviderScalarMappings, +} from './contract.generated.js' +export { + parseProviderMeasurementBundleInput, + parseNormalizedProviderRecord, +} from './provider.js' +export { + buildProviderRecordingBundle, + encodeRecordingBytes, + parseCanonicalBase64, + parseProviderRecordingBundleInput, + parseImmutableRecordingUrl, + parseMediaType, + parseSha1Base64, +} from './recording.js' +export type { + CanonicalBase64, + ProviderAdapter, + ProviderMeasurementBundleInput, + ProviderRecordingAttachmentInput, + ProviderRecordingBundleInput, + ProviderRecordingSource, + ProviderRecordingSourceRecord, + ProviderSourceRecord, + ConnectedProvider, + ConnectedProviderMeasurements, + ConnectedProviderRecord, + ConnectedRawProvider, + ConnectedRawSourceType, + ConnectedSourceType, + EmbeddedRecordingAttachmentInput, + ExternalRecordingAttachmentInput, + ImmutableRecordingUrl, + MeasurementRepositoryAssignedResourceIds, + MediaType, + NormalizedProviderRecord, + NormalizedSourceRecord, + ProviderAccountPseudonymInput, + ProviderRecordingFormat, + RawPayloadAdmissionAssertion, + RecordingRepositoryAssignedResourceIds, + Sha1Base64, + SupportedConnectedProviderMeasurementKind, +} from './types.js' +export type { + ApplicationDeviceInput, + GatewayApplicationInput, + MobileMeasurement, + RecordingDeviceInput, + RecordingMethod, + ResourceIdentityInput, +} from '../mobile/types.js' diff --git a/packages/fhir/src/providers/privacy.ts b/packages/fhir/src/providers/privacy.ts new file mode 100644 index 00000000..1e108e9f --- /dev/null +++ b/packages/fhir/src/providers/privacy.ts @@ -0,0 +1,41 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/** Raw, form-decoded, and recursively percent-decoded string representations. */ +const decodedStringRepresentations = (value: string): readonly string[] => { + const representations = new Set([value]) + let decoded = value + for (let layer = 0; layer <= value.length; layer += 1) { + const formDecoded = decoded.replaceAll('+', ' ') + representations.add(formDecoded) + let next: string + try { + next = decodeURIComponent(formDecoded) + } catch { + break + } + representations.add(next) + if (next === decoded || next === formDecoded) break + decoded = next + } + return [...representations] +} + +export const containsReversibleIdentityRepresentation = ( + emitted: string, + privateValue: string, +): boolean => { + const privateRepresentations = decodedStringRepresentations(privateValue) + return decodedStringRepresentations(emitted).some((emittedValue) => + privateRepresentations.some( + (privateRepresentation) => + privateRepresentation !== '' && + emittedValue.includes(privateRepresentation), + ), + ) +} diff --git a/packages/fhir/src/providers/profiles.ts b/packages/fhir/src/providers/profiles.ts new file mode 100644 index 00000000..38b2a143 --- /dev/null +++ b/packages/fhir/src/providers/profiles.ts @@ -0,0 +1,76 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/* eslint-disable sonarjs/no-clear-text-protocols -- FHIR fixes these canonical URIs to HTTP. */ + +import { + groveProviderPackageCanonicals, + groveProviderProfileCanonicals, +} from './contract.generated.js' +import { groveFhirExchangeIdentity } from '../mobile/measurement-catalog.generated.js' + +type GroveSystems = Readonly<{ + groveAggregationMethod: string + groveRecordingMethod: string + isoLifecycle: string + observationCategory: string + provenanceParticipant: string +}> + +export const SYSTEMS: GroveSystems = { + groveAggregationMethod: `${groveProviderPackageCanonicals.mobile}/CodeSystem/grove-aggregation-method`, + groveRecordingMethod: `${groveProviderPackageCanonicals.mobile}/CodeSystem/grove-recording-method`, + isoLifecycle: 'http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle', + observationCategory: + 'http://terminology.hl7.org/CodeSystem/observation-category', + provenanceParticipant: + 'http://terminology.hl7.org/CodeSystem/provenance-participant-type', +} as const + +type GroveProfiles = Readonly<{ + mobileBundle: string + recordingDevice: string + applicationDevice: string + providerObservation: string + sensorRecordingDocument: string + providerRecordingDocument: string + providerConversionProvenance: string +}> + +export const PROFILES: GroveProfiles = { + mobileBundle: groveProviderProfileCanonicals['grove-mobile-exchange-bundle'], + recordingDevice: groveProviderProfileCanonicals['grove-recording-device'], + applicationDevice: groveProviderProfileCanonicals['grove-application-device'], + providerObservation: groveProviderProfileCanonicals['provider-observation'], + sensorRecordingDocument: + groveProviderProfileCanonicals['grove-sensor-recording-document'], + providerRecordingDocument: + groveProviderProfileCanonicals['provider-recording-document'], + providerConversionProvenance: + groveProviderProfileCanonicals['provider-conversion-provenance'], +} as const + +type GroveExtensions = Readonly<{ + gatewayDevice: string + recordingMethod: string + provider: string + providerSourceType: string + exchangeEntryIdentifier: string + researchStudy: string +}> + +export const EXTENSIONS: GroveExtensions = { + gatewayDevice: + 'http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice', + recordingMethod: `${groveProviderPackageCanonicals.mobile}/StructureDefinition/grove-recording-method`, + provider: `${groveProviderPackageCanonicals.providers}/StructureDefinition/provider`, + providerSourceType: `${groveProviderPackageCanonicals.providers}/StructureDefinition/provider-source-type`, + exchangeEntryIdentifier: groveFhirExchangeIdentity.entryIdentifierExtension, + researchStudy: + 'http://hl7.org/fhir/StructureDefinition/workflow-researchStudy', +} as const diff --git a/packages/fhir/src/providers/provider.ts b/packages/fhir/src/providers/provider.ts new file mode 100644 index 00000000..ba3de108 --- /dev/null +++ b/packages/fhir/src/providers/provider.ts @@ -0,0 +1,827 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { z } from 'zod' +import { + providerRecordEffectiveRules, + providerScalarMappings, +} from './contract.generated.js' +import { + deriveProviderIdentities, + parseResourceIdentityInput, +} from './identity.js' +import { containsReversibleIdentityRepresentation } from './privacy.js' +import type { + ProviderMeasurementBundleInput, + ConnectedProvider, + NormalizedProviderRecord, +} from './types.js' +import { + compareFhirInstants, + deepFreeze, + issues, + ok, + parseAbsoluteUri, + parseFhirInstant, + parsePatientReference, + parsePositiveInteger, + parseResearchStudyReference, + type Issue, + type Result, +} from '../core/index.js' +import { + sharedMobileMeasurementCatalog, + type SharedMobileMeasurementKind, +} from '../mobile/measurement-catalog.generated.js' +import { canonicalizeMobileEffectiveInstant } from '../mobile/time.js' +import type { + InstantCodedMeasurementKind, + InstantQuantityMeasurementKind, + MobileMeasurement, + PeriodCodedMeasurementKind, + PeriodQuantityMeasurementKind, +} from '../mobile/types.js' + +const primitiveInstantSchemaValue = z + .string() + .refine((value) => parseFhirInstant(value).ok, { + message: + 'Expected an RFC 3339 instant with seconds and an explicit UTC offset.', + }) +export const primitiveInstantSchema: z.ZodString = primitiveInstantSchemaValue + +const mobileEffectiveInstantSchema = z.string().transform((value, context) => { + const canonical = canonicalizeMobileEffectiveInstant(value) + if (!canonical.ok) { + for (const issue of canonical.issues) { + context.addIssue({ code: 'custom', message: issue.message }) + } + return z.NEVER + } + return canonical.value +}) + +const nonBlankStringSchemaValue = z + .string() + .refine((value) => value.trim() !== '', 'Expected a non-blank string.') +export const nonBlankStringSchema: z.ZodString = nonBlankStringSchemaValue + +const identifierInputSchemaValue = z.strictObject({ + system: z.url(), + value: nonBlankStringSchema, +}) +export const identifierInputSchema: z.ZodObject< + { system: z.ZodURL; value: z.ZodString }, + z.core.$strict +> = identifierInputSchemaValue + +const fhirIdSchemaValue = z.string().regex(/^[A-Za-z0-9\-.]{1,64}$/u) +export const fhirIdSchema: z.ZodString = fhirIdSchemaValue + +const resourceIdentitySchema = z.strictObject({ + identifier: identifierInputSchema, + id: fhirIdSchema.optional(), +}) + +const applicationDeviceSchemaValue = z.strictObject({ + identity: resourceIdentitySchema, + name: nonBlankStringSchema, + version: nonBlankStringSchema.optional(), + manufacturer: nonBlankStringSchema.optional(), +}) +export const applicationDeviceSchema: z.ZodType<{ + identity: { + identifier: { system: string; value: string } + id?: string | undefined + } + name: string + version?: string | undefined + manufacturer?: string | undefined +}> = applicationDeviceSchemaValue + +const recordingDeviceBase = { + identity: resourceIdentitySchema, + name: nonBlankStringSchema.optional(), + manufacturer: nonBlankStringSchema.optional(), + modelNumber: nonBlankStringSchema.optional(), +} as const + +const recordingDeviceSchema = z.discriminatedUnion('identityScope', [ + z.strictObject({ + ...recordingDeviceBase, + identityScope: z.literal('deployment-scoped'), + }), + z.strictObject({ + ...recordingDeviceBase, + identityScope: z.literal('authorized-hardware'), + disclosureAuthorization: z.literal('authorized-for-exchange'), + }), +]) + +const gatewayApplicationSchema = z.discriminatedUnion('kind', [ + z.strictObject({ + kind: z.literal('converter-application'), + roleAssurance: z.literal('mediated-or-routed-measurement'), + }), + z.strictObject({ + kind: z.literal('distinct-application'), + roleAssurance: z.literal('mediated-or-routed-measurement'), + application: applicationDeviceSchema, + }), +]) + +const sourceBase = { + recordingMethod: z + .enum(['actively-recorded', 'automatically-recorded', 'manual-entry']) + .optional(), + recordingDevice: recordingDeviceSchema.optional(), +} as const + +const sourceSchema = z.strictObject({ + ...sourceBase, + adapter: z.strictObject({ + kind: z.literal('providers'), + provider: z.enum( + Object.keys(providerScalarMappings) as [ + ConnectedProvider, + ...ConnectedProvider[], + ], + ), + }), + providerAccountIdentifier: identifierInputSchema.extend({ + assurance: z.literal('deployment-scoped-pseudonym'), + }), + sourceType: nonBlankStringSchema, + sourceNativeId: nonBlankStringSchema, + dataOrigin: applicationDeviceSchema, +}) + +const instantEffectiveSchema = z.strictObject({ + kind: z.literal('date-time'), + value: mobileEffectiveInstantSchema, +}) + +const periodEffectiveSchema = z + .strictObject({ + kind: z.literal('period'), + start: mobileEffectiveInstantSchema, + end: mobileEffectiveInstantSchema, + }) + .refine( + (value) => { + const ordering = compareFhirInstants(value.start, value.end) + return ordering.ok && ordering.value === -1 + }, + { message: 'A measurement Period must end after it starts.' }, + ) + +const measurementKindsWhere = ( + valueKind: 'codeableConcept' | 'quantity', + effective: 'Period' | 'dateTime', + excluded?: SharedMobileMeasurementKind, +): [Kind, ...Kind[]] => + ( + Object.keys(sharedMobileMeasurementCatalog) as SharedMobileMeasurementKind[] + ).filter((kind) => { + const definition = sharedMobileMeasurementCatalog[kind] + return ( + definition.valueKind === valueKind && + definition.effective === effective && + kind !== excluded + ) + }) as [Kind, ...Kind[]] + +const instantQuantityMeasurementSchema = z.strictObject({ + kind: z.enum( + measurementKindsWhere( + 'quantity', + 'dateTime', + ), + ), + value: z.number(), + effective: instantEffectiveSchema, +}) + +const periodQuantityMeasurementSchema = z.strictObject({ + kind: z.enum( + measurementKindsWhere('quantity', 'Period'), + ), + value: z.number().nonnegative(), + effective: periodEffectiveSchema, +}) + +const instantCodedMeasurementSchema = z.strictObject({ + kind: z.enum( + measurementKindsWhere( + 'codeableConcept', + 'dateTime', + ), + ), + value: nonBlankStringSchema, + effective: instantEffectiveSchema, +}) + +const periodCodedMeasurementSchema = z.strictObject({ + kind: z.enum( + measurementKindsWhere( + 'codeableConcept', + 'Period', + 'sleep-stage', + ), + ), + value: nonBlankStringSchema, + effective: periodEffectiveSchema, +}) + +const bloodPressureMeasurementSchema = z.strictObject({ + kind: z.literal('blood-pressure'), + systolic: z.number().positive(), + diastolic: z.number().positive(), + effective: instantEffectiveSchema, +}) + +const sleepStageMeasurementSchema = z.strictObject({ + kind: z.literal('sleep-stage'), + stage: z.enum(sharedMobileMeasurementCatalog['sleep-stage'].allowedValues), + sourceStageCoding: z + .strictObject({ + system: z.url(), + code: nonBlankStringSchema, + display: nonBlankStringSchema.optional(), + }) + .optional(), + effective: periodEffectiveSchema, +}) + +const measurementSchema = z.discriminatedUnion('kind', [ + instantQuantityMeasurementSchema, + periodQuantityMeasurementSchema, + instantCodedMeasurementSchema, + periodCodedMeasurementSchema, + bloodPressureMeasurementSchema, + sleepStageMeasurementSchema, +]) + +const refineMeasurement = ( + measurement: z.infer, + path: ReadonlyArray, + context: z.core.$RefinementCtx, +) => { + if ( + measurement.effective.kind === 'date-time' && + 'value' in measurement && + typeof measurement.value === 'number' && + measurement.kind !== 'oxygen-saturation' && + measurement.value <= 0 + ) { + context.addIssue({ + code: 'custom', + path: [...path, 'value'], + message: 'An instantaneous measurement value must be greater than zero.', + }) + } + if ('value' in measurement && typeof measurement.value === 'string') { + const definition = sharedMobileMeasurementCatalog[measurement.kind] + if ( + !('allowedValues' in definition) || + !(definition.allowedValues as readonly string[]).includes( + measurement.value, + ) + ) { + context.addIssue({ + code: 'custom', + path: [...path, 'value'], + message: `Expected a catalog-allowed coded result for ${measurement.kind}.`, + }) + } + } + if ( + measurement.kind === 'oxygen-saturation' && + (measurement.value < 0 || measurement.value > 100) + ) { + context.addIssue({ + code: 'custom', + path: [...path, 'value'], + message: 'Oxygen saturation must be between 0 and 100 percent.', + }) + } + if ( + measurement.kind === 'step-count' && + !Number.isInteger(measurement.value) + ) { + context.addIssue({ + code: 'custom', + path: [...path, 'value'], + message: 'Step count must be an integer.', + }) + } +} + +const normalizedProviderRecordShape = { + source: sourceSchema, + measurements: z.array(measurementSchema).nonempty(), +} as const + +const refineMeasurements = ( + value: { + readonly measurements: ReadonlyArray> + }, + context: z.core.$RefinementCtx, +) => { + for (const [index, measurement] of value.measurements.entries()) { + refineMeasurement(measurement, ['measurements', index], context) + } +} + +const normalizedProviderRecordSchema = z + .strictObject(normalizedProviderRecordShape) + .superRefine(refineMeasurements) + +const providerMeasurementBundleInputSchema = z + .strictObject({ + ...normalizedProviderRecordShape, + subject: z.string().refine((value) => parsePatientReference(value).ok, { + message: + 'Expected Patient/{id} or an absolute HTTP(S) URL ending in /Patient/{id}.', + }), + application: applicationDeviceSchema, + gatewayApplication: gatewayApplicationSchema.optional(), + eventSequence: z + .number() + .refine((value) => parsePositiveInteger(value).ok, { + message: 'Expected a positive safe integer greater than zero.', + }), + graphIdentifierSystem: z + .string() + .refine((value) => parseAbsoluteUri(value).ok, { + message: 'Expected an absolute URI the deployment owns.', + }), + issued: primitiveInstantSchema, + recorded: primitiveInstantSchema, + repositoryIds: z + .strictObject({ + bundle: fhirIdSchema.optional(), + observations: z + .partialRecord( + z.enum( + Object.keys(sharedMobileMeasurementCatalog) as [ + MobileMeasurement['kind'], + ...Array, + ], + ), + fhirIdSchema, + ) + .optional(), + provenance: fhirIdSchema.optional(), + }) + .optional(), + researchStudyReferences: z.array(z.string()).optional(), + }) + .superRefine(refineMeasurements) + +const applicationStrings = ( + input: z.infer | undefined, +): readonly string[] => + input === undefined ? + [] + : [ + input.identity.identifier.system, + input.identity.identifier.value, + input.identity.id ?? '', + input.name, + input.version ?? '', + input.manufacturer ?? '', + ] + +const recordingDeviceStrings = ( + input: z.infer | undefined, +): readonly string[] => + input === undefined ? + [] + : [ + input.identity.identifier.system, + input.identity.identifier.value, + input.identity.id ?? '', + input.name ?? '', + input.manufacturer ?? '', + input.modelNumber ?? '', + ] + +const stringLeaves = (value: unknown): readonly string[] => { + if (typeof value === 'string') return [value] + if (Array.isArray(value)) return value.flatMap(stringLeaves) + if (typeof value !== 'object' || value === null) return [] + return Object.values(value).flatMap(stringLeaves) +} + +const normalizedEmittedCallerStrings = ( + input: z.infer, +): readonly string[] => [ + input.source.adapter.provider, + input.source.sourceType, + input.source.recordingMethod ?? '', + ...stringLeaves(input.measurements), + ...applicationStrings(input.source.dataOrigin), + ...recordingDeviceStrings(input.source.recordingDevice), +] + +const graphEmittedCallerStrings = ( + input: z.infer, +): readonly string[] => [ + ...normalizedEmittedCallerStrings(input), + input.subject, + input.issued, + input.recorded, + ...applicationStrings(input.application), + ...applicationStrings( + input.gatewayApplication?.kind === 'distinct-application' ? + input.gatewayApplication.application + : undefined, + ), + input.repositoryIds?.bundle ?? '', + ...Object.values(input.repositoryIds?.observations ?? {}), + input.repositoryIds?.provenance ?? '', + ...(input.researchStudyReferences ?? []), +] + +const identityLeakageIssues = ( + source: z.infer, + emitted: readonly string[], +): readonly Issue[] => { + const privateInputs = [ + [source.sourceNativeId, ['source', 'sourceNativeId'], 'sourceNativeId'], + [ + source.providerAccountIdentifier.value, + ['source', 'providerAccountIdentifier', 'value'], + 'providerAccountIdentifier.value', + ], + ] as const + return privateInputs.flatMap(([privateValue, path, label]) => + ( + emitted.some( + (value) => + value !== '' && + containsReversibleIdentityRepresentation(value, privateValue), + ) + ) ? + [ + { + severity: 'error' as const, + code: 'invalid-identifier' as const, + path, + message: `${label} is an identity input only and must not appear in emitted FHIR metadata.`, + }, + ] + : [], + ) +} + +export const normalizeZodIssue = (entry: z.core.$ZodIssue): Issue => ({ + severity: 'error', + code: 'schema-invalid', + path: entry.path.map((component) => + typeof component === 'symbol' ? + (component.description ?? component.toString()) + : component, + ), + message: entry.message, +}) + +const providerSourceMapping = ( + provider: ConnectedProvider, + sourceType: string, +): Readonly> | undefined => { + const providerMappings = providerScalarMappings[provider] as Record< + string, + Readonly> | undefined + > + return providerMappings[sourceType] +} + +interface RecordEffectiveRule { + readonly kind: 'complete-civil-day-period' + readonly measurementIds: readonly string[] + readonly outputsShareEffective: true +} + +const providerRecordEffectiveRule = ( + provider: ConnectedProvider, + sourceType: string, +): RecordEffectiveRule | undefined => { + const providers = providerRecordEffectiveRules as Readonly< + Record> | undefined> + > + return providers[provider]?.[sourceType] +} + +const CIVIL_DAY_BOUNDARY = + /^(\d{4}-\d{2}-\d{2})T00:00:00\.000(?:Z|[+-]\d{2}:\d{2})$/u + +const nextCivilDate = (value: string): string | undefined => { + const parts = /^(\d{4})-(\d{2})-(\d{2})$/u.exec(value) + if (parts === null) return undefined + const year = Number(parts[1]) + const month = Number(parts[2]) + const day = Number(parts[3]) + const instant = new Date(0) + instant.setUTCHours(0, 0, 0, 0) + instant.setUTCFullYear(year, month - 1, day + 1) + return `${String(instant.getUTCFullYear()).padStart(4, '0')}-${String( + instant.getUTCMonth() + 1, + ).padStart(2, '0')}-${String(instant.getUTCDate()).padStart(2, '0')}` +} + +const isCompleteCivilDay = (start: string, end: string): boolean => { + const startDate = CIVIL_DAY_BOUNDARY.exec(start)?.[1] + const endDate = CIVIL_DAY_BOUNDARY.exec(end)?.[1] + return ( + startDate !== undefined && + endDate !== undefined && + nextCivilDate(startDate) === endDate + ) +} + +const recordEffectiveIssues = (input: { + readonly source: z.infer + readonly measurements: ReadonlyArray> +}): readonly Issue[] => { + const rule = providerRecordEffectiveRule( + input.source.adapter.provider, + input.source.sourceType, + ) + if (rule === undefined) return [] + const first = input.measurements[0]?.effective + if (first?.kind !== 'period' || !isCompleteCivilDay(first.start, first.end)) { + return [ + { + severity: 'error', + code: 'value-mismatch', + path: ['measurements', 0, 'effective'], + message: + 'This source record requires the complete source civil day as a midnight-to-midnight Period.', + }, + ] + } + return input.measurements.flatMap((measurement, index) => + ( + measurement.effective.kind !== 'period' || + measurement.effective.start !== first.start || + measurement.effective.end !== first.end + ) ? + [ + { + severity: 'error' as const, + code: 'value-mismatch' as const, + path: ['measurements', index, 'effective'], + message: + 'Every output from this source record must share the same complete civil-day Period.', + }, + ] + : [], + ) +} + +export const providerOutputDiscriminator = ( + provider: ConnectedProvider, + sourceType: string, + kind: MobileMeasurement['kind'], +): string | undefined => { + return providerSourceMapping(provider, sourceType)?.[kind] +} + +const recordMappingIssues = (input: { + readonly source: z.infer + readonly measurements: ReadonlyArray> + readonly repositoryIds?: + | { + readonly observations?: + | Readonly>> + | undefined + } + | undefined +}): readonly Issue[] => { + const mapping = providerSourceMapping( + input.source.adapter.provider, + input.source.sourceType, + ) + if (mapping === undefined) { + return [ + { + severity: 'error', + code: 'unsupported-measurement', + path: ['source', 'sourceType'], + message: `${input.source.adapter.provider}/${input.source.sourceType} does not have a supported scalar mapping.`, + }, + ] + } + + const findings: Issue[] = [] + const kinds = input.measurements.map(({ kind }) => kind) + for (const [index, kind] of kinds.entries()) { + if (!Object.hasOwn(mapping, kind)) { + findings.push({ + severity: 'error', + code: 'unsupported-measurement', + path: ['measurements', index, 'kind'], + message: `${input.source.adapter.provider}/${input.source.sourceType} does not have a supported scalar mapping for ${kind}.`, + }) + } + } + if (new Set(kinds).size !== kinds.length) { + findings.push({ + severity: 'error', + code: 'duplicate-identifier', + path: ['measurements'], + message: + 'A source record may emit each admitted measurement kind at most once.', + }) + } + for (const kind of Object.keys(input.repositoryIds?.observations ?? {})) { + if (!kinds.includes(kind as MobileMeasurement['kind'])) { + findings.push({ + severity: 'error', + code: 'invalid-reference', + path: ['repositoryIds', 'observations', kind], + message: + 'A repository Observation id requires a matching emitted measurement.', + }) + } + } + findings.push(...recordEffectiveIssues(input)) + return findings +} + +const sortMeasurements = < + Value extends { + readonly source: z.infer + readonly measurements: ReadonlyArray> + }, +>( + value: Value, +): Value => { + const mapping = providerSourceMapping( + value.source.adapter.provider, + value.source.sourceType, + ) + if (mapping === undefined) return value + const order = new Map( + Object.keys(mapping).map((kind, index) => [kind, index] as const), + ) + return { + ...value, + measurements: [...value.measurements].sort( + (left, right) => + (order.get(left.kind) ?? Number.MAX_SAFE_INTEGER) - + (order.get(right.kind) ?? Number.MAX_SAFE_INTEGER), + ), + } +} + +/** + * Parses the provider-neutral handoff produced by an external provider adapter. + * Raw provider payload fields are rejected rather than retained or stripped. + */ +export const parseNormalizedProviderRecord = ( + input: unknown, +): Result => { + const result = normalizedProviderRecordSchema.safeParse(input) + if (!result.success) { + return issues(result.error.issues.map(normalizeZodIssue)) + } + const leakageIssues = identityLeakageIssues( + result.data.source, + normalizedEmittedCallerStrings(result.data), + ) + if (leakageIssues.length > 0) return issues(leakageIssues) + const mappingIssues = recordMappingIssues(result.data) + if (mappingIssues.length > 0) return issues(mappingIssues) + return ok( + deepFreeze(sortMeasurements(result.data)) as NormalizedProviderRecord, + ) +} + +/** Strict runtime boundary for the complete deterministic graph input. */ +export const parseProviderMeasurementBundleInput = ( + input: unknown, +): Result => { + const result = providerMeasurementBundleInputSchema.safeParse(input) + if (!result.success) { + return issues(result.error.issues.map(normalizeZodIssue)) + } + type GraphIdentityInput = readonly [ + Parameters[0], + readonly string[], + ] + const graphIdentityInputs: GraphIdentityInput[] = [ + [result.data.application.identity, ['application', 'identity']], + [ + result.data.source.dataOrigin.identity, + ['source', 'dataOrigin', 'identity'], + ], + ] + if (result.data.source.recordingDevice !== undefined) { + graphIdentityInputs.push([ + result.data.source.recordingDevice.identity, + ['source', 'recordingDevice', 'identity'], + ]) + } + if (result.data.gatewayApplication?.kind === 'distinct-application') { + graphIdentityInputs.push([ + result.data.gatewayApplication.application.identity, + ['gatewayApplication', 'application', 'identity'], + ]) + } + const graphIdentityIssues = graphIdentityInputs.flatMap( + ([identity, path]) => { + const parsedIdentity = parseResourceIdentityInput(identity) + return parsedIdentity.ok ? + [] + : parsedIdentity.issues.map((entry) => ({ + ...entry, + path: [...path, ...entry.path], + })) + }, + ) + if (graphIdentityIssues.length > 0) return issues(graphIdentityIssues) + const researchStudyIssues: Issue[] = [] + for (const [index, reference] of ( + result.data.researchStudyReferences ?? [] + ).entries()) { + if (!parseResearchStudyReference(reference).ok) { + researchStudyIssues.push({ + severity: 'error', + code: 'invalid-reference', + path: ['researchStudyReferences', index], + message: + 'Expected ResearchStudy/{id} or an absolute HTTP(S) URL ending in /ResearchStudy/{id} without a query or fragment.', + }) + } + } + if ( + result.data.researchStudyReferences !== undefined && + new Set(result.data.researchStudyReferences).size !== + result.data.researchStudyReferences.length + ) { + researchStudyIssues.push({ + severity: 'error', + code: 'duplicate-identifier', + path: ['researchStudyReferences'], + message: 'ResearchStudy references must be unique.', + }) + } + if (researchStudyIssues.length > 0) return issues(researchStudyIssues) + const leakageIssues = identityLeakageIssues( + result.data.source, + graphEmittedCallerStrings(result.data), + ) + if (leakageIssues.length > 0) return issues(leakageIssues) + const mappingIssues = recordMappingIssues(result.data) + if (mappingIssues.length > 0) return issues(mappingIssues) + const outputDiscriminators = result.data.measurements.map(({ kind }) => + providerOutputDiscriminator( + result.data.source.adapter.provider, + result.data.source.sourceType, + kind, + ), + ) + if (outputDiscriminators.some((value) => value === undefined)) { + return issues([ + { + severity: 'error', + code: 'unsupported-measurement', + path: ['measurements'], + message: + 'Every emitted measurement requires a catalog-owned Provider output discriminator.', + }, + ]) + } + const providerAccountSystem = parseAbsoluteUri( + result.data.source.providerAccountIdentifier.system, + ) + const eventSequence = parsePositiveInteger(result.data.eventSequence) + const graphSystem = parseAbsoluteUri(result.data.graphIdentifierSystem) + if (!providerAccountSystem.ok) return providerAccountSystem + if (!eventSequence.ok) return eventSequence + if (!graphSystem.ok) return graphSystem + const identity = deriveProviderIdentities({ + provider: result.data.source.adapter.provider, + providerAccountIdentifier: { + system: providerAccountSystem.value, + value: result.data.source.providerAccountIdentifier.value, + }, + sourceType: result.data.source.sourceType, + sourceNativeId: result.data.source.sourceNativeId, + outputDiscriminators: outputDiscriminators as [string, ...string[]], + eventSequence: eventSequence.value, + graphIdentifierSystem: graphSystem.value, + }) + if (!identity.ok) return identity + return ok( + deepFreeze(sortMeasurements(result.data)) as ProviderMeasurementBundleInput, + ) +} diff --git a/packages/fhir/src/providers/recording.ts b/packages/fhir/src/providers/recording.ts new file mode 100644 index 00000000..77b53914 --- /dev/null +++ b/packages/fhir/src/providers/recording.ts @@ -0,0 +1,686 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { sha1 } from '@noble/hashes/legacy.js' +import { z } from 'zod' +import { + groveFhirContractVersion, + groveRecordingFormatRegistry, + providerAdapterCatalog, + providerRawMappings, + type ProviderRecordingFormat, +} from './contract.generated.js' +import { + assemblerAgent, + coding, + identifiedEntry, + identifier, + makeApplicationDevice, + provenanceActivity, + resourceId, + sourceEntityAgent, +} from './graph.js' +import { + deriveProviderIdentities, + parseResourceIdentityInput, +} from './identity.js' +import { containsReversibleIdentityRepresentation } from './privacy.js' +import { EXTENSIONS, PROFILES } from './profiles.js' +import { + applicationDeviceSchema, + fhirIdSchema, + identifierInputSchema, + nonBlankStringSchema, + normalizeZodIssue, + primitiveInstantSchema, +} from './provider.js' +import type { + CanonicalBase64, + ProviderRecordingBundleInput, + ConnectedRawProvider, + ImmutableRecordingUrl, + MediaType, + Sha1Base64, +} from './types.js' +import { + deepFreeze, + err, + issues, + ok, + parseAbsoluteUri, + parseFhirInstant, + parsePatientReference, + parsePositiveInteger, + type Issue, + type PositiveInteger, + type Result, +} from '../core/index.js' +import { createEntryIdentity } from '../mobile/identity.js' +import type { IdentifiedEntryIdentityInput } from '../mobile/types.js' +import { parseCollectionBundle, type CollectionBundle } from '../r4/index.js' + +const BASE64_ALPHABET = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' +const BASE64 = /^(?:[A-Za-z\d+/]{4})*(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?$/u +const MEDIA_TYPE = /^[A-Za-z\d!#$&^_.+-]+\/[A-Za-z\d!#$&^_.+-]+$/u + +const encodeBase64 = (bytes: Uint8Array): string => { + let output = '' + const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength) + for (let index = 0; index < bytes.length; index += 3) { + const first = view.getUint8(index) + const second = bytes[index + 1] + const third = bytes[index + 2] + const block = (first << 16) | ((second ?? 0) << 8) | (third ?? 0) + output += BASE64_ALPHABET.charAt((block >>> 18) & 63) + output += BASE64_ALPHABET.charAt((block >>> 12) & 63) + output += + second === undefined ? '=' : BASE64_ALPHABET.charAt((block >>> 6) & 63) + output += third === undefined ? '=' : BASE64_ALPHABET.charAt(block & 63) + } + return output +} + +const decodeBase64 = (value: string): Uint8Array => { + let padding = 0 + if (value.endsWith('==')) padding = 2 + else if (value.endsWith('=')) padding = 1 + const bytes = new Uint8Array((value.length / 4) * 3 - padding) + let outputIndex = 0 + for (let index = 0; index < value.length; index += 4) { + const first = BASE64_ALPHABET.indexOf(value.charAt(index)) + const second = BASE64_ALPHABET.indexOf(value.charAt(index + 1)) + const thirdCharacter = value.charAt(index + 2) + const fourthCharacter = value.charAt(index + 3) + const third = + thirdCharacter === '=' ? 0 : BASE64_ALPHABET.indexOf(thirdCharacter) + const fourth = + fourthCharacter === '=' ? 0 : BASE64_ALPHABET.indexOf(fourthCharacter) + const block = (first << 18) | (second << 12) | (third << 6) | fourth + bytes[outputIndex++] = block >>> 16 + if (thirdCharacter !== '=') bytes[outputIndex++] = block >>> 8 + if (fourthCharacter !== '=') bytes[outputIndex++] = block + } + return bytes +} + +const decodeCanonicalBase64 = (value: string): Uint8Array | undefined => { + if (value.length === 0 || !BASE64.test(value)) return undefined + const bytes = decodeBase64(value) + return encodeBase64(bytes) === value ? bytes : undefined +} + +export const parseCanonicalBase64 = ( + value: unknown, +): Result => { + if (typeof value !== 'string' || decodeCanonicalBase64(value) === undefined) { + return err( + 'invalid-code', + 'Expected non-empty, canonically padded RFC 4648 base64.', + ) + } + return ok(value as CanonicalBase64) +} + +export const encodeRecordingBytes = ( + bytes: Uint8Array, +): Result => { + if (!(bytes instanceof Uint8Array) || bytes.length === 0) { + return err('missing-required', 'Recording bytes must not be empty.') + } + return ok(encodeBase64(bytes) as CanonicalBase64) +} + +export const parseSha1Base64 = (value: unknown): Result => { + if (typeof value !== 'string') { + return err('invalid-code', 'Expected a base64-encoded SHA-1 digest.') + } + const decoded = decodeCanonicalBase64(value) + if (decoded?.length !== 20) { + return err( + 'invalid-code', + 'Expected a canonically padded base64-encoded 20-byte SHA-1 digest.', + ) + } + return ok(value as Sha1Base64) +} + +export const parseMediaType = (value: unknown): Result => { + if (typeof value !== 'string' || !MEDIA_TYPE.test(value)) { + return err( + 'invalid-code', + 'Expected a media type in type/subtype form without parameters.', + ) + } + return ok(value as MediaType) +} + +const parseAttachmentSize = (value: unknown): Result => { + const parsed = parsePositiveInteger(value) + if (!parsed.ok) return parsed + if (parsed.value > 2_147_483_647) { + return err( + 'out-of-range', + 'Attachment.size must fit the FHIR R4 unsignedInt range.', + ) + } + return parsed +} + +export const parseImmutableRecordingUrl = ( + value: unknown, +): Result => { + if (typeof value !== 'string' || /\s/u.test(value)) { + return err('invalid-uri', 'Expected an immutable HTTP(S) recording URL.') + } + try { + const parsed = new URL(value) + if ( + !['http:', 'https:'].includes(parsed.protocol) || + parsed.username !== '' || + parsed.password !== '' + ) { + return err( + 'invalid-uri', + 'Expected an immutable HTTP(S) recording URL without user information.', + ) + } + decodeURIComponent(value) + } catch { + return err( + 'invalid-uri', + 'Expected an immutable HTTP(S) recording URL with valid percent encoding.', + ) + } + return ok(value as ImmutableRecordingUrl) +} + +const providerValues = Object.keys(providerRawMappings) as [ + ConnectedRawProvider, + ...ConnectedRawProvider[], +] + +const recordingSourceSchema = z.strictObject({ + adapter: z.strictObject({ + kind: z.literal('providers'), + provider: z.enum(providerValues), + }), + providerAccountIdentifier: identifierInputSchema.extend({ + assurance: z.literal('deployment-scoped-pseudonym'), + }), + sourceType: nonBlankStringSchema, + sourceNativeId: nonBlankStringSchema, + dataOrigin: applicationDeviceSchema, +}) + +const recordingAttachmentBase = { + contentType: z.string().min(1), + title: nonBlankStringSchema, + format: z.enum( + Object.keys(groveRecordingFormatRegistry.formats) as [ + ProviderRecordingFormat, + ...ProviderRecordingFormat[], + ], + ), + payloadAssertion: z.enum( + providerAdapterCatalog.rawPayloadAdmission.allowedAssertions, + ), +} as const + +const recordingAttachmentSchema = z.discriminatedUnion('kind', [ + z.strictObject({ + ...recordingAttachmentBase, + kind: z.literal('embedded'), + dataBase64: z.string().min(1), + }), + z.strictObject({ + ...recordingAttachmentBase, + kind: z.literal('external'), + url: z.string().min(1), + size: z.number(), + hash: z.string().min(1), + immutabilityAssurance: z.literal('immutable-version-specific'), + }), +]) + +const recordingBundleInputSchema = z.strictObject({ + source: recordingSourceSchema, + attachment: recordingAttachmentSchema, + subject: z.string(), + application: applicationDeviceSchema, + eventSequence: z.number(), + graphIdentifierSystem: z.string(), + documentDate: primitiveInstantSchema, + recorded: primitiveInstantSchema, + repositoryIds: z + .strictObject({ + bundle: fhirIdSchema.optional(), + document: fhirIdSchema.optional(), + provenance: fhirIdSchema.optional(), + }) + .optional(), +}) + +const recordingGraphIdentityIssues = ( + input: z.infer, +): readonly Issue[] => { + const graphIdentityInputs: ReadonlyArray< + readonly [ + Parameters[0], + readonly string[], + ] + > = [ + [input.application.identity, ['application', 'identity']], + [input.source.dataOrigin.identity, ['source', 'dataOrigin', 'identity']], + ] + return graphIdentityInputs.flatMap(([identity, path]) => { + const parsedIdentity = parseResourceIdentityInput(identity) + return parsedIdentity.ok ? + [] + : parsedIdentity.issues.map((entry) => ({ + ...entry, + path: [...path, ...entry.path], + })) + }) +} + +const hasRawMapping = ( + provider: ConnectedRawProvider, + sourceType: string, +): boolean => Object.hasOwn(providerRawMappings[provider], sourceType) + +const attachmentMetadataStrings = ( + attachment: z.infer, +): readonly string[] => { + if (attachment.kind === 'external') { + return [ + attachment.contentType, + attachment.title, + attachment.hash, + attachment.url, + ] + } + const bytes = decodeCanonicalBase64(attachment.dataBase64) + return [ + attachment.contentType, + attachment.title, + ...(bytes === undefined ? [] : [encodeBase64(sha1(bytes))]), + ] +} + +const emittedCallerStrings = ( + input: z.infer, +): readonly string[] => [ + input.source.adapter.provider, + input.source.sourceType, + input.subject, + input.documentDate, + input.recorded, + ...attachmentMetadataStrings(input.attachment), + input.application.identity.identifier.system, + input.application.identity.identifier.value, + input.application.identity.id ?? '', + input.application.name, + input.application.version ?? '', + input.application.manufacturer ?? '', + input.source.dataOrigin.identity.identifier.system, + input.source.dataOrigin.identity.identifier.value, + input.source.dataOrigin.identity.id ?? '', + input.source.dataOrigin.name, + input.source.dataOrigin.version ?? '', + input.source.dataOrigin.manufacturer ?? '', + input.repositoryIds?.bundle ?? '', + input.repositoryIds?.document ?? '', + input.repositoryIds?.provenance ?? '', +] + +/** Strict parser for the closed Provider mapped-standard facade. */ +export const parseProviderRecordingBundleInput = ( + input: unknown, +): Result => { + const parsed = recordingBundleInputSchema.safeParse(input) + if (!parsed.success) { + return issues(parsed.error.issues.map(normalizeZodIssue)) + } + + const findings: Issue[] = [] + findings.push(...recordingGraphIdentityIssues(parsed.data)) + if ( + !hasRawMapping( + parsed.data.source.adapter.provider, + parsed.data.source.sourceType, + ) + ) { + findings.push({ + severity: 'error', + code: 'unsupported-measurement', + path: ['source', 'sourceType'], + message: `${parsed.data.source.adapter.provider}/${parsed.data.source.sourceType} is not admitted as a Provider native recording.`, + }) + } + const checks: ReadonlyArray< + readonly [Result, ReadonlyArray] + > = [ + [parsePatientReference(parsed.data.subject), ['subject']], + [parsePositiveInteger(parsed.data.eventSequence), ['eventSequence']], + [parseFhirInstant(parsed.data.documentDate), ['documentDate']], + [parseFhirInstant(parsed.data.recorded), ['recorded']], + [ + parseMediaType(parsed.data.attachment.contentType), + ['attachment', 'contentType'], + ], + ...(parsed.data.attachment.kind === 'embedded' ? + [ + [ + parseCanonicalBase64(parsed.data.attachment.dataBase64), + ['attachment', 'dataBase64'], + ] as const, + ] + : [ + [ + parseImmutableRecordingUrl(parsed.data.attachment.url), + ['attachment', 'url'], + ] as const, + [ + parseAttachmentSize(parsed.data.attachment.size), + ['attachment', 'size'], + ] as const, + [ + parseSha1Base64(parsed.data.attachment.hash), + ['attachment', 'hash'], + ] as const, + ]), + ] + for (const [check, path] of checks) { + if (!check.ok) { + findings.push( + ...check.issues.map((finding) => ({ ...finding, path: [...path] })), + ) + } + } + + const declaredFormat = + groveRecordingFormatRegistry.formats[parsed.data.attachment.format] + if (parsed.data.attachment.contentType !== declaredFormat.contentType) { + findings.push({ + severity: 'error', + code: 'value-mismatch', + path: ['attachment', 'contentType'], + message: `Recording contentType must be ${declaredFormat.contentType} for the declared ${parsed.data.attachment.format} registry format.`, + }) + } + + const emittedStrings = emittedCallerStrings(parsed.data) + for (const [privateValue, path, label] of [ + [ + parsed.data.source.sourceNativeId, + ['source', 'sourceNativeId'], + 'sourceNativeId', + ], + [ + parsed.data.source.providerAccountIdentifier.value, + ['source', 'providerAccountIdentifier', 'value'], + 'providerAccountIdentifier.value', + ], + ] as const) { + if ( + emittedStrings.some( + (value) => + value !== '' && + containsReversibleIdentityRepresentation(value, privateValue), + ) + ) { + findings.push({ + severity: 'error', + code: 'invalid-identifier', + path, + message: `${label} is an identity input only and must not appear in emitted FHIR metadata.`, + }) + } + } + + if (findings.length === 0) { + const providerAccountSystem = parseAbsoluteUri( + parsed.data.source.providerAccountIdentifier.system, + ) + const eventSequence = parsePositiveInteger(parsed.data.eventSequence) + const graphSystem = parseAbsoluteUri(parsed.data.graphIdentifierSystem) + if (!graphSystem.ok) { + findings.push(...graphSystem.issues) + } else if (!providerAccountSystem.ok) { + findings.push(...providerAccountSystem.issues) + } else if (!eventSequence.ok) { + findings.push(...eventSequence.issues) + } else { + const identity = deriveProviderIdentities({ + provider: parsed.data.source.adapter.provider, + providerAccountIdentifier: { + system: providerAccountSystem.value, + value: parsed.data.source.providerAccountIdentifier.value, + }, + sourceType: parsed.data.source.sourceType, + sourceNativeId: parsed.data.source.sourceNativeId, + outputDiscriminators: [ + providerAdapterCatalog.recordingDocument.outputDiscriminator, + ], + eventSequence: eventSequence.value, + graphIdentifierSystem: graphSystem.value, + }) + if (!identity.ok) findings.push(...identity.issues) + } + } + + if (findings.length > 0) return issues(findings) + return ok(deepFreeze(parsed.data) as unknown as ProviderRecordingBundleInput) +} + +interface RecordingGraphIdentities { + readonly sourceRecord: { + readonly system: import('../core/index.js').AbsoluteUri + readonly value: string + } + readonly exchange: { + readonly system: import('../core/index.js').AbsoluteUri + readonly value: string + } + readonly document: IdentifiedEntryIdentityInput + readonly provenance: IdentifiedEntryIdentityInput + readonly application: IdentifiedEntryIdentityInput + readonly dataOrigin: IdentifiedEntryIdentityInput +} + +const resolveRecordingGraphIdentities = ( + input: ProviderRecordingBundleInput, +): Result => { + const connected = deriveProviderIdentities({ + provider: input.source.adapter.provider, + providerAccountIdentifier: input.source.providerAccountIdentifier, + sourceType: input.source.sourceType, + sourceNativeId: input.source.sourceNativeId, + outputDiscriminators: [ + providerAdapterCatalog.recordingDocument.outputDiscriminator, + ], + eventSequence: input.eventSequence, + graphIdentifierSystem: input.graphIdentifierSystem, + }) + if (!connected.ok) return connected + // A recording document is a one-to-one conversion, so its identity is the source record's. + const document = createEntryIdentity( + connected.value.outputs[0] ?? connected.value.sourceRecord, + input.repositoryIds?.document, + ) + if (!document.ok) return document + const provenance = createEntryIdentity( + connected.value.conversion, + input.repositoryIds?.provenance, + ) + if (!provenance.ok) return provenance + const application = createEntryIdentity( + input.application.identity.identifier, + input.application.identity.id, + ) + if (!application.ok) return application + const dataOrigin = createEntryIdentity( + input.source.dataOrigin.identity.identifier, + input.source.dataOrigin.identity.id, + ) + if (!dataOrigin.ok) return dataOrigin + + const fullUrls = [ + document.value.fullUrl, + provenance.value.fullUrl, + application.value.fullUrl, + dataOrigin.value.fullUrl, + ] + if (new Set(fullUrls).size !== fullUrls.length) { + return err( + 'duplicate-identifier', + 'Every Bundle entry requires a distinct business identifier.', + ) + } + return ok({ + sourceRecord: connected.value.sourceRecord, + exchange: connected.value.exchange, + document: document.value, + provenance: provenance.value, + application: application.value, + dataOrigin: dataOrigin.value, + }) +} + +const providerTitle = (provider: ConnectedRawProvider): string => + providerAdapterCatalog.providers.find((entry) => entry.id === provider) + ?.title ?? provider + +const attachmentFor = (input: ProviderRecordingBundleInput['attachment']) => { + if (input.kind === 'external') { + return { + contentType: input.contentType, + url: input.url, + size: input.size, + hash: input.hash, + title: input.title, + } + } + const validatedBytes = decodeBase64(input.dataBase64) + return { + contentType: input.contentType, + data: input.dataBase64, + size: validatedBytes.length, + hash: encodeBase64(sha1(validatedBytes)), + title: input.title, + } +} + +/** + * Builds the complete deterministic R4 graph for one already-obtained native + * provider recording. This pure facade performs no fetching, authentication, + * webhook handling, vendor parsing, or credential management. + */ +export const buildProviderRecordingBundle = ( + input: ProviderRecordingBundleInput, +): Result => { + const parsed = parseProviderRecordingBundleInput(input) + if (!parsed.ok) return parsed + const validated = parsed.value + const identities = resolveRecordingGraphIdentities(validated) + if (!identities.ok) return identities + + const sourceCode = `${validated.source.adapter.provider}/${validated.source.sourceType}` + const document = { + resourceType: 'DocumentReference' as const, + ...resourceId(identities.value.document), + meta: { + profile: [ + PROFILES.sensorRecordingDocument, + PROFILES.providerRecordingDocument, + ], + }, + extension: [ + { + url: EXTENSIONS.provider, + valueCode: validated.source.adapter.provider, + }, + { + url: EXTENSIONS.providerSourceType, + valueCode: sourceCode, + }, + ], + // A one-to-one conversion identifies the document by its source record, so the entry identity + // is that same pair; listing it twice would repeat one identifier on the resource. + identifier: [ + identifier(identities.value.sourceRecord), + ...(( + identities.value.document.identifier.system === + identities.value.sourceRecord.system && + identities.value.document.identifier.value === + identities.value.sourceRecord.value + ) ? + [] + : [identifier(identities.value.document.identifier)]), + ], + status: 'current' as const, + type: { + text: `${providerTitle(validated.source.adapter.provider)} ${validated.source.sourceType} archive`, + }, + subject: { reference: validated.subject }, + date: validated.documentDate, + author: [{ reference: identities.value.application.fullUrl }], + content: [ + { + attachment: attachmentFor(validated.attachment), + format: { + // The release travels in the instance: a receiver reads which registry generation + // defined this code, and the producer contract refuses a version that never did. + ...coding( + groveRecordingFormatRegistry.codeSystem, + validated.attachment.format, + groveRecordingFormatRegistry.formats[validated.attachment.format] + .title, + ), + version: groveFhirContractVersion, + }, + }, + ], + } + const application = makeApplicationDevice(validated.application) + const dataOrigin = makeApplicationDevice(validated.source.dataOrigin) + const provenance = { + resourceType: 'Provenance' as const, + ...resourceId(identities.value.provenance), + meta: { profile: [PROFILES.providerConversionProvenance] }, + target: [{ reference: identities.value.document.fullUrl }], + recorded: validated.recorded, + activity: provenanceActivity(), + agent: [assemblerAgent(identities.value.application.fullUrl)], + entity: [ + { + role: 'source' as const, + what: { identifier: identifier(identities.value.sourceRecord) }, + agent: [sourceEntityAgent(identities.value.dataOrigin.fullUrl)], + }, + ], + } + + return parseCollectionBundle({ + resourceType: 'Bundle', + ...(validated.repositoryIds?.bundle === undefined ? + {} + : { id: validated.repositoryIds.bundle }), + meta: { profile: [PROFILES.mobileBundle] }, + identifier: identifier(identities.value.exchange), + type: 'collection', + timestamp: validated.recorded, + entry: [ + identifiedEntry(identities.value.document, document), + identifiedEntry(identities.value.dataOrigin, dataOrigin), + identifiedEntry(identities.value.application, application), + identifiedEntry(identities.value.provenance, provenance), + ], + }) +} diff --git a/packages/fhir/src/providers/types.ts b/packages/fhir/src/providers/types.ts new file mode 100644 index 00000000..22ce96b8 --- /dev/null +++ b/packages/fhir/src/providers/types.ts @@ -0,0 +1,266 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import type { + ProviderRawMappings, + ProviderScalarMappings, +} from './contract.generated.js' +import type { + AbsoluteUri, + FhirId, + FhirInstant, + PatientReference, + PositiveInteger, + ResearchStudyReference, +} from '../core/index.js' +import type { SharedMobileMeasurementKind } from '../mobile/measurement-catalog.generated.js' +import type { + ApplicationDeviceInput, + CompleteIdentifierInput, + GatewayApplicationInput, + MobileMeasurement, + RecordingDeviceInput, + RecordingMethod, +} from '../mobile/types.js' + +type ScalarMappings = ProviderScalarMappings + +/** Explicit assertion that this account key is a deployment-owned pseudonym. */ +export interface ProviderAccountPseudonymInput extends CompleteIdentifierInput { + readonly assurance: 'deployment-scoped-pseudonym' +} + +/** Exact closed provider codes defined by the Provider IG. */ +export type ConnectedProvider = keyof ScalarMappings + +/** Exact source tokens that contain at least one admitted scalar mapping. */ +export type ConnectedSourceType = + keyof ScalarMappings[Provider] & string + +export type SupportedConnectedProviderMeasurementKind< + Provider extends ConnectedProvider, + SourceType extends ConnectedSourceType = + ConnectedSourceType, +> = keyof ScalarMappings[Provider][SourceType] & SharedMobileMeasurementKind + +export interface ProviderAdapter< + Provider extends ConnectedProvider = ConnectedProvider, +> { + readonly kind: 'providers' + readonly provider: Provider +} + +interface NormalizedSourceRecordBase { + readonly recordingMethod?: RecordingMethod + readonly recordingDevice?: RecordingDeviceInput +} + +/** + * Identity-only source fields. The native id is hashed and never copied into + * FHIR output. The account identifier must be deployment-scoped and + * pseudonymous; an email, vendor account id, or access credential is invalid. + */ +export interface ProviderSourceRecord< + Provider extends ConnectedProvider, + SourceType extends ConnectedSourceType, +> extends NormalizedSourceRecordBase { + readonly adapter: ProviderAdapter + readonly providerAccountIdentifier: ProviderAccountPseudonymInput + readonly sourceType: SourceType + readonly sourceNativeId: string + /** Application that entered the record into the connected provider. */ + readonly dataOrigin: ApplicationDeviceInput +} + +export type ConnectedProviderMeasurements< + Provider extends ConnectedProvider, + SourceType extends ConnectedSourceType, +> = readonly [ + Extract< + MobileMeasurement, + { + readonly kind: SupportedConnectedProviderMeasurementKind< + Provider, + SourceType + > + } + >, + ...Array< + Extract< + MobileMeasurement, + { + readonly kind: SupportedConnectedProviderMeasurementKind< + Provider, + SourceType + > + } + > + >, +] + +export interface ConnectedProviderRecord< + Provider extends ConnectedProvider, + SourceType extends ConnectedSourceType, +> { + /** Non-empty unique subset of catalog-admitted outputs for one source record. */ + readonly measurements: ConnectedProviderMeasurements + readonly source: ProviderSourceRecord +} + +type ConnectedProviderRecordFor = { + readonly [ + SourceType in ConnectedSourceType + ]: ConnectedProviderRecord +}[ConnectedSourceType] + +/** Provider-neutral output of an external adapter, before FHIR construction. */ +export type NormalizedProviderRecord = { + readonly [Provider in ConnectedProvider]: ConnectedProviderRecordFor +}[ConnectedProvider] + +export type NormalizedSourceRecord = NormalizedProviderRecord['source'] + +type RawMappings = ProviderRawMappings + +/** Providers with at least one source admitted as a native Sensor recording. */ +export type ConnectedRawProvider = keyof RawMappings + +/** Exact provider source tokens admitted by the Provider raw contract. */ +export type ConnectedRawSourceType = + keyof RawMappings[Provider] & string + +declare const recordingInputBrand: unique symbol + +/** Canonically padded RFC 4648 base64 containing at least one byte. */ +export type CanonicalBase64 = string & { + readonly [recordingInputBrand]: 'CanonicalBase64' +} + +/** Base64-encoded 20-byte SHA-1 digest required by FHIR R4 Attachment.hash. */ +export type Sha1Base64 = string & { + readonly [recordingInputBrand]: 'Sha1Base64' +} + +/** A syntactically valid media type without content-transfer parameters. */ +export type MediaType = string & { + readonly [recordingInputBrand]: 'MediaType' +} + +/** Caller-asserted immutable, version-specific HTTP(S) recording URL. */ +export type ImmutableRecordingUrl = string & { + readonly [recordingInputBrand]: 'ImmutableRecordingUrl' +} + +export interface ProviderRecordingSourceRecord< + Provider extends ConnectedRawProvider, + SourceType extends ConnectedRawSourceType, +> { + readonly adapter: ProviderAdapter + readonly providerAccountIdentifier: ProviderAccountPseudonymInput + readonly sourceType: SourceType + /** Digest input only. It is never copied into emitted FHIR metadata. */ + readonly sourceNativeId: string + /** Application that entered the already-obtained record at the provider. */ + readonly dataOrigin: ApplicationDeviceInput +} + +interface RecordingAttachmentBaseInput { + readonly contentType: MediaType + readonly title: string + /** Registered payload format the recording bytes conform to. */ + readonly format: ProviderRecordingFormat + /** Required deployment assertion for opaque content Grove cannot inspect. */ + readonly payloadAssertion: RawPayloadAdmissionAssertion +} + +export type ProviderRecordingFormat = + import('./contract.generated.js').ProviderRecordingFormat + +export type RawPayloadAdmissionAssertion = + (typeof import('./contract.generated.js').providerAdapterCatalog)['rawPayloadAdmission']['allowedAssertions'][number] + +export interface EmbeddedRecordingAttachmentInput extends RecordingAttachmentBaseInput { + readonly kind: 'embedded' + /** Exact caller-supplied bytes represented as canonical RFC 4648 base64. */ + readonly dataBase64: CanonicalBase64 +} + +export interface ExternalRecordingAttachmentInput extends RecordingAttachmentBaseInput { + readonly kind: 'external' + readonly url: ImmutableRecordingUrl + readonly size: PositiveInteger + readonly hash: Sha1Base64 + readonly immutabilityAssurance: 'immutable-version-specific' +} + +export type ProviderRecordingAttachmentInput = + EmbeddedRecordingAttachmentInput | ExternalRecordingAttachmentInput + +export type ProviderRecordingSource = { + readonly [Provider in ConnectedRawProvider]: { + readonly [ + SourceType in ConnectedRawSourceType + ]: ProviderRecordingSourceRecord + }[ConnectedRawSourceType] +}[ConnectedRawProvider] + +export interface RecordingRepositoryAssignedResourceIds { + readonly bundle?: FhirId + readonly document?: FhirId + readonly provenance?: FhirId +} + +/** Closed input for one mapped-standard Provider native recording. */ +export interface ProviderRecordingBundleInput { + readonly source: ProviderRecordingSource + readonly attachment: ProviderRecordingAttachmentInput + readonly subject: PatientReference + readonly application: ApplicationDeviceInput + /** Durable caller-owned sequence for a new conversion/exchange event. */ + readonly eventSequence: PositiveInteger + /** + * The namespace this deployment owns for graph nodes the export creates. + * + * The conversion Provenance and the exchange Bundle record an export event rather than anything + * read from the provider, so they are named here rather than in a namespace this guide owns. + */ + readonly graphIdentifierSystem: AbsoluteUri + readonly documentDate: FhirInstant + readonly recorded: FhirInstant + readonly repositoryIds?: RecordingRepositoryAssignedResourceIds +} + +export interface MeasurementRepositoryAssignedResourceIds { + readonly bundle?: FhirId + readonly observations?: Readonly< + Partial> + > + readonly provenance?: FhirId +} + +interface ProviderMeasurementGraphInput { + readonly subject: PatientReference + readonly application: ApplicationDeviceInput + readonly gatewayApplication?: GatewayApplicationInput + /** Durable caller-owned sequence for a new conversion/exchange event. */ + readonly eventSequence: PositiveInteger + /** + * The namespace this deployment owns for graph nodes the export creates. + * + * The conversion Provenance and the exchange Bundle record an export event rather than anything + * read from the provider, so they are named here rather than in a namespace this guide owns. + */ + readonly graphIdentifierSystem: AbsoluteUri + readonly issued: FhirInstant + readonly recorded: FhirInstant + readonly repositoryIds?: MeasurementRepositoryAssignedResourceIds + readonly researchStudyReferences?: readonly ResearchStudyReference[] +} + +export type ProviderMeasurementBundleInput = NormalizedProviderRecord & + ProviderMeasurementGraphInput diff --git a/packages/fhir/src/questionnaire/builder.ts b/packages/fhir/src/questionnaire/builder.ts new file mode 100644 index 00000000..7e151eba --- /dev/null +++ b/packages/fhir/src/questionnaire/builder.ts @@ -0,0 +1,313 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import type { z } from 'zod' +import { groveQuestionnaireProfileCanonicals } from './contract.generated.js' +import { + validateQuestionnaireContract, + validateQuestionnaireResponseItemContract, +} from './contract.js' +import { parseQuestionnaire, parseQuestionnaireResponse } from './parse.js' +import { isR4ResourceType } from './r4-resource-types.js' +import { + questionnaireBuilderInputSchema, + questionnaireResponseBuilderInputSchema, +} from './schemas.js' +import type { + GroveQuestionnaire, + GroveQuestionnaireResponse, + QuestionnaireInput, + QuestionnaireResponseInput, +} from './types.js' +import { + issues, + parseAbsoluteUri, + parseCanonical, + parseFhirId, + parseFhirInstant, + parsePatientReference, + parseSemVer, + type Issue, + type Result, +} from '../core/index.js' + +/* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 canonicals are normative HTTP URIs. */ + +const VERSION_ALGORITHM = + 'http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm' +const VERSION_ALGORITHM_SYSTEM = 'http://hl7.org/fhir/version-algorithm' +const COMPLETION_MODE = + 'http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode' +const PARTICIPATION_MODE = + 'http://terminology.hl7.org/CodeSystem/v3-ParticipationMode' +const issue = ( + code: Issue['code'], + path: Issue['path'], + message: string, +): Issue => ({ severity: 'error', code, path, message }) + +const schemaIssue = (entry: z.core.$ZodIssue): Issue => ({ + severity: 'error', + code: 'schema-invalid', + path: entry.path.map((component) => + typeof component === 'symbol' ? + (component.description ?? component.toString()) + : component, + ), + message: entry.message, +}) + +const parseBuilderInput = (schema: z.ZodType, input: unknown): Result => { + const parsed = schema.safeParse(input) + return parsed.success ? + ({ ok: true, value: parsed.data as T, warnings: [] } as const) + : issues(parsed.error.issues.map(schemaIssue)) +} + +const extensionCount = ( + extensions: ReadonlyArray<{ readonly url: string }> | undefined, + url: string, +) => extensions?.filter((extension) => extension.url === url).length ?? 0 + +const validateQuestionnaireInput = ( + input: QuestionnaireInput, +): readonly Issue[] => { + const failures: Issue[] = [] + if (!parseAbsoluteUri(input.url).ok) { + failures.push( + issue('invalid-uri', ['url'], 'Questionnaire.url is invalid.'), + ) + } + if (!parseSemVer(input.version).ok) { + failures.push( + issue( + 'invalid-code', + ['version'], + 'Questionnaire.version must be SemVer.', + ), + ) + } + if (input.id !== undefined && !parseFhirId(input.id).ok) { + failures.push( + issue('invalid-identifier', ['id'], 'Questionnaire.id is invalid.'), + ) + } + if (input.date !== undefined && !parseFhirInstant(input.date).ok) { + failures.push( + issue('invalid-date-time', ['date'], 'Questionnaire.date is invalid.'), + ) + } + if (input.subjectTypes?.some((entry) => entry.trim() === '') === true) { + failures.push( + issue( + 'missing-required', + ['subjectTypes'], + 'Questionnaire.subjectTypes cannot contain empty resource type codes.', + ), + ) + } + if ( + input.subjectTypes?.some( + (entry) => entry.trim() !== '' && !isR4ResourceType(entry), + ) === true + ) { + failures.push( + issue( + 'invalid-code', + ['subjectTypes'], + 'Questionnaire.subjectTypes contains an unknown R4 ResourceType code.', + ), + ) + } + if (extensionCount(input.extensions, VERSION_ALGORITHM) > 0) { + failures.push( + issue( + 'duplicate-identifier', + ['extensions'], + 'The builder owns the single SemVer algorithm extension.', + ), + ) + } + failures.push(...validateQuestionnaireContract(input.extensions, input.items)) + return failures +} + +/** Builds a versioned Grove R4 Questionnaire with the SemVer contract stamped. */ +export const buildQuestionnaire = ( + input: QuestionnaireInput, +): Result => { + const parsedInput = parseBuilderInput( + questionnaireBuilderInputSchema, + input, + ) + if (!parsedInput.ok) return parsedInput + const validatedInput = parsedInput.value + const failures = validateQuestionnaireInput(validatedInput) + if (failures.length > 0) return issues(failures) + + return parseQuestionnaire({ + resourceType: 'Questionnaire', + ...(validatedInput.id === undefined ? {} : { id: validatedInput.id }), + meta: { + profile: [groveQuestionnaireProfileCanonicals['grove-questionnaire']], + }, + extension: [ + ...(validatedInput.extensions ?? []), + { + url: VERSION_ALGORITHM, + valueCoding: { + system: VERSION_ALGORITHM_SYSTEM, + code: 'semver', + }, + }, + ], + url: validatedInput.url, + version: validatedInput.version, + ...(validatedInput.name === undefined ? {} : { name: validatedInput.name }), + ...(validatedInput.title === undefined ? + {} + : { title: validatedInput.title }), + status: validatedInput.status, + ...(validatedInput.subjectTypes === undefined ? + {} + : { subjectType: validatedInput.subjectTypes }), + ...(validatedInput.date === undefined ? {} : { date: validatedInput.date }), + ...(validatedInput.description === undefined ? + {} + : { description: validatedInput.description }), + ...(validatedInput.purpose === undefined ? + {} + : { purpose: validatedInput.purpose }), + item: validatedInput.items, + }) +} + +const validateResponseInput = ( + input: QuestionnaireResponseInput, +): readonly Issue[] => { + const failures: Issue[] = [] + const canonical = parseCanonical(input.questionnaire) + if (!canonical.ok || !/^[^|#]+\|[^|#]+$/u.test(input.questionnaire)) { + failures.push( + issue( + 'invalid-uri', + ['questionnaire'], + 'Response.questionnaire must be an exact url|version canonical.', + ), + ) + } + const version = input.questionnaire.split('|')[1] + if (!parseSemVer(version).ok) { + failures.push( + issue( + 'invalid-code', + ['questionnaire'], + 'Response.questionnaire version must be SemVer.', + ), + ) + } + if ( + !parseAbsoluteUri(input.identifier.system).ok || + input.identifier.value.trim() === '' + ) { + failures.push( + issue( + 'invalid-identifier', + ['identifier'], + 'Response identifier requires an absolute system and non-empty value.', + ), + ) + } + if (!parseFhirInstant(input.authored).ok) { + failures.push( + issue('invalid-date-time', ['authored'], 'Response.authored is invalid.'), + ) + } + if (input.id !== undefined && !parseFhirId(input.id).ok) { + failures.push( + issue('invalid-identifier', ['id'], 'Response.id is invalid.'), + ) + } + if (input.subject !== undefined && !parsePatientReference(input.subject).ok) { + failures.push( + issue('invalid-reference', ['subject'], 'Response.subject is invalid.'), + ) + } + for (const [field, reference] of [ + ['authorReference', input.authorReference], + ['sourceReference', input.sourceReference], + ] as const) { + if (reference?.trim() === '') { + failures.push( + issue('invalid-reference', [field], `${field} must not be empty.`), + ) + } + } + if (extensionCount(input.extensions, COMPLETION_MODE) > 0) { + failures.push( + issue( + 'duplicate-identifier', + ['extensions'], + 'The builder owns the single electronic completion-mode extension.', + ), + ) + } + failures.push( + ...validateQuestionnaireResponseItemContract(input.items ?? [], ['items']), + ) + return failures +} + +/** Builds a Grove R4 QuestionnaireResponse for one exact instrument version. */ +export const buildQuestionnaireResponse = ( + input: QuestionnaireResponseInput, +): Result => { + const parsedInput = parseBuilderInput( + questionnaireResponseBuilderInputSchema, + input, + ) + if (!parsedInput.ok) return parsedInput + const validatedInput = parsedInput.value + const failures = validateResponseInput(validatedInput) + if (failures.length > 0) return issues(failures) + + return parseQuestionnaireResponse({ + resourceType: 'QuestionnaireResponse', + ...(validatedInput.id === undefined ? {} : { id: validatedInput.id }), + meta: { + profile: [ + groveQuestionnaireProfileCanonicals['grove-questionnaire-response'], + ], + }, + extension: [ + ...(validatedInput.extensions ?? []), + { + url: COMPLETION_MODE, + valueCodeableConcept: { + coding: [{ system: PARTICIPATION_MODE, code: 'ELECTRONIC' }], + }, + }, + ], + identifier: validatedInput.identifier, + questionnaire: validatedInput.questionnaire, + status: validatedInput.status, + ...(validatedInput.subject === undefined ? + {} + : { subject: { reference: validatedInput.subject } }), + authored: validatedInput.authored, + ...(validatedInput.authorReference === undefined ? + {} + : { author: { reference: validatedInput.authorReference } }), + ...(validatedInput.sourceReference === undefined ? + {} + : { source: { reference: validatedInput.sourceReference } }), + ...(validatedInput.items === undefined ? + {} + : { item: validatedInput.items }), + }) +} diff --git a/packages/fhir/src/questionnaire/contract.generated.ts b/packages/fhir/src/questionnaire/contract.generated.ts new file mode 100644 index 00000000..e11ec70d --- /dev/null +++ b/packages/fhir/src/questionnaire/contract.generated.ts @@ -0,0 +1,35 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// +// Generated by scripts/generate-measurement-catalog.mjs. Do not edit directly. + +import { deepFreeze } from '../core/index.js' + +export const groveFhirContractVersion = '0.5.0' as const + +export const groveFhirVersion = '4.0.1' as const + +const groveQuestionnairePackageMetadataValue = { + fhirVersion: '4.0.1', + version: '0.5.0', + packageId: 'org.grovealliance.fhir.questionnaire', + canonical: 'https://grovealliance.org/fhir/questionnaire', + dependencies: ['hl7.fhir.uv.sdc#4.0.0', 'hl7.fhir.uv.extensions.r4#5.3.0'], +} as const + +export const groveQuestionnairePackageMetadata: typeof groveQuestionnairePackageMetadataValue = + deepFreeze(groveQuestionnairePackageMetadataValue) + +const groveQuestionnaireProfileCanonicalsValue = { + 'grove-questionnaire': + 'https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire', + 'grove-questionnaire-response': + 'https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response', +} as const + +export const groveQuestionnaireProfileCanonicals: typeof groveQuestionnaireProfileCanonicalsValue = + deepFreeze(groveQuestionnaireProfileCanonicalsValue) diff --git a/packages/fhir/src/questionnaire/contract.ts b/packages/fhir/src/questionnaire/contract.ts new file mode 100644 index 00000000..bcf6c081 --- /dev/null +++ b/packages/fhir/src/questionnaire/contract.ts @@ -0,0 +1,1319 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + compareR4Temporal, + isR4Date, + isR4DateTime, + isR4Time, + type R4TemporalKind, +} from './temporal.js' +import type { + QuestionnaireItemInput, + QuestionnaireResponseAnswerInput, + QuestionnaireResponseItemInput, +} from './types.js' +import type { Issue } from '../core/index.js' +import type { Extension } from '../r4/index.js' + +export const QUESTIONNAIRE_EXTENSIONS = { + calculatedExpression: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression', + enableWhenExpression: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression', + initialExpression: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', + hidden: 'http://hl7.org/fhir/StructureDefinition/questionnaire-hidden', + itemWeight: 'http://hl7.org/fhir/StructureDefinition/itemWeight', + maxDecimalPlaces: 'http://hl7.org/fhir/StructureDefinition/maxDecimalPlaces', + maxOccurs: 'http://hl7.org/fhir/StructureDefinition/questionnaire-maxOccurs', + maxQuantity: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-maxQuantity', + maxSize: 'http://hl7.org/fhir/StructureDefinition/maxSize', + maxValue: 'http://hl7.org/fhir/StructureDefinition/maxValue', + mimeType: 'http://hl7.org/fhir/StructureDefinition/mimeType', + minLength: 'http://hl7.org/fhir/StructureDefinition/minLength', + minOccurs: 'http://hl7.org/fhir/StructureDefinition/questionnaire-minOccurs', + minQuantity: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-minQuantity', + minValue: 'http://hl7.org/fhir/StructureDefinition/minValue', + optionExclusive: + 'http://hl7.org/fhir/StructureDefinition/questionnaire-optionExclusive', + questionnaireUnit: + 'http://hl7.org/fhir/StructureDefinition/questionnaire-unit', + styleSensitive: + 'http://hl7.org/fhir/StructureDefinition/rendering-styleSensitive', + targetConstraint: 'http://hl7.org/fhir/StructureDefinition/targetConstraint', + unitOption: + 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption', + unitValueSet: + 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitValueSet', + variable: 'http://hl7.org/fhir/StructureDefinition/variable', +} as const + +interface ExtensionElement { + readonly extension?: readonly Extension[] +} + +export interface ExtensionValue { + readonly key: string + readonly value: unknown +} + +export type QuestionnaireAnswerValueKey = + | 'valueAttachment' + | 'valueBoolean' + | 'valueCoding' + | 'valueDate' + | 'valueDateTime' + | 'valueDecimal' + | 'valueInteger' + | 'valueQuantity' + | 'valueString' + | 'valueTime' + | 'valueUri' + +/** Expected Questionnaire/Response value[x] discriminators for an item type. */ +export const expectedQuestionnaireAnswerKeys = ( + type: QuestionnaireItemInput['type'], +): readonly QuestionnaireAnswerValueKey[] => { + switch (type) { + case 'boolean': + return ['valueBoolean'] + case 'decimal': + return ['valueDecimal'] + case 'integer': + return ['valueInteger'] + case 'date': + return ['valueDate'] + case 'dateTime': + return ['valueDateTime'] + case 'time': + return ['valueTime'] + case 'string': + case 'text': + return ['valueString'] + case 'url': + return ['valueUri'] + case 'choice': + return ['valueCoding'] + case 'open-choice': + return ['valueCoding', 'valueString'] + case 'attachment': + return ['valueAttachment'] + case 'quantity': + return ['valueQuantity'] + case 'display': + case 'group': + return [] + } +} + +const expectedAnswerOptionKeys = ( + type: QuestionnaireItemInput['type'], +): readonly QuestionnaireAnswerValueKey[] => { + switch (type) { + case 'integer': + return ['valueInteger'] + case 'date': + return ['valueDate'] + case 'time': + return ['valueTime'] + case 'string': + return ['valueString'] + case 'choice': + return ['valueCoding'] + case 'open-choice': + return ['valueCoding', 'valueString'] + default: + return [] + } +} + +const questionnaireAnswerValueKeys: readonly QuestionnaireAnswerValueKey[] = [ + 'valueBoolean', + 'valueDecimal', + 'valueInteger', + 'valueDate', + 'valueDateTime', + 'valueTime', + 'valueString', + 'valueUri', + 'valueAttachment', + 'valueCoding', + 'valueQuantity', +] + +const populatedQuestionnaireAnswerKey = ( + value: object, +): QuestionnaireAnswerValueKey | undefined => + questionnaireAnswerValueKeys.find( + (key) => Reflect.get(value, key) !== undefined, + ) + +const issue = ( + code: Issue['code'], + path: Issue['path'], + message: string, +): Issue => ({ severity: 'error', code, path, message }) + +export const extensionsFor = ( + element: ExtensionElement, + url: string, +): readonly Extension[] => + (element.extension ?? []).filter((extension) => extension.url === url) + +export const extensionValue = ( + extension: Extension | undefined, +): ExtensionValue | undefined => { + if (extension === undefined) return undefined + const values = Object.entries(extension).filter( + ([key]) => key.startsWith('value') && key !== 'valueSet', + ) + return values.length === 1 ? + { key: values[0]?.[0] ?? '', value: values[0]?.[1] } + : undefined +} + +export const firstExtensionValue = ( + element: ExtensionElement, + url: string, +): ExtensionValue | undefined => { + const first = extensionsFor(element, url)[0] + return first === undefined ? undefined : extensionValue(first) +} + +const expressionPart = (value: unknown, key: string): unknown => + typeof value === 'object' && value !== null ? + Reflect.get(value, key) + : undefined + +const isValidFhirPathExpression = ( + value: unknown, + requireName = false, +): boolean => + expressionPart(value, 'language') === 'text/fhirpath' && + typeof expressionPart(value, 'expression') === 'string' && + (expressionPart(value, 'expression') as string).trim() !== '' && + (!requireName || + (typeof expressionPart(value, 'name') === 'string' && + (expressionPart(value, 'name') as string).trim() !== '')) + +const expressionUrls = new Set([ + QUESTIONNAIRE_EXTENSIONS.variable, + QUESTIONNAIRE_EXTENSIONS.enableWhenExpression, + QUESTIONNAIRE_EXTENSIONS.initialExpression, + QUESTIONNAIRE_EXTENSIONS.calculatedExpression, +]) + +const variableName = /^[A-Za-z]\w*$/u +const reservedVariableNames = new Set([ + 'context', + 'definition', + 'loinc', + 'qitem', + 'questionnaire', + 'resource', + 'rootResource', + 'sct', + 'target', + 'ucum', +]) + +const targetParts = ( + extension: Extension, + name: string, +): readonly Extension[] => + extension.extension?.filter((part) => part.url === name) ?? [] + +const validateVariableName = ( + value: unknown, + names: Set, + path: ReadonlyArray, +): readonly Issue[] => { + const name = expressionPart(value, 'name') + if (typeof name !== 'string' || name === '') return [] + const failures: Issue[] = [] + if (!variableName.test(name) || reservedVariableNames.has(name)) { + failures.push( + issue( + 'invalid-code', + [...path, 'valueExpression', 'name'], + `FHIRPath variable name ${name} is invalid or reserved.`, + ), + ) + } + if (names.has(name)) { + failures.push( + issue( + 'duplicate-identifier', + [...path, 'valueExpression', 'name'], + `FHIRPath variable name ${name} is duplicated in this scope.`, + ), + ) + } + names.add(name) + return failures +} + +const validateExpressionExtension = ( + extension: Extension, + names: Set, + path: ReadonlyArray, +): readonly Issue[] => { + if (!expressionUrls.has(extension.url)) return [] + const value = extensionValue(extension)?.value + const isVariable = extension.url === QUESTIONNAIRE_EXTENSIONS.variable + const failures: Issue[] = [] + if (!isValidFhirPathExpression(value, isVariable)) { + failures.push( + issue( + 'invalid-type', + path, + 'Expression extensions require a non-empty text/fhirpath expression and variables also require a name.', + ), + ) + } + if (isVariable) failures.push(...validateVariableName(value, names, path)) + return failures +} + +const validateTargetConstraint = ( + extension: Extension, + targetKeys: Set, + path: ReadonlyArray, +): readonly Issue[] => { + if (extension.url !== QUESTIONNAIRE_EXTENSIONS.targetConstraint) return [] + const failures: Issue[] = [] + const values = new Map() + for (const name of ['key', 'severity', 'human', 'expression']) { + const matching = targetParts(extension, name) + if (matching.length !== 1) { + failures.push( + issue( + matching.length === 0 ? 'missing-required' : 'duplicate-identifier', + path, + `targetConstraint requires exactly one ${name} part.`, + ), + ) + } + values.set(name, matching.length === 1 ? matching[0] : undefined) + } + if ((extension.extension ?? []).some((part) => !values.has(part.url))) { + failures.push( + issue( + 'invalid-choice', + path, + 'targetConstraint contains an unsupported nested part.', + ), + ) + } + const key = extensionValue(values.get('key')) + const severity = extensionValue(values.get('severity')) + const human = extensionValue(values.get('human')) + const expression = extensionValue(values.get('expression')) + if ( + key?.key !== 'valueId' || + typeof key.value !== 'string' || + key.value.trim() === '' + ) { + failures.push( + issue( + 'missing-required', + path, + 'targetConstraint requires a non-empty valueId key.', + ), + ) + } else if (targetKeys.has(key.value)) { + failures.push( + issue( + 'duplicate-identifier', + path, + `targetConstraint key ${key.value} is not unique.`, + ), + ) + } else { + targetKeys.add(key.value) + } + if ( + severity?.key !== 'valueCode' || + !['error', 'warning'].includes(String(severity.value)) + ) { + failures.push( + issue( + 'invalid-code', + path, + 'targetConstraint severity must be error or warning.', + ), + ) + } + if ( + human?.key !== 'valueString' || + typeof human.value !== 'string' || + human.value.trim() === '' + ) { + failures.push( + issue( + 'missing-required', + path, + 'targetConstraint requires non-empty human guidance.', + ), + ) + } + if ( + expression?.key !== 'valueExpression' || + !isValidFhirPathExpression(expression.value) + ) { + failures.push( + issue( + 'invalid-type', + path, + 'targetConstraint requires a non-empty text/fhirpath expression.', + ), + ) + } + return failures +} + +const validateExpressionScope = ( + element: ExtensionElement, + path: ReadonlyArray, + targetKeys: Set, +): readonly Issue[] => { + const failures: Issue[] = [] + const names = new Set() + for (const [index, extension] of (element.extension ?? []).entries()) { + const extensionPath = [...path, 'extension', index] + failures.push( + ...validateExpressionExtension(extension, names, extensionPath), + ) + failures.push( + ...validateTargetConstraint(extension, targetKeys, extensionPath), + ) + } + return failures +} + +const comparableQuantity = ( + value: unknown, +): readonly [number, string, string] | undefined => { + if (typeof value !== 'object' || value === null) return undefined + const quantity = value as Readonly> + const quantityValue = quantity.value + const system = quantity.system + const code = quantity.code + return ( + typeof quantityValue === 'number' && + Number.isFinite(quantityValue) && + typeof system === 'string' && + typeof code === 'string' + ) ? + [quantityValue, system, code] + : undefined +} + +/** Returns whether left is less than or equal to a comparable right value. */ +export const boundIsOrdered = ( + left: unknown, + right: unknown, + temporalKind?: R4TemporalKind, +): boolean | undefined => { + if (typeof left === 'number' && typeof right === 'number') { + return Number.isFinite(left) && Number.isFinite(right) ? + left <= right + : undefined + } + if (typeof left === 'string' && typeof right === 'string') { + if (temporalKind === undefined) return left <= right + const comparison = compareR4Temporal(left, right, temporalKind) + return comparison === undefined ? undefined : comparison <= 0 + } + const leftQuantity = comparableQuantity(left) + const rightQuantity = comparableQuantity(right) + if (leftQuantity === undefined || rightQuantity === undefined) + return undefined + return ( + leftQuantity[1] === rightQuantity[1] && + leftQuantity[2] === rightQuantity[2] + ) ? + leftQuantity[0] <= rightQuantity[0] + : undefined +} + +const temporalKindForItem = ( + type: QuestionnaireItemInput['type'], +): R4TemporalKind | undefined => { + if (type === 'date' || type === 'dateTime' || type === 'time') return type + return undefined +} + +const temporalValidatorForItem = ( + type: QuestionnaireItemInput['type'], +): ((value: unknown) => boolean) | undefined => { + switch (type) { + case 'date': + return isR4Date + case 'dateTime': + return isR4DateTime + case 'time': + return isR4Time + default: + return undefined + } +} + +const validateBoundPair = ( + item: QuestionnaireItemInput, + path: ReadonlyArray, + minimumUrl: string, + maximumUrl: string, +): readonly Issue[] => { + const minimum = firstExtensionValue(item, minimumUrl) + const maximum = firstExtensionValue(item, maximumUrl) + if (minimum === undefined || maximum === undefined) return [] + return ( + minimum.key === maximum.key && + boundIsOrdered( + minimum.value, + maximum.value, + temporalKindForItem(item.type), + ) === true + ) ? + [] + : [ + issue( + 'out-of-range', + path, + 'Minimum and maximum must use comparable values and minimum must not exceed maximum.', + ), + ] +} + +const extensionCount = (item: ExtensionElement, url: string): number => + extensionsFor(item, url).length + +interface KnownExtensionRule { + readonly url: string + readonly valueKeys: readonly string[] + readonly repeatable?: boolean +} + +const knownItemExtensionRules: readonly KnownExtensionRule[] = [ + { + url: QUESTIONNAIRE_EXTENSIONS.hidden, + valueKeys: ['valueBoolean'], + }, + { + url: QUESTIONNAIRE_EXTENSIONS.enableWhenExpression, + valueKeys: ['valueExpression'], + }, + { + url: QUESTIONNAIRE_EXTENSIONS.initialExpression, + valueKeys: ['valueExpression'], + }, + { + url: QUESTIONNAIRE_EXTENSIONS.calculatedExpression, + valueKeys: ['valueExpression'], + }, + { + url: QUESTIONNAIRE_EXTENSIONS.maxDecimalPlaces, + valueKeys: ['valueInteger'], + }, + { url: QUESTIONNAIRE_EXTENSIONS.minLength, valueKeys: ['valueInteger'] }, + { + url: QUESTIONNAIRE_EXTENSIONS.minValue, + valueKeys: [ + 'valueDate', + 'valueDateTime', + 'valueDecimal', + 'valueInteger', + 'valueTime', + ], + }, + { + url: QUESTIONNAIRE_EXTENSIONS.maxValue, + valueKeys: [ + 'valueDate', + 'valueDateTime', + 'valueDecimal', + 'valueInteger', + 'valueTime', + ], + }, + { + url: QUESTIONNAIRE_EXTENSIONS.minQuantity, + valueKeys: ['valueQuantity'], + }, + { + url: QUESTIONNAIRE_EXTENSIONS.maxQuantity, + valueKeys: ['valueQuantity'], + }, + { + url: QUESTIONNAIRE_EXTENSIONS.questionnaireUnit, + valueKeys: ['valueCoding'], + }, + { + url: QUESTIONNAIRE_EXTENSIONS.unitOption, + valueKeys: ['valueCoding'], + repeatable: true, + }, + { + url: QUESTIONNAIRE_EXTENSIONS.unitValueSet, + valueKeys: ['valueCanonical'], + }, + { + url: QUESTIONNAIRE_EXTENSIONS.mimeType, + valueKeys: ['valueCode'], + repeatable: true, + }, + { url: QUESTIONNAIRE_EXTENSIONS.maxSize, valueKeys: ['valueDecimal'] }, + { url: QUESTIONNAIRE_EXTENSIONS.minOccurs, valueKeys: ['valueInteger'] }, + { url: QUESTIONNAIRE_EXTENSIONS.maxOccurs, valueKeys: ['valueInteger'] }, +] + +const validateKnownExtensionRule = ( + element: ExtensionElement, + path: ReadonlyArray, + rule: KnownExtensionRule, +): readonly Issue[] => { + const failures: Issue[] = [] + const matching = extensionsFor(element, rule.url) + if (rule.repeatable !== true && matching.length > 1) { + failures.push( + issue( + 'duplicate-identifier', + [...path, 'extension'], + `Extension ${rule.url} may occur at most once.`, + ), + ) + } + for (const [index, extension] of matching.entries()) { + const value = extensionValue(extension) + if (value === undefined || !rule.valueKeys.includes(value.key)) { + failures.push( + issue( + 'invalid-type', + [...path, 'extension', index], + `Extension ${rule.url} requires ${rule.valueKeys.join(' or ')}.`, + ), + ) + } + } + return failures +} + +const itemWeightRule: KnownExtensionRule = { + url: QUESTIONNAIRE_EXTENSIONS.itemWeight, + valueKeys: ['valueDecimal'], +} + +const optionExclusiveRule: KnownExtensionRule = { + url: QUESTIONNAIRE_EXTENSIONS.optionExclusive, + valueKeys: ['valueBoolean'], +} + +const validateKnownItemExtensions = ( + item: QuestionnaireItemInput, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] + for (const rule of knownItemExtensionRules) { + failures.push(...validateKnownExtensionRule(item, path, rule)) + } + + for (const [optionIndex, option] of (item.answerOption ?? []).entries()) { + const optionPath = [...path, 'answerOption', optionIndex] + failures.push( + ...validateKnownExtensionRule(option, optionPath, optionExclusiveRule), + ...validateKnownExtensionRule(option, optionPath, itemWeightRule), + ) + const coding: unknown = Reflect.get(option, 'valueCoding') + if (typeof coding === 'object' && coding !== null) { + failures.push( + ...validateKnownExtensionRule( + coding, + [...optionPath, 'valueCoding'], + itemWeightRule, + ), + ) + } + } + return failures +} + +const validateAuthoredItemValues = ( + item: QuestionnaireItemInput, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] + const initialKeys = expectedQuestionnaireAnswerKeys(item.type) + for (const [index, initial] of (item.initial ?? []).entries()) { + const key = populatedQuestionnaireAnswerKey(initial) + if (key === undefined || !initialKeys.includes(key)) { + failures.push( + issue( + 'invalid-type', + [...path, 'initial', index], + `Initial value does not match item type ${item.type}.`, + ), + ) + } + } + + const optionKeys = expectedAnswerOptionKeys(item.type) + for (const [index, option] of (item.answerOption ?? []).entries()) { + const key = populatedQuestionnaireAnswerKey(option) + if (key === undefined || !optionKeys.includes(key)) { + failures.push( + issue( + 'invalid-type', + [...path, 'answerOption', index], + `answerOption value does not match item type ${item.type}.`, + ), + ) + } + } + return failures +} + +// Each linear branch below mirrors one separately named normative rule family. +/* eslint-disable sonarjs/cognitive-complexity */ +const validateItem = ( + item: QuestionnaireItemInput, + path: ReadonlyArray, + seenLinkIds: Set, + targetKeys: Set, +): readonly Issue[] => { + const failures: Issue[] = [] + failures.push(...validateKnownItemExtensions(item, path)) + failures.push(...validateAuthoredItemValues(item, path)) + if (item.linkId.trim() === '') { + failures.push( + issue( + 'missing-required', + [...path, 'linkId'], + 'Questionnaire linkId is required.', + ), + ) + } else if (seenLinkIds.has(item.linkId)) { + failures.push( + issue( + 'duplicate-identifier', + [...path, 'linkId'], + `Questionnaire linkId ${item.linkId} is duplicated.`, + ), + ) + } else { + seenLinkIds.add(item.linkId) + } + if (Array.from(item.linkId).length > 255) { + failures.push( + issue( + 'out-of-range', + [...path, 'linkId'], + 'Questionnaire linkId cannot exceed 255 Unicode code points.', + ), + ) + } + if (item.type !== 'group' && (item.text?.trim() ?? '') === '') { + failures.push( + issue( + 'missing-required', + [...path, 'text'], + 'Every non-group item requires text.', + ), + ) + } + if (item.type === 'group' && (item.item?.length ?? 0) === 0) { + failures.push( + issue( + 'missing-required', + [...path, 'item'], + 'A group Questionnaire item requires at least one nested item.', + ), + ) + } + if (item.type === 'display' && (item.item?.length ?? 0) > 0) { + failures.push( + issue( + 'invalid-choice', + [...path, 'item'], + 'A display Questionnaire item cannot have nested items.', + ), + ) + } + if ( + item.type === 'display' && + (item.required !== undefined || item.repeats !== undefined) + ) { + failures.push( + issue( + 'invalid-choice', + path, + 'A display Questionnaire item cannot declare required or repeats.', + ), + ) + } + if (item.type === 'display' && (item.code?.length ?? 0) > 0) { + failures.push( + issue( + 'invalid-choice', + [...path, 'code'], + 'A display Questionnaire item cannot declare code.', + ), + ) + } + if (item.type === 'display' && item.readOnly !== undefined) { + failures.push( + issue( + 'invalid-choice', + [...path, 'readOnly'], + 'A display Questionnaire item cannot declare readOnly.', + ), + ) + } + if ( + item.repeats === true && + !['attachment', 'choice', 'open-choice'].includes(item.type) + ) { + failures.push( + issue( + 'invalid-choice', + [...path, 'repeats'], + 'Only choice, open-choice, and attachment items may repeat.', + ), + ) + } + if ((item.enableWhen?.length ?? 0) > 1 && item.enableBehavior === undefined) { + failures.push( + issue( + 'missing-required', + [...path, 'enableBehavior'], + 'Multiple enableWhen rules require enableBehavior.', + ), + ) + } + for (const [index, condition] of (item.enableWhen ?? []).entries()) { + if ( + condition.operator === 'exists' && + Reflect.get(condition, 'answerBoolean') === undefined + ) { + failures.push( + issue( + 'invalid-type', + [...path, 'enableWhen', index], + 'The exists enableWhen operator requires answerBoolean.', + ), + ) + } + } + if ( + (item.enableWhen?.length ?? 0) > 0 && + extensionCount(item, QUESTIONNAIRE_EXTENSIONS.enableWhenExpression) > 0 + ) { + failures.push( + issue( + 'invalid-choice', + path, + 'Use either enableWhen or enableWhenExpression.', + ), + ) + } + if ( + (item.initial?.length ?? 0) > 0 && + extensionCount(item, QUESTIONNAIRE_EXTENSIONS.initialExpression) > 0 + ) { + failures.push( + issue('invalid-choice', path, 'Use either initial or initialExpression.'), + ) + } + if ( + (item.answerOption?.length ?? 0) > 0 && + item.answerValueSet !== undefined + ) { + failures.push( + issue( + 'invalid-choice', + path, + 'Use answerOption or answerValueSet, not both.', + ), + ) + } + if ( + (item.answerOption?.length ?? 0) > 0 && + ![ + 'choice', + 'date', + 'dateTime', + 'decimal', + 'integer', + 'open-choice', + 'quantity', + 'string', + 'time', + ].includes(item.type) + ) { + failures.push( + issue( + 'invalid-choice', + [...path, 'answerOption'], + 'answerOption does not match this item type.', + ), + ) + } + if ( + item.answerValueSet !== undefined && + !['choice', 'open-choice'].includes(item.type) + ) { + failures.push( + issue( + 'invalid-choice', + [...path, 'answerValueSet'], + 'answerValueSet is only permitted for choice and open-choice items.', + ), + ) + } + if ((item.answerOption?.length ?? 0) > 0 && (item.initial?.length ?? 0) > 0) { + failures.push( + issue('invalid-choice', path, 'Use answerOption or initial, not both.'), + ) + } + if ( + ['display', 'group'].includes(item.type) && + (item.initial?.length ?? 0) > 0 + ) { + failures.push( + issue( + 'invalid-choice', + [...path, 'initial'], + 'Group and display items cannot declare initial values.', + ), + ) + } + if ((item.initial?.length ?? 0) > 1 && item.repeats !== true) { + failures.push( + issue( + 'invalid-choice', + [...path, 'initial'], + 'Multiple initial values require repeats=true.', + ), + ) + } + if (extensionCount(item, QUESTIONNAIRE_EXTENSIONS.styleSensitive) > 0) { + failures.push( + issue( + 'invalid-code', + [...path, 'extension'], + 'styleSensitive is not supported.', + ), + ) + } + failures.push(...validateExpressionScope(item, path, targetKeys)) + + const minimumLength = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.minLength, + ) + const hasLength = item.maxLength !== undefined || minimumLength !== undefined + if ( + hasLength && + !['open-choice', 'string', 'text', 'url'].includes(item.type) + ) { + failures.push( + issue( + 'invalid-choice', + path, + 'Length constraints require a textual item.', + ), + ) + } + if ( + minimumLength !== undefined && + (minimumLength.key !== 'valueInteger' || + typeof minimumLength.value !== 'number' || + !Number.isInteger(minimumLength.value) || + minimumLength.value < 0 || + (item.maxLength !== undefined && minimumLength.value > item.maxLength)) + ) { + failures.push( + issue( + 'out-of-range', + path, + 'Text length bounds are invalid or reversed.', + ), + ) + } + + const decimalPlaces = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.maxDecimalPlaces, + ) + if (decimalPlaces !== undefined && item.type !== 'decimal') { + failures.push( + issue( + 'invalid-choice', + path, + 'maxDecimalPlaces requires a decimal item.', + ), + ) + } + if ( + decimalPlaces !== undefined && + (decimalPlaces.key !== 'valueInteger' || + typeof decimalPlaces.value !== 'number' || + !Number.isInteger(decimalPlaces.value) || + decimalPlaces.value < 0) + ) { + failures.push( + issue( + 'out-of-range', + path, + 'maxDecimalPlaces must be a non-negative integer.', + ), + ) + } + + const minimumValue = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.minValue, + ) + const maximumValue = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.maxValue, + ) + if ( + (minimumValue !== undefined || maximumValue !== undefined) && + !['date', 'dateTime', 'decimal', 'integer', 'time'].includes(item.type) + ) { + failures.push( + issue( + 'invalid-choice', + path, + 'Generic value bounds do not match the item type.', + ), + ) + } + const boundKeyByType: Partial< + Record + > = { + date: 'valueDate', + dateTime: 'valueDateTime', + decimal: 'valueDecimal', + integer: 'valueInteger', + time: 'valueTime', + } + const expectedBoundKey = boundKeyByType[item.type] + if ( + expectedBoundKey !== undefined && + [minimumValue, maximumValue].some( + (entry) => entry !== undefined && entry.key !== expectedBoundKey, + ) + ) { + failures.push( + issue('invalid-type', path, 'Bound datatype must match the item type.'), + ) + } else { + const temporalValidator = temporalValidatorForItem(item.type) + if ( + temporalValidator !== undefined && + [minimumValue, maximumValue].some( + (entry) => entry !== undefined && !temporalValidator(entry.value), + ) + ) { + failures.push( + issue( + 'invalid-date-time', + path, + `Temporal bounds must be valid FHIR R4 ${item.type} primitives.`, + ), + ) + } + failures.push( + ...validateBoundPair( + item, + path, + QUESTIONNAIRE_EXTENSIONS.minValue, + QUESTIONNAIRE_EXTENSIONS.maxValue, + ), + ) + } + + const minimumQuantity = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.minQuantity, + ) + const maximumQuantity = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.maxQuantity, + ) + if ( + (minimumQuantity !== undefined || maximumQuantity !== undefined) && + item.type !== 'quantity' + ) { + failures.push( + issue('invalid-choice', path, 'Quantity bounds require a quantity item.'), + ) + } + if ( + [minimumQuantity, maximumQuantity].some( + (entry) => entry !== undefined && entry.key !== 'valueQuantity', + ) + ) { + failures.push( + issue('invalid-type', path, 'Quantity bounds require valueQuantity.'), + ) + } else { + failures.push( + ...validateBoundPair( + item, + path, + QUESTIONNAIRE_EXTENSIONS.minQuantity, + QUESTIONNAIRE_EXTENSIONS.maxQuantity, + ), + ) + } + + const fixedUnits = extensionsFor( + item, + QUESTIONNAIRE_EXTENSIONS.questionnaireUnit, + ) + const unitOptions = extensionsFor(item, QUESTIONNAIRE_EXTENSIONS.unitOption) + const unitValueSets = extensionsFor( + item, + QUESTIONNAIRE_EXTENSIONS.unitValueSet, + ) + if (fixedUnits.length > 0 && !['decimal', 'integer'].includes(item.type)) { + failures.push( + issue( + 'invalid-choice', + path, + 'A fixed unit requires an integer or decimal item.', + ), + ) + } + if ( + (unitOptions.length > 0 || unitValueSets.length > 0) && + item.type !== 'quantity' + ) { + failures.push( + issue( + 'invalid-choice', + path, + 'Selectable units require a quantity item.', + ), + ) + } + if (unitOptions.length > 0 && unitValueSets.length > 0) { + failures.push( + issue( + 'invalid-choice', + path, + 'Use unitOption or unitValueSet, not both.', + ), + ) + } + + const mimeTypes = extensionsFor(item, QUESTIONNAIRE_EXTENSIONS.mimeType) + const maximumSize = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.maxSize, + ) + if ( + (mimeTypes.length > 0 || maximumSize !== undefined) && + item.type !== 'attachment' + ) { + failures.push( + issue( + 'invalid-choice', + path, + 'Attachment constraints require an attachment item.', + ), + ) + } + if ( + maximumSize !== undefined && + (maximumSize.key !== 'valueDecimal' || + typeof maximumSize.value !== 'number' || + !Number.isFinite(maximumSize.value) || + maximumSize.value <= 0) + ) { + failures.push( + issue('out-of-range', path, 'Maximum attachment size must be positive.'), + ) + } + + const minimumOccurs = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.minOccurs, + ) + const maximumOccurs = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.maxOccurs, + ) + if ( + (minimumOccurs !== undefined || maximumOccurs !== undefined) && + item.repeats !== true + ) { + failures.push( + issue( + 'invalid-choice', + path, + 'Occurrence constraints require repeats=true.', + ), + ) + } + const minimumOccursValue = minimumOccurs?.value + const maximumOccursValue = maximumOccurs?.value + if ( + (minimumOccurs !== undefined && + (minimumOccurs.key !== 'valueInteger' || + typeof minimumOccursValue !== 'number' || + !Number.isInteger(minimumOccursValue) || + minimumOccursValue < 0)) || + (maximumOccurs !== undefined && + (maximumOccurs.key !== 'valueInteger' || + typeof maximumOccursValue !== 'number' || + !Number.isInteger(maximumOccursValue) || + maximumOccursValue < 1)) || + (typeof minimumOccursValue === 'number' && + typeof maximumOccursValue === 'number' && + minimumOccursValue > maximumOccursValue) + ) { + failures.push( + issue('out-of-range', path, 'Occurrence bounds are invalid or reversed.'), + ) + } + + for (const [index, child] of (item.item ?? []).entries()) { + failures.push( + ...validateItem(child, [...path, 'item', index], seenLinkIds, targetKeys), + ) + } + return failures +} +/* eslint-enable sonarjs/cognitive-complexity */ + +const validateQuestionnaireResponseAnswerContract = ( + answer: QuestionnaireResponseAnswerInput, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [ + ...validateKnownExtensionRule(answer, path, itemWeightRule), + ] + const coding: unknown = Reflect.get(answer, 'valueCoding') + if (typeof coding === 'object' && coding !== null) { + failures.push( + ...validateKnownExtensionRule( + coding, + [...path, 'valueCoding'], + itemWeightRule, + ), + ) + } + failures.push( + ...validateQuestionnaireResponseItemContract(answer.item ?? [], [ + ...path, + 'item', + ]), + ) + return failures +} + +/** Standalone invariants owned by the Grove QuestionnaireResponse profile. */ +export const validateQuestionnaireResponseItemContract = ( + items: readonly QuestionnaireResponseItemInput[], + path: ReadonlyArray = ['item'], +): readonly Issue[] => { + const failures: Issue[] = [] + const answeredLinkIds = new Set() + for (const [itemIndex, item] of items.entries()) { + const itemPath = [...path, itemIndex] + const answered = (item.answer?.length ?? 0) > 0 + if (answered && (item.text?.trim() ?? '') === '') { + failures.push( + issue( + 'missing-required', + [...itemPath, 'text'], + 'Every answered response item must repeat the question text.', + ), + ) + } + if (answered && answeredLinkIds.has(item.linkId)) { + failures.push( + issue( + 'duplicate-identifier', + [...itemPath, 'linkId'], + `Repeated answers for ${item.linkId} must share one answer array.`, + ), + ) + } else if (answered) { + answeredLinkIds.add(item.linkId) + } + + failures.push( + ...validateQuestionnaireResponseItemContract(item.item ?? [], [ + ...itemPath, + 'item', + ]), + ) + for (const [answerIndex, answer] of (item.answer ?? []).entries()) { + const answerPath = [...itemPath, 'answer', answerIndex] + failures.push( + ...validateQuestionnaireResponseAnswerContract(answer, answerPath), + ) + } + } + return failures +} + +/** Static rules shared by the builder and pair preflight. */ +export const validateQuestionnaireContract = ( + extension: readonly Extension[] | undefined, + items: readonly QuestionnaireItemInput[], +): readonly Issue[] => { + const root: ExtensionElement = extension === undefined ? {} : { extension } + const failures: Issue[] = [] + if (extensionCount(root, QUESTIONNAIRE_EXTENSIONS.styleSensitive) > 0) { + failures.push( + issue('invalid-code', ['extension'], 'styleSensitive is not supported.'), + ) + } + const targetKeys = new Set() + failures.push(...validateExpressionScope(root, [], targetKeys)) + const seenLinkIds = new Set() + for (const [index, item] of items.entries()) { + failures.push( + ...validateItem(item, ['item', index], seenLinkIds, targetKeys), + ) + } + for (const [index, item] of items.entries()) { + const visit = ( + candidate: QuestionnaireItemInput, + path: ReadonlyArray, + ) => { + for (const [conditionIndex, condition] of ( + candidate.enableWhen ?? [] + ).entries()) { + if (!seenLinkIds.has(condition.question)) { + failures.push( + issue( + 'invalid-reference', + [...path, 'enableWhen', conditionIndex, 'question'], + `enableWhen refers to unknown linkId ${condition.question}.`, + ), + ) + } + } + for (const [childIndex, child] of (candidate.item ?? []).entries()) { + visit(child, [...path, 'item', childIndex]) + } + } + visit(item, ['item', index]) + } + return failures +} diff --git a/packages/fhir/src/questionnaire/index.ts b/packages/fhir/src/questionnaire/index.ts new file mode 100644 index 00000000..5c5fad9b --- /dev/null +++ b/packages/fhir/src/questionnaire/index.ts @@ -0,0 +1,42 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +export { buildQuestionnaire, buildQuestionnaireResponse } from './builder.js' +export { + groveFhirContractVersion, + groveFhirVersion, + groveQuestionnairePackageMetadata, +} from './contract.generated.js' +export { parseQuestionnaire, parseQuestionnaireResponse } from './parse.js' +export { preflightQuestionnairePair } from './preflight.js' +export { + questionnaireItemSchema, + questionnaireResponseAnswerSchema, + questionnaireResponseItemSchema, + questionnaireResponseSchema, + questionnaireSchema, +} from './schemas.js' +export type { + GroveQuestionnaire, + GroveQuestionnaireResponse, + QuestionnaireAnswerOptionInput, + QuestionnaireAnswerValueInput, + QuestionnaireEnableWhenInput, + QuestionnaireInitialInput, + QuestionnaireInput, + QuestionnaireItemInput, + QuestionnaireItemType, + QuestionnairePair, + QuestionnairePreflightOptions, + QuestionnaireResponseAnswerInput, + QuestionnaireResponseIdentifierInput, + QuestionnaireResponseInput, + QuestionnaireResponseItemInput, + QuestionnaireValueSetConceptInput, + ResolvedQuestionnaireValueSetInput, +} from './types.js' diff --git a/packages/fhir/src/questionnaire/parse.ts b/packages/fhir/src/questionnaire/parse.ts new file mode 100644 index 00000000..d3981397 --- /dev/null +++ b/packages/fhir/src/questionnaire/parse.ts @@ -0,0 +1,293 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { type z } from 'zod' +import { groveQuestionnaireProfileCanonicals } from './contract.generated.js' +import { + extensionValue, + extensionsFor, + validateQuestionnaireContract, + validateQuestionnaireResponseItemContract, +} from './contract.js' +import { questionnaireResponseSchema, questionnaireSchema } from './schemas.js' +import type { + GroveQuestionnaire, + GroveQuestionnaireResponse, + QuestionnaireItemInput, + QuestionnaireResponseItemInput, +} from './types.js' +import { + deepFreeze, + issues, + ok, + parseCanonical, + parseSemVer, + type Issue, + type Result, +} from '../core/index.js' +import type { Extension } from '../r4/index.js' + +/* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 canonicals are normative HTTP URIs. */ + +const VERSION_ALGORITHM = + 'http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm' +const VERSION_ALGORITHM_SYSTEM = 'http://hl7.org/fhir/version-algorithm' +const COMPLETION_MODE = + 'http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode' +const PARTICIPATION_MODE = + 'http://terminology.hl7.org/CodeSystem/v3-ParticipationMode' + +const extensionValueKeys = [ + 'valueBoolean', + 'valueCanonical', + 'valueCode', + 'valueCodeableConcept', + 'valueCoding', + 'valueDate', + 'valueDateTime', + 'valueDecimal', + 'valueExpression', + 'valueIdentifier', + 'valueInstant', + 'valueInteger', + 'valueId', + 'valueQuantity', + 'valueReference', + 'valueString', + 'valueTime', + 'valueUri', + 'valueUrl', +] as const + +const issue = ( + code: Issue['code'], + path: Issue['path'], + message: string, +): Issue => ({ severity: 'error', code, path, message }) + +const objectPart = (value: unknown, property: string): unknown => + typeof value === 'object' && value !== null ? + Reflect.get(value, property) + : undefined + +const normalizeIssue = (entry: z.core.$ZodIssue): Issue => ({ + severity: 'error', + code: 'schema-invalid', + path: entry.path.map((component) => + typeof component === 'symbol' ? + (component.description ?? component.toString()) + : component, + ), + message: entry.message, +}) + +const exactProfileIssues = ( + resource: { + readonly meta?: + { readonly profile?: readonly string[] | undefined } | undefined + }, + expected: string, +): readonly Issue[] => + ( + resource.meta?.profile?.length === 1 && + resource.meta.profile[0] === expected + ) ? + [] + : [ + issue( + 'value-mismatch', + ['meta', 'profile'], + `Resource requires exactly the Grove profile ${expected}.`, + ), + ] + +const validateExtension = ( + extension: Extension, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] + const populatedValues = extensionValueKeys.filter( + (key) => Reflect.get(extension, key) !== undefined, + ).length + const nested = extension.extension ?? [] + if ((populatedValues === 1) === nested.length > 0) { + failures.push( + issue( + 'invalid-choice', + path, + 'Extension requires either one value[x] or at least one nested extension, but not both.', + ), + ) + } + for (const [index, child] of nested.entries()) { + failures.push(...validateExtension(child, [...path, 'extension', index])) + } + for (const [key, value] of Object.entries(extension)) { + if (key !== 'extension') { + failures.push(...validateElementExtensions(value, [...path, key])) + } + } + return failures +} + +const validateElementExtensions = ( + value: unknown, + path: ReadonlyArray = [], +): readonly Issue[] => { + if (Array.isArray(value)) { + return value.flatMap((entry, index) => + validateElementExtensions(entry, [...path, index]), + ) + } + if (typeof value !== 'object' || value === null) return [] + + const failures: Issue[] = [] + for (const [key, child] of Object.entries(value)) { + if ( + (key === 'extension' || key === 'modifierExtension') && + Array.isArray(child) + ) { + for (const [index, extension] of child.entries()) { + failures.push( + ...validateExtension(extension as Extension, [...path, key, index]), + ) + } + } else { + failures.push(...validateElementExtensions(child, [...path, key])) + } + } + return failures +} + +const questionnaireContractIssues = ( + questionnaire: GroveQuestionnaire, +): readonly Issue[] => { + const failures: Issue[] = [ + ...exactProfileIssues( + questionnaire, + groveQuestionnaireProfileCanonicals['grove-questionnaire'], + ), + ...validateElementExtensions(questionnaire), + ] + if (!parseSemVer(questionnaire.version).ok) { + failures.push( + issue( + 'invalid-code', + ['version'], + 'Questionnaire.version must be SemVer.', + ), + ) + } + const algorithms = extensionsFor( + { extension: questionnaire.extension ?? [] }, + VERSION_ALGORITHM, + ) + const algorithm = + algorithms.length === 1 ? extensionValue(algorithms[0]) : undefined + if ( + algorithm?.key !== 'valueCoding' || + objectPart(algorithm.value, 'system') !== VERSION_ALGORITHM_SYSTEM || + objectPart(algorithm.value, 'code') !== 'semver' + ) { + failures.push( + issue( + 'missing-required', + ['extension'], + 'Questionnaire requires exactly one SemVer version-algorithm Coding.', + ), + ) + } + failures.push( + ...validateQuestionnaireContract( + questionnaire.extension, + questionnaire.item as unknown as readonly QuestionnaireItemInput[], + ), + ) + return failures +} + +const responseContractIssues = ( + response: GroveQuestionnaireResponse, +): readonly Issue[] => { + const failures: Issue[] = [ + ...exactProfileIssues( + response, + groveQuestionnaireProfileCanonicals['grove-questionnaire-response'], + ), + ...validateElementExtensions(response), + ] + if ( + !parseCanonical(response.questionnaire).ok || + !/^[^|#]+\|[^|#]+$/u.test(response.questionnaire) || + !parseSemVer(response.questionnaire.split('|')[1]).ok + ) { + failures.push( + issue( + 'invalid-uri', + ['questionnaire'], + 'QuestionnaireResponse.questionnaire must be one exact url|SemVer canonical without fragments.', + ), + ) + } + const completionModes = extensionsFor( + { extension: response.extension ?? [] }, + COMPLETION_MODE, + ) + const completionMode = + completionModes.length === 1 ? + extensionValue(completionModes[0]) + : undefined + const codings = objectPart(completionMode?.value, 'coding') + const coding: unknown = + Array.isArray(codings) && codings.length === 1 ? codings[0] : undefined + if ( + completionMode?.key !== 'valueCodeableConcept' || + objectPart(coding, 'system') !== PARTICIPATION_MODE || + objectPart(coding, 'code') !== 'ELECTRONIC' + ) { + failures.push( + issue( + 'missing-required', + ['extension'], + 'QuestionnaireResponse requires exactly one ELECTRONIC completion-mode Coding.', + ), + ) + } + failures.push( + ...validateQuestionnaireResponseItemContract( + (response.item ?? + []) as unknown as readonly QuestionnaireResponseItemInput[], + ), + ) + return failures +} + +// The schema decides the parsed type; a caller-chosen T would make the cast below a lie. +const parseWith = (schema: z.ZodType, input: unknown): Result => { + const result = schema.safeParse(input) + if (!result.success) return issues(result.error.issues.map(normalizeIssue)) + return ok(deepFreeze(result.data) as T) +} + +export const parseQuestionnaire = ( + input: unknown, +): Result => { + const parsed = parseWith(questionnaireSchema, input) + if (!parsed.ok) return parsed + const failures = questionnaireContractIssues(parsed.value) + return failures.length === 0 ? parsed : issues(failures) +} + +export const parseQuestionnaireResponse = ( + input: unknown, +): Result => { + const parsed = parseWith(questionnaireResponseSchema, input) + if (!parsed.ok) return parsed + const failures = responseContractIssues(parsed.value) + return failures.length === 0 ? parsed : issues(failures) +} diff --git a/packages/fhir/src/questionnaire/preflight.ts b/packages/fhir/src/questionnaire/preflight.ts new file mode 100644 index 00000000..0e03d837 --- /dev/null +++ b/packages/fhir/src/questionnaire/preflight.ts @@ -0,0 +1,1176 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + boundIsOrdered, + extensionValue, + expectedQuestionnaireAnswerKeys, + extensionsFor, + firstExtensionValue, + QUESTIONNAIRE_EXTENSIONS, + validateQuestionnaireContract, +} from './contract.js' +import { parseQuestionnaire, parseQuestionnaireResponse } from './parse.js' +import { compareR4Temporal, type R4TemporalKind } from './temporal.js' +import type { + QuestionnaireItemInput, + QuestionnairePair, + QuestionnairePreflightOptions, + QuestionnaireResponseAnswerInput, + QuestionnaireResponseItemInput, +} from './types.js' +import { + issues, + ok, + parseAbsoluteUri, + parseCanonical, + parseSemVer, + type Issue, + type Result, +} from '../core/index.js' +import type { Extension } from '../r4/index.js' + +/* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 canonicals are normative HTTP URIs. */ + +const completedStatuses = new Set(['amended', 'completed']) +const VERSION_ALGORITHM = + 'http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm' +const VERSION_ALGORITHM_SYSTEM = 'http://hl7.org/fhir/version-algorithm' +const COMPLETION_MODE = + 'http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode' +const PARTICIPATION_MODE = + 'http://terminology.hl7.org/CodeSystem/v3-ParticipationMode' + +const answerKeys = [ + 'valueBoolean', + 'valueDecimal', + 'valueInteger', + 'valueDate', + 'valueDateTime', + 'valueTime', + 'valueString', + 'valueUri', + 'valueAttachment', + 'valueCoding', + 'valueQuantity', +] as const + +type AnswerKey = (typeof answerKeys)[number] + +const issue = ( + code: Issue['code'], + path: Issue['path'], + message: string, +): Issue => ({ severity: 'error', code, path, message }) + +const warning = ( + code: Issue['code'], + path: Issue['path'], + message: string, +): Issue => ({ severity: 'warning', code, path, message }) + +const prefixed = ( + entries: readonly Issue[], + prefix: string, +): readonly Issue[] => + entries.map((entry) => ({ ...entry, path: [prefix, ...entry.path] })) + +const validateQuestionnaireEnvelope = ( + questionnaire: QuestionnairePair['questionnaire'], +): readonly Issue[] => { + const failures: Issue[] = [] + if (!parseSemVer(questionnaire.version).ok) { + failures.push( + issue( + 'invalid-code', + ['version'], + 'Questionnaire.version must be SemVer.', + ), + ) + } + + const algorithms = extensionsFor( + { extension: questionnaire.extension ?? [] }, + VERSION_ALGORITHM, + ) + const algorithm = + algorithms.length === 1 ? extensionValue(algorithms[0]) : undefined + const algorithmCoding = algorithm?.value + if ( + algorithm?.key !== 'valueCoding' || + codingPart(algorithmCoding, 'system') !== VERSION_ALGORITHM_SYSTEM || + codingPart(algorithmCoding, 'code') !== 'semver' + ) { + failures.push( + issue( + 'missing-required', + ['extension'], + 'Questionnaire requires exactly one SemVer version-algorithm Coding.', + ), + ) + } + return failures +} + +const validateQuestionnaireResponseEnvelope = ( + response: QuestionnairePair['response'], +): readonly Issue[] => { + const failures: Issue[] = [] + if ( + !parseCanonical(response.questionnaire).ok || + !/^[^|#]+\|[^|#]+$/u.test(response.questionnaire) || + !parseSemVer(response.questionnaire.split('|')[1]).ok + ) { + failures.push( + issue( + 'invalid-uri', + ['questionnaire'], + 'QuestionnaireResponse.questionnaire must be one exact url|SemVer canonical without fragments.', + ), + ) + } + + const identifierSystem = codingPart(response.identifier, 'system') + const identifierValue = codingPart(response.identifier, 'value') + if ( + !parseAbsoluteUri(identifierSystem).ok || + typeof identifierValue !== 'string' || + identifierValue.trim() === '' + ) { + failures.push( + issue( + 'invalid-identifier', + ['identifier'], + 'QuestionnaireResponse requires a complete business Identifier.', + ), + ) + } + + const completionModes = extensionsFor( + { extension: response.extension ?? [] }, + COMPLETION_MODE, + ) + const completionMode = + completionModes.length === 1 ? + extensionValue(completionModes[0]) + : undefined + const completionCodings = codingPart(completionMode?.value, 'coding') + const completionCoding: unknown = + Array.isArray(completionCodings) && completionCodings.length === 1 ? + completionCodings[0] + : undefined + if ( + completionMode?.key !== 'valueCodeableConcept' || + codingPart(completionCoding, 'system') !== PARTICIPATION_MODE || + codingPart(completionCoding, 'code') !== 'ELECTRONIC' + ) { + failures.push( + issue( + 'missing-required', + ['extension'], + 'QuestionnaireResponse requires exactly one ELECTRONIC completion-mode Coding.', + ), + ) + } + return failures +} + +const populatedAnswerKeys = ( + answer: QuestionnaireResponseAnswerInput, +): readonly AnswerKey[] => + answerKeys.filter((key) => Reflect.get(answer, key) !== undefined) + +const codingPart = (value: unknown, property: string): unknown => + typeof value === 'object' && value !== null ? + Reflect.get(value, property) + : undefined + +const quantityIdentity = (value: unknown): readonly unknown[] | undefined => { + const numericValue = codingPart(value, 'value') + const system = codingPart(value, 'system') + const code = codingPart(value, 'code') + return typeof numericValue === 'number' ? + [numericValue, system, code] + : undefined +} + +const valuesMatch = ( + key: AnswerKey, + optionValue: unknown, + answerValue: unknown, +): boolean => { + if (key !== 'valueCoding') return Object.is(optionValue, answerValue) + const optionCode = codingPart(optionValue, 'code') + const answerCode = codingPart(answerValue, 'code') + return ( + typeof optionCode === 'string' && + typeof answerCode === 'string' && + codingPart(optionValue, 'system') === codingPart(answerValue, 'system') && + optionCode === answerCode + ) +} + +const temporalKindForAnswer = (key: AnswerKey): R4TemporalKind | undefined => { + switch (key) { + case 'valueDate': + return 'date' + case 'valueDateTime': + return 'dateTime' + case 'valueTime': + return 'time' + default: + return undefined + } +} + +const questionnaireItemTemporalKind = ( + type: QuestionnaireItemInput['type'], +): R4TemporalKind | undefined => + type === 'date' || type === 'dateTime' || type === 'time' ? type : undefined + +const semanticValuesMatch = ( + left: unknown, + right: unknown, + key?: AnswerKey, +): boolean => { + const leftCode = codingPart(left, 'code') + const rightCode = codingPart(right, 'code') + if (leftCode !== undefined || rightCode !== undefined) { + return ( + typeof leftCode === 'string' && + typeof rightCode === 'string' && + codingPart(left, 'system') === codingPart(right, 'system') && + leftCode === rightCode + ) + } + const leftQuantity = quantityIdentity(left) + const rightQuantity = quantityIdentity(right) + if (leftQuantity !== undefined || rightQuantity !== undefined) { + return ( + leftQuantity !== undefined && + rightQuantity !== undefined && + leftQuantity.every((value, index) => + Object.is(value, rightQuantity[index]), + ) + ) + } + const temporalKind = + key === undefined ? undefined : temporalKindForAnswer(key) + if (temporalKind !== undefined) { + return compareR4Temporal(left, right, temporalKind) === 0 + } + return Object.is(left, right) +} + +const enableAnswerKeys = { + answerBoolean: 'valueBoolean', + answerCoding: 'valueCoding', + answerDate: 'valueDate', + answerDateTime: 'valueDateTime', + answerDecimal: 'valueDecimal', + answerInteger: 'valueInteger', + answerQuantity: 'valueQuantity', + answerString: 'valueString', + answerTime: 'valueTime', +} as const + +type EnableAnswerKey = keyof typeof enableAnswerKeys + +const enableExpectedValue = ( + condition: NonNullable[number], +): readonly [AnswerKey, unknown] | undefined => { + const key = (Object.keys(enableAnswerKeys) as EnableAnswerKey[]).find( + (candidate) => Reflect.get(condition, candidate) !== undefined, + ) + return key === undefined ? undefined : ( + [enableAnswerKeys[key], Reflect.get(condition, key)] + ) +} + +const responseAnswers = ( + items: readonly QuestionnaireResponseItemInput[], + values: Map = new Map(), +): ReadonlyMap => { + for (const item of items) { + const bucket = values.get(item.linkId) ?? [] + values.set(item.linkId, bucket) + for (const answer of item.answer ?? []) { + const key = populatedAnswerKeys(answer)[0] + if (key !== undefined) bucket.push(Reflect.get(answer, key)) + responseAnswers(answer.item ?? [], values) + } + responseAnswers(item.item ?? [], values) + } + return values +} + +const compare = ( + left: unknown, + right: unknown, + operator: '<' | '<=' | '>' | '>=', + key: AnswerKey, +): boolean | undefined => { + const temporalKind = temporalKindForAnswer(key) + if (temporalKind !== undefined) { + const comparison = compareR4Temporal(left, right, temporalKind) + if (comparison === undefined) return undefined + switch (operator) { + case '<': + return comparison < 0 + case '<=': + return comparison <= 0 + case '>': + return comparison > 0 + case '>=': + return comparison >= 0 + } + } + if ( + (typeof left !== 'number' || typeof right !== 'number') && + (typeof left !== 'string' || typeof right !== 'string') + ) { + return undefined + } + switch (operator) { + case '<': + return left < right + case '<=': + return left <= right + case '>': + return left > right + case '>=': + return left >= right + } +} + +const isRelationalOperator = ( + value: string, +): value is '<' | '<=' | '>' | '>=' => + value === '<' || value === '<=' || value === '>' || value === '>=' + +const evaluateEnableWhen = ( + item: QuestionnaireItemInput, + answers: ReadonlyMap, +): boolean | undefined => { + if ((item.enableWhen?.length ?? 0) === 0) return true + const outcomes: boolean[] = [] + for (const condition of item.enableWhen ?? []) { + const expected = enableExpectedValue(condition) + if (expected === undefined) return undefined + const actualValues = answers.get(condition.question) ?? [] + if (condition.operator === 'exists') { + if (typeof expected[1] !== 'boolean') return undefined + outcomes.push(actualValues.length > 0 === expected[1]) + } else if (condition.operator === '=') { + outcomes.push( + actualValues.some((value) => + semanticValuesMatch(value, expected[1], expected[0]), + ), + ) + } else if (condition.operator === '!=') { + outcomes.push( + actualValues.some( + (value) => !semanticValuesMatch(value, expected[1], expected[0]), + ), + ) + } else { + if (!isRelationalOperator(condition.operator)) return undefined + const operator = condition.operator + const comparisons = actualValues.map((value) => + compare(value, expected[1], operator, expected[0]), + ) + if (comparisons.includes(undefined)) return undefined + outcomes.push(comparisons.includes(true)) + } + } + return item.enableBehavior === 'any' ? + outcomes.includes(true) + : outcomes.every(Boolean) +} + +type ResolvedValueSet = NonNullable< + QuestionnairePreflightOptions['valueSets'] +>[number] + +interface PreflightContext { + readonly completed: boolean + readonly answers: ReadonlyMap + readonly valueSets: ReadonlyMap +} + +const targetConstraintKey = (extension: Extension): string => { + const keyPart = extension.extension?.find((part) => part.url === 'key') + const key = extensionValue(keyPart) + return key?.key === 'valueId' && typeof key.value === 'string' ? + key.value + : 'targetConstraint' +} + +const targetConstraintSeverity = (extension: Extension) => { + const severityPart = extension.extension?.find( + (part) => part.url === 'severity', + ) + const severity = extensionValue(severityPart) + return severity?.key === 'valueCode' && severity.value === 'warning' ? + 'warning' + : 'error' +} + +const validateExpressionEngineRequirements = ( + rootExtensions: readonly Extension[] | undefined, + items: readonly QuestionnaireItemInput[], +): readonly Issue[] => { + const failures: Issue[] = [] + const checkExtensions = ( + extensions: readonly Extension[] | undefined, + path: ReadonlyArray, + linkId?: string, + ) => { + for (const extension of extensions ?? []) { + if (extension.url === QUESTIONNAIRE_EXTENSIONS.targetConstraint) { + const createFinding = + targetConstraintSeverity(extension) === 'warning' ? warning : issue + failures.push( + createFinding( + 'external-validation-required', + path, + `FHIRPath must evaluate ${targetConstraintKey(extension)} before a completed or amended response can be accepted.`, + ), + ) + } + if (extension.url === QUESTIONNAIRE_EXTENSIONS.enableWhenExpression) { + failures.push( + issue( + 'external-validation-required', + path, + `FHIRPath enablement must be evaluated for ${linkId ?? 'this item'}.`, + ), + ) + } + if (extension.url === QUESTIONNAIRE_EXTENSIONS.calculatedExpression) { + failures.push( + issue( + 'external-validation-required', + path, + `calculatedExpression output for ${linkId ?? 'this element'} must be recomputed by a conforming SDC FHIRPath engine.`, + ), + ) + } + } + } + checkExtensions(rootExtensions, ['response']) + const visit = ( + candidates: readonly QuestionnaireItemInput[], + path: ReadonlyArray, + ) => { + for (const [index, item] of candidates.entries()) { + const itemPath = [...path, index] + checkExtensions(item.extension, itemPath, item.linkId) + visit(item.item ?? [], [...itemPath, 'item']) + } + } + visit(items, ['questionnaire', 'item']) + return failures +} + +const valueSetMembership = ( + canonical: string, + coding: unknown, + context: PreflightContext, +): boolean | undefined => { + const valueSet = context.valueSets.get(canonical) + const system = codingPart(coding, 'system') + const code = codingPart(coding, 'code') + if ( + valueSet === undefined || + typeof system !== 'string' || + typeof code !== 'string' + ) { + return undefined + } + return valueSet.concepts.some( + (concept) => concept.system === system && concept.code === code, + ) +} + +const optionMatches = ( + item: QuestionnaireItemInput, + answer: QuestionnaireResponseAnswerInput, +): boolean => { + if (item.answerOption === undefined || item.answerOption.length === 0) { + return true + } + const key = populatedAnswerKeys(answer)[0] + if ( + key === undefined || + (item.type === 'open-choice' && key === 'valueString') + ) { + return true + } + + const answerValue = Reflect.get(answer, key) + return item.answerOption.some((option) => + valuesMatch(key, Reflect.get(option, key), answerValue), + ) +} + +const selectedInlineOption = ( + item: QuestionnaireItemInput, + answer: QuestionnaireResponseAnswerInput, +) => { + const key = populatedAnswerKeys(answer)[0] + if (key === undefined) return undefined + const answerValue = Reflect.get(answer, key) + return item.answerOption?.find((option) => + valuesMatch(key, Reflect.get(option, key), answerValue), + ) +} + +const optionIsExclusive = (option: { + readonly extension?: readonly unknown[] +}) => + (option.extension ?? []).some((candidate) => { + if (typeof candidate !== 'object' || candidate === null) return false + if ( + Reflect.get(candidate, 'url') !== QUESTIONNAIRE_EXTENSIONS.optionExclusive + ) { + return false + } + return Reflect.get(candidate, 'valueBoolean') === true + }) + +const codeAndSystemMatch = (left: unknown, right: unknown): boolean => + typeof codingPart(left, 'code') === 'string' && + codingPart(left, 'system') === codingPart(right, 'system') && + codingPart(left, 'code') === codingPart(right, 'code') + +const decimalPlaces = (value: number): number => { + const representation = value.toString().toLowerCase() + const [coefficient = '', exponentText = '0'] = representation.split('e') + const exponent = Number(exponentText) + const fractionLength = coefficient.split('.')[1]?.length ?? 0 + return Math.max(0, fractionLength - exponent) +} + +const textLength = (value: string): number => Array.from(value).length + +const validateTextLength = ( + item: QuestionnaireItemInput, + value: unknown, + path: ReadonlyArray, +): readonly Issue[] => { + if (typeof value !== 'string') return [] + const minimum = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.minLength, + )?.value + const length = textLength(value) + const failures: Issue[] = [] + if (typeof minimum === 'number' && length < minimum) { + failures.push( + issue('out-of-range', path, 'Answer is shorter than minLength.'), + ) + } + if (item.maxLength !== undefined && length > item.maxLength) { + failures.push(issue('out-of-range', path, 'Answer exceeds maxLength.')) + } + return failures +} + +const validateDecimalPlaces = ( + item: QuestionnaireItemInput, + value: unknown, + path: ReadonlyArray, +): readonly Issue[] => { + const maximum = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.maxDecimalPlaces, + )?.value + return ( + typeof value === 'number' && + typeof maximum === 'number' && + decimalPlaces(value) > maximum + ) ? + [issue('out-of-range', path, 'Answer exceeds maxDecimalPlaces.')] + : [] +} + +const validateBounds = ( + item: QuestionnaireItemInput, + value: unknown, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] + for (const [url, direction, label] of [ + [QUESTIONNAIRE_EXTENSIONS.minValue, 'minimum', 'value'], + [QUESTIONNAIRE_EXTENSIONS.maxValue, 'maximum', 'value'], + [QUESTIONNAIRE_EXTENSIONS.minQuantity, 'minimum', 'quantity'], + [QUESTIONNAIRE_EXTENSIONS.maxQuantity, 'maximum', 'quantity'], + ] as const) { + const bound = firstExtensionValue(item, url)?.value + if (bound === undefined) continue + const valid = + direction === 'minimum' ? + boundIsOrdered(bound, value, questionnaireItemTemporalKind(item.type)) + : boundIsOrdered(value, bound, questionnaireItemTemporalKind(item.type)) + if (valid !== true) { + failures.push( + issue( + 'out-of-range', + path, + `Answer violates the ${direction} ${label} bound or uses an incomparable unit.`, + ), + ) + } + } + return failures +} + +const validateQuantityUnits = ( + item: QuestionnaireItemInput, + value: unknown, + context: PreflightContext, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] + const options = extensionsFor( + item, + QUESTIONNAIRE_EXTENSIONS.unitOption, + ).flatMap((extension) => { + const option = extensionValue(extension) + return option?.key === 'valueCoding' ? [option.value] : [] + }) + if ( + options.length > 0 && + !options.some((option) => codeAndSystemMatch(value, option)) + ) { + failures.push( + issue('value-mismatch', path, 'Quantity unit is not one of unitOption.'), + ) + } + const valueSet = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.unitValueSet, + ) + if ( + valueSet?.key === 'valueCanonical' && + typeof valueSet.value === 'string' + ) { + const membership = valueSetMembership( + valueSet.value, + { + system: codingPart(value, 'system'), + code: codingPart(value, 'code'), + }, + context, + ) + if (membership === undefined) { + failures.push( + issue( + 'external-validation-required', + path, + `Resolved concepts are required for ${valueSet.value}.`, + ), + ) + } else if (!membership) { + failures.push( + issue( + 'value-mismatch', + path, + 'Quantity unit is not certified by unitValueSet.', + ), + ) + } + } + return failures +} + +const validateAttachment = ( + item: QuestionnaireItemInput, + value: unknown, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] + const allowedTypes = new Set( + extensionsFor(item, QUESTIONNAIRE_EXTENSIONS.mimeType).flatMap( + (extension) => { + const allowed = extensionValue(extension) + return ( + allowed?.key === 'valueCode' && typeof allowed.value === 'string' + ) ? + [allowed.value] + : [] + }, + ), + ) + const contentType = codingPart(value, 'contentType') + if (allowedTypes.size > 0 && !allowedTypes.has(String(contentType))) { + failures.push( + issue('value-mismatch', path, 'Attachment contentType is not allowed.'), + ) + } + const maximumSize = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.maxSize, + )?.value + const size = codingPart(value, 'size') + if ( + typeof maximumSize === 'number' && + (typeof size !== 'number' || !Number.isInteger(size) || size > maximumSize) + ) { + failures.push( + issue( + 'out-of-range', + path, + 'Attachment exceeds maxSize or does not declare size.', + ), + ) + } + return failures +} + +const validateAnswer = ( + questionnaireItem: QuestionnaireItemInput, + answer: QuestionnaireResponseAnswerInput, + context: PreflightContext, + path: ReadonlyArray, +): readonly Issue[] => { + const populated = populatedAnswerKeys(answer) + const expected = expectedQuestionnaireAnswerKeys(questionnaireItem.type) + const failures: Issue[] = [] + const actual = populated[0] + if ( + populated.length !== 1 || + actual === undefined || + !expected.includes(actual) + ) { + failures.push( + issue( + 'invalid-type', + path, + `Answer for ${questionnaireItem.linkId} does not match item type ${questionnaireItem.type}.`, + ), + ) + return failures + } + + const value = Reflect.get(answer, actual) + if (!optionMatches(questionnaireItem, answer)) { + failures.push( + issue( + 'value-mismatch', + path, + `Answer for ${questionnaireItem.linkId} is not an admitted answerOption.`, + ), + ) + } + if ( + actual === 'valueCoding' && + questionnaireItem.answerValueSet !== undefined + ) { + const membership = valueSetMembership( + questionnaireItem.answerValueSet, + value, + context, + ) + if (membership === undefined) { + failures.push( + issue( + 'external-validation-required', + path, + `Resolved concepts are required for ${questionnaireItem.answerValueSet}.`, + ), + ) + } else if (!membership) { + failures.push( + issue( + 'value-mismatch', + path, + `Answer for ${questionnaireItem.linkId} is not in its resolved ValueSet.`, + ), + ) + } + } + if (actual === 'valueString' || actual === 'valueUri') { + failures.push(...validateTextLength(questionnaireItem, value, path)) + } + if (actual === 'valueDecimal') { + failures.push(...validateDecimalPlaces(questionnaireItem, value, path)) + } + failures.push(...validateBounds(questionnaireItem, value, path)) + if (actual === 'valueQuantity') { + failures.push( + ...validateQuantityUnits(questionnaireItem, value, context, path), + ) + } + if (actual === 'valueAttachment') { + failures.push(...validateAttachment(questionnaireItem, value, path)) + } + return failures +} + +const validateOmittedGroupChildren = ( + item: QuestionnaireItemInput, + responseItem: QuestionnaireResponseItemInput | undefined, + enabled: boolean, + context: PreflightContext, + path: ReadonlyArray, +): readonly Issue[] => + ( + context.completed && + enabled && + item.type === 'group' && + responseItem === undefined + ) ? + validateResponseItems(item.item ?? [], [], context, [ + ...path, + item.linkId, + 'item', + ]) + : [] + +const validateRequiredItems = ( + questionnaireItems: readonly QuestionnaireItemInput[], + responseByLinkId: ReadonlyMap, + context: PreflightContext, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] + for (const questionnaireItem of questionnaireItems) { + const responseItem = responseByLinkId.get(questionnaireItem.linkId) + const enabled = evaluateEnableWhen(questionnaireItem, context.answers) + if (context.completed && enabled === undefined) { + failures.push( + issue( + 'external-validation-required', + path, + `enableWhen could not be evaluated for ${questionnaireItem.linkId}.`, + ), + ) + continue + } + if (enabled === undefined) continue + if (!enabled && responseItem !== undefined) { + failures.push( + issue( + 'invalid-choice', + path, + `Disabled item ${questionnaireItem.linkId} must not be present.`, + ), + ) + } + const required = + context.completed && questionnaireItem.required === true && enabled + const hasRequiredContent = + questionnaireItem.type === 'group' ? + responseItem !== undefined + : (responseItem?.answer?.length ?? 0) > 0 + if (required && !hasRequiredContent) { + failures.push( + issue( + 'missing-required', + path, + `Completed response is missing required item ${questionnaireItem.linkId}.`, + ), + ) + } + failures.push( + ...validateOmittedGroupChildren( + questionnaireItem, + responseItem, + enabled, + context, + path, + ), + ) + } + return failures +} + +const validateMatchedResponseItem = ( + questionnaireItem: QuestionnaireItemInput, + responseItem: QuestionnaireResponseItemInput, + context: PreflightContext, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] + const answers = responseItem.answer ?? [] + if (responseItem.text !== questionnaireItem.text) { + failures.push( + issue( + 'value-mismatch', + [...path, 'text'], + 'Response item text must exactly match Questionnaire.item.text.', + ), + ) + } + if (questionnaireItem.repeats !== true && answers.length > 1) { + failures.push( + issue( + 'out-of-range', + [...path, 'answer'], + 'A non-repeating Questionnaire item permits at most one answer.', + ), + ) + } + const minimumOccurs = firstExtensionValue( + questionnaireItem, + QUESTIONNAIRE_EXTENSIONS.minOccurs, + )?.value + const maximumOccurs = firstExtensionValue( + questionnaireItem, + QUESTIONNAIRE_EXTENSIONS.maxOccurs, + )?.value + if (typeof minimumOccurs === 'number' && answers.length < minimumOccurs) { + failures.push( + issue( + 'out-of-range', + [...path, 'answer'], + 'Answer count is below minOccurs.', + ), + ) + } + if (typeof maximumOccurs === 'number' && answers.length > maximumOccurs) { + failures.push( + issue( + 'out-of-range', + [...path, 'answer'], + 'Answer count exceeds maxOccurs.', + ), + ) + } + if ( + answers.length > 1 && + answers.some((answer) => { + const selected = selectedInlineOption(questionnaireItem, answer) + return selected !== undefined && optionIsExclusive(selected) + }) + ) { + failures.push( + issue( + 'invalid-choice', + [...path, 'answer'], + 'An exclusive option cannot be combined with another answer.', + ), + ) + } + for (const [answerIndex, answer] of answers.entries()) { + const answerPath = [...path, 'answer', answerIndex] + failures.push( + ...validateAnswer(questionnaireItem, answer, context, answerPath), + ) + failures.push( + ...validateResponseItems( + questionnaireItem.item ?? [], + answer.item ?? [], + context, + [...answerPath, 'item'], + ), + ) + } + + if (questionnaireItem.type === 'group') { + if (answers.length > 0) { + failures.push( + issue( + 'invalid-type', + [...path, 'answer'], + 'Group items cannot carry answers.', + ), + ) + } + failures.push( + ...validateResponseItems( + questionnaireItem.item ?? [], + responseItem.item ?? [], + context, + [...path, 'item'], + ), + ) + } else if ((responseItem.item?.length ?? 0) > 0) { + failures.push( + issue( + 'invalid-choice', + [...path, 'item'], + 'Children of an answered question belong under answer.item.', + ), + ) + } + return failures +} + +const validateResponseItems = ( + questionnaireItems: readonly QuestionnaireItemInput[], + responseItems: readonly QuestionnaireResponseItemInput[], + context: PreflightContext, + path: ReadonlyArray = ['response', 'item'], +): readonly Issue[] => { + const failures: Issue[] = [] + const questionnaireByLinkId = new Map( + questionnaireItems.map((item) => [item.linkId, item]), + ) + const responseByLinkId = new Map() + + for (const [responseIndex, responseItem] of responseItems.entries()) { + const itemPath = [...path, responseIndex] + if (responseByLinkId.has(responseItem.linkId)) { + failures.push( + issue( + 'duplicate-identifier', + [...itemPath, 'linkId'], + `Response linkId ${responseItem.linkId} is duplicated among siblings.`, + ), + ) + continue + } + responseByLinkId.set(responseItem.linkId, responseItem) + const questionnaireItem = questionnaireByLinkId.get(responseItem.linkId) + if (questionnaireItem === undefined) { + failures.push( + issue( + 'invalid-reference', + [...itemPath, 'linkId'], + `Response linkId ${responseItem.linkId} is not valid at this nesting level.`, + ), + ) + continue + } + + failures.push( + ...validateMatchedResponseItem( + questionnaireItem, + responseItem, + context, + itemPath, + ), + ) + } + + failures.push( + ...validateRequiredItems( + questionnaireItems, + responseByLinkId, + context, + path, + ), + ) + + return failures +} + +/** + * Strictly parses and cross-checks one Questionnaire/QuestionnaireResponse pair. + * It validates exact canonical version identity, nesting, answer types, options, + * portable answer constraints, status-aware required items, expressions, and + * response text fidelity. Warning-severity target constraints are returned in + * the successful Result's `warnings`; unexecuted behavior that can change + * completion acceptance fails closed. + */ +export const preflightQuestionnairePair = ( + questionnaireInput: unknown, + responseInput: unknown, + options: QuestionnairePreflightOptions = {}, +): Result => { + const questionnaire = parseQuestionnaire(questionnaireInput) + const response = parseQuestionnaireResponse(responseInput) + const parseFailures = [ + ...(questionnaire.ok ? + [] + : prefixed(questionnaire.issues, 'questionnaire')), + ...(response.ok ? [] : prefixed(response.issues, 'response')), + ] + if (parseFailures.length > 0) return issues(parseFailures) + if (!questionnaire.ok || !response.ok) return issues(parseFailures) + + const failures: Issue[] = [ + ...prefixed( + validateQuestionnaireEnvelope(questionnaire.value), + 'questionnaire', + ), + ...prefixed( + validateQuestionnaireResponseEnvelope(response.value), + 'response', + ), + ] + const expectedCanonical = `${questionnaire.value.url}|${questionnaire.value.version}` + if (response.value.questionnaire !== expectedCanonical) { + failures.push( + issue( + 'value-mismatch', + ['response', 'questionnaire'], + 'QuestionnaireResponse must name the exact Questionnaire url|version.', + ), + ) + } + if (response.value.status === 'entered-in-error') { + failures.push( + issue( + 'invalid-choice', + ['response', 'status'], + 'An entered-in-error response cannot be accepted as answer data.', + ), + ) + } + + const valueSets = new Map() + for (const valueSet of options.valueSets ?? []) { + if (valueSets.has(valueSet.canonical)) { + failures.push( + issue( + 'duplicate-identifier', + ['options', 'valueSets'], + `Resolved ValueSet ${valueSet.canonical} is duplicated.`, + ), + ) + } else { + valueSets.set(valueSet.canonical, valueSet) + } + } + const context: PreflightContext = { + completed: completedStatuses.has(response.value.status), + answers: responseAnswers( + (response.value.item ?? + []) as unknown as readonly QuestionnaireResponseItemInput[], + ), + valueSets, + } + + const questionnaireItems = questionnaire.value + .item as unknown as readonly QuestionnaireItemInput[] + const responseItems = (response.value.item ?? + []) as unknown as readonly QuestionnaireResponseItemInput[] + failures.push( + ...prefixed( + validateQuestionnaireContract( + questionnaire.value.extension, + questionnaireItems, + ), + 'questionnaire', + ), + ) + if (context.completed) { + failures.push( + ...validateExpressionEngineRequirements( + questionnaire.value.extension, + questionnaireItems, + ), + ) + } + failures.push( + ...validateResponseItems(questionnaireItems, responseItems, context), + ) + + const errors = failures.filter((entry) => entry.severity === 'error') + const warnings = failures.filter((entry) => entry.severity === 'warning') + return errors.length === 0 ? + ok( + { questionnaire: questionnaire.value, response: response.value }, + warnings, + ) + : issues(failures) +} diff --git a/packages/fhir/src/questionnaire/r4-resource-types.ts b/packages/fhir/src/questionnaire/r4-resource-types.ts new file mode 100644 index 00000000..9ff402ea --- /dev/null +++ b/packages/fhir/src/questionnaire/r4-resource-types.ts @@ -0,0 +1,163 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +// Exact codes from the required R4 ResourceType value set (FHIR 4.0.1). +const R4_RESOURCE_TYPES = new Set([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', +]) + +/** Whether a code belongs to the required FHIR R4 ResourceType value set. */ +export const isR4ResourceType = (value: string): boolean => + R4_RESOURCE_TYPES.has(value) diff --git a/packages/fhir/src/questionnaire/schemas.ts b/packages/fhir/src/questionnaire/schemas.ts new file mode 100644 index 00000000..fd26f315 --- /dev/null +++ b/packages/fhir/src/questionnaire/schemas.ts @@ -0,0 +1,582 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import type { + QuestionnaireItem as R4QuestionnaireItem, + QuestionnaireResponseItem as R4QuestionnaireResponseItem, + QuestionnaireResponseItemAnswer as R4QuestionnaireResponseItemAnswer, +} from 'fhir/r4.js' +import { z } from 'zod' +import { isR4ResourceType } from './r4-resource-types.js' +import { + compareR4Temporal, + isR4Date, + isR4DateTime, + isR4Time, +} from './temporal.js' +import type { GroveQuestionnaire, GroveQuestionnaireResponse } from './types.js' +import { parseFhirInstant } from '../core/index.js' +import { + attachmentSchema, + codingSchema, + extensionSchema, + identifierSchema, + metaSchema, + primitiveElementSchema, + quantitySchema, + referenceSchema, +} from '../r4/schemas.js' + +const primitiveMetadata = primitiveElementSchema.optional() + +const r4DateSchema = z + .string() + .refine(isR4Date, 'Expected a valid FHIR R4 date primitive.') +const r4DateTimeSchema = z + .string() + .refine(isR4DateTime, 'Expected a valid FHIR R4 dateTime primitive.') +const r4TimeSchema = z + .string() + .refine(isR4Time, 'Expected a valid FHIR R4 time primitive.') +const r4InstantSchema = z + .string() + .refine( + (value) => parseFhirInstant(value).ok, + 'Expected a valid FHIR R4 instant primitive.', + ) +const questionnairePeriodSchema = z + .strictObject({ + id: z.string().optional(), + extension: z.array(extensionSchema).optional(), + start: r4DateTimeSchema.optional(), + _start: primitiveMetadata, + end: r4DateTimeSchema.optional(), + _end: primitiveMetadata, + }) + .superRefine((value, context) => { + if (value.start === undefined || value.end === undefined) return + const comparison = compareR4Temporal(value.start, value.end, 'dateTime') + if (comparison === undefined || comparison > 0) { + context.addIssue({ + code: 'custom', + message: + 'Questionnaire.effectivePeriod.start must not be later than or incomparable with end.', + }) + } + }) +const r4IntegerSchema = z.number().int().min(-2_147_483_648).max(2_147_483_647) +const nonEmptyStringSchema = z.string().min(1) +const nonEmptyUriSchema = z + .string() + .regex(/^\S+$/u, 'Expected a non-empty FHIR uri primitive.') + +const childHasContent = (value: unknown): boolean => { + if (value === undefined || value === null) return false + if (typeof value === 'string') return value.length > 0 + if (Array.isArray(value)) return value.some(childHasContent) + if (typeof value === 'object') return elementHasContent(value) + return true +} + +const elementHasContent = (value: object): boolean => + Object.entries(value).some( + ([key, child]) => key !== 'id' && childHasContent(child), + ) + +const answerAttachmentSchema = attachmentSchema.refine( + elementHasContent, + 'Attachment answer values cannot be empty.', +) +const answerCodingSchema = codingSchema.refine( + elementHasContent, + 'Coding answer values cannot be empty.', +) +const answerQuantitySchema = quantitySchema.refine( + elementHasContent, + 'Quantity answer values cannot be empty.', +) + +const resourceFields = { + id: z.string().optional(), + _id: primitiveMetadata, + meta: metaSchema.optional(), + implicitRules: z.string().optional(), + _implicitRules: primitiveMetadata, + language: z.string().optional(), + _language: primitiveMetadata, + extension: z.array(extensionSchema).optional(), + modifierExtension: z.array(extensionSchema).optional(), +} as const + +const answerValueFields = { + valueBoolean: z.boolean().optional(), + valueDecimal: z.number().optional(), + valueInteger: r4IntegerSchema.optional(), + valueDate: r4DateSchema.optional(), + valueDateTime: r4DateTimeSchema.optional(), + valueTime: r4TimeSchema.optional(), + valueString: nonEmptyStringSchema.optional(), + valueUri: nonEmptyUriSchema.optional(), + valueAttachment: answerAttachmentSchema.optional(), + valueCoding: answerCodingSchema.optional(), + valueQuantity: answerQuantitySchema.optional(), +} as const + +const answerValueKeys = Object.keys(answerValueFields) + +const answerOptionFields = { + valueInteger: r4IntegerSchema.optional(), + valueDate: r4DateSchema.optional(), + valueTime: r4TimeSchema.optional(), + valueString: nonEmptyStringSchema.optional(), + valueCoding: answerCodingSchema.optional(), +} as const + +const answerOptionKeys = Object.keys(answerOptionFields) + +const populatedCount = (value: object, keys: readonly string[]): number => + keys.filter((key) => Reflect.get(value, key) !== undefined).length + +const exactlyOneAnswerChoice = ( + value: object, + keys: readonly string[], + context: z.RefinementCtx, +) => { + if (populatedCount(value, keys) !== 1) { + context.addIssue({ + code: 'custom', + message: 'Expected exactly one supported answer value[x].', + }) + } +} + +const enableWhenSchema = z + .strictObject({ + id: z.string().optional(), + extension: z.array(extensionSchema).optional(), + modifierExtension: z.array(extensionSchema).optional(), + question: z.string().min(1), + _question: primitiveMetadata, + operator: z.enum(['exists', '=', '!=', '>', '<', '>=', '<=']), + _operator: primitiveMetadata, + answerBoolean: z.boolean().optional(), + answerDecimal: z.number().optional(), + answerInteger: r4IntegerSchema.optional(), + answerDate: r4DateSchema.optional(), + answerDateTime: r4DateTimeSchema.optional(), + answerTime: r4TimeSchema.optional(), + answerString: nonEmptyStringSchema.optional(), + answerCoding: answerCodingSchema.optional(), + answerQuantity: answerQuantitySchema.optional(), + }) + .superRefine((value, context) => { + if ( + populatedCount(value, [ + 'answerBoolean', + 'answerDecimal', + 'answerInteger', + 'answerDate', + 'answerDateTime', + 'answerTime', + 'answerString', + 'answerCoding', + 'answerQuantity', + ]) !== 1 + ) { + context.addIssue({ + code: 'custom', + message: 'Questionnaire.enableWhen requires exactly one answer[x].', + }) + } + }) + +const answerOptionSchema = z + .strictObject({ + id: z.string().optional(), + extension: z.array(extensionSchema).optional(), + modifierExtension: z.array(extensionSchema).optional(), + ...answerOptionFields, + initialSelected: z.boolean().optional(), + _initialSelected: primitiveMetadata, + }) + .superRefine((value, context) => + exactlyOneAnswerChoice(value, answerOptionKeys, context), + ) + +const initialSchema = z + .strictObject({ + id: z.string().optional(), + extension: z.array(extensionSchema).optional(), + modifierExtension: z.array(extensionSchema).optional(), + ...answerValueFields, + }) + .superRefine((value, context) => + exactlyOneAnswerChoice(value, answerValueKeys, context), + ) + +// Written here rather than reused from the generated R4 schemas: an instrument's every item, +// at every nesting depth, must carry a non-blank linkId. The generated base states the release's +// own constraint, which admits a blank one, and layering the narrowing on top would take a +// recursive walk of item.item that is harder to read than the constraint stated inline. +export const questionnaireItemSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: z.string().optional(), + extension: z.array(extensionSchema).optional(), + modifierExtension: z.array(extensionSchema).optional(), + linkId: z.string().min(1), + _linkId: primitiveMetadata, + definition: z.string().optional(), + _definition: primitiveMetadata, + code: z.array(codingSchema).optional(), + prefix: z.string().optional(), + _prefix: primitiveMetadata, + text: z.string().optional(), + _text: primitiveMetadata, + type: z.enum([ + 'group', + 'display', + 'boolean', + 'decimal', + 'integer', + 'date', + 'dateTime', + 'time', + 'string', + 'text', + 'url', + 'choice', + 'open-choice', + 'attachment', + 'quantity', + ]), + _type: primitiveMetadata, + enableWhen: z.array(enableWhenSchema).optional(), + enableBehavior: z.enum(['all', 'any']).optional(), + _enableBehavior: primitiveMetadata, + required: z.boolean().optional(), + _required: primitiveMetadata, + repeats: z.boolean().optional(), + _repeats: primitiveMetadata, + readOnly: z.boolean().optional(), + _readOnly: primitiveMetadata, + maxLength: r4IntegerSchema.nonnegative().optional(), + _maxLength: primitiveMetadata, + answerValueSet: z.string().optional(), + _answerValueSet: primitiveMetadata, + answerOption: z.array(answerOptionSchema).optional(), + initial: z.array(initialSchema).optional(), + item: z.array(questionnaireItemSchema).optional(), + }) + .superRefine((value, context) => { + if (value.type === 'group' && (value.item?.length ?? 0) === 0) { + context.addIssue({ + code: 'custom', + path: ['item'], + message: 'A group Questionnaire item requires nested items.', + }) + } + if (value.type === 'display' && (value.item?.length ?? 0) > 0) { + context.addIssue({ + code: 'custom', + path: ['item'], + message: 'A display Questionnaire item cannot have nested items.', + }) + } + if ( + value.type === 'display' && + (value.required !== undefined || value.repeats !== undefined) + ) { + context.addIssue({ + code: 'custom', + message: + 'A display Questionnaire item cannot declare required or repeats.', + }) + } + if ( + (value.answerOption?.length ?? 0) > 0 && + (value.initial?.length ?? 0) > 0 + ) { + context.addIssue({ + code: 'custom', + message: + 'A Questionnaire item cannot declare both answerOption and initial.', + }) + } + }), +) + +const questionnaireSchemaValue = z.strictObject({ + resourceType: z.literal('Questionnaire'), + ...resourceFields, + url: z.url(), + _url: primitiveMetadata, + version: z.string().min(1), + _version: primitiveMetadata, + name: z.string().optional(), + _name: primitiveMetadata, + title: z.string().optional(), + _title: primitiveMetadata, + derivedFrom: z.array(z.string()).optional(), + _derivedFrom: z.array(primitiveElementSchema.nullable()).optional(), + status: z.enum(['draft', 'active', 'retired', 'unknown']), + _status: primitiveMetadata, + experimental: z.boolean().optional(), + _experimental: primitiveMetadata, + subjectType: z + .array(z.string().refine(isR4ResourceType, 'Unknown R4 ResourceType code.')) + .min(1) + .optional(), + _subjectType: z.array(primitiveElementSchema.nullable()).optional(), + date: r4DateTimeSchema.optional(), + _date: primitiveMetadata, + description: z.string().optional(), + _description: primitiveMetadata, + purpose: z.string().optional(), + _purpose: primitiveMetadata, + copyright: z.string().optional(), + _copyright: primitiveMetadata, + approvalDate: r4DateSchema.optional(), + _approvalDate: primitiveMetadata, + lastReviewDate: r4DateSchema.optional(), + _lastReviewDate: primitiveMetadata, + effectivePeriod: questionnairePeriodSchema.optional(), + code: z.array(codingSchema).optional(), + item: z.array(questionnaireItemSchema).min(1), +}) +export const questionnaireSchema: z.ZodType = + questionnaireSchemaValue as z.ZodType + +export const questionnaireResponseAnswerSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z.array(extensionSchema).optional(), + modifierExtension: z.array(extensionSchema).optional(), + ...answerValueFields, + item: z.array(questionnaireResponseItemSchema).optional(), + }) + .superRefine((value, context) => + exactlyOneAnswerChoice(value, answerValueKeys, context), + ), + ) + +export const questionnaireResponseItemSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z.array(extensionSchema).optional(), + modifierExtension: z.array(extensionSchema).optional(), + linkId: z.string().min(1), + _linkId: primitiveMetadata, + definition: z.string().optional(), + _definition: primitiveMetadata, + text: z.string().optional(), + _text: primitiveMetadata, + answer: z.array(questionnaireResponseAnswerSchema).optional(), + item: z.array(questionnaireResponseItemSchema).optional(), + }) + .superRefine((value, context) => { + if ((value.answer?.length ?? 0) > 0 && (value.item?.length ?? 0) > 0) { + context.addIssue({ + code: 'custom', + message: + 'A QuestionnaireResponse item cannot contain both answer and item.', + }) + } + }), + ) + +const questionnaireResponseSchemaValue = z.strictObject({ + resourceType: z.literal('QuestionnaireResponse'), + ...resourceFields, + identifier: identifierSchema, + basedOn: z.array(referenceSchema).optional(), + partOf: z.array(referenceSchema).optional(), + questionnaire: z.string().min(1), + _questionnaire: primitiveMetadata, + status: z.enum([ + 'in-progress', + 'completed', + 'amended', + 'entered-in-error', + 'stopped', + ]), + _status: primitiveMetadata, + subject: referenceSchema.optional(), + encounter: referenceSchema.optional(), + authored: r4InstantSchema, + _authored: primitiveMetadata, + author: referenceSchema.optional(), + source: referenceSchema.optional(), + item: z.array(questionnaireResponseItemSchema).optional(), +}) +export const questionnaireResponseSchema: z.ZodType = + questionnaireResponseSchemaValue + +const builderBackboneFields = { + id: z.string().optional(), + extension: z.array(extensionSchema).optional(), + modifierExtension: z.array(extensionSchema).optional(), +} as const + +const builderEnableWhenSchema = z + .strictObject({ + ...builderBackboneFields, + question: z.string(), + operator: z.enum(['exists', '=', '!=', '>', '<', '>=', '<=']), + answerBoolean: z.boolean().optional(), + answerDecimal: z.number().optional(), + answerInteger: r4IntegerSchema.optional(), + answerDate: r4DateSchema.optional(), + answerDateTime: r4DateTimeSchema.optional(), + answerTime: r4TimeSchema.optional(), + answerString: nonEmptyStringSchema.optional(), + answerCoding: answerCodingSchema.optional(), + answerQuantity: answerQuantitySchema.optional(), + }) + .superRefine((value, context) => { + if ( + populatedCount(value, [ + 'answerBoolean', + 'answerDecimal', + 'answerInteger', + 'answerDate', + 'answerDateTime', + 'answerTime', + 'answerString', + 'answerCoding', + 'answerQuantity', + ]) !== 1 + ) { + context.addIssue({ + code: 'custom', + message: 'Questionnaire.enableWhen requires exactly one answer[x].', + }) + } + }) + +const builderAnswerOptionSchema = z + .strictObject({ + ...builderBackboneFields, + ...answerOptionFields, + initialSelected: z.boolean().optional(), + }) + .superRefine((value, context) => + exactlyOneAnswerChoice(value, answerOptionKeys, context), + ) + +const builderInitialSchema = z + .strictObject({ + ...builderBackboneFields, + ...answerValueFields, + }) + .superRefine((value, context) => + exactlyOneAnswerChoice(value, answerValueKeys, context), + ) + +const questionnaireBuilderItemSchema: z.ZodType = z.lazy(() => + z.strictObject({ + ...builderBackboneFields, + linkId: z.string(), + definition: z.string().optional(), + code: z.array(codingSchema).optional(), + prefix: z.string().optional(), + text: z.string().optional(), + type: z.enum([ + 'group', + 'display', + 'boolean', + 'decimal', + 'integer', + 'date', + 'dateTime', + 'time', + 'string', + 'text', + 'url', + 'choice', + 'open-choice', + 'attachment', + 'quantity', + ]), + enableWhen: z.array(builderEnableWhenSchema).optional(), + enableBehavior: z.enum(['all', 'any']).optional(), + required: z.boolean().optional(), + repeats: z.boolean().optional(), + readOnly: z.boolean().optional(), + maxLength: r4IntegerSchema.nonnegative().optional(), + answerValueSet: z.string().optional(), + answerOption: z.array(builderAnswerOptionSchema).optional(), + initial: z.array(builderInitialSchema).optional(), + item: z.array(questionnaireBuilderItemSchema).optional(), + }), +) + +/** Strict runtime boundary for the public Questionnaire builder input. */ +export const questionnaireBuilderInputSchema: z.ZodType = z.strictObject({ + id: z.string().optional(), + url: z.string(), + version: z.string(), + name: z.string().optional(), + title: z.string().optional(), + status: z.enum(['draft', 'active', 'retired', 'unknown']), + subjectTypes: z.array(z.string()).optional(), + date: z.string().optional(), + description: z.string().optional(), + purpose: z.string().optional(), + extensions: z.array(extensionSchema).optional(), + items: z.array(questionnaireBuilderItemSchema).min(1), +}) + +const questionnaireResponseBuilderItemSchema: z.ZodType = z.lazy(() => + z.strictObject({ + ...builderBackboneFields, + linkId: z.string(), + definition: z.string().optional(), + text: z.string().optional(), + answer: z + .array( + z + .strictObject({ + ...builderBackboneFields, + ...answerValueFields, + item: z.array(questionnaireResponseBuilderItemSchema).optional(), + }) + .superRefine((value, context) => + exactlyOneAnswerChoice(value, answerValueKeys, context), + ), + ) + .optional(), + item: z.array(questionnaireResponseBuilderItemSchema).optional(), + }), +) + +/** Strict runtime boundary for the public QuestionnaireResponse builder input. */ +export const questionnaireResponseBuilderInputSchema: z.ZodType = + z.strictObject({ + id: z.string().optional(), + questionnaire: z.string(), + identifier: z.strictObject({ system: z.string(), value: z.string() }), + status: z.enum([ + 'in-progress', + 'completed', + 'amended', + 'entered-in-error', + 'stopped', + ]), + subject: z.string().optional(), + authored: z.string(), + authorReference: z.string().optional(), + sourceReference: z.string().optional(), + extensions: z.array(extensionSchema).optional(), + items: z.array(questionnaireResponseBuilderItemSchema).optional(), + }) diff --git a/packages/fhir/src/questionnaire/temporal.ts b/packages/fhir/src/questionnaire/temporal.ts new file mode 100644 index 00000000..40223659 --- /dev/null +++ b/packages/fhir/src/questionnaire/temporal.ts @@ -0,0 +1,183 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { compareFhirInstants, parseFhirInstant } from '../core/index.js' + +const DATE = /^(?\d{4})(?:-(?\d{2})(?:-(?\d{2}))?)?$/u +const DATE_TIME = /^(?\d{4}-\d{2}-\d{2})T/u +const TIME = + /^(?\d{2}):(?\d{2}):(?\d{2})(?:\.(?\d+))?$/u + +const isLeapYear = (year: number): boolean => + year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) + +const daysInMonth = (year: number, month: number): number => { + if (month === 2) return isLeapYear(year) ? 29 : 28 + if ([4, 6, 9, 11].includes(month)) return 30 + return 31 +} + +interface DateParts { + readonly year: number + readonly month?: number + readonly day?: number +} + +const dateParts = (value: unknown): DateParts | undefined => { + if (typeof value !== 'string') return undefined + const groups = DATE.exec(value)?.groups + if (groups === undefined) return undefined + const year = Number(groups.year) + if (year === 0) return undefined + if (groups.month === undefined) return { year } + const month = Number(groups.month) + if (month < 1 || month > 12) return undefined + if (groups.day === undefined) return { year, month } + const day = Number(groups.day) + return day >= 1 && day <= daysInMonth(year, month) ? + { year, month, day } + : undefined +} + +interface TimeParts { + readonly hour: number + readonly minute: number + readonly second: number + readonly fraction: string +} + +const timeParts = (value: unknown): TimeParts | undefined => { + if (typeof value !== 'string') return undefined + const groups = TIME.exec(value)?.groups + if (groups === undefined) return undefined + const hour = Number(groups.hour) + const minute = Number(groups.minute) + const second = Number(groups.second) + return hour <= 23 && minute <= 59 && second <= 60 ? + { hour, minute, second, fraction: groups.fraction ?? '' } + : undefined +} + +type Comparison = -1 | 0 | 1 + +const compareNumbers = (left: number, right: number): Comparison => { + if (left < right) return -1 + if (left > right) return 1 + return 0 +} + +const compareFractions = (left: string, right: string): Comparison => { + const length = Math.max(left.length, right.length) + for (let index = 0; index < length; index += 1) { + const comparison = compareNumbers( + Number(left[index] ?? '0'), + Number(right[index] ?? '0'), + ) + if (comparison !== 0) return comparison + } + return 0 +} + +const compareTimes = (left: TimeParts, right: TimeParts): Comparison => { + for (const [leftPart, rightPart] of [ + [left.hour, right.hour], + [left.minute, right.minute], + [left.second, right.second], + ] as const) { + const comparison = compareNumbers(leftPart, rightPart) + if (comparison !== 0) return comparison + } + return compareFractions(left.fraction, right.fraction) +} + +const datePrecision = (parts: DateParts): 1 | 2 | 3 => { + if (parts.day !== undefined) return 3 + if (parts.month !== undefined) return 2 + return 1 +} + +const compareDates = ( + left: DateParts, + right: DateParts, +): Comparison | undefined => { + const sharedParts = Math.min(datePrecision(left), datePrecision(right)) + const comparisons = [ + compareNumbers(left.year, right.year), + ...(sharedParts >= 2 ? + [compareNumbers(left.month ?? 0, right.month ?? 0)] + : []), + ...(sharedParts >= 3 ? + [compareNumbers(left.day ?? 0, right.day ?? 0)] + : []), + ] + const firstDifference = comparisons.find((entry) => entry !== 0) + if (firstDifference !== undefined) return firstDifference + return datePrecision(left) === datePrecision(right) ? 0 : undefined +} + +/** Whether a value is a semantically valid FHIR R4 date primitive. */ +export const isR4Date = (value: unknown): value is string => + dateParts(value) !== undefined + +/** Whether a value is a semantically valid FHIR R4 dateTime primitive. */ +export const isR4DateTime = (value: unknown): value is string => { + if (typeof value !== 'string') return false + if (dateParts(value) !== undefined) return true + const full = DATE_TIME.exec(value) + return ( + full !== null && + dateParts(full.groups?.date) !== undefined && + parseFhirInstant(value).ok + ) +} + +/** Whether a value is a semantically valid FHIR R4 time primitive. */ +export const isR4Time = (value: unknown): value is string => + timeParts(value) !== undefined + +export type R4TemporalKind = 'date' | 'dateTime' | 'time' + +/** + * Compares valid R4 temporal primitives. Different partial precisions that + * denote overlapping ranges are deliberately incomparable. + */ +export const compareR4Temporal = ( + left: unknown, + right: unknown, + kind: R4TemporalKind, +): Comparison | undefined => { + if (kind === 'time') { + const leftTime = timeParts(left) + const rightTime = timeParts(right) + return leftTime === undefined || rightTime === undefined ? + undefined + : compareTimes(leftTime, rightTime) + } + + if (kind === 'date') { + const leftDate = dateParts(left) + const rightDate = dateParts(right) + return leftDate === undefined || rightDate === undefined ? + undefined + : compareDates(leftDate, rightDate) + } + + if (!isR4DateTime(left) || !isR4DateTime(right)) return undefined + const leftInstant = DATE_TIME.test(left) + const rightInstant = DATE_TIME.test(right) + if (leftInstant || rightInstant) { + if (!leftInstant || !rightInstant) return undefined + const comparison = compareFhirInstants(left, right) + return comparison.ok ? comparison.value : undefined + } + const leftDate = dateParts(left) + const rightDate = dateParts(right) + return leftDate === undefined || rightDate === undefined ? + undefined + : compareDates(leftDate, rightDate) +} diff --git a/packages/fhir/src/questionnaire/types.ts b/packages/fhir/src/questionnaire/types.ts new file mode 100644 index 00000000..b4f8a9e0 --- /dev/null +++ b/packages/fhir/src/questionnaire/types.ts @@ -0,0 +1,220 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import type { + AbsoluteUri, + Canonical, + FhirId, + FhirInstant, + PatientReference, + SemVer, +} from '../core/index.js' +import type { + Attachment, + Coding, + Extension, + Identifier, + Questionnaire, + QuestionnaireResponse, + Quantity, +} from '../r4/index.js' + +type ExactlyOne = { + readonly [Key in keyof Values]: Readonly< + { readonly [Selected in Key]-?: Values[Selected] } & Readonly< + Partial, never>> + > + > +}[keyof Values] + +type WithBackbone< + Value extends object, + Additional extends object = Record, +> = Value extends object ? Readonly : never + +interface BackboneInput { + readonly id?: string + readonly extension?: readonly Extension[] + readonly modifierExtension?: readonly Extension[] +} + +interface AnswerValues { + readonly valueBoolean: boolean + readonly valueDecimal: number + readonly valueInteger: number + readonly valueDate: string + readonly valueDateTime: string + readonly valueTime: string + readonly valueString: string + readonly valueUri: string + readonly valueAttachment: Attachment + readonly valueCoding: Coding + readonly valueQuantity: Quantity +} + +interface EnableWhenValues { + readonly answerBoolean: boolean + readonly answerDecimal: number + readonly answerInteger: number + readonly answerDate: string + readonly answerDateTime: string + readonly answerTime: string + readonly answerString: string + readonly answerCoding: Coding + readonly answerQuantity: Quantity +} + +interface AnswerOptionValues { + readonly valueInteger: number + readonly valueDate: string + readonly valueTime: string + readonly valueString: string + readonly valueCoding: Coding +} + +export type QuestionnaireAnswerValueInput = ExactlyOne + +export type QuestionnaireItemType = + | 'attachment' + | 'boolean' + | 'choice' + | 'date' + | 'dateTime' + | 'decimal' + | 'display' + | 'group' + | 'integer' + | 'open-choice' + | 'quantity' + | 'string' + | 'text' + | 'time' + | 'url' + +export type QuestionnaireEnableWhenInput = WithBackbone< + ExactlyOne, + { + readonly question: string + readonly operator: '!=' | '<' | '<=' | '=' | '>' | '>=' | 'exists' + } +> + +export type QuestionnaireAnswerOptionInput = WithBackbone< + ExactlyOne, + { readonly initialSelected?: boolean } +> + +export type QuestionnaireInitialInput = + WithBackbone + +/** Bounded R4 Questionnaire item surface supported by the Grove profile. */ +export interface QuestionnaireItemInput extends BackboneInput { + readonly linkId: string + readonly definition?: string + readonly code?: readonly Coding[] + readonly prefix?: string + readonly text?: string + readonly type: QuestionnaireItemType + readonly enableWhen?: readonly QuestionnaireEnableWhenInput[] + readonly enableBehavior?: 'all' | 'any' + readonly required?: boolean + readonly repeats?: boolean + readonly readOnly?: boolean + readonly maxLength?: number + readonly answerValueSet?: string + readonly answerOption?: readonly QuestionnaireAnswerOptionInput[] + readonly initial?: readonly QuestionnaireInitialInput[] + readonly item?: readonly QuestionnaireItemInput[] +} + +export type QuestionnaireResponseAnswerInput = WithBackbone< + QuestionnaireAnswerValueInput, + { readonly item?: readonly QuestionnaireResponseItemInput[] } +> + +/** Bounded R4 QuestionnaireResponse item surface supported by the Grove profile. */ +export interface QuestionnaireResponseItemInput extends BackboneInput { + readonly linkId: string + readonly definition?: string + readonly text?: string + readonly answer?: readonly QuestionnaireResponseAnswerInput[] + readonly item?: readonly QuestionnaireResponseItemInput[] +} + +export interface QuestionnaireInput { + readonly id?: FhirId + readonly url: AbsoluteUri + readonly version: SemVer + readonly name?: string + readonly title?: string + readonly status: Questionnaire['status'] + readonly subjectTypes?: readonly string[] + readonly date?: FhirInstant + readonly description?: string + readonly purpose?: string + readonly extensions?: readonly Extension[] + readonly items: readonly [QuestionnaireItemInput, ...QuestionnaireItemInput[]] +} + +export interface QuestionnaireResponseIdentifierInput { + readonly system: AbsoluteUri + readonly value: string +} + +export interface QuestionnaireResponseInput { + readonly id?: FhirId + /** Exact `Questionnaire.url|Questionnaire.version` canonical. */ + readonly questionnaire: Canonical + readonly identifier: QuestionnaireResponseIdentifierInput + readonly status: QuestionnaireResponse['status'] + readonly subject?: PatientReference + readonly authored: FhirInstant + readonly authorReference?: string + readonly sourceReference?: string + readonly extensions?: readonly Extension[] + readonly items?: readonly QuestionnaireResponseItemInput[] +} + +export type GroveQuestionnaire = Readonly< + Omit & { + readonly item: NonNullable + } +> + +export type GroveQuestionnaireResponse = Readonly< + Omit< + QuestionnaireResponse, + 'authored' | 'identifier' | 'item' | 'questionnaire' + > & { + readonly identifier: Identifier + readonly questionnaire: string + readonly authored: string + readonly item?: QuestionnaireResponse['item'] + } +> + +export interface QuestionnairePair { + readonly questionnaire: GroveQuestionnaire + readonly response: GroveQuestionnaireResponse +} + +export interface QuestionnaireValueSetConceptInput { + readonly system: AbsoluteUri + readonly code: string + readonly version?: string +} + +/** Deterministically expanded terminology supplied by the caller; no network lookup occurs. */ +export interface ResolvedQuestionnaireValueSetInput { + readonly canonical: Canonical + readonly concepts: readonly QuestionnaireValueSetConceptInput[] +} + +export interface QuestionnairePreflightOptions { + readonly valueSets?: readonly ResolvedQuestionnaireValueSetInput[] +} diff --git a/packages/fhir/src/r4/index.ts b/packages/fhir/src/r4/index.ts new file mode 100644 index 00000000..553f5668 --- /dev/null +++ b/packages/fhir/src/r4/index.ts @@ -0,0 +1,62 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +export { + parseCollectionBundle, + parseDevice, + parseDocumentReference, + parseObservation, + parseProvenance, + parseSpecimen, + parseSupportedR4Resource, +} from './parse.js' +export { + attachmentSchema, + codeableConceptSchema, + codingSchema, + collectionBundleSchema, + deviceSchema, + documentReferenceSchema, + expressionSchema, + extensionSchema, + graphResourceSchema, + identifierSchema, + metaSchema, + observationComponentSchema, + observationSchema, + periodSchema, + primitiveElementSchema, + provenanceSchema, + quantitySchema, + referenceSchema, + resolvableReferenceSchema, + sampledDataSchema, + specimenSchema, + supportedR4ResourceSchema, +} from './schemas.js' +export type { + Attachment, + CodeableConcept, + Coding, + CollectionBundle, + Device, + DocumentReference, + Extension, + GraphResource, + Identifier, + Observation, + Period, + Provenance, + Questionnaire, + QuestionnaireResponse, + Quantity, + Reference, + SampledData, + Specimen, + SupportedR4Resource, +} from './types.js' diff --git a/packages/fhir/src/r4/parse.ts b/packages/fhir/src/r4/parse.ts new file mode 100644 index 00000000..d36f4dc9 --- /dev/null +++ b/packages/fhir/src/r4/parse.ts @@ -0,0 +1,78 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { type z } from 'zod' +import { + collectionBundleSchema, + deviceSchema, + documentReferenceSchema, + observationSchema, + provenanceSchema, + specimenSchema, + supportedR4ResourceSchema, +} from './schemas.js' +import type { + CollectionBundle, + Device, + DocumentReference, + Observation, + Provenance, + Specimen, + SupportedR4Resource, +} from './types.js' +import { + deepFreeze, + issues, + ok, + type Issue, + type Result, +} from '../core/index.js' + +const normalizeIssue = (entry: z.core.$ZodIssue): Issue => ({ + severity: 'error', + code: 'schema-invalid', + path: entry.path.map((component) => + typeof component === 'symbol' ? + (component.description ?? component.toString()) + : component, + ), + message: entry.message, +}) + +// The schema decides the parsed type; a caller-chosen T would make the cast below a lie. +const parseWith = (schema: z.ZodType, input: unknown): Result => { + const result = schema.safeParse(input) + if (!result.success) { + return issues(result.error.issues.map(normalizeIssue)) + } + return ok(deepFreeze(result.data) as T) +} + +export const parseObservation = (input: unknown): Result => + parseWith(observationSchema, input) + +export const parseDevice = (input: unknown): Result => + parseWith(deviceSchema, input) + +export const parseDocumentReference = ( + input: unknown, +): Result => parseWith(documentReferenceSchema, input) + +export const parseProvenance = (input: unknown): Result => + parseWith(provenanceSchema, input) + +export const parseSpecimen = (input: unknown): Result => + parseWith(specimenSchema, input) + +export const parseCollectionBundle = ( + input: unknown, +): Result => parseWith(collectionBundleSchema, input) + +export const parseSupportedR4Resource = ( + input: unknown, +): Result => parseWith(supportedR4ResourceSchema, input) diff --git a/packages/fhir/src/r4/schemas.ts b/packages/fhir/src/r4/schemas.ts new file mode 100644 index 00000000..daf7912f --- /dev/null +++ b/packages/fhir/src/r4/schemas.ts @@ -0,0 +1,156 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import type { Reference as R4Reference } from 'fhir/r4.js' +import { z } from 'zod' +import type { + CollectionBundle, + GraphResource, + SupportedR4Resource, +} from './types.js' +import { + attachmentSchema, + codeableConceptSchema, + codingSchema, + deviceSchema, + documentReferenceSchema, + elementSchema, + expressionSchema, + extensionSchema, + identifierSchema, + instantSchema, + medicationAdministrationSchema, + medicationStatementSchema, + metaSchema, + observationComponentSchema, + observationSchema, + patientSchema, + periodSchema, + planDefinitionSchema, + provenanceSchema, + quantitySchema, + questionnaireResponseSchema, + questionnaireSchema, + referenceSchema as baseReferenceSchema, + researchStudySchema, + researchSubjectSchema, + sampledDataSchema, + specimenSchema, + visionPrescriptionSchema, +} from '../zod/r4/index.js' + +// The FHIR R4 model is generated from the release's own StructureDefinitions; only Grove's +// narrowings are written here. Anything hand-written that merely restates the specification +// would be a second source of truth for it. +export { + attachmentSchema, + codeableConceptSchema, + codingSchema, + deviceSchema, + documentReferenceSchema, + expressionSchema, + extensionSchema, + identifierSchema, + metaSchema, + observationComponentSchema, + observationSchema, + periodSchema, + provenanceSchema, + quantitySchema, + sampledDataSchema, + specimenSchema, +} + +/** + * A reference Grove can resolve. + * + * FHIR admits a Reference carrying only a display string. An exchange graph cannot follow one, so + * Grove requires an actual pointer: a literal reference or a business identifier. + */ +export const resolvableReferenceSchema: z.ZodType = + baseReferenceSchema.refine( + (value) => { + const reference = value as { reference?: unknown; identifier?: unknown } + return ( + reference.reference !== undefined || reference.identifier !== undefined + ) + }, + { message: 'A Reference requires reference or identifier.' }, + ) + +/** + * The reference every Grove resource uses. + * + * Named separately from the generated FHIR one, which the `/zod/r4` entry point also exports as + * `referenceSchema`: they are different objects with different semantics, and a consumer + * reaching for the wrong one would get no error. + */ +export const referenceSchema: z.ZodType = resolvableReferenceSchema + +/** The Element a primitive carries its id and extensions in. */ +export const primitiveElementSchema: typeof elementSchema = elementSchema + +/** The resources a Grove exchange graph may carry. */ +const graphResourceMembers = [ + observationSchema, + deviceSchema, + documentReferenceSchema, + provenanceSchema, + specimenSchema, + patientSchema, + researchStudySchema, + researchSubjectSchema, + planDefinitionSchema, + medicationAdministrationSchema, + medicationStatementSchema, + visionPrescriptionSchema, + questionnaireSchema, + questionnaireResponseSchema, +] as const + +export const graphResourceSchema: z.ZodType = + z.union(graphResourceMembers) + +/** + * A Grove exchange bundle. + * + * Narrower than a FHIR Bundle in three ways the exchange contract requires: the type is always + * `collection`, every entry carries a `urn:uuid:` full URL, and an entry's resource is one of the + * graph resources rather than any resource at all. + */ +export const collectionBundleSchema: z.ZodType = + z.strictObject({ + resourceType: z.literal('Bundle'), + id: z.string().optional(), + meta: metaSchema.optional(), + implicitRules: z.string().optional(), + language: z.string().optional(), + identifier: identifierSchema.optional(), + type: z.literal('collection'), + timestamp: instantSchema.optional(), + entry: z + .array( + z.strictObject({ + id: z.string().optional(), + extension: z.array(extensionSchema).optional(), + modifierExtension: z.array(extensionSchema).optional(), + fullUrl: z.string().regex( + // Any UUID version, either case: RFC 4122 is case-insensitive on input and the + // version nibble keeps growing. Grove mints lowercase v4 itself, but this also + // validates bundles it receives, and rejecting a conformant v7 would be wrong. + /^urn:uuid:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/u, + ), + resource: graphResourceSchema, + }), + ) + .optional(), + }) as unknown as z.ZodType + +/** Any resource the package parses on its own. */ +export const supportedR4ResourceSchema: z.ZodType = + z.union(graphResourceMembers) diff --git a/packages/fhir/src/r4/types.ts b/packages/fhir/src/r4/types.ts new file mode 100644 index 00000000..527163dc --- /dev/null +++ b/packages/fhir/src/r4/types.ts @@ -0,0 +1,92 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import type { + Attachment as R4Attachment, + Bundle as R4Bundle, + CodeableConcept as R4CodeableConcept, + Coding as R4Coding, + Device as R4Device, + DocumentReference as R4DocumentReference, + Extension as R4Extension, + Identifier as R4Identifier, + MedicationAdministration as R4MedicationAdministration, + MedicationStatement as R4MedicationStatement, + Observation as R4Observation, + Patient as R4Patient, + Period as R4Period, + PlanDefinition as R4PlanDefinition, + Provenance as R4Provenance, + Questionnaire as R4Questionnaire, + QuestionnaireResponse as R4QuestionnaireResponse, + Quantity as R4Quantity, + Reference as R4Reference, + ResearchStudy as R4ResearchStudy, + ResearchSubject as R4ResearchSubject, + SampledData as R4SampledData, + Specimen as R4Specimen, + VisionPrescription as R4VisionPrescription, +} from 'fhir/r4.js' +import type { ReadonlyDeep } from '../core/index.js' + +export type Attachment = ReadonlyDeep +export type Coding = ReadonlyDeep +export type CodeableConcept = ReadonlyDeep +export type Device = ReadonlyDeep +export type DocumentReference = ReadonlyDeep +export type Extension = ReadonlyDeep +export type Identifier = ReadonlyDeep +export type MedicationAdministration = ReadonlyDeep +export type MedicationStatement = ReadonlyDeep +export type Observation = ReadonlyDeep +export type Patient = ReadonlyDeep +export type Period = ReadonlyDeep +export type PlanDefinition = ReadonlyDeep +export type Provenance = ReadonlyDeep +export type Questionnaire = ReadonlyDeep +export type QuestionnaireResponse = ReadonlyDeep +export type Quantity = ReadonlyDeep +export type Reference = ReadonlyDeep +export type SampledData = ReadonlyDeep +export type ResearchStudy = ReadonlyDeep +export type ResearchSubject = ReadonlyDeep +export type Specimen = ReadonlyDeep +export type VisionPrescription = ReadonlyDeep + +/** Every resource a Grove exchange graph may carry, as the guides publish them. */ +export type GraphResource = + | Device + | DocumentReference + | MedicationAdministration + | MedicationStatement + | Observation + | Patient + | PlanDefinition + | Provenance + | Questionnaire + | QuestionnaireResponse + | ResearchStudy + | ResearchSubject + | Specimen + | VisionPrescription + +type BundleEntry = ReadonlyDeep[number]> + +export type CollectionBundle = ReadonlyDeep< + Omit & { + readonly type: 'collection' + readonly entry: ReadonlyArray< + Omit & { + readonly fullUrl: string + readonly resource: GraphResource + } + > + } +> + +export type SupportedR4Resource = CollectionBundle | GraphResource diff --git a/packages/fhir/src/resources/account.ts b/packages/fhir/src/resources/account.ts deleted file mode 100644 index fa566feb..00000000 --- a/packages/fhir/src/resources/account.ts +++ /dev/null @@ -1,147 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type AccountCoverage, - type AccountGuarantor, - type Account, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - backboneElementSchema, - elementSchema, - codeableConceptSchema, - periodSchema, - referenceSchema, - stringSchema, - booleanSchema, - positiveIntSchema, -} from '../elements/index.js' -import { accountStatusSchema } from '../valueSets/index.js' - -const accountCoverageSchema: ZodType = - backboneElementSchema.extend({ - coverage: referenceSchema, - priority: positiveIntSchema.optional(), - }) - -const accountGuarantorSchema: ZodType = - backboneElementSchema.extend({ - party: referenceSchema, - onHold: booleanSchema.optional(), - _onHold: elementSchema.optional(), - period: periodSchema.optional(), - }) - -/** - * Zod schema for FHIR Account resource (untyped version). - */ -export const untypedAccountSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Account').readonly(), - identifier: identifierSchema.array().optional(), - status: accountStatusSchema, - _status: elementSchema.optional(), - type: codeableConceptSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - subject: referenceSchema.array().optional(), - servicePeriod: periodSchema.optional(), - coverage: accountCoverageSchema.array().optional(), - owner: referenceSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - guarantor: accountGuarantorSchema.array().optional(), - partOf: referenceSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Account resource. - */ -export const accountSchema: ZodType = untypedAccountSchema - -/** - * Wrapper class for FHIR Account resources. - * Provides convenience methods to parse and access common Account fields - * such as identifiers and display strings. - */ -export class FhirAccount extends FhirDomainResource { - // Static Functions - - /** - * Parses an Account resource from unknown data. - * - * @param value - The data to parse and validate against the Account schema - * @returns A FhirAccount instance containing the validated resource - */ - public static parse(value: unknown): FhirAccount { - return new FhirAccount(accountSchema.parse(value)) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * Uses the generic FhirDomainResource helper under the hood. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } - - /** - * Gets the account type as human-readable display text. - * Falls back to the first coding display if text is not present. - * - * @returns The account type display text, if available - */ - public get typeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.type) - } -} diff --git a/packages/fhir/src/resources/activityDefinition.ts b/packages/fhir/src/resources/activityDefinition.ts deleted file mode 100644 index e7f02879..00000000 --- a/packages/fhir/src/resources/activityDefinition.ts +++ /dev/null @@ -1,294 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ActivityDefinition, - type ActivityDefinitionParticipant, - type ActivityDefinitionDynamicValue, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - canonicalSchema, - codeableConceptSchema, - contactDetailSchema, - dateSchema, - dateTimeSchema, - domainResourceSchema, - dosageSchema, - elementSchema, - expressionSchema, - identifierSchema, - markdownSchema, - periodSchema, - quantitySchema, - rangeSchema, - referenceSchema, - relatedArtifactSchema, - stringSchema, - timingSchema, - uriSchema, - usageContextSchema, -} from '../elements/index.js' -import { - actionParticipantTypeSchema, - publicationStatusSchema, - requestResourceTypeSchema, - requestIntentSchema, - requestPrioritySchema, -} from '../valueSets/index.js' - -const activityDefinitionParticipantSchema: ZodType = - backboneElementSchema.extend({ - type: actionParticipantTypeSchema, - _type: elementSchema.optional(), - role: codeableConceptSchema.optional(), - }) - -const activityDefinitionDynamicValueSchema: ZodType = - backboneElementSchema.extend({ - path: stringSchema, - _path: elementSchema.optional(), - expression: expressionSchema, - }) - -/** - * Zod schema for FHIR ActivityDefinition resource (untyped version). - */ -export const untypedActivityDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ActivityDefinition').readonly(), - url: uriSchema.optional(), - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - subtitle: stringSchema.optional(), - _subtitle: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - subjectCodeableConcept: codeableConceptSchema.optional(), - subjectReference: referenceSchema.optional(), - subjectCanonical: canonicalSchema.optional(), - _subjectCanonical: elementSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - purpose: markdownSchema.optional(), - _purpose: elementSchema.optional(), - usage: stringSchema.optional(), - _usage: elementSchema.optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - approvalDate: dateSchema.optional(), - _approvalDate: elementSchema.optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - topic: codeableConceptSchema.array().optional(), - author: contactDetailSchema.array().optional(), - editor: contactDetailSchema.array().optional(), - reviewer: contactDetailSchema.array().optional(), - endorser: contactDetailSchema.array().optional(), - relatedArtifact: relatedArtifactSchema.array().optional(), - library: canonicalSchema.array().optional(), - _library: elementSchema.array().optional(), - kind: requestResourceTypeSchema.optional(), - _kind: elementSchema.optional(), - profile: canonicalSchema.optional(), - _profile: elementSchema.optional(), - code: codeableConceptSchema.optional(), - intent: requestIntentSchema.optional(), - _intent: elementSchema.optional(), - priority: requestPrioritySchema.optional(), - _priority: elementSchema.optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: elementSchema.optional(), - timingTiming: timingSchema.optional(), - timingDateTime: dateTimeSchema.optional(), - _timingDateTime: elementSchema.optional(), - timingAge: quantitySchema.optional(), - timingPeriod: periodSchema.optional(), - timingRange: rangeSchema.optional(), - timingDuration: quantitySchema.optional(), - location: referenceSchema.optional(), - participant: activityDefinitionParticipantSchema.array().optional(), - productReference: referenceSchema.optional(), - productCodeableConcept: codeableConceptSchema.optional(), - quantity: quantitySchema.optional(), - dosage: dosageSchema.array().optional(), - bodySite: codeableConceptSchema.array().optional(), - specimenRequirement: referenceSchema.array().optional(), - observationRequirement: referenceSchema.array().optional(), - observationResultRequirement: referenceSchema.array().optional(), - transform: canonicalSchema.optional(), - _transform: elementSchema.optional(), - dynamicValue: activityDefinitionDynamicValueSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ActivityDefinition resource. - */ -export const activityDefinitionSchema: ZodType = - untypedActivityDefinitionSchema - -/** - * Wrapper class for FHIR ActivityDefinition resources. - * Provides utility methods for working with activity definitions and their effective periods. - */ -export class FhirActivityDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses an ActivityDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the ActivityDefinition schema - * @returns A FhirActivityDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirActivityDefinition { - return new FhirActivityDefinition(activityDefinitionSchema.parse(value)) - } - - /** - * Get the publication date as a Date object. - * @returns The publication date, or undefined if not set - */ - public get publicationDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.date) - } - - /** - * Get the approval date as a Date object. - * @returns The approval date, or undefined if not set - */ - public get approvalDate(): Date | undefined { - return FhirDomainResource.parseDate(this.value.approvalDate) - } - - /** - * Get the last review date as a Date object. - * @returns The last review date, or undefined if not set - */ - public get lastReviewDate(): Date | undefined { - return FhirDomainResource.parseDate(this.value.lastReviewDate) - } - - /** - * Get effective period start date. - * @returns The start date, or undefined if not set - */ - public get effectivePeriodStart(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.effectivePeriod?.start) - } - - /** - * Get effective period end date. - * @returns The end date, or undefined if not set - */ - public get effectivePeriodEnd(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.effectivePeriod?.end) - } - - /** - * Check if the activity definition is currently effective. - * @param asOfDate Optional date to check against (defaults to current date) - * @returns True if currently within effective period - */ - public isCurrentlyEffective(asOfDate?: Date): boolean { - return FhirDomainResource.periodIsActive( - this.value.effectivePeriod, - asOfDate, - ) - } - - /** - * Get code display text. - * @returns Code display text - */ - public get codeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.code) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } - - /** - * Get topic displays from all topics. - * @returns Array of topic display texts - */ - public get topicDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.topic) - } - - /** - * Get jurisdiction displays. - * @returns Array of jurisdiction display texts - */ - public get jurisdictionDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.jurisdiction) - } -} diff --git a/packages/fhir/src/resources/administrableProductDefinition.ts b/packages/fhir/src/resources/administrableProductDefinition.ts deleted file mode 100644 index b67484f2..00000000 --- a/packages/fhir/src/resources/administrableProductDefinition.ts +++ /dev/null @@ -1,170 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type AdministrableProductDefinition, - type AdministrableProductDefinitionProperty, - type AdministrableProductDefinitionRouteOfAdministration, - type AdministrableProductDefinitionRouteOfAdministrationTargetSpecies, - type AdministrableProductDefinitionRouteOfAdministrationTargetSpeciesWithdrawalPeriod, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - attachmentSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - dateSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - quantitySchema, - ratioSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { administrableProductDefinitionStatusSchema } from '../valueSets/index.js' - -const administrableProductDefinitionPropertySchema: ZodType = - backboneElementSchema.extend({ - status: codeableConceptSchema.optional(), - type: codeableConceptSchema, - valueCodeableConcept: codeableConceptSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueDate: dateSchema.optional(), - _valueDate: elementSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueAttachment: attachmentSchema.optional(), - }) - -const administrableProductDefinitionRouteOfAdministrationTargetSpeciesWithdrawalPeriodSchema: ZodType = - backboneElementSchema.extend({ - supportingInformation: stringSchema.optional(), - _supportingInformation: elementSchema.optional(), - tissue: codeableConceptSchema, - value: quantitySchema, - }) - -const administrableProductDefinitionRouteOfAdministrationTargetSpeciesSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema, - withdrawalPeriod: - administrableProductDefinitionRouteOfAdministrationTargetSpeciesWithdrawalPeriodSchema - .array() - .optional(), - }) - -const administrableProductDefinitionRouteOfAdministrationSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema, - firstDose: quantitySchema.optional(), - maxDosePerDay: quantitySchema.optional(), - maxDosePerTreatmentPeriod: ratioSchema.optional(), - maxSingleDose: quantitySchema.optional(), - maxTreatmentPeriod: quantitySchema.optional(), - targetSpecies: - administrableProductDefinitionRouteOfAdministrationTargetSpeciesSchema - .array() - .optional(), - }) - -/** - * Zod schema for FHIR AdministrableProductDefinition resource (untyped version). - */ -export const untypedAdministrableProductDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('AdministrableProductDefinition').readonly(), - administrableDoseForm: codeableConceptSchema.optional(), - device: referenceSchema.optional(), - formOf: referenceSchema.array().optional(), - identifier: identifierSchema.array().optional(), - ingredient: codeableConceptSchema.array().optional(), - producedFrom: referenceSchema.array().optional(), - property: administrableProductDefinitionPropertySchema.array().optional(), - routeOfAdministration: - administrableProductDefinitionRouteOfAdministrationSchema.array(), - status: administrableProductDefinitionStatusSchema, - _status: elementSchema.optional(), - unitOfPresentation: codeableConceptSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR AdministrableProductDefinition resource. - */ -export const administrableProductDefinitionSchema: ZodType = - untypedAdministrableProductDefinitionSchema - -/** - * Wrapper class for FHIR AdministrableProductDefinition resources. - * Provides utility methods for working with administrable product definitions. - */ -export class FhirAdministrableProductDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses an AdministrableProductDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the AdministrableProductDefinition schema - * @returns A FhirAdministrableProductDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirAdministrableProductDefinition { - return new FhirAdministrableProductDefinition( - administrableProductDefinitionSchema.parse(value), - ) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/adverseEvent.ts b/packages/fhir/src/resources/adverseEvent.ts deleted file mode 100644 index 4e2bd6ab..00000000 --- a/packages/fhir/src/resources/adverseEvent.ts +++ /dev/null @@ -1,192 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type AdverseEventSuspectEntity, - type AdverseEventSuspectEntityCausality, - type AdverseEvent, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { adverseEventActualitySchema } from '../valueSets/index.js' - -const adverseEventSuspectEntityCausalitySchema: ZodType = - backboneElementSchema.extend({ - assessment: codeableConceptSchema.optional(), - productRelatedness: stringSchema.optional(), - _productRelatedness: elementSchema.optional(), - author: referenceSchema.optional(), - method: codeableConceptSchema.optional(), - }) - -const adverseEventSuspectEntitySchema: ZodType = - backboneElementSchema.extend({ - instance: referenceSchema, - causality: adverseEventSuspectEntityCausalitySchema.array().optional(), - }) - -/** - * Zod schema for FHIR AdverseEvent resource (untyped version). - */ -export const untypedAdverseEventSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('AdverseEvent').readonly(), - identifier: identifierSchema.optional(), - actuality: adverseEventActualitySchema, - _actuality: elementSchema.optional(), - category: codeableConceptSchema.array().optional(), - event: codeableConceptSchema.optional(), - subject: referenceSchema, - encounter: referenceSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - detected: dateTimeSchema.optional(), - _detected: elementSchema.optional(), - recordedDate: dateTimeSchema.optional(), - _recordedDate: elementSchema.optional(), - resultingCondition: referenceSchema.array().optional(), - location: referenceSchema.optional(), - seriousness: codeableConceptSchema.optional(), - severity: codeableConceptSchema.optional(), - outcome: codeableConceptSchema.optional(), - recorder: referenceSchema.optional(), - contributor: referenceSchema.array().optional(), - suspectEntity: adverseEventSuspectEntitySchema.array().optional(), - subjectMedicalHistory: referenceSchema.array().optional(), - referenceDocument: referenceSchema.array().optional(), - study: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR AdverseEvent resource. - */ -export const adverseEventSchema: ZodType = - untypedAdverseEventSchema - -/** - * Wrapper class for FHIR AdverseEvent resources. - * Provides utility methods for working with adverse event records. - */ -export class FhirAdverseEvent extends FhirDomainResource { - // Static Functions - - /** - * Parses an AdverseEvent resource from unknown data. - * - * @param value - The data to parse and validate against the AdverseEvent schema - * @returns A FhirAdverseEvent instance containing the validated resource - */ - public static parse(value: unknown): FhirAdverseEvent { - return new FhirAdverseEvent(adverseEventSchema.parse(value)) - } - - /** - * Gets the adverse event date as a JavaScript Date object. - * - * @returns The event date if available, undefined otherwise - * - * @example - * ```typescript - * const eventDate = adverseEvent.eventDate - * if (eventDate) { - * console.log(`Event occurred on: ${eventDate.toLocaleDateString()}`) - * } - * ``` - */ - public get eventDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.date) - } - - /** - * Gets the recorded date as a JavaScript Date object. - * - * @returns The recorded date if available, undefined otherwise - * - * @example - * ```typescript - * const recorded = adverseEvent.recordedDate - * console.log(`Recorded on: ${recorded?.toLocaleDateString()}`) - * ``` - */ - public get recordedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.recordedDate) - } - - /** - * Gets the event display text from the event CodeableConcept. - * - * @returns Display text for the adverse event, or undefined if not available - * - * @example - * ```typescript - * const eventType = adverseEvent.eventDisplay - * console.log(`Event type: ${eventType}`) - * ``` - */ - public get eventDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.event) - } - - /** - * Gets the seriousness display text. - * - * @returns Display text for seriousness level, or undefined if not available - * - * @example - * ```typescript - * const seriousness = adverseEvent.seriousnessDisplay - * if (seriousness) { - * console.log(`Seriousness: ${seriousness}`) - * } - * ``` - */ - public get seriousnessDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.seriousness) - } - - /** - * Gets the severity display text. - * - * @returns Display text for severity level, or undefined if not available - * - * @example - * ```typescript - * const severity = adverseEvent.severityDisplay - * console.log(`Severity: ${severity}`) - * ``` - */ - public get severityDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.severity) - } - - /** - * Gets all category displays as an array of strings. - * - * @returns Array of category display texts - * - * @example - * ```typescript - * const categories = adverseEvent.getCategoryDisplays() - * console.log(`Categories: ${categories.join(', ')}`) - * ``` - */ - public get categoryDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.category) - } -} diff --git a/packages/fhir/src/resources/allergyIntolerance.ts b/packages/fhir/src/resources/allergyIntolerance.ts deleted file mode 100644 index 3c13ec65..00000000 --- a/packages/fhir/src/resources/allergyIntolerance.ts +++ /dev/null @@ -1,256 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type AllergyIntoleranceReaction, - type AllergyIntolerance, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - quantitySchema, - rangeSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { - allergyIntoleranceCategorySchema, - allergyIntoleranceCriticalitySchema, - allergyIntoleranceReactionSeveritySchema, - allergyIntoleranceTypeSchema, -} from '../valueSets/index.js' - -const allergyIntoleranceReactionSchema: ZodType = - backboneElementSchema.extend({ - substance: codeableConceptSchema.optional(), - manifestation: codeableConceptSchema.array().min(1), - onset: dateTimeSchema.optional(), - _onset: elementSchema.optional(), - severity: allergyIntoleranceReactionSeveritySchema.optional(), - _severity: elementSchema.optional(), - exposureRoute: codeableConceptSchema.optional(), - note: annotationSchema.array().optional(), - }) - -/** - * Zod schema for FHIR AllergyIntolerance resource (untyped version). - */ -export const untypedAllergyIntoleranceSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('AllergyIntolerance').readonly(), - identifier: identifierSchema.array().optional(), - clinicalStatus: codeableConceptSchema.optional(), - verificationStatus: codeableConceptSchema.optional(), - type: allergyIntoleranceTypeSchema.optional(), - _type: elementSchema.optional(), - category: allergyIntoleranceCategorySchema.array().optional(), - _category: elementSchema.array().optional(), - criticality: allergyIntoleranceCriticalitySchema.optional(), - code: codeableConceptSchema.optional(), - patient: referenceSchema, - encounter: referenceSchema.optional(), - onsetDateTime: dateTimeSchema.optional(), - _onsetDateTime: elementSchema.optional(), - onsetAge: quantitySchema.optional(), - onsetPeriod: periodSchema.optional(), - onsetRange: rangeSchema.optional(), - onsetString: stringSchema.optional(), - _onsetString: elementSchema.optional(), - recordedDate: dateTimeSchema.optional(), - _recordedDate: elementSchema.optional(), - recorder: referenceSchema.optional(), - asserter: referenceSchema.optional(), - lastOccurrence: dateTimeSchema.optional(), - _lastOccurrence: elementSchema.optional(), - note: annotationSchema.array().optional(), - reaction: allergyIntoleranceReactionSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR AllergyIntolerance resource. - */ -export const allergyIntoleranceSchema: ZodType = - untypedAllergyIntoleranceSchema - -/** - * Wrapper class for FHIR AllergyIntolerance resources. - * Provides utility methods for working with allergy information. - */ -export class FhirAllergyIntolerance extends FhirDomainResource { - // Static Functions - - /** - * Parses an AllergyIntolerance resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirAllergyIntolerance instance - */ - public static parse(value: unknown): FhirAllergyIntolerance { - return new FhirAllergyIntolerance(allergyIntoleranceSchema.parse(value)) - } - - // Properties - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } - - /** - * Gets the onset date as a JavaScript Date object (from onsetDateTime). - * - * @returns The onset date if available - */ - public get onsetDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.onsetDateTime) - } - - /** - * Gets the recorded date as a JavaScript Date object. - * - * @returns The recorded date if available - */ - public get recordedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.recordedDate) - } - - /** - * Gets the last occurrence date as a JavaScript Date object. - * - * @returns The last occurrence date if available - */ - public get lastOccurrenceDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.lastOccurrence) - } - - /** - * Gets the allergy/intolerance code display text. - * - * @returns The code display text - */ - public get codeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.code) - } - - /** - * Gets the clinical status display text. - * - * @returns The clinical status display - */ - public get clinicalStatusDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.clinicalStatus) - } - - /** - * Gets the verification status display text. - * - * @returns The verification status display - */ - public get verificationStatusDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay( - this.value.verificationStatus, - ) - } - - /** - * Checks if the allergy is currently active. - * - * @returns true if clinical status is active - */ - public get isActive(): boolean { - return FhirDomainResource.containsCoding(this.value.clinicalStatus, { - system: - 'http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical', - code: 'active', - }) - } - - /** - * Checks if the allergy is confirmed. - * - * @returns true if verification status is confirmed - */ - public get isConfirmed(): boolean { - return FhirDomainResource.containsCoding(this.value.verificationStatus, { - system: - 'http://terminology.hl7.org/CodeSystem/allergyintolerance-verification', - code: 'confirmed', - }) - } - - /** - * Gets all reaction manifestations. - * - * @returns Array of manifestation display texts - */ - public get manifestationDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays( - this.value.reaction?.flatMap((reaction) => reaction.manifestation), - ) - } - - /** - * Gets all note texts. - * - * @returns Array of note texts - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } -} diff --git a/packages/fhir/src/resources/appointment.ts b/packages/fhir/src/resources/appointment.ts deleted file mode 100644 index 68a71996..00000000 --- a/packages/fhir/src/resources/appointment.ts +++ /dev/null @@ -1,247 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type AppointmentParticipant, - type Appointment, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - instantSchema, - intSchema, - periodSchema, - referenceSchema, - stringSchema, - unsignedIntSchema, -} from '../elements/index.js' -import { - appointmentStatusSchema, - appointmentParticipantRequiredSchema, - appointmentParticipantStatusSchema, -} from '../valueSets/index.js' - -const appointmentParticipantSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.array().optional(), - period: periodSchema.optional(), - actor: referenceSchema.optional(), - required: appointmentParticipantRequiredSchema.optional(), - status: appointmentParticipantStatusSchema, - _status: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR Appointment resource (untyped version). - */ -export const untypedAppointmentSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Appointment').readonly(), - identifier: identifierSchema.array().optional(), - status: appointmentStatusSchema, - _status: elementSchema.optional(), - cancelationReason: codeableConceptSchema.optional(), - serviceCategory: codeableConceptSchema.array().optional(), - serviceType: codeableConceptSchema.array().optional(), - specialty: codeableConceptSchema.array().optional(), - appointmentType: codeableConceptSchema.optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - priority: unsignedIntSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - supportingInformation: referenceSchema.array().optional(), - start: instantSchema.optional(), - _start: elementSchema.optional(), - end: instantSchema.optional(), - _end: elementSchema.optional(), - minutesDuration: intSchema.optional(), - _minutesDuration: elementSchema.optional(), - slot: referenceSchema.array().optional(), - created: dateTimeSchema.optional(), - _created: elementSchema.optional(), - comment: stringSchema.optional(), - _comment: elementSchema.optional(), - patientInstruction: stringSchema.optional(), - _patientInstruction: elementSchema.optional(), - basedOn: referenceSchema.array().optional(), - participant: appointmentParticipantSchema.array().min(1), - requestPeriod: periodSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Appointment resource. - */ -export const appointmentSchema: ZodType = untypedAppointmentSchema - -/** - * Wrapper class for FHIR Appointment resources. - * Provides utility methods for working with appointments and their dates. - */ -export class FhirAppointment extends FhirDomainResource { - // Static Functions - - /** - * Parses an Appointment resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirAppointment instance - */ - public static parse(value: unknown): FhirAppointment { - return new FhirAppointment(appointmentSchema.parse(value)) - } - - // Properties - - /** - * Gets the appointment start date/time as a JavaScript Date object. - * - * @returns The start date if available, undefined otherwise - * - * @example - * ```typescript - * const startDate = appointment.startDate - * if (startDate) { - * console.log(`Appointment starts at: ${startDate.toLocaleString()}`) - * } - * ``` - */ - public get startDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.start) - } - - /** - * Gets the appointment end date/time as a JavaScript Date object. - * - * @returns The end date if available, undefined otherwise - * - * @example - * ```typescript - * const endDate = appointment.endDate - * if (endDate) { - * console.log(`Appointment ends at: ${endDate.toLocaleString()}`) - * } - * ``` - */ - public get endDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.end) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } - - /** - * Calculates the duration of the appointment in milliseconds. - * - * @returns Duration in milliseconds if both start and end dates are available, undefined otherwise - * - * @example - * ```typescript - * const duration = appointment.getDurationInMilliseconds() - * if (duration) { - * const minutes = duration / (1000 * 60) - * console.log(`Appointment duration: ${minutes} minutes`) - * } - * ``` - */ - public get duration(): number | undefined { - const start = this.startDate - const end = this.endDate - if (start && end) { - return end.getTime() - start.getTime() - } - return undefined - } - - /** - * Checks if the appointment is in the past relative to a given date. - * - * @param asOf - The reference date (defaults to now) - * @returns True if the appointment end time is in the past, false otherwise - * - * @example - * ```typescript - * if (appointment.isPast()) { - * console.log('This appointment has already occurred') - * } - * ``` - */ - public isPast(asOf: Date = new Date()): boolean { - const end = this.endDate ?? this.startDate - return end !== undefined && end < asOf - } - - /** - * Checks if the appointment is upcoming relative to a given date. - * - * @param asOf - The reference date (defaults to now) - * @returns True if the appointment start time is in the future, false otherwise - * - * @example - * ```typescript - * if (appointment.isUpcoming()) { - * console.log('This appointment is scheduled for the future') - * } - * ``` - */ - public isUpcoming(asOf: Date = new Date()): boolean { - const start = this.startDate - return start !== undefined && start > asOf - } -} diff --git a/packages/fhir/src/resources/appointmentResponse.ts b/packages/fhir/src/resources/appointmentResponse.ts deleted file mode 100644 index 690a60f1..00000000 --- a/packages/fhir/src/resources/appointmentResponse.ts +++ /dev/null @@ -1,149 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type AppointmentResponse } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { appointmentResponseParticipantStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR AppointmentResponse resource (untyped version). - */ -export const untypedAppointmentResponseSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('AppointmentResponse').readonly(), - identifier: identifierSchema.array().optional(), - appointment: referenceSchema, - start: dateTimeSchema.optional(), - _start: elementSchema.optional(), - end: dateTimeSchema.optional(), - _end: elementSchema.optional(), - participantType: codeableConceptSchema.array().optional(), - actor: referenceSchema.optional(), - participantStatus: appointmentResponseParticipantStatusSchema, - _participantStatus: elementSchema.optional(), - comment: stringSchema.optional(), - _comment: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR AppointmentResponse resource. - */ -export const appointmentResponseSchema: ZodType = - untypedAppointmentResponseSchema - -/** - * Wrapper class for FHIR AppointmentResponse resources. - * Provides utility methods for working with appointment response records. - */ -export class FhirAppointmentResponse extends FhirDomainResource { - // Static Functions - - /** - * Parses an AppointmentResponse resource from unknown data. - * - * @param value - The data to parse and validate against the AppointmentResponse schema - * @returns A FhirAppointmentResponse instance containing the validated resource - */ - public static parse(value: unknown): FhirAppointmentResponse { - return new FhirAppointmentResponse(appointmentResponseSchema.parse(value)) - } - - // Properties - - /** - * Gets the appointment response start date/time as a JavaScript Date object. - * - * @returns The start date if available - */ - public get startDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.start) - } - - /** - * Gets the appointment response end date/time as a JavaScript Date object. - * - * @returns The end date if available - */ - public get endDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.end) - } - - /** - * Gets the participant type displays. - * - * @returns Array of participant type display texts - * - * @example - * ```typescript - * const types = response.getParticipantTypeDisplays() - * console.log(`Participant types: ${types.join(', ')}`) - * ``` - */ - public get participantTypeDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays( - this.value.participantType, - ) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/auditEvent.ts b/packages/fhir/src/resources/auditEvent.ts deleted file mode 100644 index de579963..00000000 --- a/packages/fhir/src/resources/auditEvent.ts +++ /dev/null @@ -1,157 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type AuditEventAgent, - type AuditEventAgentNetwork, - type AuditEventEntity, - type AuditEventEntityDetail, - type AuditEventSource, - type AuditEvent, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - base64BinarySchema, - booleanSchema, - codeableConceptSchema, - codingSchema, - domainResourceSchema, - elementSchema, - instantSchema, - periodSchema, - referenceSchema, - stringSchema, - uriSchema, -} from '../elements/index.js' - -const auditEventActionSchema = z.enum(['C', 'R', 'U', 'D', 'E']) -const auditEventAgentNetworkTypeSchema = z.enum(['1', '2', '3', '4', '5']) -const auditEventOutcomeSchema = z.enum(['0', '4', '8', '12']) - -const auditEventAgentNetworkSchema: ZodType = - backboneElementSchema.extend({ - address: stringSchema.optional(), - _address: elementSchema.optional(), - type: auditEventAgentNetworkTypeSchema.optional(), - _type: elementSchema.optional(), - }) - -const auditEventAgentSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - role: codeableConceptSchema.array().optional(), - who: referenceSchema.optional(), - altId: stringSchema.optional(), - _altId: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - requestor: booleanSchema, - _requestor: elementSchema.optional(), - location: referenceSchema.optional(), - policy: uriSchema.array().optional(), - _policy: elementSchema.array().optional(), - media: codingSchema.optional(), - network: auditEventAgentNetworkSchema.optional(), - purposeOfUse: codeableConceptSchema.array().optional(), - }) - -const auditEventSourceSchema: ZodType = - backboneElementSchema.extend({ - site: stringSchema.optional(), - _site: elementSchema.optional(), - observer: referenceSchema, - type: codingSchema.array().optional(), - }) - -const auditEventEntityDetailSchema: ZodType = - backboneElementSchema.extend({ - type: stringSchema, - _type: elementSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: elementSchema.optional(), - }) - -const auditEventEntitySchema: ZodType = - backboneElementSchema.extend({ - what: referenceSchema.optional(), - type: codingSchema.optional(), - role: codingSchema.optional(), - lifecycle: codingSchema.optional(), - securityLabel: codingSchema.array().optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - query: base64BinarySchema.optional(), - _query: elementSchema.optional(), - detail: auditEventEntityDetailSchema.array().optional(), - }) - -/** - * Zod schema for FHIR AuditEvent resource (untyped version). - */ -export const untypedAuditEventSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('AuditEvent').readonly(), - type: codingSchema, - subtype: codingSchema.array().optional(), - action: auditEventActionSchema.optional(), - _action: elementSchema.optional(), - period: periodSchema.optional(), - recorded: instantSchema, - _recorded: elementSchema.optional(), - outcome: auditEventOutcomeSchema.optional(), - _outcome: elementSchema.optional(), - outcomeDesc: stringSchema.optional(), - _outcomeDesc: elementSchema.optional(), - purposeOfEvent: codeableConceptSchema.array().optional(), - agent: auditEventAgentSchema.array().min(1), - source: auditEventSourceSchema, - entity: auditEventEntitySchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR AuditEvent resource. - */ -export const auditEventSchema: ZodType = untypedAuditEventSchema - -/** - * Wrapper class for FHIR AuditEvent resources. - * Provides utility methods for working with audit events and their recorded times. - */ -export class FhirAuditEvent extends FhirDomainResource { - /** - * Parses an AuditEvent resource from unknown data. - * - * @param value - The data to parse and validate against the AuditEvent schema - * @returns A FhirAuditEvent instance containing the validated resource - */ - public static parse(value: unknown): FhirAuditEvent { - return new FhirAuditEvent(auditEventSchema.parse(value)) - } - - /** - * Gets the date/time when the auditable event was recorded. - * - * @returns The recorded date if available, undefined otherwise - * - * @example - * ```typescript - * const recordedDate = auditEvent.recordedDate - * console.log(`Event recorded: ${recordedDate?.toISOString()}`) - * ``` - */ - public get recordedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.recorded) - } -} diff --git a/packages/fhir/src/resources/basic.ts b/packages/fhir/src/resources/basic.ts deleted file mode 100644 index 8fcf795d..00000000 --- a/packages/fhir/src/resources/basic.ts +++ /dev/null @@ -1,103 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type Basic } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - codeableConceptSchema, - dateSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - referenceSchema, -} from '../elements/index.js' - -/** - * Zod schema for FHIR Basic resource (untyped version). - */ -export const untypedBasicSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Basic').readonly(), - identifier: identifierSchema.array().optional(), - code: codeableConceptSchema, - subject: referenceSchema.optional(), - created: dateSchema.optional(), - _created: elementSchema.optional(), - author: referenceSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Basic resource. - */ -export const basicSchema: ZodType = untypedBasicSchema - -/** - * Wrapper class for FHIR Basic resources. - * Provides identifier helper accessors and parsing convenience. - */ -export class FhirBasic extends FhirDomainResource { - // Static Functions - - /** - * Parses a Basic resource from unknown data. - * - * @param value - The data to parse and validate against the Basic schema - * @returns A FhirBasic instance containing the validated resource - */ - public static parse(value: unknown): FhirBasic { - return new FhirBasic(basicSchema.parse(value)) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/binary.ts b/packages/fhir/src/resources/binary.ts deleted file mode 100644 index 9f51a27f..00000000 --- a/packages/fhir/src/resources/binary.ts +++ /dev/null @@ -1,76 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Binary } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { referenceSchema } from '../elements/dataTypes/reference.js' -import { - base64BinarySchema, - codeSchema, - elementSchema, -} from '../elements/index.js' -import { resourceSchema } from '../elements/resource.js' - -/** - * Zod schema for FHIR Binary resource (untyped version). - */ -export const untypedBinarySchema = z.lazy(() => - resourceSchema.extend({ - resourceType: z.literal('Binary').readonly(), - contentType: codeSchema, - _contentType: elementSchema.optional(), - securityContext: referenceSchema.optional(), - data: base64BinarySchema.optional(), - _data: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Binary resource. - */ -export const binarySchema: ZodType = untypedBinarySchema - -/** - * Wrapper class for FHIR Binary resources. - * Provides utility methods for working with binary data. - */ -export class FhirBinary extends FhirDomainResource { - // Static Functions - - /** - * Parses a Binary resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirBinary instance - */ - public static parse(value: unknown): FhirBinary { - return new FhirBinary(binarySchema.parse(value)) - } - - // Methods - - /** - * Decodes the base64-encoded data into a Uint8Array. - * Works in both browser and Node.js environments. - * - * @returns A Uint8Array containing the decoded binary data, or undefined if no data - * - * @example - * ```typescript - * const binary = FhirBinary.parse(binaryResource) - * const data = binary.getDataAsUint8Array() - * if (data) { - * console.log(`Binary data size: ${data.length} bytes`) - * } - * ``` - */ - public get dataAsUint8Array(): Uint8Array | undefined { - return FhirDomainResource.decodeBase64Binary(this.value.data) - } -} diff --git a/packages/fhir/src/resources/biologicallyDerivedProduct.ts b/packages/fhir/src/resources/biologicallyDerivedProduct.ts deleted file mode 100644 index 3eb113e4..00000000 --- a/packages/fhir/src/resources/biologicallyDerivedProduct.ts +++ /dev/null @@ -1,169 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type BiologicallyDerivedProduct, - type BiologicallyDerivedProductCollection, - type BiologicallyDerivedProductManipulation, - type BiologicallyDerivedProductProcessing, - type BiologicallyDerivedProductStorage, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - decimalSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - intSchema, - periodSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { - biologicallyDerivedProductCategorySchema, - biologicallyDerivedProductStatusSchema, - biologicallyDerivedProductStorageScaleSchema, -} from '../valueSets/index.js' - -const biologicallyDerivedProductCollectionSchema: ZodType = - backboneElementSchema.extend({ - collectedDateTime: dateTimeSchema.optional(), - _collectedDateTime: elementSchema.optional(), - collectedPeriod: periodSchema.optional(), - collector: referenceSchema.optional(), - source: referenceSchema.optional(), - }) - -const biologicallyDerivedProductProcessingSchema: ZodType = - backboneElementSchema.extend({ - additive: referenceSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - procedure: codeableConceptSchema.optional(), - timeDateTime: dateTimeSchema.optional(), - _timeDateTime: elementSchema.optional(), - timePeriod: periodSchema.optional(), - }) - -const biologicallyDerivedProductManipulationSchema: ZodType = - backboneElementSchema.extend({ - description: stringSchema.optional(), - _description: elementSchema.optional(), - timeDateTime: dateTimeSchema.optional(), - _timeDateTime: elementSchema.optional(), - timePeriod: periodSchema.optional(), - }) - -const biologicallyDerivedProductStorageSchema: ZodType = - backboneElementSchema.extend({ - description: stringSchema.optional(), - _description: elementSchema.optional(), - duration: periodSchema.optional(), - scale: biologicallyDerivedProductStorageScaleSchema.optional(), - _scale: elementSchema.optional(), - temperature: decimalSchema.optional(), - }) - -/** - * Zod schema for FHIR BiologicallyDerivedProduct resource (untyped version). - */ -export const untypedBiologicallyDerivedProductSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('BiologicallyDerivedProduct').readonly(), - collection: biologicallyDerivedProductCollectionSchema.optional(), - identifier: identifierSchema.array().optional(), - manipulation: biologicallyDerivedProductManipulationSchema.optional(), - parent: referenceSchema.array().optional(), - processing: biologicallyDerivedProductProcessingSchema.array().optional(), - productCategory: biologicallyDerivedProductCategorySchema.optional(), - _productCategory: elementSchema.optional(), - productCode: codeableConceptSchema.optional(), - quantity: intSchema.optional(), - request: referenceSchema.array().optional(), - status: biologicallyDerivedProductStatusSchema.optional(), - _status: elementSchema.optional(), - storage: biologicallyDerivedProductStorageSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR BiologicallyDerivedProduct resource. - */ -export const biologicallyDerivedProductSchema: ZodType = - untypedBiologicallyDerivedProductSchema - -/** - * Wrapper class for FHIR BiologicallyDerivedProduct resources. - * Provides utility methods for working with biologically derived products (blood, tissue, etc.). - */ -export class FhirBiologicallyDerivedProduct extends FhirDomainResource { - // Static Functions - - /** - * Parses a BiologicallyDerivedProduct resource from unknown data. - * - * @param value - The data to parse and validate against the BiologicallyDerivedProduct schema - * @returns A FhirBiologicallyDerivedProduct instance containing the validated resource - */ - public static parse(value: unknown): FhirBiologicallyDerivedProduct { - return new FhirBiologicallyDerivedProduct( - biologicallyDerivedProductSchema.parse(value), - ) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/bodyStructure.ts b/packages/fhir/src/resources/bodyStructure.ts deleted file mode 100644 index 684284e7..00000000 --- a/packages/fhir/src/resources/bodyStructure.ts +++ /dev/null @@ -1,117 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type BodyStructure } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - attachmentSchema, - booleanSchema, - codeableConceptSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' - -/** - * Zod schema for FHIR BodyStructure resource (untyped version). - */ -export const untypedBodyStructureSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('BodyStructure').readonly(), - identifier: identifierSchema.array().optional(), - active: booleanSchema.optional(), - _active: elementSchema.optional(), - morphology: codeableConceptSchema.optional(), - location: codeableConceptSchema.optional(), - locationQualifier: codeableConceptSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - image: attachmentSchema.array().optional(), - patient: referenceSchema, - }), -) satisfies ZodType - -/** - * Zod schema for FHIR BodyStructure resource. - */ -export const bodyStructureSchema: ZodType = - untypedBodyStructureSchema - -/** - * Wrapper class for FHIR BodyStructure resources. - * Provides utility methods for working with body structure information. - */ -export class FhirBodyStructure extends FhirDomainResource { - /** - * Parses a BodyStructure resource from unknown data. - * - * @param value - The data to parse and validate against the BodyStructure schema - * @returns A FhirBodyStructure instance containing the validated resource - */ - public static parse(value: unknown): FhirBodyStructure { - return new FhirBodyStructure(bodyStructureSchema.parse(value)) - } - - /** - * Gets the location display text for the body structure. - * - * @returns The location display text, if available - */ - public get locationDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.location) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/bundle.ts b/packages/fhir/src/resources/bundle.ts deleted file mode 100644 index 44df70ff..00000000 --- a/packages/fhir/src/resources/bundle.ts +++ /dev/null @@ -1,254 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type BundleEntry, - type BundleEntryRequest, - type BundleEntryResponse, - type BundleEntrySearch, - type BundleLink, - type Bundle, - type Resource, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { fhirResourceSchema } from './fhirResource.js' -import { - backboneElementSchema, - decimalSchema, - elementSchema, - identifierSchema, - instantSchema, - resourceSchema, - signatureSchema, - stringSchema, - unsignedIntSchema, - uriSchema, -} from '../elements/index.js' -import { - bundleTypeSchema, - bundleEntrySearchModeSchema, - bundleEntryRequestMethodSchema, -} from '../valueSets/index.js' - -const bundleLinkSchema: ZodType = backboneElementSchema.extend({ - relation: stringSchema, - _relation: elementSchema.optional(), - url: uriSchema, - _url: elementSchema.optional(), -}) - -const bundleEntrySearchSchema: ZodType = - backboneElementSchema.extend({ - mode: bundleEntrySearchModeSchema.optional(), - _mode: elementSchema.optional(), - score: decimalSchema.optional(), - }) - -const bundleEntryRequestSchema: ZodType = - backboneElementSchema.extend({ - method: bundleEntryRequestMethodSchema, - _method: elementSchema.optional(), - url: uriSchema, - _url: elementSchema.optional(), - ifNoneExist: stringSchema.optional(), - _ifNoneExist: elementSchema.optional(), - ifModifiedSince: instantSchema.optional(), - _ifModifiedSince: elementSchema.optional(), - ifMatch: stringSchema.optional(), - _ifMatch: elementSchema.optional(), - ifNoneMatch: stringSchema.optional(), - _ifNoneMatch: elementSchema.optional(), - }) - -const bundleEntryResponseSchema: ZodType = - backboneElementSchema.extend({ - status: stringSchema, - _status: elementSchema.optional(), - location: uriSchema.optional(), - _location: elementSchema.optional(), - etag: stringSchema.optional(), - _etag: elementSchema.optional(), - lastModified: instantSchema.optional(), - _lastModified: elementSchema.optional(), - get outcome() { - return fhirResourceSchema.optional() - }, - }) - -/** - * Creates a BundleEntry schema for the given resource type. - * - * @template R - The type of resource contained in the bundle entry - * @param schema - The Zod schema for the resource type - * @returns A Zod schema for BundleEntry containing the specified resource type - */ -const bundleEntrySchema = ( - schema: ZodType, -): ZodType> => - backboneElementSchema.extend({ - link: bundleLinkSchema.array().optional(), - fullUrl: uriSchema.optional(), - _fullUrl: elementSchema.optional(), - resource: schema.optional(), - search: bundleEntrySearchSchema.optional(), - request: bundleEntryRequestSchema.optional(), - response: bundleEntryResponseSchema.optional(), - }) - -/** - * Creates an untyped Bundle schema for the given resource type. - * - * @param schema - The Zod schema for the resource type - * @returns A Zod schema for Bundle containing the specified resource type - */ -export const untypedBundleSchema = (schema: ZodType) => - resourceSchema.extend({ - resourceType: z.literal('Bundle').readonly(), - identifier: identifierSchema.optional(), - type: bundleTypeSchema, - _type: elementSchema.optional(), - timestamp: instantSchema.optional(), - _timestamp: elementSchema.optional(), - total: unsignedIntSchema.optional(), - _total: elementSchema.optional(), - link: bundleLinkSchema.array().optional(), - get entry() { - return bundleEntrySchema(schema).array().optional() - }, - signature: signatureSchema.optional(), - }) satisfies ZodType> - -/** - * Creates a Bundle schema for the given resource type. - * - * @param schema - The Zod schema for the resource type - * @returns A Zod schema for Bundle containing the specified resource type - */ -export const bundleSchema = ( - schema: ZodType, -): ZodType> => untypedBundleSchema(schema) - -/** - * Zod schema for FHIR Bundle resource (untyped version, generic). - */ -export const untypedGenericBundleSchema = z.lazy(() => { - const resourceCompatibleSchema: ZodType = fhirResourceSchema - return untypedBundleSchema(resourceCompatibleSchema) -}) - -/** - * Zod schema for FHIR Bundle resource (generic). - */ -export const genericBundleSchema: ZodType = untypedGenericBundleSchema - -/** - * Wrapper class for FHIR Bundle resources. - * Provides utility methods for working with bundles and their entries. - * - * @template R - The type of resources contained in the bundle - */ -export class FhirBundle extends FhirDomainResource< - Bundle -> { - // Static Functions - - /** - * Parses a generic FHIR Bundle from unknown data. - * - * @param value - The data to parse - * @returns A FhirBundle instance containing any FHIR resource - */ - public static parseGeneric(value: unknown): FhirBundle { - return new FhirBundle(genericBundleSchema.parse(value)) - } - - /** - * Parses a typed FHIR Bundle from unknown data. - * - * @template R - The type of resources in the bundle - * @param value - The data to parse - * @param schema - Zod schema for validating the resource type - * @returns A FhirBundle instance containing resources of type R - */ - public static parse( - value: unknown, - schema: ZodType, - ): FhirBundle { - return new FhirBundle(bundleSchema(schema).parse(value)) - } - - // Functions - - /** - * Finds all resources in the bundle with a specific resource type. - * - * @template T - The specific resource type to find - * @param resourceType - The FHIR resource type name (e.g., 'Patient', 'Observation') - * @param predicate - Optional filter function to apply to resources - * @returns Array of resources matching the specified type - * - * @example - * ```typescript - * const patients = bundle.findResources('Patient') - * const observations = bundle.findResources('Observation') - * ``` - */ - public findResources( - resourceType: T['resourceType'], - predicate?: (resource: T) => boolean, - ): T[] { - return (this.value.entry ?? []) - .filter( - (entry) => - entry.resource?.resourceType === resourceType && - (predicate ? predicate(entry.resource as T) : true), - ) - .map((entry) => entry.resource as T) - } - - /** - * Finds a resource by its ID. - * - * @template T - The specific resource type to find - * @param resourceType - The FHIR resource type name - * @param id - The resource ID to find - * @returns The resource with matching ID or undefined - * - * @example - * ```typescript - * const patient = bundle.findResourceById('patient-123') - * ``` - */ - public resourceById( - resourceType: T['resourceType'], - id: string, - ): T | undefined { - return this.findResources( - resourceType, - (resource) => resource.id === id, - ).at(0) - } - - /** - * Finds a resource by reference string. - * - * @param reference - Reference string (e.g., "Patient/123") - * @returns The resource matching the reference or undefined - * - * @example - * ```typescript - * const patient = bundle.findResourceByReference('Patient/patient-123') - * ``` - */ - public findResourceByReference(reference: string): R | undefined { - const [resourceType, id] = reference.split('/') - if (!resourceType || !id) return undefined - return this.resourceById(resourceType, id) - } -} diff --git a/packages/fhir/src/resources/capabilityStatement.ts b/packages/fhir/src/resources/capabilityStatement.ts deleted file mode 100644 index f36a526a..00000000 --- a/packages/fhir/src/resources/capabilityStatement.ts +++ /dev/null @@ -1,296 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type CapabilityStatementDocument, - type CapabilityStatementImplementation, - type CapabilityStatementMessaging, - type CapabilityStatementMessagingEndpoint, - type CapabilityStatementMessagingSupportedMessage, - type CapabilityStatementRest, - type CapabilityStatementRestInteraction, - type CapabilityStatementRestResource, - type CapabilityStatementRestResourceInteraction, - type CapabilityStatementRestResourceOperation, - type CapabilityStatementRestResourceSearchParam, - type CapabilityStatementRestSecurity, - type CapabilityStatementSoftware, - type CapabilityStatement, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - codingSchema, - contactDetailSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - referenceSchema, - stringSchema, - unsignedIntSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - capabilityStatementKindSchema, - conditionalDeleteStatusSchema, - conditionalReadStatusSchema, - documentModeSchema, - eventCapabilityModeSchema, - fhirResourceTypeSchema, - fhirVersionSchema, - publicationStatusSchema, - referencePolicySchema, - resourceVersionPolicySchema, - restfulCapabilityModeSchema, - systemRestfulInteractionSchema, - typeRestfulInteractionSchema, - searchParameterTypeSchema, -} from '../valueSets/index.js' - -const capabilityStatementSoftwareSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - releaseDate: dateTimeSchema.optional(), - _releaseDate: elementSchema.optional(), - }) - -const capabilityStatementImplementationSchema: ZodType = - backboneElementSchema.extend({ - description: stringSchema, - _description: elementSchema.optional(), - url: urlSchema.optional(), - _url: elementSchema.optional(), - custodian: referenceSchema.optional(), - }) - -const capabilityStatementRestSecuritySchema: ZodType = - backboneElementSchema.extend({ - cors: booleanSchema.optional(), - _cors: elementSchema.optional(), - service: codeableConceptSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - }) - -const capabilityStatementRestResourceInteractionSchema: ZodType = - backboneElementSchema.extend({ - code: typeRestfulInteractionSchema, - _code: elementSchema.optional(), - documentation: stringSchema.optional(), - _documentation: elementSchema.optional(), - }) - -const capabilityStatementRestResourceSearchParamSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - definition: urlSchema.optional(), - _definition: elementSchema.optional(), - type: searchParameterTypeSchema, - _type: elementSchema.optional(), - documentation: stringSchema.optional(), - _documentation: elementSchema.optional(), - }) - -const capabilityStatementRestResourceOperationSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - definition: urlSchema, - _definition: elementSchema.optional(), - documentation: stringSchema.optional(), - _documentation: elementSchema.optional(), - }) - -const capabilityStatementRestResourceSchema: ZodType = - backboneElementSchema.extend({ - type: fhirResourceTypeSchema, - _type: elementSchema.optional(), - profile: urlSchema.optional(), - _profile: elementSchema.optional(), - supportedProfile: urlSchema.array().optional(), - _supportedProfile: elementSchema.array().optional(), - documentation: stringSchema.optional(), - _documentation: elementSchema.optional(), - interaction: capabilityStatementRestResourceInteractionSchema - .array() - .optional(), - versioning: resourceVersionPolicySchema.optional(), - _versioning: elementSchema.optional(), - readHistory: booleanSchema.optional(), - _readHistory: elementSchema.optional(), - updateCreate: booleanSchema.optional(), - _updateCreate: elementSchema.optional(), - conditionalCreate: booleanSchema.optional(), - _conditionalCreate: elementSchema.optional(), - conditionalRead: conditionalReadStatusSchema.optional(), - _conditionalRead: elementSchema.optional(), - conditionalUpdate: booleanSchema.optional(), - _conditionalUpdate: elementSchema.optional(), - conditionalDelete: conditionalDeleteStatusSchema.optional(), - _conditionalDelete: elementSchema.optional(), - referencePolicy: referencePolicySchema.array().optional(), - _referencePolicy: elementSchema.array().optional(), - searchInclude: stringSchema.array().optional(), - _searchInclude: elementSchema.array().optional(), - searchRevInclude: stringSchema.array().optional(), - _searchRevInclude: elementSchema.array().optional(), - searchParam: capabilityStatementRestResourceSearchParamSchema - .array() - .optional(), - operation: capabilityStatementRestResourceOperationSchema - .array() - .optional(), - }) - -const capabilityStatementRestInteractionSchema: ZodType = - backboneElementSchema.extend({ - code: systemRestfulInteractionSchema, - _code: elementSchema.optional(), - documentation: stringSchema.optional(), - _documentation: elementSchema.optional(), - }) - -const capabilityStatementRestSchema: ZodType = - backboneElementSchema.extend({ - mode: restfulCapabilityModeSchema, - _mode: elementSchema.optional(), - documentation: stringSchema.optional(), - _documentation: elementSchema.optional(), - security: capabilityStatementRestSecuritySchema.optional(), - resource: capabilityStatementRestResourceSchema.array().optional(), - interaction: capabilityStatementRestInteractionSchema.array().optional(), - searchParam: capabilityStatementRestResourceSearchParamSchema - .array() - .optional(), - operation: capabilityStatementRestResourceOperationSchema - .array() - .optional(), - compartment: urlSchema.array().optional(), - _compartment: elementSchema.array().optional(), - }) - -const capabilityStatementMessagingEndpointSchema: ZodType = - backboneElementSchema.extend({ - protocol: codingSchema, - address: urlSchema, - _address: elementSchema.optional(), - }) - -const capabilityStatementMessagingSupportedMessageSchema: ZodType = - backboneElementSchema.extend({ - mode: eventCapabilityModeSchema, - _mode: elementSchema.optional(), - definition: urlSchema, - _definition: elementSchema.optional(), - }) - -const capabilityStatementMessagingSchema: ZodType = - backboneElementSchema.extend({ - endpoint: capabilityStatementMessagingEndpointSchema.array().optional(), - reliableCache: unsignedIntSchema.optional(), - documentation: stringSchema.optional(), - _documentation: elementSchema.optional(), - supportedMessage: capabilityStatementMessagingSupportedMessageSchema - .array() - .optional(), - }) - -const capabilityStatementDocumentSchema: ZodType = - backboneElementSchema.extend({ - mode: documentModeSchema, - _mode: elementSchema.optional(), - documentation: stringSchema.optional(), - _documentation: elementSchema.optional(), - profile: urlSchema, - _profile: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR CapabilityStatement resource (untyped version). - */ -export const untypedCapabilityStatementSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('CapabilityStatement').readonly(), - url: urlSchema.optional(), - _url: elementSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - date: dateTimeSchema, - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - purpose: stringSchema.optional(), - _purpose: elementSchema.optional(), - copyright: stringSchema.optional(), - _copyright: elementSchema.optional(), - kind: capabilityStatementKindSchema, - _kind: elementSchema.optional(), - instantiates: urlSchema.array().optional(), - _instantiates: elementSchema.array().optional(), - imports: urlSchema.array().optional(), - _imports: elementSchema.array().optional(), - software: capabilityStatementSoftwareSchema.optional(), - implementation: capabilityStatementImplementationSchema.optional(), - fhirVersion: fhirVersionSchema, - _fhirVersion: elementSchema.optional(), - format: stringSchema.array(), - _format: elementSchema.array().optional(), - patchFormat: stringSchema.array().optional(), - _patchFormat: elementSchema.array().optional(), - implementationGuide: urlSchema.array().optional(), - _implementationGuide: elementSchema.array().optional(), - rest: capabilityStatementRestSchema.array().optional(), - messaging: capabilityStatementMessagingSchema.array().optional(), - document: capabilityStatementDocumentSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR CapabilityStatement resource. - */ -export const capabilityStatementSchema: ZodType = - untypedCapabilityStatementSchema - -/** - * Wrapper class for FHIR CapabilityStatement resources. - * Provides utility methods for working with capability statements that describe system capabilities. - */ -export class FhirCapabilityStatement extends FhirDomainResource { - // Static Functions - - /** - * Parses a CapabilityStatement resource from unknown data. - * - * @param value - The data to parse and validate against the CapabilityStatement schema - * @returns A FhirCapabilityStatement instance containing the validated resource - */ - public static parse(value: unknown): FhirCapabilityStatement { - return new FhirCapabilityStatement(capabilityStatementSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/carePlan.ts b/packages/fhir/src/resources/carePlan.ts deleted file mode 100644 index 648e5cf3..00000000 --- a/packages/fhir/src/resources/carePlan.ts +++ /dev/null @@ -1,244 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type CarePlan, - type CarePlanActivity, - type CarePlanActivityDetail, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - booleanSchema, - canonicalSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - quantitySchema, - referenceSchema, - stringSchema, - timingSchema, - uriSchema, -} from '../elements/index.js' -import { - carePlanActivityKindSchema, - carePlanActivityStatusSchema, - carePlanIntentSchema, - carePlanStatusSchema, -} from '../valueSets/index.js' - -const carePlanActivityDetailSchema: ZodType = - backboneElementSchema.extend({ - kind: carePlanActivityKindSchema.optional(), - _kind: elementSchema.optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: elementSchema.array().optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: elementSchema.array().optional(), - code: codeableConceptSchema.optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - goal: referenceSchema.array().optional(), - status: carePlanActivityStatusSchema, - _status: elementSchema.optional(), - statusReason: codeableConceptSchema.optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: elementSchema.optional(), - scheduledTiming: timingSchema.optional(), - scheduledPeriod: periodSchema.optional(), - scheduledString: stringSchema.optional(), - _scheduledString: elementSchema.optional(), - location: referenceSchema.optional(), - performer: referenceSchema.array().optional(), - productCodeableConcept: codeableConceptSchema.optional(), - productReference: referenceSchema.optional(), - dailyAmount: quantitySchema.optional(), - quantity: quantitySchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - }) - -const carePlanActivitySchema: ZodType = - backboneElementSchema.extend({ - outcomeCodeableConcept: codeableConceptSchema.array().optional(), - outcomeReference: referenceSchema.array().optional(), - progress: annotationSchema.array().optional(), - reference: referenceSchema.optional(), - detail: carePlanActivityDetailSchema.optional(), - }) - -/** - * Zod schema for FHIR CarePlan resource (untyped version). - */ -export const untypedCarePlanSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('CarePlan').readonly(), - identifier: identifierSchema.array().optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: elementSchema.array().optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: elementSchema.array().optional(), - basedOn: referenceSchema.array().optional(), - replaces: referenceSchema.array().optional(), - partOf: referenceSchema.array().optional(), - status: carePlanStatusSchema, - _status: elementSchema.optional(), - intent: carePlanIntentSchema, - _intent: elementSchema.optional(), - category: codeableConceptSchema.array().optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - subject: referenceSchema, - encounter: referenceSchema.optional(), - period: periodSchema.optional(), - created: dateTimeSchema.optional(), - _created: elementSchema.optional(), - author: referenceSchema.optional(), - contributor: referenceSchema.array().optional(), - careTeam: referenceSchema.array().optional(), - addresses: referenceSchema.array().optional(), - supportingInfo: referenceSchema.array().optional(), - goal: referenceSchema.array().optional(), - activity: carePlanActivitySchema.array().optional(), - note: annotationSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR CarePlan resource. - */ -export const carePlanSchema: ZodType = untypedCarePlanSchema - -/** - * Wrapper class for FHIR CarePlan resources. - * Provides utility methods for working with care plans. - */ -export class FhirCarePlan extends FhirDomainResource { - // Static Functions - - /** - * Parses a CarePlan resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirCarePlan instance - */ - public static parse(value: unknown): FhirCarePlan { - return new FhirCarePlan(carePlanSchema.parse(value)) - } - - // Properties - - /** - * Gets the created date as a JavaScript Date object. - * - * @returns The created date if available - */ - public get createdDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.created) - } - - /** - * Gets the period start date. - * - * @returns The start date if available - */ - public get periodStart(): Date | undefined { - return FhirDomainResource.parseDate(this.value.period?.start) - } - - /** - * Gets the period end date. - * - * @returns The end date if available - */ - public get periodEnd(): Date | undefined { - return FhirDomainResource.parseDate(this.value.period?.end) - } - - /** - * Checks if the care plan period is currently active. - * - * @param asOfDate - Date to check against (defaults to now) - * @returns true if the period is active - */ - public periodIsActive(asOfDate: Date = new Date()): boolean { - return FhirDomainResource.periodIsActive(this.value.period, asOfDate) - } - - /** - * Gets all category displays. - * - * @returns Array of category display texts - */ - public get categoryDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.category) - } - - /** - * Gets note texts from the care plan. - * - * @returns Array of note texts - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/careTeam.ts b/packages/fhir/src/resources/careTeam.ts deleted file mode 100644 index c6793e5f..00000000 --- a/packages/fhir/src/resources/careTeam.ts +++ /dev/null @@ -1,154 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type CareTeam, - type CareTeamParticipant, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - codeableConceptSchema, - contactPointSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { careTeamStatusSchema } from '../valueSets/index.js' - -const careTeamParticipantSchema: ZodType = - backboneElementSchema.extend({ - role: codeableConceptSchema.array().optional(), - member: referenceSchema.optional(), - onBehalfOf: referenceSchema.optional(), - period: periodSchema.optional(), - }) - -/** - * Zod schema for FHIR CareTeam resource (untyped version). - */ -export const untypedCareTeamSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('CareTeam').readonly(), - identifier: identifierSchema.array().optional(), - status: careTeamStatusSchema.optional(), - _status: elementSchema.optional(), - category: codeableConceptSchema.array().optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - subject: referenceSchema.optional(), - encounter: referenceSchema.optional(), - period: periodSchema.optional(), - participant: careTeamParticipantSchema.array().optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - managingOrganization: referenceSchema.array().optional(), - telecom: contactPointSchema.array().optional(), - note: annotationSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR CareTeam resource. - */ -export const careTeamSchema: ZodType = untypedCareTeamSchema - -/** - * Wrapper class for FHIR CareTeam resources. - * Provides utility methods for working with care team information. - */ -export class FhirCareTeam extends FhirDomainResource { - /** - * Parses a CareTeam resource from unknown data. - * - * @param value - The data to parse and validate against the CareTeam schema - * @returns A FhirCareTeam instance containing the validated resource - */ - public static parse(value: unknown): FhirCareTeam { - return new FhirCareTeam(careTeamSchema.parse(value)) - } - - /** - * Gets the care team period start date as a JavaScript Date object. - * - * @returns The period start date, if available - */ - public get periodStart(): Date | undefined { - return FhirDomainResource.parseDate(this.value.period?.start) - } - - /** - * Gets the care team period end date as a JavaScript Date object. - * - * @returns The period end date, if available - */ - public get periodEnd(): Date | undefined { - return FhirDomainResource.parseDate(this.value.period?.end) - } - - /** - * Gets human-readable display strings for all category CodeableConcepts. - * - * @returns Array of category display texts - */ - public get categoryDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.category) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/catalogEntry.ts b/packages/fhir/src/resources/catalogEntry.ts deleted file mode 100644 index 9c60437f..00000000 --- a/packages/fhir/src/resources/catalogEntry.ts +++ /dev/null @@ -1,133 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type CatalogEntry, - type CatalogEntryRelatedEntry, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - referenceSchema, -} from '../elements/index.js' -import { - catalogEntryRelationtypeSchema, - catalogEntryStatusSchema, -} from '../valueSets/index.js' - -const catalogEntryRelatedEntrySchema: ZodType = - backboneElementSchema.extend({ - relationtype: catalogEntryRelationtypeSchema, - _relationtype: elementSchema.optional(), - item: referenceSchema, - }) - -/** - * Zod schema for FHIR CatalogEntry resource (untyped version). - */ -export const untypedCatalogEntrySchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('CatalogEntry').readonly(), - identifier: identifierSchema.array().optional(), - type: codeableConceptSchema.optional(), - orderable: booleanSchema, - _orderable: elementSchema.optional(), - referencedItem: referenceSchema, - additionalIdentifier: identifierSchema.array().optional(), - classification: codeableConceptSchema.array().optional(), - status: catalogEntryStatusSchema.optional(), - _status: elementSchema.optional(), - validityPeriod: periodSchema.optional(), - validTo: dateTimeSchema.optional(), - _validTo: elementSchema.optional(), - lastUpdated: dateTimeSchema.optional(), - _lastUpdated: elementSchema.optional(), - additionalCharacteristic: codeableConceptSchema.array().optional(), - additionalClassification: codeableConceptSchema.array().optional(), - relatedEntry: catalogEntryRelatedEntrySchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR CatalogEntry resource. - */ -export const catalogEntrySchema: ZodType = - untypedCatalogEntrySchema - -/** - * Wrapper class for FHIR CatalogEntry resources. - * Provides utility methods for working with catalog entries. - */ -export class FhirCatalogEntry extends FhirDomainResource { - // Static Functions - - /** - * Parses a CatalogEntry resource from unknown data. - * - * @param value - The data to parse and validate against the CatalogEntry schema - * @returns A FhirCatalogEntry instance containing the validated resource - */ - public static parse(value: unknown): FhirCatalogEntry { - return new FhirCatalogEntry(catalogEntrySchema.parse(value)) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/chargeItem.ts b/packages/fhir/src/resources/chargeItem.ts deleted file mode 100644 index 4f1cdaad..00000000 --- a/packages/fhir/src/resources/chargeItem.ts +++ /dev/null @@ -1,153 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ChargeItemPerformer, - type ChargeItem, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - backboneElementSchema, - elementSchema, - codeableConceptSchema, - periodSchema, - referenceSchema, - stringSchema, - quantitySchema, - moneySchema, - annotationSchema, - timingSchema, - decimalSchema, - dateTimeSchema, - canonicalSchema, - uriSchema, -} from '../elements/index.js' -import { chargeItemStatusSchema } from '../valueSets/index.js' - -const chargeItemPerformerSchema: ZodType = - backboneElementSchema.extend({ - function: codeableConceptSchema.optional(), - actor: referenceSchema, - }) - -/** - * Zod schema for FHIR ChargeItem resource (untyped version). - */ -export const untypedChargeItemSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ChargeItem').readonly(), - identifier: identifierSchema.array().optional(), - definitionUri: uriSchema.array().optional(), - _definitionUri: elementSchema.array().optional(), - definitionCanonical: canonicalSchema.array().optional(), - _definitionCanonical: elementSchema.array().optional(), - status: chargeItemStatusSchema, - _status: elementSchema.optional(), - partOf: referenceSchema.array().optional(), - code: codeableConceptSchema, - subject: referenceSchema, - context: referenceSchema.optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: elementSchema.optional(), - occurrencePeriod: periodSchema.optional(), - occurrenceTiming: timingSchema.optional(), - performer: chargeItemPerformerSchema.array().optional(), - performingOrganization: referenceSchema.optional(), - requestingOrganization: referenceSchema.optional(), - costCenter: referenceSchema.optional(), - quantity: quantitySchema.optional(), - bodysite: codeableConceptSchema.array().optional(), - factorOverride: decimalSchema.optional(), - priceOverride: moneySchema.optional(), - overrideReason: stringSchema.optional(), - _overrideReason: elementSchema.optional(), - enterer: referenceSchema.optional(), - enteredDate: dateTimeSchema.optional(), - _enteredDate: elementSchema.optional(), - reason: codeableConceptSchema.array().optional(), - service: referenceSchema.array().optional(), - productReference: referenceSchema.optional(), - productCodeableConcept: codeableConceptSchema.optional(), - account: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - supportingInformation: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ChargeItem resource. - */ -export const chargeItemSchema: ZodType = untypedChargeItemSchema - -/** - * Wrapper class for FHIR ChargeItem resources. - * Provides utility methods for working with billing/charge items. - */ -export class FhirChargeItem extends FhirDomainResource { - // Static Functions - - /** - * Parses a ChargeItem resource from unknown data. - * - * @param value - The data to parse and validate against the ChargeItem schema - * @returns A FhirChargeItem instance containing the validated resource - */ - public static parse(value: unknown): FhirChargeItem { - return new FhirChargeItem(chargeItemSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/chargeItemDefinition.ts b/packages/fhir/src/resources/chargeItemDefinition.ts deleted file mode 100644 index 727d0a42..00000000 --- a/packages/fhir/src/resources/chargeItemDefinition.ts +++ /dev/null @@ -1,182 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ChargeItemDefinitionApplicability, - type ChargeItemDefinitionPropertyGroup, - type ChargeItemDefinitionPropertyGroupPriceComponent, - type ChargeItemDefinition, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - backboneElementSchema, - elementSchema, - codeableConceptSchema, - periodSchema, - referenceSchema, - stringSchema, - booleanSchema, - dateSchema, - uriSchema, - canonicalSchema, - moneySchema, - decimalSchema, - usageContextSchema, - contactDetailSchema, -} from '../elements/index.js' -import { - chargeItemDefinitionStatusSchema, - priceComponentTypeSchema, -} from '../valueSets/index.js' - -const chargeItemDefinitionApplicabilitySchema: ZodType = - backboneElementSchema.extend({ - description: stringSchema.optional(), - _description: elementSchema.optional(), - language: stringSchema.optional(), - _language: elementSchema.optional(), - expression: stringSchema.optional(), - _expression: elementSchema.optional(), - }) - -const chargeItemDefinitionPropertyGroupPriceComponentSchema: ZodType = - backboneElementSchema.extend({ - type: priceComponentTypeSchema, - _type: elementSchema.optional(), - code: codeableConceptSchema.optional(), - factor: decimalSchema.optional(), - amount: moneySchema.optional(), - }) - -const chargeItemDefinitionPropertyGroupSchema: ZodType = - backboneElementSchema.extend({ - applicability: chargeItemDefinitionApplicabilitySchema.array().optional(), - priceComponent: chargeItemDefinitionPropertyGroupPriceComponentSchema - .array() - .optional(), - }) - -/** - * Zod schema for FHIR ChargeItemDefinition resource (untyped version). - */ -export const untypedChargeItemDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ChargeItemDefinition').readonly(), - url: uriSchema, - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - derivedFromUri: uriSchema.array().optional(), - _derivedFromUri: elementSchema.array().optional(), - partOf: canonicalSchema.array().optional(), - _partOf: elementSchema.array().optional(), - replaces: canonicalSchema.array().optional(), - _replaces: elementSchema.array().optional(), - status: chargeItemDefinitionStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - date: dateSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - copyright: stringSchema.optional(), - _copyright: elementSchema.optional(), - approvalDate: dateSchema.optional(), - _approvalDate: elementSchema.optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - code: codeableConceptSchema.optional(), - instance: referenceSchema.array().optional(), - applicability: chargeItemDefinitionApplicabilitySchema.array().optional(), - propertyGroup: chargeItemDefinitionPropertyGroupSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ChargeItemDefinition resource. - */ -export const chargeItemDefinitionSchema: ZodType = - untypedChargeItemDefinitionSchema - -/** - * Wrapper class for FHIR ChargeItemDefinition resources. - * Provides utility methods for working with charge item definitions and pricing rules. - */ -export class FhirChargeItemDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses a ChargeItemDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the ChargeItemDefinition schema - * @returns A FhirChargeItemDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirChargeItemDefinition { - return new FhirChargeItemDefinition(chargeItemDefinitionSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/citation.ts b/packages/fhir/src/resources/citation.ts deleted file mode 100644 index 18ad46de..00000000 --- a/packages/fhir/src/resources/citation.ts +++ /dev/null @@ -1,396 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type CitationCitedArtifact, - type CitationCitedArtifactAbstract, - type CitationCitedArtifactContributorship, - type CitationCitedArtifactContributorshipEntry, - type CitationCitedArtifactContributorshipEntryAffiliationInfo, - type CitationCitedArtifactContributorshipEntryContributionInstance, - type CitationCitedArtifactContributorshipSummary, - type CitationCitedArtifactPart, - type CitationCitedArtifactPublicationForm, - type CitationCitedArtifactPublicationFormPeriodicRelease, - type CitationCitedArtifactPublicationFormPeriodicReleaseDateOfPublication, - type CitationCitedArtifactPublicationFormPublishedIn, - type CitationCitedArtifactTitle, - type CitationCitedArtifactVersion, - type CitationCitedArtifactWebLocation, - type CitationClassification, - type CitationRelatesTo, - type CitationStatusDate, - type CitationSummary, - type Citation, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - addressSchema, - annotationSchema, - attachmentSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactDetailSchema, - contactPointSchema, - dateSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - humanNameSchema, - identifierSchema, - markdownSchema, - periodSchema, - positiveIntSchema, - referenceSchema, - stringSchema, - uriSchema, - usageContextSchema, -} from '../elements/index.js' -import { publicationStatusSchema } from '../valueSets/index.js' - -// Define Citation sub-schemas using getter properties for self-referencing -const citationSummarySchema: ZodType = - backboneElementSchema.extend({ - style: codeableConceptSchema.optional(), - text: markdownSchema, - _text: elementSchema.optional(), - }) - -const citationClassificationSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - classifier: codeableConceptSchema.array().optional(), - }) - -const citationStatusDateSchema: ZodType = - backboneElementSchema.extend({ - activity: codeableConceptSchema, - actual: booleanSchema.optional(), - _actual: elementSchema.optional(), - period: periodSchema, - }) - -const citationRelatesToSchema: ZodType = - backboneElementSchema.extend({ - relationshipType: codeableConceptSchema, - targetClassifier: codeableConceptSchema.array().optional(), - targetUri: uriSchema.optional(), - _targetUri: elementSchema.optional(), - targetIdentifier: identifierSchema.optional(), - targetReference: referenceSchema.optional(), - targetAttachment: attachmentSchema.optional(), - }) - -const citationCitedArtifactVersionSchema: ZodType = - backboneElementSchema.extend({ - value: stringSchema, - _value: elementSchema.optional(), - baseCitation: referenceSchema.optional(), - }) - -const citationCitedArtifactTitleSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.array().optional(), - language: codeableConceptSchema.optional(), - text: markdownSchema, - _text: elementSchema.optional(), - }) - -const citationCitedArtifactAbstractSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - language: codeableConceptSchema.optional(), - text: markdownSchema, - _text: elementSchema.optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - }) - -const citationCitedArtifactPartSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - value: stringSchema.optional(), - _value: elementSchema.optional(), - baseCitation: referenceSchema.optional(), - }) - -const citationCitedArtifactPublicationFormPublishedInSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - identifier: identifierSchema.array().optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - publisher: referenceSchema.optional(), - publisherLocation: stringSchema.optional(), - _publisherLocation: elementSchema.optional(), - }) - -const citationCitedArtifactPublicationFormPeriodicReleaseDateOfPublicationSchema: ZodType = - backboneElementSchema.extend({ - date: dateSchema.optional(), - _date: elementSchema.optional(), - year: stringSchema.optional(), - _year: elementSchema.optional(), - month: stringSchema.optional(), - _month: elementSchema.optional(), - day: stringSchema.optional(), - _day: elementSchema.optional(), - season: stringSchema.optional(), - _season: elementSchema.optional(), - text: stringSchema.optional(), - _text: elementSchema.optional(), - }) - -const citationCitedArtifactPublicationFormPeriodicReleaseSchema: ZodType = - backboneElementSchema.extend({ - citedMedium: codeableConceptSchema.optional(), - volume: stringSchema.optional(), - _volume: elementSchema.optional(), - issue: stringSchema.optional(), - _issue: elementSchema.optional(), - dateOfPublication: - citationCitedArtifactPublicationFormPeriodicReleaseDateOfPublicationSchema.optional(), - }) - -const citationCitedArtifactPublicationFormSchema: ZodType = - backboneElementSchema.extend({ - publishedIn: - citationCitedArtifactPublicationFormPublishedInSchema.optional(), - periodicRelease: - citationCitedArtifactPublicationFormPeriodicReleaseSchema.optional(), - articleDate: dateTimeSchema.optional(), - _articleDate: elementSchema.optional(), - lastRevisionDate: dateTimeSchema.optional(), - _lastRevisionDate: elementSchema.optional(), - language: codeableConceptSchema.array().optional(), - accessionNumber: stringSchema.optional(), - _accessionNumber: elementSchema.optional(), - pageString: stringSchema.optional(), - _pageString: elementSchema.optional(), - firstPage: stringSchema.optional(), - _firstPage: elementSchema.optional(), - lastPage: stringSchema.optional(), - _lastPage: elementSchema.optional(), - pageCount: stringSchema.optional(), - _pageCount: elementSchema.optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - }) - -const citationCitedArtifactWebLocationSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - url: uriSchema.optional(), - _url: elementSchema.optional(), - }) - -const citationCitedArtifactContributorshipEntryAffiliationInfoSchema: ZodType = - backboneElementSchema.extend({ - affiliation: stringSchema.optional(), - _affiliation: elementSchema.optional(), - role: stringSchema.optional(), - _role: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - }) - -const citationCitedArtifactContributorshipEntryContributionInstance: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - time: dateTimeSchema.optional(), - _time: elementSchema.optional(), - }) - -const citationCitedArtifactContributorshipEntrySchema: ZodType = - backboneElementSchema.extend({ - name: humanNameSchema.optional(), - initials: stringSchema.optional(), - _initials: elementSchema.optional(), - collectiveName: stringSchema.optional(), - _collectiveName: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - affiliationInfo: - citationCitedArtifactContributorshipEntryAffiliationInfoSchema - .array() - .optional(), - address: addressSchema.array().optional(), - telecom: contactPointSchema.array().optional(), - contributionType: codeableConceptSchema.array().optional(), - role: codeableConceptSchema.optional(), - contributionInstance: - citationCitedArtifactContributorshipEntryContributionInstance - .array() - .optional(), - correspondingContact: booleanSchema.optional(), - _correspondingContact: elementSchema.optional(), - listOrder: positiveIntSchema.optional(), - _listOrder: elementSchema.optional(), - }) - -const citationCitedArtifactContributorshipSummarySchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - style: codeableConceptSchema.optional(), - source: codeableConceptSchema.optional(), - value: markdownSchema, - _value: elementSchema.optional(), - }) - -const citationCitedArtifactContributorshipSchema: ZodType = - backboneElementSchema.extend({ - complete: booleanSchema.optional(), - _complete: elementSchema.optional(), - entry: citationCitedArtifactContributorshipEntrySchema.array().optional(), - summary: citationCitedArtifactContributorshipSummarySchema - .array() - .optional(), - }) - -const citationCitedArtifactSchema: ZodType = - backboneElementSchema.extend({ - identifier: identifierSchema.array().optional(), - relatedIdentifier: identifierSchema.array().optional(), - dateAccessed: dateTimeSchema.optional(), - _dateAccessed: elementSchema.optional(), - version: citationCitedArtifactVersionSchema.optional(), - currentState: codeableConceptSchema.array().optional(), - statusDate: citationStatusDateSchema.array().optional(), - title: citationCitedArtifactTitleSchema.array().optional(), - abstract: citationCitedArtifactAbstractSchema.array().optional(), - part: citationCitedArtifactPartSchema.optional(), - relatesTo: citationRelatesToSchema.array().optional(), - publicationForm: citationCitedArtifactPublicationFormSchema - .array() - .optional(), - webLocation: citationCitedArtifactWebLocationSchema.array().optional(), - classification: citationClassificationSchema.array().optional(), - contributorship: citationCitedArtifactContributorshipSchema.optional(), - note: annotationSchema.array().optional(), - }) - -/** - * Zod schema for FHIR Citation resource (untyped version). - */ -export const untypedCitationSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Citation').readonly(), - url: uriSchema.optional(), - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - purpose: markdownSchema.optional(), - _purpose: elementSchema.optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - approvalDate: dateSchema.optional(), - _approvalDate: elementSchema.optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - author: contactDetailSchema.array().optional(), - editor: contactDetailSchema.array().optional(), - reviewer: contactDetailSchema.array().optional(), - endorser: contactDetailSchema.array().optional(), - summary: citationSummarySchema.array().optional(), - classification: citationClassificationSchema.array().optional(), - note: annotationSchema.array().optional(), - currentState: codeableConceptSchema.array().optional(), - statusDate: citationStatusDateSchema.array().optional(), - relatesTo: citationRelatesToSchema.array().optional(), - citedArtifact: citationCitedArtifactSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Citation resource. - */ -export const citationSchema: ZodType = untypedCitationSchema - -/** - * Wrapper class for FHIR Citation resources. - * Provides utility methods for working with bibliographic citations and references. - */ -export class FhirCitation extends FhirDomainResource { - // Static Functions - - /** - * Parses a Citation resource from unknown data. - * - * @param value - The data to parse and validate against the Citation schema - * @returns A FhirCitation instance containing the validated resource - */ - public static parse(value: unknown): FhirCitation { - return new FhirCitation(citationSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/claim.ts b/packages/fhir/src/resources/claim.ts deleted file mode 100644 index b654c8a9..00000000 --- a/packages/fhir/src/resources/claim.ts +++ /dev/null @@ -1,311 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ClaimAccident, - type ClaimCareTeam, - type ClaimDiagnosis, - type ClaimInsurance, - type ClaimItem, - type ClaimItemDetail, - type ClaimPayee, - type ClaimProcedure, - type ClaimRelated, - type ClaimSupportingInfo, - type Claim, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - backboneElementSchema, - elementSchema, - codeableConceptSchema, - referenceSchema, - stringSchema, - periodSchema, - dateTimeSchema, - moneySchema, - quantitySchema, - decimalSchema, - addressSchema, - booleanSchema, - positiveIntSchema, - dateSchema, - attachmentSchema, -} from '../elements/index.js' -import { - financialResourceStatusSchema, - claimUseSchema, -} from '../valueSets/index.js' - -const claimRelatedSchema: ZodType = backboneElementSchema.extend({ - claim: referenceSchema.optional(), - relationship: codeableConceptSchema.optional(), - reference: identifierSchema.optional(), -}) - -const claimPayeeSchema: ZodType = backboneElementSchema.extend({ - type: codeableConceptSchema, - party: referenceSchema.optional(), -}) - -const claimCareTeamSchema: ZodType = - backboneElementSchema.extend({ - sequence: positiveIntSchema, - provider: referenceSchema, - responsible: booleanSchema.optional(), - _responsible: elementSchema.optional(), - role: codeableConceptSchema.optional(), - qualification: codeableConceptSchema.optional(), - }) - -const claimSupportingInfoSchema: ZodType = - backboneElementSchema.extend({ - sequence: positiveIntSchema, - category: codeableConceptSchema, - code: codeableConceptSchema.optional(), - timingDate: dateSchema.optional(), - _timingDate: elementSchema.optional(), - timingPeriod: periodSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueAttachment: attachmentSchema.optional(), - valueReference: referenceSchema.optional(), - reason: codeableConceptSchema.optional(), - }) - -const claimDiagnosisSchema: ZodType = - backboneElementSchema.extend({ - sequence: positiveIntSchema, - diagnosisCodeableConcept: codeableConceptSchema.optional(), - diagnosisReference: referenceSchema.optional(), - type: codeableConceptSchema.array().optional(), - onAdmission: codeableConceptSchema.optional(), - packageCode: codeableConceptSchema.optional(), - }) - -const claimProcedureSchema: ZodType = - backboneElementSchema.extend({ - sequence: positiveIntSchema, - type: codeableConceptSchema.array().optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - procedureCodeableConcept: codeableConceptSchema.optional(), - procedureReference: referenceSchema.optional(), - udi: referenceSchema.array().optional(), - }) - -const claimInsuranceSchema: ZodType = - backboneElementSchema.extend({ - sequence: positiveIntSchema, - focal: booleanSchema, - _focal: elementSchema.optional(), - identifier: identifierSchema.optional(), - coverage: referenceSchema, - businessArrangement: stringSchema.optional(), - _businessArrangement: elementSchema.optional(), - preAuthRef: stringSchema.array().optional(), - _preAuthRef: elementSchema.array().optional(), - claimResponse: referenceSchema.optional(), - }) - -const claimAccidentSchema: ZodType = - backboneElementSchema.extend({ - date: dateSchema, - _date: elementSchema.optional(), - type: codeableConceptSchema.optional(), - locationAddress: addressSchema.optional(), - locationReference: referenceSchema.optional(), - }) - -const claimItemDetailSchema: ZodType = - backboneElementSchema.extend({ - sequence: positiveIntSchema, - revenue: codeableConceptSchema.optional(), - category: codeableConceptSchema.optional(), - productOrService: codeableConceptSchema, - modifier: codeableConceptSchema.array().optional(), - programCode: codeableConceptSchema.array().optional(), - quantity: quantitySchema.optional(), - unitPrice: moneySchema.optional(), - factor: decimalSchema.optional(), - net: moneySchema.optional(), - udi: referenceSchema.array().optional(), - }) - -const claimItemSchema: ZodType = backboneElementSchema.extend({ - sequence: positiveIntSchema, - careTeamSequence: positiveIntSchema.array().optional(), - diagnosisSequence: positiveIntSchema.array().optional(), - procedureSequence: positiveIntSchema.array().optional(), - informationSequence: positiveIntSchema.array().optional(), - revenue: codeableConceptSchema.optional(), - category: codeableConceptSchema.optional(), - productOrService: codeableConceptSchema, - modifier: codeableConceptSchema.array().optional(), - programCode: codeableConceptSchema.array().optional(), - servicedDate: dateSchema.optional(), - _servicedDate: elementSchema.optional(), - servicedPeriod: periodSchema.optional(), - locationCodeableConcept: codeableConceptSchema.optional(), - locationAddress: addressSchema.optional(), - locationReference: referenceSchema.optional(), - quantity: quantitySchema.optional(), - unitPrice: moneySchema.optional(), - factor: decimalSchema.optional(), - net: moneySchema.optional(), - udi: referenceSchema.array().optional(), - bodySite: codeableConceptSchema.optional(), - subSite: codeableConceptSchema.array().optional(), - encounter: referenceSchema.array().optional(), - detail: claimItemDetailSchema.array().optional(), -}) - -/** - * Zod schema for FHIR Claim resource (untyped version). - */ -export const untypedClaimSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Claim').readonly(), - identifier: identifierSchema.array().optional(), - status: financialResourceStatusSchema, - _status: elementSchema.optional(), - type: codeableConceptSchema, - subType: codeableConceptSchema.optional(), - use: claimUseSchema, - _use: elementSchema.optional(), - patient: referenceSchema, - billablePeriod: periodSchema.optional(), - created: dateTimeSchema, - _created: elementSchema.optional(), - enterer: referenceSchema.optional(), - insurer: referenceSchema.optional(), - provider: referenceSchema, - priority: codeableConceptSchema, - fundsReserve: codeableConceptSchema.optional(), - related: claimRelatedSchema.array().optional(), - prescription: referenceSchema.optional(), - originalPrescription: referenceSchema.optional(), - payee: claimPayeeSchema.optional(), - referral: referenceSchema.optional(), - facility: referenceSchema.optional(), - careTeam: claimCareTeamSchema.array().optional(), - supportingInfo: claimSupportingInfoSchema.array().optional(), - diagnosis: claimDiagnosisSchema.array().optional(), - procedure: claimProcedureSchema.array().optional(), - insurance: claimInsuranceSchema.array(), - accident: claimAccidentSchema.optional(), - item: claimItemSchema.array().optional(), - total: moneySchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Claim resource. - */ -export const claimSchema: ZodType = untypedClaimSchema - -/** - * Wrapper class for FHIR Claim resources. - * Provides utility methods for working with billing claims. - */ -export class FhirClaim extends FhirDomainResource { - // Static Functions - - /** - * Parses a Claim resource from unknown data. - * - * @param value - The data to parse and validate against the Claim schema - * @returns A FhirClaim instance containing the validated resource - */ - public static parse(value: unknown): FhirClaim { - return new FhirClaim(claimSchema.parse(value)) - } - - /** - * Gets the created date as a JavaScript Date object. - * - * @returns The created date if available, undefined otherwise - * - * @example - * ```typescript - * const created = claim.createdDate - * console.log(`Claim created: ${created?.toLocaleDateString()}`) - * ``` - */ - public get createdDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.created) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } - - /** - * Gets the claim type display text. - * - * @returns The type display text, or undefined if not available - * - * @example - * ```typescript - * const type = claim.typeDisplay - * console.log(`Claim type: ${type}`) - * ``` - */ - public get typeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.type) - } -} diff --git a/packages/fhir/src/resources/claimResponse.ts b/packages/fhir/src/resources/claimResponse.ts deleted file mode 100644 index 61afe0a3..00000000 --- a/packages/fhir/src/resources/claimResponse.ts +++ /dev/null @@ -1,266 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ClaimResponseItem, - type ClaimResponseItemAdjudication, - type ClaimResponseItemDetail, - type ClaimResponse, - type Coding, - type ClaimResponseAddItem, - type ClaimResponseAddItemDetail, - type ClaimResponseTotal, - type ClaimResponsePayment, - type ClaimResponseProcessNote, - type ClaimResponseInsurance, - type ClaimResponseError, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - backboneElementSchema, - elementSchema, - codeableConceptSchema, - referenceSchema, - stringSchema, - dateTimeSchema, - moneySchema, - decimalSchema, - addressSchema, - periodSchema, - positiveIntSchema, - quantitySchema, - attachmentSchema, - booleanSchema, -} from '../elements/index.js' -import { - financialResourceStatusSchema, - remittanceOutcomeSchema, - claimUseSchema, - noteTypeSchema, -} from '../valueSets/index.js' - -const claimResponseItemAdjudicationSchema: ZodType = - backboneElementSchema.extend({ - category: codeableConceptSchema, - reason: codeableConceptSchema.optional(), - amount: moneySchema.optional(), - value: decimalSchema.optional(), - }) - -const claimResponseItemDetailSchema: ZodType = - backboneElementSchema.extend({ - detailSequence: positiveIntSchema, - noteNumber: positiveIntSchema.array().optional(), - adjudication: claimResponseItemAdjudicationSchema.array(), - }) - -const claimResponseItemSchema: ZodType = - backboneElementSchema.extend({ - itemSequence: positiveIntSchema, - noteNumber: positiveIntSchema.array().optional(), - adjudication: claimResponseItemAdjudicationSchema.array(), - detail: claimResponseItemDetailSchema.array().optional(), - }) - -const claimResponseAddItemDetailSchema: ZodType = - backboneElementSchema.extend({ - productOrService: codeableConceptSchema, - modifier: codeableConceptSchema.array().optional(), - quantity: quantitySchema.optional(), - unitPrice: moneySchema.optional(), - factor: decimalSchema.optional(), - net: moneySchema.optional(), - noteNumber: positiveIntSchema.array().optional(), - adjudication: claimResponseItemAdjudicationSchema.array(), - }) - -const claimResponseAddItemSchema: ZodType = - backboneElementSchema.extend({ - itemSequence: positiveIntSchema.array().optional(), - detailSequence: positiveIntSchema.array().optional(), - subdetailSequence: positiveIntSchema.array().optional(), - provider: referenceSchema.array().optional(), - productOrService: codeableConceptSchema, - modifier: codeableConceptSchema.array().optional(), - programCode: codeableConceptSchema.array().optional(), - servicedDate: z.string().optional(), - _servicedDate: elementSchema.optional(), - servicedPeriod: periodSchema.optional(), - locationCodeableConcept: codeableConceptSchema.optional(), - locationAddress: addressSchema.optional(), - locationReference: referenceSchema.optional(), - quantity: quantitySchema.optional(), - unitPrice: moneySchema.optional(), - factor: decimalSchema.optional(), - net: moneySchema.optional(), - bodySite: codeableConceptSchema.optional(), - subSite: codeableConceptSchema.array().optional(), - noteNumber: positiveIntSchema.array().optional(), - adjudication: claimResponseItemAdjudicationSchema.array(), - detail: claimResponseAddItemDetailSchema.array().optional(), - }) - -const claimResponseTotalSchema: ZodType = - backboneElementSchema.extend({ - category: codeableConceptSchema, - amount: moneySchema, - }) - -const claimResponsePaymentSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - adjustment: moneySchema.optional(), - adjustmentReason: codeableConceptSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - amount: moneySchema, - identifier: identifierSchema.optional(), - }) - -const claimResponseProcessNoteSchema: ZodType = - backboneElementSchema.extend({ - number: positiveIntSchema.optional(), - type: noteTypeSchema.optional(), - _type: elementSchema.optional(), - text: stringSchema, - _text: elementSchema.optional(), - language: codeableConceptSchema.optional(), - }) - -const claimResponseInsuranceSchema: ZodType = - backboneElementSchema.extend({ - sequence: positiveIntSchema, - focal: booleanSchema, - _focal: elementSchema.optional(), - coverage: referenceSchema, - businessArrangement: stringSchema.optional(), - _businessArrangement: elementSchema.optional(), - claimResponse: referenceSchema.optional(), - }) - -const claimResponseErrorSchema: ZodType = - backboneElementSchema.extend({ - itemSequence: positiveIntSchema.optional(), - detailSequence: positiveIntSchema.optional(), - subDetailSequence: positiveIntSchema.optional(), - code: codeableConceptSchema, - }) - -/** - * Zod schema for FHIR ClaimResponse resource (untyped version). - */ -export const untypedClaimResponseSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ClaimResponse').readonly(), - identifier: identifierSchema.array().optional(), - status: financialResourceStatusSchema, - _status: elementSchema.optional(), - type: codeableConceptSchema, - subType: codeableConceptSchema.optional(), - use: claimUseSchema, - _use: elementSchema.optional(), - patient: referenceSchema, - created: dateTimeSchema, - _created: elementSchema.optional(), - insurer: referenceSchema, - requestor: referenceSchema.optional(), - request: referenceSchema.optional(), - outcome: remittanceOutcomeSchema, - _outcome: elementSchema.optional(), - disposition: stringSchema.optional(), - _disposition: elementSchema.optional(), - preAuthRef: stringSchema.optional(), - _preAuthRef: elementSchema.optional(), - preAuthPeriod: periodSchema.optional(), - payeeType: codeableConceptSchema.optional(), - item: claimResponseItemSchema.array().optional(), - addItem: claimResponseAddItemSchema.array().optional(), - total: claimResponseTotalSchema.array().optional(), - payment: claimResponsePaymentSchema.optional(), - fundsReserve: codeableConceptSchema.optional(), - formCode: codeableConceptSchema.optional(), - form: attachmentSchema.optional(), - processNote: claimResponseProcessNoteSchema.array().optional(), - communicationRequest: referenceSchema.array().optional(), - insurance: claimResponseInsuranceSchema.array().optional(), - error: claimResponseErrorSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ClaimResponse resource. - */ -export const claimResponseSchema: ZodType = - untypedClaimResponseSchema - -/** - * Wrapper class for FHIR ClaimResponse resources. - * Provides utility methods for working with claim responses and adjudications. - */ -export class FhirClaimResponse extends FhirDomainResource { - // Static Functions - - /** - * Parses a ClaimResponse resource from unknown data. - * - * @param value - The data to parse and validate against the ClaimResponse schema - * @returns A FhirClaimResponse instance containing the validated resource - */ - public static parse(value: unknown): FhirClaimResponse { - return new FhirClaimResponse(claimResponseSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/clinicalImpression.ts b/packages/fhir/src/resources/clinicalImpression.ts deleted file mode 100644 index 3c8bfe4f..00000000 --- a/packages/fhir/src/resources/clinicalImpression.ts +++ /dev/null @@ -1,174 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type ClinicalImpression, - type ClinicalImpressionFinding, - type ClinicalImpressionInvestigation, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { clinicalImpressionStatusSchema } from '../valueSets/index.js' - -const clinicalImpressionFindingSchema: ZodType = - backboneElementSchema.extend({ - itemCodeableConcept: codeableConceptSchema.optional(), - itemReference: referenceSchema.optional(), - basis: stringSchema.optional(), - _basis: elementSchema.optional(), - }) - -const clinicalImpressionInvestigationSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema, - item: referenceSchema.array().optional(), - }) - -/** - * Zod schema for FHIR ClinicalImpression resource (untyped version). - */ -export const untypedClinicalImpressionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ClinicalImpression').readonly(), - identifier: identifierSchema.array().optional(), - status: clinicalImpressionStatusSchema, - _status: elementSchema.optional(), - statusReason: codeableConceptSchema.optional(), - code: codeableConceptSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - subject: referenceSchema, - encounter: referenceSchema.optional(), - effectiveDateTime: dateTimeSchema.optional(), - _effectiveDateTime: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - assessor: referenceSchema.optional(), - previous: referenceSchema.optional(), - problem: referenceSchema.array().optional(), - investigation: clinicalImpressionInvestigationSchema.array().optional(), - protocol: z.string().array().optional(), - _protocol: elementSchema.array().optional(), - summary: stringSchema.optional(), - _summary: elementSchema.optional(), - finding: clinicalImpressionFindingSchema.array().optional(), - prognosisCodeableConcept: codeableConceptSchema.array().optional(), - prognosisReference: referenceSchema.array().optional(), - supportingInfo: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ClinicalImpression resource. - */ -export const clinicalImpressionSchema: ZodType = - untypedClinicalImpressionSchema - -/** - * Wrapper class for FHIR ClinicalImpression resources. - * Provides utility methods for working with clinical assessments and impressions. - */ -export class FhirClinicalImpression extends FhirDomainResource { - /** - * Parses a ClinicalImpression resource from unknown data. - * - * @param value - The data to parse and validate against the ClinicalImpression schema - * @returns A FhirClinicalImpression instance containing the validated resource - */ - public static parse(value: unknown): FhirClinicalImpression { - return new FhirClinicalImpression(clinicalImpressionSchema.parse(value)) - } - - /** - * Gets the date of the clinical impression as a JavaScript Date object. - * - * @returns The impression date, if available - */ - public get date(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.date) - } - - /** - * Gets the effective date/time as a JavaScript Date object. - * - * @returns The effective date, if available - */ - public get effectiveDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.effectiveDateTime) - } - - /** - * Gets note texts from the impression. - * - * @returns Array of note text strings - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/clinicalUseDefinition.ts b/packages/fhir/src/resources/clinicalUseDefinition.ts deleted file mode 100644 index 6b458db0..00000000 --- a/packages/fhir/src/resources/clinicalUseDefinition.ts +++ /dev/null @@ -1,186 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type ClinicalUseDefinition, - type ClinicalUseDefinitionContraindication, - type ClinicalUseDefinitionIndication, - type ClinicalUseDefinitionInteraction, - type ClinicalUseDefinitionInteractionInteractant, - type ClinicalUseDefinitionUndesirableEffect, - type ClinicalUseDefinitionWarning, - type ClinicalUseDefinitionContraindicationOtherTherapy, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - codeableReferenceSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { clinicalUseDefinitionTypeSchema } from '../valueSets/index.js' - -const clinicalUseDefinitionContraindicationOtherTherapySchema: ZodType = - backboneElementSchema.extend({ - relationshipType: codeableConceptSchema, - therapy: codeableReferenceSchema, - }) - -const clinicalUseDefinitionContraindicationSchema: ZodType = - backboneElementSchema.extend({ - diseaseSymptomProcedure: codeableReferenceSchema.optional(), - diseaseStatus: codeableReferenceSchema.optional(), - comorbidity: codeableReferenceSchema.array().optional(), - indication: referenceSchema.array().optional(), - otherTherapy: clinicalUseDefinitionContraindicationOtherTherapySchema - .array() - .optional(), - }) - -const clinicalUseDefinitionIndicationSchema: ZodType = - backboneElementSchema.extend({ - diseaseSymptomProcedure: codeableReferenceSchema.optional(), - diseaseStatus: codeableReferenceSchema.optional(), - comorbidity: codeableReferenceSchema.array().optional(), - intendedEffect: codeableReferenceSchema.optional(), - duration: elementSchema.optional(), - undesirableEffect: referenceSchema.array().optional(), - otherTherapy: clinicalUseDefinitionContraindicationOtherTherapySchema - .array() - .optional(), - }) - -const clinicalUseDefinitionInteractionInteractantSchema: ZodType = - backboneElementSchema.extend({ - itemReference: referenceSchema.optional(), - itemCodeableConcept: codeableConceptSchema.optional(), - }) - -const clinicalUseDefinitionInteractionSchema: ZodType = - backboneElementSchema.extend({ - interactant: clinicalUseDefinitionInteractionInteractantSchema - .array() - .optional(), - type: codeableConceptSchema.optional(), - effect: codeableReferenceSchema.optional(), - incidence: codeableConceptSchema.optional(), - management: codeableConceptSchema.array().optional(), - }) - -const clinicalUseDefinitionUndesirableEffectSchema: ZodType = - backboneElementSchema.extend({ - symptomConditionEffect: codeableReferenceSchema.optional(), - classification: codeableConceptSchema.optional(), - frequencyOfOccurrence: codeableConceptSchema.optional(), - }) - -const clinicalUseDefinitionWarningSchema: ZodType = - backboneElementSchema.extend({ - description: stringSchema.optional(), - _description: elementSchema.optional(), - code: codeableConceptSchema.optional(), - }) - -/** - * Zod schema for FHIR ClinicalUseDefinition resource (untyped version). - */ -export const untypedClinicalUseDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ClinicalUseDefinition').readonly(), - identifier: identifierSchema.array().optional(), - type: clinicalUseDefinitionTypeSchema, - _type: elementSchema.optional(), - category: codeableConceptSchema.array().optional(), - subject: referenceSchema.array().optional(), - status: codeableConceptSchema.optional(), - contraindication: clinicalUseDefinitionContraindicationSchema.optional(), - indication: clinicalUseDefinitionIndicationSchema.optional(), - interaction: clinicalUseDefinitionInteractionSchema.optional(), - population: referenceSchema.array().optional(), - undesirableEffect: clinicalUseDefinitionUndesirableEffectSchema.optional(), - warning: clinicalUseDefinitionWarningSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ClinicalUseDefinition resource. - */ -export const clinicalUseDefinitionSchema: ZodType = - untypedClinicalUseDefinitionSchema - -/** - * Wrapper class for FHIR ClinicalUseDefinition resources. - * Provides utility methods for working with clinical use definitions including indications, contraindications, and interactions. - */ -export class FhirClinicalUseDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses a ClinicalUseDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the ClinicalUseDefinition schema - * @returns A FhirClinicalUseDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirClinicalUseDefinition { - return new FhirClinicalUseDefinition( - clinicalUseDefinitionSchema.parse(value), - ) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/codeSystem.ts b/packages/fhir/src/resources/codeSystem.ts deleted file mode 100644 index fe7d5149..00000000 --- a/packages/fhir/src/resources/codeSystem.ts +++ /dev/null @@ -1,230 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type CodeSystemConceptDesignation, - type CodeSystemConceptProperty, - type CodeSystemFilter, - type CodeSystemProperty, - type CodeSystem, - type CodeSystemConcept, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - codingSchema, - contactDetailSchema, - dateTimeSchema, - decimalSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - intSchema, - stringSchema, - uriSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - codeSystemContentSchema, - codeSystemHierarchyMeaningSchema, - codeSystemPropertyTypeSchema, - filterOperatorSchema, - publicationStatusSchema, -} from '../valueSets/index.js' - -const codeSystemConceptDesignationSchema: ZodType = - backboneElementSchema.extend({ - language: stringSchema.optional(), - _language: elementSchema.optional(), - use: codingSchema.optional(), - value: stringSchema, - _value: elementSchema.optional(), - }) - -const codeSystemConceptPropertySchema: ZodType = - backboneElementSchema.extend({ - code: stringSchema, - _code: elementSchema.optional(), - valueCode: stringSchema.optional(), - _valueCode: elementSchema.optional(), - valueCoding: codingSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueInteger: intSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: elementSchema.optional(), - valueDecimal: decimalSchema.optional(), - }) - -const codeSystemConceptSchema: ZodType = - backboneElementSchema.extend({ - code: stringSchema, - _code: elementSchema.optional(), - display: stringSchema.optional(), - _display: elementSchema.optional(), - definition: stringSchema.optional(), - _definition: elementSchema.optional(), - designation: codeSystemConceptDesignationSchema.array().optional(), - property: codeSystemConceptPropertySchema.array().optional(), - get concept() { - return codeSystemConceptSchema.array().optional() - }, - }) - -const codeSystemFilterSchema: ZodType = - backboneElementSchema.extend({ - code: stringSchema, - _code: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - operator: filterOperatorSchema.array(), - _operator: elementSchema.array().optional(), - value: stringSchema, - _value: elementSchema.optional(), - }) - -const codeSystemPropertySchema: ZodType = - backboneElementSchema.extend({ - code: stringSchema, - _code: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - type: codeSystemPropertyTypeSchema, - _type: elementSchema.optional(), - uri: uriSchema.optional(), - _uri: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR CodeSystem resource (untyped version). - */ -export const untypedCodeSystemSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('CodeSystem').readonly(), - url: urlSchema.optional(), - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - purpose: stringSchema.optional(), - _purpose: elementSchema.optional(), - copyright: stringSchema.optional(), - _copyright: elementSchema.optional(), - caseSensitive: booleanSchema.optional(), - _caseSensitive: elementSchema.optional(), - valueSet: urlSchema.optional(), - _valueSet: elementSchema.optional(), - hierarchyMeaning: codeSystemHierarchyMeaningSchema.optional(), - _hierarchyMeaning: elementSchema.optional(), - compositional: booleanSchema.optional(), - _compositional: elementSchema.optional(), - versionNeeded: booleanSchema.optional(), - _versionNeeded: elementSchema.optional(), - content: codeSystemContentSchema, - _content: elementSchema.optional(), - supplements: urlSchema.optional(), - _supplements: elementSchema.optional(), - count: intSchema.optional(), - filter: codeSystemFilterSchema.array().optional(), - property: codeSystemPropertySchema.array().optional(), - concept: codeSystemConceptSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR CodeSystem resource. - */ -export const codeSystemSchema: ZodType = untypedCodeSystemSchema - -/** - * Wrapper class for FHIR CodeSystem resources. - * Provides utility methods for working with code systems and terminology definitions. - */ -export class FhirCodeSystem extends FhirDomainResource { - // Static Functions - - /** - * Parses a CodeSystem resource from unknown data. - * - * @param value - The data to parse and validate against the CodeSystem schema - * @returns A FhirCodeSystem instance containing the validated resource - */ - public static parse(value: unknown): FhirCodeSystem { - return new FhirCodeSystem(codeSystemSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/communication.ts b/packages/fhir/src/resources/communication.ts deleted file mode 100644 index 606e5989..00000000 --- a/packages/fhir/src/resources/communication.ts +++ /dev/null @@ -1,172 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type Communication, - type CommunicationPayload, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - attachmentSchema, - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { - communicationStatusSchema, - requestPrioritySchema, -} from '../valueSets/index.js' - -const communicationPayloadSchema: ZodType = - backboneElementSchema.extend({ - contentString: stringSchema.optional(), - _contentString: elementSchema.optional(), - contentAttachment: attachmentSchema.optional(), - contentReference: referenceSchema.optional(), - }) - -/** - * Zod schema for FHIR Communication resource (untyped version). - */ -export const untypedCommunicationSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Communication').readonly(), - identifier: identifierSchema.array().optional(), - instantiatesCanonical: z.string().array().optional(), - _instantiatesCanonical: elementSchema.array().optional(), - instantiatesUri: z.string().array().optional(), - _instantiatesUri: elementSchema.array().optional(), - basedOn: referenceSchema.array().optional(), - partOf: referenceSchema.array().optional(), - inResponseTo: referenceSchema.array().optional(), - status: communicationStatusSchema, - _status: elementSchema.optional(), - statusReason: codeableConceptSchema.optional(), - category: codeableConceptSchema.array().optional(), - priority: requestPrioritySchema.optional(), - _priority: elementSchema.optional(), - medium: codeableConceptSchema.array().optional(), - subject: referenceSchema.optional(), - topic: codeableConceptSchema.optional(), - about: referenceSchema.array().optional(), - encounter: referenceSchema.optional(), - sent: dateTimeSchema.optional(), - _sent: elementSchema.optional(), - received: dateTimeSchema.optional(), - _received: elementSchema.optional(), - recipient: referenceSchema.array().optional(), - sender: referenceSchema.optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - payload: communicationPayloadSchema.array().optional(), - note: annotationSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Communication resource. - */ -export const communicationSchema: ZodType = - untypedCommunicationSchema - -/** - * Wrapper class for FHIR Communication resources. - * Provides utility methods for working with communication events. - */ -export class FhirCommunication extends FhirDomainResource { - /** - * Parses a Communication resource from unknown data. - * - * @param value - The data to parse and validate against the Communication schema - * @returns A FhirCommunication instance containing the validated resource - */ - public static parse(value: unknown): FhirCommunication { - return new FhirCommunication(communicationSchema.parse(value)) - } - - /** - * Gets the received date/time as a JavaScript Date object. - * - * @returns The received date, if available - */ - public get receivedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.received) - } - - /** - * Gets the sent date/time as a JavaScript Date object. - * - * @returns The sent date, if available - */ - public get sentDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.sent) - } - - /** - * Gets note texts from the communication. - * - * @returns Array of note text strings - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/communicationRequest.ts b/packages/fhir/src/resources/communicationRequest.ts deleted file mode 100644 index 3fbfcaef..00000000 --- a/packages/fhir/src/resources/communicationRequest.ts +++ /dev/null @@ -1,164 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type CommunicationRequest, - type CommunicationRequestPayload, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - attachmentSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { - communicationRequestStatusSchema, - requestPrioritySchema, -} from '../valueSets/index.js' - -const communicationRequestPayloadSchema: ZodType = - backboneElementSchema.extend({ - contentString: stringSchema.optional(), - _contentString: elementSchema.optional(), - contentAttachment: attachmentSchema.optional(), - contentReference: referenceSchema.optional(), - }) - -/** - * Zod schema for FHIR CommunicationRequest resource (untyped version). - */ -export const untypedCommunicationRequestSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('CommunicationRequest').readonly(), - identifier: identifierSchema.array().optional(), - basedOn: referenceSchema.array().optional(), - replaces: referenceSchema.array().optional(), - groupIdentifier: identifierSchema.optional(), - status: communicationRequestStatusSchema, - _status: elementSchema.optional(), - statusReason: codeableConceptSchema.optional(), - category: codeableConceptSchema.array().optional(), - priority: requestPrioritySchema.optional(), - _priority: elementSchema.optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: elementSchema.optional(), - medium: codeableConceptSchema.array().optional(), - subject: referenceSchema.optional(), - about: referenceSchema.array().optional(), - encounter: referenceSchema.optional(), - payload: communicationRequestPayloadSchema.array().optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: elementSchema.optional(), - occurrencePeriod: periodSchema.optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: elementSchema.optional(), - requester: referenceSchema.optional(), - recipient: referenceSchema.array().optional(), - sender: referenceSchema.optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR CommunicationRequest resource. - */ -export const communicationRequestSchema: ZodType = - untypedCommunicationRequestSchema - -/** - * Wrapper class for FHIR CommunicationRequest resources. - * Provides utility methods for working with communication requests. - */ -export class FhirCommunicationRequest extends FhirDomainResource { - /** - * Parses a CommunicationRequest resource from unknown data. - * - * @param value - The data to parse and validate against the CommunicationRequest schema - * @returns A FhirCommunicationRequest instance containing the validated resource - */ - public static parse(value: unknown): FhirCommunicationRequest { - return new FhirCommunicationRequest(communicationRequestSchema.parse(value)) - } - - /** - * Gets the authored date as a JavaScript Date object. - * - * @returns The authored date, if available - */ - public get authoredDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.authoredOn) - } - - /** - * Gets note texts from the communication request. - * - * @returns Array of note text strings - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/compartmentDefinition.ts b/packages/fhir/src/resources/compartmentDefinition.ts deleted file mode 100644 index 8da459d8..00000000 --- a/packages/fhir/src/resources/compartmentDefinition.ts +++ /dev/null @@ -1,100 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type CompartmentDefinitionResource, - type CompartmentDefinition, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - contactDetailSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - stringSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - compartmentDefinitionCodeSchema, - fhirResourceTypeSchema, - publicationStatusSchema, -} from '../valueSets/index.js' - -const compartmentDefinitionResourceSchema: ZodType = - backboneElementSchema.extend({ - code: fhirResourceTypeSchema, - _code: elementSchema.optional(), - documentation: stringSchema.optional(), - _documentation: elementSchema.optional(), - param: stringSchema.array().optional(), - _param: elementSchema.array().optional(), - }) - -/** - * Zod schema for FHIR CompartmentDefinition resource (untyped version). - */ -export const untypedCompartmentDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('CompartmentDefinition').readonly(), - code: compartmentDefinitionCodeSchema, - _code: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - name: stringSchema, - _name: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - purpose: stringSchema.optional(), - _purpose: elementSchema.optional(), - resource: compartmentDefinitionResourceSchema.array().optional(), - search: booleanSchema, - _search: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - url: urlSchema, - _url: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR CompartmentDefinition resource. - */ -export const compartmentDefinitionSchema: ZodType = - untypedCompartmentDefinitionSchema - -/** - * Wrapper class for FHIR CompartmentDefinition resources. - * Provides utility methods for working with compartment definitions that organize resources by access contexts. - */ -export class FhirCompartmentDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses a CompartmentDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the CompartmentDefinition schema - * @returns A FhirCompartmentDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirCompartmentDefinition { - return new FhirCompartmentDefinition( - compartmentDefinitionSchema.parse(value), - ) - } -} diff --git a/packages/fhir/src/resources/composition.ts b/packages/fhir/src/resources/composition.ts deleted file mode 100644 index 62b20d09..00000000 --- a/packages/fhir/src/resources/composition.ts +++ /dev/null @@ -1,138 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type CompositionAttester, - type CompositionEvent, - type CompositionRelatesTo, - type CompositionSection, - type Composition, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - narrativeSchema, - periodSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { - compositionAttestationModeSchema, - compositionRelatestoCodeSchema, - compositionStatusSchema, - listModeSchema, -} from '../valueSets/index.js' - -const attesterSchema: ZodType = - backboneElementSchema.extend({ - mode: compositionAttestationModeSchema, - time: stringSchema.optional(), - _time: elementSchema.optional(), - party: referenceSchema.optional(), - }) - -const relatesToSchema: ZodType = - backboneElementSchema.extend({ - code: compositionRelatestoCodeSchema, - targetIdentifier: identifierSchema.optional(), - targetReference: referenceSchema.optional(), - }) - -const eventSchema: ZodType = backboneElementSchema.extend({ - code: codeableConceptSchema.array().optional(), - period: periodSchema.optional(), - detail: referenceSchema.array().optional(), -}) - -const sectionSchema: ZodType = backboneElementSchema.extend( - { - title: stringSchema.optional(), - _title: elementSchema.optional(), - code: codeableConceptSchema.optional(), - text: narrativeSchema.optional(), - mode: listModeSchema.optional(), - orderedBy: codeableConceptSchema.optional(), - entry: referenceSchema.array().optional(), - emptyReason: codeableConceptSchema.optional(), - get section() { - return sectionSchema.array().optional() - }, - }, -) - -/** - * Zod schema for FHIR Composition resource (untyped version). - */ -export const untypedCompositionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Composition').readonly(), - identifier: identifierSchema.optional(), - status: compositionStatusSchema, - _status: elementSchema.optional(), - type: codeableConceptSchema, - category: codeableConceptSchema.array().optional(), - subject: referenceSchema.optional(), - encounter: referenceSchema.optional(), - date: stringSchema, - _date: elementSchema.optional(), - author: referenceSchema.array(), - title: stringSchema, - _title: elementSchema.optional(), - confidentiality: stringSchema.optional(), - _confidentiality: elementSchema.optional(), - attester: attesterSchema.array().optional(), - custodian: referenceSchema.optional(), - relatesTo: relatesToSchema.array().optional(), - event: eventSchema.array().optional(), - section: sectionSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Composition resource. - */ -export const compositionSchema: ZodType = untypedCompositionSchema - -/** - * Wrapper class for FHIR Composition resources. - * Provides utility methods for working with clinical document compositions. - */ -export class FhirComposition extends FhirDomainResource { - /** - * Parses a Composition resource from unknown data. - * - * @param value - The data to parse and validate against the Composition schema - * @returns A FhirComposition instance containing the validated resource - */ - public static parse(value: unknown): FhirComposition { - return new FhirComposition(compositionSchema.parse(value)) - } - - /** - * Gets the composition date as a JavaScript Date object. - * - * @returns The composition date, if available - */ - public get date(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.date) - } - - /** - * Gets the type of composition as display text. - * - * @returns The type display text, if available - */ - public get typeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.type) - } -} diff --git a/packages/fhir/src/resources/conceptMap.ts b/packages/fhir/src/resources/conceptMap.ts deleted file mode 100644 index 18f5ded1..00000000 --- a/packages/fhir/src/resources/conceptMap.ts +++ /dev/null @@ -1,164 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ConceptMapGroup, - type ConceptMapGroupElement, - type ConceptMapGroupElementTarget, - type ConceptMapGroupElementTargetDependsOn, - type ConceptMapGroupUnmapped, - type ConceptMap, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactDetailSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - stringSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - conceptMapEquivalenceSchema, - conceptMapUnmappedModeSchema, - publicationStatusSchema, -} from '../valueSets/index.js' - -const conceptMapGroupElementTargetDependsOnSchema: ZodType = - backboneElementSchema.extend({ - property: urlSchema, - _property: elementSchema.optional(), - system: urlSchema.optional(), - _system: elementSchema.optional(), - value: stringSchema, - _value: elementSchema.optional(), - display: stringSchema.optional(), - _display: elementSchema.optional(), - }) - -const conceptMapGroupElementTargetSchema: ZodType = - backboneElementSchema.extend({ - code: stringSchema.optional(), - _code: elementSchema.optional(), - display: stringSchema.optional(), - _display: elementSchema.optional(), - equivalence: conceptMapEquivalenceSchema, - _equivalence: elementSchema.optional(), - comment: stringSchema.optional(), - _comment: elementSchema.optional(), - dependsOn: conceptMapGroupElementTargetDependsOnSchema.array().optional(), - product: conceptMapGroupElementTargetDependsOnSchema.array().optional(), - }) - -const conceptMapGroupElementSchema: ZodType = - backboneElementSchema.extend({ - code: stringSchema.optional(), - _code: elementSchema.optional(), - display: stringSchema.optional(), - _display: elementSchema.optional(), - target: conceptMapGroupElementTargetSchema.array().optional(), - }) - -const conceptMapGroupUnmappedSchema: ZodType = - backboneElementSchema.extend({ - mode: conceptMapUnmappedModeSchema, - _mode: elementSchema.optional(), - code: stringSchema.optional(), - _code: elementSchema.optional(), - display: stringSchema.optional(), - _display: elementSchema.optional(), - url: urlSchema.optional(), - _url: elementSchema.optional(), - }) - -const conceptMapGroupSchema: ZodType = - backboneElementSchema.extend({ - source: urlSchema.optional(), - _source: elementSchema.optional(), - sourceVersion: stringSchema.optional(), - _sourceVersion: elementSchema.optional(), - target: urlSchema.optional(), - _target: elementSchema.optional(), - targetVersion: stringSchema.optional(), - _targetVersion: elementSchema.optional(), - element: conceptMapGroupElementSchema.array(), - unmapped: conceptMapGroupUnmappedSchema.optional(), - }) - -/** - * Zod schema for FHIR ConceptMap resource (untyped version). - */ -export const untypedConceptMapSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ConceptMap').readonly(), - url: urlSchema.optional(), - _url: elementSchema.optional(), - identifier: identifierSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - purpose: stringSchema.optional(), - _purpose: elementSchema.optional(), - copyright: stringSchema.optional(), - _copyright: elementSchema.optional(), - sourceUri: urlSchema.optional(), - _sourceUri: elementSchema.optional(), - sourceCanonical: urlSchema.optional(), - _sourceCanonical: elementSchema.optional(), - targetUri: urlSchema.optional(), - _targetUri: elementSchema.optional(), - targetCanonical: urlSchema.optional(), - _targetCanonical: elementSchema.optional(), - group: conceptMapGroupSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ConceptMap resource. - */ -export const conceptMapSchema: ZodType = untypedConceptMapSchema - -/** - * Wrapper class for FHIR ConceptMap resources. - * Provides utility methods for working with concept maps that define mappings between code systems. - */ -export class FhirConceptMap extends FhirDomainResource { - // Static Functions - - /** - * Parses a ConceptMap resource from unknown data. - * - * @param value - The data to parse and validate against the ConceptMap schema - * @returns A FhirConceptMap instance containing the validated resource - */ - public static parse(value: unknown): FhirConceptMap { - return new FhirConceptMap(conceptMapSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/condition.ts b/packages/fhir/src/resources/condition.ts deleted file mode 100644 index 2de5a54b..00000000 --- a/packages/fhir/src/resources/condition.ts +++ /dev/null @@ -1,296 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ConditionEvidence, - type ConditionStage, - type Condition, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - quantitySchema, - rangeSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' - -const conditionStageSchema: ZodType = - backboneElementSchema.extend({ - summary: codeableConceptSchema.optional(), - assessment: referenceSchema.array().optional(), - type: codeableConceptSchema.optional(), - }) - -const conditionEvidenceSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema.array().optional(), - detail: referenceSchema.array().optional(), - }) - -/** - * Zod schema for FHIR Condition resource (untyped version). - */ -export const untypedConditionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Condition').readonly(), - identifier: identifierSchema.array().optional(), - clinicalStatus: codeableConceptSchema.optional(), - verificationStatus: codeableConceptSchema.optional(), - category: codeableConceptSchema.array().optional(), - severity: codeableConceptSchema.optional(), - code: codeableConceptSchema.optional(), - bodySite: codeableConceptSchema.array().optional(), - subject: referenceSchema, - encounter: referenceSchema.optional(), - onsetDateTime: dateTimeSchema.optional(), - _onsetDateTime: elementSchema.optional(), - onsetAge: quantitySchema.optional(), - onsetPeriod: periodSchema.optional(), - onsetRange: rangeSchema.optional(), - onsetString: stringSchema.optional(), - _onsetString: elementSchema.optional(), - abatementDateTime: dateTimeSchema.optional(), - _abatementDateTime: elementSchema.optional(), - abatementAge: quantitySchema.optional(), - abatementPeriod: periodSchema.optional(), - abatementRange: rangeSchema.optional(), - abatementString: stringSchema.optional(), - _abatementString: elementSchema.optional(), - recordedDate: dateTimeSchema.optional(), - _recordedDate: elementSchema.optional(), - recorder: referenceSchema.optional(), - asserter: referenceSchema.optional(), - stage: conditionStageSchema.array().optional(), - evidence: conditionEvidenceSchema.array().optional(), - note: annotationSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Condition resource. - */ -export const conditionSchema: ZodType = untypedConditionSchema - -/** - * Wrapper class for FHIR Condition resources. - * Provides utility methods for working with conditions and their temporal aspects. - */ -export class FhirCondition extends FhirDomainResource { - // Static Functions - - /** - * Parses a Condition resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirCondition instance - */ - public static parse(value: unknown): FhirCondition { - return new FhirCondition(conditionSchema.parse(value)) - } - - // Properties - - /** - * Gets the condition onset date as a JavaScript Date object (from onsetDateTime). - * - * @returns The onset date if available, undefined otherwise - * - * @example - * ```typescript - * const onsetDate = condition.onsetDate - * if (onsetDate) { - * console.log(`Condition started on: ${onsetDate.toLocaleDateString()}`) - * } - * ``` - */ - public get onsetDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.onsetDateTime) - } - - /** - * Gets the condition abatement date as a JavaScript Date object (from abatementDateTime). - * - * @returns The abatement date if available, undefined otherwise - * - * @example - * ```typescript - * const abatementDate = condition.abatementDate - * if (abatementDate) { - * console.log(`Condition resolved on: ${abatementDate.toLocaleDateString()}`) - * } - * ``` - */ - public get abatementDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.abatementDateTime) - } - - /** - * Gets the recorded date as a JavaScript Date object. - * - * @returns The recorded date if available, undefined otherwise - */ - public get recordedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.recordedDate) - } - - /** - * Checks if the condition is currently active (no abatement). - * - * @returns true if the condition is active (has no abatement information) - * - * @example - * ```typescript - * if (condition.isActive) { - * console.log('Condition is currently active') - * } - * ``` - */ - public get isActive(): boolean { - return ( - !this.value.abatementDateTime && - !this.value.abatementAge && - !this.value.abatementPeriod && - !this.value.abatementRange && - !this.value.abatementString - ) - } - - /** - * Checks if the condition has a confirmed clinical status. - * - * @returns true if clinical status is confirmed - * - * @example - * ```typescript - * if (condition.isConfirmed) { - * console.log('Condition is confirmed') - * } - * ``` - */ - public get isConfirmed(): boolean { - return FhirDomainResource.containsCoding(this.value.verificationStatus, { - system: 'http://terminology.hl7.org/CodeSystem/condition-ver-status', - code: 'confirmed', - }) - } - - /** - * Calculates the duration of the condition in milliseconds. - * If abatement is not set, duration is undefined. - * - * @returns Duration in milliseconds, or undefined if onset or abatement is missing - * - * @example - * ```typescript - * const duration = condition.getDuration() - * if (duration) { - * const days = duration / (1000 * 60 * 60 * 24) - * console.log(`Condition lasted ${days.toFixed(0)} days`) - * } - * ``` - */ - public get duration(): number | undefined { - const onset = this.onsetDate - const abatement = this.abatementDate - - if (!onset || !abatement) return undefined - - return abatement.getTime() - onset.getTime() - } - - /** - * Determines if this is likely a chronic condition based on duration. - * A condition is considered chronic if it has lasted more than 90 days or has no abatement date. - * - * @param chronicThresholdDays - Number of days to consider a condition chronic (default: 90) - * @returns true if condition appears to be chronic - * - * @example - * ```typescript - * if (condition.isChronicCondition()) { - * console.log('This is a chronic condition requiring ongoing management') - * } - * ``` - */ - public isChronicCondition(chronicThresholdDays = 90): boolean { - // If no abatement date, assume ongoing and potentially chronic - if (!this.abatementDate) { - const onset = this.onsetDate - if (!onset) return false - - // Check if onset was more than threshold days ago - const now = new Date() - const daysSinceOnset = - (now.getTime() - onset.getTime()) / (1000 * 60 * 60 * 24) - return daysSinceOnset > chronicThresholdDays - } - - // If there is abatement, check duration - const duration = this.duration - if (!duration) return false - - const durationDays = duration / (1000 * 60 * 60 * 24) - return durationDays > chronicThresholdDays - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/consent.ts b/packages/fhir/src/resources/consent.ts deleted file mode 100644 index 0f21943d..00000000 --- a/packages/fhir/src/resources/consent.ts +++ /dev/null @@ -1,206 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ConsentPolicy, - type ConsentProvision, - type ConsentProvisionActor, - type ConsentProvisionData, - type ConsentVerification, - type Consent, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - attachmentSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - codingSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - referenceSchema, - stringSchema, - uriSchema, -} from '../elements/index.js' -import { - consentDataMeaningSchema, - consentStatusSchema, - consentProvisionTypeSchema, -} from '../valueSets/index.js' - -const consentPolicySchema: ZodType = - backboneElementSchema.extend({ - authority: stringSchema.optional(), - _authority: elementSchema.optional(), - uri: uriSchema.optional(), - _uri: elementSchema.optional(), - }) - -const consentVerificationSchema: ZodType = - backboneElementSchema.extend({ - verified: booleanSchema, - _verified: elementSchema.optional(), - verificationDate: dateTimeSchema.optional(), - _verificationDate: elementSchema.optional(), - verifiedWith: referenceSchema.optional(), - }) - -const consentProvisionActorSchema: ZodType = - backboneElementSchema.extend({ - role: codeableConceptSchema, - reference: referenceSchema, - }) - -const consentProvisionDataSchema: ZodType = - backboneElementSchema.extend({ - meaning: consentDataMeaningSchema, - _meaning: elementSchema.optional(), - reference: referenceSchema, - }) - -const consentProvisionSchema: ZodType = - backboneElementSchema.extend({ - type: consentProvisionTypeSchema.optional(), - _type: elementSchema.optional(), - period: periodSchema.optional(), - actor: consentProvisionActorSchema.array().optional(), - action: codeableConceptSchema.array().optional(), - securityLabel: codingSchema.array().optional(), - purpose: codingSchema.array().optional(), - class: codingSchema.array().optional(), - code: codeableConceptSchema.array().optional(), - dataPeriod: periodSchema.optional(), - data: consentProvisionDataSchema.array().optional(), - get provision() { - return consentProvisionSchema.array().optional() - }, - }) - -/** - * Zod schema for FHIR Consent resource (untyped version). - */ -export const untypedConsentSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Consent').readonly(), - identifier: identifierSchema.array().optional(), - status: consentStatusSchema, - _status: elementSchema.optional(), - scope: codeableConceptSchema, - category: codeableConceptSchema.array().min(1), - patient: referenceSchema.optional(), - dateTime: dateTimeSchema.optional(), - _dateTime: elementSchema.optional(), - performer: referenceSchema.array().optional(), - organization: referenceSchema.array().optional(), - sourceAttachment: attachmentSchema.optional(), - sourceReference: referenceSchema.optional(), - policy: consentPolicySchema.array().optional(), - policyRule: codeableConceptSchema.optional(), - verification: consentVerificationSchema.array().optional(), - provision: consentProvisionSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Consent resource. - */ -export const consentSchema: ZodType = untypedConsentSchema - -/** - * Wrapper class for FHIR Consent resources. - * Provides utility methods for working with patient consent records. - */ -export class FhirConsent extends FhirDomainResource { - /** - * Parses a Consent resource from unknown data. - * - * @param value - The data to parse and validate against the Consent schema - * @returns A FhirConsent instance containing the validated resource - */ - public static parse(value: unknown): FhirConsent { - return new FhirConsent(consentSchema.parse(value)) - } - - /** - * Gets the date/time when consent was agreed to as a JavaScript Date object. - * - * @returns The consent date/time, if available - */ - public get dateTime(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.dateTime) - } - - /** - * Gets the scope of the consent as display text. - * - * @returns The scope display text, if available - */ - public get scopeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.scope) - } - - /** - * Gets human-readable display strings for all consent category CodeableConcepts. - * - * @returns Array of category display texts - */ - public get categoryDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.category) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/contract.ts b/packages/fhir/src/resources/contract.ts deleted file mode 100644 index 3340bc07..00000000 --- a/packages/fhir/src/resources/contract.ts +++ /dev/null @@ -1,420 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ContractContentDefinition, - type ContractFriendly, - type ContractLegal, - type ContractRule, - type ContractSigner, - type ContractTermAction, - type ContractTermActionSubject, - type ContractTermAsset, - type ContractTermAssetContext, - type ContractTermAssetValuedItem, - type ContractTermOffer, - type ContractTermOfferAnswer, - type ContractTermOfferParty, - type ContractTermSecurityLabel, - type Coding, - type Contract, - type ContractTerm, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - attachmentSchema, - backboneElementSchema, - codeableConceptSchema, - codingSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - moneySchema, - periodSchema, - quantitySchema, - referenceSchema, - signatureSchema, - stringSchema, - timingSchema, - uriSchema, -} from '../elements/index.js' -import { - contractPublicationStatusSchema, - contractStatusSchema, -} from '../valueSets/index.js' - -// Keep the nested schemas together to mirror the FHIR Contract structure. - -const contractTermSecurityLabelSchema: ZodType = - backboneElementSchema.extend({ - number: z.number().array().optional(), - classification: codingSchema, - category: codingSchema.array().optional(), - control: codingSchema.array().optional(), - }) - -const contractTermOfferPartySchema: ZodType = - backboneElementSchema.extend({ - reference: referenceSchema.array().min(1), - role: codeableConceptSchema, - }) - -const contractTermOfferAnswerSchema: ZodType = - backboneElementSchema.extend({ - valueBoolean: z.boolean().optional(), - _valueBoolean: elementSchema.optional(), - valueDecimal: z.number().optional(), - valueInteger: z.number().optional(), - valueDate: z.string().optional(), - _valueDate: elementSchema.optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: elementSchema.optional(), - valueTime: z.string().optional(), - _valueTime: elementSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueUri: uriSchema.optional(), - _valueUri: elementSchema.optional(), - valueAttachment: attachmentSchema.optional(), - valueCoding: codingSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueReference: referenceSchema.optional(), - }) - -const contractTermOfferSchema: ZodType = - backboneElementSchema.extend({ - identifier: identifierSchema.array().optional(), - party: contractTermOfferPartySchema.array().optional(), - topic: referenceSchema.optional(), - type: codeableConceptSchema.optional(), - decision: codeableConceptSchema.optional(), - decisionMode: codeableConceptSchema.array().optional(), - answer: contractTermOfferAnswerSchema.array().optional(), - text: stringSchema.optional(), - _text: elementSchema.optional(), - linkId: stringSchema.array().optional(), - _linkId: elementSchema.array().optional(), - securityLabelNumber: z.number().array().optional(), - }) - -const contractTermAssetContextSchema: ZodType = - backboneElementSchema.extend({ - reference: referenceSchema.optional(), - code: codeableConceptSchema.array().optional(), - text: stringSchema.optional(), - _text: elementSchema.optional(), - }) - -const contractTermAssetValuedItemSchema: ZodType = - backboneElementSchema.extend({ - entityCodeableConcept: codeableConceptSchema.optional(), - entityReference: referenceSchema.optional(), - identifier: identifierSchema.optional(), - effectiveTime: dateTimeSchema.optional(), - _effectiveTime: elementSchema.optional(), - quantity: quantitySchema.optional(), - unitPrice: moneySchema.optional(), - factor: z.number().optional(), - points: z.number().optional(), - net: moneySchema.optional(), - payment: stringSchema.optional(), - _payment: elementSchema.optional(), - paymentDate: dateTimeSchema.optional(), - _paymentDate: elementSchema.optional(), - responsible: referenceSchema.optional(), - recipient: referenceSchema.optional(), - linkId: stringSchema.array().optional(), - _linkId: elementSchema.array().optional(), - securityLabelNumber: z.number().array().optional(), - }) - -const contractTermAssetSchema: ZodType = - backboneElementSchema.extend({ - scope: codeableConceptSchema.optional(), - type: codeableConceptSchema.array().optional(), - typeReference: referenceSchema.array().optional(), - subtype: codeableConceptSchema.array().optional(), - relationship: codingSchema.optional(), - context: contractTermAssetContextSchema.array().optional(), - condition: stringSchema.optional(), - _condition: elementSchema.optional(), - periodType: codeableConceptSchema.array().optional(), - period: periodSchema.array().optional(), - usePeriod: periodSchema.array().optional(), - text: stringSchema.optional(), - _text: elementSchema.optional(), - linkId: stringSchema.array().optional(), - _linkId: elementSchema.array().optional(), - answer: backboneElementSchema.array().optional(), - securityLabelNumber: z.number().array().optional(), - valuedItem: contractTermAssetValuedItemSchema.array().optional(), - }) - -const contractTermActionSubjectSchema: ZodType = - backboneElementSchema.extend({ - reference: referenceSchema.array().min(1), - role: codeableConceptSchema.optional(), - }) - -const contractTermActionSchema: ZodType = - backboneElementSchema.extend({ - doNotPerform: z.boolean().optional(), - _doNotPerform: elementSchema.optional(), - type: codeableConceptSchema, - subject: contractTermActionSubjectSchema.array().optional(), - intent: codeableConceptSchema, - linkId: stringSchema.array().optional(), - _linkId: elementSchema.array().optional(), - status: codeableConceptSchema, - context: referenceSchema.optional(), - contextLinkId: stringSchema.array().optional(), - _contextLinkId: elementSchema.array().optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: elementSchema.optional(), - occurrencePeriod: periodSchema.optional(), - occurrenceTiming: timingSchema.optional(), - requester: referenceSchema.array().optional(), - requesterLinkId: stringSchema.array().optional(), - _requesterLinkId: elementSchema.array().optional(), - performerType: codeableConceptSchema.array().optional(), - performerRole: codeableConceptSchema.optional(), - performer: referenceSchema.optional(), - performerLinkId: stringSchema.array().optional(), - _performerLinkId: elementSchema.array().optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - reason: stringSchema.array().optional(), - _reason: elementSchema.array().optional(), - reasonLinkId: stringSchema.array().optional(), - _reasonLinkId: elementSchema.array().optional(), - note: annotationSchema.array().optional(), - securityLabelNumber: z.number().array().optional(), - }) - -const contractTermSchema: ZodType = backboneElementSchema.extend({ - identifier: identifierSchema.optional(), - issued: dateTimeSchema.optional(), - _issued: elementSchema.optional(), - applies: periodSchema.optional(), - topicCodeableConcept: codeableConceptSchema.optional(), - topicReference: referenceSchema.optional(), - type: codeableConceptSchema.optional(), - subType: codeableConceptSchema.optional(), - text: stringSchema.optional(), - _text: elementSchema.optional(), - securityLabel: contractTermSecurityLabelSchema.array().optional(), - offer: contractTermOfferSchema, - asset: contractTermAssetSchema.array().optional(), - action: contractTermActionSchema.array().optional(), - get group() { - return contractTermSchema.array().optional() - }, -}) - -const contractContentDefinitionSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - subType: codeableConceptSchema.optional(), - publisher: referenceSchema.optional(), - publicationDate: dateTimeSchema.optional(), - _publicationDate: elementSchema.optional(), - publicationStatus: contractPublicationStatusSchema, - _publicationStatus: elementSchema.optional(), - copyright: z.string().optional(), - _copyright: elementSchema.optional(), - }) - -const contractSignerSchema: ZodType = - backboneElementSchema.extend({ - type: codingSchema, - party: referenceSchema, - signature: signatureSchema.array().min(1), - }) - -const contractFriendlySchema: ZodType = - backboneElementSchema.extend({ - contentAttachment: attachmentSchema.optional(), - contentReference: referenceSchema.optional(), - }) - -const contractLegalSchema: ZodType = - backboneElementSchema.extend({ - contentAttachment: attachmentSchema.optional(), - contentReference: referenceSchema.optional(), - }) - -const contractRuleSchema: ZodType = backboneElementSchema.extend({ - contentAttachment: attachmentSchema.optional(), - contentReference: referenceSchema.optional(), -}) - -/** - * Zod schema for FHIR Contract resource (untyped version). - */ -export const untypedContractSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Contract').readonly(), - identifier: identifierSchema.array().optional(), - url: uriSchema.optional(), - _url: elementSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - status: contractStatusSchema.optional(), - _status: elementSchema.optional(), - legalState: codeableConceptSchema.optional(), - instantiatesCanonical: referenceSchema.optional(), - instantiatesUri: uriSchema.optional(), - _instantiatesUri: elementSchema.optional(), - contentDerivative: codeableConceptSchema.optional(), - issued: dateTimeSchema.optional(), - _issued: elementSchema.optional(), - applies: periodSchema.optional(), - expirationType: codeableConceptSchema.optional(), - subject: referenceSchema.array().optional(), - authority: referenceSchema.array().optional(), - domain: referenceSchema.array().optional(), - site: referenceSchema.array().optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - subtitle: stringSchema.optional(), - _subtitle: elementSchema.optional(), - alias: stringSchema.array().optional(), - _alias: elementSchema.array().optional(), - author: referenceSchema.optional(), - scope: codeableConceptSchema.optional(), - topicCodeableConcept: codeableConceptSchema.optional(), - topicReference: referenceSchema.optional(), - type: codeableConceptSchema.optional(), - subType: codeableConceptSchema.array().optional(), - contentDefinition: contractContentDefinitionSchema.optional(), - term: contractTermSchema.array().optional(), - supportingInfo: referenceSchema.array().optional(), - relevantHistory: referenceSchema.array().optional(), - signer: contractSignerSchema.array().optional(), - friendly: contractFriendlySchema.array().optional(), - legal: contractLegalSchema.array().optional(), - rule: contractRuleSchema.array().optional(), - legallyBindingAttachment: attachmentSchema.optional(), - legallyBindingReference: referenceSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Contract resource. - */ -export const contractSchema: ZodType = untypedContractSchema - -/** - * Wrapper class for FHIR Contract resources. - * Provides utility methods for working with legal contracts and agreements. - */ -export class FhirContract extends FhirDomainResource { - // Static Functions - - /** - * Parses a Contract resource from unknown data. - * - * @param value - The data to parse and validate against the Contract schema - * @returns A FhirContract instance containing the validated resource - */ - public static parse(value: unknown): FhirContract { - return new FhirContract(contractSchema.parse(value)) - } - - /** - * Get the issued date as a Date object. - * @returns The issued date, or undefined if not set - */ - public get issuedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.issued) - } - - /** - * Get the applies period start date. - * @returns The start date, or undefined if not set - */ - public get appliesPeriodStart(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.applies?.start) - } - - /** - * Get the applies period end date. - * @returns The end date, or undefined if not set - */ - public get appliesPeriodEnd(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.applies?.end) - } - - /** - * Check if the contract applies to the current date. - * @param asOfDate Optional date to check against (defaults to current date) - * @returns True if the contract is currently active - */ - public isCurrentlyActive(asOfDate?: Date): boolean { - return FhirDomainResource.periodIsActive(this.value.applies, asOfDate) - } - - /** - * Get display text from the contract type. - * @returns Type display text - */ - public get typeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.type) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/coverage.ts b/packages/fhir/src/resources/coverage.ts deleted file mode 100644 index a2f0880f..00000000 --- a/packages/fhir/src/resources/coverage.ts +++ /dev/null @@ -1,173 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type CoverageClass, - type CoverageCostToBeneficiary, - type CoverageCostToBeneficiaryException, - type Coverage, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - backboneElementSchema, - elementSchema, - codeableConceptSchema, - periodSchema, - referenceSchema, - quantitySchema, - moneySchema, - positiveIntSchema, - stringSchema, - booleanSchema, -} from '../elements/index.js' -import { coverageStatusSchema } from '../valueSets/index.js' - -const coverageClassSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - value: stringSchema, - _value: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - }) - -const coverageCostToBeneficiaryExceptionSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - period: periodSchema.optional(), - }) - -const coverageCostToBeneficiarySchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueMoney: moneySchema.optional(), - exception: coverageCostToBeneficiaryExceptionSchema.array().optional(), - }) - -/** - * Zod schema for FHIR Coverage resource (untyped version). - */ -export const untypedCoverageSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Coverage').readonly(), - identifier: identifierSchema.array().optional(), - status: coverageStatusSchema, - _status: elementSchema.optional(), - type: codeableConceptSchema.optional(), - policyHolder: referenceSchema.optional(), - subscriber: referenceSchema.optional(), - subscriberId: stringSchema.optional(), - _subscriberId: elementSchema.optional(), - beneficiary: referenceSchema, - dependent: stringSchema.optional(), - _dependent: elementSchema.optional(), - relationship: codeableConceptSchema.optional(), - period: periodSchema.optional(), - payor: referenceSchema.array(), - class: coverageClassSchema.array().optional(), - order: positiveIntSchema.optional(), - network: stringSchema.optional(), - _network: elementSchema.optional(), - costToBeneficiary: coverageCostToBeneficiarySchema.array().optional(), - subrogation: booleanSchema.optional(), - _subrogation: elementSchema.optional(), - contract: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Coverage resource. - */ -export const coverageSchema: ZodType = untypedCoverageSchema - -/** - * Wrapper class for FHIR Coverage resources. - * Provides utility methods for working with insurance coverage information. - */ -export class FhirCoverage extends FhirDomainResource { - // Static Functions - - /** - * Parses a Coverage resource from unknown data. - * - * @param value - The data to parse and validate against the Coverage schema - * @returns A FhirCoverage instance containing the validated resource - */ - public static parse(value: unknown): FhirCoverage { - return new FhirCoverage(coverageSchema.parse(value)) - } - - /** - * Gets the coverage period start date as a JavaScript Date object. - * - * @returns The period start date, if available - */ - public get periodStart(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.period?.start) - } - - /** - * Gets the coverage period end date as a JavaScript Date object. - * - * @returns The period end date, if available - */ - public get periodEnd(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.period?.end) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/coverageEligibilityRequest.ts b/packages/fhir/src/resources/coverageEligibilityRequest.ts deleted file mode 100644 index 49a07160..00000000 --- a/packages/fhir/src/resources/coverageEligibilityRequest.ts +++ /dev/null @@ -1,168 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type CoverageEligibilityRequest } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - backboneElementSchema, - elementSchema, - codeableConceptSchema, - referenceSchema, - dateTimeSchema, - periodSchema, - dateSchema, - positiveIntSchema, - moneySchema, - stringSchema, - quantitySchema, - booleanSchema, -} from '../elements/index.js' -import { - financialResourceStatusSchema, - eligibilityRequestPurposeSchema, -} from '../valueSets/index.js' - -/** - * Zod schema for FHIR CoverageEligibilityRequest resource (untyped version). - */ -export const untypedCoverageEligibilityRequestSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('CoverageEligibilityRequest').readonly(), - identifier: identifierSchema.array().optional(), - status: financialResourceStatusSchema, - _status: elementSchema.optional(), - priority: codeableConceptSchema.optional(), - purpose: eligibilityRequestPurposeSchema.array(), - _purpose: elementSchema.array().optional(), - patient: referenceSchema, - servicedDate: dateSchema.optional(), - _servicedDate: elementSchema.optional(), - servicedPeriod: periodSchema.optional(), - created: dateTimeSchema, - _created: elementSchema.optional(), - enterer: referenceSchema.optional(), - provider: referenceSchema.optional(), - insurer: referenceSchema, - facility: referenceSchema.optional(), - supportingInfo: backboneElementSchema - .extend({ - sequence: positiveIntSchema, - information: referenceSchema, - appliesToAll: booleanSchema.optional(), - _appliesToAll: elementSchema.optional(), - }) - .array() - .optional(), - insurance: backboneElementSchema - .extend({ - focal: z.boolean().optional(), - _focal: elementSchema.optional(), - coverage: referenceSchema, - businessArrangement: stringSchema.optional(), - _businessArrangement: elementSchema.optional(), - }) - .array() - .optional(), - item: backboneElementSchema - .extend({ - supportingInfoSequence: positiveIntSchema.array().optional(), - category: codeableConceptSchema.optional(), - productOrService: codeableConceptSchema.optional(), - modifier: codeableConceptSchema.array().optional(), - provider: referenceSchema.optional(), - quantity: quantitySchema.optional(), - unitPrice: moneySchema.optional(), - facility: referenceSchema.optional(), - diagnosis: backboneElementSchema - .extend({ - diagnosisCodeableConcept: codeableConceptSchema.optional(), - diagnosisReference: referenceSchema.optional(), - }) - .array() - .optional(), - detail: referenceSchema.array().optional(), - }) - .array() - .optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR CoverageEligibilityRequest resource. - */ -export const coverageEligibilityRequestSchema: ZodType = - untypedCoverageEligibilityRequestSchema - -/** - * Wrapper class for FHIR CoverageEligibilityRequest resources. - * Provides utility methods for working with insurance coverage eligibility requests. - */ -export class FhirCoverageEligibilityRequest extends FhirDomainResource { - // Static Functions - - /** - * Parses a CoverageEligibilityRequest resource from unknown data. - * - * @param value - The data to parse and validate against the CoverageEligibilityRequest schema - * @returns A FhirCoverageEligibilityRequest instance containing the validated resource - */ - public static parse(value: unknown): FhirCoverageEligibilityRequest { - return new FhirCoverageEligibilityRequest( - coverageEligibilityRequestSchema.parse(value), - ) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/coverageEligibilityResponse.ts b/packages/fhir/src/resources/coverageEligibilityResponse.ts deleted file mode 100644 index 9995f991..00000000 --- a/packages/fhir/src/resources/coverageEligibilityResponse.ts +++ /dev/null @@ -1,184 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type CoverageEligibilityResponse } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - backboneElementSchema, - elementSchema, - codeableConceptSchema, - referenceSchema, - stringSchema, - dateTimeSchema, - periodSchema, - dateSchema, - moneySchema, - booleanSchema, -} from '../elements/index.js' -import { - financialResourceStatusSchema, - remittanceOutcomeSchema, - eligibilityResponsePurposeSchema, -} from '../valueSets/index.js' - -/** - * Zod schema for FHIR CoverageEligibilityResponse resource (untyped version). - */ -export const untypedCoverageEligibilityResponseSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('CoverageEligibilityResponse').readonly(), - identifier: identifierSchema.array().optional(), - status: financialResourceStatusSchema, - _status: elementSchema.optional(), - purpose: eligibilityResponsePurposeSchema.array(), - _purpose: elementSchema.array().optional(), - patient: referenceSchema, - servicedDate: dateSchema.optional(), - _servicedDate: elementSchema.optional(), - servicedPeriod: periodSchema.optional(), - created: dateTimeSchema, - _created: elementSchema.optional(), - requestor: referenceSchema.optional(), - request: referenceSchema, - outcome: remittanceOutcomeSchema, - _outcome: elementSchema.optional(), - disposition: stringSchema.optional(), - _disposition: elementSchema.optional(), - insurer: referenceSchema, - insurance: backboneElementSchema - .extend({ - coverage: referenceSchema, - inforce: booleanSchema.optional(), - _inforce: elementSchema.optional(), - benefitPeriod: periodSchema.optional(), - item: backboneElementSchema - .extend({ - category: codeableConceptSchema.optional(), - productOrService: codeableConceptSchema.optional(), - modifier: codeableConceptSchema.array().optional(), - provider: referenceSchema.optional(), - excluded: booleanSchema.optional(), - _excluded: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - network: codeableConceptSchema.optional(), - unit: codeableConceptSchema.optional(), - term: codeableConceptSchema.optional(), - benefit: backboneElementSchema - .extend({ - type: codeableConceptSchema, - allowedUnsignedInt: z.number().optional(), - allowedString: stringSchema.optional(), - _allowedString: elementSchema.optional(), - allowedMoney: moneySchema.optional(), - usedUnsignedInt: z.number().optional(), - usedString: stringSchema.optional(), - _usedString: elementSchema.optional(), - usedMoney: moneySchema.optional(), - }) - .array() - .optional(), - authorizationRequired: booleanSchema.optional(), - _authorizationRequired: elementSchema.optional(), - authorizationSupporting: codeableConceptSchema.array().optional(), - authorizationUrl: z.string().optional(), - _authorizationUrl: elementSchema.optional(), - }) - .array() - .optional(), - }) - .array() - .optional(), - preAuthRef: stringSchema.optional(), - _preAuthRef: elementSchema.optional(), - form: codeableConceptSchema.optional(), - error: backboneElementSchema - .extend({ - code: codeableConceptSchema, - }) - .array() - .optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR CoverageEligibilityResponse resource. - */ -export const coverageEligibilityResponseSchema: ZodType = - untypedCoverageEligibilityResponseSchema - -/** - * Wrapper class for FHIR CoverageEligibilityResponse resources. - * Provides utility methods for working with insurance coverage eligibility responses. - */ -export class FhirCoverageEligibilityResponse extends FhirDomainResource { - // Static Functions - - /** - * Parses a CoverageEligibilityResponse resource from unknown data. - * - * @param value - The data to parse and validate against the CoverageEligibilityResponse schema - * @returns A FhirCoverageEligibilityResponse instance containing the validated resource - */ - public static parse(value: unknown): FhirCoverageEligibilityResponse { - return new FhirCoverageEligibilityResponse( - coverageEligibilityResponseSchema.parse(value), - ) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/detectedIssue.ts b/packages/fhir/src/resources/detectedIssue.ts deleted file mode 100644 index d461fcf3..00000000 --- a/packages/fhir/src/resources/detectedIssue.ts +++ /dev/null @@ -1,153 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type DetectedIssue } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { - detectedIssueSeveritySchema, - detectedIssueStatusSchema, -} from '../valueSets/index.js' - -/** - * Zod schema for FHIR DetectedIssue resource (untyped version). - */ -export const untypedDetectedIssueSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('DetectedIssue').readonly(), - identifier: identifierSchema.array().optional(), - status: detectedIssueStatusSchema, - _status: elementSchema.optional(), - code: codeableConceptSchema.optional(), - severity: detectedIssueSeveritySchema.optional(), - _severity: elementSchema.optional(), - patient: referenceSchema.optional(), - identifiedDateTime: dateTimeSchema.optional(), - _identifiedDateTime: elementSchema.optional(), - identifiedPeriod: periodSchema.optional(), - author: referenceSchema.optional(), - implicated: referenceSchema.array().optional(), - evidence: backboneElementSchema - .extend({ - code: codeableConceptSchema.array().optional(), - detail: referenceSchema.array().optional(), - }) - .array() - .optional(), - detail: stringSchema.optional(), - _detail: elementSchema.optional(), - reference: stringSchema.optional(), - _reference: elementSchema.optional(), - mitigation: backboneElementSchema - .extend({ - action: codeableConceptSchema, - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - author: referenceSchema.optional(), - }) - .array() - .optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR DetectedIssue resource. - */ -export const detectedIssueSchema: ZodType = - untypedDetectedIssueSchema - -/** - * Wrapper class for FHIR DetectedIssue resources. - * Provides utility methods for working with detected clinical issues. - */ -export class FhirDetectedIssue extends FhirDomainResource { - /** - * Parses a DetectedIssue resource from unknown data. - * - * @param value - The data to parse and validate against the DetectedIssue schema - * @returns A FhirDetectedIssue instance containing the validated resource - */ - public static parse(value: unknown): FhirDetectedIssue { - return new FhirDetectedIssue(detectedIssueSchema.parse(value)) - } - - /** - * Gets the date the issue was identified as a JavaScript Date object. - * - * @returns The identification date, if available - */ - public get identifiedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.identifiedDateTime) - } - - /** - * Gets the code display for the detected issue. - * - * @returns The code display text, if available - */ - public get codeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.code) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/device.ts b/packages/fhir/src/resources/device.ts deleted file mode 100644 index 99174bba..00000000 --- a/packages/fhir/src/resources/device.ts +++ /dev/null @@ -1,197 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type DeviceDeviceName, - type DeviceProperty, - type DeviceSpecialization, - type DeviceUdiCarrier, - type DeviceVersion, - type Device, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - codeableConceptSchema, - contactPointSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - quantitySchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { deviceNameTypeSchema, deviceStatusSchema } from '../valueSets/index.js' - -const deviceDeviceNameSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - type: deviceNameTypeSchema, - _type: elementSchema.optional(), - }) - -const devicePropertySchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - valueCode: codeableConceptSchema.array().optional(), - valueQuantity: quantitySchema.array().optional(), - }) - -const deviceDefinitionSpecializationSchema: ZodType = - backboneElementSchema.extend({ - systemType: codeableConceptSchema, - _systemType: elementSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - }) - -const deviceUdiCarrierSchema: ZodType = - backboneElementSchema.extend({ - deviceIdentifier: stringSchema, - _deviceIdentifier: elementSchema.optional(), - issuer: stringSchema.optional(), - _issuer: elementSchema.optional(), - jurisdiction: stringSchema.optional(), - _jurisdiction: elementSchema.optional(), - carrierHRF: stringSchema.optional(), - _carrierHRF: elementSchema.optional(), - carrierAIDC: stringSchema.optional(), - _carrierAIDC: elementSchema.optional(), - }) - -const deviceVersionSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - component: codeableConceptSchema.optional(), - value: stringSchema, - _value: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR Device resource (untyped version). - */ -export const untypedDeviceSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Device').readonly(), - contact: contactPointSchema.array().optional(), - definition: referenceSchema.optional(), - deviceName: deviceDeviceNameSchema.array().optional(), - distinctIdentifier: stringSchema.optional(), - _distinctIdentifier: elementSchema.optional(), - expirationDate: stringSchema.optional(), - _expirationDate: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - location: referenceSchema.optional(), - lotNumber: stringSchema.optional(), - _lotNumber: elementSchema.optional(), - manufactureDate: stringSchema.optional(), - _manufactureDate: elementSchema.optional(), - manufacturer: stringSchema.optional(), - _manufacturer: elementSchema.optional(), - modelNumber: stringSchema.optional(), - _modelNumber: elementSchema.optional(), - note: annotationSchema.array().optional(), - owner: referenceSchema.optional(), - parent: referenceSchema.optional(), - partNumber: stringSchema.optional(), - _partNumber: elementSchema.optional(), - patient: referenceSchema.optional(), - property: devicePropertySchema.array().optional(), - safety: codeableConceptSchema.array().optional(), - serialNumber: stringSchema.optional(), - _serialNumber: elementSchema.optional(), - specialization: deviceDefinitionSpecializationSchema.array().optional(), - status: deviceStatusSchema.optional(), - _status: elementSchema.optional(), - statusReason: codeableConceptSchema.array().optional(), - type: codeableConceptSchema.optional(), - udiCarrier: deviceUdiCarrierSchema.array().optional(), - url: stringSchema.optional(), - _url: elementSchema.optional(), - version: deviceVersionSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Device resource. - */ -export const deviceSchema: ZodType = untypedDeviceSchema - -/** - * Wrapper class for FHIR Device resources. - * Provides utility methods for working with device information. - */ -export class FhirDevice extends FhirDomainResource { - /** - * Parses a Device resource from unknown data. - * - * @param value - The data to parse and validate against the Device schema - * @returns A FhirDevice instance containing the validated resource - */ - public static parse(value: unknown): FhirDevice { - return new FhirDevice(deviceSchema.parse(value)) - } - - /** - * Gets the device type as display text. - * - * @returns The type display text, if available - */ - public get typeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.type) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/deviceDefinition.ts b/packages/fhir/src/resources/deviceDefinition.ts deleted file mode 100644 index 423c7fd9..00000000 --- a/packages/fhir/src/resources/deviceDefinition.ts +++ /dev/null @@ -1,220 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type DeviceDefinitionCapability, - type DeviceDefinitionDeviceName, - type DeviceDefinitionMaterial, - type DeviceDefinitionProperty, - type DeviceDefinitionSpecialization, - type DeviceDefinitionUdiDeviceIdentifier, - type ProdCharacteristic, - type ProductShelfLife, - type DeviceDefinition, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - attachmentSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactPointSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - quantitySchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { deviceDefinitionNameTypeSchema } from '../valueSets/index.js' - -const prodCharacteristicSchema: ZodType = - backboneElementSchema.extend({ - height: quantitySchema.optional(), - width: quantitySchema.optional(), - depth: quantitySchema.optional(), - weight: quantitySchema.optional(), - nominalVolume: quantitySchema.optional(), - externalDiameter: quantitySchema.optional(), - shape: stringSchema.optional(), - _shape: elementSchema.optional(), - color: stringSchema.array().optional(), - _color: elementSchema.array().optional(), - imprint: stringSchema.array().optional(), - _imprint: elementSchema.array().optional(), - image: attachmentSchema.array().optional(), - scoring: codeableConceptSchema.optional(), - }) - -const productShelfLifeSchema: ZodType = - backboneElementSchema.extend({ - identifier: identifierSchema.optional(), - type: codeableConceptSchema, - period: quantitySchema, - specialPrecautionsForStorage: codeableConceptSchema.array().optional(), - }) - -const deviceDefinitionUdiDeviceIdentifierSchema: ZodType = - backboneElementSchema.extend({ - deviceIdentifier: stringSchema, - _deviceIdentifier: elementSchema.optional(), - issuer: stringSchema, - _issuer: elementSchema.optional(), - jurisdiction: stringSchema, - _jurisdiction: elementSchema.optional(), - }) - -const deviceDefinitionDeviceNameSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - type: deviceDefinitionNameTypeSchema, - _type: elementSchema.optional(), - }) - -const deviceDefinitionSpecializationSchema: ZodType = - backboneElementSchema.extend({ - systemType: stringSchema, - _systemType: elementSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - }) - -const deviceDefinitionCapabilitySchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - description: codeableConceptSchema.array().optional(), - }) - -const deviceDefinitionPropertySchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - valueCode: codeableConceptSchema.array().optional(), - valueQuantity: quantitySchema.array().optional(), - }) - -const deviceDefinitionMaterialSchema: ZodType = - backboneElementSchema.extend({ - substance: codeableConceptSchema, - alternate: booleanSchema.optional(), - _alternate: elementSchema.optional(), - allergenicIndicator: booleanSchema.optional(), - _allergenicIndicator: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR DeviceDefinition resource (untyped version). - */ -export const untypedDeviceDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('DeviceDefinition').readonly(), - identifier: identifierSchema.array().optional(), - udiDeviceIdentifier: deviceDefinitionUdiDeviceIdentifierSchema - .array() - .optional(), - manufacturerString: stringSchema.optional(), - _manufacturerString: elementSchema.optional(), - manufacturerReference: referenceSchema.optional(), - deviceName: deviceDefinitionDeviceNameSchema.array().optional(), - modelNumber: stringSchema.optional(), - _modelNumber: elementSchema.optional(), - type: codeableConceptSchema.optional(), - specialization: deviceDefinitionSpecializationSchema.array().optional(), - version: stringSchema.array().optional(), - _version: elementSchema.array().optional(), - safety: codeableConceptSchema.array().optional(), - shelfLifeStorage: productShelfLifeSchema.array().optional(), - physicalCharacteristics: prodCharacteristicSchema.optional(), - languageCode: codeableConceptSchema.array().optional(), - capability: deviceDefinitionCapabilitySchema.array().optional(), - property: deviceDefinitionPropertySchema.array().optional(), - owner: referenceSchema.optional(), - contact: contactPointSchema.array().optional(), - url: stringSchema.optional(), - _url: elementSchema.optional(), - onlineInformation: stringSchema.optional(), - _onlineInformation: elementSchema.optional(), - note: annotationSchema.array().optional(), - quantity: quantitySchema.optional(), - parentDevice: referenceSchema.optional(), - material: deviceDefinitionMaterialSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR DeviceDefinition resource. - */ -export const deviceDefinitionSchema: ZodType = - untypedDeviceDefinitionSchema - -/** - * Wrapper class for FHIR DeviceDefinition resources. - * Provides utility methods for working with device definitions and specifications. - */ -export class FhirDeviceDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses a DeviceDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the DeviceDefinition schema - * @returns A FhirDeviceDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirDeviceDefinition { - return new FhirDeviceDefinition(deviceDefinitionSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/deviceMetric.ts b/packages/fhir/src/resources/deviceMetric.ts deleted file mode 100644 index 6ae3e532..00000000 --- a/packages/fhir/src/resources/deviceMetric.ts +++ /dev/null @@ -1,134 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type DeviceMetricCalibration, - type DeviceMetric, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - instantSchema, - referenceSchema, - timingSchema, -} from '../elements/index.js' -import { - deviceMetricCalibrationStateSchema, - deviceMetricCalibrationTypeSchema, - deviceMetricCategorySchema, - deviceMetricColorSchema, - deviceMetricOperationalStatusSchema, -} from '../valueSets/index.js' - -const deviceMetricCalibrationSchema: ZodType = - backboneElementSchema.extend({ - type: deviceMetricCalibrationTypeSchema.optional(), - _type: elementSchema.optional(), - state: deviceMetricCalibrationStateSchema.optional(), - _state: elementSchema.optional(), - time: instantSchema.optional(), - _time: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR DeviceMetric resource (untyped version). - */ -export const untypedDeviceMetricSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('DeviceMetric').readonly(), - identifier: identifierSchema.array().optional(), - type: codeableConceptSchema, - unit: codeableConceptSchema.optional(), - source: referenceSchema.optional(), - parent: referenceSchema.optional(), - operationalStatus: deviceMetricOperationalStatusSchema.optional(), - _operationalStatus: elementSchema.optional(), - color: deviceMetricColorSchema.optional(), - _color: elementSchema.optional(), - category: deviceMetricCategorySchema, - _category: elementSchema.optional(), - measurementPeriod: timingSchema.optional(), - calibration: deviceMetricCalibrationSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR DeviceMetric resource. - */ -export const deviceMetricSchema: ZodType = - untypedDeviceMetricSchema - -/** - * Wrapper class for FHIR DeviceMetric resources. - * Provides utility methods for working with device metrics and measurements. - */ -export class FhirDeviceMetric extends FhirDomainResource { - // Static Functions - - /** - * Parses a DeviceMetric resource from unknown data. - * - * @param value - The data to parse and validate against the DeviceMetric schema - * @returns A FhirDeviceMetric instance containing the validated resource - */ - public static parse(value: unknown): FhirDeviceMetric { - return new FhirDeviceMetric(deviceMetricSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/deviceRequest.ts b/packages/fhir/src/resources/deviceRequest.ts deleted file mode 100644 index 480daa52..00000000 --- a/packages/fhir/src/resources/deviceRequest.ts +++ /dev/null @@ -1,217 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type DeviceRequestParameter, - type DeviceRequest, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - quantitySchema, - rangeSchema, - referenceSchema, - stringSchema, - timingSchema, -} from '../elements/index.js' -import { - deviceRequestIntentSchema, - deviceRequestStatusSchema, - requestPrioritySchema, -} from '../valueSets/index.js' - -const deviceRequestParameterSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema.optional(), - valueCodeableConcept: codeableConceptSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueRange: rangeSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR DeviceRequest resource (untyped version). - */ -export const untypedDeviceRequestSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('DeviceRequest').readonly(), - identifier: identifierSchema.array().optional(), - instantiatesCanonical: stringSchema.array().optional(), - _instantiatesCanonical: elementSchema.array().optional(), - instantiatesUri: stringSchema.array().optional(), - _instantiatesUri: elementSchema.array().optional(), - basedOn: referenceSchema.array().optional(), - priorRequest: referenceSchema.array().optional(), - groupIdentifier: identifierSchema.optional(), - status: deviceRequestStatusSchema.optional(), - _status: elementSchema.optional(), - intent: deviceRequestIntentSchema, - _intent: elementSchema.optional(), - priority: requestPrioritySchema.optional(), - _priority: elementSchema.optional(), - codeReference: referenceSchema.optional(), - codeCodeableConcept: codeableConceptSchema.optional(), - parameter: deviceRequestParameterSchema.array().optional(), - subject: referenceSchema, - encounter: referenceSchema.optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: elementSchema.optional(), - occurrencePeriod: periodSchema.optional(), - occurrenceTiming: timingSchema.optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: elementSchema.optional(), - requester: referenceSchema.optional(), - performerType: codeableConceptSchema.optional(), - performer: referenceSchema.optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - insurance: referenceSchema.array().optional(), - supportingInfo: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - relevantHistory: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR DeviceRequest resource. - */ -export const deviceRequestSchema: ZodType = - untypedDeviceRequestSchema - -/** - * Wrapper class for FHIR DeviceRequest resources. - * Provides utility methods for working with device requests and orders. - */ -export class FhirDeviceRequest extends FhirDomainResource { - // Static Functions - - /** - * Parses a DeviceRequest resource from unknown data. - * - * @param value - The data to parse and validate against the DeviceRequest schema - * @returns A FhirDeviceRequest instance containing the validated resource - */ - public static parse(value: unknown): FhirDeviceRequest { - return new FhirDeviceRequest(deviceRequestSchema.parse(value)) - } - - /** - * Get the authored date as a Date object. - * @returns The authored date, or undefined if not set - */ - public get authoredDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.authoredOn) - } - - /** - * Get the occurrence date/time if specified as a dateTime. - * @returns The occurrence date, or undefined if not set or not a dateTime - */ - public get occurrenceDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.occurrenceDateTime) - } - - /** - * Get the occurrence period start date. - * @returns The start date, or undefined if not set - */ - public get occurrencePeriodStart(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.occurrencePeriod?.start) - } - - /** - * Get the occurrence period end date. - * @returns The end date, or undefined if not set - */ - public get occurrencePeriodEnd(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.occurrencePeriod?.end) - } - - /** - * Get device code display text. - * @returns Device code display text - */ - public get deviceCodeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay( - this.value.codeCodeableConcept, - ) - } - - /** - * Get note texts from the device request. - * @returns Array of note texts - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Get reason displays from reason codes. - * @returns Array of reason display texts - */ - public get reasonDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.reasonCode) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/deviceUseStatement.ts b/packages/fhir/src/resources/deviceUseStatement.ts deleted file mode 100644 index f25f9a06..00000000 --- a/packages/fhir/src/resources/deviceUseStatement.ts +++ /dev/null @@ -1,176 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type DeviceUseStatement } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - referenceSchema, - timingSchema, -} from '../elements/index.js' -import { deviceUseStatementStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR DeviceUseStatement resource (untyped version). - */ -export const untypedDeviceUseStatementSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('DeviceUseStatement').readonly(), - identifier: identifierSchema.array().optional(), - basedOn: referenceSchema.array().optional(), - status: deviceUseStatementStatusSchema, - _status: elementSchema.optional(), - subject: referenceSchema, - derivedFrom: referenceSchema.array().optional(), - timingTiming: timingSchema.optional(), - timingPeriod: periodSchema.optional(), - timingDateTime: dateTimeSchema.optional(), - _timingDateTime: elementSchema.optional(), - recordedOn: dateTimeSchema.optional(), - _recordedOn: elementSchema.optional(), - source: referenceSchema.optional(), - device: referenceSchema, - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - bodySite: codeableConceptSchema.optional(), - note: annotationSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR DeviceUseStatement resource. - */ -export const deviceUseStatementSchema: ZodType = - untypedDeviceUseStatementSchema - -/** - * Wrapper class for FHIR DeviceUseStatement resources. - * Provides utility methods for working with device usage records. - */ -export class FhirDeviceUseStatement extends FhirDomainResource { - // Static Functions - - /** - * Parses a DeviceUseStatement resource from unknown data. - * - * @param value - The data to parse and validate against the DeviceUseStatement schema - * @returns A FhirDeviceUseStatement instance containing the validated resource - */ - public static parse(value: unknown): FhirDeviceUseStatement { - return new FhirDeviceUseStatement(deviceUseStatementSchema.parse(value)) - } - - /** - * Get the recorded date as a Date object. - * @returns The recorded date, or undefined if not set - */ - public get recordedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.recordedOn) - } - - /** - * Get the timing date/time if specified as a dateTime. - * @returns The timing date, or undefined if not set or not a dateTime - */ - public get timingDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.timingDateTime) - } - - /** - * Get the timing period start date. - * @returns The start date, or undefined if not set - */ - public get timingPeriodStart(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.timingPeriod?.start) - } - - /** - * Get the timing period end date. - * @returns The end date, or undefined if not set - */ - public get timingPeriodEnd(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.timingPeriod?.end) - } - - /** - * Get body site display text. - * @returns Body site display text - */ - public get bodySiteDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.bodySite) - } - - /** - * Get reason displays from reason codes. - * @returns Array of reason display texts - */ - public get reasonDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.reasonCode) - } - - /** - * Get note texts from the device use statement. - * @returns Array of note texts - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/diagnosticReport.ts b/packages/fhir/src/resources/diagnosticReport.ts deleted file mode 100644 index 57b5b14b..00000000 --- a/packages/fhir/src/resources/diagnosticReport.ts +++ /dev/null @@ -1,133 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type DiagnosticReportMedia, type DiagnosticReport } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - attachmentSchema, - backboneElementSchema, - codeableConceptSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { diagnosticReportStatusSchema } from '../valueSets/index.js' - -const diagnosticReportMediaSchema: ZodType = - backboneElementSchema.extend({ - comment: stringSchema.optional(), - _comment: elementSchema.optional(), - link: referenceSchema, - }) - -/** - * Zod schema for FHIR DiagnosticReport resource (untyped version). - */ -export const untypedDiagnosticReportSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('DiagnosticReport').readonly(), - basedOn: referenceSchema.array().optional(), - category: codeableConceptSchema.array().optional(), - code: codeableConceptSchema, - conclusion: stringSchema.optional(), - _conclusion: elementSchema.optional(), - conclusionCode: codeableConceptSchema.array().optional(), - effectiveDateTime: stringSchema.optional(), - _effectiveDateTime: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - encounter: referenceSchema.optional(), - identifier: identifierSchema.array().optional(), - imagingStudy: referenceSchema.array().optional(), - issued: stringSchema.optional(), - _issued: elementSchema.optional(), - media: diagnosticReportMediaSchema.array().optional(), - performer: referenceSchema.array().optional(), - presentedForm: attachmentSchema.array().optional(), - result: referenceSchema.array().optional(), - resultsInterpreter: referenceSchema.array().optional(), - specimen: referenceSchema.array().optional(), - status: diagnosticReportStatusSchema, - _status: elementSchema.optional(), - subject: referenceSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR DiagnosticReport resource. - */ -export const diagnosticReportSchema: ZodType = - untypedDiagnosticReportSchema - -/** - * Wrapper class for FHIR DiagnosticReport resources. - * Provides utility methods for working with diagnostic reports. - */ -export class FhirDiagnosticReport extends FhirDomainResource { - // Static Functions - - /** - * Parses a DiagnosticReport resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirDiagnosticReport instance - */ - public static parse(value: unknown): FhirDiagnosticReport { - return new FhirDiagnosticReport(diagnosticReportSchema.parse(value)) - } - - // Properties - - /** - * Gets the effective date as a JavaScript Date object. - * - * @returns The effective date if available - */ - public get effectiveDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.effectiveDateTime) - } - - /** - * Gets the issued date as a JavaScript Date object. - * - * @returns The issued date if available - */ - public get issuedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.issued) - } - - /** - * Gets the code display text (type of report). - * - * @returns The code display - */ - public get codeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.code) - } - - /** - * Gets all conclusion code displays. - * - * @returns Array of conclusion code display texts - */ - public get conclusionDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.conclusionCode) - } - - /** - * Gets all category displays. - * - * @returns Array of category display texts - */ - public get categoryDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.category) - } -} diff --git a/packages/fhir/src/resources/documentManifest.ts b/packages/fhir/src/resources/documentManifest.ts deleted file mode 100644 index 13adb374..00000000 --- a/packages/fhir/src/resources/documentManifest.ts +++ /dev/null @@ -1,128 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type DocumentManifest, - type DocumentManifestRelated, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - referenceSchema, - stringSchema, - uriSchema, -} from '../elements/index.js' -import { documentManifestStatusSchema } from '../valueSets/index.js' - -const documentManifestRelatedSchema: ZodType = - backboneElementSchema.extend({ - identifier: identifierSchema.optional(), - ref: referenceSchema.optional(), - }) - -/** - * Zod schema for FHIR DocumentManifest resource (untyped version). - */ -export const untypedDocumentManifestSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('DocumentManifest').readonly(), - masterIdentifier: identifierSchema.optional(), - identifier: identifierSchema.array().optional(), - status: documentManifestStatusSchema, - _status: elementSchema.optional(), - type: codeableConceptSchema.optional(), - subject: referenceSchema.optional(), - created: dateTimeSchema.optional(), - _created: elementSchema.optional(), - author: referenceSchema.array().optional(), - recipient: referenceSchema.array().optional(), - source: uriSchema.optional(), - _source: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - content: referenceSchema.array(), - related: documentManifestRelatedSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR DocumentManifest resource. - */ -export const documentManifestSchema: ZodType = - untypedDocumentManifestSchema - -/** - * Wrapper class for FHIR DocumentManifest resources. - * Provides utility methods for working with document manifests and collections. - */ -export class FhirDocumentManifest extends FhirDomainResource { - // Static Functions - - /** - * Parses a DocumentManifest resource from unknown data. - * - * @param value - The data to parse and validate against the DocumentManifest schema - * @returns A FhirDocumentManifest instance containing the validated resource - */ - public static parse(value: unknown): FhirDocumentManifest { - return new FhirDocumentManifest(documentManifestSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/documentReference.ts b/packages/fhir/src/resources/documentReference.ts deleted file mode 100644 index 3801fcce..00000000 --- a/packages/fhir/src/resources/documentReference.ts +++ /dev/null @@ -1,187 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type DocumentReference, - type DocumentReferenceContent, - type DocumentReferenceContext, - type DocumentReferenceRelatesTo, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - attachmentSchema, - backboneElementSchema, - codeableConceptSchema, - codingSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - instantSchema, - periodSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { - documentReferenceDocStatusSchema, - documentReferenceRelatesToCodeSchema, - documentReferenceStatusSchema, -} from '../valueSets/index.js' - -const documentReferenceRelatesToSchema: ZodType = - backboneElementSchema.extend({ - code: documentReferenceRelatesToCodeSchema, - _code: elementSchema.optional(), - target: referenceSchema, - }) - -const documentReferenceContentSchema: ZodType = - backboneElementSchema.extend({ - attachment: attachmentSchema, - format: codingSchema.optional(), - }) - -const documentReferenceContextSchema: ZodType = - backboneElementSchema.extend({ - encounter: referenceSchema.array().optional(), - event: codeableConceptSchema.array().optional(), - period: periodSchema.optional(), - facilityType: codeableConceptSchema.optional(), - practiceSetting: codeableConceptSchema.optional(), - sourcePatientInfo: referenceSchema.optional(), - related: referenceSchema.array().optional(), - }) - -/** - * Zod schema for FHIR DocumentReference resource (untyped version). - */ -export const untypedDocumentReferenceSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('DocumentReference').readonly(), - masterIdentifier: identifierSchema.optional(), - identifier: identifierSchema.array().optional(), - status: documentReferenceStatusSchema, - _status: elementSchema.optional(), - docStatus: documentReferenceDocStatusSchema.optional(), - _docStatus: elementSchema.optional(), - type: codeableConceptSchema.optional(), - category: codeableConceptSchema.array().optional(), - subject: referenceSchema.optional(), - date: instantSchema.optional(), - _date: elementSchema.optional(), - author: referenceSchema.array().optional(), - authenticator: referenceSchema.optional(), - custodian: referenceSchema.optional(), - relatesTo: documentReferenceRelatesToSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - securityLabel: codeableConceptSchema.array().optional(), - content: documentReferenceContentSchema.array(), - context: documentReferenceContextSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR DocumentReference resource. - */ -export const documentReferenceSchema: ZodType = - untypedDocumentReferenceSchema - -/** - * Wrapper class for FHIR DocumentReference resources. - * Provides utility methods for working with document references. - */ -export class FhirDocumentReference extends FhirDomainResource { - // Static Functions - - /** - * Parses a DocumentReference resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirDocumentReference instance - */ - public static parse(value: unknown): FhirDocumentReference { - return new FhirDocumentReference(documentReferenceSchema.parse(value)) - } - - // Properties - - /** - * Gets the date as a JavaScript Date object. - * - * @returns The date if available - */ - public get date(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.date) - } - - /** - * Gets the type display text. - * - * @returns The type display - */ - public get typeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.type) - } - - /** - * Gets all category displays. - * - * @returns Array of category display texts - */ - public get categoryDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.category) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/encounter.ts b/packages/fhir/src/resources/encounter.ts deleted file mode 100644 index 01f6114f..00000000 --- a/packages/fhir/src/resources/encounter.ts +++ /dev/null @@ -1,250 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type EncounterClassHistory, - type EncounterDiagnosis, - type EncounterHospitalization, - type EncounterLocation, - type EncounterParticipant, - type EncounterStatusHistory, - type Encounter, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - codingSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - intSchema, - periodSchema, - quantitySchema, - referenceSchema, -} from '../elements/index.js' -import { - encounterStatusSchema, - encounterLocationStatusSchema, -} from '../valueSets/index.js' - -const encounterClassHistorySchema: ZodType = - backboneElementSchema.extend({ - class: codingSchema, - period: periodSchema, - }) - -const encounterDiagnosisSchema: ZodType = - backboneElementSchema.extend({ - condition: referenceSchema, - use: codeableConceptSchema.optional(), - rank: intSchema.optional(), - }) - -const encounterHospitalizationSchema: ZodType = - backboneElementSchema.extend({ - preAdmissionIdentifier: identifierSchema.optional(), - origin: referenceSchema.optional(), - admitSource: codeableConceptSchema.optional(), - reAdmission: codeableConceptSchema.optional(), - dietPreference: codeableConceptSchema.array().optional(), - specialCourtesy: codeableConceptSchema.array().optional(), - specialArrangement: codeableConceptSchema.array().optional(), - destination: referenceSchema.optional(), - dischargeDisposition: codeableConceptSchema.optional(), - }) - -const encounterLocationSchema: ZodType = - backboneElementSchema.extend({ - location: referenceSchema, - status: encounterLocationStatusSchema.optional(), - _status: elementSchema.optional(), - physicalType: codeableConceptSchema.optional(), - period: periodSchema.optional(), - }) - -const encounterParticipantSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.array().optional(), - period: periodSchema.optional(), - individual: referenceSchema.optional(), - }) - -const encounterStatusHistorySchema: ZodType = - backboneElementSchema.extend({ - status: encounterStatusSchema, - period: periodSchema, - }) - -/** - * Zod schema for FHIR Encounter resource (untyped version). - */ -export const untypedEncounterSchema = z.lazy( - () => - domainResourceSchema.extend({ - resourceType: z.literal('Encounter').readonly(), - account: referenceSchema.array().optional(), - appointment: referenceSchema.array().optional(), - basedOn: referenceSchema.array().optional(), - class: codingSchema, - classHistory: encounterClassHistorySchema.array().optional(), - diagnosis: encounterDiagnosisSchema.array().optional(), - episodeOfCare: referenceSchema.array().optional(), - hospitalization: encounterHospitalizationSchema.optional(), - identifier: identifierSchema.array().optional(), - length: quantitySchema.optional(), - location: encounterLocationSchema.array().optional(), - participant: encounterParticipantSchema.array().optional(), - partOf: referenceSchema.optional(), - period: periodSchema.optional(), - priority: codeableConceptSchema.optional(), - reasonReference: referenceSchema.array().optional(), - serviceProvider: referenceSchema.optional(), - status: encounterStatusSchema, - _status: elementSchema.optional(), - statusHistory: encounterStatusHistorySchema.array().optional(), - subject: referenceSchema.optional(), - type: codeableConceptSchema.array().optional(), - }) satisfies ZodType, -) - -/** - * Zod schema for FHIR Encounter resource. - */ -export const encounterSchema: ZodType = untypedEncounterSchema - -/** - * Wrapper class for FHIR Encounter resources. - * Provides utility methods for working with encounters and their periods. - */ -export class FhirEncounter extends FhirDomainResource { - // Static Functions - - /** - * Parses an Encounter resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirEncounter instance - */ - public static parse(value: unknown): FhirEncounter { - return new FhirEncounter(encounterSchema.parse(value)) - } - - // Properties - - /** - * Gets the encounter start date/time as a JavaScript Date object. - * - * @returns The start date if available, undefined otherwise - * - * @example - * ```typescript - * const startDate = encounter.startDate - * if (startDate) { - * console.log(`Encounter started: ${startDate.toLocaleString()}`) - * } - * ``` - */ - public get startDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.period?.start) - } - - /** - * Gets the encounter end date/time as a JavaScript Date object. - * - * @returns The end date if available, undefined otherwise - */ - public get endDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.period?.end) - } - - /** - * Checks if the encounter is currently in progress (has start but no end date). - * - * @returns true if the encounter is in progress - * - * @example - * ```typescript - * if (encounter.isInProgress) { - * console.log('Encounter is currently active') - * } - * ``` - */ - public get isInProgress(): boolean { - return this.startDate !== undefined && this.endDate === undefined - } - - /** - * Calculates the duration of the encounter in milliseconds. - * - * @returns Duration in milliseconds, or undefined if dates are incomplete - * - * @example - * ```typescript - * const duration = encounter.durationInMilliseconds - * if (duration) { - * const hours = duration / (1000 * 60 * 60) - * console.log(`Encounter lasted ${hours} hours`) - * } - * ``` - */ - public get duration(): number | undefined { - const start = this.startDate - const end = this.endDate - if (!start || !end) return undefined - return end.getTime() - start.getTime() - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/endpoint.ts b/packages/fhir/src/resources/endpoint.ts deleted file mode 100644 index c8a05326..00000000 --- a/packages/fhir/src/resources/endpoint.ts +++ /dev/null @@ -1,118 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type Endpoint } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - codeableConceptSchema, - codingSchema, - contactPointSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - referenceSchema, - stringSchema, - urlSchema, -} from '../elements/index.js' -import { endpointStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR Endpoint resource (untyped version). - */ -export const untypedEndpointSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Endpoint').readonly(), - identifier: identifierSchema.array().optional(), - status: endpointStatusSchema, - _status: elementSchema.optional(), - connectionType: codingSchema, - name: stringSchema.optional(), - _name: elementSchema.optional(), - managingOrganization: referenceSchema.optional(), - contact: contactPointSchema.array().optional(), - period: periodSchema.optional(), - payloadType: codeableConceptSchema.array(), - payloadMimeType: stringSchema.array().optional(), - _payloadMimeType: elementSchema.array().optional(), - address: urlSchema, - _address: elementSchema.optional(), - header: stringSchema.array().optional(), - _header: elementSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Endpoint resource. - */ -export const endpointSchema: ZodType = untypedEndpointSchema - -/** - * Wrapper class for FHIR Endpoint resources. - * Provides utility methods for working with technical endpoints and connectivity details. - */ -export class FhirEndpoint extends FhirDomainResource { - // Static Functions - - /** - * Parses an Endpoint resource from unknown data. - * - * @param value - The data to parse and validate against the Endpoint schema - * @returns A FhirEndpoint instance containing the validated resource - */ - public static parse(value: unknown): FhirEndpoint { - return new FhirEndpoint(endpointSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/enrollmentRequest.ts b/packages/fhir/src/resources/enrollmentRequest.ts deleted file mode 100644 index 6a731489..00000000 --- a/packages/fhir/src/resources/enrollmentRequest.ts +++ /dev/null @@ -1,107 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type EnrollmentRequest } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - elementSchema, - referenceSchema, - dateTimeSchema, -} from '../elements/index.js' -import { financialResourceStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR EnrollmentRequest resource (untyped version). - */ -export const untypedEnrollmentRequestSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('EnrollmentRequest').readonly(), - identifier: identifierSchema.array().optional(), - status: financialResourceStatusSchema.optional(), - _status: elementSchema.optional(), - created: dateTimeSchema.optional(), - _created: elementSchema.optional(), - insurer: referenceSchema.optional(), - provider: referenceSchema.optional(), - candidate: referenceSchema.optional(), - coverage: referenceSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR EnrollmentRequest resource. - */ -export const enrollmentRequestSchema: ZodType = - untypedEnrollmentRequestSchema - -/** - * Wrapper class for FHIR EnrollmentRequest resources. - * Provides utility methods for working with enrollment requests. - */ -export class FhirEnrollmentRequest extends FhirDomainResource { - // Static Functions - - /** - * Parses an EnrollmentRequest resource from unknown data. - * - * @param value - The data to parse and validate against the EnrollmentRequest schema - * @returns A FhirEnrollmentRequest instance containing the validated resource - */ - public static parse(value: unknown): FhirEnrollmentRequest { - return new FhirEnrollmentRequest(enrollmentRequestSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/enrollmentResponse.ts b/packages/fhir/src/resources/enrollmentResponse.ts deleted file mode 100644 index 2aea04f6..00000000 --- a/packages/fhir/src/resources/enrollmentResponse.ts +++ /dev/null @@ -1,114 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type EnrollmentResponse } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - elementSchema, - referenceSchema, - stringSchema, - dateTimeSchema, -} from '../elements/index.js' -import { - financialResourceStatusSchema, - remittanceOutcomeSchema, -} from '../valueSets/index.js' - -/** - * Zod schema for FHIR EnrollmentResponse resource (untyped version). - */ -export const untypedEnrollmentResponseSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('EnrollmentResponse').readonly(), - identifier: identifierSchema.array().optional(), - status: financialResourceStatusSchema.optional(), - _status: elementSchema.optional(), - request: referenceSchema.optional(), - outcome: remittanceOutcomeSchema.optional(), - _outcome: elementSchema.optional(), - disposition: stringSchema.optional(), - _disposition: elementSchema.optional(), - created: dateTimeSchema.optional(), - _created: elementSchema.optional(), - organization: referenceSchema.optional(), - requestProvider: referenceSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR EnrollmentResponse resource. - */ -export const enrollmentResponseSchema: ZodType = - untypedEnrollmentResponseSchema - -/** - * Wrapper class for FHIR EnrollmentResponse resources. - * Provides utility methods for working with enrollment responses. - */ -export class FhirEnrollmentResponse extends FhirDomainResource { - // Static Functions - - /** - * Parses an EnrollmentResponse resource from unknown data. - * - * @param value - The data to parse and validate against the EnrollmentResponse schema - * @returns A FhirEnrollmentResponse instance containing the validated resource - */ - public static parse(value: unknown): FhirEnrollmentResponse { - return new FhirEnrollmentResponse(enrollmentResponseSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/episodeOfCare.ts b/packages/fhir/src/resources/episodeOfCare.ts deleted file mode 100644 index 6eee09ff..00000000 --- a/packages/fhir/src/resources/episodeOfCare.ts +++ /dev/null @@ -1,146 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type EpisodeOfCareDiagnosis, - type EpisodeOfCareStatusHistory, - type EpisodeOfCare, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - domainResourceSchema, - identifierSchema, - periodSchema, - positiveIntSchema, - referenceSchema, -} from '../elements/index.js' -import { episodeOfCareStatusSchema } from '../valueSets/index.js' - -const episodeOfCareStatusHistorySchema: ZodType = - backboneElementSchema.extend({ - status: episodeOfCareStatusSchema, - period: periodSchema, - }) - -const episodeOfCareDiagnosisSchema: ZodType = - backboneElementSchema.extend({ - condition: referenceSchema, - role: codeableConceptSchema.optional(), - rank: positiveIntSchema.optional(), - }) - -/** - * Zod schema for FHIR EpisodeOfCare resource (untyped version). - */ -export const untypedEpisodeOfCareSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('EpisodeOfCare').readonly(), - identifier: identifierSchema.array().optional(), - status: episodeOfCareStatusSchema, - statusHistory: episodeOfCareStatusHistorySchema.array().optional(), - type: codeableConceptSchema.array().optional(), - diagnosis: episodeOfCareDiagnosisSchema.array().optional(), - patient: referenceSchema, - managingOrganization: referenceSchema.optional(), - period: periodSchema.optional(), - referralRequest: referenceSchema.array().optional(), - careManager: referenceSchema.optional(), - team: referenceSchema.array().optional(), - account: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR EpisodeOfCare resource. - */ -export const episodeOfCareSchema: ZodType = - untypedEpisodeOfCareSchema - -/** - * Wrapper class for FHIR EpisodeOfCare resources. - * Provides utility methods for working with episodes of care and their periods. - */ -export class FhirEpisodeOfCare extends FhirDomainResource { - /** - * Parses an EpisodeOfCare resource from unknown data. - * - * @param value - The data to parse and validate against the EpisodeOfCare schema - * @returns A FhirEpisodeOfCare instance containing the validated resource - */ - public static parse(value: unknown): FhirEpisodeOfCare { - return new FhirEpisodeOfCare(episodeOfCareSchema.parse(value)) - } - - /** - * Gets the episode period start date. - * - * @returns The start date if available, undefined otherwise - */ - public get periodStart(): Date | undefined { - return FhirDomainResource.parseDate(this.value.period?.start) - } - - /** - * Gets the episode period end date. - * - * @returns The end date if available, undefined otherwise - */ - public get periodEnd(): Date | undefined { - return FhirDomainResource.parseDate(this.value.period?.end) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/eventDefinition.ts b/packages/fhir/src/resources/eventDefinition.ts deleted file mode 100644 index 27f9c11d..00000000 --- a/packages/fhir/src/resources/eventDefinition.ts +++ /dev/null @@ -1,153 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type EventDefinition } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - booleanSchema, - codeableConceptSchema, - contactDetailSchema, - dateSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - markdownSchema, - periodSchema, - referenceSchema, - relatedArtifactSchema, - stringSchema, - triggerDefinitionSchema, - uriSchema, - usageContextSchema, -} from '../elements/index.js' -import { publicationStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR EventDefinition resource (untyped version). - */ -export const untypedEventDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('EventDefinition').readonly(), - url: uriSchema.optional(), - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - subtitle: stringSchema.optional(), - _subtitle: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - subjectCodeableConcept: codeableConceptSchema.optional(), - subjectReference: referenceSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - purpose: markdownSchema.optional(), - _purpose: elementSchema.optional(), - usage: stringSchema.optional(), - _usage: elementSchema.optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - approvalDate: dateSchema.optional(), - _approvalDate: elementSchema.optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - topic: codeableConceptSchema.array().optional(), - author: contactDetailSchema.array().optional(), - editor: contactDetailSchema.array().optional(), - reviewer: contactDetailSchema.array().optional(), - endorser: contactDetailSchema.array().optional(), - relatedArtifact: relatedArtifactSchema.array().optional(), - trigger: triggerDefinitionSchema.array(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR EventDefinition resource. - */ -export const eventDefinitionSchema: ZodType = - untypedEventDefinitionSchema - -/** - * Wrapper class for FHIR EventDefinition resources. - * Provides utility methods for working with event definitions that describe clinical or workflow events. - */ -export class FhirEventDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses an EventDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the EventDefinition schema - * @returns A FhirEventDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirEventDefinition { - return new FhirEventDefinition(eventDefinitionSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/evidence.ts b/packages/fhir/src/resources/evidence.ts deleted file mode 100644 index b4fabe8f..00000000 --- a/packages/fhir/src/resources/evidence.ts +++ /dev/null @@ -1,257 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Evidence, - type EvidenceVariableDefinition, - type EvidenceStatistic, - type EvidenceStatisticAttributeEstimate, - type EvidenceCertainty, - type EvidenceStatisticSampleSize, - type EvidenceStatisticModelCharacteristic, - type EvidenceStatisticModelCharacteristicVariable, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - codeableConceptSchema, - contactDetailSchema, - dateSchema, - dateTimeSchema, - decimalSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - markdownSchema, - quantitySchema, - rangeSchema, - referenceSchema, - relatedArtifactSchema, - stringSchema, - uriSchema, - usageContextSchema, - unsignedIntSchema, -} from '../elements/index.js' -import { - evidenceVariableHandlingSchema, - publicationStatusSchema, -} from '../valueSets/index.js' - -const evidenceVariableDefinitionSchema: ZodType = - backboneElementSchema.extend({ - description: markdownSchema.optional(), - _description: elementSchema.optional(), - note: annotationSchema.array().optional(), - variableRole: codeableConceptSchema, - observed: referenceSchema.optional(), - intended: referenceSchema.optional(), - directnessMatch: codeableConceptSchema.optional(), - }) - -const evidenceStatisticAttributeEstimateSchema: ZodType = - backboneElementSchema.extend({ - description: stringSchema.optional(), - _description: elementSchema.optional(), - note: annotationSchema.array().optional(), - type: codeableConceptSchema.optional(), - quantity: quantitySchema.optional(), - level: decimalSchema.optional(), - _level: elementSchema.optional(), - range: rangeSchema.optional(), - get attributeEstimate() { - return evidenceStatisticAttributeEstimateSchema.array().optional() - }, - }) - -const evidenceStatisticSampleSizeSchema: ZodType = - backboneElementSchema.extend({ - description: stringSchema.optional(), - _description: elementSchema.optional(), - note: annotationSchema.array().optional(), - numberOfStudies: unsignedIntSchema.optional(), - _numberOfStudies: elementSchema.optional(), - numberOfParticipants: unsignedIntSchema.optional(), - _numberOfParticipants: elementSchema.optional(), - knownDataCount: unsignedIntSchema.optional(), - _knownDataCount: elementSchema.optional(), - }) - -const evidenceStatisticModelCharacteristicVariableSchema: ZodType = - backboneElementSchema.extend({ - variableDefinition: referenceSchema, - handling: evidenceVariableHandlingSchema.optional(), - _handling: elementSchema.optional(), - valueCategory: codeableConceptSchema.array().optional(), - valueQuantity: quantitySchema.array().optional(), - valueRange: rangeSchema.array().optional(), - }) - -const evidenceStatisticModelCharacteristicSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema, - value: quantitySchema.optional(), - variable: evidenceStatisticModelCharacteristicVariableSchema - .array() - .optional(), - attributeEstimate: evidenceStatisticAttributeEstimateSchema - .array() - .optional(), - }) - -const evidenceStatisticSchema: ZodType = - backboneElementSchema.extend({ - description: stringSchema.optional(), - _description: elementSchema.optional(), - note: annotationSchema.array().optional(), - statisticType: codeableConceptSchema.optional(), - category: codeableConceptSchema.optional(), - quantity: quantitySchema.optional(), - numberOfEvents: unsignedIntSchema.optional(), - _numberOfEvents: elementSchema.optional(), - numberAffected: unsignedIntSchema.optional(), - _numberAffected: elementSchema.optional(), - sampleSize: evidenceStatisticSampleSizeSchema.optional(), - attributeEstimate: evidenceStatisticAttributeEstimateSchema - .array() - .optional(), - modelCharacteristic: evidenceStatisticModelCharacteristicSchema - .array() - .optional(), - }) - -const evidenceCertaintySchema: ZodType = - backboneElementSchema.extend({ - description: stringSchema.optional(), - _description: elementSchema.optional(), - note: annotationSchema.array().optional(), - type: codeableConceptSchema.optional(), - rating: codeableConceptSchema.optional(), - rater: stringSchema.optional(), - _rater: elementSchema.optional(), - get subcomponent() { - return evidenceCertaintySchema.array().optional() - }, - }) - -/** - * Zod schema for FHIR Evidence resource (untyped version). - */ -export const untypedEvidenceSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Evidence').readonly(), - url: uriSchema.optional(), - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - citeAsReference: referenceSchema.optional(), - citeAsMarkdown: markdownSchema.optional(), - _citeAsMarkdown: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - approvalDate: dateSchema.optional(), - _approvalDate: elementSchema.optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - author: contactDetailSchema.array().optional(), - editor: contactDetailSchema.array().optional(), - reviewer: contactDetailSchema.array().optional(), - endorser: contactDetailSchema.array().optional(), - relatedArtifact: relatedArtifactSchema.array().optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - assertion: markdownSchema.optional(), - _assertion: elementSchema.optional(), - note: annotationSchema.array().optional(), - variableDefinition: evidenceVariableDefinitionSchema.array(), - synthesisType: codeableConceptSchema.optional(), - studyType: codeableConceptSchema.optional(), - statistic: evidenceStatisticSchema.array().optional(), - certainty: evidenceCertaintySchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Evidence resource. - */ -export const evidenceSchema: ZodType = untypedEvidenceSchema - -/** - * Wrapper class for FHIR Evidence resources. - * Provides utility methods for working with evidence resources in evidence-based medicine. - */ -export class FhirEvidence extends FhirDomainResource { - // Static Functions - - /** - * Parses an Evidence resource from unknown data. - * - * @param value - The data to parse and validate against the Evidence schema - * @returns A FhirEvidence instance containing the validated resource - */ - public static parse(value: unknown): FhirEvidence { - return new FhirEvidence(evidenceSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/evidenceReport.ts b/packages/fhir/src/resources/evidenceReport.ts deleted file mode 100644 index a4772fcb..00000000 --- a/packages/fhir/src/resources/evidenceReport.ts +++ /dev/null @@ -1,148 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type EvidenceReport, - type EvidenceReportSubject, - type EvidenceReportSection, - type EvidenceReportSubjectCharacteristic, - type EvidenceReportRelatesTo, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactDetailSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - markdownSchema, - narrativeSchema, - periodSchema, - quantitySchema, - rangeSchema, - referenceSchema, - relatedArtifactSchema, - stringSchema, - uriSchema, - usageContextSchema, -} from '../elements/index.js' -import { - evidenceReportRelatesToCodeSchema, - publicationStatusSchema, - sectionModeSchema, -} from '../valueSets/index.js' - -const evidenceReportSubjectCharacteristicSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema, - valueReference: referenceSchema.optional(), - valueCodeableConcept: codeableConceptSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueRange: rangeSchema.optional(), - exclude: booleanSchema.optional(), - _exclude: elementSchema.optional(), - period: periodSchema.optional(), - }) - -const evidenceReportSubjectSchema: ZodType = - backboneElementSchema.extend({ - characteristic: evidenceReportSubjectCharacteristicSchema - .array() - .optional(), - note: annotationSchema.array().optional(), - }) - -const evidenceReportSectionSchema: ZodType = - backboneElementSchema.extend({ - title: stringSchema.optional(), - _title: elementSchema.optional(), - focus: codeableConceptSchema.optional(), - focusReference: referenceSchema.optional(), - author: referenceSchema.array().optional(), - text: narrativeSchema.optional(), - mode: sectionModeSchema.optional(), - _mode: elementSchema.optional(), - orderedBy: codeableConceptSchema.optional(), - entryClassifier: codeableConceptSchema.array().optional(), - entryReference: referenceSchema.array().optional(), - entryQuantity: quantitySchema.array().optional(), - emptyReason: codeableConceptSchema.optional(), - get section() { - return evidenceReportSectionSchema.array().optional() - }, - }) - -const evidenceReportRelatesToSchema: ZodType = - backboneElementSchema.extend({ - code: evidenceReportRelatesToCodeSchema, - _code: elementSchema.optional(), - targetIdentifier: identifierSchema.optional(), - targetReference: referenceSchema.optional(), - }) - -/** - * Zod schema for FHIR EvidenceReport resource (untyped version). - */ -export const untypedEvidenceReportSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('EvidenceReport').readonly(), - url: uriSchema.optional(), - _url: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - identifier: identifierSchema.array().optional(), - relatedIdentifier: identifierSchema.array().optional(), - citeAsReference: referenceSchema.optional(), - citeAsMarkdown: markdownSchema.optional(), - _citeAsMarkdown: elementSchema.optional(), - type: codeableConceptSchema.optional(), - note: annotationSchema.array().optional(), - relatedArtifact: relatedArtifactSchema.array().optional(), - subject: evidenceReportSubjectSchema, - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - author: contactDetailSchema.array().optional(), - editor: contactDetailSchema.array().optional(), - reviewer: contactDetailSchema.array().optional(), - endorser: contactDetailSchema.array().optional(), - relatesTo: evidenceReportRelatesToSchema.array().optional(), - section: evidenceReportSectionSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR EvidenceReport resource. - */ -export const evidenceReportSchema: ZodType = - untypedEvidenceReportSchema - -/** - * Wrapper class for FHIR EvidenceReport resources. - * Provides utility methods for working with evidence reports that compile and present research findings. - */ -export class FhirEvidenceReport extends FhirDomainResource { - // Static Functions - - /** - * Parses an EvidenceReport resource from unknown data. - * - * @param value - The data to parse and validate against the EvidenceReport schema - * @returns A FhirEvidenceReport instance containing the validated resource - */ - public static parse(value: unknown): FhirEvidenceReport { - return new FhirEvidenceReport(evidenceReportSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/evidenceVariable.ts b/packages/fhir/src/resources/evidenceVariable.ts deleted file mode 100644 index 13c817a2..00000000 --- a/packages/fhir/src/resources/evidenceVariable.ts +++ /dev/null @@ -1,206 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type EvidenceVariableCategory, - type EvidenceVariableCharacteristicTimeFromStart, - type EvidenceVariable, - type EvidenceVariableCharacteristic, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - booleanSchema, - canonicalSchema, - codeableConceptSchema, - contactDetailSchema, - dataRequirementSchema, - dateSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - expressionSchema, - identifierSchema, - markdownSchema, - periodSchema, - quantitySchema, - rangeSchema, - referenceSchema, - relatedArtifactSchema, - stringSchema, - triggerDefinitionSchema, - uriSchema, - usageContextSchema, -} from '../elements/index.js' -import { - evidenceVariableHandlingSchema, - groupMeasureSchema, - publicationStatusSchema, -} from '../valueSets/index.js' - -const evidenceVariableCharacteristicTimeFromStartSchema: ZodType = - backboneElementSchema.extend({ - description: stringSchema.optional(), - _description: elementSchema.optional(), - quantity: quantitySchema.optional(), - range: rangeSchema.optional(), - note: annotationSchema.array().optional(), - }) - -const evidenceVariableCharacteristicSchema: ZodType = - backboneElementSchema.extend({ - description: stringSchema.optional(), - _description: elementSchema.optional(), - definitionReference: referenceSchema.optional(), - definitionCanonical: canonicalSchema.optional(), - _definitionCanonical: elementSchema.optional(), - definitionCodeableConcept: codeableConceptSchema.optional(), - definitionExpression: expressionSchema.optional(), - definitionDataRequirement: dataRequirementSchema.optional(), - definitionTriggerDefinition: triggerDefinitionSchema.optional(), - method: codeableConceptSchema.optional(), - device: referenceSchema.optional(), - exclude: booleanSchema.optional(), - _exclude: elementSchema.optional(), - timeFromStart: evidenceVariableCharacteristicTimeFromStartSchema.optional(), - groupMeasure: groupMeasureSchema.optional(), - _groupMeasure: elementSchema.optional(), - }) - -const evidenceVariableCategorySchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema.optional(), - _name: elementSchema.optional(), - valueCodeableConcept: codeableConceptSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueRange: rangeSchema.optional(), - }) - -/** - * Zod schema for FHIR EvidenceVariable resource (untyped version). - */ -export const untypedEvidenceVariableSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('EvidenceVariable').readonly(), - url: uriSchema.optional(), - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - shortTitle: stringSchema.optional(), - _shortTitle: elementSchema.optional(), - subtitle: stringSchema.optional(), - _subtitle: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - note: annotationSchema.array().optional(), - useContext: usageContextSchema.array().optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - approvalDate: dateSchema.optional(), - _approvalDate: elementSchema.optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - author: contactDetailSchema.array().optional(), - editor: contactDetailSchema.array().optional(), - reviewer: contactDetailSchema.array().optional(), - endorser: contactDetailSchema.array().optional(), - relatedArtifact: relatedArtifactSchema.array().optional(), - actual: booleanSchema.optional(), - _actual: elementSchema.optional(), - characteristic: evidenceVariableCharacteristicSchema.array().optional(), - handling: evidenceVariableHandlingSchema.optional(), - _handling: elementSchema.optional(), - category: evidenceVariableCategorySchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR EvidenceVariable resource. - */ -export const evidenceVariableSchema: ZodType = - untypedEvidenceVariableSchema - -/** - * Wrapper class for FHIR EvidenceVariable resources. - * Provides utility methods for working with evidence variables used in research and clinical studies. - */ -export class FhirEvidenceVariable extends FhirDomainResource { - // Static Functions - - /** - * Parses an EvidenceVariable resource from unknown data. - * - * @param value - The data to parse and validate against the EvidenceVariable schema - * @returns A FhirEvidenceVariable instance containing the validated resource - */ - public static parse(value: unknown): FhirEvidenceVariable { - return new FhirEvidenceVariable(evidenceVariableSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/exampleScenario.ts b/packages/fhir/src/resources/exampleScenario.ts deleted file mode 100644 index 12702a32..00000000 --- a/packages/fhir/src/resources/exampleScenario.ts +++ /dev/null @@ -1,248 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ExampleScenarioActor, - type ExampleScenarioInstance, - type ExampleScenarioInstanceContainedInstance, - type ExampleScenarioInstanceVersion, - type ExampleScenarioProcessStepAlternative, - type ExampleScenarioProcessStepOperation, - type ExampleScenario, - type ExampleScenarioProcess, - type ExampleScenarioProcessStep, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactDetailSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - markdownSchema, - stringSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - exampleScenarioActorTypeSchema, - fhirResourceTypeSchema, - publicationStatusSchema, -} from '../valueSets/index.js' - -const exampleScenarioInstanceContainedInstanceSchema: ZodType = - backboneElementSchema.extend({ - resourceId: stringSchema, - _resourceId: elementSchema.optional(), - versionId: stringSchema.optional(), - _versionId: elementSchema.optional(), - }) - -const exampleScenarioProcessStepAlternativeSchema: ZodType = - backboneElementSchema.extend({ - description: markdownSchema.optional(), - _description: elementSchema.optional(), - get step() { - return exampleScenarioProcessStepSchema.array().optional() - }, - title: stringSchema, - _title: elementSchema.optional(), - }) - -const exampleScenarioProcessStepOperationSchema: ZodType = - backboneElementSchema.extend({ - description: markdownSchema.optional(), - _description: elementSchema.optional(), - initiator: stringSchema.optional(), - _initiator: elementSchema.optional(), - initiatorActive: booleanSchema.optional(), - _initiatorActive: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - number: stringSchema, - _number: elementSchema.optional(), - receiver: stringSchema.optional(), - _receiver: elementSchema.optional(), - receiverActive: booleanSchema.optional(), - _receiverActive: elementSchema.optional(), - request: exampleScenarioInstanceContainedInstanceSchema.optional(), - response: exampleScenarioInstanceContainedInstanceSchema.optional(), - type: stringSchema.optional(), - _type: elementSchema.optional(), - }) - -const exampleScenarioProcessStepSchema: ZodType = - backboneElementSchema.extend({ - alternative: exampleScenarioProcessStepAlternativeSchema.array().optional(), - operation: exampleScenarioProcessStepOperationSchema.optional(), - pause: booleanSchema.optional(), - _pause: elementSchema.optional(), - get process() { - return exampleScenarioProcessSchema.array().optional() - }, - }) - -const exampleScenarioProcessSchema: ZodType = - backboneElementSchema.extend({ - description: markdownSchema.optional(), - _description: elementSchema.optional(), - postConditions: markdownSchema.optional(), - _postConditions: elementSchema.optional(), - preConditions: markdownSchema.optional(), - _preConditions: elementSchema.optional(), - step: exampleScenarioProcessStepSchema.array().optional(), - title: stringSchema, - _title: elementSchema.optional(), - }) - -const exampleScenarioActorSchema: ZodType = - backboneElementSchema.extend({ - actorId: stringSchema, - _actorId: elementSchema.optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - type: exampleScenarioActorTypeSchema, - _type: elementSchema.optional(), - }) - -const exampleScenarioInstanceVersionSchema: ZodType = - backboneElementSchema.extend({ - description: markdownSchema, - _description: elementSchema.optional(), - versionId: stringSchema, - _versionId: elementSchema.optional(), - }) - -const exampleScenarioInstanceSchema: ZodType = - backboneElementSchema.extend({ - containedInstance: exampleScenarioInstanceContainedInstanceSchema - .array() - .optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - resourceId: stringSchema, - _resourceId: elementSchema.optional(), - resourceType: fhirResourceTypeSchema, - _resourceType: elementSchema.optional(), - version: exampleScenarioInstanceVersionSchema.array().optional(), - }) - -/** - * Zod schema for FHIR ExampleScenario resource (untyped version). - */ -export const untypedExampleScenarioSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ExampleScenario').readonly(), - actor: exampleScenarioActorSchema.array().optional(), - contact: contactDetailSchema.array().optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - instance: exampleScenarioInstanceSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - process: exampleScenarioProcessSchema.array().optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - purpose: markdownSchema.optional(), - _purpose: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - url: urlSchema.optional(), - _url: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - workflow: urlSchema.array().optional(), - _workflow: elementSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ExampleScenario resource. - */ -export const exampleScenarioSchema: ZodType = - untypedExampleScenarioSchema - -/** - * Wrapper class for FHIR ExampleScenario resources. - * Provides utility methods for working with example scenarios that demonstrate workflow patterns. - */ -export class FhirExampleScenario extends FhirDomainResource { - // Static Functions - - /** - * Parses an ExampleScenario resource from unknown data. - * - * @param value - The data to parse and validate against the ExampleScenario schema - * @returns A FhirExampleScenario instance containing the validated resource - */ - public static parse(value: unknown): FhirExampleScenario { - return new FhirExampleScenario(exampleScenarioSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/explanationOfBenefit.ts b/packages/fhir/src/resources/explanationOfBenefit.ts deleted file mode 100644 index 042f3329..00000000 --- a/packages/fhir/src/resources/explanationOfBenefit.ts +++ /dev/null @@ -1,473 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ExplanationOfBenefitAccident, - type ExplanationOfBenefitAddItem, - type ExplanationOfBenefitBenefitBalance, - type ExplanationOfBenefitBenefitBalanceFinancial, - type ExplanationOfBenefitCareTeam, - type ExplanationOfBenefitDiagnosis, - type ExplanationOfBenefitInsurance, - type ExplanationOfBenefitItem, - type ExplanationOfBenefitItemAdjudication, - type ExplanationOfBenefitItemDetail, - type ExplanationOfBenefitPayee, - type ExplanationOfBenefitPayment, - type ExplanationOfBenefitProcedure, - type ExplanationOfBenefitProcessNote, - type ExplanationOfBenefitRelated, - type ExplanationOfBenefitSupportingInfo, - type ExplanationOfBenefitTotal, - type ExplanationOfBenefit, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - backboneElementSchema, - elementSchema, - codeableConceptSchema, - referenceSchema, - stringSchema, - dateTimeSchema, - periodSchema, - moneySchema, - decimalSchema, - positiveIntSchema, - booleanSchema, - dateSchema, - attachmentSchema, - addressSchema, - quantitySchema, - unsignedIntSchema, -} from '../elements/index.js' -import { - financialResourceStatusSchema, - remittanceOutcomeSchema, - claimUseSchema, - noteTypeSchema, -} from '../valueSets/index.js' - -const explanationOfBenefitRelatedSchema: ZodType = - backboneElementSchema.extend({ - claim: referenceSchema.optional(), - relationship: codeableConceptSchema.optional(), - reference: identifierSchema.optional(), - }) - -const explanationOfBenefitPayeeSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - party: referenceSchema.optional(), - }) - -const explanationOfBenefitCareTeamSchema: ZodType = - backboneElementSchema.extend({ - sequence: positiveIntSchema, - provider: referenceSchema, - responsible: booleanSchema.optional(), - _responsible: elementSchema.optional(), - role: codeableConceptSchema.optional(), - qualification: codeableConceptSchema.optional(), - }) - -const explanationOfBenefitSupportingInfoSchema: ZodType = - backboneElementSchema.extend({ - sequence: positiveIntSchema, - category: codeableConceptSchema, - code: codeableConceptSchema.optional(), - timingDate: dateSchema.optional(), - _timingDate: elementSchema.optional(), - timingPeriod: periodSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueAttachment: attachmentSchema.optional(), - valueReference: referenceSchema.optional(), - reason: codeableConceptSchema.optional(), - }) - -const explanationOfBenefitDiagnosisSchema: ZodType = - backboneElementSchema.extend({ - sequence: positiveIntSchema, - diagnosisCodeableConcept: codeableConceptSchema.optional(), - diagnosisReference: referenceSchema.optional(), - type: codeableConceptSchema.array().optional(), - onAdmission: codeableConceptSchema.optional(), - packageCode: codeableConceptSchema.optional(), - }) - -const explanationOfBenefitProcedureSchema: ZodType = - backboneElementSchema.extend({ - sequence: positiveIntSchema, - type: codeableConceptSchema.array().optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - procedureCodeableConcept: codeableConceptSchema.optional(), - procedureReference: referenceSchema.optional(), - udi: referenceSchema.array().optional(), - }) - -const explanationOfBenefitInsuranceSchema: ZodType = - backboneElementSchema.extend({ - focal: booleanSchema, - _focal: elementSchema.optional(), - coverage: referenceSchema, - preAuthRef: stringSchema.array().optional(), - _preAuthRef: elementSchema.array().optional(), - }) - -const explanationOfBenefitAccidentSchema: ZodType = - backboneElementSchema.extend({ - date: dateSchema.optional(), - _date: elementSchema.optional(), - type: codeableConceptSchema.optional(), - locationAddress: addressSchema.optional(), - locationReference: referenceSchema.optional(), - }) - -const explanationOfBenefitItemAdjudicationSchema: ZodType = - backboneElementSchema.extend({ - category: codeableConceptSchema, - reason: codeableConceptSchema.optional(), - amount: moneySchema.optional(), - value: decimalSchema.optional(), - }) - -const explanationOfBenefitItemDetailSchema: ZodType = - backboneElementSchema.extend({ - sequence: positiveIntSchema, - revenue: codeableConceptSchema.optional(), - category: codeableConceptSchema.optional(), - productOrService: codeableConceptSchema, - modifier: codeableConceptSchema.array().optional(), - programCode: codeableConceptSchema.array().optional(), - quantity: quantitySchema.optional(), - unitPrice: moneySchema.optional(), - factor: decimalSchema.optional(), - net: moneySchema.optional(), - udi: referenceSchema.array().optional(), - noteNumber: positiveIntSchema.array().optional(), - adjudication: explanationOfBenefitItemAdjudicationSchema.array().optional(), - }) - -const explanationOfBenefitItemSchema: ZodType = - backboneElementSchema.extend({ - sequence: positiveIntSchema, - careTeamSequence: positiveIntSchema.array().optional(), - diagnosisSequence: positiveIntSchema.array().optional(), - procedureSequence: positiveIntSchema.array().optional(), - informationSequence: positiveIntSchema.array().optional(), - revenue: codeableConceptSchema.optional(), - category: codeableConceptSchema.optional(), - productOrService: codeableConceptSchema, - modifier: codeableConceptSchema.array().optional(), - programCode: codeableConceptSchema.array().optional(), - servicedDate: dateSchema.optional(), - _servicedDate: elementSchema.optional(), - servicedPeriod: periodSchema.optional(), - locationCodeableConcept: codeableConceptSchema.optional(), - locationAddress: addressSchema.optional(), - locationReference: referenceSchema.optional(), - quantity: quantitySchema.optional(), - unitPrice: moneySchema.optional(), - factor: decimalSchema.optional(), - net: moneySchema.optional(), - udi: referenceSchema.array().optional(), - bodySite: codeableConceptSchema.optional(), - subSite: codeableConceptSchema.array().optional(), - encounter: referenceSchema.array().optional(), - noteNumber: positiveIntSchema.array().optional(), - adjudication: explanationOfBenefitItemAdjudicationSchema.array().optional(), - detail: explanationOfBenefitItemDetailSchema.array().optional(), - }) - -const explanationOfBenefitAddItemSchema: ZodType = - backboneElementSchema.extend({ - itemSequence: positiveIntSchema.array().optional(), - detailSequence: positiveIntSchema.array().optional(), - subdetailSequence: positiveIntSchema.array().optional(), - provider: referenceSchema.array().optional(), - productOrService: codeableConceptSchema, - modifier: codeableConceptSchema.array().optional(), - programCode: codeableConceptSchema.array().optional(), - servicedDate: dateSchema.optional(), - _servicedDate: elementSchema.optional(), - servicedPeriod: periodSchema.optional(), - locationCodeableConcept: codeableConceptSchema.optional(), - locationAddress: addressSchema.optional(), - locationReference: referenceSchema.optional(), - quantity: quantitySchema.optional(), - unitPrice: moneySchema.optional(), - factor: decimalSchema.optional(), - net: moneySchema.optional(), - bodySite: codeableConceptSchema.optional(), - subSite: codeableConceptSchema.array().optional(), - noteNumber: positiveIntSchema.array().optional(), - adjudication: explanationOfBenefitItemAdjudicationSchema.array(), - }) - -const explanationOfBenefitTotalSchema: ZodType = - backboneElementSchema.extend({ - category: codeableConceptSchema, - amount: moneySchema, - }) - -const explanationOfBenefitPaymentSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - adjustment: moneySchema.optional(), - adjustmentReason: codeableConceptSchema.optional(), - date: dateSchema.optional(), - _date: elementSchema.optional(), - amount: moneySchema.optional(), - identifier: identifierSchema.optional(), - }) - -const explanationOfBenefitProcessNoteSchema: ZodType = - backboneElementSchema.extend({ - number: positiveIntSchema.optional(), - type: noteTypeSchema.optional(), - _type: elementSchema.optional(), - text: stringSchema.optional(), - _text: elementSchema.optional(), - language: codeableConceptSchema.optional(), - }) - -const explanationOfBenefitBenefitBalanceFinancialSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - allowedUnsignedInt: unsignedIntSchema.optional(), - allowedString: stringSchema.optional(), - _allowedString: elementSchema.optional(), - allowedMoney: moneySchema.optional(), - usedUnsignedInt: unsignedIntSchema.optional(), - usedMoney: moneySchema.optional(), - }) - -const explanationOfBenefitBenefitBalanceSchema: ZodType = - backboneElementSchema.extend({ - category: codeableConceptSchema, - excluded: booleanSchema.optional(), - _excluded: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - network: codeableConceptSchema.optional(), - unit: codeableConceptSchema.optional(), - term: codeableConceptSchema.optional(), - financial: explanationOfBenefitBenefitBalanceFinancialSchema - .array() - .optional(), - }) - -/** - * Zod schema for FHIR ExplanationOfBenefit resource (untyped version). - */ -export const untypedExplanationOfBenefitSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ExplanationOfBenefit').readonly(), - identifier: identifierSchema.array().optional(), - status: financialResourceStatusSchema, - _status: elementSchema.optional(), - type: codeableConceptSchema, - subType: codeableConceptSchema.optional(), - use: claimUseSchema, - _use: elementSchema.optional(), - patient: referenceSchema, - billablePeriod: periodSchema.optional(), - created: dateTimeSchema, - _created: elementSchema.optional(), - enterer: referenceSchema.optional(), - insurer: referenceSchema, - provider: referenceSchema, - priority: codeableConceptSchema.optional(), - fundsReserveRequested: codeableConceptSchema.optional(), - fundsReserve: codeableConceptSchema.optional(), - related: explanationOfBenefitRelatedSchema.array().optional(), - prescription: referenceSchema.optional(), - originalPrescription: referenceSchema.optional(), - payee: explanationOfBenefitPayeeSchema.optional(), - referral: referenceSchema.optional(), - facility: referenceSchema.optional(), - claim: referenceSchema.optional(), - claimResponse: referenceSchema.optional(), - outcome: remittanceOutcomeSchema, - _outcome: elementSchema.optional(), - disposition: stringSchema.optional(), - _disposition: elementSchema.optional(), - preAuthRef: stringSchema.array().optional(), - _preAuthRef: elementSchema.array().optional(), - preAuthRefPeriod: periodSchema.array().optional(), - careTeam: explanationOfBenefitCareTeamSchema.array().optional(), - supportingInfo: explanationOfBenefitSupportingInfoSchema.array().optional(), - diagnosis: explanationOfBenefitDiagnosisSchema.array().optional(), - procedure: explanationOfBenefitProcedureSchema.array().optional(), - precedence: positiveIntSchema.optional(), - insurance: explanationOfBenefitInsuranceSchema.array(), - accident: explanationOfBenefitAccidentSchema.optional(), - item: explanationOfBenefitItemSchema.array().optional(), - addItem: explanationOfBenefitAddItemSchema.array().optional(), - adjudication: explanationOfBenefitItemAdjudicationSchema.array().optional(), - total: explanationOfBenefitTotalSchema.array().optional(), - payment: explanationOfBenefitPaymentSchema.optional(), - formCode: codeableConceptSchema.optional(), - form: attachmentSchema.optional(), - processNote: explanationOfBenefitProcessNoteSchema.array().optional(), - benefitPeriod: periodSchema.optional(), - benefitBalance: explanationOfBenefitBenefitBalanceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ExplanationOfBenefit resource. - */ -export const explanationOfBenefitSchema: ZodType = - untypedExplanationOfBenefitSchema - -/** - * Wrapper class for FHIR ExplanationOfBenefit resources. - * Provides utility methods for working with explanation of benefit claims and adjudications. - */ -export class FhirExplanationOfBenefit extends FhirDomainResource { - // Static Functions - - /** - * Parses an ExplanationOfBenefit resource from unknown data. - * - * @param value - The data to parse and validate against the ExplanationOfBenefit schema - * @returns A FhirExplanationOfBenefit instance containing the validated resource - */ - public static parse(value: unknown): FhirExplanationOfBenefit { - return new FhirExplanationOfBenefit(explanationOfBenefitSchema.parse(value)) - } - - /** - * Get the created date as a Date object. - * @returns The created date - */ - public get createdDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.created) - } - - /** - * Get the billable period start date. - * @returns The start date, or undefined if not set - */ - public get billablePeriodStart(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.billablePeriod?.start) - } - - /** - * Get the billable period end date. - * @returns The end date, or undefined if not set - */ - public get billablePeriodEnd(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.billablePeriod?.end) - } - - /** - * Get type display text. - * @returns Type display text - */ - public get typeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.type) - } - - /** - * Get subtype display text. - * @returns Subtype display text - */ - public get subTypeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.subType) - } - - /** - * Get total amount for a specific category. - * @param categoryCode The category code to filter by - * @returns The money amount, or undefined if not found - */ - public getTotalByCategory(categoryCode: string): number | undefined { - const total = this.value.total?.find((t) => - t.category.coding?.some((c) => c.code === categoryCode), - ) - return total?.amount.value - } - - /** - * Get all diagnosis displays. - * @returns Array of diagnosis display texts - */ - public get diagnosisDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.diagnosis) - } - - /** - * Get all procedure displays. - * @returns Array of procedure display texts - */ - public get procedureDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays( - this.value.procedure?.flatMap((p) => - p.procedureCodeableConcept ? [p.procedureCodeableConcept] : [], - ), - ) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/familyMemberHistory.ts b/packages/fhir/src/resources/familyMemberHistory.ts deleted file mode 100644 index a71b8481..00000000 --- a/packages/fhir/src/resources/familyMemberHistory.ts +++ /dev/null @@ -1,189 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type FamilyMemberHistoryCondition, - type FamilyMemberHistory, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - dateSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - quantitySchema, - rangeSchema, - referenceSchema, - stringSchema, - uriSchema, -} from '../elements/index.js' -import { familyMemberHistoryStatusSchema } from '../valueSets/index.js' - -const familyMemberHistoryConditionSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema, - outcome: codeableConceptSchema.optional(), - contributedToDeath: booleanSchema.optional(), - _contributedToDeath: elementSchema.optional(), - onsetAge: quantitySchema.optional(), - onsetRange: rangeSchema.optional(), - onsetPeriod: periodSchema.optional(), - onsetString: stringSchema.optional(), - _onsetString: elementSchema.optional(), - note: annotationSchema.array().optional(), - }) - -/** - * Zod schema for FHIR FamilyMemberHistory resource (untyped version). - */ -export const untypedFamilyMemberHistorySchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('FamilyMemberHistory').readonly(), - identifier: identifierSchema.array().optional(), - instantiatesCanonical: uriSchema.array().optional(), - _instantiatesCanonical: elementSchema.array().optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: elementSchema.array().optional(), - status: familyMemberHistoryStatusSchema, - _status: elementSchema.optional(), - dataAbsentReason: codeableConceptSchema.optional(), - patient: referenceSchema, - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - relationship: codeableConceptSchema, - sex: codeableConceptSchema.optional(), - bornPeriod: periodSchema.optional(), - bornDate: dateSchema.optional(), - _bornDate: elementSchema.optional(), - bornString: stringSchema.optional(), - _bornString: elementSchema.optional(), - ageAge: quantitySchema.optional(), - ageRange: rangeSchema.optional(), - ageString: stringSchema.optional(), - _ageString: elementSchema.optional(), - estimatedAge: booleanSchema.optional(), - _estimatedAge: elementSchema.optional(), - deceasedBoolean: booleanSchema.optional(), - _deceasedBoolean: elementSchema.optional(), - deceasedAge: quantitySchema.optional(), - deceasedRange: rangeSchema.optional(), - deceasedDate: dateSchema.optional(), - _deceasedDate: elementSchema.optional(), - deceasedString: stringSchema.optional(), - _deceasedString: elementSchema.optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - condition: familyMemberHistoryConditionSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR FamilyMemberHistory resource. - */ -export const familyMemberHistorySchema: ZodType = - untypedFamilyMemberHistorySchema - -/** - * Wrapper class for FHIR FamilyMemberHistory resources. - * Provides utility methods for working with family medical history records. - */ -export class FhirFamilyMemberHistory extends FhirDomainResource { - /** - * Parses a FamilyMemberHistory resource from unknown data. - * - * @param value - The data to parse and validate against the FamilyMemberHistory schema - * @returns A FhirFamilyMemberHistory instance containing the validated resource - */ - public static parse(value: unknown): FhirFamilyMemberHistory { - return new FhirFamilyMemberHistory(familyMemberHistorySchema.parse(value)) - } - - /** - * Gets the date when the history was recorded. - * - * @returns The recorded date if available, undefined otherwise - */ - public get date(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.date) - } - - /** - * Gets the family member's birth date. - * - * @returns The birth date if available, undefined otherwise - */ - public get bornDate(): Date | undefined { - return FhirDomainResource.parseDate(this.value.bornDate) - } - - /** - * Gets the relationship display text. - * - * @returns The relationship display text - */ - public get relationshipDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.relationship) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/fhirDomainResource.ts b/packages/fhir/src/resources/fhirDomainResource.ts deleted file mode 100644 index e9f2d09a..00000000 --- a/packages/fhir/src/resources/fhirDomainResource.ts +++ /dev/null @@ -1,636 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type CodeableConcept, - type Coding, - type Extension, - type DomainResource, - type Reference, - type Identifier, - type HumanName, - type ContactPoint, - type Period, - type Annotation, - type FhirResource, -} from 'fhir/r4b.js' - -/** - * Base class for FHIR domain resource wrappers. - * Provides utility methods for common operations on FHIR resources. - * - * @template ResourceType - The specific FHIR resource type this class wraps - */ -export abstract class FhirDomainResource { - // Constructor - - /** - * Creates a new FhirDomainResource wrapper. - * - * @param value - The FHIR resource to wrap - */ - public constructor(public value: ResourceType) {} - - // Methods - - /** - * Extracts all codes from a CodeableConcept that match the given filter criteria. - * - * @param concept - The CodeableConcept to extract codes from - * @param system - The system to filter for - * @param version - Optional version to filter for - * @returns Array of code strings that match the filter - * - * @example - * ```typescript - * const codes = resource.codesBySystem(observation.code, 'http://loinc.org') - * // Returns: ['8867-4', '85354-9'] - * ``` - */ - public static codesBySystem( - concept: CodeableConcept | undefined, - system: string, - version?: string, - ): string[] { - return (concept?.coding ?? []).flatMap((coding) => { - if (coding.system !== system) return [] - if (version && coding.version !== version) return [] - return coding.code ? [coding.code] : [] - }) - } - - /** - * Checks if a CodeableConcept contains any of the specified codings. - * - * @param concept - The CodeableConcept to check - * @param filter - Array of Coding objects to match against - * @returns true if the concept contains at least one matching coding - * - * @example - * ```typescript - * const hasLoinc = resource.containsCoding(observation.code, [ - * { system: 'http://loinc.org', code: '8867-4' } - * ]) - * ``` - */ - public static containsCoding( - concept: CodeableConcept | undefined, - ...filter: Coding[] - ): boolean { - return filter.some((filterCoding) => - (concept?.coding ?? []).some((coding) => { - if (filterCoding.code && coding.code !== filterCoding.code) return false - if (filterCoding.system && coding.system !== filterCoding.system) - return false - if (filterCoding.version && coding.version !== filterCoding.version) - return false - return true - }), - ) - } - - /** - * Retrieves a contained resource by its ID. - * - * @template T - The type of the contained resource - * @param id - The ID of the contained resource to retrieve - * @returns The contained resource if found, undefined otherwise - * - * @example - * ```typescript - * const patient = resource.containedResource('patient-1') - * ``` - */ - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- The caller selects the expected contained FHIR resource subtype. - public containedResource( - id: string, - ): T | undefined { - return this.value.contained?.find((resource) => resource.id === id) as T - } - - /** - * Retrieves all extensions with a specific URL. - * - * @param url - The URL of the extensions to retrieve - * @returns Array of extensions with the specified URL - * - * @example - * ```typescript - * const extensions = resource.extensionsWithUrl('http://example.org/fhir/StructureDefinition/my-extension') - * ``` - */ - public extensionsByUrl(...url: string[]): Extension[] { - return (this.value.extension ?? []).filter( - (extension) => extension.url && url.includes(extension.url), - ) - } - - /** - * Checks if this resource is referenced by a given Reference. - * Compares the reference string against the resource's ID and fullUrl patterns. - * - * @param reference - The Reference to check - * @returns true if the reference points to this resource - * - * @example - * ```typescript - * const isReferenced = resource.isReferencedBy({ reference: 'Patient/123' }) - * ``` - */ - public isReferencedBy(reference: Reference): boolean { - if (!reference.reference) { - return false - } - - const resourceId = this.value.id - if (!resourceId) { - return false - } - - const resourceType = this.value.resourceType - - return ( - reference.reference === `${resourceType}/${resourceId}` || - reference.reference.endsWith(`/${resourceType}/${resourceId}`) || - reference.reference === resourceId - ) - } - - /** - * Decodes a base64-encoded string into a Uint8Array. - * Works in both browser and Node.js environments. - * This is a utility method that can be used for any base64Binary field in FHIR resources. - * - * @param base64String - The base64-encoded string to decode - * @returns A Uint8Array containing the decoded binary data, or undefined if input is undefined - * - * @example - * ```typescript - * const data = FhirDomainResource.decodeBase64Binary(attachment.data) - * if (data) { - * console.log(`Binary data size: ${data.length} bytes`) - * } - * ``` - */ - public static decodeBase64Binary( - base64String: string | undefined, - ): Uint8Array | undefined { - if (base64String === undefined) { - return undefined - } - - try { - // Determine the environment and decode accordingly - if (typeof Buffer !== 'undefined') { - // Node.js environment - return new Uint8Array(Buffer.from(base64String, 'base64')) - } else if (typeof atob === 'function') { - // Browser environment - const binaryString = atob(base64String) - const bytes = new Uint8Array(binaryString.length) - for (let i = 0; i < binaryString.length; i++) { - bytes[i] = binaryString.charCodeAt(i) - } - return bytes - } else { - throw new Error( - 'No base64 decoding method available in this environment', - ) - } - } catch (error) { - throw new Error( - `Failed to decode base64 data: ${error instanceof Error ? error.message : String(error)}`, - { cause: error }, - ) - } - } - - /** - * Generic method to get identifiers by system from an array of identifiers. - * Can be used by any resource that has an identifier field. - * - * @param identifiers - Array of identifiers to search - * @param system - The system URL to filter by - * @returns Array of identifier values matching the system - * - * @example - * ```typescript - * const mrns = FhirDomainResource.getIdentifiersBySystem( - * patient.identifier, - * 'http://hospital.org/mrn' - * ) - * ``` - */ - public static identifiersBySystem( - identifiers: Identifier[] | undefined, - ...system: string[] - ): string[] { - return (identifiers ?? []) - .filter((id) => id.system && system.includes(id.system)) - .flatMap((id) => (id.value ? [id.value] : [])) - } - - /** - * Gets an identifier by system from an array of identifiers (returns first match). - * - * @param identifiers - Array of identifiers to search - * @param system - The system URL to filter by - * @returns The first identifier value matching the system, or undefined - * - * @example - * ```typescript - * const mrn = FhirDomainResource.getIdentifierBySystem( - * patient.identifier, - * 'http://hospital.org/mrn' - * ) - * ``` - */ - public static identifierBySystem( - identifiers: Identifier[] | undefined, - ...system: string[] - ): string | undefined { - return identifiers?.find((id) => id.system && system.includes(id.system)) - ?.value - } - - /** - * Gets all identifiers by type from an array of identifiers. - * - * @param identifiers - Array of identifiers to search - * @param types - One or more Coding objects representing identifier types to match - * @returns Array of identifier values matching any of the specified types - * - * @example - * ```typescript - * const passports = FhirDomainResource.identifiersByType( - * patient.identifier, - * { system: 'http://terminology.hl7.org/CodeSystem/v2-0203', code: 'PPN' } - * ) - * ``` - */ - public static identifiersByType( - identifiers: Identifier[] | undefined, - ...types: Coding[] - ): string[] { - return (identifiers ?? []) - .filter((id) => this.containsCoding(id.type, ...types)) - .flatMap((id) => (id.value ? [id.value] : [])) - } - - /** - * Gets the first identifier by type from an array of identifiers. - * - * @param identifiers - Array of identifiers to search - * @param types - One or more Coding objects representing identifier types to match - * @returns The first identifier value matching any of the specified types, or undefined - * - * @example - * ```typescript - * const passport = FhirDomainResource.identifierByType( - * patient.identifier, - * { system: 'http://terminology.hl7.org/CodeSystem/v2-0203', code: 'PPN' } - * ) - * ``` - */ - public static identifierByType( - identifiers: Identifier[] | undefined, - ...types: Coding[] - ): string | undefined { - return identifiers?.find((id) => this.containsCoding(id.type, ...types)) - ?.value - } - - /** - * Extracts name parts from a HumanName into an array. - * - * @param name - The HumanName to extract parts from - * @param includePrefix - Whether to include prefix (e.g., "Dr.") - * @param includeSuffix - Whether to include suffix (e.g., "Jr.") - * @returns Array of name parts in order - * - * @example - * ```typescript - * const parts = FhirDomainResource.humanNameParts(name, true, true) - * // Returns: ['Dr.', 'John', 'Michael', 'Doe', 'Jr.'] - * ``` - */ - public static humanNameParts( - name: HumanName | undefined, - includePrefix = false, - includeSuffix = false, - ): string[] { - if (!name) return [] - - const parts: string[] = [] - - if (includePrefix && name.prefix) { - parts.push(...name.prefix) - } - - if (name.given) { - parts.push(...name.given) - } - - if (name.family) { - parts.push(name.family) - } - - if (includeSuffix && name.suffix) { - parts.push(...name.suffix) - } - - return parts - } - - /** - * Generic method to format a HumanName as a full name string. - * - * @param name - The HumanName to format - * @param includePrefix - Whether to include prefix (e.g., "Dr.") - * @param includeSuffix - Whether to include suffix (e.g., "Jr.") - * @returns Formatted name string - * - * @example - * ```typescript - * const fullName = FhirDomainResource.formatHumanName(name, true, true) - * // Returns: "Dr. John Michael Doe Jr." - * ``` - */ - public static formatHumanName( - name: HumanName | undefined, - includePrefix = false, - includeSuffix = false, - ): string | undefined { - const parts = this.humanNameParts(name, includePrefix, includeSuffix) - return parts.length > 0 ? parts.join(' ') : undefined - } - - /** - * Generic method to extract contact point value by system. - * - * @param telecom - Array of ContactPoints - * @param system - The system to filter by (e.g., 'phone', 'email') - * @param use - Optional use filter (e.g., 'home', 'work', 'mobile') - * @returns The contact point value if found - * - * @example - * ```typescript - * const workEmail = FhirDomainResource.getContactPointBySystem( - * patient.telecom, - * 'email', - * 'work' - * ) - * ``` - */ - public static contactPointBySystem( - telecom: ContactPoint[] | undefined, - system: ContactPoint['system'], - use?: ContactPoint['use'], - ): string | undefined { - if (!telecom) return undefined - - const filtered = telecom.filter((cp) => cp.system === system) - - if (use) { - const withUse = filtered.find((cp) => cp.use === use) - if (withUse) return withUse.value - } - - return filtered[0]?.value - } - - /** - * Generic method to get all contact points by system. - * - * @param telecom - Array of ContactPoints - * @param system - The system to filter by - * @returns Array of contact point values - */ - public static contactPointsBySystem( - telecom: ContactPoint[] | undefined, - system: ContactPoint['system'], - ): string[] { - return (telecom ?? []) - .filter((cp) => cp.system === system) - .flatMap((cp) => (cp.value ? [cp.value] : [])) - } - - /** - * Checks if a period is currently active (overlaps with current date/time). - * - * @param period - The Period to check - * @param asOfDate - Date to check against (defaults to now) - * @returns true if the period is active - * - * @example - * ```typescript - * if (FhirDomainResource.periodIsActive(practitioner.period)) { - * console.log('Practitioner is currently active') - * } - * ``` - */ - public static periodIsActive( - period: Period | undefined, - asOfDate: Date = new Date(), - ): boolean { - if (!period) return true // No period means always active - - const start = period.start ? new Date(period.start) : undefined - const end = period.end ? new Date(period.end) : undefined - - if (start && asOfDate < start) return false - if (end && asOfDate > end) return false - - return true - } - - /** - * Checks if a period overlaps with a date range. - * - * @param period - The Period to check - * @param rangeStart - Start of the date range - * @param rangeEnd - End of the date range - * @returns true if there is any overlap - */ - public static periodOverlaps( - period: Period | undefined, - rangeStart: Date, - rangeEnd: Date, - ): boolean { - if (!period) return false - - const periodStart = period.start ? new Date(period.start) : new Date(0) - const periodEnd = - period.end ? new Date(period.end) : new Date(8640000000000000) - - return periodStart <= rangeEnd && periodEnd >= rangeStart - } - - /** - * Converts a FHIR date string (YYYY-MM-DD) to a JavaScript Date object. - * - * @param dateString - FHIR date string - * @returns Date object or undefined - */ - public static parseDate(dateString: string | undefined): Date | undefined { - return dateString ? new Date(dateString) : undefined - } - - /** - * Converts a FHIR dateTime/instant string to a JavaScript Date object. - * - * @param dateTimeString - FHIR dateTime or instant string - * @returns Date object or undefined - */ - public static parseDateTime( - dateTimeString: string | undefined, - ): Date | undefined { - return dateTimeString ? new Date(dateTimeString) : undefined - } - - /** - * Formats a Date object to a FHIR date string (YYYY-MM-DD). - * - * @param date - Date object to format - * @returns FHIR date string - */ - public static formatDate(date: Date): string { - const day = String(date.getDate()).padStart(2, '0') - const month = String(date.getMonth() + 1).padStart(2, '0') - const year = String(date.getFullYear()).padStart(4, '0') - return `${year}-${month}-${day}` - } - - /** - * Formats a Date object to a FHIR dateTime string. - * - * @param date - Date object to format - * @returns FHIR dateTime string - */ - public static formatDateTime(date: Date): string { - return date.toISOString() - } - - /** - * Gets the text content from an Annotation array. - * - * @param annotations - Array of Annotations - * @returns Array of annotation texts - * - * @example - * ```typescript - * const notes = FhirDomainResource.getAnnotationTexts(condition.note) - * ``` - */ - public static annotationTexts( - annotations: Annotation[] | undefined, - ): string[] { - if (!annotations) return [] - const texts: string[] = [] - for (const annotation of annotations) { - if (annotation.text) { - texts.push(annotation.text) - } - } - return texts - } - - /** - * Gets the display text from a CodeableConcept. - * - * @param concept - The CodeableConcept - * @returns The text or first coding display, or undefined - */ - public static codeableConceptDisplay( - concept: CodeableConcept | undefined, - ): string | undefined { - if (concept?.text) return concept.text - for (const coding of concept?.coding ?? []) { - if (coding.display) { - return coding.display - } - } - return undefined - } - - /** - * Gets display texts from an array of CodeableConcepts. - * - * @param concept - Array of CodeableConcepts - * @returns Array of display texts (excludes undefined values) - * - * @example - * ```typescript - * const displays = FhirDomainResource.codeableConceptDisplays(condition.category) - * // Returns: ['Encounter Diagnosis', 'Problem List Item'] - * ``` - */ - public static codeableConceptDisplays( - concept: CodeableConcept[] | undefined, - ): string[] { - return (concept ?? []) - .map((concept) => this.codeableConceptDisplay(concept)) - .flatMap((display) => (display ? [display] : [])) - } - - /** - * Checks if a Reference points to a specific resource type. - * - * @param reference - The Reference to check - * @param resourceType - The resource type to check for - * @returns true if the reference is to the specified resource type - * - * @example - * ```typescript - * if (FhirDomainResource.isReferenceToType(observation.subject, 'Patient')) { - * console.log('Subject is a Patient') - * } - * ``` - */ - public static isReferenceToType( - reference: Reference | undefined, - resourceType: FhirResource['resourceType'], - ): boolean { - return reference?.reference?.startsWith(`${resourceType}/`) ?? false - } - - /** - * Gets all contained resources of a specific type. - * - * @template T - The type of resources to retrieve - * @param resourceType - The resource type to filter by - * @returns Array of contained resources of the specified type - * - * @example - * ```typescript - * const patients = bundle.containedResourcesByType('Patient') - * ``` - */ - public containedResourcesByType( - resourceType: T['resourceType'], - ): T[] { - return (this.value.contained ?? []).filter( - (resource) => resource.resourceType === resourceType, - ) as T[] - } - - /** - * Creates a Reference object for this resource. - * - * @param display - Optional display text for the reference - * @returns Reference object or undefined if no ID - */ - public toReference(display?: string): Reference | undefined { - if (!this.value.id) return undefined - - return { - reference: `${this.value.resourceType}/${this.value.id}`, - ...(display ? { display } : {}), - } - } -} diff --git a/packages/fhir/src/resources/fhirResource.ts b/packages/fhir/src/resources/fhirResource.ts deleted file mode 100644 index 45504d3b..00000000 --- a/packages/fhir/src/resources/fhirResource.ts +++ /dev/null @@ -1,300 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type FhirResource } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { untypedAccountSchema } from './account.js' -import { untypedActivityDefinitionSchema } from './activityDefinition.js' -import { untypedAdministrableProductDefinitionSchema } from './administrableProductDefinition.js' -import { untypedAdverseEventSchema } from './adverseEvent.js' -import { untypedAllergyIntoleranceSchema } from './allergyIntolerance.js' -import { untypedAppointmentSchema } from './appointment.js' -import { untypedAppointmentResponseSchema } from './appointmentResponse.js' -import { untypedAuditEventSchema } from './auditEvent.js' -import { untypedBasicSchema } from './basic.js' -import { untypedBinarySchema } from './binary.js' -import { untypedBiologicallyDerivedProductSchema } from './biologicallyDerivedProduct.js' -import { untypedBodyStructureSchema } from './bodyStructure.js' -import { untypedGenericBundleSchema } from './bundle.js' -import { untypedCapabilityStatementSchema } from './capabilityStatement.js' -import { untypedCarePlanSchema } from './carePlan.js' -import { untypedCareTeamSchema } from './careTeam.js' -import { untypedCatalogEntrySchema } from './catalogEntry.js' -import { untypedChargeItemSchema } from './chargeItem.js' -import { untypedChargeItemDefinitionSchema } from './chargeItemDefinition.js' -import { untypedCitationSchema } from './citation.js' -import { untypedClaimSchema } from './claim.js' -import { untypedClaimResponseSchema } from './claimResponse.js' -import { untypedClinicalImpressionSchema } from './clinicalImpression.js' -import { untypedClinicalUseDefinitionSchema } from './clinicalUseDefinition.js' -import { untypedCodeSystemSchema } from './codeSystem.js' -import { untypedCommunicationSchema } from './communication.js' -import { untypedCommunicationRequestSchema } from './communicationRequest.js' -import { untypedCompartmentDefinitionSchema } from './compartmentDefinition.js' -import { untypedCompositionSchema } from './composition.js' -import { untypedConceptMapSchema } from './conceptMap.js' -import { untypedConditionSchema } from './condition.js' -import { untypedConsentSchema } from './consent.js' -import { untypedContractSchema } from './contract.js' -import { untypedCoverageSchema } from './coverage.js' -import { untypedCoverageEligibilityRequestSchema } from './coverageEligibilityRequest.js' -import { untypedCoverageEligibilityResponseSchema } from './coverageEligibilityResponse.js' -import { untypedDetectedIssueSchema } from './detectedIssue.js' -import { untypedDeviceSchema } from './device.js' -import { untypedDeviceDefinitionSchema } from './deviceDefinition.js' -import { untypedDeviceMetricSchema } from './deviceMetric.js' -import { untypedDeviceRequestSchema } from './deviceRequest.js' -import { untypedDeviceUseStatementSchema } from './deviceUseStatement.js' -import { untypedDiagnosticReportSchema } from './diagnosticReport.js' -import { untypedDocumentManifestSchema } from './documentManifest.js' -import { untypedDocumentReferenceSchema } from './documentReference.js' -import { untypedEncounterSchema } from './encounter.js' -import { untypedEndpointSchema } from './endpoint.js' -import { untypedEnrollmentRequestSchema } from './enrollmentRequest.js' -import { untypedEnrollmentResponseSchema } from './enrollmentResponse.js' -import { untypedEpisodeOfCareSchema } from './episodeOfCare.js' -import { untypedEventDefinitionSchema } from './eventDefinition.js' -import { untypedEvidenceSchema } from './evidence.js' -import { untypedEvidenceReportSchema } from './evidenceReport.js' -import { untypedEvidenceVariableSchema } from './evidenceVariable.js' -import { untypedExampleScenarioSchema } from './exampleScenario.js' -import { untypedExplanationOfBenefitSchema } from './explanationOfBenefit.js' -import { untypedFamilyMemberHistorySchema } from './familyMemberHistory.js' -import { untypedFlagSchema } from './flag.js' -import { untypedGoalSchema } from './goal.js' -import { untypedGraphDefinitionSchema } from './graphDefinition.js' -import { untypedGroupSchema } from './group.js' -import { untypedGuidanceResponseSchema } from './guidanceResponse.js' -import { untypedHealthcareServiceSchema } from './healthcareService.js' -import { untypedImagingStudySchema } from './imagingStudy.js' -import { untypedImmunizationSchema } from './immunization.js' -import { untypedImmunizationEvaluationSchema } from './immunizationEvaluation.js' -import { untypedImmunizationRecommendationSchema } from './immunizationRecommendation.js' -import { untypedImplementationGuideSchema } from './implementationGuide.js' -import { untypedIngredientSchema } from './ingredient.js' -import { untypedInsurancePlanSchema } from './insurancePlan.js' -import { untypedInvoiceSchema } from './invoice.js' -import { untypedLibrarySchema } from './library.js' -import { untypedLinkageSchema } from './linkage.js' -import { untypedListSchema } from './list.js' -import { untypedLocationSchema } from './location.js' -import { untypedManufacturedItemDefinitionSchema } from './manufacturedItemDefinition.js' -import { untypedMeasureSchema } from './measure.js' -import { untypedMeasureReportSchema } from './measureReport.js' -import { untypedMediaSchema } from './media.js' -import { untypedMedicationSchema } from './medication.js' -import { untypedMedicationAdministrationSchema } from './medicationAdministration.js' -import { untypedMedicationDispenseSchema } from './medicationDispense.js' -import { untypedMedicationKnowledgeSchema } from './medicationKnowledge.js' -import { untypedMedicationRequestSchema } from './medicationRequest.js' -import { untypedMedicationStatementSchema } from './medicationStatement.js' -import { untypedMedicinalProductDefinitionSchema } from './medicinalProductDefinition.js' -import { untypedMessageDefinitionSchema } from './messageDefinition.js' -import { untypedMessageHeaderSchema } from './messageHeader.js' -import { untypedMolecularSequenceSchema } from './molecularSequence.js' -import { untypedNamingSystemSchema } from './namingSystem.js' -import { untypedNutritionOrderSchema } from './nutritionOrder.js' -import { untypedNutritionProductSchema } from './nutritionProduct.js' -import { untypedObservationSchema } from './observation.js' -import { untypedObservationDefinitionSchema } from './observationDefinition.js' -import { untypedOperationDefinitionSchema } from './operationDefinition.js' -import { untypedOperationOutcomeSchema } from './operationOutcome.js' -import { untypedOrganizationSchema } from './organization.js' -import { untypedOrganizationAffiliationSchema } from './organizationAffiliation.js' -import { untypedPackagedProductDefinitionSchema } from './packagedProductDefinition.js' -import { untypedParametersSchema } from './parameters.js' -import { untypedPatientSchema } from './patient.js' -import { untypedPaymentNoticeSchema } from './paymentNotice.js' -import { untypedPaymentReconciliationSchema } from './paymentReconciliation.js' -import { untypedPersonSchema } from './person.js' -import { untypedPlanDefinitionSchema } from './planDefinition.js' -import { untypedPractitionerSchema } from './practitioner.js' -import { untypedPractitionerRoleSchema } from './practitionerRole.js' -import { untypedProcedureSchema } from './procedure.js' -import { untypedProvenanceSchema } from './provenance.js' -import { untypedQuestionnaireSchema } from './questionnaire.js' -import { untypedQuestionnaireResponseSchema } from './questionnaireResponse.js' -import { untypedRegulatedAuthorizationSchema } from './regulatedAuthorization.js' -import { untypedRelatedPersonSchema } from './relatedPerson.js' -import { untypedRequestGroupSchema } from './requestGroup.js' -import { untypedResearchDefinitionSchema } from './researchDefinition.js' -import { untypedResearchElementDefinitionSchema } from './researchElementDefinition.js' -import { untypedResearchStudySchema } from './researchStudy.js' -import { untypedResearchSubjectSchema } from './researchSubject.js' -import { untypedRiskAssessmentSchema } from './riskAssessment.js' -import { untypedScheduleSchema } from './schedule.js' -import { untypedSearchParameterSchema } from './searchParameter.js' -import { untypedServiceRequestSchema } from './serviceRequest.js' -import { untypedSlotSchema } from './slot.js' -import { untypedSpecimenSchema } from './specimen.js' -import { untypedSpecimenDefinitionSchema } from './specimenDefinition.js' -import { untypedStructureDefinitionSchema } from './structureDefinition.js' -import { untypedStructureMapSchema } from './structureMap.js' -import { untypedSubscriptionSchema } from './subscription.js' -import { untypedSubscriptionStatusSchema } from './subscriptionStatus.js' -import { untypedSubscriptionTopicSchema } from './subscriptionTopic.js' -import { untypedSubstanceSchema } from './substance.js' -import { untypedSubstanceDefinitionSchema } from './substanceDefinition.js' -import { untypedSupplyDeliverySchema } from './supplyDelivery.js' -import { untypedSupplyRequestSchema } from './supplyRequest.js' -import { untypedTaskSchema } from './task.js' -import { untypedTerminologyCapabilitiesSchema } from './terminologyCapabilities.js' -import { untypedTestReportSchema } from './testReport.js' -import { untypedTestScriptSchema } from './testScript.js' -import { untypedValueSetSchema } from './valueSet.js' -import { untypedVerificationResultSchema } from './verificationResult.js' -import { untypedVisionPrescriptionSchema } from './visionPrescription.js' - -/** - * Zod schema for FHIR Resource (discriminated union of all resource types). - */ -export const fhirResourceSchema: ZodType = z.lazy(() => - z.discriminatedUnion('resourceType', [ - untypedAccountSchema, - untypedActivityDefinitionSchema, - untypedAdministrableProductDefinitionSchema, - untypedAdverseEventSchema, - untypedAllergyIntoleranceSchema, - untypedAppointmentSchema, - untypedAppointmentResponseSchema, - untypedAuditEventSchema, - untypedBasicSchema, - untypedBinarySchema, - untypedBiologicallyDerivedProductSchema, - untypedBodyStructureSchema, - untypedCapabilityStatementSchema, - untypedCarePlanSchema, - untypedCareTeamSchema, - untypedCatalogEntrySchema, - untypedChargeItemSchema, - untypedChargeItemDefinitionSchema, - untypedCitationSchema, - untypedClaimSchema, - untypedClaimResponseSchema, - untypedClinicalImpressionSchema, - untypedClinicalUseDefinitionSchema, - untypedCodeSystemSchema, - untypedCommunicationSchema, - untypedCommunicationRequestSchema, - untypedCompartmentDefinitionSchema, - untypedCompositionSchema, - untypedConceptMapSchema, - untypedConditionSchema, - untypedConsentSchema, - untypedContractSchema, - untypedCoverageSchema, - untypedCoverageEligibilityRequestSchema, - untypedCoverageEligibilityResponseSchema, - untypedDetectedIssueSchema, - untypedDeviceSchema, - untypedDeviceDefinitionSchema, - untypedDeviceMetricSchema, - untypedDeviceRequestSchema, - untypedDeviceUseStatementSchema, - untypedDiagnosticReportSchema, - untypedDocumentManifestSchema, - untypedDocumentReferenceSchema, - untypedEncounterSchema, - untypedEndpointSchema, - untypedEnrollmentRequestSchema, - untypedEnrollmentResponseSchema, - untypedEpisodeOfCareSchema, - untypedEventDefinitionSchema, - untypedEvidenceSchema, - untypedEvidenceReportSchema, - untypedEvidenceVariableSchema, - untypedExampleScenarioSchema, - untypedExplanationOfBenefitSchema, - untypedFamilyMemberHistorySchema, - untypedFlagSchema, - untypedGenericBundleSchema, - untypedGoalSchema, - untypedGraphDefinitionSchema, - untypedGroupSchema, - untypedGuidanceResponseSchema, - untypedHealthcareServiceSchema, - untypedImagingStudySchema, - untypedImplementationGuideSchema, - untypedImmunizationSchema, - untypedImmunizationEvaluationSchema, - untypedImmunizationRecommendationSchema, - untypedIngredientSchema, - untypedInsurancePlanSchema, - untypedInvoiceSchema, - untypedLibrarySchema, - untypedLinkageSchema, - untypedListSchema, - untypedLocationSchema, - untypedManufacturedItemDefinitionSchema, - untypedMeasureSchema, - untypedMeasureReportSchema, - untypedMediaSchema, - untypedMedicationSchema, - untypedMedicationAdministrationSchema, - untypedMedicationDispenseSchema, - untypedMedicationKnowledgeSchema, - untypedMedicationRequestSchema, - untypedMedicationStatementSchema, - untypedMedicinalProductDefinitionSchema, - untypedMessageDefinitionSchema, - untypedMessageHeaderSchema, - untypedMolecularSequenceSchema, - untypedNamingSystemSchema, - untypedNutritionOrderSchema, - untypedNutritionProductSchema, - untypedObservationSchema, - untypedObservationDefinitionSchema, - untypedOperationDefinitionSchema, - untypedOperationOutcomeSchema, - untypedOrganizationSchema, - untypedOrganizationAffiliationSchema, - untypedPackagedProductDefinitionSchema, - untypedParametersSchema, - untypedPatientSchema, - untypedPaymentNoticeSchema, - untypedPaymentReconciliationSchema, - untypedPersonSchema, - untypedPlanDefinitionSchema, - untypedPractitionerSchema, - untypedPractitionerRoleSchema, - untypedProcedureSchema, - untypedProvenanceSchema, - untypedQuestionnaireSchema, - untypedQuestionnaireResponseSchema, - untypedRegulatedAuthorizationSchema, - untypedRelatedPersonSchema, - untypedRequestGroupSchema, - untypedResearchDefinitionSchema, - untypedResearchElementDefinitionSchema, - untypedResearchStudySchema, - untypedResearchSubjectSchema, - untypedRiskAssessmentSchema, - untypedScheduleSchema, - untypedSearchParameterSchema, - untypedServiceRequestSchema, - untypedSlotSchema, - untypedSpecimenSchema, - untypedSpecimenDefinitionSchema, - untypedStructureDefinitionSchema, - untypedStructureMapSchema, - untypedSubscriptionSchema, - untypedSubscriptionStatusSchema, - untypedSubscriptionTopicSchema, - untypedSubstanceSchema, - untypedSubstanceDefinitionSchema, - untypedSupplyDeliverySchema, - untypedSupplyRequestSchema, - untypedTaskSchema, - untypedTerminologyCapabilitiesSchema, - untypedTestReportSchema, - untypedTestScriptSchema, - untypedValueSetSchema, - untypedVerificationResultSchema, - untypedVisionPrescriptionSchema, - ]), -) diff --git a/packages/fhir/src/resources/flag.ts b/packages/fhir/src/resources/flag.ts deleted file mode 100644 index 92f521cc..00000000 --- a/packages/fhir/src/resources/flag.ts +++ /dev/null @@ -1,124 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type Flag } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - codeableConceptSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - referenceSchema, -} from '../elements/index.js' -import { flagStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR Flag resource (untyped version). - */ -export const untypedFlagSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Flag').readonly(), - identifier: identifierSchema.array().optional(), - status: flagStatusSchema, - _status: elementSchema.optional(), - category: codeableConceptSchema.array().optional(), - code: codeableConceptSchema, - subject: referenceSchema, - period: periodSchema.optional(), - encounter: referenceSchema.optional(), - author: referenceSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Flag resource. - */ -export const flagSchema: ZodType = untypedFlagSchema - -/** - * Wrapper class for FHIR Flag resources. - */ -export class FhirFlag extends FhirDomainResource { - /** - * Parses a Flag resource from unknown data. - * - * @param value - The data to parse and validate against the Flag schema - * @returns A FhirFlag instance containing the validated resource - */ - public static parse(value: unknown): FhirFlag { - return new FhirFlag(flagSchema.parse(value)) - } - - /** - * Gets the flag code as display text. - * Falls back to the first coding display if text is not present. - * - * @returns The code display text, if available - */ - public get codeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.code) - } - - /** - * Checks whether the flag period is active as of the given date. - * - * @param asOfDate - Optional date to check against (defaults to now) - * @returns true if the period overlaps the given date - */ - public periodIsActive(asOfDate?: Date): boolean { - return FhirDomainResource.periodIsActive(this.value.period, asOfDate) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/goal.ts b/packages/fhir/src/resources/goal.ts deleted file mode 100644 index b45aca46..00000000 --- a/packages/fhir/src/resources/goal.ts +++ /dev/null @@ -1,181 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type Goal, type GoalTarget } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - dateSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - quantitySchema, - rangeSchema, - ratioSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { goalLifecycleStatusSchema } from '../valueSets/index.js' - -const goalTargetSchema: ZodType = backboneElementSchema.extend({ - measure: codeableConceptSchema.optional(), - detailQuantity: quantitySchema.optional(), - detailRange: rangeSchema.optional(), - detailCodeableConcept: codeableConceptSchema.optional(), - detailString: stringSchema.optional(), - _detailString: elementSchema.optional(), - detailBoolean: booleanSchema.optional(), - _detailBoolean: elementSchema.optional(), - detailInteger: z.number().optional(), - _detailInteger: elementSchema.optional(), - detailRatio: ratioSchema.optional(), - dueDate: dateSchema.optional(), - _dueDate: elementSchema.optional(), - dueDuration: quantitySchema.optional(), -}) - -/** - * Zod schema for FHIR Goal resource (untyped version). - */ -export const untypedGoalSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Goal').readonly(), - identifier: identifierSchema.array().optional(), - lifecycleStatus: goalLifecycleStatusSchema, - _lifecycleStatus: elementSchema.optional(), - achievementStatus: codeableConceptSchema.optional(), - category: codeableConceptSchema.array().optional(), - priority: codeableConceptSchema.optional(), - description: codeableConceptSchema, - subject: referenceSchema, - startDate: dateSchema.optional(), - _startDate: elementSchema.optional(), - startCodeableConcept: codeableConceptSchema.optional(), - target: goalTargetSchema.array().optional(), - statusDate: dateSchema.optional(), - _statusDate: elementSchema.optional(), - statusReason: stringSchema.optional(), - _statusReason: elementSchema.optional(), - expressedBy: referenceSchema.optional(), - addresses: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - outcomeCode: codeableConceptSchema.array().optional(), - outcomeReference: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Goal resource. - */ -export const goalSchema: ZodType = untypedGoalSchema - -/** - * Wrapper class for FHIR Goal resources. - * Provides utility methods for working with goals. - */ -export class FhirGoal extends FhirDomainResource { - // Static Functions - - /** - * Parses a Goal resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirGoal instance - */ - public static parse(value: unknown): FhirGoal { - return new FhirGoal(goalSchema.parse(value)) - } - - // Properties - - /** - * Gets the start date as a JavaScript Date object. - * - * @returns The start date if available - */ - public get startDate(): Date | undefined { - return FhirDomainResource.parseDate(this.value.startDate) - } - - /** - * Gets the description display text. - * - * @returns The description display - */ - public get descriptionDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.description) - } - - /** - * Gets all category displays. - * - * @returns Array of category display texts - */ - public get categoryDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.category) - } - - /** - * Gets note texts from the goal. - * - * @returns Array of note texts - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/graphDefinition.ts b/packages/fhir/src/resources/graphDefinition.ts deleted file mode 100644 index 2d2e8c72..00000000 --- a/packages/fhir/src/resources/graphDefinition.ts +++ /dev/null @@ -1,137 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type GraphDefinitionLinkTarget, - type GraphDefinitionLinkTargetCompartment, - type GraphDefinition, - type GraphDefinitionLink, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactDetailSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - intSchema, - stringSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - compartmentDefinitionCodeSchema, - fhirResourceTypeSchema, - graphDefinitionLinkTargetCompartmentRuleSchema, - graphDefinitionLinkTargetCompartmentUseSchema, - publicationStatusSchema, -} from '../valueSets/index.js' - -const graphDefinitionLinkTargetCompartmentSchema: ZodType = - backboneElementSchema.extend({ - code: compartmentDefinitionCodeSchema, - _code: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - expression: stringSchema.optional(), - _expression: elementSchema.optional(), - rule: graphDefinitionLinkTargetCompartmentRuleSchema, - _rule: elementSchema.optional(), - use: graphDefinitionLinkTargetCompartmentUseSchema, - _use: elementSchema.optional(), - }) - -const graphDefinitionLinkTargetSchema: ZodType = - backboneElementSchema.extend({ - compartment: graphDefinitionLinkTargetCompartmentSchema.array().optional(), - get link() { - return graphDefinitionLinkSchema.array().optional() - }, - params: stringSchema.optional(), - _params: elementSchema.optional(), - profile: urlSchema.optional(), - _profile: elementSchema.optional(), - type: fhirResourceTypeSchema, - _type: elementSchema.optional(), - }) - -const graphDefinitionLinkSchema: ZodType = - backboneElementSchema.extend({ - description: stringSchema.optional(), - _description: elementSchema.optional(), - max: stringSchema.optional(), - _max: elementSchema.optional(), - min: intSchema.optional(), - path: stringSchema.optional(), - _path: elementSchema.optional(), - sliceName: stringSchema.optional(), - _sliceName: elementSchema.optional(), - target: graphDefinitionLinkTargetSchema.array().optional(), - }) - -/** - * Zod schema for FHIR GraphDefinition resource (untyped version). - */ -export const untypedGraphDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('GraphDefinition').readonly(), - contact: contactDetailSchema.array().optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - jurisdiction: codeableConceptSchema.array().optional(), - link: graphDefinitionLinkSchema.array().optional(), - name: stringSchema, - _name: elementSchema.optional(), - profile: urlSchema.optional(), - _profile: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - purpose: stringSchema.optional(), - _purpose: elementSchema.optional(), - start: fhirResourceTypeSchema, - _start: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - url: urlSchema.optional(), - _url: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR GraphDefinition resource. - */ -export const graphDefinitionSchema: ZodType = - untypedGraphDefinitionSchema - -/** - * Wrapper class for FHIR GraphDefinition resources. - * Provides utility methods for working with graph definitions that specify resource relationships. - */ -export class FhirGraphDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses a GraphDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the GraphDefinition schema - * @returns A FhirGraphDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirGraphDefinition { - return new FhirGraphDefinition(graphDefinitionSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/group.ts b/packages/fhir/src/resources/group.ts deleted file mode 100644 index 69a14784..00000000 --- a/packages/fhir/src/resources/group.ts +++ /dev/null @@ -1,139 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type Group } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - quantitySchema, - rangeSchema, - referenceSchema, - stringSchema, - unsignedIntSchema, -} from '../elements/index.js' -import { groupTypeSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR Group resource (untyped version). - */ -export const untypedGroupSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Group').readonly(), - identifier: identifierSchema.array().optional(), - active: booleanSchema.optional(), - _active: elementSchema.optional(), - type: groupTypeSchema, - _type: elementSchema.optional(), - actual: booleanSchema, - _actual: elementSchema.optional(), - code: codeableConceptSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - quantity: unsignedIntSchema.optional(), - _quantity: elementSchema.optional(), - managingEntity: referenceSchema.optional(), - characteristic: backboneElementSchema - .extend({ - code: codeableConceptSchema, - valueCodeableConcept: codeableConceptSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueRange: rangeSchema.optional(), - valueReference: referenceSchema.optional(), - exclude: booleanSchema, - _exclude: elementSchema.optional(), - period: periodSchema.optional(), - }) - .array() - .optional(), - member: backboneElementSchema - .extend({ - entity: referenceSchema, - period: periodSchema.optional(), - inactive: booleanSchema.optional(), - _inactive: elementSchema.optional(), - }) - .array() - .optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Group resource. - */ -export const groupSchema: ZodType = untypedGroupSchema - -/** - * Wrapper class for FHIR Group resources. - * Provides utility methods for working with groups of individuals, organizations, or other entities. - */ -export class FhirGroup extends FhirDomainResource { - /** - * Parses a Group resource from unknown data. - * - * @param value - The data to parse and validate against the Group schema - * @returns A FhirGroup instance containing the validated resource - */ - public static parse(value: unknown): FhirGroup { - return new FhirGroup(groupSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/guidanceResponse.ts b/packages/fhir/src/resources/guidanceResponse.ts deleted file mode 100644 index f374731c..00000000 --- a/packages/fhir/src/resources/guidanceResponse.ts +++ /dev/null @@ -1,124 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type GuidanceResponse } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - canonicalSchema, - codeableConceptSchema, - dataRequirementSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - referenceSchema, - uriSchema, -} from '../elements/index.js' -import { guidanceResponseStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR GuidanceResponse resource (untyped version). - */ -export const untypedGuidanceResponseSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('GuidanceResponse').readonly(), - requestIdentifier: identifierSchema.optional(), - identifier: identifierSchema.array().optional(), - moduleUri: uriSchema.optional(), - _moduleUri: elementSchema.optional(), - moduleCanonical: canonicalSchema.optional(), - _moduleCanonical: elementSchema.optional(), - moduleCodeableConcept: codeableConceptSchema.optional(), - status: guidanceResponseStatusSchema, - _status: elementSchema.optional(), - subject: referenceSchema.optional(), - encounter: referenceSchema.optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: elementSchema.optional(), - performer: referenceSchema.optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - evaluationMessage: referenceSchema.array().optional(), - outputParameters: referenceSchema.optional(), - result: referenceSchema.optional(), - dataRequirement: dataRequirementSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR GuidanceResponse resource. - */ -export const guidanceResponseSchema: ZodType = - untypedGuidanceResponseSchema - -/** - * Wrapper class for FHIR GuidanceResponse resources. - * Provides utility methods for working with guidance responses and decision support outcomes. - */ -export class FhirGuidanceResponse extends FhirDomainResource { - // Static Functions - - /** - * Parses a GuidanceResponse resource from unknown data. - * - * @param value - The data to parse and validate against the GuidanceResponse schema - * @returns A FhirGuidanceResponse instance containing the validated resource - */ - public static parse(value: unknown): FhirGuidanceResponse { - return new FhirGuidanceResponse(guidanceResponseSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/healthcareService.ts b/packages/fhir/src/resources/healthcareService.ts deleted file mode 100644 index 7fb36c18..00000000 --- a/packages/fhir/src/resources/healthcareService.ts +++ /dev/null @@ -1,218 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type HealthcareService } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - attachmentSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactPointSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - markdownSchema, - periodSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { daysOfWeekSchema } from '../valueSets/index.js' - -const availableTimeSchema = backboneElementSchema.extend({ - daysOfWeek: daysOfWeekSchema.array().optional(), - _daysOfWeek: elementSchema.array().optional(), - allDay: booleanSchema.optional(), - _allDay: elementSchema.optional(), - availableStartTime: stringSchema.optional(), - _availableStartTime: elementSchema.optional(), - availableEndTime: stringSchema.optional(), - _availableEndTime: elementSchema.optional(), -}) - -const eligibilitySchema = backboneElementSchema.extend({ - code: codeableConceptSchema.optional(), - comment: stringSchema.optional(), - _comment: elementSchema.optional(), -}) - -const notAvailableSchema = backboneElementSchema.extend({ - description: stringSchema, - _description: elementSchema.optional(), - during: periodSchema.optional(), -}) - -/** - * Zod schema for FHIR HealthcareService resource (untyped version). - */ -export const untypedHealthcareServiceSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('HealthcareService').readonly(), - identifier: identifierSchema.array().optional(), - active: booleanSchema.optional(), - _active: elementSchema.optional(), - providedBy: referenceSchema.optional(), - category: codeableConceptSchema.array().optional(), - type: codeableConceptSchema.array().optional(), - specialty: codeableConceptSchema.array().optional(), - location: referenceSchema.array().optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - comment: stringSchema.optional(), - _comment: elementSchema.optional(), - extraDetails: markdownSchema.optional(), - _extraDetails: elementSchema.optional(), - photo: attachmentSchema.optional(), - telecom: contactPointSchema.array().optional(), - coverageArea: referenceSchema.array().optional(), - serviceProvisionCode: codeableConceptSchema.array().optional(), - eligibility: eligibilitySchema.array().optional(), - program: codeableConceptSchema.array().optional(), - characteristic: codeableConceptSchema.array().optional(), - communication: codeableConceptSchema.array().optional(), - referralMethod: codeableConceptSchema.array().optional(), - appointmentRequired: booleanSchema.optional(), - _appointmentRequired: elementSchema.optional(), - availableTime: availableTimeSchema.array().optional(), - notAvailable: notAvailableSchema.array().optional(), - availabilityExceptions: stringSchema.optional(), - _availabilityExceptions: elementSchema.optional(), - endpoint: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR HealthcareService resource. - */ -export const healthcareServiceSchema: ZodType = - untypedHealthcareServiceSchema - -/** - * Wrapper class for FHIR HealthcareService resources. - * Provides utility methods for working with healthcare services and their properties. - */ -export class FhirHealthcareService extends FhirDomainResource { - // Static Functions - - /** - * Parses a HealthcareService resource from unknown data. - * - * @param value - The data to parse and validate against the HealthcareService schema - * @returns A FhirHealthcareService instance containing the validated resource - */ - public static parse(value: unknown): FhirHealthcareService { - return new FhirHealthcareService(healthcareServiceSchema.parse(value)) - } - - /** - * Gets all category displays for the service. - * - * @returns Array of category display texts - * - * @example - * ```typescript - * const categories = service.getCategoryDisplays() - * console.log(`Categories: ${categories.join(', ')}`) - * ``` - */ - public get categoryDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.category) - } - - /** - * Gets all service type displays. - * - * @returns Array of service type display texts - * - * @example - * ```typescript - * const types = service.getServiceTypeDisplays() - * console.log(`Service types: ${types.join(', ')}`) - * ``` - */ - public get serviceTypeDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.type) - } - - /** - * Gets all specialty displays for the service. - * - * @returns Array of specialty display texts - * - * @example - * ```typescript - * const specialties = service.getSpecialtyDisplays() - * console.log(`Specialties: ${specialties.join(', ')}`) - * ``` - */ - public get specialtyDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.specialty) - } - - /** - * Gets all phone numbers for the service. - * - * @returns Array of phone number strings - * - * @example - * ```typescript - * const phones = service.getAllPhones() - * phones.forEach(phone => console.log(phone)) - * ``` - */ - public get phoneNumbers(): string[] { - return FhirDomainResource.contactPointsBySystem(this.value.telecom, 'phone') - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/imagingStudy.ts b/packages/fhir/src/resources/imagingStudy.ts deleted file mode 100644 index 1ffd75e1..00000000 --- a/packages/fhir/src/resources/imagingStudy.ts +++ /dev/null @@ -1,223 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type ImagingStudy } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - codeableConceptSchema, - codingSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - idSchema, - referenceSchema, - stringSchema, - unsignedIntSchema, -} from '../elements/index.js' -import { imagingStudyStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR ImagingStudy resource (untyped version). - */ -export const untypedImagingStudySchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ImagingStudy').readonly(), - identifier: identifierSchema.array().optional(), - status: imagingStudyStatusSchema, - _status: elementSchema.optional(), - modality: codingSchema.array().optional(), - subject: referenceSchema, - encounter: referenceSchema.optional(), - started: dateTimeSchema.optional(), - _started: elementSchema.optional(), - basedOn: referenceSchema.array().optional(), - referrer: referenceSchema.optional(), - interpreter: referenceSchema.array().optional(), - endpoint: referenceSchema.array().optional(), - numberOfSeries: unsignedIntSchema.optional(), - numberOfInstances: unsignedIntSchema.optional(), - procedureReference: referenceSchema.optional(), - procedureCode: codeableConceptSchema.array().optional(), - location: referenceSchema.optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - series: backboneElementSchema - .extend({ - uid: idSchema, - _uid: elementSchema.optional(), - number: unsignedIntSchema.optional(), - modality: codingSchema, - description: stringSchema.optional(), - _description: elementSchema.optional(), - numberOfInstances: unsignedIntSchema.optional(), - endpoint: referenceSchema.array().optional(), - bodySite: codingSchema.optional(), - laterality: codingSchema.optional(), - specimen: referenceSchema.array().optional(), - started: dateTimeSchema.optional(), - _started: elementSchema.optional(), - performer: backboneElementSchema - .extend({ - function: codeableConceptSchema.optional(), - actor: referenceSchema, - }) - .array() - .optional(), - instance: backboneElementSchema - .extend({ - uid: idSchema, - _uid: elementSchema.optional(), - sopClass: codingSchema, - number: unsignedIntSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - }) - .array() - .optional(), - }) - .array() - .optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ImagingStudy resource. - */ -export const imagingStudySchema: ZodType = - untypedImagingStudySchema - -/** - * Wrapper class for FHIR ImagingStudy resources. - * Provides utility methods for working with imaging studies and their metadata. - */ -export class FhirImagingStudy extends FhirDomainResource { - // Static Functions - - /** - * Parses an ImagingStudy resource from unknown data. - * - * @param value - The data to parse and validate against the ImagingStudy schema - * @returns A FhirImagingStudy instance containing the validated resource - */ - public static parse(value: unknown): FhirImagingStudy { - return new FhirImagingStudy(imagingStudySchema.parse(value)) - } - - /** - * Gets the study started date as a JavaScript Date object. - * - * @returns The started date if available, undefined otherwise - * - * @example - * ```typescript - * const startedDate = study.startedDate - * console.log(`Study started: ${startedDate?.toLocaleDateString()}`) - * ``` - */ - public get startedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.started) - } - - /** - * Gets all modalities used in the study. - * - * @returns Array of modality display texts - * - * @example - * ```typescript - * const modalities = study.getModalityDisplays() - * console.log(`Modalities: ${modalities.join(', ')}`) - * ``` - */ - public get modalityDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.modality) - } - - /** - * Gets the reason code displays for why the study was performed. - * - * @returns Array of reason display texts - * - * @example - * ```typescript - * const reasons = study.getReasonDisplays() - * console.log(`Study reasons: ${reasons.join(', ')}`) - * ``` - */ - public get reasonDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.reasonCode) - } - - /** - * Gets the note texts as an array of strings. - * - * @returns Array of note texts - * - * @example - * ```typescript - * const notes = study.noteTexts - * notes.forEach(note => console.log(note)) - * ``` - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/immunization.ts b/packages/fhir/src/resources/immunization.ts deleted file mode 100644 index 6e67774c..00000000 --- a/packages/fhir/src/resources/immunization.ts +++ /dev/null @@ -1,274 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type Immunization } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - positiveIntSchema, - quantitySchema, - referenceSchema, - stringSchema, - uriSchema, -} from '../elements/index.js' -import { immunizationStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR Immunization resource (untyped version). - */ -export const untypedImmunizationSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Immunization').readonly(), - identifier: identifierSchema.array().optional(), - status: immunizationStatusSchema, - _status: elementSchema.optional(), - statusReason: codeableConceptSchema.optional(), - vaccineCode: codeableConceptSchema, - patient: referenceSchema, - encounter: referenceSchema.optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: elementSchema.optional(), - occurrenceString: stringSchema.optional(), - _occurrenceString: elementSchema.optional(), - recorded: dateTimeSchema.optional(), - _recorded: elementSchema.optional(), - primarySource: booleanSchema.optional(), - _primarySource: elementSchema.optional(), - reportOrigin: codeableConceptSchema.optional(), - location: referenceSchema.optional(), - manufacturer: referenceSchema.optional(), - lotNumber: stringSchema.optional(), - _lotNumber: elementSchema.optional(), - expirationDate: dateTimeSchema.optional(), - _expirationDate: elementSchema.optional(), - site: codeableConceptSchema.optional(), - route: codeableConceptSchema.optional(), - doseQuantity: quantitySchema.optional(), - performer: backboneElementSchema - .extend({ - function: codeableConceptSchema.optional(), - actor: referenceSchema, - }) - .array() - .optional(), - note: annotationSchema.array().optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - isSubpotent: booleanSchema.optional(), - _isSubpotent: elementSchema.optional(), - subpotentReason: codeableConceptSchema.array().optional(), - education: backboneElementSchema - .extend({ - documentType: stringSchema.optional(), - _documentType: elementSchema.optional(), - reference: uriSchema.optional(), - _reference: elementSchema.optional(), - publicationDate: dateTimeSchema.optional(), - _publicationDate: elementSchema.optional(), - presentationDate: dateTimeSchema.optional(), - _presentationDate: elementSchema.optional(), - }) - .array() - .optional(), - programEligibility: codeableConceptSchema.array().optional(), - fundingSource: codeableConceptSchema.optional(), - reaction: backboneElementSchema - .extend({ - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - detail: referenceSchema.optional(), - reported: booleanSchema.optional(), - _reported: elementSchema.optional(), - }) - .array() - .optional(), - protocolApplied: backboneElementSchema - .extend({ - series: stringSchema.optional(), - _series: elementSchema.optional(), - authority: referenceSchema.optional(), - targetDisease: codeableConceptSchema.array().optional(), - doseNumberPositiveInt: positiveIntSchema.optional(), - doseNumberString: stringSchema.optional(), - _doseNumberString: elementSchema.optional(), - seriesDosesPositiveInt: positiveIntSchema.optional(), - seriesDosesString: stringSchema.optional(), - _seriesDosesString: elementSchema.optional(), - }) - .array() - .optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Immunization resource. - */ -export const immunizationSchema: ZodType = - untypedImmunizationSchema - -/** - * Wrapper class for FHIR Immunization resources. - * Provides utility methods for working with immunizations. - */ -export class FhirImmunization extends FhirDomainResource { - // Static Functions - - /** - * Parses an Immunization resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirImmunization instance - */ - public static parse(value: unknown): FhirImmunization { - return new FhirImmunization(immunizationSchema.parse(value)) - } - - // Properties - - /** - * Gets the occurrence date as a JavaScript Date object. - * - * @returns The occurrence date if available - */ - public get occurrenceDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.occurrenceDateTime) - } - - /** - * Gets the recorded date as a JavaScript Date object. - * - * @returns The recorded date if available - */ - public get recordedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.recorded) - } - - /** - * Gets the expiration date as a JavaScript Date object. - * - * @returns The expiration date if available - */ - public get expirationDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.expirationDate) - } - - /** - * Gets the vaccine code display text. - * - * @returns The vaccine code display - */ - public get vaccineDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.vaccineCode) - } - - /** - * Gets the site display text (anatomical location). - * - * @returns The site display - */ - public get siteDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.site) - } - - /** - * Gets the route display text (how administered). - * - * @returns The route display - */ - public get routeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.route) - } - - /** - * Checks if the vaccine is expired based on expiration date. - * - * @param asOfDate - Date to check against (defaults to now) - * @returns true if expired - */ - public isExpired(asOfDate: Date = new Date()): boolean { - const expDate = this.expirationDate - if (!expDate) return false - return asOfDate > expDate - } - - /** - * Gets note texts from the immunization. - * - * @returns Array of note texts - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Gets all reaction displays. - * - * @returns Array of reaction display texts - */ - public get reactionDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays( - this.value.reaction?.flatMap((reaction) => - reaction.detail ? [reaction.detail] : [], - ), - ) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/immunizationEvaluation.ts b/packages/fhir/src/resources/immunizationEvaluation.ts deleted file mode 100644 index 343fb48d..00000000 --- a/packages/fhir/src/resources/immunizationEvaluation.ts +++ /dev/null @@ -1,124 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type ImmunizationEvaluation } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - positiveIntSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { immunizationEvaluationStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR ImmunizationEvaluation resource (untyped version). - */ -export const untypedImmunizationEvaluationSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ImmunizationEvaluation').readonly(), - identifier: identifierSchema.array().optional(), - status: immunizationEvaluationStatusSchema, - _status: elementSchema.optional(), - patient: referenceSchema, - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - authority: referenceSchema.optional(), - targetDisease: codeableConceptSchema, - immunizationEvent: referenceSchema, - doseStatus: codeableConceptSchema, - doseStatusReason: codeableConceptSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - series: stringSchema.optional(), - _series: elementSchema.optional(), - doseNumberPositiveInt: positiveIntSchema.optional(), - doseNumberString: stringSchema.optional(), - _doseNumberString: elementSchema.optional(), - seriesDosesPositiveInt: positiveIntSchema.optional(), - seriesDosesString: stringSchema.optional(), - _seriesDosesString: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ImmunizationEvaluation resource. - */ -export const immunizationEvaluationSchema: ZodType = - untypedImmunizationEvaluationSchema - -/** - * Wrapper class for FHIR ImmunizationEvaluation resources. - * Provides utility methods for working with immunization evaluations and assessments. - */ -export class FhirImmunizationEvaluation extends FhirDomainResource { - // Static Functions - - /** - * Parses an ImmunizationEvaluation resource from unknown data. - * - * @param value - The data to parse and validate against the ImmunizationEvaluation schema - * @returns A FhirImmunizationEvaluation instance containing the validated resource - */ - public static parse(value: unknown): FhirImmunizationEvaluation { - return new FhirImmunizationEvaluation( - immunizationEvaluationSchema.parse(value), - ) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/immunizationRecommendation.ts b/packages/fhir/src/resources/immunizationRecommendation.ts deleted file mode 100644 index 4614fc1f..00000000 --- a/packages/fhir/src/resources/immunizationRecommendation.ts +++ /dev/null @@ -1,185 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ImmunizationRecommendationRecommendation, - type ImmunizationRecommendationRecommendationDateCriterion, - type ImmunizationRecommendation, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - positiveIntSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' - -const immunizationRecommendationRecommendationDateCriterionSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema, - value: dateTimeSchema, - _value: elementSchema.optional(), - }) - -const immunizationRecommendationRecommendationSchema: ZodType = - backboneElementSchema.extend({ - vaccineCode: codeableConceptSchema.array().optional(), - targetDisease: codeableConceptSchema.optional(), - contraindicatedVaccineCode: codeableConceptSchema.array().optional(), - forecastStatus: codeableConceptSchema, - forecastReason: codeableConceptSchema.array().optional(), - dateCriterion: immunizationRecommendationRecommendationDateCriterionSchema - .array() - .optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - series: stringSchema.optional(), - _series: elementSchema.optional(), - doseNumberPositiveInt: positiveIntSchema.optional(), - doseNumberString: stringSchema.optional(), - _doseNumberString: elementSchema.optional(), - seriesDosesPositiveInt: positiveIntSchema.optional(), - seriesDosesString: stringSchema.optional(), - _seriesDosesString: elementSchema.optional(), - supportingImmunization: referenceSchema.array().optional(), - supportingPatientInformation: referenceSchema.array().optional(), - }) - -/** - * Zod schema for FHIR ImmunizationRecommendation resource (untyped version). - */ -export const untypedImmunizationRecommendationSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ImmunizationRecommendation').readonly(), - identifier: identifierSchema.array().optional(), - patient: referenceSchema, - date: dateTimeSchema, - _date: elementSchema.optional(), - authority: referenceSchema.optional(), - recommendation: immunizationRecommendationRecommendationSchema.array(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ImmunizationRecommendation resource. - */ -export const immunizationRecommendationSchema: ZodType = - untypedImmunizationRecommendationSchema - -/** - * Wrapper class for FHIR ImmunizationRecommendation resources. - * Provides utility methods for working with immunization recommendations and forecasts. - */ -export class FhirImmunizationRecommendation extends FhirDomainResource { - // Static Functions - - /** - * Parses an ImmunizationRecommendation resource from unknown data. - * - * @param value - The data to parse and validate against the ImmunizationRecommendation schema - * @returns A FhirImmunizationRecommendation instance containing the validated resource - */ - public static parse(value: unknown): FhirImmunizationRecommendation { - return new FhirImmunizationRecommendation( - immunizationRecommendationSchema.parse(value), - ) - } - - /** - * Get the recommendation date as a Date object. - * @returns The recommendation date - */ - public get recommendationDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.date) - } - - /** - * Get all vaccine code displays from recommendations. - * @returns Array of vaccine code display texts - */ - public get vaccineCodeDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays( - this.value.recommendation.flatMap((rec) => rec.vaccineCode ?? []), - ) - } - - /** - * Get all target disease displays from recommendations. - * @returns Array of target disease display texts - */ - public get targetDiseaseDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays( - this.value.recommendation.flatMap((rec) => - rec.targetDisease ? [rec.targetDisease] : [], - ), - ) - } - - /** - * Get all forecast status displays from recommendations. - * @returns Array of forecast status display texts - */ - public get forecastStatusDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays( - this.value.recommendation.map((rec) => rec.forecastStatus), - ) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/implementationGuide.ts b/packages/fhir/src/resources/implementationGuide.ts deleted file mode 100644 index 202bfaf9..00000000 --- a/packages/fhir/src/resources/implementationGuide.ts +++ /dev/null @@ -1,228 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ImplementationGuide, - type ImplementationGuideDependsOn, - type ImplementationGuideDefinition, - type ImplementationGuideDefinitionGrouping, - type ImplementationGuideDefinitionPage, - type ImplementationGuideDefinitionParameter, - type ImplementationGuideDefinitionResource, - type ImplementationGuideDefinitionTemplate, - type ImplementationGuideGlobal, - type ImplementationGuideManifest, - type ImplementationGuideManifestPage, - type ImplementationGuideManifestResource, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactDetailSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - referenceSchema, - stringSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - fhirLicenseSchema, - fhirResourceTypeSchema, - fhirVersionSchema, - guidePageGenerationSchema, - guideParameterCodeSchema, - publicationStatusSchema, -} from '../valueSets/index.js' - -const implementationGuideDependsOnSchema: ZodType = - backboneElementSchema.extend({ - uri: urlSchema, - _uri: elementSchema.optional(), - packageId: stringSchema.optional(), - _packageId: elementSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - }) - -const implementationGuideGlobalSchema: ZodType = - backboneElementSchema.extend({ - type: fhirResourceTypeSchema, - _type: elementSchema.optional(), - profile: urlSchema, - _profile: elementSchema.optional(), - }) - -const implementationGuideDefinitionResourceSchema: ZodType = - backboneElementSchema.extend({ - reference: referenceSchema, - fhirVersion: fhirVersionSchema.array().optional(), - _fhirVersion: elementSchema.array().optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - exampleBoolean: booleanSchema.optional(), - _exampleBoolean: elementSchema.optional(), - exampleCanonical: urlSchema.optional(), - _exampleCanonical: elementSchema.optional(), - groupingId: stringSchema.optional(), - _groupingId: elementSchema.optional(), - }) - -const implementationGuideDefinitionPageSchema: ZodType = - backboneElementSchema.extend({ - nameUrl: urlSchema.optional(), - _nameUrl: elementSchema.optional(), - nameReference: referenceSchema.optional(), - title: stringSchema, - _title: elementSchema.optional(), - generation: guidePageGenerationSchema, - _generation: elementSchema.optional(), - get page() { - return implementationGuideDefinitionPageSchema.array().optional() - }, - }) - -const implementationGuideDefinitionParameterSchema: ZodType = - backboneElementSchema.extend({ - code: guideParameterCodeSchema, - _code: elementSchema.optional(), - value: stringSchema, - _value: elementSchema.optional(), - }) - -const implementationGuideDefinitionTemplateSchema: ZodType = - backboneElementSchema.extend({ - code: stringSchema, - _code: elementSchema.optional(), - source: stringSchema, - _source: elementSchema.optional(), - scope: stringSchema.optional(), - _scope: elementSchema.optional(), - }) - -const implementationGuideDefinitionGroupingSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - }) - -const implementationGuideDefinitionSchema: ZodType = - backboneElementSchema.extend({ - grouping: implementationGuideDefinitionGroupingSchema.array().optional(), - resource: implementationGuideDefinitionResourceSchema.array(), - page: implementationGuideDefinitionPageSchema.optional(), - parameter: implementationGuideDefinitionParameterSchema.array().optional(), - template: implementationGuideDefinitionTemplateSchema.array().optional(), - }) - -const implementationGuideManifestResourceSchema: ZodType = - backboneElementSchema.extend({ - reference: referenceSchema, - exampleBoolean: booleanSchema.optional(), - _exampleBoolean: elementSchema.optional(), - exampleCanonical: urlSchema.optional(), - _exampleCanonical: elementSchema.optional(), - relativePath: urlSchema.optional(), - _relativePath: elementSchema.optional(), - }) - -const implementationGuideManifestPageSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - anchor: stringSchema.array().optional(), - _anchor: elementSchema.array().optional(), - }) - -const implementationGuideManifestSchema: ZodType = - backboneElementSchema.extend({ - rendering: urlSchema.optional(), - _rendering: elementSchema.optional(), - resource: implementationGuideManifestResourceSchema.array(), - page: implementationGuideManifestPageSchema.array().optional(), - image: stringSchema.array().optional(), - _image: elementSchema.array().optional(), - other: stringSchema.array().optional(), - _other: elementSchema.array().optional(), - }) - -/** - * Zod schema for FHIR ImplementationGuide resource (untyped version). - */ -export const untypedImplementationGuideSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ImplementationGuide').readonly(), - url: urlSchema, - _url: elementSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema, - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - copyright: stringSchema.optional(), - _copyright: elementSchema.optional(), - packageId: stringSchema, - _packageId: elementSchema.optional(), - license: fhirLicenseSchema.optional(), - _license: elementSchema.optional(), - fhirVersion: fhirVersionSchema.array(), - _fhirVersion: elementSchema.array().optional(), - dependsOn: implementationGuideDependsOnSchema.array().optional(), - global: implementationGuideGlobalSchema.array().optional(), - definition: implementationGuideDefinitionSchema.optional(), - manifest: implementationGuideManifestSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ImplementationGuide resource. - */ -export const implementationGuideSchema: ZodType = - untypedImplementationGuideSchema - -/** - * Wrapper class for FHIR ImplementationGuide resources. - * Provides utility methods for working with implementation guides that define FHIR specifications. - */ -export class FhirImplementationGuide extends FhirDomainResource { - // Static Functions - - /** - * Parses an ImplementationGuide resource from unknown data. - * - * @param value - The data to parse and validate against the ImplementationGuide schema - * @returns A FhirImplementationGuide instance containing the validated resource - */ - public static parse(value: unknown): FhirImplementationGuide { - return new FhirImplementationGuide(implementationGuideSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/ingredient.ts b/packages/fhir/src/resources/ingredient.ts deleted file mode 100644 index 7527d3a6..00000000 --- a/packages/fhir/src/resources/ingredient.ts +++ /dev/null @@ -1,117 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Ingredient, - type IngredientManufacturer, - type IngredientSubstance, - type IngredientSubstanceStrength, - type IngredientSubstanceStrengthReferenceStrength, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - codeableReferenceSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - ratioSchema, - ratioRangeSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { - ingredientStatusSchema, - ingredientManufacturerRoleSchema, -} from '../valueSets/index.js' - -const ingredientSubstanceStrengthReferenceStrengthSchema: ZodType = - backboneElementSchema.extend({ - country: codeableConceptSchema.array().optional(), - measurementPoint: stringSchema.optional(), - _measurementPoint: elementSchema.optional(), - strengthRatio: ratioSchema.optional(), - strengthRatioRange: ratioRangeSchema.optional(), - substance: codeableReferenceSchema.optional(), - }) - -const ingredientSubstanceStrengthSchema: ZodType = - backboneElementSchema.extend({ - concentrationRatio: ratioSchema.optional(), - concentrationRatioRange: ratioRangeSchema.optional(), - country: codeableConceptSchema.array().optional(), - measurementPoint: stringSchema.optional(), - _measurementPoint: elementSchema.optional(), - presentationRatio: ratioSchema.optional(), - presentationRatioRange: ratioRangeSchema.optional(), - referenceStrength: ingredientSubstanceStrengthReferenceStrengthSchema - .array() - .optional(), - textConcentration: stringSchema.optional(), - _textConcentration: elementSchema.optional(), - textPresentation: stringSchema.optional(), - _textPresentation: elementSchema.optional(), - }) - -const ingredientSubstanceSchema: ZodType = - backboneElementSchema.extend({ - code: codeableReferenceSchema, - strength: ingredientSubstanceStrengthSchema.array().optional(), - }) - -const ingredientManufacturerSchema: ZodType = - backboneElementSchema.extend({ - manufacturer: referenceSchema, - role: ingredientManufacturerRoleSchema.optional(), - _role: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR Ingredient resource (untyped version). - */ -export const untypedIngredientSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Ingredient').readonly(), - allergenicIndicator: booleanSchema.optional(), - _allergenicIndicator: elementSchema.optional(), - for: referenceSchema.array().optional(), - function: codeableConceptSchema.array().optional(), - identifier: identifierSchema.optional(), - manufacturer: ingredientManufacturerSchema.array().optional(), - role: codeableConceptSchema, - status: ingredientStatusSchema, - _status: elementSchema.optional(), - substance: ingredientSubstanceSchema, - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Ingredient resource. - */ -export const ingredientSchema: ZodType = untypedIngredientSchema - -/** - * Wrapper class for FHIR Ingredient resources. - * Provides utility methods for working with medication and product ingredients. - */ -export class FhirIngredient extends FhirDomainResource { - // Static Functions - - /** - * Parses an Ingredient resource from unknown data. - * - * @param value - The data to parse and validate against the Ingredient schema - * @returns A FhirIngredient instance containing the validated resource - */ - public static parse(value: unknown): FhirIngredient { - return new FhirIngredient(ingredientSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/insurancePlan.ts b/packages/fhir/src/resources/insurancePlan.ts deleted file mode 100644 index a7ef4e2f..00000000 --- a/packages/fhir/src/resources/insurancePlan.ts +++ /dev/null @@ -1,191 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type InsurancePlan } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - backboneElementSchema, - elementSchema, - codeableConceptSchema, - referenceSchema, - stringSchema, - periodSchema, - humanNameSchema, - addressSchema, - contactPointSchema, - quantitySchema, - moneySchema, - positiveIntSchema, -} from '../elements/index.js' -import { insurancePlanStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR InsurancePlan resource (untyped version). - */ -export const untypedInsurancePlanSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('InsurancePlan').readonly(), - identifier: identifierSchema.array().optional(), - status: insurancePlanStatusSchema.optional(), - _status: elementSchema.optional(), - type: codeableConceptSchema.array().optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - alias: stringSchema.array().optional(), - _alias: elementSchema.array().optional(), - period: periodSchema.optional(), - ownedBy: referenceSchema.optional(), - administeredBy: referenceSchema.optional(), - coverageArea: referenceSchema.array().optional(), - contact: backboneElementSchema - .extend({ - purpose: codeableConceptSchema.optional(), - name: humanNameSchema.optional(), - telecom: contactPointSchema.array().optional(), - address: addressSchema.optional(), - }) - .array() - .optional(), - endpoint: referenceSchema.array().optional(), - network: referenceSchema.array().optional(), - coverage: backboneElementSchema - .extend({ - type: codeableConceptSchema, - network: referenceSchema.array().optional(), - benefit: backboneElementSchema - .extend({ - type: codeableConceptSchema, - requirement: stringSchema.optional(), - _requirement: elementSchema.optional(), - limit: backboneElementSchema - .extend({ - value: quantitySchema.optional(), - code: codeableConceptSchema.optional(), - }) - .array() - .optional(), - }) - .array(), - }) - .array() - .optional(), - plan: backboneElementSchema - .extend({ - identifier: identifierSchema.array().optional(), - type: codeableConceptSchema.optional(), - coverageArea: referenceSchema.array().optional(), - network: referenceSchema.array().optional(), - generalCost: backboneElementSchema - .extend({ - type: codeableConceptSchema.optional(), - groupSize: positiveIntSchema.optional(), - cost: moneySchema.optional(), - comment: stringSchema.optional(), - _comment: elementSchema.optional(), - }) - .array() - .optional(), - specificCost: backboneElementSchema - .extend({ - category: codeableConceptSchema, - benefit: backboneElementSchema - .extend({ - type: codeableConceptSchema, - cost: backboneElementSchema - .extend({ - type: codeableConceptSchema, - applicability: codeableConceptSchema.optional(), - qualifiers: codeableConceptSchema.array().optional(), - value: quantitySchema.optional(), - }) - .array() - .optional(), - }) - .array() - .optional(), - }) - .array() - .optional(), - }) - .array() - .optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR InsurancePlan resource. - */ -export const insurancePlanSchema: ZodType = - untypedInsurancePlanSchema - -/** - * Wrapper class for FHIR InsurancePlan resources. - * Provides utility methods for working with insurance plans and coverage details. - */ -export class FhirInsurancePlan extends FhirDomainResource { - // Static Functions - - /** - * Parses an InsurancePlan resource from unknown data. - * - * @param value - The data to parse and validate against the InsurancePlan schema - * @returns A FhirInsurancePlan instance containing the validated resource - */ - public static parse(value: unknown): FhirInsurancePlan { - return new FhirInsurancePlan(insurancePlanSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/invoice.ts b/packages/fhir/src/resources/invoice.ts deleted file mode 100644 index e72df66a..00000000 --- a/packages/fhir/src/resources/invoice.ts +++ /dev/null @@ -1,183 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type InvoiceLineItem, - type InvoiceLineItemPriceComponent, - type InvoiceParticipant, - type Coding, - type Invoice, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - backboneElementSchema, - elementSchema, - codeableConceptSchema, - referenceSchema, - stringSchema, - annotationSchema, - moneySchema, - decimalSchema, - dateTimeSchema, - positiveIntSchema, -} from '../elements/index.js' -import { - invoiceStatusSchema, - priceComponentTypeSchema, -} from '../valueSets/index.js' - -const invoiceParticipantSchema: ZodType = - backboneElementSchema.extend({ - role: codeableConceptSchema.optional(), - actor: referenceSchema, - }) - -const invoiceLineItemPriceComponentSchema: ZodType = - backboneElementSchema.extend({ - type: priceComponentTypeSchema, - _type: elementSchema.optional(), - code: codeableConceptSchema.optional(), - factor: decimalSchema.optional(), - amount: moneySchema.optional(), - }) - -const invoiceLineItemSchema: ZodType = - backboneElementSchema.extend({ - sequence: positiveIntSchema.optional(), - chargeItemReference: referenceSchema.optional(), - chargeItemCodeableConcept: codeableConceptSchema.optional(), - priceComponent: invoiceLineItemPriceComponentSchema.array().optional(), - }) - -/** - * Zod schema for FHIR Invoice resource (untyped version). - */ -export const untypedInvoiceSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Invoice').readonly(), - identifier: identifierSchema.array().optional(), - status: invoiceStatusSchema, - _status: elementSchema.optional(), - cancelledReason: stringSchema.optional(), - _cancelledReason: elementSchema.optional(), - type: codeableConceptSchema.optional(), - subject: referenceSchema.optional(), - recipient: referenceSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - participant: invoiceParticipantSchema.array().optional(), - issuer: referenceSchema.optional(), - account: referenceSchema.optional(), - lineItem: invoiceLineItemSchema.array().optional(), - totalPriceComponent: invoiceLineItemPriceComponentSchema.array().optional(), - totalNet: moneySchema.optional(), - totalGross: moneySchema.optional(), - paymentTerms: stringSchema.optional(), - _paymentTerms: elementSchema.optional(), - note: annotationSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Invoice resource. - */ -export const invoiceSchema: ZodType = untypedInvoiceSchema - -/** - * Wrapper class for FHIR Invoice resources. - * Provides utility methods for working with invoices and billing information. - */ -export class FhirInvoice extends FhirDomainResource { - // Static Functions - - /** - * Parses an Invoice resource from unknown data. - * - * @param value - The data to parse and validate against the Invoice schema - * @returns A FhirInvoice instance containing the validated resource - */ - public static parse(value: unknown): FhirInvoice { - return new FhirInvoice(invoiceSchema.parse(value)) - } - - /** - * Gets the invoice date as a JavaScript Date object. - * - * @returns The invoice date, or undefined if not set - */ - public get date(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.date) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } - - /** - * Gets the invoice type as human-readable display text. - * - * @returns The invoice type display text, if available - */ - public get typeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.type) - } - - /** - * Gets all note texts from the invoice. - * - * @returns Array of note texts - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } -} diff --git a/packages/fhir/src/resources/library.ts b/packages/fhir/src/resources/library.ts deleted file mode 100644 index 04c570be..00000000 --- a/packages/fhir/src/resources/library.ts +++ /dev/null @@ -1,151 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type Library } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - attachmentSchema, - booleanSchema, - codeableConceptSchema, - contactDetailSchema, - dataRequirementSchema, - dateSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - markdownSchema, - parameterDefinitionSchema, - periodSchema, - referenceSchema, - relatedArtifactSchema, - stringSchema, - uriSchema, - usageContextSchema, -} from '../elements/index.js' -import { publicationStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR Library resource (untyped version). - */ -export const untypedLibrarySchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Library').readonly(), - url: uriSchema.optional(), - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - subtitle: stringSchema.optional(), - _subtitle: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - type: codeableConceptSchema, - subjectCodeableConcept: codeableConceptSchema.optional(), - subjectReference: referenceSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - approvalDate: dateSchema.optional(), - _approvalDate: elementSchema.optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - topic: codeableConceptSchema.array().optional(), - author: contactDetailSchema.array().optional(), - editor: contactDetailSchema.array().optional(), - reviewer: contactDetailSchema.array().optional(), - endorser: contactDetailSchema.array().optional(), - relatedArtifact: relatedArtifactSchema.array().optional(), - parameter: parameterDefinitionSchema.array().optional(), - dataRequirement: dataRequirementSchema.array().optional(), - content: attachmentSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Library resource. - */ -export const librarySchema: ZodType = untypedLibrarySchema - -/** - * Wrapper class for FHIR Library resources. - * Provides utility methods for working with shareable logic libraries and knowledge artifacts. - */ -export class FhirLibrary extends FhirDomainResource { - // Static Functions - - /** - * Parses a Library resource from unknown data. - * - * @param value - The data to parse and validate against the Library schema - * @returns A FhirLibrary instance containing the validated resource - */ - public static parse(value: unknown): FhirLibrary { - return new FhirLibrary(librarySchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/linkage.ts b/packages/fhir/src/resources/linkage.ts deleted file mode 100644 index 91a26caf..00000000 --- a/packages/fhir/src/resources/linkage.ts +++ /dev/null @@ -1,61 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type LinkageItem, type Linkage } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - domainResourceSchema, - elementSchema, - referenceSchema, -} from '../elements/index.js' -import { linkageItemTypeSchema } from '../valueSets/index.js' - -const linkageItemSchema: ZodType = backboneElementSchema.extend({ - resource: referenceSchema, - type: linkageItemTypeSchema, - _type: elementSchema.optional(), -}) - -/** - * Zod schema for FHIR Linkage resource (untyped version). - */ -export const untypedLinkageSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Linkage').readonly(), - active: booleanSchema.optional(), - _active: elementSchema.optional(), - author: referenceSchema.optional(), - item: linkageItemSchema.array(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Linkage resource. - */ -export const linkageSchema: ZodType = untypedLinkageSchema - -/** - * Wrapper class for FHIR Linkage resources. - * Provides utility methods for working with linkages that connect related resources. - */ -export class FhirLinkage extends FhirDomainResource { - // Static Functions - - /** - * Parses a Linkage resource from unknown data. - * - * @param value - The data to parse and validate against the Linkage schema - * @returns A FhirLinkage instance containing the validated resource - */ - public static parse(value: unknown): FhirLinkage { - return new FhirLinkage(linkageSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/list.ts b/packages/fhir/src/resources/list.ts deleted file mode 100644 index 3ec08a55..00000000 --- a/packages/fhir/src/resources/list.ts +++ /dev/null @@ -1,143 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type ListEntry, type Coding, type List } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { listStatusSchema, listModeSchema } from '../valueSets/index.js' - -const listEntrySchema: ZodType = backboneElementSchema.extend({ - flag: codeableConceptSchema.optional(), - deleted: booleanSchema.optional(), - _deleted: elementSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - item: referenceSchema, -}) - -/** - * Zod schema for FHIR List resource (untyped version). - */ -export const untypedListSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('List').readonly(), - identifier: identifierSchema.array().optional(), - status: listStatusSchema.readonly(), - _status: elementSchema.optional(), - mode: listModeSchema.readonly(), - _mode: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - code: codeableConceptSchema.optional(), - subject: referenceSchema.optional(), - encounter: referenceSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - source: referenceSchema.optional(), - orderedBy: codeableConceptSchema.optional(), - note: annotationSchema.array().optional(), - entry: listEntrySchema.array().optional(), - emptyReason: codeableConceptSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR List resource. - */ -export const listSchema: ZodType = untypedListSchema - -/** - * Wrapper class for FHIR List resources. - */ -export class FhirList extends FhirDomainResource { - /** - * Parses a List resource from unknown data. - * - * @param value - The data to parse and validate against the List schema - * @returns A FhirList instance containing the validated resource - */ - public static parse(value: unknown): FhirList { - return new FhirList(listSchema.parse(value)) - } - - /** - * Gets the date/time the list was prepared as a JavaScript Date object. - * - * @returns The preparation date if available - */ - public get date(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.date) - } - - /** - * Gets note texts from the list. - * - * @returns Array of note text strings - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/location.ts b/packages/fhir/src/resources/location.ts deleted file mode 100644 index 7d74b465..00000000 --- a/packages/fhir/src/resources/location.ts +++ /dev/null @@ -1,175 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type LocationHoursOfOperation, - type LocationPosition, - type Coding, - type Location, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - addressSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - codingSchema, - contactPointSchema, - decimalSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - referenceSchema, - stringSchema, - timeSchema, -} from '../elements/index.js' -import { - daysOfWeekSchema, - locationModeSchema, - locationStatusSchema, -} from '../valueSets/index.js' - -const locationPositionSchema: ZodType = - backboneElementSchema.extend({ - longitude: decimalSchema, - latitude: decimalSchema, - altitude: decimalSchema.optional(), - }) - -const locationHoursOfOperationSchema: ZodType = - backboneElementSchema.extend({ - daysOfWeek: daysOfWeekSchema.array().optional(), - _daysOfWeek: elementSchema.array().optional(), - allDay: booleanSchema.optional(), - openingTime: timeSchema.optional(), - _openingTime: elementSchema.optional(), - closingTime: timeSchema.optional(), - _closingTime: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR Location resource (untyped version). - */ -export const untypedLocationSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Location').readonly(), - identifier: identifierSchema.array().optional(), - status: locationStatusSchema.optional(), - _status: elementSchema.optional(), - operationalStatus: codingSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - alias: stringSchema.array().optional(), - _alias: elementSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - mode: locationModeSchema.optional(), - _mode: elementSchema.optional(), - type: codeableConceptSchema.array().optional(), - telecom: contactPointSchema.array().optional(), - address: addressSchema.optional(), - physicalType: codeableConceptSchema.optional(), - position: locationPositionSchema.optional(), - managingOrganization: referenceSchema.optional(), - partOf: referenceSchema.optional(), - hoursOfOperation: locationHoursOfOperationSchema.array().optional(), - availabilityExceptions: stringSchema.optional(), - _availabilityExceptions: elementSchema.optional(), - endpoint: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Location resource. - */ -export const locationSchema: ZodType = untypedLocationSchema - -/** - * Wrapper class for FHIR Location resources. - * Provides utility methods for working with locations. - */ -export class FhirLocation extends FhirDomainResource { - // Static Functions - - /** - * Parses a Location resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirLocation instance - */ - public static parse(value: unknown): FhirLocation { - return new FhirLocation(locationSchema.parse(value)) - } - - // Properties - - /** - * Gets the type display text. - * - * @returns Array of type display texts - */ - public get typeDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.type) - } - - /** - * Gets all phone numbers for the location. - * - * @returns Array of phone numbers - */ - public get phoneNumbers(): string[] { - return FhirDomainResource.contactPointsBySystem(this.value.telecom, 'phone') - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/manufacturedItemDefinition.ts b/packages/fhir/src/resources/manufacturedItemDefinition.ts deleted file mode 100644 index 01e4a110..00000000 --- a/packages/fhir/src/resources/manufacturedItemDefinition.ts +++ /dev/null @@ -1,129 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type ManufacturedItemDefinition, - type ManufacturedItemDefinitionProperty, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - attachmentSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - dateSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - quantitySchema, - referenceSchema, -} from '../elements/index.js' -import { manufacturedItemDefinitionStatusSchema } from '../valueSets/index.js' - -const manufacturedItemDefinitionPropertySchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - valueCodeableConcept: codeableConceptSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueDate: dateSchema.optional(), - _valueDate: elementSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueAttachment: attachmentSchema.optional(), - }) - -/** - * Zod schema for FHIR ManufacturedItemDefinition resource (untyped version). - */ -export const untypedManufacturedItemDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ManufacturedItemDefinition').readonly(), - identifier: identifierSchema.array().optional(), - ingredient: codeableConceptSchema.array().optional(), - manufacturedDoseForm: codeableConceptSchema, - manufacturer: referenceSchema.array().optional(), - property: manufacturedItemDefinitionPropertySchema.array().optional(), - status: manufacturedItemDefinitionStatusSchema, - _status: elementSchema.optional(), - unitOfPresentation: codeableConceptSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ManufacturedItemDefinition resource. - */ -export const manufacturedItemDefinitionSchema: ZodType = - untypedManufacturedItemDefinitionSchema - -/** - * Wrapper class for FHIR ManufacturedItemDefinition resources. - * Provides utility methods for working with manufactured item definitions and product specifications. - */ -export class FhirManufacturedItemDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses a ManufacturedItemDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the ManufacturedItemDefinition schema - * @returns A FhirManufacturedItemDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirManufacturedItemDefinition { - return new FhirManufacturedItemDefinition( - manufacturedItemDefinitionSchema.parse(value), - ) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/measure.ts b/packages/fhir/src/resources/measure.ts deleted file mode 100644 index 6baeea01..00000000 --- a/packages/fhir/src/resources/measure.ts +++ /dev/null @@ -1,225 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type Measure, - type MeasureGroup, - type MeasureGroupPopulation, - type MeasureGroupStratifier, - type MeasureGroupStratifierComponent, - type MeasureSupplementalData, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - canonicalSchema, - codeableConceptSchema, - contactDetailSchema, - dateSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - expressionSchema, - identifierSchema, - markdownSchema, - periodSchema, - referenceSchema, - relatedArtifactSchema, - stringSchema, - uriSchema, - usageContextSchema, -} from '../elements/index.js' -import { publicationStatusSchema } from '../valueSets/index.js' - -const measureGroupStratifierComponentSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - criteria: expressionSchema, - }) - -const measureGroupStratifierSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - criteria: expressionSchema.optional(), - component: measureGroupStratifierComponentSchema.array().optional(), - }) - -const measureGroupPopulationSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - criteria: expressionSchema, - }) - -const measureGroupSchema: ZodType = backboneElementSchema.extend({ - code: codeableConceptSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - population: measureGroupPopulationSchema.array().optional(), - stratifier: measureGroupStratifierSchema.array().optional(), -}) - -const measureSupplementalDataSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema.optional(), - usage: codeableConceptSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - criteria: expressionSchema, - }) - -/** - * Zod schema for FHIR Measure resource (untyped version). - */ -export const untypedMeasureSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Measure').readonly(), - url: uriSchema.optional(), - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - subtitle: stringSchema.optional(), - _subtitle: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - subjectCodeableConcept: codeableConceptSchema.optional(), - subjectReference: referenceSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - purpose: markdownSchema.optional(), - _purpose: elementSchema.optional(), - usage: stringSchema.optional(), - _usage: elementSchema.optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - approvalDate: dateSchema.optional(), - _approvalDate: elementSchema.optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - topic: codeableConceptSchema.array().optional(), - author: contactDetailSchema.array().optional(), - editor: contactDetailSchema.array().optional(), - reviewer: contactDetailSchema.array().optional(), - endorser: contactDetailSchema.array().optional(), - relatedArtifact: relatedArtifactSchema.array().optional(), - library: canonicalSchema.array().optional(), - _library: elementSchema.array().optional(), - disclaimer: markdownSchema.optional(), - _disclaimer: elementSchema.optional(), - scoring: codeableConceptSchema.optional(), - compositeScoring: codeableConceptSchema.optional(), - type: codeableConceptSchema.array().optional(), - riskAdjustment: stringSchema.optional(), - _riskAdjustment: elementSchema.optional(), - rateAggregation: stringSchema.optional(), - _rateAggregation: elementSchema.optional(), - rationale: markdownSchema.optional(), - _rationale: elementSchema.optional(), - clinicalRecommendationStatement: markdownSchema.optional(), - _clinicalRecommendationStatement: elementSchema.optional(), - improvementNotation: codeableConceptSchema.optional(), - definition: markdownSchema.array().optional(), - _definition: elementSchema.array().optional(), - guidance: markdownSchema.optional(), - _guidance: elementSchema.optional(), - group: measureGroupSchema.array().optional(), - supplementalData: measureSupplementalDataSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Measure resource. - */ -export const measureSchema: ZodType = untypedMeasureSchema - -/** - * Wrapper class for FHIR Measure resources. - * Provides utility methods for working with quality measures and clinical decision support. - */ -export class FhirMeasure extends FhirDomainResource { - // Static Functions - - /** - * Parses a Measure resource from unknown data. - * - * @param value - The data to parse and validate against the Measure schema - * @returns A FhirMeasure instance containing the validated resource - */ - public static parse(value: unknown): FhirMeasure { - return new FhirMeasure(measureSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/measureReport.ts b/packages/fhir/src/resources/measureReport.ts deleted file mode 100644 index 4f8fe7ee..00000000 --- a/packages/fhir/src/resources/measureReport.ts +++ /dev/null @@ -1,202 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type MeasureReport, - type MeasureReportGroup, - type MeasureReportGroupPopulation, - type MeasureReportGroupStratifier, - type MeasureReportGroupStratifierStratum, - type MeasureReportGroupStratifierStratumPopulation, - type MeasureReportGroupStratifierStratumComponent, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - intSchema, - periodSchema, - quantitySchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { - measureReportStatusSchema, - measureReportTypeSchema, -} from '../valueSets/index.js' - -const measureReportGroupStratifierStratumComponentSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema, - value: codeableConceptSchema, - }) - -const measureReportGroupStratifierStratumPopulationSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema.optional(), - count: intSchema.optional(), - subjectResults: referenceSchema.optional(), - }) - -const measureReportGroupStratifierStratumSchema: ZodType = - backboneElementSchema.extend({ - value: codeableConceptSchema.optional(), - component: measureReportGroupStratifierStratumComponentSchema - .array() - .optional(), - population: measureReportGroupStratifierStratumPopulationSchema - .array() - .optional(), - measureScore: quantitySchema.optional(), - }) - -const measureReportGroupStratifierSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema.array().optional(), - stratum: measureReportGroupStratifierStratumSchema.array().optional(), - }) - -const measureReportGroupPopulationSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema.optional(), - count: intSchema.optional(), - subjectResults: referenceSchema.optional(), - }) - -const measureReportGroupSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema.optional(), - population: measureReportGroupPopulationSchema.array().optional(), - measureScore: quantitySchema.optional(), - stratifier: measureReportGroupStratifierSchema.array().optional(), - }) - -/** - * Zod schema for FHIR MeasureReport resource (untyped version). - */ -export const untypedMeasureReportSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('MeasureReport').readonly(), - identifier: identifierSchema.array().optional(), - status: measureReportStatusSchema, - _status: elementSchema.optional(), - type: measureReportTypeSchema, - _type: elementSchema.optional(), - measure: stringSchema, - _measure: elementSchema.optional(), - subject: referenceSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - reporter: referenceSchema.optional(), - period: periodSchema, - improvementNotation: codeableConceptSchema.optional(), - group: measureReportGroupSchema.array().optional(), - evaluatedResource: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR MeasureReport resource. - */ -export const measureReportSchema: ZodType = - untypedMeasureReportSchema - -/** - * Wrapper class for FHIR MeasureReport resources. - * Provides utility methods for working with quality measure reports and results. - */ -export class FhirMeasureReport extends FhirDomainResource { - /** - * Parses a MeasureReport resource from unknown data. - * - * @param value - The data to parse and validate against the MeasureReport schema - * @returns A FhirMeasureReport instance containing the validated resource - */ - public static parse(value: unknown): FhirMeasureReport { - return new FhirMeasureReport(measureReportSchema.parse(value)) - } - - /** - * Get the date the report was generated. - * - * @returns The date the report was generated, or undefined if not set - */ - public get date(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.date) - } - - /** - * Get the period start of the report. - * - * @returns The period start date, or undefined if not set - */ - public get periodStart(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.period.start) - } - - /** - * Get the period end of the report. - * - * @returns The period end date, or undefined if not set - */ - public get periodEnd(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.period.end) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/media.ts b/packages/fhir/src/resources/media.ts deleted file mode 100644 index 8fd182e2..00000000 --- a/packages/fhir/src/resources/media.ts +++ /dev/null @@ -1,166 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type Media } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - attachmentSchema, - codeableConceptSchema, - dateTimeSchema, - decimalSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - instantSchema, - intSchema, - periodSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { mediaStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR Media resource (untyped version). - */ -export const untypedMediaSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Media').readonly(), - identifier: identifierSchema.array().optional(), - basedOn: referenceSchema.array().optional(), - partOf: referenceSchema.array().optional(), - status: mediaStatusSchema, - _status: elementSchema.optional(), - type: codeableConceptSchema.optional(), - modality: codeableConceptSchema.optional(), - view: codeableConceptSchema.optional(), - subject: referenceSchema.optional(), - encounter: referenceSchema.optional(), - createdDateTime: dateTimeSchema.optional(), - _createdDateTime: elementSchema.optional(), - createdPeriod: periodSchema.optional(), - issued: instantSchema.optional(), - _issued: elementSchema.optional(), - operator: referenceSchema.optional(), - reasonCode: codeableConceptSchema.array().optional(), - bodySite: codeableConceptSchema.optional(), - deviceName: stringSchema.optional(), - _deviceName: elementSchema.optional(), - device: referenceSchema.optional(), - height: intSchema.optional(), - width: intSchema.optional(), - frames: intSchema.optional(), - duration: decimalSchema.optional(), - content: attachmentSchema, - note: annotationSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Media resource. - */ -export const mediaSchema: ZodType = untypedMediaSchema - -/** - * Wrapper class for FHIR Media resources. - * Provides utility methods for working with media attachments and their metadata. - */ -export class FhirMedia extends FhirDomainResource { - /** - * Parses a Media resource from unknown data. - * - * @param value - The data to parse and validate against the Media schema - * @returns A FhirMedia instance containing the validated resource - */ - public static parse(value: unknown): FhirMedia { - return new FhirMedia(mediaSchema.parse(value)) - } - - /** - * Gets the creation date/time of the media as a JavaScript Date object. - * - * @returns The creation date if available - */ - public get createdDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.createdDateTime) - } - - /** - * Gets the issued date/time of the media as a JavaScript Date object. - * - * @returns The issued date if available - */ - public get issuedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.issued) - } - - /** - * Gets the media type as display text. - * - * @returns The type display text, if available - */ - public get typeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.type) - } - - /** - * Get the modality of the media as display text. - * - * @returns The modality display text, or undefined if not set - */ - public get modalityDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.modality) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/medication.ts b/packages/fhir/src/resources/medication.ts deleted file mode 100644 index c2152490..00000000 --- a/packages/fhir/src/resources/medication.ts +++ /dev/null @@ -1,153 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type MedicationBatch, - type MedicationIngredient, - type Coding, - type Medication, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - quantitySchema, - ratioSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { medicationStatusSchema } from '../valueSets/index.js' - -const medicationIngredientSchema: ZodType = - backboneElementSchema.extend({ - itemCodeableConcept: codeableConceptSchema.optional(), - itemReference: referenceSchema.optional(), - isActive: booleanSchema.optional(), - _isActive: elementSchema.optional(), - strength: ratioSchema.optional(), - }) - -const medicationBatchSchema: ZodType = elementSchema.extend({ - lotNumber: stringSchema.optional(), - _lotNumber: elementSchema.optional(), - expirationDate: dateTimeSchema.optional(), - _expirationDate: elementSchema.optional(), -}) - -/** - * Zod schema for FHIR Medication resource (untyped version). - */ -export const untypedMedicationSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Medication').readonly(), - identifier: identifierSchema.array().optional(), - code: codeableConceptSchema.optional(), - status: medicationStatusSchema.optional(), - _status: elementSchema.optional(), - manufacturer: referenceSchema.optional(), - form: codeableConceptSchema.optional(), - amount: quantitySchema.optional(), - ingredient: medicationIngredientSchema.array().optional(), - batch: medicationBatchSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Medication resource. - */ -export const medicationSchema: ZodType = untypedMedicationSchema - -/** - * Wrapper class for FHIR Medication resources. - * Provides utility methods for working with medications. - */ -export class FhirMedication extends FhirDomainResource { - // Static Functions - - /** - * Parses a Medication resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirMedication instance - */ - public static parse(value: unknown): FhirMedication { - return new FhirMedication(medicationSchema.parse(value)) - } - - // Properties - - /** - * Gets the medication code display text. - * - * @returns The code display - */ - public get codeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.code) - } - - /** - * Gets the medication form display text. - * - * @returns The form display - */ - public get formDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.form) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/medicationAdministration.ts b/packages/fhir/src/resources/medicationAdministration.ts deleted file mode 100644 index ae7140eb..00000000 --- a/packages/fhir/src/resources/medicationAdministration.ts +++ /dev/null @@ -1,201 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type MedicationAdministrationPerformer, - type Coding, - type MedicationAdministration, - type MedicationAdministrationDosage, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - quantitySchema, - ratioSchema, - referenceSchema, - stringSchema, - uriSchema, -} from '../elements/index.js' -import { medicationAdministrationStatusSchema } from '../valueSets/index.js' - -const medicationAdministrationPerformerSchema: ZodType = - backboneElementSchema.extend({ - function: codeableConceptSchema.optional(), - actor: referenceSchema, - }) - -const medicationAdministrationDosageSchema: ZodType = - backboneElementSchema.extend({ - text: stringSchema.optional(), - site: codeableConceptSchema.optional(), - route: codeableConceptSchema.optional(), - method: codeableConceptSchema.optional(), - dose: quantitySchema.optional(), - rateRatio: ratioSchema.optional(), - rateQuantity: quantitySchema.optional(), - }) - -/** - * Zod schema for FHIR MedicationAdministration resource (untyped version). - */ -export const untypedMedicationAdministrationSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('MedicationAdministration').readonly(), - identifier: identifierSchema.array().optional(), - instantiates: uriSchema.array().optional(), - _instantiates: elementSchema.array().optional(), - partOf: referenceSchema.array().optional(), - status: medicationAdministrationStatusSchema, - _status: elementSchema.optional(), - statusReason: codeableConceptSchema.array().optional(), - category: codeableConceptSchema.optional(), - medicationCodeableConcept: codeableConceptSchema.optional(), - medicationReference: referenceSchema.optional(), - subject: referenceSchema, - context: referenceSchema.optional(), - supportingInformation: referenceSchema.array().optional(), - effectiveDateTime: dateTimeSchema.optional(), - _effectiveDateTime: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - performer: medicationAdministrationPerformerSchema.array().optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - request: referenceSchema.optional(), - device: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - dosage: medicationAdministrationDosageSchema.optional(), - eventHistory: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR MedicationAdministration resource. - */ -export const medicationAdministrationSchema: ZodType = - untypedMedicationAdministrationSchema - -/** - * Wrapper class for FHIR MedicationAdministration resources. - * Provides utility methods for working with medication administrations. - */ -export class FhirMedicationAdministration extends FhirDomainResource { - // Static Functions - - /** - * Parses a MedicationAdministration resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirMedicationAdministration instance - */ - public static parse(value: unknown): FhirMedicationAdministration { - return new FhirMedicationAdministration( - medicationAdministrationSchema.parse(value), - ) - } - - // Properties - - /** - * Gets the effective date/time as a JavaScript Date object. - * - * @returns The effective date if available - */ - public get effectiveDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.effectiveDateTime) - } - - /** - * Gets the effective period start date. - * - * @returns The effective period start date, or undefined if not set - */ - public get effectivePeriodStartDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.effectivePeriod?.start) - } - - /** - * Gets the effective period end date. - * - * @returns The effective period end date, or undefined if not set - */ - public get effectivePeriodEndDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.effectivePeriod?.end) - } - - /** - * Gets note texts from the medication administration. - * - * @returns Array of note texts - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Gets all reason code displays. - * - * @returns Array of reason display texts - */ - public get reasonDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.reasonCode) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/medicationDispense.ts b/packages/fhir/src/resources/medicationDispense.ts deleted file mode 100644 index 29d6050e..00000000 --- a/packages/fhir/src/resources/medicationDispense.ts +++ /dev/null @@ -1,221 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type MedicationDispensePerformer, - type MedicationDispenseSubstitution, - type Coding, - type MedicationDispense, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - dosageSchema, - elementSchema, - identifierSchema, - quantitySchema, - referenceSchema, -} from '../elements/index.js' -import { medicationDispenseStatusSchema } from '../valueSets/index.js' - -const medicationDispensePerformerSchema: ZodType = - backboneElementSchema.extend({ - function: codeableConceptSchema.optional(), - actor: referenceSchema, - }) - -const medicationDispenseSubstitutionSchema: ZodType = - backboneElementSchema.extend({ - wasSubstituted: booleanSchema, - _wasSubstituted: elementSchema.optional(), - type: codeableConceptSchema.optional(), - reason: codeableConceptSchema.array().optional(), - responsibleParty: referenceSchema.array().optional(), - }) - -/** - * Zod schema for FHIR MedicationDispense resource (untyped version). - */ -export const untypedMedicationDispenseSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('MedicationDispense').readonly(), - identifier: identifierSchema.array().optional(), - partOf: referenceSchema.array().optional(), - status: medicationDispenseStatusSchema, - _status: elementSchema.optional(), - statusReasonCodeableConcept: codeableConceptSchema.optional(), - statusReasonReference: referenceSchema.optional(), - category: codeableConceptSchema.optional(), - medicationCodeableConcept: codeableConceptSchema.optional(), - medicationReference: referenceSchema.optional(), - subject: referenceSchema.optional(), - context: referenceSchema.optional(), - supportingInformation: referenceSchema.array().optional(), - performer: medicationDispensePerformerSchema.array().optional(), - location: referenceSchema.optional(), - authorizingPrescription: referenceSchema.array().optional(), - type: codeableConceptSchema.optional(), - quantity: quantitySchema.optional(), - daysSupply: quantitySchema.optional(), - whenPrepared: dateTimeSchema.optional(), - _whenPrepared: elementSchema.optional(), - whenHandedOver: dateTimeSchema.optional(), - _whenHandedOver: elementSchema.optional(), - destination: referenceSchema.optional(), - receiver: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - dosageInstruction: dosageSchema.array().optional(), - substitution: medicationDispenseSubstitutionSchema.optional(), - detectedIssue: referenceSchema.array().optional(), - eventHistory: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR MedicationDispense resource. - */ -export const medicationDispenseSchema: ZodType = - untypedMedicationDispenseSchema - -/** - * Wrapper class for FHIR MedicationDispense resources. - * Provides utility methods for working with medication dispensing information. - */ -export class FhirMedicationDispense extends FhirDomainResource { - // Static Functions - - /** - * Parses a MedicationDispense resource from unknown data. - * - * @param value - The data to parse and validate against the MedicationDispense schema - * @returns A FhirMedicationDispense instance containing the validated resource - */ - public static parse(value: unknown): FhirMedicationDispense { - return new FhirMedicationDispense(medicationDispenseSchema.parse(value)) - } - - /** - * Gets the date when the medication was prepared as a JavaScript Date object. - * - * @returns The preparation date if available, undefined otherwise - * - * @example - * ```typescript - * const preparedDate = dispense.whenPreparedDate - * console.log(`Prepared on: ${preparedDate?.toLocaleDateString()}`) - * ``` - */ - public get whenPreparedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.whenPrepared) - } - - /** - * Gets the date when the medication was handed over as a JavaScript Date object. - * - * @returns The handover date if available, undefined otherwise - * - * @example - * ```typescript - * const handedOverDate = dispense.whenHandedOverDate - * console.log(`Handed over on: ${handedOverDate?.toLocaleDateString()}`) - * ``` - */ - public get whenHandedOverDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.whenHandedOver) - } - - /** - * Gets the medication display text from either CodeableConcept or Reference. - * - * @returns Display text for the medication, or undefined if not available - * - * @example - * ```typescript - * const medication = dispense.medicationDisplay - * console.log(`Dispensed medication: ${medication}`) - * ``` - */ - public get medicationDisplay(): string | undefined { - if (this.value.medicationCodeableConcept) { - return FhirDomainResource.codeableConceptDisplay( - this.value.medicationCodeableConcept, - ) - } - if (this.value.medicationReference) { - return this.value.medicationReference.display - } - return undefined - } - - /** - * Gets the note texts as an array of strings. - * - * @returns Array of note texts - * - * @example - * ```typescript - * const notes = dispense.noteTexts - * notes.forEach(note => console.log(note)) - * ``` - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/medicationKnowledge.ts b/packages/fhir/src/resources/medicationKnowledge.ts deleted file mode 100644 index 6f494139..00000000 --- a/packages/fhir/src/resources/medicationKnowledge.ts +++ /dev/null @@ -1,278 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type MedicationKnowledgeAdministrationGuidelines, - type MedicationKnowledgeCost, - type MedicationKnowledgeIngredient, - type MedicationKnowledgeMedicineClassification, - type MedicationKnowledgeMonitoringProgram, - type MedicationKnowledgeMonograph, - type MedicationKnowledgePackaging, - type MedicationKnowledgeRelatedMedicationKnowledge, - type MedicationKnowledge, - type MedicationKnowledgeRegulatory, - type MedicationKnowledgeDrugCharacteristic, - type MedicationKnowledgeKinetics, - type MedicationKnowledgeRegulatorySubstitution, - type MedicationKnowledgeRegulatorySchedule, - type MedicationKnowledgeRegulatoryMaxDispense, - type MedicationKnowledgeAdministrationGuidelinesDosage, - type MedicationKnowledgeAdministrationGuidelinesPatientCharacteristics, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - base64BinarySchema, - booleanSchema, - codeableConceptSchema, - domainResourceSchema, - dosageSchema, - elementSchema, - markdownSchema, - quantitySchema, - ratioSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { medicationStatusSchema } from '../valueSets/index.js' - -const medicationKnowledgeRelatedMedicationKnowledgeSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - _type: elementSchema.optional(), - reference: referenceSchema.array(), - }) - -const medicationKnowledgeMonographSchema: ZodType = - backboneElementSchema.extend({ - source: referenceSchema.optional(), - type: codeableConceptSchema.optional(), - }) - -const medicationKnowledgeIngredientSchema: ZodType = - backboneElementSchema.extend({ - itemCodeableConcept: codeableConceptSchema.optional(), - itemReference: referenceSchema.optional(), - isActive: booleanSchema.optional(), - _isActive: elementSchema.optional(), - strength: ratioSchema.optional(), - }) - -const medicationKnowledgeCostSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - source: stringSchema.optional(), - _source: elementSchema.optional(), - cost: quantitySchema, - }) - -const medicationKnowledgeMonitoringProgramSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - }) - -const medicationKnowledgeAdministrationGuidelinesDosageSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - dosage: dosageSchema.array(), - }) - -const medicationKnowledgeAdministrationGuidelinesPatientCharacteristicsSchema: ZodType = - backboneElementSchema.extend({ - characteristicCodeableConcept: codeableConceptSchema.optional(), - characteristicQuantity: quantitySchema.optional(), - value: stringSchema.array().optional(), - }) - -const medicationKnowledgeAdministrationGuidelinesSchema: ZodType = - backboneElementSchema.extend({ - dosage: medicationKnowledgeAdministrationGuidelinesDosageSchema - .array() - .optional(), - indicationCodeableConcept: codeableConceptSchema.optional(), - indicationReference: referenceSchema.optional(), - patientCharacteristics: - medicationKnowledgeAdministrationGuidelinesPatientCharacteristicsSchema - .array() - .optional(), - }) - -const medicationKnowledgeMedicineClassificationSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - classification: codeableConceptSchema.array(), - }) - -const medicationKnowledgePackagingSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - quantity: quantitySchema, - }) - -const medicationKnowledgeDrugCharacteristicSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - valueCodeableConcept: codeableConceptSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: elementSchema.optional(), - }) - -const medicationKnowledgeRegulatorySubstitutionSchema: ZodType = - backboneElementSchema.extend({ - allowed: booleanSchema, - _allowed: elementSchema.optional(), - type: codeableConceptSchema, - }) - -const medicationKnowledgeRegulatoryScheduleSchema: ZodType = - backboneElementSchema.extend({ - schedule: codeableConceptSchema, - quantity: quantitySchema.optional(), - }) - -const medicationKnowledgeRegulatoryMaxDispenseSchema: ZodType = - backboneElementSchema.extend({ - quantity: quantitySchema, - period: quantitySchema.optional(), - }) - -const medicationKnowledgeRegulatorySchema: ZodType = - backboneElementSchema.extend({ - regulatoryAuthority: referenceSchema, - substitution: medicationKnowledgeRegulatorySubstitutionSchema - .array() - .optional(), - schedule: medicationKnowledgeRegulatoryScheduleSchema.array().optional(), - maxDispense: medicationKnowledgeRegulatoryMaxDispenseSchema.optional(), - }) - -const medicationKnowledgeKineticsSchema: ZodType = - backboneElementSchema.extend({ - areaUnderCurve: quantitySchema.array().optional(), - lethalDose50: quantitySchema.array().optional(), - halfLifePeriod: quantitySchema.optional(), - }) - -/** - * Zod schema for FHIR MedicationKnowledge resource (untyped version). - */ -export const untypedMedicationKnowledgeSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('MedicationKnowledge').readonly(), - code: codeableConceptSchema.optional(), - status: medicationStatusSchema.optional(), - _status: elementSchema.optional(), - manufacturer: referenceSchema.optional(), - doseForm: codeableConceptSchema.optional(), - amount: quantitySchema.optional(), - synonym: stringSchema.array().optional(), - _synonym: elementSchema.array().optional(), - relatedMedicationKnowledge: - medicationKnowledgeRelatedMedicationKnowledgeSchema.array().optional(), - associatedMedication: referenceSchema.array().optional(), - productType: codeableConceptSchema.array().optional(), - monograph: medicationKnowledgeMonographSchema.array().optional(), - ingredient: medicationKnowledgeIngredientSchema.array().optional(), - preparationInstruction: markdownSchema.optional(), - intendedRoute: codeableConceptSchema.array().optional(), - cost: medicationKnowledgeCostSchema.array().optional(), - monitoringProgram: medicationKnowledgeMonitoringProgramSchema - .array() - .optional(), - administrationGuidelines: medicationKnowledgeAdministrationGuidelinesSchema - .array() - .optional(), - medicineClassification: medicationKnowledgeMedicineClassificationSchema - .array() - .optional(), - packaging: medicationKnowledgePackagingSchema.optional(), - drugCharacteristic: medicationKnowledgeDrugCharacteristicSchema - .array() - .optional(), - contraindication: referenceSchema.array().optional(), - regulatory: medicationKnowledgeRegulatorySchema.array().optional(), - kinetics: medicationKnowledgeKineticsSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR MedicationKnowledge resource. - */ -export const medicationKnowledgeSchema: ZodType = - untypedMedicationKnowledgeSchema - -/** - * Wrapper class for FHIR MedicationKnowledge resources. - * Provides utility methods for working with medication knowledge and formulary information. - */ -export class FhirMedicationKnowledge extends FhirDomainResource { - // Static Functions - - /** - * Parses a MedicationKnowledge resource from unknown data. - * - * @param value - The data to parse and validate against the MedicationKnowledge schema - * @returns A FhirMedicationKnowledge instance containing the validated resource - */ - public static parse(value: unknown): FhirMedicationKnowledge { - return new FhirMedicationKnowledge(medicationKnowledgeSchema.parse(value)) - } - - /** - * Get code display text. - * @returns Code display text - */ - public get codeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.code) - } - - /** - * Get dose form display text. - * @returns Dose form display text - */ - public get doseFormDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.doseForm) - } - - /** - * Get all medicine classification displays. - * @returns Array of classification display texts - */ - public get medicineClassificationDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays( - this.value.medicineClassification?.map((c) => c.type), - ) - } - - /** - * Get all monitoring program names. - * @returns Array of monitoring program names - */ - public get monitoringProgramNames(): string[] { - return (this.value.monitoringProgram ?? []).flatMap((program) => - program.name ? [program.name] : [], - ) - } - - /** - * Check if medication has any contraindications. - * @returns True if contraindications exist - */ - public get hasContraindications(): boolean { - return ( - !!this.value.contraindication && this.value.contraindication.length > 0 - ) - } -} diff --git a/packages/fhir/src/resources/medicationRequest.ts b/packages/fhir/src/resources/medicationRequest.ts deleted file mode 100644 index 89961f01..00000000 --- a/packages/fhir/src/resources/medicationRequest.ts +++ /dev/null @@ -1,191 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type MedicationRequest } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - booleanSchema, - canonicalSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - dosageSchema, - elementSchema, - identifierSchema, - periodSchema, - quantitySchema, - referenceSchema, - unsignedIntSchema, - uriSchema, -} from '../elements/index.js' -import { - medicationRequestIntentSchema, - medicationRequestStatusSchema, - requestPrioritySchema, -} from '../valueSets/index.js' - -/** - * Zod schema for FHIR MedicationRequest resource (untyped version). - */ -export const untypedMedicationRequestSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('MedicationRequest'), - identifier: identifierSchema.array().optional(), - status: medicationRequestStatusSchema, - _status: elementSchema.optional(), - statusReason: codeableConceptSchema.optional(), - intent: medicationRequestIntentSchema, - _intent: elementSchema.optional(), - category: codeableConceptSchema.array().optional(), - priority: requestPrioritySchema.optional(), - _priority: elementSchema.optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: elementSchema.optional(), - medicationCodeableConcept: codeableConceptSchema.optional(), - medicationReference: referenceSchema.optional(), - subject: referenceSchema, - encounter: referenceSchema.optional(), - supportingInformation: referenceSchema.array().optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: elementSchema.optional(), - requester: referenceSchema.optional(), - performer: referenceSchema.optional(), - performerType: codeableConceptSchema.optional(), - recorder: referenceSchema.optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: elementSchema.array().optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: elementSchema.array().optional(), - basedOn: referenceSchema.array().optional(), - groupIdentifier: identifierSchema.optional(), - courseOfTherapyType: codeableConceptSchema.optional(), - insurance: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - dosageInstruction: dosageSchema.array().optional(), - dispenseRequest: backboneElementSchema - .extend({ - initialFill: backboneElementSchema - .extend({ - quantity: quantitySchema.optional(), - duration: quantitySchema.optional(), - }) - .optional(), - dispenseInterval: quantitySchema.optional(), - validityPeriod: periodSchema.optional(), - numberOfRepeatsAllowed: unsignedIntSchema.optional(), - quantity: quantitySchema.optional(), - expectedSupplyDuration: quantitySchema.optional(), - performer: referenceSchema.optional(), - }) - .optional(), - substitution: backboneElementSchema - .extend({ - allowedBoolean: booleanSchema.optional(), - _allowedBoolean: elementSchema.optional(), - allowedCodeableConcept: codeableConceptSchema.optional(), - }) - .optional(), - priorPrescription: referenceSchema.optional(), - detectedIssue: referenceSchema.array().optional(), - eventHistory: referenceSchema.array().optional(), - reportedBoolean: booleanSchema.optional(), - _reportedBoolean: elementSchema.optional(), - reportedReference: referenceSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR MedicationRequest resource. - */ -export const medicationRequestSchema: ZodType = - untypedMedicationRequestSchema - -/** - * Wrapper class for FHIR MedicationRequest resources. - * Provides utility methods for working with medication orders. - */ -export class FhirMedicationRequest extends FhirDomainResource { - // Static Functions - - /** - * Parses a MedicationRequest resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirMedicationRequest instance - */ - public static parse(value: unknown): FhirMedicationRequest { - return new FhirMedicationRequest(medicationRequestSchema.parse(value)) - } - - // Properties - - /** - * Gets the authored date as a JavaScript Date object. - * - * @returns The authored date if available, undefined otherwise - * - * @example - * ```typescript - * const authoredDate = medicationRequest.authoredDate - * ``` - */ - public get authoredDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.authoredOn) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/medicationStatement.ts b/packages/fhir/src/resources/medicationStatement.ts deleted file mode 100644 index ea1f8eb2..00000000 --- a/packages/fhir/src/resources/medicationStatement.ts +++ /dev/null @@ -1,153 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type MedicationStatement } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - codeableConceptSchema, - dateTimeSchema, - dosageSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - referenceSchema, -} from '../elements/index.js' -import { medicationStatementStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR MedicationStatement resource (untyped version). - */ -export const untypedMedicationStatementSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('MedicationStatement').readonly(), - identifier: identifierSchema.array().optional(), - basedOn: referenceSchema.array().optional(), - partOf: referenceSchema.array().optional(), - status: medicationStatementStatusSchema, - _status: elementSchema.optional(), - statusReason: codeableConceptSchema.array().optional(), - category: codeableConceptSchema.optional(), - medicationCodeableConcept: codeableConceptSchema.optional(), - medicationReference: referenceSchema.optional(), - subject: referenceSchema, - context: referenceSchema.optional(), - effectiveDateTime: dateTimeSchema.optional(), - _effectiveDateTime: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - dateAsserted: dateTimeSchema.optional(), - _dateAsserted: elementSchema.optional(), - informationSource: referenceSchema.optional(), - derivedFrom: referenceSchema.array().optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - dosage: dosageSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR MedicationStatement resource. - */ -export const medicationStatementSchema: ZodType = - untypedMedicationStatementSchema - -/** - * Wrapper class for FHIR MedicationStatement resources. - * Provides utility methods for working with medication statements. - */ -export class FhirMedicationStatement extends FhirDomainResource { - // Static Functions - - /** - * Parses a MedicationStatement resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirMedicationStatement instance - */ - public static parse(value: unknown): FhirMedicationStatement { - return new FhirMedicationStatement(medicationStatementSchema.parse(value)) - } - - // Properties - - /** - * Gets the effective date/time as a JavaScript Date object. - * - * @returns The effective date if available - */ - public get effectiveDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.effectiveDateTime) - } - - /** - * Gets the date asserted as a JavaScript Date object. - * - * @returns The date asserted if available - */ - public get dateAsserted(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.dateAsserted) - } - - /** - * Gets note texts from the medication statement. - * - * @returns Array of note texts - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/medicinalProductDefinition.ts b/packages/fhir/src/resources/medicinalProductDefinition.ts deleted file mode 100644 index af17cfff..00000000 --- a/packages/fhir/src/resources/medicinalProductDefinition.ts +++ /dev/null @@ -1,210 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type MedicinalProductDefinition, - type MedicinalProductDefinitionCharacteristic, - type MedicinalProductDefinitionContact, - type MedicinalProductDefinitionCrossReference, - type MedicinalProductDefinitionName, - type MedicinalProductDefinitionNameCountryLanguage, - type MedicinalProductDefinitionNameNamePart, - type MedicinalProductDefinitionOperation, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - codeableReferenceSchema, - codingSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - intSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' - -const medicinalProductDefinitionCharacteristicSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - valueCodeableConcept: codeableConceptSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueQuantity: elementSchema.optional(), - valueInteger: intSchema.optional(), - valueDate: stringSchema.optional(), - _valueDate: elementSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueAttachment: elementSchema.optional(), - }) - -const medicinalProductDefinitionContactSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - contact: referenceSchema, - }) - -const medicinalProductDefinitionCrossReferenceSchema: ZodType = - backboneElementSchema.extend({ - product: codeableReferenceSchema, - type: codeableConceptSchema.optional(), - }) - -const medicinalProductDefinitionNameNamePartSchema: ZodType = - backboneElementSchema.extend({ - part: stringSchema, - _part: elementSchema.optional(), - type: codeableConceptSchema, - }) - -const medicinalProductDefinitionNameCountryLanguageSchema: ZodType = - backboneElementSchema.extend({ - country: codeableConceptSchema, - jurisdiction: codeableConceptSchema.optional(), - language: codeableConceptSchema, - }) - -const medicinalProductDefinitionNameSchema: ZodType = - backboneElementSchema.extend({ - productName: stringSchema, - _productName: elementSchema.optional(), - type: codeableConceptSchema.optional(), - namePart: medicinalProductDefinitionNameNamePartSchema.array().optional(), - countryLanguage: medicinalProductDefinitionNameCountryLanguageSchema - .array() - .optional(), - }) - -const medicinalProductDefinitionOperationSchema: ZodType = - backboneElementSchema.extend({ - type: codeableReferenceSchema.optional(), - effectiveDate: elementSchema.optional(), - organization: referenceSchema.array().optional(), - confidentialityIndicator: codeableConceptSchema.optional(), - }) - -/** - * Zod schema for FHIR MedicinalProductDefinition resource (untyped version). - */ -export const untypedMedicinalProductDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('MedicinalProductDefinition').readonly(), - identifier: identifierSchema.array().optional(), - type: codeableConceptSchema.optional(), - domain: codeableConceptSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - status: codeableConceptSchema.optional(), - statusDate: stringSchema.optional(), - _statusDate: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - combinedPharmaceuticalDoseForm: codeableConceptSchema.optional(), - route: codeableConceptSchema.array().optional(), - indication: stringSchema.optional(), - _indication: elementSchema.optional(), - legalStatusOfSupply: codeableConceptSchema.optional(), - additionalMonitoringIndicator: codeableConceptSchema.optional(), - specialMeasures: codeableConceptSchema.array().optional(), - pediatricUseIndicator: codeableConceptSchema.optional(), - classification: codeableConceptSchema.array().optional(), - packagedMedicinalProduct: codeableConceptSchema.array().optional(), - ingredient: codeableConceptSchema.array().optional(), - impurity: codeableReferenceSchema.array().optional(), - attachedDocument: referenceSchema.array().optional(), - masterFile: referenceSchema.array().optional(), - contact: medicinalProductDefinitionContactSchema.array().optional(), - clinicalTrial: referenceSchema.array().optional(), - code: codingSchema.array().optional(), - name: medicinalProductDefinitionNameSchema.array(), - crossReference: medicinalProductDefinitionCrossReferenceSchema - .array() - .optional(), - operation: medicinalProductDefinitionOperationSchema.array().optional(), - characteristic: medicinalProductDefinitionCharacteristicSchema - .array() - .optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR MedicinalProductDefinition resource. - */ -export const medicinalProductDefinitionSchema: ZodType = - untypedMedicinalProductDefinitionSchema - -/** - * Wrapper class for FHIR MedicinalProductDefinition resources. - * Provides utility methods for working with medicinal product definitions and regulatory information. - */ -export class FhirMedicinalProductDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses a MedicinalProductDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the MedicinalProductDefinition schema - * @returns A FhirMedicinalProductDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirMedicinalProductDefinition { - return new FhirMedicinalProductDefinition( - medicinalProductDefinitionSchema.parse(value), - ) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/messageDefinition.ts b/packages/fhir/src/resources/messageDefinition.ts deleted file mode 100644 index 720ff37d..00000000 --- a/packages/fhir/src/resources/messageDefinition.ts +++ /dev/null @@ -1,132 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type MessageDefinitionAllowedResponse, - type MessageDefinitionFocus, - type MessageDefinition, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - codingSchema, - contactDetailSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - intSchema, - markdownSchema, - stringSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - fhirResourceTypeSchema, - messageDefinitionCategorySchema, - messageDefinitionResponseRequiredSchema, - publicationStatusSchema, -} from '../valueSets/index.js' - -const messageDefinitionAllowedResponseSchema: ZodType = - backboneElementSchema.extend({ - message: urlSchema, - _message: elementSchema.optional(), - situation: markdownSchema.optional(), - _situation: elementSchema.optional(), - }) - -const messageDefinitionFocusSchema: ZodType = - backboneElementSchema.extend({ - code: fhirResourceTypeSchema, - _code: elementSchema.optional(), - max: stringSchema.optional(), - _max: elementSchema.optional(), - min: intSchema, - profile: urlSchema.optional(), - _profile: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR MessageDefinition resource (untyped version). - */ -export const untypedMessageDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('MessageDefinition').readonly(), - allowedResponse: messageDefinitionAllowedResponseSchema.array().optional(), - base: urlSchema.optional(), - _base: elementSchema.optional(), - category: messageDefinitionCategorySchema.optional(), - _category: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - date: dateTimeSchema, - _date: elementSchema.optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - eventCoding: codingSchema.optional(), - eventUri: urlSchema.optional(), - _eventUri: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - focus: messageDefinitionFocusSchema.array().optional(), - graph: urlSchema.array().optional(), - _graph: elementSchema.array().optional(), - identifier: identifierSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - parent: urlSchema.array().optional(), - _parent: elementSchema.array().optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - purpose: markdownSchema.optional(), - _purpose: elementSchema.optional(), - replaces: urlSchema.array().optional(), - _replaces: elementSchema.array().optional(), - responseRequired: messageDefinitionResponseRequiredSchema.optional(), - _responseRequired: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - url: urlSchema.optional(), - _url: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR MessageDefinition resource. - */ -export const messageDefinitionSchema: ZodType = - untypedMessageDefinitionSchema - -/** - * Wrapper class for FHIR MessageDefinition resources. - * Provides utility methods for working with message definitions in messaging workflows. - */ -export class FhirMessageDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses a MessageDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the MessageDefinition schema - * @returns A FhirMessageDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirMessageDefinition { - return new FhirMessageDefinition(messageDefinitionSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/messageHeader.ts b/packages/fhir/src/resources/messageHeader.ts deleted file mode 100644 index 690a60f4..00000000 --- a/packages/fhir/src/resources/messageHeader.ts +++ /dev/null @@ -1,106 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type MessageHeaderDestination, - type MessageHeaderResponse, - type MessageHeaderSource, - type MessageHeader, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - canonicalSchema, - codeableConceptSchema, - codingSchema, - domainResourceSchema, - elementSchema, - idSchema, - referenceSchema, - stringSchema, - uriSchema, - urlSchema, -} from '../elements/index.js' -import { messageHeaderResponseCodeSchema } from '../valueSets/index.js' - -const messageHeaderDestinationSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema.optional(), - _name: elementSchema.optional(), - target: referenceSchema.optional(), - endpoint: uriSchema, - _endpoint: elementSchema.optional(), - receiver: referenceSchema.optional(), - }) - -const messageHeaderSourceSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema.optional(), - _name: elementSchema.optional(), - software: stringSchema.optional(), - _software: elementSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - endpoint: urlSchema, - _endpoint: elementSchema.optional(), - }) - -const messageHeaderResponseSchema: ZodType = - backboneElementSchema.extend({ - identifier: idSchema, - _identifier: elementSchema.optional(), - code: messageHeaderResponseCodeSchema, - details: referenceSchema.optional(), - }) - -/** - * Zod schema for FHIR MessageHeader resource (untyped version). - */ -export const untypedMessageHeaderSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('MessageHeader').readonly(), - eventCoding: codingSchema.optional(), - eventUri: uriSchema.optional(), - _eventUri: elementSchema.optional(), - destination: messageHeaderDestinationSchema.array().optional(), - sender: referenceSchema.optional(), - enterer: referenceSchema.optional(), - author: referenceSchema.optional(), - source: messageHeaderSourceSchema, - responsible: referenceSchema.optional(), - reason: codeableConceptSchema.optional(), - response: messageHeaderResponseSchema.optional(), - focus: referenceSchema.array().optional(), - definition: canonicalSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR MessageHeader resource. - */ -export const messageHeaderSchema: ZodType = - untypedMessageHeaderSchema - -/** - * Wrapper class for FHIR MessageHeader resources. - * Provides utility methods for working with message headers in messaging workflows. - */ -export class FhirMessageHeader extends FhirDomainResource { - // Static Functions - - /** - * Parses a MessageHeader resource from unknown data. - * - * @param value - The data to parse and validate against the MessageHeader schema - * @returns A FhirMessageHeader instance containing the validated resource - */ - public static parse(value: unknown): FhirMessageHeader { - return new FhirMessageHeader(messageHeaderSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/molecularSequence.ts b/packages/fhir/src/resources/molecularSequence.ts deleted file mode 100644 index e5ca1c74..00000000 --- a/packages/fhir/src/resources/molecularSequence.ts +++ /dev/null @@ -1,239 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type MolecularSequenceQuality, - type MolecularSequenceQualityRoc, - type MolecularSequenceReferenceSeq, - type MolecularSequenceRepository, - type MolecularSequenceStructureVariant, - type MolecularSequenceStructureVariantInner, - type MolecularSequenceStructureVariantOuter, - type MolecularSequenceVariant, - type MolecularSequence, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - intSchema, - quantitySchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { - molecularSequenceTypeSchema, - orientationTypeSchema, - strandTypeSchema, - qualityTypeSchema, - repositoryTypeSchema, -} from '../valueSets/index.js' - -const molecularSequenceReferenceSeqSchema: ZodType = - backboneElementSchema.extend({ - chromosome: codeableConceptSchema.optional(), - genomeBuild: stringSchema.optional(), - _genomeBuild: elementSchema.optional(), - orientation: orientationTypeSchema.optional(), - _orientation: elementSchema.optional(), - referenceSeqId: codeableConceptSchema.optional(), - referenceSeqPointer: referenceSchema.optional(), - referenceSeqString: stringSchema.optional(), - _referenceSeqString: elementSchema.optional(), - strand: strandTypeSchema.optional(), - _strand: elementSchema.optional(), - windowStart: intSchema.optional(), - windowEnd: intSchema.optional(), - }) - -const molecularSequenceVariantSchema: ZodType = - backboneElementSchema.extend({ - start: intSchema.optional(), - end: intSchema.optional(), - observedAllele: stringSchema.optional(), - _observedAllele: elementSchema.optional(), - referenceAllele: stringSchema.optional(), - _referenceAllele: elementSchema.optional(), - cigar: stringSchema.optional(), - _cigar: elementSchema.optional(), - variantPointer: referenceSchema.optional(), - }) - -const molecularSequenceQualityRocSchema: ZodType = - backboneElementSchema.extend({ - score: intSchema.array().optional(), - numTP: intSchema.array().optional(), - numFP: intSchema.array().optional(), - numFN: intSchema.array().optional(), - precision: intSchema.array().optional(), - sensitivity: intSchema.array().optional(), - fMeasure: intSchema.array().optional(), - }) - -const molecularSequenceQualitySchema: ZodType = - backboneElementSchema.extend({ - type: qualityTypeSchema, - _type: elementSchema.optional(), - standardSequence: codeableConceptSchema.optional(), - start: intSchema.optional(), - end: intSchema.optional(), - score: quantitySchema.optional(), - method: codeableConceptSchema.optional(), - truthTP: intSchema.optional(), - queryTP: intSchema.optional(), - truthFN: intSchema.optional(), - queryFP: intSchema.optional(), - gtFP: intSchema.optional(), - precision: intSchema.optional(), - recall: intSchema.optional(), - fScore: intSchema.optional(), - roc: molecularSequenceQualityRocSchema.optional(), - }) - -const molecularSequenceRepositorySchema: ZodType = - backboneElementSchema.extend({ - type: repositoryTypeSchema, - _type: elementSchema.optional(), - url: stringSchema.optional(), - _url: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - datasetId: stringSchema.optional(), - _datasetId: elementSchema.optional(), - variantsetId: stringSchema.optional(), - _variantsetId: elementSchema.optional(), - readsetId: stringSchema.optional(), - _readsetId: elementSchema.optional(), - }) - -const molecularSequenceStructureVariantInnerSchema: ZodType = - backboneElementSchema.extend({ - start: intSchema.optional(), - end: intSchema.optional(), - }) - -const molecularSequenceStructureVariantOuterSchema: ZodType = - backboneElementSchema.extend({ - start: intSchema.optional(), - end: intSchema.optional(), - }) - -const molecularSequenceStructureVariantSchema: ZodType = - backboneElementSchema.extend({ - variantType: codeableConceptSchema.optional(), - exact: booleanSchema.optional(), - _exact: elementSchema.optional(), - length: intSchema.optional(), - outer: molecularSequenceStructureVariantOuterSchema.optional(), - inner: molecularSequenceStructureVariantInnerSchema.optional(), - }) - -/** - * Zod schema for FHIR MolecularSequence resource (untyped version). - */ -export const untypedMolecularSequenceSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('MolecularSequence').readonly(), - identifier: identifierSchema.array().optional(), - type: molecularSequenceTypeSchema.optional(), - _type: elementSchema.optional(), - coordinateSystem: intSchema, - patient: referenceSchema.optional(), - specimen: referenceSchema.optional(), - device: referenceSchema.optional(), - performer: referenceSchema.optional(), - quantity: quantitySchema.optional(), - referenceSeq: molecularSequenceReferenceSeqSchema.optional(), - variant: molecularSequenceVariantSchema.array().optional(), - observedSeq: stringSchema.optional(), - _observedSeq: elementSchema.optional(), - quality: molecularSequenceQualitySchema.array().optional(), - readCoverage: intSchema.optional(), - repository: molecularSequenceRepositorySchema.array().optional(), - pointer: referenceSchema.array().optional(), - structureVariant: molecularSequenceStructureVariantSchema - .array() - .optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR MolecularSequence resource. - */ -export const molecularSequenceSchema: ZodType = - untypedMolecularSequenceSchema - -/** - * Wrapper class for FHIR MolecularSequence resources. - * Provides utility methods for working with molecular sequences and genomic data. - */ -export class FhirMolecularSequence extends FhirDomainResource { - // Static Functions - - /** - * Parses a MolecularSequence resource from unknown data. - * - * @param value - The data to parse and validate against the MolecularSequence schema - * @returns A FhirMolecularSequence instance containing the validated resource - */ - public static parse(value: unknown): FhirMolecularSequence { - return new FhirMolecularSequence(molecularSequenceSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/namingSystem.ts b/packages/fhir/src/resources/namingSystem.ts deleted file mode 100644 index b928c0c0..00000000 --- a/packages/fhir/src/resources/namingSystem.ts +++ /dev/null @@ -1,95 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type NamingSystemUniqueId, type NamingSystem } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactDetailSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - periodSchema, - stringSchema, - usageContextSchema, -} from '../elements/index.js' -import { - namingSystemKindSchema, - namingSystemUniqueIdTypeSchema, - publicationStatusSchema, -} from '../valueSets/index.js' - -const namingSystemUniqueIdSchema: ZodType = - backboneElementSchema.extend({ - type: namingSystemUniqueIdTypeSchema, - _type: elementSchema.optional(), - value: stringSchema, - _value: elementSchema.optional(), - preferred: booleanSchema.optional(), - _preferred: elementSchema.optional(), - comment: stringSchema.optional(), - _comment: elementSchema.optional(), - period: periodSchema.optional(), - }) - -/** - * Zod schema for FHIR NamingSystem resource (untyped version). - */ -export const untypedNamingSystemSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('NamingSystem').readonly(), - name: stringSchema, - _name: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - kind: namingSystemKindSchema, - _kind: elementSchema.optional(), - date: dateTimeSchema, - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - responsible: stringSchema.optional(), - _responsible: elementSchema.optional(), - type: codeableConceptSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - usage: stringSchema.optional(), - _usage: elementSchema.optional(), - uniqueId: namingSystemUniqueIdSchema.array(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR NamingSystem resource. - */ -export const namingSystemSchema: ZodType = - untypedNamingSystemSchema - -/** - * Wrapper class for FHIR NamingSystem resources. - * Provides utility methods for working with naming systems and identifier registries. - */ -export class FhirNamingSystem extends FhirDomainResource { - // Static Functions - - /** - * Parses a NamingSystem resource from unknown data. - * - * @param value - The data to parse and validate against the NamingSystem schema - * @returns A FhirNamingSystem instance containing the validated resource - */ - public static parse(value: unknown): FhirNamingSystem { - return new FhirNamingSystem(namingSystemSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/nutritionOrder.ts b/packages/fhir/src/resources/nutritionOrder.ts deleted file mode 100644 index 2264a6a6..00000000 --- a/packages/fhir/src/resources/nutritionOrder.ts +++ /dev/null @@ -1,162 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type NutritionOrder, - type NutritionOrderEnteralFormula, - type NutritionOrderEnteralFormulaAdministration, - type NutritionOrderOralDiet, - type NutritionOrderOralDietNutrient, - type NutritionOrderOralDietTexture, - type NutritionOrderSupplement, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - canonicalSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - quantitySchema, - ratioSchema, - referenceSchema, - stringSchema, - timingSchema, - uriSchema, -} from '../elements/index.js' -import { - nutritionOrderIntentSchema, - nutritionOrderStatusSchema, -} from '../valueSets/index.js' - -const nutritionOrderOralDietNutrientSchema: ZodType = - backboneElementSchema.extend({ - amount: quantitySchema.optional(), - modifier: codeableConceptSchema.optional(), - }) - -const nutritionOrderOralDietTextureSchema: ZodType = - backboneElementSchema.extend({ - foodType: codeableConceptSchema.optional(), - modifier: codeableConceptSchema.optional(), - }) - -const nutritionOrderOralDietSchema: ZodType = - backboneElementSchema.extend({ - fluidConsistencyType: codeableConceptSchema.array().optional(), - instruction: stringSchema.optional(), - _instruction: elementSchema.optional(), - nutrient: nutritionOrderOralDietNutrientSchema.array().optional(), - schedule: timingSchema.array().optional(), - texture: nutritionOrderOralDietTextureSchema.array().optional(), - type: codeableConceptSchema.array().optional(), - }) - -const nutritionOrderSupplementSchema: ZodType = - backboneElementSchema.extend({ - instruction: stringSchema.optional(), - _instruction: elementSchema.optional(), - productName: stringSchema.optional(), - _productName: elementSchema.optional(), - quantity: quantitySchema.optional(), - schedule: timingSchema.array().optional(), - type: codeableConceptSchema.optional(), - }) - -const nutritionOrderEnteralFormulaAdministrationSchema: ZodType = - backboneElementSchema.extend({ - quantity: quantitySchema.optional(), - rateQuantity: quantitySchema.optional(), - rateRatio: ratioSchema.optional(), - schedule: timingSchema.optional(), - }) - -const nutritionOrderEnteralFormulaSchema: ZodType = - backboneElementSchema.extend({ - additiveProductName: stringSchema.optional(), - _additiveProductName: elementSchema.optional(), - additiveType: codeableConceptSchema.optional(), - administrationInstruction: stringSchema.optional(), - _administrationInstruction: elementSchema.optional(), - administration: nutritionOrderEnteralFormulaAdministrationSchema - .array() - .optional(), - baseFormulaProductName: stringSchema.optional(), - _baseFormulaProductName: elementSchema.optional(), - baseFormulaType: codeableConceptSchema.optional(), - caloricDensity: quantitySchema.optional(), - maxVolumeToDeliver: quantitySchema.optional(), - routeofAdministration: codeableConceptSchema.optional(), - }) - -/** - * Zod schema for FHIR NutritionOrder resource (untyped version). - */ -export const untypedNutritionOrderSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('NutritionOrder').readonly(), - allergyIntolerance: referenceSchema.array().optional(), - dateTime: dateTimeSchema, - _dateTime: elementSchema.optional(), - encounter: referenceSchema.optional(), - enteralFormula: nutritionOrderEnteralFormulaSchema.optional(), - excludeFoodModifier: codeableConceptSchema.array().optional(), - foodPreferenceModifier: codeableConceptSchema.array().optional(), - identifier: identifierSchema.array().optional(), - instantiates: uriSchema.array().optional(), - _instantiates: elementSchema.array().optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: elementSchema.array().optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: elementSchema.array().optional(), - intent: nutritionOrderIntentSchema, - _intent: elementSchema.optional(), - note: annotationSchema.array().optional(), - oralDiet: nutritionOrderOralDietSchema.optional(), - orderer: referenceSchema.optional(), - patient: referenceSchema, - status: nutritionOrderStatusSchema, - _status: elementSchema.optional(), - supplement: nutritionOrderSupplementSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR NutritionOrder resource. - */ -export const nutritionOrderSchema: ZodType = - untypedNutritionOrderSchema - -/** - * Wrapper class for FHIR NutritionOrder resources. - * Provides utility methods for working with nutrition orders and dietary requests. - */ -export class FhirNutritionOrder extends FhirDomainResource { - /** - * Parses a NutritionOrder resource from unknown data. - * - * @param value - The data to parse and validate against the NutritionOrder schema - * @returns A FhirNutritionOrder instance containing the validated resource - */ - public static parse(value: unknown): FhirNutritionOrder { - return new FhirNutritionOrder(nutritionOrderSchema.parse(value)) - } - - /** - * Get the date/time the order was made. - * - * @returns The dateTime the order was made, or undefined if not set - */ - public get dateTime(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.dateTime) - } -} diff --git a/packages/fhir/src/resources/nutritionProduct.ts b/packages/fhir/src/resources/nutritionProduct.ts deleted file mode 100644 index d3a1fa79..00000000 --- a/packages/fhir/src/resources/nutritionProduct.ts +++ /dev/null @@ -1,119 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type NutritionProduct, - type NutritionProductIngredient, - type NutritionProductInstance, - type NutritionProductNutrient, - type NutritionProductProductCharacteristic, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - attachmentSchema, - backboneElementSchema, - base64BinarySchema, - booleanSchema, - codeableConceptSchema, - codeableReferenceSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - quantitySchema, - ratioSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { nutritionProductStatusSchema } from '../valueSets/index.js' - -const nutritionProductNutrientSchema: ZodType = - backboneElementSchema.extend({ - amount: ratioSchema.array().optional(), - item: codeableReferenceSchema.optional(), - }) - -const nutritionProductIngredientSchema: ZodType = - backboneElementSchema.extend({ - amount: ratioSchema.array().optional(), - item: codeableReferenceSchema, - }) - -const nutritionProductProductCharacteristicSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - valueCodeableConcept: codeableConceptSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: elementSchema.optional(), - valueAttachment: attachmentSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - }) - -const nutritionProductInstanceSchema: ZodType = - backboneElementSchema.extend({ - expiry: dateTimeSchema.optional(), - _expiry: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - lotNumber: stringSchema.optional(), - _lotNumber: elementSchema.optional(), - quantity: quantitySchema.optional(), - useBy: dateTimeSchema.optional(), - _useBy: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR NutritionProduct resource (untyped version). - */ -export const untypedNutritionProductSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('NutritionProduct').readonly(), - category: codeableConceptSchema.array().optional(), - code: codeableConceptSchema.optional(), - ingredient: nutritionProductIngredientSchema.array().optional(), - instance: nutritionProductInstanceSchema.optional(), - knownAllergen: codeableReferenceSchema.array().optional(), - manufacturer: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - nutrient: nutritionProductNutrientSchema.array().optional(), - productCharacteristic: nutritionProductProductCharacteristicSchema - .array() - .optional(), - status: nutritionProductStatusSchema, - _status: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR NutritionProduct resource. - */ -export const nutritionProductSchema: ZodType = - untypedNutritionProductSchema - -/** - * Wrapper class for FHIR NutritionProduct resources. - * Provides utility methods for working with nutrition products and dietary supplements. - */ -export class FhirNutritionProduct extends FhirDomainResource { - // Static Functions - - /** - * Parses a NutritionProduct resource from unknown data. - * - * @param value - The data to parse and validate against the NutritionProduct schema - * @returns A FhirNutritionProduct instance containing the validated resource - */ - public static parse(value: unknown): FhirNutritionProduct { - return new FhirNutritionProduct(nutritionProductSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/observation.ts b/packages/fhir/src/resources/observation.ts deleted file mode 100644 index 729db257..00000000 --- a/packages/fhir/src/resources/observation.ts +++ /dev/null @@ -1,373 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type Observation } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - booleanSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - instantSchema, - intSchema, - periodSchema, - quantitySchema, - rangeSchema, - ratioSchema, - referenceSchema, - sampledDataSchema, - stringSchema, - timeSchema, - timingSchema, -} from '../elements/index.js' -import { observationStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR Observation resource (untyped version). - */ -export const untypedObservationSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Observation'), - identifier: identifierSchema.array().optional(), - basedOn: referenceSchema.array().optional(), - partOf: referenceSchema.array().optional(), - status: observationStatusSchema, - category: codeableConceptSchema.array().optional(), - code: codeableConceptSchema, - subject: referenceSchema.optional(), - focus: referenceSchema.array().optional(), - encounter: referenceSchema.optional(), - effectiveDateTime: dateTimeSchema.optional(), - _effectiveDateTime: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - effectiveTiming: timingSchema.optional(), - effectiveInstant: instantSchema.optional(), - _effectiveInstant: elementSchema.optional(), - issued: instantSchema.optional(), - _issued: elementSchema.optional(), - performer: referenceSchema.array().optional(), - valueQuantity: quantitySchema.optional(), - valueCodeableConcept: codeableConceptSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueInteger: intSchema.optional(), - valueRange: rangeSchema.optional(), - valueRatio: ratioSchema.optional(), - valueSampledData: sampledDataSchema.optional(), - valueTime: timeSchema.optional(), - _valueTime: elementSchema.optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: elementSchema.optional(), - valuePeriod: periodSchema.optional(), - dataAbsentReason: codeableConceptSchema.optional(), - interpretation: codeableConceptSchema.array().optional(), - referenceRange: elementSchema - .extend({ - low: quantitySchema.optional(), - high: quantitySchema.optional(), - type: codeableConceptSchema.optional(), - appliesTo: codeableConceptSchema.array().optional(), - age: rangeSchema.optional(), - text: stringSchema.optional(), - _text: elementSchema.optional(), - }) - .array() - .optional(), - hasMember: referenceSchema.array().optional(), - derivedFrom: referenceSchema.array().optional(), - component: elementSchema - .extend({ - code: codeableConceptSchema, - valueQuantity: quantitySchema.optional(), - valueCodeableConcept: codeableConceptSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueInteger: intSchema.optional(), - valueRange: rangeSchema.optional(), - valueRatio: ratioSchema.optional(), - valueSampledData: sampledDataSchema.optional(), - valueTime: timeSchema.optional(), - _valueTime: elementSchema.optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: elementSchema.optional(), - valuePeriod: periodSchema.optional(), - dataAbsentReason: codeableConceptSchema.optional(), - interpretation: codeableConceptSchema.array().optional(), - referenceRange: elementSchema - .extend({ - low: quantitySchema.optional(), - high: quantitySchema.optional(), - type: codeableConceptSchema.optional(), - appliesTo: codeableConceptSchema.array().optional(), - age: rangeSchema.optional(), - text: stringSchema.optional(), - _text: elementSchema.optional(), - }) - .array() - .optional(), - }) - .array() - .optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Observation resource. - */ -export const observationSchema: ZodType = untypedObservationSchema - -/** - * Wrapper class for FHIR Observation resources. - * Provides utility methods for working with observations and their effective dates. - */ -export class FhirObservation extends FhirDomainResource { - // Static Functions - - /** - * Parses an Observation resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirObservation instance - */ - public static parse(value: unknown): FhirObservation { - return new FhirObservation(observationSchema.parse(value)) - } - - // Properties - - /** - * Gets the effective date/time as a JavaScript Date object. - * Handles effectiveDateTime and effectiveInstant types. - * - * @returns The effective date if available, undefined otherwise - * - * @example - * ```typescript - * const effectiveDate = observation.effectiveDate - * if (effectiveDate) { - * console.log(`Observed on: ${effectiveDate.toISOString()}`) - * } - * ``` - */ - public get effectiveDate(): Date | undefined { - return ( - FhirDomainResource.parseDateTime(this.value.effectiveDateTime) ?? - FhirDomainResource.parseDateTime(this.value.effectiveInstant) - ) - } - - /** - * Gets the effective period start date as a JavaScript Date object. - * - * @returns The start date of the effective period if available, undefined otherwise - * - * @example - * ```typescript - * const startDate = observation.effectivePeriodStart - * ``` - */ - public get effectivePeriodStart(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.effectivePeriod?.start) - } - - /** - * Gets the effective period end date as a JavaScript Date object. - * - * @returns The end date of the effective period if available, undefined otherwise - * - * @example - * ```typescript - * const endDate = observation.effectivePeriodEnd - * ``` - */ - public get effectivePeriodEnd(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.effectivePeriod?.end) - } - - // Methods - - /** - * Checks if the observation's effective time overlaps with a given date range. - * Handles effectiveDateTime, effectiveInstant, and effectivePeriod. - * - * @param rangeStart - Start of the date range to check - * @param rangeEnd - End of the date range to check - * @returns true if the observation's effective time overlaps with the range - * - * @example - * ```typescript - * const startDate = new Date('2024-01-01') - * const endDate = new Date('2024-12-31') - * const isInRange = observation.effectiveOverlaps(startDate, endDate) - * ``` - */ - public effectiveOverlaps(rangeStart: Date, rangeEnd: Date): boolean { - const effectiveDate = this.effectiveDate - if (effectiveDate) { - return effectiveDate >= rangeStart && effectiveDate <= rangeEnd - } - - return FhirDomainResource.periodOverlaps( - this.value.effectivePeriod, - rangeStart, - rangeEnd, - ) - } - - /** - * Gets the issued date as a JavaScript Date object. - * - * @returns The issued date if available - */ - public get issuedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.issued) - } - - /** - * Gets the display text for the observation code. - * - * @returns The code display text - */ - public get codeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.code) - } - - /** - * Gets the observation value as a numeric value (from valueQuantity). - * - * @returns The numeric value if available - */ - public get numericValue(): number | undefined { - return this.value.valueQuantity?.value - } - - /** - * Gets the observation value unit (from valueQuantity). - * - * @returns The unit string if available - */ - public get unit(): string | undefined { - return this.value.valueQuantity?.unit - } - - /** - * Gets the observation value as a string (from valueString). - * - * @returns The string value if available - */ - public get stringValue(): string | undefined { - return this.value.valueString - } - - /** - * Gets the observation value as a boolean (from valueBoolean). - * - * @returns The boolean value if available - */ - public get booleanValue(): boolean | undefined { - return this.value.valueBoolean - } - - /** - * Gets the observation value display for CodeableConcept value. - * - * @returns The value display text - */ - public get valueCodeableConceptDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay( - this.value.valueCodeableConcept, - ) - } - - /** - * Checks if the observation has a specific category. - * - * @param code - The category code to check for - * @param system - Optional system to filter by - * @param version - Optional version to filter by - * @returns true if the observation has the category - */ - public hasCategory(code: string, system?: string, version?: string): boolean { - if (!this.value.category) return false - - return this.value.category.some((cat) => - FhirDomainResource.containsCoding(cat, { system, code, version }), - ) - } - - /** - * Gets interpretation display text. - * - * @returns Array of interpretation display texts - */ - public get interpretationDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.interpretation) - } - - /** - * Gets note texts from the observation. - * - * @returns Array of note texts - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/observationDefinition.ts b/packages/fhir/src/resources/observationDefinition.ts deleted file mode 100644 index 8d5b1ff2..00000000 --- a/packages/fhir/src/resources/observationDefinition.ts +++ /dev/null @@ -1,159 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ObservationDefinitionQualifiedInterval, - type ObservationDefinitionQuantitativeDetails, - type ObservationDefinition, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - decimalSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - intSchema, - rangeSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { - administrativeGenderSchema, - observationDefinitionPermittedDataTypeSchema, - observationDefinitionRangeCategorySchema, -} from '../valueSets/index.js' - -const observationDefinitionQuantitativeDetailsSchema: ZodType = - backboneElementSchema.extend({ - conversionFactor: decimalSchema.optional(), - decimalPrecision: intSchema.optional(), - unit: codeableConceptSchema.optional(), - customaryUnit: codeableConceptSchema.optional(), - }) - -const observationDefinitionQualifiedIntervalSchema: ZodType = - backboneElementSchema.extend({ - category: observationDefinitionRangeCategorySchema.optional(), - _category: elementSchema.optional(), - range: rangeSchema.optional(), - context: codeableConceptSchema.optional(), - appliesTo: codeableConceptSchema.array().optional(), - gender: administrativeGenderSchema.optional(), - _gender: elementSchema.optional(), - age: rangeSchema.optional(), - gestationalAge: rangeSchema.optional(), - condition: stringSchema.optional(), - _condition: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR ObservationDefinition resource (untyped version). - */ -export const untypedObservationDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ObservationDefinition').readonly(), - category: codeableConceptSchema.array().optional(), - code: codeableConceptSchema, - identifier: identifierSchema.array().optional(), - permittedDataType: observationDefinitionPermittedDataTypeSchema - .array() - .optional(), - _permittedDataType: elementSchema.array().optional(), - multipleResultsAllowed: booleanSchema.optional(), - _multipleResultsAllowed: elementSchema.optional(), - method: codeableConceptSchema.optional(), - preferredReportName: stringSchema.optional(), - _preferredReportName: elementSchema.optional(), - quantitativeDetails: - observationDefinitionQuantitativeDetailsSchema.optional(), - qualifiedInterval: observationDefinitionQualifiedIntervalSchema - .array() - .optional(), - validCodedValueSet: referenceSchema.optional(), - normalCodedValueSet: referenceSchema.optional(), - abnormalCodedValueSet: referenceSchema.optional(), - criticalCodedValueSet: referenceSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ObservationDefinition resource. - */ -export const observationDefinitionSchema: ZodType = - untypedObservationDefinitionSchema - -/** - * Wrapper class for FHIR ObservationDefinition resources. - * Provides utility methods for working with observation definitions and test specifications. - */ -export class FhirObservationDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses an ObservationDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the ObservationDefinition schema - * @returns A FhirObservationDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirObservationDefinition { - return new FhirObservationDefinition( - observationDefinitionSchema.parse(value), - ) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/operationDefinition.ts b/packages/fhir/src/resources/operationDefinition.ts deleted file mode 100644 index fa6a726e..00000000 --- a/packages/fhir/src/resources/operationDefinition.ts +++ /dev/null @@ -1,170 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type OperationDefinitionOverload, - type OperationDefinitionParameterBinding, - type OperationDefinitionParameterReferencedFrom, - type OperationDefinition, - type OperationDefinitionParameter, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactDetailSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - intSchema, - markdownSchema, - stringSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - bindingStrengthSchema, - fhirResourceTypeSchema, - fhirTypeSchema, - operationDefinitionKindSchema, - operationDefinitionParameterUseSchema, - publicationStatusSchema, - searchParameterTypeSchema, -} from '../valueSets/index.js' - -const operationDefinitionParameterBindingSchema: ZodType = - backboneElementSchema.extend({ - strength: bindingStrengthSchema, - _strength: elementSchema.optional(), - valueSet: urlSchema, - _valueSet: elementSchema.optional(), - }) - -const operationDefinitionParameterReferencedFromSchema: ZodType = - backboneElementSchema.extend({ - source: stringSchema, - _source: elementSchema.optional(), - sourceId: stringSchema.optional(), - _sourceId: elementSchema.optional(), - }) - -const operationDefinitionParameterSchema: ZodType = - backboneElementSchema.extend({ - binding: operationDefinitionParameterBindingSchema.optional(), - documentation: markdownSchema.optional(), - _documentation: elementSchema.optional(), - max: stringSchema, - _max: elementSchema.optional(), - min: intSchema, - name: stringSchema, - _name: elementSchema.optional(), - get part() { - return operationDefinitionParameterSchema.array().optional() - }, - referencedFrom: operationDefinitionParameterReferencedFromSchema - .array() - .optional(), - searchType: searchParameterTypeSchema.optional(), - _searchType: elementSchema.optional(), - targetProfile: urlSchema.array().optional(), - _targetProfile: elementSchema.array().optional(), - type: fhirTypeSchema.optional(), - _type: elementSchema.optional(), - use: operationDefinitionParameterUseSchema, - _use: elementSchema.optional(), - }) - -const operationDefinitionOverloadSchema: ZodType = - backboneElementSchema.extend({ - comment: stringSchema.optional(), - _comment: elementSchema.optional(), - parameterName: stringSchema.array().optional(), - _parameterName: elementSchema.array().optional(), - }) - -/** - * Zod schema for FHIR OperationDefinition resource (untyped version). - */ -export const untypedOperationDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('OperationDefinition').readonly(), - affectsState: booleanSchema.optional(), - _affectsState: elementSchema.optional(), - base: urlSchema.optional(), - _base: elementSchema.optional(), - code: stringSchema, - _code: elementSchema.optional(), - comment: markdownSchema.optional(), - _comment: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - inputProfile: urlSchema.optional(), - _inputProfile: elementSchema.optional(), - instance: booleanSchema, - _instance: elementSchema.optional(), - jurisdiction: codeableConceptSchema.array().optional(), - kind: operationDefinitionKindSchema, - _kind: elementSchema.optional(), - name: stringSchema, - _name: elementSchema.optional(), - outputProfile: urlSchema.optional(), - _outputProfile: elementSchema.optional(), - overload: operationDefinitionOverloadSchema.array().optional(), - parameter: operationDefinitionParameterSchema.array().optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - purpose: markdownSchema.optional(), - _purpose: elementSchema.optional(), - resource: fhirResourceTypeSchema.array().optional(), - _resource: elementSchema.array().optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - system: booleanSchema, - _system: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - type: booleanSchema, - _type: elementSchema.optional(), - url: urlSchema.optional(), - _url: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR OperationDefinition resource. - */ -export const operationDefinitionSchema: ZodType = - untypedOperationDefinitionSchema - -/** - * Wrapper class for FHIR OperationDefinition resources. - * Provides utility methods for working with operation definitions and custom FHIR operations. - */ -export class FhirOperationDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses an OperationDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the OperationDefinition schema - * @returns A FhirOperationDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirOperationDefinition { - return new FhirOperationDefinition(operationDefinitionSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/operationOutcome.ts b/packages/fhir/src/resources/operationOutcome.ts deleted file mode 100644 index 360d0fae..00000000 --- a/packages/fhir/src/resources/operationOutcome.ts +++ /dev/null @@ -1,68 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type OperationOutcomeIssue, type OperationOutcome } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - domainResourceSchema, - elementSchema, - stringSchema, -} from '../elements/index.js' -import { issueSeveritySchema, issueTypeSchema } from '../valueSets/index.js' - -const operationOutcomeIssueSchema: ZodType = - backboneElementSchema.extend({ - severity: issueSeveritySchema, - _severity: elementSchema.optional(), - code: issueTypeSchema, - _code: elementSchema.optional(), - details: codeableConceptSchema.optional(), - diagnostics: stringSchema.optional(), - _diagnostics: elementSchema.optional(), - location: stringSchema.array().optional(), - _location: elementSchema.array().optional(), - expression: stringSchema.array().optional(), - _expression: elementSchema.array().optional(), - }) - -/** - * Zod schema for FHIR OperationOutcome resource (untyped version). - */ -export const untypedOperationOutcomeSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('OperationOutcome').readonly(), - issue: operationOutcomeIssueSchema.array(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR OperationOutcome resource. - */ -export const operationOutcomeSchema: ZodType = - untypedOperationOutcomeSchema - -/** - * Wrapper class for FHIR OperationOutcome resources. - * Provides utility methods for working with operation outcomes and error information. - */ -export class FhirOperationOutcome extends FhirDomainResource { - // Static Functions - - /** - * Parses an OperationOutcome resource from unknown data. - * - * @param value - The data to parse and validate against the OperationOutcome schema - * @returns A FhirOperationOutcome instance containing the validated resource - */ - public static parse(value: unknown): FhirOperationOutcome { - return new FhirOperationOutcome(operationOutcomeSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/organization.ts b/packages/fhir/src/resources/organization.ts deleted file mode 100644 index c4c1428d..00000000 --- a/packages/fhir/src/resources/organization.ts +++ /dev/null @@ -1,156 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type OrganizationContact, - type Coding, - type Organization, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - addressSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactPointSchema, - domainResourceSchema, - elementSchema, - humanNameSchema, - identifierSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' - -const organizationContactSchema: ZodType = - backboneElementSchema.extend({ - purpose: codeableConceptSchema.optional(), - name: humanNameSchema.optional(), - telecom: contactPointSchema.array().optional(), - address: addressSchema.optional(), - }) - -/** - * Zod schema for FHIR Organization resource (untyped version). - */ -export const untypedOrganizationSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Organization').readonly(), - identifier: identifierSchema.array().optional(), - active: booleanSchema.optional(), - _active: elementSchema.optional(), - type: codeableConceptSchema.array().optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - alias: stringSchema.array().optional(), - _alias: elementSchema.array().optional(), - telecom: contactPointSchema.array().optional(), - address: addressSchema.array().optional(), - partOf: referenceSchema.optional(), - contact: organizationContactSchema.array().optional(), - endpoint: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Organization resource. - */ -export const organizationSchema: ZodType = - untypedOrganizationSchema - -/** - * Wrapper class for FHIR Organization resources. - * Provides utility methods for working with organizations. - */ -export class FhirOrganization extends FhirDomainResource { - // Static Functions - - /** - * Parses an Organization resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirOrganization instance - */ - public static parse(value: unknown): FhirOrganization { - return new FhirOrganization(organizationSchema.parse(value)) - } - - // Properties - - /** - * Gets identifiers by system. - * - * @param system - The system URL to filter by - * @returns Array of identifier values matching the system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } - /** - * Gets human-readable display strings for all organization type CodeableConcepts. - * - * @returns Array of type display texts - */ - public get typeDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.type) - } - - /** - * Gets all phone numbers for the organization. - * - * @returns Array of phone numbers - */ - public get phoneNumbers(): string[] { - return FhirDomainResource.contactPointsBySystem(this.value.telecom, 'phone') - } - - /** - * Gets all email addresses for the organization. - * - * @returns Array of email addresses - */ - public get emailAddresses(): string[] { - return FhirDomainResource.contactPointsBySystem(this.value.telecom, 'email') - } -} diff --git a/packages/fhir/src/resources/organizationAffiliation.ts b/packages/fhir/src/resources/organizationAffiliation.ts deleted file mode 100644 index a278835e..00000000 --- a/packages/fhir/src/resources/organizationAffiliation.ts +++ /dev/null @@ -1,115 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type OrganizationAffiliation } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - booleanSchema, - codeableConceptSchema, - contactPointSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - referenceSchema, -} from '../elements/index.js' - -/** - * Zod schema for FHIR OrganizationAffiliation resource (untyped version). - */ -export const untypedOrganizationAffiliationSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('OrganizationAffiliation').readonly(), - identifier: identifierSchema.array().optional(), - active: booleanSchema.optional(), - _active: elementSchema.optional(), - period: periodSchema.optional(), - organization: referenceSchema.optional(), - participatingOrganization: referenceSchema.optional(), - network: referenceSchema.array().optional(), - code: codeableConceptSchema.array().optional(), - specialty: codeableConceptSchema.array().optional(), - location: referenceSchema.array().optional(), - healthcareService: referenceSchema.array().optional(), - telecom: contactPointSchema.array().optional(), - endpoint: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR OrganizationAffiliation resource. - */ -export const organizationAffiliationSchema: ZodType = - untypedOrganizationAffiliationSchema - -/** - * Wrapper class for FHIR OrganizationAffiliation resources. - * Provides utility methods for working with organization affiliations and relationships. - */ -export class FhirOrganizationAffiliation extends FhirDomainResource { - // Static Functions - - /** - * Parses an OrganizationAffiliation resource from unknown data. - * - * @param value - The data to parse and validate against the OrganizationAffiliation schema - * @returns A FhirOrganizationAffiliation instance containing the validated resource - */ - public static parse(value: unknown): FhirOrganizationAffiliation { - return new FhirOrganizationAffiliation( - organizationAffiliationSchema.parse(value), - ) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/packagedProductDefinition.ts b/packages/fhir/src/resources/packagedProductDefinition.ts deleted file mode 100644 index 843f70ef..00000000 --- a/packages/fhir/src/resources/packagedProductDefinition.ts +++ /dev/null @@ -1,143 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type PackagedProductDefinition, - type PackagedProductDefinitionLegalStatusOfSupply, - type PackagedProductDefinitionPackage, - type PackagedProductDefinitionPackageContainedItem, - type PackagedProductDefinitionPackageProperty, - type PackagedProductDefinitionPackageShelfLifeStorage, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - attachmentSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - codeableReferenceSchema, - dateSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - intSchema, - marketingStatusSchema, - quantitySchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' - -const packagedProductDefinitionLegalStatusOfSupplySchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema.optional(), - jurisdiction: codeableConceptSchema.optional(), - }) - -const packagedProductDefinitionPackageShelfLifeStorageSchema: ZodType = - backboneElementSchema.extend({ - periodDuration: quantitySchema.optional(), - periodString: stringSchema.optional(), - _periodString: elementSchema.optional(), - specialPrecautionsForStorage: codeableConceptSchema.array().optional(), - type: codeableConceptSchema.optional(), - }) - -const packagedProductDefinitionPackagePropertySchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - valueCodeableConcept: codeableConceptSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueDate: dateSchema.optional(), - _valueDate: elementSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueAttachment: attachmentSchema.optional(), - }) - -const packagedProductDefinitionPackageContainedItemSchema: ZodType = - backboneElementSchema.extend({ - amount: quantitySchema.optional(), - item: codeableReferenceSchema, - }) - -const packagedProductDefinitionPackageSchema: ZodType = - backboneElementSchema.extend({ - alternateMaterial: codeableConceptSchema.array().optional(), - containedItem: packagedProductDefinitionPackageContainedItemSchema - .array() - .optional(), - identifier: identifierSchema.array().optional(), - manufacturer: referenceSchema.array().optional(), - material: codeableConceptSchema.array().optional(), - get package() { - return packagedProductDefinitionPackageSchema.array().optional() - }, - property: packagedProductDefinitionPackagePropertySchema.array().optional(), - quantity: intSchema.optional(), - shelfLifeStorage: packagedProductDefinitionPackageShelfLifeStorageSchema - .array() - .optional(), - type: codeableConceptSchema.optional(), - }) - -/** - * Zod schema for FHIR PackagedProductDefinition resource (untyped version). - */ -export const untypedPackagedProductDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('PackagedProductDefinition').readonly(), - characteristic: codeableConceptSchema.array().optional(), - containedItemQuantity: quantitySchema.array().optional(), - copackagedIndicator: booleanSchema.optional(), - _copackagedIndicator: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - legalStatusOfSupply: packagedProductDefinitionLegalStatusOfSupplySchema - .array() - .optional(), - manufacturer: referenceSchema.array().optional(), - marketingStatus: marketingStatusSchema.array().optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - package: packagedProductDefinitionPackageSchema.optional(), - packageFor: referenceSchema.array().optional(), - status: codeableConceptSchema.optional(), - statusDate: dateTimeSchema.optional(), - _statusDate: elementSchema.optional(), - type: codeableConceptSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR PackagedProductDefinition resource. - */ -export const packagedProductDefinitionSchema: ZodType = - untypedPackagedProductDefinitionSchema - -/** - * Wrapper class for FHIR PackagedProductDefinition resources. - * Provides utility methods for working with packaged product definitions and pharmaceutical packaging. - */ -export class FhirPackagedProductDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses a PackagedProductDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the PackagedProductDefinition schema - * @returns A FhirPackagedProductDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirPackagedProductDefinition { - return new FhirPackagedProductDefinition( - packagedProductDefinitionSchema.parse(value), - ) - } -} diff --git a/packages/fhir/src/resources/parameters.ts b/packages/fhir/src/resources/parameters.ts deleted file mode 100644 index e64f8629..00000000 --- a/packages/fhir/src/resources/parameters.ts +++ /dev/null @@ -1,63 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Parameters, type ParametersParameter } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { fhirResourceSchema } from './fhirResource.js' -import { anyValueSchema } from '../elements/anyValueSchema.js' -import { - domainResourceSchema, - elementSchema, - stringSchema, -} from '../elements/index.js' - -const parametersParameterSchema: ZodType = - anyValueSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - get resource() { - return fhirResourceSchema.optional() - }, - get part() { - return parametersParameterSchema.array().optional() - }, - }) - -/** - * Zod schema for FHIR Parameters resource (untyped version). - */ -export const untypedParametersSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Parameters').readonly(), - parameter: parametersParameterSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Parameters resource. - */ -export const parametersSchema: ZodType = untypedParametersSchema - -/** - * Wrapper class for FHIR Parameters resources. - * Useful for handling operation inputs/outputs that are represented as Parameters. - */ -export class FhirParameters extends FhirDomainResource { - // Static Functions - - /** - * Parses a Parameters resource from unknown data. - * - * @param value - The data to parse and validate against the Parameters schema - * @returns A FhirParameters instance containing the validated resource - */ - public static parse(value: unknown): FhirParameters { - return new FhirParameters(parametersSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/patient.ts b/packages/fhir/src/resources/patient.ts deleted file mode 100644 index 8a4d7251..00000000 --- a/packages/fhir/src/resources/patient.ts +++ /dev/null @@ -1,283 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Patient, type ContactPoint, type Coding } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - addressSchema, - attachmentSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactPointSchema, - dateSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - humanNameSchema, - identifierSchema, - intSchema, - periodSchema, - referenceSchema, -} from '../elements/index.js' -import { - administrativeGenderSchema, - patientLinkTypeSchema, -} from '../valueSets/index.js' - -/** - * Zod schema for FHIR Patient resource (untyped version). - */ -export const untypedPatientSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Patient').readonly(), - identifier: identifierSchema.array().optional(), - active: booleanSchema.optional(), - _active: elementSchema.optional(), - name: humanNameSchema.array().optional(), - telecom: contactPointSchema.array().optional(), - gender: administrativeGenderSchema.optional(), - _gender: elementSchema.optional(), - birthDate: dateSchema.optional(), - _birthDate: elementSchema.optional(), - deceasedBoolean: booleanSchema.optional(), - _deceasedBoolean: elementSchema.optional(), - deceasedDateTime: dateTimeSchema.optional(), - _deceasedDateTime: elementSchema.optional(), - address: addressSchema.array().optional(), - maritalStatus: codeableConceptSchema.optional(), - multipleBirthBoolean: booleanSchema.optional(), - _multipleBirthBoolean: elementSchema.optional(), - multipleBirthInteger: intSchema.optional(), - _multipleBirthInteger: elementSchema.optional(), - photo: attachmentSchema.array().optional(), - contact: backboneElementSchema - .extend({ - relationship: codeableConceptSchema.array().optional(), - name: humanNameSchema.optional(), - telecom: contactPointSchema.array().optional(), - address: addressSchema.optional(), - gender: administrativeGenderSchema.optional(), - _gender: elementSchema.optional(), - organization: referenceSchema.optional(), - period: periodSchema.optional(), - }) - .array() - .optional(), - communication: backboneElementSchema - .extend({ - language: codeableConceptSchema, - preferred: booleanSchema.optional(), - _preferred: elementSchema.optional(), - }) - .array() - .optional(), - generalPractitioner: referenceSchema.array().optional(), - managingOrganization: referenceSchema.optional(), - link: backboneElementSchema - .extend({ - other: referenceSchema, - type: patientLinkTypeSchema, - _type: elementSchema.optional(), - }) - .array() - .optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Patient resource. - */ -export const patientSchema: ZodType = untypedPatientSchema - -/** - * Wrapper class for FHIR Patient resources. - * Provides utility methods for working with patient demographics and identifiers. - */ -export class FhirPatient extends FhirDomainResource { - // Static Functions - - /** - * Parses a Patient resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirPatient instance - */ - public static parse(value: unknown): FhirPatient { - return new FhirPatient(patientSchema.parse(value)) - } - - // Properties - - /** - * Gets the patient's birth date as a JavaScript Date object. - * - * @returns The birth date if available, undefined otherwise - * - * @example - * ```typescript - * const birthDate = patient.birthDate - * if (birthDate) { - * const age = new Date().getFullYear() - birthDate.getFullYear() - * } - * ``` - */ - public get birthDate(): Date | undefined { - return FhirDomainResource.parseDate(this.value.birthDate) - } - - /** - * Gets the patient's deceased date as a JavaScript Date object (if deceased by date). - * - * @returns The deceased date if available, undefined otherwise - */ - public get deceasedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.deceasedDateTime) - } - - /** - * Checks if the patient is deceased (either by boolean flag or date). - * - * @returns true if the patient is marked as deceased - * - * @example - * ```typescript - * if (patient.isDeceased) { - * console.log('Patient is deceased') - * } - * ``` - */ - public get isDeceased(): boolean { - return ( - this.value.deceasedBoolean === true || - this.value.deceasedDateTime !== undefined - ) - } - - /** - * Gets all identifiers with a specific system. - * Uses the generic FhirDomainResource method. - * - * @param system - The system URL to filter by - * @returns Array of identifier values matching the system - * - * @example - * ```typescript - * const mrns = patient.getIdentifiersBySystem('http://hospital.org/mrn') - * ``` - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } - - /** - * Gets all phone numbers for the patient. - * - * @returns Array of phone numbers - */ - public get phoneNumbers(): string[] { - return FhirDomainResource.contactPointsBySystem(this.value.telecom, 'phone') - } - - /** - * Gets all email addresses for the patient. - * - * @returns Array of email addresses - */ - public get emailAddresses(): string[] { - return FhirDomainResource.contactPointsBySystem(this.value.telecom, 'email') - } - - /** - * Gets a phone number by use (e.g., 'home', 'work', 'mobile'). - * - * @param use - The use type to filter by - * @returns The phone number if found - */ - public phoneNumberByUse(use: ContactPoint['use']): string | undefined { - return FhirDomainResource.contactPointBySystem( - this.value.telecom, - 'phone', - use, - ) - } - - /** - * Calculates the patient's age in years based on birth date. - * - * @param asOfDate - Date to calculate age as of (defaults to today) - * @returns Age in years, or undefined if no birth date - * - * @example - * ```typescript - * const age = patient.calculateAge() // 34 - * ``` - */ - public ageInYears(asOfDate: Date = new Date()): number | undefined { - const birth = this.birthDate - if (!birth) return undefined - - let age = asOfDate.getFullYear() - birth.getFullYear() - const monthDiff = asOfDate.getMonth() - birth.getMonth() - - if ( - monthDiff < 0 || - (monthDiff === 0 && asOfDate.getDate() < birth.getDate()) - ) { - age-- - } - - return age - } - - /** - * Gets the patient's marital status display text. - * - * @returns Marital status display or undefined - */ - public get maritalStatusDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.maritalStatus) - } -} diff --git a/packages/fhir/src/resources/paymentNotice.ts b/packages/fhir/src/resources/paymentNotice.ts deleted file mode 100644 index 1921ccb6..00000000 --- a/packages/fhir/src/resources/paymentNotice.ts +++ /dev/null @@ -1,116 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type PaymentNotice } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - elementSchema, - codeableConceptSchema, - referenceSchema, - moneySchema, - dateTimeSchema, - dateSchema, -} from '../elements/index.js' -import { financialResourceStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR PaymentNotice resource (untyped version). - */ -export const untypedPaymentNoticeSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('PaymentNotice').readonly(), - identifier: identifierSchema.array().optional(), - status: financialResourceStatusSchema, - _status: elementSchema.optional(), - request: referenceSchema.optional(), - response: referenceSchema.optional(), - created: dateTimeSchema, - _created: elementSchema.optional(), - provider: referenceSchema.optional(), - payment: referenceSchema, - paymentDate: dateSchema.optional(), - _paymentDate: elementSchema.optional(), - payee: referenceSchema.optional(), - recipient: referenceSchema, - amount: moneySchema, - paymentStatus: codeableConceptSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR PaymentNotice resource. - */ -export const paymentNoticeSchema: ZodType = - untypedPaymentNoticeSchema - -/** - * Wrapper class for FHIR PaymentNotice resources. - * Provides utility methods for working with payment notices and financial transactions. - */ -export class FhirPaymentNotice extends FhirDomainResource { - // Static Functions - - /** - * Parses a PaymentNotice resource from unknown data. - * - * @param value - The data to parse and validate against the PaymentNotice schema - * @returns A FhirPaymentNotice instance containing the validated resource - */ - public static parse(value: unknown): FhirPaymentNotice { - return new FhirPaymentNotice(paymentNoticeSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/paymentReconciliation.ts b/packages/fhir/src/resources/paymentReconciliation.ts deleted file mode 100644 index adfa54f4..00000000 --- a/packages/fhir/src/resources/paymentReconciliation.ts +++ /dev/null @@ -1,158 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type PaymentReconciliationDetail, - type PaymentReconciliationProcessNote, - type Coding, - type PaymentReconciliation, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - backboneElementSchema, - elementSchema, - codeableConceptSchema, - referenceSchema, - stringSchema, - moneySchema, - dateTimeSchema, - periodSchema, - dateSchema, -} from '../elements/index.js' -import { - financialResourceStatusSchema, - remittanceOutcomeSchema, - noteTypeSchema, -} from '../valueSets/index.js' - -const paymentReconciliationDetailSchema: ZodType = - backboneElementSchema.extend({ - identifier: identifierSchema.optional(), - predecessor: identifierSchema.optional(), - type: codeableConceptSchema, - request: referenceSchema.optional(), - submitter: referenceSchema.optional(), - response: referenceSchema.optional(), - date: dateSchema.optional(), - _date: elementSchema.optional(), - responsible: referenceSchema.optional(), - payee: referenceSchema.optional(), - amount: moneySchema.optional(), - }) - -const paymentReconciliationProcessNoteSchema: ZodType = - backboneElementSchema.extend({ - type: noteTypeSchema.optional(), - _type: elementSchema.optional(), - text: stringSchema.optional(), - _text: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR PaymentReconciliation resource (untyped version). - */ -export const untypedPaymentReconciliationSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('PaymentReconciliation').readonly(), - identifier: identifierSchema.array().optional(), - status: financialResourceStatusSchema, - _status: elementSchema.optional(), - period: periodSchema.optional(), - created: dateTimeSchema, - _created: elementSchema.optional(), - paymentIssuer: referenceSchema.optional(), - request: referenceSchema.optional(), - requestor: referenceSchema.optional(), - outcome: remittanceOutcomeSchema.optional(), - _outcome: elementSchema.optional(), - disposition: stringSchema.optional(), - _disposition: elementSchema.optional(), - paymentDate: dateSchema, - _paymentDate: elementSchema.optional(), - paymentAmount: moneySchema, - paymentIdentifier: identifierSchema.optional(), - detail: paymentReconciliationDetailSchema.array().optional(), - formCode: codeableConceptSchema.optional(), - processNote: paymentReconciliationProcessNoteSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR PaymentReconciliation resource. - */ -export const paymentReconciliationSchema: ZodType = - untypedPaymentReconciliationSchema - -/** - * Wrapper class for FHIR PaymentReconciliation resources. - * Provides utility methods for working with payment reconciliations and financial settlements. - */ -export class FhirPaymentReconciliation extends FhirDomainResource { - // Static Functions - - /** - * Parses a PaymentReconciliation resource from unknown data. - * - * @param value - The data to parse and validate against the PaymentReconciliation schema - * @returns A FhirPaymentReconciliation instance containing the validated resource - */ - public static parse(value: unknown): FhirPaymentReconciliation { - return new FhirPaymentReconciliation( - paymentReconciliationSchema.parse(value), - ) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/person.ts b/packages/fhir/src/resources/person.ts deleted file mode 100644 index ee15dad1..00000000 --- a/packages/fhir/src/resources/person.ts +++ /dev/null @@ -1,131 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type PersonLink, type Coding, type Person } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - addressSchema, - attachmentSchema, - backboneElementSchema, - booleanSchema, - contactPointSchema, - dateSchema, - domainResourceSchema, - elementSchema, - humanNameSchema, - identifierSchema, - referenceSchema, -} from '../elements/index.js' -import { - administrativeGenderSchema, - identityAssuranceLevelSchema, -} from '../valueSets/index.js' - -const personLinkSchema: ZodType = backboneElementSchema.extend({ - target: referenceSchema, - assurance: identityAssuranceLevelSchema.optional(), - _assurance: elementSchema.optional(), -}) - -/** - * Zod schema for FHIR Person resource (untyped version). - */ -export const untypedPersonSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Person').readonly(), - identifier: identifierSchema.array().optional(), - name: humanNameSchema.array().optional(), - telecom: contactPointSchema.array().optional(), - gender: administrativeGenderSchema.optional(), - _gender: elementSchema.optional(), - birthDate: dateSchema.optional(), - _birthDate: elementSchema.optional(), - address: addressSchema.array().optional(), - photo: attachmentSchema.optional(), - managingOrganization: referenceSchema.optional(), - active: booleanSchema.optional(), - link: personLinkSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Person resource. - */ -export const personSchema: ZodType = untypedPersonSchema - -/** - * Wrapper class for FHIR Person resources. - * Provides convenience accessors for demographic details and identifiers. - */ -export class FhirPerson extends FhirDomainResource { - /** - * Parses a Person resource from unknown data. - * - * @param value - The data to parse and validate against the Person schema - * @returns A FhirPerson instance containing the validated resource - */ - public static parse(value: unknown): FhirPerson { - return new FhirPerson(personSchema.parse(value)) - } - - /** - * Gets the birth date of the person as a JavaScript Date object. - * - * @returns The birth date if available - */ - public get birthDate(): Date | undefined { - return FhirDomainResource.parseDate(this.value.birthDate) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/planDefinition.ts b/packages/fhir/src/resources/planDefinition.ts deleted file mode 100644 index b3fe7412..00000000 --- a/packages/fhir/src/resources/planDefinition.ts +++ /dev/null @@ -1,302 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type PlanDefinitionActionCondition, - type PlanDefinitionActionDynamicValue, - type PlanDefinitionActionParticipant, - type PlanDefinitionActionRelatedAction, - type PlanDefinition, - type PlanDefinitionAction, - type PlanDefinitionGoal, - type PlanDefinitionGoalTarget, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - canonicalSchema, - codeableConceptSchema, - contactDetailSchema, - dataRequirementSchema, - dateSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - expressionSchema, - identifierSchema, - markdownSchema, - periodSchema, - quantitySchema, - rangeSchema, - referenceSchema, - relatedArtifactSchema, - stringSchema, - timingSchema, - triggerDefinitionSchema, - uriSchema, - usageContextSchema, -} from '../elements/index.js' -import { - planDefinitionActionCardinalityBehaviorSchema, - planDefinitionActionConditionKindSchema, - planDefinitionActionGroupingBehaviorSchema, - planDefinitionActionParticipantTypeSchema, - planDefinitionActionPrecheckBehaviorSchema, - planDefinitionActionRelationshipTypeSchema, - planDefinitionActionRequiredBehaviorSchema, - planDefinitionActionSelectionBehaviorSchema, - publicationStatusSchema, - requestPrioritySchema, -} from '../valueSets/index.js' - -const planDefinitionGoalTargetSchema: ZodType = - backboneElementSchema.extend({ - measure: codeableConceptSchema.optional(), - detailQuantity: quantitySchema.optional(), - detailRange: rangeSchema.optional(), - detailCodeableConcept: codeableConceptSchema.optional(), - detailString: stringSchema.optional(), - _detailString: elementSchema.optional(), - detailBoolean: booleanSchema.optional(), - _detailBoolean: elementSchema.optional(), - detailInteger: z.number().optional(), - detailRatio: elementSchema.optional(), - due: quantitySchema.optional(), - }) - -const planDefinitionGoalSchema: ZodType = - backboneElementSchema.extend({ - category: codeableConceptSchema.optional(), - description: codeableConceptSchema, - priority: codeableConceptSchema.optional(), - start: codeableConceptSchema.optional(), - addresses: codeableConceptSchema.array().optional(), - documentation: relatedArtifactSchema.array().optional(), - target: planDefinitionGoalTargetSchema.array().optional(), - }) - -const planDefinitionActionConditionSchema: ZodType = - backboneElementSchema.extend({ - kind: planDefinitionActionConditionKindSchema, - _kind: elementSchema.optional(), - expression: expressionSchema.optional(), - }) - -const planDefinitionActionRelatedActionSchema: ZodType = - backboneElementSchema.extend({ - actionId: stringSchema, - _actionId: elementSchema.optional(), - relationship: planDefinitionActionRelationshipTypeSchema, - _relationship: elementSchema.optional(), - offsetDuration: quantitySchema.optional(), - offsetRange: rangeSchema.optional(), - }) - -const planDefinitionActionParticipantSchema: ZodType = - backboneElementSchema.extend({ - type: planDefinitionActionParticipantTypeSchema, - _type: elementSchema.optional(), - role: codeableConceptSchema.optional(), - }) - -const planDefinitionActionDynamicValueSchema: ZodType = - backboneElementSchema.extend({ - path: stringSchema.optional(), - _path: elementSchema.optional(), - expression: expressionSchema.optional(), - }) - -const planDefinitionActionSchema: ZodType = - backboneElementSchema.extend({ - prefix: stringSchema.optional(), - _prefix: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - textEquivalent: stringSchema.optional(), - _textEquivalent: elementSchema.optional(), - priority: requestPrioritySchema.optional(), - _priority: elementSchema.optional(), - code: codeableConceptSchema.array().optional(), - reason: codeableConceptSchema.array().optional(), - documentation: relatedArtifactSchema.array().optional(), - goalId: stringSchema.array().optional(), - _goalId: elementSchema.array().optional(), - subjectCodeableConcept: codeableConceptSchema.optional(), - subjectReference: referenceSchema.optional(), - subjectCanonical: canonicalSchema.optional(), - _subjectCanonical: elementSchema.optional(), - trigger: triggerDefinitionSchema.array().optional(), - condition: planDefinitionActionConditionSchema.array().optional(), - input: dataRequirementSchema.array().optional(), - output: dataRequirementSchema.array().optional(), - relatedAction: planDefinitionActionRelatedActionSchema.array().optional(), - timingDateTime: dateTimeSchema.optional(), - _timingDateTime: elementSchema.optional(), - timingAge: quantitySchema.optional(), - timingPeriod: periodSchema.optional(), - timingDuration: quantitySchema.optional(), - timingRange: rangeSchema.optional(), - timingTiming: timingSchema.optional(), - participant: planDefinitionActionParticipantSchema.array().optional(), - type: codeableConceptSchema.optional(), - groupingBehavior: planDefinitionActionGroupingBehaviorSchema.optional(), - _groupingBehavior: elementSchema.optional(), - selectionBehavior: planDefinitionActionSelectionBehaviorSchema.optional(), - _selectionBehavior: elementSchema.optional(), - requiredBehavior: planDefinitionActionRequiredBehaviorSchema.optional(), - _requiredBehavior: elementSchema.optional(), - precheckBehavior: planDefinitionActionPrecheckBehaviorSchema.optional(), - _precheckBehavior: elementSchema.optional(), - cardinalityBehavior: - planDefinitionActionCardinalityBehaviorSchema.optional(), - _cardinalityBehavior: elementSchema.optional(), - definitionCanonical: canonicalSchema.optional(), - _definitionCanonical: elementSchema.optional(), - definitionUri: uriSchema.optional(), - _definitionUri: elementSchema.optional(), - transform: canonicalSchema.optional(), - _transform: elementSchema.optional(), - dynamicValue: planDefinitionActionDynamicValueSchema.array().optional(), - get action() { - return planDefinitionActionSchema.array().optional() - }, - }) - -/** - * Zod schema for FHIR PlanDefinition resource (untyped version). - */ -export const untypedPlanDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('PlanDefinition').readonly(), - url: uriSchema.optional(), - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - subtitle: stringSchema.optional(), - _subtitle: elementSchema.optional(), - type: codeableConceptSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - subjectCodeableConcept: codeableConceptSchema.optional(), - subjectReference: referenceSchema.optional(), - subjectCanonical: canonicalSchema.optional(), - _subjectCanonical: elementSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - purpose: markdownSchema.optional(), - _purpose: elementSchema.optional(), - usage: stringSchema.optional(), - _usage: elementSchema.optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - approvalDate: dateSchema.optional(), - _approvalDate: elementSchema.optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - topic: codeableConceptSchema.array().optional(), - author: contactDetailSchema.array().optional(), - editor: contactDetailSchema.array().optional(), - reviewer: contactDetailSchema.array().optional(), - endorser: contactDetailSchema.array().optional(), - relatedArtifact: relatedArtifactSchema.array().optional(), - library: canonicalSchema.array().optional(), - _library: elementSchema.array().optional(), - goal: planDefinitionGoalSchema.array().optional(), - action: planDefinitionActionSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR PlanDefinition resource. - */ -export const planDefinitionSchema: ZodType = - untypedPlanDefinitionSchema - -/** - * Wrapper class for FHIR PlanDefinition resources. - * Provides utility methods for working with plan definitions and clinical protocols. - */ -export class FhirPlanDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses a PlanDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the PlanDefinition schema - * @returns A FhirPlanDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirPlanDefinition { - return new FhirPlanDefinition(planDefinitionSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/practitioner.ts b/packages/fhir/src/resources/practitioner.ts deleted file mode 100644 index cbee6a59..00000000 --- a/packages/fhir/src/resources/practitioner.ts +++ /dev/null @@ -1,160 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type PractitionerQualification, - type Coding, - type Practitioner, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - attachmentSchema, - humanNameSchema, - identifierSchema, - backboneElementSchema, - booleanSchema, - elementSchema, - codeableConceptSchema, - periodSchema, - referenceSchema, - addressSchema, - contactPointSchema, - dateSchema, -} from '../elements/index.js' -import { administrativeGenderSchema } from '../valueSets/index.js' - -const practitionerQualificationSchema: ZodType = - backboneElementSchema.extend({ - identifier: identifierSchema.array().optional(), - code: codeableConceptSchema, - period: periodSchema.optional(), - issuer: referenceSchema.optional(), - }) - -/** - * Zod schema for FHIR Practitioner resource (untyped version). - */ -export const untypedPractitionerSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Practitioner').readonly(), - identifier: identifierSchema.array().optional(), - active: booleanSchema.optional(), - _active: elementSchema.optional(), - name: humanNameSchema.array().optional(), - birthDate: dateSchema.optional(), - _birthDate: elementSchema.optional(), - telecom: contactPointSchema.array().optional(), - gender: administrativeGenderSchema.optional(), - _gender: elementSchema.optional(), - address: addressSchema.array().optional(), - photo: attachmentSchema.array().optional(), - qualification: practitionerQualificationSchema.array().optional(), - communication: codeableConceptSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Practitioner resource. - */ -export const practitionerSchema: ZodType = - untypedPractitionerSchema - -/** - * Wrapper class for FHIR Practitioner resources. - * Provides utility methods for working with practitioners. - */ -export class FhirPractitioner extends FhirDomainResource { - // Static Functions - - /** - * Parses a Practitioner resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirPractitioner instance - */ - public static parse(value: unknown): FhirPractitioner { - return new FhirPractitioner(practitionerSchema.parse(value)) - } - - // Properties - - /** - * Gets the practitioner's birth date as a JavaScript Date object. - * - * @returns The birth date if available - */ - public get birthDate(): Date | undefined { - return FhirDomainResource.parseDate(this.value.birthDate) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } - - /** - * Gets all phone numbers for the practitioner. - * - * @returns Array of phone numbers - */ - public get phoneNumbers(): string[] { - return FhirDomainResource.contactPointsBySystem(this.value.telecom, 'phone') - } - - /** - * Gets all email addresses for the practitioner. - * - * @returns Array of email addresses - */ - public get emailAddresses(): string[] { - return FhirDomainResource.contactPointsBySystem(this.value.telecom, 'email') - } -} diff --git a/packages/fhir/src/resources/practitionerRole.ts b/packages/fhir/src/resources/practitionerRole.ts deleted file mode 100644 index a74eadac..00000000 --- a/packages/fhir/src/resources/practitionerRole.ts +++ /dev/null @@ -1,203 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type PractitionerRoleAvailableTime, - type PractitionerRoleNotAvailable, - type Coding, - type PractitionerRole, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { domainResourceSchema } from '../elements/domainResource.js' -import { - identifierSchema, - backboneElementSchema, - booleanSchema, - elementSchema, - codeableConceptSchema, - periodSchema, - referenceSchema, - contactPointSchema, - stringSchema, -} from '../elements/index.js' -import { daysOfWeekSchema } from '../valueSets/index.js' - -const practitionerRoleAvailableTimeSchema: ZodType = - backboneElementSchema.extend({ - daysOfWeek: daysOfWeekSchema.array().optional(), - allDay: booleanSchema.optional(), - _allDay: elementSchema.optional(), - availableStartTime: stringSchema.optional(), - _availableStartTime: elementSchema.optional(), - availableEndTime: stringSchema.optional(), - _availableEndTime: elementSchema.optional(), - }) - -const practitionerRoleNotAvailableSchema: ZodType = - backboneElementSchema.extend({ - description: stringSchema, - _description: elementSchema.optional(), - during: periodSchema.optional(), - }) - -/** - * Zod schema for FHIR PractitionerRole resource (untyped version). - */ -export const untypedPractitionerRoleSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('PractitionerRole').readonly(), - identifier: identifierSchema.array().optional(), - active: booleanSchema.optional(), - _active: elementSchema.optional(), - period: periodSchema.optional(), - practitioner: referenceSchema.optional(), - organization: referenceSchema.optional(), - code: codeableConceptSchema.array().optional(), - specialty: codeableConceptSchema.array().optional(), - location: referenceSchema.array().optional(), - healthcareService: referenceSchema.array().optional(), - telecom: contactPointSchema.array().optional(), - availableTime: practitionerRoleAvailableTimeSchema.array().optional(), - notAvailable: practitionerRoleNotAvailableSchema.array().optional(), - availabilityExceptions: stringSchema.optional(), - _availabilityExceptions: elementSchema.optional(), - endpoint: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR PractitionerRole resource. - */ -export const practitionerRoleSchema: ZodType = - untypedPractitionerRoleSchema - -/** - * Wrapper class for FHIR PractitionerRole resources. - * Provides utility methods for working with practitioner roles and healthcare provider assignments. - */ -export class FhirPractitionerRole extends FhirDomainResource { - // Static Functions - - /** - * Parses a PractitionerRole resource from unknown data. - * - * @param value - The data to parse and validate against the PractitionerRole schema - * @returns A FhirPractitionerRole instance containing the validated resource - */ - public static parse(value: unknown): FhirPractitionerRole { - return new FhirPractitionerRole(practitionerRoleSchema.parse(value)) - } - - /** - * Get the period start date. - * @returns The start date, or undefined if not set - */ - public get periodStart(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.period?.start) - } - - /** - * Get the period end date. - * @returns The end date, or undefined if not set - */ - public get periodEnd(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.period?.end) - } - - /** - * Check if the practitioner role is currently active. - * @param asOfDate Optional date to check against (defaults to current date) - * @returns True if currently within active period - */ - public isCurrentlyActive(asOfDate?: Date): boolean { - const periodActive = FhirDomainResource.periodIsActive( - this.value.period, - asOfDate, - ) - return periodActive && (this.value.active ?? true) - } - - /** - * Get all role/code displays. - * @returns Array of role display texts - */ - public get roleDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.code) - } - - /** - * Get all specialty displays. - * @returns Array of specialty display texts - */ - public get specialtyDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.specialty) - } - - /** - * Get all phone numbers from telecom. - * @returns Array of phone numbers - */ - public get phoneNumbers(): string[] { - return FhirDomainResource.contactPointsBySystem(this.value.telecom, 'phone') - } - - /** - * Get all email addresses from telecom. - * @returns Array of email addresses - */ - public get emailAddresses(): string[] { - return FhirDomainResource.contactPointsBySystem(this.value.telecom, 'email') - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/procedure.ts b/packages/fhir/src/resources/procedure.ts deleted file mode 100644 index c323a56c..00000000 --- a/packages/fhir/src/resources/procedure.ts +++ /dev/null @@ -1,255 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ProcedureFocalDevice, - type ProcedurePerformer, - type Coding, - type Procedure, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - canonicalSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - quantitySchema, - rangeSchema, - referenceSchema, - stringSchema, - uriSchema, -} from '../elements/index.js' -import { procedureStatusSchema } from '../valueSets/index.js' - -const procedurePerformerSchema: ZodType = - backboneElementSchema.extend({ - function: codeableConceptSchema.optional(), - actor: referenceSchema, - onBehalfOf: referenceSchema.optional(), - }) - -const procedureFocalDeviceSchema: ZodType = - backboneElementSchema.extend({ - action: codeableConceptSchema.optional(), - manipulated: referenceSchema, - }) - -/** - * Zod schema for FHIR Procedure resource (untyped version). - */ -export const untypedProcedureSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Procedure').readonly(), - identifier: identifierSchema.array().optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: elementSchema.array().optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: elementSchema.array().optional(), - basedOn: referenceSchema.array().optional(), - partOf: referenceSchema.array().optional(), - status: procedureStatusSchema, - _status: elementSchema.optional(), - statusReason: codeableConceptSchema.optional(), - category: codeableConceptSchema.optional(), - code: codeableConceptSchema.optional(), - subject: referenceSchema, - encounter: referenceSchema.optional(), - performedDateTime: dateTimeSchema.optional(), - _performedDateTime: elementSchema.optional(), - performedPeriod: periodSchema.optional(), - performedString: stringSchema.optional(), - _performedString: elementSchema.optional(), - performedAge: quantitySchema.optional(), - performedRange: rangeSchema.optional(), - recorder: referenceSchema.optional(), - asserter: referenceSchema.optional(), - performer: procedurePerformerSchema.array().optional(), - location: referenceSchema.optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - bodySite: codeableConceptSchema.array().optional(), - outcome: codeableConceptSchema.optional(), - report: referenceSchema.array().optional(), - complication: codeableConceptSchema.array().optional(), - complicationDetail: referenceSchema.array().optional(), - followUp: codeableConceptSchema.array().optional(), - note: annotationSchema.array().optional(), - focalDevice: procedureFocalDeviceSchema.array().optional(), - usedReference: referenceSchema.array().optional(), - usedCode: codeableConceptSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Procedure resource. - */ -export const procedureSchema: ZodType = untypedProcedureSchema - -/** - * Wrapper class for FHIR Procedure resources. - * Provides utility methods for working with procedures. - */ -export class FhirProcedure extends FhirDomainResource { - // Static Functions - - /** - * Parses a Procedure resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirProcedure instance - */ - public static parse(value: unknown): FhirProcedure { - return new FhirProcedure(procedureSchema.parse(value)) - } - - // Properties - - /** - * Gets the performed date as a JavaScript Date object (from performedDateTime). - * - * @returns The performed date if available - */ - public get performedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.performedDateTime) - } - - /** - * Gets the performed period start date. - * - * @returns The start date if available - */ - public get performedPeriodStart(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.performedPeriod?.start) - } - - /** - * Gets the performed period end date. - * - * @returns The end date if available - */ - public get performedPeriodEnd(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.performedPeriod?.end) - } - - /** - * Gets the procedure code display text. - * - * @returns The code display - */ - public get codeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.code) - } - - /** - * Gets the outcome display text. - * - * @returns The outcome display - */ - public get outcomeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.outcome) - } - - /** - * Gets all body site displays. - * - * @returns Array of body site display texts - */ - public get bodySiteDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.bodySite) - } - - /** - * Gets all reason displays (why the procedure was performed). - * - * @returns Array of reason display texts - */ - public get reasonDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.reasonCode) - } - - /** - * Gets note texts from the procedure. - * - * @returns Array of note texts - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Checks if the procedure was performed within a specific date range. - * - * @param rangeStart - Start of the date range - * @param rangeEnd - End of the date range - * @returns true if the procedure was performed within the range - */ - public wasPerformedInRange(rangeStart: Date, rangeEnd: Date): boolean { - const performedDate = this.performedDate - if (performedDate) { - return performedDate >= rangeStart && performedDate <= rangeEnd - } - - return FhirDomainResource.periodOverlaps( - this.value.performedPeriod, - rangeStart, - rangeEnd, - ) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/provenance.ts b/packages/fhir/src/resources/provenance.ts deleted file mode 100644 index 1c4637b5..00000000 --- a/packages/fhir/src/resources/provenance.ts +++ /dev/null @@ -1,106 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ProvenanceAgent, - type ProvenanceEntity, - type Provenance, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - instantSchema, - periodSchema, - referenceSchema, - signatureSchema, - uriSchema, -} from '../elements/index.js' -import { provenanceEntityRoleSchema } from '../valueSets/index.js' - -const provenanceAgentSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - role: codeableConceptSchema.array().optional(), - who: referenceSchema, - onBehalfOf: referenceSchema.optional(), - }) - -const provenanceEntitySchema: ZodType = - backboneElementSchema.extend({ - role: provenanceEntityRoleSchema, - _role: elementSchema.optional(), - what: referenceSchema, - agent: provenanceAgentSchema.array().optional(), - }) - -/** - * Zod schema for FHIR Provenance resource (untyped version). - */ -export const untypedProvenanceSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Provenance').readonly(), - target: referenceSchema.array().min(1), - occurredPeriod: periodSchema.optional(), - occurredDateTime: dateTimeSchema.optional(), - _occurredDateTime: elementSchema.optional(), - recorded: instantSchema, - _recorded: elementSchema.optional(), - policy: uriSchema.array().optional(), - _policy: elementSchema.array().optional(), - location: referenceSchema.optional(), - reason: codeableConceptSchema.array().optional(), - activity: codeableConceptSchema.optional(), - agent: provenanceAgentSchema.array().min(1), - entity: provenanceEntitySchema.array().optional(), - signature: signatureSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Provenance resource. - */ -export const provenanceSchema: ZodType = untypedProvenanceSchema - -/** - * Wrapper class for FHIR Provenance resources. - * Provides utility methods for working with provenance and audit trail information. - */ -export class FhirProvenance extends FhirDomainResource { - /** - * Parses a Provenance resource from unknown data. - * - * @param value - The data to parse and validate against the Provenance schema - * @returns A FhirProvenance instance containing the validated resource - */ - public static parse(value: unknown): FhirProvenance { - return new FhirProvenance(provenanceSchema.parse(value)) - } - - /** - * Get the date/time the activity occurred. - * - * @returns The recorded date/time, or undefined if not set - */ - public get recordedDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.recorded) - } - - /** - * Get the date/time of the activity. - * - * @returns The occurred date/time, or undefined if not set - */ - public get occurredDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.occurredDateTime) - } -} diff --git a/packages/fhir/src/resources/questionnaire.ts b/packages/fhir/src/resources/questionnaire.ts deleted file mode 100644 index 8ceb1733..00000000 --- a/packages/fhir/src/resources/questionnaire.ts +++ /dev/null @@ -1,269 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type QuestionnaireItemAnswerOption, - type QuestionnaireItemEnableWhen, - type QuestionnaireItemInitial, - type Questionnaire, - type QuestionnaireItem, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - attachmentSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - codingSchema, - contactDetailSchema, - dateSchema, - dateTimeSchema, - decimalSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - intSchema, - markdownSchema, - periodSchema, - quantitySchema, - referenceSchema, - stringSchema, - timeSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - fhirResourceTypeSchema, - questionnaireItemEnableBehaviorSchema, - questionnaireItemEnableWhenOperatorSchema, - questionnaireItemTypeSchema, - questionnaireStatusSchema, -} from '../valueSets/index.js' - -const questionnaireItemEnableWhenSchema: ZodType = - backboneElementSchema.extend({ - question: stringSchema, - _question: elementSchema.optional(), - operator: questionnaireItemEnableWhenOperatorSchema, - _operator: elementSchema.optional(), - answerBoolean: booleanSchema.optional(), - _answerBoolean: elementSchema.optional(), - answerDecimal: decimalSchema.optional(), - answerInteger: intSchema.optional(), - _answerInteger: elementSchema.optional(), - answerDate: dateSchema.optional(), - _answerDate: elementSchema.optional(), - answerDateTime: dateTimeSchema.optional(), - _answerDateTime: elementSchema.optional(), - answerTime: timeSchema.optional(), - _answerTime: elementSchema.optional(), - answerString: stringSchema.optional(), - _answerString: elementSchema.optional(), - answerUri: urlSchema.optional(), - _answerUri: elementSchema.optional(), - answerAttachment: attachmentSchema.optional(), - answerCoding: codingSchema.optional(), - answerQuantity: quantitySchema.optional(), - answerReference: referenceSchema.optional(), - }) - -const questionnaireItemAnswerOptionSchema: ZodType = - backboneElementSchema.extend({ - valueInteger: intSchema.optional(), - valueDecimal: decimalSchema.optional(), - valueDate: dateSchema.optional(), - _valueDate: elementSchema.optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: elementSchema.optional(), - valueTime: timeSchema.optional(), - _valueTime: elementSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueUri: urlSchema.optional(), - _valueUri: elementSchema.optional(), - valueAttachment: attachmentSchema.optional(), - valueCoding: codingSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueReference: referenceSchema.optional(), - initialSelected: booleanSchema.optional(), - _initialSelected: elementSchema.optional(), - }) - -const questionnaireItemInitialSchema: ZodType = - backboneElementSchema.extend({ - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueDecimal: decimalSchema.optional(), - valueInteger: intSchema.optional(), - valueDate: dateSchema.optional(), - _valueDate: elementSchema.optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: elementSchema.optional(), - valueTime: timeSchema.optional(), - _valueTime: elementSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueUri: urlSchema.optional(), - _valueUri: elementSchema.optional(), - valueAttachment: attachmentSchema.optional(), - valueCoding: codingSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueReference: referenceSchema.optional(), - }) - -const questionnaireItemSchema: ZodType = - backboneElementSchema.extend({ - linkId: stringSchema, - _linkId: elementSchema.optional(), - definition: urlSchema.optional(), - _definition: elementSchema.optional(), - code: codingSchema.array().optional(), - prefix: stringSchema.optional(), - _prefix: elementSchema.optional(), - text: stringSchema.optional(), - _text: elementSchema.optional(), - type: questionnaireItemTypeSchema, - enableWhen: questionnaireItemEnableWhenSchema.array().optional(), - enableBehavior: questionnaireItemEnableBehaviorSchema.optional(), - required: booleanSchema.optional(), - _required: elementSchema.optional(), - repeats: booleanSchema.optional(), - _repeats: elementSchema.optional(), - readOnly: booleanSchema.optional(), - _readOnly: elementSchema.optional(), - maxLength: intSchema.optional(), - _maxLength: elementSchema.optional(), - answerOption: questionnaireItemAnswerOptionSchema.array().optional(), - initial: questionnaireItemInitialSchema.array().optional(), - get item() { - return questionnaireItemSchema.array().optional() - }, - }) - -/** - * Zod schema for FHIR Questionnaire resource (untyped version). - */ -export const untypedQuestionnaireSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Questionnaire').readonly(), - url: urlSchema.optional(), - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - status: questionnaireStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - subjectType: fhirResourceTypeSchema.array().optional(), - _subjectType: elementSchema.array().optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - purpose: markdownSchema.optional(), - _purpose: elementSchema.optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - approvalDate: dateTimeSchema.optional(), - _approvalDate: elementSchema.optional(), - lastReviewDate: dateTimeSchema.optional(), - _lastReviewDate: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - code: codingSchema.array().optional(), - item: questionnaireItemSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Questionnaire resource. - */ -export const questionnaireSchema: ZodType = - untypedQuestionnaireSchema - -/** - * Wrapper class for FHIR Questionnaire resources. - * Provides utility methods for working with questionnaires and structured data capture forms. - */ -export class FhirQuestionnaire extends FhirDomainResource { - /** - * Parses a Questionnaire resource from unknown data. - * - * @param value - The data to parse and validate against the Questionnaire schema - * @returns A FhirQuestionnaire instance containing the validated resource - */ - public static parse(value: unknown): FhirQuestionnaire { - return new FhirQuestionnaire(questionnaireSchema.parse(value)) - } - - /** - * Get the publication date of the questionnaire. - * - * @returns The publication date, or undefined if not set - */ - public get date(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.date) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/questionnaireResponse.ts b/packages/fhir/src/resources/questionnaireResponse.ts deleted file mode 100644 index e17e5475..00000000 --- a/packages/fhir/src/resources/questionnaireResponse.ts +++ /dev/null @@ -1,307 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type QuestionnaireResponseItemAnswer, - type QuestionnaireResponse, - type QuestionnaireResponseItem, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - attachmentSchema, - backboneElementSchema, - booleanSchema, - codingSchema, - dateSchema, - dateTimeSchema, - decimalSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - intSchema, - quantitySchema, - referenceSchema, - stringSchema, - timeSchema, - uriSchema, -} from '../elements/index.js' -import { questionnaireResponseStatusSchema } from '../valueSets/index.js' - -const questionnaireResponseItemAnswerSchema: ZodType = - backboneElementSchema.extend({ - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: elementSchema.optional(), - valueInteger: intSchema.optional(), - _valueInteger: elementSchema.optional(), - valueDate: dateSchema.optional(), - _valueDate: elementSchema.optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: elementSchema.optional(), - valueTime: timeSchema.optional(), - _valueTime: elementSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueUri: uriSchema.optional(), - _valueUri: elementSchema.optional(), - valueAttachment: attachmentSchema.optional(), - valueCoding: codingSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueReference: referenceSchema.optional(), - get item() { - return questionnaireResponseItemSchema.array().optional() - }, - }) - -const questionnaireResponseItemSchema: ZodType = - backboneElementSchema.extend({ - linkId: stringSchema, - _linkId: elementSchema.optional(), - definition: uriSchema.optional(), - _definition: elementSchema.optional(), - text: stringSchema.optional(), - _text: elementSchema.optional(), - answer: questionnaireResponseItemAnswerSchema.array().optional(), - get item() { - return questionnaireResponseItemSchema.array().optional() - }, - }) - -/** - * Zod schema for FHIR QuestionnaireResponse resource (untyped version). - */ -export const untypedQuestionnaireResponseSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('QuestionnaireResponse').readonly(), - identifier: identifierSchema.optional(), - basedOn: referenceSchema.array().optional(), - partOf: referenceSchema.array().optional(), - questionnaire: uriSchema.optional(), - _questionnaire: elementSchema.optional(), - status: questionnaireResponseStatusSchema, - _status: elementSchema.optional(), - subject: referenceSchema.optional(), - encounter: referenceSchema.optional(), - authored: dateTimeSchema.optional(), - _authored: elementSchema.optional(), - author: referenceSchema.optional(), - source: referenceSchema.optional(), - item: questionnaireResponseItemSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR QuestionnaireResponse resource. - */ -export const questionnaireResponseSchema: ZodType = - untypedQuestionnaireResponseSchema - -/** - * Wrapper class for FHIR QuestionnaireResponse resources. - * Provides utility methods for retrieving questionnaire response items. - */ -export class FhirQuestionnaireResponse extends FhirDomainResource { - // Static Functions - - /** - * Parses a QuestionnaireResponse resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirQuestionnaireResponse instance - */ - public static parse(value: unknown): FhirQuestionnaireResponse { - return new FhirQuestionnaireResponse( - questionnaireResponseSchema.parse(value), - ) - } - - // Properties - - /** - * Gets the authored date/time as a JavaScript Date object. - * - * @returns The authored date if available, undefined otherwise - * - * @example - * ```typescript - * const authoredDate = response.authoredDate - * if (authoredDate) { - * console.log(`Response created on: ${authoredDate.toLocaleDateString()}`) - * } - * ``` - */ - public get authoredDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.authored) - } - - // Methods - - /** - * Retrieves a single response item by its linkId path. - * Throws an error if multiple items are found. - * - * @param linkIdPath - Array of linkId strings forming the path to the item - * @returns The matching item, or undefined if not found - * @throws {Error} If multiple items match the path - * - * @example - * ```typescript - * // Get a nested item by path - * const item = response.uniqueResponseItem(['section1', 'question1']) - * ``` - */ - public uniqueResponseItem( - linkIdPath: string[], - ): QuestionnaireResponseItem | undefined { - const items = this.responseItems(linkIdPath) - switch (items.length) { - case 0: - return undefined - case 1: - return items[0] - default: - throw new Error(`Unexpected number of response items found.`) - } - } - - /** - * Retrieves all response items matching a linkId path. - * Searches recursively through the item hierarchy. - * - * @param linkIdPath - Array of linkId strings forming the path to the items - * @returns Array of matching response items - * - * @example - * ```typescript - * // Get all items matching a path - * const items = response.responseItems(['section1', 'question1']) - * items.forEach(item => console.log(item.answer)) - * ``` - */ - public responseItems(linkIdPath: string[]): QuestionnaireResponseItem[] { - const resultValue: QuestionnaireResponseItem[] = [] - for (const child of this.value.item ?? []) { - resultValue.push(...this.responseItemsRecursive(linkIdPath, child)) - } - return resultValue - } - - /** - * Recursively searches for questionnaire response items matching the given linkId path. - * - * @param linkIdPath - The path of linkIds to match - * @param item - The current item to search within - * @returns Array of matching questionnaire response items - */ - private responseItemsRecursive( - linkIdPath: string[], - item: QuestionnaireResponseItem, - ): QuestionnaireResponseItem[] { - switch (linkIdPath.length) { - case 0: - break - case 1: - if (item.linkId === linkIdPath[0]) { - return [item] - } - break - default: - if (item.linkId === linkIdPath[0]) { - const childLinkIds = linkIdPath.slice(1) - const resultValue: QuestionnaireResponseItem[] = [] - for (const child of item.item ?? []) { - resultValue.push( - ...this.responseItemsRecursive(childLinkIds, child), - ) - } - return resultValue - } - break - } - return [] - } - - // Methods - Response items from leaf link id - - /** - * Retrieves a single leaf response item by its linkId. - * A leaf item is one with no nested items. - * Throws an error if multiple items are found. - * - * @param linkId - The linkId of the leaf item - * @returns The matching leaf item, or undefined if not found - * @throws {Error} If multiple leaf items match - * - * @example - * ```typescript - * const leafItem = response.uniqueLeafResponseItem('question1') - * if (leafItem?.answer) { - * console.log(leafItem.answer[0].valueString) - * } - * ``` - */ - public uniqueLeafResponseItem( - linkId: string, - ): QuestionnaireResponseItem | undefined { - const items = this.leafResponseItems(linkId) - switch (items.length) { - case 0: - return undefined - case 1: - return items[0] - default: - throw new Error('Unexpected number of leaf response items found.') - } - } - - /** - * Retrieves all leaf response items with a specific linkId. - * A leaf item is one with no nested items. - * Searches recursively through the entire item hierarchy. - * - * @param linkId - The linkId to search for - * @returns Array of matching leaf response items - * - * @example - * ```typescript - * // Find all leaf items with a specific linkId - * const items = response.leafResponseItems('question1') - * ``` - */ - public leafResponseItems(linkId: string): QuestionnaireResponseItem[] { - const items: QuestionnaireResponseItem[] = [] - for (const item of this.value.item ?? []) { - items.push(...this.leafResponseItemsRecursive(linkId, item)) - } - return items - } - - /** - * Recursively searches for leaf questionnaire response items matching the given linkId. - * - * @param linkId - The linkId to match - * @param item - The current item to search within - * @returns Array of matching leaf questionnaire response items - */ - private leafResponseItemsRecursive( - linkId: string, - item: QuestionnaireResponseItem, - ): QuestionnaireResponseItem[] { - const children = item.item ?? [] - if (children.length === 0 && item.linkId === linkId) { - return [item] - } - const items: QuestionnaireResponseItem[] = [] - for (const child of item.item ?? []) { - items.push(...this.leafResponseItemsRecursive(linkId, child)) - } - return items - } -} diff --git a/packages/fhir/src/resources/regulatedAuthorization.ts b/packages/fhir/src/resources/regulatedAuthorization.ts deleted file mode 100644 index c855cee7..00000000 --- a/packages/fhir/src/resources/regulatedAuthorization.ts +++ /dev/null @@ -1,90 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type RegulatedAuthorization, - type RegulatedAuthorizationCase, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - codeableReferenceSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' - -const regulatedAuthorizationCaseSchema: ZodType = - backboneElementSchema.extend({ - get application() { - return regulatedAuthorizationCaseSchema.array().optional() - }, - datePeriod: periodSchema.optional(), - dateDateTime: dateTimeSchema.optional(), - _dateDateTime: elementSchema.optional(), - identifier: identifierSchema.optional(), - status: codeableConceptSchema.optional(), - type: codeableConceptSchema.optional(), - }) - -/** - * Zod schema for FHIR RegulatedAuthorization resource (untyped version). - */ -export const untypedRegulatedAuthorizationSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('RegulatedAuthorization').readonly(), - basis: codeableConceptSchema.array().optional(), - case: regulatedAuthorizationCaseSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - holder: referenceSchema.optional(), - identifier: identifierSchema.array().optional(), - indication: codeableReferenceSchema.optional(), - intendedUse: codeableConceptSchema.optional(), - region: codeableConceptSchema.array().optional(), - regulator: referenceSchema.optional(), - status: codeableConceptSchema.optional(), - statusDate: dateTimeSchema.optional(), - _statusDate: elementSchema.optional(), - subject: referenceSchema.array().optional(), - type: codeableConceptSchema.optional(), - validityPeriod: periodSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR RegulatedAuthorization resource. - */ -export const regulatedAuthorizationSchema: ZodType = - untypedRegulatedAuthorizationSchema - -/** - * Wrapper class for FHIR RegulatedAuthorization resources. - * Provides utility methods for working with regulated authorizations and product approvals. - */ -export class FhirRegulatedAuthorization extends FhirDomainResource { - // Static Functions - - /** - * Parses a RegulatedAuthorization resource from unknown data. - * - * @param value - The data to parse and validate against the RegulatedAuthorization schema - * @returns A FhirRegulatedAuthorization instance containing the validated resource - */ - public static parse(value: unknown): FhirRegulatedAuthorization { - return new FhirRegulatedAuthorization( - regulatedAuthorizationSchema.parse(value), - ) - } -} diff --git a/packages/fhir/src/resources/relatedPerson.ts b/packages/fhir/src/resources/relatedPerson.ts deleted file mode 100644 index bdab46da..00000000 --- a/packages/fhir/src/resources/relatedPerson.ts +++ /dev/null @@ -1,139 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type RelatedPersonCommunication, - type Coding, - type RelatedPerson, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - addressSchema, - attachmentSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactPointSchema, - dateSchema, - domainResourceSchema, - elementSchema, - humanNameSchema, - identifierSchema, - periodSchema, - referenceSchema, -} from '../elements/index.js' -import { administrativeGenderSchema } from '../valueSets/index.js' - -const relatedPersonCommunicationSchema: ZodType = - backboneElementSchema.extend({ - language: codeableConceptSchema, - preferred: booleanSchema.optional(), - _preferred: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR RelatedPerson resource (untyped version). - */ -export const untypedRelatedPersonSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('RelatedPerson').readonly(), - identifier: identifierSchema.array().optional(), - active: booleanSchema.optional(), - _active: elementSchema.optional(), - patient: referenceSchema, - relationship: codeableConceptSchema.array().optional(), - name: humanNameSchema.array().optional(), - telecom: contactPointSchema.array().optional(), - gender: administrativeGenderSchema.optional(), - _gender: elementSchema.optional(), - birthDate: dateSchema.optional(), - _birthDate: elementSchema.optional(), - address: addressSchema.array().optional(), - photo: attachmentSchema.array().optional(), - period: periodSchema.optional(), - communication: relatedPersonCommunicationSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR RelatedPerson resource. - */ -export const relatedPersonSchema: ZodType = - untypedRelatedPersonSchema - -/** - * Wrapper class for FHIR RelatedPerson resources. - * Provides utility methods for working with related person demographics. - */ -export class FhirRelatedPerson extends FhirDomainResource { - /** - * Parses a RelatedPerson resource from unknown data. - * - * @param value - The data to parse and validate against the RelatedPerson schema - * @returns A FhirRelatedPerson instance containing the validated resource - */ - public static parse(value: unknown): FhirRelatedPerson { - return new FhirRelatedPerson(relatedPersonSchema.parse(value)) - } - - /** - * Gets the birth date of the related person as a JavaScript Date object. - * - * @returns The birth date if available - */ - public get birthDate(): Date | undefined { - return FhirDomainResource.parseDate(this.value.birthDate) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/requestGroup.ts b/packages/fhir/src/resources/requestGroup.ts deleted file mode 100644 index bb8b4914..00000000 --- a/packages/fhir/src/resources/requestGroup.ts +++ /dev/null @@ -1,233 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type RequestGroup, - type RequestGroupAction, - type RequestGroupActionCondition, - type RequestGroupActionRelatedAction, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - canonicalSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - expressionSchema, - identifierSchema, - periodSchema, - quantitySchema, - rangeSchema, - referenceSchema, - relatedArtifactSchema, - stringSchema, - timingSchema, -} from '../elements/index.js' -import { - requestGroupActionCardinalityBehaviorSchema, - requestGroupActionConditionKindSchema, - requestGroupActionGroupingBehaviorSchema, - requestGroupActionPrecheckBehaviorSchema, - requestGroupActionRelationshipTypeSchema, - requestGroupActionRequiredBehaviorSchema, - requestGroupActionSelectionBehaviorSchema, - requestGroupIntentSchema, - requestGroupStatusSchema, - requestPrioritySchema, -} from '../valueSets/index.js' - -const requestGroupActionConditionSchema: ZodType = - backboneElementSchema.extend({ - kind: requestGroupActionConditionKindSchema, - _kind: elementSchema.optional(), - expression: expressionSchema.optional(), - }) - -const requestGroupActionRelatedActionSchema: ZodType = - backboneElementSchema.extend({ - actionId: z.string(), - _actionId: elementSchema.optional(), - relationship: requestGroupActionRelationshipTypeSchema, - _relationship: elementSchema.optional(), - offsetDuration: quantitySchema.optional(), - offsetRange: rangeSchema.optional(), - }) - -const requestGroupActionSchema: ZodType = - backboneElementSchema.extend({ - prefix: stringSchema.optional(), - _prefix: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - textEquivalent: stringSchema.optional(), - _textEquivalent: elementSchema.optional(), - priority: requestPrioritySchema.optional(), - _priority: elementSchema.optional(), - code: codeableConceptSchema.array().optional(), - documentation: relatedArtifactSchema.array().optional(), - condition: requestGroupActionConditionSchema.array().optional(), - relatedAction: requestGroupActionRelatedActionSchema.array().optional(), - timingDateTime: dateTimeSchema.optional(), - _timingDateTime: elementSchema.optional(), - timingAge: quantitySchema.optional(), - timingPeriod: periodSchema.optional(), - timingDuration: quantitySchema.optional(), - timingRange: rangeSchema.optional(), - timingTiming: timingSchema.optional(), - participant: referenceSchema.array().optional(), - type: codeableConceptSchema.optional(), - groupingBehavior: requestGroupActionGroupingBehaviorSchema.optional(), - _groupingBehavior: elementSchema.optional(), - selectionBehavior: requestGroupActionSelectionBehaviorSchema.optional(), - _selectionBehavior: elementSchema.optional(), - requiredBehavior: requestGroupActionRequiredBehaviorSchema.optional(), - _requiredBehavior: elementSchema.optional(), - precheckBehavior: requestGroupActionPrecheckBehaviorSchema.optional(), - _precheckBehavior: elementSchema.optional(), - cardinalityBehavior: requestGroupActionCardinalityBehaviorSchema.optional(), - _cardinalityBehavior: elementSchema.optional(), - resource: referenceSchema.optional(), - get action() { - return requestGroupActionSchema.array().optional() - }, - }) - -/** - * Zod schema for FHIR RequestGroup resource (untyped version). - */ -export const untypedRequestGroupSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('RequestGroup').readonly(), - identifier: identifierSchema.array().optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: elementSchema.array().optional(), - instantiatesUri: z.string().array().optional(), - _instantiatesUri: elementSchema.array().optional(), - basedOn: referenceSchema.array().optional(), - replaces: referenceSchema.array().optional(), - groupIdentifier: identifierSchema.optional(), - status: requestGroupStatusSchema, - _status: elementSchema.optional(), - intent: requestGroupIntentSchema, - _intent: elementSchema.optional(), - priority: requestPrioritySchema.optional(), - _priority: elementSchema.optional(), - code: codeableConceptSchema.optional(), - subject: referenceSchema.optional(), - encounter: referenceSchema.optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: elementSchema.optional(), - author: referenceSchema.optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - action: requestGroupActionSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR RequestGroup resource. - */ -export const requestGroupSchema: ZodType = - untypedRequestGroupSchema - -/** - * Wrapper class for FHIR RequestGroup resources. - * Provides utility methods for working with request groups and coordinated care activities. - */ -export class FhirRequestGroup extends FhirDomainResource { - /** - * Parses a RequestGroup resource from unknown data. - * - * @param value - The data to parse and validate against the RequestGroup schema - * @returns A FhirRequestGroup instance containing the validated resource - */ - public static parse(value: unknown): FhirRequestGroup { - return new FhirRequestGroup(requestGroupSchema.parse(value)) - } - - /** - * Get the authored date. - * - * @returns The authored date, or undefined if not set - */ - public get authoredDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.authoredOn) - } - - /** - * Get the code display for the request group. - * - * @returns The code display text, or undefined if not set - */ - public get codeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.code) - } - - /** - * Get note texts from the request group. - * - * @returns Array of note texts - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/researchDefinition.ts b/packages/fhir/src/resources/researchDefinition.ts deleted file mode 100644 index a6de8c19..00000000 --- a/packages/fhir/src/resources/researchDefinition.ts +++ /dev/null @@ -1,162 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type ResearchDefinition } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - booleanSchema, - canonicalSchema, - codeableConceptSchema, - contactDetailSchema, - dateSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - markdownSchema, - periodSchema, - referenceSchema, - relatedArtifactSchema, - stringSchema, - uriSchema, - usageContextSchema, -} from '../elements/index.js' -import { publicationStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR ResearchDefinition resource (untyped version). - */ -export const untypedResearchDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ResearchDefinition').readonly(), - url: uriSchema.optional(), - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - shortTitle: stringSchema.optional(), - _shortTitle: elementSchema.optional(), - subtitle: stringSchema.optional(), - _subtitle: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - subjectCodeableConcept: codeableConceptSchema.optional(), - subjectReference: referenceSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - comment: stringSchema.array().optional(), - _comment: elementSchema.array().optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - purpose: markdownSchema.optional(), - _purpose: elementSchema.optional(), - usage: stringSchema.optional(), - _usage: elementSchema.optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - approvalDate: dateSchema.optional(), - _approvalDate: elementSchema.optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - topic: codeableConceptSchema.array().optional(), - author: contactDetailSchema.array().optional(), - editor: contactDetailSchema.array().optional(), - reviewer: contactDetailSchema.array().optional(), - endorser: contactDetailSchema.array().optional(), - relatedArtifact: relatedArtifactSchema.array().optional(), - library: canonicalSchema.array().optional(), - _library: elementSchema.array().optional(), - population: referenceSchema, - exposure: referenceSchema.optional(), - exposureAlternative: referenceSchema.optional(), - outcome: referenceSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ResearchDefinition resource. - */ -export const researchDefinitionSchema: ZodType = - untypedResearchDefinitionSchema - -/** - * Wrapper class for FHIR ResearchDefinition resources. - * Provides utility methods for working with research definitions and study protocols. - */ -export class FhirResearchDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses a ResearchDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the ResearchDefinition schema - * @returns A FhirResearchDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirResearchDefinition { - return new FhirResearchDefinition(researchDefinitionSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/researchElementDefinition.ts b/packages/fhir/src/resources/researchElementDefinition.ts deleted file mode 100644 index 9c304f5f..00000000 --- a/packages/fhir/src/resources/researchElementDefinition.ts +++ /dev/null @@ -1,212 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type ResearchElementDefinition, - type ResearchElementDefinitionCharacteristic, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - canonicalSchema, - codeableConceptSchema, - contactDetailSchema, - dataRequirementSchema, - dateSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - expressionSchema, - identifierSchema, - markdownSchema, - periodSchema, - quantitySchema, - referenceSchema, - relatedArtifactSchema, - stringSchema, - timingSchema, - uriSchema, - usageContextSchema, -} from '../elements/index.js' -import { - groupMeasureSchema, - publicationStatusSchema, - researchElementDefinitionTypeSchema, - variableTypeSchema, -} from '../valueSets/index.js' - -const researchElementDefinitionCharacteristicSchema: ZodType = - backboneElementSchema.extend({ - definitionCodeableConcept: codeableConceptSchema.optional(), - definitionCanonical: canonicalSchema.optional(), - _definitionCanonical: elementSchema.optional(), - definitionExpression: expressionSchema.optional(), - definitionDataRequirement: dataRequirementSchema.optional(), - usageContext: usageContextSchema.array().optional(), - exclude: booleanSchema.optional(), - _exclude: elementSchema.optional(), - unitOfMeasure: codeableConceptSchema.optional(), - studyEffectiveDescription: stringSchema.optional(), - _studyEffectiveDescription: elementSchema.optional(), - studyEffectiveDateTime: dateTimeSchema.optional(), - _studyEffectiveDateTime: elementSchema.optional(), - studyEffectivePeriod: periodSchema.optional(), - studyEffectiveDuration: quantitySchema.optional(), - studyEffectiveTiming: timingSchema.optional(), - studyEffectiveTimeFromStart: quantitySchema.optional(), - studyEffectiveGroupMeasure: groupMeasureSchema.optional(), - _studyEffectiveGroupMeasure: elementSchema.optional(), - participantEffectiveDescription: stringSchema.optional(), - _participantEffectiveDescription: elementSchema.optional(), - participantEffectiveDateTime: dateTimeSchema.optional(), - _participantEffectiveDateTime: elementSchema.optional(), - participantEffectivePeriod: periodSchema.optional(), - participantEffectiveDuration: quantitySchema.optional(), - participantEffectiveTiming: timingSchema.optional(), - participantEffectiveTimeFromStart: quantitySchema.optional(), - participantEffectiveGroupMeasure: groupMeasureSchema.optional(), - _participantEffectiveGroupMeasure: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR ResearchElementDefinition resource (untyped version). - */ -export const untypedResearchElementDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ResearchElementDefinition').readonly(), - url: uriSchema.optional(), - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - shortTitle: stringSchema.optional(), - _shortTitle: elementSchema.optional(), - subtitle: stringSchema.optional(), - _subtitle: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - subjectCodeableConcept: codeableConceptSchema.optional(), - subjectReference: referenceSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - comment: stringSchema.array().optional(), - _comment: elementSchema.array().optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - purpose: markdownSchema.optional(), - _purpose: elementSchema.optional(), - usage: stringSchema.optional(), - _usage: elementSchema.optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - approvalDate: dateSchema.optional(), - _approvalDate: elementSchema.optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - topic: codeableConceptSchema.array().optional(), - author: contactDetailSchema.array().optional(), - editor: contactDetailSchema.array().optional(), - reviewer: contactDetailSchema.array().optional(), - endorser: contactDetailSchema.array().optional(), - relatedArtifact: relatedArtifactSchema.array().optional(), - library: canonicalSchema.array().optional(), - _library: elementSchema.array().optional(), - type: researchElementDefinitionTypeSchema, - _type: elementSchema.optional(), - variableType: variableTypeSchema.optional(), - _variableType: elementSchema.optional(), - characteristic: researchElementDefinitionCharacteristicSchema.array(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ResearchElementDefinition resource. - */ -export const researchElementDefinitionSchema: ZodType = - untypedResearchElementDefinitionSchema - -/** - * Wrapper class for FHIR ResearchElementDefinition resources. - * Provides utility methods for working with research element definitions used in evidence-based medicine. - */ -export class FhirResearchElementDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses a ResearchElementDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the ResearchElementDefinition schema - * @returns A FhirResearchElementDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirResearchElementDefinition { - return new FhirResearchElementDefinition( - researchElementDefinitionSchema.parse(value), - ) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/researchStudy.ts b/packages/fhir/src/resources/researchStudy.ts deleted file mode 100644 index 4c68faf1..00000000 --- a/packages/fhir/src/resources/researchStudy.ts +++ /dev/null @@ -1,153 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type ResearchStudy, - type ResearchStudyArm, - type ResearchStudyObjective, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - codeableConceptSchema, - contactDetailSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - markdownSchema, - periodSchema, - referenceSchema, - relatedArtifactSchema, - stringSchema, -} from '../elements/index.js' -import { researchStudyStatusSchema } from '../valueSets/index.js' - -const researchStudyArmSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - type: codeableConceptSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - }) - -const researchStudyObjectiveSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema.optional(), - _name: elementSchema.optional(), - type: codeableConceptSchema.optional(), - }) - -/** - * Zod schema for FHIR ResearchStudy resource (untyped version). - */ -export const untypedResearchStudySchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ResearchStudy').readonly(), - identifier: identifierSchema.array().optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - protocol: referenceSchema.array().optional(), - partOf: referenceSchema.array().optional(), - status: researchStudyStatusSchema, - _status: elementSchema.optional(), - primaryPurposeType: codeableConceptSchema.optional(), - phase: codeableConceptSchema.optional(), - category: codeableConceptSchema.array().optional(), - focus: codeableConceptSchema.array().optional(), - condition: codeableConceptSchema.array().optional(), - contact: contactDetailSchema.array().optional(), - relatedArtifact: relatedArtifactSchema.array().optional(), - keyword: codeableConceptSchema.array().optional(), - location: codeableConceptSchema.array().optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - enrollment: referenceSchema.array().optional(), - period: periodSchema.optional(), - sponsor: referenceSchema.optional(), - principalInvestigator: referenceSchema.optional(), - site: referenceSchema.array().optional(), - reasonStopped: codeableConceptSchema.optional(), - note: annotationSchema.array().optional(), - arm: researchStudyArmSchema.array().optional(), - objective: researchStudyObjectiveSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ResearchStudy resource. - */ -export const researchStudySchema: ZodType = - untypedResearchStudySchema - -/** - * Wrapper class for FHIR ResearchStudy resources. - * Provides utility methods for working with research studies and clinical trials. - */ -export class FhirResearchStudy extends FhirDomainResource { - // Static Functions - - /** - * Parses a ResearchStudy resource from unknown data. - * - * @param value - The data to parse and validate against the ResearchStudy schema - * @returns A FhirResearchStudy instance containing the validated resource - */ - public static parse(value: unknown): FhirResearchStudy { - return new FhirResearchStudy(researchStudySchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/researchSubject.ts b/packages/fhir/src/resources/researchSubject.ts deleted file mode 100644 index 20233363..00000000 --- a/packages/fhir/src/resources/researchSubject.ts +++ /dev/null @@ -1,110 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type ResearchSubject } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { researchSubjectStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR ResearchSubject resource (untyped version). - */ -export const untypedResearchSubjectSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ResearchSubject').readonly(), - identifier: identifierSchema.array().optional(), - status: researchSubjectStatusSchema, - _status: elementSchema.optional(), - period: periodSchema.optional(), - study: referenceSchema, - individual: referenceSchema, - assignedArm: stringSchema.optional(), - _assignedArm: elementSchema.optional(), - actualArm: stringSchema.optional(), - _actualArm: elementSchema.optional(), - consent: referenceSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ResearchSubject resource. - */ -export const researchSubjectSchema: ZodType = - untypedResearchSubjectSchema - -/** - * Wrapper class for FHIR ResearchSubject resources. - * Provides utility methods for working with research subjects and study participants. - */ -export class FhirResearchSubject extends FhirDomainResource { - // Static Functions - - /** - * Parses a ResearchSubject resource from unknown data. - * - * @param value - The data to parse and validate against the ResearchSubject schema - * @returns A FhirResearchSubject instance containing the validated resource - */ - public static parse(value: unknown): FhirResearchSubject { - return new FhirResearchSubject(researchSubjectSchema.parse(value)) - } - - /** - * Gets all identifier values that match any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns Array of identifier values matching the specified systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value that matches any of the provided systems. - * - * @param system - One or more system URIs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values that match any of the provided types. - * - * @param type - One or more type codings to match - * @returns Array of identifier values matching the specified types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value that matches any of the provided types. - * - * @param type - One or more type codings to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/riskAssessment.ts b/packages/fhir/src/resources/riskAssessment.ts deleted file mode 100644 index 9efce579..00000000 --- a/packages/fhir/src/resources/riskAssessment.ts +++ /dev/null @@ -1,168 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type RiskAssessmentPrediction, - type RiskAssessment, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - decimalSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - rangeSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { riskAssessmentStatusSchema } from '../valueSets/index.js' - -const riskAssessmentPredictionSchema: ZodType = - backboneElementSchema.extend({ - outcome: codeableConceptSchema.optional(), - probabilityDecimal: decimalSchema.optional(), - probabilityRange: rangeSchema.optional(), - qualitativeRisk: codeableConceptSchema.optional(), - relativeRisk: decimalSchema.optional(), - whenPeriod: periodSchema.optional(), - whenRange: rangeSchema.optional(), - rationale: stringSchema.optional(), - _rationale: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR RiskAssessment resource (untyped version). - */ -export const untypedRiskAssessmentSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('RiskAssessment').readonly(), - identifier: identifierSchema.array().optional(), - basedOn: referenceSchema.optional(), - parent: referenceSchema.optional(), - status: riskAssessmentStatusSchema, - _status: elementSchema.optional(), - method: codeableConceptSchema.optional(), - code: codeableConceptSchema.optional(), - subject: referenceSchema, - encounter: referenceSchema.optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: elementSchema.optional(), - occurrencePeriod: periodSchema.optional(), - condition: referenceSchema.optional(), - performer: referenceSchema.optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - basis: referenceSchema.array().optional(), - prediction: riskAssessmentPredictionSchema.array().optional(), - mitigation: stringSchema.optional(), - _mitigation: elementSchema.optional(), - note: annotationSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR RiskAssessment resource. - */ -export const riskAssessmentSchema: ZodType = - untypedRiskAssessmentSchema - -/** - * Wrapper class for FHIR RiskAssessment resources. - * Provides utility methods for working with risk assessments and clinical predictions. - */ -export class FhirRiskAssessment extends FhirDomainResource { - /** - * Parses a RiskAssessment resource from unknown data. - * - * @param value - The data to parse and validate against the RiskAssessment schema - * @returns A FhirRiskAssessment instance containing the validated resource - */ - public static parse(value: unknown): FhirRiskAssessment { - return new FhirRiskAssessment(riskAssessmentSchema.parse(value)) - } - - /** - * Get the date the risk assessment was performed. - * - * @returns The occurrence date, or undefined if not set - */ - public get occurrenceDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.occurrenceDateTime) - } - - /** - * Get the note texts from the assessment. - * - * @returns Array of note texts - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Get the code display for the risk assessment. - * - * @returns The code display text, or undefined if not set - */ - public get codeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.code) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/schedule.ts b/packages/fhir/src/resources/schedule.ts deleted file mode 100644 index 2ea21dee..00000000 --- a/packages/fhir/src/resources/schedule.ts +++ /dev/null @@ -1,170 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type Schedule } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - codeableConceptSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' - -/** - * Zod schema for FHIR Schedule resource (untyped version). - */ -export const untypedScheduleSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Schedule').readonly(), - identifier: identifierSchema.array().optional(), - active: z.boolean().optional(), - _active: elementSchema.optional(), - serviceCategory: codeableConceptSchema.array().optional(), - serviceType: codeableConceptSchema.array().optional(), - specialty: codeableConceptSchema.array().optional(), - actor: referenceSchema.array(), - planningHorizon: periodSchema.optional(), - comment: stringSchema.optional(), - _comment: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Schedule resource. - */ -export const scheduleSchema: ZodType = untypedScheduleSchema - -/** - * Wrapper class for FHIR Schedule resources. - * Provides utility methods for working with schedules and availability information. - */ -export class FhirSchedule extends FhirDomainResource { - // Static Functions - - /** - * Parses a Schedule resource from unknown data. - * - * @param value - The data to parse and validate against the Schedule schema - * @returns A FhirSchedule instance containing the validated resource - */ - public static parse(value: unknown): FhirSchedule { - return new FhirSchedule(scheduleSchema.parse(value)) - } - - /** - * Gets the planning horizon start date as a JavaScript Date object. - * - * @returns The planning horizon start date if available, undefined otherwise - * - * @example - * ```typescript - * const start = schedule.planningHorizonStart - * console.log(`Planning starts: ${start?.toLocaleDateString()}`) - * ``` - */ - public get planningHorizonStartDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.planningHorizon?.start) - } - - /** - * Gets the planning horizon end date as a JavaScript Date object. - * - * @returns The planning horizon end date if available, undefined otherwise - * - * @example - * ```typescript - * const end = schedule.planningHorizonEnd - * console.log(`Planning ends: ${end?.toLocaleDateString()}`) - * ``` - */ - public get planningHorizonEndDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.planningHorizon?.end) - } - - /** - * Gets all service category displays. - * - * @returns Array of service category display texts - * - * @example - * ```typescript - * const categories = schedule.getServiceCategoryDisplays() - * console.log(`Categories: ${categories.join(', ')}`) - * ``` - */ - public get serviceCategoryDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays( - this.value.serviceCategory, - ) - } - - /** - * Gets all specialty displays. - * - * @returns Array of specialty display texts - * - * @example - * ```typescript - * const specialties = schedule.getSpecialtyDisplays() - * console.log(`Specialties: ${specialties.join(', ')}`) - * ``` - */ - public get specialtyDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.specialty) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/searchParameter.ts b/packages/fhir/src/resources/searchParameter.ts deleted file mode 100644 index 4d26fceb..00000000 --- a/packages/fhir/src/resources/searchParameter.ts +++ /dev/null @@ -1,123 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type SearchParameterComponent, - type SearchParameter, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactDetailSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - stringSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - fhirResourceTypeSchema, - publicationStatusSchema, - searchParameterComparatorSchema, - searchParameterModifierSchema, - searchParameterTypeSchema, - searchParameterXpathUsageSchema, -} from '../valueSets/index.js' - -const searchParameterComponentSchema: ZodType = - backboneElementSchema.extend({ - definition: urlSchema, - _definition: elementSchema.optional(), - expression: stringSchema, - _expression: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR SearchParameter resource (untyped version). - */ -export const untypedSearchParameterSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('SearchParameter').readonly(), - base: fhirResourceTypeSchema.array(), - _base: elementSchema.array().optional(), - chain: stringSchema.array().optional(), - _chain: elementSchema.array().optional(), - code: stringSchema, - _code: elementSchema.optional(), - comparator: searchParameterComparatorSchema.array().optional(), - _comparator: elementSchema.array().optional(), - component: searchParameterComponentSchema.array().optional(), - contact: contactDetailSchema.array().optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - derivedFrom: urlSchema.optional(), - _derivedFrom: elementSchema.optional(), - description: stringSchema, - _description: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - expression: stringSchema.optional(), - _expression: elementSchema.optional(), - jurisdiction: codeableConceptSchema.array().optional(), - modifier: searchParameterModifierSchema.array().optional(), - _modifier: elementSchema.array().optional(), - multipleAnd: booleanSchema.optional(), - _multipleAnd: elementSchema.optional(), - multipleOr: booleanSchema.optional(), - _multipleOr: elementSchema.optional(), - name: stringSchema, - _name: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - purpose: stringSchema.optional(), - _purpose: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - target: fhirResourceTypeSchema.array().optional(), - _target: elementSchema.array().optional(), - type: searchParameterTypeSchema, - _type: elementSchema.optional(), - url: urlSchema, - _url: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - xpath: stringSchema.optional(), - _xpath: elementSchema.optional(), - xpathUsage: searchParameterXpathUsageSchema.optional(), - _xpathUsage: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR SearchParameter resource. - */ -export const searchParameterSchema: ZodType = - untypedSearchParameterSchema - -/** - * Wrapper class for FHIR SearchParameter resources. - * Provides utility methods for working with search parameters and custom search definitions. - */ -export class FhirSearchParameter extends FhirDomainResource { - // Static Functions - - /** - * Parses a SearchParameter resource from unknown data. - * - * @param value - The data to parse and validate against the SearchParameter schema - * @returns A FhirSearchParameter instance containing the validated resource - */ - public static parse(value: unknown): FhirSearchParameter { - return new FhirSearchParameter(searchParameterSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/serviceRequest.ts b/packages/fhir/src/resources/serviceRequest.ts deleted file mode 100644 index 141fdda6..00000000 --- a/packages/fhir/src/resources/serviceRequest.ts +++ /dev/null @@ -1,205 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type ServiceRequest } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - booleanSchema, - codeableConceptSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - quantitySchema, - rangeSchema, - ratioSchema, - referenceSchema, - stringSchema, - timingSchema, -} from '../elements/index.js' -import { - requestPrioritySchema, - serviceRequestIntentSchema, - serviceRequestStatusSchema, -} from '../valueSets/index.js' - -/** - * Zod schema for FHIR ServiceRequest resource (untyped version). - */ -export const untypedServiceRequestSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ServiceRequest').readonly(), - asNeededBoolean: booleanSchema.optional(), - _asNeededBoolean: elementSchema.optional(), - asNeededCodeableConcept: codeableConceptSchema.optional(), - authoredOn: stringSchema.optional(), - _authoredOn: elementSchema.optional(), - basedOn: referenceSchema.array().optional(), - bodySite: codeableConceptSchema.array().optional(), - category: codeableConceptSchema.array().optional(), - code: codeableConceptSchema.optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: elementSchema.optional(), - encounter: referenceSchema.optional(), - identifier: identifierSchema.array().optional(), - instantiatesCanonical: stringSchema.array().optional(), - _instantiatesCanonical: elementSchema.array().optional(), - instantiatesUri: stringSchema.array().optional(), - _instantiatesUri: elementSchema.array().optional(), - insurance: referenceSchema.array().optional(), - intent: serviceRequestIntentSchema, - _intent: elementSchema.optional(), - locationCode: codeableConceptSchema.array().optional(), - locationReference: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - occurrenceDateTime: stringSchema.optional(), - _occurrenceDateTime: elementSchema.optional(), - occurrencePeriod: periodSchema.optional(), - occurrenceTiming: timingSchema.optional(), - orderDetail: codeableConceptSchema.array().optional(), - patientInstruction: stringSchema.optional(), - _patientInstruction: elementSchema.optional(), - performer: referenceSchema.array().optional(), - performerType: codeableConceptSchema.optional(), - priority: requestPrioritySchema.optional(), - _priority: elementSchema.optional(), - quantityQuantity: quantitySchema.optional(), - quantityRatio: ratioSchema.optional(), - quantityRange: rangeSchema.optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - relevantHistory: referenceSchema.array().optional(), - replaces: referenceSchema.array().optional(), - requester: referenceSchema.optional(), - requisition: identifierSchema.optional(), - specimen: referenceSchema.array().optional(), - status: serviceRequestStatusSchema, - _status: elementSchema.optional(), - subject: referenceSchema, - supportingInfo: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ServiceRequest resource. - */ -export const serviceRequestSchema: ZodType = - untypedServiceRequestSchema - -/** - * Wrapper class for FHIR ServiceRequest resources. - * Provides utility methods for working with service requests. - */ -export class FhirServiceRequest extends FhirDomainResource { - // Static Functions - - /** - * Parses a ServiceRequest resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirServiceRequest instance - */ - public static parse(value: unknown): FhirServiceRequest { - return new FhirServiceRequest(serviceRequestSchema.parse(value)) - } - - // Properties - - /** - * Gets the authored date as a JavaScript Date object. - * - * @returns The authored date if available - */ - public get authoredDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.authoredOn) - } - - /** - * Gets the occurrence date as a JavaScript Date object. - * - * @returns The occurrence date if available - */ - public get occurrenceDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.occurrenceDateTime) - } - - /** - * Gets the code display text (what is being requested). - * - * @returns The code display - */ - public get codeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.code) - } - - /** - * Gets all category displays. - * - * @returns Array of category display texts - */ - public getCategoryDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.category) - } - - /** - * Gets note texts from the service request. - * - * @returns Array of note texts - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/slot.ts b/packages/fhir/src/resources/slot.ts deleted file mode 100644 index d6bb705c..00000000 --- a/packages/fhir/src/resources/slot.ts +++ /dev/null @@ -1,192 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Coding, type Slot } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { slotStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR Slot resource (untyped version). - */ -export const untypedSlotSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Slot').readonly(), - identifier: identifierSchema.array().optional(), - serviceCategory: codeableConceptSchema.array().optional(), - serviceType: codeableConceptSchema.array().optional(), - specialty: codeableConceptSchema.array().optional(), - appointmentType: codeableConceptSchema.optional(), - schedule: referenceSchema, - status: slotStatusSchema, - _status: elementSchema.optional(), - start: dateTimeSchema, - _start: elementSchema.optional(), - end: dateTimeSchema, - _end: elementSchema.optional(), - overbooked: z.boolean().optional(), - _overbooked: elementSchema.optional(), - comment: stringSchema.optional(), - _comment: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Slot resource. - */ -export const slotSchema: ZodType = untypedSlotSchema - -/** - * Wrapper class for FHIR Slot resources. - * Provides convenience accessors for temporal calculations and specialty display extraction. - */ -export class FhirSlot extends FhirDomainResource { - // Static Functions - - /** - * Parses a Slot resource from unknown data. - * - * @param value - The data to parse and validate against the Slot schema - * @returns A FhirSlot instance containing the validated resource - */ - public static parse(value: unknown): FhirSlot { - return new FhirSlot(slotSchema.parse(value)) - } - - /** - * Gets the slot start date/time as a JavaScript Date object. - * - * @returns The start date - * - * @example - * ```typescript - * const start = slot.startDate - * console.log(`Slot starts at: ${start.toLocaleString()}`) - * ``` - */ - public get startDate(): Date { - return new Date(this.value.start) - } - - /** - * Gets the slot end date/time as a JavaScript Date object. - * - * @returns The end date - * - * @example - * ```typescript - * const end = slot.endDate - * console.log(`Slot ends at: ${end.toLocaleString()}`) - * ``` - */ - public get endDate(): Date { - return new Date(this.value.end) - } - - /** - * Calculates the duration of the slot in milliseconds. - * - * @returns Duration in milliseconds - * - * @example - * ```typescript - * const minutes = slot.duration / (1000 * 60) - * console.log(`Slot duration: ${minutes} minutes`) - * ``` - */ - public get duration(): number { - return this.endDate.getTime() - this.startDate.getTime() - } - - /** - * Checks if the slot is in the past relative to a given date. - * - * @param asOfDate - The reference date (defaults to now) - * @returns True if the slot end time is in the past, false otherwise - * - * @example - * ```typescript - * if (slot.isPast()) { - * console.log('This slot has already passed') - * } - * ``` - */ - public isPast(asOfDate: Date = new Date()): boolean { - return this.endDate < asOfDate - } - - /** - * Gets all specialty displays for the slot. - * - * @returns Array of specialty display texts - * - * @example - * ```typescript - * const specialties = slot.getSpecialtyDisplays() - * console.log(`Specialties: ${specialties.join(', ')}`) - * ``` - */ - public get specialtyDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays(this.value.specialty) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/specimen.ts b/packages/fhir/src/resources/specimen.ts deleted file mode 100644 index 197e2f49..00000000 --- a/packages/fhir/src/resources/specimen.ts +++ /dev/null @@ -1,200 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type SpecimenCollection, - type SpecimenContainer, - type SpecimenProcessing, - type Coding, - type Specimen, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - quantitySchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { specimenStatusSchema } from '../valueSets/index.js' - -const specimenCollectionSchema: ZodType = - backboneElementSchema.extend({ - collector: referenceSchema.optional(), - collectedDateTime: dateTimeSchema.optional(), - _collectedDateTime: elementSchema.optional(), - collectedPeriod: periodSchema.optional(), - duration: quantitySchema.optional(), - quantity: quantitySchema.optional(), - method: codeableConceptSchema.optional(), - bodySite: codeableConceptSchema.optional(), - fastingStatusCodeableConcept: codeableConceptSchema.optional(), - fastingStatusDuration: quantitySchema.optional(), - }) - -const specimenProcessingSchema: ZodType = - backboneElementSchema.extend({ - description: stringSchema.optional(), - _description: elementSchema.optional(), - procedure: codeableConceptSchema.optional(), - additive: referenceSchema.array().optional(), - timeDateTime: dateTimeSchema.optional(), - _timeDateTime: elementSchema.optional(), - timePeriod: periodSchema.optional(), - }) - -const specimenContainerSchema: ZodType = - backboneElementSchema.extend({ - identifier: identifierSchema.array().optional(), - description: stringSchema.optional(), - type: codeableConceptSchema.optional(), - capacity: quantitySchema.optional(), - specimenQuantity: quantitySchema.optional(), - additiveCodeableConcept: codeableConceptSchema.optional(), - additiveReference: referenceSchema.optional(), - }) - -/** - * Zod schema for FHIR Specimen resource (untyped version). - */ -export const untypedSpecimenSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Specimen').readonly(), - identifier: identifierSchema.array().optional(), - accessionIdentifier: identifierSchema.optional(), - status: specimenStatusSchema.optional(), - _status: elementSchema.optional(), - type: codeableConceptSchema.optional(), - subject: referenceSchema.optional(), - receivedTime: dateTimeSchema.optional(), - _receivedTime: elementSchema.optional(), - parent: referenceSchema.array().optional(), - request: referenceSchema.array().optional(), - collection: specimenCollectionSchema.optional(), - processing: specimenProcessingSchema.array().optional(), - container: specimenContainerSchema.array().optional(), - condition: codeableConceptSchema.array().optional(), - note: annotationSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Specimen resource. - */ -export const specimenSchema: ZodType = untypedSpecimenSchema - -/** - * Wrapper class for FHIR Specimen resources. - * Provides utility methods for working with specimens. - */ -export class FhirSpecimen extends FhirDomainResource { - // Static Functions - - /** - * Parses a Specimen resource from unknown data. - * - * @param value - The data to parse - * @returns A FhirSpecimen instance - */ - public static parse(value: unknown): FhirSpecimen { - return new FhirSpecimen(specimenSchema.parse(value)) - } - - // Properties - - /** - * Gets the received time as a JavaScript Date object. - * - * @returns The received time if available - */ - public get receivedTime(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.receivedTime) - } - - /** - * Gets the collection time as a JavaScript Date object. - * - * @returns The collection time if available - */ - public get collectionTime(): Date | undefined { - return FhirDomainResource.parseDateTime( - this.value.collection?.collectedDateTime, - ) - } - - /** - * Gets the type display text. - * - * @returns The type display - */ - public get typeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.type) - } - - /** - * Gets identifiers by system. - * - * @param system - The system URL to filter by - * @returns Array of identifier values matching the system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } - - /** - * Gets note texts from the specimen. - * - * @returns Array of note texts - */ - public get noteTexts(): string[] { - return FhirDomainResource.annotationTexts(this.value.note) - } -} diff --git a/packages/fhir/src/resources/specimenDefinition.ts b/packages/fhir/src/resources/specimenDefinition.ts deleted file mode 100644 index e2f1349c..00000000 --- a/packages/fhir/src/resources/specimenDefinition.ts +++ /dev/null @@ -1,118 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type SpecimenDefinitionTypeTested, - type SpecimenDefinitionTypeTestedContainer, - type SpecimenDefinitionTypeTestedContainerAdditive, - type SpecimenDefinitionTypeTestedHandling, - type SpecimenDefinition, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - quantitySchema, - rangeSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { specimenDefinitionTypeTestedPreferenceSchema } from '../valueSets/index.js' - -const specimenDefinitionTypeTestedContainerAdditiveSchema: ZodType = - backboneElementSchema.extend({ - additiveCodeableConcept: codeableConceptSchema.optional(), - additiveReference: referenceSchema.optional(), - }) - -const specimenDefinitionTypeTestedContainerSchema: ZodType = - backboneElementSchema.extend({ - material: codeableConceptSchema.optional(), - type: codeableConceptSchema.optional(), - cap: codeableConceptSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - capacity: quantitySchema.optional(), - minimumVolumeQuantity: quantitySchema.optional(), - minimumVolumeString: stringSchema.optional(), - _minimumVolumeString: elementSchema.optional(), - additive: specimenDefinitionTypeTestedContainerAdditiveSchema - .array() - .optional(), - preparation: stringSchema.optional(), - _preparation: elementSchema.optional(), - }) - -const specimenDefinitionTypeTestedHandlingSchema: ZodType = - backboneElementSchema.extend({ - temperatureQualifier: codeableConceptSchema.optional(), - temperatureRange: rangeSchema.optional(), - maxDuration: quantitySchema.optional(), - instruction: stringSchema.optional(), - _instruction: elementSchema.optional(), - }) - -const specimenDefinitionTypeTestedSchema: ZodType = - backboneElementSchema.extend({ - isDerived: booleanSchema.optional(), - _isDerived: elementSchema.optional(), - type: codeableConceptSchema.optional(), - preference: specimenDefinitionTypeTestedPreferenceSchema, - _preference: elementSchema.optional(), - container: specimenDefinitionTypeTestedContainerSchema.optional(), - requirement: stringSchema.optional(), - _requirement: elementSchema.optional(), - retentionTime: quantitySchema.optional(), - rejectionCriterion: codeableConceptSchema.array().optional(), - handling: specimenDefinitionTypeTestedHandlingSchema.array().optional(), - }) - -/** - * Zod schema for FHIR SpecimenDefinition resource (untyped version). - */ -export const untypedSpecimenDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('SpecimenDefinition').readonly(), - identifier: identifierSchema.optional(), - typeCollected: codeableConceptSchema.optional(), - patientPreparation: codeableConceptSchema.array().optional(), - timeAspect: stringSchema.optional(), - _timeAspect: elementSchema.optional(), - collection: codeableConceptSchema.array().optional(), - typeTested: specimenDefinitionTypeTestedSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR SpecimenDefinition resource. - */ -export const specimenDefinitionSchema: ZodType = - untypedSpecimenDefinitionSchema - -/** - * Wrapper class for FHIR SpecimenDefinition resources. - * Provides utility methods for working with specimen definitions and collection protocols. - */ -export class FhirSpecimenDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses a SpecimenDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the SpecimenDefinition schema - * @returns A FhirSpecimenDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirSpecimenDefinition { - return new FhirSpecimenDefinition(specimenDefinitionSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/structureDefinition.ts b/packages/fhir/src/resources/structureDefinition.ts deleted file mode 100644 index 58fcf458..00000000 --- a/packages/fhir/src/resources/structureDefinition.ts +++ /dev/null @@ -1,594 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ElementDefinition, - type ElementDefinitionBase, - type ElementDefinitionBinding, - type ElementDefinitionConstraint, - type ElementDefinitionExample, - type ElementDefinitionMapping, - type ElementDefinitionSlicing, - type ElementDefinitionSlicingDiscriminator, - type ElementDefinitionType, - type StructureDefinitionContext, - type StructureDefinitionDifferential, - type StructureDefinitionMapping, - type StructureDefinitionSnapshot, - type StructureDefinition, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - addressSchema, - annotationSchema, - attachmentSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - codingSchema, - contactDetailSchema, - contactPointSchema, - contributorSchema, - dataRequirementSchema, - dateTimeSchema, - decimalSchema, - domainResourceSchema, - dosageSchema, - elementSchema, - expressionSchema, - humanNameSchema, - identifierSchema, - intSchema, - moneySchema, - parameterDefinitionSchema, - periodSchema, - quantitySchema, - rangeSchema, - ratioSchema, - referenceSchema, - relatedArtifactSchema, - sampledDataSchema, - signatureSchema, - stringSchema, - timingSchema, - triggerDefinitionSchema, - unsignedIntSchema, - uriSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - aggregationModeSchema, - bindingStrengthSchema, - constraintSeveritySchema, - discriminatorTypeSchema, - extensionContextTypeSchema, - fhirVersionSchema, - propertyRepresentationSchema, - publicationStatusSchema, - referenceVersionRulesSchema, - slicingRulesSchema, - structureDefinitionDerivationSchema, - structureDefinitionKindSchema, -} from '../valueSets/index.js' - -const structureDefinitionMappingSchema: ZodType = - backboneElementSchema.extend({ - identity: stringSchema, - _identity: elementSchema.optional(), - uri: urlSchema.optional(), - _uri: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - comment: stringSchema.optional(), - _comment: elementSchema.optional(), - }) - -const structureDefinitionContextSchema: ZodType = - backboneElementSchema.extend({ - type: extensionContextTypeSchema, - _type: elementSchema.optional(), - expression: stringSchema, - _expression: elementSchema.optional(), - }) - -const elementDefinitionSlicingDiscriminatorSchema: ZodType = - backboneElementSchema.extend({ - type: discriminatorTypeSchema, - _type: elementSchema.optional(), - path: stringSchema, - _path: elementSchema.optional(), - }) - -const elementDefinitionSlicingSchema: ZodType = - backboneElementSchema.extend({ - discriminator: elementDefinitionSlicingDiscriminatorSchema - .array() - .optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - ordered: booleanSchema.optional(), - _ordered: elementSchema.optional(), - rules: slicingRulesSchema, - _rules: elementSchema.optional(), - }) - -const elementDefinitionBaseSchema: ZodType = - backboneElementSchema.extend({ - path: stringSchema, - _path: elementSchema.optional(), - min: unsignedIntSchema, - max: stringSchema, - _max: elementSchema.optional(), - }) - -const elementDefinitionTypeSchema: ZodType = - backboneElementSchema.extend({ - code: uriSchema, - _code: elementSchema.optional(), - profile: urlSchema.array().optional(), - _profile: elementSchema.array().optional(), - targetProfile: urlSchema.array().optional(), - _targetProfile: elementSchema.array().optional(), - aggregation: aggregationModeSchema.array().optional(), - _aggregation: elementSchema.array().optional(), - versioning: referenceVersionRulesSchema.optional(), - _versioning: elementSchema.optional(), - }) - -const elementDefinitionExampleSchema: ZodType = - backboneElementSchema.extend({ - label: stringSchema, - _label: elementSchema.optional(), - valueBase64Binary: stringSchema.optional(), - _valueBase64Binary: elementSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueCanonical: urlSchema.optional(), - _valueCanonical: elementSchema.optional(), - valueCode: stringSchema.optional(), - _valueCode: elementSchema.optional(), - valueDate: stringSchema.optional(), - _valueDate: elementSchema.optional(), - valueDateTime: stringSchema.optional(), - _valueDateTime: elementSchema.optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: elementSchema.optional(), - valueId: stringSchema.optional(), - _valueId: elementSchema.optional(), - valueInstant: stringSchema.optional(), - _valueInstant: elementSchema.optional(), - valueInteger: intSchema.optional(), - _valueInteger: elementSchema.optional(), - valueMarkdown: stringSchema.optional(), - _valueMarkdown: elementSchema.optional(), - valueOid: stringSchema.optional(), - _valueOid: elementSchema.optional(), - valuePositiveInt: intSchema.optional(), - _valuePositiveInt: elementSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueTime: stringSchema.optional(), - _valueTime: elementSchema.optional(), - valueUnsignedInt: unsignedIntSchema.optional(), - _valueUnsignedInt: elementSchema.optional(), - valueUri: uriSchema.optional(), - _valueUri: elementSchema.optional(), - valueUrl: urlSchema.optional(), - _valueUrl: elementSchema.optional(), - valueUuid: stringSchema.optional(), - _valueUuid: elementSchema.optional(), - }) - -const elementDefinitionConstraintSchema: ZodType = - backboneElementSchema.extend({ - key: stringSchema, - _key: elementSchema.optional(), - requirements: stringSchema.optional(), - _requirements: elementSchema.optional(), - severity: constraintSeveritySchema, - _severity: elementSchema.optional(), - human: stringSchema, - _human: elementSchema.optional(), - expression: stringSchema.optional(), - _expression: elementSchema.optional(), - xpath: stringSchema.optional(), - _xpath: elementSchema.optional(), - source: urlSchema.optional(), - _source: elementSchema.optional(), - }) - -const elementDefinitionBindingSchema: ZodType = - backboneElementSchema.extend({ - strength: bindingStrengthSchema, - _strength: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - valueSet: urlSchema.optional(), - _valueSet: elementSchema.optional(), - }) - -const elementDefinitionMappingSchema: ZodType = - backboneElementSchema.extend({ - identity: stringSchema, - _identity: elementSchema.optional(), - language: stringSchema.optional(), - _language: elementSchema.optional(), - map: stringSchema, - _map: elementSchema.optional(), - comment: stringSchema.optional(), - _comment: elementSchema.optional(), - }) - -const elementDefinitionSchema: ZodType = - backboneElementSchema.extend({ - id: stringSchema.optional(), - path: stringSchema, - _path: elementSchema.optional(), - representation: propertyRepresentationSchema.array().optional(), - _representation: elementSchema.array().optional(), - sliceName: stringSchema.optional(), - _sliceName: elementSchema.optional(), - sliceIsConstraining: booleanSchema.optional(), - _sliceIsConstraining: elementSchema.optional(), - label: stringSchema.optional(), - _label: elementSchema.optional(), - code: codingSchema.array().optional(), - slicing: elementDefinitionSlicingSchema.optional(), - short: stringSchema.optional(), - _short: elementSchema.optional(), - definition: stringSchema.optional(), - _definition: elementSchema.optional(), - comment: stringSchema.optional(), - _comment: elementSchema.optional(), - requirements: stringSchema.optional(), - _requirements: elementSchema.optional(), - alias: stringSchema.array().optional(), - _alias: elementSchema.array().optional(), - min: unsignedIntSchema.optional(), - max: stringSchema.optional(), - _max: elementSchema.optional(), - base: elementDefinitionBaseSchema.optional(), - contentReference: uriSchema.optional(), - _contentReference: elementSchema.optional(), - type: elementDefinitionTypeSchema.array().optional(), - defaultValueBase64Binary: stringSchema.optional(), - _defaultValueBase64Binary: elementSchema.optional(), - defaultValueBoolean: booleanSchema.optional(), - _defaultValueBoolean: elementSchema.optional(), - defaultValueCanonical: urlSchema.optional(), - _defaultValueCanonical: elementSchema.optional(), - defaultValueCode: stringSchema.optional(), - _defaultValueCode: elementSchema.optional(), - defaultValueDate: stringSchema.optional(), - _defaultValueDate: elementSchema.optional(), - defaultValueDateTime: stringSchema.optional(), - _defaultValueDateTime: elementSchema.optional(), - defaultValueDecimal: decimalSchema.optional(), - _defaultValueDecimal: elementSchema.optional(), - defaultValueId: stringSchema.optional(), - _defaultValueId: elementSchema.optional(), - defaultValueInstant: stringSchema.optional(), - _defaultValueInstant: elementSchema.optional(), - defaultValueInteger: intSchema.optional(), - _defaultValueInteger: elementSchema.optional(), - defaultValueMarkdown: stringSchema.optional(), - _defaultValueMarkdown: elementSchema.optional(), - defaultValueOid: stringSchema.optional(), - _defaultValueOid: elementSchema.optional(), - defaultValuePositiveInt: intSchema.optional(), - _defaultValuePositiveInt: elementSchema.optional(), - defaultValueString: stringSchema.optional(), - _defaultValueString: elementSchema.optional(), - defaultValueTime: stringSchema.optional(), - _defaultValueTime: elementSchema.optional(), - defaultValueUnsignedInt: unsignedIntSchema.optional(), - _defaultValueUnsignedInt: elementSchema.optional(), - defaultValueUri: uriSchema.optional(), - _defaultValueUri: elementSchema.optional(), - defaultValueUrl: urlSchema.optional(), - _defaultValueUrl: elementSchema.optional(), - defaultValueUuid: stringSchema.optional(), - _defaultValueUuid: elementSchema.optional(), - defaultValueAddress: addressSchema.optional(), - defaultValueAge: quantitySchema.optional(), - defaultValueAnnotation: annotationSchema.optional(), - defaultValueAttachment: attachmentSchema.optional(), - defaultValueCodeableConcept: codeableConceptSchema.optional(), - defaultValueCoding: codingSchema.optional(), - defaultValueContactPoint: contactPointSchema.optional(), - defaultValueCount: quantitySchema.optional(), - defaultValueDistance: quantitySchema.optional(), - defaultValueDuration: quantitySchema.optional(), - defaultValueHumanName: humanNameSchema.optional(), - defaultValueIdentifier: identifierSchema.optional(), - defaultValueMoney: moneySchema.optional(), - defaultValuePeriod: periodSchema.optional(), - defaultValueQuantity: quantitySchema.optional(), - defaultValueRange: rangeSchema.optional(), - defaultValueRatio: ratioSchema.optional(), - defaultValueReference: referenceSchema.optional(), - defaultValueSampledData: sampledDataSchema.optional(), - defaultValueSignature: signatureSchema.optional(), - defaultValueTiming: timingSchema.optional(), - defaultValueContactDetail: contactDetailSchema.optional(), - defaultValueContributor: contributorSchema.optional(), - defaultValueDataRequirement: dataRequirementSchema.optional(), - defaultValueExpression: expressionSchema.optional(), - defaultValueParameterDefinition: parameterDefinitionSchema.optional(), - defaultValueRelatedArtifact: relatedArtifactSchema.optional(), - defaultValueTriggerDefinition: triggerDefinitionSchema.optional(), - defaultValueUsageContext: usageContextSchema.optional(), - defaultValueDosage: dosageSchema.optional(), - meaningWhenMissing: stringSchema.optional(), - _meaningWhenMissing: elementSchema.optional(), - orderMeaning: stringSchema.optional(), - _orderMeaning: elementSchema.optional(), - fixedBase64Binary: stringSchema.optional(), - _fixedBase64Binary: elementSchema.optional(), - fixedBoolean: booleanSchema.optional(), - _fixedBoolean: elementSchema.optional(), - fixedCanonical: urlSchema.optional(), - _fixedCanonical: elementSchema.optional(), - fixedCode: stringSchema.optional(), - _fixedCode: elementSchema.optional(), - fixedDate: stringSchema.optional(), - _fixedDate: elementSchema.optional(), - fixedDateTime: stringSchema.optional(), - _fixedDateTime: elementSchema.optional(), - fixedDecimal: decimalSchema.optional(), - _fixedDecimal: elementSchema.optional(), - fixedId: stringSchema.optional(), - _fixedId: elementSchema.optional(), - fixedInstant: stringSchema.optional(), - _fixedInstant: elementSchema.optional(), - fixedInteger: intSchema.optional(), - _fixedInteger: elementSchema.optional(), - fixedMarkdown: stringSchema.optional(), - _fixedMarkdown: elementSchema.optional(), - fixedOid: stringSchema.optional(), - _fixedOid: elementSchema.optional(), - fixedPositiveInt: intSchema.optional(), - _fixedPositiveInt: elementSchema.optional(), - fixedString: stringSchema.optional(), - _fixedString: elementSchema.optional(), - fixedTime: stringSchema.optional(), - _fixedTime: elementSchema.optional(), - fixedUnsignedInt: unsignedIntSchema.optional(), - _fixedUnsignedInt: elementSchema.optional(), - fixedUri: uriSchema.optional(), - _fixedUri: elementSchema.optional(), - fixedUrl: urlSchema.optional(), - _fixedUrl: elementSchema.optional(), - fixedUuid: stringSchema.optional(), - _fixedUuid: elementSchema.optional(), - patternBase64Binary: stringSchema.optional(), - _patternBase64Binary: elementSchema.optional(), - patternBoolean: booleanSchema.optional(), - _patternBoolean: elementSchema.optional(), - patternCanonical: urlSchema.optional(), - _patternCanonical: elementSchema.optional(), - patternCode: stringSchema.optional(), - _patternCode: elementSchema.optional(), - patternDate: stringSchema.optional(), - _patternDate: elementSchema.optional(), - patternDateTime: stringSchema.optional(), - _patternDateTime: elementSchema.optional(), - patternDecimal: decimalSchema.optional(), - _patternDecimal: elementSchema.optional(), - patternId: stringSchema.optional(), - _patternId: elementSchema.optional(), - patternInstant: stringSchema.optional(), - _patternInstant: elementSchema.optional(), - patternInteger: intSchema.optional(), - _patternInteger: elementSchema.optional(), - patternMarkdown: stringSchema.optional(), - _patternMarkdown: elementSchema.optional(), - patternOid: stringSchema.optional(), - _patternOid: elementSchema.optional(), - patternPositiveInt: intSchema.optional(), - _patternPositiveInt: elementSchema.optional(), - patternString: stringSchema.optional(), - _patternString: elementSchema.optional(), - patternTime: stringSchema.optional(), - _patternTime: elementSchema.optional(), - patternUnsignedInt: unsignedIntSchema.optional(), - _patternUnsignedInt: elementSchema.optional(), - patternUri: uriSchema.optional(), - _patternUri: elementSchema.optional(), - patternUrl: urlSchema.optional(), - _patternUrl: elementSchema.optional(), - patternUuid: stringSchema.optional(), - _patternUuid: elementSchema.optional(), - example: elementDefinitionExampleSchema.array().optional(), - minValueDate: stringSchema.optional(), - _minValueDate: elementSchema.optional(), - minValueDateTime: stringSchema.optional(), - _minValueDateTime: elementSchema.optional(), - minValueInstant: stringSchema.optional(), - _minValueInstant: elementSchema.optional(), - minValueTime: stringSchema.optional(), - _minValueTime: elementSchema.optional(), - minValueDecimal: decimalSchema.optional(), - _minValueDecimal: elementSchema.optional(), - minValueInteger: intSchema.optional(), - _minValueInteger: elementSchema.optional(), - minValuePositiveInt: intSchema.optional(), - _minValuePositiveInt: elementSchema.optional(), - minValueUnsignedInt: unsignedIntSchema.optional(), - _minValueUnsignedInt: elementSchema.optional(), - minValueQuantity: quantitySchema.optional(), - maxValueDate: stringSchema.optional(), - _maxValueDate: elementSchema.optional(), - maxValueDateTime: stringSchema.optional(), - _maxValueDateTime: elementSchema.optional(), - maxValueInstant: stringSchema.optional(), - _maxValueInstant: elementSchema.optional(), - maxValueTime: stringSchema.optional(), - _maxValueTime: elementSchema.optional(), - maxValueDecimal: decimalSchema.optional(), - _maxValueDecimal: elementSchema.optional(), - maxValueInteger: intSchema.optional(), - _maxValueInteger: elementSchema.optional(), - maxValuePositiveInt: intSchema.optional(), - _maxValuePositiveInt: elementSchema.optional(), - maxValueUnsignedInt: unsignedIntSchema.optional(), - _maxValueUnsignedInt: elementSchema.optional(), - maxValueQuantity: quantitySchema.optional(), - maxLength: intSchema.optional(), - _maxLength: elementSchema.optional(), - condition: stringSchema.array().optional(), - _condition: elementSchema.array().optional(), - constraint: elementDefinitionConstraintSchema.array().optional(), - mustSupport: booleanSchema.optional(), - _mustSupport: elementSchema.optional(), - isModifier: booleanSchema.optional(), - _isModifier: elementSchema.optional(), - isModifierReason: stringSchema.optional(), - _isModifierReason: elementSchema.optional(), - isSummary: booleanSchema.optional(), - _isSummary: elementSchema.optional(), - binding: elementDefinitionBindingSchema.optional(), - mapping: elementDefinitionMappingSchema.array().optional(), - }) - -const structureDefinitionSnapshotSchema: ZodType = - backboneElementSchema.extend({ - element: elementDefinitionSchema.array(), - }) - -const structureDefinitionDifferentialSchema: ZodType = - backboneElementSchema.extend({ - element: elementDefinitionSchema.array(), - }) - -/** - * Zod schema for FHIR StructureDefinition resource (untyped version). - */ -export const untypedStructureDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('StructureDefinition').readonly(), - url: urlSchema, - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema, - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - purpose: stringSchema.optional(), - _purpose: elementSchema.optional(), - copyright: stringSchema.optional(), - _copyright: elementSchema.optional(), - keyword: codingSchema.array().optional(), - fhirVersion: fhirVersionSchema.optional(), - _fhirVersion: elementSchema.optional(), - mapping: structureDefinitionMappingSchema.array().optional(), - kind: structureDefinitionKindSchema, - _kind: elementSchema.optional(), - abstract: booleanSchema, - _abstract: elementSchema.optional(), - context: structureDefinitionContextSchema.array().optional(), - contextInvariant: stringSchema.array().optional(), - _contextInvariant: elementSchema.array().optional(), - type: urlSchema, - _type: elementSchema.optional(), - baseDefinition: urlSchema.optional(), - _baseDefinition: elementSchema.optional(), - derivation: structureDefinitionDerivationSchema.optional(), - _derivation: elementSchema.optional(), - snapshot: structureDefinitionSnapshotSchema.optional(), - differential: structureDefinitionDifferentialSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR StructureDefinition resource. - */ -export const structureDefinitionSchema: ZodType = - untypedStructureDefinitionSchema - -/** - * Wrapper class for FHIR StructureDefinition resources. - * Provides utility methods for working with structure definitions that define the structure and constraints of FHIR resources, data types, and extensions. - */ -export class FhirStructureDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses a StructureDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the StructureDefinition schema - * @returns A FhirStructureDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirStructureDefinition { - return new FhirStructureDefinition(structureDefinitionSchema.parse(value)) - } - - /** - * Retrieves all identifier values matching any of the specified system URIs. - * - * @param system - One or more system URIs to filter identifiers by - * @returns Array of identifier values from matching systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Retrieves the first identifier value matching any of the specified system URIs. - * - * @param system - One or more system URIs to filter identifiers by - * @returns The first matching identifier value, or undefined if none found - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Retrieves all identifier values matching any of the specified type codings. - * - * @param type - One or more Coding objects representing identifier types - * @returns Array of identifier values from matching types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Retrieves the first identifier value matching any of the specified type codings. - * - * @param type - One or more Coding objects representing identifier types - * @returns The first matching identifier value, or undefined if none found - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/structureMap.ts b/packages/fhir/src/resources/structureMap.ts deleted file mode 100644 index ff89f6d0..00000000 --- a/packages/fhir/src/resources/structureMap.ts +++ /dev/null @@ -1,264 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type StructureMapGroup, - type StructureMapGroupInput, - type StructureMapGroupRuleDependent, - type StructureMapGroupRuleSource, - type StructureMapGroupRuleTarget, - type StructureMapGroupRuleTargetParameter, - type StructureMapStructure, - type StructureMap, - type StructureMapGroupRule, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - base64BinarySchema, - booleanSchema, - codeableConceptSchema, - contactDetailSchema, - dateSchema, - dateTimeSchema, - decimalSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - idSchema, - instantSchema, - intSchema, - markdownSchema, - positiveIntSchema, - stringSchema, - timeSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - publicationStatusSchema, - structureMapGroupInputModeSchema, - structureMapGroupTypeModeSchema, - structureMapModelModeSchema, - structureMapSourceListModeSchema, - structureMapTargetContextTypeSchema, - structureMapTargetListModeSchema, - structureMapTransformSchema, -} from '../valueSets/index.js' - -const structureMapGroupRuleSourceSchema: ZodType = - backboneElementSchema.extend({ - context: stringSchema, - _context: elementSchema.optional(), - min: intSchema.optional(), - max: stringSchema.optional(), - _max: elementSchema.optional(), - type: stringSchema.optional(), - _type: elementSchema.optional(), - defaultValueBase64Binary: base64BinarySchema.optional(), - _defaultValueBase64Binary: elementSchema.optional(), - defaultValueBoolean: booleanSchema.optional(), - _defaultValueBoolean: elementSchema.optional(), - defaultValueCanonical: urlSchema.optional(), - _defaultValueCanonical: elementSchema.optional(), - defaultValueCode: stringSchema.optional(), - _defaultValueCode: elementSchema.optional(), - defaultValueDate: dateSchema.optional(), - _defaultValueDate: elementSchema.optional(), - defaultValueDateTime: dateTimeSchema.optional(), - _defaultValueDateTime: elementSchema.optional(), - defaultValueDecimal: decimalSchema.optional(), - defaultValueId: stringSchema.optional(), - _defaultValueId: elementSchema.optional(), - defaultValueInstant: instantSchema.optional(), - _defaultValueInstant: elementSchema.optional(), - defaultValueInteger: intSchema.optional(), - defaultValueMarkdown: markdownSchema.optional(), - _defaultValueMarkdown: elementSchema.optional(), - defaultValueOid: stringSchema.optional(), - _defaultValueOid: elementSchema.optional(), - defaultValuePositiveInt: positiveIntSchema.optional(), - defaultValueString: stringSchema.optional(), - _defaultValueString: elementSchema.optional(), - defaultValueTime: timeSchema.optional(), - _defaultValueTime: elementSchema.optional(), - defaultValueUnsignedInt: intSchema.optional(), - defaultValueUri: urlSchema.optional(), - _defaultValueUri: elementSchema.optional(), - defaultValueUrl: urlSchema.optional(), - _defaultValueUrl: elementSchema.optional(), - defaultValueUuid: stringSchema.optional(), - _defaultValueUuid: elementSchema.optional(), - element: stringSchema.optional(), - _element: elementSchema.optional(), - listMode: structureMapSourceListModeSchema.optional(), - _listMode: elementSchema.optional(), - variable: stringSchema.optional(), - _variable: elementSchema.optional(), - condition: stringSchema.optional(), - _condition: elementSchema.optional(), - check: stringSchema.optional(), - _check: elementSchema.optional(), - logMessage: stringSchema.optional(), - _logMessage: elementSchema.optional(), - }) - -const structureMapGroupRuleTargetParameterSchema: ZodType = - backboneElementSchema.extend({ - valueId: idSchema.optional(), - _valueId: elementSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueInteger: intSchema.optional(), - valueDecimal: decimalSchema.optional(), - }) - -const structureMapGroupRuleTargetSchema: ZodType = - backboneElementSchema.extend({ - context: stringSchema.optional(), - _context: elementSchema.optional(), - contextType: structureMapTargetContextTypeSchema.optional(), - _contextType: elementSchema.optional(), - element: stringSchema.optional(), - _element: elementSchema.optional(), - variable: stringSchema.optional(), - _variable: elementSchema.optional(), - listMode: structureMapTargetListModeSchema.array().optional(), - _listMode: elementSchema.array().optional(), - listRuleId: stringSchema.optional(), - _listRuleId: elementSchema.optional(), - transform: structureMapTransformSchema.optional(), - _transform: elementSchema.optional(), - parameter: structureMapGroupRuleTargetParameterSchema.array().optional(), - }) - -const structureMapGroupRuleDependentSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - variable: stringSchema.array(), - _variable: elementSchema.array().optional(), - }) - -const structureMapGroupRuleSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - source: structureMapGroupRuleSourceSchema.array(), - target: structureMapGroupRuleTargetSchema.array().optional(), - get rule() { - return structureMapGroupRuleSchema.array().optional() - }, - dependent: structureMapGroupRuleDependentSchema.array().optional(), - documentation: stringSchema.optional(), - _documentation: elementSchema.optional(), - }) - -const structureMapStructureSchema: ZodType = - backboneElementSchema.extend({ - url: urlSchema, - _url: elementSchema.optional(), - mode: structureMapModelModeSchema, - _mode: elementSchema.optional(), - alias: stringSchema.optional(), - _alias: elementSchema.optional(), - documentation: stringSchema.optional(), - _documentation: elementSchema.optional(), - }) - -const structureMapGroupInputSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - type: stringSchema.optional(), - _type: elementSchema.optional(), - mode: structureMapGroupInputModeSchema, - _mode: elementSchema.optional(), - documentation: stringSchema.optional(), - _documentation: elementSchema.optional(), - }) - -const structureMapGroupSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - extends: stringSchema.optional(), - _extends: elementSchema.optional(), - typeMode: structureMapGroupTypeModeSchema, - _typeMode: elementSchema.optional(), - documentation: stringSchema.optional(), - _documentation: elementSchema.optional(), - input: structureMapGroupInputSchema.array(), - rule: structureMapGroupRuleSchema.array(), - }) - -/** - * Zod schema for FHIR StructureMap resource (untyped version). - */ -export const untypedStructureMapSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('StructureMap').readonly(), - url: urlSchema, - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema, - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - purpose: markdownSchema.optional(), - _purpose: elementSchema.optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - structure: structureMapStructureSchema.array().optional(), - import: urlSchema.array().optional(), - _import: elementSchema.array().optional(), - group: structureMapGroupSchema.array(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR StructureMap resource. - */ -export const structureMapSchema: ZodType = - untypedStructureMapSchema - -/** - * Wrapper class for FHIR StructureMap resources. - * Provides utility methods for working with structure maps that define transformations between data structures. - */ -export class FhirStructureMap extends FhirDomainResource { - // Static Functions - - /** - * Parses a StructureMap resource from unknown data. - * - * @param value - The data to parse and validate against the StructureMap schema - * @returns A FhirStructureMap instance containing the validated resource - */ - public static parse(value: unknown): FhirStructureMap { - return new FhirStructureMap(structureMapSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/subscription.ts b/packages/fhir/src/resources/subscription.ts deleted file mode 100644 index 79301e13..00000000 --- a/packages/fhir/src/resources/subscription.ts +++ /dev/null @@ -1,81 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type SubscriptionChannel, type Subscription } from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - contactPointSchema, - domainResourceSchema, - elementSchema, - instantSchema, - stringSchema, - urlSchema, -} from '../elements/index.js' -import { - subscriptionResourceStatusSchema, - subscriptionChannelTypeSchema, -} from '../valueSets/index.js' - -const subscriptionChannelSchema: ZodType = - backboneElementSchema.extend({ - type: subscriptionChannelTypeSchema, - _type: elementSchema.optional(), - endpoint: urlSchema.optional(), - _endpoint: elementSchema.optional(), - payload: stringSchema.optional(), - _payload: elementSchema.optional(), - header: stringSchema.array().optional(), - _header: elementSchema.array().optional(), - }) - -/** - * Zod schema for FHIR Subscription resource (untyped version). - */ -export const untypedSubscriptionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Subscription').readonly(), - status: subscriptionResourceStatusSchema, - _status: elementSchema.optional(), - contact: contactPointSchema.array().optional(), - end: instantSchema.optional(), - _end: elementSchema.optional(), - reason: stringSchema, - _reason: elementSchema.optional(), - criteria: stringSchema, - _criteria: elementSchema.optional(), - error: stringSchema.optional(), - _error: elementSchema.optional(), - channel: subscriptionChannelSchema, - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Subscription resource. - */ -export const subscriptionSchema: ZodType = - untypedSubscriptionSchema - -/** - * Wrapper class for FHIR Subscription resources. - * Provides utility methods for working with subscriptions that describe server push notifications for specific events. - */ -export class FhirSubscription extends FhirDomainResource { - // Static Functions - - /** - * Parses a Subscription resource from unknown data. - * - * @param value - The data to parse and validate against the Subscription schema - * @returns A FhirSubscription instance containing the validated resource - */ - public static parse(value: unknown): FhirSubscription { - return new FhirSubscription(subscriptionSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/subscriptionStatus.ts b/packages/fhir/src/resources/subscriptionStatus.ts deleted file mode 100644 index 5d08be44..00000000 --- a/packages/fhir/src/resources/subscriptionStatus.ts +++ /dev/null @@ -1,84 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type SubscriptionStatusNotificationEvent, - type SubscriptionStatus, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - domainResourceSchema, - elementSchema, - instantSchema, - referenceSchema, - stringSchema, - urlSchema, -} from '../elements/index.js' -import { - subscriptionStatusStatusSchema, - subscriptionStatusTypeSchema, -} from '../valueSets/index.js' - -const subscriptionStatusNotificationEventSchema: ZodType = - backboneElementSchema.extend({ - additionalContext: referenceSchema.array().optional(), - eventNumber: stringSchema, - _eventNumber: elementSchema.optional(), - focus: referenceSchema.optional(), - timestamp: instantSchema.optional(), - _timestamp: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR SubscriptionStatus resource (untyped version). - */ -export const untypedSubscriptionStatusSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('SubscriptionStatus').readonly(), - error: codeableConceptSchema.array().optional(), - eventsSinceSubscriptionStart: stringSchema.optional(), - _eventsSinceSubscriptionStart: elementSchema.optional(), - notificationEvent: subscriptionStatusNotificationEventSchema - .array() - .optional(), - status: subscriptionStatusStatusSchema.optional(), - _status: elementSchema.optional(), - subscription: referenceSchema, - topic: urlSchema.optional(), - _topic: elementSchema.optional(), - type: subscriptionStatusTypeSchema, - _type: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR SubscriptionStatus resource. - */ -export const subscriptionStatusSchema: ZodType = - untypedSubscriptionStatusSchema - -/** - * Wrapper class for FHIR SubscriptionStatus resources. - * Provides utility methods for working with subscription status information and notification bundles. - */ -export class FhirSubscriptionStatus extends FhirDomainResource { - // Static Functions - - /** - * Parses a SubscriptionStatus resource from unknown data. - * - * @param value - The data to parse and validate against the SubscriptionStatus schema - * @returns A FhirSubscriptionStatus instance containing the validated resource - */ - public static parse(value: unknown): FhirSubscriptionStatus { - return new FhirSubscriptionStatus(subscriptionStatusSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/subscriptionTopic.ts b/packages/fhir/src/resources/subscriptionTopic.ts deleted file mode 100644 index b66250d0..00000000 --- a/packages/fhir/src/resources/subscriptionTopic.ts +++ /dev/null @@ -1,222 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type SubscriptionTopicCanFilterBy, - type SubscriptionTopicEventTrigger, - type SubscriptionTopicNotificationShape, - type SubscriptionTopicResourceTrigger, - type SubscriptionTopicResourceTriggerQueryCriteria, - type SubscriptionTopic, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactDetailSchema, - dateSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - markdownSchema, - periodSchema, - stringSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - publicationStatusSchema, - subscriptionTopicCanFilterByModifierSchema, - subscriptionTopicResourceTriggerInteractionSchema, - subscriptionTopicResourceTriggerQueryCriteriaResultSchema, -} from '../valueSets/index.js' - -const subscriptionTopicCanFilterBySchema: ZodType = - backboneElementSchema.extend({ - description: markdownSchema.optional(), - _description: elementSchema.optional(), - filterDefinition: urlSchema.optional(), - _filterDefinition: elementSchema.optional(), - filterParameter: stringSchema, - _filterParameter: elementSchema.optional(), - modifier: subscriptionTopicCanFilterByModifierSchema.array().optional(), - _modifier: elementSchema.array().optional(), - resource: urlSchema.optional(), - _resource: elementSchema.optional(), - }) - -const subscriptionTopicEventTriggerSchema: ZodType = - backboneElementSchema.extend({ - description: markdownSchema.optional(), - _description: elementSchema.optional(), - event: codeableConceptSchema, - resource: urlSchema, - _resource: elementSchema.optional(), - }) - -const subscriptionTopicNotificationShapeSchema: ZodType = - backboneElementSchema.extend({ - include: stringSchema.array().optional(), - _include: elementSchema.array().optional(), - resource: urlSchema, - _resource: elementSchema.optional(), - revInclude: stringSchema.array().optional(), - _revInclude: elementSchema.array().optional(), - }) - -const subscriptionTopicResourceTriggerQueryCriteriaSchema: ZodType = - backboneElementSchema.extend({ - current: stringSchema.optional(), - _current: elementSchema.optional(), - previous: stringSchema.optional(), - _previous: elementSchema.optional(), - requireBoth: booleanSchema.optional(), - _requireBoth: elementSchema.optional(), - resultForCreate: - subscriptionTopicResourceTriggerQueryCriteriaResultSchema.optional(), - _resultForCreate: elementSchema.optional(), - resultForDelete: - subscriptionTopicResourceTriggerQueryCriteriaResultSchema.optional(), - _resultForDelete: elementSchema.optional(), - }) - -const subscriptionTopicResourceTriggerSchema: ZodType = - backboneElementSchema.extend({ - description: markdownSchema.optional(), - _description: elementSchema.optional(), - fhirPathCriteria: stringSchema.optional(), - _fhirPathCriteria: elementSchema.optional(), - queryCriteria: - subscriptionTopicResourceTriggerQueryCriteriaSchema.optional(), - resource: urlSchema, - _resource: elementSchema.optional(), - supportedInteraction: subscriptionTopicResourceTriggerInteractionSchema - .array() - .optional(), - _supportedInteraction: elementSchema.array().optional(), - }) - -/** - * Zod schema for FHIR SubscriptionTopic resource (untyped version). - */ -export const untypedSubscriptionTopicSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('SubscriptionTopic').readonly(), - approvalDate: dateSchema.optional(), - _approvalDate: elementSchema.optional(), - canFilterBy: subscriptionTopicCanFilterBySchema.array().optional(), - contact: contactDetailSchema.array().optional(), - copyright: markdownSchema.optional(), - _copyright: elementSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - derivedFrom: urlSchema.array().optional(), - _derivedFrom: elementSchema.array().optional(), - description: markdownSchema.optional(), - _description: elementSchema.optional(), - effectivePeriod: periodSchema.optional(), - eventTrigger: subscriptionTopicEventTriggerSchema.array().optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: elementSchema.optional(), - notificationShape: subscriptionTopicNotificationShapeSchema - .array() - .optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - purpose: markdownSchema.optional(), - _purpose: elementSchema.optional(), - resourceTrigger: subscriptionTopicResourceTriggerSchema.array().optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - url: urlSchema, - _url: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR SubscriptionTopic resource. - */ -export const subscriptionTopicSchema: ZodType = - untypedSubscriptionTopicSchema - -/** - * Wrapper class for FHIR SubscriptionTopic resources. - * Provides utility methods for working with subscription topics that define events for pub/sub notification patterns. - */ -export class FhirSubscriptionTopic extends FhirDomainResource { - // Static Functions - - /** - * Parses a SubscriptionTopic resource from unknown data. - * - * @param value - The data to parse and validate against the SubscriptionTopic schema - * @returns A FhirSubscriptionTopic instance containing the validated resource - */ - public static parse(value: unknown): FhirSubscriptionTopic { - return new FhirSubscriptionTopic(subscriptionTopicSchema.parse(value)) - } - - /** - * Retrieves all identifier values matching any of the specified system URIs. - * - * @param system - One or more system URIs to filter identifiers by - * @returns Array of identifier values from matching systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Retrieves the first identifier value matching any of the specified system URIs. - * - * @param system - One or more system URIs to filter identifiers by - * @returns The first matching identifier value, or undefined if none found - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Retrieves all identifier values matching any of the specified type codings. - * - * @param type - One or more Coding objects representing identifier types - * @returns Array of identifier values from matching types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Retrieves the first identifier value matching any of the specified type codings. - * - * @param type - One or more Coding objects representing identifier types - * @returns The first matching identifier value, or undefined if none found - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/substance.ts b/packages/fhir/src/resources/substance.ts deleted file mode 100644 index fe39ae9d..00000000 --- a/packages/fhir/src/resources/substance.ts +++ /dev/null @@ -1,138 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type SubstanceIngredient, - type SubstanceInstance, - type Substance, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - quantitySchema, - ratioSchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { substanceStatusSchema } from '../valueSets/index.js' - -const substanceInstanceSchema: ZodType = - backboneElementSchema.extend({ - identifier: identifierSchema.optional(), - expiry: dateTimeSchema.optional(), - _expiry: elementSchema.optional(), - quantity: quantitySchema.optional(), - }) - -const substanceIngredientSchema: ZodType = - backboneElementSchema.extend({ - quantity: ratioSchema.optional(), - substanceCodeableConcept: codeableConceptSchema.optional(), - substanceReference: referenceSchema.optional(), - }) - -/** - * Zod schema for FHIR Substance resource (untyped version). - */ -export const untypedSubstanceSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Substance').readonly(), - identifier: identifierSchema.array().optional(), - status: substanceStatusSchema.optional(), - _status: elementSchema.optional(), - category: codeableConceptSchema.array().optional(), - code: codeableConceptSchema, - description: stringSchema.optional(), - _description: elementSchema.optional(), - instance: substanceInstanceSchema.array().optional(), - ingredient: substanceIngredientSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Substance resource. - */ -export const substanceSchema: ZodType = untypedSubstanceSchema - -/** - * Wrapper class for FHIR Substance resources. - * Provides utility methods for working with substance information. - */ -export class FhirSubstance extends FhirDomainResource { - /** - * Parses a Substance resource from unknown data. - * - * @param value - The data to parse and validate against the Substance schema - * @returns A FhirSubstance instance containing the validated resource - */ - public static parse(value: unknown): FhirSubstance { - return new FhirSubstance(substanceSchema.parse(value)) - } - - /** - * Gets the substance code display text. - * - * @returns The code display text, if available - */ - public get codeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.code) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided Coding - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/substanceDefinition.ts b/packages/fhir/src/resources/substanceDefinition.ts deleted file mode 100644 index 89c08944..00000000 --- a/packages/fhir/src/resources/substanceDefinition.ts +++ /dev/null @@ -1,266 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type SubstanceDefinition, - type SubstanceDefinitionName, - type SubstanceDefinitionMoiety, - type SubstanceDefinitionProperty, - type SubstanceDefinitionRelationship, - type SubstanceDefinitionSourceMaterial, - type SubstanceDefinitionMolecularWeight, - type SubstanceDefinitionStructure, - type SubstanceDefinitionStructureRepresentation, - type SubstanceDefinitionNameOfficial, - type SubstanceDefinitionCode, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - quantitySchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' - -const substanceDefinitionCodeSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema.optional(), - status: codeableConceptSchema.optional(), - statusDate: stringSchema.optional(), - _statusDate: elementSchema.optional(), - note: annotationSchema.array().optional(), - source: referenceSchema.array().optional(), - }) - -const substanceDefinitionNameOfficialSchema: ZodType = - backboneElementSchema.extend({ - authority: codeableConceptSchema.optional(), - status: codeableConceptSchema.optional(), - date: stringSchema.optional(), - _date: elementSchema.optional(), - }) - -const substanceDefinitionNameSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - type: codeableConceptSchema.optional(), - status: codeableConceptSchema.optional(), - preferred: z.boolean().optional(), - _preferred: elementSchema.optional(), - language: codeableConceptSchema.array().optional(), - domain: codeableConceptSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - get synonym() { - return substanceDefinitionNameSchema.array().optional() - }, - get translation() { - return substanceDefinitionNameSchema.array().optional() - }, - official: substanceDefinitionNameOfficialSchema.array().optional(), - source: referenceSchema.array().optional(), - }) - -const substanceDefinitionMoietySchema: ZodType = - backboneElementSchema.extend({ - role: codeableConceptSchema.optional(), - identifier: identifierSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - stereochemistry: codeableConceptSchema.optional(), - opticalActivity: codeableConceptSchema.optional(), - molecularFormula: stringSchema.optional(), - _molecularFormula: elementSchema.optional(), - amountQuantity: elementSchema.optional(), - amountString: stringSchema.optional(), - _amountString: elementSchema.optional(), - measurementType: codeableConceptSchema.optional(), - }) - -const substanceDefinitionPropertySchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema, - valueCodeableConcept: codeableConceptSchema.optional(), - valueQuantity: elementSchema.optional(), - valueDate: stringSchema.optional(), - _valueDate: elementSchema.optional(), - valueBoolean: z.boolean().optional(), - _valueBoolean: elementSchema.optional(), - valueAttachment: elementSchema.optional(), - }) - -const substanceDefinitionRelationshipSchema: ZodType = - backboneElementSchema.extend({ - substanceDefinitionReference: referenceSchema.optional(), - substanceDefinitionCodeableConcept: codeableConceptSchema.optional(), - type: codeableConceptSchema, - isDefining: booleanSchema.optional(), - _isDefining: elementSchema.optional(), - amountQuantity: elementSchema.optional(), - amountRatio: elementSchema.optional(), - amountString: stringSchema.optional(), - _amountString: elementSchema.optional(), - ratioHighLimitAmount: elementSchema.optional(), - comparator: codeableConceptSchema.optional(), - source: referenceSchema.array().optional(), - }) - -const substanceDefinitionSourceMaterialSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - genus: codeableConceptSchema.optional(), - species: codeableConceptSchema.optional(), - part: codeableConceptSchema.optional(), - countryOfOrigin: codeableConceptSchema.array().optional(), - }) - -const substanceDefinitionMolecularWeightSchema: ZodType = - backboneElementSchema.extend({ - method: codeableConceptSchema.optional(), - type: codeableConceptSchema.optional(), - amount: quantitySchema, - }) - -const substanceDefinitionStructureRepresentationSchema: ZodType = - backboneElementSchema.extend({ - type: codeableConceptSchema.optional(), - representation: stringSchema.optional(), - _representation: elementSchema.optional(), - format: codeableConceptSchema.optional(), - document: referenceSchema.optional(), - }) - -const substanceDefinitionStructureSchema: ZodType = - backboneElementSchema.extend({ - stereochemistry: codeableConceptSchema.optional(), - opticalActivity: codeableConceptSchema.optional(), - molecularFormula: stringSchema.optional(), - _molecularFormula: elementSchema.optional(), - molecularFormulaByMoiety: stringSchema.optional(), - _molecularFormulaByMoiety: elementSchema.optional(), - molecularWeight: substanceDefinitionMolecularWeightSchema.optional(), - technique: codeableConceptSchema.array().optional(), - sourceDocument: referenceSchema.array().optional(), - representation: substanceDefinitionStructureRepresentationSchema - .array() - .optional(), - }) - -/** - * Zod schema for FHIR SubstanceDefinition resource (untyped version). - */ -export const untypedSubstanceDefinitionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('SubstanceDefinition').readonly(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - status: codeableConceptSchema.optional(), - classification: codeableConceptSchema.array().optional(), - domain: codeableConceptSchema.optional(), - grade: codeableConceptSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - informationSource: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - manufacturer: referenceSchema.array().optional(), - supplier: referenceSchema.array().optional(), - moiety: substanceDefinitionMoietySchema.array().optional(), - property: substanceDefinitionPropertySchema.array().optional(), - referenceInformation: referenceSchema.optional(), - molecularWeight: substanceDefinitionMolecularWeightSchema - .array() - .optional(), - structure: substanceDefinitionStructureSchema.optional(), - code: substanceDefinitionCodeSchema.array().optional(), - name: substanceDefinitionNameSchema.array().optional(), - relationship: substanceDefinitionRelationshipSchema.array().optional(), - nucleicAcid: referenceSchema.optional(), - polymer: referenceSchema.optional(), - protein: referenceSchema.optional(), - sourceMaterial: substanceDefinitionSourceMaterialSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR SubstanceDefinition resource. - */ -export const substanceDefinitionSchema: ZodType = - untypedSubstanceDefinitionSchema - -/** - * Wrapper class for FHIR SubstanceDefinition resources. - * Provides utility methods for working with substance definitions containing detailed information about the composition and properties of substances. - */ -export class FhirSubstanceDefinition extends FhirDomainResource { - // Static Functions - - /** - * Parses a SubstanceDefinition resource from unknown data. - * - * @param value - The data to parse and validate against the SubstanceDefinition schema - * @returns A FhirSubstanceDefinition instance containing the validated resource - */ - public static parse(value: unknown): FhirSubstanceDefinition { - return new FhirSubstanceDefinition(substanceDefinitionSchema.parse(value)) - } - - /** - * Retrieves all identifier values matching any of the specified system URIs. - * - * @param system - One or more system URIs to filter identifiers by - * @returns Array of identifier values from matching systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Retrieves the first identifier value matching any of the specified system URIs. - * - * @param system - One or more system URIs to filter identifiers by - * @returns The first matching identifier value, or undefined if none found - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Retrieves all identifier values matching any of the specified type codings. - * - * @param type - One or more Coding objects representing identifier types - * @returns Array of identifier values from matching types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Retrieves the first identifier value matching any of the specified type codings. - * - * @param type - One or more Coding objects representing identifier types - * @returns The first matching identifier value, or undefined if none found - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/supplyDelivery.ts b/packages/fhir/src/resources/supplyDelivery.ts deleted file mode 100644 index db6861b6..00000000 --- a/packages/fhir/src/resources/supplyDelivery.ts +++ /dev/null @@ -1,148 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type SupplyDelivery, - type SupplyDeliverySuppliedItem, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - quantitySchema, - referenceSchema, - timingSchema, -} from '../elements/index.js' -import { supplyDeliveryStatusSchema } from '../valueSets/index.js' - -/** - * Zod schema for FHIR SupplyDeliverySuppliedItem backbone element. - */ -const supplyDeliverySuppliedItemSchema: ZodType = - backboneElementSchema.extend({ - quantity: quantitySchema.optional(), - itemCodeableConcept: codeableConceptSchema.optional(), - itemReference: referenceSchema.optional(), - }) - -/** - * Untyped Zod schema for FHIR SupplyDelivery resource (for lazy evaluation). - */ -export const untypedSupplyDeliverySchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('SupplyDelivery').readonly(), - identifier: identifierSchema.array().optional(), - basedOn: referenceSchema.array().optional(), - partOf: referenceSchema.array().optional(), - status: supplyDeliveryStatusSchema.optional(), - _status: elementSchema.optional(), - patient: referenceSchema.optional(), - type: codeableConceptSchema.optional(), - suppliedItem: supplyDeliverySuppliedItemSchema.optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: elementSchema.optional(), - occurrencePeriod: periodSchema.optional(), - occurrenceTiming: timingSchema.optional(), - supplier: referenceSchema.optional(), - destination: referenceSchema.optional(), - receiver: referenceSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR SupplyDelivery resource. - */ -export const supplyDeliverySchema: ZodType = - untypedSupplyDeliverySchema - -/** - * Wrapper class for FHIR SupplyDelivery resources. - * Provides utility methods for working with supply deliveries that record the delivery of supplies. - */ -export class FhirSupplyDelivery extends FhirDomainResource { - /** - * Parses a SupplyDelivery resource from unknown data. - * - * @param value - The data to parse and validate against the SupplyDelivery schema - * @returns A FhirSupplyDelivery instance containing the validated resource - */ - public static parse(value: unknown): FhirSupplyDelivery { - return new FhirSupplyDelivery(supplyDeliverySchema.parse(value)) - } - - /** - * Get the occurrence date/time. - * - * @returns The occurrence date/time, or undefined if not set - */ - public get occurrenceDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.occurrenceDateTime) - } - - /** - * Get the type of supply delivery as display text. - * - * @returns The type display text, or undefined if not set - */ - public get typeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.type) - } - - /** - * Retrieves all identifier values matching any of the specified system URIs. - * - * @param system - One or more system URIs to filter identifiers by - * @returns Array of identifier values from matching systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Retrieves the first identifier value matching any of the specified system URIs. - * - * @param system - One or more system URIs to filter identifiers by - * @returns The first matching identifier value, or undefined if none found - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Retrieves all identifier values matching any of the specified type codings. - * - * @param type - One or more Coding objects representing identifier types - * @returns Array of identifier values from matching types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Retrieves the first identifier value matching any of the specified type codings. - * - * @param type - One or more Coding objects representing identifier types - * @returns The first matching identifier value, or undefined if none found - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/supplyRequest.ts b/packages/fhir/src/resources/supplyRequest.ts deleted file mode 100644 index 038bdd04..00000000 --- a/packages/fhir/src/resources/supplyRequest.ts +++ /dev/null @@ -1,160 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type SupplyRequest, - type SupplyRequestParameter, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - quantitySchema, - rangeSchema, - referenceSchema, - timingSchema, -} from '../elements/index.js' -import { - requestPrioritySchema, - supplyRequestStatusSchema, -} from '../valueSets/index.js' - -const supplyRequestParameterSchema: ZodType = - backboneElementSchema.extend({ - code: codeableConceptSchema.optional(), - valueCodeableConcept: codeableConceptSchema.optional(), - valueQuantity: quantitySchema.optional(), - valueRange: rangeSchema.optional(), - valueBoolean: booleanSchema.optional(), - _value: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR SupplyRequest resource (untyped version). - */ -export const untypedSupplyRequestSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('SupplyRequest').readonly(), - identifier: identifierSchema.array().optional(), - status: supplyRequestStatusSchema.optional(), - _status: elementSchema.optional(), - category: codeableConceptSchema.optional(), - priority: requestPrioritySchema.optional(), - _priority: elementSchema.optional(), - itemCodeableConcept: codeableConceptSchema.optional(), - itemReference: referenceSchema.optional(), - quantity: quantitySchema, - parameter: supplyRequestParameterSchema.array().optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: elementSchema.optional(), - occurrencePeriod: periodSchema.optional(), - occurrenceTiming: timingSchema.optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: elementSchema.optional(), - requester: referenceSchema.optional(), - supplier: referenceSchema.array().optional(), - reasonCode: codeableConceptSchema.array().optional(), - reasonReference: referenceSchema.array().optional(), - deliverFrom: referenceSchema.optional(), - deliverTo: referenceSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR SupplyRequest resource. - */ -export const supplyRequestSchema: ZodType = - untypedSupplyRequestSchema - -/** - * Wrapper class for FHIR SupplyRequest resources. - * Provides utility methods for working with supply requests that record requests for supplies to be ordered or delivered. - */ -export class FhirSupplyRequest extends FhirDomainResource { - /** - * Parses a SupplyRequest resource from unknown data. - * - * @param value - The data to parse and validate against the SupplyRequest schema - * @returns A FhirSupplyRequest instance containing the validated resource - */ - public static parse(value: unknown): FhirSupplyRequest { - return new FhirSupplyRequest(supplyRequestSchema.parse(value)) - } - - /** - * Get the date the request was authored. - * - * @returns The authored date, or undefined if not set - */ - public get authoredDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.authoredOn) - } - - /** - * Get the occurrence date/time. - * - * @returns The occurrence date/time, or undefined if not set - */ - public get occurrenceDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.occurrenceDateTime) - } - - /** - * Retrieves all identifier values matching any of the specified system URIs. - * - * @param system - One or more system URIs to filter identifiers by - * @returns Array of identifier values from matching systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Retrieves the first identifier value matching any of the specified system URIs. - * - * @param system - One or more system URIs to filter identifiers by - * @returns The first matching identifier value, or undefined if none found - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Retrieves all identifier values matching any of the specified type codings. - * - * @param type - One or more Coding objects representing identifier types - * @returns Array of identifier values from matching types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Retrieves the first identifier value matching any of the specified type codings. - * - * @param type - One or more Coding objects representing identifier types - * @returns The first matching identifier value, or undefined if none found - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/task.ts b/packages/fhir/src/resources/task.ts deleted file mode 100644 index 134452f2..00000000 --- a/packages/fhir/src/resources/task.ts +++ /dev/null @@ -1,186 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type TaskInput, - type TaskOutput, - type TaskRestriction, - type Task, - type Coding, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { anyValueSchema } from '../elements/anyValueSchema.js' -import { - annotationSchema, - backboneElementSchema, - canonicalSchema, - codeableConceptSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - periodSchema, - positiveIntSchema, - referenceSchema, - stringSchema, - uriSchema, -} from '../elements/index.js' -import { - requestPrioritySchema, - taskIntentSchema, - taskStatusSchema, -} from '../valueSets/index.js' - -const taskRestrictionSchema: ZodType = - backboneElementSchema.extend({ - repetitions: positiveIntSchema.optional(), - period: periodSchema.optional(), - recipient: referenceSchema.array().optional(), - }) - -const taskInputSchema: ZodType = anyValueSchema.extend({ - type: codeableConceptSchema, -}) - -const taskOutputSchema: ZodType = anyValueSchema.extend({ - type: codeableConceptSchema, -}) - -/** - * Zod schema for FHIR Task resource (untyped version). - */ -export const untypedTaskSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('Task').readonly(), - identifier: identifierSchema.array().optional(), - instantiatesCanonical: canonicalSchema.optional(), - _instantiatesCanonical: elementSchema.optional(), - instantiatesUri: uriSchema.optional(), - _instantiatesUri: elementSchema.optional(), - basedOn: referenceSchema.array().optional(), - groupIdentifier: identifierSchema.optional(), - partOf: referenceSchema.array().optional(), - status: taskStatusSchema, - _status: elementSchema.optional(), - statusReason: codeableConceptSchema.optional(), - businessStatus: codeableConceptSchema.optional(), - intent: taskIntentSchema, - _intent: elementSchema.optional(), - priority: requestPrioritySchema.optional(), - _priority: elementSchema.optional(), - code: codeableConceptSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - focus: referenceSchema.optional(), - for: referenceSchema.optional(), - encounter: referenceSchema.optional(), - executionPeriod: periodSchema.optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: elementSchema.optional(), - lastModified: dateTimeSchema.optional(), - _lastModified: elementSchema.optional(), - requester: referenceSchema.optional(), - performerType: codeableConceptSchema.array().optional(), - owner: referenceSchema.optional(), - location: referenceSchema.optional(), - reasonCode: codeableConceptSchema.optional(), - reasonReference: referenceSchema.optional(), - insurance: referenceSchema.array().optional(), - note: annotationSchema.array().optional(), - relevantHistory: referenceSchema.array().optional(), - restriction: taskRestrictionSchema.optional(), - input: taskInputSchema.array().optional(), - output: taskOutputSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR Task resource. - */ -export const taskSchema: ZodType = untypedTaskSchema - -/** - * Wrapper class for FHIR Task resources. - * Provides utility methods for working with tasks that describe activities to be performed. - */ -export class FhirTask extends FhirDomainResource { - /** - * Parses a Task resource from unknown data. - * - * @param value - The data to parse and validate against the Task schema - * @returns A FhirTask instance containing the validated resource - */ - public static parse(value: unknown): FhirTask { - return new FhirTask(taskSchema.parse(value)) - } - - /** - * Gets the authored date as a JavaScript Date object. - * - * @returns The parsed authored date, or undefined if not set - */ - public get authoredDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.authoredOn) - } - - /** - * Gets the display text from the code field. - * - * @returns The code display text, or undefined if not set - */ - public get codeDisplay(): string | undefined { - return FhirDomainResource.codeableConceptDisplay(this.value.code) - } - - /** - * Retrieves all identifier values matching any of the specified system URIs. - * - * @param system - One or more system URIs to filter identifiers by - * @returns Array of identifier values from matching systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Retrieves the first identifier value matching any of the specified system URIs. - * - * @param system - One or more system URIs to filter identifiers by - * @returns The first matching identifier value, or undefined if none found - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Retrieves all identifier values matching any of the specified type codings. - * - * @param type - One or more Coding objects representing identifier types - * @returns Array of identifier values from matching types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Retrieves the first identifier value matching any of the specified type codings. - * - * @param type - One or more Coding objects representing identifier types - * @returns The first matching identifier value, or undefined if none found - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/terminologyCapabilities.ts b/packages/fhir/src/resources/terminologyCapabilities.ts deleted file mode 100644 index a2cb41c2..00000000 --- a/packages/fhir/src/resources/terminologyCapabilities.ts +++ /dev/null @@ -1,204 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type TerminologyCapabilitiesClosure, - type TerminologyCapabilitiesCodeSystem, - type TerminologyCapabilitiesCodeSystemVersion, - type TerminologyCapabilitiesCodeSystemVersionFilter, - type TerminologyCapabilitiesExpansion, - type TerminologyCapabilitiesExpansionParameter, - type TerminologyCapabilitiesImplementation, - type TerminologyCapabilitiesSoftware, - type TerminologyCapabilitiesTranslation, - type TerminologyCapabilitiesValidateCode, - type TerminologyCapabilities, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - contactDetailSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - stringSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - capabilityStatementKindSchema, - codeSearchSupportSchema, - publicationStatusSchema, -} from '../valueSets/index.js' - -const terminologyCapabilitiesSoftwareSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - }) - -const terminologyCapabilitiesImplementationSchema: ZodType = - backboneElementSchema.extend({ - description: stringSchema, - _description: elementSchema.optional(), - url: urlSchema.optional(), - _url: elementSchema.optional(), - }) - -const terminologyCapabilitiesCodeSystemVersionFilterSchema: ZodType = - backboneElementSchema.extend({ - code: stringSchema, - _code: elementSchema.optional(), - op: stringSchema.array(), - _op: elementSchema.array().optional(), - }) - -const terminologyCapabilitiesCodeSystemVersionSchema: ZodType = - backboneElementSchema.extend({ - code: stringSchema.optional(), - _code: elementSchema.optional(), - isDefault: booleanSchema.optional(), - _isDefault: elementSchema.optional(), - compositional: booleanSchema.optional(), - _compositional: elementSchema.optional(), - language: stringSchema.array().optional(), - _language: elementSchema.array().optional(), - filter: terminologyCapabilitiesCodeSystemVersionFilterSchema - .array() - .optional(), - property: stringSchema.array().optional(), - _property: elementSchema.array().optional(), - }) - -const terminologyCapabilitiesCodeSystemSchema: ZodType = - backboneElementSchema.extend({ - uri: urlSchema.optional(), - _uri: elementSchema.optional(), - version: terminologyCapabilitiesCodeSystemVersionSchema.array().optional(), - subsumption: booleanSchema.optional(), - _subsumption: elementSchema.optional(), - }) - -const terminologyCapabilitiesExpansionParameterSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - documentation: stringSchema.optional(), - _documentation: elementSchema.optional(), - }) - -const terminologyCapabilitiesExpansionSchema: ZodType = - backboneElementSchema.extend({ - hierarchical: booleanSchema.optional(), - _hierarchical: elementSchema.optional(), - paging: booleanSchema.optional(), - _paging: elementSchema.optional(), - incomplete: booleanSchema.optional(), - _incomplete: elementSchema.optional(), - parameter: terminologyCapabilitiesExpansionParameterSchema - .array() - .optional(), - textFilter: stringSchema.optional(), - _textFilter: elementSchema.optional(), - }) - -const terminologyCapabilitiesValidateCodeSchema: ZodType = - backboneElementSchema.extend({ - translations: booleanSchema, - _translations: elementSchema.optional(), - }) - -const terminologyCapabilitiesTranslationSchema: ZodType = - backboneElementSchema.extend({ - needsMap: booleanSchema, - _needsMap: elementSchema.optional(), - }) - -const terminologyCapabilitiesClosureSchema: ZodType = - backboneElementSchema.extend({ - translation: booleanSchema.optional(), - _translation: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR TerminologyCapabilities resource (untyped version). - */ -export const untypedTerminologyCapabilitiesSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('TerminologyCapabilities').readonly(), - url: urlSchema.optional(), - _url: elementSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - date: dateTimeSchema, - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - purpose: stringSchema.optional(), - _purpose: elementSchema.optional(), - copyright: stringSchema.optional(), - _copyright: elementSchema.optional(), - kind: capabilityStatementKindSchema, - _kind: elementSchema.optional(), - software: terminologyCapabilitiesSoftwareSchema.optional(), - implementation: terminologyCapabilitiesImplementationSchema.optional(), - lockedDate: booleanSchema.optional(), - _lockedDate: elementSchema.optional(), - codeSystem: terminologyCapabilitiesCodeSystemSchema.array().optional(), - expansion: terminologyCapabilitiesExpansionSchema.optional(), - codeSearch: codeSearchSupportSchema.optional(), - _codeSearch: elementSchema.optional(), - validateCode: terminologyCapabilitiesValidateCodeSchema.optional(), - translation: terminologyCapabilitiesTranslationSchema.optional(), - closure: terminologyCapabilitiesClosureSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR TerminologyCapabilities resource. - */ -export const terminologyCapabilitiesSchema: ZodType = - untypedTerminologyCapabilitiesSchema - -/** - * Wrapper class for FHIR TerminologyCapabilities resources. - * Provides utility methods for working with terminology capabilities that describe the capabilities of a terminology service. - */ -export class FhirTerminologyCapabilities extends FhirDomainResource { - // Static Functions - - /** - * Parses a TerminologyCapabilities resource from unknown data. - * - * @param value - The data to parse and validate against the TerminologyCapabilities schema - * @returns A FhirTerminologyCapabilities instance containing the validated resource - */ - public static parse(value: unknown): FhirTerminologyCapabilities { - return new FhirTerminologyCapabilities( - terminologyCapabilitiesSchema.parse(value), - ) - } -} diff --git a/packages/fhir/src/resources/testReport.ts b/packages/fhir/src/resources/testReport.ts deleted file mode 100644 index 543880a4..00000000 --- a/packages/fhir/src/resources/testReport.ts +++ /dev/null @@ -1,155 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type TestReportParticipant, - type TestReportSetup, - type TestReportSetupAction, - type TestReportSetupActionAssert, - type TestReportSetupActionOperation, - type TestReportTeardown, - type TestReportTeardownAction, - type TestReportTest, - type TestReportTestAction, - type TestReport, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - decimalSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - instantSchema, - referenceSchema, - stringSchema, - urlSchema, -} from '../elements/index.js' -import { - testReportActionResultSchema, - testReportParticipantTypeSchema, - testReportResultSchema, - testReportStatusSchema, -} from '../valueSets/index.js' - -const testReportSetupActionOperationSchema: ZodType = - backboneElementSchema.extend({ - detail: urlSchema.optional(), - _detail: elementSchema.optional(), - message: stringSchema.optional(), - _message: elementSchema.optional(), - result: testReportActionResultSchema, - _result: elementSchema.optional(), - }) - -const testReportSetupActionAssertSchema: ZodType = - backboneElementSchema.extend({ - detail: urlSchema.optional(), - _detail: elementSchema.optional(), - message: stringSchema.optional(), - _message: elementSchema.optional(), - result: testReportActionResultSchema, - _result: elementSchema.optional(), - }) - -const testReportParticipantSchema: ZodType = - backboneElementSchema.extend({ - display: stringSchema.optional(), - _display: elementSchema.optional(), - type: testReportParticipantTypeSchema, - _type: elementSchema.optional(), - uri: urlSchema, - _uri: elementSchema.optional(), - }) - -const testReportSetupActionSchema: ZodType = - backboneElementSchema.extend({ - assert: testReportSetupActionAssertSchema.optional(), - operation: testReportSetupActionOperationSchema.optional(), - }) - -const testReportSetupSchema: ZodType = - backboneElementSchema.extend({ - action: testReportSetupActionSchema.array(), - }) - -const testReportTeardownActionSchema: ZodType = - backboneElementSchema.extend({ - operation: testReportSetupActionOperationSchema, - }) - -const testReportTeardownSchema: ZodType = - backboneElementSchema.extend({ - action: testReportTeardownActionSchema.array(), - }) - -const testReportTestActionSchema: ZodType = - backboneElementSchema.extend({ - assert: testReportSetupActionAssertSchema.optional(), - operation: testReportSetupActionOperationSchema.optional(), - }) - -const testReportTestSchema: ZodType = - backboneElementSchema.extend({ - action: testReportTestActionSchema.array(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - }) - -/** - * Zod schema for FHIR TestReport resource (untyped version). - */ -export const untypedTestReportSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('TestReport').readonly(), - identifier: identifierSchema.optional(), - issued: instantSchema.optional(), - _issued: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - participant: testReportParticipantSchema.array().optional(), - result: testReportResultSchema, - _result: elementSchema.optional(), - score: decimalSchema.optional(), - _score: elementSchema.optional(), - setup: testReportSetupSchema.optional(), - status: testReportStatusSchema, - _status: elementSchema.optional(), - teardown: testReportTeardownSchema.optional(), - test: testReportTestSchema.array().optional(), - tester: stringSchema.optional(), - _tester: elementSchema.optional(), - testScript: referenceSchema, - }), -) satisfies ZodType - -/** - * Zod schema for FHIR TestReport resource. - */ -export const testReportSchema: ZodType = untypedTestReportSchema - -/** - * Wrapper class for FHIR TestReport resources. - * Provides utility methods for working with test reports that describe the results of executing a TestScript. - */ -export class FhirTestReport extends FhirDomainResource { - // Static Functions - - /** - * Parses a TestReport resource from unknown data. - * - * @param value - The data to parse and validate against the TestReport schema - * @returns A FhirTestReport instance containing the validated resource - */ - public static parse(value: unknown): FhirTestReport { - return new FhirTestReport(testReportSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/testScript.ts b/packages/fhir/src/resources/testScript.ts deleted file mode 100644 index e27b3d1b..00000000 --- a/packages/fhir/src/resources/testScript.ts +++ /dev/null @@ -1,327 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type TestScriptDestination, - type TestScriptMetadata, - type TestScriptOrigin, - type TestScript, - type TestScriptMetadataLink, - type TestScriptMetadataCapability, - type TestScriptFixture, - type TestScriptVariable, - type TestScriptSetup, - type TestScriptSetupAction, - type TestScriptSetupActionOperation, - type TestScriptSetupActionAssert, - type TestScriptSetupActionOperationRequestHeader, - type TestScriptTest, - type TestScriptTeardown, - type TestScriptTeardownAction, - type TestScriptTestAction, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - codingSchema, - contactDetailSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - intSchema, - referenceSchema, - stringSchema, - urlSchema, - uriSchema, - usageContextSchema, -} from '../elements/index.js' -import { - assertDirectionSchema, - assertOperatorSchema, - assertResponseCodeSchema, - fhirDefinedTypeSchema, - mimeTypeSchema, - publicationStatusSchema, - testScriptRequestMethodSchema, -} from '../valueSets/index.js' - -const testScriptOriginSchema: ZodType = - backboneElementSchema.extend({ - index: intSchema, - profile: codingSchema, - }) - -const testScriptDestinationSchema: ZodType = - backboneElementSchema.extend({ - index: intSchema, - profile: codingSchema, - }) - -const testScriptMetadataLinkSchema: ZodType = - backboneElementSchema.extend({ - url: uriSchema, - _url: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - }) - -const testScriptMetadataCapabilitySchema: ZodType = - backboneElementSchema.extend({ - required: booleanSchema, - _required: elementSchema.optional(), - validated: booleanSchema, - _validated: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - origin: intSchema.array().optional(), - _origin: elementSchema.array().optional(), - destination: intSchema.optional(), - _destination: elementSchema.optional(), - link: uriSchema.array().optional(), - _link: elementSchema.array().optional(), - capabilities: urlSchema, - _capabilities: elementSchema.optional(), - }) - -const testScriptMetadataSchema: ZodType = - backboneElementSchema.extend({ - link: testScriptMetadataLinkSchema.array().optional(), - capability: testScriptMetadataCapabilitySchema.array(), - }) - -const testScriptFixtureSchema: ZodType = - backboneElementSchema.extend({ - autocreate: booleanSchema, - _autocreate: elementSchema.optional(), - autodelete: booleanSchema, - _autodelete: elementSchema.optional(), - resource: referenceSchema.optional(), - }) - -const testScriptVariableSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - defaultValue: stringSchema.optional(), - _defaultValue: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - expression: stringSchema.optional(), - _expression: elementSchema.optional(), - headerField: stringSchema.optional(), - _headerField: elementSchema.optional(), - hint: stringSchema.optional(), - _hint: elementSchema.optional(), - path: stringSchema.optional(), - _path: elementSchema.optional(), - sourceId: stringSchema.optional(), - _sourceId: elementSchema.optional(), - }) - -const testScriptSetupActionOperationRequestHeader: ZodType = - backboneElementSchema.extend({ - field: stringSchema, - _field: elementSchema.optional(), - value: stringSchema, - _value: elementSchema.optional(), - }) - -const testScriptSetupActionOperationSchema: ZodType = - backboneElementSchema.extend({ - type: codingSchema.optional(), - resource: fhirDefinedTypeSchema.optional(), - _resource: elementSchema.optional(), - label: stringSchema.optional(), - _label: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - accept: stringSchema.optional(), - _accept: elementSchema.optional(), - contentType: mimeTypeSchema.optional(), - _contentType: elementSchema.optional(), - destination: intSchema.optional(), - _destination: elementSchema.optional(), - encodeRequestUrl: booleanSchema, - _encodeRequestUrl: elementSchema.optional(), - method: testScriptRequestMethodSchema.optional(), - _method: elementSchema.optional(), - origin: intSchema.optional(), - _origin: elementSchema.optional(), - params: stringSchema.optional(), - _params: elementSchema.optional(), - requestHeader: testScriptSetupActionOperationRequestHeader - .array() - .optional(), - requestId: stringSchema.optional(), - _requestId: elementSchema.optional(), - responseId: stringSchema.optional(), - _responseId: elementSchema.optional(), - sourceId: stringSchema.optional(), - _sourceId: elementSchema.optional(), - targetId: stringSchema.optional(), - _targetId: elementSchema.optional(), - url: stringSchema.optional(), - _url: elementSchema.optional(), - }) - -const testScriptSetupActionAssertSchema: ZodType = - backboneElementSchema.extend({ - label: stringSchema.optional(), - _label: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - direction: assertDirectionSchema.optional(), - _direction: elementSchema.optional(), - compareToSourceId: stringSchema.optional(), - _compareToSourceId: elementSchema.optional(), - compareToSourceExpression: stringSchema.optional(), - _compareToSourceExpression: elementSchema.optional(), - compareToSourcePath: stringSchema.optional(), - _compareToSourcePath: elementSchema.optional(), - contentType: mimeTypeSchema.optional(), - _contentType: elementSchema.optional(), - expression: stringSchema.optional(), - _expression: elementSchema.optional(), - headerField: stringSchema.optional(), - _headerField: elementSchema.optional(), - minimumId: stringSchema.optional(), - _minimumId: elementSchema.optional(), - navigationLinks: booleanSchema.optional(), - _navigationLinks: elementSchema.optional(), - operator: assertOperatorSchema.optional(), - _operator: elementSchema.optional(), - path: stringSchema.optional(), - _path: elementSchema.optional(), - requestMethod: testScriptRequestMethodSchema.optional(), - _requestMethod: elementSchema.optional(), - requestURL: stringSchema.optional(), - _requestURL: elementSchema.optional(), - resource: fhirDefinedTypeSchema.optional(), - _resource: elementSchema.optional(), - response: assertResponseCodeSchema.optional(), - _response: elementSchema.optional(), - responseCode: stringSchema.optional(), - _responseCode: elementSchema.optional(), - sourceId: stringSchema.optional(), - _sourceId: elementSchema.optional(), - stopTestOnFail: booleanSchema, - _stopTestOnFail: elementSchema.optional(), - validateProfileId: stringSchema.optional(), - _validateProfileId: elementSchema.optional(), - value: stringSchema.optional(), - _value: elementSchema.optional(), - warningOnly: booleanSchema, - _warningOnly: elementSchema.optional(), - }) - -const testScriptSetupActionSchema: ZodType = - backboneElementSchema.extend({ - operation: testScriptSetupActionOperationSchema.optional(), - assert: testScriptSetupActionAssertSchema.optional(), - }) - -const testScriptSetupSchema: ZodType = - backboneElementSchema.extend({ - action: testScriptSetupActionSchema.array(), - }) - -const testScriptTestActionSchema: ZodType = - backboneElementSchema.extend({ - operation: testScriptSetupActionOperationSchema.optional(), - assert: testScriptSetupActionAssertSchema.optional(), - }) - -const testScriptTestSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema.optional(), - _name: elementSchema.optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - action: testScriptTestActionSchema.array(), - }) - -const testScriptTeardownActionSchema: ZodType = - backboneElementSchema.extend({ - operation: testScriptSetupActionOperationSchema, - }) - -const testScriptTeardownSchema: ZodType = - backboneElementSchema.extend({ - action: testScriptTeardownActionSchema.array(), - }) - -/** - * Zod schema for FHIR TestScript resource (untyped version). - */ -export const untypedTestScriptSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('TestScript').readonly(), - url: urlSchema, - _url: elementSchema.optional(), - identifier: identifierSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema, - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - purpose: stringSchema.optional(), - _purpose: elementSchema.optional(), - copyright: stringSchema.optional(), - _copyright: elementSchema.optional(), - origin: testScriptOriginSchema.array().optional(), - destination: testScriptDestinationSchema.array().optional(), - metadata: testScriptMetadataSchema.optional(), - fixture: testScriptFixtureSchema.array().optional(), - profile: referenceSchema.array().optional(), - variable: testScriptVariableSchema.array().optional(), - setup: testScriptSetupSchema.optional(), - test: testScriptTestSchema.array().optional(), - teardown: testScriptTeardownSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR TestScript resource. - */ -export const testScriptSchema: ZodType = untypedTestScriptSchema - -/** - * Wrapper class for FHIR TestScript resources. - * Provides utility methods for working with test scripts that define automated test sequences for validating FHIR server implementations. - */ -export class FhirTestScript extends FhirDomainResource { - // Static Functions - - /** - * Parses a TestScript resource from unknown data. - * - * @param value - The data to parse and validate against the TestScript schema - * @returns A FhirTestScript instance containing the validated resource - */ - public static parse(value: unknown): FhirTestScript { - return new FhirTestScript(testScriptSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/valueSet.ts b/packages/fhir/src/resources/valueSet.ts deleted file mode 100644 index 7f8c4fac..00000000 --- a/packages/fhir/src/resources/valueSet.ts +++ /dev/null @@ -1,254 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type ValueSetComposeIncludeConceptDesignation, - type ValueSet, - type ValueSetExpansionContains, - type Coding, - type ValueSetCompose, - type ValueSetExpansion, - type ValueSetExpansionParameter, - type ValueSetComposeInclude, - type ValueSetComposeIncludeConcept, - type ValueSetComposeIncludeFilter, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - booleanSchema, - codeableConceptSchema, - codingSchema, - contactDetailSchema, - dateTimeSchema, - decimalSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - intSchema, - stringSchema, - urlSchema, - usageContextSchema, -} from '../elements/index.js' -import { - filterOperatorSchema, - publicationStatusSchema, -} from '../valueSets/index.js' - -const valueSetComposeIncludeConceptDesignationSchema: ZodType = - backboneElementSchema.extend({ - language: stringSchema.optional(), - _language: elementSchema.optional(), - use: codingSchema.optional(), - value: stringSchema, - _value: elementSchema.optional(), - }) - -const valueSetExpansionContainsSchema: ZodType = - backboneElementSchema.extend({ - system: urlSchema.optional(), - _system: elementSchema.optional(), - abstract: booleanSchema.optional(), - _abstract: elementSchema.optional(), - inactive: booleanSchema.optional(), - _inactive: elementSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - code: stringSchema.optional(), - _code: elementSchema.optional(), - display: stringSchema.optional(), - _display: elementSchema.optional(), - designation: valueSetComposeIncludeConceptDesignationSchema - .array() - .optional(), - get contains() { - return valueSetExpansionContainsSchema.array().optional() - }, - }) - -const valueSetComposeIncludeConceptSchema: ZodType = - backboneElementSchema.extend({ - code: stringSchema, - _code: elementSchema.optional(), - display: stringSchema.optional(), - _display: elementSchema.optional(), - designation: valueSetComposeIncludeConceptDesignationSchema - .array() - .optional(), - }) - -const valueSetComposeIncludeFilterSchema: ZodType = - backboneElementSchema.extend({ - property: stringSchema, - _property: elementSchema.optional(), - op: filterOperatorSchema, - _op: elementSchema.optional(), - value: stringSchema, - _value: elementSchema.optional(), - }) - -const valueSetComposeIncludeSchema: ZodType = - backboneElementSchema.extend({ - system: urlSchema.optional(), - _system: elementSchema.optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - concept: valueSetComposeIncludeConceptSchema.array().optional(), - filter: valueSetComposeIncludeFilterSchema.array().optional(), - valueSet: urlSchema.array().optional(), - _valueSet: elementSchema.array().optional(), - }) - -const valueSetComposeSchema: ZodType = - backboneElementSchema.extend({ - lockedDate: dateTimeSchema.optional(), - _lockedDate: elementSchema.optional(), - inactive: booleanSchema.optional(), - _inactive: elementSchema.optional(), - include: valueSetComposeIncludeSchema.array(), - exclude: valueSetComposeIncludeSchema.array().optional(), - }) - -const valueSetExpansionParameterSchema: ZodType = - backboneElementSchema.extend({ - name: stringSchema, - _name: elementSchema.optional(), - valueString: stringSchema.optional(), - _valueString: elementSchema.optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: elementSchema.optional(), - valueInteger: intSchema.optional(), - valueDecimal: decimalSchema.optional(), - valueUri: urlSchema.optional(), - _valueUri: elementSchema.optional(), - valueCode: stringSchema.optional(), - _valueCode: elementSchema.optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: elementSchema.optional(), - }) - -const valueSetExpansionSchema: ZodType = - backboneElementSchema.extend({ - identifier: urlSchema.optional(), - _identifier: elementSchema.optional(), - timestamp: dateTimeSchema, - _timestamp: elementSchema.optional(), - total: intSchema.optional(), - offset: intSchema.optional(), - parameter: valueSetExpansionParameterSchema.array().optional(), - contains: valueSetExpansionContainsSchema.array().optional(), - }) - -/** - * Zod schema for FHIR ValueSet resource (untyped version). - */ -export const untypedValueSetSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('ValueSet').readonly(), - url: urlSchema.optional(), - _url: elementSchema.optional(), - identifier: identifierSchema.array().optional(), - version: stringSchema.optional(), - _version: elementSchema.optional(), - name: stringSchema.optional(), - _name: elementSchema.optional(), - title: stringSchema.optional(), - _title: elementSchema.optional(), - status: publicationStatusSchema, - _status: elementSchema.optional(), - experimental: booleanSchema.optional(), - _experimental: elementSchema.optional(), - date: dateTimeSchema.optional(), - _date: elementSchema.optional(), - publisher: stringSchema.optional(), - _publisher: elementSchema.optional(), - contact: contactDetailSchema.array().optional(), - description: stringSchema.optional(), - _description: elementSchema.optional(), - useContext: usageContextSchema.array().optional(), - jurisdiction: codeableConceptSchema.array().optional(), - immutable: booleanSchema.optional(), - _immutable: elementSchema.optional(), - purpose: stringSchema.optional(), - _purpose: elementSchema.optional(), - copyright: stringSchema.optional(), - _copyright: elementSchema.optional(), - compose: valueSetComposeSchema.optional(), - expansion: valueSetExpansionSchema.optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR ValueSet resource. - */ -export const valueSetSchema: ZodType = untypedValueSetSchema - -/** - * Wrapper class for FHIR ValueSet resources. - * Provides utility methods for working with value sets that define sets of coded values for use in specific contexts. - */ -export class FhirValueSet extends FhirDomainResource { - // Static Functions - - /** - * Parses a ValueSet resource from unknown data. - * - * @param value - The data to parse and validate against the ValueSet schema - * @returns A FhirValueSet instance containing the validated resource - */ - public static parse(value: unknown): FhirValueSet { - return new FhirValueSet(valueSetSchema.parse(value)) - } - - /** - * Retrieves all identifier values matching any of the specified system URIs. - * - * @param system - One or more system URIs to filter identifiers by - * @returns Array of identifier values from matching systems - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Retrieves the first identifier value matching any of the specified system URIs. - * - * @param system - One or more system URIs to filter identifiers by - * @returns The first matching identifier value, or undefined if none found - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Retrieves all identifier values matching any of the specified type codings. - * - * @param type - One or more Coding objects representing identifier types - * @returns Array of identifier values from matching types - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Retrieves the first identifier value matching any of the specified type codings. - * - * @param type - One or more Coding objects representing identifier types - * @returns The first matching identifier value, or undefined if none found - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/resources/verificationResult.ts b/packages/fhir/src/resources/verificationResult.ts deleted file mode 100644 index d269ceea..00000000 --- a/packages/fhir/src/resources/verificationResult.ts +++ /dev/null @@ -1,116 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type VerificationResultAttestation, - type VerificationResultPrimarySource, - type VerificationResultValidator, - type VerificationResult, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - backboneElementSchema, - codeableConceptSchema, - dateSchema, - dateTimeSchema, - domainResourceSchema, - elementSchema, - referenceSchema, - signatureSchema, - stringSchema, - timingSchema, -} from '../elements/index.js' -import { verificationResultStatusSchema } from '../valueSets/index.js' - -const verificationResultPrimarySourceSchema: ZodType = - backboneElementSchema.extend({ - who: referenceSchema.optional(), - type: codeableConceptSchema.array().optional(), - communicationMethod: codeableConceptSchema.array().optional(), - validationStatus: codeableConceptSchema.optional(), - validationDate: dateTimeSchema.optional(), - _validationDate: elementSchema.optional(), - canPushUpdates: codeableConceptSchema.optional(), - pushTypeAvailable: codeableConceptSchema.array().optional(), - }) - -const verificationResultAttestationSchema: ZodType = - backboneElementSchema.extend({ - who: referenceSchema.optional(), - onBehalfOf: referenceSchema.optional(), - communicationMethod: codeableConceptSchema.optional(), - date: dateSchema.optional(), - _date: elementSchema.optional(), - sourceIdentityCertificate: stringSchema.optional(), - _sourceIdentityCertificate: elementSchema.optional(), - proxyIdentityCertificate: stringSchema.optional(), - _proxyIdentityCertificate: elementSchema.optional(), - proxySignature: signatureSchema.optional(), - sourceSignature: signatureSchema.optional(), - }) - -const verificationResultValidatorSchema: ZodType = - backboneElementSchema.extend({ - organization: referenceSchema, - identityCertificate: stringSchema.optional(), - _identityCertificate: elementSchema.optional(), - attestationSignature: signatureSchema.optional(), - }) - -/** - * Zod schema for FHIR VerificationResult resource (untyped version). - */ -export const untypedVerificationResultSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('VerificationResult').readonly(), - target: referenceSchema.array().optional(), - targetLocation: stringSchema.array().optional(), - _targetLocation: elementSchema.array().optional(), - need: codeableConceptSchema.optional(), - status: verificationResultStatusSchema, - _status: elementSchema.optional(), - statusDate: dateTimeSchema.optional(), - _statusDate: elementSchema.optional(), - validationType: codeableConceptSchema.optional(), - validationProcess: codeableConceptSchema.array().optional(), - frequency: timingSchema.optional(), - lastPerformed: dateTimeSchema.optional(), - _lastPerformed: elementSchema.optional(), - nextScheduled: dateSchema.optional(), - _nextScheduled: elementSchema.optional(), - failureAction: codeableConceptSchema.optional(), - primarySource: verificationResultPrimarySourceSchema.array().optional(), - attestation: verificationResultAttestationSchema.optional(), - validator: verificationResultValidatorSchema.array().optional(), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR VerificationResult resource. - */ -export const verificationResultSchema: ZodType = - untypedVerificationResultSchema - -/** - * Wrapper class for FHIR VerificationResult resources. - * Provides utility methods for working with verification results that describe validation/verification of information. - */ -export class FhirVerificationResult extends FhirDomainResource { - // Static Functions - - /** - * Parses a VerificationResult resource from unknown data. - * - * @param value - The data to parse and validate against the VerificationResult schema - * @returns A FhirVerificationResult instance containing the validated resource - */ - public static parse(value: unknown): FhirVerificationResult { - return new FhirVerificationResult(verificationResultSchema.parse(value)) - } -} diff --git a/packages/fhir/src/resources/visionPrescription.ts b/packages/fhir/src/resources/visionPrescription.ts deleted file mode 100644 index 2e56206d..00000000 --- a/packages/fhir/src/resources/visionPrescription.ts +++ /dev/null @@ -1,187 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Coding, - type VisionPrescription, - type VisionPrescriptionLensSpecification, - type VisionPrescriptionLensSpecificationPrism, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' -import { FhirDomainResource } from './fhirDomainResource.js' -import { - annotationSchema, - backboneElementSchema, - codeableConceptSchema, - dateTimeSchema, - decimalSchema, - domainResourceSchema, - elementSchema, - identifierSchema, - intSchema, - quantitySchema, - referenceSchema, - stringSchema, -} from '../elements/index.js' -import { - visionBaseSchema, - visionEyesSchema, - visionPrescriptionStatusSchema, -} from '../valueSets/index.js' - -const visionPrescriptionLensSpecificationPrismSchema: ZodType = - backboneElementSchema.extend({ - amount: decimalSchema, - _amount: elementSchema.optional(), - base: visionBaseSchema, - _base: elementSchema.optional(), - }) - -const visionPrescriptionLensSpecificationSchema: ZodType = - backboneElementSchema.extend({ - product: codeableConceptSchema, - eye: visionEyesSchema, - _eye: elementSchema.optional(), - sphere: decimalSchema.optional(), - _sphere: elementSchema.optional(), - cylinder: decimalSchema.optional(), - _cylinder: elementSchema.optional(), - axis: intSchema.optional(), - _axis: elementSchema.optional(), - prism: visionPrescriptionLensSpecificationPrismSchema.array().optional(), - add: decimalSchema.optional(), - _add: elementSchema.optional(), - power: decimalSchema.optional(), - _power: elementSchema.optional(), - backCurve: decimalSchema.optional(), - _backCurve: elementSchema.optional(), - diameter: decimalSchema.optional(), - _diameter: elementSchema.optional(), - duration: quantitySchema.optional(), - color: stringSchema.optional(), - _color: elementSchema.optional(), - brand: stringSchema.optional(), - _brand: elementSchema.optional(), - note: annotationSchema.array().optional(), - }) - -/** - * Zod schema for FHIR VisionPrescription resource (untyped version). - */ -export const untypedVisionPrescriptionSchema = z.lazy(() => - domainResourceSchema.extend({ - resourceType: z.literal('VisionPrescription').readonly(), - identifier: identifierSchema.array().optional(), - status: visionPrescriptionStatusSchema, - _status: elementSchema.optional(), - created: dateTimeSchema, - _created: elementSchema.optional(), - patient: referenceSchema, - encounter: referenceSchema.optional(), - dateWritten: dateTimeSchema, - _dateWritten: elementSchema.optional(), - prescriber: referenceSchema, - lensSpecification: visionPrescriptionLensSpecificationSchema.array().min(1), - }), -) satisfies ZodType - -/** - * Zod schema for FHIR VisionPrescription resource. - */ -export const visionPrescriptionSchema: ZodType = - untypedVisionPrescriptionSchema - -/** - * Wrapper class for FHIR VisionPrescription resources. - * Provides utility methods for working with vision prescriptions for corrective lenses. - */ -export class FhirVisionPrescription extends FhirDomainResource { - // Static Functions - - /** - * Parses a VisionPrescription resource from unknown data. - * - * @param value - The data to parse and validate against the VisionPrescription schema - * @returns A FhirVisionPrescription instance containing the validated resource - */ - public static parse(value: unknown): FhirVisionPrescription { - return new FhirVisionPrescription(visionPrescriptionSchema.parse(value)) - } - - /** - * Get the created date as a Date object. - * @returns The created date - */ - public get createdDate(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.created) - } - - /** - * Get the date written as a Date object. - * @returns The date written - */ - public get dateWritten(): Date | undefined { - return FhirDomainResource.parseDateTime(this.value.dateWritten) - } - - /** - * Get all product displays from lens specifications. - * @returns Array of product display texts - */ - public get productDisplays(): string[] { - return FhirDomainResource.codeableConceptDisplays( - this.value.lensSpecification.map((spec) => spec.product), - ) - } - - /** - * Gets all identifier values whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns Array of identifier values matching any provided system - */ - public identifiersBySystem(...system: string[]): string[] { - return FhirDomainResource.identifiersBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets the first identifier value whose system matches any of the provided system URLs. - * - * @param system - One or more identifier system URLs to match - * @returns The first matching identifier value, or undefined if none match - */ - public identifierBySystem(...system: string[]): string | undefined { - return FhirDomainResource.identifierBySystem( - this.value.identifier, - ...system, - ) - } - - /** - * Gets all identifier values whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns Array of identifier values matching any provided type - */ - public identifiersByType(...type: Coding[]): string[] { - return FhirDomainResource.identifiersByType(this.value.identifier, ...type) - } - - /** - * Gets the first identifier value whose type matches any of the provided Coding filters. - * - * @param type - One or more Coding filters to match against Identifier.type - * @returns The first matching identifier value, or undefined if none match - */ - public identifierByType(...type: Coding[]): string | undefined { - return FhirDomainResource.identifierByType(this.value.identifier, ...type) - } -} diff --git a/packages/fhir/src/valueSets/account.ts b/packages/fhir/src/valueSets/account.ts deleted file mode 100644 index a96dd9f3..00000000 --- a/packages/fhir/src/valueSets/account.ts +++ /dev/null @@ -1,27 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Indicates whether the account is available to be used. - * http://hl7.org/fhir/valueset-account-status.html - */ -export const accountStatusSchema = z.enum([ - 'active', - 'inactive', - 'entered-in-error', - 'on-hold', - 'unknown', -]) - -/** - * Indicates whether the account is available to be used. - * http://hl7.org/fhir/valueset-account-status.html - */ -export type AccountStatus = z.infer diff --git a/packages/fhir/src/valueSets/actionParticipantType.ts b/packages/fhir/src/valueSets/actionParticipantType.ts deleted file mode 100644 index 389cf7ea..00000000 --- a/packages/fhir/src/valueSets/actionParticipantType.ts +++ /dev/null @@ -1,26 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The type of participant for the action. - * http://hl7.org/fhir/ValueSet/action-participant-type - */ -export const actionParticipantTypeSchema = z.enum([ - 'patient', - 'practitioner', - 'related-person', - 'device', -]) - -/** - * The type of participant for the action. - * http://hl7.org/fhir/ValueSet/action-participant-type - */ -export type ActionParticipantType = z.infer diff --git a/packages/fhir/src/valueSets/administrableProductDefinition.ts b/packages/fhir/src/valueSets/administrableProductDefinition.ts deleted file mode 100644 index 65e27f7b..00000000 --- a/packages/fhir/src/valueSets/administrableProductDefinition.ts +++ /dev/null @@ -1,28 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Status of administrable product definition. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export const administrableProductDefinitionStatusSchema = z.enum([ - 'draft', - 'active', - 'retired', - 'unknown', -]) - -/** - * Status of administrable product definition. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export type AdministrableProductDefinitionStatus = z.infer< - typeof administrableProductDefinitionStatusSchema -> diff --git a/packages/fhir/src/valueSets/administrativeGender.ts b/packages/fhir/src/valueSets/administrativeGender.ts deleted file mode 100644 index 04d09fd0..00000000 --- a/packages/fhir/src/valueSets/administrativeGender.ts +++ /dev/null @@ -1,26 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The gender of a person used for administrative purposes. - * http://hl7.org/fhir/valueset-administrative-gender.html - */ -export const administrativeGenderSchema = z.enum([ - 'male', - 'female', - 'other', - 'unknown', -]) - -/** - * The gender of a person used for administrative purposes. - * http://hl7.org/fhir/valueset-administrative-gender.html - */ -export type AdministrativeGender = z.infer diff --git a/packages/fhir/src/valueSets/adverseEvent.ts b/packages/fhir/src/valueSets/adverseEvent.ts deleted file mode 100644 index 9a229c5e..00000000 --- a/packages/fhir/src/valueSets/adverseEvent.ts +++ /dev/null @@ -1,21 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Overall nature of the adverse event, e.g. real or potential. - * http://hl7.org/fhir/valueset-adverse-event-actuality.html - */ -export const adverseEventActualitySchema = z.enum(['actual', 'potential']) - -/** - * Overall nature of the adverse event, e.g. real or potential. - * http://hl7.org/fhir/valueset-adverse-event-actuality.html - */ -export type AdverseEventActuality = z.infer diff --git a/packages/fhir/src/valueSets/allergyIntolerance.ts b/packages/fhir/src/valueSets/allergyIntolerance.ts deleted file mode 100644 index bf89c8a9..00000000 --- a/packages/fhir/src/valueSets/allergyIntolerance.ts +++ /dev/null @@ -1,78 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Category of an identified substance associated with allergies or intolerances. - * http://hl7.org/fhir/valueset-allergy-intolerance-category.html - */ -export const allergyIntoleranceCategorySchema = z.enum([ - 'food', - 'medication', - 'environment', - 'biologic', -]) - -/** - * Category of an identified substance associated with allergies or intolerances. - * http://hl7.org/fhir/valueset-allergy-intolerance-category.html - */ -export type AllergyIntoleranceCategory = z.infer< - typeof allergyIntoleranceCategorySchema -> - -/** - * Estimate of the potential clinical harm, or seriousness, of a reaction to an identified substance. - * http://hl7.org/fhir/valueset-allergy-intolerance-criticality.html - */ -export const allergyIntoleranceCriticalitySchema = z.enum([ - 'low', - 'high', - 'unable-to-assess', -]) - -/** - * Estimate of the potential clinical harm, or seriousness, of a reaction to an identified substance. - * http://hl7.org/fhir/valueset-allergy-intolerance-criticality.html - */ -export type AllergyIntoleranceCriticality = z.infer< - typeof allergyIntoleranceCriticalitySchema -> - -/** - * Clinical assessment of the severity of a reaction event as a whole. - * http://hl7.org/fhir/valueset-reaction-event-severity.html - */ -export const allergyIntoleranceReactionSeveritySchema = z.enum([ - 'mild', - 'moderate', - 'severe', -]) - -/** - * Clinical assessment of the severity of a reaction event as a whole. - * http://hl7.org/fhir/valueset-reaction-event-severity.html - */ -export type AllergyIntoleranceReactionSeverity = z.infer< - typeof allergyIntoleranceReactionSeveritySchema -> - -/** - * Identification of the underlying physiological mechanism for a Reaction Risk. - * http://hl7.org/fhir/valueset-allergy-intolerance-type.html - */ -export const allergyIntoleranceTypeSchema = z.enum(['allergy', 'intolerance']) - -/** - * Identification of the underlying physiological mechanism for a Reaction Risk. - * http://hl7.org/fhir/valueset-allergy-intolerance-type.html - */ -export type AllergyIntoleranceType = z.infer< - typeof allergyIntoleranceTypeSchema -> diff --git a/packages/fhir/src/valueSets/appointment.ts b/packages/fhir/src/valueSets/appointment.ts deleted file mode 100644 index c5464ff8..00000000 --- a/packages/fhir/src/valueSets/appointment.ts +++ /dev/null @@ -1,69 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The free/busy status of an appointment. - * http://hl7.org/fhir/valueset-appointmentstatus.html - */ -export const appointmentStatusSchema = z.enum([ - 'proposed', - 'pending', - 'booked', - 'arrived', - 'fulfilled', - 'cancelled', - 'noshow', - 'entered-in-error', - 'checked-in', - 'waitlist', -]) - -/** - * The free/busy status of an appointment. - * http://hl7.org/fhir/valueset-appointmentstatus.html - */ -export type AppointmentStatus = z.infer - -/** - * Is the Participant required to attend the appointment. - * http://hl7.org/fhir/valueset-participantrequired.html - */ -export const appointmentParticipantRequiredSchema = z.enum([ - 'required', - 'optional', - 'information-only', -]) - -/** - * Is the Participant required to attend the appointment. - * http://hl7.org/fhir/valueset-participantrequired.html - */ -export type AppointmentParticipantRequired = z.infer< - typeof appointmentParticipantRequiredSchema -> - -/** - * Participation status of the appointment. - * http://hl7.org/fhir/valueset-participationstatus.html - */ -export const appointmentParticipantStatusSchema = z.enum([ - 'accepted', - 'declined', - 'tentative', - 'needs-action', -]) - -/** - * Participation status of the appointment. - * http://hl7.org/fhir/valueset-participationstatus.html - */ -export type AppointmentParticipantStatus = z.infer< - typeof appointmentParticipantStatusSchema -> diff --git a/packages/fhir/src/valueSets/appointmentResponse.ts b/packages/fhir/src/valueSets/appointmentResponse.ts deleted file mode 100644 index 2aa1d657..00000000 --- a/packages/fhir/src/valueSets/appointmentResponse.ts +++ /dev/null @@ -1,28 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Participation status of the appointment. - * http://hl7.org/fhir/valueset-participationstatus.html - */ -export const appointmentResponseParticipantStatusSchema = z.enum([ - 'accepted', - 'declined', - 'tentative', - 'needs-action', -]) - -/** - * Participation status of the appointment. - * http://hl7.org/fhir/valueset-participationstatus.html - */ -export type AppointmentResponseParticipantStatus = z.infer< - typeof appointmentResponseParticipantStatusSchema -> diff --git a/packages/fhir/src/valueSets/biologicallyDerivedProduct.ts b/packages/fhir/src/valueSets/biologicallyDerivedProduct.ts deleted file mode 100644 index 697960d9..00000000 --- a/packages/fhir/src/valueSets/biologicallyDerivedProduct.ts +++ /dev/null @@ -1,64 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Broad category of biologically derived product. - * http://hl7.org/fhir/valueset-product-category.html - */ -export const biologicallyDerivedProductCategorySchema = z.enum([ - 'organ', - 'tissue', - 'fluid', - 'cells', - 'biologicalAgent', -]) - -/** - * Broad category of biologically derived product. - * http://hl7.org/fhir/valueset-product-category.html - */ -export type BiologicallyDerivedProductCategory = z.infer< - typeof biologicallyDerivedProductCategorySchema -> - -/** - * Status of biologically derived product. - * http://hl7.org/fhir/valueset-product-status.html - */ -export const biologicallyDerivedProductStatusSchema = z.enum([ - 'available', - 'unavailable', -]) - -/** - * Status of biologically derived product. - * http://hl7.org/fhir/valueset-product-status.html - */ -export type BiologicallyDerivedProductStatus = z.infer< - typeof biologicallyDerivedProductStatusSchema -> - -/** - * Temperature scale used for storage. - * http://hl7.org/fhir/valueset-product-storage-scale.html - */ -export const biologicallyDerivedProductStorageScaleSchema = z.enum([ - 'farenheit', - 'celsius', - 'kelvin', -]) - -/** - * Temperature scale used for storage. - * http://hl7.org/fhir/valueset-product-storage-scale.html - */ -export type BiologicallyDerivedProductStorageScale = z.infer< - typeof biologicallyDerivedProductStorageScaleSchema -> diff --git a/packages/fhir/src/valueSets/bundle.ts b/packages/fhir/src/valueSets/bundle.ts deleted file mode 100644 index 49ffc2b5..00000000 --- a/packages/fhir/src/valueSets/bundle.ts +++ /dev/null @@ -1,68 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The type of a bundle. - * http://hl7.org/fhir/valueset-bundle-type.html - */ -export const bundleTypeSchema = z.enum([ - 'document', - 'message', - 'transaction', - 'transaction-response', - 'batch', - 'batch-response', - 'history', - 'searchset', - 'collection', -]) - -/** - * The type of a bundle. - * http://hl7.org/fhir/valueset-bundle-type.html - */ -export type BundleType = z.infer - -/** - * The search mode of an entry in a bundle. - * http://hl7.org/fhir/valueset-search-entry-mode.html - */ -export const bundleEntrySearchModeSchema = z.enum([ - 'match', - 'include', - 'outcome', -]) - -/** - * The search mode of an entry in a bundle. - * http://hl7.org/fhir/valueset-search-entry-mode.html - */ -export type BundleEntrySearchMode = z.infer - -/** - * HTTP verbs (in the HTTP command line). - * http://hl7.org/fhir/valueset-http-verb.html - */ -export const bundleEntryRequestMethodSchema = z.enum([ - 'GET', - 'HEAD', - 'POST', - 'PUT', - 'DELETE', - 'PATCH', -]) - -/** - * HTTP verbs (in the HTTP command line). - * http://hl7.org/fhir/valueset-http-verb.html - */ -export type BundleEntryRequestMethod = z.infer< - typeof bundleEntryRequestMethodSchema -> diff --git a/packages/fhir/src/valueSets/capabilityStatement.ts b/packages/fhir/src/valueSets/capabilityStatement.ts deleted file mode 100644 index 5f00ebb9..00000000 --- a/packages/fhir/src/valueSets/capabilityStatement.ts +++ /dev/null @@ -1,175 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The type of CapabilityStatement - * http://hl7.org/fhir/valueset-capability-statement-kind.html - */ -export const capabilityStatementKindSchema = z.enum([ - 'instance', - 'capability', - 'requirements', -]) - -/** - * The type of CapabilityStatement - * http://hl7.org/fhir/valueset-capability-statement-kind.html - */ -export type CapabilityStatementKind = z.infer< - typeof capabilityStatementKindSchema -> - -/** - * The mode of a RESTful capability statement - * http://hl7.org/fhir/valueset-restful-capability-mode.html - */ -export const restfulCapabilityModeSchema = z.enum(['client', 'server']) - -/** - * The mode of a RESTful capability statement - * http://hl7.org/fhir/valueset-restful-capability-mode.html - */ -export type RestfulCapabilityMode = z.infer - -/** - * Whether the application produces or consumes documents - * http://hl7.org/fhir/valueset-document-mode.html - */ -export const documentModeSchema = z.enum(['producer', 'consumer']) - -/** - * Whether the application produces or consumes documents - * http://hl7.org/fhir/valueset-document-mode.html - */ -export type DocumentMode = z.infer - -/** - * The mode of a message capability statement - * http://hl7.org/fhir/valueset-event-capability-mode.html - */ -export const eventCapabilityModeSchema = z.enum(['sender', 'receiver']) - -/** - * The mode of a message capability statement - * http://hl7.org/fhir/valueset-event-capability-mode.html - */ -export type EventCapabilityMode = z.infer - -/** - * Operations supported by REST at the system level - * http://hl7.org/fhir/valueset-system-restful-interaction.html - */ -export const systemRestfulInteractionSchema = z.enum([ - 'transaction', - 'batch', - 'search-system', - 'history-system', -]) - -/** - * Operations supported by REST at the system level - * http://hl7.org/fhir/valueset-system-restful-interaction.html - */ -export type SystemRestfulInteraction = z.infer< - typeof systemRestfulInteractionSchema -> - -/** - * How the system supports versioning for a resource - * http://hl7.org/fhir/valueset-versioning-policy.html - */ -export const resourceVersionPolicySchema = z.enum([ - 'no-version', - 'versioned', - 'versioned-update', -]) - -/** - * How the system supports versioning for a resource - * http://hl7.org/fhir/valueset-versioning-policy.html - */ -export type ResourceVersionPolicy = z.infer - -/** - * Code that indicates how the server supports conditional read - * http://hl7.org/fhir/valueset-conditional-read-status.html - */ -export const conditionalReadStatusSchema = z.enum([ - 'not-supported', - 'modified-since', - 'not-match', - 'full-support', -]) - -/** - * Code that indicates how the server supports conditional read - * http://hl7.org/fhir/valueset-conditional-read-status.html - */ -export type ConditionalReadStatus = z.infer - -/** - * Code that indicates how the server supports conditional delete - * http://hl7.org/fhir/valueset-conditional-delete-status.html - */ -export const conditionalDeleteStatusSchema = z.enum([ - 'not-supported', - 'single', - 'multiple', -]) - -/** - * Code that indicates how the server supports conditional delete - * http://hl7.org/fhir/valueset-conditional-delete-status.html - */ -export type ConditionalDeleteStatus = z.infer< - typeof conditionalDeleteStatusSchema -> - -/** - * Operations supported by REST at the resource instance level - * http://hl7.org/fhir/valueset-type-restful-interaction.html - */ -export const typeRestfulInteractionSchema = z.enum([ - 'read', - 'vread', - 'update', - 'patch', - 'delete', - 'history-instance', - 'history-type', - 'create', - 'search-type', -]) - -/** - * Operations supported by REST at the resource instance level - * http://hl7.org/fhir/valueset-type-restful-interaction.html - */ -export type TypeRestfulInteraction = z.infer< - typeof typeRestfulInteractionSchema -> - -/** - * How aggregated references are handled when matching - * http://hl7.org/fhir/valueset-reference-handling-policy.html - */ -export const referencePolicySchema = z.enum([ - 'literal', - 'logical', - 'resolves', - 'enforced', - 'local', -]) - -/** - * How aggregated references are handled when matching - * http://hl7.org/fhir/valueset-reference-handling-policy.html - */ -export type ReferencePolicy = z.infer diff --git a/packages/fhir/src/valueSets/carePlan.ts b/packages/fhir/src/valueSets/carePlan.ts deleted file mode 100644 index d0e9b97a..00000000 --- a/packages/fhir/src/valueSets/carePlan.ts +++ /dev/null @@ -1,91 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Indicates whether the plan is currently being acted upon, represents future intentions or is now a historical record. - * http://hl7.org/fhir/valueset-care-plan-status.html - */ -export const carePlanStatusSchema = z.enum([ - 'draft', - 'active', - 'on-hold', - 'revoked', - 'completed', - 'entered-in-error', - 'unknown', -]) - -/** - * Indicates whether the plan is currently being acted upon, represents future intentions or is now a historical record. - * http://hl7.org/fhir/valueset-care-plan-status.html - */ -export type CarePlanStatus = z.infer - -/** - * Codes indicating the degree of authority/intentionality associated with a care plan. - * http://hl7.org/fhir/valueset-care-plan-intent.html - */ -export const carePlanIntentSchema = z.enum([ - 'proposal', - 'plan', - 'order', - 'option', -]) - -/** - * Codes indicating the degree of authority/intentionality associated with a care plan. - * http://hl7.org/fhir/valueset-care-plan-intent.html - */ -export type CarePlanIntent = z.infer - -/** - * Codes that reflect the current state of a care plan activity within its overall life cycle. - * http://hl7.org/fhir/valueset-care-plan-activity-status.html - */ -export const carePlanActivityStatusSchema = z.enum([ - 'not-started', - 'scheduled', - 'in-progress', - 'on-hold', - 'completed', - 'cancelled', - 'stopped', - 'unknown', - 'entered-in-error', -]) - -/** - * Codes that reflect the current state of a care plan activity within its overall life cycle. - * http://hl7.org/fhir/valueset-care-plan-activity-status.html - */ -export type CarePlanActivityStatus = z.infer< - typeof carePlanActivityStatusSchema -> - -/** - * Resource types defined as part of FHIR that can be represented as in-line definitions of a care plan activity. - * http://hl7.org/fhir/ValueSet/care-plan-activity-kind.html - */ -export const carePlanActivityKindSchema = z.enum([ - 'Appointment', - 'CommunicationRequest', - 'DeviceRequest', - 'MedicationRequest', - 'NutritionOrder', - 'Task', - 'ServiceRequest', - 'VisionPrescription', -]) - -/** - * Resource types defined as part of FHIR that can be represented as in-line definitions of a care plan activity. - * http://hl7.org/fhir/ValueSet/care-plan-activity-kind.html - */ -export type CarePlanActivityKind = z.infer diff --git a/packages/fhir/src/valueSets/careTeam.ts b/packages/fhir/src/valueSets/careTeam.ts deleted file mode 100644 index e494bd69..00000000 --- a/packages/fhir/src/valueSets/careTeam.ts +++ /dev/null @@ -1,27 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Indicates the status of the care team. - * http://hl7.org/fhir/valueset-care-team-status.html - */ -export const careTeamStatusSchema = z.enum([ - 'proposed', - 'active', - 'suspended', - 'inactive', - 'entered-in-error', -]) - -/** - * Indicates the status of the care team. - * http://hl7.org/fhir/valueset-care-team-status.html - */ -export type CareTeamStatus = z.infer diff --git a/packages/fhir/src/valueSets/catalogEntry.ts b/packages/fhir/src/valueSets/catalogEntry.ts deleted file mode 100644 index 9c2f4bd2..00000000 --- a/packages/fhir/src/valueSets/catalogEntry.ts +++ /dev/null @@ -1,43 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The workflow/clinical status of the catalog entry. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export const catalogEntryStatusSchema = z.enum([ - 'draft', - 'active', - 'retired', - 'unknown', -]) - -/** - * The workflow/clinical status of the catalog entry. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export type CatalogEntryStatus = z.infer - -/** - * The type of relation between catalog entries. - * http://hl7.org/fhir/valueset-relation-type.html - */ -export const catalogEntryRelationtypeSchema = z.enum([ - 'triggers', - 'is-replaced-by', -]) - -/** - * The type of relation between catalog entries. - * http://hl7.org/fhir/valueset-relation-type.html - */ -export type CatalogEntryRelationtype = z.infer< - typeof catalogEntryRelationtypeSchema -> diff --git a/packages/fhir/src/valueSets/chargeItem.ts b/packages/fhir/src/valueSets/chargeItem.ts deleted file mode 100644 index 398cfad8..00000000 --- a/packages/fhir/src/valueSets/chargeItem.ts +++ /dev/null @@ -1,29 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Codes identifying the lifecycle stage of a ChargeItem. - * http://hl7.org/fhir/valueset-chargeitem-status.html - */ -export const chargeItemStatusSchema = z.enum([ - 'planned', - 'billable', - 'not-billable', - 'aborted', - 'billed', - 'entered-in-error', - 'unknown', -]) - -/** - * Codes identifying the lifecycle stage of a ChargeItem. - * http://hl7.org/fhir/valueset-chargeitem-status.html - */ -export type ChargeItemStatus = z.infer diff --git a/packages/fhir/src/valueSets/chargeItemDefinition.ts b/packages/fhir/src/valueSets/chargeItemDefinition.ts deleted file mode 100644 index 10f51fe1..00000000 --- a/packages/fhir/src/valueSets/chargeItemDefinition.ts +++ /dev/null @@ -1,28 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Type of price component for charge items. - * http://hl7.org/fhir/valueset-invoice-priceComponentType.html - */ -export const priceComponentTypeSchema = z.enum([ - 'base', - 'surcharge', - 'deduction', - 'discount', - 'tax', - 'informational', -]) - -/** - * Type of price component for charge items. - * http://hl7.org/fhir/valueset-invoice-priceComponentType.html - */ -export type PriceComponentType = z.infer diff --git a/packages/fhir/src/valueSets/claim.ts b/packages/fhir/src/valueSets/claim.ts deleted file mode 100644 index 170aa64c..00000000 --- a/packages/fhir/src/valueSets/claim.ts +++ /dev/null @@ -1,25 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The purpose of the Claim: predetermination, preauthorization, claim. - * http://hl7.org/fhir/valueset-claim-use.html - */ -export const claimUseSchema = z.enum([ - 'claim', - 'preauthorization', - 'predetermination', -]) - -/** - * The purpose of the Claim: predetermination, preauthorization, claim. - * http://hl7.org/fhir/valueset-claim-use.html - */ -export type ClaimUse = z.infer diff --git a/packages/fhir/src/valueSets/clinicalImpression.ts b/packages/fhir/src/valueSets/clinicalImpression.ts deleted file mode 100644 index 9ec7211e..00000000 --- a/packages/fhir/src/valueSets/clinicalImpression.ts +++ /dev/null @@ -1,27 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The workflow state of a clinical impression. - * http://hl7.org/fhir/valueset-clinicalimpression-status.html - */ -export const clinicalImpressionStatusSchema = z.enum([ - 'in-progress', - 'completed', - 'entered-in-error', -]) - -/** - * The workflow state of a clinical impression. - * http://hl7.org/fhir/valueset-clinicalimpression-status.html - */ -export type ClinicalImpressionStatus = z.infer< - typeof clinicalImpressionStatusSchema -> diff --git a/packages/fhir/src/valueSets/clinicalUseDefinition.ts b/packages/fhir/src/valueSets/clinicalUseDefinition.ts deleted file mode 100644 index 75533080..00000000 --- a/packages/fhir/src/valueSets/clinicalUseDefinition.ts +++ /dev/null @@ -1,29 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Type of clinical use definition - * http://hl7.org/fhir/valueset-clinical-use-definition-type.html - */ -export const clinicalUseDefinitionTypeSchema = z.enum([ - 'indication', - 'contraindication', - 'interaction', - 'undesirable-effect', - 'warning', -]) - -/** - * Type of clinical use definition - * http://hl7.org/fhir/valueset-clinical-use-definition-type.html - */ -export type ClinicalUseDefinitionType = z.infer< - typeof clinicalUseDefinitionTypeSchema -> diff --git a/packages/fhir/src/valueSets/codeSystem.ts b/packages/fhir/src/valueSets/codeSystem.ts deleted file mode 100644 index 185c625d..00000000 --- a/packages/fhir/src/valueSets/codeSystem.ts +++ /dev/null @@ -1,90 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The extent of the content of the code system. - * http://hl7.org/fhir/valueset-codesystem-content-mode.html - */ -export const codeSystemContentSchema = z.enum([ - 'not-present', - 'example', - 'fragment', - 'complete', - 'supplement', -]) - -/** - * The extent of the content of the code system. - * http://hl7.org/fhir/valueset-codesystem-content-mode.html - */ -export type CodeSystemContent = z.infer - -/** - * The meaning of the hierarchy of concepts in a code system. - * http://hl7.org/fhir/valueset-codesystem-hierarchy-meaning.html - */ -export const codeSystemHierarchyMeaningSchema = z.enum([ - 'grouped-by', - 'is-a', - 'part-of', - 'classified-with', -]) - -/** - * The meaning of the hierarchy of concepts in a code system. - * http://hl7.org/fhir/valueset-codesystem-hierarchy-meaning.html - */ -export type CodeSystemHierarchyMeaning = z.infer< - typeof codeSystemHierarchyMeaningSchema -> - -/** - * The type of a property value. - * http://hl7.org/fhir/valueset-concept-property-type.html - */ -export const codeSystemPropertyTypeSchema = z.enum([ - 'code', - 'Coding', - 'string', - 'integer', - 'boolean', - 'dateTime', - 'decimal', -]) - -/** - * The type of a property value. - * http://hl7.org/fhir/valueset-concept-property-type.html - */ -export type CodeSystemPropertyType = z.infer< - typeof codeSystemPropertyTypeSchema -> - -/** - * Operations supported for $lookup and value set filters. - * http://hl7.org/fhir/valueset-filter-operator.html - */ -export const filterOperatorSchema = z.enum([ - '=', - 'is-a', - 'descendent-of', - 'is-not-a', - 'regex', - 'in', - 'not-in', - 'generalizes', - 'exists', -]) - -/** - * Operations supported for $lookup and value set filters. - * http://hl7.org/fhir/valueset-filter-operator.html - */ -export type FilterOperator = z.infer diff --git a/packages/fhir/src/valueSets/communication.ts b/packages/fhir/src/valueSets/communication.ts deleted file mode 100644 index 72bbfd5a..00000000 --- a/packages/fhir/src/valueSets/communication.ts +++ /dev/null @@ -1,30 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the communication. - * http://hl7.org/fhir/valueset-event-status.html - */ -export const communicationStatusSchema = z.enum([ - 'preparation', - 'in-progress', - 'not-done', - 'on-hold', - 'stopped', - 'completed', - 'entered-in-error', - 'unknown', -]) - -/** - * The status of the communication. - * http://hl7.org/fhir/valueset-event-status.html - */ -export type CommunicationStatus = z.infer diff --git a/packages/fhir/src/valueSets/communicationRequest.ts b/packages/fhir/src/valueSets/communicationRequest.ts deleted file mode 100644 index 0f42d241..00000000 --- a/packages/fhir/src/valueSets/communicationRequest.ts +++ /dev/null @@ -1,31 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the communication request. - * http://hl7.org/fhir/valueset-request-status.html - */ -export const communicationRequestStatusSchema = z.enum([ - 'draft', - 'active', - 'on-hold', - 'revoked', - 'completed', - 'entered-in-error', - 'unknown', -]) - -/** - * The status of the communication request. - * http://hl7.org/fhir/valueset-request-status.html - */ -export type CommunicationRequestStatus = z.infer< - typeof communicationRequestStatusSchema -> diff --git a/packages/fhir/src/valueSets/compartmentDefinition.ts b/packages/fhir/src/valueSets/compartmentDefinition.ts deleted file mode 100644 index aef3aa1a..00000000 --- a/packages/fhir/src/valueSets/compartmentDefinition.ts +++ /dev/null @@ -1,21 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Zod schema for FHIR CompartmentDefinitionCode value set. - * Which type of compartment a compartment definition describes. - */ -export const compartmentDefinitionCodeSchema = z.enum([ - 'Patient', - 'Encounter', - 'RelatedPerson', - 'Practitioner', - 'Device', -]) diff --git a/packages/fhir/src/valueSets/composition.ts b/packages/fhir/src/valueSets/composition.ts deleted file mode 100644 index a7eb38ea..00000000 --- a/packages/fhir/src/valueSets/composition.ts +++ /dev/null @@ -1,64 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The mode of a composition. - * http://hl7.org/fhir/valueset-composition-attestation-mode.html - */ -export const compositionAttestationModeSchema = z.enum([ - 'personal', - 'professional', - 'legal', - 'official', -]) - -/** - * The mode of a composition. - * http://hl7.org/fhir/valueset-composition-attestation-mode.html - */ -export type CompositionAttestationMode = z.infer< - typeof compositionAttestationModeSchema -> - -/** - * The way in which this related composition is related. - * http://hl7.org/fhir/valueset-document-relationship-type.html - */ -export const compositionRelatestoCodeSchema = z.enum([ - 'replaces', - 'transforms', - 'signs', - 'appends', -]) - -/** - * The way in which this related composition is related. - * http://hl7.org/fhir/valueset-document-relationship-type.html - */ -export type CompositionRelatesToCode = z.infer< - typeof compositionRelatestoCodeSchema -> - -/** - * The workflow/clinical status of the composition. - * http://hl7.org/fhir/valueset-composition-status.html - */ -export const compositionStatusSchema = z.enum([ - 'preliminary', - 'final', - 'amended', - 'entered-in-error', -]) - -/** - * The workflow/clinical status of the composition. - * http://hl7.org/fhir/valueset-composition-status.html - */ -export type CompositionStatus = z.infer diff --git a/packages/fhir/src/valueSets/conceptMap.ts b/packages/fhir/src/valueSets/conceptMap.ts deleted file mode 100644 index 9d85938c..00000000 --- a/packages/fhir/src/valueSets/conceptMap.ts +++ /dev/null @@ -1,50 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The degree of equivalence between concepts. - * http://hl7.org/fhir/valueset-concept-map-equivalence.html - */ -export const conceptMapEquivalenceSchema = z.enum([ - 'relatedto', - 'equivalent', - 'equal', - 'wider', - 'subsumes', - 'narrower', - 'specializes', - 'inexact', - 'unmatched', - 'disjoint', -]) - -/** - * The degree of equivalence between concepts. - * http://hl7.org/fhir/valueset-concept-map-equivalence.html - */ -export type ConceptMapEquivalence = z.infer - -/** - * Defines which action to take if there is no match in the group. - * http://hl7.org/fhir/valueset-conceptmap-unmapped-mode.html - */ -export const conceptMapUnmappedModeSchema = z.enum([ - 'provided', - 'fixed', - 'other-map', -]) - -/** - * Defines which action to take if there is no match in the group. - * http://hl7.org/fhir/valueset-conceptmap-unmapped-mode.html - */ -export type ConceptMapUnmappedMode = z.infer< - typeof conceptMapUnmappedModeSchema -> diff --git a/packages/fhir/src/valueSets/consent.ts b/packages/fhir/src/valueSets/consent.ts deleted file mode 100644 index 60b3b041..00000000 --- a/packages/fhir/src/valueSets/consent.ts +++ /dev/null @@ -1,57 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Indicates the state of the consent. - * http://hl7.org/fhir/valueset-consent-state-codes.html - */ -export const consentStatusSchema = z.enum([ - 'draft', - 'proposed', - 'active', - 'rejected', - 'inactive', - 'entered-in-error', -]) - -/** - * Indicates the state of the consent. - * http://hl7.org/fhir/valueset-consent-state-codes.html - */ -export type ConsentStatus = z.infer - -/** - * How a resource reference is interpreted when testing consent restrictions. - * http://hl7.org/fhir/valueset-consent-data-meaning.html - */ -export const consentDataMeaningSchema = z.enum([ - 'instance', - 'related', - 'dependents', - 'authoredby', -]) - -/** - * How a resource reference is interpreted when testing consent restrictions. - * http://hl7.org/fhir/valueset-consent-data-meaning.html - */ -export type ConsentDataMeaning = z.infer - -/** - * How a rule statement is applied, such as adding additional consent or removing consent. - * http://hl7.org/fhir/valueset-consent-provision-type.html - */ -export const consentProvisionTypeSchema = z.enum(['deny', 'permit']) - -/** - * How a rule statement is applied, such as adding additional consent or removing consent. - * http://hl7.org/fhir/valueset-consent-provision-type.html - */ -export type ConsentProvisionType = z.infer diff --git a/packages/fhir/src/valueSets/contract.ts b/packages/fhir/src/valueSets/contract.ts deleted file mode 100644 index 73ed276e..00000000 --- a/packages/fhir/src/valueSets/contract.ts +++ /dev/null @@ -1,67 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the contract. - * http://hl7.org/fhir/valueset-contract-status.html - */ -export const contractStatusSchema = z.enum([ - 'amended', - 'appended', - 'cancelled', - 'disputed', - 'entered-in-error', - 'executable', - 'executed', - 'negotiable', - 'offered', - 'policy', - 'rejected', - 'renewed', - 'revoked', - 'resolved', - 'terminated', -]) - -/** - * The status of the contract. - * http://hl7.org/fhir/valueset-contract-status.html - */ -export type ContractStatus = z.infer - -/** - * The publication status of the contract content definition. - * http://hl7.org/fhir/valueset-contract-publicationstatus.html - */ -export const contractPublicationStatusSchema = z.enum([ - 'amended', - 'appended', - 'cancelled', - 'disputed', - 'entered-in-error', - 'executable', - 'executed', - 'negotiable', - 'offered', - 'policy', - 'rejected', - 'renewed', - 'revoked', - 'resolved', - 'terminated', -]) - -/** - * The publication status of the contract content definition. - * http://hl7.org/fhir/valueset-contract-publicationstatus.html - */ -export type ContractPublicationStatus = z.infer< - typeof contractPublicationStatusSchema -> diff --git a/packages/fhir/src/valueSets/coverage.ts b/packages/fhir/src/valueSets/coverage.ts deleted file mode 100644 index 1b868c20..00000000 --- a/packages/fhir/src/valueSets/coverage.ts +++ /dev/null @@ -1,26 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the coverage. - * http://hl7.org/fhir/valueset-fm-status.html - */ -export const coverageStatusSchema = z.enum([ - 'active', - 'cancelled', - 'draft', - 'entered-in-error', -]) - -/** - * The status of the coverage. - * http://hl7.org/fhir/valueset-fm-status.html - */ -export type CoverageStatus = z.infer diff --git a/packages/fhir/src/valueSets/coverageEligibility.ts b/packages/fhir/src/valueSets/coverageEligibility.ts deleted file mode 100644 index 71cb3606..00000000 --- a/packages/fhir/src/valueSets/coverageEligibility.ts +++ /dev/null @@ -1,47 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * A code specifying the types of information being requested. - * http://hl7.org/fhir/valueset-eligibilityrequest-purpose.html - */ -export const eligibilityRequestPurposeSchema = z.enum([ - 'auth-requirements', - 'benefits', - 'discovery', - 'validation', -]) - -/** - * A code specifying the types of information being requested. - * http://hl7.org/fhir/valueset-eligibilityrequest-purpose.html - */ -export type EligibilityRequestPurpose = z.infer< - typeof eligibilityRequestPurposeSchema -> - -/** - * A code specifying the types of information being requested. - * http://hl7.org/fhir/valueset-eligibilityresponse-purpose.html - */ -export const eligibilityResponsePurposeSchema = z.enum([ - 'auth-requirements', - 'benefits', - 'discovery', - 'validation', -]) - -/** - * A code specifying the types of information being requested. - * http://hl7.org/fhir/valueset-eligibilityresponse-purpose.html - */ -export type EligibilityResponsePurpose = z.infer< - typeof eligibilityResponsePurposeSchema -> diff --git a/packages/fhir/src/valueSets/daysOfWeek.ts b/packages/fhir/src/valueSets/daysOfWeek.ts deleted file mode 100644 index f69faace..00000000 --- a/packages/fhir/src/valueSets/daysOfWeek.ts +++ /dev/null @@ -1,29 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The days of the week. - * http://hl7.org/fhir/valueset-days-of-week.html - */ -export const daysOfWeekSchema = z.enum([ - 'mon', - 'tue', - 'wed', - 'thu', - 'fri', - 'sat', - 'sun', -]) - -/** - * The days of the week. - * http://hl7.org/fhir/valueset-days-of-week.html - */ -export type DaysOfWeek = z.infer diff --git a/packages/fhir/src/valueSets/detectedIssue.ts b/packages/fhir/src/valueSets/detectedIssue.ts deleted file mode 100644 index 9474bf63..00000000 --- a/packages/fhir/src/valueSets/detectedIssue.ts +++ /dev/null @@ -1,42 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Indicates the potential degree of impact of the identified issue on the patient. - * http://hl7.org/fhir/valueset-detectedissue-severity.html - */ -export const detectedIssueSeveritySchema = z.enum(['high', 'moderate', 'low']) - -/** - * Indicates the potential degree of impact of the identified issue on the patient. - * http://hl7.org/fhir/valueset-detectedissue-severity.html - */ -export type DetectedIssueSeverity = z.infer - -/** - * Codes providing the status of a detected issue. - * http://hl7.org/fhir/valueset-observation-status.html - */ -export const detectedIssueStatusSchema = z.enum([ - 'registered', - 'preliminary', - 'final', - 'amended', - 'corrected', - 'cancelled', - 'entered-in-error', - 'unknown', -]) - -/** - * Codes providing the status of a detected issue. - * http://hl7.org/fhir/valueset-observation-status.html - */ -export type DetectedIssueStatus = z.infer diff --git a/packages/fhir/src/valueSets/device.ts b/packages/fhir/src/valueSets/device.ts deleted file mode 100644 index 0114fff3..00000000 --- a/packages/fhir/src/valueSets/device.ts +++ /dev/null @@ -1,45 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The type of name the device is referred by. - * http://hl7.org/fhir/valueset-device-nametype.html - */ -export const deviceNameTypeSchema = z.enum([ - 'udi-label-name', - 'user-friendly-name', - 'patient-reported-name', - 'manufacturer-name', - 'model-name', - 'other', -]) - -/** - * The type of name the device is referred by. - * http://hl7.org/fhir/valueset-device-nametype.html - */ -export type DeviceNameType = z.infer - -/** - * The availability status of the device. - * http://hl7.org/fhir/valueset-device-status.html - */ -export const deviceStatusSchema = z.enum([ - 'active', - 'inactive', - 'entered-in-error', - 'unknown', -]) - -/** - * The availability status of the device. - * http://hl7.org/fhir/valueset-device-status.html - */ -export type DeviceStatus = z.infer diff --git a/packages/fhir/src/valueSets/deviceDefinition.ts b/packages/fhir/src/valueSets/deviceDefinition.ts deleted file mode 100644 index fe57d0bb..00000000 --- a/packages/fhir/src/valueSets/deviceDefinition.ts +++ /dev/null @@ -1,30 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The type of name the device is referred by. - * http://hl7.org/fhir/valueset-device-nametype.html - */ -export const deviceDefinitionNameTypeSchema = z.enum([ - 'udi-label-name', - 'user-friendly-name', - 'patient-reported-name', - 'manufacturer-name', - 'model-name', - 'other', -]) - -/** - * The type of name the device is referred by. - * http://hl7.org/fhir/valueset-device-nametype.html - */ -export type DeviceDefinitionNameType = z.infer< - typeof deviceDefinitionNameTypeSchema -> diff --git a/packages/fhir/src/valueSets/deviceMetric.ts b/packages/fhir/src/valueSets/deviceMetric.ts deleted file mode 100644 index 15451886..00000000 --- a/packages/fhir/src/valueSets/deviceMetric.ts +++ /dev/null @@ -1,104 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Describes the type of a metric calibration. - * http://hl7.org/fhir/valueset-metric-calibration-type.html - */ -export const deviceMetricCalibrationTypeSchema = z.enum([ - 'unspecified', - 'offset', - 'gain', - 'two-point', -]) - -/** - * Describes the type of a metric calibration. - * http://hl7.org/fhir/valueset-metric-calibration-type.html - */ -export type DeviceMetricCalibrationType = z.infer< - typeof deviceMetricCalibrationTypeSchema -> - -/** - * Describes the state of a metric calibration. - * http://hl7.org/fhir/valueset-metric-calibration-state.html - */ -export const deviceMetricCalibrationStateSchema = z.enum([ - 'not-calibrated', - 'calibration-required', - 'calibrated', - 'unspecified', -]) - -/** - * Describes the state of a metric calibration. - * http://hl7.org/fhir/valueset-metric-calibration-state.html - */ -export type DeviceMetricCalibrationState = z.infer< - typeof deviceMetricCalibrationStateSchema -> - -/** - * Describes the operational status of the DeviceMetric. - * http://hl7.org/fhir/valueset-metric-operational-status.html - */ -export const deviceMetricOperationalStatusSchema = z.enum([ - 'on', - 'off', - 'standby', - 'entered-in-error', -]) - -/** - * Describes the operational status of the DeviceMetric. - * http://hl7.org/fhir/valueset-metric-operational-status.html - */ -export type DeviceMetricOperationalStatus = z.infer< - typeof deviceMetricOperationalStatusSchema -> - -/** - * Describes the typical color of representation. - * http://hl7.org/fhir/valueset-metric-color.html - */ -export const deviceMetricColorSchema = z.enum([ - 'black', - 'red', - 'green', - 'yellow', - 'blue', - 'magenta', - 'cyan', - 'white', -]) - -/** - * Describes the typical color of representation. - * http://hl7.org/fhir/valueset-metric-color.html - */ -export type DeviceMetricColor = z.infer - -/** - * Describes the category of the metric. - * http://hl7.org/fhir/valueset-metric-category.html - */ -export const deviceMetricCategorySchema = z.enum([ - 'measurement', - 'setting', - 'calculation', - 'unspecified', -]) - -/** - * Describes the category of the metric. - * http://hl7.org/fhir/valueset-metric-category.html - */ -export type DeviceMetricCategory = z.infer diff --git a/packages/fhir/src/valueSets/deviceRequest.ts b/packages/fhir/src/valueSets/deviceRequest.ts deleted file mode 100644 index 109695f5..00000000 --- a/packages/fhir/src/valueSets/deviceRequest.ts +++ /dev/null @@ -1,51 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the device request. - * http://hl7.org/fhir/valueset-request-status.html - */ -export const deviceRequestStatusSchema = z.enum([ - 'draft', - 'active', - 'on-hold', - 'revoked', - 'completed', - 'entered-in-error', - 'unknown', -]) - -/** - * The status of the device request. - * http://hl7.org/fhir/valueset-request-status.html - */ -export type DeviceRequestStatus = z.infer - -/** - * The kind of device request. - * http://hl7.org/fhir/valueset-request-intent.html - */ -export const deviceRequestIntentSchema = z.enum([ - 'proposal', - 'plan', - 'directive', - 'order', - 'original-order', - 'reflex-order', - 'filler-order', - 'instance-order', - 'option', -]) - -/** - * The kind of device request. - * http://hl7.org/fhir/valueset-request-intent.html - */ -export type DeviceRequestIntent = z.infer diff --git a/packages/fhir/src/valueSets/deviceUseStatement.ts b/packages/fhir/src/valueSets/deviceUseStatement.ts deleted file mode 100644 index 367bc8f8..00000000 --- a/packages/fhir/src/valueSets/deviceUseStatement.ts +++ /dev/null @@ -1,30 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * A coded concept indicating the current status of the Device Usage. - * http://hl7.org/fhir/valueset-device-statement-status.html - */ -export const deviceUseStatementStatusSchema = z.enum([ - 'active', - 'completed', - 'entered-in-error', - 'intended', - 'stopped', - 'on-hold', -]) - -/** - * A coded concept indicating the current status of the Device Usage. - * http://hl7.org/fhir/valueset-device-statement-status.html - */ -export type DeviceUseStatementStatus = z.infer< - typeof deviceUseStatementStatusSchema -> diff --git a/packages/fhir/src/valueSets/diagnosticReport.ts b/packages/fhir/src/valueSets/diagnosticReport.ts deleted file mode 100644 index 8364fbe1..00000000 --- a/packages/fhir/src/valueSets/diagnosticReport.ts +++ /dev/null @@ -1,34 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of a diagnostic report. - * http://hl7.org/fhir/valueset-diagnostic-report-status.html - */ -export const diagnosticReportStatusSchema = z.enum([ - 'registered', - 'partial', - 'preliminary', - 'final', - 'amended', - 'corrected', - 'appended', - 'cancelled', - 'entered-in-error', - 'unknown', -]) - -/** - * The status of a diagnostic report. - * http://hl7.org/fhir/valueset-diagnostic-report-status.html - */ -export type DiagnosticReportStatus = z.infer< - typeof diagnosticReportStatusSchema -> diff --git a/packages/fhir/src/valueSets/documentManifest.ts b/packages/fhir/src/valueSets/documentManifest.ts deleted file mode 100644 index 2ab72410..00000000 --- a/packages/fhir/src/valueSets/documentManifest.ts +++ /dev/null @@ -1,27 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the document manifest. - * http://hl7.org/fhir/valueset-document-reference-status.html - */ -export const documentManifestStatusSchema = z.enum([ - 'current', - 'superseded', - 'entered-in-error', -]) - -/** - * The status of the document manifest. - * http://hl7.org/fhir/valueset-document-reference-status.html - */ -export type DocumentManifestStatus = z.infer< - typeof documentManifestStatusSchema -> diff --git a/packages/fhir/src/valueSets/documentReference.ts b/packages/fhir/src/valueSets/documentReference.ts deleted file mode 100644 index c3064c3c..00000000 --- a/packages/fhir/src/valueSets/documentReference.ts +++ /dev/null @@ -1,65 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the document reference. - * http://hl7.org/fhir/valueset-document-reference-status.html - */ -export const documentReferenceStatusSchema = z.enum([ - 'current', - 'superseded', - 'entered-in-error', -]) - -/** - * The status of the document reference. - * http://hl7.org/fhir/valueset-document-reference-status.html - */ -export type DocumentReferenceStatus = z.infer< - typeof documentReferenceStatusSchema -> - -/** - * The status of the underlying document. - * http://hl7.org/fhir/valueset-composition-status.html - */ -export const documentReferenceDocStatusSchema = z.enum([ - 'preliminary', - 'final', - 'amended', - 'entered-in-error', -]) - -/** - * The status of the underlying document. - * http://hl7.org/fhir/valueset-composition-status.html - */ -export type DocumentReferenceDocStatus = z.infer< - typeof documentReferenceDocStatusSchema -> - -/** - * The type of relationship between documents. - * http://hl7.org/fhir/valueset-document-relationship-type.html - */ -export const documentReferenceRelatesToCodeSchema = z.enum([ - 'replaces', - 'transforms', - 'signs', - 'appends', -]) - -/** - * The type of relationship between documents. - * http://hl7.org/fhir/valueset-document-relationship-type.html - */ -export type DocumentReferenceRelatesToCode = z.infer< - typeof documentReferenceRelatesToCodeSchema -> diff --git a/packages/fhir/src/valueSets/encounter.ts b/packages/fhir/src/valueSets/encounter.ts deleted file mode 100644 index bdde42d9..00000000 --- a/packages/fhir/src/valueSets/encounter.ts +++ /dev/null @@ -1,50 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of an encounter. - * http://hl7.org/fhir/valueset-encounter-status.html - */ -export const encounterStatusSchema = z.enum([ - 'planned', - 'arrived', - 'triaged', - 'in-progress', - 'onleave', - 'finished', - 'cancelled', - 'entered-in-error', - 'unknown', -]) - -/** - * The status of an encounter. - * http://hl7.org/fhir/valueset-encounter-status.html - */ -export type EncounterStatus = z.infer - -/** - * The status of the location. - * http://hl7.org/fhir/valueset-encounter-location-status.html - */ -export const encounterLocationStatusSchema = z.enum([ - 'planned', - 'active', - 'reserved', - 'completed', -]) - -/** - * The status of the location. - * http://hl7.org/fhir/valueset-encounter-location-status.html - */ -export type EncounterLocationStatus = z.infer< - typeof encounterLocationStatusSchema -> diff --git a/packages/fhir/src/valueSets/endpoint.ts b/packages/fhir/src/valueSets/endpoint.ts deleted file mode 100644 index 51f8a0ca..00000000 --- a/packages/fhir/src/valueSets/endpoint.ts +++ /dev/null @@ -1,28 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the endpoint. - * http://hl7.org/fhir/valueset-endpoint-status.html - */ -export const endpointStatusSchema = z.enum([ - 'active', - 'suspended', - 'error', - 'off', - 'entered-in-error', - 'test', -]) - -/** - * The status of the endpoint. - * http://hl7.org/fhir/valueset-endpoint-status.html - */ -export type EndpointStatus = z.infer diff --git a/packages/fhir/src/valueSets/episodeOfCare.ts b/packages/fhir/src/valueSets/episodeOfCare.ts deleted file mode 100644 index 7d1fc9a4..00000000 --- a/packages/fhir/src/valueSets/episodeOfCare.ts +++ /dev/null @@ -1,29 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of an episode of care. - * http://hl7.org/fhir/valueset-episode-of-care-status.html - */ -export const episodeOfCareStatusSchema = z.enum([ - 'planned', - 'waitlist', - 'active', - 'onhold', - 'finished', - 'cancelled', - 'entered-in-error', -]) - -/** - * The status of an episode of care. - * http://hl7.org/fhir/valueset-episode-of-care-status.html - */ -export type EpisodeOfCareStatus = z.infer diff --git a/packages/fhir/src/valueSets/evidenceReport.ts b/packages/fhir/src/valueSets/evidenceReport.ts deleted file mode 100644 index 108e4eaa..00000000 --- a/packages/fhir/src/valueSets/evidenceReport.ts +++ /dev/null @@ -1,61 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * EvidenceReport Type - * http://hl7.org/fhir/ValueSet/evidence-report-type - */ -export const evidenceReportTypeSchema = z.enum([ - 'classification', - 'search-results', - 'text', - 'resources-compiled', -]) - -/** - * EvidenceReport Type - * http://hl7.org/fhir/ValueSet/evidence-report-type - */ -export type EvidenceReportType = z.infer - -/** - * Section Mode - * http://hl7.org/fhir/ValueSet/list-mode - */ -export const sectionModeSchema = z.enum(['working', 'snapshot', 'changes']) - -/** - * Section Mode - * http://hl7.org/fhir/ValueSet/list-mode - */ -export type SectionMode = z.infer - -/** - * EvidenceReport Relates To Code - * http://hl7.org/fhir/ValueSet/evidence-report-relation-type - */ -export const evidenceReportRelatesToCodeSchema = z.enum([ - 'replaces', - 'amends', - 'appends', - 'transforms', - 'replacedWith', - 'amendedWith', - 'appendedWith', - 'transformedWith', -]) - -/** - * EvidenceReport Relates To Code - * http://hl7.org/fhir/ValueSet/evidence-report-relation-type - */ -export type EvidenceReportRelatesToCode = z.infer< - typeof evidenceReportRelatesToCodeSchema -> diff --git a/packages/fhir/src/valueSets/evidenceVariable.ts b/packages/fhir/src/valueSets/evidenceVariable.ts deleted file mode 100644 index 0e1edbb2..00000000 --- a/packages/fhir/src/valueSets/evidenceVariable.ts +++ /dev/null @@ -1,47 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * EvidenceVariable Handling - * http://hl7.org/fhir/ValueSet/variable-handling - */ -export const evidenceVariableHandlingSchema = z.enum([ - 'continuous', - 'dichotomous', - 'ordinal', - 'polychotomous', -]) - -/** - * EvidenceVariable Handling - * http://hl7.org/fhir/ValueSet/variable-handling - */ -export type EvidenceVariableHandling = z.infer< - typeof evidenceVariableHandlingSchema -> - -/** - * Group Measure - * http://hl7.org/fhir/ValueSet/group-measure - */ -export const groupMeasureSchema = z.enum([ - 'mean', - 'median', - 'mean-of-mean', - 'mean-of-median', - 'median-of-mean', - 'median-of-median', -]) - -/** - * Group Measure - * http://hl7.org/fhir/ValueSet/group-measure - */ -export type GroupMeasure = z.infer diff --git a/packages/fhir/src/valueSets/exampleScenario.ts b/packages/fhir/src/valueSets/exampleScenario.ts deleted file mode 100644 index 9dbdbb89..00000000 --- a/packages/fhir/src/valueSets/exampleScenario.ts +++ /dev/null @@ -1,22 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Zod schema for FHIR ExampleScenarioActorType value set. - * The type of actor in an example scenario. - */ -export const exampleScenarioActorTypeSchema = z.enum(['person', 'entity']) - -/** - * TypeScript type for FHIR ExampleScenarioActorType value set. - */ -export type ExampleScenarioActorType = z.infer< - typeof exampleScenarioActorTypeSchema -> diff --git a/packages/fhir/src/valueSets/explanationOfBenefit.ts b/packages/fhir/src/valueSets/explanationOfBenefit.ts deleted file mode 100644 index c7ae4a41..00000000 --- a/packages/fhir/src/valueSets/explanationOfBenefit.ts +++ /dev/null @@ -1,26 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Zod schema for FHIR ExplanationOfBenefitUse value set. - * Indicates whether the claim is for costs already incurred or a preauthorization/predetermination. - */ -export const explanationOfBenefitUseSchema = z.enum([ - 'claim', - 'preauthorization', - 'predetermination', -]) - -/** - * TypeScript type for FHIR ExplanationOfBenefitUse value set. - */ -export type ExplanationOfBenefitUse = z.infer< - typeof explanationOfBenefitUseSchema -> diff --git a/packages/fhir/src/valueSets/familyMemberHistory.ts b/packages/fhir/src/valueSets/familyMemberHistory.ts deleted file mode 100644 index 40e3a466..00000000 --- a/packages/fhir/src/valueSets/familyMemberHistory.ts +++ /dev/null @@ -1,28 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * A code that identifies the status of the family history record. - * http://hl7.org/fhir/valueset-history-status.html - */ -export const familyMemberHistoryStatusSchema = z.enum([ - 'partial', - 'completed', - 'entered-in-error', - 'health-unknown', -]) - -/** - * A code that identifies the status of the family history record. - * http://hl7.org/fhir/valueset-history-status.html - */ -export type FamilyMemberHistoryStatus = z.infer< - typeof familyMemberHistoryStatusSchema -> diff --git a/packages/fhir/src/valueSets/fhir.ts b/packages/fhir/src/valueSets/fhir.ts deleted file mode 100644 index 32d034ac..00000000 --- a/packages/fhir/src/valueSets/fhir.ts +++ /dev/null @@ -1,797 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type CapabilityStatement, - type DataRequirement, - type ExampleScenarioInstance, - type ImplementationGuide, - type TimingRepeat, -} from 'fhir/r4b.js' -import { z, type ZodType } from 'zod' - -/** Resource type codes defined by FHIR R4B. */ -export const fhirResourceTypeSchema: ZodType< - ExampleScenarioInstance['resourceType'] -> = z.enum([ - 'Resource', - 'Binary', - 'Bundle', - 'DomainResource', - 'Account', - 'ActivityDefinition', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RegulatedAuthorization', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'RiskAssessment', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - 'Parameters', -]) - -/** Resource, data type, and primitive type codes defined by FHIR R4B. */ -export const fhirTypeSchema = z.enum([ - 'string', - 'boolean', - 'Resource', - 'Binary', - 'Bundle', - 'DomainResource', - 'Account', - 'ActivityDefinition', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RegulatedAuthorization', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'RiskAssessment', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - 'Parameters', - 'Address', - 'Age', - 'Annotation', - 'Attachment', - 'BackboneElement', - 'CodeableConcept', - 'CodeableReference', - 'Coding', - 'ContactDetail', - 'ContactPoint', - 'Contributor', - 'Count', - 'DataRequirement', - 'Distance', - 'Dosage', - 'Duration', - 'Element', - 'ElementDefinition', - 'Expression', - 'Extension', - 'HumanName', - 'Identifier', - 'MarketingStatus', - 'Meta', - 'Money', - 'MoneyQuantity', - 'Narrative', - 'ParameterDefinition', - 'Period', - 'Population', - 'ProdCharacteristic', - 'ProductShelfLife', - 'Quantity', - 'Range', - 'Ratio', - 'RatioRange', - 'Reference', - 'RelatedArtifact', - 'SampledData', - 'Signature', - 'SimpleQuantity', - 'Timing', - 'TriggerDefinition', - 'UsageContext', - 'base64Binary', - 'canonical', - 'code', - 'date', - 'dateTime', - 'decimal', - 'id', - 'instant', - 'integer', - 'markdown', - 'oid', - 'positiveInt', - 'time', - 'unsignedInt', - 'uri', - 'url', - 'uuid', - 'xhtml', - 'Type', - 'Any', -]) satisfies ZodType - -/** Concrete FHIR R4B types, excluding the abstract `Type` and `Any` codes. */ -export const fhirDefinedTypeSchema = fhirTypeSchema.exclude(['Type', 'Any']) - -/** Published FHIR versions recognized by FHIR R4B. */ -export const fhirVersionSchema: ZodType = - z.enum([ - '0.01', - '0.05', - '0.06', - '0.11', - '0.0.80', - '0.0.81', - '0.0.82', - '0.4.0', - '0.5.0', - '1.0.0', - '1.0.1', - '1.0.2', - '1.1.0', - '1.4.0', - '1.6.0', - '1.8.0', - '3.0.0', - '3.0.1', - '3.0.2', - '3.3.0', - '3.5.0', - '4.0.0', - '4.0.1', - '4.1.0', - '4.3.0-cibuild', - '4.3.0-snapshot1', - '4.3.0', - ]) - -/** License codes recognized by the FHIR R4B SPDX value set. */ -export const fhirLicenseSchema: ZodType< - NonNullable -> = z.enum([ - 'not-open-source', - '0BSD', - 'AAL', - 'Abstyles', - 'Adobe-2006', - 'Adobe-Glyph', - 'ADSL', - 'AFL-1.1', - 'AFL-1.2', - 'AFL-2.0', - 'AFL-2.1', - 'AFL-3.0', - 'Afmparse', - 'AGPL-1.0-only', - 'AGPL-1.0-or-later', - 'AGPL-3.0-only', - 'AGPL-3.0-or-later', - 'Aladdin', - 'AMDPLPA', - 'AML', - 'AMPAS', - 'ANTLR-PD', - 'Apache-1.0', - 'Apache-1.1', - 'Apache-2.0', - 'APAFML', - 'APL-1.0', - 'APSL-1.0', - 'APSL-1.1', - 'APSL-1.2', - 'APSL-2.0', - 'Artistic-1.0-cl8', - 'Artistic-1.0-Perl', - 'Artistic-1.0', - 'Artistic-2.0', - 'Bahyph', - 'Barr', - 'Beerware', - 'BitTorrent-1.0', - 'BitTorrent-1.1', - 'Borceux', - 'BSD-1-Clause', - 'BSD-2-Clause-FreeBSD', - 'BSD-2-Clause-NetBSD', - 'BSD-2-Clause-Patent', - 'BSD-2-Clause', - 'BSD-3-Clause-Attribution', - 'BSD-3-Clause-Clear', - 'BSD-3-Clause-LBNL', - 'BSD-3-Clause-No-Nuclear-License-2014', - 'BSD-3-Clause-No-Nuclear-License', - 'BSD-3-Clause-No-Nuclear-Warranty', - 'BSD-3-Clause', - 'BSD-4-Clause-UC', - 'BSD-4-Clause', - 'BSD-Protection', - 'BSD-Source-Code', - 'BSL-1.0', - 'bzip2-1.0.5', - 'bzip2-1.0.6', - 'Caldera', - 'CATOSL-1.1', - 'CC-BY-1.0', - 'CC-BY-2.0', - 'CC-BY-2.5', - 'CC-BY-3.0', - 'CC-BY-4.0', - 'CC-BY-NC-1.0', - 'CC-BY-NC-2.0', - 'CC-BY-NC-2.5', - 'CC-BY-NC-3.0', - 'CC-BY-NC-4.0', - 'CC-BY-NC-ND-1.0', - 'CC-BY-NC-ND-2.0', - 'CC-BY-NC-ND-2.5', - 'CC-BY-NC-ND-3.0', - 'CC-BY-NC-ND-4.0', - 'CC-BY-NC-SA-1.0', - 'CC-BY-NC-SA-2.0', - 'CC-BY-NC-SA-2.5', - 'CC-BY-NC-SA-3.0', - 'CC-BY-NC-SA-4.0', - 'CC-BY-ND-1.0', - 'CC-BY-ND-2.0', - 'CC-BY-ND-2.5', - 'CC-BY-ND-3.0', - 'CC-BY-ND-4.0', - 'CC-BY-SA-1.0', - 'CC-BY-SA-2.0', - 'CC-BY-SA-2.5', - 'CC-BY-SA-3.0', - 'CC-BY-SA-4.0', - 'CC0-1.0', - 'CDDL-1.0', - 'CDDL-1.1', - 'CDLA-Permissive-1.0', - 'CDLA-Sharing-1.0', - 'CECILL-1.0', - 'CECILL-1.1', - 'CECILL-2.0', - 'CECILL-2.1', - 'CECILL-B', - 'CECILL-C', - 'ClArtistic', - 'CNRI-Jython', - 'CNRI-Python-GPL-Compatible', - 'CNRI-Python', - 'Condor-1.1', - 'CPAL-1.0', - 'CPL-1.0', - 'CPOL-1.02', - 'Crossword', - 'CrystalStacker', - 'CUA-OPL-1.0', - 'Cube', - 'curl', - 'D-FSL-1.0', - 'diffmark', - 'DOC', - 'Dotseqn', - 'DSDP', - 'dvipdfm', - 'ECL-1.0', - 'ECL-2.0', - 'EFL-1.0', - 'EFL-2.0', - 'eGenix', - 'Entessa', - 'EPL-1.0', - 'EPL-2.0', - 'ErlPL-1.1', - 'EUDatagrid', - 'EUPL-1.0', - 'EUPL-1.1', - 'EUPL-1.2', - 'Eurosym', - 'Fair', - 'Frameworx-1.0', - 'FreeImage', - 'FSFAP', - 'FSFUL', - 'FSFULLR', - 'FTL', - 'GFDL-1.1-only', - 'GFDL-1.1-or-later', - 'GFDL-1.2-only', - 'GFDL-1.2-or-later', - 'GFDL-1.3-only', - 'GFDL-1.3-or-later', - 'Giftware', - 'GL2PS', - 'Glide', - 'Glulxe', - 'gnuplot', - 'GPL-1.0-only', - 'GPL-1.0-or-later', - 'GPL-2.0-only', - 'GPL-2.0-or-later', - 'GPL-3.0-only', - 'GPL-3.0-or-later', - 'gSOAP-1.3b', - 'HaskellReport', - 'HPND', - 'IBM-pibs', - 'ICU', - 'IJG', - 'ImageMagick', - 'iMatix', - 'Imlib2', - 'Info-ZIP', - 'Intel-ACPI', - 'Intel', - 'Interbase-1.0', - 'IPA', - 'IPL-1.0', - 'ISC', - 'JasPer-2.0', - 'JSON', - 'LAL-1.2', - 'LAL-1.3', - 'Latex2e', - 'Leptonica', - 'LGPL-2.0-only', - 'LGPL-2.0-or-later', - 'LGPL-2.1-only', - 'LGPL-2.1-or-later', - 'LGPL-3.0-only', - 'LGPL-3.0-or-later', - 'LGPLLR', - 'Libpng', - 'libtiff', - 'LiLiQ-P-1.1', - 'LiLiQ-R-1.1', - 'LiLiQ-Rplus-1.1', - 'Linux-OpenIB', - 'LPL-1.0', - 'LPL-1.02', - 'LPPL-1.0', - 'LPPL-1.1', - 'LPPL-1.2', - 'LPPL-1.3a', - 'LPPL-1.3c', - 'MakeIndex', - 'MirOS', - 'MIT-0', - 'MIT-advertising', - 'MIT-CMU', - 'MIT-enna', - 'MIT-feh', - 'MIT', - 'MITNFA', - 'Motosoto', - 'mpich2', - 'MPL-1.0', - 'MPL-1.1', - 'MPL-2.0-no-copyleft-exception', - 'MPL-2.0', - 'MS-PL', - 'MS-RL', - 'MTLL', - 'Multics', - 'Mup', - 'NASA-1.3', - 'Naumen', - 'NBPL-1.0', - 'NCSA', - 'Net-SNMP', - 'NetCDF', - 'Newsletr', - 'NGPL', - 'NLOD-1.0', - 'NLPL', - 'Nokia', - 'NOSL', - 'Noweb', - 'NPL-1.0', - 'NPL-1.1', - 'NPOSL-3.0', - 'NRL', - 'NTP', - 'OCCT-PL', - 'OCLC-2.0', - 'ODbL-1.0', - 'OFL-1.0', - 'OFL-1.1', - 'OGTSL', - 'OLDAP-1.1', - 'OLDAP-1.2', - 'OLDAP-1.3', - 'OLDAP-1.4', - 'OLDAP-2.0.1', - 'OLDAP-2.0', - 'OLDAP-2.1', - 'OLDAP-2.2.1', - 'OLDAP-2.2.2', - 'OLDAP-2.2', - 'OLDAP-2.3', - 'OLDAP-2.4', - 'OLDAP-2.5', - 'OLDAP-2.6', - 'OLDAP-2.7', - 'OLDAP-2.8', - 'OML', - 'OpenSSL', - 'OPL-1.0', - 'OSET-PL-2.1', - 'OSL-1.0', - 'OSL-1.1', - 'OSL-2.0', - 'OSL-2.1', - 'OSL-3.0', - 'PDDL-1.0', - 'PHP-3.0', - 'PHP-3.01', - 'Plexus', - 'PostgreSQL', - 'psfrag', - 'psutils', - 'Python-2.0', - 'Qhull', - 'QPL-1.0', - 'Rdisc', - 'RHeCos-1.1', - 'RPL-1.1', - 'RPL-1.5', - 'RPSL-1.0', - 'RSA-MD', - 'RSCPL', - 'Ruby', - 'SAX-PD', - 'Saxpath', - 'SCEA', - 'Sendmail', - 'SGI-B-1.0', - 'SGI-B-1.1', - 'SGI-B-2.0', - 'SimPL-2.0', - 'SISSL-1.2', - 'SISSL', - 'Sleepycat', - 'SMLNJ', - 'SMPPL', - 'SNIA', - 'Spencer-86', - 'Spencer-94', - 'Spencer-99', - 'SPL-1.0', - 'SugarCRM-1.1.3', - 'SWL', - 'TCL', - 'TCP-wrappers', - 'TMate', - 'TORQUE-1.1', - 'TOSL', - 'Unicode-DFS-2015', - 'Unicode-DFS-2016', - 'Unicode-TOU', - 'Unlicense', - 'UPL-1.0', - 'Vim', - 'VOSTROM', - 'VSL-1.0', - 'W3C-19980720', - 'W3C-20150513', - 'W3C', - 'Watcom-1.0', - 'Wsuipa', - 'WTFPL', - 'X11', - 'Xerox', - 'XFree86-1.1', - 'xinetd', - 'Xnet', - 'xpp', - 'XSkat', - 'YPL-1.0', - 'YPL-1.1', - 'Zed', - 'Zend-2.0', - 'Zimbra-1.3', - 'Zimbra-1.4', - 'zlib-acknowledgement', - 'Zlib', - 'ZPL-1.1', - 'ZPL-2.0', - 'ZPL-2.1', -]) - -/** Event timing codes defined by FHIR R4B. */ -export const eventTimingSchema: ZodType< - NonNullable[number] -> = z.enum([ - 'MORN', - 'MORN.early', - 'MORN.late', - 'NOON', - 'AFT', - 'AFT.early', - 'AFT.late', - 'EVE', - 'EVE.early', - 'EVE.late', - 'NIGHT', - 'PHS', - 'HS', - 'WAKE', - 'C', - 'CM', - 'CD', - 'CV', - 'AC', - 'ACM', - 'ACD', - 'ACV', - 'PC', - 'PCM', - 'PCD', - 'PCV', -]) diff --git a/packages/fhir/src/valueSets/financialResource.ts b/packages/fhir/src/valueSets/financialResource.ts deleted file mode 100644 index a435020f..00000000 --- a/packages/fhir/src/valueSets/financialResource.ts +++ /dev/null @@ -1,99 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * A code specifying the state of the resource instance. - * http://hl7.org/fhir/valueset-fm-status.html - */ -export const financialResourceStatusSchema = z.enum([ - 'active', - 'cancelled', - 'draft', - 'entered-in-error', -]) - -/** - * A code specifying the state of the resource instance. - * http://hl7.org/fhir/valueset-fm-status.html - */ -export type FinancialResourceStatus = z.infer< - typeof financialResourceStatusSchema -> - -/** - * A code specifying the state of a ChargeItemDefinition. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export const chargeItemDefinitionStatusSchema = z.enum([ - 'draft', - 'active', - 'retired', - 'unknown', -]) - -/** - * A code specifying the state of a ChargeItemDefinition. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export type ChargeItemDefinitionStatus = z.infer< - typeof chargeItemDefinitionStatusSchema -> - -/** - * Codes identifying the lifecycle stage of an Invoice. - * http://hl7.org/fhir/valueset-invoice-status.html - */ -export const invoiceStatusSchema = z.enum([ - 'draft', - 'issued', - 'balanced', - 'cancelled', - 'entered-in-error', -]) - -/** - * Codes identifying the lifecycle stage of an Invoice. - * http://hl7.org/fhir/valueset-invoice-status.html - */ -export type InvoiceStatus = z.infer - -/** - * Codes identifying the lifecycle stage of an InsurancePlan. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export const insurancePlanStatusSchema = z.enum([ - 'draft', - 'active', - 'retired', - 'unknown', -]) - -/** - * Codes identifying the lifecycle stage of an InsurancePlan. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export type InsurancePlanStatus = z.infer - -/** - * The processing outcome for the request. - * http://hl7.org/fhir/valueset-remittance-outcome.html - */ -export const remittanceOutcomeSchema = z.enum([ - 'queued', - 'complete', - 'error', - 'partial', -]) - -/** - * The processing outcome for the request. - * http://hl7.org/fhir/valueset-remittance-outcome.html - */ -export type RemittanceOutcome = z.infer diff --git a/packages/fhir/src/valueSets/flag.ts b/packages/fhir/src/valueSets/flag.ts deleted file mode 100644 index 7a875266..00000000 --- a/packages/fhir/src/valueSets/flag.ts +++ /dev/null @@ -1,25 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Indicates whether this flag is active and needs to be displayed to a user, or whether it is no longer needed or was entered in error. - * http://hl7.org/fhir/valueset-flag-status.html - */ -export const flagStatusSchema = z.enum([ - 'active', - 'inactive', - 'entered-in-error', -]) - -/** - * Indicates whether this flag is active and needs to be displayed to a user, or whether it is no longer needed or was entered in error. - * http://hl7.org/fhir/valueset-flag-status.html - */ -export type FlagStatus = z.infer diff --git a/packages/fhir/src/valueSets/goal.ts b/packages/fhir/src/valueSets/goal.ts deleted file mode 100644 index e3e7d83c..00000000 --- a/packages/fhir/src/valueSets/goal.ts +++ /dev/null @@ -1,31 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Codes that reflect the current state of a goal and whether the goal is still being targeted. - * http://hl7.org/fhir/valueset-goal-status.html - */ -export const goalLifecycleStatusSchema = z.enum([ - 'proposed', - 'planned', - 'accepted', - 'active', - 'on-hold', - 'completed', - 'cancelled', - 'entered-in-error', - 'rejected', -]) - -/** - * Codes that reflect the current state of a goal and whether the goal is still being targeted. - * http://hl7.org/fhir/valueset-goal-status.html - */ -export type GoalLifecycleStatus = z.infer diff --git a/packages/fhir/src/valueSets/graphDefinition.ts b/packages/fhir/src/valueSets/graphDefinition.ts deleted file mode 100644 index 80336035..00000000 --- a/packages/fhir/src/valueSets/graphDefinition.ts +++ /dev/null @@ -1,43 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Zod schema for FHIR GraphDefinitionLinkTargetCompartmentRule value set. - * How the compartment must match for the relationship to be included. - */ -export const graphDefinitionLinkTargetCompartmentRuleSchema = z.enum([ - 'identical', - 'matching', - 'different', - 'custom', -]) - -/** - * TypeScript type for FHIR GraphDefinitionLinkTargetCompartmentRule value set. - */ -export type GraphDefinitionLinkTargetCompartmentRule = z.infer< - typeof graphDefinitionLinkTargetCompartmentRuleSchema -> - -/** - * Zod schema for FHIR GraphDefinitionLinkTargetCompartmentUse value set. - * Whether the compartment rule is used to test for membership or to limit membership. - */ -export const graphDefinitionLinkTargetCompartmentUseSchema = z.enum([ - 'condition', - 'requirement', -]) - -/** - * TypeScript type for FHIR GraphDefinitionLinkTargetCompartmentUse value set. - */ -export type GraphDefinitionLinkTargetCompartmentUse = z.infer< - typeof graphDefinitionLinkTargetCompartmentUseSchema -> diff --git a/packages/fhir/src/valueSets/group.ts b/packages/fhir/src/valueSets/group.ts deleted file mode 100644 index b7a34818..00000000 --- a/packages/fhir/src/valueSets/group.ts +++ /dev/null @@ -1,28 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Types of resources that are part of group. - * http://hl7.org/fhir/valueset-group-type.html - */ -export const groupTypeSchema = z.enum([ - 'person', - 'animal', - 'practitioner', - 'device', - 'medication', - 'substance', -]) - -/** - * Types of resources that are part of group. - * http://hl7.org/fhir/valueset-group-type.html - */ -export type GroupType = z.infer diff --git a/packages/fhir/src/valueSets/guidanceResponse.ts b/packages/fhir/src/valueSets/guidanceResponse.ts deleted file mode 100644 index 56a3f40b..00000000 --- a/packages/fhir/src/valueSets/guidanceResponse.ts +++ /dev/null @@ -1,30 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the guidance response. - * http://hl7.org/fhir/valueset-guidance-response-status.html - */ -export const guidanceResponseStatusSchema = z.enum([ - 'success', - 'data-requested', - 'data-required', - 'in-progress', - 'failure', - 'entered-in-error', -]) - -/** - * The status of the guidance response. - * http://hl7.org/fhir/valueset-guidance-response-status.html - */ -export type GuidanceResponseStatus = z.infer< - typeof guidanceResponseStatusSchema -> diff --git a/packages/fhir/src/valueSets/identityAssuranceLevel.ts b/packages/fhir/src/valueSets/identityAssuranceLevel.ts deleted file mode 100644 index 0b564a45..00000000 --- a/packages/fhir/src/valueSets/identityAssuranceLevel.ts +++ /dev/null @@ -1,28 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The level of confidence that this link represents the same actual person. - * http://hl7.org/fhir/valueset-identity-assuranceLevel.html - */ -export const identityAssuranceLevelSchema = z.enum([ - 'level1', - 'level2', - 'level3', - 'level4', -]) - -/** - * The level of confidence that this link represents the same actual person. - * http://hl7.org/fhir/valueset-identity-assuranceLevel.html - */ -export type IdentityAssuranceLevel = z.infer< - typeof identityAssuranceLevelSchema -> diff --git a/packages/fhir/src/valueSets/imagingStudy.ts b/packages/fhir/src/valueSets/imagingStudy.ts deleted file mode 100644 index 89b374c6..00000000 --- a/packages/fhir/src/valueSets/imagingStudy.ts +++ /dev/null @@ -1,27 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the ImagingStudy. - * http://hl7.org/fhir/valueset-imagingstudy-status.html - */ -export const imagingStudyStatusSchema = z.enum([ - 'registered', - 'available', - 'cancelled', - 'entered-in-error', - 'unknown', -]) - -/** - * The status of the ImagingStudy. - * http://hl7.org/fhir/valueset-imagingstudy-status.html - */ -export type ImagingStudyStatus = z.infer diff --git a/packages/fhir/src/valueSets/immunization.ts b/packages/fhir/src/valueSets/immunization.ts deleted file mode 100644 index 0b7047cb..00000000 --- a/packages/fhir/src/valueSets/immunization.ts +++ /dev/null @@ -1,25 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the immunization event. - * http://hl7.org/fhir/valueset-immunization-status.html - */ -export const immunizationStatusSchema = z.enum([ - 'completed', - 'entered-in-error', - 'not-done', -]) - -/** - * The status of the immunization event. - * http://hl7.org/fhir/valueset-immunization-status.html - */ -export type ImmunizationStatus = z.infer diff --git a/packages/fhir/src/valueSets/immunizationEvaluation.ts b/packages/fhir/src/valueSets/immunizationEvaluation.ts deleted file mode 100644 index 795fb92b..00000000 --- a/packages/fhir/src/valueSets/immunizationEvaluation.ts +++ /dev/null @@ -1,26 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the immunization evaluation. - * http://hl7.org/fhir/valueset-immunization-evaluation-status.html - */ -export const immunizationEvaluationStatusSchema = z.enum([ - 'completed', - 'entered-in-error', -]) - -/** - * The status of the immunization evaluation. - * http://hl7.org/fhir/valueset-immunization-evaluation-status.html - */ -export type ImmunizationEvaluationStatus = z.infer< - typeof immunizationEvaluationStatusSchema -> diff --git a/packages/fhir/src/valueSets/implementationGuide.ts b/packages/fhir/src/valueSets/implementationGuide.ts deleted file mode 100644 index ed635ac3..00000000 --- a/packages/fhir/src/valueSets/implementationGuide.ts +++ /dev/null @@ -1,49 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * A code that indicates how the page is generated - * http://hl7.org/fhir/valueset-guide-page-generation.html - */ -export const guidePageGenerationSchema = z.enum([ - 'html', - 'markdown', - 'xml', - 'generated', -]) - -/** - * A code that indicates how the page is generated - * http://hl7.org/fhir/valueset-guide-page-generation.html - */ -export type GuidePageGeneration = z.infer - -/** - * Code of parameter that is input to the guide - * http://hl7.org/fhir/valueset-guide-parameter-code.html - */ -export const guideParameterCodeSchema = z.enum([ - 'apply', - 'path-resource', - 'path-pages', - 'path-tx-cache', - 'expansion-parameter', - 'rule-broken-links', - 'generate-xml', - 'generate-json', - 'generate-turtle', - 'html-template', -]) - -/** - * Code of parameter that is input to the guide - * http://hl7.org/fhir/valueset-guide-parameter-code.html - */ -export type GuideParameterCode = z.infer diff --git a/packages/fhir/src/valueSets/index.ts b/packages/fhir/src/valueSets/index.ts deleted file mode 100644 index ad38ecca..00000000 --- a/packages/fhir/src/valueSets/index.ts +++ /dev/null @@ -1,129 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -export * from './account.js' -export * from './actionParticipantType.js' -export * from './administrativeGender.js' -export * from './administrableProductDefinition.js' -export * from './adverseEvent.js' -export * from './allergyIntolerance.js' -export * from './appointment.js' -export * from './appointmentResponse.js' -export * from './biologicallyDerivedProduct.js' -export * from './bundle.js' -export * from './capabilityStatement.js' -export * from './carePlan.js' -export * from './careTeam.js' -export * from './catalogEntry.js' -export * from './chargeItem.js' -export * from './chargeItemDefinition.js' -export * from './claim.js' -export * from './claimResponse.js' -export * from './clinicalImpression.js' -export * from './clinicalUseDefinition.js' -export * from './codeSystem.js' -export * from './communication.js' -export * from './communicationRequest.js' -export * from './compartmentDefinition.js' -export * from './composition.js' -export * from './conceptMap.js' -export * from './consent.js' -export * from './contract.js' -export * from './coverage.js' -export * from './coverageEligibility.js' -export * from './daysOfWeek.js' -export * from './detectedIssue.js' -export * from './device.js' -export * from './deviceDefinition.js' -export * from './deviceMetric.js' -export * from './deviceRequest.js' -export * from './deviceUseStatement.js' -export * from './diagnosticReport.js' -export * from './documentManifest.js' -export * from './documentReference.js' -export * from './encounter.js' -export * from './endpoint.js' -export * from './episodeOfCare.js' -export * from './evidenceReport.js' -export * from './evidenceVariable.js' -export * from './exampleScenario.js' -export * from './explanationOfBenefit.js' -export * from './familyMemberHistory.js' -export * from './financialResource.js' -export * from './fhir.js' -export * from './flag.js' -export * from './goal.js' -export * from './graphDefinition.js' -export * from './group.js' -export * from './guidanceResponse.js' -export * from './identityAssuranceLevel.js' -export * from './imagingStudy.js' -export * from './implementationGuide.js' -export * from './immunization.js' -export * from './ingredient.js' -export * from './immunizationEvaluation.js' -export * from './invoice.js' -export * from './linkage.js' -export * from './list.js' -export * from './location.js' -export * from './manufacturedItemDefinition.js' -export * from './measure.js' -export * from './medication.js' -export * from './medicationAdministration.js' -export * from './medicationDispense.js' -export * from './medicationRequest.js' -export * from './medicationStatement.js' -export * from './media.js' -export * from './messageDefinition.js' -export * from './messageHeader.js' -export * from './molecularSequence.js' -export * from './namingSystem.js' -export * from './nutritionOrder.js' -export * from './nutritionProduct.js' -export * from './observation.js' -export * from './observationDefinition.js' -export * from './operationDefinition.js' -export * from './operationOutcome.js' -export * from './patient.js' -export * from './planDefinition.js' -export * from './procedure.js' -export * from './provenance.js' -export * from './publicationStatus.js' -export * from './questionnaire.js' -export * from './questionnaireResponse.js' -export * from './referenceVersionRules.js' -export * from './requestGroup.js' -export * from './requestIntent.js' -export * from './requestPriority.js' -export * from './requestResourceType.js' -export * from './researchElementDefinition.js' -export * from './researchStudy.js' -export * from './researchSubject.js' -export * from './riskAssessment.js' -export * from './searchParameter.js' -export * from './serviceRequest.js' -export * from './slot.js' -export * from './specimen.js' -export * from './specimenDefinition.js' -export * from './structureDefinition.js' -export * from './structureMap.js' -export * from './subscription.js' -export * from './subscriptionStatus.js' -export * from './subscriptionTopic.js' -export * from './substance.js' -export * from './supplyDelivery.js' -export * from './supplyRequest.js' -export * from './task.js' -export { - codeSearchSupportSchema, - type CodeSearchSupport, -} from './terminologyCapabilities.js' -export * from './testReport.js' -export * from './testScript.js' -export * from './verificationResult.js' -export * from './visionPrescription.js' diff --git a/packages/fhir/src/valueSets/ingredient.ts b/packages/fhir/src/valueSets/ingredient.ts deleted file mode 100644 index f572880b..00000000 --- a/packages/fhir/src/valueSets/ingredient.ts +++ /dev/null @@ -1,44 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Allows filtering of ingredient that are appropriate for use versus not. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export const ingredientStatusSchema = z.enum([ - 'draft', - 'active', - 'retired', - 'unknown', -]) - -/** - * Allows filtering of ingredient that are appropriate for use versus not. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export type IngredientStatus = z.infer - -/** - * The way in which a manufacturer is associated with the ingredient. - * http://hl7.org/fhir/valueset-ingredient-manufacturer-role.html - */ -export const ingredientManufacturerRoleSchema = z.enum([ - 'allowed', - 'possible', - 'actual', -]) - -/** - * The way in which a manufacturer is associated with the ingredient. - * http://hl7.org/fhir/valueset-ingredient-manufacturer-role.html - */ -export type IngredientManufacturerRole = z.infer< - typeof ingredientManufacturerRoleSchema -> diff --git a/packages/fhir/src/valueSets/invoice.ts b/packages/fhir/src/valueSets/invoice.ts deleted file mode 100644 index 84e96ab8..00000000 --- a/packages/fhir/src/valueSets/invoice.ts +++ /dev/null @@ -1,29 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Zod schema for FHIR InvoicePriceComponentType value set. - * The type of price component in an invoice. - */ -export const invoicePriceComponentTypeSchema = z.enum([ - 'base', - 'surcharge', - 'deduction', - 'discount', - 'tax', - 'informational', -]) - -/** - * TypeScript type for FHIR InvoicePriceComponentType value set. - */ -export type InvoicePriceComponentType = z.infer< - typeof invoicePriceComponentTypeSchema -> diff --git a/packages/fhir/src/valueSets/linkage.ts b/packages/fhir/src/valueSets/linkage.ts deleted file mode 100644 index f478f41a..00000000 --- a/packages/fhir/src/valueSets/linkage.ts +++ /dev/null @@ -1,25 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Distinguishes which item is "source of truth" (if any) and which items are no longer considered to be current representations. - * http://hl7.org/fhir/valueset-linkage-type.html - */ -export const linkageItemTypeSchema = z.enum([ - 'source', - 'alternate', - 'historical', -]) - -/** - * Distinguishes which item is "source of truth" (if any) and which items are no longer considered to be current representations. - * http://hl7.org/fhir/valueset-linkage-type.html - */ -export type LinkageItemType = z.infer diff --git a/packages/fhir/src/valueSets/list.ts b/packages/fhir/src/valueSets/list.ts deleted file mode 100644 index 59fe9183..00000000 --- a/packages/fhir/src/valueSets/list.ts +++ /dev/null @@ -1,37 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The current state of the list. - * http://hl7.org/fhir/valueset-list-status.html - */ -export const listStatusSchema = z.enum([ - 'current', - 'retired', - 'entered-in-error', -]) - -/** - * The current state of the list. - * http://hl7.org/fhir/valueset-list-status.html - */ -export type ListStatus = z.infer - -/** - * The processing mode that applies to this list. - * http://hl7.org/fhir/valueset-list-mode.html - */ -export const listModeSchema = z.enum(['working', 'snapshot', 'changes']) - -/** - * The processing mode that applies to this list. - * http://hl7.org/fhir/valueset-list-mode.html - */ -export type ListMode = z.infer diff --git a/packages/fhir/src/valueSets/location.ts b/packages/fhir/src/valueSets/location.ts deleted file mode 100644 index 9b452cb0..00000000 --- a/packages/fhir/src/valueSets/location.ts +++ /dev/null @@ -1,33 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of a location. - * http://hl7.org/fhir/valueset-location-status.html - */ -export const locationStatusSchema = z.enum(['active', 'suspended', 'inactive']) - -/** - * The status of a location. - * http://hl7.org/fhir/valueset-location-status.html - */ -export type LocationStatus = z.infer - -/** - * Indicates whether a resource instance represents a specific location or a class of locations. - * http://hl7.org/fhir/valueset-location-mode.html - */ -export const locationModeSchema = z.enum(['instance', 'kind']) - -/** - * Indicates whether a resource instance represents a specific location or a class of locations. - * http://hl7.org/fhir/valueset-location-mode.html - */ -export type LocationMode = z.infer diff --git a/packages/fhir/src/valueSets/manufacturedItemDefinition.ts b/packages/fhir/src/valueSets/manufacturedItemDefinition.ts deleted file mode 100644 index ee76b322..00000000 --- a/packages/fhir/src/valueSets/manufacturedItemDefinition.ts +++ /dev/null @@ -1,28 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Allows filtering of manufactured items that are appropriate for use versus not. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export const manufacturedItemDefinitionStatusSchema = z.enum([ - 'draft', - 'active', - 'retired', - 'unknown', -]) - -/** - * Allows filtering of manufactured items that are appropriate for use versus not. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export type ManufacturedItemDefinitionStatus = z.infer< - typeof manufacturedItemDefinitionStatusSchema -> diff --git a/packages/fhir/src/valueSets/measure.ts b/packages/fhir/src/valueSets/measure.ts deleted file mode 100644 index 51546a22..00000000 --- a/packages/fhir/src/valueSets/measure.ts +++ /dev/null @@ -1,77 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The scoring type of the measure. - * http://hl7.org/fhir/valueset-measure-scoring.html - */ -export const measureScoringSchema = z.enum([ - 'proportion', - 'ratio', - 'continuous-variable', - 'cohort', -]) - -/** - * The scoring type of the measure. - * http://hl7.org/fhir/valueset-measure-scoring.html - */ -export type MeasureScoring = z.infer - -/** - * The type of measure (includes codes from 2.16.840.1.113883.1.11.20368). - * http://hl7.org/fhir/valueset-measure-type.html - */ -export const measureTypeSchema = z.enum([ - 'process', - 'outcome', - 'structure', - 'patient-reported-outcome', - 'composite', -]) - -/** - * The type of measure (includes codes from 2.16.840.1.113883.1.11.20368). - * http://hl7.org/fhir/valueset-measure-type.html - */ -export type MeasureType = z.infer - -/** - * The status of the measure report. - * http://hl7.org/fhir/valueset-measure-report-status.html - */ -export const measureReportStatusSchema = z.enum([ - 'complete', - 'pending', - 'error', -]) - -/** - * The status of the measure report. - * http://hl7.org/fhir/valueset-measure-report-status.html - */ -export type MeasureReportStatus = z.infer - -/** - * The type of the measure report. - * http://hl7.org/fhir/valueset-measure-report-type.html - */ -export const measureReportTypeSchema = z.enum([ - 'individual', - 'subject-list', - 'summary', - 'data-collection', -]) - -/** - * The type of the measure report. - * http://hl7.org/fhir/valueset-measure-report-type.html - */ -export type MeasureReportType = z.infer diff --git a/packages/fhir/src/valueSets/media.ts b/packages/fhir/src/valueSets/media.ts deleted file mode 100644 index e94b560f..00000000 --- a/packages/fhir/src/valueSets/media.ts +++ /dev/null @@ -1,30 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the media resource. - * http://hl7.org/fhir/valueset-event-status.html - */ -export const mediaStatusSchema = z.enum([ - 'preparation', - 'in-progress', - 'not-done', - 'on-hold', - 'stopped', - 'completed', - 'entered-in-error', - 'unknown', -]) - -/** - * The status of the media resource. - * http://hl7.org/fhir/valueset-event-status.html - */ -export type MediaStatus = z.infer diff --git a/packages/fhir/src/valueSets/medication.ts b/packages/fhir/src/valueSets/medication.ts deleted file mode 100644 index c09c05f1..00000000 --- a/packages/fhir/src/valueSets/medication.ts +++ /dev/null @@ -1,25 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * A coded concept defining if the medication is in active use. - * http://hl7.org/fhir/valueset-medication-status.html - */ -export const medicationStatusSchema = z.enum([ - 'active', - 'inactive', - 'entered-in-error', -]) - -/** - * A coded concept defining if the medication is in active use. - * http://hl7.org/fhir/valueset-medication-status.html - */ -export type MedicationStatus = z.infer diff --git a/packages/fhir/src/valueSets/medicationAdministration.ts b/packages/fhir/src/valueSets/medicationAdministration.ts deleted file mode 100644 index 1f79709a..00000000 --- a/packages/fhir/src/valueSets/medicationAdministration.ts +++ /dev/null @@ -1,31 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * A code specifying the state of the medication administration. - * http://hl7.org/fhir/valueset-medication-admin-status.html - */ -export const medicationAdministrationStatusSchema = z.enum([ - 'in-progress', - 'not-done', - 'on-hold', - 'completed', - 'entered-in-error', - 'stopped', - 'unknown', -]) - -/** - * A code specifying the state of the medication administration. - * http://hl7.org/fhir/valueset-medication-admin-status.html - */ -export type MedicationAdministrationStatus = z.infer< - typeof medicationAdministrationStatusSchema -> diff --git a/packages/fhir/src/valueSets/medicationDispense.ts b/packages/fhir/src/valueSets/medicationDispense.ts deleted file mode 100644 index 695ee68a..00000000 --- a/packages/fhir/src/valueSets/medicationDispense.ts +++ /dev/null @@ -1,33 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * A code specifying the state of the dispense event. - * http://hl7.org/fhir/valueset-medicationdispense-status.html - */ -export const medicationDispenseStatusSchema = z.enum([ - 'preparation', - 'in-progress', - 'cancelled', - 'on-hold', - 'completed', - 'entered-in-error', - 'stopped', - 'declined', - 'unknown', -]) - -/** - * A code specifying the state of the dispense event. - * http://hl7.org/fhir/valueset-medicationdispense-status.html - */ -export type MedicationDispenseStatus = z.infer< - typeof medicationDispenseStatusSchema -> diff --git a/packages/fhir/src/valueSets/medicationRequest.ts b/packages/fhir/src/valueSets/medicationRequest.ts deleted file mode 100644 index 6f8add23..00000000 --- a/packages/fhir/src/valueSets/medicationRequest.ts +++ /dev/null @@ -1,55 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of a MedicationRequest. - * http://hl7.org/fhir/valueset-medicationrequest-status.html - */ -export const medicationRequestStatusSchema = z.enum([ - 'active', - 'on-hold', - 'cancelled', - 'completed', - 'entered-in-error', - 'stopped', - 'draft', - 'unknown', -]) - -/** - * The status of a MedicationRequest. - * http://hl7.org/fhir/valueset-medicationrequest-status.html - */ -export type MedicationRequestStatus = z.infer< - typeof medicationRequestStatusSchema -> - -/** - * The kind of medication order. - * http://hl7.org/fhir/valueset-medicationrequest-intent.html - */ -export const medicationRequestIntentSchema = z.enum([ - 'proposal', - 'plan', - 'order', - 'original-order', - 'reflex-order', - 'filler-order', - 'instance-order', - 'option', -]) - -/** - * The kind of medication order. - * http://hl7.org/fhir/valueset-medicationrequest-intent.html - */ -export type MedicationRequestIntent = z.infer< - typeof medicationRequestIntentSchema -> diff --git a/packages/fhir/src/valueSets/medicationStatement.ts b/packages/fhir/src/valueSets/medicationStatement.ts deleted file mode 100644 index 501122ed..00000000 --- a/packages/fhir/src/valueSets/medicationStatement.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * A code specifying the state of the medication statement. - * http://hl7.org/fhir/valueset-medication-statement-status.html - */ -export const medicationStatementStatusSchema = z.enum([ - 'active', - 'completed', - 'entered-in-error', - 'intended', - 'stopped', - 'on-hold', - 'unknown', - 'not-taken', -]) - -/** - * A code specifying the state of the medication statement. - * http://hl7.org/fhir/valueset-medication-statement-status.html - */ -export type MedicationStatementStatus = z.infer< - typeof medicationStatementStatusSchema -> diff --git a/packages/fhir/src/valueSets/messageDefinition.ts b/packages/fhir/src/valueSets/messageDefinition.ts deleted file mode 100644 index 589c2091..00000000 --- a/packages/fhir/src/valueSets/messageDefinition.ts +++ /dev/null @@ -1,44 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Zod schema for FHIR MessageDefinitionCategory value set. - * The category of message. - */ -export const messageDefinitionCategorySchema = z.enum([ - 'consequence', - 'currency', - 'notification', -]) - -/** - * TypeScript type for FHIR MessageDefinitionCategory value set. - */ -export type MessageDefinitionCategory = z.infer< - typeof messageDefinitionCategorySchema -> - -/** - * Zod schema for FHIR MessageDefinitionResponseRequired value set. - * Whether a response is required for a message. - */ -export const messageDefinitionResponseRequiredSchema = z.enum([ - 'always', - 'on-error', - 'never', - 'on-success', -]) - -/** - * TypeScript type for FHIR MessageDefinitionResponseRequired value set. - */ -export type MessageDefinitionResponseRequired = z.infer< - typeof messageDefinitionResponseRequiredSchema -> diff --git a/packages/fhir/src/valueSets/messageHeader.ts b/packages/fhir/src/valueSets/messageHeader.ts deleted file mode 100644 index 4208c70a..00000000 --- a/packages/fhir/src/valueSets/messageHeader.ts +++ /dev/null @@ -1,27 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the response to an individual message header. - * http://hl7.org/fhir/valueset-response-code.html - */ -export const messageHeaderResponseCodeSchema = z.enum([ - 'ok', - 'transient-error', - 'fatal-error', -]) - -/** - * The status of the response to an individual message header. - * http://hl7.org/fhir/valueset-response-code.html - */ -export type MessageHeaderResponseCode = z.infer< - typeof messageHeaderResponseCodeSchema -> diff --git a/packages/fhir/src/valueSets/molecularSequence.ts b/packages/fhir/src/valueSets/molecularSequence.ts deleted file mode 100644 index cedfe34c..00000000 --- a/packages/fhir/src/valueSets/molecularSequence.ts +++ /dev/null @@ -1,74 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Type of a sequence -- DNA, RNA, or amino acid sequence. - * http://hl7.org/fhir/valueset-sequence-type.html - */ -export const molecularSequenceTypeSchema = z.enum(['aa', 'dna', 'rna']) - -/** - * Type if a sequence -- DNA, RNA, or amino acid sequence. - * http://hl7.org/fhir/valueset-sequence-type.html - */ -export type MolecularSequenceType = z.infer - -/** - * Directionality of DNA ( +1/-1) relative to some reference. - * http://hl7.org/fhir/valueset-orientation-type.html - */ -export const orientationTypeSchema = z.enum(['sense', 'antisense']) - -/** - * Directionality of DNA ( +1/-1) relative to some reference. - * http://hl7.org/fhir/valueset-orientation-type.html - */ -export type OrientationType = z.infer - -/** - * Strand of DNA sequence. - * http://hl7.org/fhir/valueset-strand-type.html - */ -export const strandTypeSchema = z.enum(['watson', 'crick']) - -/** - * Strand of DNA sequence. - * http://hl7.org/fhir/valueset-strand-type.html - */ -export type StrandType = z.infer - -/** - * Type for quality report. - * http://hl7.org/fhir/valueset-quality-type.html - */ -export const qualityTypeSchema = z.enum(['indel', 'snp', 'unknown']) - -/** - * Type for quality report. - * http://hl7.org/fhir/valueset-quality-type.html - */ -export type QualityType = z.infer - -/** - * Type for access of external URI. - * http://hl7.org/fhir/valueset-repository-type.html - */ -export const repositoryTypeSchema = z.enum([ - 'directlink', - 'openapi', - 'login', - 'oauth', -]) - -/** - * Type for access of external URI. - * http://hl7.org/fhir/valueset-repository-type.html - */ -export type RepositoryType = z.infer diff --git a/packages/fhir/src/valueSets/namingSystem.ts b/packages/fhir/src/valueSets/namingSystem.ts deleted file mode 100644 index e2711e36..00000000 --- a/packages/fhir/src/valueSets/namingSystem.ts +++ /dev/null @@ -1,44 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Identifies the purpose of a naming system. - * http://hl7.org/fhir/valueset-namingsystem-type.html - */ -export const namingSystemKindSchema = z.enum([ - 'codesystem', - 'identifier', - 'root', -]) - -/** - * Identifies the purpose of a naming system. - * http://hl7.org/fhir/valueset-namingsystem-type.html - */ -export type NamingSystemKind = z.infer - -/** - * Identifies the style of unique identifier used to identify a namespace. - * http://hl7.org/fhir/valueset-namingsystem-identifier-type.html - */ -export const namingSystemUniqueIdTypeSchema = z.enum([ - 'oid', - 'uuid', - 'uri', - 'other', -]) - -/** - * Identifies the style of unique identifier used to identify a namespace. - * http://hl7.org/fhir/valueset-namingsystem-identifier-type.html - */ -export type NamingSystemUniqueIdType = z.infer< - typeof namingSystemUniqueIdTypeSchema -> diff --git a/packages/fhir/src/valueSets/nutritionOrder.ts b/packages/fhir/src/valueSets/nutritionOrder.ts deleted file mode 100644 index b7006d95..00000000 --- a/packages/fhir/src/valueSets/nutritionOrder.ts +++ /dev/null @@ -1,51 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The workflow status of the nutrition order/request. - * http://hl7.org/fhir/valueset-request-status.html - */ -export const nutritionOrderStatusSchema = z.enum([ - 'draft', - 'active', - 'on-hold', - 'revoked', - 'completed', - 'entered-in-error', - 'unknown', -]) - -/** - * The workflow status of the nutrition order/request. - * http://hl7.org/fhir/valueset-request-status.html - */ -export type NutritionOrderStatus = z.infer - -/** - * Indicates the level of authority/intentionality associated with the nutrition order. - * http://hl7.org/fhir/valueset-request-intent.html - */ -export const nutritionOrderIntentSchema = z.enum([ - 'proposal', - 'plan', - 'directive', - 'order', - 'original-order', - 'reflex-order', - 'filler-order', - 'instance-order', - 'option', -]) - -/** - * Indicates the level of authority/intentionality associated with the nutrition order. - * http://hl7.org/fhir/valueset-request-intent.html - */ -export type NutritionOrderIntent = z.infer diff --git a/packages/fhir/src/valueSets/nutritionProduct.ts b/packages/fhir/src/valueSets/nutritionProduct.ts deleted file mode 100644 index fac36dc6..00000000 --- a/packages/fhir/src/valueSets/nutritionProduct.ts +++ /dev/null @@ -1,27 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the nutrition product. - * http://hl7.org/fhir/valueset-nutrition-product-status.html - */ -export const nutritionProductStatusSchema = z.enum([ - 'active', - 'inactive', - 'entered-in-error', -]) - -/** - * The status of the nutrition product. - * http://hl7.org/fhir/valueset-nutrition-product-status.html - */ -export type NutritionProductStatus = z.infer< - typeof nutritionProductStatusSchema -> diff --git a/packages/fhir/src/valueSets/observation.ts b/packages/fhir/src/valueSets/observation.ts deleted file mode 100644 index 93b9f6ec..00000000 --- a/packages/fhir/src/valueSets/observation.ts +++ /dev/null @@ -1,30 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Codes providing the status of an observation. - * http://hl7.org/fhir/valueset-observation-status.html - */ -export const observationStatusSchema = z.enum([ - 'registered', - 'preliminary', - 'final', - 'amended', - 'corrected', - 'cancelled', - 'entered-in-error', - 'unknown', -]) - -/** - * Codes providing the status of an observation. - * http://hl7.org/fhir/valueset-observation-status.html - */ -export type ObservationStatus = z.infer diff --git a/packages/fhir/src/valueSets/observationDefinition.ts b/packages/fhir/src/valueSets/observationDefinition.ts deleted file mode 100644 index f2936376..00000000 --- a/packages/fhir/src/valueSets/observationDefinition.ts +++ /dev/null @@ -1,53 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The category of interval of values for continuous or ordinal observations. - * http://hl7.org/fhir/valueset-observation-range-category.html - */ -export const observationDefinitionRangeCategorySchema = z.enum([ - 'reference', - 'critical', - 'absolute', -]) - -/** - * The category of interval of values for continuous or ordinal observations. - * http://hl7.org/fhir/valueset-observation-range-category.html - */ -export type ObservationDefinitionRangeCategory = z.infer< - typeof observationDefinitionRangeCategorySchema -> - -/** - * Permitted data type for observation value. - * http://hl7.org/fhir/valueset-permitted-data-type.html - */ -export const observationDefinitionPermittedDataTypeSchema = z.enum([ - 'Quantity', - 'CodeableConcept', - 'string', - 'boolean', - 'integer', - 'Range', - 'Ratio', - 'SampledData', - 'time', - 'dateTime', - 'Period', -]) - -/** - * Permitted data type for observation value. - * http://hl7.org/fhir/valueset-permitted-data-type.html - */ -export type ObservationDefinitionPermittedDataType = z.infer< - typeof observationDefinitionPermittedDataTypeSchema -> diff --git a/packages/fhir/src/valueSets/operationDefinition.ts b/packages/fhir/src/valueSets/operationDefinition.ts deleted file mode 100644 index 6b4a2e24..00000000 --- a/packages/fhir/src/valueSets/operationDefinition.ts +++ /dev/null @@ -1,51 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Zod schema for FHIR BindingStrength value set. - * Indication of the degree of conformance expectations associated with a binding. - */ -export const bindingStrengthSchema = z.enum([ - 'required', - 'extensible', - 'preferred', - 'example', -]) - -/** - * TypeScript type for FHIR BindingStrength value set. - */ -export type BindingStrength = z.infer - -/** - * Zod schema for FHIR OperationDefinitionKind value set. - * Whether an operation is a normal operation or a query. - */ -export const operationDefinitionKindSchema = z.enum(['operation', 'query']) - -/** - * TypeScript type for FHIR OperationDefinitionKind value set. - */ -export type OperationDefinitionKind = z.infer< - typeof operationDefinitionKindSchema -> - -/** - * Zod schema for FHIR OperationDefinitionParameterUse value set. - * Whether an operation parameter is an input or an output parameter. - */ -export const operationDefinitionParameterUseSchema = z.enum(['in', 'out']) - -/** - * TypeScript type for FHIR OperationDefinitionParameterUse value set. - */ -export type OperationDefinitionParameterUse = z.infer< - typeof operationDefinitionParameterUseSchema -> diff --git a/packages/fhir/src/valueSets/operationOutcome.ts b/packages/fhir/src/valueSets/operationOutcome.ts deleted file mode 100644 index 7418ff6c..00000000 --- a/packages/fhir/src/valueSets/operationOutcome.ts +++ /dev/null @@ -1,69 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * How the issue affects the success of the action. - * http://hl7.org/fhir/valueset-issue-severity.html - */ -export const issueSeveritySchema = z.enum([ - 'fatal', - 'error', - 'warning', - 'information', -]) - -/** - * How the issue affects the success of the action. - * http://hl7.org/fhir/valueset-issue-severity.html - */ -export type IssueSeverity = z.infer - -/** - * A code that describes the type of issue. - * http://hl7.org/fhir/valueset-issue-type.html - */ -export const issueTypeSchema = z.enum([ - 'invalid', - 'structure', - 'required', - 'value', - 'invariant', - 'security', - 'login', - 'unknown', - 'expired', - 'forbidden', - 'suppressed', - 'processing', - 'not-supported', - 'duplicate', - 'multiple-matches', - 'not-found', - 'deleted', - 'too-long', - 'code-invalid', - 'extension', - 'too-costly', - 'business-rule', - 'conflict', - 'transient', - 'lock-error', - 'no-store', - 'exception', - 'timeout', - 'throttled', - 'informational', -]) - -/** - * A code that describes the type of issue. - * http://hl7.org/fhir/valueset-issue-type.html - */ -export type IssueType = z.infer diff --git a/packages/fhir/src/valueSets/patient.ts b/packages/fhir/src/valueSets/patient.ts deleted file mode 100644 index a40e2e6b..00000000 --- a/packages/fhir/src/valueSets/patient.ts +++ /dev/null @@ -1,26 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The type of link between this patient resource and another patient resource. - * http://hl7.org/fhir/valueset-link-type.html - */ -export const patientLinkTypeSchema = z.enum([ - 'replaced-by', - 'replaces', - 'refer', - 'seealso', -]) - -/** - * The type of link between this patient resource and another patient resource. - * http://hl7.org/fhir/valueset-link-type.html - */ -export type PatientLinkType = z.infer diff --git a/packages/fhir/src/valueSets/planDefinition.ts b/packages/fhir/src/valueSets/planDefinition.ts deleted file mode 100644 index 8d02a1ca..00000000 --- a/packages/fhir/src/valueSets/planDefinition.ts +++ /dev/null @@ -1,158 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The kind of condition for the action. - * http://hl7.org/fhir/valueset-action-condition-kind.html - */ -export const planDefinitionActionConditionKindSchema = z.enum([ - 'applicability', - 'start', - 'stop', -]) - -/** - * The kind of condition for the action. - * http://hl7.org/fhir/valueset-action-condition-kind.html - */ -export type PlanDefinitionActionConditionKind = z.infer< - typeof planDefinitionActionConditionKindSchema -> - -/** - * Defines organization behavior of a group. - * http://hl7.org/fhir/valueset-action-grouping-behavior.html - */ -export const planDefinitionActionGroupingBehaviorSchema = z.enum([ - 'visual-group', - 'logical-group', - 'sentence-group', -]) - -/** - * Defines organization behavior of a group. - * http://hl7.org/fhir/valueset-action-grouping-behavior.html - */ -export type PlanDefinitionActionGroupingBehavior = z.infer< - typeof planDefinitionActionGroupingBehaviorSchema -> - -/** - * Defines selection behavior of a group. - * http://hl7.org/fhir/valueset-action-selection-behavior.html - */ -export const planDefinitionActionSelectionBehaviorSchema = z.enum([ - 'any', - 'all', - 'all-or-none', - 'exactly-one', - 'at-most-one', - 'one-or-more', -]) - -/** - * Defines selection behavior of a group. - * http://hl7.org/fhir/valueset-action-selection-behavior.html - */ -export type PlanDefinitionActionSelectionBehavior = z.infer< - typeof planDefinitionActionSelectionBehaviorSchema -> - -/** - * Defines expectations around whether an action or action group is required. - * http://hl7.org/fhir/valueset-action-required-behavior.html - */ -export const planDefinitionActionRequiredBehaviorSchema = z.enum([ - 'must', - 'could', - 'must-unless-documented', -]) - -/** - * Defines expectations around whether an action or action group is required. - * http://hl7.org/fhir/valueset-action-required-behavior.html - */ -export type PlanDefinitionActionRequiredBehavior = z.infer< - typeof planDefinitionActionRequiredBehaviorSchema -> - -/** - * Defines selection frequency behavior for an action or group. - * http://hl7.org/fhir/valueset-action-precheck-behavior.html - */ -export const planDefinitionActionPrecheckBehaviorSchema = z.enum(['yes', 'no']) - -/** - * Defines selection frequency behavior for an action or group. - * http://hl7.org/fhir/valueset-action-precheck-behavior.html - */ -export type PlanDefinitionActionPrecheckBehavior = z.infer< - typeof planDefinitionActionPrecheckBehaviorSchema -> - -/** - * Defines behavior for an action or a group for how many times that item may be repeated. - * http://hl7.org/fhir/valueset-action-cardinality-behavior.html - */ -export const planDefinitionActionCardinalityBehaviorSchema = z.enum([ - 'single', - 'multiple', -]) - -/** - * Defines behavior for an action or a group for how many times that item may be repeated. - * http://hl7.org/fhir/valueset-action-cardinality-behavior.html - */ -export type PlanDefinitionActionCardinalityBehavior = z.infer< - typeof planDefinitionActionCardinalityBehaviorSchema -> - -/** - * Defines the types of relationships between actions. - * http://hl7.org/fhir/valueset-action-relationship-type.html - */ -export const planDefinitionActionRelationshipTypeSchema = z.enum([ - 'before-start', - 'before', - 'before-end', - 'concurrent-with-start', - 'concurrent', - 'concurrent-with-end', - 'after-start', - 'after', - 'after-end', -]) - -/** - * Defines the types of relationships between actions. - * http://hl7.org/fhir/valueset-action-relationship-type.html - */ -export type PlanDefinitionActionRelationshipType = z.infer< - typeof planDefinitionActionRelationshipTypeSchema -> - -/** - * The type of participant in the action. - * http://hl7.org/fhir/valueset-action-participant-type.html - */ -export const planDefinitionActionParticipantTypeSchema = z.enum([ - 'patient', - 'practitioner', - 'related-person', - 'device', -]) - -/** - * The type of participant in the action. - * http://hl7.org/fhir/valueset-action-participant-type.html - */ -export type PlanDefinitionActionParticipantType = z.infer< - typeof planDefinitionActionParticipantTypeSchema -> diff --git a/packages/fhir/src/valueSets/procedure.ts b/packages/fhir/src/valueSets/procedure.ts deleted file mode 100644 index 41f4787b..00000000 --- a/packages/fhir/src/valueSets/procedure.ts +++ /dev/null @@ -1,30 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * A code specifying the state of the procedure. - * http://hl7.org/fhir/valueset-event-status.html - */ -export const procedureStatusSchema = z.enum([ - 'preparation', - 'in-progress', - 'not-done', - 'on-hold', - 'stopped', - 'completed', - 'entered-in-error', - 'unknown', -]) - -/** - * A code specifying the state of the procedure. - * http://hl7.org/fhir/valueset-event-status.html - */ -export type ProcedureStatus = z.infer diff --git a/packages/fhir/src/valueSets/provenance.ts b/packages/fhir/src/valueSets/provenance.ts deleted file mode 100644 index 8c4b06e7..00000000 --- a/packages/fhir/src/valueSets/provenance.ts +++ /dev/null @@ -1,27 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * How an entity was used in an activity. - * http://hl7.org/fhir/valueset-provenance-entity-role.html - */ -export const provenanceEntityRoleSchema = z.enum([ - 'derivation', - 'revision', - 'quotation', - 'source', - 'removal', -]) - -/** - * How an entity was used in an activity. - * http://hl7.org/fhir/valueset-provenance-entity-role.html - */ -export type ProvenanceEntityRole = z.infer diff --git a/packages/fhir/src/valueSets/publicationStatus.ts b/packages/fhir/src/valueSets/publicationStatus.ts deleted file mode 100644 index cc4ae1b3..00000000 --- a/packages/fhir/src/valueSets/publicationStatus.ts +++ /dev/null @@ -1,26 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The lifecycle status of an artifact. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export const publicationStatusSchema = z.enum([ - 'draft', - 'active', - 'retired', - 'unknown', -]) - -/** - * The lifecycle status of an artifact. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export type PublicationStatus = z.infer diff --git a/packages/fhir/src/valueSets/questionnaire.ts b/packages/fhir/src/valueSets/questionnaire.ts deleted file mode 100644 index 5085c495..00000000 --- a/packages/fhir/src/valueSets/questionnaire.ts +++ /dev/null @@ -1,91 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The lifecycle status of an artifact. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export const questionnaireStatusSchema = z.enum([ - 'draft', - 'active', - 'retired', - 'unknown', -]) - -/** - * The lifecycle status of an artifact. - * http://hl7.org/fhir/valueset-publication-status.html - */ -export type QuestionnaireStatus = z.infer - -/** - * Distinguishes groups from questions and display text and indicates data type for questions. - * http://hl7.org/fhir/valueset-item-type.html - */ -export const questionnaireItemTypeSchema = z.enum([ - 'group', - 'display', - 'boolean', - 'decimal', - 'integer', - 'date', - 'dateTime', - 'time', - 'string', - 'text', - 'url', - 'choice', - 'open-choice', - 'attachment', - 'reference', - 'quantity', -]) - -/** - * Distinguishes groups from questions and display text and indicates data type for questions. - * http://hl7.org/fhir/valueset-item-type.html - */ -export type QuestionnaireItemType = z.infer - -/** - * Controls how multiple enableWhen values are interpreted - whether all or any must be true. - * http://hl7.org/fhir/valueset-questionnaire-enable-behavior.html - */ -export const questionnaireItemEnableBehaviorSchema = z.enum(['all', 'any']) - -/** - * Controls how multiple enableWhen values are interpreted - whether all or any must be true. - * http://hl7.org/fhir/valueset-questionnaire-enable-behavior.html - */ -export type QuestionnaireItemEnableBehavior = z.infer< - typeof questionnaireItemEnableBehaviorSchema -> - -/** - * The criteria by which a question is enabled. - * http://hl7.org/fhir/valueset-questionnaire-enable-operator.html - */ -export const questionnaireItemEnableWhenOperatorSchema = z.enum([ - 'exists', - '=', - '!=', - '<', - '<=', - '>', - '>=', -]) - -/** - * The criteria by which a question is enabled. - * http://hl7.org/fhir/valueset-questionnaire-enable-operator.html - */ -export type QuestionnaireItemEnableWhenOperator = z.infer< - typeof questionnaireItemEnableWhenOperatorSchema -> diff --git a/packages/fhir/src/valueSets/questionnaireResponse.ts b/packages/fhir/src/valueSets/questionnaireResponse.ts deleted file mode 100644 index 2ed29e14..00000000 --- a/packages/fhir/src/valueSets/questionnaireResponse.ts +++ /dev/null @@ -1,29 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the questionnaire response. - * http://hl7.org/fhir/valueset-questionnaire-answers-status.html - */ -export const questionnaireResponseStatusSchema = z.enum([ - 'in-progress', - 'completed', - 'amended', - 'entered-in-error', - 'stopped', -]) - -/** - * The status of the questionnaire response. - * http://hl7.org/fhir/valueset-questionnaire-answers-status.html - */ -export type QuestionnaireResponseStatus = z.infer< - typeof questionnaireResponseStatusSchema -> diff --git a/packages/fhir/src/valueSets/referenceVersionRules.ts b/packages/fhir/src/valueSets/referenceVersionRules.ts deleted file mode 100644 index b60ed476..00000000 --- a/packages/fhir/src/valueSets/referenceVersionRules.ts +++ /dev/null @@ -1,25 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Whether a reference needs to be version specific or version independent, or whether either can be used. - * http://hl7.org/fhir/valueset-reference-version-rules.html - */ -export const referenceVersionRulesSchema = z.enum([ - 'either', - 'independent', - 'specific', -]) - -/** - * Whether a reference needs to be version specific or version independent, or whether either can be used. - * http://hl7.org/fhir/valueset-reference-version-rules.html - */ -export type ReferenceVersionRules = z.infer diff --git a/packages/fhir/src/valueSets/requestGroup.ts b/packages/fhir/src/valueSets/requestGroup.ts deleted file mode 100644 index 5b12ae7c..00000000 --- a/packages/fhir/src/valueSets/requestGroup.ts +++ /dev/null @@ -1,181 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of the request group. - * http://hl7.org/fhir/valueset-request-status.html - */ -export const requestGroupStatusSchema = z.enum([ - 'draft', - 'active', - 'on-hold', - 'revoked', - 'completed', - 'entered-in-error', - 'unknown', -]) - -/** - * The status of the request group. - * http://hl7.org/fhir/valueset-request-status.html - */ -export type RequestGroupStatus = z.infer - -/** - * Codes indicating the intent of the request group. - * http://hl7.org/fhir/valueset-request-intent.html - */ -export const requestGroupIntentSchema = z.enum([ - 'proposal', - 'plan', - 'directive', - 'order', - 'original-order', - 'reflex-order', - 'filler-order', - 'instance-order', - 'option', -]) - -/** - * Codes indicating the intent of the request group. - * http://hl7.org/fhir/valueset-request-intent.html - */ -export type RequestGroupIntent = z.infer - -/** - * Defines the kinds of conditions that can appear on actions. - * http://hl7.org/fhir/valueset-action-condition-kind.html - */ -export const requestGroupActionConditionKindSchema = z.enum([ - 'applicability', - 'start', - 'stop', -]) - -/** - * Defines the kinds of conditions that can appear on actions. - * http://hl7.org/fhir/valueset-action-condition-kind.html - */ -export type RequestGroupActionConditionKind = z.infer< - typeof requestGroupActionConditionKindSchema -> - -/** - * Defines the types of relationships between actions. - * http://hl7.org/fhir/valueset-action-relationship-type.html - */ -export const requestGroupActionRelationshipTypeSchema = z.enum([ - 'before-start', - 'before', - 'before-end', - 'concurrent-with-start', - 'concurrent', - 'concurrent-with-end', - 'after-start', - 'after', - 'after-end', -]) - -/** - * Defines the types of relationships between actions. - * http://hl7.org/fhir/valueset-action-relationship-type.html - */ -export type RequestGroupActionRelationshipType = z.infer< - typeof requestGroupActionRelationshipTypeSchema -> - -/** - * Defines organization behavior of a group. - * http://hl7.org/fhir/valueset-action-grouping-behavior.html - */ -export const requestGroupActionGroupingBehaviorSchema = z.enum([ - 'visual-group', - 'logical-group', - 'sentence-group', -]) - -/** - * Defines organization behavior of a group. - * http://hl7.org/fhir/valueset-action-grouping-behavior.html - */ -export type RequestGroupActionGroupingBehavior = z.infer< - typeof requestGroupActionGroupingBehaviorSchema -> - -/** - * Defines selection behavior of a group. - * http://hl7.org/fhir/valueset-action-selection-behavior.html - */ -export const requestGroupActionSelectionBehaviorSchema = z.enum([ - 'any', - 'all', - 'all-or-none', - 'exactly-one', - 'at-most-one', - 'one-or-more', -]) - -/** - * Defines selection behavior of a group. - * http://hl7.org/fhir/valueset-action-selection-behavior.html - */ -export type RequestGroupActionSelectionBehavior = z.infer< - typeof requestGroupActionSelectionBehaviorSchema -> - -/** - * Defines expectations around whether an action or action group is required. - * http://hl7.org/fhir/valueset-action-required-behavior.html - */ -export const requestGroupActionRequiredBehaviorSchema = z.enum([ - 'must', - 'could', - 'must-unless-documented', -]) - -/** - * Defines expectations around whether an action or action group is required. - * http://hl7.org/fhir/valueset-action-required-behavior.html - */ -export type RequestGroupActionRequiredBehavior = z.infer< - typeof requestGroupActionRequiredBehaviorSchema -> - -/** - * Defines selection frequency behavior for an action or group. - * http://hl7.org/fhir/valueset-action-precheck-behavior.html - */ -export const requestGroupActionPrecheckBehaviorSchema = z.enum(['yes', 'no']) - -/** - * Defines selection frequency behavior for an action or group. - * http://hl7.org/fhir/valueset-action-precheck-behavior.html - */ -export type RequestGroupActionPrecheckBehavior = z.infer< - typeof requestGroupActionPrecheckBehaviorSchema -> - -/** - * Defines behavior for an action or a group for how many times that item may be repeated. - * http://hl7.org/fhir/valueset-action-cardinality-behavior.html - */ -export const requestGroupActionCardinalityBehaviorSchema = z.enum([ - 'single', - 'multiple', -]) - -/** - * Defines behavior for an action or a group for how many times that item may be repeated. - * http://hl7.org/fhir/valueset-action-cardinality-behavior.html - */ -export type RequestGroupActionCardinalityBehavior = z.infer< - typeof requestGroupActionCardinalityBehaviorSchema -> diff --git a/packages/fhir/src/valueSets/requestIntent.ts b/packages/fhir/src/valueSets/requestIntent.ts deleted file mode 100644 index c445eab0..00000000 --- a/packages/fhir/src/valueSets/requestIntent.ts +++ /dev/null @@ -1,31 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Codes indicating the level of authority/intentionality associated with a request. - * http://hl7.org/fhir/ValueSet/request-intent - */ -export const requestIntentSchema = z.enum([ - 'proposal', - 'plan', - 'directive', - 'order', - 'original-order', - 'reflex-order', - 'filler-order', - 'instance-order', - 'option', -]) - -/** - * Codes indicating the level of authority/intentionality associated with a request. - * http://hl7.org/fhir/ValueSet/request-intent - */ -export type RequestIntent = z.infer diff --git a/packages/fhir/src/valueSets/requestPriority.ts b/packages/fhir/src/valueSets/requestPriority.ts deleted file mode 100644 index 45ec3d66..00000000 --- a/packages/fhir/src/valueSets/requestPriority.ts +++ /dev/null @@ -1,26 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Identifies the level of importance to be assigned to actioning the request. - * http://hl7.org/fhir/valueset-request-priority.html - */ -export const requestPrioritySchema = z.enum([ - 'routine', - 'urgent', - 'asap', - 'stat', -]) - -/** - * Identifies the level of importance to be assigned to actioning the request. - * http://hl7.org/fhir/valueset-request-priority.html - */ -export type RequestPriority = z.infer diff --git a/packages/fhir/src/valueSets/requestResourceType.ts b/packages/fhir/src/valueSets/requestResourceType.ts deleted file mode 100644 index 4d456824..00000000 --- a/packages/fhir/src/valueSets/requestResourceType.ts +++ /dev/null @@ -1,37 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * A list of all the request resource types defined in this version of the FHIR specification. - * http://hl7.org/fhir/valueset-request-resource-types - */ -export const requestResourceTypeSchema = z.enum([ - 'Appointment', - 'AppointmentResponse', - 'CarePlan', - 'Claim', - 'CommunicationRequest', - 'Contract', - 'DeviceRequest', - 'EnrollmentRequest', - 'ImmunizationRecommendation', - 'MedicationRequest', - 'NutritionOrder', - 'ServiceRequest', - 'SupplyRequest', - 'Task', - 'VisionPrescription', -]) - -/** - * A list of all the request resource types defined in this version of the FHIR specification. - * http://hl7.org/fhir/valueset-request-resource-types - */ -export type RequestResourceType = z.infer diff --git a/packages/fhir/src/valueSets/researchElementDefinition.ts b/packages/fhir/src/valueSets/researchElementDefinition.ts deleted file mode 100644 index f878499c..00000000 --- a/packages/fhir/src/valueSets/researchElementDefinition.ts +++ /dev/null @@ -1,43 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * ResearchElementDefinition Type - * http://hl7.org/fhir/ValueSet/research-element-type - */ -export const researchElementDefinitionTypeSchema = z.enum([ - 'population', - 'exposure', - 'outcome', -]) - -/** - * ResearchElementDefinition Type - * http://hl7.org/fhir/ValueSet/research-element-type - */ -export type ResearchElementDefinitionType = z.infer< - typeof researchElementDefinitionTypeSchema -> - -/** - * Variable Type - * http://hl7.org/fhir/ValueSet/variable-type - */ -export const variableTypeSchema = z.enum([ - 'dichotomous', - 'continuous', - 'descriptive', -]) - -/** - * Variable Type - * http://hl7.org/fhir/ValueSet/variable-type - */ -export type VariableType = z.infer diff --git a/packages/fhir/src/valueSets/researchStudy.ts b/packages/fhir/src/valueSets/researchStudy.ts deleted file mode 100644 index e95e5b3b..00000000 --- a/packages/fhir/src/valueSets/researchStudy.ts +++ /dev/null @@ -1,72 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * ResearchStudy Status - * http://hl7.org/fhir/ValueSet/research-study-status - */ -export const researchStudyStatusSchema = z.enum([ - 'active', - 'administratively-completed', - 'approved', - 'closed-to-accrual', - 'closed-to-accrual-and-intervention', - 'completed', - 'disapproved', - 'in-review', - 'temporarily-closed-to-accrual', - 'temporarily-closed-to-accrual-and-intervention', - 'withdrawn', -]) - -/** - * ResearchStudy Status - * http://hl7.org/fhir/ValueSet/research-study-status - */ -export type ResearchStudyStatus = z.infer - -/** - * ResearchStudy Phase - * http://hl7.org/fhir/ValueSet/research-study-phase - */ -export const researchStudyPhaseSchema = z.enum([ - 'n-a', - 'early-phase-1', - 'phase-1', - 'phase-1-phase-2', - 'phase-2', - 'phase-2-phase-3', - 'phase-3', - 'phase-4', -]) - -/** - * ResearchStudy Phase - * http://hl7.org/fhir/ValueSet/research-study-phase - */ -export type ResearchStudyPhase = z.infer - -/** - * ResearchStudy Objective Type - * http://hl7.org/fhir/ValueSet/research-study-objective-type - */ -export const researchStudyObjectiveTypeSchema = z.enum([ - 'primary', - 'secondary', - 'exploratory', -]) - -/** - * ResearchStudy Objective Type - * http://hl7.org/fhir/ValueSet/research-study-objective-type - */ -export type ResearchStudyObjectiveType = z.infer< - typeof researchStudyObjectiveTypeSchema -> diff --git a/packages/fhir/src/valueSets/researchSubject.ts b/packages/fhir/src/valueSets/researchSubject.ts deleted file mode 100644 index ac4ae499..00000000 --- a/packages/fhir/src/valueSets/researchSubject.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * ResearchSubject Status - * http://hl7.org/fhir/ValueSet/research-subject-status - */ -export const researchSubjectStatusSchema = z.enum([ - 'candidate', - 'eligible', - 'follow-up', - 'ineligible', - 'not-registered', - 'off-study', - 'on-study', - 'on-study-intervention', - 'on-study-observation', - 'pending-on-study', - 'potential-candidate', - 'screening', - 'withdrawn', -]) - -/** - * ResearchSubject Status - * http://hl7.org/fhir/ValueSet/research-subject-status - */ -export type ResearchSubjectStatus = z.infer diff --git a/packages/fhir/src/valueSets/searchParameter.ts b/packages/fhir/src/valueSets/searchParameter.ts deleted file mode 100644 index 30692121..00000000 --- a/packages/fhir/src/valueSets/searchParameter.ts +++ /dev/null @@ -1,98 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Zod schema for FHIR SearchParameterComparator value set. - * Comparators supported for search parameters. - */ -export const searchParameterComparatorSchema = z.enum([ - 'eq', - 'ne', - 'gt', - 'lt', - 'ge', - 'le', - 'sa', - 'eb', - 'ap', -]) - -/** - * TypeScript type for FHIR SearchParameterComparator value set. - */ -export type SearchParameterComparator = z.infer< - typeof searchParameterComparatorSchema -> - -/** - * Zod schema for FHIR SearchParameterModifier value set. - * Modifiers supported for search parameters. - */ -export const searchParameterModifierSchema = z.enum([ - 'missing', - 'exact', - 'contains', - 'not', - 'text', - 'in', - 'not-in', - 'below', - 'above', - 'type', - 'identifier', - 'ofType', -]) - -/** - * TypeScript type for FHIR SearchParameterModifier value set. - */ -export type SearchParameterModifier = z.infer< - typeof searchParameterModifierSchema -> - -/** - * Zod schema for FHIR SearchParameterType value set. - * Data types allowed to be used for search parameters. - */ -export const searchParameterTypeSchema = z.enum([ - 'number', - 'date', - 'string', - 'token', - 'reference', - 'composite', - 'quantity', - 'uri', - 'special', -]) - -/** - * TypeScript type for FHIR SearchParameterType value set. - */ -export type SearchParameterType = z.infer - -/** - * Zod schema for FHIR SearchParameterXpathUsage value set. - * How XPath is used in search parameter evaluation. - */ -export const searchParameterXpathUsageSchema = z.enum([ - 'normal', - 'phonetic', - 'nearby', - 'distance', - 'other', -]) - -/** - * TypeScript type for FHIR SearchParameterXpathUsage value set. - */ -export type SearchParameterXpathUsage = z.infer< - typeof searchParameterXpathUsageSchema -> diff --git a/packages/fhir/src/valueSets/serviceRequest.ts b/packages/fhir/src/valueSets/serviceRequest.ts deleted file mode 100644 index 0e85b244..00000000 --- a/packages/fhir/src/valueSets/serviceRequest.ts +++ /dev/null @@ -1,51 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of a service request. - * http://hl7.org/fhir/valueset-request-status.html - */ -export const serviceRequestStatusSchema = z.enum([ - 'draft', - 'active', - 'on-hold', - 'revoked', - 'completed', - 'entered-in-error', - 'unknown', -]) - -/** - * The status of a service request. - * http://hl7.org/fhir/valueset-request-status.html - */ -export type ServiceRequestStatus = z.infer - -/** - * The kind of service request. - * http://hl7.org/fhir/valueset-request-intent.html - */ -export const serviceRequestIntentSchema = z.enum([ - 'proposal', - 'plan', - 'directive', - 'order', - 'original-order', - 'reflex-order', - 'filler-order', - 'instance-order', - 'option', -]) - -/** - * The kind of service request. - * http://hl7.org/fhir/valueset-request-intent.html - */ -export type ServiceRequestIntent = z.infer diff --git a/packages/fhir/src/valueSets/slot.ts b/packages/fhir/src/valueSets/slot.ts deleted file mode 100644 index dfa2be38..00000000 --- a/packages/fhir/src/valueSets/slot.ts +++ /dev/null @@ -1,27 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of a slot. - * http://hl7.org/fhir/valueset-slotstatus.html - */ -export const slotStatusSchema = z.enum([ - 'busy', - 'free', - 'busy-unavailable', - 'busy-tentative', - 'entered-in-error', -]) - -/** - * The status of a slot. - * http://hl7.org/fhir/valueset-slotstatus.html - */ -export type SlotStatus = z.infer diff --git a/packages/fhir/src/valueSets/specimen.ts b/packages/fhir/src/valueSets/specimen.ts deleted file mode 100644 index 279407e2..00000000 --- a/packages/fhir/src/valueSets/specimen.ts +++ /dev/null @@ -1,26 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The availability status of the specimen. - * http://hl7.org/fhir/valueset-specimen-status.html - */ -export const specimenStatusSchema = z.enum([ - 'available', - 'unavailable', - 'unsatisfactory', - 'entered-in-error', -]) - -/** - * The availability status of the specimen. - * http://hl7.org/fhir/valueset-specimen-status.html - */ -export type SpecimenStatus = z.infer diff --git a/packages/fhir/src/valueSets/specimenDefinition.ts b/packages/fhir/src/valueSets/specimenDefinition.ts deleted file mode 100644 index 4a5d9471..00000000 --- a/packages/fhir/src/valueSets/specimenDefinition.ts +++ /dev/null @@ -1,26 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Degree of preference of a type of conditioned specimen. - * http://hl7.org/fhir/valueset-specimen-contained-preference.html - */ -export const specimenDefinitionTypeTestedPreferenceSchema = z.enum([ - 'preferred', - 'alternate', -]) - -/** - * Degree of preference of a type of conditioned specimen. - * http://hl7.org/fhir/valueset-specimen-contained-preference.html - */ -export type SpecimenDefinitionTypeTestedPreference = z.infer< - typeof specimenDefinitionTypeTestedPreferenceSchema -> diff --git a/packages/fhir/src/valueSets/structureDefinition.ts b/packages/fhir/src/valueSets/structureDefinition.ts deleted file mode 100644 index 7df2d5f1..00000000 --- a/packages/fhir/src/valueSets/structureDefinition.ts +++ /dev/null @@ -1,139 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Defines the kind of structure that this definition is describing - * http://hl7.org/fhir/valueset-structure-definition-kind.html - */ -export const structureDefinitionKindSchema = z.enum([ - 'primitive-type', - 'complex-type', - 'resource', - 'logical', -]) - -/** - * Defines the kind of structure that this definition is describing - * http://hl7.org/fhir/valueset-structure-definition-kind.html - */ -export type StructureDefinitionKind = z.infer< - typeof structureDefinitionKindSchema -> - -/** - * How a structure is used in defining the extension - * http://hl7.org/fhir/valueset-extension-context-type.html - */ -export const extensionContextTypeSchema = z.enum([ - 'fhirpath', - 'element', - 'extension', -]) - -/** - * How a structure is used in defining the extension - * http://hl7.org/fhir/valueset-extension-context-type.html - */ -export type ExtensionContextType = z.infer - -/** - * How a type relates to its base definition - * http://hl7.org/fhir/valueset-type-derivation-rule.html - */ -export const structureDefinitionDerivationSchema = z.enum([ - 'specialization', - 'constraint', -]) - -/** - * How a type relates to its base definition - * http://hl7.org/fhir/valueset-type-derivation-rule.html - */ -export type StructureDefinitionDerivation = z.infer< - typeof structureDefinitionDerivationSchema -> - -/** - * SHALL applications comply with this constraint? - * http://hl7.org/fhir/valueset-constraint-severity.html - */ -export const constraintSeveritySchema = z.enum(['error', 'warning']) - -/** - * SHALL applications comply with this constraint? - * http://hl7.org/fhir/valueset-constraint-severity.html - */ -export type ConstraintSeverity = z.infer - -/** - * How a property is represented when serialized - * http://hl7.org/fhir/valueset-property-representation.html - */ -export const propertyRepresentationSchema = z.enum([ - 'xmlAttr', - 'xmlText', - 'typeAttr', - 'cdaText', - 'xhtml', -]) - -/** - * How a property is represented when serialized - * http://hl7.org/fhir/valueset-property-representation.html - */ -export type PropertyRepresentation = z.infer< - typeof propertyRepresentationSchema -> - -/** - * How slicing discriminators are interpreted - * http://hl7.org/fhir/valueset-discriminator-type.html - */ -export const discriminatorTypeSchema = z.enum([ - 'value', - 'exists', - 'pattern', - 'type', - 'profile', -]) - -/** - * How slicing discriminators are interpreted - * http://hl7.org/fhir/valueset-discriminator-type.html - */ -export type DiscriminatorType = z.infer - -/** - * How slicing rules are applied when slicing is used - * http://hl7.org/fhir/valueset-slicing-rules.html - */ -export const slicingRulesSchema = z.enum(['closed', 'open', 'openAtEnd']) - -/** - * How slicing rules are applied when slicing is used - * http://hl7.org/fhir/valueset-slicing-rules.html - */ -export type SlicingRules = z.infer - -/** - * How resource references can be aggregated - * http://hl7.org/fhir/valueset-resource-aggregation-mode.html - */ -export const aggregationModeSchema = z.enum([ - 'contained', - 'referenced', - 'bundled', -]) - -/** - * How resource references can be aggregated - * http://hl7.org/fhir/valueset-resource-aggregation-mode.html - */ -export type AggregationMode = z.infer diff --git a/packages/fhir/src/valueSets/structureMap.ts b/packages/fhir/src/valueSets/structureMap.ts deleted file mode 100644 index b3103e8e..00000000 --- a/packages/fhir/src/valueSets/structureMap.ts +++ /dev/null @@ -1,134 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Zod schema for FHIR StructureMapModelMode value set. - * How the referenced structure is used in the mapping. - */ -export const structureMapModelModeSchema = z.enum([ - 'source', - 'queried', - 'target', - 'produced', -]) - -/** - * TypeScript type for FHIR StructureMapModelMode value set. - */ -export type StructureMapModelMode = z.infer - -/** - * Zod schema for FHIR StructureMapGroupInputMode value set. - * Mode for input in a structure map group. - */ -export const structureMapGroupInputModeSchema = z.enum(['source', 'target']) - -/** - * TypeScript type for FHIR StructureMapGroupInputMode value set. - */ -export type StructureMapGroupInputMode = z.infer< - typeof structureMapGroupInputModeSchema -> - -/** - * Zod schema for FHIR StructureMapGroupTypeMode value set. - * Type handling in structure map groups. - */ -export const structureMapGroupTypeModeSchema = z.enum([ - 'none', - 'types', - 'type-and-types', -]) - -/** - * TypeScript type for FHIR StructureMapGroupTypeMode value set. - */ -export type StructureMapGroupTypeMode = z.infer< - typeof structureMapGroupTypeModeSchema -> - -/** - * Zod schema for FHIR StructureMapSourceListMode value set. - * If the source list has multiple items, how to handle them in the mapping. - */ -export const structureMapSourceListModeSchema = z.enum([ - 'first', - 'not_first', - 'last', - 'not_last', - 'only_one', -]) - -/** - * TypeScript type for FHIR StructureMapSourceListMode value set. - */ -export type StructureMapSourceListMode = z.infer< - typeof structureMapSourceListModeSchema -> - -/** - * Zod schema for FHIR StructureMapTargetContextType value set. - * How to interpret the context for a target element. - */ -export const structureMapTargetContextTypeSchema = z.enum(['type', 'variable']) - -/** - * TypeScript type for FHIR StructureMapTargetContextType value set. - */ -export type StructureMapTargetContextType = z.infer< - typeof structureMapTargetContextTypeSchema -> - -/** - * Zod schema for FHIR StructureMapTargetListMode value set. - * If the target list has multiple items, how to handle them in the mapping. - */ -export const structureMapTargetListModeSchema = z.enum([ - 'first', - 'share', - 'last', - 'collate', -]) - -/** - * TypeScript type for FHIR StructureMapTargetListMode value set. - */ -export type StructureMapTargetListMode = z.infer< - typeof structureMapTargetListModeSchema -> - -/** - * Zod schema for FHIR StructureMapTransform value set. - * How data is transformed in a structure map. - */ -export const structureMapTransformSchema = z.enum([ - 'create', - 'copy', - 'truncate', - 'escape', - 'cast', - 'append', - 'translate', - 'reference', - 'dateOp', - 'uuid', - 'pointer', - 'evaluate', - 'cc', - 'c', - 'qty', - 'id', - 'cp', -]) - -/** - * TypeScript type for FHIR StructureMapTransform value set. - */ -export type StructureMapTransform = z.infer diff --git a/packages/fhir/src/valueSets/subscription.ts b/packages/fhir/src/valueSets/subscription.ts deleted file mode 100644 index 59444840..00000000 --- a/packages/fhir/src/valueSets/subscription.ts +++ /dev/null @@ -1,48 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The status of a subscription. - * http://hl7.org/fhir/valueset-subscription-status.html - */ -export const subscriptionResourceStatusSchema = z.enum([ - 'requested', - 'active', - 'error', - 'off', -]) - -/** - * The status of a subscription. - * http://hl7.org/fhir/valueset-subscription-status.html - */ -export type SubscriptionResourceStatus = z.infer< - typeof subscriptionResourceStatusSchema -> - -/** - * The type of method used to execute a subscription. - * http://hl7.org/fhir/valueset-subscription-channel-type.html - */ -export const subscriptionChannelTypeSchema = z.enum([ - 'rest-hook', - 'websocket', - 'email', - 'sms', - 'message', -]) - -/** - * The type of method used to execute a subscription. - * http://hl7.org/fhir/valueset-subscription-channel-type.html - */ -export type SubscriptionChannelType = z.infer< - typeof subscriptionChannelTypeSchema -> diff --git a/packages/fhir/src/valueSets/subscriptionStatus.ts b/packages/fhir/src/valueSets/subscriptionStatus.ts deleted file mode 100644 index 4e4b6431..00000000 --- a/packages/fhir/src/valueSets/subscriptionStatus.ts +++ /dev/null @@ -1,46 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Zod schema for FHIR SubscriptionStatusStatus value set. - * The status of a subscription notification. - */ -export const subscriptionStatusStatusSchema = z.enum([ - 'requested', - 'active', - 'error', - 'off', -]) - -/** - * TypeScript type for FHIR SubscriptionStatusStatus value set. - */ -export type SubscriptionStatusStatus = z.infer< - typeof subscriptionStatusStatusSchema -> - -/** - * Zod schema for FHIR SubscriptionStatusType value set. - * The type of notification being sent in a subscription status. - */ -export const subscriptionStatusTypeSchema = z.enum([ - 'handshake', - 'heartbeat', - 'event-notification', - 'query-status', - 'query-event', -]) - -/** - * TypeScript type for FHIR SubscriptionStatusType value set. - */ -export type SubscriptionStatusType = z.infer< - typeof subscriptionStatusTypeSchema -> diff --git a/packages/fhir/src/valueSets/subscriptionTopic.ts b/packages/fhir/src/valueSets/subscriptionTopic.ts deleted file mode 100644 index e305578c..00000000 --- a/packages/fhir/src/valueSets/subscriptionTopic.ts +++ /dev/null @@ -1,70 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Zod schema for FHIR SubscriptionTopicCanFilterByModifier value set. - * Modifiers that can be applied to filter criteria in a subscription topic. - */ -export const subscriptionTopicCanFilterByModifierSchema = z.enum([ - '=', - 'eq', - 'ne', - 'gt', - 'lt', - 'ge', - 'le', - 'sa', - 'eb', - 'ap', - 'above', - 'below', - 'in', - 'not-in', - 'of-type', -]) - -/** - * TypeScript type for FHIR SubscriptionTopicCanFilterByModifier value set. - */ -export type SubscriptionTopicCanFilterByModifier = z.infer< - typeof subscriptionTopicCanFilterByModifierSchema -> - -/** - * Zod schema for FHIR SubscriptionTopicResourceTriggerInteraction value set. - * The type of interaction that triggers a subscription notification. - */ -export const subscriptionTopicResourceTriggerInteractionSchema = z.enum([ - 'create', - 'update', - 'delete', -]) - -/** - * TypeScript type for FHIR SubscriptionTopicResourceTriggerInteraction value set. - */ -export type SubscriptionTopicResourceTriggerInteraction = z.infer< - typeof subscriptionTopicResourceTriggerInteractionSchema -> - -/** - * Zod schema for FHIR SubscriptionTopicResourceTriggerQueryCriteriaResult value set. - * How query criteria are evaluated for a subscription topic. - */ -export const subscriptionTopicResourceTriggerQueryCriteriaResultSchema = z.enum( - ['test-passes', 'test-fails'], -) - -/** - * TypeScript type for FHIR SubscriptionTopicResourceTriggerQueryCriteriaResult value set. - */ -export type SubscriptionTopicResourceTriggerQueryCriteriaResult = z.infer< - typeof subscriptionTopicResourceTriggerQueryCriteriaResultSchema -> diff --git a/packages/fhir/src/valueSets/substance.ts b/packages/fhir/src/valueSets/substance.ts deleted file mode 100644 index ca3ce541..00000000 --- a/packages/fhir/src/valueSets/substance.ts +++ /dev/null @@ -1,25 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * A coded concept defining if the substance is active or inactive. - * http://hl7.org/fhir/valueset-substance-status.html - */ -export const substanceStatusSchema = z.enum([ - 'active', - 'inactive', - 'entered-in-error', -]) - -/** - * A coded concept defining if the substance is active or inactive. - * http://hl7.org/fhir/valueset-substance-status.html - */ -export type SubstanceStatus = z.infer diff --git a/packages/fhir/src/valueSets/supplyDelivery.ts b/packages/fhir/src/valueSets/supplyDelivery.ts deleted file mode 100644 index b0a6d3ff..00000000 --- a/packages/fhir/src/valueSets/supplyDelivery.ts +++ /dev/null @@ -1,26 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Status of the supply delivery - * http://hl7.org/fhir/valueset-supplydelivery-status.html - */ -export const supplyDeliveryStatusSchema = z.enum([ - 'in-progress', - 'completed', - 'abandoned', - 'entered-in-error', -]) - -/** - * Status of the supply delivery - * http://hl7.org/fhir/valueset-supplydelivery-status.html - */ -export type SupplyDeliveryStatus = z.infer diff --git a/packages/fhir/src/valueSets/supplyRequest.ts b/packages/fhir/src/valueSets/supplyRequest.ts deleted file mode 100644 index fa70f64e..00000000 --- a/packages/fhir/src/valueSets/supplyRequest.ts +++ /dev/null @@ -1,29 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Status of the supply request - * http://hl7.org/fhir/valueset-supplyrequest-status.html - */ -export const supplyRequestStatusSchema = z.enum([ - 'draft', - 'active', - 'suspended', - 'cancelled', - 'completed', - 'entered-in-error', - 'unknown', -]) - -/** - * Status of the supply request - * http://hl7.org/fhir/valueset-supplyrequest-status.html - */ -export type SupplyRequestStatus = z.infer diff --git a/packages/fhir/src/valueSets/task.ts b/packages/fhir/src/valueSets/task.ts deleted file mode 100644 index aad1f418..00000000 --- a/packages/fhir/src/valueSets/task.ts +++ /dev/null @@ -1,56 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The current status of the task. - * http://hl7.org/fhir/valueset-task-status.html - */ -export const taskStatusSchema = z.enum([ - 'draft', - 'requested', - 'received', - 'accepted', - 'rejected', - 'ready', - 'cancelled', - 'in-progress', - 'on-hold', - 'failed', - 'completed', - 'entered-in-error', -]) - -/** - * The current status of the task. - * http://hl7.org/fhir/valueset-task-status.html - */ -export type TaskStatus = z.infer - -/** - * Distinguishes whether the task is a proposal, plan or full order. - * http://hl7.org/fhir/valueset-task-intent.html - */ -export const taskIntentSchema = z.enum([ - 'unknown', - 'proposal', - 'plan', - 'order', - 'original-order', - 'reflex-order', - 'filler-order', - 'instance-order', - 'option', -]) - -/** - * Distinguishes whether the task is a proposal, plan or full order. - * http://hl7.org/fhir/valueset-task-intent.html - */ -export type TaskIntent = z.infer diff --git a/packages/fhir/src/valueSets/terminologyCapabilities.ts b/packages/fhir/src/valueSets/terminologyCapabilities.ts deleted file mode 100644 index 96eefcf7..00000000 --- a/packages/fhir/src/valueSets/terminologyCapabilities.ts +++ /dev/null @@ -1,21 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The type of search supported. - * http://hl7.org/fhir/valueset-code-search-support.html - */ -export const codeSearchSupportSchema = z.enum(['explicit', 'all']) - -/** - * The type of search supported. - * http://hl7.org/fhir/valueset-code-search-support.html - */ -export type CodeSearchSupport = z.infer diff --git a/packages/fhir/src/valueSets/testReport.ts b/packages/fhir/src/valueSets/testReport.ts deleted file mode 100644 index 20c63d7d..00000000 --- a/packages/fhir/src/valueSets/testReport.ts +++ /dev/null @@ -1,73 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * Zod schema for FHIR TestReportActionResult value set. - * The result of executing a test action. - */ -export const testReportActionResultSchema = z.enum([ - 'pass', - 'skip', - 'fail', - 'warning', - 'error', -]) - -/** - * TypeScript type for FHIR TestReportActionResult value set. - */ -export type TestReportActionResult = z.infer< - typeof testReportActionResultSchema -> - -/** - * Zod schema for FHIR TestReportParticipantType value set. - * The type of participant in a test report. - */ -export const testReportParticipantTypeSchema = z.enum([ - 'test-engine', - 'client', - 'server', -]) - -/** - * TypeScript type for FHIR TestReportParticipantType value set. - */ -export type TestReportParticipantType = z.infer< - typeof testReportParticipantTypeSchema -> - -/** - * Zod schema for FHIR TestReportResult value set. - * The overall result of a test report. - */ -export const testReportResultSchema = z.enum(['pass', 'fail', 'pending']) - -/** - * TypeScript type for FHIR TestReportResult value set. - */ -export type TestReportResult = z.infer - -/** - * Zod schema for FHIR TestReportStatus value set. - * The current status of a test report. - */ -export const testReportStatusSchema = z.enum([ - 'completed', - 'in-progress', - 'waiting', - 'stopped', - 'entered-in-error', -]) - -/** - * TypeScript type for FHIR TestReportStatus value set. - */ -export type TestReportStatus = z.infer diff --git a/packages/fhir/src/valueSets/testScript.ts b/packages/fhir/src/valueSets/testScript.ts deleted file mode 100644 index aae70944..00000000 --- a/packages/fhir/src/valueSets/testScript.ts +++ /dev/null @@ -1,131 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The type of direction to use for assertion - * http://hl7.org/fhir/valueset-assert-direction-codes.html - */ -export const assertDirectionSchema = z.enum(['request', 'response']) - -/** - * The type of direction to use for assertion - * http://hl7.org/fhir/valueset-assert-direction-codes.html - */ -export type AssertDirection = z.infer - -/** - * The operator type defines the conditional operator - * http://hl7.org/fhir/valueset-assert-operator-codes.html - */ -export const assertOperatorSchema = z.enum([ - 'equals', - 'notEquals', - 'in', - 'notIn', - 'greaterThan', - 'lessThan', - 'empty', - 'notEmpty', - 'contains', - 'notContains', - 'eval', -]) - -/** - * The operator type defines the conditional operator - * http://hl7.org/fhir/valueset-assert-operator-codes.html - */ -export type AssertOperator = z.infer - -/** - * HTTP request method codes - * http://hl7.org/fhir/valueset-http-verb.html - */ -export const httpVerbSchema = z.enum([ - 'GET', - 'HEAD', - 'POST', - 'PUT', - 'DELETE', - 'PATCH', -]) - -/** - * HTTP request method codes - * http://hl7.org/fhir/valueset-http-verb.html - */ -export type HttpVerb = z.infer - -/** - * Test script request method codes (lowercase) - * http://hl7.org/fhir/valueset-http-operations.html - */ -export const testScriptRequestMethodSchema = z.enum([ - 'delete', - 'get', - 'options', - 'patch', - 'post', - 'put', - 'head', -]) - -/** - * Test script request method codes (lowercase) - * http://hl7.org/fhir/valueset-http-operations.html - */ -export type TestScriptRequestMethod = z.infer< - typeof testScriptRequestMethodSchema -> - -/** - * assertion response code(s) for the operation - * http://hl7.org/fhir/valueset-assert-response-code-types.html - */ -export const assertResponseCodeSchema = z.enum([ - 'okay', - 'created', - 'noContent', - 'notModified', - 'bad', - 'forbidden', - 'notFound', - 'methodNotAllowed', - 'conflict', - 'gone', - 'preconditionFailed', - 'unprocessable', -]) - -/** - * assertion response code(s) for the operation - * http://hl7.org/fhir/valueset-assert-response-code-types.html - */ -export type AssertResponseCode = z.infer - -/** - * MIME content types - * http://hl7.org/fhir/valueset-mimetypes.html - */ -export const mimeTypeSchema = z.enum([ - 'application/json', - 'application/xml', - 'application/fhir+json', - 'application/fhir+xml', - 'text/html', - 'text/plain', - 'text/xml', -]) - -/** - * MIME content types - * http://hl7.org/fhir/valueset-mimetypes.html - */ -export type MimeType = z.infer diff --git a/packages/fhir/src/valueSets/verificationResult.ts b/packages/fhir/src/valueSets/verificationResult.ts deleted file mode 100644 index d08bb2e1..00000000 --- a/packages/fhir/src/valueSets/verificationResult.ts +++ /dev/null @@ -1,30 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * The validation status of the target. - * http://hl7.org/fhir/valueset-verificationresult-status.html - */ -export const verificationResultStatusSchema = z.enum([ - 'attested', - 'validated', - 'in-process', - 'req-revalid', - 'val-fail', - 'reval-fail', -]) - -/** - * The validation status of the target. - * http://hl7.org/fhir/valueset-verificationresult-status.html - */ -export type VerificationResultStatus = z.infer< - typeof verificationResultStatusSchema -> diff --git a/packages/fhir/src/valueSets/visionPrescription.ts b/packages/fhir/src/valueSets/visionPrescription.ts deleted file mode 100644 index 5efb942f..00000000 --- a/packages/fhir/src/valueSets/visionPrescription.ts +++ /dev/null @@ -1,52 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { z } from 'zod' - -/** - * A code specifying the state of the resource instance. - * http://hl7.org/fhir/valueset-fm-status.html - */ -export const visionPrescriptionStatusSchema = z.enum([ - 'active', - 'cancelled', - 'draft', - 'entered-in-error', -]) - -/** - * A code specifying the state of the resource instance. - * http://hl7.org/fhir/valueset-fm-status.html - */ -export type VisionPrescriptionStatus = z.infer< - typeof visionPrescriptionStatusSchema -> - -/** - * A coded concept listing the eye codes. - * http://hl7.org/fhir/valueset-vision-eye-codes.html - */ -export const visionEyesSchema = z.enum(['right', 'left']) - -/** - * A coded concept listing the eye codes. - * http://hl7.org/fhir/valueset-vision-eye-codes.html - */ -export type VisionEyes = z.infer - -/** - * A coded concept listing the base codes. - * http://hl7.org/fhir/valueset-vision-base-codes.html - */ -export const visionBaseSchema = z.enum(['up', 'down', 'in', 'out']) - -/** - * A coded concept listing the base codes. - * http://hl7.org/fhir/valueset-vision-base-codes.html - */ -export type VisionBase = z.infer diff --git a/packages/fhir/src/valueSets/riskAssessment.ts b/packages/fhir/src/zod/r4/index.ts similarity index 50% rename from packages/fhir/src/valueSets/riskAssessment.ts rename to packages/fhir/src/zod/r4/index.ts index f2b160b9..d320f7a3 100644 --- a/packages/fhir/src/valueSets/riskAssessment.ts +++ b/packages/fhir/src/zod/r4/index.ts @@ -6,6 +6,4 @@ // SPDX-License-Identifier: MIT // -// RiskAssessment uses the same status values as Observation -// The status schema is imported from observation.ts -export { observationStatusSchema as riskAssessmentStatusSchema } from './observation.js' +export * from './schemas.js' diff --git a/packages/fhir/src/zod/r4/schemas.ts b/packages/fhir/src/zod/r4/schemas.ts new file mode 100644 index 00000000..cfb1f553 --- /dev/null +++ b/packages/fhir/src/zod/r4/schemas.ts @@ -0,0 +1,31881 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// +// GENERATED FILE. Run `npm run generate:zod` after changing the generator or the release pin. +// + +/* eslint-disable sonarjs/regex-complexity, sonarjs/concise-regex, sonarjs/single-char-in-character-classes, sonarjs/single-character-alternation */ + +import type { + Account as FhirAccount, + AccountCoverage as FhirAccountCoverage, + AccountGuarantor as FhirAccountGuarantor, + ActivityDefinition as FhirActivityDefinition, + ActivityDefinitionDynamicValue as FhirActivityDefinitionDynamicValue, + ActivityDefinitionParticipant as FhirActivityDefinitionParticipant, + Address as FhirAddress, + AdverseEvent as FhirAdverseEvent, + AdverseEventSuspectEntity as FhirAdverseEventSuspectEntity, + AdverseEventSuspectEntityCausality as FhirAdverseEventSuspectEntityCausality, + Age as FhirAge, + AllergyIntolerance as FhirAllergyIntolerance, + AllergyIntoleranceReaction as FhirAllergyIntoleranceReaction, + Annotation as FhirAnnotation, + Appointment as FhirAppointment, + AppointmentParticipant as FhirAppointmentParticipant, + AppointmentResponse as FhirAppointmentResponse, + Attachment as FhirAttachment, + AuditEvent as FhirAuditEvent, + AuditEventAgent as FhirAuditEventAgent, + AuditEventAgentNetwork as FhirAuditEventAgentNetwork, + AuditEventEntity as FhirAuditEventEntity, + AuditEventEntityDetail as FhirAuditEventEntityDetail, + AuditEventSource as FhirAuditEventSource, + Basic as FhirBasic, + Binary as FhirBinary, + BiologicallyDerivedProduct as FhirBiologicallyDerivedProduct, + BiologicallyDerivedProductCollection as FhirBiologicallyDerivedProductCollection, + BiologicallyDerivedProductManipulation as FhirBiologicallyDerivedProductManipulation, + BiologicallyDerivedProductProcessing as FhirBiologicallyDerivedProductProcessing, + BiologicallyDerivedProductStorage as FhirBiologicallyDerivedProductStorage, + BodyStructure as FhirBodyStructure, + Bundle as FhirBundle, + BundleEntry as FhirBundleEntry, + BundleEntryRequest as FhirBundleEntryRequest, + BundleEntryResponse as FhirBundleEntryResponse, + BundleEntrySearch as FhirBundleEntrySearch, + BundleLink as FhirBundleLink, + CapabilityStatement as FhirCapabilityStatement, + CapabilityStatementDocument as FhirCapabilityStatementDocument, + CapabilityStatementImplementation as FhirCapabilityStatementImplementation, + CapabilityStatementMessaging as FhirCapabilityStatementMessaging, + CapabilityStatementMessagingEndpoint as FhirCapabilityStatementMessagingEndpoint, + CapabilityStatementMessagingSupportedMessage as FhirCapabilityStatementMessagingSupportedMessage, + CapabilityStatementRest as FhirCapabilityStatementRest, + CapabilityStatementRestInteraction as FhirCapabilityStatementRestInteraction, + CapabilityStatementRestResource as FhirCapabilityStatementRestResource, + CapabilityStatementRestResourceInteraction as FhirCapabilityStatementRestResourceInteraction, + CapabilityStatementRestResourceOperation as FhirCapabilityStatementRestResourceOperation, + CapabilityStatementRestResourceSearchParam as FhirCapabilityStatementRestResourceSearchParam, + CapabilityStatementRestSecurity as FhirCapabilityStatementRestSecurity, + CapabilityStatementSoftware as FhirCapabilityStatementSoftware, + CarePlan as FhirCarePlan, + CarePlanActivity as FhirCarePlanActivity, + CarePlanActivityDetail as FhirCarePlanActivityDetail, + CareTeam as FhirCareTeam, + CareTeamParticipant as FhirCareTeamParticipant, + CatalogEntry as FhirCatalogEntry, + CatalogEntryRelatedEntry as FhirCatalogEntryRelatedEntry, + ChargeItem as FhirChargeItem, + ChargeItemDefinition as FhirChargeItemDefinition, + ChargeItemDefinitionApplicability as FhirChargeItemDefinitionApplicability, + ChargeItemDefinitionPropertyGroup as FhirChargeItemDefinitionPropertyGroup, + ChargeItemDefinitionPropertyGroupPriceComponent as FhirChargeItemDefinitionPropertyGroupPriceComponent, + ChargeItemPerformer as FhirChargeItemPerformer, + Claim as FhirClaim, + ClaimAccident as FhirClaimAccident, + ClaimCareTeam as FhirClaimCareTeam, + ClaimDiagnosis as FhirClaimDiagnosis, + ClaimInsurance as FhirClaimInsurance, + ClaimItem as FhirClaimItem, + ClaimItemDetail as FhirClaimItemDetail, + ClaimItemDetailSubDetail as FhirClaimItemDetailSubDetail, + ClaimPayee as FhirClaimPayee, + ClaimProcedure as FhirClaimProcedure, + ClaimRelated as FhirClaimRelated, + ClaimResponse as FhirClaimResponse, + ClaimResponseAddItem as FhirClaimResponseAddItem, + ClaimResponseAddItemDetail as FhirClaimResponseAddItemDetail, + ClaimResponseAddItemDetailSubDetail as FhirClaimResponseAddItemDetailSubDetail, + ClaimResponseError as FhirClaimResponseError, + ClaimResponseInsurance as FhirClaimResponseInsurance, + ClaimResponseItem as FhirClaimResponseItem, + ClaimResponseItemAdjudication as FhirClaimResponseItemAdjudication, + ClaimResponseItemDetail as FhirClaimResponseItemDetail, + ClaimResponseItemDetailSubDetail as FhirClaimResponseItemDetailSubDetail, + ClaimResponsePayment as FhirClaimResponsePayment, + ClaimResponseProcessNote as FhirClaimResponseProcessNote, + ClaimResponseTotal as FhirClaimResponseTotal, + ClaimSupportingInfo as FhirClaimSupportingInfo, + ClinicalImpression as FhirClinicalImpression, + ClinicalImpressionFinding as FhirClinicalImpressionFinding, + ClinicalImpressionInvestigation as FhirClinicalImpressionInvestigation, + CodeSystem as FhirCodeSystem, + CodeSystemConcept as FhirCodeSystemConcept, + CodeSystemConceptDesignation as FhirCodeSystemConceptDesignation, + CodeSystemConceptProperty as FhirCodeSystemConceptProperty, + CodeSystemFilter as FhirCodeSystemFilter, + CodeSystemProperty as FhirCodeSystemProperty, + CodeableConcept as FhirCodeableConcept, + Coding as FhirCoding, + Communication as FhirCommunication, + CommunicationPayload as FhirCommunicationPayload, + CommunicationRequest as FhirCommunicationRequest, + CommunicationRequestPayload as FhirCommunicationRequestPayload, + CompartmentDefinition as FhirCompartmentDefinition, + CompartmentDefinitionResource as FhirCompartmentDefinitionResource, + Composition as FhirComposition, + CompositionAttester as FhirCompositionAttester, + CompositionEvent as FhirCompositionEvent, + CompositionRelatesTo as FhirCompositionRelatesTo, + CompositionSection as FhirCompositionSection, + ConceptMap as FhirConceptMap, + ConceptMapGroup as FhirConceptMapGroup, + ConceptMapGroupElement as FhirConceptMapGroupElement, + ConceptMapGroupElementTarget as FhirConceptMapGroupElementTarget, + ConceptMapGroupElementTargetDependsOn as FhirConceptMapGroupElementTargetDependsOn, + ConceptMapGroupUnmapped as FhirConceptMapGroupUnmapped, + Condition as FhirCondition, + ConditionEvidence as FhirConditionEvidence, + ConditionStage as FhirConditionStage, + Consent as FhirConsent, + ConsentPolicy as FhirConsentPolicy, + ConsentProvision as FhirConsentProvision, + ConsentProvisionActor as FhirConsentProvisionActor, + ConsentProvisionData as FhirConsentProvisionData, + ConsentVerification as FhirConsentVerification, + ContactDetail as FhirContactDetail, + ContactPoint as FhirContactPoint, + Contract as FhirContract, + ContractContentDefinition as FhirContractContentDefinition, + ContractFriendly as FhirContractFriendly, + ContractLegal as FhirContractLegal, + ContractRule as FhirContractRule, + ContractSigner as FhirContractSigner, + ContractTerm as FhirContractTerm, + ContractTermAction as FhirContractTermAction, + ContractTermActionSubject as FhirContractTermActionSubject, + ContractTermAsset as FhirContractTermAsset, + ContractTermAssetContext as FhirContractTermAssetContext, + ContractTermAssetValuedItem as FhirContractTermAssetValuedItem, + ContractTermOffer as FhirContractTermOffer, + ContractTermOfferAnswer as FhirContractTermOfferAnswer, + ContractTermOfferParty as FhirContractTermOfferParty, + ContractTermSecurityLabel as FhirContractTermSecurityLabel, + Contributor as FhirContributor, + Count as FhirCount, + Coverage as FhirCoverage, + CoverageClass as FhirCoverageClass, + CoverageCostToBeneficiary as FhirCoverageCostToBeneficiary, + CoverageCostToBeneficiaryException as FhirCoverageCostToBeneficiaryException, + CoverageEligibilityRequest as FhirCoverageEligibilityRequest, + CoverageEligibilityRequestInsurance as FhirCoverageEligibilityRequestInsurance, + CoverageEligibilityRequestItem as FhirCoverageEligibilityRequestItem, + CoverageEligibilityRequestItemDiagnosis as FhirCoverageEligibilityRequestItemDiagnosis, + CoverageEligibilityRequestSupportingInfo as FhirCoverageEligibilityRequestSupportingInfo, + CoverageEligibilityResponse as FhirCoverageEligibilityResponse, + CoverageEligibilityResponseError as FhirCoverageEligibilityResponseError, + CoverageEligibilityResponseInsurance as FhirCoverageEligibilityResponseInsurance, + CoverageEligibilityResponseInsuranceItem as FhirCoverageEligibilityResponseInsuranceItem, + CoverageEligibilityResponseInsuranceItemBenefit as FhirCoverageEligibilityResponseInsuranceItemBenefit, + DataRequirement as FhirDataRequirement, + DataRequirementCodeFilter as FhirDataRequirementCodeFilter, + DataRequirementDateFilter as FhirDataRequirementDateFilter, + DataRequirementSort as FhirDataRequirementSort, + DetectedIssue as FhirDetectedIssue, + DetectedIssueEvidence as FhirDetectedIssueEvidence, + DetectedIssueMitigation as FhirDetectedIssueMitigation, + Device as FhirDevice, + DeviceDefinition as FhirDeviceDefinition, + DeviceDefinitionCapability as FhirDeviceDefinitionCapability, + DeviceDefinitionDeviceName as FhirDeviceDefinitionDeviceName, + DeviceDefinitionMaterial as FhirDeviceDefinitionMaterial, + DeviceDefinitionProperty as FhirDeviceDefinitionProperty, + DeviceDefinitionSpecialization as FhirDeviceDefinitionSpecialization, + DeviceDefinitionUdiDeviceIdentifier as FhirDeviceDefinitionUdiDeviceIdentifier, + DeviceDeviceName as FhirDeviceDeviceName, + DeviceMetric as FhirDeviceMetric, + DeviceMetricCalibration as FhirDeviceMetricCalibration, + DeviceProperty as FhirDeviceProperty, + DeviceRequest as FhirDeviceRequest, + DeviceRequestParameter as FhirDeviceRequestParameter, + DeviceSpecialization as FhirDeviceSpecialization, + DeviceUdiCarrier as FhirDeviceUdiCarrier, + DeviceUseStatement as FhirDeviceUseStatement, + DeviceVersion as FhirDeviceVersion, + DiagnosticReport as FhirDiagnosticReport, + DiagnosticReportMedia as FhirDiagnosticReportMedia, + Distance as FhirDistance, + DocumentManifest as FhirDocumentManifest, + DocumentManifestRelated as FhirDocumentManifestRelated, + DocumentReference as FhirDocumentReference, + DocumentReferenceContent as FhirDocumentReferenceContent, + DocumentReferenceContext as FhirDocumentReferenceContext, + DocumentReferenceRelatesTo as FhirDocumentReferenceRelatesTo, + Dosage as FhirDosage, + DosageDoseAndRate as FhirDosageDoseAndRate, + Duration as FhirDuration, + EffectEvidenceSynthesis as FhirEffectEvidenceSynthesis, + EffectEvidenceSynthesisCertainty as FhirEffectEvidenceSynthesisCertainty, + EffectEvidenceSynthesisCertaintyCertaintySubcomponent as FhirEffectEvidenceSynthesisCertaintyCertaintySubcomponent, + EffectEvidenceSynthesisEffectEstimate as FhirEffectEvidenceSynthesisEffectEstimate, + EffectEvidenceSynthesisEffectEstimatePrecisionEstimate as FhirEffectEvidenceSynthesisEffectEstimatePrecisionEstimate, + EffectEvidenceSynthesisResultsByExposure as FhirEffectEvidenceSynthesisResultsByExposure, + EffectEvidenceSynthesisSampleSize as FhirEffectEvidenceSynthesisSampleSize, + Element as FhirElement, + ElementDefinition as FhirElementDefinition, + ElementDefinitionBase as FhirElementDefinitionBase, + ElementDefinitionBinding as FhirElementDefinitionBinding, + ElementDefinitionConstraint as FhirElementDefinitionConstraint, + ElementDefinitionExample as FhirElementDefinitionExample, + ElementDefinitionMapping as FhirElementDefinitionMapping, + ElementDefinitionSlicing as FhirElementDefinitionSlicing, + ElementDefinitionSlicingDiscriminator as FhirElementDefinitionSlicingDiscriminator, + ElementDefinitionType as FhirElementDefinitionType, + Encounter as FhirEncounter, + EncounterClassHistory as FhirEncounterClassHistory, + EncounterDiagnosis as FhirEncounterDiagnosis, + EncounterHospitalization as FhirEncounterHospitalization, + EncounterLocation as FhirEncounterLocation, + EncounterParticipant as FhirEncounterParticipant, + EncounterStatusHistory as FhirEncounterStatusHistory, + Endpoint as FhirEndpoint, + EnrollmentRequest as FhirEnrollmentRequest, + EnrollmentResponse as FhirEnrollmentResponse, + EpisodeOfCare as FhirEpisodeOfCare, + EpisodeOfCareDiagnosis as FhirEpisodeOfCareDiagnosis, + EpisodeOfCareStatusHistory as FhirEpisodeOfCareStatusHistory, + EventDefinition as FhirEventDefinition, + Evidence as FhirEvidence, + EvidenceVariable as FhirEvidenceVariable, + EvidenceVariableCharacteristic as FhirEvidenceVariableCharacteristic, + ExampleScenario as FhirExampleScenario, + ExampleScenarioActor as FhirExampleScenarioActor, + ExampleScenarioInstance as FhirExampleScenarioInstance, + ExampleScenarioInstanceContainedInstance as FhirExampleScenarioInstanceContainedInstance, + ExampleScenarioInstanceVersion as FhirExampleScenarioInstanceVersion, + ExampleScenarioProcess as FhirExampleScenarioProcess, + ExampleScenarioProcessStep as FhirExampleScenarioProcessStep, + ExampleScenarioProcessStepAlternative as FhirExampleScenarioProcessStepAlternative, + ExampleScenarioProcessStepOperation as FhirExampleScenarioProcessStepOperation, + ExplanationOfBenefit as FhirExplanationOfBenefit, + ExplanationOfBenefitAccident as FhirExplanationOfBenefitAccident, + ExplanationOfBenefitAddItem as FhirExplanationOfBenefitAddItem, + ExplanationOfBenefitAddItemDetail as FhirExplanationOfBenefitAddItemDetail, + ExplanationOfBenefitAddItemDetailSubDetail as FhirExplanationOfBenefitAddItemDetailSubDetail, + ExplanationOfBenefitBenefitBalance as FhirExplanationOfBenefitBenefitBalance, + ExplanationOfBenefitBenefitBalanceFinancial as FhirExplanationOfBenefitBenefitBalanceFinancial, + ExplanationOfBenefitCareTeam as FhirExplanationOfBenefitCareTeam, + ExplanationOfBenefitDiagnosis as FhirExplanationOfBenefitDiagnosis, + ExplanationOfBenefitInsurance as FhirExplanationOfBenefitInsurance, + ExplanationOfBenefitItem as FhirExplanationOfBenefitItem, + ExplanationOfBenefitItemAdjudication as FhirExplanationOfBenefitItemAdjudication, + ExplanationOfBenefitItemDetail as FhirExplanationOfBenefitItemDetail, + ExplanationOfBenefitItemDetailSubDetail as FhirExplanationOfBenefitItemDetailSubDetail, + ExplanationOfBenefitPayee as FhirExplanationOfBenefitPayee, + ExplanationOfBenefitPayment as FhirExplanationOfBenefitPayment, + ExplanationOfBenefitProcedure as FhirExplanationOfBenefitProcedure, + ExplanationOfBenefitProcessNote as FhirExplanationOfBenefitProcessNote, + ExplanationOfBenefitRelated as FhirExplanationOfBenefitRelated, + ExplanationOfBenefitSupportingInfo as FhirExplanationOfBenefitSupportingInfo, + ExplanationOfBenefitTotal as FhirExplanationOfBenefitTotal, + Expression as FhirExpression, + Extension as FhirExtension, + FamilyMemberHistory as FhirFamilyMemberHistory, + FamilyMemberHistoryCondition as FhirFamilyMemberHistoryCondition, + Flag as FhirFlag, + Goal as FhirGoal, + GoalTarget as FhirGoalTarget, + GraphDefinition as FhirGraphDefinition, + GraphDefinitionLink as FhirGraphDefinitionLink, + GraphDefinitionLinkTarget as FhirGraphDefinitionLinkTarget, + GraphDefinitionLinkTargetCompartment as FhirGraphDefinitionLinkTargetCompartment, + Group as FhirGroup, + GroupCharacteristic as FhirGroupCharacteristic, + GroupMember as FhirGroupMember, + GuidanceResponse as FhirGuidanceResponse, + HealthcareService as FhirHealthcareService, + HealthcareServiceAvailableTime as FhirHealthcareServiceAvailableTime, + HealthcareServiceEligibility as FhirHealthcareServiceEligibility, + HealthcareServiceNotAvailable as FhirHealthcareServiceNotAvailable, + HumanName as FhirHumanName, + Identifier as FhirIdentifier, + ImagingStudy as FhirImagingStudy, + ImagingStudySeries as FhirImagingStudySeries, + ImagingStudySeriesInstance as FhirImagingStudySeriesInstance, + ImagingStudySeriesPerformer as FhirImagingStudySeriesPerformer, + Immunization as FhirImmunization, + ImmunizationEducation as FhirImmunizationEducation, + ImmunizationEvaluation as FhirImmunizationEvaluation, + ImmunizationPerformer as FhirImmunizationPerformer, + ImmunizationProtocolApplied as FhirImmunizationProtocolApplied, + ImmunizationReaction as FhirImmunizationReaction, + ImmunizationRecommendation as FhirImmunizationRecommendation, + ImmunizationRecommendationRecommendation as FhirImmunizationRecommendationRecommendation, + ImmunizationRecommendationRecommendationDateCriterion as FhirImmunizationRecommendationRecommendationDateCriterion, + ImplementationGuide as FhirImplementationGuide, + ImplementationGuideDefinition as FhirImplementationGuideDefinition, + ImplementationGuideDefinitionGrouping as FhirImplementationGuideDefinitionGrouping, + ImplementationGuideDefinitionPage as FhirImplementationGuideDefinitionPage, + ImplementationGuideDefinitionParameter as FhirImplementationGuideDefinitionParameter, + ImplementationGuideDefinitionResource as FhirImplementationGuideDefinitionResource, + ImplementationGuideDefinitionTemplate as FhirImplementationGuideDefinitionTemplate, + ImplementationGuideDependsOn as FhirImplementationGuideDependsOn, + ImplementationGuideGlobal as FhirImplementationGuideGlobal, + ImplementationGuideManifest as FhirImplementationGuideManifest, + ImplementationGuideManifestPage as FhirImplementationGuideManifestPage, + ImplementationGuideManifestResource as FhirImplementationGuideManifestResource, + InsurancePlan as FhirInsurancePlan, + InsurancePlanContact as FhirInsurancePlanContact, + InsurancePlanCoverage as FhirInsurancePlanCoverage, + InsurancePlanCoverageBenefit as FhirInsurancePlanCoverageBenefit, + InsurancePlanCoverageBenefitLimit as FhirInsurancePlanCoverageBenefitLimit, + InsurancePlanPlan as FhirInsurancePlanPlan, + InsurancePlanPlanGeneralCost as FhirInsurancePlanPlanGeneralCost, + InsurancePlanPlanSpecificCost as FhirInsurancePlanPlanSpecificCost, + InsurancePlanPlanSpecificCostBenefit as FhirInsurancePlanPlanSpecificCostBenefit, + InsurancePlanPlanSpecificCostBenefitCost as FhirInsurancePlanPlanSpecificCostBenefitCost, + Invoice as FhirInvoice, + InvoiceLineItem as FhirInvoiceLineItem, + InvoiceLineItemPriceComponent as FhirInvoiceLineItemPriceComponent, + InvoiceParticipant as FhirInvoiceParticipant, + Library as FhirLibrary, + Linkage as FhirLinkage, + LinkageItem as FhirLinkageItem, + List as FhirList, + ListEntry as FhirListEntry, + Location as FhirLocation, + LocationHoursOfOperation as FhirLocationHoursOfOperation, + LocationPosition as FhirLocationPosition, + MarketingStatus as FhirMarketingStatus, + Measure as FhirMeasure, + MeasureGroup as FhirMeasureGroup, + MeasureGroupPopulation as FhirMeasureGroupPopulation, + MeasureGroupStratifier as FhirMeasureGroupStratifier, + MeasureGroupStratifierComponent as FhirMeasureGroupStratifierComponent, + MeasureReport as FhirMeasureReport, + MeasureReportGroup as FhirMeasureReportGroup, + MeasureReportGroupPopulation as FhirMeasureReportGroupPopulation, + MeasureReportGroupStratifier as FhirMeasureReportGroupStratifier, + MeasureReportGroupStratifierStratum as FhirMeasureReportGroupStratifierStratum, + MeasureReportGroupStratifierStratumComponent as FhirMeasureReportGroupStratifierStratumComponent, + MeasureReportGroupStratifierStratumPopulation as FhirMeasureReportGroupStratifierStratumPopulation, + MeasureSupplementalData as FhirMeasureSupplementalData, + Media as FhirMedia, + Medication as FhirMedication, + MedicationAdministration as FhirMedicationAdministration, + MedicationAdministrationDosage as FhirMedicationAdministrationDosage, + MedicationAdministrationPerformer as FhirMedicationAdministrationPerformer, + MedicationBatch as FhirMedicationBatch, + MedicationDispense as FhirMedicationDispense, + MedicationDispensePerformer as FhirMedicationDispensePerformer, + MedicationDispenseSubstitution as FhirMedicationDispenseSubstitution, + MedicationIngredient as FhirMedicationIngredient, + MedicationKnowledge as FhirMedicationKnowledge, + MedicationKnowledgeAdministrationGuidelines as FhirMedicationKnowledgeAdministrationGuidelines, + MedicationKnowledgeAdministrationGuidelinesDosage as FhirMedicationKnowledgeAdministrationGuidelinesDosage, + MedicationKnowledgeAdministrationGuidelinesPatientCharacteristics as FhirMedicationKnowledgeAdministrationGuidelinesPatientCharacteristics, + MedicationKnowledgeCost as FhirMedicationKnowledgeCost, + MedicationKnowledgeDrugCharacteristic as FhirMedicationKnowledgeDrugCharacteristic, + MedicationKnowledgeIngredient as FhirMedicationKnowledgeIngredient, + MedicationKnowledgeKinetics as FhirMedicationKnowledgeKinetics, + MedicationKnowledgeMedicineClassification as FhirMedicationKnowledgeMedicineClassification, + MedicationKnowledgeMonitoringProgram as FhirMedicationKnowledgeMonitoringProgram, + MedicationKnowledgeMonograph as FhirMedicationKnowledgeMonograph, + MedicationKnowledgePackaging as FhirMedicationKnowledgePackaging, + MedicationKnowledgeRegulatory as FhirMedicationKnowledgeRegulatory, + MedicationKnowledgeRegulatoryMaxDispense as FhirMedicationKnowledgeRegulatoryMaxDispense, + MedicationKnowledgeRegulatorySchedule as FhirMedicationKnowledgeRegulatorySchedule, + MedicationKnowledgeRegulatorySubstitution as FhirMedicationKnowledgeRegulatorySubstitution, + MedicationKnowledgeRelatedMedicationKnowledge as FhirMedicationKnowledgeRelatedMedicationKnowledge, + MedicationRequest as FhirMedicationRequest, + MedicationRequestDispenseRequest as FhirMedicationRequestDispenseRequest, + MedicationRequestDispenseRequestInitialFill as FhirMedicationRequestDispenseRequestInitialFill, + MedicationRequestSubstitution as FhirMedicationRequestSubstitution, + MedicationStatement as FhirMedicationStatement, + MedicinalProduct as FhirMedicinalProduct, + MedicinalProductAuthorization as FhirMedicinalProductAuthorization, + MedicinalProductAuthorizationJurisdictionalAuthorization as FhirMedicinalProductAuthorizationJurisdictionalAuthorization, + MedicinalProductAuthorizationProcedure as FhirMedicinalProductAuthorizationProcedure, + MedicinalProductContraindication as FhirMedicinalProductContraindication, + MedicinalProductContraindicationOtherTherapy as FhirMedicinalProductContraindicationOtherTherapy, + MedicinalProductIndication as FhirMedicinalProductIndication, + MedicinalProductIndicationOtherTherapy as FhirMedicinalProductIndicationOtherTherapy, + MedicinalProductIngredient as FhirMedicinalProductIngredient, + MedicinalProductIngredientSpecifiedSubstance as FhirMedicinalProductIngredientSpecifiedSubstance, + MedicinalProductIngredientSpecifiedSubstanceStrength as FhirMedicinalProductIngredientSpecifiedSubstanceStrength, + MedicinalProductIngredientSpecifiedSubstanceStrengthReferenceStrength as FhirMedicinalProductIngredientSpecifiedSubstanceStrengthReferenceStrength, + MedicinalProductIngredientSubstance as FhirMedicinalProductIngredientSubstance, + MedicinalProductInteraction as FhirMedicinalProductInteraction, + MedicinalProductInteractionInteractant as FhirMedicinalProductInteractionInteractant, + MedicinalProductManufactured as FhirMedicinalProductManufactured, + MedicinalProductManufacturingBusinessOperation as FhirMedicinalProductManufacturingBusinessOperation, + MedicinalProductName as FhirMedicinalProductName, + MedicinalProductNameCountryLanguage as FhirMedicinalProductNameCountryLanguage, + MedicinalProductNameNamePart as FhirMedicinalProductNameNamePart, + MedicinalProductPackaged as FhirMedicinalProductPackaged, + MedicinalProductPackagedBatchIdentifier as FhirMedicinalProductPackagedBatchIdentifier, + MedicinalProductPackagedPackageItem as FhirMedicinalProductPackagedPackageItem, + MedicinalProductPharmaceutical as FhirMedicinalProductPharmaceutical, + MedicinalProductPharmaceuticalCharacteristics as FhirMedicinalProductPharmaceuticalCharacteristics, + MedicinalProductPharmaceuticalRouteOfAdministration as FhirMedicinalProductPharmaceuticalRouteOfAdministration, + MedicinalProductPharmaceuticalRouteOfAdministrationTargetSpecies as FhirMedicinalProductPharmaceuticalRouteOfAdministrationTargetSpecies, + MedicinalProductPharmaceuticalRouteOfAdministrationTargetSpeciesWithdrawalPeriod as FhirMedicinalProductPharmaceuticalRouteOfAdministrationTargetSpeciesWithdrawalPeriod, + MedicinalProductSpecialDesignation as FhirMedicinalProductSpecialDesignation, + MedicinalProductUndesirableEffect as FhirMedicinalProductUndesirableEffect, + MessageDefinition as FhirMessageDefinition, + MessageDefinitionAllowedResponse as FhirMessageDefinitionAllowedResponse, + MessageDefinitionFocus as FhirMessageDefinitionFocus, + MessageHeader as FhirMessageHeader, + MessageHeaderDestination as FhirMessageHeaderDestination, + MessageHeaderResponse as FhirMessageHeaderResponse, + MessageHeaderSource as FhirMessageHeaderSource, + Meta as FhirMeta, + MolecularSequence as FhirMolecularSequence, + MolecularSequenceQuality as FhirMolecularSequenceQuality, + MolecularSequenceQualityRoc as FhirMolecularSequenceQualityRoc, + MolecularSequenceReferenceSeq as FhirMolecularSequenceReferenceSeq, + MolecularSequenceRepository as FhirMolecularSequenceRepository, + MolecularSequenceStructureVariant as FhirMolecularSequenceStructureVariant, + MolecularSequenceStructureVariantInner as FhirMolecularSequenceStructureVariantInner, + MolecularSequenceStructureVariantOuter as FhirMolecularSequenceStructureVariantOuter, + MolecularSequenceVariant as FhirMolecularSequenceVariant, + Money as FhirMoney, + NamingSystem as FhirNamingSystem, + NamingSystemUniqueId as FhirNamingSystemUniqueId, + Narrative as FhirNarrative, + NutritionOrder as FhirNutritionOrder, + NutritionOrderEnteralFormula as FhirNutritionOrderEnteralFormula, + NutritionOrderEnteralFormulaAdministration as FhirNutritionOrderEnteralFormulaAdministration, + NutritionOrderOralDiet as FhirNutritionOrderOralDiet, + NutritionOrderOralDietNutrient as FhirNutritionOrderOralDietNutrient, + NutritionOrderOralDietTexture as FhirNutritionOrderOralDietTexture, + NutritionOrderSupplement as FhirNutritionOrderSupplement, + Observation as FhirObservation, + ObservationComponent as FhirObservationComponent, + ObservationDefinition as FhirObservationDefinition, + ObservationDefinitionQualifiedInterval as FhirObservationDefinitionQualifiedInterval, + ObservationDefinitionQuantitativeDetails as FhirObservationDefinitionQuantitativeDetails, + ObservationReferenceRange as FhirObservationReferenceRange, + OperationDefinition as FhirOperationDefinition, + OperationDefinitionOverload as FhirOperationDefinitionOverload, + OperationDefinitionParameter as FhirOperationDefinitionParameter, + OperationDefinitionParameterBinding as FhirOperationDefinitionParameterBinding, + OperationDefinitionParameterReferencedFrom as FhirOperationDefinitionParameterReferencedFrom, + OperationOutcome as FhirOperationOutcome, + OperationOutcomeIssue as FhirOperationOutcomeIssue, + Organization as FhirOrganization, + OrganizationAffiliation as FhirOrganizationAffiliation, + OrganizationContact as FhirOrganizationContact, + ParameterDefinition as FhirParameterDefinition, + Parameters as FhirParameters, + ParametersParameter as FhirParametersParameter, + Patient as FhirPatient, + PatientCommunication as FhirPatientCommunication, + PatientContact as FhirPatientContact, + PatientLink as FhirPatientLink, + PaymentNotice as FhirPaymentNotice, + PaymentReconciliation as FhirPaymentReconciliation, + PaymentReconciliationDetail as FhirPaymentReconciliationDetail, + PaymentReconciliationProcessNote as FhirPaymentReconciliationProcessNote, + Period as FhirPeriod, + Person as FhirPerson, + PersonLink as FhirPersonLink, + PlanDefinition as FhirPlanDefinition, + PlanDefinitionAction as FhirPlanDefinitionAction, + PlanDefinitionActionCondition as FhirPlanDefinitionActionCondition, + PlanDefinitionActionDynamicValue as FhirPlanDefinitionActionDynamicValue, + PlanDefinitionActionParticipant as FhirPlanDefinitionActionParticipant, + PlanDefinitionActionRelatedAction as FhirPlanDefinitionActionRelatedAction, + PlanDefinitionGoal as FhirPlanDefinitionGoal, + PlanDefinitionGoalTarget as FhirPlanDefinitionGoalTarget, + Population as FhirPopulation, + Practitioner as FhirPractitioner, + PractitionerQualification as FhirPractitionerQualification, + PractitionerRole as FhirPractitionerRole, + PractitionerRoleAvailableTime as FhirPractitionerRoleAvailableTime, + PractitionerRoleNotAvailable as FhirPractitionerRoleNotAvailable, + Procedure as FhirProcedure, + ProcedureFocalDevice as FhirProcedureFocalDevice, + ProcedurePerformer as FhirProcedurePerformer, + ProdCharacteristic as FhirProdCharacteristic, + ProductShelfLife as FhirProductShelfLife, + Provenance as FhirProvenance, + ProvenanceAgent as FhirProvenanceAgent, + ProvenanceEntity as FhirProvenanceEntity, + Quantity as FhirQuantity, + Questionnaire as FhirQuestionnaire, + QuestionnaireItem as FhirQuestionnaireItem, + QuestionnaireItemAnswerOption as FhirQuestionnaireItemAnswerOption, + QuestionnaireItemEnableWhen as FhirQuestionnaireItemEnableWhen, + QuestionnaireItemInitial as FhirQuestionnaireItemInitial, + QuestionnaireResponse as FhirQuestionnaireResponse, + QuestionnaireResponseItem as FhirQuestionnaireResponseItem, + QuestionnaireResponseItemAnswer as FhirQuestionnaireResponseItemAnswer, + Range as FhirRange, + Ratio as FhirRatio, + Reference as FhirReference, + RelatedArtifact as FhirRelatedArtifact, + RelatedPerson as FhirRelatedPerson, + RelatedPersonCommunication as FhirRelatedPersonCommunication, + RequestGroup as FhirRequestGroup, + RequestGroupAction as FhirRequestGroupAction, + RequestGroupActionCondition as FhirRequestGroupActionCondition, + RequestGroupActionRelatedAction as FhirRequestGroupActionRelatedAction, + ResearchDefinition as FhirResearchDefinition, + ResearchElementDefinition as FhirResearchElementDefinition, + ResearchElementDefinitionCharacteristic as FhirResearchElementDefinitionCharacteristic, + ResearchStudy as FhirResearchStudy, + ResearchStudyArm as FhirResearchStudyArm, + ResearchStudyObjective as FhirResearchStudyObjective, + ResearchSubject as FhirResearchSubject, + Resource as FhirResource, + RiskAssessment as FhirRiskAssessment, + RiskAssessmentPrediction as FhirRiskAssessmentPrediction, + RiskEvidenceSynthesis as FhirRiskEvidenceSynthesis, + RiskEvidenceSynthesisCertainty as FhirRiskEvidenceSynthesisCertainty, + RiskEvidenceSynthesisCertaintyCertaintySubcomponent as FhirRiskEvidenceSynthesisCertaintyCertaintySubcomponent, + RiskEvidenceSynthesisRiskEstimate as FhirRiskEvidenceSynthesisRiskEstimate, + RiskEvidenceSynthesisRiskEstimatePrecisionEstimate as FhirRiskEvidenceSynthesisRiskEstimatePrecisionEstimate, + RiskEvidenceSynthesisSampleSize as FhirRiskEvidenceSynthesisSampleSize, + SampledData as FhirSampledData, + Schedule as FhirSchedule, + SearchParameter as FhirSearchParameter, + SearchParameterComponent as FhirSearchParameterComponent, + ServiceRequest as FhirServiceRequest, + Signature as FhirSignature, + Slot as FhirSlot, + Specimen as FhirSpecimen, + SpecimenCollection as FhirSpecimenCollection, + SpecimenContainer as FhirSpecimenContainer, + SpecimenDefinition as FhirSpecimenDefinition, + SpecimenDefinitionTypeTested as FhirSpecimenDefinitionTypeTested, + SpecimenDefinitionTypeTestedContainer as FhirSpecimenDefinitionTypeTestedContainer, + SpecimenDefinitionTypeTestedContainerAdditive as FhirSpecimenDefinitionTypeTestedContainerAdditive, + SpecimenDefinitionTypeTestedHandling as FhirSpecimenDefinitionTypeTestedHandling, + SpecimenProcessing as FhirSpecimenProcessing, + StructureDefinition as FhirStructureDefinition, + StructureDefinitionContext as FhirStructureDefinitionContext, + StructureDefinitionDifferential as FhirStructureDefinitionDifferential, + StructureDefinitionMapping as FhirStructureDefinitionMapping, + StructureDefinitionSnapshot as FhirStructureDefinitionSnapshot, + StructureMap as FhirStructureMap, + StructureMapGroup as FhirStructureMapGroup, + StructureMapGroupInput as FhirStructureMapGroupInput, + StructureMapGroupRule as FhirStructureMapGroupRule, + StructureMapGroupRuleDependent as FhirStructureMapGroupRuleDependent, + StructureMapGroupRuleSource as FhirStructureMapGroupRuleSource, + StructureMapGroupRuleTarget as FhirStructureMapGroupRuleTarget, + StructureMapGroupRuleTargetParameter as FhirStructureMapGroupRuleTargetParameter, + StructureMapStructure as FhirStructureMapStructure, + Subscription as FhirSubscription, + SubscriptionChannel as FhirSubscriptionChannel, + Substance as FhirSubstance, + SubstanceAmount as FhirSubstanceAmount, + SubstanceAmountReferenceRange as FhirSubstanceAmountReferenceRange, + SubstanceIngredient as FhirSubstanceIngredient, + SubstanceInstance as FhirSubstanceInstance, + SubstanceNucleicAcid as FhirSubstanceNucleicAcid, + SubstanceNucleicAcidSubunit as FhirSubstanceNucleicAcidSubunit, + SubstanceNucleicAcidSubunitLinkage as FhirSubstanceNucleicAcidSubunitLinkage, + SubstanceNucleicAcidSubunitSugar as FhirSubstanceNucleicAcidSubunitSugar, + SubstancePolymer as FhirSubstancePolymer, + SubstancePolymerMonomerSet as FhirSubstancePolymerMonomerSet, + SubstancePolymerMonomerSetStartingMaterial as FhirSubstancePolymerMonomerSetStartingMaterial, + SubstancePolymerRepeat as FhirSubstancePolymerRepeat, + SubstancePolymerRepeatRepeatUnit as FhirSubstancePolymerRepeatRepeatUnit, + SubstancePolymerRepeatRepeatUnitDegreeOfPolymerisation as FhirSubstancePolymerRepeatRepeatUnitDegreeOfPolymerisation, + SubstancePolymerRepeatRepeatUnitStructuralRepresentation as FhirSubstancePolymerRepeatRepeatUnitStructuralRepresentation, + SubstanceProtein as FhirSubstanceProtein, + SubstanceProteinSubunit as FhirSubstanceProteinSubunit, + SubstanceReferenceInformation as FhirSubstanceReferenceInformation, + SubstanceReferenceInformationClassification as FhirSubstanceReferenceInformationClassification, + SubstanceReferenceInformationGene as FhirSubstanceReferenceInformationGene, + SubstanceReferenceInformationGeneElement as FhirSubstanceReferenceInformationGeneElement, + SubstanceReferenceInformationTarget as FhirSubstanceReferenceInformationTarget, + SubstanceSourceMaterial as FhirSubstanceSourceMaterial, + SubstanceSourceMaterialFractionDescription as FhirSubstanceSourceMaterialFractionDescription, + SubstanceSourceMaterialOrganism as FhirSubstanceSourceMaterialOrganism, + SubstanceSourceMaterialOrganismAuthor as FhirSubstanceSourceMaterialOrganismAuthor, + SubstanceSourceMaterialOrganismHybrid as FhirSubstanceSourceMaterialOrganismHybrid, + SubstanceSourceMaterialOrganismOrganismGeneral as FhirSubstanceSourceMaterialOrganismOrganismGeneral, + SubstanceSourceMaterialPartDescription as FhirSubstanceSourceMaterialPartDescription, + SubstanceSpecification as FhirSubstanceSpecification, + SubstanceSpecificationCode as FhirSubstanceSpecificationCode, + SubstanceSpecificationMoiety as FhirSubstanceSpecificationMoiety, + SubstanceSpecificationName as FhirSubstanceSpecificationName, + SubstanceSpecificationNameOfficial as FhirSubstanceSpecificationNameOfficial, + SubstanceSpecificationProperty as FhirSubstanceSpecificationProperty, + SubstanceSpecificationRelationship as FhirSubstanceSpecificationRelationship, + SubstanceSpecificationStructure as FhirSubstanceSpecificationStructure, + SubstanceSpecificationStructureIsotope as FhirSubstanceSpecificationStructureIsotope, + SubstanceSpecificationStructureIsotopeMolecularWeight as FhirSubstanceSpecificationStructureIsotopeMolecularWeight, + SubstanceSpecificationStructureRepresentation as FhirSubstanceSpecificationStructureRepresentation, + SupplyDelivery as FhirSupplyDelivery, + SupplyDeliverySuppliedItem as FhirSupplyDeliverySuppliedItem, + SupplyRequest as FhirSupplyRequest, + SupplyRequestParameter as FhirSupplyRequestParameter, + Task as FhirTask, + TaskInput as FhirTaskInput, + TaskOutput as FhirTaskOutput, + TaskRestriction as FhirTaskRestriction, + TerminologyCapabilities as FhirTerminologyCapabilities, + TerminologyCapabilitiesClosure as FhirTerminologyCapabilitiesClosure, + TerminologyCapabilitiesCodeSystem as FhirTerminologyCapabilitiesCodeSystem, + TerminologyCapabilitiesCodeSystemVersion as FhirTerminologyCapabilitiesCodeSystemVersion, + TerminologyCapabilitiesCodeSystemVersionFilter as FhirTerminologyCapabilitiesCodeSystemVersionFilter, + TerminologyCapabilitiesExpansion as FhirTerminologyCapabilitiesExpansion, + TerminologyCapabilitiesExpansionParameter as FhirTerminologyCapabilitiesExpansionParameter, + TerminologyCapabilitiesImplementation as FhirTerminologyCapabilitiesImplementation, + TerminologyCapabilitiesSoftware as FhirTerminologyCapabilitiesSoftware, + TerminologyCapabilitiesTranslation as FhirTerminologyCapabilitiesTranslation, + TerminologyCapabilitiesValidateCode as FhirTerminologyCapabilitiesValidateCode, + TestReport as FhirTestReport, + TestReportParticipant as FhirTestReportParticipant, + TestReportSetup as FhirTestReportSetup, + TestReportSetupAction as FhirTestReportSetupAction, + TestReportSetupActionAssert as FhirTestReportSetupActionAssert, + TestReportSetupActionOperation as FhirTestReportSetupActionOperation, + TestReportTeardown as FhirTestReportTeardown, + TestReportTeardownAction as FhirTestReportTeardownAction, + TestReportTest as FhirTestReportTest, + TestReportTestAction as FhirTestReportTestAction, + TestScript as FhirTestScript, + TestScriptDestination as FhirTestScriptDestination, + TestScriptFixture as FhirTestScriptFixture, + TestScriptMetadata as FhirTestScriptMetadata, + TestScriptMetadataCapability as FhirTestScriptMetadataCapability, + TestScriptMetadataLink as FhirTestScriptMetadataLink, + TestScriptOrigin as FhirTestScriptOrigin, + TestScriptSetup as FhirTestScriptSetup, + TestScriptSetupAction as FhirTestScriptSetupAction, + TestScriptSetupActionAssert as FhirTestScriptSetupActionAssert, + TestScriptSetupActionOperation as FhirTestScriptSetupActionOperation, + TestScriptSetupActionOperationRequestHeader as FhirTestScriptSetupActionOperationRequestHeader, + TestScriptTeardown as FhirTestScriptTeardown, + TestScriptTeardownAction as FhirTestScriptTeardownAction, + TestScriptTest as FhirTestScriptTest, + TestScriptTestAction as FhirTestScriptTestAction, + TestScriptVariable as FhirTestScriptVariable, + Timing as FhirTiming, + TimingRepeat as FhirTimingRepeat, + TriggerDefinition as FhirTriggerDefinition, + UsageContext as FhirUsageContext, + ValueSet as FhirValueSet, + ValueSetCompose as FhirValueSetCompose, + ValueSetComposeInclude as FhirValueSetComposeInclude, + ValueSetComposeIncludeConcept as FhirValueSetComposeIncludeConcept, + ValueSetComposeIncludeConceptDesignation as FhirValueSetComposeIncludeConceptDesignation, + ValueSetComposeIncludeFilter as FhirValueSetComposeIncludeFilter, + ValueSetExpansion as FhirValueSetExpansion, + ValueSetExpansionContains as FhirValueSetExpansionContains, + ValueSetExpansionParameter as FhirValueSetExpansionParameter, + VerificationResult as FhirVerificationResult, + VerificationResultAttestation as FhirVerificationResultAttestation, + VerificationResultPrimarySource as FhirVerificationResultPrimarySource, + VerificationResultValidator as FhirVerificationResultValidator, + VisionPrescription as FhirVisionPrescription, + VisionPrescriptionLensSpecification as FhirVisionPrescriptionLensSpecification, + VisionPrescriptionLensSpecificationPrism as FhirVisionPrescriptionLensSpecificationPrism, +} from 'fhir/r4.js' +import { z } from 'zod' +import { fhirDateTimeToEpoch } from '../support.js' + +// Generated from hl7.fhir.r4.core#4.0.1 (FHIR 4.0.1). +// 206 structures: every type the exposed roots reach. + +export const accountCoverageSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + coverage: z.lazy(() => referenceSchema), + priority: positiveIntSchema.optional(), + _priority: z.lazy(() => elementSchema).optional(), + }), +) + +export const accountGuarantorSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + party: z.lazy(() => referenceSchema), + onHold: booleanSchema.optional(), + _onHold: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), +) + +export const accountSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Account'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'entered-in-error', + 'inactive', + 'on-hold', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + subject: z + .lazy(() => referenceSchema) + .array() + .optional(), + servicePeriod: z.lazy(() => periodSchema).optional(), + coverage: accountCoverageSchema.array().optional(), + owner: z.lazy(() => referenceSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + guarantor: accountGuarantorSchema.array().optional(), + partOf: z.lazy(() => referenceSchema).optional(), + }), +) + +export const activityDefinitionParticipantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['device', 'patient', 'practitioner', 'related-person']), + _type: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const activityDefinitionDynamicValueSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema, + _path: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema), + }), + ) + +export const activityDefinitionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ActivityDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + library: canonicalSchema.array().optional(), + _library: z + .lazy(() => elementSchema) + .array() + .optional(), + kind: z + .enum([ + 'Appointment', + 'AppointmentResponse', + 'CarePlan', + 'Claim', + 'CommunicationRequest', + 'Contract', + 'DeviceRequest', + 'EnrollmentRequest', + 'ImmunizationRecommendation', + 'MedicationRequest', + 'NutritionOrder', + 'ServiceRequest', + 'SupplyRequest', + 'Task', + 'VisionPrescription', + ]) + .optional(), + _kind: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + intent: z + .enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]) + .optional(), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + timingAge: z.lazy(() => ageSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingRange: z.lazy(() => rangeSchema).optional(), + timingDuration: z.lazy(() => durationSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + participant: activityDefinitionParticipantSchema.array().optional(), + productReference: z.lazy(() => referenceSchema).optional(), + productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + dosage: z + .lazy(() => dosageSchema) + .array() + .optional(), + bodySite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specimenRequirement: z + .lazy(() => referenceSchema) + .array() + .optional(), + observationRequirement: z + .lazy(() => referenceSchema) + .array() + .optional(), + observationResultRequirement: z + .lazy(() => referenceSchema) + .array() + .optional(), + transform: canonicalSchema.optional(), + _transform: z.lazy(() => elementSchema).optional(), + dynamicValue: activityDefinitionDynamicValueSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: ['subjectCodeableConcept', 'subjectReference'], + required: false, + }, + { + names: [ + 'timingTiming', + 'timingDateTime', + 'timingAge', + 'timingPeriod', + 'timingRange', + 'timingDuration', + ], + required: false, + }, + { + names: ['productReference', 'productCodeableConcept'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const addressSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + use: z.enum(['billing', 'home', 'old', 'temp', 'work']).optional(), + _use: z.lazy(() => elementSchema).optional(), + type: z.enum(['both', 'physical', 'postal']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + line: stringSchema.array().optional(), + _line: z + .lazy(() => elementSchema) + .array() + .optional(), + city: stringSchema.optional(), + _city: z.lazy(() => elementSchema).optional(), + district: stringSchema.optional(), + _district: z.lazy(() => elementSchema).optional(), + state: stringSchema.optional(), + _state: z.lazy(() => elementSchema).optional(), + postalCode: stringSchema.optional(), + _postalCode: z.lazy(() => elementSchema).optional(), + country: stringSchema.optional(), + _country: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), +) + +export const adverseEventSuspectEntitySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + instance: z.lazy(() => referenceSchema), + causality: adverseEventSuspectEntityCausalitySchema.array().optional(), + }), + ) + +export const adverseEventSuspectEntityCausalitySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + assessment: z.lazy(() => codeableConceptSchema).optional(), + productRelatedness: stringSchema.optional(), + _productRelatedness: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const adverseEventSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('AdverseEvent'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + actuality: z.enum(['actual', 'potential']), + _actuality: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + event: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + detected: dateTimeSchema.optional(), + _detected: z.lazy(() => elementSchema).optional(), + recordedDate: dateTimeSchema.optional(), + _recordedDate: z.lazy(() => elementSchema).optional(), + resultingCondition: z + .lazy(() => referenceSchema) + .array() + .optional(), + location: z.lazy(() => referenceSchema).optional(), + seriousness: z.lazy(() => codeableConceptSchema).optional(), + severity: z.lazy(() => codeableConceptSchema).optional(), + outcome: z.lazy(() => codeableConceptSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + contributor: z + .lazy(() => referenceSchema) + .array() + .optional(), + suspectEntity: adverseEventSuspectEntitySchema.array().optional(), + subjectMedicalHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + referenceDocument: z + .lazy(() => referenceSchema) + .array() + .optional(), + study: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const ageSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }), +) + +export const allergyIntoleranceReactionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + substance: z.lazy(() => codeableConceptSchema).optional(), + manifestation: z + .lazy(() => codeableConceptSchema) + .array() + .min(1), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + onset: dateTimeSchema.optional(), + _onset: z.lazy(() => elementSchema).optional(), + severity: z.enum(['mild', 'moderate', 'severe']).optional(), + _severity: z.lazy(() => elementSchema).optional(), + exposureRoute: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), + ) + +export const allergyIntoleranceSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('AllergyIntolerance'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + clinicalStatus: z.lazy(() => codeableConceptSchema).optional(), + verificationStatus: z.lazy(() => codeableConceptSchema).optional(), + type: z.enum(['allergy', 'intolerance']).optional(), + _type: z.lazy(() => elementSchema).optional(), + category: z + .enum(['biologic', 'environment', 'food', 'medication']) + .array() + .optional(), + _category: z + .lazy(() => elementSchema) + .array() + .optional(), + criticality: z.enum(['high', 'low', 'unable-to-assess']).optional(), + _criticality: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + patient: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + onsetDateTime: dateTimeSchema.optional(), + _onsetDateTime: z.lazy(() => elementSchema).optional(), + onsetAge: z.lazy(() => ageSchema).optional(), + onsetPeriod: z.lazy(() => periodSchema).optional(), + onsetRange: z.lazy(() => rangeSchema).optional(), + onsetString: stringSchema.optional(), + _onsetString: z.lazy(() => elementSchema).optional(), + recordedDate: dateTimeSchema.optional(), + _recordedDate: z.lazy(() => elementSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + asserter: z.lazy(() => referenceSchema).optional(), + lastOccurrence: dateTimeSchema.optional(), + _lastOccurrence: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + reaction: allergyIntoleranceReactionSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'onsetDateTime', + 'onsetAge', + 'onsetPeriod', + 'onsetRange', + 'onsetString', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const annotationSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + authorReference: z.lazy(() => referenceSchema).optional(), + authorString: stringSchema.optional(), + _authorString: z.lazy(() => elementSchema).optional(), + time: dateTimeSchema.optional(), + _time: z.lazy(() => elementSchema).optional(), + text: markdownSchema, + _text: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [{ names: ['authorReference', 'authorString'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const appointmentParticipantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + actor: z.lazy(() => referenceSchema).optional(), + required: z.enum(['information-only', 'optional', 'required']).optional(), + _required: z.lazy(() => elementSchema).optional(), + status: z.enum(['accepted', 'declined', 'needs-action', 'tentative']), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), + ) + +export const appointmentSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Appointment'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'arrived', + 'booked', + 'cancelled', + 'checked-in', + 'entered-in-error', + 'fulfilled', + 'noshow', + 'pending', + 'proposed', + 'waitlist', + ]), + _status: z.lazy(() => elementSchema).optional(), + cancelationReason: z.lazy(() => codeableConceptSchema).optional(), + serviceCategory: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + serviceType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + appointmentType: z.lazy(() => codeableConceptSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + priority: unsignedIntSchema.optional(), + _priority: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .optional(), + start: instantSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: instantSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + minutesDuration: positiveIntSchema.optional(), + _minutesDuration: z.lazy(() => elementSchema).optional(), + slot: z + .lazy(() => referenceSchema) + .array() + .optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + patientInstruction: stringSchema.optional(), + _patientInstruction: z.lazy(() => elementSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + participant: appointmentParticipantSchema.array().min(1), + requestedPeriod: z + .lazy(() => periodSchema) + .array() + .optional(), + }), +) + +export const appointmentResponseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('AppointmentResponse'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + appointment: z.lazy(() => referenceSchema), + start: instantSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: instantSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + participantType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + actor: z.lazy(() => referenceSchema).optional(), + participantStatus: z.enum([ + 'accepted', + 'declined', + 'needs-action', + 'tentative', + ]), + _participantStatus: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), + ) + +export const attachmentSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + contentType: codeSchema.optional(), + _contentType: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + data: base64BinarySchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + url: urlSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + size: unsignedIntSchema.optional(), + _size: z.lazy(() => elementSchema).optional(), + hash: base64BinarySchema.optional(), + _hash: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + creation: dateTimeSchema.optional(), + _creation: z.lazy(() => elementSchema).optional(), + }), +) + +export const auditEventAgentSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + role: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + who: z.lazy(() => referenceSchema).optional(), + altId: stringSchema.optional(), + _altId: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + requestor: booleanSchema, + _requestor: z.lazy(() => elementSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + policy: uriSchema.array().optional(), + _policy: z + .lazy(() => elementSchema) + .array() + .optional(), + media: z.lazy(() => codingSchema).optional(), + network: auditEventAgentNetworkSchema.optional(), + purposeOfUse: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), +) + +export const auditEventSourceSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + site: stringSchema.optional(), + _site: z.lazy(() => elementSchema).optional(), + observer: z.lazy(() => referenceSchema), + type: z + .lazy(() => codingSchema) + .array() + .optional(), + }), +) + +export const auditEventEntitySchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + what: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codingSchema).optional(), + role: z.lazy(() => codingSchema).optional(), + lifecycle: z.lazy(() => codingSchema).optional(), + securityLabel: z + .lazy(() => codingSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + query: base64BinarySchema.optional(), + _query: z.lazy(() => elementSchema).optional(), + detail: auditEventEntityDetailSchema.array().optional(), + }), +) + +export const auditEventAgentNetworkSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + address: stringSchema.optional(), + _address: z.lazy(() => elementSchema).optional(), + type: z.enum(['1', '2', '3', '4', '5']).optional(), + _type: z.lazy(() => elementSchema).optional(), + }), + ) + +export const auditEventEntityDetailSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: stringSchema, + _type: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['valueString', 'valueBase64Binary'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const auditEventSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('AuditEvent'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codingSchema), + subtype: z + .lazy(() => codingSchema) + .array() + .optional(), + action: z.enum(['C', 'D', 'E', 'R', 'U']).optional(), + _action: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + recorded: instantSchema, + _recorded: z.lazy(() => elementSchema).optional(), + outcome: z.enum(['0', '12', '4', '8']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + outcomeDesc: stringSchema.optional(), + _outcomeDesc: z.lazy(() => elementSchema).optional(), + purposeOfEvent: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + agent: auditEventAgentSchema.array().min(1), + source: auditEventSourceSchema, + entity: auditEventEntitySchema.array().optional(), + }), +) + +export const basicSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Basic'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema).optional(), + created: dateSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + }), +) + +export const binarySchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Binary'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + contentType: codeSchema, + _contentType: z.lazy(() => elementSchema).optional(), + securityContext: z.lazy(() => referenceSchema).optional(), + data: base64BinarySchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + }), +) + +export const biologicallyDerivedProductCollectionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + collector: z.lazy(() => referenceSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + collectedDateTime: dateTimeSchema.optional(), + _collectedDateTime: z.lazy(() => elementSchema).optional(), + collectedPeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['collectedDateTime', 'collectedPeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const biologicallyDerivedProductProcessingSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + procedure: z.lazy(() => codeableConceptSchema).optional(), + additive: z.lazy(() => referenceSchema).optional(), + timeDateTime: dateTimeSchema.optional(), + _timeDateTime: z.lazy(() => elementSchema).optional(), + timePeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [{ names: ['timeDateTime', 'timePeriod'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const biologicallyDerivedProductManipulationSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + timeDateTime: dateTimeSchema.optional(), + _timeDateTime: z.lazy(() => elementSchema).optional(), + timePeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [{ names: ['timeDateTime', 'timePeriod'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const biologicallyDerivedProductStorageSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + temperature: decimalSchema.optional(), + _temperature: z.lazy(() => elementSchema).optional(), + scale: z.enum(['celsius', 'farenheit', 'kelvin']).optional(), + _scale: z.lazy(() => elementSchema).optional(), + duration: z.lazy(() => periodSchema).optional(), + }), + ) + +export const biologicallyDerivedProductSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('BiologicallyDerivedProduct'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + productCategory: z + .enum(['biologicalAgent', 'cells', 'fluid', 'organ', 'tissue']) + .optional(), + _productCategory: z.lazy(() => elementSchema).optional(), + productCode: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum(['available', 'unavailable']).optional(), + _status: z.lazy(() => elementSchema).optional(), + request: z + .lazy(() => referenceSchema) + .array() + .optional(), + quantity: integerSchema.optional(), + _quantity: z.lazy(() => elementSchema).optional(), + parent: z + .lazy(() => referenceSchema) + .array() + .optional(), + collection: biologicallyDerivedProductCollectionSchema.optional(), + processing: biologicallyDerivedProductProcessingSchema.array().optional(), + manipulation: biologicallyDerivedProductManipulationSchema.optional(), + storage: biologicallyDerivedProductStorageSchema.array().optional(), + }), + ) + +export const bodyStructureSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('BodyStructure'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + morphology: z.lazy(() => codeableConceptSchema).optional(), + location: z.lazy(() => codeableConceptSchema).optional(), + locationQualifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + image: z + .lazy(() => attachmentSchema) + .array() + .optional(), + patient: z.lazy(() => referenceSchema), + }), +) + +export const bundleLinkSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + relation: stringSchema, + _relation: z.lazy(() => elementSchema).optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + }), +) + +export const bundleEntrySchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + link: z + .lazy(() => bundleLinkSchema) + .array() + .optional(), + fullUrl: uriSchema.optional(), + _fullUrl: z.lazy(() => elementSchema).optional(), + resource: z.lazy(() => resourceSchema).optional(), + search: bundleEntrySearchSchema.optional(), + request: bundleEntryRequestSchema.optional(), + response: bundleEntryResponseSchema.optional(), + }), +) + +export const bundleEntrySearchSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + mode: z.enum(['include', 'match', 'outcome']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + score: decimalSchema.optional(), + _score: z.lazy(() => elementSchema).optional(), + }), +) + +export const bundleEntryRequestSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + method: z.enum(['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT']), + _method: z.lazy(() => elementSchema).optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + ifNoneMatch: stringSchema.optional(), + _ifNoneMatch: z.lazy(() => elementSchema).optional(), + ifModifiedSince: instantSchema.optional(), + _ifModifiedSince: z.lazy(() => elementSchema).optional(), + ifMatch: stringSchema.optional(), + _ifMatch: z.lazy(() => elementSchema).optional(), + ifNoneExist: stringSchema.optional(), + _ifNoneExist: z.lazy(() => elementSchema).optional(), + }), + ) + +export const bundleEntryResponseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + status: stringSchema, + _status: z.lazy(() => elementSchema).optional(), + location: uriSchema.optional(), + _location: z.lazy(() => elementSchema).optional(), + etag: stringSchema.optional(), + _etag: z.lazy(() => elementSchema).optional(), + lastModified: instantSchema.optional(), + _lastModified: z.lazy(() => elementSchema).optional(), + outcome: z.lazy(() => resourceSchema).optional(), + }), + ) + +export const bundleSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Bundle'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + type: z.enum([ + 'batch', + 'batch-response', + 'collection', + 'document', + 'history', + 'message', + 'searchset', + 'transaction', + 'transaction-response', + ]), + _type: z.lazy(() => elementSchema).optional(), + timestamp: instantSchema.optional(), + _timestamp: z.lazy(() => elementSchema).optional(), + total: unsignedIntSchema.optional(), + _total: z.lazy(() => elementSchema).optional(), + link: bundleLinkSchema.array().optional(), + entry: bundleEntrySchema.array().optional(), + signature: z.lazy(() => signatureSchema).optional(), + }), +) + +export const capabilityStatementSoftwareSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + releaseDate: dateTimeSchema.optional(), + _releaseDate: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementImplementationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema, + _description: z.lazy(() => elementSchema).optional(), + url: urlSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + custodian: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const capabilityStatementRestSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + mode: z.enum(['client', 'server']), + _mode: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + security: capabilityStatementRestSecuritySchema.optional(), + resource: capabilityStatementRestResourceSchema.array().optional(), + interaction: capabilityStatementRestInteractionSchema.array().optional(), + searchParam: z + .lazy(() => capabilityStatementRestResourceSearchParamSchema) + .array() + .optional(), + operation: z + .lazy(() => capabilityStatementRestResourceOperationSchema) + .array() + .optional(), + compartment: canonicalSchema.array().optional(), + _compartment: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const capabilityStatementMessagingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + endpoint: capabilityStatementMessagingEndpointSchema.array().optional(), + reliableCache: unsignedIntSchema.optional(), + _reliableCache: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + supportedMessage: capabilityStatementMessagingSupportedMessageSchema + .array() + .optional(), + }), + ) + +export const capabilityStatementDocumentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + mode: z.enum(['consumer', 'producer']), + _mode: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema, + _profile: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementRestSecuritySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + cors: booleanSchema.optional(), + _cors: z.lazy(() => elementSchema).optional(), + service: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementRestResourceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]), + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + supportedProfile: canonicalSchema.array().optional(), + _supportedProfile: z + .lazy(() => elementSchema) + .array() + .optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + interaction: capabilityStatementRestResourceInteractionSchema + .array() + .optional(), + versioning: z + .enum(['no-version', 'versioned', 'versioned-update']) + .optional(), + _versioning: z.lazy(() => elementSchema).optional(), + readHistory: booleanSchema.optional(), + _readHistory: z.lazy(() => elementSchema).optional(), + updateCreate: booleanSchema.optional(), + _updateCreate: z.lazy(() => elementSchema).optional(), + conditionalCreate: booleanSchema.optional(), + _conditionalCreate: z.lazy(() => elementSchema).optional(), + conditionalRead: z + .enum(['full-support', 'modified-since', 'not-match', 'not-supported']) + .optional(), + _conditionalRead: z.lazy(() => elementSchema).optional(), + conditionalUpdate: booleanSchema.optional(), + _conditionalUpdate: z.lazy(() => elementSchema).optional(), + conditionalDelete: z + .enum(['multiple', 'not-supported', 'single']) + .optional(), + _conditionalDelete: z.lazy(() => elementSchema).optional(), + referencePolicy: z + .enum(['enforced', 'literal', 'local', 'logical', 'resolves']) + .array() + .optional(), + _referencePolicy: z + .lazy(() => elementSchema) + .array() + .optional(), + searchInclude: stringSchema.array().optional(), + _searchInclude: z + .lazy(() => elementSchema) + .array() + .optional(), + searchRevInclude: stringSchema.array().optional(), + _searchRevInclude: z + .lazy(() => elementSchema) + .array() + .optional(), + searchParam: capabilityStatementRestResourceSearchParamSchema + .array() + .optional(), + operation: capabilityStatementRestResourceOperationSchema + .array() + .optional(), + }), + ) + +export const capabilityStatementRestInteractionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.enum(['batch', 'history-system', 'search-system', 'transaction']), + _code: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementRestResourceSearchParamSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + definition: canonicalSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + type: z.enum([ + 'composite', + 'date', + 'number', + 'quantity', + 'reference', + 'special', + 'string', + 'token', + 'uri', + ]), + _type: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementRestResourceOperationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + definition: canonicalSchema, + _definition: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementMessagingEndpointSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + protocol: z.lazy(() => codingSchema), + address: urlSchema, + _address: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementMessagingSupportedMessageSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + mode: z.enum(['receiver', 'sender']), + _mode: z.lazy(() => elementSchema).optional(), + definition: canonicalSchema, + _definition: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementRestResourceInteractionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.enum([ + 'create', + 'delete', + 'history-instance', + 'history-type', + 'patch', + 'read', + 'search-type', + 'update', + 'vread', + ]), + _code: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('CapabilityStatement'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema, + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + kind: z.enum(['capability', 'instance', 'requirements']), + _kind: z.lazy(() => elementSchema).optional(), + instantiates: canonicalSchema.array().optional(), + _instantiates: z + .lazy(() => elementSchema) + .array() + .optional(), + imports: canonicalSchema.array().optional(), + _imports: z + .lazy(() => elementSchema) + .array() + .optional(), + software: capabilityStatementSoftwareSchema.optional(), + implementation: capabilityStatementImplementationSchema.optional(), + fhirVersion: z.enum([ + '0.0.80', + '0.0.81', + '0.0.82', + '0.01', + '0.05', + '0.06', + '0.11', + '0.4.0', + '0.5.0', + '1.0.0', + '1.0.1', + '1.0.2', + '1.1.0', + '1.4.0', + '1.6.0', + '1.8.0', + '3.0.0', + '3.0.1', + '3.3.0', + '3.5.0', + '4.0.0', + '4.0.1', + ]), + _fhirVersion: z.lazy(() => elementSchema).optional(), + format: codeSchema.array().min(1), + _format: z + .lazy(() => elementSchema) + .array() + .optional(), + patchFormat: codeSchema.array().optional(), + _patchFormat: z + .lazy(() => elementSchema) + .array() + .optional(), + implementationGuide: canonicalSchema.array().optional(), + _implementationGuide: z + .lazy(() => elementSchema) + .array() + .optional(), + rest: capabilityStatementRestSchema.array().optional(), + messaging: capabilityStatementMessagingSchema.array().optional(), + document: capabilityStatementDocumentSchema.array().optional(), + }), + ) + +export const carePlanActivitySchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + outcomeCodeableConcept: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + outcomeReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + progress: z + .lazy(() => annotationSchema) + .array() + .optional(), + reference: z.lazy(() => referenceSchema).optional(), + detail: carePlanActivityDetailSchema.optional(), + }), +) + +export const carePlanActivityDetailSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + kind: z + .enum([ + 'Appointment', + 'CommunicationRequest', + 'DeviceRequest', + 'MedicationRequest', + 'NutritionOrder', + 'ServiceRequest', + 'Task', + 'VisionPrescription', + ]) + .optional(), + _kind: z.lazy(() => elementSchema).optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + goal: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'cancelled', + 'completed', + 'entered-in-error', + 'in-progress', + 'not-started', + 'on-hold', + 'scheduled', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + scheduledTiming: z.lazy(() => timingSchema).optional(), + scheduledPeriod: z.lazy(() => periodSchema).optional(), + scheduledString: stringSchema.optional(), + _scheduledString: z.lazy(() => elementSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + performer: z + .lazy(() => referenceSchema) + .array() + .optional(), + productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + productReference: z.lazy(() => referenceSchema).optional(), + dailyAmount: z.lazy(() => quantitySchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['scheduledTiming', 'scheduledPeriod', 'scheduledString'], + required: false, + }, + { + names: ['productCodeableConcept', 'productReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const carePlanSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('CarePlan'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + replaces: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + intent: z.enum(['option', 'order', 'plan', 'proposal']), + _intent: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + contributor: z + .lazy(() => referenceSchema) + .array() + .optional(), + careTeam: z + .lazy(() => referenceSchema) + .array() + .optional(), + addresses: z + .lazy(() => referenceSchema) + .array() + .optional(), + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .optional(), + goal: z + .lazy(() => referenceSchema) + .array() + .optional(), + activity: carePlanActivitySchema.array().optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), +) + +export const careTeamParticipantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + role: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + member: z.lazy(() => referenceSchema).optional(), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), + ) + +export const careTeamSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('CareTeam'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z + .enum(['active', 'entered-in-error', 'inactive', 'proposed', 'suspended']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + participant: careTeamParticipantSchema.array().optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + managingOrganization: z + .lazy(() => referenceSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), +) + +export const catalogEntryRelatedEntrySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + relationtype: z.enum(['is-replaced-by', 'triggers']), + _relationtype: z.lazy(() => elementSchema).optional(), + item: z.lazy(() => referenceSchema), + }), + ) + +export const catalogEntrySchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('CatalogEntry'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + orderable: booleanSchema, + _orderable: z.lazy(() => elementSchema).optional(), + referencedItem: z.lazy(() => referenceSchema), + additionalIdentifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + classification: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + validityPeriod: z.lazy(() => periodSchema).optional(), + validTo: dateTimeSchema.optional(), + _validTo: z.lazy(() => elementSchema).optional(), + lastUpdated: dateTimeSchema.optional(), + _lastUpdated: z.lazy(() => elementSchema).optional(), + additionalCharacteristic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + additionalClassification: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + relatedEntry: catalogEntryRelatedEntrySchema.array().optional(), + }), +) + +export const chargeItemPerformerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }), + ) + +export const chargeItemSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ChargeItem'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + definitionUri: uriSchema.array().optional(), + _definitionUri: z + .lazy(() => elementSchema) + .array() + .optional(), + definitionCanonical: canonicalSchema.array().optional(), + _definitionCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + status: z.enum([ + 'aborted', + 'billable', + 'billed', + 'entered-in-error', + 'not-billable', + 'planned', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema), + context: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + performer: chargeItemPerformerSchema.array().optional(), + performingOrganization: z.lazy(() => referenceSchema).optional(), + requestingOrganization: z.lazy(() => referenceSchema).optional(), + costCenter: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + bodysite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + factorOverride: decimalSchema.optional(), + _factorOverride: z.lazy(() => elementSchema).optional(), + priceOverride: z.lazy(() => moneySchema).optional(), + overrideReason: stringSchema.optional(), + _overrideReason: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + enteredDate: dateTimeSchema.optional(), + _enteredDate: z.lazy(() => elementSchema).optional(), + reason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + service: z + .lazy(() => referenceSchema) + .array() + .optional(), + productReference: z.lazy(() => referenceSchema).optional(), + productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + account: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'occurrenceDateTime', + 'occurrencePeriod', + 'occurrenceTiming', + ], + required: false, + }, + { + names: ['productReference', 'productCodeableConcept'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const chargeItemDefinitionApplicabilitySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + language: stringSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + }), + ) + +export const chargeItemDefinitionPropertyGroupSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + applicability: z + .lazy(() => chargeItemDefinitionApplicabilitySchema) + .array() + .optional(), + priceComponent: chargeItemDefinitionPropertyGroupPriceComponentSchema + .array() + .optional(), + }), + ) + +export const chargeItemDefinitionPropertyGroupPriceComponentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'base', + 'deduction', + 'discount', + 'informational', + 'surcharge', + 'tax', + ]), + _type: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + }), + ) + +export const chargeItemDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('ChargeItemDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + derivedFromUri: uriSchema.array().optional(), + _derivedFromUri: z + .lazy(() => elementSchema) + .array() + .optional(), + partOf: canonicalSchema.array().optional(), + _partOf: z + .lazy(() => elementSchema) + .array() + .optional(), + replaces: canonicalSchema.array().optional(), + _replaces: z + .lazy(() => elementSchema) + .array() + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + instance: z + .lazy(() => referenceSchema) + .array() + .optional(), + applicability: chargeItemDefinitionApplicabilitySchema.array().optional(), + propertyGroup: chargeItemDefinitionPropertyGroupSchema.array().optional(), + }), + ) + +export const claimRelatedSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + claim: z.lazy(() => referenceSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema).optional(), + reference: z.lazy(() => identifierSchema).optional(), + }), +) + +export const claimPayeeSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + party: z.lazy(() => referenceSchema).optional(), + }), +) + +export const claimCareTeamSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + provider: z.lazy(() => referenceSchema), + responsible: booleanSchema.optional(), + _responsible: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + qualification: z.lazy(() => codeableConceptSchema).optional(), + }), +) + +export const claimSupportingInfoSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + category: z.lazy(() => codeableConceptSchema), + code: z.lazy(() => codeableConceptSchema).optional(), + timingDate: dateSchema.optional(), + _timingDate: z.lazy(() => elementSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + reason: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['timingDate', 'timingPeriod'], required: false }, + { + names: [ + 'valueBoolean', + 'valueString', + 'valueQuantity', + 'valueAttachment', + 'valueReference', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const claimDiagnosisSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + diagnosisCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + diagnosisReference: z.lazy(() => referenceSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + onAdmission: z.lazy(() => codeableConceptSchema).optional(), + packageCode: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['diagnosisCodeableConcept', 'diagnosisReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const claimProcedureSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + procedureCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + procedureReference: z.lazy(() => referenceSchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['procedureCodeableConcept', 'procedureReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const claimInsuranceSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + focal: booleanSchema, + _focal: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + coverage: z.lazy(() => referenceSchema), + businessArrangement: stringSchema.optional(), + _businessArrangement: z.lazy(() => elementSchema).optional(), + preAuthRef: stringSchema.array().optional(), + _preAuthRef: z + .lazy(() => elementSchema) + .array() + .optional(), + claimResponse: z.lazy(() => referenceSchema).optional(), + }), +) + +export const claimAccidentSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + date: dateSchema, + _date: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['locationAddress', 'locationReference'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const claimItemSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + careTeamSequence: positiveIntSchema.array().optional(), + _careTeamSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + diagnosisSequence: positiveIntSchema.array().optional(), + _diagnosisSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + procedureSequence: positiveIntSchema.array().optional(), + _procedureSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + informationSequence: positiveIntSchema.array().optional(), + _informationSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + encounter: z + .lazy(() => referenceSchema) + .array() + .optional(), + detail: claimItemDetailSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['servicedDate', 'servicedPeriod'], required: false }, + { + names: [ + 'locationCodeableConcept', + 'locationAddress', + 'locationReference', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const claimItemDetailSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + subDetail: claimItemDetailSubDetailSchema.array().optional(), + }), +) + +export const claimItemDetailSubDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const claimSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Claim'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + use: z.enum(['claim', 'preauthorization', 'predetermination']), + _use: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + billablePeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + insurer: z.lazy(() => referenceSchema).optional(), + provider: z.lazy(() => referenceSchema), + priority: z.lazy(() => codeableConceptSchema), + fundsReserve: z.lazy(() => codeableConceptSchema).optional(), + related: claimRelatedSchema.array().optional(), + prescription: z.lazy(() => referenceSchema).optional(), + originalPrescription: z.lazy(() => referenceSchema).optional(), + payee: claimPayeeSchema.optional(), + referral: z.lazy(() => referenceSchema).optional(), + facility: z.lazy(() => referenceSchema).optional(), + careTeam: claimCareTeamSchema.array().optional(), + supportingInfo: claimSupportingInfoSchema.array().optional(), + diagnosis: claimDiagnosisSchema.array().optional(), + procedure: claimProcedureSchema.array().optional(), + insurance: claimInsuranceSchema.array().min(1), + accident: claimAccidentSchema.optional(), + item: claimItemSchema.array().optional(), + total: z.lazy(() => moneySchema).optional(), + }), +) + +export const claimResponseItemSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemSequence: positiveIntSchema, + _itemSequence: z.lazy(() => elementSchema).optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: claimResponseItemAdjudicationSchema.array().min(1), + detail: claimResponseItemDetailSchema.array().optional(), + }), +) + +export const claimResponseAddItemSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemSequence: positiveIntSchema.array().optional(), + _itemSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + detailSequence: positiveIntSchema.array().optional(), + _detailSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + subdetailSequence: positiveIntSchema.array().optional(), + _subdetailSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + provider: z + .lazy(() => referenceSchema) + .array() + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1), + detail: claimResponseAddItemDetailSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['servicedDate', 'servicedPeriod'], required: false }, + { + names: [ + 'locationCodeableConcept', + 'locationAddress', + 'locationReference', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const claimResponseItemAdjudicationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema), + reason: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const claimResponseTotalSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema), + amount: z.lazy(() => moneySchema), + }), + ) + +export const claimResponsePaymentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + adjustment: z.lazy(() => moneySchema).optional(), + adjustmentReason: z.lazy(() => codeableConceptSchema).optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema), + identifier: z.lazy(() => identifierSchema).optional(), + }), + ) + +export const claimResponseProcessNoteSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + number: positiveIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + type: z.enum(['display', 'print', 'printoper']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema, + _text: z.lazy(() => elementSchema).optional(), + language: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const claimResponseInsuranceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + focal: booleanSchema, + _focal: z.lazy(() => elementSchema).optional(), + coverage: z.lazy(() => referenceSchema), + businessArrangement: stringSchema.optional(), + _businessArrangement: z.lazy(() => elementSchema).optional(), + claimResponse: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const claimResponseErrorSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemSequence: positiveIntSchema.optional(), + _itemSequence: z.lazy(() => elementSchema).optional(), + detailSequence: positiveIntSchema.optional(), + _detailSequence: z.lazy(() => elementSchema).optional(), + subDetailSequence: positiveIntSchema.optional(), + _subDetailSequence: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema), + }), + ) + +export const claimResponseItemDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + detailSequence: positiveIntSchema, + _detailSequence: z.lazy(() => elementSchema).optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1), + subDetail: claimResponseItemDetailSubDetailSchema.array().optional(), + }), + ) + +export const claimResponseAddItemDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1), + subDetail: claimResponseAddItemDetailSubDetailSchema.array().optional(), + }), + ) + +export const claimResponseItemDetailSubDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + subDetailSequence: positiveIntSchema, + _subDetailSequence: z.lazy(() => elementSchema).optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .optional(), + }), + ) + +export const claimResponseAddItemDetailSubDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1), + }), + ) + +export const claimResponseSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('ClaimResponse'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + use: z.enum(['claim', 'preauthorization', 'predetermination']), + _use: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + insurer: z.lazy(() => referenceSchema), + requestor: z.lazy(() => referenceSchema).optional(), + request: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + preAuthRef: stringSchema.optional(), + _preAuthRef: z.lazy(() => elementSchema).optional(), + preAuthPeriod: z.lazy(() => periodSchema).optional(), + payeeType: z.lazy(() => codeableConceptSchema).optional(), + item: claimResponseItemSchema.array().optional(), + addItem: claimResponseAddItemSchema.array().optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .optional(), + total: claimResponseTotalSchema.array().optional(), + payment: claimResponsePaymentSchema.optional(), + fundsReserve: z.lazy(() => codeableConceptSchema).optional(), + formCode: z.lazy(() => codeableConceptSchema).optional(), + form: z.lazy(() => attachmentSchema).optional(), + processNote: claimResponseProcessNoteSchema.array().optional(), + communicationRequest: z + .lazy(() => referenceSchema) + .array() + .optional(), + insurance: claimResponseInsuranceSchema.array().optional(), + error: claimResponseErrorSchema.array().optional(), + }), +) + +export const clinicalImpressionInvestigationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + item: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const clinicalImpressionFindingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + basis: stringSchema.optional(), + _basis: z.lazy(() => elementSchema).optional(), + }), + ) + +export const clinicalImpressionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ClinicalImpression'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['completed', 'entered-in-error', 'in-progress']), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + assessor: z.lazy(() => referenceSchema).optional(), + previous: z.lazy(() => referenceSchema).optional(), + problem: z + .lazy(() => referenceSchema) + .array() + .optional(), + investigation: clinicalImpressionInvestigationSchema.array().optional(), + protocol: uriSchema.array().optional(), + _protocol: z + .lazy(() => elementSchema) + .array() + .optional(), + summary: stringSchema.optional(), + _summary: z.lazy(() => elementSchema).optional(), + finding: clinicalImpressionFindingSchema.array().optional(), + prognosisCodeableConcept: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + prognosisReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['effectiveDateTime', 'effectivePeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const codeSystemFilterSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + operator: z + .enum([ + '=', + 'descendent-of', + 'exists', + 'generalizes', + 'in', + 'is-a', + 'is-not-a', + 'not-in', + 'regex', + ]) + .array() + .min(1), + _operator: z + .lazy(() => elementSchema) + .array() + .optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + }), +) + +export const codeSystemPropertySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + uri: uriSchema.optional(), + _uri: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + type: z.enum([ + 'Coding', + 'boolean', + 'code', + 'dateTime', + 'decimal', + 'integer', + 'string', + ]), + _type: z.lazy(() => elementSchema).optional(), + }), + ) + +export const codeSystemConceptSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + definition: stringSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + designation: codeSystemConceptDesignationSchema.array().optional(), + property: codeSystemConceptPropertySchema.array().optional(), + concept: z + .lazy(() => codeSystemConceptSchema) + .array() + .optional(), + }), +) + +export const codeSystemConceptDesignationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + use: z.lazy(() => codingSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const codeSystemConceptPropertySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCode', + 'valueCoding', + 'valueString', + 'valueInteger', + 'valueBoolean', + 'valueDateTime', + 'valueDecimal', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const codeSystemSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('CodeSystem'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + caseSensitive: booleanSchema.optional(), + _caseSensitive: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema.optional(), + _valueSet: z.lazy(() => elementSchema).optional(), + hierarchyMeaning: z + .enum(['classified-with', 'grouped-by', 'is-a', 'part-of']) + .optional(), + _hierarchyMeaning: z.lazy(() => elementSchema).optional(), + compositional: booleanSchema.optional(), + _compositional: z.lazy(() => elementSchema).optional(), + versionNeeded: booleanSchema.optional(), + _versionNeeded: z.lazy(() => elementSchema).optional(), + content: z.enum([ + 'complete', + 'example', + 'fragment', + 'not-present', + 'supplement', + ]), + _content: z.lazy(() => elementSchema).optional(), + supplements: canonicalSchema.optional(), + _supplements: z.lazy(() => elementSchema).optional(), + count: unsignedIntSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + filter: codeSystemFilterSchema.array().optional(), + property: codeSystemPropertySchema.array().optional(), + concept: codeSystemConceptSchema.array().optional(), + }), +) + +export const codeableConceptSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + coding: z + .lazy(() => codingSchema) + .array() + .optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }), +) + +export const codingSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + userSelected: booleanSchema.optional(), + _userSelected: z.lazy(() => elementSchema).optional(), + }), +) + +export const communicationPayloadSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + contentString: stringSchema.optional(), + _contentString: z.lazy(() => elementSchema).optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['contentString', 'contentAttachment', 'contentReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const communicationSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Communication'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + inResponseTo: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + medium: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + subject: z.lazy(() => referenceSchema).optional(), + topic: z.lazy(() => codeableConceptSchema).optional(), + about: z + .lazy(() => referenceSchema) + .array() + .optional(), + encounter: z.lazy(() => referenceSchema).optional(), + sent: dateTimeSchema.optional(), + _sent: z.lazy(() => elementSchema).optional(), + received: dateTimeSchema.optional(), + _received: z.lazy(() => elementSchema).optional(), + recipient: z + .lazy(() => referenceSchema) + .array() + .optional(), + sender: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + payload: communicationPayloadSchema.array().optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), +) + +export const communicationRequestPayloadSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + contentString: stringSchema.optional(), + _contentString: z.lazy(() => elementSchema).optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['contentString', 'contentAttachment', 'contentReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const communicationRequestSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CommunicationRequest'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + replaces: z + .lazy(() => referenceSchema) + .array() + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + status: z.enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + medium: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + subject: z.lazy(() => referenceSchema).optional(), + about: z + .lazy(() => referenceSchema) + .array() + .optional(), + encounter: z.lazy(() => referenceSchema).optional(), + payload: communicationRequestPayloadSchema.array().optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + recipient: z + .lazy(() => referenceSchema) + .array() + .optional(), + sender: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['occurrenceDateTime', 'occurrencePeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const compartmentDefinitionResourceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]), + _code: z.lazy(() => elementSchema).optional(), + param: stringSchema.array().optional(), + _param: z + .lazy(() => elementSchema) + .array() + .optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const compartmentDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('CompartmentDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + code: z.enum([ + 'Device', + 'Encounter', + 'Patient', + 'Practitioner', + 'RelatedPerson', + ]), + _code: z.lazy(() => elementSchema).optional(), + search: booleanSchema, + _search: z.lazy(() => elementSchema).optional(), + resource: compartmentDefinitionResourceSchema.array().optional(), + }), + ) + +export const compositionAttesterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + mode: z.enum(['legal', 'official', 'personal', 'professional']), + _mode: z.lazy(() => elementSchema).optional(), + time: dateTimeSchema.optional(), + _time: z.lazy(() => elementSchema).optional(), + party: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const compositionRelatesToSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.enum(['appends', 'replaces', 'signs', 'transforms']), + _code: z.lazy(() => elementSchema).optional(), + targetIdentifier: z.lazy(() => identifierSchema).optional(), + targetReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['targetIdentifier', 'targetReference'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const compositionEventSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + detail: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const compositionSectionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + author: z + .lazy(() => referenceSchema) + .array() + .optional(), + focus: z.lazy(() => referenceSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + mode: z.enum(['changes', 'snapshot', 'working']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + orderedBy: z.lazy(() => codeableConceptSchema).optional(), + entry: z + .lazy(() => referenceSchema) + .array() + .optional(), + emptyReason: z.lazy(() => codeableConceptSchema).optional(), + section: z + .lazy(() => compositionSectionSchema) + .array() + .optional(), + }), + ) + +export const compositionSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Composition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + status: z.enum(['amended', 'entered-in-error', 'final', 'preliminary']), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema, + _date: z.lazy(() => elementSchema).optional(), + author: z + .lazy(() => referenceSchema) + .array() + .min(1), + title: stringSchema, + _title: z.lazy(() => elementSchema).optional(), + confidentiality: z.enum(['L', 'M', 'N', 'R', 'U', 'V']).optional(), + _confidentiality: z.lazy(() => elementSchema).optional(), + attester: compositionAttesterSchema.array().optional(), + custodian: z.lazy(() => referenceSchema).optional(), + relatesTo: compositionRelatesToSchema.array().optional(), + event: compositionEventSchema.array().optional(), + section: compositionSectionSchema.array().optional(), + }), +) + +export const conceptMapGroupSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + source: uriSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + sourceVersion: stringSchema.optional(), + _sourceVersion: z.lazy(() => elementSchema).optional(), + target: uriSchema.optional(), + _target: z.lazy(() => elementSchema).optional(), + targetVersion: stringSchema.optional(), + _targetVersion: z.lazy(() => elementSchema).optional(), + element: conceptMapGroupElementSchema.array().min(1), + unmapped: conceptMapGroupUnmappedSchema.optional(), + }), +) + +export const conceptMapGroupElementSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + target: conceptMapGroupElementTargetSchema.array().optional(), + }), + ) + +export const conceptMapGroupUnmappedSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + mode: z.enum(['fixed', 'other-map', 'provided']), + _mode: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + url: canonicalSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + }), + ) + +export const conceptMapGroupElementTargetSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + equivalence: z.enum([ + 'disjoint', + 'equal', + 'equivalent', + 'inexact', + 'narrower', + 'relatedto', + 'specializes', + 'subsumes', + 'unmatched', + 'wider', + ]), + _equivalence: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + dependsOn: conceptMapGroupElementTargetDependsOnSchema.array().optional(), + product: z + .lazy(() => conceptMapGroupElementTargetDependsOnSchema) + .array() + .optional(), + }), + ) + +export const conceptMapGroupElementTargetDependsOnSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + property: uriSchema, + _property: z.lazy(() => elementSchema).optional(), + system: canonicalSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + }), + ) + +export const conceptMapSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ConceptMap'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + sourceUri: uriSchema.optional(), + _sourceUri: z.lazy(() => elementSchema).optional(), + sourceCanonical: canonicalSchema.optional(), + _sourceCanonical: z.lazy(() => elementSchema).optional(), + targetUri: uriSchema.optional(), + _targetUri: z.lazy(() => elementSchema).optional(), + targetCanonical: canonicalSchema.optional(), + _targetCanonical: z.lazy(() => elementSchema).optional(), + group: conceptMapGroupSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['sourceUri', 'sourceCanonical'], required: false }, + { names: ['targetUri', 'targetCanonical'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const conditionStageSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + summary: z.lazy(() => codeableConceptSchema).optional(), + assessment: z + .lazy(() => referenceSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + }), +) + +export const conditionEvidenceSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + detail: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const conditionSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Condition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + clinicalStatus: z.lazy(() => codeableConceptSchema).optional(), + verificationStatus: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + severity: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + bodySite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + onsetDateTime: dateTimeSchema.optional(), + _onsetDateTime: z.lazy(() => elementSchema).optional(), + onsetAge: z.lazy(() => ageSchema).optional(), + onsetPeriod: z.lazy(() => periodSchema).optional(), + onsetRange: z.lazy(() => rangeSchema).optional(), + onsetString: stringSchema.optional(), + _onsetString: z.lazy(() => elementSchema).optional(), + abatementDateTime: dateTimeSchema.optional(), + _abatementDateTime: z.lazy(() => elementSchema).optional(), + abatementAge: z.lazy(() => ageSchema).optional(), + abatementPeriod: z.lazy(() => periodSchema).optional(), + abatementRange: z.lazy(() => rangeSchema).optional(), + abatementString: stringSchema.optional(), + _abatementString: z.lazy(() => elementSchema).optional(), + recordedDate: dateTimeSchema.optional(), + _recordedDate: z.lazy(() => elementSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + asserter: z.lazy(() => referenceSchema).optional(), + stage: conditionStageSchema.array().optional(), + evidence: conditionEvidenceSchema.array().optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'onsetDateTime', + 'onsetAge', + 'onsetPeriod', + 'onsetRange', + 'onsetString', + ], + required: false, + }, + { + names: [ + 'abatementDateTime', + 'abatementAge', + 'abatementPeriod', + 'abatementRange', + 'abatementString', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const consentPolicySchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + authority: uriSchema.optional(), + _authority: z.lazy(() => elementSchema).optional(), + uri: uriSchema.optional(), + _uri: z.lazy(() => elementSchema).optional(), + }), +) + +export const consentVerificationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + verified: booleanSchema, + _verified: z.lazy(() => elementSchema).optional(), + verifiedWith: z.lazy(() => referenceSchema).optional(), + verificationDate: dateTimeSchema.optional(), + _verificationDate: z.lazy(() => elementSchema).optional(), + }), + ) + +export const consentProvisionSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['deny', 'permit']).optional(), + _type: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + actor: consentProvisionActorSchema.array().optional(), + action: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + securityLabel: z + .lazy(() => codingSchema) + .array() + .optional(), + purpose: z + .lazy(() => codingSchema) + .array() + .optional(), + class: z + .lazy(() => codingSchema) + .array() + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + dataPeriod: z.lazy(() => periodSchema).optional(), + data: consentProvisionDataSchema.array().optional(), + provision: z + .lazy(() => consentProvisionSchema) + .array() + .optional(), + }), +) + +export const consentProvisionActorSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + role: z.lazy(() => codeableConceptSchema), + reference: z.lazy(() => referenceSchema), + }), + ) + +export const consentProvisionDataSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + meaning: z.enum(['authoredby', 'dependents', 'instance', 'related']), + _meaning: z.lazy(() => elementSchema).optional(), + reference: z.lazy(() => referenceSchema), + }), + ) + +export const consentSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Consent'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'draft', + 'entered-in-error', + 'inactive', + 'proposed', + 'rejected', + ]), + _status: z.lazy(() => elementSchema).optional(), + scope: z.lazy(() => codeableConceptSchema), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1), + patient: z.lazy(() => referenceSchema).optional(), + dateTime: dateTimeSchema.optional(), + _dateTime: z.lazy(() => elementSchema).optional(), + performer: z + .lazy(() => referenceSchema) + .array() + .optional(), + organization: z + .lazy(() => referenceSchema) + .array() + .optional(), + sourceAttachment: z.lazy(() => attachmentSchema).optional(), + sourceReference: z.lazy(() => referenceSchema).optional(), + policy: consentPolicySchema.array().optional(), + policyRule: z.lazy(() => codeableConceptSchema).optional(), + verification: consentVerificationSchema.array().optional(), + provision: consentProvisionSchema.optional(), + }) + .refine( + (value) => + [ + { names: ['sourceAttachment', 'sourceReference'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const contactDetailSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + }), +) + +export const contactPointSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + system: z + .enum(['email', 'fax', 'other', 'pager', 'phone', 'sms', 'url']) + .optional(), + _system: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + use: z.enum(['home', 'mobile', 'old', 'temp', 'work']).optional(), + _use: z.lazy(() => elementSchema).optional(), + rank: positiveIntSchema.optional(), + _rank: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), +) + +export const contractContentDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + publisher: z.lazy(() => referenceSchema).optional(), + publicationDate: dateTimeSchema.optional(), + _publicationDate: z.lazy(() => elementSchema).optional(), + publicationStatus: z.enum([ + 'amended', + 'appended', + 'cancelled', + 'disputed', + 'entered-in-error', + 'executable', + 'executed', + 'negotiable', + 'offered', + 'policy', + 'rejected', + 'renewed', + 'resolved', + 'revoked', + 'terminated', + ]), + _publicationStatus: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + }), + ) + +export const contractTermSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + issued: dateTimeSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + applies: z.lazy(() => periodSchema).optional(), + topicCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + topicReference: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subType: z.lazy(() => codeableConceptSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + securityLabel: contractTermSecurityLabelSchema.array().optional(), + offer: contractTermOfferSchema, + asset: contractTermAssetSchema.array().optional(), + action: contractTermActionSchema.array().optional(), + group: z + .lazy(() => contractTermSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['topicCodeableConcept', 'topicReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const contractSignerSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codingSchema), + party: z.lazy(() => referenceSchema), + signature: z + .lazy(() => signatureSchema) + .array() + .min(1), + }), +) + +export const contractFriendlySchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['contentAttachment', 'contentReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const contractLegalSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['contentAttachment', 'contentReference'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const contractRuleSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['contentAttachment', 'contentReference'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const contractTermSecurityLabelSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + number: unsignedIntSchema.array().optional(), + _number: z + .lazy(() => elementSchema) + .array() + .optional(), + classification: z.lazy(() => codingSchema), + category: z + .lazy(() => codingSchema) + .array() + .optional(), + control: z + .lazy(() => codingSchema) + .array() + .optional(), + }), + ) + +export const contractTermOfferSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + party: contractTermOfferPartySchema.array().optional(), + topic: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + decision: z.lazy(() => codeableConceptSchema).optional(), + decisionMode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + answer: contractTermOfferAnswerSchema.array().optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + linkId: stringSchema.array().optional(), + _linkId: z + .lazy(() => elementSchema) + .array() + .optional(), + securityLabelNumber: unsignedIntSchema.array().optional(), + _securityLabelNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + }), +) + +export const contractTermAssetSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + scope: z.lazy(() => codeableConceptSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + typeReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + subtype: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + relationship: z.lazy(() => codingSchema).optional(), + context: contractTermAssetContextSchema.array().optional(), + condition: stringSchema.optional(), + _condition: z.lazy(() => elementSchema).optional(), + periodType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + period: z + .lazy(() => periodSchema) + .array() + .optional(), + usePeriod: z + .lazy(() => periodSchema) + .array() + .optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + linkId: stringSchema.array().optional(), + _linkId: z + .lazy(() => elementSchema) + .array() + .optional(), + answer: z + .lazy(() => contractTermOfferAnswerSchema) + .array() + .optional(), + securityLabelNumber: unsignedIntSchema.array().optional(), + _securityLabelNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + valuedItem: contractTermAssetValuedItemSchema.array().optional(), + }), +) + +export const contractTermActionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subject: contractTermActionSubjectSchema.array().optional(), + intent: z.lazy(() => codeableConceptSchema), + linkId: stringSchema.array().optional(), + _linkId: z + .lazy(() => elementSchema) + .array() + .optional(), + status: z.lazy(() => codeableConceptSchema), + context: z.lazy(() => referenceSchema).optional(), + contextLinkId: stringSchema.array().optional(), + _contextLinkId: z + .lazy(() => elementSchema) + .array() + .optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + requester: z + .lazy(() => referenceSchema) + .array() + .optional(), + requesterLinkId: stringSchema.array().optional(), + _requesterLinkId: z + .lazy(() => elementSchema) + .array() + .optional(), + performerType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + performerRole: z.lazy(() => codeableConceptSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + performerLinkId: stringSchema.array().optional(), + _performerLinkId: z + .lazy(() => elementSchema) + .array() + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + reason: stringSchema.array().optional(), + _reason: z + .lazy(() => elementSchema) + .array() + .optional(), + reasonLinkId: stringSchema.array().optional(), + _reasonLinkId: z + .lazy(() => elementSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + securityLabelNumber: unsignedIntSchema.array().optional(), + _securityLabelNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'occurrenceDateTime', + 'occurrencePeriod', + 'occurrenceTiming', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const contractTermOfferPartySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + reference: z + .lazy(() => referenceSchema) + .array() + .min(1), + role: z.lazy(() => codeableConceptSchema), + }), + ) + +export const contractTermOfferAnswerSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBoolean', + 'valueDecimal', + 'valueInteger', + 'valueDate', + 'valueDateTime', + 'valueTime', + 'valueString', + 'valueUri', + 'valueAttachment', + 'valueCoding', + 'valueQuantity', + 'valueReference', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const contractTermAssetContextSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + reference: z.lazy(() => referenceSchema).optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }), + ) + +export const contractTermAssetValuedItemSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + entityCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + entityReference: z.lazy(() => referenceSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + effectiveTime: dateTimeSchema.optional(), + _effectiveTime: z.lazy(() => elementSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + points: decimalSchema.optional(), + _points: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + payment: stringSchema.optional(), + _payment: z.lazy(() => elementSchema).optional(), + paymentDate: dateTimeSchema.optional(), + _paymentDate: z.lazy(() => elementSchema).optional(), + responsible: z.lazy(() => referenceSchema).optional(), + recipient: z.lazy(() => referenceSchema).optional(), + linkId: stringSchema.array().optional(), + _linkId: z + .lazy(() => elementSchema) + .array() + .optional(), + securityLabelNumber: unsignedIntSchema.array().optional(), + _securityLabelNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['entityCodeableConcept', 'entityReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const contractTermActionSubjectSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + reference: z + .lazy(() => referenceSchema) + .array() + .min(1), + role: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const contractSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Contract'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + status: z + .enum([ + 'amended', + 'appended', + 'cancelled', + 'disputed', + 'entered-in-error', + 'executable', + 'executed', + 'negotiable', + 'offered', + 'policy', + 'rejected', + 'renewed', + 'resolved', + 'revoked', + 'terminated', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + legalState: z.lazy(() => codeableConceptSchema).optional(), + instantiatesCanonical: z.lazy(() => referenceSchema).optional(), + instantiatesUri: uriSchema.optional(), + _instantiatesUri: z.lazy(() => elementSchema).optional(), + contentDerivative: z.lazy(() => codeableConceptSchema).optional(), + issued: dateTimeSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + applies: z.lazy(() => periodSchema).optional(), + expirationType: z.lazy(() => codeableConceptSchema).optional(), + subject: z + .lazy(() => referenceSchema) + .array() + .optional(), + authority: z + .lazy(() => referenceSchema) + .array() + .optional(), + domain: z + .lazy(() => referenceSchema) + .array() + .optional(), + site: z + .lazy(() => referenceSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + alias: stringSchema.array().optional(), + _alias: z + .lazy(() => elementSchema) + .array() + .optional(), + author: z.lazy(() => referenceSchema).optional(), + scope: z.lazy(() => codeableConceptSchema).optional(), + topicCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + topicReference: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + contentDefinition: contractContentDefinitionSchema.optional(), + term: contractTermSchema.array().optional(), + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .optional(), + relevantHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + signer: contractSignerSchema.array().optional(), + friendly: contractFriendlySchema.array().optional(), + legal: contractLegalSchema.array().optional(), + rule: contractRuleSchema.array().optional(), + legallyBindingAttachment: z.lazy(() => attachmentSchema).optional(), + legallyBindingReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['topicCodeableConcept', 'topicReference'], + required: false, + }, + { + names: ['legallyBindingAttachment', 'legallyBindingReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const contributorSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['author', 'editor', 'endorser', 'reviewer']), + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + }), +) + +export const countSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }), +) + +export const coverageClassSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + }), +) + +export const coverageCostToBeneficiarySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + exception: coverageCostToBeneficiaryExceptionSchema.array().optional(), + }) + .refine( + (value) => + [{ names: ['valueQuantity', 'valueMoney'], required: true }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const coverageCostToBeneficiaryExceptionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + period: z.lazy(() => periodSchema).optional(), + }), + ) + +export const coverageSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Coverage'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + policyHolder: z.lazy(() => referenceSchema).optional(), + subscriber: z.lazy(() => referenceSchema).optional(), + subscriberId: stringSchema.optional(), + _subscriberId: z.lazy(() => elementSchema).optional(), + beneficiary: z.lazy(() => referenceSchema), + dependent: stringSchema.optional(), + _dependent: z.lazy(() => elementSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + payor: z + .lazy(() => referenceSchema) + .array() + .min(1), + class: coverageClassSchema.array().optional(), + order: positiveIntSchema.optional(), + _order: z.lazy(() => elementSchema).optional(), + network: stringSchema.optional(), + _network: z.lazy(() => elementSchema).optional(), + costToBeneficiary: coverageCostToBeneficiarySchema.array().optional(), + subrogation: booleanSchema.optional(), + _subrogation: z.lazy(() => elementSchema).optional(), + contract: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const coverageEligibilityRequestSupportingInfoSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + information: z.lazy(() => referenceSchema), + appliesToAll: booleanSchema.optional(), + _appliesToAll: z.lazy(() => elementSchema).optional(), + }), + ) + +export const coverageEligibilityRequestInsuranceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + focal: booleanSchema.optional(), + _focal: z.lazy(() => elementSchema).optional(), + coverage: z.lazy(() => referenceSchema), + businessArrangement: stringSchema.optional(), + _businessArrangement: z.lazy(() => elementSchema).optional(), + }), + ) + +export const coverageEligibilityRequestItemSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + supportingInfoSequence: positiveIntSchema.array().optional(), + _supportingInfoSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema).optional(), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + provider: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + facility: z.lazy(() => referenceSchema).optional(), + diagnosis: coverageEligibilityRequestItemDiagnosisSchema + .array() + .optional(), + detail: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const coverageEligibilityRequestItemDiagnosisSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + diagnosisCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + diagnosisReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['diagnosisCodeableConcept', 'diagnosisReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const coverageEligibilityRequestSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CoverageEligibilityRequest'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + priority: z.lazy(() => codeableConceptSchema).optional(), + purpose: z + .enum(['auth-requirements', 'benefits', 'discovery', 'validation']) + .array() + .min(1), + _purpose: z + .lazy(() => elementSchema) + .array() + .optional(), + patient: z.lazy(() => referenceSchema), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + provider: z.lazy(() => referenceSchema).optional(), + insurer: z.lazy(() => referenceSchema), + facility: z.lazy(() => referenceSchema).optional(), + supportingInfo: coverageEligibilityRequestSupportingInfoSchema + .array() + .optional(), + insurance: coverageEligibilityRequestInsuranceSchema.array().optional(), + item: coverageEligibilityRequestItemSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['servicedDate', 'servicedPeriod'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const coverageEligibilityResponseInsuranceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + coverage: z.lazy(() => referenceSchema), + inforce: booleanSchema.optional(), + _inforce: z.lazy(() => elementSchema).optional(), + benefitPeriod: z.lazy(() => periodSchema).optional(), + item: coverageEligibilityResponseInsuranceItemSchema.array().optional(), + }), + ) + +export const coverageEligibilityResponseErrorSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + }), + ) + +export const coverageEligibilityResponseInsuranceItemSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema).optional(), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + provider: z.lazy(() => referenceSchema).optional(), + excluded: booleanSchema.optional(), + _excluded: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + network: z.lazy(() => codeableConceptSchema).optional(), + unit: z.lazy(() => codeableConceptSchema).optional(), + term: z.lazy(() => codeableConceptSchema).optional(), + benefit: coverageEligibilityResponseInsuranceItemBenefitSchema + .array() + .optional(), + authorizationRequired: booleanSchema.optional(), + _authorizationRequired: z.lazy(() => elementSchema).optional(), + authorizationSupporting: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + authorizationUrl: uriSchema.optional(), + _authorizationUrl: z.lazy(() => elementSchema).optional(), + }), + ) + +export const coverageEligibilityResponseInsuranceItemBenefitSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + allowedUnsignedInt: unsignedIntSchema.optional(), + _allowedUnsignedInt: z.lazy(() => elementSchema).optional(), + allowedString: stringSchema.optional(), + _allowedString: z.lazy(() => elementSchema).optional(), + allowedMoney: z.lazy(() => moneySchema).optional(), + usedUnsignedInt: unsignedIntSchema.optional(), + _usedUnsignedInt: z.lazy(() => elementSchema).optional(), + usedString: stringSchema.optional(), + _usedString: z.lazy(() => elementSchema).optional(), + usedMoney: z.lazy(() => moneySchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['allowedUnsignedInt', 'allowedString', 'allowedMoney'], + required: false, + }, + { + names: ['usedUnsignedInt', 'usedString', 'usedMoney'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const coverageEligibilityResponseSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CoverageEligibilityResponse'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + purpose: z + .enum(['auth-requirements', 'benefits', 'discovery', 'validation']) + .array() + .min(1), + _purpose: z + .lazy(() => elementSchema) + .array() + .optional(), + patient: z.lazy(() => referenceSchema), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + requestor: z.lazy(() => referenceSchema).optional(), + request: z.lazy(() => referenceSchema), + outcome: z.enum(['complete', 'error', 'partial', 'queued']), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + insurer: z.lazy(() => referenceSchema), + insurance: coverageEligibilityResponseInsuranceSchema + .array() + .optional(), + preAuthRef: stringSchema.optional(), + _preAuthRef: z.lazy(() => elementSchema).optional(), + form: z.lazy(() => codeableConceptSchema).optional(), + error: coverageEligibilityResponseErrorSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['servicedDate', 'servicedPeriod'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const dataRequirementCodeFilterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + searchParam: stringSchema.optional(), + _searchParam: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema.optional(), + _valueSet: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codingSchema) + .array() + .optional(), + }), + ) + +export const dataRequirementDateFilterSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + searchParam: stringSchema.optional(), + _searchParam: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['valueDateTime', 'valuePeriod', 'valueDuration'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const dataRequirementSortSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema, + _path: z.lazy(() => elementSchema).optional(), + direction: z.enum(['ascending', 'descending']), + _direction: z.lazy(() => elementSchema).optional(), + }), + ) + +export const dataRequirementSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Any', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'CodeableConcept', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'EffectEvidenceSynthesis', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'Reference', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceAmount', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'Type', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]), + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.array().optional(), + _profile: z + .lazy(() => elementSchema) + .array() + .optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + mustSupport: stringSchema.array().optional(), + _mustSupport: z + .lazy(() => elementSchema) + .array() + .optional(), + codeFilter: dataRequirementCodeFilterSchema.array().optional(), + dateFilter: dataRequirementDateFilterSchema.array().optional(), + limit: positiveIntSchema.optional(), + _limit: z.lazy(() => elementSchema).optional(), + sort: dataRequirementSortSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: ['subjectCodeableConcept', 'subjectReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const detectedIssueEvidenceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + detail: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const detectedIssueMitigationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + action: z.lazy(() => codeableConceptSchema), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const detectedIssueSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('DetectedIssue'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'amended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'preliminary', + 'registered', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + severity: z.enum(['high', 'low', 'moderate']).optional(), + _severity: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema).optional(), + identifiedDateTime: dateTimeSchema.optional(), + _identifiedDateTime: z.lazy(() => elementSchema).optional(), + identifiedPeriod: z.lazy(() => periodSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + implicated: z + .lazy(() => referenceSchema) + .array() + .optional(), + evidence: detectedIssueEvidenceSchema.array().optional(), + detail: stringSchema.optional(), + _detail: z.lazy(() => elementSchema).optional(), + reference: uriSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + mitigation: detectedIssueMitigationSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: ['identifiedDateTime', 'identifiedPeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const deviceUdiCarrierSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + deviceIdentifier: stringSchema.optional(), + _deviceIdentifier: z.lazy(() => elementSchema).optional(), + issuer: uriSchema.optional(), + _issuer: z.lazy(() => elementSchema).optional(), + jurisdiction: uriSchema.optional(), + _jurisdiction: z.lazy(() => elementSchema).optional(), + carrierAIDC: base64BinarySchema.optional(), + _carrierAIDC: z.lazy(() => elementSchema).optional(), + carrierHRF: stringSchema.optional(), + _carrierHRF: z.lazy(() => elementSchema).optional(), + entryType: z + .enum(['barcode', 'card', 'manual', 'rfid', 'self-reported', 'unknown']) + .optional(), + _entryType: z.lazy(() => elementSchema).optional(), + }), +) + +export const deviceDeviceNameSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + type: z.enum([ + 'manufacturer-name', + 'model-name', + 'other', + 'patient-reported-name', + 'udi-label-name', + 'user-friendly-name', + ]), + _type: z.lazy(() => elementSchema).optional(), + }), +) + +export const deviceSpecializationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + systemType: z.lazy(() => codeableConceptSchema), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + }), + ) + +export const deviceVersionSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + component: z.lazy(() => identifierSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + }), +) + +export const devicePropertySchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueQuantity: z + .lazy(() => quantitySchema) + .array() + .optional(), + valueCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), +) + +export const deviceSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Device'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + definition: z.lazy(() => referenceSchema).optional(), + udiCarrier: deviceUdiCarrierSchema.array().optional(), + status: z + .enum(['active', 'entered-in-error', 'inactive', 'unknown']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + distinctIdentifier: stringSchema.optional(), + _distinctIdentifier: z.lazy(() => elementSchema).optional(), + manufacturer: stringSchema.optional(), + _manufacturer: z.lazy(() => elementSchema).optional(), + manufactureDate: dateTimeSchema.optional(), + _manufactureDate: z.lazy(() => elementSchema).optional(), + expirationDate: dateTimeSchema.optional(), + _expirationDate: z.lazy(() => elementSchema).optional(), + lotNumber: stringSchema.optional(), + _lotNumber: z.lazy(() => elementSchema).optional(), + serialNumber: stringSchema.optional(), + _serialNumber: z.lazy(() => elementSchema).optional(), + deviceName: deviceDeviceNameSchema.array().optional(), + modelNumber: stringSchema.optional(), + _modelNumber: z.lazy(() => elementSchema).optional(), + partNumber: stringSchema.optional(), + _partNumber: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + specialization: deviceSpecializationSchema.array().optional(), + version: deviceVersionSchema.array().optional(), + property: devicePropertySchema.array().optional(), + patient: z.lazy(() => referenceSchema).optional(), + owner: z.lazy(() => referenceSchema).optional(), + contact: z + .lazy(() => contactPointSchema) + .array() + .optional(), + location: z.lazy(() => referenceSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + safety: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + parent: z.lazy(() => referenceSchema).optional(), + }), +) + +export const deviceDefinitionUdiDeviceIdentifierSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + deviceIdentifier: stringSchema, + _deviceIdentifier: z.lazy(() => elementSchema).optional(), + issuer: uriSchema, + _issuer: z.lazy(() => elementSchema).optional(), + jurisdiction: uriSchema, + _jurisdiction: z.lazy(() => elementSchema).optional(), + }), + ) + +export const deviceDefinitionDeviceNameSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + type: z.enum([ + 'manufacturer-name', + 'model-name', + 'other', + 'patient-reported-name', + 'udi-label-name', + 'user-friendly-name', + ]), + _type: z.lazy(() => elementSchema).optional(), + }), + ) + +export const deviceDefinitionSpecializationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + systemType: stringSchema, + _systemType: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + }), + ) + +export const deviceDefinitionCapabilitySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + description: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), + ) + +export const deviceDefinitionPropertySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueQuantity: z + .lazy(() => quantitySchema) + .array() + .optional(), + valueCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), + ) + +export const deviceDefinitionMaterialSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + substance: z.lazy(() => codeableConceptSchema), + alternate: booleanSchema.optional(), + _alternate: z.lazy(() => elementSchema).optional(), + allergenicIndicator: booleanSchema.optional(), + _allergenicIndicator: z.lazy(() => elementSchema).optional(), + }), + ) + +export const deviceDefinitionSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('DeviceDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + udiDeviceIdentifier: deviceDefinitionUdiDeviceIdentifierSchema + .array() + .optional(), + manufacturerString: stringSchema.optional(), + _manufacturerString: z.lazy(() => elementSchema).optional(), + manufacturerReference: z.lazy(() => referenceSchema).optional(), + deviceName: deviceDefinitionDeviceNameSchema.array().optional(), + modelNumber: stringSchema.optional(), + _modelNumber: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + specialization: deviceDefinitionSpecializationSchema.array().optional(), + version: stringSchema.array().optional(), + _version: z + .lazy(() => elementSchema) + .array() + .optional(), + safety: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + shelfLifeStorage: z + .lazy(() => productShelfLifeSchema) + .array() + .optional(), + physicalCharacteristics: z + .lazy(() => prodCharacteristicSchema) + .optional(), + languageCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + capability: deviceDefinitionCapabilitySchema.array().optional(), + property: deviceDefinitionPropertySchema.array().optional(), + owner: z.lazy(() => referenceSchema).optional(), + contact: z + .lazy(() => contactPointSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + onlineInformation: uriSchema.optional(), + _onlineInformation: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + parentDevice: z.lazy(() => referenceSchema).optional(), + material: deviceDefinitionMaterialSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: ['manufacturerString', 'manufacturerReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const deviceMetricCalibrationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['gain', 'offset', 'two-point', 'unspecified']).optional(), + _type: z.lazy(() => elementSchema).optional(), + state: z + .enum([ + 'calibrated', + 'calibration-required', + 'not-calibrated', + 'unspecified', + ]) + .optional(), + _state: z.lazy(() => elementSchema).optional(), + time: instantSchema.optional(), + _time: z.lazy(() => elementSchema).optional(), + }), + ) + +export const deviceMetricSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('DeviceMetric'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + unit: z.lazy(() => codeableConceptSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + parent: z.lazy(() => referenceSchema).optional(), + operationalStatus: z + .enum(['entered-in-error', 'off', 'on', 'standby']) + .optional(), + _operationalStatus: z.lazy(() => elementSchema).optional(), + color: z + .enum([ + 'black', + 'blue', + 'cyan', + 'green', + 'magenta', + 'red', + 'white', + 'yellow', + ]) + .optional(), + _color: z.lazy(() => elementSchema).optional(), + category: z.enum(['calculation', 'measurement', 'setting', 'unspecified']), + _category: z.lazy(() => elementSchema).optional(), + measurementPeriod: z.lazy(() => timingSchema).optional(), + calibration: deviceMetricCalibrationSchema.array().optional(), + }), +) + +export const deviceRequestParameterSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueQuantity', + 'valueRange', + 'valueBoolean', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const deviceRequestSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('DeviceRequest'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + priorRequest: z + .lazy(() => referenceSchema) + .array() + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + status: z + .enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + intent: z.enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + codeReference: z.lazy(() => referenceSchema).optional(), + codeCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + parameter: deviceRequestParameterSchema.array().optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performerType: z.lazy(() => codeableConceptSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + insurance: z + .lazy(() => referenceSchema) + .array() + .optional(), + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + relevantHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { names: ['codeReference', 'codeCodeableConcept'], required: true }, + { + names: [ + 'occurrenceDateTime', + 'occurrencePeriod', + 'occurrenceTiming', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const deviceUseStatementSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('DeviceUseStatement'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'completed', + 'entered-in-error', + 'intended', + 'on-hold', + 'stopped', + ]), + _status: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema), + derivedFrom: z + .lazy(() => referenceSchema) + .array() + .optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + recordedOn: dateTimeSchema.optional(), + _recordedOn: z.lazy(() => elementSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + device: z.lazy(() => referenceSchema), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['timingTiming', 'timingPeriod', 'timingDateTime'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const diagnosticReportMediaSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + link: z.lazy(() => referenceSchema), + }), + ) + +export const diagnosticReportSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('DiagnosticReport'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'amended', + 'appended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'partial', + 'preliminary', + 'registered', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + issued: instantSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + performer: z + .lazy(() => referenceSchema) + .array() + .optional(), + resultsInterpreter: z + .lazy(() => referenceSchema) + .array() + .optional(), + specimen: z + .lazy(() => referenceSchema) + .array() + .optional(), + result: z + .lazy(() => referenceSchema) + .array() + .optional(), + imagingStudy: z + .lazy(() => referenceSchema) + .array() + .optional(), + media: diagnosticReportMediaSchema.array().optional(), + conclusion: stringSchema.optional(), + _conclusion: z.lazy(() => elementSchema).optional(), + conclusionCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + presentedForm: z + .lazy(() => attachmentSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['effectiveDateTime', 'effectivePeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const distanceSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }), +) + +export const documentManifestRelatedSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + ref: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const documentManifestSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('DocumentManifest'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + masterIdentifier: z.lazy(() => identifierSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['current', 'entered-in-error', 'superseded']), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + author: z + .lazy(() => referenceSchema) + .array() + .optional(), + recipient: z + .lazy(() => referenceSchema) + .array() + .optional(), + source: uriSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + content: z + .lazy(() => referenceSchema) + .array() + .min(1), + related: documentManifestRelatedSchema.array().optional(), + }), +) + +export const documentReferenceRelatesToSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.enum(['appends', 'replaces', 'signs', 'transforms']), + _code: z.lazy(() => elementSchema).optional(), + target: z.lazy(() => referenceSchema), + }), + ) + +export const documentReferenceContentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + attachment: z.lazy(() => attachmentSchema), + format: z.lazy(() => codingSchema).optional(), + }), + ) + +export const documentReferenceContextSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + encounter: z + .lazy(() => referenceSchema) + .array() + .optional(), + event: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + facilityType: z.lazy(() => codeableConceptSchema).optional(), + practiceSetting: z.lazy(() => codeableConceptSchema).optional(), + sourcePatientInfo: z.lazy(() => referenceSchema).optional(), + related: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const documentReferenceSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('DocumentReference'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + masterIdentifier: z.lazy(() => identifierSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['current', 'entered-in-error', 'superseded']), + _status: z.lazy(() => elementSchema).optional(), + docStatus: z + .enum(['amended', 'entered-in-error', 'final', 'preliminary']) + .optional(), + _docStatus: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + subject: z.lazy(() => referenceSchema).optional(), + date: instantSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + author: z + .lazy(() => referenceSchema) + .array() + .optional(), + authenticator: z.lazy(() => referenceSchema).optional(), + custodian: z.lazy(() => referenceSchema).optional(), + relatesTo: documentReferenceRelatesToSchema.array().optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + securityLabel: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + content: documentReferenceContentSchema.array().min(1), + context: documentReferenceContextSchema.optional(), + }), +) + +export const dosageDoseAndRateSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + doseRange: z.lazy(() => rangeSchema).optional(), + doseQuantity: z.lazy(() => quantitySchema).optional(), + rateRatio: z.lazy(() => ratioSchema).optional(), + rateRange: z.lazy(() => rangeSchema).optional(), + rateQuantity: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => + [ + { names: ['doseRange', 'doseQuantity'], required: false }, + { + names: ['rateRatio', 'rateRange', 'rateQuantity'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const dosageSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: integerSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + additionalInstruction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + patientInstruction: stringSchema.optional(), + _patientInstruction: z.lazy(() => elementSchema).optional(), + timing: z.lazy(() => timingSchema).optional(), + asNeededBoolean: booleanSchema.optional(), + _asNeededBoolean: z.lazy(() => elementSchema).optional(), + asNeededCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + site: z.lazy(() => codeableConceptSchema).optional(), + route: z.lazy(() => codeableConceptSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + doseAndRate: dosageDoseAndRateSchema.array().optional(), + maxDosePerPeriod: z.lazy(() => ratioSchema).optional(), + maxDosePerAdministration: z.lazy(() => quantitySchema).optional(), + maxDosePerLifetime: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['asNeededBoolean', 'asNeededCodeableConcept'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const durationSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }), +) + +export const effectEvidenceSynthesisSampleSizeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + numberOfStudies: integerSchema.optional(), + _numberOfStudies: z.lazy(() => elementSchema).optional(), + numberOfParticipants: integerSchema.optional(), + _numberOfParticipants: z.lazy(() => elementSchema).optional(), + }), + ) + +export const effectEvidenceSynthesisResultsByExposureSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + exposureState: z.enum(['exposure', 'exposure-alternative']).optional(), + _exposureState: z.lazy(() => elementSchema).optional(), + variantState: z.lazy(() => codeableConceptSchema).optional(), + riskEvidenceSynthesis: z.lazy(() => referenceSchema), + }), + ) + +export const effectEvidenceSynthesisEffectEstimateSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + variantState: z.lazy(() => codeableConceptSchema).optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + unitOfMeasure: z.lazy(() => codeableConceptSchema).optional(), + precisionEstimate: + effectEvidenceSynthesisEffectEstimatePrecisionEstimateSchema + .array() + .optional(), + }), + ) + +export const effectEvidenceSynthesisCertaintySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + rating: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + certaintySubcomponent: + effectEvidenceSynthesisCertaintyCertaintySubcomponentSchema + .array() + .optional(), + }), + ) + +export const effectEvidenceSynthesisEffectEstimatePrecisionEstimateSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + level: decimalSchema.optional(), + _level: z.lazy(() => elementSchema).optional(), + from: decimalSchema.optional(), + _from: z.lazy(() => elementSchema).optional(), + to: decimalSchema.optional(), + _to: z.lazy(() => elementSchema).optional(), + }), + ) + +export const effectEvidenceSynthesisCertaintyCertaintySubcomponentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + rating: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), + ) + +export const effectEvidenceSynthesisSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('EffectEvidenceSynthesis'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + synthesisType: z.lazy(() => codeableConceptSchema).optional(), + studyType: z.lazy(() => codeableConceptSchema).optional(), + population: z.lazy(() => referenceSchema), + exposure: z.lazy(() => referenceSchema), + exposureAlternative: z.lazy(() => referenceSchema), + outcome: z.lazy(() => referenceSchema), + sampleSize: effectEvidenceSynthesisSampleSizeSchema.optional(), + resultsByExposure: effectEvidenceSynthesisResultsByExposureSchema + .array() + .optional(), + effectEstimate: effectEvidenceSynthesisEffectEstimateSchema + .array() + .optional(), + certainty: effectEvidenceSynthesisCertaintySchema.array().optional(), + }), + ) + +export const elementSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + }), +) + +export const elementDefinitionSlicingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + discriminator: elementDefinitionSlicingDiscriminatorSchema + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + ordered: booleanSchema.optional(), + _ordered: z.lazy(() => elementSchema).optional(), + rules: z.enum(['closed', 'open', 'openAtEnd']), + _rules: z.lazy(() => elementSchema).optional(), + }), + ) + +export const elementDefinitionBaseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema, + _path: z.lazy(() => elementSchema).optional(), + min: unsignedIntSchema, + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema, + _max: z.lazy(() => elementSchema).optional(), + }), + ) + +export const elementDefinitionTypeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: uriSchema, + _code: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.array().optional(), + _profile: z + .lazy(() => elementSchema) + .array() + .optional(), + targetProfile: canonicalSchema.array().optional(), + _targetProfile: z + .lazy(() => elementSchema) + .array() + .optional(), + aggregation: z + .enum(['bundled', 'contained', 'referenced']) + .array() + .optional(), + _aggregation: z + .lazy(() => elementSchema) + .array() + .optional(), + versioning: z.enum(['either', 'independent', 'specific']).optional(), + _versioning: z.lazy(() => elementSchema).optional(), + }), + ) + +export const elementDefinitionExampleSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + label: stringSchema, + _label: z.lazy(() => elementSchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + valueMeta: z.lazy(() => metaSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBase64Binary', + 'valueBoolean', + 'valueCanonical', + 'valueCode', + 'valueDate', + 'valueDateTime', + 'valueDecimal', + 'valueId', + 'valueInstant', + 'valueInteger', + 'valueMarkdown', + 'valueOid', + 'valuePositiveInt', + 'valueString', + 'valueTime', + 'valueUnsignedInt', + 'valueUri', + 'valueUrl', + 'valueUuid', + 'valueAddress', + 'valueAge', + 'valueAnnotation', + 'valueAttachment', + 'valueCodeableConcept', + 'valueCoding', + 'valueContactPoint', + 'valueCount', + 'valueDistance', + 'valueDuration', + 'valueHumanName', + 'valueIdentifier', + 'valueMoney', + 'valuePeriod', + 'valueQuantity', + 'valueRange', + 'valueRatio', + 'valueReference', + 'valueSampledData', + 'valueSignature', + 'valueTiming', + 'valueContactDetail', + 'valueContributor', + 'valueDataRequirement', + 'valueExpression', + 'valueParameterDefinition', + 'valueRelatedArtifact', + 'valueTriggerDefinition', + 'valueUsageContext', + 'valueDosage', + 'valueMeta', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const elementDefinitionConstraintSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + key: idSchema, + _key: z.lazy(() => elementSchema).optional(), + requirements: stringSchema.optional(), + _requirements: z.lazy(() => elementSchema).optional(), + severity: z.enum(['error', 'warning']), + _severity: z.lazy(() => elementSchema).optional(), + human: stringSchema, + _human: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + xpath: stringSchema.optional(), + _xpath: z.lazy(() => elementSchema).optional(), + source: canonicalSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + }), + ) + +export const elementDefinitionBindingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + strength: z.enum(['example', 'extensible', 'preferred', 'required']), + _strength: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema.optional(), + _valueSet: z.lazy(() => elementSchema).optional(), + }), + ) + +export const elementDefinitionMappingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identity: idSchema, + _identity: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + map: stringSchema, + _map: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), + ) + +export const elementDefinitionSlicingDiscriminatorSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['exists', 'pattern', 'profile', 'type', 'value']), + _type: z.lazy(() => elementSchema).optional(), + path: stringSchema, + _path: z.lazy(() => elementSchema).optional(), + }), + ) + +export const elementDefinitionSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema, + _path: z.lazy(() => elementSchema).optional(), + representation: z + .enum(['cdaText', 'typeAttr', 'xhtml', 'xmlAttr', 'xmlText']) + .array() + .optional(), + _representation: z + .lazy(() => elementSchema) + .array() + .optional(), + sliceName: stringSchema.optional(), + _sliceName: z.lazy(() => elementSchema).optional(), + sliceIsConstraining: booleanSchema.optional(), + _sliceIsConstraining: z.lazy(() => elementSchema).optional(), + label: stringSchema.optional(), + _label: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codingSchema) + .array() + .optional(), + slicing: elementDefinitionSlicingSchema.optional(), + short: stringSchema.optional(), + _short: z.lazy(() => elementSchema).optional(), + definition: markdownSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + comment: markdownSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + requirements: markdownSchema.optional(), + _requirements: z.lazy(() => elementSchema).optional(), + alias: stringSchema.array().optional(), + _alias: z + .lazy(() => elementSchema) + .array() + .optional(), + min: unsignedIntSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + base: elementDefinitionBaseSchema.optional(), + contentReference: uriSchema.optional(), + _contentReference: z.lazy(() => elementSchema).optional(), + type: elementDefinitionTypeSchema.array().optional(), + defaultValueBase64Binary: base64BinarySchema.optional(), + _defaultValueBase64Binary: z.lazy(() => elementSchema).optional(), + defaultValueBoolean: booleanSchema.optional(), + _defaultValueBoolean: z.lazy(() => elementSchema).optional(), + defaultValueCanonical: canonicalSchema.optional(), + _defaultValueCanonical: z.lazy(() => elementSchema).optional(), + defaultValueCode: codeSchema.optional(), + _defaultValueCode: z.lazy(() => elementSchema).optional(), + defaultValueDate: dateSchema.optional(), + _defaultValueDate: z.lazy(() => elementSchema).optional(), + defaultValueDateTime: dateTimeSchema.optional(), + _defaultValueDateTime: z.lazy(() => elementSchema).optional(), + defaultValueDecimal: decimalSchema.optional(), + _defaultValueDecimal: z.lazy(() => elementSchema).optional(), + defaultValueId: idSchema.optional(), + _defaultValueId: z.lazy(() => elementSchema).optional(), + defaultValueInstant: instantSchema.optional(), + _defaultValueInstant: z.lazy(() => elementSchema).optional(), + defaultValueInteger: integerSchema.optional(), + _defaultValueInteger: z.lazy(() => elementSchema).optional(), + defaultValueMarkdown: markdownSchema.optional(), + _defaultValueMarkdown: z.lazy(() => elementSchema).optional(), + defaultValueOid: oidSchema.optional(), + _defaultValueOid: z.lazy(() => elementSchema).optional(), + defaultValuePositiveInt: positiveIntSchema.optional(), + _defaultValuePositiveInt: z.lazy(() => elementSchema).optional(), + defaultValueString: stringSchema.optional(), + _defaultValueString: z.lazy(() => elementSchema).optional(), + defaultValueTime: timeSchema.optional(), + _defaultValueTime: z.lazy(() => elementSchema).optional(), + defaultValueUnsignedInt: unsignedIntSchema.optional(), + _defaultValueUnsignedInt: z.lazy(() => elementSchema).optional(), + defaultValueUri: uriSchema.optional(), + _defaultValueUri: z.lazy(() => elementSchema).optional(), + defaultValueUrl: urlSchema.optional(), + _defaultValueUrl: z.lazy(() => elementSchema).optional(), + defaultValueUuid: uuidSchema.optional(), + _defaultValueUuid: z.lazy(() => elementSchema).optional(), + defaultValueAddress: z.lazy(() => addressSchema).optional(), + defaultValueAge: z.lazy(() => ageSchema).optional(), + defaultValueAnnotation: z.lazy(() => annotationSchema).optional(), + defaultValueAttachment: z.lazy(() => attachmentSchema).optional(), + defaultValueCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + defaultValueCoding: z.lazy(() => codingSchema).optional(), + defaultValueContactPoint: z.lazy(() => contactPointSchema).optional(), + defaultValueCount: z.lazy(() => countSchema).optional(), + defaultValueDistance: z.lazy(() => distanceSchema).optional(), + defaultValueDuration: z.lazy(() => durationSchema).optional(), + defaultValueHumanName: z.lazy(() => humanNameSchema).optional(), + defaultValueIdentifier: z.lazy(() => identifierSchema).optional(), + defaultValueMoney: z.lazy(() => moneySchema).optional(), + defaultValuePeriod: z.lazy(() => periodSchema).optional(), + defaultValueQuantity: z.lazy(() => quantitySchema).optional(), + defaultValueRange: z.lazy(() => rangeSchema).optional(), + defaultValueRatio: z.lazy(() => ratioSchema).optional(), + defaultValueReference: z.lazy(() => referenceSchema).optional(), + defaultValueSampledData: z.lazy(() => sampledDataSchema).optional(), + defaultValueSignature: z.lazy(() => signatureSchema).optional(), + defaultValueTiming: z.lazy(() => timingSchema).optional(), + defaultValueContactDetail: z.lazy(() => contactDetailSchema).optional(), + defaultValueContributor: z.lazy(() => contributorSchema).optional(), + defaultValueDataRequirement: z + .lazy(() => dataRequirementSchema) + .optional(), + defaultValueExpression: z.lazy(() => expressionSchema).optional(), + defaultValueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + defaultValueRelatedArtifact: z + .lazy(() => relatedArtifactSchema) + .optional(), + defaultValueTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + defaultValueUsageContext: z.lazy(() => usageContextSchema).optional(), + defaultValueDosage: z.lazy(() => dosageSchema).optional(), + defaultValueMeta: z.lazy(() => metaSchema).optional(), + meaningWhenMissing: markdownSchema.optional(), + _meaningWhenMissing: z.lazy(() => elementSchema).optional(), + orderMeaning: stringSchema.optional(), + _orderMeaning: z.lazy(() => elementSchema).optional(), + fixedBase64Binary: base64BinarySchema.optional(), + _fixedBase64Binary: z.lazy(() => elementSchema).optional(), + fixedBoolean: booleanSchema.optional(), + _fixedBoolean: z.lazy(() => elementSchema).optional(), + fixedCanonical: canonicalSchema.optional(), + _fixedCanonical: z.lazy(() => elementSchema).optional(), + fixedCode: codeSchema.optional(), + _fixedCode: z.lazy(() => elementSchema).optional(), + fixedDate: dateSchema.optional(), + _fixedDate: z.lazy(() => elementSchema).optional(), + fixedDateTime: dateTimeSchema.optional(), + _fixedDateTime: z.lazy(() => elementSchema).optional(), + fixedDecimal: decimalSchema.optional(), + _fixedDecimal: z.lazy(() => elementSchema).optional(), + fixedId: idSchema.optional(), + _fixedId: z.lazy(() => elementSchema).optional(), + fixedInstant: instantSchema.optional(), + _fixedInstant: z.lazy(() => elementSchema).optional(), + fixedInteger: integerSchema.optional(), + _fixedInteger: z.lazy(() => elementSchema).optional(), + fixedMarkdown: markdownSchema.optional(), + _fixedMarkdown: z.lazy(() => elementSchema).optional(), + fixedOid: oidSchema.optional(), + _fixedOid: z.lazy(() => elementSchema).optional(), + fixedPositiveInt: positiveIntSchema.optional(), + _fixedPositiveInt: z.lazy(() => elementSchema).optional(), + fixedString: stringSchema.optional(), + _fixedString: z.lazy(() => elementSchema).optional(), + fixedTime: timeSchema.optional(), + _fixedTime: z.lazy(() => elementSchema).optional(), + fixedUnsignedInt: unsignedIntSchema.optional(), + _fixedUnsignedInt: z.lazy(() => elementSchema).optional(), + fixedUri: uriSchema.optional(), + _fixedUri: z.lazy(() => elementSchema).optional(), + fixedUrl: urlSchema.optional(), + _fixedUrl: z.lazy(() => elementSchema).optional(), + fixedUuid: uuidSchema.optional(), + _fixedUuid: z.lazy(() => elementSchema).optional(), + fixedAddress: z.lazy(() => addressSchema).optional(), + fixedAge: z.lazy(() => ageSchema).optional(), + fixedAnnotation: z.lazy(() => annotationSchema).optional(), + fixedAttachment: z.lazy(() => attachmentSchema).optional(), + fixedCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + fixedCoding: z.lazy(() => codingSchema).optional(), + fixedContactPoint: z.lazy(() => contactPointSchema).optional(), + fixedCount: z.lazy(() => countSchema).optional(), + fixedDistance: z.lazy(() => distanceSchema).optional(), + fixedDuration: z.lazy(() => durationSchema).optional(), + fixedHumanName: z.lazy(() => humanNameSchema).optional(), + fixedIdentifier: z.lazy(() => identifierSchema).optional(), + fixedMoney: z.lazy(() => moneySchema).optional(), + fixedPeriod: z.lazy(() => periodSchema).optional(), + fixedQuantity: z.lazy(() => quantitySchema).optional(), + fixedRange: z.lazy(() => rangeSchema).optional(), + fixedRatio: z.lazy(() => ratioSchema).optional(), + fixedReference: z.lazy(() => referenceSchema).optional(), + fixedSampledData: z.lazy(() => sampledDataSchema).optional(), + fixedSignature: z.lazy(() => signatureSchema).optional(), + fixedTiming: z.lazy(() => timingSchema).optional(), + fixedContactDetail: z.lazy(() => contactDetailSchema).optional(), + fixedContributor: z.lazy(() => contributorSchema).optional(), + fixedDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + fixedExpression: z.lazy(() => expressionSchema).optional(), + fixedParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + fixedRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + fixedTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + fixedUsageContext: z.lazy(() => usageContextSchema).optional(), + fixedDosage: z.lazy(() => dosageSchema).optional(), + fixedMeta: z.lazy(() => metaSchema).optional(), + patternBase64Binary: base64BinarySchema.optional(), + _patternBase64Binary: z.lazy(() => elementSchema).optional(), + patternBoolean: booleanSchema.optional(), + _patternBoolean: z.lazy(() => elementSchema).optional(), + patternCanonical: canonicalSchema.optional(), + _patternCanonical: z.lazy(() => elementSchema).optional(), + patternCode: codeSchema.optional(), + _patternCode: z.lazy(() => elementSchema).optional(), + patternDate: dateSchema.optional(), + _patternDate: z.lazy(() => elementSchema).optional(), + patternDateTime: dateTimeSchema.optional(), + _patternDateTime: z.lazy(() => elementSchema).optional(), + patternDecimal: decimalSchema.optional(), + _patternDecimal: z.lazy(() => elementSchema).optional(), + patternId: idSchema.optional(), + _patternId: z.lazy(() => elementSchema).optional(), + patternInstant: instantSchema.optional(), + _patternInstant: z.lazy(() => elementSchema).optional(), + patternInteger: integerSchema.optional(), + _patternInteger: z.lazy(() => elementSchema).optional(), + patternMarkdown: markdownSchema.optional(), + _patternMarkdown: z.lazy(() => elementSchema).optional(), + patternOid: oidSchema.optional(), + _patternOid: z.lazy(() => elementSchema).optional(), + patternPositiveInt: positiveIntSchema.optional(), + _patternPositiveInt: z.lazy(() => elementSchema).optional(), + patternString: stringSchema.optional(), + _patternString: z.lazy(() => elementSchema).optional(), + patternTime: timeSchema.optional(), + _patternTime: z.lazy(() => elementSchema).optional(), + patternUnsignedInt: unsignedIntSchema.optional(), + _patternUnsignedInt: z.lazy(() => elementSchema).optional(), + patternUri: uriSchema.optional(), + _patternUri: z.lazy(() => elementSchema).optional(), + patternUrl: urlSchema.optional(), + _patternUrl: z.lazy(() => elementSchema).optional(), + patternUuid: uuidSchema.optional(), + _patternUuid: z.lazy(() => elementSchema).optional(), + patternAddress: z.lazy(() => addressSchema).optional(), + patternAge: z.lazy(() => ageSchema).optional(), + patternAnnotation: z.lazy(() => annotationSchema).optional(), + patternAttachment: z.lazy(() => attachmentSchema).optional(), + patternCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + patternCoding: z.lazy(() => codingSchema).optional(), + patternContactPoint: z.lazy(() => contactPointSchema).optional(), + patternCount: z.lazy(() => countSchema).optional(), + patternDistance: z.lazy(() => distanceSchema).optional(), + patternDuration: z.lazy(() => durationSchema).optional(), + patternHumanName: z.lazy(() => humanNameSchema).optional(), + patternIdentifier: z.lazy(() => identifierSchema).optional(), + patternMoney: z.lazy(() => moneySchema).optional(), + patternPeriod: z.lazy(() => periodSchema).optional(), + patternQuantity: z.lazy(() => quantitySchema).optional(), + patternRange: z.lazy(() => rangeSchema).optional(), + patternRatio: z.lazy(() => ratioSchema).optional(), + patternReference: z.lazy(() => referenceSchema).optional(), + patternSampledData: z.lazy(() => sampledDataSchema).optional(), + patternSignature: z.lazy(() => signatureSchema).optional(), + patternTiming: z.lazy(() => timingSchema).optional(), + patternContactDetail: z.lazy(() => contactDetailSchema).optional(), + patternContributor: z.lazy(() => contributorSchema).optional(), + patternDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + patternExpression: z.lazy(() => expressionSchema).optional(), + patternParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + patternRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + patternTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + patternUsageContext: z.lazy(() => usageContextSchema).optional(), + patternDosage: z.lazy(() => dosageSchema).optional(), + patternMeta: z.lazy(() => metaSchema).optional(), + example: elementDefinitionExampleSchema.array().optional(), + minValueDate: dateSchema.optional(), + _minValueDate: z.lazy(() => elementSchema).optional(), + minValueDateTime: dateTimeSchema.optional(), + _minValueDateTime: z.lazy(() => elementSchema).optional(), + minValueInstant: instantSchema.optional(), + _minValueInstant: z.lazy(() => elementSchema).optional(), + minValueTime: timeSchema.optional(), + _minValueTime: z.lazy(() => elementSchema).optional(), + minValueDecimal: decimalSchema.optional(), + _minValueDecimal: z.lazy(() => elementSchema).optional(), + minValueInteger: integerSchema.optional(), + _minValueInteger: z.lazy(() => elementSchema).optional(), + minValuePositiveInt: positiveIntSchema.optional(), + _minValuePositiveInt: z.lazy(() => elementSchema).optional(), + minValueUnsignedInt: unsignedIntSchema.optional(), + _minValueUnsignedInt: z.lazy(() => elementSchema).optional(), + minValueQuantity: z.lazy(() => quantitySchema).optional(), + maxValueDate: dateSchema.optional(), + _maxValueDate: z.lazy(() => elementSchema).optional(), + maxValueDateTime: dateTimeSchema.optional(), + _maxValueDateTime: z.lazy(() => elementSchema).optional(), + maxValueInstant: instantSchema.optional(), + _maxValueInstant: z.lazy(() => elementSchema).optional(), + maxValueTime: timeSchema.optional(), + _maxValueTime: z.lazy(() => elementSchema).optional(), + maxValueDecimal: decimalSchema.optional(), + _maxValueDecimal: z.lazy(() => elementSchema).optional(), + maxValueInteger: integerSchema.optional(), + _maxValueInteger: z.lazy(() => elementSchema).optional(), + maxValuePositiveInt: positiveIntSchema.optional(), + _maxValuePositiveInt: z.lazy(() => elementSchema).optional(), + maxValueUnsignedInt: unsignedIntSchema.optional(), + _maxValueUnsignedInt: z.lazy(() => elementSchema).optional(), + maxValueQuantity: z.lazy(() => quantitySchema).optional(), + maxLength: integerSchema.optional(), + _maxLength: z.lazy(() => elementSchema).optional(), + condition: idSchema.array().optional(), + _condition: z + .lazy(() => elementSchema) + .array() + .optional(), + constraint: elementDefinitionConstraintSchema.array().optional(), + mustSupport: booleanSchema.optional(), + _mustSupport: z.lazy(() => elementSchema).optional(), + isModifier: booleanSchema.optional(), + _isModifier: z.lazy(() => elementSchema).optional(), + isModifierReason: stringSchema.optional(), + _isModifierReason: z.lazy(() => elementSchema).optional(), + isSummary: booleanSchema.optional(), + _isSummary: z.lazy(() => elementSchema).optional(), + binding: elementDefinitionBindingSchema.optional(), + mapping: elementDefinitionMappingSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'defaultValueBase64Binary', + 'defaultValueBoolean', + 'defaultValueCanonical', + 'defaultValueCode', + 'defaultValueDate', + 'defaultValueDateTime', + 'defaultValueDecimal', + 'defaultValueId', + 'defaultValueInstant', + 'defaultValueInteger', + 'defaultValueMarkdown', + 'defaultValueOid', + 'defaultValuePositiveInt', + 'defaultValueString', + 'defaultValueTime', + 'defaultValueUnsignedInt', + 'defaultValueUri', + 'defaultValueUrl', + 'defaultValueUuid', + 'defaultValueAddress', + 'defaultValueAge', + 'defaultValueAnnotation', + 'defaultValueAttachment', + 'defaultValueCodeableConcept', + 'defaultValueCoding', + 'defaultValueContactPoint', + 'defaultValueCount', + 'defaultValueDistance', + 'defaultValueDuration', + 'defaultValueHumanName', + 'defaultValueIdentifier', + 'defaultValueMoney', + 'defaultValuePeriod', + 'defaultValueQuantity', + 'defaultValueRange', + 'defaultValueRatio', + 'defaultValueReference', + 'defaultValueSampledData', + 'defaultValueSignature', + 'defaultValueTiming', + 'defaultValueContactDetail', + 'defaultValueContributor', + 'defaultValueDataRequirement', + 'defaultValueExpression', + 'defaultValueParameterDefinition', + 'defaultValueRelatedArtifact', + 'defaultValueTriggerDefinition', + 'defaultValueUsageContext', + 'defaultValueDosage', + 'defaultValueMeta', + ], + required: false, + }, + { + names: [ + 'fixedBase64Binary', + 'fixedBoolean', + 'fixedCanonical', + 'fixedCode', + 'fixedDate', + 'fixedDateTime', + 'fixedDecimal', + 'fixedId', + 'fixedInstant', + 'fixedInteger', + 'fixedMarkdown', + 'fixedOid', + 'fixedPositiveInt', + 'fixedString', + 'fixedTime', + 'fixedUnsignedInt', + 'fixedUri', + 'fixedUrl', + 'fixedUuid', + 'fixedAddress', + 'fixedAge', + 'fixedAnnotation', + 'fixedAttachment', + 'fixedCodeableConcept', + 'fixedCoding', + 'fixedContactPoint', + 'fixedCount', + 'fixedDistance', + 'fixedDuration', + 'fixedHumanName', + 'fixedIdentifier', + 'fixedMoney', + 'fixedPeriod', + 'fixedQuantity', + 'fixedRange', + 'fixedRatio', + 'fixedReference', + 'fixedSampledData', + 'fixedSignature', + 'fixedTiming', + 'fixedContactDetail', + 'fixedContributor', + 'fixedDataRequirement', + 'fixedExpression', + 'fixedParameterDefinition', + 'fixedRelatedArtifact', + 'fixedTriggerDefinition', + 'fixedUsageContext', + 'fixedDosage', + 'fixedMeta', + ], + required: false, + }, + { + names: [ + 'patternBase64Binary', + 'patternBoolean', + 'patternCanonical', + 'patternCode', + 'patternDate', + 'patternDateTime', + 'patternDecimal', + 'patternId', + 'patternInstant', + 'patternInteger', + 'patternMarkdown', + 'patternOid', + 'patternPositiveInt', + 'patternString', + 'patternTime', + 'patternUnsignedInt', + 'patternUri', + 'patternUrl', + 'patternUuid', + 'patternAddress', + 'patternAge', + 'patternAnnotation', + 'patternAttachment', + 'patternCodeableConcept', + 'patternCoding', + 'patternContactPoint', + 'patternCount', + 'patternDistance', + 'patternDuration', + 'patternHumanName', + 'patternIdentifier', + 'patternMoney', + 'patternPeriod', + 'patternQuantity', + 'patternRange', + 'patternRatio', + 'patternReference', + 'patternSampledData', + 'patternSignature', + 'patternTiming', + 'patternContactDetail', + 'patternContributor', + 'patternDataRequirement', + 'patternExpression', + 'patternParameterDefinition', + 'patternRelatedArtifact', + 'patternTriggerDefinition', + 'patternUsageContext', + 'patternDosage', + 'patternMeta', + ], + required: false, + }, + { + names: [ + 'minValueDate', + 'minValueDateTime', + 'minValueInstant', + 'minValueTime', + 'minValueDecimal', + 'minValueInteger', + 'minValuePositiveInt', + 'minValueUnsignedInt', + 'minValueQuantity', + ], + required: false, + }, + { + names: [ + 'maxValueDate', + 'maxValueDateTime', + 'maxValueInstant', + 'maxValueTime', + 'maxValueDecimal', + 'maxValueInteger', + 'maxValuePositiveInt', + 'maxValueUnsignedInt', + 'maxValueQuantity', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const encounterStatusHistorySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + status: z.enum([ + 'arrived', + 'cancelled', + 'entered-in-error', + 'finished', + 'in-progress', + 'onleave', + 'planned', + 'triaged', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema), + }), + ) + +export const encounterClassHistorySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + class: z.lazy(() => codingSchema), + period: z.lazy(() => periodSchema), + }), + ) + +export const encounterParticipantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + individual: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const encounterDiagnosisSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + condition: z.lazy(() => referenceSchema), + use: z.lazy(() => codeableConceptSchema).optional(), + rank: positiveIntSchema.optional(), + _rank: z.lazy(() => elementSchema).optional(), + }), + ) + +export const encounterHospitalizationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + preAdmissionIdentifier: z.lazy(() => identifierSchema).optional(), + origin: z.lazy(() => referenceSchema).optional(), + admitSource: z.lazy(() => codeableConceptSchema).optional(), + reAdmission: z.lazy(() => codeableConceptSchema).optional(), + dietPreference: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialCourtesy: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialArrangement: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + destination: z.lazy(() => referenceSchema).optional(), + dischargeDisposition: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const encounterLocationSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + location: z.lazy(() => referenceSchema), + status: z.enum(['active', 'completed', 'planned', 'reserved']).optional(), + _status: z.lazy(() => elementSchema).optional(), + physicalType: z.lazy(() => codeableConceptSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), +) + +export const encounterSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Encounter'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'arrived', + 'cancelled', + 'entered-in-error', + 'finished', + 'in-progress', + 'onleave', + 'planned', + 'triaged', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusHistory: encounterStatusHistorySchema.array().optional(), + class: z.lazy(() => codingSchema), + classHistory: encounterClassHistorySchema.array().optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + serviceType: z.lazy(() => codeableConceptSchema).optional(), + priority: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + episodeOfCare: z + .lazy(() => referenceSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + participant: encounterParticipantSchema.array().optional(), + appointment: z + .lazy(() => referenceSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + length: z.lazy(() => durationSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + diagnosis: encounterDiagnosisSchema.array().optional(), + account: z + .lazy(() => referenceSchema) + .array() + .optional(), + hospitalization: encounterHospitalizationSchema.optional(), + location: encounterLocationSchema.array().optional(), + serviceProvider: z.lazy(() => referenceSchema).optional(), + partOf: z.lazy(() => referenceSchema).optional(), + }), +) + +export const endpointSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Endpoint'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'entered-in-error', + 'error', + 'off', + 'suspended', + 'test', + ]), + _status: z.lazy(() => elementSchema).optional(), + connectionType: z.lazy(() => codingSchema), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + contact: z + .lazy(() => contactPointSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + payloadType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1), + payloadMimeType: codeSchema.array().optional(), + _payloadMimeType: z + .lazy(() => elementSchema) + .array() + .optional(), + address: urlSchema, + _address: z.lazy(() => elementSchema).optional(), + header: stringSchema.array().optional(), + _header: z + .lazy(() => elementSchema) + .array() + .optional(), + }), +) + +export const enrollmentRequestSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('EnrollmentRequest'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + insurer: z.lazy(() => referenceSchema).optional(), + provider: z.lazy(() => referenceSchema).optional(), + candidate: z.lazy(() => referenceSchema).optional(), + coverage: z.lazy(() => referenceSchema).optional(), + }), +) + +export const enrollmentResponseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('EnrollmentResponse'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + request: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + requestProvider: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const episodeOfCareStatusHistorySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'cancelled', + 'entered-in-error', + 'finished', + 'onhold', + 'planned', + 'waitlist', + ]), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema), + }), + ) + +export const episodeOfCareDiagnosisSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + condition: z.lazy(() => referenceSchema), + role: z.lazy(() => codeableConceptSchema).optional(), + rank: positiveIntSchema.optional(), + _rank: z.lazy(() => elementSchema).optional(), + }), + ) + +export const episodeOfCareSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('EpisodeOfCare'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'cancelled', + 'entered-in-error', + 'finished', + 'onhold', + 'planned', + 'waitlist', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusHistory: episodeOfCareStatusHistorySchema.array().optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + diagnosis: episodeOfCareDiagnosisSchema.array().optional(), + patient: z.lazy(() => referenceSchema), + managingOrganization: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + referralRequest: z + .lazy(() => referenceSchema) + .array() + .optional(), + careManager: z.lazy(() => referenceSchema).optional(), + team: z + .lazy(() => referenceSchema) + .array() + .optional(), + account: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const eventDefinitionSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('EventDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + trigger: z + .lazy(() => triggerDefinitionSchema) + .array() + .min(1), + }) + .refine( + (value) => + [ + { + names: ['subjectCodeableConcept', 'subjectReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const evidenceSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Evidence'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + shortTitle: stringSchema.optional(), + _shortTitle: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + exposureBackground: z.lazy(() => referenceSchema), + exposureVariant: z + .lazy(() => referenceSchema) + .array() + .optional(), + outcome: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const evidenceVariableCharacteristicSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + definitionReference: z.lazy(() => referenceSchema).optional(), + definitionCanonical: canonicalSchema.optional(), + _definitionCanonical: z.lazy(() => elementSchema).optional(), + definitionCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + definitionExpression: z.lazy(() => expressionSchema).optional(), + definitionDataRequirement: z + .lazy(() => dataRequirementSchema) + .optional(), + definitionTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + usageContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + exclude: booleanSchema.optional(), + _exclude: z.lazy(() => elementSchema).optional(), + participantEffectiveDateTime: dateTimeSchema.optional(), + _participantEffectiveDateTime: z.lazy(() => elementSchema).optional(), + participantEffectivePeriod: z.lazy(() => periodSchema).optional(), + participantEffectiveDuration: z.lazy(() => durationSchema).optional(), + participantEffectiveTiming: z.lazy(() => timingSchema).optional(), + timeFromStart: z.lazy(() => durationSchema).optional(), + groupMeasure: z + .enum([ + 'mean', + 'mean-of-mean', + 'mean-of-median', + 'median', + 'median-of-mean', + 'median-of-median', + ]) + .optional(), + _groupMeasure: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'definitionReference', + 'definitionCanonical', + 'definitionCodeableConcept', + 'definitionExpression', + 'definitionDataRequirement', + 'definitionTriggerDefinition', + ], + required: true, + }, + { + names: [ + 'participantEffectiveDateTime', + 'participantEffectivePeriod', + 'participantEffectiveDuration', + 'participantEffectiveTiming', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const evidenceVariableSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('EvidenceVariable'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + shortTitle: stringSchema.optional(), + _shortTitle: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + type: z.enum(['continuous', 'descriptive', 'dichotomous']).optional(), + _type: z.lazy(() => elementSchema).optional(), + characteristic: evidenceVariableCharacteristicSchema.array().min(1), + }), +) + +export const exampleScenarioActorSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + actorId: stringSchema, + _actorId: z.lazy(() => elementSchema).optional(), + type: z.enum(['entity', 'person']), + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }), + ) + +export const exampleScenarioInstanceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + resourceId: stringSchema, + _resourceId: z.lazy(() => elementSchema).optional(), + resourceType: z.enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]), + _resourceType: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + version: exampleScenarioInstanceVersionSchema.array().optional(), + containedInstance: exampleScenarioInstanceContainedInstanceSchema + .array() + .optional(), + }), + ) + +export const exampleScenarioProcessSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + title: stringSchema, + _title: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + preConditions: markdownSchema.optional(), + _preConditions: z.lazy(() => elementSchema).optional(), + postConditions: markdownSchema.optional(), + _postConditions: z.lazy(() => elementSchema).optional(), + step: exampleScenarioProcessStepSchema.array().optional(), + }), + ) + +export const exampleScenarioInstanceVersionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + versionId: stringSchema, + _versionId: z.lazy(() => elementSchema).optional(), + description: markdownSchema, + _description: z.lazy(() => elementSchema).optional(), + }), + ) + +export const exampleScenarioInstanceContainedInstanceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + resourceId: stringSchema, + _resourceId: z.lazy(() => elementSchema).optional(), + versionId: stringSchema.optional(), + _versionId: z.lazy(() => elementSchema).optional(), + }), + ) + +export const exampleScenarioProcessStepSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + process: z + .lazy(() => exampleScenarioProcessSchema) + .array() + .optional(), + pause: booleanSchema.optional(), + _pause: z.lazy(() => elementSchema).optional(), + operation: exampleScenarioProcessStepOperationSchema.optional(), + alternative: exampleScenarioProcessStepAlternativeSchema + .array() + .optional(), + }), + ) + +export const exampleScenarioProcessStepOperationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + number: stringSchema, + _number: z.lazy(() => elementSchema).optional(), + type: stringSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + initiator: stringSchema.optional(), + _initiator: z.lazy(() => elementSchema).optional(), + receiver: stringSchema.optional(), + _receiver: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + initiatorActive: booleanSchema.optional(), + _initiatorActive: z.lazy(() => elementSchema).optional(), + receiverActive: booleanSchema.optional(), + _receiverActive: z.lazy(() => elementSchema).optional(), + request: z + .lazy(() => exampleScenarioInstanceContainedInstanceSchema) + .optional(), + response: z + .lazy(() => exampleScenarioInstanceContainedInstanceSchema) + .optional(), + }), + ) + +export const exampleScenarioProcessStepAlternativeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + title: stringSchema, + _title: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + step: z + .lazy(() => exampleScenarioProcessStepSchema) + .array() + .optional(), + }), + ) + +export const exampleScenarioSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('ExampleScenario'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + actor: exampleScenarioActorSchema.array().optional(), + instance: exampleScenarioInstanceSchema.array().optional(), + process: exampleScenarioProcessSchema.array().optional(), + workflow: canonicalSchema.array().optional(), + _workflow: z + .lazy(() => elementSchema) + .array() + .optional(), + }), +) + +export const explanationOfBenefitRelatedSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + claim: z.lazy(() => referenceSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema).optional(), + reference: z.lazy(() => identifierSchema).optional(), + }), + ) + +export const explanationOfBenefitPayeeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + party: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const explanationOfBenefitCareTeamSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + provider: z.lazy(() => referenceSchema), + responsible: booleanSchema.optional(), + _responsible: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + qualification: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const explanationOfBenefitSupportingInfoSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + category: z.lazy(() => codeableConceptSchema), + code: z.lazy(() => codeableConceptSchema).optional(), + timingDate: dateSchema.optional(), + _timingDate: z.lazy(() => elementSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + reason: z.lazy(() => codingSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['timingDate', 'timingPeriod'], required: false }, + { + names: [ + 'valueBoolean', + 'valueString', + 'valueQuantity', + 'valueAttachment', + 'valueReference', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const explanationOfBenefitDiagnosisSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + diagnosisCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + diagnosisReference: z.lazy(() => referenceSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + onAdmission: z.lazy(() => codeableConceptSchema).optional(), + packageCode: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['diagnosisCodeableConcept', 'diagnosisReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const explanationOfBenefitProcedureSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + procedureCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + procedureReference: z.lazy(() => referenceSchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['procedureCodeableConcept', 'procedureReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const explanationOfBenefitInsuranceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + focal: booleanSchema, + _focal: z.lazy(() => elementSchema).optional(), + coverage: z.lazy(() => referenceSchema), + preAuthRef: stringSchema.array().optional(), + _preAuthRef: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const explanationOfBenefitAccidentSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['locationAddress', 'locationReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const explanationOfBenefitItemSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + careTeamSequence: positiveIntSchema.array().optional(), + _careTeamSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + diagnosisSequence: positiveIntSchema.array().optional(), + _diagnosisSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + procedureSequence: positiveIntSchema.array().optional(), + _procedureSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + informationSequence: positiveIntSchema.array().optional(), + _informationSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + encounter: z + .lazy(() => referenceSchema) + .array() + .optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: explanationOfBenefitItemAdjudicationSchema + .array() + .optional(), + detail: explanationOfBenefitItemDetailSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['servicedDate', 'servicedPeriod'], required: false }, + { + names: [ + 'locationCodeableConcept', + 'locationAddress', + 'locationReference', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const explanationOfBenefitAddItemSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemSequence: positiveIntSchema.array().optional(), + _itemSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + detailSequence: positiveIntSchema.array().optional(), + _detailSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + subDetailSequence: positiveIntSchema.array().optional(), + _subDetailSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + provider: z + .lazy(() => referenceSchema) + .array() + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .optional(), + detail: explanationOfBenefitAddItemDetailSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['servicedDate', 'servicedPeriod'], required: false }, + { + names: [ + 'locationCodeableConcept', + 'locationAddress', + 'locationReference', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const explanationOfBenefitItemAdjudicationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema), + reason: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const explanationOfBenefitTotalSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema), + amount: z.lazy(() => moneySchema), + }), + ) + +export const explanationOfBenefitPaymentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + adjustment: z.lazy(() => moneySchema).optional(), + adjustmentReason: z.lazy(() => codeableConceptSchema).optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + }), + ) + +export const explanationOfBenefitProcessNoteSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + number: positiveIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + type: z.enum(['display', 'print', 'printoper']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + language: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const explanationOfBenefitBenefitBalanceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema), + excluded: booleanSchema.optional(), + _excluded: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + network: z.lazy(() => codeableConceptSchema).optional(), + unit: z.lazy(() => codeableConceptSchema).optional(), + term: z.lazy(() => codeableConceptSchema).optional(), + financial: explanationOfBenefitBenefitBalanceFinancialSchema + .array() + .optional(), + }), + ) + +export const explanationOfBenefitItemDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .optional(), + subDetail: explanationOfBenefitItemDetailSubDetailSchema + .array() + .optional(), + }), + ) + +export const explanationOfBenefitAddItemDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .optional(), + subDetail: explanationOfBenefitAddItemDetailSubDetailSchema + .array() + .optional(), + }), + ) + +export const explanationOfBenefitBenefitBalanceFinancialSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + allowedUnsignedInt: unsignedIntSchema.optional(), + _allowedUnsignedInt: z.lazy(() => elementSchema).optional(), + allowedString: stringSchema.optional(), + _allowedString: z.lazy(() => elementSchema).optional(), + allowedMoney: z.lazy(() => moneySchema).optional(), + usedUnsignedInt: unsignedIntSchema.optional(), + _usedUnsignedInt: z.lazy(() => elementSchema).optional(), + usedMoney: z.lazy(() => moneySchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['allowedUnsignedInt', 'allowedString', 'allowedMoney'], + required: false, + }, + { names: ['usedUnsignedInt', 'usedMoney'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const explanationOfBenefitItemDetailSubDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .optional(), + }), + ) + +export const explanationOfBenefitAddItemDetailSubDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .optional(), + }), + ) + +export const explanationOfBenefitSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('ExplanationOfBenefit'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + use: z.enum(['claim', 'preauthorization', 'predetermination']), + _use: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + billablePeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + insurer: z.lazy(() => referenceSchema), + provider: z.lazy(() => referenceSchema), + priority: z.lazy(() => codeableConceptSchema).optional(), + fundsReserveRequested: z.lazy(() => codeableConceptSchema).optional(), + fundsReserve: z.lazy(() => codeableConceptSchema).optional(), + related: explanationOfBenefitRelatedSchema.array().optional(), + prescription: z.lazy(() => referenceSchema).optional(), + originalPrescription: z.lazy(() => referenceSchema).optional(), + payee: explanationOfBenefitPayeeSchema.optional(), + referral: z.lazy(() => referenceSchema).optional(), + facility: z.lazy(() => referenceSchema).optional(), + claim: z.lazy(() => referenceSchema).optional(), + claimResponse: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + preAuthRef: stringSchema.array().optional(), + _preAuthRef: z + .lazy(() => elementSchema) + .array() + .optional(), + preAuthRefPeriod: z + .lazy(() => periodSchema) + .array() + .optional(), + careTeam: explanationOfBenefitCareTeamSchema.array().optional(), + supportingInfo: explanationOfBenefitSupportingInfoSchema + .array() + .optional(), + diagnosis: explanationOfBenefitDiagnosisSchema.array().optional(), + procedure: explanationOfBenefitProcedureSchema.array().optional(), + precedence: positiveIntSchema.optional(), + _precedence: z.lazy(() => elementSchema).optional(), + insurance: explanationOfBenefitInsuranceSchema.array().min(1), + accident: explanationOfBenefitAccidentSchema.optional(), + item: explanationOfBenefitItemSchema.array().optional(), + addItem: explanationOfBenefitAddItemSchema.array().optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .optional(), + total: explanationOfBenefitTotalSchema.array().optional(), + payment: explanationOfBenefitPaymentSchema.optional(), + formCode: z.lazy(() => codeableConceptSchema).optional(), + form: z.lazy(() => attachmentSchema).optional(), + processNote: explanationOfBenefitProcessNoteSchema.array().optional(), + benefitPeriod: z.lazy(() => periodSchema).optional(), + benefitBalance: explanationOfBenefitBenefitBalanceSchema + .array() + .optional(), + }), + ) + +export const expressionSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + name: idSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + language: codeSchema, + _language: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + reference: uriSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + }), +) + +export const extensionSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + valueMeta: z.lazy(() => metaSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBase64Binary', + 'valueBoolean', + 'valueCanonical', + 'valueCode', + 'valueDate', + 'valueDateTime', + 'valueDecimal', + 'valueId', + 'valueInstant', + 'valueInteger', + 'valueMarkdown', + 'valueOid', + 'valuePositiveInt', + 'valueString', + 'valueTime', + 'valueUnsignedInt', + 'valueUri', + 'valueUrl', + 'valueUuid', + 'valueAddress', + 'valueAge', + 'valueAnnotation', + 'valueAttachment', + 'valueCodeableConcept', + 'valueCoding', + 'valueContactPoint', + 'valueCount', + 'valueDistance', + 'valueDuration', + 'valueHumanName', + 'valueIdentifier', + 'valueMoney', + 'valuePeriod', + 'valueQuantity', + 'valueRange', + 'valueRatio', + 'valueReference', + 'valueSampledData', + 'valueSignature', + 'valueTiming', + 'valueContactDetail', + 'valueContributor', + 'valueDataRequirement', + 'valueExpression', + 'valueParameterDefinition', + 'valueRelatedArtifact', + 'valueTriggerDefinition', + 'valueUsageContext', + 'valueDosage', + 'valueMeta', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => + ((value as { extension?: unknown[] }).extension?.length ?? 0) > 0 !== + Object.keys(value as object).some( + // A primitive sibling means the value element exists carrying only extensions, + // which ext-1 counts as the value being present. + (key) => key.startsWith('value') || key.startsWith('_value'), + ), + { + message: + 'ext-1: an extension has either extensions or a value, never both.', + }, + ), +) + +export const familyMemberHistoryConditionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + outcome: z.lazy(() => codeableConceptSchema).optional(), + contributedToDeath: booleanSchema.optional(), + _contributedToDeath: z.lazy(() => elementSchema).optional(), + onsetAge: z.lazy(() => ageSchema).optional(), + onsetRange: z.lazy(() => rangeSchema).optional(), + onsetPeriod: z.lazy(() => periodSchema).optional(), + onsetString: stringSchema.optional(), + _onsetString: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['onsetAge', 'onsetRange', 'onsetPeriod', 'onsetString'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const familyMemberHistorySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('FamilyMemberHistory'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + status: z.enum([ + 'completed', + 'entered-in-error', + 'health-unknown', + 'partial', + ]), + _status: z.lazy(() => elementSchema).optional(), + dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), + patient: z.lazy(() => referenceSchema), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema), + sex: z.lazy(() => codeableConceptSchema).optional(), + bornPeriod: z.lazy(() => periodSchema).optional(), + bornDate: dateSchema.optional(), + _bornDate: z.lazy(() => elementSchema).optional(), + bornString: stringSchema.optional(), + _bornString: z.lazy(() => elementSchema).optional(), + ageAge: z.lazy(() => ageSchema).optional(), + ageRange: z.lazy(() => rangeSchema).optional(), + ageString: stringSchema.optional(), + _ageString: z.lazy(() => elementSchema).optional(), + estimatedAge: booleanSchema.optional(), + _estimatedAge: z.lazy(() => elementSchema).optional(), + deceasedBoolean: booleanSchema.optional(), + _deceasedBoolean: z.lazy(() => elementSchema).optional(), + deceasedAge: z.lazy(() => ageSchema).optional(), + deceasedRange: z.lazy(() => rangeSchema).optional(), + deceasedDate: dateSchema.optional(), + _deceasedDate: z.lazy(() => elementSchema).optional(), + deceasedString: stringSchema.optional(), + _deceasedString: z.lazy(() => elementSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + condition: familyMemberHistoryConditionSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: ['bornPeriod', 'bornDate', 'bornString'], + required: false, + }, + { names: ['ageAge', 'ageRange', 'ageString'], required: false }, + { + names: [ + 'deceasedBoolean', + 'deceasedAge', + 'deceasedRange', + 'deceasedDate', + 'deceasedString', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const flagSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Flag'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'entered-in-error', 'inactive']), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema), + period: z.lazy(() => periodSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + }), +) + +export const goalTargetSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + measure: z.lazy(() => codeableConceptSchema).optional(), + detailQuantity: z.lazy(() => quantitySchema).optional(), + detailRange: z.lazy(() => rangeSchema).optional(), + detailCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + detailString: stringSchema.optional(), + _detailString: z.lazy(() => elementSchema).optional(), + detailBoolean: booleanSchema.optional(), + _detailBoolean: z.lazy(() => elementSchema).optional(), + detailInteger: integerSchema.optional(), + _detailInteger: z.lazy(() => elementSchema).optional(), + detailRatio: z.lazy(() => ratioSchema).optional(), + dueDate: dateSchema.optional(), + _dueDate: z.lazy(() => elementSchema).optional(), + dueDuration: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'detailQuantity', + 'detailRange', + 'detailCodeableConcept', + 'detailString', + 'detailBoolean', + 'detailInteger', + 'detailRatio', + ], + required: false, + }, + { names: ['dueDate', 'dueDuration'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const goalSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Goal'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + lifecycleStatus: z.enum([ + 'accepted', + 'active', + 'cancelled', + 'completed', + 'entered-in-error', + 'on-hold', + 'planned', + 'proposed', + 'rejected', + ]), + _lifecycleStatus: z.lazy(() => elementSchema).optional(), + achievementStatus: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + priority: z.lazy(() => codeableConceptSchema).optional(), + description: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema), + startDate: dateSchema.optional(), + _startDate: z.lazy(() => elementSchema).optional(), + startCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + target: goalTargetSchema.array().optional(), + statusDate: dateSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), + statusReason: stringSchema.optional(), + _statusReason: z.lazy(() => elementSchema).optional(), + expressedBy: z.lazy(() => referenceSchema).optional(), + addresses: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + outcomeCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + outcomeReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { names: ['startDate', 'startCodeableConcept'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const graphDefinitionLinkSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + sliceName: stringSchema.optional(), + _sliceName: z.lazy(() => elementSchema).optional(), + min: integerSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + target: graphDefinitionLinkTargetSchema.array().optional(), + }), + ) + +export const graphDefinitionLinkTargetSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]), + _type: z.lazy(() => elementSchema).optional(), + params: stringSchema.optional(), + _params: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + compartment: graphDefinitionLinkTargetCompartmentSchema + .array() + .optional(), + link: z + .lazy(() => graphDefinitionLinkSchema) + .array() + .optional(), + }), + ) + +export const graphDefinitionLinkTargetCompartmentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + use: z.enum(['condition', 'requirement']), + _use: z.lazy(() => elementSchema).optional(), + code: z.enum([ + 'Device', + 'Encounter', + 'Patient', + 'Practitioner', + 'RelatedPerson', + ]), + _code: z.lazy(() => elementSchema).optional(), + rule: z.enum(['custom', 'different', 'identical', 'matching']), + _rule: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }), + ) + +export const graphDefinitionSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('GraphDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + start: z.enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]), + _start: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + link: graphDefinitionLinkSchema.array().optional(), + }), +) + +export const groupCharacteristicSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + exclude: booleanSchema, + _exclude: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueBoolean', + 'valueQuantity', + 'valueRange', + 'valueReference', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const groupMemberSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + entity: z.lazy(() => referenceSchema), + period: z.lazy(() => periodSchema).optional(), + inactive: booleanSchema.optional(), + _inactive: z.lazy(() => elementSchema).optional(), + }), +) + +export const groupSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Group'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + type: z.enum([ + 'animal', + 'device', + 'medication', + 'person', + 'practitioner', + 'substance', + ]), + _type: z.lazy(() => elementSchema).optional(), + actual: booleanSchema, + _actual: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + quantity: unsignedIntSchema.optional(), + _quantity: z.lazy(() => elementSchema).optional(), + managingEntity: z.lazy(() => referenceSchema).optional(), + characteristic: groupCharacteristicSchema.array().optional(), + member: groupMemberSchema.array().optional(), + }), +) + +export const guidanceResponseSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('GuidanceResponse'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + requestIdentifier: z.lazy(() => identifierSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + moduleUri: uriSchema.optional(), + _moduleUri: z.lazy(() => elementSchema).optional(), + moduleCanonical: canonicalSchema.optional(), + _moduleCanonical: z.lazy(() => elementSchema).optional(), + moduleCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum([ + 'data-requested', + 'data-required', + 'entered-in-error', + 'failure', + 'in-progress', + 'success', + ]), + _status: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + evaluationMessage: z + .lazy(() => referenceSchema) + .array() + .optional(), + outputParameters: z.lazy(() => referenceSchema).optional(), + result: z.lazy(() => referenceSchema).optional(), + dataRequirement: z + .lazy(() => dataRequirementSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['moduleUri', 'moduleCanonical', 'moduleCodeableConcept'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const healthcareServiceEligibilitySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + comment: markdownSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), + ) + +export const healthcareServiceAvailableTimeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + daysOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .array() + .optional(), + _daysOfWeek: z + .lazy(() => elementSchema) + .array() + .optional(), + allDay: booleanSchema.optional(), + _allDay: z.lazy(() => elementSchema).optional(), + availableStartTime: timeSchema.optional(), + _availableStartTime: z.lazy(() => elementSchema).optional(), + availableEndTime: timeSchema.optional(), + _availableEndTime: z.lazy(() => elementSchema).optional(), + }), + ) + +export const healthcareServiceNotAvailableSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema, + _description: z.lazy(() => elementSchema).optional(), + during: z.lazy(() => periodSchema).optional(), + }), + ) + +export const healthcareServiceSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('HealthcareService'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + providedBy: z.lazy(() => referenceSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + location: z + .lazy(() => referenceSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + extraDetails: markdownSchema.optional(), + _extraDetails: z.lazy(() => elementSchema).optional(), + photo: z.lazy(() => attachmentSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + coverageArea: z + .lazy(() => referenceSchema) + .array() + .optional(), + serviceProvisionCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + eligibility: healthcareServiceEligibilitySchema.array().optional(), + program: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + characteristic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + communication: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + referralMethod: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + appointmentRequired: booleanSchema.optional(), + _appointmentRequired: z.lazy(() => elementSchema).optional(), + availableTime: healthcareServiceAvailableTimeSchema.array().optional(), + notAvailable: healthcareServiceNotAvailableSchema.array().optional(), + availabilityExceptions: stringSchema.optional(), + _availabilityExceptions: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const humanNameSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + use: z + .enum([ + 'anonymous', + 'maiden', + 'nickname', + 'official', + 'old', + 'temp', + 'usual', + ]) + .optional(), + _use: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + family: stringSchema.optional(), + _family: z.lazy(() => elementSchema).optional(), + given: stringSchema.array().optional(), + _given: z + .lazy(() => elementSchema) + .array() + .optional(), + prefix: stringSchema.array().optional(), + _prefix: z + .lazy(() => elementSchema) + .array() + .optional(), + suffix: stringSchema.array().optional(), + _suffix: z + .lazy(() => elementSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + }), +) + +export const identifierSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + use: z.enum(['official', 'old', 'secondary', 'temp', 'usual']).optional(), + _use: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + assigner: z.lazy(() => referenceSchema).optional(), + }), +) + +export const imagingStudySeriesSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + uid: idSchema, + _uid: z.lazy(() => elementSchema).optional(), + number: unsignedIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + modality: z.lazy(() => codingSchema), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + numberOfInstances: unsignedIntSchema.optional(), + _numberOfInstances: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + bodySite: z.lazy(() => codingSchema).optional(), + laterality: z.lazy(() => codingSchema).optional(), + specimen: z + .lazy(() => referenceSchema) + .array() + .optional(), + started: dateTimeSchema.optional(), + _started: z.lazy(() => elementSchema).optional(), + performer: imagingStudySeriesPerformerSchema.array().optional(), + instance: imagingStudySeriesInstanceSchema.array().optional(), + }), + ) + +export const imagingStudySeriesPerformerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }), + ) + +export const imagingStudySeriesInstanceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + uid: idSchema, + _uid: z.lazy(() => elementSchema).optional(), + sopClass: z.lazy(() => codingSchema), + number: unsignedIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + }), + ) + +export const imagingStudySchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('ImagingStudy'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'available', + 'cancelled', + 'entered-in-error', + 'registered', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + modality: z + .lazy(() => codingSchema) + .array() + .optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + started: dateTimeSchema.optional(), + _started: z.lazy(() => elementSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + referrer: z.lazy(() => referenceSchema).optional(), + interpreter: z + .lazy(() => referenceSchema) + .array() + .optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + numberOfSeries: unsignedIntSchema.optional(), + _numberOfSeries: z.lazy(() => elementSchema).optional(), + numberOfInstances: unsignedIntSchema.optional(), + _numberOfInstances: z.lazy(() => elementSchema).optional(), + procedureReference: z.lazy(() => referenceSchema).optional(), + procedureCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + location: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + series: imagingStudySeriesSchema.array().optional(), + }), +) + +export const immunizationPerformerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }), + ) + +export const immunizationEducationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + documentType: stringSchema.optional(), + _documentType: z.lazy(() => elementSchema).optional(), + reference: uriSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + publicationDate: dateTimeSchema.optional(), + _publicationDate: z.lazy(() => elementSchema).optional(), + presentationDate: dateTimeSchema.optional(), + _presentationDate: z.lazy(() => elementSchema).optional(), + }), + ) + +export const immunizationReactionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + detail: z.lazy(() => referenceSchema).optional(), + reported: booleanSchema.optional(), + _reported: z.lazy(() => elementSchema).optional(), + }), + ) + +export const immunizationProtocolAppliedSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + series: stringSchema.optional(), + _series: z.lazy(() => elementSchema).optional(), + authority: z.lazy(() => referenceSchema).optional(), + targetDisease: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + doseNumberPositiveInt: positiveIntSchema.optional(), + _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), + doseNumberString: stringSchema.optional(), + _doseNumberString: z.lazy(() => elementSchema).optional(), + seriesDosesPositiveInt: positiveIntSchema.optional(), + _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), + seriesDosesString: stringSchema.optional(), + _seriesDosesString: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['doseNumberPositiveInt', 'doseNumberString'], + required: true, + }, + { + names: ['seriesDosesPositiveInt', 'seriesDosesString'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const immunizationSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Immunization'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['completed', 'entered-in-error', 'not-done']), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + vaccineCode: z.lazy(() => codeableConceptSchema), + patient: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrenceString: stringSchema.optional(), + _occurrenceString: z.lazy(() => elementSchema).optional(), + recorded: dateTimeSchema.optional(), + _recorded: z.lazy(() => elementSchema).optional(), + primarySource: booleanSchema.optional(), + _primarySource: z.lazy(() => elementSchema).optional(), + reportOrigin: z.lazy(() => codeableConceptSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + manufacturer: z.lazy(() => referenceSchema).optional(), + lotNumber: stringSchema.optional(), + _lotNumber: z.lazy(() => elementSchema).optional(), + expirationDate: dateSchema.optional(), + _expirationDate: z.lazy(() => elementSchema).optional(), + site: z.lazy(() => codeableConceptSchema).optional(), + route: z.lazy(() => codeableConceptSchema).optional(), + doseQuantity: z.lazy(() => quantitySchema).optional(), + performer: immunizationPerformerSchema.array().optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + isSubpotent: booleanSchema.optional(), + _isSubpotent: z.lazy(() => elementSchema).optional(), + subpotentReason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + education: immunizationEducationSchema.array().optional(), + programEligibility: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + fundingSource: z.lazy(() => codeableConceptSchema).optional(), + reaction: immunizationReactionSchema.array().optional(), + protocolApplied: immunizationProtocolAppliedSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['occurrenceDateTime', 'occurrenceString'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const immunizationEvaluationSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ImmunizationEvaluation'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['completed', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + authority: z.lazy(() => referenceSchema).optional(), + targetDisease: z.lazy(() => codeableConceptSchema), + immunizationEvent: z.lazy(() => referenceSchema), + doseStatus: z.lazy(() => codeableConceptSchema), + doseStatusReason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + series: stringSchema.optional(), + _series: z.lazy(() => elementSchema).optional(), + doseNumberPositiveInt: positiveIntSchema.optional(), + _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), + doseNumberString: stringSchema.optional(), + _doseNumberString: z.lazy(() => elementSchema).optional(), + seriesDosesPositiveInt: positiveIntSchema.optional(), + _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), + seriesDosesString: stringSchema.optional(), + _seriesDosesString: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['doseNumberPositiveInt', 'doseNumberString'], + required: false, + }, + { + names: ['seriesDosesPositiveInt', 'seriesDosesString'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const immunizationRecommendationRecommendationSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + vaccineCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + targetDisease: z.lazy(() => codeableConceptSchema).optional(), + contraindicatedVaccineCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + forecastStatus: z.lazy(() => codeableConceptSchema), + forecastReason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + dateCriterion: + immunizationRecommendationRecommendationDateCriterionSchema + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + series: stringSchema.optional(), + _series: z.lazy(() => elementSchema).optional(), + doseNumberPositiveInt: positiveIntSchema.optional(), + _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), + doseNumberString: stringSchema.optional(), + _doseNumberString: z.lazy(() => elementSchema).optional(), + seriesDosesPositiveInt: positiveIntSchema.optional(), + _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), + seriesDosesString: stringSchema.optional(), + _seriesDosesString: z.lazy(() => elementSchema).optional(), + supportingImmunization: z + .lazy(() => referenceSchema) + .array() + .optional(), + supportingPatientInformation: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['doseNumberPositiveInt', 'doseNumberString'], + required: false, + }, + { + names: ['seriesDosesPositiveInt', 'seriesDosesString'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const immunizationRecommendationRecommendationDateCriterionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + value: dateTimeSchema, + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const immunizationRecommendationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('ImmunizationRecommendation'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + patient: z.lazy(() => referenceSchema), + date: dateTimeSchema, + _date: z.lazy(() => elementSchema).optional(), + authority: z.lazy(() => referenceSchema).optional(), + recommendation: immunizationRecommendationRecommendationSchema + .array() + .min(1), + }), + ) + +export const implementationGuideDependsOnSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + uri: canonicalSchema, + _uri: z.lazy(() => elementSchema).optional(), + packageId: idSchema.optional(), + _packageId: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + }), + ) + +export const implementationGuideGlobalSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]), + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema, + _profile: z.lazy(() => elementSchema).optional(), + }), + ) + +export const implementationGuideDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + grouping: implementationGuideDefinitionGroupingSchema.array().optional(), + resource: implementationGuideDefinitionResourceSchema.array().min(1), + page: implementationGuideDefinitionPageSchema.optional(), + parameter: implementationGuideDefinitionParameterSchema + .array() + .optional(), + template: implementationGuideDefinitionTemplateSchema.array().optional(), + }), + ) + +export const implementationGuideManifestSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + rendering: urlSchema.optional(), + _rendering: z.lazy(() => elementSchema).optional(), + resource: implementationGuideManifestResourceSchema.array().min(1), + page: implementationGuideManifestPageSchema.array().optional(), + image: stringSchema.array().optional(), + _image: z + .lazy(() => elementSchema) + .array() + .optional(), + other: stringSchema.array().optional(), + _other: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const implementationGuideDefinitionGroupingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }), + ) + +export const implementationGuideDefinitionResourceSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + reference: z.lazy(() => referenceSchema), + fhirVersion: z + .enum([ + '0.0.80', + '0.0.81', + '0.0.82', + '0.01', + '0.05', + '0.06', + '0.11', + '0.4.0', + '0.5.0', + '1.0.0', + '1.0.1', + '1.0.2', + '1.1.0', + '1.4.0', + '1.6.0', + '1.8.0', + '3.0.0', + '3.0.1', + '3.3.0', + '3.5.0', + '4.0.0', + '4.0.1', + ]) + .array() + .optional(), + _fhirVersion: z + .lazy(() => elementSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + exampleBoolean: booleanSchema.optional(), + _exampleBoolean: z.lazy(() => elementSchema).optional(), + exampleCanonical: canonicalSchema.optional(), + _exampleCanonical: z.lazy(() => elementSchema).optional(), + groupingId: idSchema.optional(), + _groupingId: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['exampleBoolean', 'exampleCanonical'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const implementationGuideDefinitionPageSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + nameUrl: urlSchema.optional(), + _nameUrl: z.lazy(() => elementSchema).optional(), + nameReference: z.lazy(() => referenceSchema).optional(), + title: stringSchema, + _title: z.lazy(() => elementSchema).optional(), + generation: z.enum(['generated', 'html', 'markdown', 'xml']), + _generation: z.lazy(() => elementSchema).optional(), + page: z + .lazy(() => implementationGuideDefinitionPageSchema) + .array() + .optional(), + }) + .refine( + (value) => + [{ names: ['nameUrl', 'nameReference'], required: true }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const implementationGuideDefinitionParameterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.enum([ + 'apply', + 'expansion-parameter', + 'generate-json', + 'generate-turtle', + 'generate-xml', + 'html-template', + 'path-pages', + 'path-resource', + 'path-tx-cache', + 'rule-broken-links', + ]), + _code: z.lazy(() => elementSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const implementationGuideDefinitionTemplateSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + source: stringSchema, + _source: z.lazy(() => elementSchema).optional(), + scope: stringSchema.optional(), + _scope: z.lazy(() => elementSchema).optional(), + }), + ) + +export const implementationGuideManifestResourceSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + reference: z.lazy(() => referenceSchema), + exampleBoolean: booleanSchema.optional(), + _exampleBoolean: z.lazy(() => elementSchema).optional(), + exampleCanonical: canonicalSchema.optional(), + _exampleCanonical: z.lazy(() => elementSchema).optional(), + relativePath: urlSchema.optional(), + _relativePath: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['exampleBoolean', 'exampleCanonical'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const implementationGuideManifestPageSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + anchor: stringSchema.array().optional(), + _anchor: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const implementationGuideSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('ImplementationGuide'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + packageId: idSchema, + _packageId: z.lazy(() => elementSchema).optional(), + license: z + .enum([ + '0BSD', + 'AAL', + 'ADSL', + 'AFL-1.1', + 'AFL-1.2', + 'AFL-2.0', + 'AFL-2.1', + 'AFL-3.0', + 'AGPL-1.0-only', + 'AGPL-1.0-or-later', + 'AGPL-3.0-only', + 'AGPL-3.0-or-later', + 'AMDPLPA', + 'AML', + 'AMPAS', + 'ANTLR-PD', + 'APAFML', + 'APL-1.0', + 'APSL-1.0', + 'APSL-1.1', + 'APSL-1.2', + 'APSL-2.0', + 'Abstyles', + 'Adobe-2006', + 'Adobe-Glyph', + 'Afmparse', + 'Aladdin', + 'Apache-1.0', + 'Apache-1.1', + 'Apache-2.0', + 'Artistic-1.0', + 'Artistic-1.0-Perl', + 'Artistic-1.0-cl8', + 'Artistic-2.0', + 'BSD-1-Clause', + 'BSD-2-Clause', + 'BSD-2-Clause-FreeBSD', + 'BSD-2-Clause-NetBSD', + 'BSD-2-Clause-Patent', + 'BSD-3-Clause', + 'BSD-3-Clause-Attribution', + 'BSD-3-Clause-Clear', + 'BSD-3-Clause-LBNL', + 'BSD-3-Clause-No-Nuclear-License', + 'BSD-3-Clause-No-Nuclear-License-2014', + 'BSD-3-Clause-No-Nuclear-Warranty', + 'BSD-4-Clause', + 'BSD-4-Clause-UC', + 'BSD-Protection', + 'BSD-Source-Code', + 'BSL-1.0', + 'Bahyph', + 'Barr', + 'Beerware', + 'BitTorrent-1.0', + 'BitTorrent-1.1', + 'Borceux', + 'CATOSL-1.1', + 'CC-BY-1.0', + 'CC-BY-2.0', + 'CC-BY-2.5', + 'CC-BY-3.0', + 'CC-BY-4.0', + 'CC-BY-NC-1.0', + 'CC-BY-NC-2.0', + 'CC-BY-NC-2.5', + 'CC-BY-NC-3.0', + 'CC-BY-NC-4.0', + 'CC-BY-NC-ND-1.0', + 'CC-BY-NC-ND-2.0', + 'CC-BY-NC-ND-2.5', + 'CC-BY-NC-ND-3.0', + 'CC-BY-NC-ND-4.0', + 'CC-BY-NC-SA-1.0', + 'CC-BY-NC-SA-2.0', + 'CC-BY-NC-SA-2.5', + 'CC-BY-NC-SA-3.0', + 'CC-BY-NC-SA-4.0', + 'CC-BY-ND-1.0', + 'CC-BY-ND-2.0', + 'CC-BY-ND-2.5', + 'CC-BY-ND-3.0', + 'CC-BY-ND-4.0', + 'CC-BY-SA-1.0', + 'CC-BY-SA-2.0', + 'CC-BY-SA-2.5', + 'CC-BY-SA-3.0', + 'CC-BY-SA-4.0', + 'CC0-1.0', + 'CDDL-1.0', + 'CDDL-1.1', + 'CDLA-Permissive-1.0', + 'CDLA-Sharing-1.0', + 'CECILL-1.0', + 'CECILL-1.1', + 'CECILL-2.0', + 'CECILL-2.1', + 'CECILL-B', + 'CECILL-C', + 'CNRI-Jython', + 'CNRI-Python', + 'CNRI-Python-GPL-Compatible', + 'CPAL-1.0', + 'CPL-1.0', + 'CPOL-1.02', + 'CUA-OPL-1.0', + 'Caldera', + 'ClArtistic', + 'Condor-1.1', + 'Crossword', + 'CrystalStacker', + 'Cube', + 'D-FSL-1.0', + 'DOC', + 'DSDP', + 'Dotseqn', + 'ECL-1.0', + 'ECL-2.0', + 'EFL-1.0', + 'EFL-2.0', + 'EPL-1.0', + 'EPL-2.0', + 'EUDatagrid', + 'EUPL-1.0', + 'EUPL-1.1', + 'EUPL-1.2', + 'Entessa', + 'ErlPL-1.1', + 'Eurosym', + 'FSFAP', + 'FSFUL', + 'FSFULLR', + 'FTL', + 'Fair', + 'Frameworx-1.0', + 'FreeImage', + 'GFDL-1.1-only', + 'GFDL-1.1-or-later', + 'GFDL-1.2-only', + 'GFDL-1.2-or-later', + 'GFDL-1.3-only', + 'GFDL-1.3-or-later', + 'GL2PS', + 'GPL-1.0-only', + 'GPL-1.0-or-later', + 'GPL-2.0-only', + 'GPL-2.0-or-later', + 'GPL-3.0-only', + 'GPL-3.0-or-later', + 'Giftware', + 'Glide', + 'Glulxe', + 'HPND', + 'HaskellReport', + 'IBM-pibs', + 'ICU', + 'IJG', + 'IPA', + 'IPL-1.0', + 'ISC', + 'ImageMagick', + 'Imlib2', + 'Info-ZIP', + 'Intel', + 'Intel-ACPI', + 'Interbase-1.0', + 'JSON', + 'JasPer-2.0', + 'LAL-1.2', + 'LAL-1.3', + 'LGPL-2.0-only', + 'LGPL-2.0-or-later', + 'LGPL-2.1-only', + 'LGPL-2.1-or-later', + 'LGPL-3.0-only', + 'LGPL-3.0-or-later', + 'LGPLLR', + 'LPL-1.0', + 'LPL-1.02', + 'LPPL-1.0', + 'LPPL-1.1', + 'LPPL-1.2', + 'LPPL-1.3a', + 'LPPL-1.3c', + 'Latex2e', + 'Leptonica', + 'LiLiQ-P-1.1', + 'LiLiQ-R-1.1', + 'LiLiQ-Rplus-1.1', + 'Libpng', + 'Linux-OpenIB', + 'MIT', + 'MIT-0', + 'MIT-CMU', + 'MIT-advertising', + 'MIT-enna', + 'MIT-feh', + 'MITNFA', + 'MPL-1.0', + 'MPL-1.1', + 'MPL-2.0', + 'MPL-2.0-no-copyleft-exception', + 'MS-PL', + 'MS-RL', + 'MTLL', + 'MakeIndex', + 'MirOS', + 'Motosoto', + 'Multics', + 'Mup', + 'NASA-1.3', + 'NBPL-1.0', + 'NCSA', + 'NGPL', + 'NLOD-1.0', + 'NLPL', + 'NOSL', + 'NPL-1.0', + 'NPL-1.1', + 'NPOSL-3.0', + 'NRL', + 'NTP', + 'Naumen', + 'Net-SNMP', + 'NetCDF', + 'Newsletr', + 'Nokia', + 'Noweb', + 'OCCT-PL', + 'OCLC-2.0', + 'ODbL-1.0', + 'OFL-1.0', + 'OFL-1.1', + 'OGTSL', + 'OLDAP-1.1', + 'OLDAP-1.2', + 'OLDAP-1.3', + 'OLDAP-1.4', + 'OLDAP-2.0', + 'OLDAP-2.0.1', + 'OLDAP-2.1', + 'OLDAP-2.2', + 'OLDAP-2.2.1', + 'OLDAP-2.2.2', + 'OLDAP-2.3', + 'OLDAP-2.4', + 'OLDAP-2.5', + 'OLDAP-2.6', + 'OLDAP-2.7', + 'OLDAP-2.8', + 'OML', + 'OPL-1.0', + 'OSET-PL-2.1', + 'OSL-1.0', + 'OSL-1.1', + 'OSL-2.0', + 'OSL-2.1', + 'OSL-3.0', + 'OpenSSL', + 'PDDL-1.0', + 'PHP-3.0', + 'PHP-3.01', + 'Plexus', + 'PostgreSQL', + 'Python-2.0', + 'QPL-1.0', + 'Qhull', + 'RHeCos-1.1', + 'RPL-1.1', + 'RPL-1.5', + 'RPSL-1.0', + 'RSA-MD', + 'RSCPL', + 'Rdisc', + 'Ruby', + 'SAX-PD', + 'SCEA', + 'SGI-B-1.0', + 'SGI-B-1.1', + 'SGI-B-2.0', + 'SISSL', + 'SISSL-1.2', + 'SMLNJ', + 'SMPPL', + 'SNIA', + 'SPL-1.0', + 'SWL', + 'Saxpath', + 'Sendmail', + 'SimPL-2.0', + 'Sleepycat', + 'Spencer-86', + 'Spencer-94', + 'Spencer-99', + 'SugarCRM-1.1.3', + 'TCL', + 'TCP-wrappers', + 'TMate', + 'TORQUE-1.1', + 'TOSL', + 'UPL-1.0', + 'Unicode-DFS-2015', + 'Unicode-DFS-2016', + 'Unicode-TOU', + 'Unlicense', + 'VOSTROM', + 'VSL-1.0', + 'Vim', + 'W3C', + 'W3C-19980720', + 'W3C-20150513', + 'WTFPL', + 'Watcom-1.0', + 'Wsuipa', + 'X11', + 'XFree86-1.1', + 'XSkat', + 'Xerox', + 'Xnet', + 'YPL-1.0', + 'YPL-1.1', + 'ZPL-1.1', + 'ZPL-2.0', + 'ZPL-2.1', + 'Zed', + 'Zend-2.0', + 'Zimbra-1.3', + 'Zimbra-1.4', + 'Zlib', + 'bzip2-1.0.5', + 'bzip2-1.0.6', + 'curl', + 'diffmark', + 'dvipdfm', + 'eGenix', + 'gSOAP-1.3b', + 'gnuplot', + 'iMatix', + 'libtiff', + 'mpich2', + 'not-open-source', + 'psfrag', + 'psutils', + 'xinetd', + 'xpp', + 'zlib-acknowledgement', + ]) + .optional(), + _license: z.lazy(() => elementSchema).optional(), + fhirVersion: z + .enum([ + '0.0.80', + '0.0.81', + '0.0.82', + '0.01', + '0.05', + '0.06', + '0.11', + '0.4.0', + '0.5.0', + '1.0.0', + '1.0.1', + '1.0.2', + '1.1.0', + '1.4.0', + '1.6.0', + '1.8.0', + '3.0.0', + '3.0.1', + '3.3.0', + '3.5.0', + '4.0.0', + '4.0.1', + ]) + .array() + .min(1), + _fhirVersion: z + .lazy(() => elementSchema) + .array() + .optional(), + dependsOn: implementationGuideDependsOnSchema.array().optional(), + global: implementationGuideGlobalSchema.array().optional(), + definition: implementationGuideDefinitionSchema.optional(), + manifest: implementationGuideManifestSchema.optional(), + }), + ) + +export const insurancePlanContactSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + purpose: z.lazy(() => codeableConceptSchema).optional(), + name: z.lazy(() => humanNameSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + address: z.lazy(() => addressSchema).optional(), + }), + ) + +export const insurancePlanCoverageSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + network: z + .lazy(() => referenceSchema) + .array() + .optional(), + benefit: insurancePlanCoverageBenefitSchema.array().min(1), + }), + ) + +export const insurancePlanPlanSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + coverageArea: z + .lazy(() => referenceSchema) + .array() + .optional(), + network: z + .lazy(() => referenceSchema) + .array() + .optional(), + generalCost: insurancePlanPlanGeneralCostSchema.array().optional(), + specificCost: insurancePlanPlanSpecificCostSchema.array().optional(), + }), +) + +export const insurancePlanCoverageBenefitSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + requirement: stringSchema.optional(), + _requirement: z.lazy(() => elementSchema).optional(), + limit: insurancePlanCoverageBenefitLimitSchema.array().optional(), + }), + ) + +export const insurancePlanPlanGeneralCostSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + groupSize: positiveIntSchema.optional(), + _groupSize: z.lazy(() => elementSchema).optional(), + cost: z.lazy(() => moneySchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), + ) + +export const insurancePlanPlanSpecificCostSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema), + benefit: insurancePlanPlanSpecificCostBenefitSchema.array().optional(), + }), + ) + +export const insurancePlanCoverageBenefitLimitSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: z.lazy(() => quantitySchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const insurancePlanPlanSpecificCostBenefitSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + cost: insurancePlanPlanSpecificCostBenefitCostSchema.array().optional(), + }), + ) + +export const insurancePlanPlanSpecificCostBenefitCostSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + applicability: z.lazy(() => codeableConceptSchema).optional(), + qualifiers: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + value: z.lazy(() => quantitySchema).optional(), + }), + ) + +export const insurancePlanSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('InsurancePlan'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + alias: stringSchema.array().optional(), + _alias: z + .lazy(() => elementSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + ownedBy: z.lazy(() => referenceSchema).optional(), + administeredBy: z.lazy(() => referenceSchema).optional(), + coverageArea: z + .lazy(() => referenceSchema) + .array() + .optional(), + contact: insurancePlanContactSchema.array().optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + network: z + .lazy(() => referenceSchema) + .array() + .optional(), + coverage: insurancePlanCoverageSchema.array().optional(), + plan: insurancePlanPlanSchema.array().optional(), + }), +) + +export const invoiceParticipantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }), + ) + +export const invoiceLineItemSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + chargeItemReference: z.lazy(() => referenceSchema).optional(), + chargeItemCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + priceComponent: invoiceLineItemPriceComponentSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: ['chargeItemReference', 'chargeItemCodeableConcept'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const invoiceLineItemPriceComponentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'base', + 'deduction', + 'discount', + 'informational', + 'surcharge', + 'tax', + ]), + _type: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + }), + ) + +export const invoiceSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Invoice'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'balanced', + 'cancelled', + 'draft', + 'entered-in-error', + 'issued', + ]), + _status: z.lazy(() => elementSchema).optional(), + cancelledReason: stringSchema.optional(), + _cancelledReason: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + recipient: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + participant: invoiceParticipantSchema.array().optional(), + issuer: z.lazy(() => referenceSchema).optional(), + account: z.lazy(() => referenceSchema).optional(), + lineItem: invoiceLineItemSchema.array().optional(), + totalPriceComponent: z + .lazy(() => invoiceLineItemPriceComponentSchema) + .array() + .optional(), + totalNet: z.lazy(() => moneySchema).optional(), + totalGross: z.lazy(() => moneySchema).optional(), + paymentTerms: markdownSchema.optional(), + _paymentTerms: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), +) + +export const librarySchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Library'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + parameter: z + .lazy(() => parameterDefinitionSchema) + .array() + .optional(), + dataRequirement: z + .lazy(() => dataRequirementSchema) + .array() + .optional(), + content: z + .lazy(() => attachmentSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['subjectCodeableConcept', 'subjectReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const linkageItemSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['alternate', 'historical', 'source']), + _type: z.lazy(() => elementSchema).optional(), + resource: z.lazy(() => referenceSchema), + }), +) + +export const linkageSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Linkage'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + item: linkageItemSchema.array().min(1), + }), +) + +export const listEntrySchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + flag: z.lazy(() => codeableConceptSchema).optional(), + deleted: booleanSchema.optional(), + _deleted: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + item: z.lazy(() => referenceSchema), + }), +) + +export const listSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('List'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['current', 'entered-in-error', 'retired']), + _status: z.lazy(() => elementSchema).optional(), + mode: z.enum(['changes', 'snapshot', 'working']), + _mode: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + orderedBy: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + entry: listEntrySchema.array().optional(), + emptyReason: z.lazy(() => codeableConceptSchema).optional(), + }), +) + +export const locationPositionSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + longitude: decimalSchema, + _longitude: z.lazy(() => elementSchema).optional(), + latitude: decimalSchema, + _latitude: z.lazy(() => elementSchema).optional(), + altitude: decimalSchema.optional(), + _altitude: z.lazy(() => elementSchema).optional(), + }), +) + +export const locationHoursOfOperationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + daysOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .array() + .optional(), + _daysOfWeek: z + .lazy(() => elementSchema) + .array() + .optional(), + allDay: booleanSchema.optional(), + _allDay: z.lazy(() => elementSchema).optional(), + openingTime: timeSchema.optional(), + _openingTime: z.lazy(() => elementSchema).optional(), + closingTime: timeSchema.optional(), + _closingTime: z.lazy(() => elementSchema).optional(), + }), + ) + +export const locationSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Location'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'inactive', 'suspended']).optional(), + _status: z.lazy(() => elementSchema).optional(), + operationalStatus: z.lazy(() => codingSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + alias: stringSchema.array().optional(), + _alias: z + .lazy(() => elementSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + mode: z.enum(['instance', 'kind']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + address: z.lazy(() => addressSchema).optional(), + physicalType: z.lazy(() => codeableConceptSchema).optional(), + position: locationPositionSchema.optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + partOf: z.lazy(() => referenceSchema).optional(), + hoursOfOperation: locationHoursOfOperationSchema.array().optional(), + availabilityExceptions: stringSchema.optional(), + _availabilityExceptions: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const marketingStatusSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + country: z.lazy(() => codeableConceptSchema), + jurisdiction: z.lazy(() => codeableConceptSchema).optional(), + status: z.lazy(() => codeableConceptSchema), + dateRange: z.lazy(() => periodSchema), + restoreDate: dateTimeSchema.optional(), + _restoreDate: z.lazy(() => elementSchema).optional(), + }), +) + +export const measureGroupSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + population: measureGroupPopulationSchema.array().optional(), + stratifier: measureGroupStratifierSchema.array().optional(), + }), +) + +export const measureSupplementalDataSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + usage: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema), + }), + ) + +export const measureGroupPopulationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema), + }), + ) + +export const measureGroupStratifierSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema).optional(), + component: measureGroupStratifierComponentSchema.array().optional(), + }), + ) + +export const measureGroupStratifierComponentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema), + }), + ) + +export const measureSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Measure'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + library: canonicalSchema.array().optional(), + _library: z + .lazy(() => elementSchema) + .array() + .optional(), + disclaimer: markdownSchema.optional(), + _disclaimer: z.lazy(() => elementSchema).optional(), + scoring: z.lazy(() => codeableConceptSchema).optional(), + compositeScoring: z.lazy(() => codeableConceptSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + riskAdjustment: stringSchema.optional(), + _riskAdjustment: z.lazy(() => elementSchema).optional(), + rateAggregation: stringSchema.optional(), + _rateAggregation: z.lazy(() => elementSchema).optional(), + rationale: markdownSchema.optional(), + _rationale: z.lazy(() => elementSchema).optional(), + clinicalRecommendationStatement: markdownSchema.optional(), + _clinicalRecommendationStatement: z.lazy(() => elementSchema).optional(), + improvementNotation: z.lazy(() => codeableConceptSchema).optional(), + definition: markdownSchema.array().optional(), + _definition: z + .lazy(() => elementSchema) + .array() + .optional(), + guidance: markdownSchema.optional(), + _guidance: z.lazy(() => elementSchema).optional(), + group: measureGroupSchema.array().optional(), + supplementalData: measureSupplementalDataSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: ['subjectCodeableConcept', 'subjectReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const measureReportGroupSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + population: measureReportGroupPopulationSchema.array().optional(), + measureScore: z.lazy(() => quantitySchema).optional(), + stratifier: measureReportGroupStratifierSchema.array().optional(), + }), + ) + +export const measureReportGroupPopulationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + count: integerSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + subjectResults: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const measureReportGroupStratifierSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + stratum: measureReportGroupStratifierStratumSchema.array().optional(), + }), + ) + +export const measureReportGroupStratifierStratumSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: z.lazy(() => codeableConceptSchema).optional(), + component: measureReportGroupStratifierStratumComponentSchema + .array() + .optional(), + population: measureReportGroupStratifierStratumPopulationSchema + .array() + .optional(), + measureScore: z.lazy(() => quantitySchema).optional(), + }), + ) + +export const measureReportGroupStratifierStratumComponentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + value: z.lazy(() => codeableConceptSchema), + }), + ) + +export const measureReportGroupStratifierStratumPopulationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + count: integerSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + subjectResults: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const measureReportSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('MeasureReport'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['complete', 'error', 'pending']), + _status: z.lazy(() => elementSchema).optional(), + type: z.enum(['data-collection', 'individual', 'subject-list', 'summary']), + _type: z.lazy(() => elementSchema).optional(), + measure: canonicalSchema, + _measure: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + reporter: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema), + improvementNotation: z.lazy(() => codeableConceptSchema).optional(), + group: measureReportGroupSchema.array().optional(), + evaluatedResource: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const mediaSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Media'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + modality: z.lazy(() => codeableConceptSchema).optional(), + view: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + createdDateTime: dateTimeSchema.optional(), + _createdDateTime: z.lazy(() => elementSchema).optional(), + createdPeriod: z.lazy(() => periodSchema).optional(), + issued: instantSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + operator: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + deviceName: stringSchema.optional(), + _deviceName: z.lazy(() => elementSchema).optional(), + device: z.lazy(() => referenceSchema).optional(), + height: positiveIntSchema.optional(), + _height: z.lazy(() => elementSchema).optional(), + width: positiveIntSchema.optional(), + _width: z.lazy(() => elementSchema).optional(), + frames: positiveIntSchema.optional(), + _frames: z.lazy(() => elementSchema).optional(), + duration: decimalSchema.optional(), + _duration: z.lazy(() => elementSchema).optional(), + content: z.lazy(() => attachmentSchema), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { names: ['createdDateTime', 'createdPeriod'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const medicationIngredientSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + isActive: booleanSchema.optional(), + _isActive: z.lazy(() => elementSchema).optional(), + strength: z.lazy(() => ratioSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['itemCodeableConcept', 'itemReference'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationBatchSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + lotNumber: stringSchema.optional(), + _lotNumber: z.lazy(() => elementSchema).optional(), + expirationDate: dateTimeSchema.optional(), + _expirationDate: z.lazy(() => elementSchema).optional(), + }), +) + +export const medicationSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Medication'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), + _status: z.lazy(() => elementSchema).optional(), + manufacturer: z.lazy(() => referenceSchema).optional(), + form: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => ratioSchema).optional(), + ingredient: medicationIngredientSchema.array().optional(), + batch: medicationBatchSchema.optional(), + }), +) + +export const medicationAdministrationPerformerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }), + ) + +export const medicationAdministrationDosageSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + site: z.lazy(() => codeableConceptSchema).optional(), + route: z.lazy(() => codeableConceptSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + dose: z.lazy(() => quantitySchema).optional(), + rateRatio: z.lazy(() => ratioSchema).optional(), + rateQuantity: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => + [{ names: ['rateRatio', 'rateQuantity'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationAdministrationSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicationAdministration'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiates: uriSchema.array().optional(), + _instantiates: z + .lazy(() => elementSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + medicationCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema), + context: z.lazy(() => referenceSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + performer: medicationAdministrationPerformerSchema.array().optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + request: z.lazy(() => referenceSchema).optional(), + device: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + dosage: medicationAdministrationDosageSchema.optional(), + eventHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['medicationCodeableConcept', 'medicationReference'], + required: true, + }, + { names: ['effectiveDateTime', 'effectivePeriod'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationDispensePerformerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }), + ) + +export const medicationDispenseSubstitutionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + wasSubstituted: booleanSchema, + _wasSubstituted: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + reason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + responsibleParty: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const medicationDispenseSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicationDispense'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'cancelled', + 'completed', + 'declined', + 'entered-in-error', + 'in-progress', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReasonCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + statusReasonReference: z.lazy(() => referenceSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + medicationCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + context: z.lazy(() => referenceSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .optional(), + performer: medicationDispensePerformerSchema.array().optional(), + location: z.lazy(() => referenceSchema).optional(), + authorizingPrescription: z + .lazy(() => referenceSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + daysSupply: z.lazy(() => quantitySchema).optional(), + whenPrepared: dateTimeSchema.optional(), + _whenPrepared: z.lazy(() => elementSchema).optional(), + whenHandedOver: dateTimeSchema.optional(), + _whenHandedOver: z.lazy(() => elementSchema).optional(), + destination: z.lazy(() => referenceSchema).optional(), + receiver: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + dosageInstruction: z + .lazy(() => dosageSchema) + .array() + .optional(), + substitution: medicationDispenseSubstitutionSchema.optional(), + detectedIssue: z + .lazy(() => referenceSchema) + .array() + .optional(), + eventHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['statusReasonCodeableConcept', 'statusReasonReference'], + required: false, + }, + { + names: ['medicationCodeableConcept', 'medicationReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationKnowledgeRelatedMedicationKnowledgeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + reference: z + .lazy(() => referenceSchema) + .array() + .min(1), + }), + ) + +export const medicationKnowledgeMonographSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const medicationKnowledgeIngredientSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + isActive: booleanSchema.optional(), + _isActive: z.lazy(() => elementSchema).optional(), + strength: z.lazy(() => ratioSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['itemCodeableConcept', 'itemReference'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationKnowledgeCostSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + source: stringSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + cost: z.lazy(() => moneySchema), + }), + ) + +export const medicationKnowledgeMonitoringProgramSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + }), + ) + +export const medicationKnowledgeAdministrationGuidelinesSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + dosage: medicationKnowledgeAdministrationGuidelinesDosageSchema + .array() + .optional(), + indicationCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + indicationReference: z.lazy(() => referenceSchema).optional(), + patientCharacteristics: + medicationKnowledgeAdministrationGuidelinesPatientCharacteristicsSchema + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['indicationCodeableConcept', 'indicationReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationKnowledgeMedicineClassificationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + classification: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), + ) + +export const medicationKnowledgePackagingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + }), + ) + +export const medicationKnowledgeDrugCharacteristicSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueString', + 'valueQuantity', + 'valueBase64Binary', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationKnowledgeRegulatorySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + regulatoryAuthority: z.lazy(() => referenceSchema), + substitution: medicationKnowledgeRegulatorySubstitutionSchema + .array() + .optional(), + schedule: medicationKnowledgeRegulatoryScheduleSchema.array().optional(), + maxDispense: medicationKnowledgeRegulatoryMaxDispenseSchema.optional(), + }), + ) + +export const medicationKnowledgeKineticsSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + areaUnderCurve: z + .lazy(() => quantitySchema) + .array() + .optional(), + lethalDose50: z + .lazy(() => quantitySchema) + .array() + .optional(), + halfLifePeriod: z.lazy(() => durationSchema).optional(), + }), + ) + +export const medicationKnowledgeAdministrationGuidelinesDosageSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + dosage: z + .lazy(() => dosageSchema) + .array() + .min(1), + }), + ) + +export const medicationKnowledgeAdministrationGuidelinesPatientCharacteristicsSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + characteristicCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + characteristicQuantity: z.lazy(() => quantitySchema).optional(), + value: stringSchema.array().optional(), + _value: z + .lazy(() => elementSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'characteristicCodeableConcept', + 'characteristicQuantity', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationKnowledgeRegulatorySubstitutionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + allowed: booleanSchema, + _allowed: z.lazy(() => elementSchema).optional(), + }), + ) + +export const medicationKnowledgeRegulatoryScheduleSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + schedule: z.lazy(() => codeableConceptSchema), + }), + ) + +export const medicationKnowledgeRegulatoryMaxDispenseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema), + period: z.lazy(() => durationSchema).optional(), + }), + ) + +export const medicationKnowledgeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('MedicationKnowledge'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), + _status: z.lazy(() => elementSchema).optional(), + manufacturer: z.lazy(() => referenceSchema).optional(), + doseForm: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => quantitySchema).optional(), + synonym: stringSchema.array().optional(), + _synonym: z + .lazy(() => elementSchema) + .array() + .optional(), + relatedMedicationKnowledge: + medicationKnowledgeRelatedMedicationKnowledgeSchema.array().optional(), + associatedMedication: z + .lazy(() => referenceSchema) + .array() + .optional(), + productType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + monograph: medicationKnowledgeMonographSchema.array().optional(), + ingredient: medicationKnowledgeIngredientSchema.array().optional(), + preparationInstruction: markdownSchema.optional(), + _preparationInstruction: z.lazy(() => elementSchema).optional(), + intendedRoute: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + cost: medicationKnowledgeCostSchema.array().optional(), + monitoringProgram: medicationKnowledgeMonitoringProgramSchema + .array() + .optional(), + administrationGuidelines: + medicationKnowledgeAdministrationGuidelinesSchema.array().optional(), + medicineClassification: medicationKnowledgeMedicineClassificationSchema + .array() + .optional(), + packaging: medicationKnowledgePackagingSchema.optional(), + drugCharacteristic: medicationKnowledgeDrugCharacteristicSchema + .array() + .optional(), + contraindication: z + .lazy(() => referenceSchema) + .array() + .optional(), + regulatory: medicationKnowledgeRegulatorySchema.array().optional(), + kinetics: medicationKnowledgeKineticsSchema.array().optional(), + }), + ) + +export const medicationRequestDispenseRequestSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + initialFill: medicationRequestDispenseRequestInitialFillSchema.optional(), + dispenseInterval: z.lazy(() => durationSchema).optional(), + validityPeriod: z.lazy(() => periodSchema).optional(), + numberOfRepeatsAllowed: unsignedIntSchema.optional(), + _numberOfRepeatsAllowed: z.lazy(() => elementSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + expectedSupplyDuration: z.lazy(() => durationSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const medicationRequestSubstitutionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + allowedBoolean: booleanSchema.optional(), + _allowedBoolean: z.lazy(() => elementSchema).optional(), + allowedCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + reason: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['allowedBoolean', 'allowedCodeableConcept'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationRequestDispenseRequestInitialFillSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + duration: z.lazy(() => durationSchema).optional(), + }), + ) + +export const medicationRequestSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('MedicationRequest'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'cancelled', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + intent: z.enum([ + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]), + _intent: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + reportedBoolean: booleanSchema.optional(), + _reportedBoolean: z.lazy(() => elementSchema).optional(), + reportedReference: z.lazy(() => referenceSchema).optional(), + medicationCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + performerType: z.lazy(() => codeableConceptSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + courseOfTherapyType: z.lazy(() => codeableConceptSchema).optional(), + insurance: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + dosageInstruction: z + .lazy(() => dosageSchema) + .array() + .optional(), + dispenseRequest: medicationRequestDispenseRequestSchema.optional(), + substitution: medicationRequestSubstitutionSchema.optional(), + priorPrescription: z.lazy(() => referenceSchema).optional(), + detectedIssue: z + .lazy(() => referenceSchema) + .array() + .optional(), + eventHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['reportedBoolean', 'reportedReference'], + required: false, + }, + { + names: ['medicationCodeableConcept', 'medicationReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const medicationStatementSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicationStatement'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'completed', + 'entered-in-error', + 'intended', + 'not-taken', + 'on-hold', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + medicationCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema), + context: z.lazy(() => referenceSchema).optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + dateAsserted: dateTimeSchema.optional(), + _dateAsserted: z.lazy(() => elementSchema).optional(), + informationSource: z.lazy(() => referenceSchema).optional(), + derivedFrom: z + .lazy(() => referenceSchema) + .array() + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + dosage: z + .lazy(() => dosageSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['medicationCodeableConcept', 'medicationReference'], + required: true, + }, + { + names: ['effectiveDateTime', 'effectivePeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicinalProductNameSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + productName: stringSchema, + _productName: z.lazy(() => elementSchema).optional(), + namePart: medicinalProductNameNamePartSchema.array().optional(), + countryLanguage: medicinalProductNameCountryLanguageSchema + .array() + .optional(), + }), + ) + +export const medicinalProductManufacturingBusinessOperationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + operationType: z.lazy(() => codeableConceptSchema).optional(), + authorisationReferenceNumber: z.lazy(() => identifierSchema).optional(), + effectiveDate: dateTimeSchema.optional(), + _effectiveDate: z.lazy(() => elementSchema).optional(), + confidentialityIndicator: z.lazy(() => codeableConceptSchema).optional(), + manufacturer: z + .lazy(() => referenceSchema) + .array() + .optional(), + regulator: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const medicinalProductSpecialDesignationSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + intendedUse: z.lazy(() => codeableConceptSchema).optional(), + indicationCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + indicationReference: z.lazy(() => referenceSchema).optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + species: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['indicationCodeableConcept', 'indicationReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicinalProductNameNamePartSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + part: stringSchema, + _part: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codingSchema), + }), + ) + +export const medicinalProductNameCountryLanguageSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + country: z.lazy(() => codeableConceptSchema), + jurisdiction: z.lazy(() => codeableConceptSchema).optional(), + language: z.lazy(() => codeableConceptSchema), + }), + ) + +export const medicinalProductSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('MedicinalProduct'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + domain: z.lazy(() => codingSchema).optional(), + combinedPharmaceuticalDoseForm: z + .lazy(() => codeableConceptSchema) + .optional(), + legalStatusOfSupply: z.lazy(() => codeableConceptSchema).optional(), + additionalMonitoringIndicator: z + .lazy(() => codeableConceptSchema) + .optional(), + specialMeasures: stringSchema.array().optional(), + _specialMeasures: z + .lazy(() => elementSchema) + .array() + .optional(), + paediatricUseIndicator: z.lazy(() => codeableConceptSchema).optional(), + productClassification: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + marketingStatus: z + .lazy(() => marketingStatusSchema) + .array() + .optional(), + pharmaceuticalProduct: z + .lazy(() => referenceSchema) + .array() + .optional(), + packagedMedicinalProduct: z + .lazy(() => referenceSchema) + .array() + .optional(), + attachedDocument: z + .lazy(() => referenceSchema) + .array() + .optional(), + masterFile: z + .lazy(() => referenceSchema) + .array() + .optional(), + contact: z + .lazy(() => referenceSchema) + .array() + .optional(), + clinicalTrial: z + .lazy(() => referenceSchema) + .array() + .optional(), + name: medicinalProductNameSchema.array().min(1), + crossReference: z + .lazy(() => identifierSchema) + .array() + .optional(), + manufacturingBusinessOperation: + medicinalProductManufacturingBusinessOperationSchema.array().optional(), + specialDesignation: medicinalProductSpecialDesignationSchema + .array() + .optional(), + }), +) + +export const medicinalProductAuthorizationJurisdictionalAuthorizationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + country: z.lazy(() => codeableConceptSchema).optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + legalStatusOfSupply: z.lazy(() => codeableConceptSchema).optional(), + validityPeriod: z.lazy(() => periodSchema).optional(), + }), + ) + +export const medicinalProductAuthorizationProcedureSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + datePeriod: z.lazy(() => periodSchema).optional(), + dateDateTime: dateTimeSchema.optional(), + _dateDateTime: z.lazy(() => elementSchema).optional(), + application: z + .lazy(() => medicinalProductAuthorizationProcedureSchema) + .array() + .optional(), + }) + .refine( + (value) => + [{ names: ['datePeriod', 'dateDateTime'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicinalProductAuthorizationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('MedicinalProductAuthorization'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + subject: z.lazy(() => referenceSchema).optional(), + country: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + statusDate: dateTimeSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), + restoreDate: dateTimeSchema.optional(), + _restoreDate: z.lazy(() => elementSchema).optional(), + validityPeriod: z.lazy(() => periodSchema).optional(), + dataExclusivityPeriod: z.lazy(() => periodSchema).optional(), + dateOfFirstAuthorization: dateTimeSchema.optional(), + _dateOfFirstAuthorization: z.lazy(() => elementSchema).optional(), + internationalBirthDate: dateTimeSchema.optional(), + _internationalBirthDate: z.lazy(() => elementSchema).optional(), + legalBasis: z.lazy(() => codeableConceptSchema).optional(), + jurisdictionalAuthorization: + medicinalProductAuthorizationJurisdictionalAuthorizationSchema + .array() + .optional(), + holder: z.lazy(() => referenceSchema).optional(), + regulator: z.lazy(() => referenceSchema).optional(), + procedure: medicinalProductAuthorizationProcedureSchema.optional(), + }), + ) + +export const medicinalProductContraindicationOtherTherapySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + therapyRelationshipType: z.lazy(() => codeableConceptSchema), + medicationCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['medicationCodeableConcept', 'medicationReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicinalProductContraindicationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('MedicinalProductContraindication'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + subject: z + .lazy(() => referenceSchema) + .array() + .optional(), + disease: z.lazy(() => codeableConceptSchema).optional(), + diseaseStatus: z.lazy(() => codeableConceptSchema).optional(), + comorbidity: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + therapeuticIndication: z + .lazy(() => referenceSchema) + .array() + .optional(), + otherTherapy: medicinalProductContraindicationOtherTherapySchema + .array() + .optional(), + population: z + .lazy(() => populationSchema) + .array() + .optional(), + }), + ) + +export const medicinalProductIndicationOtherTherapySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + therapyRelationshipType: z.lazy(() => codeableConceptSchema), + medicationCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['medicationCodeableConcept', 'medicationReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicinalProductIndicationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('MedicinalProductIndication'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + subject: z + .lazy(() => referenceSchema) + .array() + .optional(), + diseaseSymptomProcedure: z.lazy(() => codeableConceptSchema).optional(), + diseaseStatus: z.lazy(() => codeableConceptSchema).optional(), + comorbidity: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + intendedEffect: z.lazy(() => codeableConceptSchema).optional(), + duration: z.lazy(() => quantitySchema).optional(), + otherTherapy: medicinalProductIndicationOtherTherapySchema + .array() + .optional(), + undesirableEffect: z + .lazy(() => referenceSchema) + .array() + .optional(), + population: z + .lazy(() => populationSchema) + .array() + .optional(), + }), + ) + +export const medicinalProductIngredientSpecifiedSubstanceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + group: z.lazy(() => codeableConceptSchema), + confidentiality: z.lazy(() => codeableConceptSchema).optional(), + strength: medicinalProductIngredientSpecifiedSubstanceStrengthSchema + .array() + .optional(), + }), + ) + +export const medicinalProductIngredientSubstanceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + strength: z + .lazy(() => medicinalProductIngredientSpecifiedSubstanceStrengthSchema) + .array() + .optional(), + }), + ) + +export const medicinalProductIngredientSpecifiedSubstanceStrengthSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + presentation: z.lazy(() => ratioSchema), + presentationLowLimit: z.lazy(() => ratioSchema).optional(), + concentration: z.lazy(() => ratioSchema).optional(), + concentrationLowLimit: z.lazy(() => ratioSchema).optional(), + measurementPoint: stringSchema.optional(), + _measurementPoint: z.lazy(() => elementSchema).optional(), + country: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + referenceStrength: + medicinalProductIngredientSpecifiedSubstanceStrengthReferenceStrengthSchema + .array() + .optional(), + }), + ) + +export const medicinalProductIngredientSpecifiedSubstanceStrengthReferenceStrengthSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + substance: z.lazy(() => codeableConceptSchema).optional(), + strength: z.lazy(() => ratioSchema), + strengthLowLimit: z.lazy(() => ratioSchema).optional(), + measurementPoint: stringSchema.optional(), + _measurementPoint: z.lazy(() => elementSchema).optional(), + country: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), + ) + +export const medicinalProductIngredientSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('MedicinalProductIngredient'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + role: z.lazy(() => codeableConceptSchema), + allergenicIndicator: booleanSchema.optional(), + _allergenicIndicator: z.lazy(() => elementSchema).optional(), + manufacturer: z + .lazy(() => referenceSchema) + .array() + .optional(), + specifiedSubstance: medicinalProductIngredientSpecifiedSubstanceSchema + .array() + .optional(), + substance: medicinalProductIngredientSubstanceSchema.optional(), + }), + ) + +export const medicinalProductInteractionInteractantSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['itemReference', 'itemCodeableConcept'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicinalProductInteractionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('MedicinalProductInteraction'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + subject: z + .lazy(() => referenceSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + interactant: medicinalProductInteractionInteractantSchema + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + effect: z.lazy(() => codeableConceptSchema).optional(), + incidence: z.lazy(() => codeableConceptSchema).optional(), + management: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const medicinalProductManufacturedSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('MedicinalProductManufactured'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + manufacturedDoseForm: z.lazy(() => codeableConceptSchema), + unitOfPresentation: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema), + manufacturer: z + .lazy(() => referenceSchema) + .array() + .optional(), + ingredient: z + .lazy(() => referenceSchema) + .array() + .optional(), + physicalCharacteristics: z + .lazy(() => prodCharacteristicSchema) + .optional(), + otherCharacteristics: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), + ) + +export const medicinalProductPackagedBatchIdentifierSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + outerPackaging: z.lazy(() => identifierSchema), + immediatePackaging: z.lazy(() => identifierSchema).optional(), + }), + ) + +export const medicinalProductPackagedPackageItemSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + quantity: z.lazy(() => quantitySchema), + material: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + alternateMaterial: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + device: z + .lazy(() => referenceSchema) + .array() + .optional(), + manufacturedItem: z + .lazy(() => referenceSchema) + .array() + .optional(), + packageItem: z + .lazy(() => medicinalProductPackagedPackageItemSchema) + .array() + .optional(), + physicalCharacteristics: z + .lazy(() => prodCharacteristicSchema) + .optional(), + otherCharacteristics: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + shelfLifeStorage: z + .lazy(() => productShelfLifeSchema) + .array() + .optional(), + manufacturer: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const medicinalProductPackagedSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('MedicinalProductPackaged'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + subject: z + .lazy(() => referenceSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + legalStatusOfSupply: z.lazy(() => codeableConceptSchema).optional(), + marketingStatus: z + .lazy(() => marketingStatusSchema) + .array() + .optional(), + marketingAuthorization: z.lazy(() => referenceSchema).optional(), + manufacturer: z + .lazy(() => referenceSchema) + .array() + .optional(), + batchIdentifier: medicinalProductPackagedBatchIdentifierSchema + .array() + .optional(), + packageItem: medicinalProductPackagedPackageItemSchema.array().min(1), + }), + ) + +export const medicinalProductPharmaceuticalCharacteristicsSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + status: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const medicinalProductPharmaceuticalRouteOfAdministrationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + firstDose: z.lazy(() => quantitySchema).optional(), + maxSingleDose: z.lazy(() => quantitySchema).optional(), + maxDosePerDay: z.lazy(() => quantitySchema).optional(), + maxDosePerTreatmentPeriod: z.lazy(() => ratioSchema).optional(), + maxTreatmentPeriod: z.lazy(() => durationSchema).optional(), + targetSpecies: + medicinalProductPharmaceuticalRouteOfAdministrationTargetSpeciesSchema + .array() + .optional(), + }), + ) + +export const medicinalProductPharmaceuticalRouteOfAdministrationTargetSpeciesSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + withdrawalPeriod: + medicinalProductPharmaceuticalRouteOfAdministrationTargetSpeciesWithdrawalPeriodSchema + .array() + .optional(), + }), + ) + +export const medicinalProductPharmaceuticalRouteOfAdministrationTargetSpeciesWithdrawalPeriodSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + tissue: z.lazy(() => codeableConceptSchema), + value: z.lazy(() => quantitySchema), + supportingInformation: stringSchema.optional(), + _supportingInformation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const medicinalProductPharmaceuticalSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('MedicinalProductPharmaceutical'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + administrableDoseForm: z.lazy(() => codeableConceptSchema), + unitOfPresentation: z.lazy(() => codeableConceptSchema).optional(), + ingredient: z + .lazy(() => referenceSchema) + .array() + .optional(), + device: z + .lazy(() => referenceSchema) + .array() + .optional(), + characteristics: medicinalProductPharmaceuticalCharacteristicsSchema + .array() + .optional(), + routeOfAdministration: + medicinalProductPharmaceuticalRouteOfAdministrationSchema + .array() + .min(1), + }), + ) + +export const medicinalProductUndesirableEffectSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('MedicinalProductUndesirableEffect'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + subject: z + .lazy(() => referenceSchema) + .array() + .optional(), + symptomConditionEffect: z.lazy(() => codeableConceptSchema).optional(), + classification: z.lazy(() => codeableConceptSchema).optional(), + frequencyOfOccurrence: z.lazy(() => codeableConceptSchema).optional(), + population: z + .lazy(() => populationSchema) + .array() + .optional(), + }), + ) + +export const messageDefinitionFocusSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]), + _code: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + min: unsignedIntSchema, + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + }), + ) + +export const messageDefinitionAllowedResponseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + message: canonicalSchema, + _message: z.lazy(() => elementSchema).optional(), + situation: markdownSchema.optional(), + _situation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const messageDefinitionSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('MessageDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + replaces: canonicalSchema.array().optional(), + _replaces: z + .lazy(() => elementSchema) + .array() + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema, + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + base: canonicalSchema.optional(), + _base: z.lazy(() => elementSchema).optional(), + parent: canonicalSchema.array().optional(), + _parent: z + .lazy(() => elementSchema) + .array() + .optional(), + eventCoding: z.lazy(() => codingSchema).optional(), + eventUri: uriSchema.optional(), + _eventUri: z.lazy(() => elementSchema).optional(), + category: z + .enum(['consequence', 'currency', 'notification']) + .optional(), + _category: z.lazy(() => elementSchema).optional(), + focus: messageDefinitionFocusSchema.array().optional(), + responseRequired: z + .enum(['always', 'never', 'on-error', 'on-success']) + .optional(), + _responseRequired: z.lazy(() => elementSchema).optional(), + allowedResponse: messageDefinitionAllowedResponseSchema + .array() + .optional(), + graph: canonicalSchema.array().optional(), + _graph: z + .lazy(() => elementSchema) + .array() + .optional(), + }) + .refine( + (value) => + [{ names: ['eventCoding', 'eventUri'], required: true }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const messageHeaderDestinationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + target: z.lazy(() => referenceSchema).optional(), + endpoint: urlSchema, + _endpoint: z.lazy(() => elementSchema).optional(), + receiver: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const messageHeaderSourceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + software: stringSchema.optional(), + _software: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + contact: z.lazy(() => contactPointSchema).optional(), + endpoint: urlSchema, + _endpoint: z.lazy(() => elementSchema).optional(), + }), + ) + +export const messageHeaderResponseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: idSchema, + _identifier: z.lazy(() => elementSchema).optional(), + code: z.enum(['fatal-error', 'ok', 'transient-error']), + _code: z.lazy(() => elementSchema).optional(), + details: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const messageHeaderSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MessageHeader'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + eventCoding: z.lazy(() => codingSchema).optional(), + eventUri: uriSchema.optional(), + _eventUri: z.lazy(() => elementSchema).optional(), + destination: messageHeaderDestinationSchema.array().optional(), + sender: z.lazy(() => referenceSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + source: messageHeaderSourceSchema, + responsible: z.lazy(() => referenceSchema).optional(), + reason: z.lazy(() => codeableConceptSchema).optional(), + response: messageHeaderResponseSchema.optional(), + focus: z + .lazy(() => referenceSchema) + .array() + .optional(), + definition: canonicalSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [{ names: ['eventCoding', 'eventUri'], required: true }].every( + (group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const metaSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + versionId: idSchema.optional(), + _versionId: z.lazy(() => elementSchema).optional(), + lastUpdated: instantSchema.optional(), + _lastUpdated: z.lazy(() => elementSchema).optional(), + source: uriSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.array().optional(), + _profile: z + .lazy(() => elementSchema) + .array() + .optional(), + security: z + .lazy(() => codingSchema) + .array() + .optional(), + tag: z + .lazy(() => codingSchema) + .array() + .optional(), + }), +) + +export const molecularSequenceReferenceSeqSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + chromosome: z.lazy(() => codeableConceptSchema).optional(), + genomeBuild: stringSchema.optional(), + _genomeBuild: z.lazy(() => elementSchema).optional(), + orientation: z.enum(['antisense', 'sense']).optional(), + _orientation: z.lazy(() => elementSchema).optional(), + referenceSeqId: z.lazy(() => codeableConceptSchema).optional(), + referenceSeqPointer: z.lazy(() => referenceSchema).optional(), + referenceSeqString: stringSchema.optional(), + _referenceSeqString: z.lazy(() => elementSchema).optional(), + strand: z.enum(['crick', 'watson']).optional(), + _strand: z.lazy(() => elementSchema).optional(), + windowStart: integerSchema.optional(), + _windowStart: z.lazy(() => elementSchema).optional(), + windowEnd: integerSchema.optional(), + _windowEnd: z.lazy(() => elementSchema).optional(), + }), + ) + +export const molecularSequenceVariantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + observedAllele: stringSchema.optional(), + _observedAllele: z.lazy(() => elementSchema).optional(), + referenceAllele: stringSchema.optional(), + _referenceAllele: z.lazy(() => elementSchema).optional(), + cigar: stringSchema.optional(), + _cigar: z.lazy(() => elementSchema).optional(), + variantPointer: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const molecularSequenceQualitySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['indel', 'snp', 'unknown']), + _type: z.lazy(() => elementSchema).optional(), + standardSequence: z.lazy(() => codeableConceptSchema).optional(), + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + score: z.lazy(() => quantitySchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + truthTP: decimalSchema.optional(), + _truthTP: z.lazy(() => elementSchema).optional(), + queryTP: decimalSchema.optional(), + _queryTP: z.lazy(() => elementSchema).optional(), + truthFN: decimalSchema.optional(), + _truthFN: z.lazy(() => elementSchema).optional(), + queryFP: decimalSchema.optional(), + _queryFP: z.lazy(() => elementSchema).optional(), + gtFP: decimalSchema.optional(), + _gtFP: z.lazy(() => elementSchema).optional(), + precision: decimalSchema.optional(), + _precision: z.lazy(() => elementSchema).optional(), + recall: decimalSchema.optional(), + _recall: z.lazy(() => elementSchema).optional(), + fScore: decimalSchema.optional(), + _fScore: z.lazy(() => elementSchema).optional(), + roc: molecularSequenceQualityRocSchema.optional(), + }), + ) + +export const molecularSequenceRepositorySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['directlink', 'login', 'oauth', 'openapi', 'other']), + _type: z.lazy(() => elementSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + datasetId: stringSchema.optional(), + _datasetId: z.lazy(() => elementSchema).optional(), + variantsetId: stringSchema.optional(), + _variantsetId: z.lazy(() => elementSchema).optional(), + readsetId: stringSchema.optional(), + _readsetId: z.lazy(() => elementSchema).optional(), + }), + ) + +export const molecularSequenceStructureVariantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + variantType: z.lazy(() => codeableConceptSchema).optional(), + exact: booleanSchema.optional(), + _exact: z.lazy(() => elementSchema).optional(), + length: integerSchema.optional(), + _length: z.lazy(() => elementSchema).optional(), + outer: molecularSequenceStructureVariantOuterSchema.optional(), + inner: molecularSequenceStructureVariantInnerSchema.optional(), + }), + ) + +export const molecularSequenceQualityRocSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + score: integerSchema.array().optional(), + _score: z + .lazy(() => elementSchema) + .array() + .optional(), + numTP: integerSchema.array().optional(), + _numTP: z + .lazy(() => elementSchema) + .array() + .optional(), + numFP: integerSchema.array().optional(), + _numFP: z + .lazy(() => elementSchema) + .array() + .optional(), + numFN: integerSchema.array().optional(), + _numFN: z + .lazy(() => elementSchema) + .array() + .optional(), + precision: decimalSchema.array().optional(), + _precision: z + .lazy(() => elementSchema) + .array() + .optional(), + sensitivity: decimalSchema.array().optional(), + _sensitivity: z + .lazy(() => elementSchema) + .array() + .optional(), + fMeasure: decimalSchema.array().optional(), + _fMeasure: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const molecularSequenceStructureVariantOuterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + }), + ) + +export const molecularSequenceStructureVariantInnerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + }), + ) + +export const molecularSequenceSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('MolecularSequence'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + type: z.enum(['aa', 'dna', 'rna']).optional(), + _type: z.lazy(() => elementSchema).optional(), + coordinateSystem: integerSchema, + _coordinateSystem: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema).optional(), + specimen: z.lazy(() => referenceSchema).optional(), + device: z.lazy(() => referenceSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + referenceSeq: molecularSequenceReferenceSeqSchema.optional(), + variant: molecularSequenceVariantSchema.array().optional(), + observedSeq: stringSchema.optional(), + _observedSeq: z.lazy(() => elementSchema).optional(), + quality: molecularSequenceQualitySchema.array().optional(), + readCoverage: integerSchema.optional(), + _readCoverage: z.lazy(() => elementSchema).optional(), + repository: molecularSequenceRepositorySchema.array().optional(), + pointer: z + .lazy(() => referenceSchema) + .array() + .optional(), + structureVariant: molecularSequenceStructureVariantSchema + .array() + .optional(), + }), +) + +export const moneySchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + currency: codeSchema.optional(), + _currency: z.lazy(() => elementSchema).optional(), + }), +) + +export const namingSystemUniqueIdSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['oid', 'other', 'uri', 'uuid']), + _type: z.lazy(() => elementSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + preferred: booleanSchema.optional(), + _preferred: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), + ) + +export const namingSystemSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('NamingSystem'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + kind: z.enum(['codesystem', 'identifier', 'root']), + _kind: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema, + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + responsible: stringSchema.optional(), + _responsible: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + uniqueId: namingSystemUniqueIdSchema.array().min(1), + }), +) + +export const narrativeSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + status: z.enum(['additional', 'empty', 'extensions', 'generated']), + _status: z.lazy(() => elementSchema).optional(), + div: xhtmlSchema, + _div: z.lazy(() => elementSchema).optional(), + }), +) + +export const nutritionOrderOralDietSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + schedule: z + .lazy(() => timingSchema) + .array() + .optional(), + nutrient: nutritionOrderOralDietNutrientSchema.array().optional(), + texture: nutritionOrderOralDietTextureSchema.array().optional(), + fluidConsistencyType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + instruction: stringSchema.optional(), + _instruction: z.lazy(() => elementSchema).optional(), + }), + ) + +export const nutritionOrderSupplementSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + productName: stringSchema.optional(), + _productName: z.lazy(() => elementSchema).optional(), + schedule: z + .lazy(() => timingSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + instruction: stringSchema.optional(), + _instruction: z.lazy(() => elementSchema).optional(), + }), + ) + +export const nutritionOrderEnteralFormulaSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + baseFormulaType: z.lazy(() => codeableConceptSchema).optional(), + baseFormulaProductName: stringSchema.optional(), + _baseFormulaProductName: z.lazy(() => elementSchema).optional(), + additiveType: z.lazy(() => codeableConceptSchema).optional(), + additiveProductName: stringSchema.optional(), + _additiveProductName: z.lazy(() => elementSchema).optional(), + caloricDensity: z.lazy(() => quantitySchema).optional(), + routeofAdministration: z.lazy(() => codeableConceptSchema).optional(), + administration: nutritionOrderEnteralFormulaAdministrationSchema + .array() + .optional(), + maxVolumeToDeliver: z.lazy(() => quantitySchema).optional(), + administrationInstruction: stringSchema.optional(), + _administrationInstruction: z.lazy(() => elementSchema).optional(), + }), + ) + +export const nutritionOrderOralDietNutrientSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifier: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => quantitySchema).optional(), + }), + ) + +export const nutritionOrderOralDietTextureSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifier: z.lazy(() => codeableConceptSchema).optional(), + foodType: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const nutritionOrderEnteralFormulaAdministrationSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + schedule: z.lazy(() => timingSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + rateQuantity: z.lazy(() => quantitySchema).optional(), + rateRatio: z.lazy(() => ratioSchema).optional(), + }) + .refine( + (value) => + [{ names: ['rateQuantity', 'rateRatio'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const nutritionOrderSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('NutritionOrder'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiates: uriSchema.array().optional(), + _instantiates: z + .lazy(() => elementSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + intent: z.enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]), + _intent: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + dateTime: dateTimeSchema, + _dateTime: z.lazy(() => elementSchema).optional(), + orderer: z.lazy(() => referenceSchema).optional(), + allergyIntolerance: z + .lazy(() => referenceSchema) + .array() + .optional(), + foodPreferenceModifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + excludeFoodModifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + oralDiet: nutritionOrderOralDietSchema.optional(), + supplement: nutritionOrderSupplementSchema.array().optional(), + enteralFormula: nutritionOrderEnteralFormulaSchema.optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), +) + +export const observationReferenceRangeSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + low: z.lazy(() => quantitySchema).optional(), + high: z.lazy(() => quantitySchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + appliesTo: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + age: z.lazy(() => rangeSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => { + const range = value as { + low?: unknown + high?: unknown + text?: unknown + } + return ( + range.low !== undefined || + range.high !== undefined || + range.text !== undefined + ) + }, + { message: 'obs-3: a reference range states a low, a high, or text.' }, + ), + ) + +export const observationComponentSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), + interpretation: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + referenceRange: z + .lazy(() => observationReferenceRangeSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueQuantity', + 'valueCodeableConcept', + 'valueString', + 'valueBoolean', + 'valueInteger', + 'valueRange', + 'valueRatio', + 'valueSampledData', + 'valueTime', + 'valueDateTime', + 'valuePeriod', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => { + const observation = value as Record + const hasValue = Object.keys(observation).some( + (key) => key.startsWith('value') && observation[key] !== undefined, + ) + return observation.dataAbsentReason === undefined || !hasValue + }, + { + message: 'obs-6: dataAbsentReason is present only when no value is.', + }, + ), + ) + +export const observationSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Observation'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'amended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'preliminary', + 'registered', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema).optional(), + focus: z + .lazy(() => referenceSchema) + .array() + .optional(), + encounter: z.lazy(() => referenceSchema).optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + effectiveTiming: z.lazy(() => timingSchema).optional(), + effectiveInstant: instantSchema.optional(), + _effectiveInstant: z.lazy(() => elementSchema).optional(), + issued: instantSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + performer: z + .lazy(() => referenceSchema) + .array() + .optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), + interpretation: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + specimen: z.lazy(() => referenceSchema).optional(), + device: z.lazy(() => referenceSchema).optional(), + referenceRange: observationReferenceRangeSchema.array().optional(), + hasMember: z + .lazy(() => referenceSchema) + .array() + .optional(), + derivedFrom: z + .lazy(() => referenceSchema) + .array() + .optional(), + component: observationComponentSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'effectiveDateTime', + 'effectivePeriod', + 'effectiveTiming', + 'effectiveInstant', + ], + required: false, + }, + { + names: [ + 'valueQuantity', + 'valueCodeableConcept', + 'valueString', + 'valueBoolean', + 'valueInteger', + 'valueRange', + 'valueRatio', + 'valueSampledData', + 'valueTime', + 'valueDateTime', + 'valuePeriod', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => { + const observation = value as Record + const hasValue = Object.keys(observation).some( + (key) => key.startsWith('value') && observation[key] !== undefined, + ) + return observation.dataAbsentReason === undefined || !hasValue + }, + { message: 'obs-6: dataAbsentReason is present only when no value is.' }, + ), +) + +export const observationDefinitionQuantitativeDetailsSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + customaryUnit: z.lazy(() => codeableConceptSchema).optional(), + unit: z.lazy(() => codeableConceptSchema).optional(), + conversionFactor: decimalSchema.optional(), + _conversionFactor: z.lazy(() => elementSchema).optional(), + decimalPrecision: integerSchema.optional(), + _decimalPrecision: z.lazy(() => elementSchema).optional(), + }), + ) + +export const observationDefinitionQualifiedIntervalSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.enum(['absolute', 'critical', 'reference']).optional(), + _category: z.lazy(() => elementSchema).optional(), + range: z.lazy(() => rangeSchema).optional(), + context: z.lazy(() => codeableConceptSchema).optional(), + appliesTo: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + age: z.lazy(() => rangeSchema).optional(), + gestationalAge: z.lazy(() => rangeSchema).optional(), + condition: stringSchema.optional(), + _condition: z.lazy(() => elementSchema).optional(), + }), + ) + +export const observationDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('ObservationDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + permittedDataType: z + .enum([ + 'CodeableConcept', + 'Period', + 'Quantity', + 'Range', + 'Ratio', + 'SampledData', + 'boolean', + 'dateTime', + 'integer', + 'string', + 'time', + ]) + .array() + .optional(), + _permittedDataType: z + .lazy(() => elementSchema) + .array() + .optional(), + multipleResultsAllowed: booleanSchema.optional(), + _multipleResultsAllowed: z.lazy(() => elementSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + preferredReportName: stringSchema.optional(), + _preferredReportName: z.lazy(() => elementSchema).optional(), + quantitativeDetails: + observationDefinitionQuantitativeDetailsSchema.optional(), + qualifiedInterval: observationDefinitionQualifiedIntervalSchema + .array() + .optional(), + validCodedValueSet: z.lazy(() => referenceSchema).optional(), + normalCodedValueSet: z.lazy(() => referenceSchema).optional(), + abnormalCodedValueSet: z.lazy(() => referenceSchema).optional(), + criticalCodedValueSet: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const operationDefinitionParameterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: codeSchema, + _name: z.lazy(() => elementSchema).optional(), + use: z.enum(['in', 'out']), + _use: z.lazy(() => elementSchema).optional(), + min: integerSchema, + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema, + _max: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Any', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'CodeableConcept', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'EffectEvidenceSynthesis', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'Reference', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceAmount', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'Type', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + targetProfile: canonicalSchema.array().optional(), + _targetProfile: z + .lazy(() => elementSchema) + .array() + .optional(), + searchType: z + .enum([ + 'composite', + 'date', + 'number', + 'quantity', + 'reference', + 'special', + 'string', + 'token', + 'uri', + ]) + .optional(), + _searchType: z.lazy(() => elementSchema).optional(), + binding: operationDefinitionParameterBindingSchema.optional(), + referencedFrom: operationDefinitionParameterReferencedFromSchema + .array() + .optional(), + part: z + .lazy(() => operationDefinitionParameterSchema) + .array() + .optional(), + }), + ) + +export const operationDefinitionOverloadSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + parameterName: stringSchema.array().optional(), + _parameterName: z + .lazy(() => elementSchema) + .array() + .optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), + ) + +export const operationDefinitionParameterBindingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + strength: z.enum(['example', 'extensible', 'preferred', 'required']), + _strength: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema, + _valueSet: z.lazy(() => elementSchema).optional(), + }), + ) + +export const operationDefinitionParameterReferencedFromSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + source: stringSchema, + _source: z.lazy(() => elementSchema).optional(), + sourceId: stringSchema.optional(), + _sourceId: z.lazy(() => elementSchema).optional(), + }), + ) + +export const operationDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('OperationDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + kind: z.enum(['operation', 'query']), + _kind: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + affectsState: booleanSchema.optional(), + _affectsState: z.lazy(() => elementSchema).optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + comment: markdownSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + base: canonicalSchema.optional(), + _base: z.lazy(() => elementSchema).optional(), + resource: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .array() + .optional(), + _resource: z + .lazy(() => elementSchema) + .array() + .optional(), + system: booleanSchema, + _system: z.lazy(() => elementSchema).optional(), + type: booleanSchema, + _type: z.lazy(() => elementSchema).optional(), + instance: booleanSchema, + _instance: z.lazy(() => elementSchema).optional(), + inputProfile: canonicalSchema.optional(), + _inputProfile: z.lazy(() => elementSchema).optional(), + outputProfile: canonicalSchema.optional(), + _outputProfile: z.lazy(() => elementSchema).optional(), + parameter: operationDefinitionParameterSchema.array().optional(), + overload: operationDefinitionOverloadSchema.array().optional(), + }), + ) + +export const operationOutcomeIssueSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + severity: z.enum(['error', 'fatal', 'information', 'warning']), + _severity: z.lazy(() => elementSchema).optional(), + code: z.enum([ + 'business-rule', + 'code-invalid', + 'conflict', + 'deleted', + 'duplicate', + 'exception', + 'expired', + 'extension', + 'forbidden', + 'incomplete', + 'informational', + 'invalid', + 'invariant', + 'lock-error', + 'login', + 'multiple-matches', + 'no-store', + 'not-found', + 'not-supported', + 'processing', + 'required', + 'security', + 'structure', + 'suppressed', + 'throttled', + 'timeout', + 'too-costly', + 'too-long', + 'transient', + 'unknown', + 'value', + ]), + _code: z.lazy(() => elementSchema).optional(), + details: z.lazy(() => codeableConceptSchema).optional(), + diagnostics: stringSchema.optional(), + _diagnostics: z.lazy(() => elementSchema).optional(), + location: stringSchema.array().optional(), + _location: z + .lazy(() => elementSchema) + .array() + .optional(), + expression: stringSchema.array().optional(), + _expression: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const operationOutcomeSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('OperationOutcome'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + issue: operationOutcomeIssueSchema.array().min(1), + }), +) + +export const organizationContactSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + purpose: z.lazy(() => codeableConceptSchema).optional(), + name: z.lazy(() => humanNameSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + address: z.lazy(() => addressSchema).optional(), + }), + ) + +export const organizationSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Organization'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + alias: stringSchema.array().optional(), + _alias: z + .lazy(() => elementSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + address: z + .lazy(() => addressSchema) + .array() + .optional(), + partOf: z.lazy(() => referenceSchema).optional(), + contact: organizationContactSchema.array().optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const organizationAffiliationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('OrganizationAffiliation'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + participatingOrganization: z.lazy(() => referenceSchema).optional(), + network: z + .lazy(() => referenceSchema) + .array() + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + location: z + .lazy(() => referenceSchema) + .array() + .optional(), + healthcareService: z + .lazy(() => referenceSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const parameterDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: codeSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + use: z.enum(['in', 'out']), + _use: z.lazy(() => elementSchema).optional(), + min: integerSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + type: z.enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Any', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'CodeableConcept', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'EffectEvidenceSynthesis', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'Reference', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceAmount', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'Type', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]), + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + }), + ) + +export const parametersParameterSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + valueMeta: z.lazy(() => metaSchema).optional(), + resource: z.lazy(() => resourceSchema).optional(), + part: z + .lazy(() => parametersParameterSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBase64Binary', + 'valueBoolean', + 'valueCanonical', + 'valueCode', + 'valueDate', + 'valueDateTime', + 'valueDecimal', + 'valueId', + 'valueInstant', + 'valueInteger', + 'valueMarkdown', + 'valueOid', + 'valuePositiveInt', + 'valueString', + 'valueTime', + 'valueUnsignedInt', + 'valueUri', + 'valueUrl', + 'valueUuid', + 'valueAddress', + 'valueAge', + 'valueAnnotation', + 'valueAttachment', + 'valueCodeableConcept', + 'valueCoding', + 'valueContactPoint', + 'valueCount', + 'valueDistance', + 'valueDuration', + 'valueHumanName', + 'valueIdentifier', + 'valueMoney', + 'valuePeriod', + 'valueQuantity', + 'valueRange', + 'valueRatio', + 'valueReference', + 'valueSampledData', + 'valueSignature', + 'valueTiming', + 'valueContactDetail', + 'valueContributor', + 'valueDataRequirement', + 'valueExpression', + 'valueParameterDefinition', + 'valueRelatedArtifact', + 'valueTriggerDefinition', + 'valueUsageContext', + 'valueDosage', + 'valueMeta', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const parametersSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Parameters'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + parameter: parametersParameterSchema.array().optional(), + }), +) + +export const patientContactSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + relationship: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + name: z.lazy(() => humanNameSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + address: z.lazy(() => addressSchema).optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), +) + +export const patientCommunicationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + language: z.lazy(() => codeableConceptSchema), + preferred: booleanSchema.optional(), + _preferred: z.lazy(() => elementSchema).optional(), + }), + ) + +export const patientLinkSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + other: z.lazy(() => referenceSchema), + type: z.enum(['refer', 'replaced-by', 'replaces', 'seealso']), + _type: z.lazy(() => elementSchema).optional(), + }), +) + +export const patientSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Patient'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + name: z + .lazy(() => humanNameSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + deceasedBoolean: booleanSchema.optional(), + _deceasedBoolean: z.lazy(() => elementSchema).optional(), + deceasedDateTime: dateTimeSchema.optional(), + _deceasedDateTime: z.lazy(() => elementSchema).optional(), + address: z + .lazy(() => addressSchema) + .array() + .optional(), + maritalStatus: z.lazy(() => codeableConceptSchema).optional(), + multipleBirthBoolean: booleanSchema.optional(), + _multipleBirthBoolean: z.lazy(() => elementSchema).optional(), + multipleBirthInteger: integerSchema.optional(), + _multipleBirthInteger: z.lazy(() => elementSchema).optional(), + photo: z + .lazy(() => attachmentSchema) + .array() + .optional(), + contact: patientContactSchema.array().optional(), + communication: patientCommunicationSchema.array().optional(), + generalPractitioner: z + .lazy(() => referenceSchema) + .array() + .optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + link: patientLinkSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['deceasedBoolean', 'deceasedDateTime'], required: false }, + { + names: ['multipleBirthBoolean', 'multipleBirthInteger'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const paymentNoticeSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('PaymentNotice'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + request: z.lazy(() => referenceSchema).optional(), + response: z.lazy(() => referenceSchema).optional(), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + provider: z.lazy(() => referenceSchema).optional(), + payment: z.lazy(() => referenceSchema), + paymentDate: dateSchema.optional(), + _paymentDate: z.lazy(() => elementSchema).optional(), + payee: z.lazy(() => referenceSchema).optional(), + recipient: z.lazy(() => referenceSchema), + amount: z.lazy(() => moneySchema), + paymentStatus: z.lazy(() => codeableConceptSchema).optional(), + }), +) + +export const paymentReconciliationDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + predecessor: z.lazy(() => identifierSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + request: z.lazy(() => referenceSchema).optional(), + submitter: z.lazy(() => referenceSchema).optional(), + response: z.lazy(() => referenceSchema).optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + responsible: z.lazy(() => referenceSchema).optional(), + payee: z.lazy(() => referenceSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + }), + ) + +export const paymentReconciliationProcessNoteSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['display', 'print', 'printoper']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }), + ) + +export const paymentReconciliationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('PaymentReconciliation'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + paymentIssuer: z.lazy(() => referenceSchema).optional(), + request: z.lazy(() => referenceSchema).optional(), + requestor: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + paymentDate: dateSchema, + _paymentDate: z.lazy(() => elementSchema).optional(), + paymentAmount: z.lazy(() => moneySchema), + paymentIdentifier: z.lazy(() => identifierSchema).optional(), + detail: paymentReconciliationDetailSchema.array().optional(), + formCode: z.lazy(() => codeableConceptSchema).optional(), + processNote: paymentReconciliationProcessNoteSchema.array().optional(), + }), + ) + +export const periodSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + start: dateTimeSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: dateTimeSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => { + const period = value as { start?: string; end?: string } + if (period.start === undefined || period.end === undefined) return true + const start = fhirDateTimeToEpoch(period.start) + const end = fhirDateTimeToEpoch(period.end) + // A malformed endpoint is a malformed date, not an ordering fault; the release says + // as much with start.hasValue(). Reporting both would name the wrong problem. + if (Number.isNaN(start) || Number.isNaN(end)) return true + return start <= end + }, + { message: 'Period.start must not be later than Period.end.' }, + ), +) + +export const personLinkSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + target: z.lazy(() => referenceSchema), + assurance: z.enum(['level1', 'level2', 'level3', 'level4']).optional(), + _assurance: z.lazy(() => elementSchema).optional(), + }), +) + +export const personSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Person'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + name: z + .lazy(() => humanNameSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + address: z + .lazy(() => addressSchema) + .array() + .optional(), + photo: z.lazy(() => attachmentSchema).optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + link: personLinkSchema.array().optional(), + }), +) + +export const planDefinitionGoalSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + description: z.lazy(() => codeableConceptSchema), + priority: z.lazy(() => codeableConceptSchema).optional(), + start: z.lazy(() => codeableConceptSchema).optional(), + addresses: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + documentation: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + target: planDefinitionGoalTargetSchema.array().optional(), + }), + ) + +export const planDefinitionActionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + prefix: stringSchema.optional(), + _prefix: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + textEquivalent: stringSchema.optional(), + _textEquivalent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + documentation: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + goalId: idSchema.array().optional(), + _goalId: z + .lazy(() => elementSchema) + .array() + .optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + trigger: z + .lazy(() => triggerDefinitionSchema) + .array() + .optional(), + condition: planDefinitionActionConditionSchema.array().optional(), + input: z + .lazy(() => dataRequirementSchema) + .array() + .optional(), + output: z + .lazy(() => dataRequirementSchema) + .array() + .optional(), + relatedAction: planDefinitionActionRelatedActionSchema + .array() + .optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + timingAge: z.lazy(() => ageSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingDuration: z.lazy(() => durationSchema).optional(), + timingRange: z.lazy(() => rangeSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + participant: planDefinitionActionParticipantSchema.array().optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + groupingBehavior: z + .enum(['logical-group', 'sentence-group', 'visual-group']) + .optional(), + _groupingBehavior: z.lazy(() => elementSchema).optional(), + selectionBehavior: z + .enum([ + 'all', + 'all-or-none', + 'any', + 'at-most-one', + 'exactly-one', + 'one-or-more', + ]) + .optional(), + _selectionBehavior: z.lazy(() => elementSchema).optional(), + requiredBehavior: z + .enum(['could', 'must', 'must-unless-documented']) + .optional(), + _requiredBehavior: z.lazy(() => elementSchema).optional(), + precheckBehavior: z.enum(['no', 'yes']).optional(), + _precheckBehavior: z.lazy(() => elementSchema).optional(), + cardinalityBehavior: z.enum(['multiple', 'single']).optional(), + _cardinalityBehavior: z.lazy(() => elementSchema).optional(), + definitionCanonical: canonicalSchema.optional(), + _definitionCanonical: z.lazy(() => elementSchema).optional(), + definitionUri: uriSchema.optional(), + _definitionUri: z.lazy(() => elementSchema).optional(), + transform: canonicalSchema.optional(), + _transform: z.lazy(() => elementSchema).optional(), + dynamicValue: planDefinitionActionDynamicValueSchema.array().optional(), + action: z + .lazy(() => planDefinitionActionSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['subjectCodeableConcept', 'subjectReference'], + required: false, + }, + { + names: [ + 'timingDateTime', + 'timingAge', + 'timingPeriod', + 'timingDuration', + 'timingRange', + 'timingTiming', + ], + required: false, + }, + { + names: ['definitionCanonical', 'definitionUri'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const planDefinitionGoalTargetSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + measure: z.lazy(() => codeableConceptSchema).optional(), + detailQuantity: z.lazy(() => quantitySchema).optional(), + detailRange: z.lazy(() => rangeSchema).optional(), + detailCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + due: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['detailQuantity', 'detailRange', 'detailCodeableConcept'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const planDefinitionActionConditionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + kind: z.enum(['applicability', 'start', 'stop']), + _kind: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema).optional(), + }), + ) + +export const planDefinitionActionRelatedActionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + actionId: idSchema, + _actionId: z.lazy(() => elementSchema).optional(), + relationship: z.enum([ + 'after', + 'after-end', + 'after-start', + 'before', + 'before-end', + 'before-start', + 'concurrent', + 'concurrent-with-end', + 'concurrent-with-start', + ]), + _relationship: z.lazy(() => elementSchema).optional(), + offsetDuration: z.lazy(() => durationSchema).optional(), + offsetRange: z.lazy(() => rangeSchema).optional(), + }) + .refine( + (value) => + [{ names: ['offsetDuration', 'offsetRange'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const planDefinitionActionParticipantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['device', 'patient', 'practitioner', 'related-person']), + _type: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const planDefinitionActionDynamicValueSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema).optional(), + }), + ) + +export const planDefinitionSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('PlanDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + library: canonicalSchema.array().optional(), + _library: z + .lazy(() => elementSchema) + .array() + .optional(), + goal: planDefinitionGoalSchema.array().optional(), + action: planDefinitionActionSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: ['subjectCodeableConcept', 'subjectReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const populationSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + ageRange: z.lazy(() => rangeSchema).optional(), + ageCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + gender: z.lazy(() => codeableConceptSchema).optional(), + race: z.lazy(() => codeableConceptSchema).optional(), + physiologicalCondition: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [{ names: ['ageRange', 'ageCodeableConcept'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const practitionerQualificationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + period: z.lazy(() => periodSchema).optional(), + issuer: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const practitionerSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Practitioner'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + name: z + .lazy(() => humanNameSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + address: z + .lazy(() => addressSchema) + .array() + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + photo: z + .lazy(() => attachmentSchema) + .array() + .optional(), + qualification: practitionerQualificationSchema.array().optional(), + communication: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), +) + +export const practitionerRoleAvailableTimeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + daysOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .array() + .optional(), + _daysOfWeek: z + .lazy(() => elementSchema) + .array() + .optional(), + allDay: booleanSchema.optional(), + _allDay: z.lazy(() => elementSchema).optional(), + availableStartTime: timeSchema.optional(), + _availableStartTime: z.lazy(() => elementSchema).optional(), + availableEndTime: timeSchema.optional(), + _availableEndTime: z.lazy(() => elementSchema).optional(), + }), + ) + +export const practitionerRoleNotAvailableSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema, + _description: z.lazy(() => elementSchema).optional(), + during: z.lazy(() => periodSchema).optional(), + }), + ) + +export const practitionerRoleSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('PractitionerRole'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + practitioner: z.lazy(() => referenceSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + location: z + .lazy(() => referenceSchema) + .array() + .optional(), + healthcareService: z + .lazy(() => referenceSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + availableTime: practitionerRoleAvailableTimeSchema.array().optional(), + notAvailable: practitionerRoleNotAvailableSchema.array().optional(), + availabilityExceptions: stringSchema.optional(), + _availabilityExceptions: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const procedurePerformerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const procedureFocalDeviceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + action: z.lazy(() => codeableConceptSchema).optional(), + manipulated: z.lazy(() => referenceSchema), + }), + ) + +export const procedureSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Procedure'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + performedDateTime: dateTimeSchema.optional(), + _performedDateTime: z.lazy(() => elementSchema).optional(), + performedPeriod: z.lazy(() => periodSchema).optional(), + performedString: stringSchema.optional(), + _performedString: z.lazy(() => elementSchema).optional(), + performedAge: z.lazy(() => ageSchema).optional(), + performedRange: z.lazy(() => rangeSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + asserter: z.lazy(() => referenceSchema).optional(), + performer: procedurePerformerSchema.array().optional(), + location: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + bodySite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + outcome: z.lazy(() => codeableConceptSchema).optional(), + report: z + .lazy(() => referenceSchema) + .array() + .optional(), + complication: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + complicationDetail: z + .lazy(() => referenceSchema) + .array() + .optional(), + followUp: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + focalDevice: procedureFocalDeviceSchema.array().optional(), + usedReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + usedCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'performedDateTime', + 'performedPeriod', + 'performedString', + 'performedAge', + 'performedRange', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const prodCharacteristicSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + height: z.lazy(() => quantitySchema).optional(), + width: z.lazy(() => quantitySchema).optional(), + depth: z.lazy(() => quantitySchema).optional(), + weight: z.lazy(() => quantitySchema).optional(), + nominalVolume: z.lazy(() => quantitySchema).optional(), + externalDiameter: z.lazy(() => quantitySchema).optional(), + shape: stringSchema.optional(), + _shape: z.lazy(() => elementSchema).optional(), + color: stringSchema.array().optional(), + _color: z + .lazy(() => elementSchema) + .array() + .optional(), + imprint: stringSchema.array().optional(), + _imprint: z + .lazy(() => elementSchema) + .array() + .optional(), + image: z + .lazy(() => attachmentSchema) + .array() + .optional(), + scoring: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const productShelfLifeSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + period: z.lazy(() => quantitySchema), + specialPrecautionsForStorage: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), +) + +export const provenanceAgentSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + role: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + who: z.lazy(() => referenceSchema), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + }), +) + +export const provenanceEntitySchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + role: z.enum([ + 'derivation', + 'quotation', + 'removal', + 'revision', + 'source', + ]), + _role: z.lazy(() => elementSchema).optional(), + what: z.lazy(() => referenceSchema), + agent: z + .lazy(() => provenanceAgentSchema) + .array() + .optional(), + }), +) + +export const provenanceSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Provenance'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + target: z + .lazy(() => referenceSchema) + .array() + .min(1), + occurredPeriod: z.lazy(() => periodSchema).optional(), + occurredDateTime: dateTimeSchema.optional(), + _occurredDateTime: z.lazy(() => elementSchema).optional(), + recorded: instantSchema, + _recorded: z.lazy(() => elementSchema).optional(), + policy: uriSchema.array().optional(), + _policy: z + .lazy(() => elementSchema) + .array() + .optional(), + location: z.lazy(() => referenceSchema).optional(), + reason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + activity: z.lazy(() => codeableConceptSchema).optional(), + agent: provenanceAgentSchema.array().min(1), + entity: provenanceEntitySchema.array().optional(), + signature: z + .lazy(() => signatureSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { names: ['occurredPeriod', 'occurredDateTime'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const quantitySchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }), +) + +export const questionnaireItemSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + linkId: stringSchema, + _linkId: z.lazy(() => elementSchema).optional(), + definition: uriSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codingSchema) + .array() + .optional(), + prefix: stringSchema.optional(), + _prefix: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + type: z.enum([ + 'attachment', + 'boolean', + 'choice', + 'date', + 'dateTime', + 'decimal', + 'display', + 'group', + 'integer', + 'open-choice', + 'quantity', + 'question', + 'reference', + 'string', + 'text', + 'time', + 'url', + ]), + _type: z.lazy(() => elementSchema).optional(), + enableWhen: questionnaireItemEnableWhenSchema.array().optional(), + enableBehavior: z.enum(['all', 'any']).optional(), + _enableBehavior: z.lazy(() => elementSchema).optional(), + required: booleanSchema.optional(), + _required: z.lazy(() => elementSchema).optional(), + repeats: booleanSchema.optional(), + _repeats: z.lazy(() => elementSchema).optional(), + readOnly: booleanSchema.optional(), + _readOnly: z.lazy(() => elementSchema).optional(), + maxLength: integerSchema.optional(), + _maxLength: z.lazy(() => elementSchema).optional(), + answerValueSet: canonicalSchema.optional(), + _answerValueSet: z.lazy(() => elementSchema).optional(), + answerOption: questionnaireItemAnswerOptionSchema.array().optional(), + initial: questionnaireItemInitialSchema.array().optional(), + item: z + .lazy(() => questionnaireItemSchema) + .array() + .optional(), + }), +) + +export const questionnaireItemEnableWhenSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + question: stringSchema, + _question: z.lazy(() => elementSchema).optional(), + operator: z.enum(['!=', '<', '<=', '=', '>', '>=', 'exists']), + _operator: z.lazy(() => elementSchema).optional(), + answerBoolean: booleanSchema.optional(), + _answerBoolean: z.lazy(() => elementSchema).optional(), + answerDecimal: decimalSchema.optional(), + _answerDecimal: z.lazy(() => elementSchema).optional(), + answerInteger: integerSchema.optional(), + _answerInteger: z.lazy(() => elementSchema).optional(), + answerDate: dateSchema.optional(), + _answerDate: z.lazy(() => elementSchema).optional(), + answerDateTime: dateTimeSchema.optional(), + _answerDateTime: z.lazy(() => elementSchema).optional(), + answerTime: timeSchema.optional(), + _answerTime: z.lazy(() => elementSchema).optional(), + answerString: stringSchema.optional(), + _answerString: z.lazy(() => elementSchema).optional(), + answerCoding: z.lazy(() => codingSchema).optional(), + answerQuantity: z.lazy(() => quantitySchema).optional(), + answerReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'answerBoolean', + 'answerDecimal', + 'answerInteger', + 'answerDate', + 'answerDateTime', + 'answerTime', + 'answerString', + 'answerCoding', + 'answerQuantity', + 'answerReference', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const questionnaireItemAnswerOptionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + initialSelected: booleanSchema.optional(), + _initialSelected: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueInteger', + 'valueDate', + 'valueTime', + 'valueString', + 'valueCoding', + 'valueReference', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const questionnaireItemInitialSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBoolean', + 'valueDecimal', + 'valueInteger', + 'valueDate', + 'valueDateTime', + 'valueTime', + 'valueString', + 'valueUri', + 'valueAttachment', + 'valueCoding', + 'valueQuantity', + 'valueReference', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const questionnaireSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Questionnaire'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + derivedFrom: canonicalSchema.array().optional(), + _derivedFrom: z + .lazy(() => elementSchema) + .array() + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectType: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .array() + .optional(), + _subjectType: z + .lazy(() => elementSchema) + .array() + .optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + code: z + .lazy(() => codingSchema) + .array() + .optional(), + item: questionnaireItemSchema.array().optional(), + }), +) + +export const questionnaireResponseItemSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + linkId: stringSchema, + _linkId: z.lazy(() => elementSchema).optional(), + definition: uriSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + answer: questionnaireResponseItemAnswerSchema.array().optional(), + item: z + .lazy(() => questionnaireResponseItemSchema) + .array() + .optional(), + }), + ) + +export const questionnaireResponseItemAnswerSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + item: z + .lazy(() => questionnaireResponseItemSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBoolean', + 'valueDecimal', + 'valueInteger', + 'valueDate', + 'valueDateTime', + 'valueTime', + 'valueString', + 'valueUri', + 'valueAttachment', + 'valueCoding', + 'valueQuantity', + 'valueReference', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const questionnaireResponseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('QuestionnaireResponse'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + questionnaire: canonicalSchema.optional(), + _questionnaire: z.lazy(() => elementSchema).optional(), + status: z.enum([ + 'amended', + 'completed', + 'entered-in-error', + 'in-progress', + 'stopped', + ]), + _status: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + authored: dateTimeSchema.optional(), + _authored: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + item: questionnaireResponseItemSchema.array().optional(), + }), + ) + +export const rangeSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + low: z.lazy(() => quantitySchema).optional(), + high: z.lazy(() => quantitySchema).optional(), + }), +) + +export const ratioSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + numerator: z.lazy(() => quantitySchema).optional(), + denominator: z.lazy(() => quantitySchema).optional(), + }), +) + +export const referenceSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + reference: stringSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + type: uriSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + }), +) + +export const relatedArtifactSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'citation', + 'composed-of', + 'depends-on', + 'derived-from', + 'documentation', + 'justification', + 'predecessor', + 'successor', + ]), + _type: z.lazy(() => elementSchema).optional(), + label: stringSchema.optional(), + _label: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + citation: markdownSchema.optional(), + _citation: z.lazy(() => elementSchema).optional(), + url: urlSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + document: z.lazy(() => attachmentSchema).optional(), + resource: canonicalSchema.optional(), + _resource: z.lazy(() => elementSchema).optional(), + }), +) + +export const relatedPersonCommunicationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + language: z.lazy(() => codeableConceptSchema), + preferred: booleanSchema.optional(), + _preferred: z.lazy(() => elementSchema).optional(), + }), + ) + +export const relatedPersonSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('RelatedPerson'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + relationship: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + name: z + .lazy(() => humanNameSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + address: z + .lazy(() => addressSchema) + .array() + .optional(), + photo: z + .lazy(() => attachmentSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + communication: relatedPersonCommunicationSchema.array().optional(), + }), +) + +export const requestGroupActionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + prefix: stringSchema.optional(), + _prefix: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + textEquivalent: stringSchema.optional(), + _textEquivalent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + documentation: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + condition: requestGroupActionConditionSchema.array().optional(), + relatedAction: requestGroupActionRelatedActionSchema.array().optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + timingAge: z.lazy(() => ageSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingDuration: z.lazy(() => durationSchema).optional(), + timingRange: z.lazy(() => rangeSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + participant: z + .lazy(() => referenceSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + groupingBehavior: z + .enum(['logical-group', 'sentence-group', 'visual-group']) + .optional(), + _groupingBehavior: z.lazy(() => elementSchema).optional(), + selectionBehavior: z + .enum([ + 'all', + 'all-or-none', + 'any', + 'at-most-one', + 'exactly-one', + 'one-or-more', + ]) + .optional(), + _selectionBehavior: z.lazy(() => elementSchema).optional(), + requiredBehavior: z + .enum(['could', 'must', 'must-unless-documented']) + .optional(), + _requiredBehavior: z.lazy(() => elementSchema).optional(), + precheckBehavior: z.enum(['no', 'yes']).optional(), + _precheckBehavior: z.lazy(() => elementSchema).optional(), + cardinalityBehavior: z.enum(['multiple', 'single']).optional(), + _cardinalityBehavior: z.lazy(() => elementSchema).optional(), + resource: z.lazy(() => referenceSchema).optional(), + action: z + .lazy(() => requestGroupActionSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'timingDateTime', + 'timingAge', + 'timingPeriod', + 'timingDuration', + 'timingRange', + 'timingTiming', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const requestGroupActionConditionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + kind: z.enum(['applicability', 'start', 'stop']), + _kind: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema).optional(), + }), + ) + +export const requestGroupActionRelatedActionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + actionId: idSchema, + _actionId: z.lazy(() => elementSchema).optional(), + relationship: z.enum([ + 'after', + 'after-end', + 'after-start', + 'before', + 'before-end', + 'before-start', + 'concurrent', + 'concurrent-with-end', + 'concurrent-with-start', + ]), + _relationship: z.lazy(() => elementSchema).optional(), + offsetDuration: z.lazy(() => durationSchema).optional(), + offsetRange: z.lazy(() => rangeSchema).optional(), + }) + .refine( + (value) => + [{ names: ['offsetDuration', 'offsetRange'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const requestGroupSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('RequestGroup'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + replaces: z + .lazy(() => referenceSchema) + .array() + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + status: z.enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + intent: z.enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + action: requestGroupActionSchema.array().optional(), + }), +) + +export const researchDefinitionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ResearchDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + shortTitle: stringSchema.optional(), + _shortTitle: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + comment: stringSchema.array().optional(), + _comment: z + .lazy(() => elementSchema) + .array() + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + library: canonicalSchema.array().optional(), + _library: z + .lazy(() => elementSchema) + .array() + .optional(), + population: z.lazy(() => referenceSchema), + exposure: z.lazy(() => referenceSchema).optional(), + exposureAlternative: z.lazy(() => referenceSchema).optional(), + outcome: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['subjectCodeableConcept', 'subjectReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const researchElementDefinitionCharacteristicSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + definitionCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + definitionCanonical: canonicalSchema.optional(), + _definitionCanonical: z.lazy(() => elementSchema).optional(), + definitionExpression: z.lazy(() => expressionSchema).optional(), + definitionDataRequirement: z + .lazy(() => dataRequirementSchema) + .optional(), + usageContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + exclude: booleanSchema.optional(), + _exclude: z.lazy(() => elementSchema).optional(), + unitOfMeasure: z.lazy(() => codeableConceptSchema).optional(), + studyEffectiveDescription: stringSchema.optional(), + _studyEffectiveDescription: z.lazy(() => elementSchema).optional(), + studyEffectiveDateTime: dateTimeSchema.optional(), + _studyEffectiveDateTime: z.lazy(() => elementSchema).optional(), + studyEffectivePeriod: z.lazy(() => periodSchema).optional(), + studyEffectiveDuration: z.lazy(() => durationSchema).optional(), + studyEffectiveTiming: z.lazy(() => timingSchema).optional(), + studyEffectiveTimeFromStart: z.lazy(() => durationSchema).optional(), + studyEffectiveGroupMeasure: z + .enum([ + 'mean', + 'mean-of-mean', + 'mean-of-median', + 'median', + 'median-of-mean', + 'median-of-median', + ]) + .optional(), + _studyEffectiveGroupMeasure: z.lazy(() => elementSchema).optional(), + participantEffectiveDescription: stringSchema.optional(), + _participantEffectiveDescription: z + .lazy(() => elementSchema) + .optional(), + participantEffectiveDateTime: dateTimeSchema.optional(), + _participantEffectiveDateTime: z.lazy(() => elementSchema).optional(), + participantEffectivePeriod: z.lazy(() => periodSchema).optional(), + participantEffectiveDuration: z.lazy(() => durationSchema).optional(), + participantEffectiveTiming: z.lazy(() => timingSchema).optional(), + participantEffectiveTimeFromStart: z + .lazy(() => durationSchema) + .optional(), + participantEffectiveGroupMeasure: z + .enum([ + 'mean', + 'mean-of-mean', + 'mean-of-median', + 'median', + 'median-of-mean', + 'median-of-median', + ]) + .optional(), + _participantEffectiveGroupMeasure: z + .lazy(() => elementSchema) + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'definitionCodeableConcept', + 'definitionCanonical', + 'definitionExpression', + 'definitionDataRequirement', + ], + required: true, + }, + { + names: [ + 'studyEffectiveDateTime', + 'studyEffectivePeriod', + 'studyEffectiveDuration', + 'studyEffectiveTiming', + ], + required: false, + }, + { + names: [ + 'participantEffectiveDateTime', + 'participantEffectivePeriod', + 'participantEffectiveDuration', + 'participantEffectiveTiming', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const researchElementDefinitionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ResearchElementDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + shortTitle: stringSchema.optional(), + _shortTitle: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + comment: stringSchema.array().optional(), + _comment: z + .lazy(() => elementSchema) + .array() + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + library: canonicalSchema.array().optional(), + _library: z + .lazy(() => elementSchema) + .array() + .optional(), + type: z.enum(['exposure', 'outcome', 'population']), + _type: z.lazy(() => elementSchema).optional(), + variableType: z + .enum(['continuous', 'descriptive', 'dichotomous']) + .optional(), + _variableType: z.lazy(() => elementSchema).optional(), + characteristic: researchElementDefinitionCharacteristicSchema + .array() + .min(1), + }) + .refine( + (value) => + [ + { + names: ['subjectCodeableConcept', 'subjectReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const researchStudyArmSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }), +) + +export const researchStudyObjectiveSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const researchStudySchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('ResearchStudy'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + protocol: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'administratively-completed', + 'approved', + 'closed-to-accrual', + 'closed-to-accrual-and-intervention', + 'completed', + 'disapproved', + 'in-review', + 'temporarily-closed-to-accrual', + 'temporarily-closed-to-accrual-and-intervention', + 'withdrawn', + ]), + _status: z.lazy(() => elementSchema).optional(), + primaryPurposeType: z.lazy(() => codeableConceptSchema).optional(), + phase: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + focus: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + condition: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + keyword: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + location: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + enrollment: z + .lazy(() => referenceSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + sponsor: z.lazy(() => referenceSchema).optional(), + principalInvestigator: z.lazy(() => referenceSchema).optional(), + site: z + .lazy(() => referenceSchema) + .array() + .optional(), + reasonStopped: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + arm: researchStudyArmSchema.array().optional(), + objective: researchStudyObjectiveSchema.array().optional(), + }), +) + +export const researchSubjectSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('ResearchSubject'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'candidate', + 'eligible', + 'follow-up', + 'ineligible', + 'not-registered', + 'off-study', + 'on-study', + 'on-study-intervention', + 'on-study-observation', + 'pending-on-study', + 'potential-candidate', + 'screening', + 'withdrawn', + ]), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + study: z.lazy(() => referenceSchema), + individual: z.lazy(() => referenceSchema), + assignedArm: stringSchema.optional(), + _assignedArm: z.lazy(() => elementSchema).optional(), + actualArm: stringSchema.optional(), + _actualArm: z.lazy(() => elementSchema).optional(), + consent: z.lazy(() => referenceSchema).optional(), + }), +) + +export const riskAssessmentPredictionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + outcome: z.lazy(() => codeableConceptSchema).optional(), + probabilityDecimal: decimalSchema.optional(), + _probabilityDecimal: z.lazy(() => elementSchema).optional(), + probabilityRange: z.lazy(() => rangeSchema).optional(), + qualitativeRisk: z.lazy(() => codeableConceptSchema).optional(), + relativeRisk: decimalSchema.optional(), + _relativeRisk: z.lazy(() => elementSchema).optional(), + whenPeriod: z.lazy(() => periodSchema).optional(), + whenRange: z.lazy(() => rangeSchema).optional(), + rationale: stringSchema.optional(), + _rationale: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['probabilityDecimal', 'probabilityRange'], + required: false, + }, + { names: ['whenPeriod', 'whenRange'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const riskAssessmentSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('RiskAssessment'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z.lazy(() => referenceSchema).optional(), + parent: z.lazy(() => referenceSchema).optional(), + status: z.enum([ + 'amended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'preliminary', + 'registered', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + condition: z.lazy(() => referenceSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + basis: z + .lazy(() => referenceSchema) + .array() + .optional(), + prediction: riskAssessmentPredictionSchema.array().optional(), + mitigation: stringSchema.optional(), + _mitigation: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['occurrenceDateTime', 'occurrencePeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const riskEvidenceSynthesisSampleSizeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + numberOfStudies: integerSchema.optional(), + _numberOfStudies: z.lazy(() => elementSchema).optional(), + numberOfParticipants: integerSchema.optional(), + _numberOfParticipants: z.lazy(() => elementSchema).optional(), + }), + ) + +export const riskEvidenceSynthesisRiskEstimateSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + unitOfMeasure: z.lazy(() => codeableConceptSchema).optional(), + denominatorCount: integerSchema.optional(), + _denominatorCount: z.lazy(() => elementSchema).optional(), + numeratorCount: integerSchema.optional(), + _numeratorCount: z.lazy(() => elementSchema).optional(), + precisionEstimate: + riskEvidenceSynthesisRiskEstimatePrecisionEstimateSchema + .array() + .optional(), + }), + ) + +export const riskEvidenceSynthesisCertaintySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + rating: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + certaintySubcomponent: + riskEvidenceSynthesisCertaintyCertaintySubcomponentSchema + .array() + .optional(), + }), + ) + +export const riskEvidenceSynthesisRiskEstimatePrecisionEstimateSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + level: decimalSchema.optional(), + _level: z.lazy(() => elementSchema).optional(), + from: decimalSchema.optional(), + _from: z.lazy(() => elementSchema).optional(), + to: decimalSchema.optional(), + _to: z.lazy(() => elementSchema).optional(), + }), + ) + +export const riskEvidenceSynthesisCertaintyCertaintySubcomponentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + rating: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), + ) + +export const riskEvidenceSynthesisSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('RiskEvidenceSynthesis'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + synthesisType: z.lazy(() => codeableConceptSchema).optional(), + studyType: z.lazy(() => codeableConceptSchema).optional(), + population: z.lazy(() => referenceSchema), + exposure: z.lazy(() => referenceSchema).optional(), + outcome: z.lazy(() => referenceSchema), + sampleSize: riskEvidenceSynthesisSampleSizeSchema.optional(), + riskEstimate: riskEvidenceSynthesisRiskEstimateSchema.optional(), + certainty: riskEvidenceSynthesisCertaintySchema.array().optional(), + }), + ) + +export const sampledDataSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + origin: z.lazy(() => quantitySchema), + period: decimalSchema, + _period: z.lazy(() => elementSchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + lowerLimit: decimalSchema.optional(), + _lowerLimit: z.lazy(() => elementSchema).optional(), + upperLimit: decimalSchema.optional(), + _upperLimit: z.lazy(() => elementSchema).optional(), + dimensions: positiveIntSchema, + _dimensions: z.lazy(() => elementSchema).optional(), + data: stringSchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + }), +) + +export const scheduleSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Schedule'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + serviceCategory: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + serviceType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + actor: z + .lazy(() => referenceSchema) + .array() + .min(1), + planningHorizon: z.lazy(() => periodSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), +) + +export const searchParameterComponentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + definition: canonicalSchema, + _definition: z.lazy(() => elementSchema).optional(), + expression: stringSchema, + _expression: z.lazy(() => elementSchema).optional(), + }), + ) + +export const searchParameterSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('SearchParameter'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + derivedFrom: canonicalSchema.optional(), + _derivedFrom: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema, + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + base: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .array() + .min(1), + _base: z + .lazy(() => elementSchema) + .array() + .optional(), + type: z.enum([ + 'composite', + 'date', + 'number', + 'quantity', + 'reference', + 'special', + 'string', + 'token', + 'uri', + ]), + _type: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + xpath: stringSchema.optional(), + _xpath: z.lazy(() => elementSchema).optional(), + xpathUsage: z + .enum(['distance', 'nearby', 'normal', 'other', 'phonetic']) + .optional(), + _xpathUsage: z.lazy(() => elementSchema).optional(), + target: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .array() + .optional(), + _target: z + .lazy(() => elementSchema) + .array() + .optional(), + multipleOr: booleanSchema.optional(), + _multipleOr: z.lazy(() => elementSchema).optional(), + multipleAnd: booleanSchema.optional(), + _multipleAnd: z.lazy(() => elementSchema).optional(), + comparator: z + .enum(['ap', 'eb', 'eq', 'ge', 'gt', 'le', 'lt', 'ne', 'sa']) + .array() + .optional(), + _comparator: z + .lazy(() => elementSchema) + .array() + .optional(), + modifier: z + .enum([ + 'above', + 'below', + 'contains', + 'exact', + 'identifier', + 'in', + 'missing', + 'not', + 'not-in', + 'ofType', + 'text', + 'type', + ]) + .array() + .optional(), + _modifier: z + .lazy(() => elementSchema) + .array() + .optional(), + chain: stringSchema.array().optional(), + _chain: z + .lazy(() => elementSchema) + .array() + .optional(), + component: searchParameterComponentSchema.array().optional(), + }), +) + +export const serviceRequestSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ServiceRequest'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + replaces: z + .lazy(() => referenceSchema) + .array() + .optional(), + requisition: z.lazy(() => identifierSchema).optional(), + status: z.enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + intent: z.enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]), + _intent: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + orderDetail: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantityQuantity: z.lazy(() => quantitySchema).optional(), + quantityRatio: z.lazy(() => ratioSchema).optional(), + quantityRange: z.lazy(() => rangeSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + asNeededBoolean: booleanSchema.optional(), + _asNeededBoolean: z.lazy(() => elementSchema).optional(), + asNeededCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performerType: z.lazy(() => codeableConceptSchema).optional(), + performer: z + .lazy(() => referenceSchema) + .array() + .optional(), + locationCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + locationReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + insurance: z + .lazy(() => referenceSchema) + .array() + .optional(), + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .optional(), + specimen: z + .lazy(() => referenceSchema) + .array() + .optional(), + bodySite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + patientInstruction: stringSchema.optional(), + _patientInstruction: z.lazy(() => elementSchema).optional(), + relevantHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['quantityQuantity', 'quantityRatio', 'quantityRange'], + required: false, + }, + { + names: [ + 'occurrenceDateTime', + 'occurrencePeriod', + 'occurrenceTiming', + ], + required: false, + }, + { + names: ['asNeededBoolean', 'asNeededCodeableConcept'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const signatureSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z + .lazy(() => codingSchema) + .array() + .min(1), + when: instantSchema, + _when: z.lazy(() => elementSchema).optional(), + who: z.lazy(() => referenceSchema), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + targetFormat: codeSchema.optional(), + _targetFormat: z.lazy(() => elementSchema).optional(), + sigFormat: codeSchema.optional(), + _sigFormat: z.lazy(() => elementSchema).optional(), + data: base64BinarySchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + }), +) + +export const slotSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Slot'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + serviceCategory: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + serviceType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + appointmentType: z.lazy(() => codeableConceptSchema).optional(), + schedule: z.lazy(() => referenceSchema), + status: z.enum([ + 'busy', + 'busy-tentative', + 'busy-unavailable', + 'entered-in-error', + 'free', + ]), + _status: z.lazy(() => elementSchema).optional(), + start: instantSchema, + _start: z.lazy(() => elementSchema).optional(), + end: instantSchema, + _end: z.lazy(() => elementSchema).optional(), + overbooked: booleanSchema.optional(), + _overbooked: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), +) + +export const specimenCollectionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + collector: z.lazy(() => referenceSchema).optional(), + collectedDateTime: dateTimeSchema.optional(), + _collectedDateTime: z.lazy(() => elementSchema).optional(), + collectedPeriod: z.lazy(() => periodSchema).optional(), + duration: z.lazy(() => durationSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + fastingStatusCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + fastingStatusDuration: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['collectedDateTime', 'collectedPeriod'], + required: false, + }, + { + names: ['fastingStatusCodeableConcept', 'fastingStatusDuration'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const specimenProcessingSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + procedure: z.lazy(() => codeableConceptSchema).optional(), + additive: z + .lazy(() => referenceSchema) + .array() + .optional(), + timeDateTime: dateTimeSchema.optional(), + _timeDateTime: z.lazy(() => elementSchema).optional(), + timePeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [{ names: ['timeDateTime', 'timePeriod'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const specimenContainerSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + capacity: z.lazy(() => quantitySchema).optional(), + specimenQuantity: z.lazy(() => quantitySchema).optional(), + additiveCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + additiveReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['additiveCodeableConcept', 'additiveReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const specimenSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Specimen'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + accessionIdentifier: z.lazy(() => identifierSchema).optional(), + status: z + .enum(['available', 'entered-in-error', 'unavailable', 'unsatisfactory']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + receivedTime: dateTimeSchema.optional(), + _receivedTime: z.lazy(() => elementSchema).optional(), + parent: z + .lazy(() => referenceSchema) + .array() + .optional(), + request: z + .lazy(() => referenceSchema) + .array() + .optional(), + collection: specimenCollectionSchema.optional(), + processing: specimenProcessingSchema.array().optional(), + container: specimenContainerSchema.array().optional(), + condition: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), +) + +export const specimenDefinitionTypeTestedSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + isDerived: booleanSchema.optional(), + _isDerived: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + preference: z.enum(['alternate', 'preferred']), + _preference: z.lazy(() => elementSchema).optional(), + container: specimenDefinitionTypeTestedContainerSchema.optional(), + requirement: stringSchema.optional(), + _requirement: z.lazy(() => elementSchema).optional(), + retentionTime: z.lazy(() => durationSchema).optional(), + rejectionCriterion: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + handling: specimenDefinitionTypeTestedHandlingSchema.array().optional(), + }), + ) + +export const specimenDefinitionTypeTestedContainerSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + material: z.lazy(() => codeableConceptSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + cap: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + capacity: z.lazy(() => quantitySchema).optional(), + minimumVolumeQuantity: z.lazy(() => quantitySchema).optional(), + minimumVolumeString: stringSchema.optional(), + _minimumVolumeString: z.lazy(() => elementSchema).optional(), + additive: specimenDefinitionTypeTestedContainerAdditiveSchema + .array() + .optional(), + preparation: stringSchema.optional(), + _preparation: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['minimumVolumeQuantity', 'minimumVolumeString'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const specimenDefinitionTypeTestedHandlingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + temperatureQualifier: z.lazy(() => codeableConceptSchema).optional(), + temperatureRange: z.lazy(() => rangeSchema).optional(), + maxDuration: z.lazy(() => durationSchema).optional(), + instruction: stringSchema.optional(), + _instruction: z.lazy(() => elementSchema).optional(), + }), + ) + +export const specimenDefinitionTypeTestedContainerAdditiveSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + additiveCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + additiveReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['additiveCodeableConcept', 'additiveReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const specimenDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('SpecimenDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + typeCollected: z.lazy(() => codeableConceptSchema).optional(), + patientPreparation: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + timeAspect: stringSchema.optional(), + _timeAspect: z.lazy(() => elementSchema).optional(), + collection: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + typeTested: specimenDefinitionTypeTestedSchema.array().optional(), + }), + ) + +export const structureDefinitionMappingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identity: idSchema, + _identity: z.lazy(() => elementSchema).optional(), + uri: uriSchema.optional(), + _uri: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), + ) + +export const structureDefinitionContextSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['element', 'extension', 'fhirpath']), + _type: z.lazy(() => elementSchema).optional(), + expression: stringSchema, + _expression: z.lazy(() => elementSchema).optional(), + }), + ) + +export const structureDefinitionSnapshotSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + element: z + .lazy(() => elementDefinitionSchema) + .array() + .min(1), + }), + ) + +export const structureDefinitionDifferentialSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + element: z + .lazy(() => elementDefinitionSchema) + .array() + .min(1), + }), + ) + +export const structureDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('StructureDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + keyword: z + .lazy(() => codingSchema) + .array() + .optional(), + fhirVersion: z + .enum([ + '0.0.80', + '0.0.81', + '0.0.82', + '0.01', + '0.05', + '0.06', + '0.11', + '0.4.0', + '0.5.0', + '1.0.0', + '1.0.1', + '1.0.2', + '1.1.0', + '1.4.0', + '1.6.0', + '1.8.0', + '3.0.0', + '3.0.1', + '3.3.0', + '3.5.0', + '4.0.0', + '4.0.1', + ]) + .optional(), + _fhirVersion: z.lazy(() => elementSchema).optional(), + mapping: structureDefinitionMappingSchema.array().optional(), + kind: z.enum(['complex-type', 'logical', 'primitive-type', 'resource']), + _kind: z.lazy(() => elementSchema).optional(), + abstract: booleanSchema, + _abstract: z.lazy(() => elementSchema).optional(), + context: structureDefinitionContextSchema.array().optional(), + contextInvariant: stringSchema.array().optional(), + _contextInvariant: z + .lazy(() => elementSchema) + .array() + .optional(), + type: uriSchema, + _type: z.lazy(() => elementSchema).optional(), + baseDefinition: canonicalSchema.optional(), + _baseDefinition: z.lazy(() => elementSchema).optional(), + derivation: z.enum(['constraint', 'specialization']).optional(), + _derivation: z.lazy(() => elementSchema).optional(), + snapshot: structureDefinitionSnapshotSchema.optional(), + differential: structureDefinitionDifferentialSchema.optional(), + }), + ) + +export const structureMapStructureSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: canonicalSchema, + _url: z.lazy(() => elementSchema).optional(), + mode: z.enum(['produced', 'queried', 'source', 'target']), + _mode: z.lazy(() => elementSchema).optional(), + alias: stringSchema.optional(), + _alias: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const structureMapGroupSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: idSchema, + _name: z.lazy(() => elementSchema).optional(), + extends: idSchema.optional(), + _extends: z.lazy(() => elementSchema).optional(), + typeMode: z.enum(['none', 'type-and-types', 'types']), + _typeMode: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + input: structureMapGroupInputSchema.array().min(1), + rule: structureMapGroupRuleSchema.array().min(1), + }), +) + +export const structureMapGroupInputSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: idSchema, + _name: z.lazy(() => elementSchema).optional(), + type: stringSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + mode: z.enum(['source', 'target']), + _mode: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const structureMapGroupRuleSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: idSchema, + _name: z.lazy(() => elementSchema).optional(), + source: structureMapGroupRuleSourceSchema.array().min(1), + target: structureMapGroupRuleTargetSchema.array().optional(), + rule: z + .lazy(() => structureMapGroupRuleSchema) + .array() + .optional(), + dependent: structureMapGroupRuleDependentSchema.array().optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const structureMapGroupRuleSourceSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + context: idSchema, + _context: z.lazy(() => elementSchema).optional(), + min: integerSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + type: stringSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + defaultValueBase64Binary: base64BinarySchema.optional(), + _defaultValueBase64Binary: z.lazy(() => elementSchema).optional(), + defaultValueBoolean: booleanSchema.optional(), + _defaultValueBoolean: z.lazy(() => elementSchema).optional(), + defaultValueCanonical: canonicalSchema.optional(), + _defaultValueCanonical: z.lazy(() => elementSchema).optional(), + defaultValueCode: codeSchema.optional(), + _defaultValueCode: z.lazy(() => elementSchema).optional(), + defaultValueDate: dateSchema.optional(), + _defaultValueDate: z.lazy(() => elementSchema).optional(), + defaultValueDateTime: dateTimeSchema.optional(), + _defaultValueDateTime: z.lazy(() => elementSchema).optional(), + defaultValueDecimal: decimalSchema.optional(), + _defaultValueDecimal: z.lazy(() => elementSchema).optional(), + defaultValueId: idSchema.optional(), + _defaultValueId: z.lazy(() => elementSchema).optional(), + defaultValueInstant: instantSchema.optional(), + _defaultValueInstant: z.lazy(() => elementSchema).optional(), + defaultValueInteger: integerSchema.optional(), + _defaultValueInteger: z.lazy(() => elementSchema).optional(), + defaultValueMarkdown: markdownSchema.optional(), + _defaultValueMarkdown: z.lazy(() => elementSchema).optional(), + defaultValueOid: oidSchema.optional(), + _defaultValueOid: z.lazy(() => elementSchema).optional(), + defaultValuePositiveInt: positiveIntSchema.optional(), + _defaultValuePositiveInt: z.lazy(() => elementSchema).optional(), + defaultValueString: stringSchema.optional(), + _defaultValueString: z.lazy(() => elementSchema).optional(), + defaultValueTime: timeSchema.optional(), + _defaultValueTime: z.lazy(() => elementSchema).optional(), + defaultValueUnsignedInt: unsignedIntSchema.optional(), + _defaultValueUnsignedInt: z.lazy(() => elementSchema).optional(), + defaultValueUri: uriSchema.optional(), + _defaultValueUri: z.lazy(() => elementSchema).optional(), + defaultValueUrl: urlSchema.optional(), + _defaultValueUrl: z.lazy(() => elementSchema).optional(), + defaultValueUuid: uuidSchema.optional(), + _defaultValueUuid: z.lazy(() => elementSchema).optional(), + defaultValueAddress: z.lazy(() => addressSchema).optional(), + defaultValueAge: z.lazy(() => ageSchema).optional(), + defaultValueAnnotation: z.lazy(() => annotationSchema).optional(), + defaultValueAttachment: z.lazy(() => attachmentSchema).optional(), + defaultValueCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + defaultValueCoding: z.lazy(() => codingSchema).optional(), + defaultValueContactPoint: z.lazy(() => contactPointSchema).optional(), + defaultValueCount: z.lazy(() => countSchema).optional(), + defaultValueDistance: z.lazy(() => distanceSchema).optional(), + defaultValueDuration: z.lazy(() => durationSchema).optional(), + defaultValueHumanName: z.lazy(() => humanNameSchema).optional(), + defaultValueIdentifier: z.lazy(() => identifierSchema).optional(), + defaultValueMoney: z.lazy(() => moneySchema).optional(), + defaultValuePeriod: z.lazy(() => periodSchema).optional(), + defaultValueQuantity: z.lazy(() => quantitySchema).optional(), + defaultValueRange: z.lazy(() => rangeSchema).optional(), + defaultValueRatio: z.lazy(() => ratioSchema).optional(), + defaultValueReference: z.lazy(() => referenceSchema).optional(), + defaultValueSampledData: z.lazy(() => sampledDataSchema).optional(), + defaultValueSignature: z.lazy(() => signatureSchema).optional(), + defaultValueTiming: z.lazy(() => timingSchema).optional(), + defaultValueContactDetail: z.lazy(() => contactDetailSchema).optional(), + defaultValueContributor: z.lazy(() => contributorSchema).optional(), + defaultValueDataRequirement: z + .lazy(() => dataRequirementSchema) + .optional(), + defaultValueExpression: z.lazy(() => expressionSchema).optional(), + defaultValueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + defaultValueRelatedArtifact: z + .lazy(() => relatedArtifactSchema) + .optional(), + defaultValueTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + defaultValueUsageContext: z.lazy(() => usageContextSchema).optional(), + defaultValueDosage: z.lazy(() => dosageSchema).optional(), + defaultValueMeta: z.lazy(() => metaSchema).optional(), + element: stringSchema.optional(), + _element: z.lazy(() => elementSchema).optional(), + listMode: z + .enum(['first', 'last', 'not_first', 'not_last', 'only_one']) + .optional(), + _listMode: z.lazy(() => elementSchema).optional(), + variable: idSchema.optional(), + _variable: z.lazy(() => elementSchema).optional(), + condition: stringSchema.optional(), + _condition: z.lazy(() => elementSchema).optional(), + check: stringSchema.optional(), + _check: z.lazy(() => elementSchema).optional(), + logMessage: stringSchema.optional(), + _logMessage: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'defaultValueBase64Binary', + 'defaultValueBoolean', + 'defaultValueCanonical', + 'defaultValueCode', + 'defaultValueDate', + 'defaultValueDateTime', + 'defaultValueDecimal', + 'defaultValueId', + 'defaultValueInstant', + 'defaultValueInteger', + 'defaultValueMarkdown', + 'defaultValueOid', + 'defaultValuePositiveInt', + 'defaultValueString', + 'defaultValueTime', + 'defaultValueUnsignedInt', + 'defaultValueUri', + 'defaultValueUrl', + 'defaultValueUuid', + 'defaultValueAddress', + 'defaultValueAge', + 'defaultValueAnnotation', + 'defaultValueAttachment', + 'defaultValueCodeableConcept', + 'defaultValueCoding', + 'defaultValueContactPoint', + 'defaultValueCount', + 'defaultValueDistance', + 'defaultValueDuration', + 'defaultValueHumanName', + 'defaultValueIdentifier', + 'defaultValueMoney', + 'defaultValuePeriod', + 'defaultValueQuantity', + 'defaultValueRange', + 'defaultValueRatio', + 'defaultValueReference', + 'defaultValueSampledData', + 'defaultValueSignature', + 'defaultValueTiming', + 'defaultValueContactDetail', + 'defaultValueContributor', + 'defaultValueDataRequirement', + 'defaultValueExpression', + 'defaultValueParameterDefinition', + 'defaultValueRelatedArtifact', + 'defaultValueTriggerDefinition', + 'defaultValueUsageContext', + 'defaultValueDosage', + 'defaultValueMeta', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const structureMapGroupRuleTargetSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + context: idSchema.optional(), + _context: z.lazy(() => elementSchema).optional(), + contextType: z.enum(['type', 'variable']).optional(), + _contextType: z.lazy(() => elementSchema).optional(), + element: stringSchema.optional(), + _element: z.lazy(() => elementSchema).optional(), + variable: idSchema.optional(), + _variable: z.lazy(() => elementSchema).optional(), + listMode: z + .enum(['collate', 'first', 'last', 'share']) + .array() + .optional(), + _listMode: z + .lazy(() => elementSchema) + .array() + .optional(), + listRuleId: idSchema.optional(), + _listRuleId: z.lazy(() => elementSchema).optional(), + transform: z + .enum([ + 'append', + 'c', + 'cast', + 'cc', + 'copy', + 'cp', + 'create', + 'dateOp', + 'escape', + 'evaluate', + 'id', + 'pointer', + 'qty', + 'reference', + 'translate', + 'truncate', + 'uuid', + ]) + .optional(), + _transform: z.lazy(() => elementSchema).optional(), + parameter: structureMapGroupRuleTargetParameterSchema.array().optional(), + }), + ) + +export const structureMapGroupRuleDependentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: idSchema, + _name: z.lazy(() => elementSchema).optional(), + variable: stringSchema.array().min(1), + _variable: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const structureMapGroupRuleTargetParameterSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueId', + 'valueString', + 'valueBoolean', + 'valueInteger', + 'valueDecimal', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const structureMapSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('StructureMap'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + structure: structureMapStructureSchema.array().optional(), + import: canonicalSchema.array().optional(), + _import: z + .lazy(() => elementSchema) + .array() + .optional(), + group: structureMapGroupSchema.array().min(1), + }), +) + +export const subscriptionChannelSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['email', 'message', 'rest-hook', 'sms', 'websocket']), + _type: z.lazy(() => elementSchema).optional(), + endpoint: urlSchema.optional(), + _endpoint: z.lazy(() => elementSchema).optional(), + payload: codeSchema.optional(), + _payload: z.lazy(() => elementSchema).optional(), + header: stringSchema.array().optional(), + _header: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const subscriptionSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Subscription'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + status: z.enum(['active', 'error', 'off', 'requested']), + _status: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactPointSchema) + .array() + .optional(), + end: instantSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + reason: stringSchema, + _reason: z.lazy(() => elementSchema).optional(), + criteria: stringSchema, + _criteria: z.lazy(() => elementSchema).optional(), + error: stringSchema.optional(), + _error: z.lazy(() => elementSchema).optional(), + channel: subscriptionChannelSchema, + }), +) + +export const substanceInstanceSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + expiry: dateTimeSchema.optional(), + _expiry: z.lazy(() => elementSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + }), +) + +export const substanceIngredientSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + quantity: z.lazy(() => ratioSchema).optional(), + substanceCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + substanceReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['substanceCodeableConcept', 'substanceReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const substanceSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Substance'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + instance: substanceInstanceSchema.array().optional(), + ingredient: substanceIngredientSchema.array().optional(), + }), +) + +export const substanceAmountReferenceRangeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + lowLimit: z.lazy(() => quantitySchema).optional(), + highLimit: z.lazy(() => quantitySchema).optional(), + }), + ) + +export const substanceAmountSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + amountQuantity: z.lazy(() => quantitySchema).optional(), + amountRange: z.lazy(() => rangeSchema).optional(), + amountString: stringSchema.optional(), + _amountString: z.lazy(() => elementSchema).optional(), + amountType: z.lazy(() => codeableConceptSchema).optional(), + amountText: stringSchema.optional(), + _amountText: z.lazy(() => elementSchema).optional(), + referenceRange: substanceAmountReferenceRangeSchema.optional(), + }) + .refine( + (value) => + [ + { + names: ['amountQuantity', 'amountRange', 'amountString'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const substanceNucleicAcidSubunitSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + subunit: integerSchema.optional(), + _subunit: z.lazy(() => elementSchema).optional(), + sequence: stringSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + length: integerSchema.optional(), + _length: z.lazy(() => elementSchema).optional(), + sequenceAttachment: z.lazy(() => attachmentSchema).optional(), + fivePrime: z.lazy(() => codeableConceptSchema).optional(), + threePrime: z.lazy(() => codeableConceptSchema).optional(), + linkage: substanceNucleicAcidSubunitLinkageSchema.array().optional(), + sugar: substanceNucleicAcidSubunitSugarSchema.array().optional(), + }), + ) + +export const substanceNucleicAcidSubunitLinkageSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + connectivity: stringSchema.optional(), + _connectivity: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + residueSite: stringSchema.optional(), + _residueSite: z.lazy(() => elementSchema).optional(), + }), + ) + +export const substanceNucleicAcidSubunitSugarSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + residueSite: stringSchema.optional(), + _residueSite: z.lazy(() => elementSchema).optional(), + }), + ) + +export const substanceNucleicAcidSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('SubstanceNucleicAcid'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequenceType: z.lazy(() => codeableConceptSchema).optional(), + numberOfSubunits: integerSchema.optional(), + _numberOfSubunits: z.lazy(() => elementSchema).optional(), + areaOfHybridisation: stringSchema.optional(), + _areaOfHybridisation: z.lazy(() => elementSchema).optional(), + oligoNucleotideType: z.lazy(() => codeableConceptSchema).optional(), + subunit: substanceNucleicAcidSubunitSchema.array().optional(), + }), + ) + +export const substancePolymerMonomerSetSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + ratioType: z.lazy(() => codeableConceptSchema).optional(), + startingMaterial: substancePolymerMonomerSetStartingMaterialSchema + .array() + .optional(), + }), + ) + +export const substancePolymerRepeatSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + numberOfUnits: integerSchema.optional(), + _numberOfUnits: z.lazy(() => elementSchema).optional(), + averageMolecularFormula: stringSchema.optional(), + _averageMolecularFormula: z.lazy(() => elementSchema).optional(), + repeatUnitAmountType: z.lazy(() => codeableConceptSchema).optional(), + repeatUnit: substancePolymerRepeatRepeatUnitSchema.array().optional(), + }), + ) + +export const substancePolymerMonomerSetStartingMaterialSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + material: z.lazy(() => codeableConceptSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + isDefining: booleanSchema.optional(), + _isDefining: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => substanceAmountSchema).optional(), + }), + ) + +export const substancePolymerRepeatRepeatUnitSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + orientationOfPolymerisation: z + .lazy(() => codeableConceptSchema) + .optional(), + repeatUnit: stringSchema.optional(), + _repeatUnit: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => substanceAmountSchema).optional(), + degreeOfPolymerisation: + substancePolymerRepeatRepeatUnitDegreeOfPolymerisationSchema + .array() + .optional(), + structuralRepresentation: + substancePolymerRepeatRepeatUnitStructuralRepresentationSchema + .array() + .optional(), + }), + ) + +export const substancePolymerRepeatRepeatUnitDegreeOfPolymerisationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + degree: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => substanceAmountSchema).optional(), + }), + ) + +export const substancePolymerRepeatRepeatUnitStructuralRepresentationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + representation: stringSchema.optional(), + _representation: z.lazy(() => elementSchema).optional(), + attachment: z.lazy(() => attachmentSchema).optional(), + }), + ) + +export const substancePolymerSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('SubstancePolymer'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + class: z.lazy(() => codeableConceptSchema).optional(), + geometry: z.lazy(() => codeableConceptSchema).optional(), + copolymerConnectivity: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + modification: stringSchema.array().optional(), + _modification: z + .lazy(() => elementSchema) + .array() + .optional(), + monomerSet: substancePolymerMonomerSetSchema.array().optional(), + repeat: substancePolymerRepeatSchema.array().optional(), + }), +) + +export const substanceProteinSubunitSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + subunit: integerSchema.optional(), + _subunit: z.lazy(() => elementSchema).optional(), + sequence: stringSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + length: integerSchema.optional(), + _length: z.lazy(() => elementSchema).optional(), + sequenceAttachment: z.lazy(() => attachmentSchema).optional(), + nTerminalModificationId: z.lazy(() => identifierSchema).optional(), + nTerminalModification: stringSchema.optional(), + _nTerminalModification: z.lazy(() => elementSchema).optional(), + cTerminalModificationId: z.lazy(() => identifierSchema).optional(), + cTerminalModification: stringSchema.optional(), + _cTerminalModification: z.lazy(() => elementSchema).optional(), + }), + ) + +export const substanceProteinSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('SubstanceProtein'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequenceType: z.lazy(() => codeableConceptSchema).optional(), + numberOfSubunits: integerSchema.optional(), + _numberOfSubunits: z.lazy(() => elementSchema).optional(), + disulfideLinkage: stringSchema.array().optional(), + _disulfideLinkage: z + .lazy(() => elementSchema) + .array() + .optional(), + subunit: substanceProteinSubunitSchema.array().optional(), + }), +) + +export const substanceReferenceInformationGeneSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + geneSequenceOrigin: z.lazy(() => codeableConceptSchema).optional(), + gene: z.lazy(() => codeableConceptSchema).optional(), + source: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const substanceReferenceInformationGeneElementSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + element: z.lazy(() => identifierSchema).optional(), + source: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const substanceReferenceInformationClassificationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + domain: z.lazy(() => codeableConceptSchema).optional(), + classification: z.lazy(() => codeableConceptSchema).optional(), + subtype: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + source: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const substanceReferenceInformationTargetSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + target: z.lazy(() => identifierSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + interaction: z.lazy(() => codeableConceptSchema).optional(), + organism: z.lazy(() => codeableConceptSchema).optional(), + organismType: z.lazy(() => codeableConceptSchema).optional(), + amountQuantity: z.lazy(() => quantitySchema).optional(), + amountRange: z.lazy(() => rangeSchema).optional(), + amountString: stringSchema.optional(), + _amountString: z.lazy(() => elementSchema).optional(), + amountType: z.lazy(() => codeableConceptSchema).optional(), + source: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['amountQuantity', 'amountRange', 'amountString'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const substanceReferenceInformationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('SubstanceReferenceInformation'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + gene: substanceReferenceInformationGeneSchema.array().optional(), + geneElement: substanceReferenceInformationGeneElementSchema + .array() + .optional(), + classification: substanceReferenceInformationClassificationSchema + .array() + .optional(), + target: substanceReferenceInformationTargetSchema.array().optional(), + }), + ) + +export const substanceSourceMaterialFractionDescriptionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + fraction: stringSchema.optional(), + _fraction: z.lazy(() => elementSchema).optional(), + materialType: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const substanceSourceMaterialOrganismSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + family: z.lazy(() => codeableConceptSchema).optional(), + genus: z.lazy(() => codeableConceptSchema).optional(), + species: z.lazy(() => codeableConceptSchema).optional(), + intraspecificType: z.lazy(() => codeableConceptSchema).optional(), + intraspecificDescription: stringSchema.optional(), + _intraspecificDescription: z.lazy(() => elementSchema).optional(), + author: substanceSourceMaterialOrganismAuthorSchema.array().optional(), + hybrid: substanceSourceMaterialOrganismHybridSchema.optional(), + organismGeneral: + substanceSourceMaterialOrganismOrganismGeneralSchema.optional(), + }), + ) + +export const substanceSourceMaterialPartDescriptionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + part: z.lazy(() => codeableConceptSchema).optional(), + partLocation: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const substanceSourceMaterialOrganismAuthorSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + authorType: z.lazy(() => codeableConceptSchema).optional(), + authorDescription: stringSchema.optional(), + _authorDescription: z.lazy(() => elementSchema).optional(), + }), + ) + +export const substanceSourceMaterialOrganismHybridSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + maternalOrganismId: stringSchema.optional(), + _maternalOrganismId: z.lazy(() => elementSchema).optional(), + maternalOrganismName: stringSchema.optional(), + _maternalOrganismName: z.lazy(() => elementSchema).optional(), + paternalOrganismId: stringSchema.optional(), + _paternalOrganismId: z.lazy(() => elementSchema).optional(), + paternalOrganismName: stringSchema.optional(), + _paternalOrganismName: z.lazy(() => elementSchema).optional(), + hybridType: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const substanceSourceMaterialOrganismOrganismGeneralSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + kingdom: z.lazy(() => codeableConceptSchema).optional(), + phylum: z.lazy(() => codeableConceptSchema).optional(), + class: z.lazy(() => codeableConceptSchema).optional(), + order: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const substanceSourceMaterialSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('SubstanceSourceMaterial'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sourceMaterialClass: z.lazy(() => codeableConceptSchema).optional(), + sourceMaterialType: z.lazy(() => codeableConceptSchema).optional(), + sourceMaterialState: z.lazy(() => codeableConceptSchema).optional(), + organismId: z.lazy(() => identifierSchema).optional(), + organismName: stringSchema.optional(), + _organismName: z.lazy(() => elementSchema).optional(), + parentSubstanceId: z + .lazy(() => identifierSchema) + .array() + .optional(), + parentSubstanceName: stringSchema.array().optional(), + _parentSubstanceName: z + .lazy(() => elementSchema) + .array() + .optional(), + countryOfOrigin: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + geographicalLocation: stringSchema.array().optional(), + _geographicalLocation: z + .lazy(() => elementSchema) + .array() + .optional(), + developmentStage: z.lazy(() => codeableConceptSchema).optional(), + fractionDescription: substanceSourceMaterialFractionDescriptionSchema + .array() + .optional(), + organism: substanceSourceMaterialOrganismSchema.optional(), + partDescription: substanceSourceMaterialPartDescriptionSchema + .array() + .optional(), + }), + ) + +export const substanceSpecificationMoietySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + stereochemistry: z.lazy(() => codeableConceptSchema).optional(), + opticalActivity: z.lazy(() => codeableConceptSchema).optional(), + molecularFormula: stringSchema.optional(), + _molecularFormula: z.lazy(() => elementSchema).optional(), + amountQuantity: z.lazy(() => quantitySchema).optional(), + amountString: stringSchema.optional(), + _amountString: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['amountQuantity', 'amountString'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const substanceSpecificationPropertySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + parameters: stringSchema.optional(), + _parameters: z.lazy(() => elementSchema).optional(), + definingSubstanceReference: z.lazy(() => referenceSchema).optional(), + definingSubstanceCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + amountQuantity: z.lazy(() => quantitySchema).optional(), + amountString: stringSchema.optional(), + _amountString: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'definingSubstanceReference', + 'definingSubstanceCodeableConcept', + ], + required: false, + }, + { names: ['amountQuantity', 'amountString'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const substanceSpecificationStructureSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + stereochemistry: z.lazy(() => codeableConceptSchema).optional(), + opticalActivity: z.lazy(() => codeableConceptSchema).optional(), + molecularFormula: stringSchema.optional(), + _molecularFormula: z.lazy(() => elementSchema).optional(), + molecularFormulaByMoiety: stringSchema.optional(), + _molecularFormulaByMoiety: z.lazy(() => elementSchema).optional(), + isotope: substanceSpecificationStructureIsotopeSchema.array().optional(), + molecularWeight: z + .lazy(() => substanceSpecificationStructureIsotopeMolecularWeightSchema) + .optional(), + source: z + .lazy(() => referenceSchema) + .array() + .optional(), + representation: substanceSpecificationStructureRepresentationSchema + .array() + .optional(), + }), + ) + +export const substanceSpecificationCodeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + statusDate: dateTimeSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + source: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const substanceSpecificationNameSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + preferred: booleanSchema.optional(), + _preferred: z.lazy(() => elementSchema).optional(), + language: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + domain: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + synonym: z + .lazy(() => substanceSpecificationNameSchema) + .array() + .optional(), + translation: z + .lazy(() => substanceSpecificationNameSchema) + .array() + .optional(), + official: substanceSpecificationNameOfficialSchema.array().optional(), + source: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const substanceSpecificationStructureIsotopeMolecularWeightSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => quantitySchema).optional(), + }), + ) + +export const substanceSpecificationRelationshipSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + substanceReference: z.lazy(() => referenceSchema).optional(), + substanceCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + relationship: z.lazy(() => codeableConceptSchema).optional(), + isDefining: booleanSchema.optional(), + _isDefining: z.lazy(() => elementSchema).optional(), + amountQuantity: z.lazy(() => quantitySchema).optional(), + amountRange: z.lazy(() => rangeSchema).optional(), + amountRatio: z.lazy(() => ratioSchema).optional(), + amountString: stringSchema.optional(), + _amountString: z.lazy(() => elementSchema).optional(), + amountRatioLowLimit: z.lazy(() => ratioSchema).optional(), + amountType: z.lazy(() => codeableConceptSchema).optional(), + source: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['substanceReference', 'substanceCodeableConcept'], + required: false, + }, + { + names: [ + 'amountQuantity', + 'amountRange', + 'amountRatio', + 'amountString', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const substanceSpecificationStructureIsotopeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + name: z.lazy(() => codeableConceptSchema).optional(), + substitution: z.lazy(() => codeableConceptSchema).optional(), + halfLife: z.lazy(() => quantitySchema).optional(), + molecularWeight: + substanceSpecificationStructureIsotopeMolecularWeightSchema.optional(), + }), + ) + +export const substanceSpecificationStructureRepresentationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + representation: stringSchema.optional(), + _representation: z.lazy(() => elementSchema).optional(), + attachment: z.lazy(() => attachmentSchema).optional(), + }), + ) + +export const substanceSpecificationNameOfficialSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + authority: z.lazy(() => codeableConceptSchema).optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + }), + ) + +export const substanceSpecificationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('SubstanceSpecification'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + domain: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + source: z + .lazy(() => referenceSchema) + .array() + .optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + moiety: substanceSpecificationMoietySchema.array().optional(), + property: substanceSpecificationPropertySchema.array().optional(), + referenceInformation: z.lazy(() => referenceSchema).optional(), + structure: substanceSpecificationStructureSchema.optional(), + code: substanceSpecificationCodeSchema.array().optional(), + name: substanceSpecificationNameSchema.array().optional(), + molecularWeight: z + .lazy(() => substanceSpecificationStructureIsotopeMolecularWeightSchema) + .array() + .optional(), + relationship: substanceSpecificationRelationshipSchema.array().optional(), + nucleicAcid: z.lazy(() => referenceSchema).optional(), + polymer: z.lazy(() => referenceSchema).optional(), + protein: z.lazy(() => referenceSchema).optional(), + sourceMaterial: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const supplyDeliverySuppliedItemSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['itemCodeableConcept', 'itemReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const supplyDeliverySchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('SupplyDelivery'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z + .enum(['abandoned', 'completed', 'entered-in-error', 'in-progress']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + suppliedItem: supplyDeliverySuppliedItemSchema.optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + supplier: z.lazy(() => referenceSchema).optional(), + destination: z.lazy(() => referenceSchema).optional(), + receiver: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'occurrenceDateTime', + 'occurrencePeriod', + 'occurrenceTiming', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const supplyRequestParameterSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueQuantity', + 'valueRange', + 'valueBoolean', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const supplyRequestSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('SupplyRequest'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z + .enum([ + 'active', + 'cancelled', + 'completed', + 'draft', + 'entered-in-error', + 'suspended', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema), + parameter: supplyRequestParameterSchema.array().optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + supplier: z + .lazy(() => referenceSchema) + .array() + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + deliverFrom: z.lazy(() => referenceSchema).optional(), + deliverTo: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['itemCodeableConcept', 'itemReference'], required: true }, + { + names: [ + 'occurrenceDateTime', + 'occurrencePeriod', + 'occurrenceTiming', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const taskRestrictionSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + repetitions: positiveIntSchema.optional(), + _repetitions: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + recipient: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const taskInputSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + valueMeta: z.lazy(() => metaSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBase64Binary', + 'valueBoolean', + 'valueCanonical', + 'valueCode', + 'valueDate', + 'valueDateTime', + 'valueDecimal', + 'valueId', + 'valueInstant', + 'valueInteger', + 'valueMarkdown', + 'valueOid', + 'valuePositiveInt', + 'valueString', + 'valueTime', + 'valueUnsignedInt', + 'valueUri', + 'valueUrl', + 'valueUuid', + 'valueAddress', + 'valueAge', + 'valueAnnotation', + 'valueAttachment', + 'valueCodeableConcept', + 'valueCoding', + 'valueContactPoint', + 'valueCount', + 'valueDistance', + 'valueDuration', + 'valueHumanName', + 'valueIdentifier', + 'valueMoney', + 'valuePeriod', + 'valueQuantity', + 'valueRange', + 'valueRatio', + 'valueReference', + 'valueSampledData', + 'valueSignature', + 'valueTiming', + 'valueContactDetail', + 'valueContributor', + 'valueDataRequirement', + 'valueExpression', + 'valueParameterDefinition', + 'valueRelatedArtifact', + 'valueTriggerDefinition', + 'valueUsageContext', + 'valueDosage', + 'valueMeta', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const taskOutputSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + valueMeta: z.lazy(() => metaSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBase64Binary', + 'valueBoolean', + 'valueCanonical', + 'valueCode', + 'valueDate', + 'valueDateTime', + 'valueDecimal', + 'valueId', + 'valueInstant', + 'valueInteger', + 'valueMarkdown', + 'valueOid', + 'valuePositiveInt', + 'valueString', + 'valueTime', + 'valueUnsignedInt', + 'valueUri', + 'valueUrl', + 'valueUuid', + 'valueAddress', + 'valueAge', + 'valueAnnotation', + 'valueAttachment', + 'valueCodeableConcept', + 'valueCoding', + 'valueContactPoint', + 'valueCount', + 'valueDistance', + 'valueDuration', + 'valueHumanName', + 'valueIdentifier', + 'valueMoney', + 'valuePeriod', + 'valueQuantity', + 'valueRange', + 'valueRatio', + 'valueReference', + 'valueSampledData', + 'valueSignature', + 'valueTiming', + 'valueContactDetail', + 'valueContributor', + 'valueDataRequirement', + 'valueExpression', + 'valueParameterDefinition', + 'valueRelatedArtifact', + 'valueTriggerDefinition', + 'valueUsageContext', + 'valueDosage', + 'valueMeta', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const taskSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Task'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.optional(), + _instantiatesCanonical: z.lazy(() => elementSchema).optional(), + instantiatesUri: uriSchema.optional(), + _instantiatesUri: z.lazy(() => elementSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'accepted', + 'cancelled', + 'completed', + 'draft', + 'entered-in-error', + 'failed', + 'in-progress', + 'on-hold', + 'ready', + 'received', + 'rejected', + 'requested', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + businessStatus: z.lazy(() => codeableConceptSchema).optional(), + intent: z.enum([ + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + 'unknown', + ]), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + focus: z.lazy(() => referenceSchema).optional(), + for: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + executionPeriod: z.lazy(() => periodSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + lastModified: dateTimeSchema.optional(), + _lastModified: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performerType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + owner: z.lazy(() => referenceSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + reasonCode: z.lazy(() => codeableConceptSchema).optional(), + reasonReference: z.lazy(() => referenceSchema).optional(), + insurance: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + relevantHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + restriction: taskRestrictionSchema.optional(), + input: taskInputSchema.array().optional(), + output: taskOutputSchema.array().optional(), + }), +) + +export const terminologyCapabilitiesSoftwareSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesImplementationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema, + _description: z.lazy(() => elementSchema).optional(), + url: urlSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesCodeSystemSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + uri: canonicalSchema.optional(), + _uri: z.lazy(() => elementSchema).optional(), + version: terminologyCapabilitiesCodeSystemVersionSchema + .array() + .optional(), + subsumption: booleanSchema.optional(), + _subsumption: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesExpansionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + hierarchical: booleanSchema.optional(), + _hierarchical: z.lazy(() => elementSchema).optional(), + paging: booleanSchema.optional(), + _paging: z.lazy(() => elementSchema).optional(), + incomplete: booleanSchema.optional(), + _incomplete: z.lazy(() => elementSchema).optional(), + parameter: terminologyCapabilitiesExpansionParameterSchema + .array() + .optional(), + textFilter: markdownSchema.optional(), + _textFilter: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesValidateCodeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + translations: booleanSchema, + _translations: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesTranslationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + needsMap: booleanSchema, + _needsMap: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesClosureSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + translation: booleanSchema.optional(), + _translation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesCodeSystemVersionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: stringSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + isDefault: booleanSchema.optional(), + _isDefault: z.lazy(() => elementSchema).optional(), + compositional: booleanSchema.optional(), + _compositional: z.lazy(() => elementSchema).optional(), + language: codeSchema.array().optional(), + _language: z + .lazy(() => elementSchema) + .array() + .optional(), + filter: terminologyCapabilitiesCodeSystemVersionFilterSchema + .array() + .optional(), + property: codeSchema.array().optional(), + _property: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const terminologyCapabilitiesExpansionParameterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: codeSchema, + _name: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesCodeSystemVersionFilterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + op: codeSchema.array().min(1), + _op: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const terminologyCapabilitiesSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('TerminologyCapabilities'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema, + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + kind: z.enum(['capability', 'instance', 'requirements']), + _kind: z.lazy(() => elementSchema).optional(), + software: terminologyCapabilitiesSoftwareSchema.optional(), + implementation: terminologyCapabilitiesImplementationSchema.optional(), + lockedDate: booleanSchema.optional(), + _lockedDate: z.lazy(() => elementSchema).optional(), + codeSystem: terminologyCapabilitiesCodeSystemSchema.array().optional(), + expansion: terminologyCapabilitiesExpansionSchema.optional(), + codeSearch: z.enum(['all', 'explicit']).optional(), + _codeSearch: z.lazy(() => elementSchema).optional(), + validateCode: terminologyCapabilitiesValidateCodeSchema.optional(), + translation: terminologyCapabilitiesTranslationSchema.optional(), + closure: terminologyCapabilitiesClosureSchema.optional(), + }), + ) + +export const testReportParticipantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['client', 'server', 'test-engine']), + _type: z.lazy(() => elementSchema).optional(), + uri: uriSchema, + _uri: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testReportSetupSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + action: testReportSetupActionSchema.array().min(1), + }), +) + +export const testReportTestSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + action: testReportTestActionSchema.array().min(1), + }), +) + +export const testReportTeardownSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + action: testReportTeardownActionSchema.array().min(1), + }), + ) + +export const testReportSetupActionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + operation: testReportSetupActionOperationSchema.optional(), + assert: testReportSetupActionAssertSchema.optional(), + }), + ) + +export const testReportTestActionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + operation: z.lazy(() => testReportSetupActionOperationSchema).optional(), + assert: z.lazy(() => testReportSetupActionAssertSchema).optional(), + }), + ) + +export const testReportTeardownActionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + operation: z.lazy(() => testReportSetupActionOperationSchema), + }), + ) + +export const testReportSetupActionOperationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + result: z.enum(['error', 'fail', 'pass', 'skip', 'warning']), + _result: z.lazy(() => elementSchema).optional(), + message: markdownSchema.optional(), + _message: z.lazy(() => elementSchema).optional(), + detail: uriSchema.optional(), + _detail: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testReportSetupActionAssertSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + result: z.enum(['error', 'fail', 'pass', 'skip', 'warning']), + _result: z.lazy(() => elementSchema).optional(), + message: markdownSchema.optional(), + _message: z.lazy(() => elementSchema).optional(), + detail: stringSchema.optional(), + _detail: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testReportSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('TestReport'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + status: z.enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'stopped', + 'waiting', + ]), + _status: z.lazy(() => elementSchema).optional(), + testScript: z.lazy(() => referenceSchema), + result: z.enum(['fail', 'pass', 'pending']), + _result: z.lazy(() => elementSchema).optional(), + score: decimalSchema.optional(), + _score: z.lazy(() => elementSchema).optional(), + tester: stringSchema.optional(), + _tester: z.lazy(() => elementSchema).optional(), + issued: dateTimeSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + participant: testReportParticipantSchema.array().optional(), + setup: testReportSetupSchema.optional(), + test: testReportTestSchema.array().optional(), + teardown: testReportTeardownSchema.optional(), + }), +) + +export const testScriptOriginSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + index: integerSchema, + _index: z.lazy(() => elementSchema).optional(), + profile: z.lazy(() => codingSchema), + }), +) + +export const testScriptDestinationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + index: integerSchema, + _index: z.lazy(() => elementSchema).optional(), + profile: z.lazy(() => codingSchema), + }), + ) + +export const testScriptMetadataSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + link: testScriptMetadataLinkSchema.array().optional(), + capability: testScriptMetadataCapabilitySchema.array().min(1), + }), + ) + +export const testScriptFixtureSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + autocreate: booleanSchema, + _autocreate: z.lazy(() => elementSchema).optional(), + autodelete: booleanSchema, + _autodelete: z.lazy(() => elementSchema).optional(), + resource: z.lazy(() => referenceSchema).optional(), + }), +) + +export const testScriptVariableSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + defaultValue: stringSchema.optional(), + _defaultValue: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + headerField: stringSchema.optional(), + _headerField: z.lazy(() => elementSchema).optional(), + hint: stringSchema.optional(), + _hint: z.lazy(() => elementSchema).optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + sourceId: idSchema.optional(), + _sourceId: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testScriptSetupSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + action: testScriptSetupActionSchema.array().min(1), + }), +) + +export const testScriptTestSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + action: testScriptTestActionSchema.array().min(1), + }), +) + +export const testScriptTeardownSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + action: testScriptTeardownActionSchema.array().min(1), + }), + ) + +export const testScriptMetadataLinkSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testScriptMetadataCapabilitySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + required: booleanSchema, + _required: z.lazy(() => elementSchema).optional(), + validated: booleanSchema, + _validated: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + origin: integerSchema.array().optional(), + _origin: z + .lazy(() => elementSchema) + .array() + .optional(), + destination: integerSchema.optional(), + _destination: z.lazy(() => elementSchema).optional(), + link: uriSchema.array().optional(), + _link: z + .lazy(() => elementSchema) + .array() + .optional(), + capabilities: canonicalSchema, + _capabilities: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testScriptSetupActionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + operation: testScriptSetupActionOperationSchema.optional(), + assert: testScriptSetupActionAssertSchema.optional(), + }), + ) + +export const testScriptTestActionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + operation: z.lazy(() => testScriptSetupActionOperationSchema).optional(), + assert: z.lazy(() => testScriptSetupActionAssertSchema).optional(), + }), + ) + +export const testScriptTeardownActionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + operation: z.lazy(() => testScriptSetupActionOperationSchema), + }), + ) + +export const testScriptSetupActionOperationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codingSchema).optional(), + resource: z + .enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'CodeableConcept', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'EffectEvidenceSynthesis', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'Reference', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceAmount', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]) + .optional(), + _resource: z.lazy(() => elementSchema).optional(), + label: stringSchema.optional(), + _label: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + accept: codeSchema.optional(), + _accept: z.lazy(() => elementSchema).optional(), + contentType: codeSchema.optional(), + _contentType: z.lazy(() => elementSchema).optional(), + destination: integerSchema.optional(), + _destination: z.lazy(() => elementSchema).optional(), + encodeRequestUrl: booleanSchema, + _encodeRequestUrl: z.lazy(() => elementSchema).optional(), + method: z + .enum(['delete', 'get', 'head', 'options', 'patch', 'post', 'put']) + .optional(), + _method: z.lazy(() => elementSchema).optional(), + origin: integerSchema.optional(), + _origin: z.lazy(() => elementSchema).optional(), + params: stringSchema.optional(), + _params: z.lazy(() => elementSchema).optional(), + requestHeader: testScriptSetupActionOperationRequestHeaderSchema + .array() + .optional(), + requestId: idSchema.optional(), + _requestId: z.lazy(() => elementSchema).optional(), + responseId: idSchema.optional(), + _responseId: z.lazy(() => elementSchema).optional(), + sourceId: idSchema.optional(), + _sourceId: z.lazy(() => elementSchema).optional(), + targetId: idSchema.optional(), + _targetId: z.lazy(() => elementSchema).optional(), + url: stringSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testScriptSetupActionAssertSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + label: stringSchema.optional(), + _label: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + direction: z.enum(['request', 'response']).optional(), + _direction: z.lazy(() => elementSchema).optional(), + compareToSourceId: stringSchema.optional(), + _compareToSourceId: z.lazy(() => elementSchema).optional(), + compareToSourceExpression: stringSchema.optional(), + _compareToSourceExpression: z.lazy(() => elementSchema).optional(), + compareToSourcePath: stringSchema.optional(), + _compareToSourcePath: z.lazy(() => elementSchema).optional(), + contentType: codeSchema.optional(), + _contentType: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + headerField: stringSchema.optional(), + _headerField: z.lazy(() => elementSchema).optional(), + minimumId: stringSchema.optional(), + _minimumId: z.lazy(() => elementSchema).optional(), + navigationLinks: booleanSchema.optional(), + _navigationLinks: z.lazy(() => elementSchema).optional(), + operator: z + .enum([ + 'contains', + 'empty', + 'equals', + 'eval', + 'greaterThan', + 'in', + 'lessThan', + 'notContains', + 'notEmpty', + 'notEquals', + 'notIn', + ]) + .optional(), + _operator: z.lazy(() => elementSchema).optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + requestMethod: z + .enum(['delete', 'get', 'head', 'options', 'patch', 'post', 'put']) + .optional(), + _requestMethod: z.lazy(() => elementSchema).optional(), + requestURL: stringSchema.optional(), + _requestURL: z.lazy(() => elementSchema).optional(), + resource: z + .enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'CodeableConcept', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'EffectEvidenceSynthesis', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'Reference', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceAmount', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]) + .optional(), + _resource: z.lazy(() => elementSchema).optional(), + response: z + .enum([ + 'bad', + 'conflict', + 'created', + 'forbidden', + 'gone', + 'methodNotAllowed', + 'noContent', + 'notFound', + 'notModified', + 'okay', + 'preconditionFailed', + 'unprocessable', + ]) + .optional(), + _response: z.lazy(() => elementSchema).optional(), + responseCode: stringSchema.optional(), + _responseCode: z.lazy(() => elementSchema).optional(), + sourceId: idSchema.optional(), + _sourceId: z.lazy(() => elementSchema).optional(), + validateProfileId: idSchema.optional(), + _validateProfileId: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + warningOnly: booleanSchema, + _warningOnly: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testScriptSetupActionOperationRequestHeaderSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + field: stringSchema, + _field: z.lazy(() => elementSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testScriptSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('TestScript'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + origin: testScriptOriginSchema.array().optional(), + destination: testScriptDestinationSchema.array().optional(), + metadata: testScriptMetadataSchema.optional(), + fixture: testScriptFixtureSchema.array().optional(), + profile: z + .lazy(() => referenceSchema) + .array() + .optional(), + variable: testScriptVariableSchema.array().optional(), + setup: testScriptSetupSchema.optional(), + test: testScriptTestSchema.array().optional(), + teardown: testScriptTeardownSchema.optional(), + }), +) + +export const timingRepeatSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + boundsDuration: z.lazy(() => durationSchema).optional(), + boundsRange: z.lazy(() => rangeSchema).optional(), + boundsPeriod: z.lazy(() => periodSchema).optional(), + count: positiveIntSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + countMax: positiveIntSchema.optional(), + _countMax: z.lazy(() => elementSchema).optional(), + duration: decimalSchema.optional(), + _duration: z.lazy(() => elementSchema).optional(), + durationMax: decimalSchema.optional(), + _durationMax: z.lazy(() => elementSchema).optional(), + durationUnit: z.enum(['a', 'd', 'h', 'min', 'mo', 's', 'wk']).optional(), + _durationUnit: z.lazy(() => elementSchema).optional(), + frequency: positiveIntSchema.optional(), + _frequency: z.lazy(() => elementSchema).optional(), + frequencyMax: positiveIntSchema.optional(), + _frequencyMax: z.lazy(() => elementSchema).optional(), + period: decimalSchema.optional(), + _period: z.lazy(() => elementSchema).optional(), + periodMax: decimalSchema.optional(), + _periodMax: z.lazy(() => elementSchema).optional(), + periodUnit: z.enum(['a', 'd', 'h', 'min', 'mo', 's', 'wk']).optional(), + _periodUnit: z.lazy(() => elementSchema).optional(), + dayOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .array() + .optional(), + _dayOfWeek: z + .lazy(() => elementSchema) + .array() + .optional(), + timeOfDay: timeSchema.array().optional(), + _timeOfDay: z + .lazy(() => elementSchema) + .array() + .optional(), + when: z + .enum([ + 'AC', + 'ACD', + 'ACM', + 'ACV', + 'AFT', + 'AFT.early', + 'AFT.late', + 'C', + 'CD', + 'CM', + 'CV', + 'EVE', + 'EVE.early', + 'EVE.late', + 'HS', + 'MORN', + 'MORN.early', + 'MORN.late', + 'NIGHT', + 'NOON', + 'PC', + 'PCD', + 'PCM', + 'PCV', + 'PHS', + 'WAKE', + ]) + .array() + .optional(), + _when: z + .lazy(() => elementSchema) + .array() + .optional(), + offset: unsignedIntSchema.optional(), + _offset: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['boundsDuration', 'boundsRange', 'boundsPeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const timingSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + event: dateTimeSchema.array().optional(), + _event: z + .lazy(() => elementSchema) + .array() + .optional(), + repeat: timingRepeatSchema.optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + }), +) + +export const triggerDefinitionSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'data-access-ended', + 'data-accessed', + 'data-added', + 'data-changed', + 'data-modified', + 'data-removed', + 'named-event', + 'periodic', + ]), + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + timingReference: z.lazy(() => referenceSchema).optional(), + timingDate: dateSchema.optional(), + _timingDate: z.lazy(() => elementSchema).optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + data: z + .lazy(() => dataRequirementSchema) + .array() + .optional(), + condition: z.lazy(() => expressionSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'timingTiming', + 'timingReference', + 'timingDate', + 'timingDateTime', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const usageContextSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codingSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueQuantity', + 'valueRange', + 'valueReference', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const valueSetComposeSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + lockedDate: dateSchema.optional(), + _lockedDate: z.lazy(() => elementSchema).optional(), + inactive: booleanSchema.optional(), + _inactive: z.lazy(() => elementSchema).optional(), + include: valueSetComposeIncludeSchema.array().min(1), + exclude: z + .lazy(() => valueSetComposeIncludeSchema) + .array() + .optional(), + }), +) + +export const valueSetExpansionSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: uriSchema.optional(), + _identifier: z.lazy(() => elementSchema).optional(), + timestamp: dateTimeSchema, + _timestamp: z.lazy(() => elementSchema).optional(), + total: integerSchema.optional(), + _total: z.lazy(() => elementSchema).optional(), + offset: integerSchema.optional(), + _offset: z.lazy(() => elementSchema).optional(), + parameter: valueSetExpansionParameterSchema.array().optional(), + contains: valueSetExpansionContainsSchema.array().optional(), + }), +) + +export const valueSetComposeIncludeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + concept: valueSetComposeIncludeConceptSchema.array().optional(), + filter: valueSetComposeIncludeFilterSchema.array().optional(), + valueSet: canonicalSchema.array().optional(), + _valueSet: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const valueSetExpansionParameterSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueString', + 'valueBoolean', + 'valueInteger', + 'valueDecimal', + 'valueUri', + 'valueCode', + 'valueDateTime', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const valueSetExpansionContainsSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + abstract: booleanSchema.optional(), + _abstract: z.lazy(() => elementSchema).optional(), + inactive: booleanSchema.optional(), + _inactive: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + designation: z + .lazy(() => valueSetComposeIncludeConceptDesignationSchema) + .array() + .optional(), + contains: z + .lazy(() => valueSetExpansionContainsSchema) + .array() + .optional(), + }), + ) + +export const valueSetComposeIncludeConceptSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + designation: valueSetComposeIncludeConceptDesignationSchema + .array() + .optional(), + }), + ) + +export const valueSetComposeIncludeFilterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + property: codeSchema, + _property: z.lazy(() => elementSchema).optional(), + op: z.enum([ + '=', + 'descendent-of', + 'exists', + 'generalizes', + 'in', + 'is-a', + 'is-not-a', + 'not-in', + 'regex', + ]), + _op: z.lazy(() => elementSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const valueSetComposeIncludeConceptDesignationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + use: z.lazy(() => codingSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const valueSetSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('ValueSet'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + immutable: booleanSchema.optional(), + _immutable: z.lazy(() => elementSchema).optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + compose: valueSetComposeSchema.optional(), + expansion: valueSetExpansionSchema.optional(), + }), +) + +export const verificationResultPrimarySourceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + who: z.lazy(() => referenceSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + communicationMethod: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + validationStatus: z.lazy(() => codeableConceptSchema).optional(), + validationDate: dateTimeSchema.optional(), + _validationDate: z.lazy(() => elementSchema).optional(), + canPushUpdates: z.lazy(() => codeableConceptSchema).optional(), + pushTypeAvailable: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), + ) + +export const verificationResultAttestationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + who: z.lazy(() => referenceSchema).optional(), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + communicationMethod: z.lazy(() => codeableConceptSchema).optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + sourceIdentityCertificate: stringSchema.optional(), + _sourceIdentityCertificate: z.lazy(() => elementSchema).optional(), + proxyIdentityCertificate: stringSchema.optional(), + _proxyIdentityCertificate: z.lazy(() => elementSchema).optional(), + proxySignature: z.lazy(() => signatureSchema).optional(), + sourceSignature: z.lazy(() => signatureSchema).optional(), + }), + ) + +export const verificationResultValidatorSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + organization: z.lazy(() => referenceSchema), + identityCertificate: stringSchema.optional(), + _identityCertificate: z.lazy(() => elementSchema).optional(), + attestationSignature: z.lazy(() => signatureSchema).optional(), + }), + ) + +export const verificationResultSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('VerificationResult'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + target: z + .lazy(() => referenceSchema) + .array() + .optional(), + targetLocation: stringSchema.array().optional(), + _targetLocation: z + .lazy(() => elementSchema) + .array() + .optional(), + need: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum([ + 'attested', + 'in-process', + 'req-revalid', + 'reval-fail', + 'val-fail', + 'validated', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusDate: dateTimeSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), + validationType: z.lazy(() => codeableConceptSchema).optional(), + validationProcess: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + frequency: z.lazy(() => timingSchema).optional(), + lastPerformed: dateTimeSchema.optional(), + _lastPerformed: z.lazy(() => elementSchema).optional(), + nextScheduled: dateSchema.optional(), + _nextScheduled: z.lazy(() => elementSchema).optional(), + failureAction: z.lazy(() => codeableConceptSchema).optional(), + primarySource: verificationResultPrimarySourceSchema.array().optional(), + attestation: verificationResultAttestationSchema.optional(), + validator: verificationResultValidatorSchema.array().optional(), + }), + ) + +export const visionPrescriptionLensSpecificationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + product: z.lazy(() => codeableConceptSchema), + eye: z.enum(['left', 'right']), + _eye: z.lazy(() => elementSchema).optional(), + sphere: decimalSchema.optional(), + _sphere: z.lazy(() => elementSchema).optional(), + cylinder: decimalSchema.optional(), + _cylinder: z.lazy(() => elementSchema).optional(), + axis: integerSchema.optional(), + _axis: z.lazy(() => elementSchema).optional(), + prism: visionPrescriptionLensSpecificationPrismSchema.array().optional(), + add: decimalSchema.optional(), + _add: z.lazy(() => elementSchema).optional(), + power: decimalSchema.optional(), + _power: z.lazy(() => elementSchema).optional(), + backCurve: decimalSchema.optional(), + _backCurve: z.lazy(() => elementSchema).optional(), + diameter: decimalSchema.optional(), + _diameter: z.lazy(() => elementSchema).optional(), + duration: z.lazy(() => quantitySchema).optional(), + color: stringSchema.optional(), + _color: z.lazy(() => elementSchema).optional(), + brand: stringSchema.optional(), + _brand: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), + ) + +export const visionPrescriptionLensSpecificationPrismSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + amount: decimalSchema, + _amount: z.lazy(() => elementSchema).optional(), + base: z.enum(['down', 'in', 'out', 'up']), + _base: z.lazy(() => elementSchema).optional(), + }), + ) + +export const visionPrescriptionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('VisionPrescription'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + dateWritten: dateTimeSchema, + _dateWritten: z.lazy(() => elementSchema).optional(), + prescriber: z.lazy(() => referenceSchema), + lensSpecification: visionPrescriptionLensSpecificationSchema + .array() + .min(1), + }), + ) + +export const base64BinarySchema: z.ZodType = z + .string() + .regex( + new RegExp('^[0-9a-zA-Z+/= \\t\\n\\r]*$'), + 'Expected a FHIR base64Binary.', + ) + .refine((value) => value.replace(/[ \t\n\r]/gu, '').length % 4 === 0, { + message: 'Expected a FHIR base64Binary.', + }) + +export const booleanSchema: z.ZodType = z.boolean() + +export const canonicalSchema: z.ZodType = z + .string() + .regex(new RegExp('^(?:[^ \\t\\n\\r]*)$'), 'Expected a FHIR canonical.') + +export const codeSchema: z.ZodType = z + .string() + .regex( + new RegExp('^(?:[^ \\t\\n\\r]+([ \\t\\n\\r][^ \\t\\n\\r]+)*)$'), + 'Expected a FHIR code.', + ) + +export const dateSchema: z.ZodType = z + .string() + .regex( + new RegExp( + '^(?:([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1]))?)?)$', + ), + 'Expected a FHIR date.', + ) + +export const dateTimeSchema: z.ZodType = z + .string() + .regex( + new RegExp( + '^(?:([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00)))?)?)?)$', + ), + 'Expected a FHIR dateTime.', + ) + +export const decimalSchema: z.ZodType = z + .number() + .refine( + (value) => + new RegExp('^(?:-?(0|[1-9][0-9]*)(\\.[0-9]+)?([eE][+-]?[0-9]+)?)$').test( + String(value), + ), + { + message: 'Expected a valid FHIR decimal.', + }, + ) + +export const idSchema: z.ZodType = z + .string() + .regex(new RegExp('^(?:[A-Za-z0-9\\-\\.]{1,64})$'), 'Expected a FHIR id.') + +export const instantSchema: z.ZodType = z + .string() + .regex( + new RegExp( + '^(?:([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00)))$', + ), + 'Expected a FHIR instant.', + ) + +export const integerSchema: z.ZodType = z + .number() + .int() + .min(-2147483648) + .max(2147483647) + .refine( + (value) => new RegExp('^(?:-?([0]|([1-9][0-9]*)))$').test(String(value)), + { + message: 'Expected a valid FHIR integer.', + }, + ) + +export const markdownSchema: z.ZodType = z + .string() + .regex(new RegExp('^(?:[\\s\\S]+)$'), 'Expected a FHIR markdown.') + +export const oidSchema: z.ZodType = z + .string() + .regex( + new RegExp('^(?:urn:oid:[0-2](\\.(0|[1-9][0-9]*))+)$'), + 'Expected a FHIR oid.', + ) + +export const positiveIntSchema: z.ZodType = z + .number() + .int() + .min(-2147483648) + .max(2147483647) + .refine((value) => new RegExp('^(?:[1-9][0-9]*)$').test(String(value)), { + message: 'Expected a valid FHIR positiveInt.', + }) + +export const stringSchema: z.ZodType = z + .string() + .regex(new RegExp('^(?:[\\s\\S]+)$'), 'Expected a FHIR string.') + +export const timeSchema: z.ZodType = z + .string() + .regex( + new RegExp( + '^(?:([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?)$', + ), + 'Expected a FHIR time.', + ) + +export const unsignedIntSchema: z.ZodType = z + .number() + .int() + .min(-2147483648) + .max(2147483647) + .refine( + (value) => new RegExp('^(?:[0]|([1-9][0-9]*))$').test(String(value)), + { + message: 'Expected a valid FHIR unsignedInt.', + }, + ) + +export const uriSchema: z.ZodType = z + .string() + .regex(new RegExp('^(?:[^ \\t\\n\\r]*)$'), 'Expected a FHIR uri.') + +export const urlSchema: z.ZodType = z + .string() + .regex(new RegExp('^(?:[^ \\t\\n\\r]*)$'), 'Expected a FHIR url.') + +export const uuidSchema: z.ZodType = z + .string() + .regex( + new RegExp( + '^(?:urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$', + ), + 'Expected a FHIR uuid.', + ) + +export const xhtmlSchema: z.ZodType = z.string() + +const modelledResources: Readonly> = { + Account: accountSchema, + ActivityDefinition: activityDefinitionSchema, + AdverseEvent: adverseEventSchema, + AllergyIntolerance: allergyIntoleranceSchema, + Appointment: appointmentSchema, + AppointmentResponse: appointmentResponseSchema, + AuditEvent: auditEventSchema, + Basic: basicSchema, + Binary: binarySchema, + BiologicallyDerivedProduct: biologicallyDerivedProductSchema, + BodyStructure: bodyStructureSchema, + Bundle: bundleSchema, + CapabilityStatement: capabilityStatementSchema, + CarePlan: carePlanSchema, + CareTeam: careTeamSchema, + CatalogEntry: catalogEntrySchema, + ChargeItem: chargeItemSchema, + ChargeItemDefinition: chargeItemDefinitionSchema, + Claim: claimSchema, + ClaimResponse: claimResponseSchema, + ClinicalImpression: clinicalImpressionSchema, + CodeSystem: codeSystemSchema, + Communication: communicationSchema, + CommunicationRequest: communicationRequestSchema, + CompartmentDefinition: compartmentDefinitionSchema, + Composition: compositionSchema, + ConceptMap: conceptMapSchema, + Condition: conditionSchema, + Consent: consentSchema, + Contract: contractSchema, + Coverage: coverageSchema, + CoverageEligibilityRequest: coverageEligibilityRequestSchema, + CoverageEligibilityResponse: coverageEligibilityResponseSchema, + DetectedIssue: detectedIssueSchema, + Device: deviceSchema, + DeviceDefinition: deviceDefinitionSchema, + DeviceMetric: deviceMetricSchema, + DeviceRequest: deviceRequestSchema, + DeviceUseStatement: deviceUseStatementSchema, + DiagnosticReport: diagnosticReportSchema, + DocumentManifest: documentManifestSchema, + DocumentReference: documentReferenceSchema, + EffectEvidenceSynthesis: effectEvidenceSynthesisSchema, + Encounter: encounterSchema, + Endpoint: endpointSchema, + EnrollmentRequest: enrollmentRequestSchema, + EnrollmentResponse: enrollmentResponseSchema, + EpisodeOfCare: episodeOfCareSchema, + EventDefinition: eventDefinitionSchema, + Evidence: evidenceSchema, + EvidenceVariable: evidenceVariableSchema, + ExampleScenario: exampleScenarioSchema, + ExplanationOfBenefit: explanationOfBenefitSchema, + FamilyMemberHistory: familyMemberHistorySchema, + Flag: flagSchema, + Goal: goalSchema, + GraphDefinition: graphDefinitionSchema, + Group: groupSchema, + GuidanceResponse: guidanceResponseSchema, + HealthcareService: healthcareServiceSchema, + ImagingStudy: imagingStudySchema, + Immunization: immunizationSchema, + ImmunizationEvaluation: immunizationEvaluationSchema, + ImmunizationRecommendation: immunizationRecommendationSchema, + ImplementationGuide: implementationGuideSchema, + InsurancePlan: insurancePlanSchema, + Invoice: invoiceSchema, + Library: librarySchema, + Linkage: linkageSchema, + List: listSchema, + Location: locationSchema, + Measure: measureSchema, + MeasureReport: measureReportSchema, + Media: mediaSchema, + Medication: medicationSchema, + MedicationAdministration: medicationAdministrationSchema, + MedicationDispense: medicationDispenseSchema, + MedicationKnowledge: medicationKnowledgeSchema, + MedicationRequest: medicationRequestSchema, + MedicationStatement: medicationStatementSchema, + MedicinalProduct: medicinalProductSchema, + MedicinalProductAuthorization: medicinalProductAuthorizationSchema, + MedicinalProductContraindication: medicinalProductContraindicationSchema, + MedicinalProductIndication: medicinalProductIndicationSchema, + MedicinalProductIngredient: medicinalProductIngredientSchema, + MedicinalProductInteraction: medicinalProductInteractionSchema, + MedicinalProductManufactured: medicinalProductManufacturedSchema, + MedicinalProductPackaged: medicinalProductPackagedSchema, + MedicinalProductPharmaceutical: medicinalProductPharmaceuticalSchema, + MedicinalProductUndesirableEffect: medicinalProductUndesirableEffectSchema, + MessageDefinition: messageDefinitionSchema, + MessageHeader: messageHeaderSchema, + MolecularSequence: molecularSequenceSchema, + NamingSystem: namingSystemSchema, + NutritionOrder: nutritionOrderSchema, + Observation: observationSchema, + ObservationDefinition: observationDefinitionSchema, + OperationDefinition: operationDefinitionSchema, + OperationOutcome: operationOutcomeSchema, + Organization: organizationSchema, + OrganizationAffiliation: organizationAffiliationSchema, + Parameters: parametersSchema, + Patient: patientSchema, + PaymentNotice: paymentNoticeSchema, + PaymentReconciliation: paymentReconciliationSchema, + Person: personSchema, + PlanDefinition: planDefinitionSchema, + Practitioner: practitionerSchema, + PractitionerRole: practitionerRoleSchema, + Procedure: procedureSchema, + Provenance: provenanceSchema, + Questionnaire: questionnaireSchema, + QuestionnaireResponse: questionnaireResponseSchema, + RelatedPerson: relatedPersonSchema, + RequestGroup: requestGroupSchema, + ResearchDefinition: researchDefinitionSchema, + ResearchElementDefinition: researchElementDefinitionSchema, + ResearchStudy: researchStudySchema, + ResearchSubject: researchSubjectSchema, + RiskAssessment: riskAssessmentSchema, + RiskEvidenceSynthesis: riskEvidenceSynthesisSchema, + Schedule: scheduleSchema, + SearchParameter: searchParameterSchema, + ServiceRequest: serviceRequestSchema, + Slot: slotSchema, + Specimen: specimenSchema, + SpecimenDefinition: specimenDefinitionSchema, + StructureDefinition: structureDefinitionSchema, + StructureMap: structureMapSchema, + Subscription: subscriptionSchema, + Substance: substanceSchema, + SubstanceNucleicAcid: substanceNucleicAcidSchema, + SubstancePolymer: substancePolymerSchema, + SubstanceProtein: substanceProteinSchema, + SubstanceReferenceInformation: substanceReferenceInformationSchema, + SubstanceSourceMaterial: substanceSourceMaterialSchema, + SubstanceSpecification: substanceSpecificationSchema, + SupplyDelivery: supplyDeliverySchema, + SupplyRequest: supplyRequestSchema, + Task: taskSchema, + TerminologyCapabilities: terminologyCapabilitiesSchema, + TestReport: testReportSchema, + TestScript: testScriptSchema, + ValueSet: valueSetSchema, + VerificationResult: verificationResultSchema, + VisionPrescription: visionPrescriptionSchema, +} + +const dispatchingResource = new WeakSet() + +export const resourceSchema: z.ZodType = z + .custom( + (value) => + typeof value === 'object' && + value !== null && + typeof (value as { resourceType?: unknown }).resourceType === 'string', + { + message: 'Expected a FHIR resource carrying a resourceType.', + abort: true, + }, + ) + .superRefine((value, ctx) => { + const modelled = modelledResources[value.resourceType] + // A resource type the release defines but this package does not model keeps the open + // shape; reporting it as invalid would reject a conformant bundle. + if (modelled === undefined) return + // JSON cannot describe a resource that contains itself, but an in-memory object can, + // and re-entering one would recur until the stack ran out. + if (dispatchingResource.has(value)) return + dispatchingResource.add(value) + try { + const result = modelled.safeParse(value) + if (result.success) return + // Re-raised rather than summarised, so a fault inside the resource still names the + // element that carries it. + for (const issue of result.error.issues) ctx.addIssue({ ...issue }) + } finally { + dispatchingResource.delete(value) + } + }) + +/** + * Invariants the release states for these paths that this module does not check. + * + * Structural rules are enforced; a rule expressed only as FHIRPath is not evaluated here. + * Normative conformance is established by the official FHIR Validator, not by this module. + */ +export const UNCHECKED_CONSTRAINTS: Readonly< + Record +> = { + Account: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + ActivityDefinition: ['adf-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + AdverseEvent: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Age: ['age-1', 'qty-3'], + AllergyIntolerance: [ + 'ait-1', + 'ait-2', + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + ], + Appointment: [ + 'app-2', + 'app-3', + 'app-4', + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + ], + 'Appointment.participant': ['app-1'], + AppointmentResponse: ['apr-1', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Attachment: ['att-1'], + AuditEvent: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'AuditEvent.entity': ['sev-1'], + Basic: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + BiologicallyDerivedProduct: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + BodyStructure: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Bundle: [ + 'bdl-1', + 'bdl-10', + 'bdl-11', + 'bdl-12', + 'bdl-2', + 'bdl-3', + 'bdl-4', + 'bdl-7', + 'bdl-9', + ], + 'Bundle.entry': ['bdl-5', 'bdl-8'], + CapabilityStatement: [ + 'cpb-0', + 'cpb-1', + 'cpb-14', + 'cpb-15', + 'cpb-16', + 'cpb-2', + 'cpb-3', + 'cpb-7', + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + ], + 'CapabilityStatement.rest': ['cpb-9'], + 'CapabilityStatement.rest.resource': ['cpb-12'], + CarePlan: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'CarePlan.activity': ['cpl-3'], + CareTeam: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'CareTeam.participant': ['ctm-1'], + CatalogEntry: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + ChargeItem: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + ChargeItemDefinition: ['cid-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Claim: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + ClaimResponse: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + ClinicalImpression: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + CodeSystem: ['csd-0', 'csd-1', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Communication: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + CommunicationRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + CompartmentDefinition: ['cpd-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Composition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Composition.section': ['cmp-1', 'cmp-2'], + ConceptMap: ['cmd-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ConceptMap.group.element.target': ['cmd-1'], + 'ConceptMap.group.unmapped': ['cmd-2', 'cmd-3'], + Condition: [ + 'con-3', + 'con-4', + 'con-5', + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + ], + 'Condition.evidence': ['con-2'], + 'Condition.stage': ['con-1'], + Consent: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'ppc-1', + 'ppc-2', + 'ppc-3', + 'ppc-4', + 'ppc-5', + ], + ContactPoint: ['cpt-2'], + Contract: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Count: ['cnt-3', 'qty-3'], + Coverage: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + CoverageEligibilityRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + CoverageEligibilityResponse: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'CoverageEligibilityResponse.insurance.item': ['ces-1'], + 'DataRequirement.codeFilter': ['drq-1'], + 'DataRequirement.dateFilter': ['drq-2'], + DetectedIssue: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Device: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + DeviceDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + DeviceMetric: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + DeviceRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + DeviceUseStatement: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + DiagnosticReport: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Distance: ['dis-1', 'qty-3'], + DocumentManifest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + DocumentReference: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Duration: ['drt-1', 'qty-3'], + EffectEvidenceSynthesis: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'ees-0', + ], + ElementDefinition: [ + 'eld-11', + 'eld-13', + 'eld-14', + 'eld-15', + 'eld-16', + 'eld-18', + 'eld-19', + 'eld-2', + 'eld-20', + 'eld-22', + 'eld-5', + 'eld-6', + 'eld-7', + 'eld-8', + ], + 'ElementDefinition.binding': ['eld-12'], + 'ElementDefinition.constraint': ['eld-21'], + 'ElementDefinition.max': ['eld-3'], + 'ElementDefinition.slicing': ['eld-1'], + 'ElementDefinition.type': ['eld-17', 'eld-4'], + Encounter: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Endpoint: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + EnrollmentRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + EnrollmentResponse: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + EpisodeOfCare: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + EventDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'evd-0'], + Evidence: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'evi-0'], + EvidenceVariable: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'evv-0'], + ExampleScenario: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'esc-0'], + ExplanationOfBenefit: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Expression: ['exp-1'], + FamilyMemberHistory: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'fhs-1', + 'fhs-2', + ], + Flag: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Goal: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Goal.target': ['gol-1'], + GraphDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'gdf-0'], + Group: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'grp-1'], + GuidanceResponse: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + HealthcareService: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + ImagingStudy: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Immunization: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Immunization.education': ['imm-1'], + ImmunizationEvaluation: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + ImmunizationRecommendation: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ImmunizationRecommendation.recommendation': ['imr-1'], + ImplementationGuide: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'ig-0', + 'ig-2', + ], + 'ImplementationGuide.definition': ['ig-1'], + InsurancePlan: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'ipn-1'], + Invoice: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Library: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'lib-0'], + Linkage: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'lnk-1'], + List: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'lst-1', + 'lst-2', + 'lst-3', + ], + Location: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Measure: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'mea-0', 'mea-1'], + MeasureReport: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'mrp-1', + 'mrp-2', + ], + Media: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Medication: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + MedicationAdministration: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'MedicationAdministration.dosage': ['mad-1'], + MedicationDispense: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'mdd-1'], + MedicationKnowledge: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + MedicationRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + MedicationStatement: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + MedicinalProduct: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + MedicinalProductAuthorization: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + MedicinalProductContraindication: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + ], + MedicinalProductIndication: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + MedicinalProductIngredient: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + MedicinalProductInteraction: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + MedicinalProductManufactured: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + MedicinalProductPackaged: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + MedicinalProductPharmaceutical: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + MedicinalProductUndesirableEffect: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + ], + MessageDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'msd-0'], + 'MessageDefinition.focus': ['md-1'], + MessageHeader: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + MolecularSequence: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'msq-3'], + 'MolecularSequence.referenceSeq': ['msq-5', 'msq-6'], + NamingSystem: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'nsd-0', + 'nsd-1', + 'nsd-2', + ], + 'Narrative.div': ['txt-1', 'txt-2'], + NutritionOrder: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'nor-1'], + Observation: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'obs-7'], + ObservationDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + OperationDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'opd-0'], + 'OperationDefinition.parameter': ['opd-1', 'opd-2', 'opd-3'], + OperationOutcome: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Organization: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'org-1'], + 'Organization.address': ['org-2'], + 'Organization.telecom': ['org-3'], + OrganizationAffiliation: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Parameters.parameter': ['inv-1'], + Patient: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Patient.contact': ['pat-1'], + PaymentNotice: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + PaymentReconciliation: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Person: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + PlanDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'pdf-0'], + Practitioner: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + PractitionerRole: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Procedure: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Provenance: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Quantity: ['qty-3'], + Questionnaire: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'que-0', + 'que-2', + ], + 'Questionnaire.item': [ + 'que-1', + 'que-10', + 'que-11', + 'que-12', + 'que-13', + 'que-3', + 'que-4', + 'que-5', + 'que-6', + 'que-8', + 'que-9', + ], + 'Questionnaire.item.enableWhen': ['que-7'], + QuestionnaireResponse: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'QuestionnaireResponse.item': ['qrs-1'], + Range: ['rng-2'], + Ratio: ['rat-1'], + Reference: ['ref-1'], + RelatedPerson: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + RequestGroup: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'RequestGroup.action': ['rqg-1'], + ResearchDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'rsd-0'], + ResearchElementDefinition: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'red-0', + ], + ResearchStudy: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + ResearchSubject: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + RiskAssessment: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'RiskAssessment.prediction': ['ras-2'], + 'RiskAssessment.prediction.probability[x]': ['ras-1'], + RiskEvidenceSynthesis: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'rvs-0'], + Schedule: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + SearchParameter: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'spd-0', + 'spd-1', + 'spd-2', + ], + ServiceRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'prr-1'], + Slot: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Specimen: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + SpecimenDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + StructureDefinition: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'sdf-0', + 'sdf-1', + 'sdf-11', + 'sdf-14', + 'sdf-15', + 'sdf-15a', + 'sdf-16', + 'sdf-17', + 'sdf-18', + 'sdf-19', + 'sdf-21', + 'sdf-22', + 'sdf-23', + 'sdf-4', + 'sdf-5', + 'sdf-6', + 'sdf-9', + ], + 'StructureDefinition.differential': ['sdf-20', 'sdf-8a'], + 'StructureDefinition.mapping': ['sdf-2'], + 'StructureDefinition.snapshot': ['sdf-3', 'sdf-8', 'sdf-8b'], + 'StructureDefinition.snapshot.element': ['sdf-10'], + StructureMap: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'smp-0'], + 'StructureMap.group.rule.target': ['smp-1', 'smp-2'], + Subscription: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Substance: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + SubstanceNucleicAcid: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + SubstancePolymer: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + SubstanceProtein: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + SubstanceReferenceInformation: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + SubstanceSourceMaterial: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + SubstanceSpecification: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + SupplyDelivery: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + SupplyRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + Task: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'inv-1'], + TerminologyCapabilities: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'tcp-0', + 'tcp-2', + 'tcp-3', + 'tcp-4', + 'tcp-5', + ], + 'TerminologyCapabilities.codeSystem': ['tcp-1'], + TestReport: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'TestReport.setup.action': ['inv-1'], + 'TestReport.test.action': ['inv-2'], + TestScript: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'tst-0'], + 'TestScript.metadata': ['tst-4'], + 'TestScript.setup.action': ['tst-1'], + 'TestScript.setup.action.assert': ['tst-10', 'tst-12', 'tst-5'], + 'TestScript.setup.action.operation': ['tst-7'], + 'TestScript.teardown.action.operation': ['tst-9'], + 'TestScript.test.action': ['tst-2'], + 'TestScript.test.action.assert': ['tst-11', 'tst-13', 'tst-6'], + 'TestScript.test.action.operation': ['tst-8'], + 'TestScript.variable': ['tst-3'], + 'Timing.repeat': [ + 'tim-1', + 'tim-10', + 'tim-2', + 'tim-4', + 'tim-5', + 'tim-6', + 'tim-7', + 'tim-8', + 'tim-9', + ], + TriggerDefinition: ['trd-1', 'trd-2', 'trd-3'], + ValueSet: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'vsd-0'], + 'ValueSet.compose.include': ['vsd-1', 'vsd-2', 'vsd-3'], + 'ValueSet.expansion.contains': ['vsd-10', 'vsd-6', 'vsd-9'], + VerificationResult: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + VisionPrescription: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], +} diff --git a/packages/fhir/src/zod/r4b/index.ts b/packages/fhir/src/zod/r4b/index.ts new file mode 100644 index 00000000..d320f7a3 --- /dev/null +++ b/packages/fhir/src/zod/r4b/index.ts @@ -0,0 +1,9 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +export * from './schemas.js' diff --git a/packages/fhir/src/zod/r4b/schemas.ts b/packages/fhir/src/zod/r4b/schemas.ts new file mode 100644 index 00000000..d0071c4f --- /dev/null +++ b/packages/fhir/src/zod/r4b/schemas.ts @@ -0,0 +1,32703 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// +// GENERATED FILE. Run `npm run generate:zod` after changing the generator or the release pin. +// + +/* eslint-disable sonarjs/regex-complexity, sonarjs/concise-regex, sonarjs/single-char-in-character-classes, sonarjs/single-character-alternation */ + +import type { + Account as FhirAccount, + AccountCoverage as FhirAccountCoverage, + AccountGuarantor as FhirAccountGuarantor, + ActivityDefinition as FhirActivityDefinition, + ActivityDefinitionDynamicValue as FhirActivityDefinitionDynamicValue, + ActivityDefinitionParticipant as FhirActivityDefinitionParticipant, + Address as FhirAddress, + AdministrableProductDefinition as FhirAdministrableProductDefinition, + AdministrableProductDefinitionProperty as FhirAdministrableProductDefinitionProperty, + AdministrableProductDefinitionRouteOfAdministration as FhirAdministrableProductDefinitionRouteOfAdministration, + AdministrableProductDefinitionRouteOfAdministrationTargetSpecies as FhirAdministrableProductDefinitionRouteOfAdministrationTargetSpecies, + AdministrableProductDefinitionRouteOfAdministrationTargetSpeciesWithdrawalPeriod as FhirAdministrableProductDefinitionRouteOfAdministrationTargetSpeciesWithdrawalPeriod, + AdverseEvent as FhirAdverseEvent, + AdverseEventSuspectEntity as FhirAdverseEventSuspectEntity, + AdverseEventSuspectEntityCausality as FhirAdverseEventSuspectEntityCausality, + Age as FhirAge, + AllergyIntolerance as FhirAllergyIntolerance, + AllergyIntoleranceReaction as FhirAllergyIntoleranceReaction, + Annotation as FhirAnnotation, + Appointment as FhirAppointment, + AppointmentParticipant as FhirAppointmentParticipant, + AppointmentResponse as FhirAppointmentResponse, + Attachment as FhirAttachment, + AuditEvent as FhirAuditEvent, + AuditEventAgent as FhirAuditEventAgent, + AuditEventAgentNetwork as FhirAuditEventAgentNetwork, + AuditEventEntity as FhirAuditEventEntity, + AuditEventEntityDetail as FhirAuditEventEntityDetail, + AuditEventSource as FhirAuditEventSource, + Basic as FhirBasic, + Binary as FhirBinary, + BiologicallyDerivedProduct as FhirBiologicallyDerivedProduct, + BiologicallyDerivedProductCollection as FhirBiologicallyDerivedProductCollection, + BiologicallyDerivedProductManipulation as FhirBiologicallyDerivedProductManipulation, + BiologicallyDerivedProductProcessing as FhirBiologicallyDerivedProductProcessing, + BiologicallyDerivedProductStorage as FhirBiologicallyDerivedProductStorage, + BodyStructure as FhirBodyStructure, + Bundle as FhirBundle, + BundleEntry as FhirBundleEntry, + BundleEntryRequest as FhirBundleEntryRequest, + BundleEntryResponse as FhirBundleEntryResponse, + BundleEntrySearch as FhirBundleEntrySearch, + BundleLink as FhirBundleLink, + CapabilityStatement as FhirCapabilityStatement, + CapabilityStatementDocument as FhirCapabilityStatementDocument, + CapabilityStatementImplementation as FhirCapabilityStatementImplementation, + CapabilityStatementMessaging as FhirCapabilityStatementMessaging, + CapabilityStatementMessagingEndpoint as FhirCapabilityStatementMessagingEndpoint, + CapabilityStatementMessagingSupportedMessage as FhirCapabilityStatementMessagingSupportedMessage, + CapabilityStatementRest as FhirCapabilityStatementRest, + CapabilityStatementRestInteraction as FhirCapabilityStatementRestInteraction, + CapabilityStatementRestResource as FhirCapabilityStatementRestResource, + CapabilityStatementRestResourceInteraction as FhirCapabilityStatementRestResourceInteraction, + CapabilityStatementRestResourceOperation as FhirCapabilityStatementRestResourceOperation, + CapabilityStatementRestResourceSearchParam as FhirCapabilityStatementRestResourceSearchParam, + CapabilityStatementRestSecurity as FhirCapabilityStatementRestSecurity, + CapabilityStatementSoftware as FhirCapabilityStatementSoftware, + CarePlan as FhirCarePlan, + CarePlanActivity as FhirCarePlanActivity, + CarePlanActivityDetail as FhirCarePlanActivityDetail, + CareTeam as FhirCareTeam, + CareTeamParticipant as FhirCareTeamParticipant, + CatalogEntry as FhirCatalogEntry, + CatalogEntryRelatedEntry as FhirCatalogEntryRelatedEntry, + ChargeItem as FhirChargeItem, + ChargeItemDefinition as FhirChargeItemDefinition, + ChargeItemDefinitionApplicability as FhirChargeItemDefinitionApplicability, + ChargeItemDefinitionPropertyGroup as FhirChargeItemDefinitionPropertyGroup, + ChargeItemDefinitionPropertyGroupPriceComponent as FhirChargeItemDefinitionPropertyGroupPriceComponent, + ChargeItemPerformer as FhirChargeItemPerformer, + Citation as FhirCitation, + CitationCitedArtifact as FhirCitationCitedArtifact, + CitationCitedArtifactAbstract as FhirCitationCitedArtifactAbstract, + CitationCitedArtifactClassification as FhirCitationCitedArtifactClassification, + CitationCitedArtifactClassificationWhoClassified as FhirCitationCitedArtifactClassificationWhoClassified, + CitationCitedArtifactContributorship as FhirCitationCitedArtifactContributorship, + CitationCitedArtifactContributorshipEntry as FhirCitationCitedArtifactContributorshipEntry, + CitationCitedArtifactContributorshipEntryAffiliationInfo as FhirCitationCitedArtifactContributorshipEntryAffiliationInfo, + CitationCitedArtifactContributorshipEntryContributionInstance as FhirCitationCitedArtifactContributorshipEntryContributionInstance, + CitationCitedArtifactContributorshipSummary as FhirCitationCitedArtifactContributorshipSummary, + CitationCitedArtifactPart as FhirCitationCitedArtifactPart, + CitationCitedArtifactPublicationForm as FhirCitationCitedArtifactPublicationForm, + CitationCitedArtifactPublicationFormPeriodicRelease as FhirCitationCitedArtifactPublicationFormPeriodicRelease, + CitationCitedArtifactPublicationFormPeriodicReleaseDateOfPublication as FhirCitationCitedArtifactPublicationFormPeriodicReleaseDateOfPublication, + CitationCitedArtifactPublicationFormPublishedIn as FhirCitationCitedArtifactPublicationFormPublishedIn, + CitationCitedArtifactRelatesTo as FhirCitationCitedArtifactRelatesTo, + CitationCitedArtifactStatusDate as FhirCitationCitedArtifactStatusDate, + CitationCitedArtifactTitle as FhirCitationCitedArtifactTitle, + CitationCitedArtifactVersion as FhirCitationCitedArtifactVersion, + CitationCitedArtifactWebLocation as FhirCitationCitedArtifactWebLocation, + CitationClassification as FhirCitationClassification, + CitationRelatesTo as FhirCitationRelatesTo, + CitationStatusDate as FhirCitationStatusDate, + CitationSummary as FhirCitationSummary, + Claim as FhirClaim, + ClaimAccident as FhirClaimAccident, + ClaimCareTeam as FhirClaimCareTeam, + ClaimDiagnosis as FhirClaimDiagnosis, + ClaimInsurance as FhirClaimInsurance, + ClaimItem as FhirClaimItem, + ClaimItemDetail as FhirClaimItemDetail, + ClaimItemDetailSubDetail as FhirClaimItemDetailSubDetail, + ClaimPayee as FhirClaimPayee, + ClaimProcedure as FhirClaimProcedure, + ClaimRelated as FhirClaimRelated, + ClaimResponse as FhirClaimResponse, + ClaimResponseAddItem as FhirClaimResponseAddItem, + ClaimResponseAddItemDetail as FhirClaimResponseAddItemDetail, + ClaimResponseAddItemDetailSubDetail as FhirClaimResponseAddItemDetailSubDetail, + ClaimResponseError as FhirClaimResponseError, + ClaimResponseInsurance as FhirClaimResponseInsurance, + ClaimResponseItem as FhirClaimResponseItem, + ClaimResponseItemAdjudication as FhirClaimResponseItemAdjudication, + ClaimResponseItemDetail as FhirClaimResponseItemDetail, + ClaimResponseItemDetailSubDetail as FhirClaimResponseItemDetailSubDetail, + ClaimResponsePayment as FhirClaimResponsePayment, + ClaimResponseProcessNote as FhirClaimResponseProcessNote, + ClaimResponseTotal as FhirClaimResponseTotal, + ClaimSupportingInfo as FhirClaimSupportingInfo, + ClinicalImpression as FhirClinicalImpression, + ClinicalImpressionFinding as FhirClinicalImpressionFinding, + ClinicalImpressionInvestigation as FhirClinicalImpressionInvestigation, + ClinicalUseDefinition as FhirClinicalUseDefinition, + ClinicalUseDefinitionContraindication as FhirClinicalUseDefinitionContraindication, + ClinicalUseDefinitionContraindicationOtherTherapy as FhirClinicalUseDefinitionContraindicationOtherTherapy, + ClinicalUseDefinitionIndication as FhirClinicalUseDefinitionIndication, + ClinicalUseDefinitionInteraction as FhirClinicalUseDefinitionInteraction, + ClinicalUseDefinitionInteractionInteractant as FhirClinicalUseDefinitionInteractionInteractant, + ClinicalUseDefinitionUndesirableEffect as FhirClinicalUseDefinitionUndesirableEffect, + ClinicalUseDefinitionWarning as FhirClinicalUseDefinitionWarning, + CodeSystem as FhirCodeSystem, + CodeSystemConcept as FhirCodeSystemConcept, + CodeSystemConceptDesignation as FhirCodeSystemConceptDesignation, + CodeSystemConceptProperty as FhirCodeSystemConceptProperty, + CodeSystemFilter as FhirCodeSystemFilter, + CodeSystemProperty as FhirCodeSystemProperty, + CodeableConcept as FhirCodeableConcept, + CodeableReference as FhirCodeableReference, + Coding as FhirCoding, + Communication as FhirCommunication, + CommunicationPayload as FhirCommunicationPayload, + CommunicationRequest as FhirCommunicationRequest, + CommunicationRequestPayload as FhirCommunicationRequestPayload, + CompartmentDefinition as FhirCompartmentDefinition, + CompartmentDefinitionResource as FhirCompartmentDefinitionResource, + Composition as FhirComposition, + CompositionAttester as FhirCompositionAttester, + CompositionEvent as FhirCompositionEvent, + CompositionRelatesTo as FhirCompositionRelatesTo, + CompositionSection as FhirCompositionSection, + ConceptMap as FhirConceptMap, + ConceptMapGroup as FhirConceptMapGroup, + ConceptMapGroupElement as FhirConceptMapGroupElement, + ConceptMapGroupElementTarget as FhirConceptMapGroupElementTarget, + ConceptMapGroupElementTargetDependsOn as FhirConceptMapGroupElementTargetDependsOn, + ConceptMapGroupUnmapped as FhirConceptMapGroupUnmapped, + Condition as FhirCondition, + ConditionEvidence as FhirConditionEvidence, + ConditionStage as FhirConditionStage, + Consent as FhirConsent, + ConsentPolicy as FhirConsentPolicy, + ConsentProvision as FhirConsentProvision, + ConsentProvisionActor as FhirConsentProvisionActor, + ConsentProvisionData as FhirConsentProvisionData, + ConsentVerification as FhirConsentVerification, + ContactDetail as FhirContactDetail, + ContactPoint as FhirContactPoint, + Contract as FhirContract, + ContractContentDefinition as FhirContractContentDefinition, + ContractFriendly as FhirContractFriendly, + ContractLegal as FhirContractLegal, + ContractRule as FhirContractRule, + ContractSigner as FhirContractSigner, + ContractTerm as FhirContractTerm, + ContractTermAction as FhirContractTermAction, + ContractTermActionSubject as FhirContractTermActionSubject, + ContractTermAsset as FhirContractTermAsset, + ContractTermAssetContext as FhirContractTermAssetContext, + ContractTermAssetValuedItem as FhirContractTermAssetValuedItem, + ContractTermOffer as FhirContractTermOffer, + ContractTermOfferAnswer as FhirContractTermOfferAnswer, + ContractTermOfferParty as FhirContractTermOfferParty, + ContractTermSecurityLabel as FhirContractTermSecurityLabel, + Contributor as FhirContributor, + Count as FhirCount, + Coverage as FhirCoverage, + CoverageClass as FhirCoverageClass, + CoverageCostToBeneficiary as FhirCoverageCostToBeneficiary, + CoverageCostToBeneficiaryException as FhirCoverageCostToBeneficiaryException, + CoverageEligibilityRequest as FhirCoverageEligibilityRequest, + CoverageEligibilityRequestInsurance as FhirCoverageEligibilityRequestInsurance, + CoverageEligibilityRequestItem as FhirCoverageEligibilityRequestItem, + CoverageEligibilityRequestItemDiagnosis as FhirCoverageEligibilityRequestItemDiagnosis, + CoverageEligibilityRequestSupportingInfo as FhirCoverageEligibilityRequestSupportingInfo, + CoverageEligibilityResponse as FhirCoverageEligibilityResponse, + CoverageEligibilityResponseError as FhirCoverageEligibilityResponseError, + CoverageEligibilityResponseInsurance as FhirCoverageEligibilityResponseInsurance, + CoverageEligibilityResponseInsuranceItem as FhirCoverageEligibilityResponseInsuranceItem, + CoverageEligibilityResponseInsuranceItemBenefit as FhirCoverageEligibilityResponseInsuranceItemBenefit, + DataRequirement as FhirDataRequirement, + DataRequirementCodeFilter as FhirDataRequirementCodeFilter, + DataRequirementDateFilter as FhirDataRequirementDateFilter, + DataRequirementSort as FhirDataRequirementSort, + DetectedIssue as FhirDetectedIssue, + DetectedIssueEvidence as FhirDetectedIssueEvidence, + DetectedIssueMitigation as FhirDetectedIssueMitigation, + Device as FhirDevice, + DeviceDefinition as FhirDeviceDefinition, + DeviceDefinitionCapability as FhirDeviceDefinitionCapability, + DeviceDefinitionDeviceName as FhirDeviceDefinitionDeviceName, + DeviceDefinitionMaterial as FhirDeviceDefinitionMaterial, + DeviceDefinitionProperty as FhirDeviceDefinitionProperty, + DeviceDefinitionSpecialization as FhirDeviceDefinitionSpecialization, + DeviceDefinitionUdiDeviceIdentifier as FhirDeviceDefinitionUdiDeviceIdentifier, + DeviceDeviceName as FhirDeviceDeviceName, + DeviceMetric as FhirDeviceMetric, + DeviceMetricCalibration as FhirDeviceMetricCalibration, + DeviceProperty as FhirDeviceProperty, + DeviceRequest as FhirDeviceRequest, + DeviceRequestParameter as FhirDeviceRequestParameter, + DeviceSpecialization as FhirDeviceSpecialization, + DeviceUdiCarrier as FhirDeviceUdiCarrier, + DeviceUseStatement as FhirDeviceUseStatement, + DeviceVersion as FhirDeviceVersion, + DiagnosticReport as FhirDiagnosticReport, + DiagnosticReportMedia as FhirDiagnosticReportMedia, + Distance as FhirDistance, + DocumentManifest as FhirDocumentManifest, + DocumentManifestRelated as FhirDocumentManifestRelated, + DocumentReference as FhirDocumentReference, + DocumentReferenceContent as FhirDocumentReferenceContent, + DocumentReferenceContext as FhirDocumentReferenceContext, + DocumentReferenceRelatesTo as FhirDocumentReferenceRelatesTo, + Dosage as FhirDosage, + DosageDoseAndRate as FhirDosageDoseAndRate, + Duration as FhirDuration, + Element as FhirElement, + ElementDefinition as FhirElementDefinition, + ElementDefinitionBase as FhirElementDefinitionBase, + ElementDefinitionBinding as FhirElementDefinitionBinding, + ElementDefinitionConstraint as FhirElementDefinitionConstraint, + ElementDefinitionExample as FhirElementDefinitionExample, + ElementDefinitionMapping as FhirElementDefinitionMapping, + ElementDefinitionSlicing as FhirElementDefinitionSlicing, + ElementDefinitionSlicingDiscriminator as FhirElementDefinitionSlicingDiscriminator, + ElementDefinitionType as FhirElementDefinitionType, + Encounter as FhirEncounter, + EncounterClassHistory as FhirEncounterClassHistory, + EncounterDiagnosis as FhirEncounterDiagnosis, + EncounterHospitalization as FhirEncounterHospitalization, + EncounterLocation as FhirEncounterLocation, + EncounterParticipant as FhirEncounterParticipant, + EncounterStatusHistory as FhirEncounterStatusHistory, + Endpoint as FhirEndpoint, + EnrollmentRequest as FhirEnrollmentRequest, + EnrollmentResponse as FhirEnrollmentResponse, + EpisodeOfCare as FhirEpisodeOfCare, + EpisodeOfCareDiagnosis as FhirEpisodeOfCareDiagnosis, + EpisodeOfCareStatusHistory as FhirEpisodeOfCareStatusHistory, + EventDefinition as FhirEventDefinition, + Evidence as FhirEvidence, + EvidenceCertainty as FhirEvidenceCertainty, + EvidenceReport as FhirEvidenceReport, + EvidenceReportRelatesTo as FhirEvidenceReportRelatesTo, + EvidenceReportSection as FhirEvidenceReportSection, + EvidenceReportSubject as FhirEvidenceReportSubject, + EvidenceReportSubjectCharacteristic as FhirEvidenceReportSubjectCharacteristic, + EvidenceStatistic as FhirEvidenceStatistic, + EvidenceStatisticAttributeEstimate as FhirEvidenceStatisticAttributeEstimate, + EvidenceStatisticModelCharacteristic as FhirEvidenceStatisticModelCharacteristic, + EvidenceStatisticModelCharacteristicVariable as FhirEvidenceStatisticModelCharacteristicVariable, + EvidenceStatisticSampleSize as FhirEvidenceStatisticSampleSize, + EvidenceVariable as FhirEvidenceVariable, + EvidenceVariableCategory as FhirEvidenceVariableCategory, + EvidenceVariableCharacteristic as FhirEvidenceVariableCharacteristic, + EvidenceVariableCharacteristicTimeFromStart as FhirEvidenceVariableCharacteristicTimeFromStart, + EvidenceVariableDefinition as FhirEvidenceVariableDefinition, + ExampleScenario as FhirExampleScenario, + ExampleScenarioActor as FhirExampleScenarioActor, + ExampleScenarioInstance as FhirExampleScenarioInstance, + ExampleScenarioInstanceContainedInstance as FhirExampleScenarioInstanceContainedInstance, + ExampleScenarioInstanceVersion as FhirExampleScenarioInstanceVersion, + ExampleScenarioProcess as FhirExampleScenarioProcess, + ExampleScenarioProcessStep as FhirExampleScenarioProcessStep, + ExampleScenarioProcessStepAlternative as FhirExampleScenarioProcessStepAlternative, + ExampleScenarioProcessStepOperation as FhirExampleScenarioProcessStepOperation, + ExplanationOfBenefit as FhirExplanationOfBenefit, + ExplanationOfBenefitAccident as FhirExplanationOfBenefitAccident, + ExplanationOfBenefitAddItem as FhirExplanationOfBenefitAddItem, + ExplanationOfBenefitAddItemDetail as FhirExplanationOfBenefitAddItemDetail, + ExplanationOfBenefitAddItemDetailSubDetail as FhirExplanationOfBenefitAddItemDetailSubDetail, + ExplanationOfBenefitBenefitBalance as FhirExplanationOfBenefitBenefitBalance, + ExplanationOfBenefitBenefitBalanceFinancial as FhirExplanationOfBenefitBenefitBalanceFinancial, + ExplanationOfBenefitCareTeam as FhirExplanationOfBenefitCareTeam, + ExplanationOfBenefitDiagnosis as FhirExplanationOfBenefitDiagnosis, + ExplanationOfBenefitInsurance as FhirExplanationOfBenefitInsurance, + ExplanationOfBenefitItem as FhirExplanationOfBenefitItem, + ExplanationOfBenefitItemAdjudication as FhirExplanationOfBenefitItemAdjudication, + ExplanationOfBenefitItemDetail as FhirExplanationOfBenefitItemDetail, + ExplanationOfBenefitItemDetailSubDetail as FhirExplanationOfBenefitItemDetailSubDetail, + ExplanationOfBenefitPayee as FhirExplanationOfBenefitPayee, + ExplanationOfBenefitPayment as FhirExplanationOfBenefitPayment, + ExplanationOfBenefitProcedure as FhirExplanationOfBenefitProcedure, + ExplanationOfBenefitProcessNote as FhirExplanationOfBenefitProcessNote, + ExplanationOfBenefitRelated as FhirExplanationOfBenefitRelated, + ExplanationOfBenefitSupportingInfo as FhirExplanationOfBenefitSupportingInfo, + ExplanationOfBenefitTotal as FhirExplanationOfBenefitTotal, + Expression as FhirExpression, + Extension as FhirExtension, + FamilyMemberHistory as FhirFamilyMemberHistory, + FamilyMemberHistoryCondition as FhirFamilyMemberHistoryCondition, + Flag as FhirFlag, + Goal as FhirGoal, + GoalTarget as FhirGoalTarget, + GraphDefinition as FhirGraphDefinition, + GraphDefinitionLink as FhirGraphDefinitionLink, + GraphDefinitionLinkTarget as FhirGraphDefinitionLinkTarget, + GraphDefinitionLinkTargetCompartment as FhirGraphDefinitionLinkTargetCompartment, + Group as FhirGroup, + GroupCharacteristic as FhirGroupCharacteristic, + GroupMember as FhirGroupMember, + GuidanceResponse as FhirGuidanceResponse, + HealthcareService as FhirHealthcareService, + HealthcareServiceAvailableTime as FhirHealthcareServiceAvailableTime, + HealthcareServiceEligibility as FhirHealthcareServiceEligibility, + HealthcareServiceNotAvailable as FhirHealthcareServiceNotAvailable, + HumanName as FhirHumanName, + Identifier as FhirIdentifier, + ImagingStudy as FhirImagingStudy, + ImagingStudySeries as FhirImagingStudySeries, + ImagingStudySeriesInstance as FhirImagingStudySeriesInstance, + ImagingStudySeriesPerformer as FhirImagingStudySeriesPerformer, + Immunization as FhirImmunization, + ImmunizationEducation as FhirImmunizationEducation, + ImmunizationEvaluation as FhirImmunizationEvaluation, + ImmunizationPerformer as FhirImmunizationPerformer, + ImmunizationProtocolApplied as FhirImmunizationProtocolApplied, + ImmunizationReaction as FhirImmunizationReaction, + ImmunizationRecommendation as FhirImmunizationRecommendation, + ImmunizationRecommendationRecommendation as FhirImmunizationRecommendationRecommendation, + ImmunizationRecommendationRecommendationDateCriterion as FhirImmunizationRecommendationRecommendationDateCriterion, + ImplementationGuide as FhirImplementationGuide, + ImplementationGuideDefinition as FhirImplementationGuideDefinition, + ImplementationGuideDefinitionGrouping as FhirImplementationGuideDefinitionGrouping, + ImplementationGuideDefinitionPage as FhirImplementationGuideDefinitionPage, + ImplementationGuideDefinitionParameter as FhirImplementationGuideDefinitionParameter, + ImplementationGuideDefinitionResource as FhirImplementationGuideDefinitionResource, + ImplementationGuideDefinitionTemplate as FhirImplementationGuideDefinitionTemplate, + ImplementationGuideDependsOn as FhirImplementationGuideDependsOn, + ImplementationGuideGlobal as FhirImplementationGuideGlobal, + ImplementationGuideManifest as FhirImplementationGuideManifest, + ImplementationGuideManifestPage as FhirImplementationGuideManifestPage, + ImplementationGuideManifestResource as FhirImplementationGuideManifestResource, + Ingredient as FhirIngredient, + IngredientManufacturer as FhirIngredientManufacturer, + IngredientSubstance as FhirIngredientSubstance, + IngredientSubstanceStrength as FhirIngredientSubstanceStrength, + IngredientSubstanceStrengthReferenceStrength as FhirIngredientSubstanceStrengthReferenceStrength, + InsurancePlan as FhirInsurancePlan, + InsurancePlanContact as FhirInsurancePlanContact, + InsurancePlanCoverage as FhirInsurancePlanCoverage, + InsurancePlanCoverageBenefit as FhirInsurancePlanCoverageBenefit, + InsurancePlanCoverageBenefitLimit as FhirInsurancePlanCoverageBenefitLimit, + InsurancePlanPlan as FhirInsurancePlanPlan, + InsurancePlanPlanGeneralCost as FhirInsurancePlanPlanGeneralCost, + InsurancePlanPlanSpecificCost as FhirInsurancePlanPlanSpecificCost, + InsurancePlanPlanSpecificCostBenefit as FhirInsurancePlanPlanSpecificCostBenefit, + InsurancePlanPlanSpecificCostBenefitCost as FhirInsurancePlanPlanSpecificCostBenefitCost, + Invoice as FhirInvoice, + InvoiceLineItem as FhirInvoiceLineItem, + InvoiceLineItemPriceComponent as FhirInvoiceLineItemPriceComponent, + InvoiceParticipant as FhirInvoiceParticipant, + Library as FhirLibrary, + Linkage as FhirLinkage, + LinkageItem as FhirLinkageItem, + List as FhirList, + ListEntry as FhirListEntry, + Location as FhirLocation, + LocationHoursOfOperation as FhirLocationHoursOfOperation, + LocationPosition as FhirLocationPosition, + ManufacturedItemDefinition as FhirManufacturedItemDefinition, + ManufacturedItemDefinitionProperty as FhirManufacturedItemDefinitionProperty, + MarketingStatus as FhirMarketingStatus, + Measure as FhirMeasure, + MeasureGroup as FhirMeasureGroup, + MeasureGroupPopulation as FhirMeasureGroupPopulation, + MeasureGroupStratifier as FhirMeasureGroupStratifier, + MeasureGroupStratifierComponent as FhirMeasureGroupStratifierComponent, + MeasureReport as FhirMeasureReport, + MeasureReportGroup as FhirMeasureReportGroup, + MeasureReportGroupPopulation as FhirMeasureReportGroupPopulation, + MeasureReportGroupStratifier as FhirMeasureReportGroupStratifier, + MeasureReportGroupStratifierStratum as FhirMeasureReportGroupStratifierStratum, + MeasureReportGroupStratifierStratumComponent as FhirMeasureReportGroupStratifierStratumComponent, + MeasureReportGroupStratifierStratumPopulation as FhirMeasureReportGroupStratifierStratumPopulation, + MeasureSupplementalData as FhirMeasureSupplementalData, + Media as FhirMedia, + Medication as FhirMedication, + MedicationAdministration as FhirMedicationAdministration, + MedicationAdministrationDosage as FhirMedicationAdministrationDosage, + MedicationAdministrationPerformer as FhirMedicationAdministrationPerformer, + MedicationBatch as FhirMedicationBatch, + MedicationDispense as FhirMedicationDispense, + MedicationDispensePerformer as FhirMedicationDispensePerformer, + MedicationDispenseSubstitution as FhirMedicationDispenseSubstitution, + MedicationIngredient as FhirMedicationIngredient, + MedicationKnowledge as FhirMedicationKnowledge, + MedicationKnowledgeAdministrationGuidelines as FhirMedicationKnowledgeAdministrationGuidelines, + MedicationKnowledgeAdministrationGuidelinesDosage as FhirMedicationKnowledgeAdministrationGuidelinesDosage, + MedicationKnowledgeAdministrationGuidelinesPatientCharacteristics as FhirMedicationKnowledgeAdministrationGuidelinesPatientCharacteristics, + MedicationKnowledgeCost as FhirMedicationKnowledgeCost, + MedicationKnowledgeDrugCharacteristic as FhirMedicationKnowledgeDrugCharacteristic, + MedicationKnowledgeIngredient as FhirMedicationKnowledgeIngredient, + MedicationKnowledgeKinetics as FhirMedicationKnowledgeKinetics, + MedicationKnowledgeMedicineClassification as FhirMedicationKnowledgeMedicineClassification, + MedicationKnowledgeMonitoringProgram as FhirMedicationKnowledgeMonitoringProgram, + MedicationKnowledgeMonograph as FhirMedicationKnowledgeMonograph, + MedicationKnowledgePackaging as FhirMedicationKnowledgePackaging, + MedicationKnowledgeRegulatory as FhirMedicationKnowledgeRegulatory, + MedicationKnowledgeRegulatoryMaxDispense as FhirMedicationKnowledgeRegulatoryMaxDispense, + MedicationKnowledgeRegulatorySchedule as FhirMedicationKnowledgeRegulatorySchedule, + MedicationKnowledgeRegulatorySubstitution as FhirMedicationKnowledgeRegulatorySubstitution, + MedicationKnowledgeRelatedMedicationKnowledge as FhirMedicationKnowledgeRelatedMedicationKnowledge, + MedicationRequest as FhirMedicationRequest, + MedicationRequestDispenseRequest as FhirMedicationRequestDispenseRequest, + MedicationRequestDispenseRequestInitialFill as FhirMedicationRequestDispenseRequestInitialFill, + MedicationRequestSubstitution as FhirMedicationRequestSubstitution, + MedicationStatement as FhirMedicationStatement, + MedicinalProductDefinition as FhirMedicinalProductDefinition, + MedicinalProductDefinitionCharacteristic as FhirMedicinalProductDefinitionCharacteristic, + MedicinalProductDefinitionContact as FhirMedicinalProductDefinitionContact, + MedicinalProductDefinitionCrossReference as FhirMedicinalProductDefinitionCrossReference, + MedicinalProductDefinitionName as FhirMedicinalProductDefinitionName, + MedicinalProductDefinitionNameCountryLanguage as FhirMedicinalProductDefinitionNameCountryLanguage, + MedicinalProductDefinitionNameNamePart as FhirMedicinalProductDefinitionNameNamePart, + MedicinalProductDefinitionOperation as FhirMedicinalProductDefinitionOperation, + MessageDefinition as FhirMessageDefinition, + MessageDefinitionAllowedResponse as FhirMessageDefinitionAllowedResponse, + MessageDefinitionFocus as FhirMessageDefinitionFocus, + MessageHeader as FhirMessageHeader, + MessageHeaderDestination as FhirMessageHeaderDestination, + MessageHeaderResponse as FhirMessageHeaderResponse, + MessageHeaderSource as FhirMessageHeaderSource, + Meta as FhirMeta, + MolecularSequence as FhirMolecularSequence, + MolecularSequenceQuality as FhirMolecularSequenceQuality, + MolecularSequenceQualityRoc as FhirMolecularSequenceQualityRoc, + MolecularSequenceReferenceSeq as FhirMolecularSequenceReferenceSeq, + MolecularSequenceRepository as FhirMolecularSequenceRepository, + MolecularSequenceStructureVariant as FhirMolecularSequenceStructureVariant, + MolecularSequenceStructureVariantInner as FhirMolecularSequenceStructureVariantInner, + MolecularSequenceStructureVariantOuter as FhirMolecularSequenceStructureVariantOuter, + MolecularSequenceVariant as FhirMolecularSequenceVariant, + Money as FhirMoney, + NamingSystem as FhirNamingSystem, + NamingSystemUniqueId as FhirNamingSystemUniqueId, + Narrative as FhirNarrative, + NutritionOrder as FhirNutritionOrder, + NutritionOrderEnteralFormula as FhirNutritionOrderEnteralFormula, + NutritionOrderEnteralFormulaAdministration as FhirNutritionOrderEnteralFormulaAdministration, + NutritionOrderOralDiet as FhirNutritionOrderOralDiet, + NutritionOrderOralDietNutrient as FhirNutritionOrderOralDietNutrient, + NutritionOrderOralDietTexture as FhirNutritionOrderOralDietTexture, + NutritionOrderSupplement as FhirNutritionOrderSupplement, + NutritionProduct as FhirNutritionProduct, + NutritionProductIngredient as FhirNutritionProductIngredient, + NutritionProductInstance as FhirNutritionProductInstance, + NutritionProductNutrient as FhirNutritionProductNutrient, + NutritionProductProductCharacteristic as FhirNutritionProductProductCharacteristic, + Observation as FhirObservation, + ObservationComponent as FhirObservationComponent, + ObservationDefinition as FhirObservationDefinition, + ObservationDefinitionQualifiedInterval as FhirObservationDefinitionQualifiedInterval, + ObservationDefinitionQuantitativeDetails as FhirObservationDefinitionQuantitativeDetails, + ObservationReferenceRange as FhirObservationReferenceRange, + OperationDefinition as FhirOperationDefinition, + OperationDefinitionOverload as FhirOperationDefinitionOverload, + OperationDefinitionParameter as FhirOperationDefinitionParameter, + OperationDefinitionParameterBinding as FhirOperationDefinitionParameterBinding, + OperationDefinitionParameterReferencedFrom as FhirOperationDefinitionParameterReferencedFrom, + OperationOutcome as FhirOperationOutcome, + OperationOutcomeIssue as FhirOperationOutcomeIssue, + Organization as FhirOrganization, + OrganizationAffiliation as FhirOrganizationAffiliation, + OrganizationContact as FhirOrganizationContact, + PackagedProductDefinition as FhirPackagedProductDefinition, + PackagedProductDefinitionLegalStatusOfSupply as FhirPackagedProductDefinitionLegalStatusOfSupply, + PackagedProductDefinitionPackage as FhirPackagedProductDefinitionPackage, + PackagedProductDefinitionPackageContainedItem as FhirPackagedProductDefinitionPackageContainedItem, + PackagedProductDefinitionPackageProperty as FhirPackagedProductDefinitionPackageProperty, + PackagedProductDefinitionPackageShelfLifeStorage as FhirPackagedProductDefinitionPackageShelfLifeStorage, + ParameterDefinition as FhirParameterDefinition, + Parameters as FhirParameters, + ParametersParameter as FhirParametersParameter, + Patient as FhirPatient, + PatientCommunication as FhirPatientCommunication, + PatientContact as FhirPatientContact, + PatientLink as FhirPatientLink, + PaymentNotice as FhirPaymentNotice, + PaymentReconciliation as FhirPaymentReconciliation, + PaymentReconciliationDetail as FhirPaymentReconciliationDetail, + PaymentReconciliationProcessNote as FhirPaymentReconciliationProcessNote, + Period as FhirPeriod, + Person as FhirPerson, + PersonLink as FhirPersonLink, + PlanDefinition as FhirPlanDefinition, + PlanDefinitionAction as FhirPlanDefinitionAction, + PlanDefinitionActionCondition as FhirPlanDefinitionActionCondition, + PlanDefinitionActionDynamicValue as FhirPlanDefinitionActionDynamicValue, + PlanDefinitionActionParticipant as FhirPlanDefinitionActionParticipant, + PlanDefinitionActionRelatedAction as FhirPlanDefinitionActionRelatedAction, + PlanDefinitionGoal as FhirPlanDefinitionGoal, + PlanDefinitionGoalTarget as FhirPlanDefinitionGoalTarget, + Practitioner as FhirPractitioner, + PractitionerQualification as FhirPractitionerQualification, + PractitionerRole as FhirPractitionerRole, + PractitionerRoleAvailableTime as FhirPractitionerRoleAvailableTime, + PractitionerRoleNotAvailable as FhirPractitionerRoleNotAvailable, + Procedure as FhirProcedure, + ProcedureFocalDevice as FhirProcedureFocalDevice, + ProcedurePerformer as FhirProcedurePerformer, + ProdCharacteristic as FhirProdCharacteristic, + ProductShelfLife as FhirProductShelfLife, + Provenance as FhirProvenance, + ProvenanceAgent as FhirProvenanceAgent, + ProvenanceEntity as FhirProvenanceEntity, + Quantity as FhirQuantity, + Questionnaire as FhirQuestionnaire, + QuestionnaireItem as FhirQuestionnaireItem, + QuestionnaireItemAnswerOption as FhirQuestionnaireItemAnswerOption, + QuestionnaireItemEnableWhen as FhirQuestionnaireItemEnableWhen, + QuestionnaireItemInitial as FhirQuestionnaireItemInitial, + QuestionnaireResponse as FhirQuestionnaireResponse, + QuestionnaireResponseItem as FhirQuestionnaireResponseItem, + QuestionnaireResponseItemAnswer as FhirQuestionnaireResponseItemAnswer, + Range as FhirRange, + Ratio as FhirRatio, + RatioRange as FhirRatioRange, + Reference as FhirReference, + RegulatedAuthorization as FhirRegulatedAuthorization, + RegulatedAuthorizationCase as FhirRegulatedAuthorizationCase, + RelatedArtifact as FhirRelatedArtifact, + RelatedPerson as FhirRelatedPerson, + RelatedPersonCommunication as FhirRelatedPersonCommunication, + RequestGroup as FhirRequestGroup, + RequestGroupAction as FhirRequestGroupAction, + RequestGroupActionCondition as FhirRequestGroupActionCondition, + RequestGroupActionRelatedAction as FhirRequestGroupActionRelatedAction, + ResearchDefinition as FhirResearchDefinition, + ResearchElementDefinition as FhirResearchElementDefinition, + ResearchElementDefinitionCharacteristic as FhirResearchElementDefinitionCharacteristic, + ResearchStudy as FhirResearchStudy, + ResearchStudyArm as FhirResearchStudyArm, + ResearchStudyObjective as FhirResearchStudyObjective, + ResearchSubject as FhirResearchSubject, + Resource as FhirResource, + RiskAssessment as FhirRiskAssessment, + RiskAssessmentPrediction as FhirRiskAssessmentPrediction, + SampledData as FhirSampledData, + Schedule as FhirSchedule, + SearchParameter as FhirSearchParameter, + SearchParameterComponent as FhirSearchParameterComponent, + ServiceRequest as FhirServiceRequest, + Signature as FhirSignature, + Slot as FhirSlot, + Specimen as FhirSpecimen, + SpecimenCollection as FhirSpecimenCollection, + SpecimenContainer as FhirSpecimenContainer, + SpecimenDefinition as FhirSpecimenDefinition, + SpecimenDefinitionTypeTested as FhirSpecimenDefinitionTypeTested, + SpecimenDefinitionTypeTestedContainer as FhirSpecimenDefinitionTypeTestedContainer, + SpecimenDefinitionTypeTestedContainerAdditive as FhirSpecimenDefinitionTypeTestedContainerAdditive, + SpecimenDefinitionTypeTestedHandling as FhirSpecimenDefinitionTypeTestedHandling, + SpecimenProcessing as FhirSpecimenProcessing, + StructureDefinition as FhirStructureDefinition, + StructureDefinitionContext as FhirStructureDefinitionContext, + StructureDefinitionDifferential as FhirStructureDefinitionDifferential, + StructureDefinitionMapping as FhirStructureDefinitionMapping, + StructureDefinitionSnapshot as FhirStructureDefinitionSnapshot, + StructureMap as FhirStructureMap, + StructureMapGroup as FhirStructureMapGroup, + StructureMapGroupInput as FhirStructureMapGroupInput, + StructureMapGroupRule as FhirStructureMapGroupRule, + StructureMapGroupRuleDependent as FhirStructureMapGroupRuleDependent, + StructureMapGroupRuleSource as FhirStructureMapGroupRuleSource, + StructureMapGroupRuleTarget as FhirStructureMapGroupRuleTarget, + StructureMapGroupRuleTargetParameter as FhirStructureMapGroupRuleTargetParameter, + StructureMapStructure as FhirStructureMapStructure, + Subscription as FhirSubscription, + SubscriptionChannel as FhirSubscriptionChannel, + SubscriptionStatus as FhirSubscriptionStatus, + SubscriptionStatusNotificationEvent as FhirSubscriptionStatusNotificationEvent, + SubscriptionTopic as FhirSubscriptionTopic, + SubscriptionTopicCanFilterBy as FhirSubscriptionTopicCanFilterBy, + SubscriptionTopicEventTrigger as FhirSubscriptionTopicEventTrigger, + SubscriptionTopicNotificationShape as FhirSubscriptionTopicNotificationShape, + SubscriptionTopicResourceTrigger as FhirSubscriptionTopicResourceTrigger, + SubscriptionTopicResourceTriggerQueryCriteria as FhirSubscriptionTopicResourceTriggerQueryCriteria, + Substance as FhirSubstance, + SubstanceDefinition as FhirSubstanceDefinition, + SubstanceDefinitionCode as FhirSubstanceDefinitionCode, + SubstanceDefinitionMoiety as FhirSubstanceDefinitionMoiety, + SubstanceDefinitionMolecularWeight as FhirSubstanceDefinitionMolecularWeight, + SubstanceDefinitionName as FhirSubstanceDefinitionName, + SubstanceDefinitionNameOfficial as FhirSubstanceDefinitionNameOfficial, + SubstanceDefinitionProperty as FhirSubstanceDefinitionProperty, + SubstanceDefinitionRelationship as FhirSubstanceDefinitionRelationship, + SubstanceDefinitionSourceMaterial as FhirSubstanceDefinitionSourceMaterial, + SubstanceDefinitionStructure as FhirSubstanceDefinitionStructure, + SubstanceDefinitionStructureRepresentation as FhirSubstanceDefinitionStructureRepresentation, + SubstanceIngredient as FhirSubstanceIngredient, + SubstanceInstance as FhirSubstanceInstance, + SupplyDelivery as FhirSupplyDelivery, + SupplyDeliverySuppliedItem as FhirSupplyDeliverySuppliedItem, + SupplyRequest as FhirSupplyRequest, + SupplyRequestParameter as FhirSupplyRequestParameter, + Task as FhirTask, + TaskInput as FhirTaskInput, + TaskOutput as FhirTaskOutput, + TaskRestriction as FhirTaskRestriction, + TerminologyCapabilities as FhirTerminologyCapabilities, + TerminologyCapabilitiesClosure as FhirTerminologyCapabilitiesClosure, + TerminologyCapabilitiesCodeSystem as FhirTerminologyCapabilitiesCodeSystem, + TerminologyCapabilitiesCodeSystemVersion as FhirTerminologyCapabilitiesCodeSystemVersion, + TerminologyCapabilitiesCodeSystemVersionFilter as FhirTerminologyCapabilitiesCodeSystemVersionFilter, + TerminologyCapabilitiesExpansion as FhirTerminologyCapabilitiesExpansion, + TerminologyCapabilitiesExpansionParameter as FhirTerminologyCapabilitiesExpansionParameter, + TerminologyCapabilitiesImplementation as FhirTerminologyCapabilitiesImplementation, + TerminologyCapabilitiesSoftware as FhirTerminologyCapabilitiesSoftware, + TerminologyCapabilitiesTranslation as FhirTerminologyCapabilitiesTranslation, + TerminologyCapabilitiesValidateCode as FhirTerminologyCapabilitiesValidateCode, + TestReport as FhirTestReport, + TestReportParticipant as FhirTestReportParticipant, + TestReportSetup as FhirTestReportSetup, + TestReportSetupAction as FhirTestReportSetupAction, + TestReportSetupActionAssert as FhirTestReportSetupActionAssert, + TestReportSetupActionOperation as FhirTestReportSetupActionOperation, + TestReportTeardown as FhirTestReportTeardown, + TestReportTeardownAction as FhirTestReportTeardownAction, + TestReportTest as FhirTestReportTest, + TestReportTestAction as FhirTestReportTestAction, + TestScript as FhirTestScript, + TestScriptDestination as FhirTestScriptDestination, + TestScriptFixture as FhirTestScriptFixture, + TestScriptMetadata as FhirTestScriptMetadata, + TestScriptMetadataCapability as FhirTestScriptMetadataCapability, + TestScriptMetadataLink as FhirTestScriptMetadataLink, + TestScriptOrigin as FhirTestScriptOrigin, + TestScriptSetup as FhirTestScriptSetup, + TestScriptSetupAction as FhirTestScriptSetupAction, + TestScriptSetupActionAssert as FhirTestScriptSetupActionAssert, + TestScriptSetupActionOperation as FhirTestScriptSetupActionOperation, + TestScriptSetupActionOperationRequestHeader as FhirTestScriptSetupActionOperationRequestHeader, + TestScriptTeardown as FhirTestScriptTeardown, + TestScriptTeardownAction as FhirTestScriptTeardownAction, + TestScriptTest as FhirTestScriptTest, + TestScriptTestAction as FhirTestScriptTestAction, + TestScriptVariable as FhirTestScriptVariable, + Timing as FhirTiming, + TimingRepeat as FhirTimingRepeat, + TriggerDefinition as FhirTriggerDefinition, + UsageContext as FhirUsageContext, + ValueSet as FhirValueSet, + ValueSetCompose as FhirValueSetCompose, + ValueSetComposeInclude as FhirValueSetComposeInclude, + ValueSetComposeIncludeConcept as FhirValueSetComposeIncludeConcept, + ValueSetComposeIncludeConceptDesignation as FhirValueSetComposeIncludeConceptDesignation, + ValueSetComposeIncludeFilter as FhirValueSetComposeIncludeFilter, + ValueSetExpansion as FhirValueSetExpansion, + ValueSetExpansionContains as FhirValueSetExpansionContains, + ValueSetExpansionParameter as FhirValueSetExpansionParameter, + VerificationResult as FhirVerificationResult, + VerificationResultAttestation as FhirVerificationResultAttestation, + VerificationResultPrimarySource as FhirVerificationResultPrimarySource, + VerificationResultValidator as FhirVerificationResultValidator, + VisionPrescription as FhirVisionPrescription, + VisionPrescriptionLensSpecification as FhirVisionPrescriptionLensSpecification, + VisionPrescriptionLensSpecificationPrism as FhirVisionPrescriptionLensSpecificationPrism, +} from 'fhir/r4b.js' +import { z } from 'zod' +import { fhirDateTimeToEpoch } from '../support.js' + +// Generated from hl7.fhir.r4b.core#4.3.0 (FHIR 4.3.0). +// 201 structures: every type the exposed roots reach. + +export const accountCoverageSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + coverage: z.lazy(() => referenceSchema), + priority: positiveIntSchema.optional(), + _priority: z.lazy(() => elementSchema).optional(), + }), +) + +export const accountGuarantorSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + party: z.lazy(() => referenceSchema), + onHold: booleanSchema.optional(), + _onHold: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), +) + +export const accountSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Account'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'entered-in-error', + 'inactive', + 'on-hold', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + subject: z + .lazy(() => referenceSchema) + .array() + .optional(), + servicePeriod: z.lazy(() => periodSchema).optional(), + coverage: accountCoverageSchema.array().optional(), + owner: z.lazy(() => referenceSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + guarantor: accountGuarantorSchema.array().optional(), + partOf: z.lazy(() => referenceSchema).optional(), + }), +) + +export const activityDefinitionParticipantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['device', 'patient', 'practitioner', 'related-person']), + _type: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const activityDefinitionDynamicValueSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema, + _path: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema), + }), + ) + +export const activityDefinitionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ActivityDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + subjectCanonical: canonicalSchema.optional(), + _subjectCanonical: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + library: canonicalSchema.array().optional(), + _library: z + .lazy(() => elementSchema) + .array() + .optional(), + kind: z + .enum([ + 'Appointment', + 'AppointmentResponse', + 'CarePlan', + 'Claim', + 'CommunicationRequest', + 'Contract', + 'DeviceRequest', + 'EnrollmentRequest', + 'ImmunizationRecommendation', + 'MedicationRequest', + 'NutritionOrder', + 'ServiceRequest', + 'SupplyRequest', + 'Task', + 'VisionPrescription', + ]) + .optional(), + _kind: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + intent: z + .enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]) + .optional(), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + timingAge: z.lazy(() => ageSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingRange: z.lazy(() => rangeSchema).optional(), + timingDuration: z.lazy(() => durationSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + participant: activityDefinitionParticipantSchema.array().optional(), + productReference: z.lazy(() => referenceSchema).optional(), + productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + dosage: z + .lazy(() => dosageSchema) + .array() + .optional(), + bodySite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specimenRequirement: z + .lazy(() => referenceSchema) + .array() + .optional(), + observationRequirement: z + .lazy(() => referenceSchema) + .array() + .optional(), + observationResultRequirement: z + .lazy(() => referenceSchema) + .array() + .optional(), + transform: canonicalSchema.optional(), + _transform: z.lazy(() => elementSchema).optional(), + dynamicValue: activityDefinitionDynamicValueSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'subjectCodeableConcept', + 'subjectReference', + 'subjectCanonical', + ], + required: false, + }, + { + names: [ + 'timingTiming', + 'timingDateTime', + 'timingAge', + 'timingPeriod', + 'timingRange', + 'timingDuration', + ], + required: false, + }, + { + names: ['productReference', 'productCodeableConcept'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const addressSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + use: z.enum(['billing', 'home', 'old', 'temp', 'work']).optional(), + _use: z.lazy(() => elementSchema).optional(), + type: z.enum(['both', 'physical', 'postal']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + line: stringSchema.array().optional(), + _line: z + .lazy(() => elementSchema) + .array() + .optional(), + city: stringSchema.optional(), + _city: z.lazy(() => elementSchema).optional(), + district: stringSchema.optional(), + _district: z.lazy(() => elementSchema).optional(), + state: stringSchema.optional(), + _state: z.lazy(() => elementSchema).optional(), + postalCode: stringSchema.optional(), + _postalCode: z.lazy(() => elementSchema).optional(), + country: stringSchema.optional(), + _country: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), +) + +export const administrableProductDefinitionPropertySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueQuantity', + 'valueDate', + 'valueBoolean', + 'valueAttachment', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const administrableProductDefinitionRouteOfAdministrationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + firstDose: z.lazy(() => quantitySchema).optional(), + maxSingleDose: z.lazy(() => quantitySchema).optional(), + maxDosePerDay: z.lazy(() => quantitySchema).optional(), + maxDosePerTreatmentPeriod: z.lazy(() => ratioSchema).optional(), + maxTreatmentPeriod: z.lazy(() => durationSchema).optional(), + targetSpecies: + administrableProductDefinitionRouteOfAdministrationTargetSpeciesSchema + .array() + .optional(), + }), + ) + +export const administrableProductDefinitionRouteOfAdministrationTargetSpeciesSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + withdrawalPeriod: + administrableProductDefinitionRouteOfAdministrationTargetSpeciesWithdrawalPeriodSchema + .array() + .optional(), + }), + ) + +export const administrableProductDefinitionRouteOfAdministrationTargetSpeciesWithdrawalPeriodSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + tissue: z.lazy(() => codeableConceptSchema), + value: z.lazy(() => quantitySchema), + supportingInformation: stringSchema.optional(), + _supportingInformation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const administrableProductDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('AdministrableProductDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + formOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + administrableDoseForm: z.lazy(() => codeableConceptSchema).optional(), + unitOfPresentation: z.lazy(() => codeableConceptSchema).optional(), + producedFrom: z + .lazy(() => referenceSchema) + .array() + .optional(), + ingredient: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + device: z.lazy(() => referenceSchema).optional(), + property: administrableProductDefinitionPropertySchema.array().optional(), + routeOfAdministration: + administrableProductDefinitionRouteOfAdministrationSchema + .array() + .min(1), + }), + ) + +export const adverseEventSuspectEntitySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + instance: z.lazy(() => referenceSchema), + causality: adverseEventSuspectEntityCausalitySchema.array().optional(), + }), + ) + +export const adverseEventSuspectEntityCausalitySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + assessment: z.lazy(() => codeableConceptSchema).optional(), + productRelatedness: stringSchema.optional(), + _productRelatedness: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const adverseEventSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('AdverseEvent'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + actuality: z.enum(['actual', 'potential']), + _actuality: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + event: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + detected: dateTimeSchema.optional(), + _detected: z.lazy(() => elementSchema).optional(), + recordedDate: dateTimeSchema.optional(), + _recordedDate: z.lazy(() => elementSchema).optional(), + resultingCondition: z + .lazy(() => referenceSchema) + .array() + .optional(), + location: z.lazy(() => referenceSchema).optional(), + seriousness: z.lazy(() => codeableConceptSchema).optional(), + severity: z.lazy(() => codeableConceptSchema).optional(), + outcome: z.lazy(() => codeableConceptSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + contributor: z + .lazy(() => referenceSchema) + .array() + .optional(), + suspectEntity: adverseEventSuspectEntitySchema.array().optional(), + subjectMedicalHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + referenceDocument: z + .lazy(() => referenceSchema) + .array() + .optional(), + study: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const ageSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }), +) + +export const allergyIntoleranceReactionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + substance: z.lazy(() => codeableConceptSchema).optional(), + manifestation: z + .lazy(() => codeableConceptSchema) + .array() + .min(1), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + onset: dateTimeSchema.optional(), + _onset: z.lazy(() => elementSchema).optional(), + severity: z.enum(['mild', 'moderate', 'severe']).optional(), + _severity: z.lazy(() => elementSchema).optional(), + exposureRoute: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), + ) + +export const allergyIntoleranceSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('AllergyIntolerance'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + clinicalStatus: z.lazy(() => codeableConceptSchema).optional(), + verificationStatus: z.lazy(() => codeableConceptSchema).optional(), + type: z.enum(['allergy', 'intolerance']).optional(), + _type: z.lazy(() => elementSchema).optional(), + category: z + .enum(['biologic', 'environment', 'food', 'medication']) + .array() + .optional(), + _category: z + .lazy(() => elementSchema) + .array() + .optional(), + criticality: z.enum(['high', 'low', 'unable-to-assess']).optional(), + _criticality: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + patient: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + onsetDateTime: dateTimeSchema.optional(), + _onsetDateTime: z.lazy(() => elementSchema).optional(), + onsetAge: z.lazy(() => ageSchema).optional(), + onsetPeriod: z.lazy(() => periodSchema).optional(), + onsetRange: z.lazy(() => rangeSchema).optional(), + onsetString: stringSchema.optional(), + _onsetString: z.lazy(() => elementSchema).optional(), + recordedDate: dateTimeSchema.optional(), + _recordedDate: z.lazy(() => elementSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + asserter: z.lazy(() => referenceSchema).optional(), + lastOccurrence: dateTimeSchema.optional(), + _lastOccurrence: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + reaction: allergyIntoleranceReactionSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'onsetDateTime', + 'onsetAge', + 'onsetPeriod', + 'onsetRange', + 'onsetString', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const annotationSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + authorReference: z.lazy(() => referenceSchema).optional(), + authorString: stringSchema.optional(), + _authorString: z.lazy(() => elementSchema).optional(), + time: dateTimeSchema.optional(), + _time: z.lazy(() => elementSchema).optional(), + text: markdownSchema, + _text: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [{ names: ['authorReference', 'authorString'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const appointmentParticipantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + actor: z.lazy(() => referenceSchema).optional(), + required: z.enum(['information-only', 'optional', 'required']).optional(), + _required: z.lazy(() => elementSchema).optional(), + status: z.enum(['accepted', 'declined', 'needs-action', 'tentative']), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), + ) + +export const appointmentSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Appointment'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'arrived', + 'booked', + 'cancelled', + 'checked-in', + 'entered-in-error', + 'fulfilled', + 'noshow', + 'pending', + 'proposed', + 'waitlist', + ]), + _status: z.lazy(() => elementSchema).optional(), + cancelationReason: z.lazy(() => codeableConceptSchema).optional(), + serviceCategory: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + serviceType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + appointmentType: z.lazy(() => codeableConceptSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + priority: unsignedIntSchema.optional(), + _priority: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .optional(), + start: instantSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: instantSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + minutesDuration: positiveIntSchema.optional(), + _minutesDuration: z.lazy(() => elementSchema).optional(), + slot: z + .lazy(() => referenceSchema) + .array() + .optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + patientInstruction: stringSchema.optional(), + _patientInstruction: z.lazy(() => elementSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + participant: appointmentParticipantSchema.array().min(1), + requestedPeriod: z + .lazy(() => periodSchema) + .array() + .optional(), + }), +) + +export const appointmentResponseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('AppointmentResponse'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + appointment: z.lazy(() => referenceSchema), + start: instantSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: instantSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + participantType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + actor: z.lazy(() => referenceSchema).optional(), + participantStatus: z.enum([ + 'accepted', + 'declined', + 'needs-action', + 'tentative', + ]), + _participantStatus: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), + ) + +export const attachmentSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + contentType: codeSchema.optional(), + _contentType: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + data: base64BinarySchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + url: urlSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + size: unsignedIntSchema.optional(), + _size: z.lazy(() => elementSchema).optional(), + hash: base64BinarySchema.optional(), + _hash: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + creation: dateTimeSchema.optional(), + _creation: z.lazy(() => elementSchema).optional(), + }), +) + +export const auditEventAgentSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + role: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + who: z.lazy(() => referenceSchema).optional(), + altId: stringSchema.optional(), + _altId: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + requestor: booleanSchema, + _requestor: z.lazy(() => elementSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + policy: uriSchema.array().optional(), + _policy: z + .lazy(() => elementSchema) + .array() + .optional(), + media: z.lazy(() => codingSchema).optional(), + network: auditEventAgentNetworkSchema.optional(), + purposeOfUse: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), +) + +export const auditEventSourceSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + site: stringSchema.optional(), + _site: z.lazy(() => elementSchema).optional(), + observer: z.lazy(() => referenceSchema), + type: z + .lazy(() => codingSchema) + .array() + .optional(), + }), +) + +export const auditEventEntitySchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + what: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codingSchema).optional(), + role: z.lazy(() => codingSchema).optional(), + lifecycle: z.lazy(() => codingSchema).optional(), + securityLabel: z + .lazy(() => codingSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + query: base64BinarySchema.optional(), + _query: z.lazy(() => elementSchema).optional(), + detail: auditEventEntityDetailSchema.array().optional(), + }), +) + +export const auditEventAgentNetworkSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + address: stringSchema.optional(), + _address: z.lazy(() => elementSchema).optional(), + type: z.enum(['1', '2', '3', '4', '5']).optional(), + _type: z.lazy(() => elementSchema).optional(), + }), + ) + +export const auditEventEntityDetailSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: stringSchema, + _type: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['valueString', 'valueBase64Binary'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const auditEventSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('AuditEvent'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codingSchema), + subtype: z + .lazy(() => codingSchema) + .array() + .optional(), + action: z.enum(['C', 'D', 'E', 'R', 'U']).optional(), + _action: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + recorded: instantSchema, + _recorded: z.lazy(() => elementSchema).optional(), + outcome: z.enum(['0', '12', '4', '8']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + outcomeDesc: stringSchema.optional(), + _outcomeDesc: z.lazy(() => elementSchema).optional(), + purposeOfEvent: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + agent: auditEventAgentSchema.array().min(1), + source: auditEventSourceSchema, + entity: auditEventEntitySchema.array().optional(), + }), +) + +export const basicSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Basic'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema).optional(), + created: dateSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + }), +) + +export const binarySchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Binary'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + contentType: codeSchema, + _contentType: z.lazy(() => elementSchema).optional(), + securityContext: z.lazy(() => referenceSchema).optional(), + data: base64BinarySchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + }), +) + +export const biologicallyDerivedProductCollectionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + collector: z.lazy(() => referenceSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + collectedDateTime: dateTimeSchema.optional(), + _collectedDateTime: z.lazy(() => elementSchema).optional(), + collectedPeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['collectedDateTime', 'collectedPeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const biologicallyDerivedProductProcessingSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + procedure: z.lazy(() => codeableConceptSchema).optional(), + additive: z.lazy(() => referenceSchema).optional(), + timeDateTime: dateTimeSchema.optional(), + _timeDateTime: z.lazy(() => elementSchema).optional(), + timePeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [{ names: ['timeDateTime', 'timePeriod'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const biologicallyDerivedProductManipulationSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + timeDateTime: dateTimeSchema.optional(), + _timeDateTime: z.lazy(() => elementSchema).optional(), + timePeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [{ names: ['timeDateTime', 'timePeriod'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const biologicallyDerivedProductStorageSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + temperature: decimalSchema.optional(), + _temperature: z.lazy(() => elementSchema).optional(), + scale: z.enum(['celsius', 'farenheit', 'kelvin']).optional(), + _scale: z.lazy(() => elementSchema).optional(), + duration: z.lazy(() => periodSchema).optional(), + }), + ) + +export const biologicallyDerivedProductSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('BiologicallyDerivedProduct'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + productCategory: z + .enum(['biologicalAgent', 'cells', 'fluid', 'organ', 'tissue']) + .optional(), + _productCategory: z.lazy(() => elementSchema).optional(), + productCode: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum(['available', 'unavailable']).optional(), + _status: z.lazy(() => elementSchema).optional(), + request: z + .lazy(() => referenceSchema) + .array() + .optional(), + quantity: integerSchema.optional(), + _quantity: z.lazy(() => elementSchema).optional(), + parent: z + .lazy(() => referenceSchema) + .array() + .optional(), + collection: biologicallyDerivedProductCollectionSchema.optional(), + processing: biologicallyDerivedProductProcessingSchema.array().optional(), + manipulation: biologicallyDerivedProductManipulationSchema.optional(), + storage: biologicallyDerivedProductStorageSchema.array().optional(), + }), + ) + +export const bodyStructureSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('BodyStructure'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + morphology: z.lazy(() => codeableConceptSchema).optional(), + location: z.lazy(() => codeableConceptSchema).optional(), + locationQualifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + image: z + .lazy(() => attachmentSchema) + .array() + .optional(), + patient: z.lazy(() => referenceSchema), + }), +) + +export const bundleLinkSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + relation: stringSchema, + _relation: z.lazy(() => elementSchema).optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + }), +) + +export const bundleEntrySchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + link: z + .lazy(() => bundleLinkSchema) + .array() + .optional(), + fullUrl: uriSchema.optional(), + _fullUrl: z.lazy(() => elementSchema).optional(), + resource: z.lazy(() => resourceSchema).optional(), + search: bundleEntrySearchSchema.optional(), + request: bundleEntryRequestSchema.optional(), + response: bundleEntryResponseSchema.optional(), + }), +) + +export const bundleEntrySearchSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + mode: z.enum(['include', 'match', 'outcome']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + score: decimalSchema.optional(), + _score: z.lazy(() => elementSchema).optional(), + }), +) + +export const bundleEntryRequestSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + method: z.enum(['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT']), + _method: z.lazy(() => elementSchema).optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + ifNoneMatch: stringSchema.optional(), + _ifNoneMatch: z.lazy(() => elementSchema).optional(), + ifModifiedSince: instantSchema.optional(), + _ifModifiedSince: z.lazy(() => elementSchema).optional(), + ifMatch: stringSchema.optional(), + _ifMatch: z.lazy(() => elementSchema).optional(), + ifNoneExist: stringSchema.optional(), + _ifNoneExist: z.lazy(() => elementSchema).optional(), + }), + ) + +export const bundleEntryResponseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + status: stringSchema, + _status: z.lazy(() => elementSchema).optional(), + location: uriSchema.optional(), + _location: z.lazy(() => elementSchema).optional(), + etag: stringSchema.optional(), + _etag: z.lazy(() => elementSchema).optional(), + lastModified: instantSchema.optional(), + _lastModified: z.lazy(() => elementSchema).optional(), + outcome: z.lazy(() => resourceSchema).optional(), + }), + ) + +export const bundleSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Bundle'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + type: z.enum([ + 'batch', + 'batch-response', + 'collection', + 'document', + 'history', + 'message', + 'searchset', + 'transaction', + 'transaction-response', + ]), + _type: z.lazy(() => elementSchema).optional(), + timestamp: instantSchema.optional(), + _timestamp: z.lazy(() => elementSchema).optional(), + total: unsignedIntSchema.optional(), + _total: z.lazy(() => elementSchema).optional(), + link: bundleLinkSchema.array().optional(), + entry: bundleEntrySchema.array().optional(), + signature: z.lazy(() => signatureSchema).optional(), + }), +) + +export const capabilityStatementSoftwareSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + releaseDate: dateTimeSchema.optional(), + _releaseDate: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementImplementationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema, + _description: z.lazy(() => elementSchema).optional(), + url: urlSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + custodian: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const capabilityStatementRestSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + mode: z.enum(['client', 'server']), + _mode: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + security: capabilityStatementRestSecuritySchema.optional(), + resource: capabilityStatementRestResourceSchema.array().optional(), + interaction: capabilityStatementRestInteractionSchema.array().optional(), + searchParam: z + .lazy(() => capabilityStatementRestResourceSearchParamSchema) + .array() + .optional(), + operation: z + .lazy(() => capabilityStatementRestResourceOperationSchema) + .array() + .optional(), + compartment: canonicalSchema.array().optional(), + _compartment: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const capabilityStatementMessagingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + endpoint: capabilityStatementMessagingEndpointSchema.array().optional(), + reliableCache: unsignedIntSchema.optional(), + _reliableCache: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + supportedMessage: capabilityStatementMessagingSupportedMessageSchema + .array() + .optional(), + }), + ) + +export const capabilityStatementDocumentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + mode: z.enum(['consumer', 'producer']), + _mode: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema, + _profile: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementRestSecuritySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + cors: booleanSchema.optional(), + _cors: z.lazy(() => elementSchema).optional(), + service: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementRestResourceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]), + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + supportedProfile: canonicalSchema.array().optional(), + _supportedProfile: z + .lazy(() => elementSchema) + .array() + .optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + interaction: capabilityStatementRestResourceInteractionSchema + .array() + .optional(), + versioning: z + .enum(['no-version', 'versioned', 'versioned-update']) + .optional(), + _versioning: z.lazy(() => elementSchema).optional(), + readHistory: booleanSchema.optional(), + _readHistory: z.lazy(() => elementSchema).optional(), + updateCreate: booleanSchema.optional(), + _updateCreate: z.lazy(() => elementSchema).optional(), + conditionalCreate: booleanSchema.optional(), + _conditionalCreate: z.lazy(() => elementSchema).optional(), + conditionalRead: z + .enum(['full-support', 'modified-since', 'not-match', 'not-supported']) + .optional(), + _conditionalRead: z.lazy(() => elementSchema).optional(), + conditionalUpdate: booleanSchema.optional(), + _conditionalUpdate: z.lazy(() => elementSchema).optional(), + conditionalDelete: z + .enum(['multiple', 'not-supported', 'single']) + .optional(), + _conditionalDelete: z.lazy(() => elementSchema).optional(), + referencePolicy: z + .enum(['enforced', 'literal', 'local', 'logical', 'resolves']) + .array() + .optional(), + _referencePolicy: z + .lazy(() => elementSchema) + .array() + .optional(), + searchInclude: stringSchema.array().optional(), + _searchInclude: z + .lazy(() => elementSchema) + .array() + .optional(), + searchRevInclude: stringSchema.array().optional(), + _searchRevInclude: z + .lazy(() => elementSchema) + .array() + .optional(), + searchParam: capabilityStatementRestResourceSearchParamSchema + .array() + .optional(), + operation: capabilityStatementRestResourceOperationSchema + .array() + .optional(), + }), + ) + +export const capabilityStatementRestInteractionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.enum(['batch', 'history-system', 'search-system', 'transaction']), + _code: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementRestResourceSearchParamSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + definition: canonicalSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + type: z.enum([ + 'composite', + 'date', + 'number', + 'quantity', + 'reference', + 'special', + 'string', + 'token', + 'uri', + ]), + _type: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementRestResourceOperationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + definition: canonicalSchema, + _definition: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementMessagingEndpointSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + protocol: z.lazy(() => codingSchema), + address: urlSchema, + _address: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementMessagingSupportedMessageSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + mode: z.enum(['receiver', 'sender']), + _mode: z.lazy(() => elementSchema).optional(), + definition: canonicalSchema, + _definition: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementRestResourceInteractionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.enum([ + 'create', + 'delete', + 'history-instance', + 'history-type', + 'patch', + 'read', + 'search-type', + 'update', + 'vread', + ]), + _code: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const capabilityStatementSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('CapabilityStatement'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema, + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + kind: z.enum(['capability', 'instance', 'requirements']), + _kind: z.lazy(() => elementSchema).optional(), + instantiates: canonicalSchema.array().optional(), + _instantiates: z + .lazy(() => elementSchema) + .array() + .optional(), + imports: canonicalSchema.array().optional(), + _imports: z + .lazy(() => elementSchema) + .array() + .optional(), + software: capabilityStatementSoftwareSchema.optional(), + implementation: capabilityStatementImplementationSchema.optional(), + fhirVersion: z.enum([ + '0.0.80', + '0.0.81', + '0.0.82', + '0.01', + '0.05', + '0.06', + '0.11', + '0.4.0', + '0.5.0', + '1.0.0', + '1.0.1', + '1.0.2', + '1.1.0', + '1.4.0', + '1.6.0', + '1.8.0', + '3.0.0', + '3.0.1', + '3.0.2', + '3.3.0', + '3.5.0', + '4.0.0', + '4.0.1', + '4.1.0', + '4.3.0', + '4.3.0-cibuild', + '4.3.0-snapshot1', + ]), + _fhirVersion: z.lazy(() => elementSchema).optional(), + format: codeSchema.array().min(1), + _format: z + .lazy(() => elementSchema) + .array() + .optional(), + patchFormat: codeSchema.array().optional(), + _patchFormat: z + .lazy(() => elementSchema) + .array() + .optional(), + implementationGuide: canonicalSchema.array().optional(), + _implementationGuide: z + .lazy(() => elementSchema) + .array() + .optional(), + rest: capabilityStatementRestSchema.array().optional(), + messaging: capabilityStatementMessagingSchema.array().optional(), + document: capabilityStatementDocumentSchema.array().optional(), + }), + ) + +export const carePlanActivitySchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + outcomeCodeableConcept: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + outcomeReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + progress: z + .lazy(() => annotationSchema) + .array() + .optional(), + reference: z.lazy(() => referenceSchema).optional(), + detail: carePlanActivityDetailSchema.optional(), + }), +) + +export const carePlanActivityDetailSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + kind: z + .enum([ + 'Appointment', + 'CommunicationRequest', + 'DeviceRequest', + 'MedicationRequest', + 'NutritionOrder', + 'ServiceRequest', + 'Task', + 'VisionPrescription', + ]) + .optional(), + _kind: z.lazy(() => elementSchema).optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + goal: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'cancelled', + 'completed', + 'entered-in-error', + 'in-progress', + 'not-started', + 'on-hold', + 'scheduled', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + scheduledTiming: z.lazy(() => timingSchema).optional(), + scheduledPeriod: z.lazy(() => periodSchema).optional(), + scheduledString: stringSchema.optional(), + _scheduledString: z.lazy(() => elementSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + performer: z + .lazy(() => referenceSchema) + .array() + .optional(), + productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + productReference: z.lazy(() => referenceSchema).optional(), + dailyAmount: z.lazy(() => quantitySchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['scheduledTiming', 'scheduledPeriod', 'scheduledString'], + required: false, + }, + { + names: ['productCodeableConcept', 'productReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const carePlanSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('CarePlan'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + replaces: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + intent: z.enum(['option', 'order', 'plan', 'proposal']), + _intent: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + contributor: z + .lazy(() => referenceSchema) + .array() + .optional(), + careTeam: z + .lazy(() => referenceSchema) + .array() + .optional(), + addresses: z + .lazy(() => referenceSchema) + .array() + .optional(), + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .optional(), + goal: z + .lazy(() => referenceSchema) + .array() + .optional(), + activity: carePlanActivitySchema.array().optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), +) + +export const careTeamParticipantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + role: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + member: z.lazy(() => referenceSchema).optional(), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), + ) + +export const careTeamSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('CareTeam'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z + .enum(['active', 'entered-in-error', 'inactive', 'proposed', 'suspended']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + participant: careTeamParticipantSchema.array().optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + managingOrganization: z + .lazy(() => referenceSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), +) + +export const catalogEntryRelatedEntrySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + relationtype: z.enum(['is-replaced-by', 'triggers']), + _relationtype: z.lazy(() => elementSchema).optional(), + item: z.lazy(() => referenceSchema), + }), + ) + +export const catalogEntrySchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('CatalogEntry'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + orderable: booleanSchema, + _orderable: z.lazy(() => elementSchema).optional(), + referencedItem: z.lazy(() => referenceSchema), + additionalIdentifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + classification: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + validityPeriod: z.lazy(() => periodSchema).optional(), + validTo: dateTimeSchema.optional(), + _validTo: z.lazy(() => elementSchema).optional(), + lastUpdated: dateTimeSchema.optional(), + _lastUpdated: z.lazy(() => elementSchema).optional(), + additionalCharacteristic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + additionalClassification: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + relatedEntry: catalogEntryRelatedEntrySchema.array().optional(), + }), +) + +export const chargeItemPerformerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }), + ) + +export const chargeItemSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ChargeItem'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + definitionUri: uriSchema.array().optional(), + _definitionUri: z + .lazy(() => elementSchema) + .array() + .optional(), + definitionCanonical: canonicalSchema.array().optional(), + _definitionCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + status: z.enum([ + 'aborted', + 'billable', + 'billed', + 'entered-in-error', + 'not-billable', + 'planned', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema), + context: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + performer: chargeItemPerformerSchema.array().optional(), + performingOrganization: z.lazy(() => referenceSchema).optional(), + requestingOrganization: z.lazy(() => referenceSchema).optional(), + costCenter: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + bodysite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + factorOverride: decimalSchema.optional(), + _factorOverride: z.lazy(() => elementSchema).optional(), + priceOverride: z.lazy(() => moneySchema).optional(), + overrideReason: stringSchema.optional(), + _overrideReason: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + enteredDate: dateTimeSchema.optional(), + _enteredDate: z.lazy(() => elementSchema).optional(), + reason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + service: z + .lazy(() => referenceSchema) + .array() + .optional(), + productReference: z.lazy(() => referenceSchema).optional(), + productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + account: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'occurrenceDateTime', + 'occurrencePeriod', + 'occurrenceTiming', + ], + required: false, + }, + { + names: ['productReference', 'productCodeableConcept'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const chargeItemDefinitionApplicabilitySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + language: stringSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + }), + ) + +export const chargeItemDefinitionPropertyGroupSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + applicability: z + .lazy(() => chargeItemDefinitionApplicabilitySchema) + .array() + .optional(), + priceComponent: chargeItemDefinitionPropertyGroupPriceComponentSchema + .array() + .optional(), + }), + ) + +export const chargeItemDefinitionPropertyGroupPriceComponentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'base', + 'deduction', + 'discount', + 'informational', + 'surcharge', + 'tax', + ]), + _type: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + }), + ) + +export const chargeItemDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('ChargeItemDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + derivedFromUri: uriSchema.array().optional(), + _derivedFromUri: z + .lazy(() => elementSchema) + .array() + .optional(), + partOf: canonicalSchema.array().optional(), + _partOf: z + .lazy(() => elementSchema) + .array() + .optional(), + replaces: canonicalSchema.array().optional(), + _replaces: z + .lazy(() => elementSchema) + .array() + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + instance: z + .lazy(() => referenceSchema) + .array() + .optional(), + applicability: chargeItemDefinitionApplicabilitySchema.array().optional(), + propertyGroup: chargeItemDefinitionPropertyGroupSchema.array().optional(), + }), + ) + +export const citationSummarySchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + style: z.lazy(() => codeableConceptSchema).optional(), + text: markdownSchema, + _text: z.lazy(() => elementSchema).optional(), + }), +) + +export const citationClassificationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + classifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), + ) + +export const citationStatusDateSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + activity: z.lazy(() => codeableConceptSchema), + actual: booleanSchema.optional(), + _actual: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema), + }), + ) + +export const citationRelatesToSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + relationshipType: z.lazy(() => codeableConceptSchema), + targetClassifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + targetUri: uriSchema.optional(), + _targetUri: z.lazy(() => elementSchema).optional(), + targetIdentifier: z.lazy(() => identifierSchema).optional(), + targetReference: z.lazy(() => referenceSchema).optional(), + targetAttachment: z.lazy(() => attachmentSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'targetUri', + 'targetIdentifier', + 'targetReference', + 'targetAttachment', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const citationCitedArtifactSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + relatedIdentifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + dateAccessed: dateTimeSchema.optional(), + _dateAccessed: z.lazy(() => elementSchema).optional(), + version: citationCitedArtifactVersionSchema.optional(), + currentState: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + statusDate: citationCitedArtifactStatusDateSchema.array().optional(), + title: citationCitedArtifactTitleSchema.array().optional(), + abstract: citationCitedArtifactAbstractSchema.array().optional(), + part: citationCitedArtifactPartSchema.optional(), + relatesTo: citationCitedArtifactRelatesToSchema.array().optional(), + publicationForm: citationCitedArtifactPublicationFormSchema + .array() + .optional(), + webLocation: citationCitedArtifactWebLocationSchema.array().optional(), + classification: citationCitedArtifactClassificationSchema + .array() + .optional(), + contributorship: citationCitedArtifactContributorshipSchema.optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), + ) + +export const citationCitedArtifactVersionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + baseCitation: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const citationCitedArtifactStatusDateSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + activity: z.lazy(() => codeableConceptSchema), + actual: booleanSchema.optional(), + _actual: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema), + }), + ) + +export const citationCitedArtifactTitleSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + language: z.lazy(() => codeableConceptSchema).optional(), + text: markdownSchema, + _text: z.lazy(() => elementSchema).optional(), + }), + ) + +export const citationCitedArtifactAbstractSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + language: z.lazy(() => codeableConceptSchema).optional(), + text: markdownSchema, + _text: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + }), + ) + +export const citationCitedArtifactPartSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + baseCitation: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const citationCitedArtifactRelatesToSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + relationshipType: z.lazy(() => codeableConceptSchema), + targetClassifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + targetUri: uriSchema.optional(), + _targetUri: z.lazy(() => elementSchema).optional(), + targetIdentifier: z.lazy(() => identifierSchema).optional(), + targetReference: z.lazy(() => referenceSchema).optional(), + targetAttachment: z.lazy(() => attachmentSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'targetUri', + 'targetIdentifier', + 'targetReference', + 'targetAttachment', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const citationCitedArtifactPublicationFormSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + publishedIn: + citationCitedArtifactPublicationFormPublishedInSchema.optional(), + periodicRelease: + citationCitedArtifactPublicationFormPeriodicReleaseSchema.optional(), + articleDate: dateTimeSchema.optional(), + _articleDate: z.lazy(() => elementSchema).optional(), + lastRevisionDate: dateTimeSchema.optional(), + _lastRevisionDate: z.lazy(() => elementSchema).optional(), + language: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + accessionNumber: stringSchema.optional(), + _accessionNumber: z.lazy(() => elementSchema).optional(), + pageString: stringSchema.optional(), + _pageString: z.lazy(() => elementSchema).optional(), + firstPage: stringSchema.optional(), + _firstPage: z.lazy(() => elementSchema).optional(), + lastPage: stringSchema.optional(), + _lastPage: z.lazy(() => elementSchema).optional(), + pageCount: stringSchema.optional(), + _pageCount: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + }), + ) + +export const citationCitedArtifactWebLocationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + }), + ) + +export const citationCitedArtifactClassificationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + classifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + whoClassified: + citationCitedArtifactClassificationWhoClassifiedSchema.optional(), + }), + ) + +export const citationCitedArtifactContributorshipSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + complete: booleanSchema.optional(), + _complete: z.lazy(() => elementSchema).optional(), + entry: citationCitedArtifactContributorshipEntrySchema.array().optional(), + summary: citationCitedArtifactContributorshipSummarySchema + .array() + .optional(), + }), + ) + +export const citationCitedArtifactPublicationFormPublishedInSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + publisher: z.lazy(() => referenceSchema).optional(), + publisherLocation: stringSchema.optional(), + _publisherLocation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const citationCitedArtifactPublicationFormPeriodicReleaseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + citedMedium: z.lazy(() => codeableConceptSchema).optional(), + volume: stringSchema.optional(), + _volume: z.lazy(() => elementSchema).optional(), + issue: stringSchema.optional(), + _issue: z.lazy(() => elementSchema).optional(), + dateOfPublication: + citationCitedArtifactPublicationFormPeriodicReleaseDateOfPublicationSchema.optional(), + }), + ) + +export const citationCitedArtifactClassificationWhoClassifiedSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + person: z.lazy(() => referenceSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + publisher: z.lazy(() => referenceSchema).optional(), + classifierCopyright: stringSchema.optional(), + _classifierCopyright: z.lazy(() => elementSchema).optional(), + freeToShare: booleanSchema.optional(), + _freeToShare: z.lazy(() => elementSchema).optional(), + }), + ) + +export const citationCitedArtifactContributorshipEntrySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: z.lazy(() => humanNameSchema).optional(), + initials: stringSchema.optional(), + _initials: z.lazy(() => elementSchema).optional(), + collectiveName: stringSchema.optional(), + _collectiveName: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + affiliationInfo: + citationCitedArtifactContributorshipEntryAffiliationInfoSchema + .array() + .optional(), + address: z + .lazy(() => addressSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + contributionType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + contributionInstance: + citationCitedArtifactContributorshipEntryContributionInstanceSchema + .array() + .optional(), + correspondingContact: booleanSchema.optional(), + _correspondingContact: z.lazy(() => elementSchema).optional(), + listOrder: positiveIntSchema.optional(), + _listOrder: z.lazy(() => elementSchema).optional(), + }), + ) + +export const citationCitedArtifactContributorshipSummarySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + style: z.lazy(() => codeableConceptSchema).optional(), + source: z.lazy(() => codeableConceptSchema).optional(), + value: markdownSchema, + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const citationCitedArtifactPublicationFormPeriodicReleaseDateOfPublicationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + year: stringSchema.optional(), + _year: z.lazy(() => elementSchema).optional(), + month: stringSchema.optional(), + _month: z.lazy(() => elementSchema).optional(), + day: stringSchema.optional(), + _day: z.lazy(() => elementSchema).optional(), + season: stringSchema.optional(), + _season: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }), + ) + +export const citationCitedArtifactContributorshipEntryAffiliationInfoSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + affiliation: stringSchema.optional(), + _affiliation: z.lazy(() => elementSchema).optional(), + role: stringSchema.optional(), + _role: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + }), + ) + +export const citationCitedArtifactContributorshipEntryContributionInstanceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + time: dateTimeSchema.optional(), + _time: z.lazy(() => elementSchema).optional(), + }), + ) + +export const citationSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Citation'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + summary: citationSummarySchema.array().optional(), + classification: citationClassificationSchema.array().optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + currentState: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + statusDate: citationStatusDateSchema.array().optional(), + relatesTo: citationRelatesToSchema.array().optional(), + citedArtifact: citationCitedArtifactSchema.optional(), + }), +) + +export const claimRelatedSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + claim: z.lazy(() => referenceSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema).optional(), + reference: z.lazy(() => identifierSchema).optional(), + }), +) + +export const claimPayeeSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + party: z.lazy(() => referenceSchema).optional(), + }), +) + +export const claimCareTeamSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + provider: z.lazy(() => referenceSchema), + responsible: booleanSchema.optional(), + _responsible: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + qualification: z.lazy(() => codeableConceptSchema).optional(), + }), +) + +export const claimSupportingInfoSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + category: z.lazy(() => codeableConceptSchema), + code: z.lazy(() => codeableConceptSchema).optional(), + timingDate: dateSchema.optional(), + _timingDate: z.lazy(() => elementSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + reason: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['timingDate', 'timingPeriod'], required: false }, + { + names: [ + 'valueBoolean', + 'valueString', + 'valueQuantity', + 'valueAttachment', + 'valueReference', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const claimDiagnosisSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + diagnosisCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + diagnosisReference: z.lazy(() => referenceSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + onAdmission: z.lazy(() => codeableConceptSchema).optional(), + packageCode: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['diagnosisCodeableConcept', 'diagnosisReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const claimProcedureSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + procedureCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + procedureReference: z.lazy(() => referenceSchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['procedureCodeableConcept', 'procedureReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const claimInsuranceSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + focal: booleanSchema, + _focal: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + coverage: z.lazy(() => referenceSchema), + businessArrangement: stringSchema.optional(), + _businessArrangement: z.lazy(() => elementSchema).optional(), + preAuthRef: stringSchema.array().optional(), + _preAuthRef: z + .lazy(() => elementSchema) + .array() + .optional(), + claimResponse: z.lazy(() => referenceSchema).optional(), + }), +) + +export const claimAccidentSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + date: dateSchema, + _date: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['locationAddress', 'locationReference'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const claimItemSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + careTeamSequence: positiveIntSchema.array().optional(), + _careTeamSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + diagnosisSequence: positiveIntSchema.array().optional(), + _diagnosisSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + procedureSequence: positiveIntSchema.array().optional(), + _procedureSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + informationSequence: positiveIntSchema.array().optional(), + _informationSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + encounter: z + .lazy(() => referenceSchema) + .array() + .optional(), + detail: claimItemDetailSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['servicedDate', 'servicedPeriod'], required: false }, + { + names: [ + 'locationCodeableConcept', + 'locationAddress', + 'locationReference', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const claimItemDetailSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + subDetail: claimItemDetailSubDetailSchema.array().optional(), + }), +) + +export const claimItemDetailSubDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const claimSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Claim'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + use: z.enum(['claim', 'preauthorization', 'predetermination']), + _use: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + billablePeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + insurer: z.lazy(() => referenceSchema).optional(), + provider: z.lazy(() => referenceSchema), + priority: z.lazy(() => codeableConceptSchema), + fundsReserve: z.lazy(() => codeableConceptSchema).optional(), + related: claimRelatedSchema.array().optional(), + prescription: z.lazy(() => referenceSchema).optional(), + originalPrescription: z.lazy(() => referenceSchema).optional(), + payee: claimPayeeSchema.optional(), + referral: z.lazy(() => referenceSchema).optional(), + facility: z.lazy(() => referenceSchema).optional(), + careTeam: claimCareTeamSchema.array().optional(), + supportingInfo: claimSupportingInfoSchema.array().optional(), + diagnosis: claimDiagnosisSchema.array().optional(), + procedure: claimProcedureSchema.array().optional(), + insurance: claimInsuranceSchema.array().min(1), + accident: claimAccidentSchema.optional(), + item: claimItemSchema.array().optional(), + total: z.lazy(() => moneySchema).optional(), + }), +) + +export const claimResponseItemSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemSequence: positiveIntSchema, + _itemSequence: z.lazy(() => elementSchema).optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: claimResponseItemAdjudicationSchema.array().min(1), + detail: claimResponseItemDetailSchema.array().optional(), + }), +) + +export const claimResponseAddItemSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemSequence: positiveIntSchema.array().optional(), + _itemSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + detailSequence: positiveIntSchema.array().optional(), + _detailSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + subdetailSequence: positiveIntSchema.array().optional(), + _subdetailSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + provider: z + .lazy(() => referenceSchema) + .array() + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1), + detail: claimResponseAddItemDetailSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['servicedDate', 'servicedPeriod'], required: false }, + { + names: [ + 'locationCodeableConcept', + 'locationAddress', + 'locationReference', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const claimResponseItemAdjudicationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema), + reason: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const claimResponseTotalSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema), + amount: z.lazy(() => moneySchema), + }), + ) + +export const claimResponsePaymentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + adjustment: z.lazy(() => moneySchema).optional(), + adjustmentReason: z.lazy(() => codeableConceptSchema).optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema), + identifier: z.lazy(() => identifierSchema).optional(), + }), + ) + +export const claimResponseProcessNoteSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + number: positiveIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + type: z.enum(['display', 'print', 'printoper']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema, + _text: z.lazy(() => elementSchema).optional(), + language: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const claimResponseInsuranceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + focal: booleanSchema, + _focal: z.lazy(() => elementSchema).optional(), + coverage: z.lazy(() => referenceSchema), + businessArrangement: stringSchema.optional(), + _businessArrangement: z.lazy(() => elementSchema).optional(), + claimResponse: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const claimResponseErrorSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemSequence: positiveIntSchema.optional(), + _itemSequence: z.lazy(() => elementSchema).optional(), + detailSequence: positiveIntSchema.optional(), + _detailSequence: z.lazy(() => elementSchema).optional(), + subDetailSequence: positiveIntSchema.optional(), + _subDetailSequence: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema), + }), + ) + +export const claimResponseItemDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + detailSequence: positiveIntSchema, + _detailSequence: z.lazy(() => elementSchema).optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1), + subDetail: claimResponseItemDetailSubDetailSchema.array().optional(), + }), + ) + +export const claimResponseAddItemDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1), + subDetail: claimResponseAddItemDetailSubDetailSchema.array().optional(), + }), + ) + +export const claimResponseItemDetailSubDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + subDetailSequence: positiveIntSchema, + _subDetailSequence: z.lazy(() => elementSchema).optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .optional(), + }), + ) + +export const claimResponseAddItemDetailSubDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1), + }), + ) + +export const claimResponseSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('ClaimResponse'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + use: z.enum(['claim', 'preauthorization', 'predetermination']), + _use: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + insurer: z.lazy(() => referenceSchema), + requestor: z.lazy(() => referenceSchema).optional(), + request: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + preAuthRef: stringSchema.optional(), + _preAuthRef: z.lazy(() => elementSchema).optional(), + preAuthPeriod: z.lazy(() => periodSchema).optional(), + payeeType: z.lazy(() => codeableConceptSchema).optional(), + item: claimResponseItemSchema.array().optional(), + addItem: claimResponseAddItemSchema.array().optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .optional(), + total: claimResponseTotalSchema.array().optional(), + payment: claimResponsePaymentSchema.optional(), + fundsReserve: z.lazy(() => codeableConceptSchema).optional(), + formCode: z.lazy(() => codeableConceptSchema).optional(), + form: z.lazy(() => attachmentSchema).optional(), + processNote: claimResponseProcessNoteSchema.array().optional(), + communicationRequest: z + .lazy(() => referenceSchema) + .array() + .optional(), + insurance: claimResponseInsuranceSchema.array().optional(), + error: claimResponseErrorSchema.array().optional(), + }), +) + +export const clinicalImpressionInvestigationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + item: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const clinicalImpressionFindingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + basis: stringSchema.optional(), + _basis: z.lazy(() => elementSchema).optional(), + }), + ) + +export const clinicalImpressionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ClinicalImpression'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['completed', 'entered-in-error', 'in-progress']), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + assessor: z.lazy(() => referenceSchema).optional(), + previous: z.lazy(() => referenceSchema).optional(), + problem: z + .lazy(() => referenceSchema) + .array() + .optional(), + investigation: clinicalImpressionInvestigationSchema.array().optional(), + protocol: uriSchema.array().optional(), + _protocol: z + .lazy(() => elementSchema) + .array() + .optional(), + summary: stringSchema.optional(), + _summary: z.lazy(() => elementSchema).optional(), + finding: clinicalImpressionFindingSchema.array().optional(), + prognosisCodeableConcept: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + prognosisReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['effectiveDateTime', 'effectivePeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const clinicalUseDefinitionContraindicationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + diseaseSymptomProcedure: z.lazy(() => codeableReferenceSchema).optional(), + diseaseStatus: z.lazy(() => codeableReferenceSchema).optional(), + comorbidity: z + .lazy(() => codeableReferenceSchema) + .array() + .optional(), + indication: z + .lazy(() => referenceSchema) + .array() + .optional(), + otherTherapy: clinicalUseDefinitionContraindicationOtherTherapySchema + .array() + .optional(), + }), + ) + +export const clinicalUseDefinitionIndicationSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + diseaseSymptomProcedure: z + .lazy(() => codeableReferenceSchema) + .optional(), + diseaseStatus: z.lazy(() => codeableReferenceSchema).optional(), + comorbidity: z + .lazy(() => codeableReferenceSchema) + .array() + .optional(), + intendedEffect: z.lazy(() => codeableReferenceSchema).optional(), + durationRange: z.lazy(() => rangeSchema).optional(), + durationString: stringSchema.optional(), + _durationString: z.lazy(() => elementSchema).optional(), + undesirableEffect: z + .lazy(() => referenceSchema) + .array() + .optional(), + otherTherapy: z + .lazy(() => clinicalUseDefinitionContraindicationOtherTherapySchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { names: ['durationRange', 'durationString'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const clinicalUseDefinitionInteractionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + interactant: clinicalUseDefinitionInteractionInteractantSchema + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + effect: z.lazy(() => codeableReferenceSchema).optional(), + incidence: z.lazy(() => codeableConceptSchema).optional(), + management: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), + ) + +export const clinicalUseDefinitionUndesirableEffectSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + symptomConditionEffect: z.lazy(() => codeableReferenceSchema).optional(), + classification: z.lazy(() => codeableConceptSchema).optional(), + frequencyOfOccurrence: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const clinicalUseDefinitionWarningSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const clinicalUseDefinitionContraindicationOtherTherapySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + relationshipType: z.lazy(() => codeableConceptSchema), + therapy: z.lazy(() => codeableReferenceSchema), + }), + ) + +export const clinicalUseDefinitionInteractionInteractantSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['itemReference', 'itemCodeableConcept'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const clinicalUseDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('ClinicalUseDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + type: z.enum([ + 'contraindication', + 'indication', + 'interaction', + 'undesirable-effect', + 'warning', + ]), + _type: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + subject: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + contraindication: clinicalUseDefinitionContraindicationSchema.optional(), + indication: clinicalUseDefinitionIndicationSchema.optional(), + interaction: clinicalUseDefinitionInteractionSchema.optional(), + population: z + .lazy(() => referenceSchema) + .array() + .optional(), + undesirableEffect: + clinicalUseDefinitionUndesirableEffectSchema.optional(), + warning: clinicalUseDefinitionWarningSchema.optional(), + }), + ) + +export const codeSystemFilterSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + operator: z + .enum([ + '=', + 'descendent-of', + 'exists', + 'generalizes', + 'in', + 'is-a', + 'is-not-a', + 'not-in', + 'regex', + ]) + .array() + .min(1), + _operator: z + .lazy(() => elementSchema) + .array() + .optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + }), +) + +export const codeSystemPropertySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + uri: uriSchema.optional(), + _uri: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + type: z.enum([ + 'Coding', + 'boolean', + 'code', + 'dateTime', + 'decimal', + 'integer', + 'string', + ]), + _type: z.lazy(() => elementSchema).optional(), + }), + ) + +export const codeSystemConceptSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + definition: stringSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + designation: codeSystemConceptDesignationSchema.array().optional(), + property: codeSystemConceptPropertySchema.array().optional(), + concept: z + .lazy(() => codeSystemConceptSchema) + .array() + .optional(), + }), +) + +export const codeSystemConceptDesignationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + use: z.lazy(() => codingSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const codeSystemConceptPropertySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCode', + 'valueCoding', + 'valueString', + 'valueInteger', + 'valueBoolean', + 'valueDateTime', + 'valueDecimal', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const codeSystemSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('CodeSystem'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + caseSensitive: booleanSchema.optional(), + _caseSensitive: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema.optional(), + _valueSet: z.lazy(() => elementSchema).optional(), + hierarchyMeaning: z + .enum(['classified-with', 'grouped-by', 'is-a', 'part-of']) + .optional(), + _hierarchyMeaning: z.lazy(() => elementSchema).optional(), + compositional: booleanSchema.optional(), + _compositional: z.lazy(() => elementSchema).optional(), + versionNeeded: booleanSchema.optional(), + _versionNeeded: z.lazy(() => elementSchema).optional(), + content: z.enum([ + 'complete', + 'example', + 'fragment', + 'not-present', + 'supplement', + ]), + _content: z.lazy(() => elementSchema).optional(), + supplements: canonicalSchema.optional(), + _supplements: z.lazy(() => elementSchema).optional(), + count: unsignedIntSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + filter: codeSystemFilterSchema.array().optional(), + property: codeSystemPropertySchema.array().optional(), + concept: codeSystemConceptSchema.array().optional(), + }), +) + +export const codeableConceptSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + coding: z + .lazy(() => codingSchema) + .array() + .optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }), +) + +export const codeableReferenceSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + concept: z.lazy(() => codeableConceptSchema).optional(), + reference: z.lazy(() => referenceSchema).optional(), + }), +) + +export const codingSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + userSelected: booleanSchema.optional(), + _userSelected: z.lazy(() => elementSchema).optional(), + }), +) + +export const communicationPayloadSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + contentString: stringSchema.optional(), + _contentString: z.lazy(() => elementSchema).optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['contentString', 'contentAttachment', 'contentReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const communicationSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Communication'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + inResponseTo: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + medium: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + subject: z.lazy(() => referenceSchema).optional(), + topic: z.lazy(() => codeableConceptSchema).optional(), + about: z + .lazy(() => referenceSchema) + .array() + .optional(), + encounter: z.lazy(() => referenceSchema).optional(), + sent: dateTimeSchema.optional(), + _sent: z.lazy(() => elementSchema).optional(), + received: dateTimeSchema.optional(), + _received: z.lazy(() => elementSchema).optional(), + recipient: z + .lazy(() => referenceSchema) + .array() + .optional(), + sender: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + payload: communicationPayloadSchema.array().optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), +) + +export const communicationRequestPayloadSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + contentString: stringSchema.optional(), + _contentString: z.lazy(() => elementSchema).optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['contentString', 'contentAttachment', 'contentReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const communicationRequestSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CommunicationRequest'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + replaces: z + .lazy(() => referenceSchema) + .array() + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + status: z.enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + medium: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + subject: z.lazy(() => referenceSchema).optional(), + about: z + .lazy(() => referenceSchema) + .array() + .optional(), + encounter: z.lazy(() => referenceSchema).optional(), + payload: communicationRequestPayloadSchema.array().optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + recipient: z + .lazy(() => referenceSchema) + .array() + .optional(), + sender: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['occurrenceDateTime', 'occurrencePeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const compartmentDefinitionResourceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]), + _code: z.lazy(() => elementSchema).optional(), + param: stringSchema.array().optional(), + _param: z + .lazy(() => elementSchema) + .array() + .optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const compartmentDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('CompartmentDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + code: z.enum([ + 'Device', + 'Encounter', + 'Patient', + 'Practitioner', + 'RelatedPerson', + ]), + _code: z.lazy(() => elementSchema).optional(), + search: booleanSchema, + _search: z.lazy(() => elementSchema).optional(), + resource: compartmentDefinitionResourceSchema.array().optional(), + }), + ) + +export const compositionAttesterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + mode: z.enum(['legal', 'official', 'personal', 'professional']), + _mode: z.lazy(() => elementSchema).optional(), + time: dateTimeSchema.optional(), + _time: z.lazy(() => elementSchema).optional(), + party: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const compositionRelatesToSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.enum(['appends', 'replaces', 'signs', 'transforms']), + _code: z.lazy(() => elementSchema).optional(), + targetIdentifier: z.lazy(() => identifierSchema).optional(), + targetReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['targetIdentifier', 'targetReference'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const compositionEventSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + detail: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const compositionSectionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + author: z + .lazy(() => referenceSchema) + .array() + .optional(), + focus: z.lazy(() => referenceSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + mode: z.enum(['changes', 'snapshot', 'working']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + orderedBy: z.lazy(() => codeableConceptSchema).optional(), + entry: z + .lazy(() => referenceSchema) + .array() + .optional(), + emptyReason: z.lazy(() => codeableConceptSchema).optional(), + section: z + .lazy(() => compositionSectionSchema) + .array() + .optional(), + }), + ) + +export const compositionSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Composition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + status: z.enum(['amended', 'entered-in-error', 'final', 'preliminary']), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema, + _date: z.lazy(() => elementSchema).optional(), + author: z + .lazy(() => referenceSchema) + .array() + .min(1), + title: stringSchema, + _title: z.lazy(() => elementSchema).optional(), + confidentiality: codeSchema.optional(), + _confidentiality: z.lazy(() => elementSchema).optional(), + attester: compositionAttesterSchema.array().optional(), + custodian: z.lazy(() => referenceSchema).optional(), + relatesTo: compositionRelatesToSchema.array().optional(), + event: compositionEventSchema.array().optional(), + section: compositionSectionSchema.array().optional(), + }), +) + +export const conceptMapGroupSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + source: uriSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + sourceVersion: stringSchema.optional(), + _sourceVersion: z.lazy(() => elementSchema).optional(), + target: uriSchema.optional(), + _target: z.lazy(() => elementSchema).optional(), + targetVersion: stringSchema.optional(), + _targetVersion: z.lazy(() => elementSchema).optional(), + element: conceptMapGroupElementSchema.array().min(1), + unmapped: conceptMapGroupUnmappedSchema.optional(), + }), +) + +export const conceptMapGroupElementSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + target: conceptMapGroupElementTargetSchema.array().optional(), + }), + ) + +export const conceptMapGroupUnmappedSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + mode: z.enum(['fixed', 'other-map', 'provided']), + _mode: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + url: canonicalSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + }), + ) + +export const conceptMapGroupElementTargetSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + equivalence: z.enum([ + 'disjoint', + 'equal', + 'equivalent', + 'inexact', + 'narrower', + 'relatedto', + 'specializes', + 'subsumes', + 'unmatched', + 'wider', + ]), + _equivalence: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + dependsOn: conceptMapGroupElementTargetDependsOnSchema.array().optional(), + product: z + .lazy(() => conceptMapGroupElementTargetDependsOnSchema) + .array() + .optional(), + }), + ) + +export const conceptMapGroupElementTargetDependsOnSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + property: uriSchema, + _property: z.lazy(() => elementSchema).optional(), + system: canonicalSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + }), + ) + +export const conceptMapSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ConceptMap'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + sourceUri: uriSchema.optional(), + _sourceUri: z.lazy(() => elementSchema).optional(), + sourceCanonical: canonicalSchema.optional(), + _sourceCanonical: z.lazy(() => elementSchema).optional(), + targetUri: uriSchema.optional(), + _targetUri: z.lazy(() => elementSchema).optional(), + targetCanonical: canonicalSchema.optional(), + _targetCanonical: z.lazy(() => elementSchema).optional(), + group: conceptMapGroupSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['sourceUri', 'sourceCanonical'], required: false }, + { names: ['targetUri', 'targetCanonical'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const conditionStageSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + summary: z.lazy(() => codeableConceptSchema).optional(), + assessment: z + .lazy(() => referenceSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + }), +) + +export const conditionEvidenceSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + detail: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const conditionSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Condition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + clinicalStatus: z.lazy(() => codeableConceptSchema).optional(), + verificationStatus: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + severity: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + bodySite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + onsetDateTime: dateTimeSchema.optional(), + _onsetDateTime: z.lazy(() => elementSchema).optional(), + onsetAge: z.lazy(() => ageSchema).optional(), + onsetPeriod: z.lazy(() => periodSchema).optional(), + onsetRange: z.lazy(() => rangeSchema).optional(), + onsetString: stringSchema.optional(), + _onsetString: z.lazy(() => elementSchema).optional(), + abatementDateTime: dateTimeSchema.optional(), + _abatementDateTime: z.lazy(() => elementSchema).optional(), + abatementAge: z.lazy(() => ageSchema).optional(), + abatementPeriod: z.lazy(() => periodSchema).optional(), + abatementRange: z.lazy(() => rangeSchema).optional(), + abatementString: stringSchema.optional(), + _abatementString: z.lazy(() => elementSchema).optional(), + recordedDate: dateTimeSchema.optional(), + _recordedDate: z.lazy(() => elementSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + asserter: z.lazy(() => referenceSchema).optional(), + stage: conditionStageSchema.array().optional(), + evidence: conditionEvidenceSchema.array().optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'onsetDateTime', + 'onsetAge', + 'onsetPeriod', + 'onsetRange', + 'onsetString', + ], + required: false, + }, + { + names: [ + 'abatementDateTime', + 'abatementAge', + 'abatementPeriod', + 'abatementRange', + 'abatementString', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const consentPolicySchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + authority: uriSchema.optional(), + _authority: z.lazy(() => elementSchema).optional(), + uri: uriSchema.optional(), + _uri: z.lazy(() => elementSchema).optional(), + }), +) + +export const consentVerificationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + verified: booleanSchema, + _verified: z.lazy(() => elementSchema).optional(), + verifiedWith: z.lazy(() => referenceSchema).optional(), + verificationDate: dateTimeSchema.optional(), + _verificationDate: z.lazy(() => elementSchema).optional(), + }), + ) + +export const consentProvisionSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['deny', 'permit']).optional(), + _type: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + actor: consentProvisionActorSchema.array().optional(), + action: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + securityLabel: z + .lazy(() => codingSchema) + .array() + .optional(), + purpose: z + .lazy(() => codingSchema) + .array() + .optional(), + class: z + .lazy(() => codingSchema) + .array() + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + dataPeriod: z.lazy(() => periodSchema).optional(), + data: consentProvisionDataSchema.array().optional(), + provision: z + .lazy(() => consentProvisionSchema) + .array() + .optional(), + }), +) + +export const consentProvisionActorSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + role: z.lazy(() => codeableConceptSchema), + reference: z.lazy(() => referenceSchema), + }), + ) + +export const consentProvisionDataSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + meaning: z.enum(['authoredby', 'dependents', 'instance', 'related']), + _meaning: z.lazy(() => elementSchema).optional(), + reference: z.lazy(() => referenceSchema), + }), + ) + +export const consentSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Consent'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'draft', + 'entered-in-error', + 'inactive', + 'proposed', + 'rejected', + ]), + _status: z.lazy(() => elementSchema).optional(), + scope: z.lazy(() => codeableConceptSchema), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1), + patient: z.lazy(() => referenceSchema).optional(), + dateTime: dateTimeSchema.optional(), + _dateTime: z.lazy(() => elementSchema).optional(), + performer: z + .lazy(() => referenceSchema) + .array() + .optional(), + organization: z + .lazy(() => referenceSchema) + .array() + .optional(), + sourceAttachment: z.lazy(() => attachmentSchema).optional(), + sourceReference: z.lazy(() => referenceSchema).optional(), + policy: consentPolicySchema.array().optional(), + policyRule: z.lazy(() => codeableConceptSchema).optional(), + verification: consentVerificationSchema.array().optional(), + provision: consentProvisionSchema.optional(), + }) + .refine( + (value) => + [ + { names: ['sourceAttachment', 'sourceReference'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const contactDetailSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + }), +) + +export const contactPointSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + system: z + .enum(['email', 'fax', 'other', 'pager', 'phone', 'sms', 'url']) + .optional(), + _system: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + use: z.enum(['home', 'mobile', 'old', 'temp', 'work']).optional(), + _use: z.lazy(() => elementSchema).optional(), + rank: positiveIntSchema.optional(), + _rank: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), +) + +export const contractContentDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + publisher: z.lazy(() => referenceSchema).optional(), + publicationDate: dateTimeSchema.optional(), + _publicationDate: z.lazy(() => elementSchema).optional(), + publicationStatus: z.enum([ + 'amended', + 'appended', + 'cancelled', + 'disputed', + 'entered-in-error', + 'executable', + 'executed', + 'negotiable', + 'offered', + 'policy', + 'rejected', + 'renewed', + 'resolved', + 'revoked', + 'terminated', + ]), + _publicationStatus: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + }), + ) + +export const contractTermSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + issued: dateTimeSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + applies: z.lazy(() => periodSchema).optional(), + topicCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + topicReference: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subType: z.lazy(() => codeableConceptSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + securityLabel: contractTermSecurityLabelSchema.array().optional(), + offer: contractTermOfferSchema, + asset: contractTermAssetSchema.array().optional(), + action: contractTermActionSchema.array().optional(), + group: z + .lazy(() => contractTermSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['topicCodeableConcept', 'topicReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const contractSignerSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codingSchema), + party: z.lazy(() => referenceSchema), + signature: z + .lazy(() => signatureSchema) + .array() + .min(1), + }), +) + +export const contractFriendlySchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['contentAttachment', 'contentReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const contractLegalSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['contentAttachment', 'contentReference'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const contractRuleSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['contentAttachment', 'contentReference'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const contractTermSecurityLabelSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + number: unsignedIntSchema.array().optional(), + _number: z + .lazy(() => elementSchema) + .array() + .optional(), + classification: z.lazy(() => codingSchema), + category: z + .lazy(() => codingSchema) + .array() + .optional(), + control: z + .lazy(() => codingSchema) + .array() + .optional(), + }), + ) + +export const contractTermOfferSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + party: contractTermOfferPartySchema.array().optional(), + topic: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + decision: z.lazy(() => codeableConceptSchema).optional(), + decisionMode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + answer: contractTermOfferAnswerSchema.array().optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + linkId: stringSchema.array().optional(), + _linkId: z + .lazy(() => elementSchema) + .array() + .optional(), + securityLabelNumber: unsignedIntSchema.array().optional(), + _securityLabelNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + }), +) + +export const contractTermAssetSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + scope: z.lazy(() => codeableConceptSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + typeReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + subtype: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + relationship: z.lazy(() => codingSchema).optional(), + context: contractTermAssetContextSchema.array().optional(), + condition: stringSchema.optional(), + _condition: z.lazy(() => elementSchema).optional(), + periodType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + period: z + .lazy(() => periodSchema) + .array() + .optional(), + usePeriod: z + .lazy(() => periodSchema) + .array() + .optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + linkId: stringSchema.array().optional(), + _linkId: z + .lazy(() => elementSchema) + .array() + .optional(), + answer: z + .lazy(() => contractTermOfferAnswerSchema) + .array() + .optional(), + securityLabelNumber: unsignedIntSchema.array().optional(), + _securityLabelNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + valuedItem: contractTermAssetValuedItemSchema.array().optional(), + }), +) + +export const contractTermActionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subject: contractTermActionSubjectSchema.array().optional(), + intent: z.lazy(() => codeableConceptSchema), + linkId: stringSchema.array().optional(), + _linkId: z + .lazy(() => elementSchema) + .array() + .optional(), + status: z.lazy(() => codeableConceptSchema), + context: z.lazy(() => referenceSchema).optional(), + contextLinkId: stringSchema.array().optional(), + _contextLinkId: z + .lazy(() => elementSchema) + .array() + .optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + requester: z + .lazy(() => referenceSchema) + .array() + .optional(), + requesterLinkId: stringSchema.array().optional(), + _requesterLinkId: z + .lazy(() => elementSchema) + .array() + .optional(), + performerType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + performerRole: z.lazy(() => codeableConceptSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + performerLinkId: stringSchema.array().optional(), + _performerLinkId: z + .lazy(() => elementSchema) + .array() + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + reason: stringSchema.array().optional(), + _reason: z + .lazy(() => elementSchema) + .array() + .optional(), + reasonLinkId: stringSchema.array().optional(), + _reasonLinkId: z + .lazy(() => elementSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + securityLabelNumber: unsignedIntSchema.array().optional(), + _securityLabelNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'occurrenceDateTime', + 'occurrencePeriod', + 'occurrenceTiming', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const contractTermOfferPartySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + reference: z + .lazy(() => referenceSchema) + .array() + .min(1), + role: z.lazy(() => codeableConceptSchema), + }), + ) + +export const contractTermOfferAnswerSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBoolean', + 'valueDecimal', + 'valueInteger', + 'valueDate', + 'valueDateTime', + 'valueTime', + 'valueString', + 'valueUri', + 'valueAttachment', + 'valueCoding', + 'valueQuantity', + 'valueReference', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const contractTermAssetContextSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + reference: z.lazy(() => referenceSchema).optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }), + ) + +export const contractTermAssetValuedItemSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + entityCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + entityReference: z.lazy(() => referenceSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + effectiveTime: dateTimeSchema.optional(), + _effectiveTime: z.lazy(() => elementSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + points: decimalSchema.optional(), + _points: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + payment: stringSchema.optional(), + _payment: z.lazy(() => elementSchema).optional(), + paymentDate: dateTimeSchema.optional(), + _paymentDate: z.lazy(() => elementSchema).optional(), + responsible: z.lazy(() => referenceSchema).optional(), + recipient: z.lazy(() => referenceSchema).optional(), + linkId: stringSchema.array().optional(), + _linkId: z + .lazy(() => elementSchema) + .array() + .optional(), + securityLabelNumber: unsignedIntSchema.array().optional(), + _securityLabelNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['entityCodeableConcept', 'entityReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const contractTermActionSubjectSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + reference: z + .lazy(() => referenceSchema) + .array() + .min(1), + role: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const contractSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Contract'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + status: z + .enum([ + 'amended', + 'appended', + 'cancelled', + 'disputed', + 'entered-in-error', + 'executable', + 'executed', + 'negotiable', + 'offered', + 'policy', + 'rejected', + 'renewed', + 'resolved', + 'revoked', + 'terminated', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + legalState: z.lazy(() => codeableConceptSchema).optional(), + instantiatesCanonical: z.lazy(() => referenceSchema).optional(), + instantiatesUri: uriSchema.optional(), + _instantiatesUri: z.lazy(() => elementSchema).optional(), + contentDerivative: z.lazy(() => codeableConceptSchema).optional(), + issued: dateTimeSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + applies: z.lazy(() => periodSchema).optional(), + expirationType: z.lazy(() => codeableConceptSchema).optional(), + subject: z + .lazy(() => referenceSchema) + .array() + .optional(), + authority: z + .lazy(() => referenceSchema) + .array() + .optional(), + domain: z + .lazy(() => referenceSchema) + .array() + .optional(), + site: z + .lazy(() => referenceSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + alias: stringSchema.array().optional(), + _alias: z + .lazy(() => elementSchema) + .array() + .optional(), + author: z.lazy(() => referenceSchema).optional(), + scope: z.lazy(() => codeableConceptSchema).optional(), + topicCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + topicReference: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + contentDefinition: contractContentDefinitionSchema.optional(), + term: contractTermSchema.array().optional(), + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .optional(), + relevantHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + signer: contractSignerSchema.array().optional(), + friendly: contractFriendlySchema.array().optional(), + legal: contractLegalSchema.array().optional(), + rule: contractRuleSchema.array().optional(), + legallyBindingAttachment: z.lazy(() => attachmentSchema).optional(), + legallyBindingReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['topicCodeableConcept', 'topicReference'], + required: false, + }, + { + names: ['legallyBindingAttachment', 'legallyBindingReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const contributorSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['author', 'editor', 'endorser', 'reviewer']), + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + }), +) + +export const countSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }), +) + +export const coverageClassSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + }), +) + +export const coverageCostToBeneficiarySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + exception: coverageCostToBeneficiaryExceptionSchema.array().optional(), + }) + .refine( + (value) => + [{ names: ['valueQuantity', 'valueMoney'], required: true }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const coverageCostToBeneficiaryExceptionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + period: z.lazy(() => periodSchema).optional(), + }), + ) + +export const coverageSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Coverage'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + policyHolder: z.lazy(() => referenceSchema).optional(), + subscriber: z.lazy(() => referenceSchema).optional(), + subscriberId: stringSchema.optional(), + _subscriberId: z.lazy(() => elementSchema).optional(), + beneficiary: z.lazy(() => referenceSchema), + dependent: stringSchema.optional(), + _dependent: z.lazy(() => elementSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + payor: z + .lazy(() => referenceSchema) + .array() + .min(1), + class: coverageClassSchema.array().optional(), + order: positiveIntSchema.optional(), + _order: z.lazy(() => elementSchema).optional(), + network: stringSchema.optional(), + _network: z.lazy(() => elementSchema).optional(), + costToBeneficiary: coverageCostToBeneficiarySchema.array().optional(), + subrogation: booleanSchema.optional(), + _subrogation: z.lazy(() => elementSchema).optional(), + contract: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const coverageEligibilityRequestSupportingInfoSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + information: z.lazy(() => referenceSchema), + appliesToAll: booleanSchema.optional(), + _appliesToAll: z.lazy(() => elementSchema).optional(), + }), + ) + +export const coverageEligibilityRequestInsuranceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + focal: booleanSchema.optional(), + _focal: z.lazy(() => elementSchema).optional(), + coverage: z.lazy(() => referenceSchema), + businessArrangement: stringSchema.optional(), + _businessArrangement: z.lazy(() => elementSchema).optional(), + }), + ) + +export const coverageEligibilityRequestItemSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + supportingInfoSequence: positiveIntSchema.array().optional(), + _supportingInfoSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema).optional(), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + provider: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + facility: z.lazy(() => referenceSchema).optional(), + diagnosis: coverageEligibilityRequestItemDiagnosisSchema + .array() + .optional(), + detail: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const coverageEligibilityRequestItemDiagnosisSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + diagnosisCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + diagnosisReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['diagnosisCodeableConcept', 'diagnosisReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const coverageEligibilityRequestSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CoverageEligibilityRequest'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + priority: z.lazy(() => codeableConceptSchema).optional(), + purpose: z + .enum(['auth-requirements', 'benefits', 'discovery', 'validation']) + .array() + .min(1), + _purpose: z + .lazy(() => elementSchema) + .array() + .optional(), + patient: z.lazy(() => referenceSchema), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + provider: z.lazy(() => referenceSchema).optional(), + insurer: z.lazy(() => referenceSchema), + facility: z.lazy(() => referenceSchema).optional(), + supportingInfo: coverageEligibilityRequestSupportingInfoSchema + .array() + .optional(), + insurance: coverageEligibilityRequestInsuranceSchema.array().optional(), + item: coverageEligibilityRequestItemSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['servicedDate', 'servicedPeriod'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const coverageEligibilityResponseInsuranceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + coverage: z.lazy(() => referenceSchema), + inforce: booleanSchema.optional(), + _inforce: z.lazy(() => elementSchema).optional(), + benefitPeriod: z.lazy(() => periodSchema).optional(), + item: coverageEligibilityResponseInsuranceItemSchema.array().optional(), + }), + ) + +export const coverageEligibilityResponseErrorSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + }), + ) + +export const coverageEligibilityResponseInsuranceItemSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema).optional(), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + provider: z.lazy(() => referenceSchema).optional(), + excluded: booleanSchema.optional(), + _excluded: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + network: z.lazy(() => codeableConceptSchema).optional(), + unit: z.lazy(() => codeableConceptSchema).optional(), + term: z.lazy(() => codeableConceptSchema).optional(), + benefit: coverageEligibilityResponseInsuranceItemBenefitSchema + .array() + .optional(), + authorizationRequired: booleanSchema.optional(), + _authorizationRequired: z.lazy(() => elementSchema).optional(), + authorizationSupporting: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + authorizationUrl: uriSchema.optional(), + _authorizationUrl: z.lazy(() => elementSchema).optional(), + }), + ) + +export const coverageEligibilityResponseInsuranceItemBenefitSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + allowedUnsignedInt: unsignedIntSchema.optional(), + _allowedUnsignedInt: z.lazy(() => elementSchema).optional(), + allowedString: stringSchema.optional(), + _allowedString: z.lazy(() => elementSchema).optional(), + allowedMoney: z.lazy(() => moneySchema).optional(), + usedUnsignedInt: unsignedIntSchema.optional(), + _usedUnsignedInt: z.lazy(() => elementSchema).optional(), + usedString: stringSchema.optional(), + _usedString: z.lazy(() => elementSchema).optional(), + usedMoney: z.lazy(() => moneySchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['allowedUnsignedInt', 'allowedString', 'allowedMoney'], + required: false, + }, + { + names: ['usedUnsignedInt', 'usedString', 'usedMoney'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const coverageEligibilityResponseSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CoverageEligibilityResponse'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + purpose: z + .enum(['auth-requirements', 'benefits', 'discovery', 'validation']) + .array() + .min(1), + _purpose: z + .lazy(() => elementSchema) + .array() + .optional(), + patient: z.lazy(() => referenceSchema), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + requestor: z.lazy(() => referenceSchema).optional(), + request: z.lazy(() => referenceSchema), + outcome: z.enum(['complete', 'error', 'partial', 'queued']), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + insurer: z.lazy(() => referenceSchema), + insurance: coverageEligibilityResponseInsuranceSchema + .array() + .optional(), + preAuthRef: stringSchema.optional(), + _preAuthRef: z.lazy(() => elementSchema).optional(), + form: z.lazy(() => codeableConceptSchema).optional(), + error: coverageEligibilityResponseErrorSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['servicedDate', 'servicedPeriod'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const dataRequirementCodeFilterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + searchParam: stringSchema.optional(), + _searchParam: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema.optional(), + _valueSet: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codingSchema) + .array() + .optional(), + }), + ) + +export const dataRequirementDateFilterSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + searchParam: stringSchema.optional(), + _searchParam: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['valueDateTime', 'valuePeriod', 'valueDuration'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const dataRequirementSortSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema, + _path: z.lazy(() => elementSchema).optional(), + direction: z.enum(['ascending', 'descending']), + _direction: z.lazy(() => elementSchema).optional(), + }), + ) + +export const dataRequirementSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Any', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'CodeableConcept', + 'CodeableReference', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'RatioRange', + 'Reference', + 'RegulatedAuthorization', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'Type', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]), + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.array().optional(), + _profile: z + .lazy(() => elementSchema) + .array() + .optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + mustSupport: stringSchema.array().optional(), + _mustSupport: z + .lazy(() => elementSchema) + .array() + .optional(), + codeFilter: dataRequirementCodeFilterSchema.array().optional(), + dateFilter: dataRequirementDateFilterSchema.array().optional(), + limit: positiveIntSchema.optional(), + _limit: z.lazy(() => elementSchema).optional(), + sort: dataRequirementSortSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: ['subjectCodeableConcept', 'subjectReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const detectedIssueEvidenceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + detail: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const detectedIssueMitigationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + action: z.lazy(() => codeableConceptSchema), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const detectedIssueSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('DetectedIssue'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'amended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'preliminary', + 'registered', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + severity: z.enum(['high', 'low', 'moderate']).optional(), + _severity: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema).optional(), + identifiedDateTime: dateTimeSchema.optional(), + _identifiedDateTime: z.lazy(() => elementSchema).optional(), + identifiedPeriod: z.lazy(() => periodSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + implicated: z + .lazy(() => referenceSchema) + .array() + .optional(), + evidence: detectedIssueEvidenceSchema.array().optional(), + detail: stringSchema.optional(), + _detail: z.lazy(() => elementSchema).optional(), + reference: uriSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + mitigation: detectedIssueMitigationSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: ['identifiedDateTime', 'identifiedPeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const deviceUdiCarrierSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + deviceIdentifier: stringSchema.optional(), + _deviceIdentifier: z.lazy(() => elementSchema).optional(), + issuer: uriSchema.optional(), + _issuer: z.lazy(() => elementSchema).optional(), + jurisdiction: uriSchema.optional(), + _jurisdiction: z.lazy(() => elementSchema).optional(), + carrierAIDC: base64BinarySchema.optional(), + _carrierAIDC: z.lazy(() => elementSchema).optional(), + carrierHRF: stringSchema.optional(), + _carrierHRF: z.lazy(() => elementSchema).optional(), + entryType: z + .enum(['barcode', 'card', 'manual', 'rfid', 'self-reported', 'unknown']) + .optional(), + _entryType: z.lazy(() => elementSchema).optional(), + }), +) + +export const deviceDeviceNameSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + type: z.enum([ + 'manufacturer-name', + 'model-name', + 'other', + 'patient-reported-name', + 'udi-label-name', + 'user-friendly-name', + ]), + _type: z.lazy(() => elementSchema).optional(), + }), +) + +export const deviceSpecializationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + systemType: z.lazy(() => codeableConceptSchema), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + }), + ) + +export const deviceVersionSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + component: z.lazy(() => identifierSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + }), +) + +export const devicePropertySchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueQuantity: z + .lazy(() => quantitySchema) + .array() + .optional(), + valueCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), +) + +export const deviceSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Device'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + definition: z.lazy(() => referenceSchema).optional(), + udiCarrier: deviceUdiCarrierSchema.array().optional(), + status: z + .enum(['active', 'entered-in-error', 'inactive', 'unknown']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + distinctIdentifier: stringSchema.optional(), + _distinctIdentifier: z.lazy(() => elementSchema).optional(), + manufacturer: stringSchema.optional(), + _manufacturer: z.lazy(() => elementSchema).optional(), + manufactureDate: dateTimeSchema.optional(), + _manufactureDate: z.lazy(() => elementSchema).optional(), + expirationDate: dateTimeSchema.optional(), + _expirationDate: z.lazy(() => elementSchema).optional(), + lotNumber: stringSchema.optional(), + _lotNumber: z.lazy(() => elementSchema).optional(), + serialNumber: stringSchema.optional(), + _serialNumber: z.lazy(() => elementSchema).optional(), + deviceName: deviceDeviceNameSchema.array().optional(), + modelNumber: stringSchema.optional(), + _modelNumber: z.lazy(() => elementSchema).optional(), + partNumber: stringSchema.optional(), + _partNumber: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + specialization: deviceSpecializationSchema.array().optional(), + version: deviceVersionSchema.array().optional(), + property: devicePropertySchema.array().optional(), + patient: z.lazy(() => referenceSchema).optional(), + owner: z.lazy(() => referenceSchema).optional(), + contact: z + .lazy(() => contactPointSchema) + .array() + .optional(), + location: z.lazy(() => referenceSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + safety: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + parent: z.lazy(() => referenceSchema).optional(), + }), +) + +export const deviceDefinitionUdiDeviceIdentifierSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + deviceIdentifier: stringSchema, + _deviceIdentifier: z.lazy(() => elementSchema).optional(), + issuer: uriSchema, + _issuer: z.lazy(() => elementSchema).optional(), + jurisdiction: uriSchema, + _jurisdiction: z.lazy(() => elementSchema).optional(), + }), + ) + +export const deviceDefinitionDeviceNameSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + type: z.enum([ + 'manufacturer-name', + 'model-name', + 'other', + 'patient-reported-name', + 'udi-label-name', + 'user-friendly-name', + ]), + _type: z.lazy(() => elementSchema).optional(), + }), + ) + +export const deviceDefinitionSpecializationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + systemType: stringSchema, + _systemType: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + }), + ) + +export const deviceDefinitionCapabilitySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + description: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), + ) + +export const deviceDefinitionPropertySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueQuantity: z + .lazy(() => quantitySchema) + .array() + .optional(), + valueCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), + ) + +export const deviceDefinitionMaterialSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + substance: z.lazy(() => codeableConceptSchema), + alternate: booleanSchema.optional(), + _alternate: z.lazy(() => elementSchema).optional(), + allergenicIndicator: booleanSchema.optional(), + _allergenicIndicator: z.lazy(() => elementSchema).optional(), + }), + ) + +export const deviceDefinitionSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('DeviceDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + udiDeviceIdentifier: deviceDefinitionUdiDeviceIdentifierSchema + .array() + .optional(), + manufacturerString: stringSchema.optional(), + _manufacturerString: z.lazy(() => elementSchema).optional(), + manufacturerReference: z.lazy(() => referenceSchema).optional(), + deviceName: deviceDefinitionDeviceNameSchema.array().optional(), + modelNumber: stringSchema.optional(), + _modelNumber: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + specialization: deviceDefinitionSpecializationSchema.array().optional(), + version: stringSchema.array().optional(), + _version: z + .lazy(() => elementSchema) + .array() + .optional(), + safety: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + shelfLifeStorage: z + .lazy(() => productShelfLifeSchema) + .array() + .optional(), + physicalCharacteristics: z + .lazy(() => prodCharacteristicSchema) + .optional(), + languageCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + capability: deviceDefinitionCapabilitySchema.array().optional(), + property: deviceDefinitionPropertySchema.array().optional(), + owner: z.lazy(() => referenceSchema).optional(), + contact: z + .lazy(() => contactPointSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + onlineInformation: uriSchema.optional(), + _onlineInformation: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + parentDevice: z.lazy(() => referenceSchema).optional(), + material: deviceDefinitionMaterialSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: ['manufacturerString', 'manufacturerReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const deviceMetricCalibrationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['gain', 'offset', 'two-point', 'unspecified']).optional(), + _type: z.lazy(() => elementSchema).optional(), + state: z + .enum([ + 'calibrated', + 'calibration-required', + 'not-calibrated', + 'unspecified', + ]) + .optional(), + _state: z.lazy(() => elementSchema).optional(), + time: instantSchema.optional(), + _time: z.lazy(() => elementSchema).optional(), + }), + ) + +export const deviceMetricSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('DeviceMetric'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + unit: z.lazy(() => codeableConceptSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + parent: z.lazy(() => referenceSchema).optional(), + operationalStatus: z + .enum(['entered-in-error', 'off', 'on', 'standby']) + .optional(), + _operationalStatus: z.lazy(() => elementSchema).optional(), + color: z + .enum([ + 'black', + 'blue', + 'cyan', + 'green', + 'magenta', + 'red', + 'white', + 'yellow', + ]) + .optional(), + _color: z.lazy(() => elementSchema).optional(), + category: z.enum(['calculation', 'measurement', 'setting', 'unspecified']), + _category: z.lazy(() => elementSchema).optional(), + measurementPeriod: z.lazy(() => timingSchema).optional(), + calibration: deviceMetricCalibrationSchema.array().optional(), + }), +) + +export const deviceRequestParameterSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueQuantity', + 'valueRange', + 'valueBoolean', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const deviceRequestSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('DeviceRequest'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + priorRequest: z + .lazy(() => referenceSchema) + .array() + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + status: z + .enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + intent: z.enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + codeReference: z.lazy(() => referenceSchema).optional(), + codeCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + parameter: deviceRequestParameterSchema.array().optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performerType: z.lazy(() => codeableConceptSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + insurance: z + .lazy(() => referenceSchema) + .array() + .optional(), + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + relevantHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { names: ['codeReference', 'codeCodeableConcept'], required: true }, + { + names: [ + 'occurrenceDateTime', + 'occurrencePeriod', + 'occurrenceTiming', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const deviceUseStatementSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('DeviceUseStatement'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'completed', + 'entered-in-error', + 'intended', + 'on-hold', + 'stopped', + ]), + _status: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema), + derivedFrom: z + .lazy(() => referenceSchema) + .array() + .optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + recordedOn: dateTimeSchema.optional(), + _recordedOn: z.lazy(() => elementSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + device: z.lazy(() => referenceSchema), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['timingTiming', 'timingPeriod', 'timingDateTime'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const diagnosticReportMediaSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + link: z.lazy(() => referenceSchema), + }), + ) + +export const diagnosticReportSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('DiagnosticReport'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'amended', + 'appended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'partial', + 'preliminary', + 'registered', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + issued: instantSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + performer: z + .lazy(() => referenceSchema) + .array() + .optional(), + resultsInterpreter: z + .lazy(() => referenceSchema) + .array() + .optional(), + specimen: z + .lazy(() => referenceSchema) + .array() + .optional(), + result: z + .lazy(() => referenceSchema) + .array() + .optional(), + imagingStudy: z + .lazy(() => referenceSchema) + .array() + .optional(), + media: diagnosticReportMediaSchema.array().optional(), + conclusion: stringSchema.optional(), + _conclusion: z.lazy(() => elementSchema).optional(), + conclusionCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + presentedForm: z + .lazy(() => attachmentSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['effectiveDateTime', 'effectivePeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const distanceSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }), +) + +export const documentManifestRelatedSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + ref: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const documentManifestSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('DocumentManifest'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + masterIdentifier: z.lazy(() => identifierSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['current', 'entered-in-error', 'superseded']), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + author: z + .lazy(() => referenceSchema) + .array() + .optional(), + recipient: z + .lazy(() => referenceSchema) + .array() + .optional(), + source: uriSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + content: z + .lazy(() => referenceSchema) + .array() + .min(1), + related: documentManifestRelatedSchema.array().optional(), + }), +) + +export const documentReferenceRelatesToSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.enum(['appends', 'replaces', 'signs', 'transforms']), + _code: z.lazy(() => elementSchema).optional(), + target: z.lazy(() => referenceSchema), + }), + ) + +export const documentReferenceContentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + attachment: z.lazy(() => attachmentSchema), + format: z.lazy(() => codingSchema).optional(), + }), + ) + +export const documentReferenceContextSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + encounter: z + .lazy(() => referenceSchema) + .array() + .optional(), + event: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + facilityType: z.lazy(() => codeableConceptSchema).optional(), + practiceSetting: z.lazy(() => codeableConceptSchema).optional(), + sourcePatientInfo: z.lazy(() => referenceSchema).optional(), + related: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const documentReferenceSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('DocumentReference'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + masterIdentifier: z.lazy(() => identifierSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['current', 'entered-in-error', 'superseded']), + _status: z.lazy(() => elementSchema).optional(), + docStatus: z + .enum(['amended', 'entered-in-error', 'final', 'preliminary']) + .optional(), + _docStatus: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + subject: z.lazy(() => referenceSchema).optional(), + date: instantSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + author: z + .lazy(() => referenceSchema) + .array() + .optional(), + authenticator: z.lazy(() => referenceSchema).optional(), + custodian: z.lazy(() => referenceSchema).optional(), + relatesTo: documentReferenceRelatesToSchema.array().optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + securityLabel: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + content: documentReferenceContentSchema.array().min(1), + context: documentReferenceContextSchema.optional(), + }), +) + +export const dosageDoseAndRateSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + doseRange: z.lazy(() => rangeSchema).optional(), + doseQuantity: z.lazy(() => quantitySchema).optional(), + rateRatio: z.lazy(() => ratioSchema).optional(), + rateRange: z.lazy(() => rangeSchema).optional(), + rateQuantity: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => + [ + { names: ['doseRange', 'doseQuantity'], required: false }, + { + names: ['rateRatio', 'rateRange', 'rateQuantity'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const dosageSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: integerSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + additionalInstruction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + patientInstruction: stringSchema.optional(), + _patientInstruction: z.lazy(() => elementSchema).optional(), + timing: z.lazy(() => timingSchema).optional(), + asNeededBoolean: booleanSchema.optional(), + _asNeededBoolean: z.lazy(() => elementSchema).optional(), + asNeededCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + site: z.lazy(() => codeableConceptSchema).optional(), + route: z.lazy(() => codeableConceptSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + doseAndRate: dosageDoseAndRateSchema.array().optional(), + maxDosePerPeriod: z.lazy(() => ratioSchema).optional(), + maxDosePerAdministration: z.lazy(() => quantitySchema).optional(), + maxDosePerLifetime: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['asNeededBoolean', 'asNeededCodeableConcept'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const durationSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }), +) + +export const elementSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + }), +) + +export const elementDefinitionSlicingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + discriminator: elementDefinitionSlicingDiscriminatorSchema + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + ordered: booleanSchema.optional(), + _ordered: z.lazy(() => elementSchema).optional(), + rules: z.enum(['closed', 'open', 'openAtEnd']), + _rules: z.lazy(() => elementSchema).optional(), + }), + ) + +export const elementDefinitionBaseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema, + _path: z.lazy(() => elementSchema).optional(), + min: unsignedIntSchema, + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema, + _max: z.lazy(() => elementSchema).optional(), + }), + ) + +export const elementDefinitionTypeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: uriSchema, + _code: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.array().optional(), + _profile: z + .lazy(() => elementSchema) + .array() + .optional(), + targetProfile: canonicalSchema.array().optional(), + _targetProfile: z + .lazy(() => elementSchema) + .array() + .optional(), + aggregation: z + .enum(['bundled', 'contained', 'referenced']) + .array() + .optional(), + _aggregation: z + .lazy(() => elementSchema) + .array() + .optional(), + versioning: z.enum(['either', 'independent', 'specific']).optional(), + _versioning: z.lazy(() => elementSchema).optional(), + }), + ) + +export const elementDefinitionExampleSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + label: stringSchema, + _label: z.lazy(() => elementSchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCodeableReference: z + .lazy(() => codeableReferenceSchema) + .optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueRatioRange: z.lazy(() => ratioRangeSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBase64Binary', + 'valueBoolean', + 'valueCanonical', + 'valueCode', + 'valueDate', + 'valueDateTime', + 'valueDecimal', + 'valueId', + 'valueInstant', + 'valueInteger', + 'valueMarkdown', + 'valueOid', + 'valuePositiveInt', + 'valueString', + 'valueTime', + 'valueUnsignedInt', + 'valueUri', + 'valueUrl', + 'valueUuid', + 'valueAddress', + 'valueAge', + 'valueAnnotation', + 'valueAttachment', + 'valueCodeableConcept', + 'valueCodeableReference', + 'valueCoding', + 'valueContactPoint', + 'valueCount', + 'valueDistance', + 'valueDuration', + 'valueHumanName', + 'valueIdentifier', + 'valueMoney', + 'valuePeriod', + 'valueQuantity', + 'valueRange', + 'valueRatio', + 'valueRatioRange', + 'valueReference', + 'valueSampledData', + 'valueSignature', + 'valueTiming', + 'valueContactDetail', + 'valueContributor', + 'valueDataRequirement', + 'valueExpression', + 'valueParameterDefinition', + 'valueRelatedArtifact', + 'valueTriggerDefinition', + 'valueUsageContext', + 'valueDosage', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const elementDefinitionConstraintSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + key: idSchema, + _key: z.lazy(() => elementSchema).optional(), + requirements: stringSchema.optional(), + _requirements: z.lazy(() => elementSchema).optional(), + severity: z.enum(['error', 'warning']), + _severity: z.lazy(() => elementSchema).optional(), + human: stringSchema, + _human: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + xpath: stringSchema.optional(), + _xpath: z.lazy(() => elementSchema).optional(), + source: canonicalSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + }), + ) + +export const elementDefinitionBindingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + strength: z.enum(['example', 'extensible', 'preferred', 'required']), + _strength: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema.optional(), + _valueSet: z.lazy(() => elementSchema).optional(), + }), + ) + +export const elementDefinitionMappingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identity: idSchema, + _identity: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + map: stringSchema, + _map: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), + ) + +export const elementDefinitionSlicingDiscriminatorSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['exists', 'pattern', 'profile', 'type', 'value']), + _type: z.lazy(() => elementSchema).optional(), + path: stringSchema, + _path: z.lazy(() => elementSchema).optional(), + }), + ) + +export const elementDefinitionSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema, + _path: z.lazy(() => elementSchema).optional(), + representation: z + .enum(['cdaText', 'typeAttr', 'xhtml', 'xmlAttr', 'xmlText']) + .array() + .optional(), + _representation: z + .lazy(() => elementSchema) + .array() + .optional(), + sliceName: stringSchema.optional(), + _sliceName: z.lazy(() => elementSchema).optional(), + sliceIsConstraining: booleanSchema.optional(), + _sliceIsConstraining: z.lazy(() => elementSchema).optional(), + label: stringSchema.optional(), + _label: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codingSchema) + .array() + .optional(), + slicing: elementDefinitionSlicingSchema.optional(), + short: stringSchema.optional(), + _short: z.lazy(() => elementSchema).optional(), + definition: markdownSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + comment: markdownSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + requirements: markdownSchema.optional(), + _requirements: z.lazy(() => elementSchema).optional(), + alias: stringSchema.array().optional(), + _alias: z + .lazy(() => elementSchema) + .array() + .optional(), + min: unsignedIntSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + base: elementDefinitionBaseSchema.optional(), + contentReference: uriSchema.optional(), + _contentReference: z.lazy(() => elementSchema).optional(), + type: elementDefinitionTypeSchema.array().optional(), + defaultValueBase64Binary: base64BinarySchema.optional(), + _defaultValueBase64Binary: z.lazy(() => elementSchema).optional(), + defaultValueBoolean: booleanSchema.optional(), + _defaultValueBoolean: z.lazy(() => elementSchema).optional(), + defaultValueCanonical: canonicalSchema.optional(), + _defaultValueCanonical: z.lazy(() => elementSchema).optional(), + defaultValueCode: codeSchema.optional(), + _defaultValueCode: z.lazy(() => elementSchema).optional(), + defaultValueDate: dateSchema.optional(), + _defaultValueDate: z.lazy(() => elementSchema).optional(), + defaultValueDateTime: dateTimeSchema.optional(), + _defaultValueDateTime: z.lazy(() => elementSchema).optional(), + defaultValueDecimal: decimalSchema.optional(), + _defaultValueDecimal: z.lazy(() => elementSchema).optional(), + defaultValueId: idSchema.optional(), + _defaultValueId: z.lazy(() => elementSchema).optional(), + defaultValueInstant: instantSchema.optional(), + _defaultValueInstant: z.lazy(() => elementSchema).optional(), + defaultValueInteger: integerSchema.optional(), + _defaultValueInteger: z.lazy(() => elementSchema).optional(), + defaultValueMarkdown: markdownSchema.optional(), + _defaultValueMarkdown: z.lazy(() => elementSchema).optional(), + defaultValueOid: oidSchema.optional(), + _defaultValueOid: z.lazy(() => elementSchema).optional(), + defaultValuePositiveInt: positiveIntSchema.optional(), + _defaultValuePositiveInt: z.lazy(() => elementSchema).optional(), + defaultValueString: stringSchema.optional(), + _defaultValueString: z.lazy(() => elementSchema).optional(), + defaultValueTime: timeSchema.optional(), + _defaultValueTime: z.lazy(() => elementSchema).optional(), + defaultValueUnsignedInt: unsignedIntSchema.optional(), + _defaultValueUnsignedInt: z.lazy(() => elementSchema).optional(), + defaultValueUri: uriSchema.optional(), + _defaultValueUri: z.lazy(() => elementSchema).optional(), + defaultValueUrl: urlSchema.optional(), + _defaultValueUrl: z.lazy(() => elementSchema).optional(), + defaultValueUuid: uuidSchema.optional(), + _defaultValueUuid: z.lazy(() => elementSchema).optional(), + defaultValueAddress: z.lazy(() => addressSchema).optional(), + defaultValueAge: z.lazy(() => ageSchema).optional(), + defaultValueAnnotation: z.lazy(() => annotationSchema).optional(), + defaultValueAttachment: z.lazy(() => attachmentSchema).optional(), + defaultValueCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + defaultValueCodeableReference: z + .lazy(() => codeableReferenceSchema) + .optional(), + defaultValueCoding: z.lazy(() => codingSchema).optional(), + defaultValueContactPoint: z.lazy(() => contactPointSchema).optional(), + defaultValueCount: z.lazy(() => countSchema).optional(), + defaultValueDistance: z.lazy(() => distanceSchema).optional(), + defaultValueDuration: z.lazy(() => durationSchema).optional(), + defaultValueHumanName: z.lazy(() => humanNameSchema).optional(), + defaultValueIdentifier: z.lazy(() => identifierSchema).optional(), + defaultValueMoney: z.lazy(() => moneySchema).optional(), + defaultValuePeriod: z.lazy(() => periodSchema).optional(), + defaultValueQuantity: z.lazy(() => quantitySchema).optional(), + defaultValueRange: z.lazy(() => rangeSchema).optional(), + defaultValueRatio: z.lazy(() => ratioSchema).optional(), + defaultValueRatioRange: z.lazy(() => ratioRangeSchema).optional(), + defaultValueReference: z.lazy(() => referenceSchema).optional(), + defaultValueSampledData: z.lazy(() => sampledDataSchema).optional(), + defaultValueSignature: z.lazy(() => signatureSchema).optional(), + defaultValueTiming: z.lazy(() => timingSchema).optional(), + defaultValueContactDetail: z.lazy(() => contactDetailSchema).optional(), + defaultValueContributor: z.lazy(() => contributorSchema).optional(), + defaultValueDataRequirement: z + .lazy(() => dataRequirementSchema) + .optional(), + defaultValueExpression: z.lazy(() => expressionSchema).optional(), + defaultValueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + defaultValueRelatedArtifact: z + .lazy(() => relatedArtifactSchema) + .optional(), + defaultValueTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + defaultValueUsageContext: z.lazy(() => usageContextSchema).optional(), + defaultValueDosage: z.lazy(() => dosageSchema).optional(), + meaningWhenMissing: markdownSchema.optional(), + _meaningWhenMissing: z.lazy(() => elementSchema).optional(), + orderMeaning: stringSchema.optional(), + _orderMeaning: z.lazy(() => elementSchema).optional(), + fixedBase64Binary: base64BinarySchema.optional(), + _fixedBase64Binary: z.lazy(() => elementSchema).optional(), + fixedBoolean: booleanSchema.optional(), + _fixedBoolean: z.lazy(() => elementSchema).optional(), + fixedCanonical: canonicalSchema.optional(), + _fixedCanonical: z.lazy(() => elementSchema).optional(), + fixedCode: codeSchema.optional(), + _fixedCode: z.lazy(() => elementSchema).optional(), + fixedDate: dateSchema.optional(), + _fixedDate: z.lazy(() => elementSchema).optional(), + fixedDateTime: dateTimeSchema.optional(), + _fixedDateTime: z.lazy(() => elementSchema).optional(), + fixedDecimal: decimalSchema.optional(), + _fixedDecimal: z.lazy(() => elementSchema).optional(), + fixedId: idSchema.optional(), + _fixedId: z.lazy(() => elementSchema).optional(), + fixedInstant: instantSchema.optional(), + _fixedInstant: z.lazy(() => elementSchema).optional(), + fixedInteger: integerSchema.optional(), + _fixedInteger: z.lazy(() => elementSchema).optional(), + fixedMarkdown: markdownSchema.optional(), + _fixedMarkdown: z.lazy(() => elementSchema).optional(), + fixedOid: oidSchema.optional(), + _fixedOid: z.lazy(() => elementSchema).optional(), + fixedPositiveInt: positiveIntSchema.optional(), + _fixedPositiveInt: z.lazy(() => elementSchema).optional(), + fixedString: stringSchema.optional(), + _fixedString: z.lazy(() => elementSchema).optional(), + fixedTime: timeSchema.optional(), + _fixedTime: z.lazy(() => elementSchema).optional(), + fixedUnsignedInt: unsignedIntSchema.optional(), + _fixedUnsignedInt: z.lazy(() => elementSchema).optional(), + fixedUri: uriSchema.optional(), + _fixedUri: z.lazy(() => elementSchema).optional(), + fixedUrl: urlSchema.optional(), + _fixedUrl: z.lazy(() => elementSchema).optional(), + fixedUuid: uuidSchema.optional(), + _fixedUuid: z.lazy(() => elementSchema).optional(), + fixedAddress: z.lazy(() => addressSchema).optional(), + fixedAge: z.lazy(() => ageSchema).optional(), + fixedAnnotation: z.lazy(() => annotationSchema).optional(), + fixedAttachment: z.lazy(() => attachmentSchema).optional(), + fixedCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + fixedCodeableReference: z + .lazy(() => codeableReferenceSchema) + .optional(), + fixedCoding: z.lazy(() => codingSchema).optional(), + fixedContactPoint: z.lazy(() => contactPointSchema).optional(), + fixedCount: z.lazy(() => countSchema).optional(), + fixedDistance: z.lazy(() => distanceSchema).optional(), + fixedDuration: z.lazy(() => durationSchema).optional(), + fixedHumanName: z.lazy(() => humanNameSchema).optional(), + fixedIdentifier: z.lazy(() => identifierSchema).optional(), + fixedMoney: z.lazy(() => moneySchema).optional(), + fixedPeriod: z.lazy(() => periodSchema).optional(), + fixedQuantity: z.lazy(() => quantitySchema).optional(), + fixedRange: z.lazy(() => rangeSchema).optional(), + fixedRatio: z.lazy(() => ratioSchema).optional(), + fixedRatioRange: z.lazy(() => ratioRangeSchema).optional(), + fixedReference: z.lazy(() => referenceSchema).optional(), + fixedSampledData: z.lazy(() => sampledDataSchema).optional(), + fixedSignature: z.lazy(() => signatureSchema).optional(), + fixedTiming: z.lazy(() => timingSchema).optional(), + fixedContactDetail: z.lazy(() => contactDetailSchema).optional(), + fixedContributor: z.lazy(() => contributorSchema).optional(), + fixedDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + fixedExpression: z.lazy(() => expressionSchema).optional(), + fixedParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + fixedRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + fixedTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + fixedUsageContext: z.lazy(() => usageContextSchema).optional(), + fixedDosage: z.lazy(() => dosageSchema).optional(), + patternBase64Binary: base64BinarySchema.optional(), + _patternBase64Binary: z.lazy(() => elementSchema).optional(), + patternBoolean: booleanSchema.optional(), + _patternBoolean: z.lazy(() => elementSchema).optional(), + patternCanonical: canonicalSchema.optional(), + _patternCanonical: z.lazy(() => elementSchema).optional(), + patternCode: codeSchema.optional(), + _patternCode: z.lazy(() => elementSchema).optional(), + patternDate: dateSchema.optional(), + _patternDate: z.lazy(() => elementSchema).optional(), + patternDateTime: dateTimeSchema.optional(), + _patternDateTime: z.lazy(() => elementSchema).optional(), + patternDecimal: decimalSchema.optional(), + _patternDecimal: z.lazy(() => elementSchema).optional(), + patternId: idSchema.optional(), + _patternId: z.lazy(() => elementSchema).optional(), + patternInstant: instantSchema.optional(), + _patternInstant: z.lazy(() => elementSchema).optional(), + patternInteger: integerSchema.optional(), + _patternInteger: z.lazy(() => elementSchema).optional(), + patternMarkdown: markdownSchema.optional(), + _patternMarkdown: z.lazy(() => elementSchema).optional(), + patternOid: oidSchema.optional(), + _patternOid: z.lazy(() => elementSchema).optional(), + patternPositiveInt: positiveIntSchema.optional(), + _patternPositiveInt: z.lazy(() => elementSchema).optional(), + patternString: stringSchema.optional(), + _patternString: z.lazy(() => elementSchema).optional(), + patternTime: timeSchema.optional(), + _patternTime: z.lazy(() => elementSchema).optional(), + patternUnsignedInt: unsignedIntSchema.optional(), + _patternUnsignedInt: z.lazy(() => elementSchema).optional(), + patternUri: uriSchema.optional(), + _patternUri: z.lazy(() => elementSchema).optional(), + patternUrl: urlSchema.optional(), + _patternUrl: z.lazy(() => elementSchema).optional(), + patternUuid: uuidSchema.optional(), + _patternUuid: z.lazy(() => elementSchema).optional(), + patternAddress: z.lazy(() => addressSchema).optional(), + patternAge: z.lazy(() => ageSchema).optional(), + patternAnnotation: z.lazy(() => annotationSchema).optional(), + patternAttachment: z.lazy(() => attachmentSchema).optional(), + patternCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + patternCodeableReference: z + .lazy(() => codeableReferenceSchema) + .optional(), + patternCoding: z.lazy(() => codingSchema).optional(), + patternContactPoint: z.lazy(() => contactPointSchema).optional(), + patternCount: z.lazy(() => countSchema).optional(), + patternDistance: z.lazy(() => distanceSchema).optional(), + patternDuration: z.lazy(() => durationSchema).optional(), + patternHumanName: z.lazy(() => humanNameSchema).optional(), + patternIdentifier: z.lazy(() => identifierSchema).optional(), + patternMoney: z.lazy(() => moneySchema).optional(), + patternPeriod: z.lazy(() => periodSchema).optional(), + patternQuantity: z.lazy(() => quantitySchema).optional(), + patternRange: z.lazy(() => rangeSchema).optional(), + patternRatio: z.lazy(() => ratioSchema).optional(), + patternRatioRange: z.lazy(() => ratioRangeSchema).optional(), + patternReference: z.lazy(() => referenceSchema).optional(), + patternSampledData: z.lazy(() => sampledDataSchema).optional(), + patternSignature: z.lazy(() => signatureSchema).optional(), + patternTiming: z.lazy(() => timingSchema).optional(), + patternContactDetail: z.lazy(() => contactDetailSchema).optional(), + patternContributor: z.lazy(() => contributorSchema).optional(), + patternDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + patternExpression: z.lazy(() => expressionSchema).optional(), + patternParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + patternRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + patternTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + patternUsageContext: z.lazy(() => usageContextSchema).optional(), + patternDosage: z.lazy(() => dosageSchema).optional(), + example: elementDefinitionExampleSchema.array().optional(), + minValueDate: dateSchema.optional(), + _minValueDate: z.lazy(() => elementSchema).optional(), + minValueDateTime: dateTimeSchema.optional(), + _minValueDateTime: z.lazy(() => elementSchema).optional(), + minValueInstant: instantSchema.optional(), + _minValueInstant: z.lazy(() => elementSchema).optional(), + minValueTime: timeSchema.optional(), + _minValueTime: z.lazy(() => elementSchema).optional(), + minValueDecimal: decimalSchema.optional(), + _minValueDecimal: z.lazy(() => elementSchema).optional(), + minValueInteger: integerSchema.optional(), + _minValueInteger: z.lazy(() => elementSchema).optional(), + minValuePositiveInt: positiveIntSchema.optional(), + _minValuePositiveInt: z.lazy(() => elementSchema).optional(), + minValueUnsignedInt: unsignedIntSchema.optional(), + _minValueUnsignedInt: z.lazy(() => elementSchema).optional(), + minValueQuantity: z.lazy(() => quantitySchema).optional(), + maxValueDate: dateSchema.optional(), + _maxValueDate: z.lazy(() => elementSchema).optional(), + maxValueDateTime: dateTimeSchema.optional(), + _maxValueDateTime: z.lazy(() => elementSchema).optional(), + maxValueInstant: instantSchema.optional(), + _maxValueInstant: z.lazy(() => elementSchema).optional(), + maxValueTime: timeSchema.optional(), + _maxValueTime: z.lazy(() => elementSchema).optional(), + maxValueDecimal: decimalSchema.optional(), + _maxValueDecimal: z.lazy(() => elementSchema).optional(), + maxValueInteger: integerSchema.optional(), + _maxValueInteger: z.lazy(() => elementSchema).optional(), + maxValuePositiveInt: positiveIntSchema.optional(), + _maxValuePositiveInt: z.lazy(() => elementSchema).optional(), + maxValueUnsignedInt: unsignedIntSchema.optional(), + _maxValueUnsignedInt: z.lazy(() => elementSchema).optional(), + maxValueQuantity: z.lazy(() => quantitySchema).optional(), + maxLength: integerSchema.optional(), + _maxLength: z.lazy(() => elementSchema).optional(), + condition: idSchema.array().optional(), + _condition: z + .lazy(() => elementSchema) + .array() + .optional(), + constraint: elementDefinitionConstraintSchema.array().optional(), + mustSupport: booleanSchema.optional(), + _mustSupport: z.lazy(() => elementSchema).optional(), + isModifier: booleanSchema.optional(), + _isModifier: z.lazy(() => elementSchema).optional(), + isModifierReason: stringSchema.optional(), + _isModifierReason: z.lazy(() => elementSchema).optional(), + isSummary: booleanSchema.optional(), + _isSummary: z.lazy(() => elementSchema).optional(), + binding: elementDefinitionBindingSchema.optional(), + mapping: elementDefinitionMappingSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'defaultValueBase64Binary', + 'defaultValueBoolean', + 'defaultValueCanonical', + 'defaultValueCode', + 'defaultValueDate', + 'defaultValueDateTime', + 'defaultValueDecimal', + 'defaultValueId', + 'defaultValueInstant', + 'defaultValueInteger', + 'defaultValueMarkdown', + 'defaultValueOid', + 'defaultValuePositiveInt', + 'defaultValueString', + 'defaultValueTime', + 'defaultValueUnsignedInt', + 'defaultValueUri', + 'defaultValueUrl', + 'defaultValueUuid', + 'defaultValueAddress', + 'defaultValueAge', + 'defaultValueAnnotation', + 'defaultValueAttachment', + 'defaultValueCodeableConcept', + 'defaultValueCodeableReference', + 'defaultValueCoding', + 'defaultValueContactPoint', + 'defaultValueCount', + 'defaultValueDistance', + 'defaultValueDuration', + 'defaultValueHumanName', + 'defaultValueIdentifier', + 'defaultValueMoney', + 'defaultValuePeriod', + 'defaultValueQuantity', + 'defaultValueRange', + 'defaultValueRatio', + 'defaultValueRatioRange', + 'defaultValueReference', + 'defaultValueSampledData', + 'defaultValueSignature', + 'defaultValueTiming', + 'defaultValueContactDetail', + 'defaultValueContributor', + 'defaultValueDataRequirement', + 'defaultValueExpression', + 'defaultValueParameterDefinition', + 'defaultValueRelatedArtifact', + 'defaultValueTriggerDefinition', + 'defaultValueUsageContext', + 'defaultValueDosage', + ], + required: false, + }, + { + names: [ + 'fixedBase64Binary', + 'fixedBoolean', + 'fixedCanonical', + 'fixedCode', + 'fixedDate', + 'fixedDateTime', + 'fixedDecimal', + 'fixedId', + 'fixedInstant', + 'fixedInteger', + 'fixedMarkdown', + 'fixedOid', + 'fixedPositiveInt', + 'fixedString', + 'fixedTime', + 'fixedUnsignedInt', + 'fixedUri', + 'fixedUrl', + 'fixedUuid', + 'fixedAddress', + 'fixedAge', + 'fixedAnnotation', + 'fixedAttachment', + 'fixedCodeableConcept', + 'fixedCodeableReference', + 'fixedCoding', + 'fixedContactPoint', + 'fixedCount', + 'fixedDistance', + 'fixedDuration', + 'fixedHumanName', + 'fixedIdentifier', + 'fixedMoney', + 'fixedPeriod', + 'fixedQuantity', + 'fixedRange', + 'fixedRatio', + 'fixedRatioRange', + 'fixedReference', + 'fixedSampledData', + 'fixedSignature', + 'fixedTiming', + 'fixedContactDetail', + 'fixedContributor', + 'fixedDataRequirement', + 'fixedExpression', + 'fixedParameterDefinition', + 'fixedRelatedArtifact', + 'fixedTriggerDefinition', + 'fixedUsageContext', + 'fixedDosage', + ], + required: false, + }, + { + names: [ + 'patternBase64Binary', + 'patternBoolean', + 'patternCanonical', + 'patternCode', + 'patternDate', + 'patternDateTime', + 'patternDecimal', + 'patternId', + 'patternInstant', + 'patternInteger', + 'patternMarkdown', + 'patternOid', + 'patternPositiveInt', + 'patternString', + 'patternTime', + 'patternUnsignedInt', + 'patternUri', + 'patternUrl', + 'patternUuid', + 'patternAddress', + 'patternAge', + 'patternAnnotation', + 'patternAttachment', + 'patternCodeableConcept', + 'patternCodeableReference', + 'patternCoding', + 'patternContactPoint', + 'patternCount', + 'patternDistance', + 'patternDuration', + 'patternHumanName', + 'patternIdentifier', + 'patternMoney', + 'patternPeriod', + 'patternQuantity', + 'patternRange', + 'patternRatio', + 'patternRatioRange', + 'patternReference', + 'patternSampledData', + 'patternSignature', + 'patternTiming', + 'patternContactDetail', + 'patternContributor', + 'patternDataRequirement', + 'patternExpression', + 'patternParameterDefinition', + 'patternRelatedArtifact', + 'patternTriggerDefinition', + 'patternUsageContext', + 'patternDosage', + ], + required: false, + }, + { + names: [ + 'minValueDate', + 'minValueDateTime', + 'minValueInstant', + 'minValueTime', + 'minValueDecimal', + 'minValueInteger', + 'minValuePositiveInt', + 'minValueUnsignedInt', + 'minValueQuantity', + ], + required: false, + }, + { + names: [ + 'maxValueDate', + 'maxValueDateTime', + 'maxValueInstant', + 'maxValueTime', + 'maxValueDecimal', + 'maxValueInteger', + 'maxValuePositiveInt', + 'maxValueUnsignedInt', + 'maxValueQuantity', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const encounterStatusHistorySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + status: z.enum([ + 'arrived', + 'cancelled', + 'entered-in-error', + 'finished', + 'in-progress', + 'onleave', + 'planned', + 'triaged', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema), + }), + ) + +export const encounterClassHistorySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + class: z.lazy(() => codingSchema), + period: z.lazy(() => periodSchema), + }), + ) + +export const encounterParticipantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + individual: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const encounterDiagnosisSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + condition: z.lazy(() => referenceSchema), + use: z.lazy(() => codeableConceptSchema).optional(), + rank: positiveIntSchema.optional(), + _rank: z.lazy(() => elementSchema).optional(), + }), + ) + +export const encounterHospitalizationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + preAdmissionIdentifier: z.lazy(() => identifierSchema).optional(), + origin: z.lazy(() => referenceSchema).optional(), + admitSource: z.lazy(() => codeableConceptSchema).optional(), + reAdmission: z.lazy(() => codeableConceptSchema).optional(), + dietPreference: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialCourtesy: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialArrangement: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + destination: z.lazy(() => referenceSchema).optional(), + dischargeDisposition: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const encounterLocationSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + location: z.lazy(() => referenceSchema), + status: z.enum(['active', 'completed', 'planned', 'reserved']).optional(), + _status: z.lazy(() => elementSchema).optional(), + physicalType: z.lazy(() => codeableConceptSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), +) + +export const encounterSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Encounter'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'arrived', + 'cancelled', + 'entered-in-error', + 'finished', + 'in-progress', + 'onleave', + 'planned', + 'triaged', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusHistory: encounterStatusHistorySchema.array().optional(), + class: z.lazy(() => codingSchema), + classHistory: encounterClassHistorySchema.array().optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + serviceType: z.lazy(() => codeableConceptSchema).optional(), + priority: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + episodeOfCare: z + .lazy(() => referenceSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + participant: encounterParticipantSchema.array().optional(), + appointment: z + .lazy(() => referenceSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + length: z.lazy(() => durationSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + diagnosis: encounterDiagnosisSchema.array().optional(), + account: z + .lazy(() => referenceSchema) + .array() + .optional(), + hospitalization: encounterHospitalizationSchema.optional(), + location: encounterLocationSchema.array().optional(), + serviceProvider: z.lazy(() => referenceSchema).optional(), + partOf: z.lazy(() => referenceSchema).optional(), + }), +) + +export const endpointSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Endpoint'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'entered-in-error', + 'error', + 'off', + 'suspended', + 'test', + ]), + _status: z.lazy(() => elementSchema).optional(), + connectionType: z.lazy(() => codingSchema), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + contact: z + .lazy(() => contactPointSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + payloadType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1), + payloadMimeType: codeSchema.array().optional(), + _payloadMimeType: z + .lazy(() => elementSchema) + .array() + .optional(), + address: urlSchema, + _address: z.lazy(() => elementSchema).optional(), + header: stringSchema.array().optional(), + _header: z + .lazy(() => elementSchema) + .array() + .optional(), + }), +) + +export const enrollmentRequestSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('EnrollmentRequest'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + insurer: z.lazy(() => referenceSchema).optional(), + provider: z.lazy(() => referenceSchema).optional(), + candidate: z.lazy(() => referenceSchema).optional(), + coverage: z.lazy(() => referenceSchema).optional(), + }), +) + +export const enrollmentResponseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('EnrollmentResponse'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + request: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + requestProvider: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const episodeOfCareStatusHistorySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'cancelled', + 'entered-in-error', + 'finished', + 'onhold', + 'planned', + 'waitlist', + ]), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema), + }), + ) + +export const episodeOfCareDiagnosisSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + condition: z.lazy(() => referenceSchema), + role: z.lazy(() => codeableConceptSchema).optional(), + rank: positiveIntSchema.optional(), + _rank: z.lazy(() => elementSchema).optional(), + }), + ) + +export const episodeOfCareSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('EpisodeOfCare'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'cancelled', + 'entered-in-error', + 'finished', + 'onhold', + 'planned', + 'waitlist', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusHistory: episodeOfCareStatusHistorySchema.array().optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + diagnosis: episodeOfCareDiagnosisSchema.array().optional(), + patient: z.lazy(() => referenceSchema), + managingOrganization: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + referralRequest: z + .lazy(() => referenceSchema) + .array() + .optional(), + careManager: z.lazy(() => referenceSchema).optional(), + team: z + .lazy(() => referenceSchema) + .array() + .optional(), + account: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const eventDefinitionSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('EventDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + trigger: z + .lazy(() => triggerDefinitionSchema) + .array() + .min(1), + }) + .refine( + (value) => + [ + { + names: ['subjectCodeableConcept', 'subjectReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const evidenceVariableDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + variableRole: z.lazy(() => codeableConceptSchema), + observed: z.lazy(() => referenceSchema).optional(), + intended: z.lazy(() => referenceSchema).optional(), + directnessMatch: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const evidenceStatisticSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + statisticType: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + numberOfEvents: unsignedIntSchema.optional(), + _numberOfEvents: z.lazy(() => elementSchema).optional(), + numberAffected: unsignedIntSchema.optional(), + _numberAffected: z.lazy(() => elementSchema).optional(), + sampleSize: evidenceStatisticSampleSizeSchema.optional(), + attributeEstimate: evidenceStatisticAttributeEstimateSchema + .array() + .optional(), + modelCharacteristic: evidenceStatisticModelCharacteristicSchema + .array() + .optional(), + }), +) + +export const evidenceCertaintySchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + rating: z.lazy(() => codeableConceptSchema).optional(), + rater: stringSchema.optional(), + _rater: z.lazy(() => elementSchema).optional(), + subcomponent: z + .lazy(() => evidenceCertaintySchema) + .array() + .optional(), + }), +) + +export const evidenceStatisticSampleSizeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + numberOfStudies: unsignedIntSchema.optional(), + _numberOfStudies: z.lazy(() => elementSchema).optional(), + numberOfParticipants: unsignedIntSchema.optional(), + _numberOfParticipants: z.lazy(() => elementSchema).optional(), + knownDataCount: unsignedIntSchema.optional(), + _knownDataCount: z.lazy(() => elementSchema).optional(), + }), + ) + +export const evidenceStatisticAttributeEstimateSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + level: decimalSchema.optional(), + _level: z.lazy(() => elementSchema).optional(), + range: z.lazy(() => rangeSchema).optional(), + attributeEstimate: z + .lazy(() => evidenceStatisticAttributeEstimateSchema) + .array() + .optional(), + }), + ) + +export const evidenceStatisticModelCharacteristicSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + value: z.lazy(() => quantitySchema).optional(), + variable: evidenceStatisticModelCharacteristicVariableSchema + .array() + .optional(), + attributeEstimate: z + .lazy(() => evidenceStatisticAttributeEstimateSchema) + .array() + .optional(), + }), + ) + +export const evidenceStatisticModelCharacteristicVariableSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + variableDefinition: z.lazy(() => referenceSchema), + handling: z + .enum(['continuous', 'dichotomous', 'ordinal', 'polychotomous']) + .optional(), + _handling: z.lazy(() => elementSchema).optional(), + valueCategory: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + valueQuantity: z + .lazy(() => quantitySchema) + .array() + .optional(), + valueRange: z + .lazy(() => rangeSchema) + .array() + .optional(), + }), + ) + +export const evidenceSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Evidence'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + citeAsReference: z.lazy(() => referenceSchema).optional(), + citeAsMarkdown: markdownSchema.optional(), + _citeAsMarkdown: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + assertion: markdownSchema.optional(), + _assertion: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + variableDefinition: evidenceVariableDefinitionSchema.array().min(1), + synthesisType: z.lazy(() => codeableConceptSchema).optional(), + studyType: z.lazy(() => codeableConceptSchema).optional(), + statistic: evidenceStatisticSchema.array().optional(), + certainty: evidenceCertaintySchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['citeAsReference', 'citeAsMarkdown'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const evidenceReportSubjectSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + characteristic: evidenceReportSubjectCharacteristicSchema + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), + ) + +export const evidenceReportRelatesToSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.enum([ + 'amendedWith', + 'amends', + 'appendedWith', + 'appends', + 'replacedWith', + 'replaces', + 'transformedWith', + 'transforms', + ]), + _code: z.lazy(() => elementSchema).optional(), + targetIdentifier: z.lazy(() => identifierSchema).optional(), + targetReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['targetIdentifier', 'targetReference'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const evidenceReportSectionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + focus: z.lazy(() => codeableConceptSchema).optional(), + focusReference: z.lazy(() => referenceSchema).optional(), + author: z + .lazy(() => referenceSchema) + .array() + .optional(), + text: z.lazy(() => narrativeSchema).optional(), + mode: z.enum(['changes', 'snapshot', 'working']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + orderedBy: z.lazy(() => codeableConceptSchema).optional(), + entryClassifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + entryReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + entryQuantity: z + .lazy(() => quantitySchema) + .array() + .optional(), + emptyReason: z.lazy(() => codeableConceptSchema).optional(), + section: z + .lazy(() => evidenceReportSectionSchema) + .array() + .optional(), + }), + ) + +export const evidenceReportSubjectCharacteristicSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + valueReference: z.lazy(() => referenceSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + exclude: booleanSchema.optional(), + _exclude: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueReference', + 'valueCodeableConcept', + 'valueBoolean', + 'valueQuantity', + 'valueRange', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const evidenceReportSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('EvidenceReport'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + relatedIdentifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + citeAsReference: z.lazy(() => referenceSchema).optional(), + citeAsMarkdown: markdownSchema.optional(), + _citeAsMarkdown: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + subject: evidenceReportSubjectSchema, + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatesTo: evidenceReportRelatesToSchema.array().optional(), + section: evidenceReportSectionSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['citeAsReference', 'citeAsMarkdown'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const evidenceVariableCharacteristicSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + definitionReference: z.lazy(() => referenceSchema).optional(), + definitionCanonical: canonicalSchema.optional(), + _definitionCanonical: z.lazy(() => elementSchema).optional(), + definitionCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + definitionExpression: z.lazy(() => expressionSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + device: z.lazy(() => referenceSchema).optional(), + exclude: booleanSchema.optional(), + _exclude: z.lazy(() => elementSchema).optional(), + timeFromStart: + evidenceVariableCharacteristicTimeFromStartSchema.optional(), + groupMeasure: z + .enum([ + 'mean', + 'mean-of-mean', + 'mean-of-median', + 'median', + 'median-of-mean', + 'median-of-median', + ]) + .optional(), + _groupMeasure: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'definitionReference', + 'definitionCanonical', + 'definitionCodeableConcept', + 'definitionExpression', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const evidenceVariableCategorySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['valueCodeableConcept', 'valueQuantity', 'valueRange'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const evidenceVariableCharacteristicTimeFromStartSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + range: z.lazy(() => rangeSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), + ) + +export const evidenceVariableSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('EvidenceVariable'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + shortTitle: stringSchema.optional(), + _shortTitle: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + actual: booleanSchema.optional(), + _actual: z.lazy(() => elementSchema).optional(), + characteristicCombination: z.enum(['intersection', 'union']).optional(), + _characteristicCombination: z.lazy(() => elementSchema).optional(), + characteristic: evidenceVariableCharacteristicSchema.array().optional(), + handling: z + .enum(['continuous', 'dichotomous', 'ordinal', 'polychotomous']) + .optional(), + _handling: z.lazy(() => elementSchema).optional(), + category: evidenceVariableCategorySchema.array().optional(), + }), +) + +export const exampleScenarioActorSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + actorId: stringSchema, + _actorId: z.lazy(() => elementSchema).optional(), + type: z.enum(['entity', 'person']), + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }), + ) + +export const exampleScenarioInstanceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + resourceId: stringSchema, + _resourceId: z.lazy(() => elementSchema).optional(), + resourceType: z.enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]), + _resourceType: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + version: exampleScenarioInstanceVersionSchema.array().optional(), + containedInstance: exampleScenarioInstanceContainedInstanceSchema + .array() + .optional(), + }), + ) + +export const exampleScenarioProcessSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + title: stringSchema, + _title: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + preConditions: markdownSchema.optional(), + _preConditions: z.lazy(() => elementSchema).optional(), + postConditions: markdownSchema.optional(), + _postConditions: z.lazy(() => elementSchema).optional(), + step: exampleScenarioProcessStepSchema.array().optional(), + }), + ) + +export const exampleScenarioInstanceVersionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + versionId: stringSchema, + _versionId: z.lazy(() => elementSchema).optional(), + description: markdownSchema, + _description: z.lazy(() => elementSchema).optional(), + }), + ) + +export const exampleScenarioInstanceContainedInstanceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + resourceId: stringSchema, + _resourceId: z.lazy(() => elementSchema).optional(), + versionId: stringSchema.optional(), + _versionId: z.lazy(() => elementSchema).optional(), + }), + ) + +export const exampleScenarioProcessStepSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + process: z + .lazy(() => exampleScenarioProcessSchema) + .array() + .optional(), + pause: booleanSchema.optional(), + _pause: z.lazy(() => elementSchema).optional(), + operation: exampleScenarioProcessStepOperationSchema.optional(), + alternative: exampleScenarioProcessStepAlternativeSchema + .array() + .optional(), + }), + ) + +export const exampleScenarioProcessStepOperationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + number: stringSchema, + _number: z.lazy(() => elementSchema).optional(), + type: stringSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + initiator: stringSchema.optional(), + _initiator: z.lazy(() => elementSchema).optional(), + receiver: stringSchema.optional(), + _receiver: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + initiatorActive: booleanSchema.optional(), + _initiatorActive: z.lazy(() => elementSchema).optional(), + receiverActive: booleanSchema.optional(), + _receiverActive: z.lazy(() => elementSchema).optional(), + request: z + .lazy(() => exampleScenarioInstanceContainedInstanceSchema) + .optional(), + response: z + .lazy(() => exampleScenarioInstanceContainedInstanceSchema) + .optional(), + }), + ) + +export const exampleScenarioProcessStepAlternativeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + title: stringSchema, + _title: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + step: z + .lazy(() => exampleScenarioProcessStepSchema) + .array() + .optional(), + }), + ) + +export const exampleScenarioSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('ExampleScenario'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + actor: exampleScenarioActorSchema.array().optional(), + instance: exampleScenarioInstanceSchema.array().optional(), + process: exampleScenarioProcessSchema.array().optional(), + workflow: canonicalSchema.array().optional(), + _workflow: z + .lazy(() => elementSchema) + .array() + .optional(), + }), +) + +export const explanationOfBenefitRelatedSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + claim: z.lazy(() => referenceSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema).optional(), + reference: z.lazy(() => identifierSchema).optional(), + }), + ) + +export const explanationOfBenefitPayeeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + party: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const explanationOfBenefitCareTeamSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + provider: z.lazy(() => referenceSchema), + responsible: booleanSchema.optional(), + _responsible: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + qualification: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const explanationOfBenefitSupportingInfoSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + category: z.lazy(() => codeableConceptSchema), + code: z.lazy(() => codeableConceptSchema).optional(), + timingDate: dateSchema.optional(), + _timingDate: z.lazy(() => elementSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + reason: z.lazy(() => codingSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['timingDate', 'timingPeriod'], required: false }, + { + names: [ + 'valueBoolean', + 'valueString', + 'valueQuantity', + 'valueAttachment', + 'valueReference', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const explanationOfBenefitDiagnosisSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + diagnosisCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + diagnosisReference: z.lazy(() => referenceSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + onAdmission: z.lazy(() => codeableConceptSchema).optional(), + packageCode: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['diagnosisCodeableConcept', 'diagnosisReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const explanationOfBenefitProcedureSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + procedureCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + procedureReference: z.lazy(() => referenceSchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['procedureCodeableConcept', 'procedureReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const explanationOfBenefitInsuranceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + focal: booleanSchema, + _focal: z.lazy(() => elementSchema).optional(), + coverage: z.lazy(() => referenceSchema), + preAuthRef: stringSchema.array().optional(), + _preAuthRef: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const explanationOfBenefitAccidentSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['locationAddress', 'locationReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const explanationOfBenefitItemSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + careTeamSequence: positiveIntSchema.array().optional(), + _careTeamSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + diagnosisSequence: positiveIntSchema.array().optional(), + _diagnosisSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + procedureSequence: positiveIntSchema.array().optional(), + _procedureSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + informationSequence: positiveIntSchema.array().optional(), + _informationSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + encounter: z + .lazy(() => referenceSchema) + .array() + .optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: explanationOfBenefitItemAdjudicationSchema + .array() + .optional(), + detail: explanationOfBenefitItemDetailSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['servicedDate', 'servicedPeriod'], required: false }, + { + names: [ + 'locationCodeableConcept', + 'locationAddress', + 'locationReference', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const explanationOfBenefitAddItemSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemSequence: positiveIntSchema.array().optional(), + _itemSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + detailSequence: positiveIntSchema.array().optional(), + _detailSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + subDetailSequence: positiveIntSchema.array().optional(), + _subDetailSequence: z + .lazy(() => elementSchema) + .array() + .optional(), + provider: z + .lazy(() => referenceSchema) + .array() + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .optional(), + detail: explanationOfBenefitAddItemDetailSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['servicedDate', 'servicedPeriod'], required: false }, + { + names: [ + 'locationCodeableConcept', + 'locationAddress', + 'locationReference', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const explanationOfBenefitItemAdjudicationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema), + reason: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const explanationOfBenefitTotalSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema), + amount: z.lazy(() => moneySchema), + }), + ) + +export const explanationOfBenefitPaymentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + adjustment: z.lazy(() => moneySchema).optional(), + adjustmentReason: z.lazy(() => codeableConceptSchema).optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + }), + ) + +export const explanationOfBenefitProcessNoteSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + number: positiveIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + type: z.enum(['display', 'print', 'printoper']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + language: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const explanationOfBenefitBenefitBalanceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema), + excluded: booleanSchema.optional(), + _excluded: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + network: z.lazy(() => codeableConceptSchema).optional(), + unit: z.lazy(() => codeableConceptSchema).optional(), + term: z.lazy(() => codeableConceptSchema).optional(), + financial: explanationOfBenefitBenefitBalanceFinancialSchema + .array() + .optional(), + }), + ) + +export const explanationOfBenefitItemDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .optional(), + subDetail: explanationOfBenefitItemDetailSubDetailSchema + .array() + .optional(), + }), + ) + +export const explanationOfBenefitAddItemDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .optional(), + subDetail: explanationOfBenefitAddItemDetailSubDetailSchema + .array() + .optional(), + }), + ) + +export const explanationOfBenefitBenefitBalanceFinancialSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + allowedUnsignedInt: unsignedIntSchema.optional(), + _allowedUnsignedInt: z.lazy(() => elementSchema).optional(), + allowedString: stringSchema.optional(), + _allowedString: z.lazy(() => elementSchema).optional(), + allowedMoney: z.lazy(() => moneySchema).optional(), + usedUnsignedInt: unsignedIntSchema.optional(), + _usedUnsignedInt: z.lazy(() => elementSchema).optional(), + usedMoney: z.lazy(() => moneySchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['allowedUnsignedInt', 'allowedString', 'allowedMoney'], + required: false, + }, + { names: ['usedUnsignedInt', 'usedMoney'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const explanationOfBenefitItemDetailSubDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema, + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .optional(), + }), + ) + +export const explanationOfBenefitAddItemDetailSubDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.array().optional(), + _noteNumber: z + .lazy(() => elementSchema) + .array() + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .optional(), + }), + ) + +export const explanationOfBenefitSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('ExplanationOfBenefit'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + use: z.enum(['claim', 'preauthorization', 'predetermination']), + _use: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + billablePeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + insurer: z.lazy(() => referenceSchema), + provider: z.lazy(() => referenceSchema), + priority: z.lazy(() => codeableConceptSchema).optional(), + fundsReserveRequested: z.lazy(() => codeableConceptSchema).optional(), + fundsReserve: z.lazy(() => codeableConceptSchema).optional(), + related: explanationOfBenefitRelatedSchema.array().optional(), + prescription: z.lazy(() => referenceSchema).optional(), + originalPrescription: z.lazy(() => referenceSchema).optional(), + payee: explanationOfBenefitPayeeSchema.optional(), + referral: z.lazy(() => referenceSchema).optional(), + facility: z.lazy(() => referenceSchema).optional(), + claim: z.lazy(() => referenceSchema).optional(), + claimResponse: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + preAuthRef: stringSchema.array().optional(), + _preAuthRef: z + .lazy(() => elementSchema) + .array() + .optional(), + preAuthRefPeriod: z + .lazy(() => periodSchema) + .array() + .optional(), + careTeam: explanationOfBenefitCareTeamSchema.array().optional(), + supportingInfo: explanationOfBenefitSupportingInfoSchema + .array() + .optional(), + diagnosis: explanationOfBenefitDiagnosisSchema.array().optional(), + procedure: explanationOfBenefitProcedureSchema.array().optional(), + precedence: positiveIntSchema.optional(), + _precedence: z.lazy(() => elementSchema).optional(), + insurance: explanationOfBenefitInsuranceSchema.array().min(1), + accident: explanationOfBenefitAccidentSchema.optional(), + item: explanationOfBenefitItemSchema.array().optional(), + addItem: explanationOfBenefitAddItemSchema.array().optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .optional(), + total: explanationOfBenefitTotalSchema.array().optional(), + payment: explanationOfBenefitPaymentSchema.optional(), + formCode: z.lazy(() => codeableConceptSchema).optional(), + form: z.lazy(() => attachmentSchema).optional(), + processNote: explanationOfBenefitProcessNoteSchema.array().optional(), + benefitPeriod: z.lazy(() => periodSchema).optional(), + benefitBalance: explanationOfBenefitBenefitBalanceSchema + .array() + .optional(), + }), + ) + +export const expressionSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + name: idSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + language: codeSchema, + _language: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + reference: uriSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + }), +) + +export const extensionSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCodeableReference: z.lazy(() => codeableReferenceSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueRatioRange: z.lazy(() => ratioRangeSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBase64Binary', + 'valueBoolean', + 'valueCanonical', + 'valueCode', + 'valueDate', + 'valueDateTime', + 'valueDecimal', + 'valueId', + 'valueInstant', + 'valueInteger', + 'valueMarkdown', + 'valueOid', + 'valuePositiveInt', + 'valueString', + 'valueTime', + 'valueUnsignedInt', + 'valueUri', + 'valueUrl', + 'valueUuid', + 'valueAddress', + 'valueAge', + 'valueAnnotation', + 'valueAttachment', + 'valueCodeableConcept', + 'valueCodeableReference', + 'valueCoding', + 'valueContactPoint', + 'valueCount', + 'valueDistance', + 'valueDuration', + 'valueHumanName', + 'valueIdentifier', + 'valueMoney', + 'valuePeriod', + 'valueQuantity', + 'valueRange', + 'valueRatio', + 'valueRatioRange', + 'valueReference', + 'valueSampledData', + 'valueSignature', + 'valueTiming', + 'valueContactDetail', + 'valueContributor', + 'valueDataRequirement', + 'valueExpression', + 'valueParameterDefinition', + 'valueRelatedArtifact', + 'valueTriggerDefinition', + 'valueUsageContext', + 'valueDosage', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => + ((value as { extension?: unknown[] }).extension?.length ?? 0) > 0 !== + Object.keys(value as object).some( + // A primitive sibling means the value element exists carrying only extensions, + // which ext-1 counts as the value being present. + (key) => key.startsWith('value') || key.startsWith('_value'), + ), + { + message: + 'ext-1: an extension has either extensions or a value, never both.', + }, + ), +) + +export const familyMemberHistoryConditionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + outcome: z.lazy(() => codeableConceptSchema).optional(), + contributedToDeath: booleanSchema.optional(), + _contributedToDeath: z.lazy(() => elementSchema).optional(), + onsetAge: z.lazy(() => ageSchema).optional(), + onsetRange: z.lazy(() => rangeSchema).optional(), + onsetPeriod: z.lazy(() => periodSchema).optional(), + onsetString: stringSchema.optional(), + _onsetString: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['onsetAge', 'onsetRange', 'onsetPeriod', 'onsetString'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const familyMemberHistorySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('FamilyMemberHistory'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + status: z.enum([ + 'completed', + 'entered-in-error', + 'health-unknown', + 'partial', + ]), + _status: z.lazy(() => elementSchema).optional(), + dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), + patient: z.lazy(() => referenceSchema), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema), + sex: z.lazy(() => codeableConceptSchema).optional(), + bornPeriod: z.lazy(() => periodSchema).optional(), + bornDate: dateSchema.optional(), + _bornDate: z.lazy(() => elementSchema).optional(), + bornString: stringSchema.optional(), + _bornString: z.lazy(() => elementSchema).optional(), + ageAge: z.lazy(() => ageSchema).optional(), + ageRange: z.lazy(() => rangeSchema).optional(), + ageString: stringSchema.optional(), + _ageString: z.lazy(() => elementSchema).optional(), + estimatedAge: booleanSchema.optional(), + _estimatedAge: z.lazy(() => elementSchema).optional(), + deceasedBoolean: booleanSchema.optional(), + _deceasedBoolean: z.lazy(() => elementSchema).optional(), + deceasedAge: z.lazy(() => ageSchema).optional(), + deceasedRange: z.lazy(() => rangeSchema).optional(), + deceasedDate: dateSchema.optional(), + _deceasedDate: z.lazy(() => elementSchema).optional(), + deceasedString: stringSchema.optional(), + _deceasedString: z.lazy(() => elementSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + condition: familyMemberHistoryConditionSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: ['bornPeriod', 'bornDate', 'bornString'], + required: false, + }, + { names: ['ageAge', 'ageRange', 'ageString'], required: false }, + { + names: [ + 'deceasedBoolean', + 'deceasedAge', + 'deceasedRange', + 'deceasedDate', + 'deceasedString', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const flagSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Flag'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'entered-in-error', 'inactive']), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema), + period: z.lazy(() => periodSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + }), +) + +export const goalTargetSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + measure: z.lazy(() => codeableConceptSchema).optional(), + detailQuantity: z.lazy(() => quantitySchema).optional(), + detailRange: z.lazy(() => rangeSchema).optional(), + detailCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + detailString: stringSchema.optional(), + _detailString: z.lazy(() => elementSchema).optional(), + detailBoolean: booleanSchema.optional(), + _detailBoolean: z.lazy(() => elementSchema).optional(), + detailInteger: integerSchema.optional(), + _detailInteger: z.lazy(() => elementSchema).optional(), + detailRatio: z.lazy(() => ratioSchema).optional(), + dueDate: dateSchema.optional(), + _dueDate: z.lazy(() => elementSchema).optional(), + dueDuration: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'detailQuantity', + 'detailRange', + 'detailCodeableConcept', + 'detailString', + 'detailBoolean', + 'detailInteger', + 'detailRatio', + ], + required: false, + }, + { names: ['dueDate', 'dueDuration'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const goalSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Goal'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + lifecycleStatus: z.enum([ + 'accepted', + 'active', + 'cancelled', + 'completed', + 'entered-in-error', + 'on-hold', + 'planned', + 'proposed', + 'rejected', + ]), + _lifecycleStatus: z.lazy(() => elementSchema).optional(), + achievementStatus: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + priority: z.lazy(() => codeableConceptSchema).optional(), + description: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema), + startDate: dateSchema.optional(), + _startDate: z.lazy(() => elementSchema).optional(), + startCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + target: goalTargetSchema.array().optional(), + statusDate: dateSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), + statusReason: stringSchema.optional(), + _statusReason: z.lazy(() => elementSchema).optional(), + expressedBy: z.lazy(() => referenceSchema).optional(), + addresses: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + outcomeCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + outcomeReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { names: ['startDate', 'startCodeableConcept'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const graphDefinitionLinkSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + sliceName: stringSchema.optional(), + _sliceName: z.lazy(() => elementSchema).optional(), + min: integerSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + target: graphDefinitionLinkTargetSchema.array().optional(), + }), + ) + +export const graphDefinitionLinkTargetSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]), + _type: z.lazy(() => elementSchema).optional(), + params: stringSchema.optional(), + _params: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + compartment: graphDefinitionLinkTargetCompartmentSchema + .array() + .optional(), + link: z + .lazy(() => graphDefinitionLinkSchema) + .array() + .optional(), + }), + ) + +export const graphDefinitionLinkTargetCompartmentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + use: z.enum(['condition', 'requirement']), + _use: z.lazy(() => elementSchema).optional(), + code: z.enum([ + 'Device', + 'Encounter', + 'Patient', + 'Practitioner', + 'RelatedPerson', + ]), + _code: z.lazy(() => elementSchema).optional(), + rule: z.enum(['custom', 'different', 'identical', 'matching']), + _rule: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }), + ) + +export const graphDefinitionSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('GraphDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + start: z.enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]), + _start: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + link: graphDefinitionLinkSchema.array().optional(), + }), +) + +export const groupCharacteristicSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + exclude: booleanSchema, + _exclude: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueBoolean', + 'valueQuantity', + 'valueRange', + 'valueReference', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const groupMemberSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + entity: z.lazy(() => referenceSchema), + period: z.lazy(() => periodSchema).optional(), + inactive: booleanSchema.optional(), + _inactive: z.lazy(() => elementSchema).optional(), + }), +) + +export const groupSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Group'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + type: z.enum([ + 'animal', + 'device', + 'medication', + 'person', + 'practitioner', + 'substance', + ]), + _type: z.lazy(() => elementSchema).optional(), + actual: booleanSchema, + _actual: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + quantity: unsignedIntSchema.optional(), + _quantity: z.lazy(() => elementSchema).optional(), + managingEntity: z.lazy(() => referenceSchema).optional(), + characteristic: groupCharacteristicSchema.array().optional(), + member: groupMemberSchema.array().optional(), + }), +) + +export const guidanceResponseSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('GuidanceResponse'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + requestIdentifier: z.lazy(() => identifierSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + moduleUri: uriSchema.optional(), + _moduleUri: z.lazy(() => elementSchema).optional(), + moduleCanonical: canonicalSchema.optional(), + _moduleCanonical: z.lazy(() => elementSchema).optional(), + moduleCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum([ + 'data-requested', + 'data-required', + 'entered-in-error', + 'failure', + 'in-progress', + 'success', + ]), + _status: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + evaluationMessage: z + .lazy(() => referenceSchema) + .array() + .optional(), + outputParameters: z.lazy(() => referenceSchema).optional(), + result: z.lazy(() => referenceSchema).optional(), + dataRequirement: z + .lazy(() => dataRequirementSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['moduleUri', 'moduleCanonical', 'moduleCodeableConcept'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const healthcareServiceEligibilitySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + comment: markdownSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), + ) + +export const healthcareServiceAvailableTimeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + daysOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .array() + .optional(), + _daysOfWeek: z + .lazy(() => elementSchema) + .array() + .optional(), + allDay: booleanSchema.optional(), + _allDay: z.lazy(() => elementSchema).optional(), + availableStartTime: timeSchema.optional(), + _availableStartTime: z.lazy(() => elementSchema).optional(), + availableEndTime: timeSchema.optional(), + _availableEndTime: z.lazy(() => elementSchema).optional(), + }), + ) + +export const healthcareServiceNotAvailableSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema, + _description: z.lazy(() => elementSchema).optional(), + during: z.lazy(() => periodSchema).optional(), + }), + ) + +export const healthcareServiceSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('HealthcareService'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + providedBy: z.lazy(() => referenceSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + location: z + .lazy(() => referenceSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + extraDetails: markdownSchema.optional(), + _extraDetails: z.lazy(() => elementSchema).optional(), + photo: z.lazy(() => attachmentSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + coverageArea: z + .lazy(() => referenceSchema) + .array() + .optional(), + serviceProvisionCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + eligibility: healthcareServiceEligibilitySchema.array().optional(), + program: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + characteristic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + communication: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + referralMethod: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + appointmentRequired: booleanSchema.optional(), + _appointmentRequired: z.lazy(() => elementSchema).optional(), + availableTime: healthcareServiceAvailableTimeSchema.array().optional(), + notAvailable: healthcareServiceNotAvailableSchema.array().optional(), + availabilityExceptions: stringSchema.optional(), + _availabilityExceptions: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const humanNameSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + use: z + .enum([ + 'anonymous', + 'maiden', + 'nickname', + 'official', + 'old', + 'temp', + 'usual', + ]) + .optional(), + _use: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + family: stringSchema.optional(), + _family: z.lazy(() => elementSchema).optional(), + given: stringSchema.array().optional(), + _given: z + .lazy(() => elementSchema) + .array() + .optional(), + prefix: stringSchema.array().optional(), + _prefix: z + .lazy(() => elementSchema) + .array() + .optional(), + suffix: stringSchema.array().optional(), + _suffix: z + .lazy(() => elementSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + }), +) + +export const identifierSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + use: z.enum(['official', 'old', 'secondary', 'temp', 'usual']).optional(), + _use: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + assigner: z.lazy(() => referenceSchema).optional(), + }), +) + +export const imagingStudySeriesSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + uid: idSchema, + _uid: z.lazy(() => elementSchema).optional(), + number: unsignedIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + modality: z.lazy(() => codingSchema), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + numberOfInstances: unsignedIntSchema.optional(), + _numberOfInstances: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + bodySite: z.lazy(() => codingSchema).optional(), + laterality: z.lazy(() => codingSchema).optional(), + specimen: z + .lazy(() => referenceSchema) + .array() + .optional(), + started: dateTimeSchema.optional(), + _started: z.lazy(() => elementSchema).optional(), + performer: imagingStudySeriesPerformerSchema.array().optional(), + instance: imagingStudySeriesInstanceSchema.array().optional(), + }), + ) + +export const imagingStudySeriesPerformerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }), + ) + +export const imagingStudySeriesInstanceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + uid: idSchema, + _uid: z.lazy(() => elementSchema).optional(), + sopClass: z.lazy(() => codingSchema), + number: unsignedIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + }), + ) + +export const imagingStudySchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('ImagingStudy'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'available', + 'cancelled', + 'entered-in-error', + 'registered', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + modality: z + .lazy(() => codingSchema) + .array() + .optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + started: dateTimeSchema.optional(), + _started: z.lazy(() => elementSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + referrer: z.lazy(() => referenceSchema).optional(), + interpreter: z + .lazy(() => referenceSchema) + .array() + .optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + numberOfSeries: unsignedIntSchema.optional(), + _numberOfSeries: z.lazy(() => elementSchema).optional(), + numberOfInstances: unsignedIntSchema.optional(), + _numberOfInstances: z.lazy(() => elementSchema).optional(), + procedureReference: z.lazy(() => referenceSchema).optional(), + procedureCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + location: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + series: imagingStudySeriesSchema.array().optional(), + }), +) + +export const immunizationPerformerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }), + ) + +export const immunizationEducationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + documentType: stringSchema.optional(), + _documentType: z.lazy(() => elementSchema).optional(), + reference: uriSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + publicationDate: dateTimeSchema.optional(), + _publicationDate: z.lazy(() => elementSchema).optional(), + presentationDate: dateTimeSchema.optional(), + _presentationDate: z.lazy(() => elementSchema).optional(), + }), + ) + +export const immunizationReactionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + detail: z.lazy(() => referenceSchema).optional(), + reported: booleanSchema.optional(), + _reported: z.lazy(() => elementSchema).optional(), + }), + ) + +export const immunizationProtocolAppliedSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + series: stringSchema.optional(), + _series: z.lazy(() => elementSchema).optional(), + authority: z.lazy(() => referenceSchema).optional(), + targetDisease: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + doseNumberPositiveInt: positiveIntSchema.optional(), + _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), + doseNumberString: stringSchema.optional(), + _doseNumberString: z.lazy(() => elementSchema).optional(), + seriesDosesPositiveInt: positiveIntSchema.optional(), + _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), + seriesDosesString: stringSchema.optional(), + _seriesDosesString: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['doseNumberPositiveInt', 'doseNumberString'], + required: true, + }, + { + names: ['seriesDosesPositiveInt', 'seriesDosesString'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const immunizationSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Immunization'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['completed', 'entered-in-error', 'not-done']), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + vaccineCode: z.lazy(() => codeableConceptSchema), + patient: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrenceString: stringSchema.optional(), + _occurrenceString: z.lazy(() => elementSchema).optional(), + recorded: dateTimeSchema.optional(), + _recorded: z.lazy(() => elementSchema).optional(), + primarySource: booleanSchema.optional(), + _primarySource: z.lazy(() => elementSchema).optional(), + reportOrigin: z.lazy(() => codeableConceptSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + manufacturer: z.lazy(() => referenceSchema).optional(), + lotNumber: stringSchema.optional(), + _lotNumber: z.lazy(() => elementSchema).optional(), + expirationDate: dateSchema.optional(), + _expirationDate: z.lazy(() => elementSchema).optional(), + site: z.lazy(() => codeableConceptSchema).optional(), + route: z.lazy(() => codeableConceptSchema).optional(), + doseQuantity: z.lazy(() => quantitySchema).optional(), + performer: immunizationPerformerSchema.array().optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + isSubpotent: booleanSchema.optional(), + _isSubpotent: z.lazy(() => elementSchema).optional(), + subpotentReason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + education: immunizationEducationSchema.array().optional(), + programEligibility: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + fundingSource: z.lazy(() => codeableConceptSchema).optional(), + reaction: immunizationReactionSchema.array().optional(), + protocolApplied: immunizationProtocolAppliedSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['occurrenceDateTime', 'occurrenceString'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const immunizationEvaluationSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ImmunizationEvaluation'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['completed', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + authority: z.lazy(() => referenceSchema).optional(), + targetDisease: z.lazy(() => codeableConceptSchema), + immunizationEvent: z.lazy(() => referenceSchema), + doseStatus: z.lazy(() => codeableConceptSchema), + doseStatusReason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + series: stringSchema.optional(), + _series: z.lazy(() => elementSchema).optional(), + doseNumberPositiveInt: positiveIntSchema.optional(), + _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), + doseNumberString: stringSchema.optional(), + _doseNumberString: z.lazy(() => elementSchema).optional(), + seriesDosesPositiveInt: positiveIntSchema.optional(), + _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), + seriesDosesString: stringSchema.optional(), + _seriesDosesString: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['doseNumberPositiveInt', 'doseNumberString'], + required: false, + }, + { + names: ['seriesDosesPositiveInt', 'seriesDosesString'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const immunizationRecommendationRecommendationSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + vaccineCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + targetDisease: z.lazy(() => codeableConceptSchema).optional(), + contraindicatedVaccineCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + forecastStatus: z.lazy(() => codeableConceptSchema), + forecastReason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + dateCriterion: + immunizationRecommendationRecommendationDateCriterionSchema + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + series: stringSchema.optional(), + _series: z.lazy(() => elementSchema).optional(), + doseNumberPositiveInt: positiveIntSchema.optional(), + _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), + doseNumberString: stringSchema.optional(), + _doseNumberString: z.lazy(() => elementSchema).optional(), + seriesDosesPositiveInt: positiveIntSchema.optional(), + _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), + seriesDosesString: stringSchema.optional(), + _seriesDosesString: z.lazy(() => elementSchema).optional(), + supportingImmunization: z + .lazy(() => referenceSchema) + .array() + .optional(), + supportingPatientInformation: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['doseNumberPositiveInt', 'doseNumberString'], + required: false, + }, + { + names: ['seriesDosesPositiveInt', 'seriesDosesString'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const immunizationRecommendationRecommendationDateCriterionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + value: dateTimeSchema, + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const immunizationRecommendationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('ImmunizationRecommendation'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + patient: z.lazy(() => referenceSchema), + date: dateTimeSchema, + _date: z.lazy(() => elementSchema).optional(), + authority: z.lazy(() => referenceSchema).optional(), + recommendation: immunizationRecommendationRecommendationSchema + .array() + .min(1), + }), + ) + +export const implementationGuideDependsOnSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + uri: canonicalSchema, + _uri: z.lazy(() => elementSchema).optional(), + packageId: idSchema.optional(), + _packageId: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + }), + ) + +export const implementationGuideGlobalSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]), + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema, + _profile: z.lazy(() => elementSchema).optional(), + }), + ) + +export const implementationGuideDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + grouping: implementationGuideDefinitionGroupingSchema.array().optional(), + resource: implementationGuideDefinitionResourceSchema.array().min(1), + page: implementationGuideDefinitionPageSchema.optional(), + parameter: implementationGuideDefinitionParameterSchema + .array() + .optional(), + template: implementationGuideDefinitionTemplateSchema.array().optional(), + }), + ) + +export const implementationGuideManifestSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + rendering: urlSchema.optional(), + _rendering: z.lazy(() => elementSchema).optional(), + resource: implementationGuideManifestResourceSchema.array().min(1), + page: implementationGuideManifestPageSchema.array().optional(), + image: stringSchema.array().optional(), + _image: z + .lazy(() => elementSchema) + .array() + .optional(), + other: stringSchema.array().optional(), + _other: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const implementationGuideDefinitionGroupingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }), + ) + +export const implementationGuideDefinitionResourceSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + reference: z.lazy(() => referenceSchema), + fhirVersion: z + .enum([ + '0.0.80', + '0.0.81', + '0.0.82', + '0.01', + '0.05', + '0.06', + '0.11', + '0.4.0', + '0.5.0', + '1.0.0', + '1.0.1', + '1.0.2', + '1.1.0', + '1.4.0', + '1.6.0', + '1.8.0', + '3.0.0', + '3.0.1', + '3.0.2', + '3.3.0', + '3.5.0', + '4.0.0', + '4.0.1', + '4.1.0', + '4.3.0', + '4.3.0-cibuild', + '4.3.0-snapshot1', + ]) + .array() + .optional(), + _fhirVersion: z + .lazy(() => elementSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + exampleBoolean: booleanSchema.optional(), + _exampleBoolean: z.lazy(() => elementSchema).optional(), + exampleCanonical: canonicalSchema.optional(), + _exampleCanonical: z.lazy(() => elementSchema).optional(), + groupingId: idSchema.optional(), + _groupingId: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['exampleBoolean', 'exampleCanonical'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const implementationGuideDefinitionPageSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + nameUrl: urlSchema.optional(), + _nameUrl: z.lazy(() => elementSchema).optional(), + nameReference: z.lazy(() => referenceSchema).optional(), + title: stringSchema, + _title: z.lazy(() => elementSchema).optional(), + generation: z.enum(['generated', 'html', 'markdown', 'xml']), + _generation: z.lazy(() => elementSchema).optional(), + page: z + .lazy(() => implementationGuideDefinitionPageSchema) + .array() + .optional(), + }) + .refine( + (value) => + [{ names: ['nameUrl', 'nameReference'], required: true }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const implementationGuideDefinitionParameterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.enum([ + 'apply', + 'expansion-parameter', + 'generate-json', + 'generate-turtle', + 'generate-xml', + 'html-template', + 'path-pages', + 'path-resource', + 'path-tx-cache', + 'rule-broken-links', + ]), + _code: z.lazy(() => elementSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const implementationGuideDefinitionTemplateSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + source: stringSchema, + _source: z.lazy(() => elementSchema).optional(), + scope: stringSchema.optional(), + _scope: z.lazy(() => elementSchema).optional(), + }), + ) + +export const implementationGuideManifestResourceSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + reference: z.lazy(() => referenceSchema), + exampleBoolean: booleanSchema.optional(), + _exampleBoolean: z.lazy(() => elementSchema).optional(), + exampleCanonical: canonicalSchema.optional(), + _exampleCanonical: z.lazy(() => elementSchema).optional(), + relativePath: urlSchema.optional(), + _relativePath: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['exampleBoolean', 'exampleCanonical'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const implementationGuideManifestPageSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + anchor: stringSchema.array().optional(), + _anchor: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const implementationGuideSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('ImplementationGuide'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + packageId: idSchema, + _packageId: z.lazy(() => elementSchema).optional(), + license: z + .enum([ + '0BSD', + 'AAL', + 'ADSL', + 'AFL-1.1', + 'AFL-1.2', + 'AFL-2.0', + 'AFL-2.1', + 'AFL-3.0', + 'AGPL-1.0-only', + 'AGPL-1.0-or-later', + 'AGPL-3.0-only', + 'AGPL-3.0-or-later', + 'AMDPLPA', + 'AML', + 'AMPAS', + 'ANTLR-PD', + 'APAFML', + 'APL-1.0', + 'APSL-1.0', + 'APSL-1.1', + 'APSL-1.2', + 'APSL-2.0', + 'Abstyles', + 'Adobe-2006', + 'Adobe-Glyph', + 'Afmparse', + 'Aladdin', + 'Apache-1.0', + 'Apache-1.1', + 'Apache-2.0', + 'Artistic-1.0', + 'Artistic-1.0-Perl', + 'Artistic-1.0-cl8', + 'Artistic-2.0', + 'BSD-1-Clause', + 'BSD-2-Clause', + 'BSD-2-Clause-FreeBSD', + 'BSD-2-Clause-NetBSD', + 'BSD-2-Clause-Patent', + 'BSD-3-Clause', + 'BSD-3-Clause-Attribution', + 'BSD-3-Clause-Clear', + 'BSD-3-Clause-LBNL', + 'BSD-3-Clause-No-Nuclear-License', + 'BSD-3-Clause-No-Nuclear-License-2014', + 'BSD-3-Clause-No-Nuclear-Warranty', + 'BSD-4-Clause', + 'BSD-4-Clause-UC', + 'BSD-Protection', + 'BSD-Source-Code', + 'BSL-1.0', + 'Bahyph', + 'Barr', + 'Beerware', + 'BitTorrent-1.0', + 'BitTorrent-1.1', + 'Borceux', + 'CATOSL-1.1', + 'CC-BY-1.0', + 'CC-BY-2.0', + 'CC-BY-2.5', + 'CC-BY-3.0', + 'CC-BY-4.0', + 'CC-BY-NC-1.0', + 'CC-BY-NC-2.0', + 'CC-BY-NC-2.5', + 'CC-BY-NC-3.0', + 'CC-BY-NC-4.0', + 'CC-BY-NC-ND-1.0', + 'CC-BY-NC-ND-2.0', + 'CC-BY-NC-ND-2.5', + 'CC-BY-NC-ND-3.0', + 'CC-BY-NC-ND-4.0', + 'CC-BY-NC-SA-1.0', + 'CC-BY-NC-SA-2.0', + 'CC-BY-NC-SA-2.5', + 'CC-BY-NC-SA-3.0', + 'CC-BY-NC-SA-4.0', + 'CC-BY-ND-1.0', + 'CC-BY-ND-2.0', + 'CC-BY-ND-2.5', + 'CC-BY-ND-3.0', + 'CC-BY-ND-4.0', + 'CC-BY-SA-1.0', + 'CC-BY-SA-2.0', + 'CC-BY-SA-2.5', + 'CC-BY-SA-3.0', + 'CC-BY-SA-4.0', + 'CC0-1.0', + 'CDDL-1.0', + 'CDDL-1.1', + 'CDLA-Permissive-1.0', + 'CDLA-Sharing-1.0', + 'CECILL-1.0', + 'CECILL-1.1', + 'CECILL-2.0', + 'CECILL-2.1', + 'CECILL-B', + 'CECILL-C', + 'CNRI-Jython', + 'CNRI-Python', + 'CNRI-Python-GPL-Compatible', + 'CPAL-1.0', + 'CPL-1.0', + 'CPOL-1.02', + 'CUA-OPL-1.0', + 'Caldera', + 'ClArtistic', + 'Condor-1.1', + 'Crossword', + 'CrystalStacker', + 'Cube', + 'D-FSL-1.0', + 'DOC', + 'DSDP', + 'Dotseqn', + 'ECL-1.0', + 'ECL-2.0', + 'EFL-1.0', + 'EFL-2.0', + 'EPL-1.0', + 'EPL-2.0', + 'EUDatagrid', + 'EUPL-1.0', + 'EUPL-1.1', + 'EUPL-1.2', + 'Entessa', + 'ErlPL-1.1', + 'Eurosym', + 'FSFAP', + 'FSFUL', + 'FSFULLR', + 'FTL', + 'Fair', + 'Frameworx-1.0', + 'FreeImage', + 'GFDL-1.1-only', + 'GFDL-1.1-or-later', + 'GFDL-1.2-only', + 'GFDL-1.2-or-later', + 'GFDL-1.3-only', + 'GFDL-1.3-or-later', + 'GL2PS', + 'GPL-1.0-only', + 'GPL-1.0-or-later', + 'GPL-2.0-only', + 'GPL-2.0-or-later', + 'GPL-3.0-only', + 'GPL-3.0-or-later', + 'Giftware', + 'Glide', + 'Glulxe', + 'HPND', + 'HaskellReport', + 'IBM-pibs', + 'ICU', + 'IJG', + 'IPA', + 'IPL-1.0', + 'ISC', + 'ImageMagick', + 'Imlib2', + 'Info-ZIP', + 'Intel', + 'Intel-ACPI', + 'Interbase-1.0', + 'JSON', + 'JasPer-2.0', + 'LAL-1.2', + 'LAL-1.3', + 'LGPL-2.0-only', + 'LGPL-2.0-or-later', + 'LGPL-2.1-only', + 'LGPL-2.1-or-later', + 'LGPL-3.0-only', + 'LGPL-3.0-or-later', + 'LGPLLR', + 'LPL-1.0', + 'LPL-1.02', + 'LPPL-1.0', + 'LPPL-1.1', + 'LPPL-1.2', + 'LPPL-1.3a', + 'LPPL-1.3c', + 'Latex2e', + 'Leptonica', + 'LiLiQ-P-1.1', + 'LiLiQ-R-1.1', + 'LiLiQ-Rplus-1.1', + 'Libpng', + 'Linux-OpenIB', + 'MIT', + 'MIT-0', + 'MIT-CMU', + 'MIT-advertising', + 'MIT-enna', + 'MIT-feh', + 'MITNFA', + 'MPL-1.0', + 'MPL-1.1', + 'MPL-2.0', + 'MPL-2.0-no-copyleft-exception', + 'MS-PL', + 'MS-RL', + 'MTLL', + 'MakeIndex', + 'MirOS', + 'Motosoto', + 'Multics', + 'Mup', + 'NASA-1.3', + 'NBPL-1.0', + 'NCSA', + 'NGPL', + 'NLOD-1.0', + 'NLPL', + 'NOSL', + 'NPL-1.0', + 'NPL-1.1', + 'NPOSL-3.0', + 'NRL', + 'NTP', + 'Naumen', + 'Net-SNMP', + 'NetCDF', + 'Newsletr', + 'Nokia', + 'Noweb', + 'OCCT-PL', + 'OCLC-2.0', + 'ODbL-1.0', + 'OFL-1.0', + 'OFL-1.1', + 'OGTSL', + 'OLDAP-1.1', + 'OLDAP-1.2', + 'OLDAP-1.3', + 'OLDAP-1.4', + 'OLDAP-2.0', + 'OLDAP-2.0.1', + 'OLDAP-2.1', + 'OLDAP-2.2', + 'OLDAP-2.2.1', + 'OLDAP-2.2.2', + 'OLDAP-2.3', + 'OLDAP-2.4', + 'OLDAP-2.5', + 'OLDAP-2.6', + 'OLDAP-2.7', + 'OLDAP-2.8', + 'OML', + 'OPL-1.0', + 'OSET-PL-2.1', + 'OSL-1.0', + 'OSL-1.1', + 'OSL-2.0', + 'OSL-2.1', + 'OSL-3.0', + 'OpenSSL', + 'PDDL-1.0', + 'PHP-3.0', + 'PHP-3.01', + 'Plexus', + 'PostgreSQL', + 'Python-2.0', + 'QPL-1.0', + 'Qhull', + 'RHeCos-1.1', + 'RPL-1.1', + 'RPL-1.5', + 'RPSL-1.0', + 'RSA-MD', + 'RSCPL', + 'Rdisc', + 'Ruby', + 'SAX-PD', + 'SCEA', + 'SGI-B-1.0', + 'SGI-B-1.1', + 'SGI-B-2.0', + 'SISSL', + 'SISSL-1.2', + 'SMLNJ', + 'SMPPL', + 'SNIA', + 'SPL-1.0', + 'SWL', + 'Saxpath', + 'Sendmail', + 'SimPL-2.0', + 'Sleepycat', + 'Spencer-86', + 'Spencer-94', + 'Spencer-99', + 'SugarCRM-1.1.3', + 'TCL', + 'TCP-wrappers', + 'TMate', + 'TORQUE-1.1', + 'TOSL', + 'UPL-1.0', + 'Unicode-DFS-2015', + 'Unicode-DFS-2016', + 'Unicode-TOU', + 'Unlicense', + 'VOSTROM', + 'VSL-1.0', + 'Vim', + 'W3C', + 'W3C-19980720', + 'W3C-20150513', + 'WTFPL', + 'Watcom-1.0', + 'Wsuipa', + 'X11', + 'XFree86-1.1', + 'XSkat', + 'Xerox', + 'Xnet', + 'YPL-1.0', + 'YPL-1.1', + 'ZPL-1.1', + 'ZPL-2.0', + 'ZPL-2.1', + 'Zed', + 'Zend-2.0', + 'Zimbra-1.3', + 'Zimbra-1.4', + 'Zlib', + 'bzip2-1.0.5', + 'bzip2-1.0.6', + 'curl', + 'diffmark', + 'dvipdfm', + 'eGenix', + 'gSOAP-1.3b', + 'gnuplot', + 'iMatix', + 'libtiff', + 'mpich2', + 'not-open-source', + 'psfrag', + 'psutils', + 'xinetd', + 'xpp', + 'zlib-acknowledgement', + ]) + .optional(), + _license: z.lazy(() => elementSchema).optional(), + fhirVersion: z + .enum([ + '0.0.80', + '0.0.81', + '0.0.82', + '0.01', + '0.05', + '0.06', + '0.11', + '0.4.0', + '0.5.0', + '1.0.0', + '1.0.1', + '1.0.2', + '1.1.0', + '1.4.0', + '1.6.0', + '1.8.0', + '3.0.0', + '3.0.1', + '3.0.2', + '3.3.0', + '3.5.0', + '4.0.0', + '4.0.1', + '4.1.0', + '4.3.0', + '4.3.0-cibuild', + '4.3.0-snapshot1', + ]) + .array() + .min(1), + _fhirVersion: z + .lazy(() => elementSchema) + .array() + .optional(), + dependsOn: implementationGuideDependsOnSchema.array().optional(), + global: implementationGuideGlobalSchema.array().optional(), + definition: implementationGuideDefinitionSchema.optional(), + manifest: implementationGuideManifestSchema.optional(), + }), + ) + +export const ingredientManufacturerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + role: z.enum(['actual', 'allowed', 'possible']).optional(), + _role: z.lazy(() => elementSchema).optional(), + manufacturer: z.lazy(() => referenceSchema), + }), + ) + +export const ingredientSubstanceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableReferenceSchema), + strength: ingredientSubstanceStrengthSchema.array().optional(), + }), + ) + +export const ingredientSubstanceStrengthSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + presentationRatio: z.lazy(() => ratioSchema).optional(), + presentationRatioRange: z.lazy(() => ratioRangeSchema).optional(), + textPresentation: stringSchema.optional(), + _textPresentation: z.lazy(() => elementSchema).optional(), + concentrationRatio: z.lazy(() => ratioSchema).optional(), + concentrationRatioRange: z.lazy(() => ratioRangeSchema).optional(), + textConcentration: stringSchema.optional(), + _textConcentration: z.lazy(() => elementSchema).optional(), + measurementPoint: stringSchema.optional(), + _measurementPoint: z.lazy(() => elementSchema).optional(), + country: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + referenceStrength: ingredientSubstanceStrengthReferenceStrengthSchema + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['presentationRatio', 'presentationRatioRange'], + required: false, + }, + { + names: ['concentrationRatio', 'concentrationRatioRange'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const ingredientSubstanceStrengthReferenceStrengthSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + substance: z.lazy(() => codeableReferenceSchema).optional(), + strengthRatio: z.lazy(() => ratioSchema).optional(), + strengthRatioRange: z.lazy(() => ratioRangeSchema).optional(), + measurementPoint: stringSchema.optional(), + _measurementPoint: z.lazy(() => elementSchema).optional(), + country: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { names: ['strengthRatio', 'strengthRatioRange'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const ingredientSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Ingredient'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + for: z + .lazy(() => referenceSchema) + .array() + .optional(), + role: z.lazy(() => codeableConceptSchema), + function: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + allergenicIndicator: booleanSchema.optional(), + _allergenicIndicator: z.lazy(() => elementSchema).optional(), + manufacturer: ingredientManufacturerSchema.array().optional(), + substance: ingredientSubstanceSchema, + }), +) + +export const insurancePlanContactSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + purpose: z.lazy(() => codeableConceptSchema).optional(), + name: z.lazy(() => humanNameSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + address: z.lazy(() => addressSchema).optional(), + }), + ) + +export const insurancePlanCoverageSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + network: z + .lazy(() => referenceSchema) + .array() + .optional(), + benefit: insurancePlanCoverageBenefitSchema.array().min(1), + }), + ) + +export const insurancePlanPlanSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + coverageArea: z + .lazy(() => referenceSchema) + .array() + .optional(), + network: z + .lazy(() => referenceSchema) + .array() + .optional(), + generalCost: insurancePlanPlanGeneralCostSchema.array().optional(), + specificCost: insurancePlanPlanSpecificCostSchema.array().optional(), + }), +) + +export const insurancePlanCoverageBenefitSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + requirement: stringSchema.optional(), + _requirement: z.lazy(() => elementSchema).optional(), + limit: insurancePlanCoverageBenefitLimitSchema.array().optional(), + }), + ) + +export const insurancePlanPlanGeneralCostSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + groupSize: positiveIntSchema.optional(), + _groupSize: z.lazy(() => elementSchema).optional(), + cost: z.lazy(() => moneySchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), + ) + +export const insurancePlanPlanSpecificCostSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema), + benefit: insurancePlanPlanSpecificCostBenefitSchema.array().optional(), + }), + ) + +export const insurancePlanCoverageBenefitLimitSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: z.lazy(() => quantitySchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const insurancePlanPlanSpecificCostBenefitSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + cost: insurancePlanPlanSpecificCostBenefitCostSchema.array().optional(), + }), + ) + +export const insurancePlanPlanSpecificCostBenefitCostSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + applicability: z.lazy(() => codeableConceptSchema).optional(), + qualifiers: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + value: z.lazy(() => quantitySchema).optional(), + }), + ) + +export const insurancePlanSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('InsurancePlan'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + alias: stringSchema.array().optional(), + _alias: z + .lazy(() => elementSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + ownedBy: z.lazy(() => referenceSchema).optional(), + administeredBy: z.lazy(() => referenceSchema).optional(), + coverageArea: z + .lazy(() => referenceSchema) + .array() + .optional(), + contact: insurancePlanContactSchema.array().optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + network: z + .lazy(() => referenceSchema) + .array() + .optional(), + coverage: insurancePlanCoverageSchema.array().optional(), + plan: insurancePlanPlanSchema.array().optional(), + }), +) + +export const invoiceParticipantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }), + ) + +export const invoiceLineItemSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + chargeItemReference: z.lazy(() => referenceSchema).optional(), + chargeItemCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + priceComponent: invoiceLineItemPriceComponentSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: ['chargeItemReference', 'chargeItemCodeableConcept'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const invoiceLineItemPriceComponentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'base', + 'deduction', + 'discount', + 'informational', + 'surcharge', + 'tax', + ]), + _type: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + }), + ) + +export const invoiceSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Invoice'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'balanced', + 'cancelled', + 'draft', + 'entered-in-error', + 'issued', + ]), + _status: z.lazy(() => elementSchema).optional(), + cancelledReason: stringSchema.optional(), + _cancelledReason: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + recipient: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + participant: invoiceParticipantSchema.array().optional(), + issuer: z.lazy(() => referenceSchema).optional(), + account: z.lazy(() => referenceSchema).optional(), + lineItem: invoiceLineItemSchema.array().optional(), + totalPriceComponent: z + .lazy(() => invoiceLineItemPriceComponentSchema) + .array() + .optional(), + totalNet: z.lazy(() => moneySchema).optional(), + totalGross: z.lazy(() => moneySchema).optional(), + paymentTerms: markdownSchema.optional(), + _paymentTerms: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), +) + +export const librarySchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Library'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + parameter: z + .lazy(() => parameterDefinitionSchema) + .array() + .optional(), + dataRequirement: z + .lazy(() => dataRequirementSchema) + .array() + .optional(), + content: z + .lazy(() => attachmentSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['subjectCodeableConcept', 'subjectReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const linkageItemSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['alternate', 'historical', 'source']), + _type: z.lazy(() => elementSchema).optional(), + resource: z.lazy(() => referenceSchema), + }), +) + +export const linkageSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Linkage'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + item: linkageItemSchema.array().min(1), + }), +) + +export const listEntrySchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + flag: z.lazy(() => codeableConceptSchema).optional(), + deleted: booleanSchema.optional(), + _deleted: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + item: z.lazy(() => referenceSchema), + }), +) + +export const listSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('List'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['current', 'entered-in-error', 'retired']), + _status: z.lazy(() => elementSchema).optional(), + mode: z.enum(['changes', 'snapshot', 'working']), + _mode: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + orderedBy: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + entry: listEntrySchema.array().optional(), + emptyReason: z.lazy(() => codeableConceptSchema).optional(), + }), +) + +export const locationPositionSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + longitude: decimalSchema, + _longitude: z.lazy(() => elementSchema).optional(), + latitude: decimalSchema, + _latitude: z.lazy(() => elementSchema).optional(), + altitude: decimalSchema.optional(), + _altitude: z.lazy(() => elementSchema).optional(), + }), +) + +export const locationHoursOfOperationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + daysOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .array() + .optional(), + _daysOfWeek: z + .lazy(() => elementSchema) + .array() + .optional(), + allDay: booleanSchema.optional(), + _allDay: z.lazy(() => elementSchema).optional(), + openingTime: timeSchema.optional(), + _openingTime: z.lazy(() => elementSchema).optional(), + closingTime: timeSchema.optional(), + _closingTime: z.lazy(() => elementSchema).optional(), + }), + ) + +export const locationSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Location'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'inactive', 'suspended']).optional(), + _status: z.lazy(() => elementSchema).optional(), + operationalStatus: z.lazy(() => codingSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + alias: stringSchema.array().optional(), + _alias: z + .lazy(() => elementSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + mode: z.enum(['instance', 'kind']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + address: z.lazy(() => addressSchema).optional(), + physicalType: z.lazy(() => codeableConceptSchema).optional(), + position: locationPositionSchema.optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + partOf: z.lazy(() => referenceSchema).optional(), + hoursOfOperation: locationHoursOfOperationSchema.array().optional(), + availabilityExceptions: stringSchema.optional(), + _availabilityExceptions: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const manufacturedItemDefinitionPropertySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueQuantity', + 'valueDate', + 'valueBoolean', + 'valueAttachment', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const manufacturedItemDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('ManufacturedItemDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + manufacturedDoseForm: z.lazy(() => codeableConceptSchema), + unitOfPresentation: z.lazy(() => codeableConceptSchema).optional(), + manufacturer: z + .lazy(() => referenceSchema) + .array() + .optional(), + ingredient: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + property: manufacturedItemDefinitionPropertySchema.array().optional(), + }), + ) + +export const marketingStatusSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + country: z.lazy(() => codeableConceptSchema).optional(), + jurisdiction: z.lazy(() => codeableConceptSchema).optional(), + status: z.lazy(() => codeableConceptSchema), + dateRange: z.lazy(() => periodSchema).optional(), + restoreDate: dateTimeSchema.optional(), + _restoreDate: z.lazy(() => elementSchema).optional(), + }), +) + +export const measureGroupSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + population: measureGroupPopulationSchema.array().optional(), + stratifier: measureGroupStratifierSchema.array().optional(), + }), +) + +export const measureSupplementalDataSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + usage: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema), + }), + ) + +export const measureGroupPopulationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema), + }), + ) + +export const measureGroupStratifierSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema).optional(), + component: measureGroupStratifierComponentSchema.array().optional(), + }), + ) + +export const measureGroupStratifierComponentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema), + }), + ) + +export const measureSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Measure'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + library: canonicalSchema.array().optional(), + _library: z + .lazy(() => elementSchema) + .array() + .optional(), + disclaimer: markdownSchema.optional(), + _disclaimer: z.lazy(() => elementSchema).optional(), + scoring: z.lazy(() => codeableConceptSchema).optional(), + compositeScoring: z.lazy(() => codeableConceptSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + riskAdjustment: stringSchema.optional(), + _riskAdjustment: z.lazy(() => elementSchema).optional(), + rateAggregation: stringSchema.optional(), + _rateAggregation: z.lazy(() => elementSchema).optional(), + rationale: markdownSchema.optional(), + _rationale: z.lazy(() => elementSchema).optional(), + clinicalRecommendationStatement: markdownSchema.optional(), + _clinicalRecommendationStatement: z.lazy(() => elementSchema).optional(), + improvementNotation: z.lazy(() => codeableConceptSchema).optional(), + definition: markdownSchema.array().optional(), + _definition: z + .lazy(() => elementSchema) + .array() + .optional(), + guidance: markdownSchema.optional(), + _guidance: z.lazy(() => elementSchema).optional(), + group: measureGroupSchema.array().optional(), + supplementalData: measureSupplementalDataSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: ['subjectCodeableConcept', 'subjectReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const measureReportGroupSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + population: measureReportGroupPopulationSchema.array().optional(), + measureScore: z.lazy(() => quantitySchema).optional(), + stratifier: measureReportGroupStratifierSchema.array().optional(), + }), + ) + +export const measureReportGroupPopulationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + count: integerSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + subjectResults: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const measureReportGroupStratifierSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + stratum: measureReportGroupStratifierStratumSchema.array().optional(), + }), + ) + +export const measureReportGroupStratifierStratumSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: z.lazy(() => codeableConceptSchema).optional(), + component: measureReportGroupStratifierStratumComponentSchema + .array() + .optional(), + population: measureReportGroupStratifierStratumPopulationSchema + .array() + .optional(), + measureScore: z.lazy(() => quantitySchema).optional(), + }), + ) + +export const measureReportGroupStratifierStratumComponentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + value: z.lazy(() => codeableConceptSchema), + }), + ) + +export const measureReportGroupStratifierStratumPopulationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + count: integerSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + subjectResults: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const measureReportSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('MeasureReport'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['complete', 'error', 'pending']), + _status: z.lazy(() => elementSchema).optional(), + type: z.enum(['data-collection', 'individual', 'subject-list', 'summary']), + _type: z.lazy(() => elementSchema).optional(), + measure: canonicalSchema, + _measure: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + reporter: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema), + improvementNotation: z.lazy(() => codeableConceptSchema).optional(), + group: measureReportGroupSchema.array().optional(), + evaluatedResource: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const mediaSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Media'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + modality: z.lazy(() => codeableConceptSchema).optional(), + view: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + createdDateTime: dateTimeSchema.optional(), + _createdDateTime: z.lazy(() => elementSchema).optional(), + createdPeriod: z.lazy(() => periodSchema).optional(), + issued: instantSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + operator: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + deviceName: stringSchema.optional(), + _deviceName: z.lazy(() => elementSchema).optional(), + device: z.lazy(() => referenceSchema).optional(), + height: positiveIntSchema.optional(), + _height: z.lazy(() => elementSchema).optional(), + width: positiveIntSchema.optional(), + _width: z.lazy(() => elementSchema).optional(), + frames: positiveIntSchema.optional(), + _frames: z.lazy(() => elementSchema).optional(), + duration: decimalSchema.optional(), + _duration: z.lazy(() => elementSchema).optional(), + content: z.lazy(() => attachmentSchema), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { names: ['createdDateTime', 'createdPeriod'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const medicationIngredientSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + isActive: booleanSchema.optional(), + _isActive: z.lazy(() => elementSchema).optional(), + strength: z.lazy(() => ratioSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['itemCodeableConcept', 'itemReference'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationBatchSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + lotNumber: stringSchema.optional(), + _lotNumber: z.lazy(() => elementSchema).optional(), + expirationDate: dateTimeSchema.optional(), + _expirationDate: z.lazy(() => elementSchema).optional(), + }), +) + +export const medicationSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Medication'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), + _status: z.lazy(() => elementSchema).optional(), + manufacturer: z.lazy(() => referenceSchema).optional(), + form: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => ratioSchema).optional(), + ingredient: medicationIngredientSchema.array().optional(), + batch: medicationBatchSchema.optional(), + }), +) + +export const medicationAdministrationPerformerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }), + ) + +export const medicationAdministrationDosageSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + site: z.lazy(() => codeableConceptSchema).optional(), + route: z.lazy(() => codeableConceptSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + dose: z.lazy(() => quantitySchema).optional(), + rateRatio: z.lazy(() => ratioSchema).optional(), + rateQuantity: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => + [{ names: ['rateRatio', 'rateQuantity'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationAdministrationSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicationAdministration'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiates: uriSchema.array().optional(), + _instantiates: z + .lazy(() => elementSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + medicationCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema), + context: z.lazy(() => referenceSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + performer: medicationAdministrationPerformerSchema.array().optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + request: z.lazy(() => referenceSchema).optional(), + device: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + dosage: medicationAdministrationDosageSchema.optional(), + eventHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['medicationCodeableConcept', 'medicationReference'], + required: true, + }, + { names: ['effectiveDateTime', 'effectivePeriod'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationDispensePerformerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }), + ) + +export const medicationDispenseSubstitutionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + wasSubstituted: booleanSchema, + _wasSubstituted: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + reason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + responsibleParty: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const medicationDispenseSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicationDispense'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'cancelled', + 'completed', + 'declined', + 'entered-in-error', + 'in-progress', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReasonCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + statusReasonReference: z.lazy(() => referenceSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + medicationCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + context: z.lazy(() => referenceSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .optional(), + performer: medicationDispensePerformerSchema.array().optional(), + location: z.lazy(() => referenceSchema).optional(), + authorizingPrescription: z + .lazy(() => referenceSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + daysSupply: z.lazy(() => quantitySchema).optional(), + whenPrepared: dateTimeSchema.optional(), + _whenPrepared: z.lazy(() => elementSchema).optional(), + whenHandedOver: dateTimeSchema.optional(), + _whenHandedOver: z.lazy(() => elementSchema).optional(), + destination: z.lazy(() => referenceSchema).optional(), + receiver: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + dosageInstruction: z + .lazy(() => dosageSchema) + .array() + .optional(), + substitution: medicationDispenseSubstitutionSchema.optional(), + detectedIssue: z + .lazy(() => referenceSchema) + .array() + .optional(), + eventHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['statusReasonCodeableConcept', 'statusReasonReference'], + required: false, + }, + { + names: ['medicationCodeableConcept', 'medicationReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationKnowledgeRelatedMedicationKnowledgeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + reference: z + .lazy(() => referenceSchema) + .array() + .min(1), + }), + ) + +export const medicationKnowledgeMonographSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const medicationKnowledgeIngredientSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + isActive: booleanSchema.optional(), + _isActive: z.lazy(() => elementSchema).optional(), + strength: z.lazy(() => ratioSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['itemCodeableConcept', 'itemReference'], required: true }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationKnowledgeCostSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + source: stringSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + cost: z.lazy(() => moneySchema), + }), + ) + +export const medicationKnowledgeMonitoringProgramSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + }), + ) + +export const medicationKnowledgeAdministrationGuidelinesSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + dosage: medicationKnowledgeAdministrationGuidelinesDosageSchema + .array() + .optional(), + indicationCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + indicationReference: z.lazy(() => referenceSchema).optional(), + patientCharacteristics: + medicationKnowledgeAdministrationGuidelinesPatientCharacteristicsSchema + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['indicationCodeableConcept', 'indicationReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationKnowledgeMedicineClassificationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + classification: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), + ) + +export const medicationKnowledgePackagingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + }), + ) + +export const medicationKnowledgeDrugCharacteristicSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueString', + 'valueQuantity', + 'valueBase64Binary', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationKnowledgeRegulatorySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + regulatoryAuthority: z.lazy(() => referenceSchema), + substitution: medicationKnowledgeRegulatorySubstitutionSchema + .array() + .optional(), + schedule: medicationKnowledgeRegulatoryScheduleSchema.array().optional(), + maxDispense: medicationKnowledgeRegulatoryMaxDispenseSchema.optional(), + }), + ) + +export const medicationKnowledgeKineticsSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + areaUnderCurve: z + .lazy(() => quantitySchema) + .array() + .optional(), + lethalDose50: z + .lazy(() => quantitySchema) + .array() + .optional(), + halfLifePeriod: z.lazy(() => durationSchema).optional(), + }), + ) + +export const medicationKnowledgeAdministrationGuidelinesDosageSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + dosage: z + .lazy(() => dosageSchema) + .array() + .min(1), + }), + ) + +export const medicationKnowledgeAdministrationGuidelinesPatientCharacteristicsSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + characteristicCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + characteristicQuantity: z.lazy(() => quantitySchema).optional(), + value: stringSchema.array().optional(), + _value: z + .lazy(() => elementSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'characteristicCodeableConcept', + 'characteristicQuantity', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationKnowledgeRegulatorySubstitutionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + allowed: booleanSchema, + _allowed: z.lazy(() => elementSchema).optional(), + }), + ) + +export const medicationKnowledgeRegulatoryScheduleSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + schedule: z.lazy(() => codeableConceptSchema), + }), + ) + +export const medicationKnowledgeRegulatoryMaxDispenseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema), + period: z.lazy(() => durationSchema).optional(), + }), + ) + +export const medicationKnowledgeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('MedicationKnowledge'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), + _status: z.lazy(() => elementSchema).optional(), + manufacturer: z.lazy(() => referenceSchema).optional(), + doseForm: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => quantitySchema).optional(), + synonym: stringSchema.array().optional(), + _synonym: z + .lazy(() => elementSchema) + .array() + .optional(), + relatedMedicationKnowledge: + medicationKnowledgeRelatedMedicationKnowledgeSchema.array().optional(), + associatedMedication: z + .lazy(() => referenceSchema) + .array() + .optional(), + productType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + monograph: medicationKnowledgeMonographSchema.array().optional(), + ingredient: medicationKnowledgeIngredientSchema.array().optional(), + preparationInstruction: markdownSchema.optional(), + _preparationInstruction: z.lazy(() => elementSchema).optional(), + intendedRoute: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + cost: medicationKnowledgeCostSchema.array().optional(), + monitoringProgram: medicationKnowledgeMonitoringProgramSchema + .array() + .optional(), + administrationGuidelines: + medicationKnowledgeAdministrationGuidelinesSchema.array().optional(), + medicineClassification: medicationKnowledgeMedicineClassificationSchema + .array() + .optional(), + packaging: medicationKnowledgePackagingSchema.optional(), + drugCharacteristic: medicationKnowledgeDrugCharacteristicSchema + .array() + .optional(), + contraindication: z + .lazy(() => referenceSchema) + .array() + .optional(), + regulatory: medicationKnowledgeRegulatorySchema.array().optional(), + kinetics: medicationKnowledgeKineticsSchema.array().optional(), + }), + ) + +export const medicationRequestDispenseRequestSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + initialFill: medicationRequestDispenseRequestInitialFillSchema.optional(), + dispenseInterval: z.lazy(() => durationSchema).optional(), + validityPeriod: z.lazy(() => periodSchema).optional(), + numberOfRepeatsAllowed: unsignedIntSchema.optional(), + _numberOfRepeatsAllowed: z.lazy(() => elementSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + expectedSupplyDuration: z.lazy(() => durationSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const medicationRequestSubstitutionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + allowedBoolean: booleanSchema.optional(), + _allowedBoolean: z.lazy(() => elementSchema).optional(), + allowedCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + reason: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['allowedBoolean', 'allowedCodeableConcept'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicationRequestDispenseRequestInitialFillSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + duration: z.lazy(() => durationSchema).optional(), + }), + ) + +export const medicationRequestSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('MedicationRequest'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'cancelled', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + intent: z.enum([ + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]), + _intent: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + reportedBoolean: booleanSchema.optional(), + _reportedBoolean: z.lazy(() => elementSchema).optional(), + reportedReference: z.lazy(() => referenceSchema).optional(), + medicationCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + performerType: z.lazy(() => codeableConceptSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + courseOfTherapyType: z.lazy(() => codeableConceptSchema).optional(), + insurance: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + dosageInstruction: z + .lazy(() => dosageSchema) + .array() + .optional(), + dispenseRequest: medicationRequestDispenseRequestSchema.optional(), + substitution: medicationRequestSubstitutionSchema.optional(), + priorPrescription: z.lazy(() => referenceSchema).optional(), + detectedIssue: z + .lazy(() => referenceSchema) + .array() + .optional(), + eventHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['reportedBoolean', 'reportedReference'], + required: false, + }, + { + names: ['medicationCodeableConcept', 'medicationReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const medicationStatementSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicationStatement'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'completed', + 'entered-in-error', + 'intended', + 'not-taken', + 'on-hold', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + medicationCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema), + context: z.lazy(() => referenceSchema).optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + dateAsserted: dateTimeSchema.optional(), + _dateAsserted: z.lazy(() => elementSchema).optional(), + informationSource: z.lazy(() => referenceSchema).optional(), + derivedFrom: z + .lazy(() => referenceSchema) + .array() + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + dosage: z + .lazy(() => dosageSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['medicationCodeableConcept', 'medicationReference'], + required: true, + }, + { + names: ['effectiveDateTime', 'effectivePeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicinalProductDefinitionContactSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + contact: z.lazy(() => referenceSchema), + }), + ) + +export const medicinalProductDefinitionNameSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + productName: stringSchema, + _productName: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + namePart: medicinalProductDefinitionNameNamePartSchema.array().optional(), + countryLanguage: medicinalProductDefinitionNameCountryLanguageSchema + .array() + .optional(), + }), + ) + +export const medicinalProductDefinitionCrossReferenceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + product: z.lazy(() => codeableReferenceSchema), + type: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const medicinalProductDefinitionOperationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableReferenceSchema).optional(), + effectiveDate: z.lazy(() => periodSchema).optional(), + organization: z + .lazy(() => referenceSchema) + .array() + .optional(), + confidentialityIndicator: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const medicinalProductDefinitionCharacteristicSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueQuantity', + 'valueDate', + 'valueBoolean', + 'valueAttachment', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const medicinalProductDefinitionNameNamePartSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + part: stringSchema, + _part: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + }), + ) + +export const medicinalProductDefinitionNameCountryLanguageSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + country: z.lazy(() => codeableConceptSchema), + jurisdiction: z.lazy(() => codeableConceptSchema).optional(), + language: z.lazy(() => codeableConceptSchema), + }), + ) + +export const medicinalProductDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('MedicinalProductDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + domain: z.lazy(() => codeableConceptSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + statusDate: dateTimeSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + combinedPharmaceuticalDoseForm: z + .lazy(() => codeableConceptSchema) + .optional(), + route: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + indication: markdownSchema.optional(), + _indication: z.lazy(() => elementSchema).optional(), + legalStatusOfSupply: z.lazy(() => codeableConceptSchema).optional(), + additionalMonitoringIndicator: z + .lazy(() => codeableConceptSchema) + .optional(), + specialMeasures: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + pediatricUseIndicator: z.lazy(() => codeableConceptSchema).optional(), + classification: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + marketingStatus: z + .lazy(() => marketingStatusSchema) + .array() + .optional(), + packagedMedicinalProduct: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + ingredient: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + impurity: z + .lazy(() => codeableReferenceSchema) + .array() + .optional(), + attachedDocument: z + .lazy(() => referenceSchema) + .array() + .optional(), + masterFile: z + .lazy(() => referenceSchema) + .array() + .optional(), + contact: medicinalProductDefinitionContactSchema.array().optional(), + clinicalTrial: z + .lazy(() => referenceSchema) + .array() + .optional(), + code: z + .lazy(() => codingSchema) + .array() + .optional(), + name: medicinalProductDefinitionNameSchema.array().min(1), + crossReference: medicinalProductDefinitionCrossReferenceSchema + .array() + .optional(), + operation: medicinalProductDefinitionOperationSchema.array().optional(), + characteristic: medicinalProductDefinitionCharacteristicSchema + .array() + .optional(), + }), + ) + +export const messageDefinitionFocusSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]), + _code: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + min: unsignedIntSchema, + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + }), + ) + +export const messageDefinitionAllowedResponseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + message: canonicalSchema, + _message: z.lazy(() => elementSchema).optional(), + situation: markdownSchema.optional(), + _situation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const messageDefinitionSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('MessageDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + replaces: canonicalSchema.array().optional(), + _replaces: z + .lazy(() => elementSchema) + .array() + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema, + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + base: canonicalSchema.optional(), + _base: z.lazy(() => elementSchema).optional(), + parent: canonicalSchema.array().optional(), + _parent: z + .lazy(() => elementSchema) + .array() + .optional(), + eventCoding: z.lazy(() => codingSchema).optional(), + eventUri: uriSchema.optional(), + _eventUri: z.lazy(() => elementSchema).optional(), + category: z + .enum(['consequence', 'currency', 'notification']) + .optional(), + _category: z.lazy(() => elementSchema).optional(), + focus: messageDefinitionFocusSchema.array().optional(), + responseRequired: z + .enum(['always', 'never', 'on-error', 'on-success']) + .optional(), + _responseRequired: z.lazy(() => elementSchema).optional(), + allowedResponse: messageDefinitionAllowedResponseSchema + .array() + .optional(), + graph: canonicalSchema.array().optional(), + _graph: z + .lazy(() => elementSchema) + .array() + .optional(), + }) + .refine( + (value) => + [{ names: ['eventCoding', 'eventUri'], required: true }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const messageHeaderDestinationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + target: z.lazy(() => referenceSchema).optional(), + endpoint: urlSchema, + _endpoint: z.lazy(() => elementSchema).optional(), + receiver: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const messageHeaderSourceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + software: stringSchema.optional(), + _software: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + contact: z.lazy(() => contactPointSchema).optional(), + endpoint: urlSchema, + _endpoint: z.lazy(() => elementSchema).optional(), + }), + ) + +export const messageHeaderResponseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: idSchema, + _identifier: z.lazy(() => elementSchema).optional(), + code: z.enum(['fatal-error', 'ok', 'transient-error']), + _code: z.lazy(() => elementSchema).optional(), + details: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const messageHeaderSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MessageHeader'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + eventCoding: z.lazy(() => codingSchema).optional(), + eventUri: uriSchema.optional(), + _eventUri: z.lazy(() => elementSchema).optional(), + destination: messageHeaderDestinationSchema.array().optional(), + sender: z.lazy(() => referenceSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + source: messageHeaderSourceSchema, + responsible: z.lazy(() => referenceSchema).optional(), + reason: z.lazy(() => codeableConceptSchema).optional(), + response: messageHeaderResponseSchema.optional(), + focus: z + .lazy(() => referenceSchema) + .array() + .optional(), + definition: canonicalSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [{ names: ['eventCoding', 'eventUri'], required: true }].every( + (group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const metaSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + versionId: idSchema.optional(), + _versionId: z.lazy(() => elementSchema).optional(), + lastUpdated: instantSchema.optional(), + _lastUpdated: z.lazy(() => elementSchema).optional(), + source: uriSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.array().optional(), + _profile: z + .lazy(() => elementSchema) + .array() + .optional(), + security: z + .lazy(() => codingSchema) + .array() + .optional(), + tag: z + .lazy(() => codingSchema) + .array() + .optional(), + }), +) + +export const molecularSequenceReferenceSeqSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + chromosome: z.lazy(() => codeableConceptSchema).optional(), + genomeBuild: stringSchema.optional(), + _genomeBuild: z.lazy(() => elementSchema).optional(), + orientation: z.enum(['antisense', 'sense']).optional(), + _orientation: z.lazy(() => elementSchema).optional(), + referenceSeqId: z.lazy(() => codeableConceptSchema).optional(), + referenceSeqPointer: z.lazy(() => referenceSchema).optional(), + referenceSeqString: stringSchema.optional(), + _referenceSeqString: z.lazy(() => elementSchema).optional(), + strand: z.enum(['crick', 'watson']).optional(), + _strand: z.lazy(() => elementSchema).optional(), + windowStart: integerSchema.optional(), + _windowStart: z.lazy(() => elementSchema).optional(), + windowEnd: integerSchema.optional(), + _windowEnd: z.lazy(() => elementSchema).optional(), + }), + ) + +export const molecularSequenceVariantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + observedAllele: stringSchema.optional(), + _observedAllele: z.lazy(() => elementSchema).optional(), + referenceAllele: stringSchema.optional(), + _referenceAllele: z.lazy(() => elementSchema).optional(), + cigar: stringSchema.optional(), + _cigar: z.lazy(() => elementSchema).optional(), + variantPointer: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const molecularSequenceQualitySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['indel', 'snp', 'unknown']), + _type: z.lazy(() => elementSchema).optional(), + standardSequence: z.lazy(() => codeableConceptSchema).optional(), + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + score: z.lazy(() => quantitySchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + truthTP: decimalSchema.optional(), + _truthTP: z.lazy(() => elementSchema).optional(), + queryTP: decimalSchema.optional(), + _queryTP: z.lazy(() => elementSchema).optional(), + truthFN: decimalSchema.optional(), + _truthFN: z.lazy(() => elementSchema).optional(), + queryFP: decimalSchema.optional(), + _queryFP: z.lazy(() => elementSchema).optional(), + gtFP: decimalSchema.optional(), + _gtFP: z.lazy(() => elementSchema).optional(), + precision: decimalSchema.optional(), + _precision: z.lazy(() => elementSchema).optional(), + recall: decimalSchema.optional(), + _recall: z.lazy(() => elementSchema).optional(), + fScore: decimalSchema.optional(), + _fScore: z.lazy(() => elementSchema).optional(), + roc: molecularSequenceQualityRocSchema.optional(), + }), + ) + +export const molecularSequenceRepositorySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['directlink', 'login', 'oauth', 'openapi', 'other']), + _type: z.lazy(() => elementSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + datasetId: stringSchema.optional(), + _datasetId: z.lazy(() => elementSchema).optional(), + variantsetId: stringSchema.optional(), + _variantsetId: z.lazy(() => elementSchema).optional(), + readsetId: stringSchema.optional(), + _readsetId: z.lazy(() => elementSchema).optional(), + }), + ) + +export const molecularSequenceStructureVariantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + variantType: z.lazy(() => codeableConceptSchema).optional(), + exact: booleanSchema.optional(), + _exact: z.lazy(() => elementSchema).optional(), + length: integerSchema.optional(), + _length: z.lazy(() => elementSchema).optional(), + outer: molecularSequenceStructureVariantOuterSchema.optional(), + inner: molecularSequenceStructureVariantInnerSchema.optional(), + }), + ) + +export const molecularSequenceQualityRocSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + score: integerSchema.array().optional(), + _score: z + .lazy(() => elementSchema) + .array() + .optional(), + numTP: integerSchema.array().optional(), + _numTP: z + .lazy(() => elementSchema) + .array() + .optional(), + numFP: integerSchema.array().optional(), + _numFP: z + .lazy(() => elementSchema) + .array() + .optional(), + numFN: integerSchema.array().optional(), + _numFN: z + .lazy(() => elementSchema) + .array() + .optional(), + precision: decimalSchema.array().optional(), + _precision: z + .lazy(() => elementSchema) + .array() + .optional(), + sensitivity: decimalSchema.array().optional(), + _sensitivity: z + .lazy(() => elementSchema) + .array() + .optional(), + fMeasure: decimalSchema.array().optional(), + _fMeasure: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const molecularSequenceStructureVariantOuterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + }), + ) + +export const molecularSequenceStructureVariantInnerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + }), + ) + +export const molecularSequenceSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('MolecularSequence'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + type: z.enum(['aa', 'dna', 'rna']).optional(), + _type: z.lazy(() => elementSchema).optional(), + coordinateSystem: integerSchema, + _coordinateSystem: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema).optional(), + specimen: z.lazy(() => referenceSchema).optional(), + device: z.lazy(() => referenceSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + referenceSeq: molecularSequenceReferenceSeqSchema.optional(), + variant: molecularSequenceVariantSchema.array().optional(), + observedSeq: stringSchema.optional(), + _observedSeq: z.lazy(() => elementSchema).optional(), + quality: molecularSequenceQualitySchema.array().optional(), + readCoverage: integerSchema.optional(), + _readCoverage: z.lazy(() => elementSchema).optional(), + repository: molecularSequenceRepositorySchema.array().optional(), + pointer: z + .lazy(() => referenceSchema) + .array() + .optional(), + structureVariant: molecularSequenceStructureVariantSchema + .array() + .optional(), + }), +) + +export const moneySchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + currency: codeSchema.optional(), + _currency: z.lazy(() => elementSchema).optional(), + }), +) + +export const namingSystemUniqueIdSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['oid', 'other', 'uri', 'uuid']), + _type: z.lazy(() => elementSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + preferred: booleanSchema.optional(), + _preferred: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), + ) + +export const namingSystemSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('NamingSystem'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + kind: z.enum(['codesystem', 'identifier', 'root']), + _kind: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema, + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + responsible: stringSchema.optional(), + _responsible: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + uniqueId: namingSystemUniqueIdSchema.array().min(1), + }), +) + +export const narrativeSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + status: z.enum(['additional', 'empty', 'extensions', 'generated']), + _status: z.lazy(() => elementSchema).optional(), + div: xhtmlSchema, + _div: z.lazy(() => elementSchema).optional(), + }), +) + +export const nutritionOrderOralDietSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + schedule: z + .lazy(() => timingSchema) + .array() + .optional(), + nutrient: nutritionOrderOralDietNutrientSchema.array().optional(), + texture: nutritionOrderOralDietTextureSchema.array().optional(), + fluidConsistencyType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + instruction: stringSchema.optional(), + _instruction: z.lazy(() => elementSchema).optional(), + }), + ) + +export const nutritionOrderSupplementSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + productName: stringSchema.optional(), + _productName: z.lazy(() => elementSchema).optional(), + schedule: z + .lazy(() => timingSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + instruction: stringSchema.optional(), + _instruction: z.lazy(() => elementSchema).optional(), + }), + ) + +export const nutritionOrderEnteralFormulaSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + baseFormulaType: z.lazy(() => codeableConceptSchema).optional(), + baseFormulaProductName: stringSchema.optional(), + _baseFormulaProductName: z.lazy(() => elementSchema).optional(), + additiveType: z.lazy(() => codeableConceptSchema).optional(), + additiveProductName: stringSchema.optional(), + _additiveProductName: z.lazy(() => elementSchema).optional(), + caloricDensity: z.lazy(() => quantitySchema).optional(), + routeofAdministration: z.lazy(() => codeableConceptSchema).optional(), + administration: nutritionOrderEnteralFormulaAdministrationSchema + .array() + .optional(), + maxVolumeToDeliver: z.lazy(() => quantitySchema).optional(), + administrationInstruction: stringSchema.optional(), + _administrationInstruction: z.lazy(() => elementSchema).optional(), + }), + ) + +export const nutritionOrderOralDietNutrientSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifier: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => quantitySchema).optional(), + }), + ) + +export const nutritionOrderOralDietTextureSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifier: z.lazy(() => codeableConceptSchema).optional(), + foodType: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const nutritionOrderEnteralFormulaAdministrationSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + schedule: z.lazy(() => timingSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + rateQuantity: z.lazy(() => quantitySchema).optional(), + rateRatio: z.lazy(() => ratioSchema).optional(), + }) + .refine( + (value) => + [{ names: ['rateQuantity', 'rateRatio'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const nutritionOrderSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('NutritionOrder'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiates: uriSchema.array().optional(), + _instantiates: z + .lazy(() => elementSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + intent: z.enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]), + _intent: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + dateTime: dateTimeSchema, + _dateTime: z.lazy(() => elementSchema).optional(), + orderer: z.lazy(() => referenceSchema).optional(), + allergyIntolerance: z + .lazy(() => referenceSchema) + .array() + .optional(), + foodPreferenceModifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + excludeFoodModifier: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + oralDiet: nutritionOrderOralDietSchema.optional(), + supplement: nutritionOrderSupplementSchema.array().optional(), + enteralFormula: nutritionOrderEnteralFormulaSchema.optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), +) + +export const nutritionProductNutrientSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + item: z.lazy(() => codeableReferenceSchema).optional(), + amount: z + .lazy(() => ratioSchema) + .array() + .optional(), + }), + ) + +export const nutritionProductIngredientSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + item: z.lazy(() => codeableReferenceSchema), + amount: z + .lazy(() => ratioSchema) + .array() + .optional(), + }), + ) + +export const nutritionProductProductCharacteristicSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueString', + 'valueQuantity', + 'valueBase64Binary', + 'valueAttachment', + 'valueBoolean', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const nutritionProductInstanceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + lotNumber: stringSchema.optional(), + _lotNumber: z.lazy(() => elementSchema).optional(), + expiry: dateTimeSchema.optional(), + _expiry: z.lazy(() => elementSchema).optional(), + useBy: dateTimeSchema.optional(), + _useBy: z.lazy(() => elementSchema).optional(), + }), + ) + +export const nutritionProductSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('NutritionProduct'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + status: z.enum(['active', 'entered-in-error', 'inactive']), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + manufacturer: z + .lazy(() => referenceSchema) + .array() + .optional(), + nutrient: nutritionProductNutrientSchema.array().optional(), + ingredient: nutritionProductIngredientSchema.array().optional(), + knownAllergen: z + .lazy(() => codeableReferenceSchema) + .array() + .optional(), + productCharacteristic: nutritionProductProductCharacteristicSchema + .array() + .optional(), + instance: nutritionProductInstanceSchema.optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), +) + +export const observationReferenceRangeSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + low: z.lazy(() => quantitySchema).optional(), + high: z.lazy(() => quantitySchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + appliesTo: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + age: z.lazy(() => rangeSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => { + const range = value as { + low?: unknown + high?: unknown + text?: unknown + } + return ( + range.low !== undefined || + range.high !== undefined || + range.text !== undefined + ) + }, + { message: 'obs-3: a reference range states a low, a high, or text.' }, + ), + ) + +export const observationComponentSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), + interpretation: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + referenceRange: z + .lazy(() => observationReferenceRangeSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueQuantity', + 'valueCodeableConcept', + 'valueString', + 'valueBoolean', + 'valueInteger', + 'valueRange', + 'valueRatio', + 'valueSampledData', + 'valueTime', + 'valueDateTime', + 'valuePeriod', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => { + const observation = value as Record + const hasValue = Object.keys(observation).some( + (key) => key.startsWith('value') && observation[key] !== undefined, + ) + return observation.dataAbsentReason === undefined || !hasValue + }, + { + message: 'obs-6: dataAbsentReason is present only when no value is.', + }, + ), + ) + +export const observationSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Observation'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'amended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'preliminary', + 'registered', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema).optional(), + focus: z + .lazy(() => referenceSchema) + .array() + .optional(), + encounter: z.lazy(() => referenceSchema).optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + effectiveTiming: z.lazy(() => timingSchema).optional(), + effectiveInstant: instantSchema.optional(), + _effectiveInstant: z.lazy(() => elementSchema).optional(), + issued: instantSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + performer: z + .lazy(() => referenceSchema) + .array() + .optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), + interpretation: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + specimen: z.lazy(() => referenceSchema).optional(), + device: z.lazy(() => referenceSchema).optional(), + referenceRange: observationReferenceRangeSchema.array().optional(), + hasMember: z + .lazy(() => referenceSchema) + .array() + .optional(), + derivedFrom: z + .lazy(() => referenceSchema) + .array() + .optional(), + component: observationComponentSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'effectiveDateTime', + 'effectivePeriod', + 'effectiveTiming', + 'effectiveInstant', + ], + required: false, + }, + { + names: [ + 'valueQuantity', + 'valueCodeableConcept', + 'valueString', + 'valueBoolean', + 'valueInteger', + 'valueRange', + 'valueRatio', + 'valueSampledData', + 'valueTime', + 'valueDateTime', + 'valuePeriod', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => { + const observation = value as Record + const hasValue = Object.keys(observation).some( + (key) => key.startsWith('value') && observation[key] !== undefined, + ) + return observation.dataAbsentReason === undefined || !hasValue + }, + { message: 'obs-6: dataAbsentReason is present only when no value is.' }, + ), +) + +export const observationDefinitionQuantitativeDetailsSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + customaryUnit: z.lazy(() => codeableConceptSchema).optional(), + unit: z.lazy(() => codeableConceptSchema).optional(), + conversionFactor: decimalSchema.optional(), + _conversionFactor: z.lazy(() => elementSchema).optional(), + decimalPrecision: integerSchema.optional(), + _decimalPrecision: z.lazy(() => elementSchema).optional(), + }), + ) + +export const observationDefinitionQualifiedIntervalSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.enum(['absolute', 'critical', 'reference']).optional(), + _category: z.lazy(() => elementSchema).optional(), + range: z.lazy(() => rangeSchema).optional(), + context: z.lazy(() => codeableConceptSchema).optional(), + appliesTo: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + age: z.lazy(() => rangeSchema).optional(), + gestationalAge: z.lazy(() => rangeSchema).optional(), + condition: stringSchema.optional(), + _condition: z.lazy(() => elementSchema).optional(), + }), + ) + +export const observationDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('ObservationDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + permittedDataType: z + .enum([ + 'CodeableConcept', + 'Period', + 'Quantity', + 'Range', + 'Ratio', + 'SampledData', + 'boolean', + 'dateTime', + 'integer', + 'string', + 'time', + ]) + .array() + .optional(), + _permittedDataType: z + .lazy(() => elementSchema) + .array() + .optional(), + multipleResultsAllowed: booleanSchema.optional(), + _multipleResultsAllowed: z.lazy(() => elementSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + preferredReportName: stringSchema.optional(), + _preferredReportName: z.lazy(() => elementSchema).optional(), + quantitativeDetails: + observationDefinitionQuantitativeDetailsSchema.optional(), + qualifiedInterval: observationDefinitionQualifiedIntervalSchema + .array() + .optional(), + validCodedValueSet: z.lazy(() => referenceSchema).optional(), + normalCodedValueSet: z.lazy(() => referenceSchema).optional(), + abnormalCodedValueSet: z.lazy(() => referenceSchema).optional(), + criticalCodedValueSet: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const operationDefinitionParameterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: codeSchema, + _name: z.lazy(() => elementSchema).optional(), + use: z.enum(['in', 'out']), + _use: z.lazy(() => elementSchema).optional(), + min: integerSchema, + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema, + _max: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Any', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'CodeableConcept', + 'CodeableReference', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'RatioRange', + 'Reference', + 'RegulatedAuthorization', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'Type', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + targetProfile: canonicalSchema.array().optional(), + _targetProfile: z + .lazy(() => elementSchema) + .array() + .optional(), + searchType: z + .enum([ + 'composite', + 'date', + 'number', + 'quantity', + 'reference', + 'special', + 'string', + 'token', + 'uri', + ]) + .optional(), + _searchType: z.lazy(() => elementSchema).optional(), + binding: operationDefinitionParameterBindingSchema.optional(), + referencedFrom: operationDefinitionParameterReferencedFromSchema + .array() + .optional(), + part: z + .lazy(() => operationDefinitionParameterSchema) + .array() + .optional(), + }), + ) + +export const operationDefinitionOverloadSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + parameterName: stringSchema.array().optional(), + _parameterName: z + .lazy(() => elementSchema) + .array() + .optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), + ) + +export const operationDefinitionParameterBindingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + strength: z.enum(['example', 'extensible', 'preferred', 'required']), + _strength: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema, + _valueSet: z.lazy(() => elementSchema).optional(), + }), + ) + +export const operationDefinitionParameterReferencedFromSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + source: stringSchema, + _source: z.lazy(() => elementSchema).optional(), + sourceId: stringSchema.optional(), + _sourceId: z.lazy(() => elementSchema).optional(), + }), + ) + +export const operationDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('OperationDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + kind: z.enum(['operation', 'query']), + _kind: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + affectsState: booleanSchema.optional(), + _affectsState: z.lazy(() => elementSchema).optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + comment: markdownSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + base: canonicalSchema.optional(), + _base: z.lazy(() => elementSchema).optional(), + resource: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .array() + .optional(), + _resource: z + .lazy(() => elementSchema) + .array() + .optional(), + system: booleanSchema, + _system: z.lazy(() => elementSchema).optional(), + type: booleanSchema, + _type: z.lazy(() => elementSchema).optional(), + instance: booleanSchema, + _instance: z.lazy(() => elementSchema).optional(), + inputProfile: canonicalSchema.optional(), + _inputProfile: z.lazy(() => elementSchema).optional(), + outputProfile: canonicalSchema.optional(), + _outputProfile: z.lazy(() => elementSchema).optional(), + parameter: operationDefinitionParameterSchema.array().optional(), + overload: operationDefinitionOverloadSchema.array().optional(), + }), + ) + +export const operationOutcomeIssueSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + severity: z.enum(['error', 'fatal', 'information', 'warning']), + _severity: z.lazy(() => elementSchema).optional(), + code: z.enum([ + 'business-rule', + 'code-invalid', + 'conflict', + 'deleted', + 'duplicate', + 'exception', + 'expired', + 'extension', + 'forbidden', + 'incomplete', + 'informational', + 'invalid', + 'invariant', + 'lock-error', + 'login', + 'multiple-matches', + 'no-store', + 'not-found', + 'not-supported', + 'processing', + 'required', + 'security', + 'structure', + 'suppressed', + 'throttled', + 'timeout', + 'too-costly', + 'too-long', + 'transient', + 'unknown', + 'value', + ]), + _code: z.lazy(() => elementSchema).optional(), + details: z.lazy(() => codeableConceptSchema).optional(), + diagnostics: stringSchema.optional(), + _diagnostics: z.lazy(() => elementSchema).optional(), + location: stringSchema.array().optional(), + _location: z + .lazy(() => elementSchema) + .array() + .optional(), + expression: stringSchema.array().optional(), + _expression: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const operationOutcomeSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('OperationOutcome'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + issue: operationOutcomeIssueSchema.array().min(1), + }), +) + +export const organizationContactSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + purpose: z.lazy(() => codeableConceptSchema).optional(), + name: z.lazy(() => humanNameSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + address: z.lazy(() => addressSchema).optional(), + }), + ) + +export const organizationSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Organization'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + alias: stringSchema.array().optional(), + _alias: z + .lazy(() => elementSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + address: z + .lazy(() => addressSchema) + .array() + .optional(), + partOf: z.lazy(() => referenceSchema).optional(), + contact: organizationContactSchema.array().optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const organizationAffiliationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('OrganizationAffiliation'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + participatingOrganization: z.lazy(() => referenceSchema).optional(), + network: z + .lazy(() => referenceSchema) + .array() + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + location: z + .lazy(() => referenceSchema) + .array() + .optional(), + healthcareService: z + .lazy(() => referenceSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const packagedProductDefinitionLegalStatusOfSupplySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + jurisdiction: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const packagedProductDefinitionPackageSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + quantity: integerSchema.optional(), + _quantity: z.lazy(() => elementSchema).optional(), + material: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + alternateMaterial: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + shelfLifeStorage: packagedProductDefinitionPackageShelfLifeStorageSchema + .array() + .optional(), + manufacturer: z + .lazy(() => referenceSchema) + .array() + .optional(), + property: packagedProductDefinitionPackagePropertySchema + .array() + .optional(), + containedItem: packagedProductDefinitionPackageContainedItemSchema + .array() + .optional(), + package: z + .lazy(() => packagedProductDefinitionPackageSchema) + .array() + .optional(), + }), + ) + +export const packagedProductDefinitionPackageShelfLifeStorageSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + periodDuration: z.lazy(() => durationSchema).optional(), + periodString: stringSchema.optional(), + _periodString: z.lazy(() => elementSchema).optional(), + specialPrecautionsForStorage: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { names: ['periodDuration', 'periodString'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const packagedProductDefinitionPackagePropertySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueQuantity', + 'valueDate', + 'valueBoolean', + 'valueAttachment', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const packagedProductDefinitionPackageContainedItemSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + item: z.lazy(() => codeableReferenceSchema), + amount: z.lazy(() => quantitySchema).optional(), + }), + ) + +export const packagedProductDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('PackagedProductDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + packageFor: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + statusDate: dateTimeSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), + containedItemQuantity: z + .lazy(() => quantitySchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + legalStatusOfSupply: packagedProductDefinitionLegalStatusOfSupplySchema + .array() + .optional(), + marketingStatus: z + .lazy(() => marketingStatusSchema) + .array() + .optional(), + characteristic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + copackagedIndicator: booleanSchema.optional(), + _copackagedIndicator: z.lazy(() => elementSchema).optional(), + manufacturer: z + .lazy(() => referenceSchema) + .array() + .optional(), + package: packagedProductDefinitionPackageSchema.optional(), + }), + ) + +export const parameterDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: codeSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + use: z.enum(['in', 'out']), + _use: z.lazy(() => elementSchema).optional(), + min: integerSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + type: z.enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Any', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'CodeableConcept', + 'CodeableReference', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'RatioRange', + 'Reference', + 'RegulatedAuthorization', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'Type', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]), + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + }), + ) + +export const parametersParameterSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + valueMeta: z.lazy(() => metaSchema).optional(), + resource: z.lazy(() => resourceSchema).optional(), + part: z + .lazy(() => parametersParameterSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBase64Binary', + 'valueBoolean', + 'valueCanonical', + 'valueCode', + 'valueDate', + 'valueDateTime', + 'valueDecimal', + 'valueId', + 'valueInstant', + 'valueInteger', + 'valueMarkdown', + 'valueOid', + 'valuePositiveInt', + 'valueString', + 'valueTime', + 'valueUnsignedInt', + 'valueUri', + 'valueUrl', + 'valueUuid', + 'valueAddress', + 'valueAge', + 'valueAnnotation', + 'valueAttachment', + 'valueCodeableConcept', + 'valueCoding', + 'valueContactPoint', + 'valueCount', + 'valueDistance', + 'valueDuration', + 'valueHumanName', + 'valueIdentifier', + 'valueMoney', + 'valuePeriod', + 'valueQuantity', + 'valueRange', + 'valueRatio', + 'valueReference', + 'valueSampledData', + 'valueSignature', + 'valueTiming', + 'valueContactDetail', + 'valueContributor', + 'valueDataRequirement', + 'valueExpression', + 'valueParameterDefinition', + 'valueRelatedArtifact', + 'valueTriggerDefinition', + 'valueUsageContext', + 'valueDosage', + 'valueMeta', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const parametersSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Parameters'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + parameter: parametersParameterSchema.array().optional(), + }), +) + +export const patientContactSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + relationship: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + name: z.lazy(() => humanNameSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + address: z.lazy(() => addressSchema).optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }), +) + +export const patientCommunicationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + language: z.lazy(() => codeableConceptSchema), + preferred: booleanSchema.optional(), + _preferred: z.lazy(() => elementSchema).optional(), + }), + ) + +export const patientLinkSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + other: z.lazy(() => referenceSchema), + type: z.enum(['refer', 'replaced-by', 'replaces', 'seealso']), + _type: z.lazy(() => elementSchema).optional(), + }), +) + +export const patientSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Patient'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + name: z + .lazy(() => humanNameSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + deceasedBoolean: booleanSchema.optional(), + _deceasedBoolean: z.lazy(() => elementSchema).optional(), + deceasedDateTime: dateTimeSchema.optional(), + _deceasedDateTime: z.lazy(() => elementSchema).optional(), + address: z + .lazy(() => addressSchema) + .array() + .optional(), + maritalStatus: z.lazy(() => codeableConceptSchema).optional(), + multipleBirthBoolean: booleanSchema.optional(), + _multipleBirthBoolean: z.lazy(() => elementSchema).optional(), + multipleBirthInteger: integerSchema.optional(), + _multipleBirthInteger: z.lazy(() => elementSchema).optional(), + photo: z + .lazy(() => attachmentSchema) + .array() + .optional(), + contact: patientContactSchema.array().optional(), + communication: patientCommunicationSchema.array().optional(), + generalPractitioner: z + .lazy(() => referenceSchema) + .array() + .optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + link: patientLinkSchema.array().optional(), + }) + .refine( + (value) => + [ + { names: ['deceasedBoolean', 'deceasedDateTime'], required: false }, + { + names: ['multipleBirthBoolean', 'multipleBirthInteger'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const paymentNoticeSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('PaymentNotice'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + request: z.lazy(() => referenceSchema).optional(), + response: z.lazy(() => referenceSchema).optional(), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + provider: z.lazy(() => referenceSchema).optional(), + payment: z.lazy(() => referenceSchema), + paymentDate: dateSchema.optional(), + _paymentDate: z.lazy(() => elementSchema).optional(), + payee: z.lazy(() => referenceSchema).optional(), + recipient: z.lazy(() => referenceSchema), + amount: z.lazy(() => moneySchema), + paymentStatus: z.lazy(() => codeableConceptSchema).optional(), + }), +) + +export const paymentReconciliationDetailSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + predecessor: z.lazy(() => identifierSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + request: z.lazy(() => referenceSchema).optional(), + submitter: z.lazy(() => referenceSchema).optional(), + response: z.lazy(() => referenceSchema).optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + responsible: z.lazy(() => referenceSchema).optional(), + payee: z.lazy(() => referenceSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + }), + ) + +export const paymentReconciliationProcessNoteSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['display', 'print', 'printoper']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }), + ) + +export const paymentReconciliationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('PaymentReconciliation'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + paymentIssuer: z.lazy(() => referenceSchema).optional(), + request: z.lazy(() => referenceSchema).optional(), + requestor: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + paymentDate: dateSchema, + _paymentDate: z.lazy(() => elementSchema).optional(), + paymentAmount: z.lazy(() => moneySchema), + paymentIdentifier: z.lazy(() => identifierSchema).optional(), + detail: paymentReconciliationDetailSchema.array().optional(), + formCode: z.lazy(() => codeableConceptSchema).optional(), + processNote: paymentReconciliationProcessNoteSchema.array().optional(), + }), + ) + +export const periodSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + start: dateTimeSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: dateTimeSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => { + const period = value as { start?: string; end?: string } + if (period.start === undefined || period.end === undefined) return true + const start = fhirDateTimeToEpoch(period.start) + const end = fhirDateTimeToEpoch(period.end) + // A malformed endpoint is a malformed date, not an ordering fault; the release says + // as much with start.hasValue(). Reporting both would name the wrong problem. + if (Number.isNaN(start) || Number.isNaN(end)) return true + return start <= end + }, + { message: 'Period.start must not be later than Period.end.' }, + ), +) + +export const personLinkSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + target: z.lazy(() => referenceSchema), + assurance: z.enum(['level1', 'level2', 'level3', 'level4']).optional(), + _assurance: z.lazy(() => elementSchema).optional(), + }), +) + +export const personSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Person'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + name: z + .lazy(() => humanNameSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + address: z + .lazy(() => addressSchema) + .array() + .optional(), + photo: z.lazy(() => attachmentSchema).optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + link: personLinkSchema.array().optional(), + }), +) + +export const planDefinitionGoalSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + description: z.lazy(() => codeableConceptSchema), + priority: z.lazy(() => codeableConceptSchema).optional(), + start: z.lazy(() => codeableConceptSchema).optional(), + addresses: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + documentation: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + target: planDefinitionGoalTargetSchema.array().optional(), + }), + ) + +export const planDefinitionActionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + prefix: stringSchema.optional(), + _prefix: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + textEquivalent: stringSchema.optional(), + _textEquivalent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + documentation: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + goalId: idSchema.array().optional(), + _goalId: z + .lazy(() => elementSchema) + .array() + .optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + subjectCanonical: canonicalSchema.optional(), + _subjectCanonical: z.lazy(() => elementSchema).optional(), + trigger: z + .lazy(() => triggerDefinitionSchema) + .array() + .optional(), + condition: planDefinitionActionConditionSchema.array().optional(), + input: z + .lazy(() => dataRequirementSchema) + .array() + .optional(), + output: z + .lazy(() => dataRequirementSchema) + .array() + .optional(), + relatedAction: planDefinitionActionRelatedActionSchema + .array() + .optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + timingAge: z.lazy(() => ageSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingDuration: z.lazy(() => durationSchema).optional(), + timingRange: z.lazy(() => rangeSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + participant: planDefinitionActionParticipantSchema.array().optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + groupingBehavior: z + .enum(['logical-group', 'sentence-group', 'visual-group']) + .optional(), + _groupingBehavior: z.lazy(() => elementSchema).optional(), + selectionBehavior: z + .enum([ + 'all', + 'all-or-none', + 'any', + 'at-most-one', + 'exactly-one', + 'one-or-more', + ]) + .optional(), + _selectionBehavior: z.lazy(() => elementSchema).optional(), + requiredBehavior: z + .enum(['could', 'must', 'must-unless-documented']) + .optional(), + _requiredBehavior: z.lazy(() => elementSchema).optional(), + precheckBehavior: z.enum(['no', 'yes']).optional(), + _precheckBehavior: z.lazy(() => elementSchema).optional(), + cardinalityBehavior: z.enum(['multiple', 'single']).optional(), + _cardinalityBehavior: z.lazy(() => elementSchema).optional(), + definitionCanonical: canonicalSchema.optional(), + _definitionCanonical: z.lazy(() => elementSchema).optional(), + definitionUri: uriSchema.optional(), + _definitionUri: z.lazy(() => elementSchema).optional(), + transform: canonicalSchema.optional(), + _transform: z.lazy(() => elementSchema).optional(), + dynamicValue: planDefinitionActionDynamicValueSchema.array().optional(), + action: z + .lazy(() => planDefinitionActionSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'subjectCodeableConcept', + 'subjectReference', + 'subjectCanonical', + ], + required: false, + }, + { + names: [ + 'timingDateTime', + 'timingAge', + 'timingPeriod', + 'timingDuration', + 'timingRange', + 'timingTiming', + ], + required: false, + }, + { + names: ['definitionCanonical', 'definitionUri'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const planDefinitionGoalTargetSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + measure: z.lazy(() => codeableConceptSchema).optional(), + detailQuantity: z.lazy(() => quantitySchema).optional(), + detailRange: z.lazy(() => rangeSchema).optional(), + detailCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + due: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['detailQuantity', 'detailRange', 'detailCodeableConcept'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const planDefinitionActionConditionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + kind: z.enum(['applicability', 'start', 'stop']), + _kind: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema).optional(), + }), + ) + +export const planDefinitionActionRelatedActionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + actionId: idSchema, + _actionId: z.lazy(() => elementSchema).optional(), + relationship: z.enum([ + 'after', + 'after-end', + 'after-start', + 'before', + 'before-end', + 'before-start', + 'concurrent', + 'concurrent-with-end', + 'concurrent-with-start', + ]), + _relationship: z.lazy(() => elementSchema).optional(), + offsetDuration: z.lazy(() => durationSchema).optional(), + offsetRange: z.lazy(() => rangeSchema).optional(), + }) + .refine( + (value) => + [{ names: ['offsetDuration', 'offsetRange'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const planDefinitionActionParticipantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['device', 'patient', 'practitioner', 'related-person']), + _type: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const planDefinitionActionDynamicValueSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema).optional(), + }), + ) + +export const planDefinitionSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('PlanDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + subjectCanonical: canonicalSchema.optional(), + _subjectCanonical: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + library: canonicalSchema.array().optional(), + _library: z + .lazy(() => elementSchema) + .array() + .optional(), + goal: planDefinitionGoalSchema.array().optional(), + action: planDefinitionActionSchema.array().optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'subjectCodeableConcept', + 'subjectReference', + 'subjectCanonical', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const practitionerQualificationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + period: z.lazy(() => periodSchema).optional(), + issuer: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const practitionerSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Practitioner'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + name: z + .lazy(() => humanNameSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + address: z + .lazy(() => addressSchema) + .array() + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + photo: z + .lazy(() => attachmentSchema) + .array() + .optional(), + qualification: practitionerQualificationSchema.array().optional(), + communication: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), +) + +export const practitionerRoleAvailableTimeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + daysOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .array() + .optional(), + _daysOfWeek: z + .lazy(() => elementSchema) + .array() + .optional(), + allDay: booleanSchema.optional(), + _allDay: z.lazy(() => elementSchema).optional(), + availableStartTime: timeSchema.optional(), + _availableStartTime: z.lazy(() => elementSchema).optional(), + availableEndTime: timeSchema.optional(), + _availableEndTime: z.lazy(() => elementSchema).optional(), + }), + ) + +export const practitionerRoleNotAvailableSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema, + _description: z.lazy(() => elementSchema).optional(), + during: z.lazy(() => periodSchema).optional(), + }), + ) + +export const practitionerRoleSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('PractitionerRole'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + practitioner: z.lazy(() => referenceSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + location: z + .lazy(() => referenceSchema) + .array() + .optional(), + healthcareService: z + .lazy(() => referenceSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + availableTime: practitionerRoleAvailableTimeSchema.array().optional(), + notAvailable: practitionerRoleNotAvailableSchema.array().optional(), + availabilityExceptions: stringSchema.optional(), + _availabilityExceptions: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const procedurePerformerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const procedureFocalDeviceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + action: z.lazy(() => codeableConceptSchema).optional(), + manipulated: z.lazy(() => referenceSchema), + }), + ) + +export const procedureSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Procedure'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + performedDateTime: dateTimeSchema.optional(), + _performedDateTime: z.lazy(() => elementSchema).optional(), + performedPeriod: z.lazy(() => periodSchema).optional(), + performedString: stringSchema.optional(), + _performedString: z.lazy(() => elementSchema).optional(), + performedAge: z.lazy(() => ageSchema).optional(), + performedRange: z.lazy(() => rangeSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + asserter: z.lazy(() => referenceSchema).optional(), + performer: procedurePerformerSchema.array().optional(), + location: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + bodySite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + outcome: z.lazy(() => codeableConceptSchema).optional(), + report: z + .lazy(() => referenceSchema) + .array() + .optional(), + complication: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + complicationDetail: z + .lazy(() => referenceSchema) + .array() + .optional(), + followUp: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + focalDevice: procedureFocalDeviceSchema.array().optional(), + usedReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + usedCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'performedDateTime', + 'performedPeriod', + 'performedString', + 'performedAge', + 'performedRange', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const prodCharacteristicSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + height: z.lazy(() => quantitySchema).optional(), + width: z.lazy(() => quantitySchema).optional(), + depth: z.lazy(() => quantitySchema).optional(), + weight: z.lazy(() => quantitySchema).optional(), + nominalVolume: z.lazy(() => quantitySchema).optional(), + externalDiameter: z.lazy(() => quantitySchema).optional(), + shape: stringSchema.optional(), + _shape: z.lazy(() => elementSchema).optional(), + color: stringSchema.array().optional(), + _color: z + .lazy(() => elementSchema) + .array() + .optional(), + imprint: stringSchema.array().optional(), + _imprint: z + .lazy(() => elementSchema) + .array() + .optional(), + image: z + .lazy(() => attachmentSchema) + .array() + .optional(), + scoring: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const productShelfLifeSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + period: z.lazy(() => quantitySchema), + specialPrecautionsForStorage: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), +) + +export const provenanceAgentSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + role: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + who: z.lazy(() => referenceSchema), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + }), +) + +export const provenanceEntitySchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + role: z.enum([ + 'derivation', + 'quotation', + 'removal', + 'revision', + 'source', + ]), + _role: z.lazy(() => elementSchema).optional(), + what: z.lazy(() => referenceSchema), + agent: z + .lazy(() => provenanceAgentSchema) + .array() + .optional(), + }), +) + +export const provenanceSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Provenance'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + target: z + .lazy(() => referenceSchema) + .array() + .min(1), + occurredPeriod: z.lazy(() => periodSchema).optional(), + occurredDateTime: dateTimeSchema.optional(), + _occurredDateTime: z.lazy(() => elementSchema).optional(), + recorded: instantSchema, + _recorded: z.lazy(() => elementSchema).optional(), + policy: uriSchema.array().optional(), + _policy: z + .lazy(() => elementSchema) + .array() + .optional(), + location: z.lazy(() => referenceSchema).optional(), + reason: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + activity: z.lazy(() => codeableConceptSchema).optional(), + agent: provenanceAgentSchema.array().min(1), + entity: provenanceEntitySchema.array().optional(), + signature: z + .lazy(() => signatureSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { names: ['occurredPeriod', 'occurredDateTime'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const quantitySchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }), +) + +export const questionnaireItemSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + linkId: stringSchema, + _linkId: z.lazy(() => elementSchema).optional(), + definition: uriSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codingSchema) + .array() + .optional(), + prefix: stringSchema.optional(), + _prefix: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + type: z.enum([ + 'attachment', + 'boolean', + 'choice', + 'date', + 'dateTime', + 'decimal', + 'display', + 'group', + 'integer', + 'open-choice', + 'quantity', + 'question', + 'reference', + 'string', + 'text', + 'time', + 'url', + ]), + _type: z.lazy(() => elementSchema).optional(), + enableWhen: questionnaireItemEnableWhenSchema.array().optional(), + enableBehavior: z.enum(['all', 'any']).optional(), + _enableBehavior: z.lazy(() => elementSchema).optional(), + required: booleanSchema.optional(), + _required: z.lazy(() => elementSchema).optional(), + repeats: booleanSchema.optional(), + _repeats: z.lazy(() => elementSchema).optional(), + readOnly: booleanSchema.optional(), + _readOnly: z.lazy(() => elementSchema).optional(), + maxLength: integerSchema.optional(), + _maxLength: z.lazy(() => elementSchema).optional(), + answerValueSet: canonicalSchema.optional(), + _answerValueSet: z.lazy(() => elementSchema).optional(), + answerOption: questionnaireItemAnswerOptionSchema.array().optional(), + initial: questionnaireItemInitialSchema.array().optional(), + item: z + .lazy(() => questionnaireItemSchema) + .array() + .optional(), + }), +) + +export const questionnaireItemEnableWhenSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + question: stringSchema, + _question: z.lazy(() => elementSchema).optional(), + operator: z.enum(['!=', '<', '<=', '=', '>', '>=', 'exists']), + _operator: z.lazy(() => elementSchema).optional(), + answerBoolean: booleanSchema.optional(), + _answerBoolean: z.lazy(() => elementSchema).optional(), + answerDecimal: decimalSchema.optional(), + _answerDecimal: z.lazy(() => elementSchema).optional(), + answerInteger: integerSchema.optional(), + _answerInteger: z.lazy(() => elementSchema).optional(), + answerDate: dateSchema.optional(), + _answerDate: z.lazy(() => elementSchema).optional(), + answerDateTime: dateTimeSchema.optional(), + _answerDateTime: z.lazy(() => elementSchema).optional(), + answerTime: timeSchema.optional(), + _answerTime: z.lazy(() => elementSchema).optional(), + answerString: stringSchema.optional(), + _answerString: z.lazy(() => elementSchema).optional(), + answerCoding: z.lazy(() => codingSchema).optional(), + answerQuantity: z.lazy(() => quantitySchema).optional(), + answerReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'answerBoolean', + 'answerDecimal', + 'answerInteger', + 'answerDate', + 'answerDateTime', + 'answerTime', + 'answerString', + 'answerCoding', + 'answerQuantity', + 'answerReference', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const questionnaireItemAnswerOptionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + initialSelected: booleanSchema.optional(), + _initialSelected: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueInteger', + 'valueDate', + 'valueTime', + 'valueString', + 'valueCoding', + 'valueReference', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const questionnaireItemInitialSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBoolean', + 'valueDecimal', + 'valueInteger', + 'valueDate', + 'valueDateTime', + 'valueTime', + 'valueString', + 'valueUri', + 'valueAttachment', + 'valueCoding', + 'valueQuantity', + 'valueReference', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const questionnaireSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Questionnaire'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + derivedFrom: canonicalSchema.array().optional(), + _derivedFrom: z + .lazy(() => elementSchema) + .array() + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectType: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .array() + .optional(), + _subjectType: z + .lazy(() => elementSchema) + .array() + .optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + code: z + .lazy(() => codingSchema) + .array() + .optional(), + item: questionnaireItemSchema.array().optional(), + }), +) + +export const questionnaireResponseItemSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + linkId: stringSchema, + _linkId: z.lazy(() => elementSchema).optional(), + definition: uriSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + answer: questionnaireResponseItemAnswerSchema.array().optional(), + item: z + .lazy(() => questionnaireResponseItemSchema) + .array() + .optional(), + }), + ) + +export const questionnaireResponseItemAnswerSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + item: z + .lazy(() => questionnaireResponseItemSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBoolean', + 'valueDecimal', + 'valueInteger', + 'valueDate', + 'valueDateTime', + 'valueTime', + 'valueString', + 'valueUri', + 'valueAttachment', + 'valueCoding', + 'valueQuantity', + 'valueReference', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const questionnaireResponseSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('QuestionnaireResponse'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + questionnaire: canonicalSchema.optional(), + _questionnaire: z.lazy(() => elementSchema).optional(), + status: z.enum([ + 'amended', + 'completed', + 'entered-in-error', + 'in-progress', + 'stopped', + ]), + _status: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + authored: dateTimeSchema.optional(), + _authored: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + item: questionnaireResponseItemSchema.array().optional(), + }), + ) + +export const rangeSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + low: z.lazy(() => quantitySchema).optional(), + high: z.lazy(() => quantitySchema).optional(), + }), +) + +export const ratioSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + numerator: z.lazy(() => quantitySchema).optional(), + denominator: z.lazy(() => quantitySchema).optional(), + }), +) + +export const ratioRangeSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + lowNumerator: z.lazy(() => quantitySchema).optional(), + highNumerator: z.lazy(() => quantitySchema).optional(), + denominator: z.lazy(() => quantitySchema).optional(), + }), +) + +export const referenceSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + reference: stringSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + type: uriSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + }), +) + +export const regulatedAuthorizationCaseSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + datePeriod: z.lazy(() => periodSchema).optional(), + dateDateTime: dateTimeSchema.optional(), + _dateDateTime: z.lazy(() => elementSchema).optional(), + application: z + .lazy(() => regulatedAuthorizationCaseSchema) + .array() + .optional(), + }) + .refine( + (value) => + [{ names: ['datePeriod', 'dateDateTime'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const regulatedAuthorizationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('RegulatedAuthorization'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + subject: z + .lazy(() => referenceSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + region: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + statusDate: dateTimeSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), + validityPeriod: z.lazy(() => periodSchema).optional(), + indication: z.lazy(() => codeableReferenceSchema).optional(), + intendedUse: z.lazy(() => codeableConceptSchema).optional(), + basis: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + holder: z.lazy(() => referenceSchema).optional(), + regulator: z.lazy(() => referenceSchema).optional(), + case: regulatedAuthorizationCaseSchema.optional(), + }), + ) + +export const relatedArtifactSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'citation', + 'composed-of', + 'depends-on', + 'derived-from', + 'documentation', + 'justification', + 'predecessor', + 'successor', + ]), + _type: z.lazy(() => elementSchema).optional(), + label: stringSchema.optional(), + _label: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + citation: markdownSchema.optional(), + _citation: z.lazy(() => elementSchema).optional(), + url: urlSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + document: z.lazy(() => attachmentSchema).optional(), + resource: canonicalSchema.optional(), + _resource: z.lazy(() => elementSchema).optional(), + }), +) + +export const relatedPersonCommunicationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + language: z.lazy(() => codeableConceptSchema), + preferred: booleanSchema.optional(), + _preferred: z.lazy(() => elementSchema).optional(), + }), + ) + +export const relatedPersonSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('RelatedPerson'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + relationship: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + name: z + .lazy(() => humanNameSchema) + .array() + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + address: z + .lazy(() => addressSchema) + .array() + .optional(), + photo: z + .lazy(() => attachmentSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + communication: relatedPersonCommunicationSchema.array().optional(), + }), +) + +export const requestGroupActionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + prefix: stringSchema.optional(), + _prefix: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + textEquivalent: stringSchema.optional(), + _textEquivalent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + documentation: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + condition: requestGroupActionConditionSchema.array().optional(), + relatedAction: requestGroupActionRelatedActionSchema.array().optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + timingAge: z.lazy(() => ageSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingDuration: z.lazy(() => durationSchema).optional(), + timingRange: z.lazy(() => rangeSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + participant: z + .lazy(() => referenceSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + groupingBehavior: z + .enum(['logical-group', 'sentence-group', 'visual-group']) + .optional(), + _groupingBehavior: z.lazy(() => elementSchema).optional(), + selectionBehavior: z + .enum([ + 'all', + 'all-or-none', + 'any', + 'at-most-one', + 'exactly-one', + 'one-or-more', + ]) + .optional(), + _selectionBehavior: z.lazy(() => elementSchema).optional(), + requiredBehavior: z + .enum(['could', 'must', 'must-unless-documented']) + .optional(), + _requiredBehavior: z.lazy(() => elementSchema).optional(), + precheckBehavior: z.enum(['no', 'yes']).optional(), + _precheckBehavior: z.lazy(() => elementSchema).optional(), + cardinalityBehavior: z.enum(['multiple', 'single']).optional(), + _cardinalityBehavior: z.lazy(() => elementSchema).optional(), + resource: z.lazy(() => referenceSchema).optional(), + action: z + .lazy(() => requestGroupActionSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'timingDateTime', + 'timingAge', + 'timingPeriod', + 'timingDuration', + 'timingRange', + 'timingTiming', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const requestGroupActionConditionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + kind: z.enum(['applicability', 'start', 'stop']), + _kind: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema).optional(), + }), + ) + +export const requestGroupActionRelatedActionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + actionId: idSchema, + _actionId: z.lazy(() => elementSchema).optional(), + relationship: z.enum([ + 'after', + 'after-end', + 'after-start', + 'before', + 'before-end', + 'before-start', + 'concurrent', + 'concurrent-with-end', + 'concurrent-with-start', + ]), + _relationship: z.lazy(() => elementSchema).optional(), + offsetDuration: z.lazy(() => durationSchema).optional(), + offsetRange: z.lazy(() => rangeSchema).optional(), + }) + .refine( + (value) => + [{ names: ['offsetDuration', 'offsetRange'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const requestGroupSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('RequestGroup'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + replaces: z + .lazy(() => referenceSchema) + .array() + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + status: z.enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + intent: z.enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + action: requestGroupActionSchema.array().optional(), + }), +) + +export const researchDefinitionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ResearchDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + shortTitle: stringSchema.optional(), + _shortTitle: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + comment: stringSchema.array().optional(), + _comment: z + .lazy(() => elementSchema) + .array() + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + library: canonicalSchema.array().optional(), + _library: z + .lazy(() => elementSchema) + .array() + .optional(), + population: z.lazy(() => referenceSchema), + exposure: z.lazy(() => referenceSchema).optional(), + exposureAlternative: z.lazy(() => referenceSchema).optional(), + outcome: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['subjectCodeableConcept', 'subjectReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const researchElementDefinitionCharacteristicSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + definitionCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + definitionCanonical: canonicalSchema.optional(), + _definitionCanonical: z.lazy(() => elementSchema).optional(), + definitionExpression: z.lazy(() => expressionSchema).optional(), + definitionDataRequirement: z + .lazy(() => dataRequirementSchema) + .optional(), + usageContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + exclude: booleanSchema.optional(), + _exclude: z.lazy(() => elementSchema).optional(), + unitOfMeasure: z.lazy(() => codeableConceptSchema).optional(), + studyEffectiveDescription: stringSchema.optional(), + _studyEffectiveDescription: z.lazy(() => elementSchema).optional(), + studyEffectiveDateTime: dateTimeSchema.optional(), + _studyEffectiveDateTime: z.lazy(() => elementSchema).optional(), + studyEffectivePeriod: z.lazy(() => periodSchema).optional(), + studyEffectiveDuration: z.lazy(() => durationSchema).optional(), + studyEffectiveTiming: z.lazy(() => timingSchema).optional(), + studyEffectiveTimeFromStart: z.lazy(() => durationSchema).optional(), + studyEffectiveGroupMeasure: z + .enum([ + 'mean', + 'mean-of-mean', + 'mean-of-median', + 'median', + 'median-of-mean', + 'median-of-median', + ]) + .optional(), + _studyEffectiveGroupMeasure: z.lazy(() => elementSchema).optional(), + participantEffectiveDescription: stringSchema.optional(), + _participantEffectiveDescription: z + .lazy(() => elementSchema) + .optional(), + participantEffectiveDateTime: dateTimeSchema.optional(), + _participantEffectiveDateTime: z.lazy(() => elementSchema).optional(), + participantEffectivePeriod: z.lazy(() => periodSchema).optional(), + participantEffectiveDuration: z.lazy(() => durationSchema).optional(), + participantEffectiveTiming: z.lazy(() => timingSchema).optional(), + participantEffectiveTimeFromStart: z + .lazy(() => durationSchema) + .optional(), + participantEffectiveGroupMeasure: z + .enum([ + 'mean', + 'mean-of-mean', + 'mean-of-median', + 'median', + 'median-of-mean', + 'median-of-median', + ]) + .optional(), + _participantEffectiveGroupMeasure: z + .lazy(() => elementSchema) + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'definitionCodeableConcept', + 'definitionCanonical', + 'definitionExpression', + 'definitionDataRequirement', + ], + required: true, + }, + { + names: [ + 'studyEffectiveDateTime', + 'studyEffectivePeriod', + 'studyEffectiveDuration', + 'studyEffectiveTiming', + ], + required: false, + }, + { + names: [ + 'participantEffectiveDateTime', + 'participantEffectivePeriod', + 'participantEffectiveDuration', + 'participantEffectiveTiming', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const researchElementDefinitionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ResearchElementDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + shortTitle: stringSchema.optional(), + _shortTitle: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + comment: stringSchema.array().optional(), + _comment: z + .lazy(() => elementSchema) + .array() + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + library: canonicalSchema.array().optional(), + _library: z + .lazy(() => elementSchema) + .array() + .optional(), + type: z.enum(['exposure', 'outcome', 'population']), + _type: z.lazy(() => elementSchema).optional(), + variableType: z + .enum(['continuous', 'descriptive', 'dichotomous']) + .optional(), + _variableType: z.lazy(() => elementSchema).optional(), + characteristic: researchElementDefinitionCharacteristicSchema + .array() + .min(1), + }) + .refine( + (value) => + [ + { + names: ['subjectCodeableConcept', 'subjectReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const researchStudyArmSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }), +) + +export const researchStudyObjectiveSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + }), + ) + +export const researchStudySchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('ResearchStudy'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + protocol: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'active', + 'administratively-completed', + 'approved', + 'closed-to-accrual', + 'closed-to-accrual-and-intervention', + 'completed', + 'disapproved', + 'in-review', + 'temporarily-closed-to-accrual', + 'temporarily-closed-to-accrual-and-intervention', + 'withdrawn', + ]), + _status: z.lazy(() => elementSchema).optional(), + primaryPurposeType: z.lazy(() => codeableConceptSchema).optional(), + phase: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + focus: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + condition: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .optional(), + keyword: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + location: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + enrollment: z + .lazy(() => referenceSchema) + .array() + .optional(), + period: z.lazy(() => periodSchema).optional(), + sponsor: z.lazy(() => referenceSchema).optional(), + principalInvestigator: z.lazy(() => referenceSchema).optional(), + site: z + .lazy(() => referenceSchema) + .array() + .optional(), + reasonStopped: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + arm: researchStudyArmSchema.array().optional(), + objective: researchStudyObjectiveSchema.array().optional(), + }), +) + +export const researchSubjectSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('ResearchSubject'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum([ + 'candidate', + 'eligible', + 'follow-up', + 'ineligible', + 'not-registered', + 'off-study', + 'on-study', + 'on-study-intervention', + 'on-study-observation', + 'pending-on-study', + 'potential-candidate', + 'screening', + 'withdrawn', + ]), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + study: z.lazy(() => referenceSchema), + individual: z.lazy(() => referenceSchema), + assignedArm: stringSchema.optional(), + _assignedArm: z.lazy(() => elementSchema).optional(), + actualArm: stringSchema.optional(), + _actualArm: z.lazy(() => elementSchema).optional(), + consent: z.lazy(() => referenceSchema).optional(), + }), +) + +export const riskAssessmentPredictionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + outcome: z.lazy(() => codeableConceptSchema).optional(), + probabilityDecimal: decimalSchema.optional(), + _probabilityDecimal: z.lazy(() => elementSchema).optional(), + probabilityRange: z.lazy(() => rangeSchema).optional(), + qualitativeRisk: z.lazy(() => codeableConceptSchema).optional(), + relativeRisk: decimalSchema.optional(), + _relativeRisk: z.lazy(() => elementSchema).optional(), + whenPeriod: z.lazy(() => periodSchema).optional(), + whenRange: z.lazy(() => rangeSchema).optional(), + rationale: stringSchema.optional(), + _rationale: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['probabilityDecimal', 'probabilityRange'], + required: false, + }, + { names: ['whenPeriod', 'whenRange'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const riskAssessmentSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('RiskAssessment'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z.lazy(() => referenceSchema).optional(), + parent: z.lazy(() => referenceSchema).optional(), + status: z.enum([ + 'amended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'preliminary', + 'registered', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + condition: z.lazy(() => referenceSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + basis: z + .lazy(() => referenceSchema) + .array() + .optional(), + prediction: riskAssessmentPredictionSchema.array().optional(), + mitigation: stringSchema.optional(), + _mitigation: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['occurrenceDateTime', 'occurrencePeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const sampledDataSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + origin: z.lazy(() => quantitySchema), + period: decimalSchema, + _period: z.lazy(() => elementSchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + lowerLimit: decimalSchema.optional(), + _lowerLimit: z.lazy(() => elementSchema).optional(), + upperLimit: decimalSchema.optional(), + _upperLimit: z.lazy(() => elementSchema).optional(), + dimensions: positiveIntSchema, + _dimensions: z.lazy(() => elementSchema).optional(), + data: stringSchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + }), +) + +export const scheduleSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Schedule'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + serviceCategory: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + serviceType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + actor: z + .lazy(() => referenceSchema) + .array() + .min(1), + planningHorizon: z.lazy(() => periodSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), +) + +export const searchParameterComponentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + definition: canonicalSchema, + _definition: z.lazy(() => elementSchema).optional(), + expression: stringSchema, + _expression: z.lazy(() => elementSchema).optional(), + }), + ) + +export const searchParameterSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('SearchParameter'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + derivedFrom: canonicalSchema.optional(), + _derivedFrom: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema, + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + base: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .array() + .min(1), + _base: z + .lazy(() => elementSchema) + .array() + .optional(), + type: z.enum([ + 'composite', + 'date', + 'number', + 'quantity', + 'reference', + 'special', + 'string', + 'token', + 'uri', + ]), + _type: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + xpath: stringSchema.optional(), + _xpath: z.lazy(() => elementSchema).optional(), + xpathUsage: z + .enum(['distance', 'nearby', 'normal', 'other', 'phonetic']) + .optional(), + _xpathUsage: z.lazy(() => elementSchema).optional(), + target: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .array() + .optional(), + _target: z + .lazy(() => elementSchema) + .array() + .optional(), + multipleOr: booleanSchema.optional(), + _multipleOr: z.lazy(() => elementSchema).optional(), + multipleAnd: booleanSchema.optional(), + _multipleAnd: z.lazy(() => elementSchema).optional(), + comparator: z + .enum(['ap', 'eb', 'eq', 'ge', 'gt', 'le', 'lt', 'ne', 'sa']) + .array() + .optional(), + _comparator: z + .lazy(() => elementSchema) + .array() + .optional(), + modifier: z + .enum([ + 'above', + 'below', + 'contains', + 'exact', + 'identifier', + 'in', + 'missing', + 'not', + 'not-in', + 'ofType', + 'text', + 'type', + ]) + .array() + .optional(), + _modifier: z + .lazy(() => elementSchema) + .array() + .optional(), + chain: stringSchema.array().optional(), + _chain: z + .lazy(() => elementSchema) + .array() + .optional(), + component: searchParameterComponentSchema.array().optional(), + }), +) + +export const serviceRequestSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ServiceRequest'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.array().optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .array() + .optional(), + instantiatesUri: uriSchema.array().optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + replaces: z + .lazy(() => referenceSchema) + .array() + .optional(), + requisition: z.lazy(() => identifierSchema).optional(), + status: z.enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]), + _status: z.lazy(() => elementSchema).optional(), + intent: z.enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]), + _intent: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + orderDetail: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + quantityQuantity: z.lazy(() => quantitySchema).optional(), + quantityRatio: z.lazy(() => ratioSchema).optional(), + quantityRange: z.lazy(() => rangeSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + asNeededBoolean: booleanSchema.optional(), + _asNeededBoolean: z.lazy(() => elementSchema).optional(), + asNeededCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performerType: z.lazy(() => codeableConceptSchema).optional(), + performer: z + .lazy(() => referenceSchema) + .array() + .optional(), + locationCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + locationReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + insurance: z + .lazy(() => referenceSchema) + .array() + .optional(), + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .optional(), + specimen: z + .lazy(() => referenceSchema) + .array() + .optional(), + bodySite: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + patientInstruction: stringSchema.optional(), + _patientInstruction: z.lazy(() => elementSchema).optional(), + relevantHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: ['quantityQuantity', 'quantityRatio', 'quantityRange'], + required: false, + }, + { + names: [ + 'occurrenceDateTime', + 'occurrencePeriod', + 'occurrenceTiming', + ], + required: false, + }, + { + names: ['asNeededBoolean', 'asNeededCodeableConcept'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const signatureSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z + .lazy(() => codingSchema) + .array() + .min(1), + when: instantSchema, + _when: z.lazy(() => elementSchema).optional(), + who: z.lazy(() => referenceSchema), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + targetFormat: codeSchema.optional(), + _targetFormat: z.lazy(() => elementSchema).optional(), + sigFormat: codeSchema.optional(), + _sigFormat: z.lazy(() => elementSchema).optional(), + data: base64BinarySchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + }), +) + +export const slotSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Slot'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + serviceCategory: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + serviceType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + appointmentType: z.lazy(() => codeableConceptSchema).optional(), + schedule: z.lazy(() => referenceSchema), + status: z.enum([ + 'busy', + 'busy-tentative', + 'busy-unavailable', + 'entered-in-error', + 'free', + ]), + _status: z.lazy(() => elementSchema).optional(), + start: instantSchema, + _start: z.lazy(() => elementSchema).optional(), + end: instantSchema, + _end: z.lazy(() => elementSchema).optional(), + overbooked: booleanSchema.optional(), + _overbooked: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), +) + +export const specimenCollectionSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + collector: z.lazy(() => referenceSchema).optional(), + collectedDateTime: dateTimeSchema.optional(), + _collectedDateTime: z.lazy(() => elementSchema).optional(), + collectedPeriod: z.lazy(() => periodSchema).optional(), + duration: z.lazy(() => durationSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + fastingStatusCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + fastingStatusDuration: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['collectedDateTime', 'collectedPeriod'], + required: false, + }, + { + names: ['fastingStatusCodeableConcept', 'fastingStatusDuration'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const specimenProcessingSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + procedure: z.lazy(() => codeableConceptSchema).optional(), + additive: z + .lazy(() => referenceSchema) + .array() + .optional(), + timeDateTime: dateTimeSchema.optional(), + _timeDateTime: z.lazy(() => elementSchema).optional(), + timePeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [{ names: ['timeDateTime', 'timePeriod'], required: false }].every( + (group) => { + const present = group.names.filter( + (name) => + (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }, + ), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const specimenContainerSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + capacity: z.lazy(() => quantitySchema).optional(), + specimenQuantity: z.lazy(() => quantitySchema).optional(), + additiveCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + additiveReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['additiveCodeableConcept', 'additiveReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const specimenSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Specimen'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + accessionIdentifier: z.lazy(() => identifierSchema).optional(), + status: z + .enum(['available', 'entered-in-error', 'unavailable', 'unsatisfactory']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + receivedTime: dateTimeSchema.optional(), + _receivedTime: z.lazy(() => elementSchema).optional(), + parent: z + .lazy(() => referenceSchema) + .array() + .optional(), + request: z + .lazy(() => referenceSchema) + .array() + .optional(), + collection: specimenCollectionSchema.optional(), + processing: specimenProcessingSchema.array().optional(), + container: specimenContainerSchema.array().optional(), + condition: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), +) + +export const specimenDefinitionTypeTestedSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + isDerived: booleanSchema.optional(), + _isDerived: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + preference: z.enum(['alternate', 'preferred']), + _preference: z.lazy(() => elementSchema).optional(), + container: specimenDefinitionTypeTestedContainerSchema.optional(), + requirement: stringSchema.optional(), + _requirement: z.lazy(() => elementSchema).optional(), + retentionTime: z.lazy(() => durationSchema).optional(), + rejectionCriterion: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + handling: specimenDefinitionTypeTestedHandlingSchema.array().optional(), + }), + ) + +export const specimenDefinitionTypeTestedContainerSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + material: z.lazy(() => codeableConceptSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + cap: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + capacity: z.lazy(() => quantitySchema).optional(), + minimumVolumeQuantity: z.lazy(() => quantitySchema).optional(), + minimumVolumeString: stringSchema.optional(), + _minimumVolumeString: z.lazy(() => elementSchema).optional(), + additive: specimenDefinitionTypeTestedContainerAdditiveSchema + .array() + .optional(), + preparation: stringSchema.optional(), + _preparation: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['minimumVolumeQuantity', 'minimumVolumeString'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const specimenDefinitionTypeTestedHandlingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + temperatureQualifier: z.lazy(() => codeableConceptSchema).optional(), + temperatureRange: z.lazy(() => rangeSchema).optional(), + maxDuration: z.lazy(() => durationSchema).optional(), + instruction: stringSchema.optional(), + _instruction: z.lazy(() => elementSchema).optional(), + }), + ) + +export const specimenDefinitionTypeTestedContainerAdditiveSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + additiveCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + additiveReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['additiveCodeableConcept', 'additiveReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const specimenDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('SpecimenDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + typeCollected: z.lazy(() => codeableConceptSchema).optional(), + patientPreparation: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + timeAspect: stringSchema.optional(), + _timeAspect: z.lazy(() => elementSchema).optional(), + collection: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + typeTested: specimenDefinitionTypeTestedSchema.array().optional(), + }), + ) + +export const structureDefinitionMappingSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identity: idSchema, + _identity: z.lazy(() => elementSchema).optional(), + uri: uriSchema.optional(), + _uri: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }), + ) + +export const structureDefinitionContextSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['element', 'extension', 'fhirpath']), + _type: z.lazy(() => elementSchema).optional(), + expression: stringSchema, + _expression: z.lazy(() => elementSchema).optional(), + }), + ) + +export const structureDefinitionSnapshotSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + element: z + .lazy(() => elementDefinitionSchema) + .array() + .min(1), + }), + ) + +export const structureDefinitionDifferentialSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + element: z + .lazy(() => elementDefinitionSchema) + .array() + .min(1), + }), + ) + +export const structureDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('StructureDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + keyword: z + .lazy(() => codingSchema) + .array() + .optional(), + fhirVersion: z + .enum([ + '0.0.80', + '0.0.81', + '0.0.82', + '0.01', + '0.05', + '0.06', + '0.11', + '0.4.0', + '0.5.0', + '1.0.0', + '1.0.1', + '1.0.2', + '1.1.0', + '1.4.0', + '1.6.0', + '1.8.0', + '3.0.0', + '3.0.1', + '3.0.2', + '3.3.0', + '3.5.0', + '4.0.0', + '4.0.1', + '4.1.0', + '4.3.0', + '4.3.0-cibuild', + '4.3.0-snapshot1', + ]) + .optional(), + _fhirVersion: z.lazy(() => elementSchema).optional(), + mapping: structureDefinitionMappingSchema.array().optional(), + kind: z.enum(['complex-type', 'logical', 'primitive-type', 'resource']), + _kind: z.lazy(() => elementSchema).optional(), + abstract: booleanSchema, + _abstract: z.lazy(() => elementSchema).optional(), + context: structureDefinitionContextSchema.array().optional(), + contextInvariant: stringSchema.array().optional(), + _contextInvariant: z + .lazy(() => elementSchema) + .array() + .optional(), + type: uriSchema, + _type: z.lazy(() => elementSchema).optional(), + baseDefinition: canonicalSchema.optional(), + _baseDefinition: z.lazy(() => elementSchema).optional(), + derivation: z.enum(['constraint', 'specialization']).optional(), + _derivation: z.lazy(() => elementSchema).optional(), + snapshot: structureDefinitionSnapshotSchema.optional(), + differential: structureDefinitionDifferentialSchema.optional(), + }), + ) + +export const structureMapStructureSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: canonicalSchema, + _url: z.lazy(() => elementSchema).optional(), + mode: z.enum(['produced', 'queried', 'source', 'target']), + _mode: z.lazy(() => elementSchema).optional(), + alias: stringSchema.optional(), + _alias: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const structureMapGroupSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: idSchema, + _name: z.lazy(() => elementSchema).optional(), + extends: idSchema.optional(), + _extends: z.lazy(() => elementSchema).optional(), + typeMode: z.enum(['none', 'type-and-types', 'types']), + _typeMode: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + input: structureMapGroupInputSchema.array().min(1), + rule: structureMapGroupRuleSchema.array().min(1), + }), +) + +export const structureMapGroupInputSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: idSchema, + _name: z.lazy(() => elementSchema).optional(), + type: stringSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + mode: z.enum(['source', 'target']), + _mode: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const structureMapGroupRuleSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: idSchema, + _name: z.lazy(() => elementSchema).optional(), + source: structureMapGroupRuleSourceSchema.array().min(1), + target: structureMapGroupRuleTargetSchema.array().optional(), + rule: z + .lazy(() => structureMapGroupRuleSchema) + .array() + .optional(), + dependent: structureMapGroupRuleDependentSchema.array().optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const structureMapGroupRuleSourceSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + context: idSchema, + _context: z.lazy(() => elementSchema).optional(), + min: integerSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + type: stringSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + defaultValueBase64Binary: base64BinarySchema.optional(), + _defaultValueBase64Binary: z.lazy(() => elementSchema).optional(), + defaultValueBoolean: booleanSchema.optional(), + _defaultValueBoolean: z.lazy(() => elementSchema).optional(), + defaultValueCanonical: canonicalSchema.optional(), + _defaultValueCanonical: z.lazy(() => elementSchema).optional(), + defaultValueCode: codeSchema.optional(), + _defaultValueCode: z.lazy(() => elementSchema).optional(), + defaultValueDate: dateSchema.optional(), + _defaultValueDate: z.lazy(() => elementSchema).optional(), + defaultValueDateTime: dateTimeSchema.optional(), + _defaultValueDateTime: z.lazy(() => elementSchema).optional(), + defaultValueDecimal: decimalSchema.optional(), + _defaultValueDecimal: z.lazy(() => elementSchema).optional(), + defaultValueId: idSchema.optional(), + _defaultValueId: z.lazy(() => elementSchema).optional(), + defaultValueInstant: instantSchema.optional(), + _defaultValueInstant: z.lazy(() => elementSchema).optional(), + defaultValueInteger: integerSchema.optional(), + _defaultValueInteger: z.lazy(() => elementSchema).optional(), + defaultValueMarkdown: markdownSchema.optional(), + _defaultValueMarkdown: z.lazy(() => elementSchema).optional(), + defaultValueOid: oidSchema.optional(), + _defaultValueOid: z.lazy(() => elementSchema).optional(), + defaultValuePositiveInt: positiveIntSchema.optional(), + _defaultValuePositiveInt: z.lazy(() => elementSchema).optional(), + defaultValueString: stringSchema.optional(), + _defaultValueString: z.lazy(() => elementSchema).optional(), + defaultValueTime: timeSchema.optional(), + _defaultValueTime: z.lazy(() => elementSchema).optional(), + defaultValueUnsignedInt: unsignedIntSchema.optional(), + _defaultValueUnsignedInt: z.lazy(() => elementSchema).optional(), + defaultValueUri: uriSchema.optional(), + _defaultValueUri: z.lazy(() => elementSchema).optional(), + defaultValueUrl: urlSchema.optional(), + _defaultValueUrl: z.lazy(() => elementSchema).optional(), + defaultValueUuid: uuidSchema.optional(), + _defaultValueUuid: z.lazy(() => elementSchema).optional(), + defaultValueAddress: z.lazy(() => addressSchema).optional(), + defaultValueAge: z.lazy(() => ageSchema).optional(), + defaultValueAnnotation: z.lazy(() => annotationSchema).optional(), + defaultValueAttachment: z.lazy(() => attachmentSchema).optional(), + defaultValueCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + defaultValueCoding: z.lazy(() => codingSchema).optional(), + defaultValueContactPoint: z.lazy(() => contactPointSchema).optional(), + defaultValueCount: z.lazy(() => countSchema).optional(), + defaultValueDistance: z.lazy(() => distanceSchema).optional(), + defaultValueDuration: z.lazy(() => durationSchema).optional(), + defaultValueHumanName: z.lazy(() => humanNameSchema).optional(), + defaultValueIdentifier: z.lazy(() => identifierSchema).optional(), + defaultValueMoney: z.lazy(() => moneySchema).optional(), + defaultValuePeriod: z.lazy(() => periodSchema).optional(), + defaultValueQuantity: z.lazy(() => quantitySchema).optional(), + defaultValueRange: z.lazy(() => rangeSchema).optional(), + defaultValueRatio: z.lazy(() => ratioSchema).optional(), + defaultValueReference: z.lazy(() => referenceSchema).optional(), + defaultValueSampledData: z.lazy(() => sampledDataSchema).optional(), + defaultValueSignature: z.lazy(() => signatureSchema).optional(), + defaultValueTiming: z.lazy(() => timingSchema).optional(), + defaultValueContactDetail: z.lazy(() => contactDetailSchema).optional(), + defaultValueContributor: z.lazy(() => contributorSchema).optional(), + defaultValueDataRequirement: z + .lazy(() => dataRequirementSchema) + .optional(), + defaultValueExpression: z.lazy(() => expressionSchema).optional(), + defaultValueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + defaultValueRelatedArtifact: z + .lazy(() => relatedArtifactSchema) + .optional(), + defaultValueTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + defaultValueUsageContext: z.lazy(() => usageContextSchema).optional(), + defaultValueDosage: z.lazy(() => dosageSchema).optional(), + defaultValueMeta: z.lazy(() => metaSchema).optional(), + element: stringSchema.optional(), + _element: z.lazy(() => elementSchema).optional(), + listMode: z + .enum(['first', 'last', 'not_first', 'not_last', 'only_one']) + .optional(), + _listMode: z.lazy(() => elementSchema).optional(), + variable: idSchema.optional(), + _variable: z.lazy(() => elementSchema).optional(), + condition: stringSchema.optional(), + _condition: z.lazy(() => elementSchema).optional(), + check: stringSchema.optional(), + _check: z.lazy(() => elementSchema).optional(), + logMessage: stringSchema.optional(), + _logMessage: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'defaultValueBase64Binary', + 'defaultValueBoolean', + 'defaultValueCanonical', + 'defaultValueCode', + 'defaultValueDate', + 'defaultValueDateTime', + 'defaultValueDecimal', + 'defaultValueId', + 'defaultValueInstant', + 'defaultValueInteger', + 'defaultValueMarkdown', + 'defaultValueOid', + 'defaultValuePositiveInt', + 'defaultValueString', + 'defaultValueTime', + 'defaultValueUnsignedInt', + 'defaultValueUri', + 'defaultValueUrl', + 'defaultValueUuid', + 'defaultValueAddress', + 'defaultValueAge', + 'defaultValueAnnotation', + 'defaultValueAttachment', + 'defaultValueCodeableConcept', + 'defaultValueCoding', + 'defaultValueContactPoint', + 'defaultValueCount', + 'defaultValueDistance', + 'defaultValueDuration', + 'defaultValueHumanName', + 'defaultValueIdentifier', + 'defaultValueMoney', + 'defaultValuePeriod', + 'defaultValueQuantity', + 'defaultValueRange', + 'defaultValueRatio', + 'defaultValueReference', + 'defaultValueSampledData', + 'defaultValueSignature', + 'defaultValueTiming', + 'defaultValueContactDetail', + 'defaultValueContributor', + 'defaultValueDataRequirement', + 'defaultValueExpression', + 'defaultValueParameterDefinition', + 'defaultValueRelatedArtifact', + 'defaultValueTriggerDefinition', + 'defaultValueUsageContext', + 'defaultValueDosage', + 'defaultValueMeta', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const structureMapGroupRuleTargetSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + context: idSchema.optional(), + _context: z.lazy(() => elementSchema).optional(), + contextType: z.enum(['type', 'variable']).optional(), + _contextType: z.lazy(() => elementSchema).optional(), + element: stringSchema.optional(), + _element: z.lazy(() => elementSchema).optional(), + variable: idSchema.optional(), + _variable: z.lazy(() => elementSchema).optional(), + listMode: z + .enum(['collate', 'first', 'last', 'share']) + .array() + .optional(), + _listMode: z + .lazy(() => elementSchema) + .array() + .optional(), + listRuleId: idSchema.optional(), + _listRuleId: z.lazy(() => elementSchema).optional(), + transform: z + .enum([ + 'append', + 'c', + 'cast', + 'cc', + 'copy', + 'cp', + 'create', + 'dateOp', + 'escape', + 'evaluate', + 'id', + 'pointer', + 'qty', + 'reference', + 'translate', + 'truncate', + 'uuid', + ]) + .optional(), + _transform: z.lazy(() => elementSchema).optional(), + parameter: structureMapGroupRuleTargetParameterSchema.array().optional(), + }), + ) + +export const structureMapGroupRuleDependentSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: idSchema, + _name: z.lazy(() => elementSchema).optional(), + variable: stringSchema.array().min(1), + _variable: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const structureMapGroupRuleTargetParameterSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueId', + 'valueString', + 'valueBoolean', + 'valueInteger', + 'valueDecimal', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const structureMapSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('StructureMap'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + structure: structureMapStructureSchema.array().optional(), + import: canonicalSchema.array().optional(), + _import: z + .lazy(() => elementSchema) + .array() + .optional(), + group: structureMapGroupSchema.array().min(1), + }), +) + +export const subscriptionChannelSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['email', 'message', 'rest-hook', 'sms', 'websocket']), + _type: z.lazy(() => elementSchema).optional(), + endpoint: urlSchema.optional(), + _endpoint: z.lazy(() => elementSchema).optional(), + payload: codeSchema.optional(), + _payload: z.lazy(() => elementSchema).optional(), + header: stringSchema.array().optional(), + _header: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const subscriptionSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Subscription'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + status: z.enum(['active', 'error', 'off', 'requested']), + _status: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactPointSchema) + .array() + .optional(), + end: instantSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + reason: stringSchema, + _reason: z.lazy(() => elementSchema).optional(), + criteria: stringSchema, + _criteria: z.lazy(() => elementSchema).optional(), + error: stringSchema.optional(), + _error: z.lazy(() => elementSchema).optional(), + channel: subscriptionChannelSchema, + }), +) + +export const subscriptionStatusNotificationEventSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + eventNumber: stringSchema, + _eventNumber: z.lazy(() => elementSchema).optional(), + timestamp: instantSchema.optional(), + _timestamp: z.lazy(() => elementSchema).optional(), + focus: z.lazy(() => referenceSchema).optional(), + additionalContext: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const subscriptionStatusSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('SubscriptionStatus'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + status: z.enum(['active', 'error', 'off', 'requested']).optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z.enum([ + 'event-notification', + 'handshake', + 'heartbeat', + 'query-event', + 'query-status', + ]), + _type: z.lazy(() => elementSchema).optional(), + eventsSinceSubscriptionStart: stringSchema.optional(), + _eventsSinceSubscriptionStart: z.lazy(() => elementSchema).optional(), + notificationEvent: subscriptionStatusNotificationEventSchema + .array() + .optional(), + subscription: z.lazy(() => referenceSchema), + topic: canonicalSchema.optional(), + _topic: z.lazy(() => elementSchema).optional(), + error: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), + ) + +export const subscriptionTopicResourceTriggerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + resource: uriSchema, + _resource: z.lazy(() => elementSchema).optional(), + supportedInteraction: z + .enum(['create', 'delete', 'update']) + .array() + .optional(), + _supportedInteraction: z + .lazy(() => elementSchema) + .array() + .optional(), + queryCriteria: + subscriptionTopicResourceTriggerQueryCriteriaSchema.optional(), + fhirPathCriteria: stringSchema.optional(), + _fhirPathCriteria: z.lazy(() => elementSchema).optional(), + }), + ) + +export const subscriptionTopicEventTriggerSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + event: z.lazy(() => codeableConceptSchema), + resource: uriSchema, + _resource: z.lazy(() => elementSchema).optional(), + }), + ) + +export const subscriptionTopicCanFilterBySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + resource: uriSchema.optional(), + _resource: z.lazy(() => elementSchema).optional(), + filterParameter: stringSchema, + _filterParameter: z.lazy(() => elementSchema).optional(), + filterDefinition: uriSchema.optional(), + _filterDefinition: z.lazy(() => elementSchema).optional(), + modifier: z + .enum([ + '=', + 'above', + 'ap', + 'below', + 'eb', + 'eq', + 'ge', + 'gt', + 'in', + 'le', + 'lt', + 'ne', + 'not-in', + 'of-type', + 'sa', + ]) + .array() + .optional(), + _modifier: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const subscriptionTopicNotificationShapeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + resource: uriSchema, + _resource: z.lazy(() => elementSchema).optional(), + include: stringSchema.array().optional(), + _include: z + .lazy(() => elementSchema) + .array() + .optional(), + revInclude: stringSchema.array().optional(), + _revInclude: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const subscriptionTopicResourceTriggerQueryCriteriaSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + previous: stringSchema.optional(), + _previous: z.lazy(() => elementSchema).optional(), + resultForCreate: z.enum(['test-fails', 'test-passes']).optional(), + _resultForCreate: z.lazy(() => elementSchema).optional(), + current: stringSchema.optional(), + _current: z.lazy(() => elementSchema).optional(), + resultForDelete: z.enum(['test-fails', 'test-passes']).optional(), + _resultForDelete: z.lazy(() => elementSchema).optional(), + requireBoth: booleanSchema.optional(), + _requireBoth: z.lazy(() => elementSchema).optional(), + }), + ) + +export const subscriptionTopicSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + resourceType: z.literal('SubscriptionTopic'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + derivedFrom: canonicalSchema.array().optional(), + _derivedFrom: z + .lazy(() => elementSchema) + .array() + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + resourceTrigger: subscriptionTopicResourceTriggerSchema + .array() + .optional(), + eventTrigger: subscriptionTopicEventTriggerSchema.array().optional(), + canFilterBy: subscriptionTopicCanFilterBySchema.array().optional(), + notificationShape: subscriptionTopicNotificationShapeSchema + .array() + .optional(), + }), +) + +export const substanceInstanceSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + expiry: dateTimeSchema.optional(), + _expiry: z.lazy(() => elementSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + }), +) + +export const substanceIngredientSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + quantity: z.lazy(() => ratioSchema).optional(), + substanceCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + substanceReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['substanceCodeableConcept', 'substanceReference'], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const substanceSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Substance'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + instance: substanceInstanceSchema.array().optional(), + ingredient: substanceIngredientSchema.array().optional(), + }), +) + +export const substanceDefinitionMoietySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + stereochemistry: z.lazy(() => codeableConceptSchema).optional(), + opticalActivity: z.lazy(() => codeableConceptSchema).optional(), + molecularFormula: stringSchema.optional(), + _molecularFormula: z.lazy(() => elementSchema).optional(), + amountQuantity: z.lazy(() => quantitySchema).optional(), + amountString: stringSchema.optional(), + _amountString: z.lazy(() => elementSchema).optional(), + measurementType: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['amountQuantity', 'amountString'], required: false }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const substanceDefinitionPropertySchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueQuantity', + 'valueDate', + 'valueBoolean', + 'valueAttachment', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const substanceDefinitionMolecularWeightSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => quantitySchema), + }), + ) + +export const substanceDefinitionStructureSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + stereochemistry: z.lazy(() => codeableConceptSchema).optional(), + opticalActivity: z.lazy(() => codeableConceptSchema).optional(), + molecularFormula: stringSchema.optional(), + _molecularFormula: z.lazy(() => elementSchema).optional(), + molecularFormulaByMoiety: stringSchema.optional(), + _molecularFormulaByMoiety: z.lazy(() => elementSchema).optional(), + molecularWeight: z + .lazy(() => substanceDefinitionMolecularWeightSchema) + .optional(), + technique: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + sourceDocument: z + .lazy(() => referenceSchema) + .array() + .optional(), + representation: substanceDefinitionStructureRepresentationSchema + .array() + .optional(), + }), + ) + +export const substanceDefinitionCodeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + statusDate: dateTimeSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + source: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const substanceDefinitionNameSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + preferred: booleanSchema.optional(), + _preferred: z.lazy(() => elementSchema).optional(), + language: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + domain: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + synonym: z + .lazy(() => substanceDefinitionNameSchema) + .array() + .optional(), + translation: z + .lazy(() => substanceDefinitionNameSchema) + .array() + .optional(), + official: substanceDefinitionNameOfficialSchema.array().optional(), + source: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), + ) + +export const substanceDefinitionRelationshipSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + substanceDefinitionReference: z.lazy(() => referenceSchema).optional(), + substanceDefinitionCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + type: z.lazy(() => codeableConceptSchema), + isDefining: booleanSchema.optional(), + _isDefining: z.lazy(() => elementSchema).optional(), + amountQuantity: z.lazy(() => quantitySchema).optional(), + amountRatio: z.lazy(() => ratioSchema).optional(), + amountString: stringSchema.optional(), + _amountString: z.lazy(() => elementSchema).optional(), + ratioHighLimitAmount: z.lazy(() => ratioSchema).optional(), + comparator: z.lazy(() => codeableConceptSchema).optional(), + source: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'substanceDefinitionReference', + 'substanceDefinitionCodeableConcept', + ], + required: false, + }, + { + names: ['amountQuantity', 'amountRatio', 'amountString'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const substanceDefinitionSourceMaterialSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + genus: z.lazy(() => codeableConceptSchema).optional(), + species: z.lazy(() => codeableConceptSchema).optional(), + part: z.lazy(() => codeableConceptSchema).optional(), + countryOfOrigin: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), + ) + +export const substanceDefinitionStructureRepresentationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + representation: stringSchema.optional(), + _representation: z.lazy(() => elementSchema).optional(), + format: z.lazy(() => codeableConceptSchema).optional(), + document: z.lazy(() => referenceSchema).optional(), + }), + ) + +export const substanceDefinitionNameOfficialSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + authority: z.lazy(() => codeableConceptSchema).optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + }), + ) + +export const substanceDefinitionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('SubstanceDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + classification: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + domain: z.lazy(() => codeableConceptSchema).optional(), + grade: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + informationSource: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + manufacturer: z + .lazy(() => referenceSchema) + .array() + .optional(), + supplier: z + .lazy(() => referenceSchema) + .array() + .optional(), + moiety: substanceDefinitionMoietySchema.array().optional(), + property: substanceDefinitionPropertySchema.array().optional(), + molecularWeight: substanceDefinitionMolecularWeightSchema + .array() + .optional(), + structure: substanceDefinitionStructureSchema.optional(), + code: substanceDefinitionCodeSchema.array().optional(), + name: substanceDefinitionNameSchema.array().optional(), + relationship: substanceDefinitionRelationshipSchema.array().optional(), + sourceMaterial: substanceDefinitionSourceMaterialSchema.optional(), + }), + ) + +export const supplyDeliverySuppliedItemSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['itemCodeableConcept', 'itemReference'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const supplyDeliverySchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('SupplyDelivery'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z + .enum(['abandoned', 'completed', 'entered-in-error', 'in-progress']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + suppliedItem: supplyDeliverySuppliedItemSchema.optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + supplier: z.lazy(() => referenceSchema).optional(), + destination: z.lazy(() => referenceSchema).optional(), + receiver: z + .lazy(() => referenceSchema) + .array() + .optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'occurrenceDateTime', + 'occurrencePeriod', + 'occurrenceTiming', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const supplyRequestParameterSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueQuantity', + 'valueRange', + 'valueBoolean', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const supplyRequestSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('SupplyRequest'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z + .enum([ + 'active', + 'cancelled', + 'completed', + 'draft', + 'entered-in-error', + 'suspended', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema), + parameter: supplyRequestParameterSchema.array().optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + supplier: z + .lazy(() => referenceSchema) + .array() + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .optional(), + deliverFrom: z.lazy(() => referenceSchema).optional(), + deliverTo: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { names: ['itemCodeableConcept', 'itemReference'], required: true }, + { + names: [ + 'occurrenceDateTime', + 'occurrencePeriod', + 'occurrenceTiming', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const taskRestrictionSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + repetitions: positiveIntSchema.optional(), + _repetitions: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + recipient: z + .lazy(() => referenceSchema) + .array() + .optional(), + }), +) + +export const taskInputSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + valueMeta: z.lazy(() => metaSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBase64Binary', + 'valueBoolean', + 'valueCanonical', + 'valueCode', + 'valueDate', + 'valueDateTime', + 'valueDecimal', + 'valueId', + 'valueInstant', + 'valueInteger', + 'valueMarkdown', + 'valueOid', + 'valuePositiveInt', + 'valueString', + 'valueTime', + 'valueUnsignedInt', + 'valueUri', + 'valueUrl', + 'valueUuid', + 'valueAddress', + 'valueAge', + 'valueAnnotation', + 'valueAttachment', + 'valueCodeableConcept', + 'valueCoding', + 'valueContactPoint', + 'valueCount', + 'valueDistance', + 'valueDuration', + 'valueHumanName', + 'valueIdentifier', + 'valueMoney', + 'valuePeriod', + 'valueQuantity', + 'valueRange', + 'valueRatio', + 'valueReference', + 'valueSampledData', + 'valueSignature', + 'valueTiming', + 'valueContactDetail', + 'valueContributor', + 'valueDataRequirement', + 'valueExpression', + 'valueParameterDefinition', + 'valueRelatedArtifact', + 'valueTriggerDefinition', + 'valueUsageContext', + 'valueDosage', + 'valueMeta', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const taskOutputSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + valueMeta: z.lazy(() => metaSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueBase64Binary', + 'valueBoolean', + 'valueCanonical', + 'valueCode', + 'valueDate', + 'valueDateTime', + 'valueDecimal', + 'valueId', + 'valueInstant', + 'valueInteger', + 'valueMarkdown', + 'valueOid', + 'valuePositiveInt', + 'valueString', + 'valueTime', + 'valueUnsignedInt', + 'valueUri', + 'valueUrl', + 'valueUuid', + 'valueAddress', + 'valueAge', + 'valueAnnotation', + 'valueAttachment', + 'valueCodeableConcept', + 'valueCoding', + 'valueContactPoint', + 'valueCount', + 'valueDistance', + 'valueDuration', + 'valueHumanName', + 'valueIdentifier', + 'valueMoney', + 'valuePeriod', + 'valueQuantity', + 'valueRange', + 'valueRatio', + 'valueReference', + 'valueSampledData', + 'valueSignature', + 'valueTiming', + 'valueContactDetail', + 'valueContributor', + 'valueDataRequirement', + 'valueExpression', + 'valueParameterDefinition', + 'valueRelatedArtifact', + 'valueTriggerDefinition', + 'valueUsageContext', + 'valueDosage', + 'valueMeta', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const taskSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('Task'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + instantiatesCanonical: canonicalSchema.optional(), + _instantiatesCanonical: z.lazy(() => elementSchema).optional(), + instantiatesUri: uriSchema.optional(), + _instantiatesUri: z.lazy(() => elementSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .optional(), + status: z.enum([ + 'accepted', + 'cancelled', + 'completed', + 'draft', + 'entered-in-error', + 'failed', + 'in-progress', + 'on-hold', + 'ready', + 'received', + 'rejected', + 'requested', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + businessStatus: z.lazy(() => codeableConceptSchema).optional(), + intent: z.enum([ + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + 'unknown', + ]), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + focus: z.lazy(() => referenceSchema).optional(), + for: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + executionPeriod: z.lazy(() => periodSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + lastModified: dateTimeSchema.optional(), + _lastModified: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performerType: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + owner: z.lazy(() => referenceSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + reasonCode: z.lazy(() => codeableConceptSchema).optional(), + reasonReference: z.lazy(() => referenceSchema).optional(), + insurance: z + .lazy(() => referenceSchema) + .array() + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + relevantHistory: z + .lazy(() => referenceSchema) + .array() + .optional(), + restriction: taskRestrictionSchema.optional(), + input: taskInputSchema.array().optional(), + output: taskOutputSchema.array().optional(), + }), +) + +export const terminologyCapabilitiesSoftwareSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesImplementationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + description: stringSchema, + _description: z.lazy(() => elementSchema).optional(), + url: urlSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesCodeSystemSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + uri: canonicalSchema.optional(), + _uri: z.lazy(() => elementSchema).optional(), + version: terminologyCapabilitiesCodeSystemVersionSchema + .array() + .optional(), + subsumption: booleanSchema.optional(), + _subsumption: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesExpansionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + hierarchical: booleanSchema.optional(), + _hierarchical: z.lazy(() => elementSchema).optional(), + paging: booleanSchema.optional(), + _paging: z.lazy(() => elementSchema).optional(), + incomplete: booleanSchema.optional(), + _incomplete: z.lazy(() => elementSchema).optional(), + parameter: terminologyCapabilitiesExpansionParameterSchema + .array() + .optional(), + textFilter: markdownSchema.optional(), + _textFilter: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesValidateCodeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + translations: booleanSchema, + _translations: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesTranslationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + needsMap: booleanSchema, + _needsMap: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesClosureSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + translation: booleanSchema.optional(), + _translation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesCodeSystemVersionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: stringSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + isDefault: booleanSchema.optional(), + _isDefault: z.lazy(() => elementSchema).optional(), + compositional: booleanSchema.optional(), + _compositional: z.lazy(() => elementSchema).optional(), + language: codeSchema.array().optional(), + _language: z + .lazy(() => elementSchema) + .array() + .optional(), + filter: terminologyCapabilitiesCodeSystemVersionFilterSchema + .array() + .optional(), + property: codeSchema.array().optional(), + _property: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const terminologyCapabilitiesExpansionParameterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: codeSchema, + _name: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }), + ) + +export const terminologyCapabilitiesCodeSystemVersionFilterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + op: codeSchema.array().min(1), + _op: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const terminologyCapabilitiesSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('TerminologyCapabilities'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema, + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + kind: z.enum(['capability', 'instance', 'requirements']), + _kind: z.lazy(() => elementSchema).optional(), + software: terminologyCapabilitiesSoftwareSchema.optional(), + implementation: terminologyCapabilitiesImplementationSchema.optional(), + lockedDate: booleanSchema.optional(), + _lockedDate: z.lazy(() => elementSchema).optional(), + codeSystem: terminologyCapabilitiesCodeSystemSchema.array().optional(), + expansion: terminologyCapabilitiesExpansionSchema.optional(), + codeSearch: z.enum(['all', 'explicit']).optional(), + _codeSearch: z.lazy(() => elementSchema).optional(), + validateCode: terminologyCapabilitiesValidateCodeSchema.optional(), + translation: terminologyCapabilitiesTranslationSchema.optional(), + closure: terminologyCapabilitiesClosureSchema.optional(), + }), + ) + +export const testReportParticipantSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum(['client', 'server', 'test-engine']), + _type: z.lazy(() => elementSchema).optional(), + uri: uriSchema, + _uri: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testReportSetupSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + action: testReportSetupActionSchema.array().min(1), + }), +) + +export const testReportTestSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + action: testReportTestActionSchema.array().min(1), + }), +) + +export const testReportTeardownSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + action: testReportTeardownActionSchema.array().min(1), + }), + ) + +export const testReportSetupActionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + operation: testReportSetupActionOperationSchema.optional(), + assert: testReportSetupActionAssertSchema.optional(), + }), + ) + +export const testReportTestActionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + operation: z.lazy(() => testReportSetupActionOperationSchema).optional(), + assert: z.lazy(() => testReportSetupActionAssertSchema).optional(), + }), + ) + +export const testReportTeardownActionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + operation: z.lazy(() => testReportSetupActionOperationSchema), + }), + ) + +export const testReportSetupActionOperationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + result: z.enum(['error', 'fail', 'pass', 'skip', 'warning']), + _result: z.lazy(() => elementSchema).optional(), + message: markdownSchema.optional(), + _message: z.lazy(() => elementSchema).optional(), + detail: uriSchema.optional(), + _detail: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testReportSetupActionAssertSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + result: z.enum(['error', 'fail', 'pass', 'skip', 'warning']), + _result: z.lazy(() => elementSchema).optional(), + message: markdownSchema.optional(), + _message: z.lazy(() => elementSchema).optional(), + detail: stringSchema.optional(), + _detail: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testReportSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('TestReport'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + status: z.enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'stopped', + 'waiting', + ]), + _status: z.lazy(() => elementSchema).optional(), + testScript: z.lazy(() => referenceSchema), + result: z.enum(['fail', 'pass', 'pending']), + _result: z.lazy(() => elementSchema).optional(), + score: decimalSchema.optional(), + _score: z.lazy(() => elementSchema).optional(), + tester: stringSchema.optional(), + _tester: z.lazy(() => elementSchema).optional(), + issued: dateTimeSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + participant: testReportParticipantSchema.array().optional(), + setup: testReportSetupSchema.optional(), + test: testReportTestSchema.array().optional(), + teardown: testReportTeardownSchema.optional(), + }), +) + +export const testScriptOriginSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + index: integerSchema, + _index: z.lazy(() => elementSchema).optional(), + profile: z.lazy(() => codingSchema), + }), +) + +export const testScriptDestinationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + index: integerSchema, + _index: z.lazy(() => elementSchema).optional(), + profile: z.lazy(() => codingSchema), + }), + ) + +export const testScriptMetadataSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + link: testScriptMetadataLinkSchema.array().optional(), + capability: testScriptMetadataCapabilitySchema.array().min(1), + }), + ) + +export const testScriptFixtureSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + autocreate: booleanSchema, + _autocreate: z.lazy(() => elementSchema).optional(), + autodelete: booleanSchema, + _autodelete: z.lazy(() => elementSchema).optional(), + resource: z.lazy(() => referenceSchema).optional(), + }), +) + +export const testScriptVariableSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + defaultValue: stringSchema.optional(), + _defaultValue: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + headerField: stringSchema.optional(), + _headerField: z.lazy(() => elementSchema).optional(), + hint: stringSchema.optional(), + _hint: z.lazy(() => elementSchema).optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + sourceId: idSchema.optional(), + _sourceId: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testScriptSetupSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + action: testScriptSetupActionSchema.array().min(1), + }), +) + +export const testScriptTestSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + action: testScriptTestActionSchema.array().min(1), + }), +) + +export const testScriptTeardownSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + action: testScriptTeardownActionSchema.array().min(1), + }), + ) + +export const testScriptMetadataLinkSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testScriptMetadataCapabilitySchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + required: booleanSchema, + _required: z.lazy(() => elementSchema).optional(), + validated: booleanSchema, + _validated: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + origin: integerSchema.array().optional(), + _origin: z + .lazy(() => elementSchema) + .array() + .optional(), + destination: integerSchema.optional(), + _destination: z.lazy(() => elementSchema).optional(), + link: uriSchema.array().optional(), + _link: z + .lazy(() => elementSchema) + .array() + .optional(), + capabilities: canonicalSchema, + _capabilities: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testScriptSetupActionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + operation: testScriptSetupActionOperationSchema.optional(), + assert: testScriptSetupActionAssertSchema.optional(), + }), + ) + +export const testScriptTestActionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + operation: z.lazy(() => testScriptSetupActionOperationSchema).optional(), + assert: z.lazy(() => testScriptSetupActionAssertSchema).optional(), + }), + ) + +export const testScriptTeardownActionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + operation: z.lazy(() => testScriptSetupActionOperationSchema), + }), + ) + +export const testScriptSetupActionOperationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.lazy(() => codingSchema).optional(), + resource: z + .enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'CodeableConcept', + 'CodeableReference', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'RatioRange', + 'Reference', + 'RegulatedAuthorization', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]) + .optional(), + _resource: z.lazy(() => elementSchema).optional(), + label: stringSchema.optional(), + _label: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + accept: codeSchema.optional(), + _accept: z.lazy(() => elementSchema).optional(), + contentType: codeSchema.optional(), + _contentType: z.lazy(() => elementSchema).optional(), + destination: integerSchema.optional(), + _destination: z.lazy(() => elementSchema).optional(), + encodeRequestUrl: booleanSchema, + _encodeRequestUrl: z.lazy(() => elementSchema).optional(), + method: z + .enum(['delete', 'get', 'head', 'options', 'patch', 'post', 'put']) + .optional(), + _method: z.lazy(() => elementSchema).optional(), + origin: integerSchema.optional(), + _origin: z.lazy(() => elementSchema).optional(), + params: stringSchema.optional(), + _params: z.lazy(() => elementSchema).optional(), + requestHeader: testScriptSetupActionOperationRequestHeaderSchema + .array() + .optional(), + requestId: idSchema.optional(), + _requestId: z.lazy(() => elementSchema).optional(), + responseId: idSchema.optional(), + _responseId: z.lazy(() => elementSchema).optional(), + sourceId: idSchema.optional(), + _sourceId: z.lazy(() => elementSchema).optional(), + targetId: idSchema.optional(), + _targetId: z.lazy(() => elementSchema).optional(), + url: stringSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testScriptSetupActionAssertSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + label: stringSchema.optional(), + _label: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + direction: z.enum(['request', 'response']).optional(), + _direction: z.lazy(() => elementSchema).optional(), + compareToSourceId: stringSchema.optional(), + _compareToSourceId: z.lazy(() => elementSchema).optional(), + compareToSourceExpression: stringSchema.optional(), + _compareToSourceExpression: z.lazy(() => elementSchema).optional(), + compareToSourcePath: stringSchema.optional(), + _compareToSourcePath: z.lazy(() => elementSchema).optional(), + contentType: codeSchema.optional(), + _contentType: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + headerField: stringSchema.optional(), + _headerField: z.lazy(() => elementSchema).optional(), + minimumId: stringSchema.optional(), + _minimumId: z.lazy(() => elementSchema).optional(), + navigationLinks: booleanSchema.optional(), + _navigationLinks: z.lazy(() => elementSchema).optional(), + operator: z + .enum([ + 'contains', + 'empty', + 'equals', + 'eval', + 'greaterThan', + 'in', + 'lessThan', + 'notContains', + 'notEmpty', + 'notEquals', + 'notIn', + ]) + .optional(), + _operator: z.lazy(() => elementSchema).optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + requestMethod: z + .enum(['delete', 'get', 'head', 'options', 'patch', 'post', 'put']) + .optional(), + _requestMethod: z.lazy(() => elementSchema).optional(), + requestURL: stringSchema.optional(), + _requestURL: z.lazy(() => elementSchema).optional(), + resource: z + .enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'CodeableConcept', + 'CodeableReference', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'RatioRange', + 'Reference', + 'RegulatedAuthorization', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]) + .optional(), + _resource: z.lazy(() => elementSchema).optional(), + response: z + .enum([ + 'bad', + 'conflict', + 'created', + 'forbidden', + 'gone', + 'methodNotAllowed', + 'noContent', + 'notFound', + 'notModified', + 'okay', + 'preconditionFailed', + 'unprocessable', + ]) + .optional(), + _response: z.lazy(() => elementSchema).optional(), + responseCode: stringSchema.optional(), + _responseCode: z.lazy(() => elementSchema).optional(), + sourceId: idSchema.optional(), + _sourceId: z.lazy(() => elementSchema).optional(), + validateProfileId: idSchema.optional(), + _validateProfileId: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + warningOnly: booleanSchema, + _warningOnly: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testScriptSetupActionOperationRequestHeaderSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + field: stringSchema, + _field: z.lazy(() => elementSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const testScriptSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('TestScript'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema, + _url: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + origin: testScriptOriginSchema.array().optional(), + destination: testScriptDestinationSchema.array().optional(), + metadata: testScriptMetadataSchema.optional(), + fixture: testScriptFixtureSchema.array().optional(), + profile: z + .lazy(() => referenceSchema) + .array() + .optional(), + variable: testScriptVariableSchema.array().optional(), + setup: testScriptSetupSchema.optional(), + test: testScriptTestSchema.array().optional(), + teardown: testScriptTeardownSchema.optional(), + }), +) + +export const timingRepeatSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + boundsDuration: z.lazy(() => durationSchema).optional(), + boundsRange: z.lazy(() => rangeSchema).optional(), + boundsPeriod: z.lazy(() => periodSchema).optional(), + count: positiveIntSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + countMax: positiveIntSchema.optional(), + _countMax: z.lazy(() => elementSchema).optional(), + duration: decimalSchema.optional(), + _duration: z.lazy(() => elementSchema).optional(), + durationMax: decimalSchema.optional(), + _durationMax: z.lazy(() => elementSchema).optional(), + durationUnit: z.enum(['a', 'd', 'h', 'min', 'mo', 's', 'wk']).optional(), + _durationUnit: z.lazy(() => elementSchema).optional(), + frequency: positiveIntSchema.optional(), + _frequency: z.lazy(() => elementSchema).optional(), + frequencyMax: positiveIntSchema.optional(), + _frequencyMax: z.lazy(() => elementSchema).optional(), + period: decimalSchema.optional(), + _period: z.lazy(() => elementSchema).optional(), + periodMax: decimalSchema.optional(), + _periodMax: z.lazy(() => elementSchema).optional(), + periodUnit: z.enum(['a', 'd', 'h', 'min', 'mo', 's', 'wk']).optional(), + _periodUnit: z.lazy(() => elementSchema).optional(), + dayOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .array() + .optional(), + _dayOfWeek: z + .lazy(() => elementSchema) + .array() + .optional(), + timeOfDay: timeSchema.array().optional(), + _timeOfDay: z + .lazy(() => elementSchema) + .array() + .optional(), + when: z + .enum([ + 'AC', + 'ACD', + 'ACM', + 'ACV', + 'AFT', + 'AFT.early', + 'AFT.late', + 'C', + 'CD', + 'CM', + 'CV', + 'EVE', + 'EVE.early', + 'EVE.late', + 'HS', + 'MORN', + 'MORN.early', + 'MORN.late', + 'NIGHT', + 'NOON', + 'PC', + 'PCD', + 'PCM', + 'PCV', + 'PHS', + 'WAKE', + ]) + .array() + .optional(), + _when: z + .lazy(() => elementSchema) + .array() + .optional(), + offset: unsignedIntSchema.optional(), + _offset: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: ['boundsDuration', 'boundsRange', 'boundsPeriod'], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const timingSchema: z.ZodType = z.lazy(() => + z.strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + event: dateTimeSchema.array().optional(), + _event: z + .lazy(() => elementSchema) + .array() + .optional(), + repeat: timingRepeatSchema.optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + }), +) + +export const triggerDefinitionSchema: z.ZodType = z.lazy( + () => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + type: z.enum([ + 'data-access-ended', + 'data-accessed', + 'data-added', + 'data-changed', + 'data-modified', + 'data-removed', + 'named-event', + 'periodic', + ]), + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + timingReference: z.lazy(() => referenceSchema).optional(), + timingDate: dateSchema.optional(), + _timingDate: z.lazy(() => elementSchema).optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + data: z + .lazy(() => dataRequirementSchema) + .array() + .optional(), + condition: z.lazy(() => expressionSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'timingTiming', + 'timingReference', + 'timingDate', + 'timingDateTime', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const usageContextSchema: z.ZodType = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: z.lazy(() => codingSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueCodeableConcept', + 'valueQuantity', + 'valueRange', + 'valueReference', + ], + required: true, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), +) + +export const valueSetComposeSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + lockedDate: dateSchema.optional(), + _lockedDate: z.lazy(() => elementSchema).optional(), + inactive: booleanSchema.optional(), + _inactive: z.lazy(() => elementSchema).optional(), + include: valueSetComposeIncludeSchema.array().min(1), + exclude: z + .lazy(() => valueSetComposeIncludeSchema) + .array() + .optional(), + }), +) + +export const valueSetExpansionSchema: z.ZodType = z.lazy( + () => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: uriSchema.optional(), + _identifier: z.lazy(() => elementSchema).optional(), + timestamp: dateTimeSchema, + _timestamp: z.lazy(() => elementSchema).optional(), + total: integerSchema.optional(), + _total: z.lazy(() => elementSchema).optional(), + offset: integerSchema.optional(), + _offset: z.lazy(() => elementSchema).optional(), + parameter: valueSetExpansionParameterSchema.array().optional(), + contains: valueSetExpansionContainsSchema.array().optional(), + }), +) + +export const valueSetComposeIncludeSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + concept: valueSetComposeIncludeConceptSchema.array().optional(), + filter: valueSetComposeIncludeFilterSchema.array().optional(), + valueSet: canonicalSchema.array().optional(), + _valueSet: z + .lazy(() => elementSchema) + .array() + .optional(), + }), + ) + +export const valueSetExpansionParameterSchema: z.ZodType = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + name: stringSchema, + _name: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + names: [ + 'valueString', + 'valueBoolean', + 'valueInteger', + 'valueDecimal', + 'valueUri', + 'valueCode', + 'valueDateTime', + ], + required: false, + }, + ].every((group) => { + const present = group.names.filter( + (name) => (value as Record)[name] !== undefined, + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ), + ) + +export const valueSetExpansionContainsSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + abstract: booleanSchema.optional(), + _abstract: z.lazy(() => elementSchema).optional(), + inactive: booleanSchema.optional(), + _inactive: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + designation: z + .lazy(() => valueSetComposeIncludeConceptDesignationSchema) + .array() + .optional(), + contains: z + .lazy(() => valueSetExpansionContainsSchema) + .array() + .optional(), + }), + ) + +export const valueSetComposeIncludeConceptSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + code: codeSchema, + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + designation: valueSetComposeIncludeConceptDesignationSchema + .array() + .optional(), + }), + ) + +export const valueSetComposeIncludeFilterSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + property: codeSchema, + _property: z.lazy(() => elementSchema).optional(), + op: z.enum([ + '=', + 'descendent-of', + 'exists', + 'generalizes', + 'in', + 'is-a', + 'is-not-a', + 'not-in', + 'regex', + ]), + _op: z.lazy(() => elementSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const valueSetComposeIncludeConceptDesignationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + use: z.lazy(() => codingSchema).optional(), + value: stringSchema, + _value: z.lazy(() => elementSchema).optional(), + }), + ) + +export const valueSetSchema: z.ZodType = z.lazy(() => + z.strictObject({ + resourceType: z.literal('ValueSet'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + immutable: booleanSchema.optional(), + _immutable: z.lazy(() => elementSchema).optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + compose: valueSetComposeSchema.optional(), + expansion: valueSetExpansionSchema.optional(), + }), +) + +export const verificationResultPrimarySourceSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + who: z.lazy(() => referenceSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + communicationMethod: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + validationStatus: z.lazy(() => codeableConceptSchema).optional(), + validationDate: dateTimeSchema.optional(), + _validationDate: z.lazy(() => elementSchema).optional(), + canPushUpdates: z.lazy(() => codeableConceptSchema).optional(), + pushTypeAvailable: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + }), + ) + +export const verificationResultAttestationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + who: z.lazy(() => referenceSchema).optional(), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + communicationMethod: z.lazy(() => codeableConceptSchema).optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + sourceIdentityCertificate: stringSchema.optional(), + _sourceIdentityCertificate: z.lazy(() => elementSchema).optional(), + proxyIdentityCertificate: stringSchema.optional(), + _proxyIdentityCertificate: z.lazy(() => elementSchema).optional(), + proxySignature: z.lazy(() => signatureSchema).optional(), + sourceSignature: z.lazy(() => signatureSchema).optional(), + }), + ) + +export const verificationResultValidatorSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + organization: z.lazy(() => referenceSchema), + identityCertificate: stringSchema.optional(), + _identityCertificate: z.lazy(() => elementSchema).optional(), + attestationSignature: z.lazy(() => signatureSchema).optional(), + }), + ) + +export const verificationResultSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('VerificationResult'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + target: z + .lazy(() => referenceSchema) + .array() + .optional(), + targetLocation: stringSchema.array().optional(), + _targetLocation: z + .lazy(() => elementSchema) + .array() + .optional(), + need: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum([ + 'attested', + 'in-process', + 'req-revalid', + 'reval-fail', + 'val-fail', + 'validated', + ]), + _status: z.lazy(() => elementSchema).optional(), + statusDate: dateTimeSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), + validationType: z.lazy(() => codeableConceptSchema).optional(), + validationProcess: z + .lazy(() => codeableConceptSchema) + .array() + .optional(), + frequency: z.lazy(() => timingSchema).optional(), + lastPerformed: dateTimeSchema.optional(), + _lastPerformed: z.lazy(() => elementSchema).optional(), + nextScheduled: dateSchema.optional(), + _nextScheduled: z.lazy(() => elementSchema).optional(), + failureAction: z.lazy(() => codeableConceptSchema).optional(), + primarySource: verificationResultPrimarySourceSchema.array().optional(), + attestation: verificationResultAttestationSchema.optional(), + validator: verificationResultValidatorSchema.array().optional(), + }), + ) + +export const visionPrescriptionLensSpecificationSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + product: z.lazy(() => codeableConceptSchema), + eye: z.enum(['left', 'right']), + _eye: z.lazy(() => elementSchema).optional(), + sphere: decimalSchema.optional(), + _sphere: z.lazy(() => elementSchema).optional(), + cylinder: decimalSchema.optional(), + _cylinder: z.lazy(() => elementSchema).optional(), + axis: integerSchema.optional(), + _axis: z.lazy(() => elementSchema).optional(), + prism: visionPrescriptionLensSpecificationPrismSchema.array().optional(), + add: decimalSchema.optional(), + _add: z.lazy(() => elementSchema).optional(), + power: decimalSchema.optional(), + _power: z.lazy(() => elementSchema).optional(), + backCurve: decimalSchema.optional(), + _backCurve: z.lazy(() => elementSchema).optional(), + diameter: decimalSchema.optional(), + _diameter: z.lazy(() => elementSchema).optional(), + duration: z.lazy(() => quantitySchema).optional(), + color: stringSchema.optional(), + _color: z.lazy(() => elementSchema).optional(), + brand: stringSchema.optional(), + _brand: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .optional(), + }), + ) + +export const visionPrescriptionLensSpecificationPrismSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + amount: decimalSchema, + _amount: z.lazy(() => elementSchema).optional(), + base: z.enum(['down', 'in', 'out', 'up']), + _base: z.lazy(() => elementSchema).optional(), + }), + ) + +export const visionPrescriptionSchema: z.ZodType = + z.lazy(() => + z.strictObject({ + resourceType: z.literal('VisionPrescription'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .optional(), + status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), + _status: z.lazy(() => elementSchema).optional(), + created: dateTimeSchema, + _created: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + dateWritten: dateTimeSchema, + _dateWritten: z.lazy(() => elementSchema).optional(), + prescriber: z.lazy(() => referenceSchema), + lensSpecification: visionPrescriptionLensSpecificationSchema + .array() + .min(1), + }), + ) + +export const base64BinarySchema: z.ZodType = z + .string() + .regex( + new RegExp('^[0-9a-zA-Z+/= \\t\\n\\r]*$'), + 'Expected a FHIR base64Binary.', + ) + .refine((value) => value.replace(/[ \t\n\r]/gu, '').length % 4 === 0, { + message: 'Expected a FHIR base64Binary.', + }) + +export const booleanSchema: z.ZodType = z.boolean() + +export const canonicalSchema: z.ZodType = z + .string() + .regex(new RegExp('^(?:[^ \\t\\n\\r]*)$'), 'Expected a FHIR canonical.') + +export const codeSchema: z.ZodType = z + .string() + .regex( + new RegExp('^(?:[^ \\t\\n\\r]+([ \\t\\n\\r][^ \\t\\n\\r]+)*)$'), + 'Expected a FHIR code.', + ) + +export const dateSchema: z.ZodType = z + .string() + .regex( + new RegExp( + '^(?:([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1]))?)?)$', + ), + 'Expected a FHIR date.', + ) + +export const dateTimeSchema: z.ZodType = z + .string() + .regex( + new RegExp( + '^(?:([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00)))?)?)?)$', + ), + 'Expected a FHIR dateTime.', + ) + +export const decimalSchema: z.ZodType = z + .number() + .refine( + (value) => + new RegExp('^(?:-?(0|[1-9][0-9]*)(\\.[0-9]+)?([eE][+-]?[0-9]+)?)$').test( + String(value), + ), + { + message: 'Expected a valid FHIR decimal.', + }, + ) + +export const idSchema: z.ZodType = z + .string() + .regex(new RegExp('^(?:[A-Za-z0-9\\-\\.]{1,64})$'), 'Expected a FHIR id.') + +export const instantSchema: z.ZodType = z + .string() + .regex( + new RegExp( + '^(?:([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00)))$', + ), + 'Expected a FHIR instant.', + ) + +export const integerSchema: z.ZodType = z + .number() + .int() + .min(-2147483648) + .max(2147483647) + .refine( + (value) => new RegExp('^(?:-?([0]|([1-9][0-9]*)))$').test(String(value)), + { + message: 'Expected a valid FHIR integer.', + }, + ) + +export const markdownSchema: z.ZodType = z + .string() + .regex(new RegExp('^(?:[\\s\\S]+)$'), 'Expected a FHIR markdown.') + +export const oidSchema: z.ZodType = z + .string() + .regex( + new RegExp('^(?:urn:oid:[0-2](\\.(0|[1-9][0-9]*))+)$'), + 'Expected a FHIR oid.', + ) + +export const positiveIntSchema: z.ZodType = z + .number() + .int() + .min(-2147483648) + .max(2147483647) + .refine((value) => new RegExp('^(?:[1-9][0-9]*)$').test(String(value)), { + message: 'Expected a valid FHIR positiveInt.', + }) + +export const stringSchema: z.ZodType = z + .string() + .regex(new RegExp('^(?:[\\s\\S]+)$'), 'Expected a FHIR string.') + +export const timeSchema: z.ZodType = z + .string() + .regex( + new RegExp( + '^(?:([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?)$', + ), + 'Expected a FHIR time.', + ) + +export const unsignedIntSchema: z.ZodType = z + .number() + .int() + .min(-2147483648) + .max(2147483647) + .refine( + (value) => new RegExp('^(?:[0]|([1-9][0-9]*))$').test(String(value)), + { + message: 'Expected a valid FHIR unsignedInt.', + }, + ) + +export const uriSchema: z.ZodType = z + .string() + .regex(new RegExp('^(?:[^ \\t\\n\\r]*)$'), 'Expected a FHIR uri.') + +export const urlSchema: z.ZodType = z + .string() + .regex(new RegExp('^(?:[^ \\t\\n\\r]*)$'), 'Expected a FHIR url.') + +export const uuidSchema: z.ZodType = z + .string() + .regex( + new RegExp( + '^(?:urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$', + ), + 'Expected a FHIR uuid.', + ) + +export const xhtmlSchema: z.ZodType = z.string() + +const modelledResources: Readonly> = { + Account: accountSchema, + ActivityDefinition: activityDefinitionSchema, + AdministrableProductDefinition: administrableProductDefinitionSchema, + AdverseEvent: adverseEventSchema, + AllergyIntolerance: allergyIntoleranceSchema, + Appointment: appointmentSchema, + AppointmentResponse: appointmentResponseSchema, + AuditEvent: auditEventSchema, + Basic: basicSchema, + Binary: binarySchema, + BiologicallyDerivedProduct: biologicallyDerivedProductSchema, + BodyStructure: bodyStructureSchema, + Bundle: bundleSchema, + CapabilityStatement: capabilityStatementSchema, + CarePlan: carePlanSchema, + CareTeam: careTeamSchema, + CatalogEntry: catalogEntrySchema, + ChargeItem: chargeItemSchema, + ChargeItemDefinition: chargeItemDefinitionSchema, + Citation: citationSchema, + Claim: claimSchema, + ClaimResponse: claimResponseSchema, + ClinicalImpression: clinicalImpressionSchema, + ClinicalUseDefinition: clinicalUseDefinitionSchema, + CodeSystem: codeSystemSchema, + Communication: communicationSchema, + CommunicationRequest: communicationRequestSchema, + CompartmentDefinition: compartmentDefinitionSchema, + Composition: compositionSchema, + ConceptMap: conceptMapSchema, + Condition: conditionSchema, + Consent: consentSchema, + Contract: contractSchema, + Coverage: coverageSchema, + CoverageEligibilityRequest: coverageEligibilityRequestSchema, + CoverageEligibilityResponse: coverageEligibilityResponseSchema, + DetectedIssue: detectedIssueSchema, + Device: deviceSchema, + DeviceDefinition: deviceDefinitionSchema, + DeviceMetric: deviceMetricSchema, + DeviceRequest: deviceRequestSchema, + DeviceUseStatement: deviceUseStatementSchema, + DiagnosticReport: diagnosticReportSchema, + DocumentManifest: documentManifestSchema, + DocumentReference: documentReferenceSchema, + Encounter: encounterSchema, + Endpoint: endpointSchema, + EnrollmentRequest: enrollmentRequestSchema, + EnrollmentResponse: enrollmentResponseSchema, + EpisodeOfCare: episodeOfCareSchema, + EventDefinition: eventDefinitionSchema, + Evidence: evidenceSchema, + EvidenceReport: evidenceReportSchema, + EvidenceVariable: evidenceVariableSchema, + ExampleScenario: exampleScenarioSchema, + ExplanationOfBenefit: explanationOfBenefitSchema, + FamilyMemberHistory: familyMemberHistorySchema, + Flag: flagSchema, + Goal: goalSchema, + GraphDefinition: graphDefinitionSchema, + Group: groupSchema, + GuidanceResponse: guidanceResponseSchema, + HealthcareService: healthcareServiceSchema, + ImagingStudy: imagingStudySchema, + Immunization: immunizationSchema, + ImmunizationEvaluation: immunizationEvaluationSchema, + ImmunizationRecommendation: immunizationRecommendationSchema, + ImplementationGuide: implementationGuideSchema, + Ingredient: ingredientSchema, + InsurancePlan: insurancePlanSchema, + Invoice: invoiceSchema, + Library: librarySchema, + Linkage: linkageSchema, + List: listSchema, + Location: locationSchema, + ManufacturedItemDefinition: manufacturedItemDefinitionSchema, + Measure: measureSchema, + MeasureReport: measureReportSchema, + Media: mediaSchema, + Medication: medicationSchema, + MedicationAdministration: medicationAdministrationSchema, + MedicationDispense: medicationDispenseSchema, + MedicationKnowledge: medicationKnowledgeSchema, + MedicationRequest: medicationRequestSchema, + MedicationStatement: medicationStatementSchema, + MedicinalProductDefinition: medicinalProductDefinitionSchema, + MessageDefinition: messageDefinitionSchema, + MessageHeader: messageHeaderSchema, + MolecularSequence: molecularSequenceSchema, + NamingSystem: namingSystemSchema, + NutritionOrder: nutritionOrderSchema, + NutritionProduct: nutritionProductSchema, + Observation: observationSchema, + ObservationDefinition: observationDefinitionSchema, + OperationDefinition: operationDefinitionSchema, + OperationOutcome: operationOutcomeSchema, + Organization: organizationSchema, + OrganizationAffiliation: organizationAffiliationSchema, + PackagedProductDefinition: packagedProductDefinitionSchema, + Parameters: parametersSchema, + Patient: patientSchema, + PaymentNotice: paymentNoticeSchema, + PaymentReconciliation: paymentReconciliationSchema, + Person: personSchema, + PlanDefinition: planDefinitionSchema, + Practitioner: practitionerSchema, + PractitionerRole: practitionerRoleSchema, + Procedure: procedureSchema, + Provenance: provenanceSchema, + Questionnaire: questionnaireSchema, + QuestionnaireResponse: questionnaireResponseSchema, + RegulatedAuthorization: regulatedAuthorizationSchema, + RelatedPerson: relatedPersonSchema, + RequestGroup: requestGroupSchema, + ResearchDefinition: researchDefinitionSchema, + ResearchElementDefinition: researchElementDefinitionSchema, + ResearchStudy: researchStudySchema, + ResearchSubject: researchSubjectSchema, + RiskAssessment: riskAssessmentSchema, + Schedule: scheduleSchema, + SearchParameter: searchParameterSchema, + ServiceRequest: serviceRequestSchema, + Slot: slotSchema, + Specimen: specimenSchema, + SpecimenDefinition: specimenDefinitionSchema, + StructureDefinition: structureDefinitionSchema, + StructureMap: structureMapSchema, + Subscription: subscriptionSchema, + SubscriptionStatus: subscriptionStatusSchema, + SubscriptionTopic: subscriptionTopicSchema, + Substance: substanceSchema, + SubstanceDefinition: substanceDefinitionSchema, + SupplyDelivery: supplyDeliverySchema, + SupplyRequest: supplyRequestSchema, + Task: taskSchema, + TerminologyCapabilities: terminologyCapabilitiesSchema, + TestReport: testReportSchema, + TestScript: testScriptSchema, + ValueSet: valueSetSchema, + VerificationResult: verificationResultSchema, + VisionPrescription: visionPrescriptionSchema, +} + +const dispatchingResource = new WeakSet() + +export const resourceSchema: z.ZodType = z + .custom( + (value) => + typeof value === 'object' && + value !== null && + typeof (value as { resourceType?: unknown }).resourceType === 'string', + { + message: 'Expected a FHIR resource carrying a resourceType.', + abort: true, + }, + ) + .superRefine((value, ctx) => { + const modelled = modelledResources[value.resourceType] + // A resource type the release defines but this package does not model keeps the open + // shape; reporting it as invalid would reject a conformant bundle. + if (modelled === undefined) return + // JSON cannot describe a resource that contains itself, but an in-memory object can, + // and re-entering one would recur until the stack ran out. + if (dispatchingResource.has(value)) return + dispatchingResource.add(value) + try { + const result = modelled.safeParse(value) + if (result.success) return + // Re-raised rather than summarised, so a fault inside the resource still names the + // element that carries it. + for (const issue of result.error.issues) ctx.addIssue({ ...issue }) + } finally { + dispatchingResource.delete(value) + } + }) + +/** + * Invariants the release states for these paths that this module does not check. + * + * Structural rules are enforced; a rule expressed only as FHIRPath is not evaluated here. + * Normative conformance is established by the official FHIR Validator, not by this module. + */ +export const UNCHECKED_CONSTRAINTS: Readonly< + Record +> = { + Account: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Account.contained': ['dom-r4b'], + ActivityDefinition: ['cnl-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ActivityDefinition.contained': ['dom-r4b'], + AdministrableProductDefinition: [ + 'apd-1', + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + ], + 'AdministrableProductDefinition.contained': ['dom-r4b'], + AdverseEvent: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'AdverseEvent.contained': ['dom-r4b'], + Age: ['age-1', 'qty-3'], + AllergyIntolerance: [ + 'ait-1', + 'ait-2', + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + ], + 'AllergyIntolerance.contained': ['dom-r4b'], + Appointment: [ + 'app-2', + 'app-3', + 'app-4', + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + ], + 'Appointment.contained': ['dom-r4b'], + 'Appointment.participant': ['app-1'], + AppointmentResponse: ['apr-1', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'AppointmentResponse.contained': ['dom-r4b'], + Attachment: ['att-1'], + AuditEvent: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'AuditEvent.contained': ['dom-r4b'], + 'AuditEvent.entity': ['sev-1'], + Basic: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Basic.contained': ['dom-r4b'], + BiologicallyDerivedProduct: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'BiologicallyDerivedProduct.contained': ['dom-r4b'], + BodyStructure: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'BodyStructure.contained': ['dom-r4b'], + Bundle: [ + 'bdl-1', + 'bdl-10', + 'bdl-11', + 'bdl-12', + 'bdl-2', + 'bdl-3', + 'bdl-4', + 'bdl-7', + 'bdl-9', + ], + 'Bundle.entry': ['bdl-5', 'bdl-8'], + CapabilityStatement: [ + 'cpb-0', + 'cpb-1', + 'cpb-14', + 'cpb-15', + 'cpb-16', + 'cpb-2', + 'cpb-3', + 'cpb-7', + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + ], + 'CapabilityStatement.contained': ['dom-r4b'], + 'CapabilityStatement.rest': ['cpb-9'], + 'CapabilityStatement.rest.resource': ['cpb-12'], + CarePlan: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'CarePlan.activity': ['cpl-3'], + 'CarePlan.contained': ['dom-r4b'], + CareTeam: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'CareTeam.contained': ['dom-r4b'], + 'CareTeam.participant': ['ctm-1'], + CatalogEntry: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'CatalogEntry.contained': ['dom-r4b'], + ChargeItem: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ChargeItem.contained': ['dom-r4b'], + ChargeItemDefinition: ['cid-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ChargeItemDefinition.contained': ['dom-r4b'], + Citation: ['cnl-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Citation.contained': ['dom-r4b'], + Claim: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Claim.contained': ['dom-r4b'], + ClaimResponse: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ClaimResponse.contained': ['dom-r4b'], + ClinicalImpression: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ClinicalImpression.contained': ['dom-r4b'], + ClinicalUseDefinition: ['cud-1', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ClinicalUseDefinition.contained': ['dom-r4b'], + CodeSystem: ['csd-0', 'csd-1', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'CodeSystem.contained': ['dom-r4b'], + Communication: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Communication.contained': ['dom-r4b'], + CommunicationRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'CommunicationRequest.contained': ['dom-r4b'], + CompartmentDefinition: ['cpd-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'CompartmentDefinition.contained': ['dom-r4b'], + Composition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Composition.contained': ['dom-r4b'], + 'Composition.section': ['cmp-1', 'cmp-2'], + ConceptMap: ['cmd-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ConceptMap.contained': ['dom-r4b'], + 'ConceptMap.group.element.target': ['cmd-1'], + 'ConceptMap.group.unmapped': ['cmd-2', 'cmd-3'], + Condition: [ + 'con-3', + 'con-4', + 'con-5', + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + ], + 'Condition.contained': ['dom-r4b'], + 'Condition.evidence': ['con-2'], + 'Condition.stage': ['con-1'], + Consent: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'ppc-1', + 'ppc-2', + 'ppc-3', + 'ppc-4', + 'ppc-5', + ], + 'Consent.contained': ['dom-r4b'], + ContactPoint: ['cpt-2'], + Contract: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Contract.contained': ['dom-r4b'], + Count: ['cnt-3', 'qty-3'], + Coverage: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Coverage.contained': ['dom-r4b'], + CoverageEligibilityRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'CoverageEligibilityRequest.contained': ['dom-r4b'], + CoverageEligibilityResponse: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'CoverageEligibilityResponse.contained': ['dom-r4b'], + 'CoverageEligibilityResponse.insurance.item': ['ces-1'], + 'DataRequirement.codeFilter': ['drq-1'], + 'DataRequirement.dateFilter': ['drq-2'], + DetectedIssue: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'DetectedIssue.contained': ['dom-r4b'], + Device: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Device.contained': ['dom-r4b'], + DeviceDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'DeviceDefinition.contained': ['dom-r4b'], + DeviceMetric: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'DeviceMetric.contained': ['dom-r4b'], + DeviceRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'DeviceRequest.contained': ['dom-r4b'], + DeviceUseStatement: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'DeviceUseStatement.contained': ['dom-r4b'], + DiagnosticReport: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'DiagnosticReport.contained': ['dom-r4b'], + Distance: ['dis-1', 'qty-3'], + DocumentManifest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'DocumentManifest.contained': ['dom-r4b'], + DocumentReference: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'DocumentReference.contained': ['dom-r4b'], + Duration: ['drt-1', 'qty-3'], + ElementDefinition: [ + 'eld-11', + 'eld-13', + 'eld-14', + 'eld-15', + 'eld-16', + 'eld-18', + 'eld-19', + 'eld-2', + 'eld-20', + 'eld-22', + 'eld-5', + 'eld-6', + 'eld-7', + 'eld-8', + ], + 'ElementDefinition.binding': ['eld-12'], + 'ElementDefinition.constraint': ['eld-21'], + 'ElementDefinition.max': ['eld-3'], + 'ElementDefinition.slicing': ['eld-1'], + 'ElementDefinition.type': ['eld-17', 'eld-4'], + Encounter: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Encounter.contained': ['dom-r4b'], + Endpoint: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Endpoint.contained': ['dom-r4b'], + EnrollmentRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'EnrollmentRequest.contained': ['dom-r4b'], + EnrollmentResponse: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'EnrollmentResponse.contained': ['dom-r4b'], + EpisodeOfCare: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'EpisodeOfCare.contained': ['dom-r4b'], + EventDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'evd-0'], + 'EventDefinition.contained': ['dom-r4b'], + Evidence: ['cnl-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Evidence.contained': ['dom-r4b'], + EvidenceReport: ['cnl-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'EvidenceReport.contained': ['dom-r4b'], + EvidenceVariable: ['cnl-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'EvidenceVariable.contained': ['dom-r4b'], + ExampleScenario: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'esc-0'], + 'ExampleScenario.contained': ['dom-r4b'], + ExplanationOfBenefit: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ExplanationOfBenefit.contained': ['dom-r4b'], + Expression: ['exp-1'], + FamilyMemberHistory: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'fhs-1', + 'fhs-2', + ], + 'FamilyMemberHistory.contained': ['dom-r4b'], + Flag: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Flag.contained': ['dom-r4b'], + Goal: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Goal.contained': ['dom-r4b'], + 'Goal.target': ['gol-1'], + GraphDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'gdf-0'], + 'GraphDefinition.contained': ['dom-r4b'], + Group: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'grp-1'], + 'Group.contained': ['dom-r4b'], + GuidanceResponse: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'GuidanceResponse.contained': ['dom-r4b'], + HealthcareService: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'HealthcareService.contained': ['dom-r4b'], + ImagingStudy: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ImagingStudy.contained': ['dom-r4b'], + Immunization: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Immunization.contained': ['dom-r4b'], + 'Immunization.education': ['imm-1'], + ImmunizationEvaluation: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ImmunizationEvaluation.contained': ['dom-r4b'], + ImmunizationRecommendation: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ImmunizationRecommendation.contained': ['dom-r4b'], + 'ImmunizationRecommendation.recommendation': ['imr-1'], + ImplementationGuide: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'ig-0', + 'ig-2', + ], + 'ImplementationGuide.contained': ['dom-r4b'], + 'ImplementationGuide.definition': ['ig-1'], + Ingredient: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'ing-1'], + 'Ingredient.contained': ['dom-r4b'], + InsurancePlan: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'ipn-1'], + 'InsurancePlan.contained': ['dom-r4b'], + Invoice: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Invoice.contained': ['dom-r4b'], + Library: ['cnl-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Library.contained': ['dom-r4b'], + Linkage: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'lnk-1'], + 'Linkage.contained': ['dom-r4b'], + List: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'lst-1', + 'lst-2', + 'lst-3', + ], + 'List.contained': ['dom-r4b'], + Location: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Location.contained': ['dom-r4b'], + ManufacturedItemDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ManufacturedItemDefinition.contained': ['dom-r4b'], + Measure: ['cnl-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'mea-1'], + 'Measure.contained': ['dom-r4b'], + MeasureReport: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'mrp-1', + 'mrp-2', + ], + 'MeasureReport.contained': ['dom-r4b'], + Media: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Media.contained': ['dom-r4b'], + Medication: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Medication.contained': ['dom-r4b'], + MedicationAdministration: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'MedicationAdministration.contained': ['dom-r4b'], + 'MedicationAdministration.dosage': ['mad-1'], + MedicationDispense: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'mdd-1'], + 'MedicationDispense.contained': ['dom-r4b'], + MedicationKnowledge: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'MedicationKnowledge.contained': ['dom-r4b'], + MedicationRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'MedicationRequest.contained': ['dom-r4b'], + MedicationStatement: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'MedicationStatement.contained': ['dom-r4b'], + MedicinalProductDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'MedicinalProductDefinition.contained': ['dom-r4b'], + MessageDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'msd-0'], + 'MessageDefinition.contained': ['dom-r4b'], + 'MessageDefinition.focus': ['md-1'], + MessageHeader: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'MessageHeader.contained': ['dom-r4b'], + MolecularSequence: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'msq-3'], + 'MolecularSequence.contained': ['dom-r4b'], + 'MolecularSequence.referenceSeq': ['msq-5', 'msq-6'], + NamingSystem: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'nsd-0', + 'nsd-1', + 'nsd-2', + ], + 'NamingSystem.contained': ['dom-r4b'], + 'Narrative.div': ['txt-1', 'txt-2'], + NutritionOrder: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'nor-1'], + 'NutritionOrder.contained': ['dom-r4b'], + NutritionProduct: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'NutritionProduct.contained': ['dom-r4b'], + Observation: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'obs-7'], + 'Observation.contained': ['dom-r4b'], + ObservationDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ObservationDefinition.contained': ['dom-r4b'], + OperationDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'opd-0'], + 'OperationDefinition.contained': ['dom-r4b'], + 'OperationDefinition.parameter': ['opd-1', 'opd-2', 'opd-3'], + OperationOutcome: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'OperationOutcome.contained': ['dom-r4b'], + Organization: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'org-1'], + 'Organization.address': ['org-2'], + 'Organization.contained': ['dom-r4b'], + 'Organization.telecom': ['org-3'], + OrganizationAffiliation: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'OrganizationAffiliation.contained': ['dom-r4b'], + PackagedProductDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'PackagedProductDefinition.contained': ['dom-r4b'], + 'Parameters.parameter': ['inv-1'], + Patient: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Patient.contact': ['pat-1'], + 'Patient.contained': ['dom-r4b'], + PaymentNotice: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'PaymentNotice.contained': ['dom-r4b'], + PaymentReconciliation: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'PaymentReconciliation.contained': ['dom-r4b'], + Person: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Person.contained': ['dom-r4b'], + PlanDefinition: ['cnl-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'PlanDefinition.contained': ['dom-r4b'], + Practitioner: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Practitioner.contained': ['dom-r4b'], + PractitionerRole: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'PractitionerRole.contained': ['dom-r4b'], + Procedure: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Procedure.contained': ['dom-r4b'], + Provenance: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Provenance.contained': ['dom-r4b'], + Quantity: ['qty-3'], + Questionnaire: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'que-0', + 'que-2', + ], + 'Questionnaire.contained': ['dom-r4b'], + 'Questionnaire.item': [ + 'que-10', + 'que-11', + 'que-12', + 'que-13', + 'que-1a', + 'que-1b', + 'que-1c', + 'que-3', + 'que-4', + 'que-5', + 'que-6', + 'que-8', + 'que-9', + ], + 'Questionnaire.item.enableWhen': ['que-7'], + QuestionnaireResponse: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'QuestionnaireResponse.contained': ['dom-r4b'], + 'QuestionnaireResponse.item': ['qrs-1'], + Range: ['rng-2'], + Ratio: ['rat-1'], + RatioRange: ['inv-1', 'inv-2'], + Reference: ['ref-1'], + RegulatedAuthorization: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'RegulatedAuthorization.contained': ['dom-r4b'], + RelatedPerson: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'RelatedPerson.contained': ['dom-r4b'], + RequestGroup: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'RequestGroup.action': ['rqg-1'], + 'RequestGroup.contained': ['dom-r4b'], + ResearchDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'rsd-0'], + 'ResearchDefinition.contained': ['dom-r4b'], + ResearchElementDefinition: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'red-0', + ], + 'ResearchElementDefinition.contained': ['dom-r4b'], + ResearchStudy: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ResearchStudy.contained': ['dom-r4b'], + ResearchSubject: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'ResearchSubject.contained': ['dom-r4b'], + RiskAssessment: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'RiskAssessment.contained': ['dom-r4b'], + 'RiskAssessment.prediction': ['ras-2'], + 'RiskAssessment.prediction.probability[x]': ['ras-1'], + Schedule: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Schedule.contained': ['dom-r4b'], + SearchParameter: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'spd-0', + 'spd-1', + 'spd-2', + ], + 'SearchParameter.contained': ['dom-r4b'], + ServiceRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'prr-1'], + 'ServiceRequest.contained': ['dom-r4b'], + Slot: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Slot.contained': ['dom-r4b'], + Specimen: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Specimen.contained': ['dom-r4b'], + SpecimenDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'SpecimenDefinition.contained': ['dom-r4b'], + StructureDefinition: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'sdf-0', + 'sdf-1', + 'sdf-11', + 'sdf-14', + 'sdf-15', + 'sdf-15a', + 'sdf-16', + 'sdf-17', + 'sdf-18', + 'sdf-19', + 'sdf-21', + 'sdf-22', + 'sdf-23', + 'sdf-4', + 'sdf-5', + 'sdf-6', + 'sdf-9', + ], + 'StructureDefinition.contained': ['dom-r4b'], + 'StructureDefinition.differential': ['sdf-20', 'sdf-8a'], + 'StructureDefinition.mapping': ['sdf-2'], + 'StructureDefinition.snapshot': [ + 'sdf-24', + 'sdf-25', + 'sdf-3', + 'sdf-8', + 'sdf-8b', + ], + 'StructureDefinition.snapshot.element': ['sdf-10'], + StructureMap: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'smp-0'], + 'StructureMap.contained': ['dom-r4b'], + 'StructureMap.group.rule.target': ['smp-1', 'smp-2'], + Subscription: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Subscription.contained': ['dom-r4b'], + SubscriptionStatus: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'sst-1'], + 'SubscriptionStatus.contained': ['dom-r4b'], + SubscriptionTopic: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'SubscriptionTopic.contained': ['dom-r4b'], + Substance: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'Substance.contained': ['dom-r4b'], + SubstanceDefinition: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'SubstanceDefinition.contained': ['dom-r4b'], + SupplyDelivery: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'SupplyDelivery.contained': ['dom-r4b'], + SupplyRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'SupplyRequest.contained': ['dom-r4b'], + Task: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'inv-1'], + 'Task.contained': ['dom-r4b'], + TerminologyCapabilities: [ + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'tcp-0', + 'tcp-2', + 'tcp-3', + 'tcp-4', + 'tcp-5', + ], + 'TerminologyCapabilities.codeSystem': ['tcp-1'], + 'TerminologyCapabilities.contained': ['dom-r4b'], + TestReport: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'TestReport.contained': ['dom-r4b'], + 'TestReport.setup.action': ['inv-1'], + 'TestReport.test.action': ['inv-2'], + TestScript: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'tst-0'], + 'TestScript.contained': ['dom-r4b'], + 'TestScript.metadata': ['tst-4'], + 'TestScript.setup.action': ['tst-1'], + 'TestScript.setup.action.assert': ['tst-10', 'tst-12', 'tst-5'], + 'TestScript.setup.action.operation': ['tst-7'], + 'TestScript.teardown.action.operation': ['tst-9'], + 'TestScript.test.action': ['tst-2'], + 'TestScript.test.action.assert': ['tst-11', 'tst-13', 'tst-6'], + 'TestScript.test.action.operation': ['tst-8'], + 'TestScript.variable': ['tst-3'], + 'Timing.repeat': [ + 'tim-1', + 'tim-10', + 'tim-2', + 'tim-4', + 'tim-5', + 'tim-6', + 'tim-7', + 'tim-8', + 'tim-9', + ], + TriggerDefinition: ['trd-1', 'trd-2', 'trd-3'], + ValueSet: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6', 'vsd-0'], + 'ValueSet.compose.include': ['vsd-1', 'vsd-2', 'vsd-3'], + 'ValueSet.contained': ['dom-r4b'], + 'ValueSet.expansion.contains': ['vsd-10', 'vsd-6', 'vsd-9'], + VerificationResult: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'VerificationResult.contained': ['dom-r4b'], + VisionPrescription: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], + 'VisionPrescription.contained': ['dom-r4b'], +} diff --git a/packages/fhir/src/zod/support.ts b/packages/fhir/src/zod/support.ts new file mode 100644 index 00000000..69e1aed7 --- /dev/null +++ b/packages/fhir/src/zod/support.ts @@ -0,0 +1,22 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/** + * A FHIR dateTime as an epoch value that orders correctly. + * + * R4 admits a leap second, which no JavaScript date parser accepts; mapping it to `:59` keeps the + * instant within the second it belongs to, which is all an ordering comparison needs. A value + * stated to lower precision parses at the start of the period it names, which is the comparison + * FHIR intends. + * + * Returns `NaN` for a value the primitive's own pattern would already have rejected; a caller + * comparing two endpoints treats that as "not comparable" rather than as "out of order", so a + * malformed date is reported as malformed and not as an ordering fault. + */ +export const fhirDateTimeToEpoch = (value: string): number => + Date.parse(value.replace(/:60(?=\.|Z|[+-]|$)/u, ':59')) diff --git a/packages/fhir/test/coverage-tsconfig.json b/packages/fhir/test/coverage-tsconfig.json deleted file mode 100644 index c70865f0..00000000 --- a/packages/fhir/test/coverage-tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "module": "NodeNext", - "moduleResolution": "NodeNext", - "noEmit": true, - "types": ["jest", "node"] - }, - "include": ["../src/**/*.ts"] -} diff --git a/packages/fhir/test/coverage-tsconfig.json.license b/packages/fhir/test/coverage-tsconfig.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/coverage-tsconfig.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/elements/elements.test.ts b/packages/fhir/test/elements/elements.test.ts deleted file mode 100644 index 27750a1c..00000000 --- a/packages/fhir/test/elements/elements.test.ts +++ /dev/null @@ -1,243 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { expectTypeOf } from 'expect-type' -import { - type Address, - type Annotation, - type Attachment, - type BackboneElement, - type CodeableConcept, - type CodeableReference, - type Coding, - type ContactDetail, - type ContactPoint, - type Contributor, - type DataRequirement, - type DomainResource, - type Dosage, - type Element, - type Expression, - type Extension, - type HumanName, - type Identifier, - type MarketingStatus, - type Meta, - type Money, - type Narrative, - type ParameterDefinition, - type Period, - type Quantity, - type Range, - type Ratio, - type RatioRange, - type Reference, - type RelatedArtifact, - type Resource, - type SampledData, - type Signature, - type Timing, - type TriggerDefinition, - type UsageContext, -} from 'fhir/r4b.js' -import { type z } from 'zod' -import { - type backboneElementSchema, - type domainResourceSchema, - type elementSchema, - type resourceSchema, - type untypedAddressSchema, - type untypedAnnotationSchema, - type untypedAttachmentSchema, - type untypedCodeableConceptSchema, - type untypedCodeableReferenceSchema, - type untypedCodingSchema, - type untypedContactDetailSchema, - type untypedContactPointSchema, - type untypedContributorSchema, - type untypedDataRequirementSchema, - type untypedDosageSchema, - type untypedExpressionSchema, - type untypedExtensionSchema, - type untypedHumanNameSchema, - type untypedIdentifierSchema, - type untypedMarketingStatusSchema, - type untypedMetaSchema, - type untypedMoneySchema, - type untypedNarrativeSchema, - type untypedParameterDefinitionSchema, - type untypedPeriodSchema, - type untypedQuantitySchema, - type untypedRangeSchema, - type untypedRatioRangeSchema, - type untypedRatioSchema, - type untypedReferenceSchema, - type untypedRelatedArtifactSchema, - type untypedSampledDataSchema, - type untypedSignatureSchema, - type untypedTimingSchema, - type untypedTriggerDefinitionSchema, - type untypedUsageContextSchema, -} from '../../src/index.js' - -describe('Elements', () => { - it('should validate data types to be typed correctly', () => { - type AddressSchema = z.infer - expectTypeOf().toExtend
() - expectTypeOf
().toExtend() - - type AnnotationSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type AttachmentSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type CodeableConceptSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type CodeableReferenceSchema = z.infer< - typeof untypedCodeableReferenceSchema - > - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type CodingSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type ContactPointSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type HumanNameSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type IdentifierSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type MarketingStatusSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type MoneySchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type NarrativeSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type PeriodSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type QuantitySchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type RangeSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type RatioSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type RatioRangeSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type ReferenceSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type SampledDataSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type SignatureSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type TimingSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - }) - - it('should validate metadata types to be typed correctly', () => { - type ContactDetailSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type ContributorSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type DataRequirementSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type ExpressionSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type ParameterDefinitionSchema = z.infer< - typeof untypedParameterDefinitionSchema - > - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type RelatedArtifactSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type TriggerDefinitionSchema = z.infer< - typeof untypedTriggerDefinitionSchema - > - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type UsageContextSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - }) - - it('should validate all other elements to be typed correctly', () => { - type DosageSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type ExtensionSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type MetaSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type ResourceSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type DomainResourceSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type BackboneElementSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - type ElementSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - }) -}) diff --git a/packages/fhir/test/exports.test.ts b/packages/fhir/test/exports.test.ts new file mode 100644 index 00000000..1a284787 --- /dev/null +++ b/packages/fhir/test/exports.test.ts @@ -0,0 +1,111 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { expectTypeOf } from 'expect-type' +import * as root from '../src/index.js' +import * as mobile from '../src/mobile/index.js' +import * as provenance from '../src/provenance/index.js' +import * as provider from '../src/providers/index.js' + +type HasMeasurementBuilder = + 'buildProviderMeasurementBundle' extends keyof T ? true : false +type HasRecordingBuilder = + 'buildProviderRecordingBundle' extends keyof T ? true : false +type HasInternalPackageGraph = + 'groveFhirPackageGraph' extends keyof T ? true : false + +describe('public entry-point boundaries', () => { + it('keeps provider-specific APIs out of the source-neutral entry points', () => { + expect('buildProviderMeasurementBundle' in root).toBe(false) + expect('buildProviderRecordingBundle' in root).toBe(false) + expect('buildProviderMeasurementBundle' in mobile).toBe(false) + expect('buildProviderRecordingBundle' in mobile).toBe(false) + expect(JSON.stringify(mobile.sharedMobileMeasurementCatalog)).not.toMatch( + /healthkit|health-connect|sensorkit|google-health-api|oura|withings|sourceTokens/u, + ) + expect('adapterMeasurementCatalog' in root).toBe(false) + expect('adapterMeasurementCatalog' in mobile).toBe(false) + expect('PROFILES' in provenance).toBe(false) + expect('SYSTEMS' in provenance).toBe(false) + + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf< + HasMeasurementBuilder + >().toEqualTypeOf() + expectTypeOf< + HasRecordingBuilder + >().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf< + HasInternalPackageGraph + >().toEqualTypeOf() + expectTypeOf< + HasInternalPackageGraph + >().toEqualTypeOf() + }) + + it('exposes the closed provider facade only from Provider', () => { + expect(typeof provider.buildProviderMeasurementBundle).toBe('function') + expect(typeof provider.buildProviderRecordingBundle).toBe('function') + + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('exposes bounded generated version and package metadata', () => { + expect(root.groveFhirVersion).toBe('4.0.1') + expect(root.groveFhirContractVersion).toBe('0.5.0') + expect(mobile.groveMobilePackageMetadata.packageId).toBe( + 'org.grovealliance.fhir.mobile', + ) + expect(provider.groveProviderPackageMetadata.packageId).toBe( + 'org.grovealliance.fhir.providers', + ) + + expectTypeOf(root.groveFhirVersion).toEqualTypeOf<'4.0.1'>() + expectTypeOf(root.groveFhirContractVersion).toEqualTypeOf<'0.5.0'>() + }) + + it('exposes owner-exclusive measurements only from the Provider contract', () => { + expect(Object.keys(provider.adapterMeasurementCatalog)).toEqual([ + 'healthkit', + 'health-connect', + 'providers', + ]) + const standHour = + provider.adapterMeasurementCatalog.healthkit['apple-stand-hour'] + expect(standHour.owner).toBe('healthkit') + expect(standHour.allowedValues).toEqual(['stood', 'idle']) + expect(standHour).not.toHaveProperty('coverage') + expect(standHour).not.toHaveProperty('generation') + expect(Object.isFrozen(provider.adapterMeasurementCatalog)).toBe(true) + expect(Object.isFrozen(standHour)).toBe(true) + }) + + it('deeply freezes every generated contract read by public builders', () => { + const heartRate = mobile.sharedMobileMeasurementCatalog['heart-rate'] + expect(Object.isFrozen(mobile.sharedMobileMeasurementCatalog)).toBe(true) + expect(Object.isFrozen(heartRate)).toBe(true) + expect(Object.isFrozen(heartRate.code)).toBe(true) + expect(Object.isFrozen(provider.providerScalarMappings)).toBe(true) + expect(provider.providerRecordEffectiveRules.oura.daily_activity.kind).toBe( + 'complete-civil-day-period', + ) + expect(Object.isFrozen(provider.providerRecordEffectiveRules)).toBe(true) + expect( + Object.isFrozen(provider.groveProviderPackageMetadata.dependencies), + ).toBe(true) + expect(Reflect.set(heartRate.code, 'code', 'mutated-clinical-code')).toBe( + false, + ) + expect(heartRate.code.code).toBe('8867-4') + }) +}) diff --git a/packages/fhir/test/extract-coverage.test.ts b/packages/fhir/test/extract-coverage.test.ts new file mode 100644 index 00000000..fa6c7b3a --- /dev/null +++ b/packages/fhir/test/extract-coverage.test.ts @@ -0,0 +1,273 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + appointmentServiceTypeDisplay, + boundsToFhirPeriod, + carePlanCreatedDate, + carePlanNoteTexts, + carePlanPeriodEnd, + carePlanPeriodStart, + codeableConceptDisplays, + codingToCodeableConcept, + conditionCategoryDisplays, + conditionClinicalStatus, + conditionCodeDisplay, + conditionRecordedDate, + contactPointBySystem, + contactPointsBySystem, + containedResource, + containedResourcesByType, + dateToFhirDate, + dateToFhirInstant, + encodeBase64Binary, + extensionByUrl, + extensionsByUrl, + formatHumanName, + humanNameParts, + identifiersByType, + immunizationNoteTexts, + immunizationOccurrenceDate, + immunizationRecordedDate, + immunizationRouteDisplay, + immunizationSiteDisplay, + observationBooleanValue, + observationCodeDisplay, + observationEffectiveEnd, + observationHasCategory, + observationInterpretationDisplays, + observationIssuedDate, + observationQuantity, + observationStringValue, + observationValueDisplay, + patientEmailAddresses, + patientMaritalStatusDisplay, + patientName, + patientPhoneNumbers, + periodDuration, + periodOverlaps, + referenceType, + scheduledEnd, + scheduledIsUpcoming, + slotSpecialtyDisplays, + toReference, + valueToFhirQuantity, +} from '../src/extract/index.js' + +/** + * The branches the behavioural tests do not reach. + * + * Every accessor takes `unknown`, so each has a guard for input that is not an object and for a + * field the resource does not state. Those guards are the difference between reporting absence + * and throwing on a resource a caller has not narrowed yet, so they are worth holding closed. + */ +describe('Guards against unnarrowed input', () => { + const notResources = [undefined, null, 42, 'text', []] as const + + it('reports absence rather than throwing on anything that is not a resource', () => { + for (const value of notResources) { + expect(observationCodeDisplay(value)).toBeUndefined() + expect(observationQuantity(value)).toBeUndefined() + expect(observationStringValue(value)).toBeUndefined() + expect(observationBooleanValue(value)).toBeUndefined() + expect(observationValueDisplay(value)).toBeUndefined() + expect(observationIssuedDate(value)).toBeUndefined() + expect(observationEffectiveEnd(value)).toBeUndefined() + expect(observationInterpretationDisplays(value)).toEqual([]) + expect(conditionClinicalStatus(value)).toBeUndefined() + expect(conditionCodeDisplay(value)).toBeUndefined() + expect(conditionRecordedDate(value)).toBeUndefined() + expect(conditionCategoryDisplays(value)).toEqual([]) + expect(immunizationSiteDisplay(value)).toBeUndefined() + expect(immunizationRouteDisplay(value)).toBeUndefined() + expect(immunizationOccurrenceDate(value)).toBeUndefined() + expect(immunizationRecordedDate(value)).toBeUndefined() + expect(immunizationNoteTexts(value)).toEqual([]) + expect(carePlanCreatedDate(value)).toBeUndefined() + expect(carePlanPeriodStart(value)).toBeUndefined() + expect(carePlanPeriodEnd(value)).toBeUndefined() + expect(carePlanNoteTexts(value)).toEqual([]) + expect(patientName(value)).toBeUndefined() + expect(patientPhoneNumbers(value)).toEqual([]) + expect(patientEmailAddresses(value)).toEqual([]) + expect(patientMaritalStatusDisplay(value)).toBeUndefined() + expect(slotSpecialtyDisplays(value)).toEqual([]) + expect(appointmentServiceTypeDisplay(value)).toBeUndefined() + expect(codeableConceptDisplays(value)).toEqual([]) + expect(extensionsByUrl(value, 'http://example.org')).toEqual([]) + expect(containedResourcesByType(value, 'Patient')).toEqual([]) + expect(identifiersByType(value, 'http://example.org', 'x')).toEqual([]) + expect(contactPointsBySystem(value, 'phone')).toEqual([]) + expect(humanNameParts(value)).toEqual([]) + expect(referenceType(value)).toBeUndefined() + expect(toReference(value).ok).toBe(false) + } + }) + + it('drops a date that does not parse rather than yielding an invalid one', () => { + expect(observationIssuedDate({ issued: 'not a date' })).toBeUndefined() + expect(carePlanCreatedDate({ created: '' })).toBeUndefined() + expect(periodDuration({ start: 'nope', end: 'also nope' })).toBeUndefined() + }) +}) + +describe('Accessors not exercised elsewhere', () => { + it('reads the remaining Observation fields', () => { + const observation = { + issued: '2026-08-20T17:00:00Z', + code: { text: 'Heart rate' }, + valueString: 'irregular', + interpretation: [{ text: 'High' }, {}], + effectivePeriod: { end: '2026-08-20T10:00:00Z' }, + } + expect(observationIssuedDate(observation)).toEqual( + new Date('2026-08-20T17:00:00Z'), + ) + expect(observationCodeDisplay(observation)).toBe('Heart rate') + expect(observationStringValue(observation)).toBe('irregular') + expect(observationInterpretationDisplays(observation)).toEqual(['High']) + expect(observationEffectiveEnd(observation)).toEqual( + new Date('2026-08-20T10:00:00Z'), + ) + expect(observationBooleanValue({ valueBoolean: false })).toBe(false) + expect( + observationValueDisplay({ valueCodeableConcept: { text: 'Positive' } }), + ).toBe('Positive') + expect(observationHasCategory({ category: 'not a list' }, 's', 'c')).toBe( + false, + ) + // A malformed Quantity is reported as absent, not as a number without its unit. + expect(observationQuantity({ valueQuantity: { value: 1 } })).toBeUndefined() + }) + + it('reads the remaining Condition and Immunization fields', () => { + expect(conditionRecordedDate({ recordedDate: '2026-01-01' })).toEqual( + new Date('2026-01-01T00:00:00Z'), + ) + expect(conditionCodeDisplay({ code: { text: 'Asthma' } })).toBe('Asthma') + expect(conditionCategoryDisplays({ category: 'not a list' })).toEqual([]) + expect( + immunizationOccurrenceDate({ + occurrenceDateTime: '2026-03-01T00:00:00Z', + }), + ).toEqual(new Date('2026-03-01T00:00:00Z')) + expect(immunizationRecordedDate({ recorded: '2026-03-02' })).toEqual( + new Date('2026-03-02T00:00:00Z'), + ) + expect(immunizationSiteDisplay({ site: { text: 'Left arm' } })).toBe( + 'Left arm', + ) + expect(immunizationRouteDisplay({ route: { text: 'Intramuscular' } })).toBe( + 'Intramuscular', + ) + expect( + immunizationNoteTexts({ note: [{ text: 'well tolerated' }] }), + ).toEqual(['well tolerated']) + }) + + it('reads the remaining CarePlan and scheduling fields', () => { + const plan = { + created: '2026-01-01T00:00:00Z', + period: { start: '2026-01-01T00:00:00Z', end: '2026-12-31T00:00:00Z' }, + note: [{ text: 'reviewed' }], + } + expect(carePlanCreatedDate(plan)).toEqual(new Date('2026-01-01T00:00:00Z')) + expect(carePlanPeriodStart(plan)).toEqual(new Date('2026-01-01T00:00:00Z')) + expect(carePlanPeriodEnd(plan)).toEqual(new Date('2026-12-31T00:00:00Z')) + expect(carePlanNoteTexts(plan)).toEqual(['reviewed']) + expect( + appointmentServiceTypeDisplay({ + serviceType: [{}, { text: 'Dermatology' }], + }), + ).toBe('Dermatology') + expect( + appointmentServiceTypeDisplay({ serviceType: 'not a list' }), + ).toBeUndefined() + expect(slotSpecialtyDisplays({ specialty: 'not a list' })).toEqual([]) + expect(scheduledEnd({ end: '2026-08-20T10:00:00Z' })).toEqual( + new Date('2026-08-20T10:00:00Z'), + ) + expect(scheduledIsUpcoming({})).toBeUndefined() + }) + + it('reads the remaining Patient and person fields', () => { + const patient = { + name: [{}, { given: ['Ada'], family: 'Lovelace', suffix: ['PhD'] }], + maritalStatus: { text: 'Married' }, + telecom: [ + { system: 'phone', value: '+1-555-0100', use: 'home' }, + { system: 'phone', value: '+1-555-0199', use: 'work' }, + { system: 'email', value: 'ada@example.org' }, + ], + } + expect(patientName(patient)).toBe('Ada Lovelace') + expect(patientName(patient, { includeSuffix: true })).toBe( + 'Ada Lovelace PhD', + ) + expect(patientPhoneNumbers(patient)).toHaveLength(2) + expect(patientEmailAddresses(patient)).toEqual(['ada@example.org']) + expect(patientMaritalStatusDisplay(patient)).toBe('Married') + expect(contactPointBySystem(patient, 'phone', 'work')).toBe('+1-555-0199') + expect(contactPointBySystem(patient, 'phone', 'mobile')).toBeUndefined() + expect(contactPointBySystem(patient, 'fax')).toBeUndefined() + expect(patientName({ name: 'not a list' })).toBeUndefined() + expect(humanNameParts({ given: 'not a list' })).toEqual([]) + expect(formatHumanName({})).toBeUndefined() + }) + + it('reads the remaining resource helpers', () => { + expect(extensionByUrl({ extension: 'not a list' }, 'x')).toBeUndefined() + expect(containedResource({ contained: 'not a list' }, '#a')).toBeUndefined() + expect(containedResource({}, '#')).toBeUndefined() + expect(containedResource({}, 'no-hash')).toBeUndefined() + expect(referenceType({ reference: 42 })).toBeUndefined() + expect(referenceType({ type: '', reference: 'Patient/1' })).toBe('Patient') + expect(toReference({ resourceType: 'Patient', id: '' }).ok).toBe(false) + expect( + identifiersByType({ identifier: [{ value: 'x' }] }, 's', 'c'), + ).toEqual([]) + }) +}) + +describe('Encoders on their edges', () => { + it('writes a period that states only an end', () => { + const end = new Date('2026-08-20T10:00:00Z') + expect(boundsToFhirPeriod({ end })).toEqual({ end: end.toISOString() }) + expect(boundsToFhirPeriod({})).toEqual({}) + }) + + it('carries a comparator through a Quantity', () => { + const written = valueToFhirQuantity({ + value: 5, + unit: 'mg', + comparator: '<', + }) + expect(written.ok && written.value.comparator).toBe('<') + expect(written.ok && written.value).not.toHaveProperty('system') + }) + + it('writes the calendar day in UTC rather than the host zone', () => { + const instant = new Date('2026-08-20T23:30:00Z') + expect(dateToFhirDate(instant)).toBe('2026-08-20') + expect(dateToFhirInstant(instant)).toBe('2026-08-20T23:30:00.000Z') + }) + + it('writes a concept without a text when no display is given', () => { + expect( + codingToCodeableConcept({ system: 'http://loinc.org', code: '1' }), + ).toEqual({ coding: [{ system: 'http://loinc.org', code: '1' }] }) + }) + + it('encodes empty bytes', () => { + expect(encodeBase64Binary(new Uint8Array())).toBe('') + }) + + it('overlaps a period that states neither end', () => { + expect(periodOverlaps({}, new Date(0), new Date())).toBe(true) + }) +}) diff --git a/packages/fhir/test/extract-resources.test.ts b/packages/fhir/test/extract-resources.test.ts new file mode 100644 index 00000000..4a58d27f --- /dev/null +++ b/packages/fhir/test/extract-resources.test.ts @@ -0,0 +1,268 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { fhirDateTimeToDate } from '../src/core/primitives.js' +import { fhirQuantityToValue } from '../src/core/quantity.js' +import { + codeableConceptDisplay, + containsCoding, +} from '../src/extract/coding.js' +import { + boundsToFhirPeriod, + carePlanCategoryDisplays, + carePlanPeriodIsActive, + codingToCodeableConcept, + conditionAbatementDate, + conditionDuration, + conditionIsActive, + conditionIsConfirmed, + conditionOnsetDate, + dateToFhirDateTime, + decodeBase64Binary, + encodeBase64Binary, + fhirPeriodToBounds, + immunizationExpirationDate, + immunizationIsExpired, + immunizationReactionDisplays, + immunizationVaccineDisplay, + scheduledCovers, + scheduledDuration, + scheduledIsPast, + scheduledIsUpcoming, + slotSpecialtyDisplays, + toFhirReference, + valueToFhirQuantity, +} from '../src/extract/index.js' +import { referenceType } from '../src/extract/resource.js' + +const CLINICAL = 'http://terminology.hl7.org/CodeSystem/condition-clinical' +const VERIFICATION = + 'http://terminology.hl7.org/CodeSystem/condition-ver-status' + +describe('Condition extraction', () => { + const condition = { + resourceType: 'Condition', + clinicalStatus: { coding: [{ system: CLINICAL, code: 'active' }] }, + verificationStatus: { + coding: [{ system: VERIFICATION, code: 'confirmed' }], + }, + onsetDateTime: '2020-01-01T00:00:00Z', + } + + it('lets the clinical status decide, not the absence of an abatement', () => { + expect(conditionIsActive(condition)).toBe(true) + // The defect the old accessor had: coded resolved, but no abatement recorded. + expect( + conditionIsActive({ + clinicalStatus: { coding: [{ system: CLINICAL, code: 'resolved' }] }, + }), + ).toBe(false) + expect( + conditionIsActive({ + clinicalStatus: { coding: [{ system: CLINICAL, code: 'remission' }] }, + }), + ).toBe(false) + expect( + conditionIsActive({ + clinicalStatus: { coding: [{ system: CLINICAL, code: 'relapse' }] }, + }), + ).toBe(true) + }) + + it('falls back to abatement only when no status is stated', () => { + expect(conditionIsActive({ onsetDateTime: '2020-01-01T00:00:00Z' })).toBe( + true, + ) + expect( + conditionIsActive({ abatementDateTime: '2021-01-01T00:00:00Z' }), + ).toBe(false) + expect(conditionIsActive({ abatementString: 'last spring' })).toBe(false) + }) + + it('reads onset and abatement from either a dateTime or a period', () => { + expect(conditionOnsetDate(condition)).toEqual( + new Date('2020-01-01T00:00:00Z'), + ) + expect( + conditionOnsetDate({ onsetPeriod: { start: '2019-06-01T00:00:00Z' } }), + ).toEqual(new Date('2019-06-01T00:00:00Z')) + expect(conditionAbatementDate(condition)).toBeUndefined() + }) + + it('reports a duration only when both ends are known', () => { + expect(conditionDuration(condition)).toBeUndefined() + expect( + conditionDuration({ + onsetDateTime: '2020-01-01T00:00:00Z', + abatementDateTime: '2020-01-02T00:00:00Z', + }), + ).toBe(86_400_000) + }) + + it('checks the verification status within its own system', () => { + expect(conditionIsConfirmed(condition)).toBe(true) + expect( + conditionIsConfirmed({ + verificationStatus: { + coding: [{ system: 'http://example.org', code: 'confirmed' }], + }, + }), + ).toBe(false) + }) +}) + +describe('Immunization extraction', () => { + const immunization = { + resourceType: 'Immunization', + vaccineCode: { text: 'Influenza' }, + expirationDate: '2026-01-01', + reaction: [{ detail: { text: 'soreness' } }, { detail: {} }], + } + + it('names the vaccine and its reactions', () => { + expect(immunizationVaccineDisplay(immunization)).toBe('Influenza') + expect(immunizationReactionDisplays(immunization)).toEqual(['soreness']) + }) + + it('separates "not known expired" from "known not expired"', () => { + expect(immunizationExpirationDate(immunization)).toEqual( + new Date('2026-01-01T00:00:00Z'), + ) + expect( + immunizationIsExpired(immunization, new Date('2026-06-01T00:00:00Z')), + ).toBe(true) + expect( + immunizationIsExpired(immunization, new Date('2025-06-01T00:00:00Z')), + ).toBe(false) + expect(immunizationIsExpired({})).toBeUndefined() + }) +}) + +describe('Scheduling extraction', () => { + const appointment = { + resourceType: 'Appointment', + start: '2026-08-20T09:00:00Z', + end: '2026-08-20T10:00:00Z', + } + + it('measures the span and places it in time', () => { + expect(scheduledDuration(appointment)).toBe(3_600_000) + expect(scheduledCovers(appointment, new Date('2026-08-20T09:30:00Z'))).toBe( + true, + ) + expect(scheduledCovers(appointment, new Date('2026-08-20T11:00:00Z'))).toBe( + false, + ) + }) + + it('does not call an appointment under way past', () => { + const during = new Date('2026-08-20T09:30:00Z') + expect(scheduledIsPast(appointment, during)).toBe(false) + expect(scheduledIsUpcoming(appointment, during)).toBe(false) + expect(scheduledIsPast(appointment, new Date('2026-08-20T10:00:01Z'))).toBe( + true, + ) + expect(scheduledIsPast({})).toBeUndefined() + }) + + it("names a slot's specialties", () => { + expect( + slotSpecialtyDisplays({ specialty: [{ text: 'Cardiology' }, {}] }), + ).toEqual(['Cardiology']) + }) +}) + +describe('CarePlan extraction', () => { + it('treats a plan with no period as covering any instant', () => { + expect(carePlanPeriodIsActive({})).toBe(true) + expect( + carePlanPeriodIsActive( + { period: { end: '2020-01-01T00:00:00Z' } }, + new Date('2026-01-01T00:00:00Z'), + ), + ).toBe(false) + }) + + it('names its categories', () => { + expect( + carePlanCategoryDisplays({ category: [{ text: 'Assessment' }] }), + ).toEqual(['Assessment']) + }) +}) + +describe('Round trips', () => { + it('carries an instant out and back unchanged', () => { + const instant = new Date('2026-08-20T09:30:00.250Z') + const written = dateToFhirDateTime(instant) + const read = fhirDateTimeToDate(written) + expect(read.ok && read.value).toEqual(instant) + }) + + it('carries a measurement out and back unchanged', () => { + const original = { + value: 72.5, + unit: 'kg', + system: 'http://unitsofmeasure.org', + } + const written = valueToFhirQuantity(original) + expect(written.ok).toBe(true) + const read = written.ok ? fhirQuantityToValue(written.value) : undefined + expect(read?.ok === true && read.value).toEqual(original) + }) + + it('refuses to write a quantity that carries no measurement', () => { + expect(valueToFhirQuantity({ value: Number.NaN, unit: 'kg' }).ok).toBe( + false, + ) + expect(valueToFhirQuantity({ value: 1, unit: '' }).ok).toBe(false) + }) + + it('carries a period out and back, leaving an open end open', () => { + const bounds = { + start: new Date('2026-08-20T09:00:00Z'), + end: new Date('2026-08-20T10:00:00Z'), + } + expect(fhirPeriodToBounds(boundsToFhirPeriod(bounds))).toEqual(bounds) + const open = { start: new Date('2026-08-20T09:00:00Z') } + expect(boundsToFhirPeriod(open)).not.toHaveProperty('end') + expect(fhirPeriodToBounds(boundsToFhirPeriod(open))).toEqual(open) + }) + + it('carries a coding out and back', () => { + const coding = { + system: 'http://loinc.org', + code: '29463-7', + display: 'Body weight', + } + const concept = codingToCodeableConcept(coding) + expect(codeableConceptDisplay(concept)).toBe('Body weight') + expect(containsCoding(concept, coding.system, coding.code)).toBe(true) + }) + + it('does not invent a text for a coding that states no display', () => { + const concept = codingToCodeableConcept({ + system: 'http://loinc.org', + code: '1234-5', + }) + expect(concept).not.toHaveProperty('text') + expect(codeableConceptDisplay(concept)).toBeUndefined() + }) + + it('carries a reference out and back', () => { + const reference = toFhirReference('Patient', 'p1') + expect(reference.reference).toBe('Patient/p1') + expect(referenceType(reference)).toBe('Patient') + }) + + it('carries bytes out and back unchanged', () => { + const bytes = new Uint8Array([0, 1, 250, 255, 65]) + const written = encodeBase64Binary(bytes) + const read = decodeBase64Binary(written) + expect(read.ok && Array.from(read.value)).toEqual(Array.from(bytes)) + }) +}) diff --git a/packages/fhir/test/extract.test.ts b/packages/fhir/test/extract.test.ts new file mode 100644 index 00000000..69688b82 --- /dev/null +++ b/packages/fhir/test/extract.test.ts @@ -0,0 +1,358 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + codeableConceptDisplay, + codeableConceptDisplays, + codesBySystem, + containedResource, + containedResourcesByType, + containsCoding, + decodeBase64Binary, + extensionByUrl, + extensionsByUrl, + formatHumanName, + humanNameParts, + identifierBySystem, + identifierByType, + identifiersBySystem, + isReferenceToType, + observationEffectiveDate, + observationEffectiveOverlaps, + observationHasCategory, + observationNoteTexts, + observationNumericValue, + observationUnit, + patientAgeInYears, + patientBirthDate, + patientIsDeceased, + patientName, + periodDuration, + periodIsActive, + periodOverlaps, + referenceType, + toReference, +} from '../src/extract/index.js' + +const LOINC = 'http://loinc.org' + +describe('CodeableConcept extraction', () => { + const concept = { + coding: [ + { system: LOINC, code: '29463-7', display: 'Body weight' }, + { system: 'http://snomed.info/sct', code: '27113001' }, + ], + text: 'Weight', + } + + it('prefers the text the source chose over the terminology label', () => { + expect(codeableConceptDisplay(concept)).toBe('Weight') + expect(codeableConceptDisplay({ coding: concept.coding })).toBe( + 'Body weight', + ) + }) + + it('reports no display rather than falling back to the bare code', () => { + expect( + codeableConceptDisplay({ coding: [{ system: LOINC, code: '1234-5' }] }), + ).toBeUndefined() + expect(codeableConceptDisplay(undefined)).toBeUndefined() + }) + + it('lists displays and selects codes by system', () => { + expect(codeableConceptDisplays(concept)).toEqual(['Body weight']) + expect(codesBySystem(concept, LOINC)).toEqual(['29463-7']) + expect(codesBySystem(concept, 'http://example.org')).toEqual([]) + }) + + it('matches a coding only when the system matches too', () => { + expect(containsCoding(concept, LOINC, '29463-7')).toBe(true) + expect(containsCoding(concept, 'http://example.org', '29463-7')).toBe(false) + }) +}) + +describe('Period extraction', () => { + const period = { start: '2026-08-20T09:00:00Z', end: '2026-08-20T10:00:00Z' } + + it('treats an absent period as unbounded rather than as never', () => { + expect(periodIsActive(undefined)).toBe(true) + expect(periodOverlaps(undefined, new Date(0), new Date())).toBe(false) + }) + + it('reads both ends inclusively', () => { + expect(periodIsActive(period, new Date('2026-08-20T09:00:00Z'))).toBe(true) + expect(periodIsActive(period, new Date('2026-08-20T10:00:00Z'))).toBe(true) + expect(periodIsActive(period, new Date('2026-08-20T10:00:01Z'))).toBe(false) + }) + + it('leaves an open end open instead of defaulting it to now', () => { + const open = { start: '2020-01-01T00:00:00Z' } + expect(periodIsActive(open, new Date('2099-01-01T00:00:00Z'))).toBe(true) + expect(periodDuration(open)).toBeUndefined() + expect(periodDuration(period)).toBe(3_600_000) + }) + + it('overlaps a range that touches only at an edge', () => { + expect( + periodOverlaps( + period, + new Date('2026-08-20T10:00:00Z'), + new Date('2026-08-21T00:00:00Z'), + ), + ).toBe(true) + }) +}) + +describe('Observation extraction', () => { + const observation = { + resourceType: 'Observation', + status: 'final', + code: { + coding: [{ system: LOINC, code: '29463-7', display: 'Body weight' }], + }, + category: [ + { + coding: [ + { + system: + 'http://terminology.hl7.org/CodeSystem/observation-category', + code: 'vital-signs', + }, + ], + }, + ], + effectiveDateTime: '2026-08-20T09:30:00Z', + valueQuantity: { + value: 72.5, + unit: 'kg', + code: 'kg', + system: 'http://unitsofmeasure.org', + }, + note: [ + { text: 'measured after breakfast' }, + { authorString: 'no text here' }, + ], + } + + it('reads the effective instant as a Date', () => { + expect(observationEffectiveDate(observation)).toEqual( + new Date('2026-08-20T09:30:00Z'), + ) + }) + + it('falls back to the start of an effective period', () => { + const period = { + effectivePeriod: { + start: '2026-08-20T09:00:00Z', + end: '2026-08-20T10:00:00Z', + }, + } + expect(observationEffectiveDate(period)).toEqual( + new Date('2026-08-20T09:00:00Z'), + ) + expect(observationEffectiveDate({})).toBeUndefined() + }) + + it('reads the quantity as a value and a comparable unit', () => { + expect(observationNumericValue(observation)).toBe(72.5) + expect(observationUnit(observation)).toBe('kg') + expect( + observationNumericValue({ valueString: 'not a number' }), + ).toBeUndefined() + }) + + it('matches a category only within its system', () => { + expect( + observationHasCategory( + observation, + 'http://terminology.hl7.org/CodeSystem/observation-category', + 'vital-signs', + ), + ).toBe(true) + expect( + observationHasCategory(observation, 'http://example.org', 'vital-signs'), + ).toBe(false) + }) + + it('takes only the notes that carry text', () => { + expect(observationNoteTexts(observation)).toEqual([ + 'measured after breakfast', + ]) + }) + + it('reports whether the effective instant falls in a range', () => { + expect( + observationEffectiveOverlaps( + observation, + new Date('2026-08-20T00:00:00Z'), + new Date('2026-08-21T00:00:00Z'), + ), + ).toBe(true) + expect( + observationEffectiveOverlaps( + observation, + new Date('2026-08-21T00:00:00Z'), + new Date('2026-08-22T00:00:00Z'), + ), + ).toBe(false) + }) +}) + +describe('Patient extraction', () => { + const patient = { + resourceType: 'Patient', + id: 'p1', + name: [{ given: ['Ada'], family: 'Lovelace', prefix: ['Ms'] }], + birthDate: '1985-04-12', + telecom: [ + { system: 'phone', value: '+1-555-0100', use: 'home' }, + { system: 'email', value: 'ada@example.org' }, + ], + } + + it('reads the birth date as the UTC start of the day', () => { + expect(patientBirthDate(patient)).toEqual(new Date('1985-04-12T00:00:00Z')) + }) + + it('computes age in UTC so the birthday does not shift by a day', () => { + expect(patientAgeInYears(patient, new Date('2026-04-11T23:59:59Z'))).toBe( + 40, + ) + expect(patientAgeInYears(patient, new Date('2026-04-12T00:00:00Z'))).toBe( + 41, + ) + expect(patientAgeInYears({}, new Date())).toBeUndefined() + }) + + it('stops ageing a patient at their recorded death', () => { + const deceased = { ...patient, deceasedDateTime: '2005-04-12T00:00:00Z' } + expect(patientAgeInYears(deceased, new Date('2026-01-01T00:00:00Z'))).toBe( + 20, + ) + expect(patientIsDeceased(deceased)).toBe(true) + expect(patientIsDeceased(patient)).toBe(false) + expect(patientIsDeceased({ deceasedBoolean: true })).toBe(true) + }) + + it('formats a name, including the prefix only when asked', () => { + expect(patientName(patient)).toBe('Ada Lovelace') + expect(patientName(patient, { includePrefix: true })).toBe( + 'Ms Ada Lovelace', + ) + expect(humanNameParts({ text: 'Family Given' })).toEqual(['Family Given']) + expect(formatHumanName(undefined)).toBeUndefined() + }) +}) + +describe('Identifier extraction', () => { + const resource = { + identifier: [ + { system: 'http://example.org/mrn', value: '123' }, + { system: 'http://example.org/mrn', value: '456' }, + { + system: 'http://example.org/ssn', + value: '789', + type: { + coding: [ + { + system: 'http://terminology.hl7.org/CodeSystem/v2-0203', + code: 'SS', + }, + ], + }, + }, + ], + } + + it('selects by system and by type', () => { + expect(identifiersBySystem(resource, 'http://example.org/mrn')).toEqual([ + '123', + '456', + ]) + expect(identifierBySystem(resource, 'http://example.org/mrn')).toBe('123') + expect( + identifierByType( + resource, + 'http://terminology.hl7.org/CodeSystem/v2-0203', + 'SS', + ), + ).toBe('789') + }) + + it('accepts a resource whose identifier is a single object', () => { + expect( + identifierBySystem({ identifier: { system: 'x', value: 'one' } }, 'x'), + ).toBe('one') + }) +}) + +describe('Resource helpers', () => { + const resource = { + resourceType: 'Observation', + id: 'obs1', + extension: [ + { url: 'http://example.org/a', valueString: 'first' }, + { url: 'http://example.org/a', valueString: 'second' }, + { url: 'http://example.org/b', valueBoolean: true }, + ], + contained: [ + { resourceType: 'Patient', id: 'inner' }, + { resourceType: 'Device', id: 'dev' }, + ], + } + + it('selects extensions by url', () => { + expect(extensionsByUrl(resource, 'http://example.org/a')).toHaveLength(2) + expect(extensionByUrl(resource, 'http://example.org/b')).toEqual({ + url: 'http://example.org/b', + valueBoolean: true, + }) + expect(extensionsByUrl(resource, 'http://example.org/missing')).toEqual([]) + }) + + it('resolves a contained resource only through a local reference', () => { + expect(containedResource(resource, '#inner')).toEqual({ + resourceType: 'Patient', + id: 'inner', + }) + expect(containedResource(resource, 'Patient/inner')).toBeUndefined() + expect(containedResourcesByType(resource, 'Device')).toHaveLength(1) + }) + + it('reads a reference type from the literal when none is stated', () => { + expect(referenceType({ reference: 'Patient/123' })).toBe('Patient') + expect( + referenceType({ + reference: 'https://ex.org/fhir/Observation/9/_history/2', + }), + ).toBe('Observation') + expect(referenceType({ type: 'Device', reference: 'urn:uuid:abc' })).toBe( + 'Device', + ) + expect(referenceType({ reference: 'urn:uuid:abc' })).toBeUndefined() + expect(isReferenceToType({ reference: 'Patient/123' }, 'Patient')).toBe( + true, + ) + }) + + it('refuses to build a reference to a resource with no id', () => { + expect(toReference(resource)).toEqual({ + ok: true, + value: 'Observation/obs1', + }) + expect(toReference({ resourceType: 'Observation' }).ok).toBe(false) + }) + + it('decodes base64 across whitespace and rejects malformed input', () => { + const decoded = decodeBase64Binary('QUJD RA==') + expect(decoded.ok).toBe(true) + expect(decoded.ok && Array.from(decoded.value)).toEqual([65, 66, 67, 68]) + expect(decodeBase64Binary('ABC').ok).toBe(false) + expect(decodeBase64Binary(42).ok).toBe(false) + }) +}) diff --git a/packages/fhir/test/foundation-boundaries.test.ts b/packages/fhir/test/foundation-boundaries.test.ts new file mode 100644 index 00000000..72cd10b5 --- /dev/null +++ b/packages/fhir/test/foundation-boundaries.test.ts @@ -0,0 +1,369 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + compareFhirInstants, + parseAbsoluteUri, + parseCanonical, + parseFhirId, + parseFhirInstant, + parsePatientReference, + parsePositiveInteger, + parseResearchStudyReference, + parseSemVer, +} from '../src/core/index.js' +import { + extensionSchema, + observationComponentSchema, + observationSchema, + periodSchema, + provenanceSchema, + referenceSchema, + sampledDataSchema, +} from '../src/r4/index.js' + +const concept = { text: 'test' } as const + +describe('foundation primitive boundaries', () => { + it.each([ + '0.0.0', + '1.2.3', + '1.2.3-alpha', + '1.2.3-alpha.1', + '1.2.3+001', + '1.2.3-alpha.1+build.001', + ])('accepts valid Semantic Versioning value %s', (value) => { + expect(parseSemVer(value).ok).toBe(true) + }) + + it.each([ + 42, + '', + '1', + '1.2', + '01.2.3', + '1.02.3', + '1.2.03', + '1.2.3-', + '1.2.3+', + '1.2.3-alpha..1', + '1.2.3-01', + '1.2.3-alpha_1', + '1.2.3+build..1', + '1.2.3+build+second', + ])('rejects invalid Semantic Versioning value %p', (value) => { + expect(parseSemVer(value).ok).toBe(false) + }) + + it.each([1, 42, Number.MAX_SAFE_INTEGER])( + 'accepts positive safe integer %d', + (value) => { + expect(parsePositiveInteger(value).ok).toBe(true) + }, + ) + + it.each([0, -1, 1.5, Number.MAX_SAFE_INTEGER + 1, Number.NaN, '1'])( + 'rejects non-positive-safe-integer %p', + (value) => { + expect(parsePositiveInteger(value).ok).toBe(false) + }, + ) + + it.each([ + ['2024-02-29T23:59:60Z', true], + ['2000-02-29T00:00:00Z', true], + ['1900-02-29T00:00:00Z', false], + ['2026-04-31T00:00:00Z', false], + ['2026-13-01T00:00:00Z', false], + ['2026-00-01T00:00:00Z', false], + ['2026-01-00T00:00:00Z', false], + ['2026-01-01T24:00:00Z', false], + ['2026-01-01T00:60:00Z', false], + ['2026-01-01T00:00:61Z', false], + ['2026-01-01T00:00:00+14:00', true], + ['2026-01-01T00:00:00-14:00', true], + ['2026-01-01T00:00:00+15:00', false], + ['2026-01-01T00:00:00+01:60', false], + ] as const)('validates instant calendar boundary %s', (value, accepted) => { + expect(parseFhirInstant(value).ok).toBe(accepted) + }) + + it.each([ + ['2026-01-01T00:00:00Z', '2026-01-01T00:00:01Z', -1], + ['2026-01-01T00:00:01Z', '2026-01-01T00:00:00Z', 1], + ['2026-01-01T00:00:00Z', '2025-12-31T16:00:00-08:00', 0], + ['2026-01-01T00:00:00.1Z', '2026-01-01T00:00:00.09Z', 1], + ['2026-01-01T00:00:00.010Z', '2026-01-01T00:00:00.01Z', 0], + ['2026-01-01T00:00:00.001Z', '2026-01-01T00:00:00.01Z', -1], + ] as const)( + 'orders %s against %s without precision loss', + (left, right, expected) => { + expect(compareFhirInstants(left, right)).toEqual({ + ok: true, + value: expected, + }) + }, + ) + + it.each([ + [parseAbsoluteUri, 42], + [parseAbsoluteUri, 'https://example.org/white space'], + [parseCanonical, 'not absolute'], + [parseCanonical, 'https://example.org|1|2'], + [parseFhirId, 42], + [parseFhirInstant, 42], + ] as const)( + 'rejects malformed non-reference primitive %p', + (parser, value) => { + expect(parser(value).ok).toBe(false) + }, + ) + + it.each([parsePatientReference, parseResearchStudyReference] as const)( + 'enforces exact typed HTTP(S) resource URLs', + (parser) => { + const resourceType = + parser === parsePatientReference ? 'Patient' : 'ResearchStudy' + const base = `https://care.example/fhir/${resourceType}/example` + + expect(parser(`${resourceType}/example`).ok).toBe(true) + expect(parser(base).ok).toBe(true) + expect( + parser(`http://care.example/fhir/${resourceType}/example`).ok, + ).toBe(true) + expect(parser(`ftp://care.example/fhir/${resourceType}/example`).ok).toBe( + false, + ) + expect( + parser(`https://user@care.example/fhir/${resourceType}/example`).ok, + ).toBe(false) + expect( + parser(`https://user:secret@care.example/fhir/${resourceType}/example`) + .ok, + ).toBe(false) + expect(parser(`${base}?_format=json`).ok).toBe(false) + expect(parser(`${base}#section`).ok).toBe(false) + expect(parser(`${base}/_history/1`).ok).toBe(false) + expect(parser(`${base}/`).ok).toBe(false) + expect( + parser(` https://care.example/fhir/${resourceType}/example`).ok, + ).toBe(false) + }, + ) +}) + +describe('bounded R4 schema invariants', () => { + it('validates Period ordering while admitting either open endpoint', () => { + expect(periodSchema.safeParse({ start: '2026-01-01' }).success).toBe(true) + expect(periodSchema.safeParse({ end: '2026-01-02' }).success).toBe(true) + expect( + periodSchema.safeParse({ + start: '2026-01-01T00:00:00Z', + end: '2026-01-02T00:00:00Z', + }).success, + ).toBe(true) + expect( + periodSchema.safeParse({ + start: '2026-01-02T00:00:00Z', + end: '2026-01-01T00:00:00Z', + }).success, + ).toBe(false) + }) + + it.each([ + 'yesterday', + '2026-13-01', + '2026-01-32', + '2026-01-01T25:00:00Z', + '2026-01-01T00:00:00', + '01/02/2026', + ])('rejects %s, which is not a FHIR dateTime', (start) => { + expect(periodSchema.safeParse({ start }).success).toBe(false) + }) + + it('reports a malformed date as malformed rather than as being out of order', () => { + const result = periodSchema.safeParse({ + start: 'yesterday', + end: '2026-01-02', + }) + expect(result.success).toBe(false) + expect(result.error?.issues.map((entry) => entry.message)).toEqual([ + 'Expected a FHIR dateTime.', + ]) + }) + + it('requires a full instant where R4 says instant, not a bare date', () => { + expect( + observationSchema.safeParse({ + resourceType: 'Observation', + status: 'final', + code: concept, + issued: '2026-01-01', + }).success, + ).toBe(false) + }) + + it('orders a period whose endpoint carries the leap second R4 permits', () => { + // Date.parse returns NaN for :60, which once let an out-of-order period through. + expect( + periodSchema.safeParse({ + start: '2026-01-01T00:00:60Z', + end: '2020-01-01T00:00:00Z', + }).success, + ).toBe(false) + expect( + periodSchema.safeParse({ + start: '2020-01-01T00:00:60Z', + end: '2026-01-01T00:00:00Z', + }).success, + ).toBe(true) + }) + + it('types every temporal field, not only the ones an example happened to use', () => { + expect( + provenanceSchema.safeParse({ + resourceType: 'Provenance', + target: [{ reference: 'Observation/x' }], + recorded: '2026-01-01T00:00:00Z', + occurredDateTime: 'yesterday', + agent: [{ who: { reference: 'Device/x' } }], + }).success, + ).toBe(false) + }) + + it('permits nested extensions or one value but never both or two values', () => { + expect( + extensionSchema.safeParse({ + url: 'https://example.org/outer', + extension: [{ url: 'https://example.org/inner', valueString: 'value' }], + }).success, + ).toBe(true) + expect( + extensionSchema.safeParse({ + url: 'https://example.org/outer', + extension: [{ url: 'https://example.org/inner' }], + valueBoolean: true, + }).success, + ).toBe(false) + expect( + extensionSchema.safeParse({ + url: 'https://example.org/outer', + valueBoolean: true, + valueInteger: 1, + }).success, + ).toBe(false) + }) + + it('enforces component value and data-absent choice semantics', () => { + const component = { code: concept } + expect( + observationComponentSchema.safeParse({ + ...component, + valueSampledData: { + origin: { value: 0 }, + period: 1, + dimensions: 1, + data: '1 2 3', + }, + }).success, + ).toBe(true) + expect( + observationComponentSchema.safeParse({ + ...component, + valueBoolean: true, + valueInteger: 1, + }).success, + ).toBe(false) + expect( + observationComponentSchema.safeParse({ + ...component, + valueString: 'present', + dataAbsentReason: concept, + }).success, + ).toBe(false) + }) + + it('enforces Observation effective and result choices', () => { + const base = { + resourceType: 'Observation', + status: 'final', + code: concept, + } as const + + expect( + observationSchema.safeParse({ + ...base, + effectiveDateTime: '2026-01-01T00:00:00Z', + effectivePeriod: { start: '2026-01-01T00:00:00Z' }, + }).success, + ).toBe(false) + expect( + observationSchema.safeParse({ + ...base, + valueBoolean: true, + valueInteger: 1, + }).success, + ).toBe(false) + expect( + observationSchema.safeParse({ + ...base, + valueQuantity: { value: 1 }, + dataAbsentReason: concept, + }).success, + ).toBe(false) + }) + + it('validates sampled data, references, and populated Provenance entities', () => { + expect( + sampledDataSchema.safeParse({ + origin: { value: 0 }, + period: 1, + dimensions: 2, + data: '1 2 3 4', + }).success, + ).toBe(true) + expect( + referenceSchema.safeParse({ + // FHIR R4 Reference.type is a URI naming the resource definition. + type: 'http://hl7.org/fhir/StructureDefinition/Patient', + identifier: { + system: 'https://example.org/patients', + value: 'patient-1', + }, + }).success, + ).toBe(true) + expect( + referenceSchema.safeParse({ display: 'missing identity' }).success, + ).toBe(false) + + const provenance = { + resourceType: 'Provenance', + target: [{ reference: 'Observation/example' }], + occurredPeriod: { + start: '2026-01-01T00:00:00Z', + end: '2026-01-01T00:01:00Z', + }, + recorded: '2026-01-01T00:02:00Z', + agent: [{ who: { reference: 'Device/application' } }], + entity: [ + { + role: 'source', + what: { reference: 'DocumentReference/source' }, + agent: [{ who: { reference: 'Device/recorder' } }], + }, + ], + } as const + expect(provenanceSchema.safeParse(provenance).success).toBe(true) + expect( + provenanceSchema.safeParse({ + ...provenance, + occurredDateTime: '2026-01-01T00:00:00Z', + }).success, + ).toBe(false) + }) +}) diff --git a/packages/fhir/test/foundation.test.ts b/packages/fhir/test/foundation.test.ts new file mode 100644 index 00000000..36d547bf --- /dev/null +++ b/packages/fhir/test/foundation.test.ts @@ -0,0 +1,438 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { expectTypeOf } from 'expect-type' +import { compareFhirInstants, deepFreeze } from '../src/core/index.js' +import { + collectResults, + err, + fhirDateTimeToDate, + fhirQuantityToValue, + mapResult, + ok, + parseAbsoluteUri, + parseCanonical, + parseCollectionBundle, + parseDevice, + parseDocumentReference, + parseFhirId, + parseFhirInstant, + parseObservation, + parsePatientReference, + parseProvenance, + parseResearchStudyReference, + parseSpecimen, + parseSupportedR4Resource, + parseUrnUuid, + type CollectionBundle, + type DocumentReference, + type Observation, + type QuantityValue, + type Result, +} from '../src/index.js' + +const observation = { + resourceType: 'Observation', + meta: { + profile: [ + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-observation', + ], + }, + identifier: [ + { system: 'https://example.org/measurements', value: 'heart-rate-1' }, + ], + status: 'final', + code: { + // FHIR R4 fixes the canonical LOINC system to this HTTP URI. + // eslint-disable-next-line sonarjs/no-clear-text-protocols + coding: [{ system: 'http://loinc.org', code: '8867-4' }], + }, + subject: { reference: 'Patient/example' }, + effectiveDateTime: '2026-08-20T12:00:00Z', + valueQuantity: { + value: 64, + unit: 'beats/minute', + system: 'http://unitsofmeasure.org', + code: '/min', + }, +} as const + +describe('deepFreeze', () => { + it('freezes nested structures', () => { + const value = deepFreeze({ outer: { inner: [1, 2] } }) + expect(Object.isFrozen(value)).toBe(true) + expect(Object.isFrozen(value.outer)).toBe(true) + expect(Object.isFrozen(value.outer.inner)).toBe(true) + }) + + it('terminates on a self-referential object', () => { + // Freezing before descending is what makes this terminate; freezing afterwards recurses + // until the stack runs out. + const cyclic: { name: string; self?: unknown } = { name: 'root' } + cyclic.self = cyclic + expect(() => deepFreeze(cyclic)).not.toThrow() + expect(Object.isFrozen(cyclic)).toBe(true) + }) + + it('terminates on a cycle through an array', () => { + const parent: { children?: unknown } = {} + parent.children = [{ parent }] + expect(() => deepFreeze(parent)).not.toThrow() + expect(Object.isFrozen(parent)).toBe(true) + }) +}) + +describe('R4 foundation', () => { + it('accepts the bounded R4 Observation without modifying admitted data', () => { + const result = parseObservation(observation) + expect(result.ok).toBe(true) + if (!result.ok) return + + expect(result.value).toEqual(observation) + expect(Object.isFrozen(result.value)).toBe(true) + expect(Object.isFrozen(result.value.meta)).toBe(true) + expectTypeOf(result).toExtend>() + }) + + it('rejects unknown fields instead of silently stripping them', () => { + const result = parseObservation({ + ...observation, + vendorPayload: { secret: true }, + }) + expect(result.ok).toBe(false) + if (result.ok) return + + expect(result.issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ code: 'schema-invalid' }), + ]), + ) + }) + + it('rejects conflicting FHIR choice elements', () => { + const result = parseObservation({ ...observation, valueInteger: 64 }) + expect(result.ok).toBe(false) + }) + + it('enforces Extension ext-1 at the bounded R4 boundary', () => { + expect( + parseObservation({ + ...observation, + extension: [{ url: 'https://example.org/empty-extension' }], + }).ok, + ).toBe(false) + expect( + parseObservation({ + ...observation, + extension: [ + { + url: 'https://example.org/valued-extension', + valueString: 'present', + }, + ], + }).ok, + ).toBe(true) + }) + + it('accepts primitive metadata without discarding it', () => { + const input = { + ...observation, + _effectiveDateTime: { + extension: [ + { + url: 'https://example.org/timezone', + valueString: 'America/Los_Angeles', + }, + ], + }, + } + const result = parseObservation(input) + expect(result.ok && result.value._effectiveDateTime).toEqual( + input._effectiveDateTime, + ) + }) + + it('validates collection graphs and internal fullUrls', () => { + const bundle = { + resourceType: 'Bundle', + type: 'collection', + entry: [ + { + fullUrl: 'urn:uuid:94dbe2d8-04fd-4c1e-a4b1-d8b97e38ec54', + resource: observation, + }, + ], + } + const result = parseCollectionBundle(bundle) + expect(result.ok).toBe(true) + expectTypeOf(result).toExtend>() + }) + + it('parses every bounded graph resource and the supported-resource union', () => { + const device = { resourceType: 'Device', status: 'active' } + const specimen = { + resourceType: 'Specimen', + status: 'available', + identifier: [ + { system: 'https://example.org/specimens', value: 'specimen-1' }, + ], + type: { + // SNOMED CT's normative FHIR system is the HTTP URI. + // eslint-disable-next-line sonarjs/no-clear-text-protocols + coding: [{ system: 'http://snomed.info/sct', code: '119361006' }], + }, + subject: { reference: 'Patient/example' }, + } + const provenance = { + resourceType: 'Provenance', + target: [{ reference: 'Patient/example' }], + recorded: '2026-08-20T12:00:00Z', + agent: [{ who: { reference: 'Device/example' } }], + } + const documentReference = { + resourceType: 'DocumentReference', + identifier: [ + { system: 'https://example.org/recordings', value: 'recording-1' }, + ], + status: 'current', + type: { text: 'Provider-native recording' }, + subject: { reference: 'Patient/example' }, + date: '2026-08-20T12:00:00Z', + author: [{ reference: 'Device/example' }], + content: [ + { + attachment: { + contentType: 'application/octet-stream', + data: 'AQID', + size: 3, + hash: 'cDeAcZjCKn0rCAc3HXY3eahP388=', + title: 'Authorized minimized provider recording', + }, + }, + ], + } as const + expect(parseDevice(device).ok).toBe(true) + const parsedDocument = parseDocumentReference(documentReference) + expect(parsedDocument.ok).toBe(true) + expectTypeOf(parsedDocument).toExtend>() + expect( + parseDocumentReference({ + ...documentReference, + content: [ + { + attachment: { + ...documentReference.content[0].attachment, + size: 2_147_483_648, + }, + }, + ], + }).ok, + ).toBe(false) + expect(parseSpecimen(specimen).ok).toBe(true) + expect(parseProvenance(provenance).ok).toBe(true) + expect(parseSupportedR4Resource(device).ok).toBe(true) + expect(parseSupportedR4Resource({ resourceType: 'Patient' }).ok).toBe(true) + // Encounter is not a resource the guides publish, so the graph union must refuse it. + expect(parseSupportedR4Resource({ resourceType: 'Encounter' }).ok).toBe( + false, + ) + }) +}) + +describe('Result composition', () => { + it('maps success, preserves failure, and collects all issues', () => { + expect(mapResult(ok(2), (value) => value * 3)).toEqual({ + ok: true, + value: 6, + }) + const failure = err('invalid-type', 'wrong value', ['value']) + expect(mapResult(failure, () => 0)).toBe(failure) + expect(collectResults([ok(1), ok(2)])).toEqual({ + ok: true, + value: [1, 2], + }) + expect(collectResults([failure, err('invalid-uri', 'bad URI')])).toEqual({ + ok: false, + issues: [ + { + severity: 'error', + code: 'invalid-type', + path: ['value'], + message: 'wrong value', + }, + { + severity: 'error', + code: 'invalid-uri', + path: [], + message: 'bad URI', + }, + ], + }) + + const warning = { + severity: 'warning', + code: 'external-validation-required', + path: ['constraint'], + message: 'Surface this non-blocking constraint.', + } as const + expect(mapResult(ok(2, [warning]), (value) => value * 3)).toEqual({ + ok: true, + value: 6, + warnings: [warning], + }) + expect(collectResults([ok(1, [warning]), ok(2)])).toEqual({ + ok: true, + value: [1, 2], + warnings: [warning], + }) + }) +}) + +describe('validated primitives', () => { + it.each([ + [parseAbsoluteUri, 'https://example.org/system'], + [parseCanonical, 'https://example.org/Questionnaire/example|1.2.3'], + [parseFhirId, 'repository-assigned.1'], + [parseFhirInstant, '2026-08-20T12:00:00-07:00'], + [parsePatientReference, 'Patient/example'], + [parsePatientReference, 'https://care.example/fhir/Patient/example'], + [parseResearchStudyReference, 'ResearchStudy/example'], + [ + parseResearchStudyReference, + 'https://research.example/fhir/ResearchStudy/example', + ], + [parseUrnUuid, 'urn:uuid:94dbe2d8-04fd-4c1e-a4b1-d8b97e38ec54'], + ] as const)('accepts a valid branded value', (parser, value) => { + expect(parser(value).ok).toBe(true) + }) + + it.each([ + [parseAbsoluteUri, '/relative'], + [parseCanonical, 42], + [parseCanonical, 'https://example.org|'], + [parseFhirId, 'invalid/id'], + [parseFhirInstant, '2026-08-20T12:00:00'], + [parseFhirInstant, '2026-02-30T12:00:00Z'], + [parseFhirInstant, '0000-01-01T12:00:00Z'], + [parseFhirInstant, '2026-08-20T12:00:00+14:01'], + [parsePatientReference, 'Practitioner/example'], + [parsePatientReference, 'https://care.example/fhir/Patient/example/'], + [ + parsePatientReference, + 'https://care.example/fhir/Patient/example?_format=json', + ], + [ + parsePatientReference, + 'https://care.example/fhir/Patient/example#section', + ], + [ + parsePatientReference, + 'https://care.example/fhir/Patient/example/_history/2', + ], + [parseResearchStudyReference, 'fhir/ResearchStudy/example'], + [parseResearchStudyReference, 42], + [ + parseResearchStudyReference, + 'https://research.example/fhir/ResearchStudy/example?_format=json', + ], + [ + parseResearchStudyReference, + 'https://research.example/fhir/ResearchStudy/example/_history/2', + ], + [parseUrnUuid, 'urn:uuid:NOT-A-UUID'], + ] as const)('rejects an invalid branded value', (parser, value) => { + expect(parser(value).ok).toBe(false) + }) + + it('rejects an invalid instant comparison before ordering', () => { + expect(compareFhirInstants('not-an-instant', 42).ok).toBe(false) + }) +}) + +const dateOf = (value: unknown): string => { + const result = fhirDateTimeToDate(value) + expect(result.ok).toBe(true) + return result.ok ? result.value.toISOString() : '' +} + +describe('native accessors', () => { + it.each([ + ['2026-08-20T12:00:00Z', '2026-08-20T12:00:00.000Z'], + ['2026-08-20T12:00:00.123456Z', '2026-08-20T12:00:00.123Z'], + ['2026-08-20T14:30:00+02:30', '2026-08-20T12:00:00.000Z'], + // A leap second belongs to the second it names, which is the one that follows it. + ['2016-12-31T23:59:60Z', '2017-01-01T00:00:00.000Z'], + // Lower precision names a period; the accessor resolves it to that period's start. + ['2026-08-20', '2026-08-20T00:00:00.000Z'], + ['2026-08', '2026-08-01T00:00:00.000Z'], + ['2026', '2026-01-01T00:00:00.000Z'], + // Well before the epoch, where the seconds and the fraction have opposite signs. + ['1969-07-20T20:17:40.5Z', '1969-07-20T20:17:40.500Z'], + ])('reads %s as an instant', (value, expected) => { + expect(dateOf(value)).toBe(expected) + }) + + it.each([ + [42], + ['20-08-2026'], + ['2026-02-30'], + ['0000'], + ['2026-13'], + // FHIR states no dateTime without an offset once a time is present. + ['2026-08-20T12:00:00'], + ])('refuses %s rather than inventing an instant', (value) => { + expect(fhirDateTimeToDate(value).ok).toBe(false) + }) + + it('reads a Quantity as its code, not its label', () => { + const result = fhirQuantityToValue({ + value: 64, + unit: 'beats/minute', + system: 'http://unitsofmeasure.org', + code: '/min', + }) + expect(result.ok && result.value).toEqual({ + value: 64, + unit: '/min', + system: 'http://unitsofmeasure.org', + }) + }) + + it('falls back to the display unit when no code is stated', () => { + const result = fhirQuantityToValue({ value: 3, unit: 'steps' }) + expect(result.ok && result.value).toEqual({ value: 3, unit: 'steps' }) + }) + + it('keeps a comparator rather than reporting a bound as a measurement', () => { + const result = fhirQuantityToValue({ + value: 5, + code: 'mg', + comparator: '<', + }) + expect(result.ok && result.value.comparator).toBe('<') + }) + + it.each([ + [undefined], + [{ unit: 'mg' }], + [{ value: Number.NaN, unit: 'mg' }], + [{ value: 1 }], + [{ value: 1, unit: '' }], + [{ value: 1, code: 'mg', comparator: '!=' }], + ])('refuses a Quantity it cannot reduce', (value) => { + expect(fhirQuantityToValue(value).ok).toBe(false) + }) + + it('states the value and unit as types rather than as unknowns', () => { + const result = fhirQuantityToValue({ value: 1, code: 'mg' }) + if (!result.ok) throw new Error('expected a reduced Quantity') + expectTypeOf(result.value).toEqualTypeOf() + expectTypeOf(result.value.value).toEqualTypeOf() + expectTypeOf(result.value.unit).toEqualTypeOf() + }) +}) diff --git a/packages/fhir/test/mobile.test.ts b/packages/fhir/test/mobile.test.ts new file mode 100644 index 00000000..be857dc4 --- /dev/null +++ b/packages/fhir/test/mobile.test.ts @@ -0,0 +1,208 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { expectTypeOf } from 'expect-type' +import { + parseAbsoluteUri, + parseFhirId, + parseFhirInstant, + type FhirInstant, + type Result, +} from '../src/core/index.js' +import * as mobile from '../src/mobile/index.js' +import { + entryIdentifierName, + canonicalizeMobileEffectiveInstant, + createEntryIdentity, + deriveEntryFullUrl, + groveFhirContractVersion, + groveFhirExchangeIdentity, + groveFhirVersion, + groveMobilePackageMetadata, + mobileEffectiveCanonicalizationVectors, + sharedMobileMeasurementCatalog, + type MobileMeasurement, +} from '../src/mobile/index.js' +import * as mobileContract from '../src/mobile/measurement-catalog.generated.js' + +const unwrap = (result: Result): T => { + if (!result.ok) throw new Error(result.issues[0]?.message) + return result.value +} + +const uri = (value: string) => unwrap(parseAbsoluteUri(value)) +const instant = (value: string): FhirInstant => unwrap(parseFhirInstant(value)) + +describe('source-neutral Mobile contract', () => { + it('exports only source-neutral data, types, identity, and time contracts', () => { + expect(groveFhirVersion).toBe('4.0.1') + expect(groveFhirContractVersion).toBe('0.5.0') + expect(groveMobilePackageMetadata.packageId).toBe( + 'org.grovealliance.fhir.mobile', + ) + expect(Object.isFrozen(groveMobilePackageMetadata)).toBe(true) + expect('buildProviderMeasurementBundle' in mobile).toBe(false) + expect('providerAdapterCatalog' in mobile).toBe(false) + expect('groveFhirProfileCanonicals' in mobile).toBe(false) + expect('PROFILES' in mobile).toBe(false) + expect('providerAdapterCatalog' in mobileContract).toBe(false) + expect('providerScalarMappings' in mobileContract).toBe(false) + expect('groveProviderPackageMetadata' in mobileContract).toBe(false) + expect('adapterMeasurementCatalog' in mobileContract).toBe(false) + }) + it('contains only evidenced shared measurements', () => { + const entries = Object.values(sharedMobileMeasurementCatalog) + expect(entries).toHaveLength(84) + expect(sharedMobileMeasurementCatalog).not.toHaveProperty('body-mass-index') + expect(sharedMobileMeasurementCatalog).not.toHaveProperty('blood-glucose') + expect(sharedMobileMeasurementCatalog).not.toHaveProperty( + 'apple-stand-hour', + ) + expect(sharedMobileMeasurementCatalog).not.toHaveProperty( + 'sleeping-heart-rate-average', + ) + + for (const definition of entries) { + expect(definition).not.toHaveProperty('coverage') + expect(definition).not.toHaveProperty('coverageDetails') + expect(definition).not.toHaveProperty('generation') + expect(definition).not.toHaveProperty('owner') + } + expect(JSON.stringify(sharedMobileMeasurementCatalog)).not.toMatch( + /healthkit|health-connect|sensorkit|google-health-api|oura|withings|sourceTokens/u, + ) + expect(Object.isFrozen(sharedMobileMeasurementCatalog)).toBe(true) + expect(Object.isFrozen(entries[0])).toBe(true) + }) + + it('keeps the source-neutral measurement union discriminated', () => { + const measurement = { + kind: 'heart-rate', + value: 64, + effective: { + kind: 'date-time', + value: instant('2026-08-20T12:00:00Z'), + }, + } as const satisfies MobileMeasurement + expectTypeOf(measurement).toExtend() + expect(measurement.kind).toBe('heart-rate') + }) +}) + +describe('Mobile exchange entry identity', () => { + it('names an identifier by joining its system and value with a vertical bar', () => { + expect( + entryIdentifierName({ + system: 'https://example.org/source' as never, + value: 'record-1', + }), + ).toEqual({ ok: true, value: 'https://example.org/source|record-1' }) + }) + + it('admits a vertical bar in a composed value but not in the system', () => { + // A composed identifier is built from vertical bars, so only the system may not carry one. + expect( + entryIdentifierName({ + system: 'https://example.org/writer' as never, + value: 'v1:com.withings.wiscale2|17348211', + }), + ).toEqual({ + ok: true, + value: 'https://example.org/writer|v1:com.withings.wiscale2|17348211', + }) + expect( + entryIdentifierName({ + system: 'https://example.org/a|b' as never, + value: 'record-1', + }).ok, + ).toBe(false) + }) + + it.each(groveFhirExchangeIdentity.vectors)( + 'matches the IG $case vector', + (vector) => { + const derived = deriveEntryFullUrl({ + system: uri(vector.system), + value: vector.value, + }) + // A rejection vector states no URN: the system carries a separator, so the name would be + // ambiguous and the derivation refuses it. + if (vector.fullUrl === null) { + expect(derived.ok).toBe(false) + return + } + expect(derived).toEqual({ ok: true, value: vector.fullUrl }) + }, + ) + + it('retains a complete business Identifier and optional repository id', () => { + const identifier = { + system: uri('https://example.org/identifiers'), + value: 'record-1', + } + const id = unwrap(parseFhirId('repository-id')) + const fullUrl = unwrap(deriveEntryFullUrl(identifier)) + const result = createEntryIdentity(identifier, id) + expect(result.ok && result.value).toEqual({ + identifier, + id, + fullUrl, + }) + expect(result.ok && Object.isFrozen(result.value)).toBe(true) + expect(Object.isFrozen(identifier)).toBe(false) + identifier.value = 'caller-mutated-after-construction' + expect(result.ok && result.value.identifier.value).toBe('record-1') + }) + + it.each([ + { system: '/relative', value: 'record-1' }, + { system: 'https://example.org/identifiers', value: '' }, + { system: 'https://example.org/identifiers', value: ' \t\n ' }, + { system: 'https://example.org/identifiers', value: 'invalid-\ud800' }, + { system: 'https://example.org/identifiers', value: 'invalid-\udc00' }, + ])('rejects incomplete or non-Unicode-scalar identity %#', (input) => { + expect( + entryIdentifierName({ + system: input.system as never, + value: input.value, + }).ok, + ).toBe(false) + }) + + it('rejects an invalid repository id and accepts a valid surrogate pair', () => { + const identifier = { + system: uri('https://example.org/identifiers'), + value: 'valid-😀', + } + expect(deriveEntryFullUrl(identifier).ok).toBe(true) + expect(createEntryIdentity(identifier, 'invalid/id' as never).ok).toBe( + false, + ) + }) +}) + +describe('Mobile effective-time canonicalization', () => { + it.each(mobileEffectiveCanonicalizationVectors)( + 'matches the IG $id vector', + ({ input, output }) => { + expect(canonicalizeMobileEffectiveInstant(input)).toEqual({ + ok: true, + value: output, + }) + }, + ) + + it.each([ + 42, + 'not-an-instant', + '2026-08-20T12:00:00', + '9999-12-31T23:59:59.9996Z', + ])('fails closed for invalid or out-of-range instant %p', (value) => { + expect(canonicalizeMobileEffectiveInstant(value).ok).toBe(false) + }) +}) diff --git a/packages/fhir/test/providers.test.ts b/packages/fhir/test/providers.test.ts new file mode 100644 index 00000000..078e05b2 --- /dev/null +++ b/packages/fhir/test/providers.test.ts @@ -0,0 +1,1772 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { readFileSync } from 'node:fs' +import { assert, double, oneof, property } from 'fast-check' +import type { + FhirInstant, + PatientReference, + PositiveInteger, +} from '../src/core/index.js' +import { + parseAbsoluteUri, + parseFhirId, + parseFhirInstant, + parsePatientReference, + parsePositiveInteger, + parseResearchStudyReference, + type Result, +} from '../src/index.js' +import { + canonicalizeMobileEffectiveInstant, + entryIdentifierName, + createEntryIdentity, + deriveEntryFullUrl, + groveFhirExchangeIdentity, + mobileEffectiveCanonicalizationVectors, + sharedMobileMeasurementCatalog, + type ApplicationDeviceInput, + type MobileMeasurement, +} from '../src/mobile/index.js' +import { deriveProviderIdentities } from '../src/providers/identity.js' +import { + adapterMeasurementCatalog, + buildProviderMeasurementBundle, + providerRecordEffectiveRules, + providerScalarMappings, + parseProviderMeasurementBundleInput, + parseNormalizedProviderRecord, + type ConnectedProvider, + type ConnectedProviderRecord, + type ProviderMeasurementBundleInput, +} from '../src/providers/index.js' + +const unwrap = (result: Result): T => { + if (!result.ok) { + throw new Error(result.issues.map((issue) => issue.message).join('\n')) + } + return result.value +} + +const uri = (value: string) => unwrap(parseAbsoluteUri(value)) +const instant = (value: string): FhirInstant => unwrap(parseFhirInstant(value)) +const patient = (value: string): PatientReference => + unwrap(parsePatientReference(value)) +const positive = (value: number): PositiveInteger => + unwrap(parsePositiveInteger(value)) +const resourceIdentity = (system: string, value: string) => ({ + identifier: { system: uri(system), value }, +}) + +const application: ApplicationDeviceInput = { + identity: resourceIdentity( + 'https://example.org/applications', + 'converter-app', + ), + name: 'Example converter', + version: '0.0.0', +} + +const dateTime = instant('2026-08-20T12:00:00Z') +const start = instant('2026-08-20T00:00:00Z') +const end = instant('2026-08-20T12:00:00Z') +const dailyEnd = instant('2026-08-21T00:00:00Z') + +const heartRateMeasurement = { + kind: 'heart-rate', + value: 64, + effective: { kind: 'date-time', value: dateTime }, +} as const satisfies MobileMeasurement + +const bloodPressureMeasurement = { + kind: 'blood-pressure', + systolic: 118, + diastolic: 76, + effective: { kind: 'date-time', value: dateTime }, +} as const satisfies MobileMeasurement + +const baseInput = ( + provider: ConnectedProvider, + sourceType: string, + measurement: MobileMeasurement, +): ProviderMeasurementBundleInput => + ({ + subject: patient('Patient/example'), + measurements: [measurement], + source: { + adapter: { kind: 'providers', provider }, + providerAccountIdentifier: { + system: uri('https://example.org/deployments/provider-accounts'), + value: `pseudonym-${provider}-001`, + assurance: 'deployment-scoped-pseudonym', + }, + sourceType, + sourceNativeId: `native-${provider}-${sourceType}`, + recordingMethod: 'automatically-recorded', + dataOrigin: { + identity: resourceIdentity( + 'https://example.org/data-origins', + provider, + ), + name: provider, + }, + }, + application, + eventSequence: positive(1), + graphIdentifierSystem: uri( + 'urn:grove:provider-graph:org.grovealliance.example', + ), + issued: instant('2026-08-20T12:01:00Z'), + recorded: instant('2026-08-20T12:02:00Z'), + }) as ProviderMeasurementBundleInput + +const scalarCases = [ + { + provider: 'google-health-api', + sourceType: 'weight', + measurement: { + kind: 'body-weight', + value: 72.5, + effective: { kind: 'date-time', value: dateTime }, + }, + }, + { + provider: 'google-health-api', + sourceType: 'core-body-temperature', + measurement: { + kind: 'body-temperature', + value: 36.8, + effective: { kind: 'date-time', value: dateTime }, + }, + }, + { + provider: 'google-health-api', + sourceType: 'height', + measurement: { + kind: 'body-height', + value: 178, + effective: { kind: 'date-time', value: dateTime }, + }, + }, + { + provider: 'google-health-api', + sourceType: 'steps', + measurement: { + kind: 'step-count', + value: 8234, + effective: { kind: 'period', start, end }, + }, + }, + { + provider: 'oura', + sourceType: 'daily_activity', + measurement: { + kind: 'distance', + value: 6123, + effective: { kind: 'period', start, end: dailyEnd }, + }, + }, + { + provider: 'google-health-api', + sourceType: 'active-energy-burned', + measurement: { + kind: 'active-energy', + value: 430, + effective: { kind: 'period', start, end }, + }, + }, + { + provider: 'oura', + sourceType: 'sleep', + measurement: { + kind: 'sleep-duration', + value: 7.4, + effective: { kind: 'period', start, end }, + }, + }, + { + provider: 'withings', + sourceType: 'getmeas:9+10', + measurement: bloodPressureMeasurement, + }, + { + provider: 'withings', + sourceType: 'getmeas:11', + measurement: heartRateMeasurement, + }, + { + provider: 'withings', + sourceType: 'getmeas:54', + measurement: { + kind: 'oxygen-saturation', + value: 98, + effective: { kind: 'date-time', value: dateTime }, + }, + }, +] as const satisfies ReadonlyArray<{ + readonly provider: ConnectedProvider + readonly sourceType: string + readonly measurement: MobileMeasurement +}> + +const resources = ( + result: ReturnType, +) => { + if (!result.ok) throw new Error(JSON.stringify(result.issues)) + return result.value.entry.map((entry) => entry.resource) +} + +describe('Provider R4 graph builder', () => { + it.each(scalarCases)( + 'builds the admitted $provider/$sourceType $measurement.kind graph', + ({ provider, sourceType, measurement }) => { + const result = buildProviderMeasurementBundle( + baseInput(provider, sourceType, measurement), + ) + expect(result.ok).toBe(true) + if (!result.ok) return + + expect(result.value.type).toBe('collection') + expect(result.value.entry).toHaveLength(4) + expect('total' in result.value).toBe(false) + expect(Object.isFrozen(result.value)).toBe(true) + + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(observation?.meta?.profile).toEqual([ + `https://grovealliance.org/fhir/mobile/StructureDefinition/${sharedMobileMeasurementCatalog[measurement.kind].profile}`, + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation', + ]) + expect(observation?.extension).toEqual( + expect.arrayContaining([ + { + url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider', + valueCode: provider, + }, + { + url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type', + valueCode: `${provider}/${sourceType}`, + }, + ]), + ) + expect( + observation?.extension?.some( + ({ url }) => + url === + 'http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice', + ), + ).toBe(false) + expect(observation?.code.coding).toHaveLength(1) + expect(observation?.id).toBeUndefined() + }, + ) + + it('builds every exact provider/source/measurement mapping in the generated catalog', () => { + const ownerExclusiveKinds = new Set( + Object.values(adapterMeasurementCatalog).flatMap((catalog) => + Object.keys(catalog), + ), + ) + const exampleMeasurement = ( + kind: string, + ): MobileMeasurement | undefined => { + if (!Object.hasOwn(sharedMobileMeasurementCatalog, kind)) return undefined + if (kind === 'blood-pressure') return bloodPressureMeasurement + if (kind === 'sleep-stage') { + return { + kind: 'sleep-stage', + stage: 'light', + effective: { kind: 'period', start, end }, + } + } + const definition = + sharedMobileMeasurementCatalog[kind as MobileMeasurement['kind']] + const effective = + definition.effective === 'Period' ? + ({ kind: 'period', start, end } as const) + : ({ kind: 'date-time', value: dateTime } as const) + const value = + 'allowedValues' in definition ? definition.allowedValues[0] : 72 + return { kind, value, effective } as MobileMeasurement + } + const exhaustiveMappings = providerScalarMappings as Readonly< + Record< + ConnectedProvider, + Readonly>>> + > + > + let checked = 0 + for (const [provider, sourceMappings] of Object.entries( + exhaustiveMappings, + )) { + for (const [sourceType, mappings] of Object.entries(sourceMappings)) { + for (const measurementKind of Object.keys(mappings)) { + const measurement = exampleMeasurement(measurementKind) + if (measurement === undefined) { + // Owner-exclusive mappings stay contract data outside the facade. + expect(ownerExclusiveKinds.has(measurementKind)).toBe(true) + continue + } + const effectiveRule = ( + providerRecordEffectiveRules as Readonly< + Record> | undefined> + > + )[provider]?.[sourceType] + const sourceMeasurement = + effectiveRule === undefined ? measurement : ( + { + ...measurement, + effective: { kind: 'period', start, end: dailyEnd }, + } + ) + expect( + buildProviderMeasurementBundle( + baseInput( + provider as ConnectedProvider, + sourceType, + sourceMeasurement as MobileMeasurement, + ), + ).ok, + ).toBe(true) + checked += 1 + } + } + } + expect(checked).toBeGreaterThan(scalarCases.length) + }) + + it('atomically builds every present Oura daily-activity output with one complete Provenance target set', () => { + const dailyActivity = baseInput('oura', 'daily_activity', { + kind: 'distance', + value: 6_123, + effective: { kind: 'period', start, end: dailyEnd }, + }) + const result = buildProviderMeasurementBundle({ + ...dailyActivity, + measurements: [ + { + kind: 'distance', + value: 6_123, + effective: { kind: 'period', start, end: dailyEnd }, + }, + { + kind: 'active-energy', + value: 430, + effective: { kind: 'period', start, end: dailyEnd }, + }, + { + kind: 'step-count', + value: 8_234, + effective: { kind: 'period', start, end: dailyEnd }, + }, + ], + } as ProviderMeasurementBundleInput) + expect(result.ok).toBe(true) + if (!result.ok) return + + const observations = result.value.entry.filter( + ({ resource }) => resource.resourceType === 'Observation', + ) + expect(observations).toHaveLength(3) + expect( + observations.map(({ resource }) => resource.meta?.profile?.[0]), + ).toEqual([ + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-step-count', + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-active-energy', + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-distance', + ]) + const provenance = result.value.entry.find( + ({ resource }) => resource.resourceType === 'Provenance', + )?.resource + expect(provenance?.resourceType).toBe('Provenance') + if (provenance?.resourceType !== 'Provenance') return + expect(provenance.target).toHaveLength(observations.length) + expect( + new Set(provenance.target.map(({ reference }) => reference)), + ).toEqual(new Set(observations.map(({ fullUrl }) => fullUrl))) + }) + + it.each([ + ['one', ['step-count']], + ['two', ['step-count', 'distance']], + ] as const)( + 'admits a %s-output subset when those are the fields present in an Oura daily-activity record', + (_label, kinds) => { + const candidates = { + 'step-count': { + kind: 'step-count', + value: 8_234, + effective: { kind: 'period', start, end: dailyEnd }, + }, + distance: { + kind: 'distance', + value: 6_123, + effective: { kind: 'period', start, end: dailyEnd }, + }, + } as const + const input = baseInput('oura', 'daily_activity', candidates[kinds[0]]) + const result = buildProviderMeasurementBundle({ + ...input, + measurements: kinds.map((kind) => candidates[kind]), + } as unknown as ProviderMeasurementBundleInput) + expect(result.ok).toBe(true) + if (!result.ok) return + expect( + result.value.entry.filter( + ({ resource }) => resource.resourceType === 'Observation', + ), + ).toHaveLength(kinds.length) + }, + ) + + it('rejects recursively encoded identity leakage through scalar metadata', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + expect( + buildProviderMeasurementBundle({ + ...input, + source: { ...input.source, sourceNativeId: 'native id/42' }, + application: { + ...input.application, + manufacturer: 'encoded-native%2520id%252F42', + }, + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + expect( + parseNormalizedProviderRecord({ + source: { + ...input.source, + providerAccountIdentifier: { + ...input.source.providerAccountIdentifier, + value: 'account value', + }, + dataOrigin: { + ...input.source.dataOrigin, + name: 'encoded-account+value', + }, + }, + measurements: input.measurements, + }).ok, + ).toBe(false) + }) + + it('requires every catalogued daily source to use one shared complete civil-day Period', () => { + const input = baseInput('oura', 'daily_activity', { + kind: 'step-count', + value: 8_234, + effective: { kind: 'period', start, end: dailyEnd }, + }) + expect( + buildProviderMeasurementBundle({ + ...input, + measurements: [ + { + kind: 'step-count', + value: 8_234, + effective: { kind: 'period', start, end }, + }, + ], + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle({ + ...input, + measurements: [ + input.measurements[0], + { + kind: 'distance', + value: 6_123, + effective: { + kind: 'period', + start, + end: instant('2026-08-22T00:00:00Z'), + }, + }, + ], + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle( + baseInput('withings', 'getactivity:steps', { + kind: 'step-count', + value: 8_234, + effective: { kind: 'period', start, end }, + }), + ).ok, + ).toBe(false) + const daylightSavingStart = instant('2026-11-01T00:00:00-07:00') + const daylightSavingEnd = instant('2026-11-02T00:00:00-08:00') + expect( + buildProviderMeasurementBundle( + baseInput('withings', 'getactivity:steps', { + kind: 'step-count', + value: 8_234, + effective: { + kind: 'period', + start: daylightSavingStart, + end: daylightSavingEnd, + }, + }), + ).ok, + ).toBe(true) + }) + + it('rejects duplicate, unsupported, invalid, and non-emitted output inputs before constructing a partial graph', () => { + const input = baseInput('oura', 'daily_activity', { + kind: 'step-count', + value: 8_234, + effective: { kind: 'period', start, end: dailyEnd }, + }) + expect( + buildProviderMeasurementBundle({ + ...input, + measurements: [], + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle({ + ...input, + measurements: [input.measurements[0], input.measurements[0]], + } as ProviderMeasurementBundleInput).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle({ + ...input, + measurements: [ + input.measurements[0], + { + kind: 'body-weight', + value: 72, + effective: { kind: 'date-time', value: dateTime }, + }, + ], + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle({ + ...input, + measurements: [ + input.measurements[0], + { + kind: 'active-energy', + value: -1, + effective: { kind: 'period', start, end: dailyEnd }, + }, + ], + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle({ + ...input, + repositoryIds: { + observations: { + distance: unwrap(parseFhirId('not-emitted')), + }, + }, + }).ok, + ).toBe(false) + }) + + it('treats effective values as emitted metadata and rejects native-id leakage', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + expect( + buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + sourceNativeId: '2026-08-20T12:00:00.000Z', + }, + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + }) + + it('derives exact source, conversion and exchange identifiers in their proper namespaces', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const result = buildProviderMeasurementBundle(input) + expect(result.ok).toBe(true) + if (!result.ok) return + + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(observation?.identifier?.map(({ system }) => system)).toEqual([ + 'https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id', + ]) + for (const identity of observation?.identifier ?? []) { + expect(identity.value).toMatch(/^(v1:[^|]+(\|[^|]+)+|[^|]+)$/u) + } + expect(result.value.identifier?.system).toBe( + 'urn:grove:provider-graph:org.grovealliance.example', + ) + const provenanceEntry = result.value.entry.find( + (entry) => entry.resource.resourceType === 'Provenance', + ) + expect(provenanceEntry?.extension?.[0]?.valueIdentifier?.system).toBe( + 'urn:grove:provider-graph:org.grovealliance.example', + ) + // The provider's own record key travels in the clear: it is a vendor row key, not + // participant data, and the same value is already public when the vendor's app writes it + // into HealthKit or Health Connect. + expect(JSON.stringify(result.value)).toContain(input.source.sourceNativeId) + // The account component is a deployment-minted pseudonym, never a vendor-side account key. + expect(input.source.providerAccountIdentifier.value).toMatch(/^pseudonym-/u) + }) + + it('matches the frozen Provider composed source and output vectors', () => { + const input = baseInput('google-health-api', 'steps', { + kind: 'step-count', + value: 123, + effective: { kind: 'period', start, end }, + }) + const result = buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + providerAccountIdentifier: { + system: uri('https://provider.example.org/accounts'), + value: 'account-001', + assurance: 'deployment-scoped-pseudonym', + }, + sourceNativeId: 'steps-2026-08-20T16:00:00Z', + }, + } as ProviderMeasurementBundleInput) + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + // Google Health is account-scoped, and a single-measurement record is a one-to-one + // conversion, so the source record is the Observation's only identity. + expect(observation?.identifier?.map((entry) => entry.value)).toEqual([ + 'v1:google-health-api|account-001|steps|steps-2026-08-20T16:00:00Z', + ]) + + const unicodeInput = baseInput('oura', 'sleep', { + kind: 'sleep-duration', + value: 7, + effective: { kind: 'period', start, end }, + }) + const unicodeResult = buildProviderMeasurementBundle({ + ...unicodeInput, + source: { + ...unicodeInput.source, + providerAccountIdentifier: { + system: uri('https://provider.example.org/accounts'), + value: 'café\n"\\', + assurance: 'deployment-scoped-pseudonym', + }, + sourceNativeId: 'résumé-\u0001', + }, + } as ProviderMeasurementBundleInput) + const unicodeObservation = resources(unicodeResult).find( + (resource) => resource.resourceType === 'Observation', + ) + // Oura documents its document ids as unique across every account, so the key is carried + // through exactly as Oura supplied it, non-ASCII included. + expect(unicodeObservation?.identifier?.[0]?.value).toBe('résumé-\u0001') + }) + + it('changes only event identities when the durable event sequence changes', () => { + const first = buildProviderMeasurementBundle( + baseInput('withings', 'getmeas:11', heartRateMeasurement), + ) + const second = buildProviderMeasurementBundle({ + ...baseInput('withings', 'getmeas:11', heartRateMeasurement), + eventSequence: positive(2), + graphIdentifierSystem: uri( + 'urn:grove:provider-graph:org.grovealliance.example', + ), + }) + expect(first.ok && second.ok).toBe(true) + if (!first.ok || !second.ok) return + + const firstObservation = resources(first).find( + (resource) => resource.resourceType === 'Observation', + ) + const secondObservation = resources(second).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(firstObservation?.identifier).toEqual(secondObservation?.identifier) + expect(first.value.identifier).not.toEqual(second.value.identifier) + }) + + it('adds a gateway reference only with explicit mediation evidence', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const converterGateway = buildProviderMeasurementBundle({ + ...input, + gatewayApplication: { + kind: 'converter-application', + roleAssurance: 'mediated-or-routed-measurement', + }, + }) + expect(converterGateway.ok).toBe(true) + if (!converterGateway.ok) return + expect(converterGateway.value.entry).toHaveLength(4) + const converterObservation = resources(converterGateway).find( + (resource) => resource.resourceType === 'Observation', + ) + const converterApplicationEntry = converterGateway.value.entry.find( + (entry) => + entry.resource.resourceType === 'Device' && + entry.resource.identifier?.[0]?.value === 'converter-app', + ) + expect( + converterObservation?.extension?.find( + ({ url }) => + url === + 'http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice', + )?.valueReference?.reference, + ).toBe(converterApplicationEntry?.fullUrl) + + expect( + buildProviderMeasurementBundle({ + ...input, + gatewayApplication: { + kind: 'converter-application', + roleAssurance: 'conversion-only', + }, + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + }) + + it('builds a fully attributed graph with distinct gateway and authorized hardware identity', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const result = buildProviderMeasurementBundle({ + ...input, + application: { + ...input.application, + manufacturer: 'Grove Alliance', + }, + gatewayApplication: { + kind: 'distinct-application', + roleAssurance: 'mediated-or-routed-measurement', + application: { + identity: resourceIdentity( + 'https://example.org/applications', + 'gateway-app', + ), + name: 'Gateway app', + version: '2.1.0', + manufacturer: 'Gateway vendor', + }, + }, + source: { + ...input.source, + dataOrigin: { + ...input.source.dataOrigin, + version: '3.0.0', + manufacturer: 'Source vendor', + }, + recordingDevice: { + identity: resourceIdentity( + 'https://example.org/authorized-hardware', + 'hardware-1', + ), + identityScope: 'authorized-hardware', + disclosureAuthorization: 'authorized-for-exchange', + name: 'Chest strap', + manufacturer: 'Recorder vendor', + modelNumber: 'Model 1', + }, + }, + researchStudyReferences: [ + unwrap(parseResearchStudyReference('ResearchStudy/study-1')), + ], + } as ProviderMeasurementBundleInput) + expect(result.ok).toBe(true) + if (!result.ok) return + expect(result.value.entry).toHaveLength(6) + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(observation?.device?.reference).toMatch(/^urn:uuid:/u) + expect( + observation?.extension?.some( + ({ url }) => + url === + 'http://hl7.org/fhir/StructureDefinition/workflow-researchStudy', + ), + ).toBe(true) + }) + + it('requires an explicit privacy scope for recording-device identity and never accepts a serial number', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const result = buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + recordingDevice: { + identity: resourceIdentity( + 'https://example.org/deployments/recording-devices', + 'device-pseudonym-1', + ), + identityScope: 'deployment-scoped', + name: 'Connected scale', + }, + }, + } as ProviderMeasurementBundleInput) + expect(result.ok).toBe(true) + if (!result.ok) return + const recordingDevice = resources(result).find( + (resource) => + resource.resourceType === 'Device' && + resource.identifier?.[0]?.value === 'device-pseudonym-1', + ) + expect(recordingDevice).toBeDefined() + expect(recordingDevice).not.toHaveProperty('serialNumber') + + expect( + buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + recordingDevice: { + identity: resourceIdentity( + 'https://example.org/hardware-identifiers', + 'global-hardware-id', + ), + identityScope: 'authorized-hardware', + serialNumber: 'SERIAL-123', + }, + }, + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + }) + + it('rejects arbitrary source codings instead of treating lineage as a clinical code', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + expect( + buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + sourceTypeCoding: { + system: 'https://provider.example/source-types', + code: 'broad-summary-record', + }, + }, + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + }) + + it('accepts only unique, unambiguous typed ResearchStudy references', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const local = unwrap(parseResearchStudyReference('ResearchStudy/local-1')) + const absolute = unwrap( + parseResearchStudyReference( + 'https://research.example/fhir/ResearchStudy/remote-1', + ), + ) + expect( + buildProviderMeasurementBundle({ + ...input, + researchStudyReferences: [local, absolute], + }).ok, + ).toBe(true) + + const duplicate = parseProviderMeasurementBundleInput({ + ...input, + researchStudyReferences: [local, local], + }) + expect(duplicate.ok).toBe(false) + if (!duplicate.ok) { + expect(duplicate.issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ code: 'duplicate-identifier' }), + ]), + ) + } + expect( + parseProviderMeasurementBundleInput({ + ...input, + researchStudyReferences: [ + 'https://research.example/fhir/ResearchStudy/remote-1?version=2', + ], + }).ok, + ).toBe(false) + expect( + parseProviderMeasurementBundleInput({ + ...baseInput('withings', 'getmeas:54', { + kind: 'oxygen-saturation', + value: 98, + effective: { kind: 'date-time', value: dateTime }, + }), + measurements: [ + { + kind: 'oxygen-saturation', + value: 0, + effective: { kind: 'date-time', value: dateTime }, + }, + ], + }).ok, + ).toBe(true) + }) + + it('rounds Mobile effective instants before enforcing a non-zero Period', () => { + const collapsed = baseInput('oura', 'sleep', { + kind: 'sleep-duration', + value: 0.1, + effective: { + kind: 'period', + start: instant('2026-08-20T12:00:00.0001Z'), + end: instant('2026-08-20T12:00:00.0002Z'), + }, + }) + expect(buildProviderMeasurementBundle(collapsed).ok).toBe(false) + + const input = baseInput('oura', 'sleep', { + kind: 'sleep-duration', + value: 0.1, + effective: { + kind: 'period', + start: instant('2026-08-20T12:00:00.0004Z'), + end: instant('2026-08-20T12:00:00.0006Z'), + }, + }) + const result = buildProviderMeasurementBundle(input) + expect(result.ok).toBe(true) + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(observation?.effectivePeriod).toEqual({ + start: '2026-08-20T12:00:00.000Z', + end: '2026-08-20T12:00:00.001Z', + }) + expect( + buildProviderMeasurementBundle({ + ...input, + measurements: [ + { + ...input.measurements[0], + effective: { + kind: 'period', + start: instant('2026-08-20T12:00:00.0006Z'), + end: instant('2026-08-20T12:00:00.0004Z'), + }, + }, + ], + } as ProviderMeasurementBundleInput).ok, + ).toBe(false) + }) + + it.each(mobileEffectiveCanonicalizationVectors)( + 'matches the IG Mobile effective-time vector $id', + ({ input, output }) => { + expect(canonicalizeMobileEffectiveInstant(input)).toEqual({ + ok: true, + value: output, + }) + }, + ) + + it('fails closed for invalid input and a rounding carry outside FHIR four-digit years', () => { + expect(canonicalizeMobileEffectiveInstant('not-an-instant').ok).toBe(false) + expect( + canonicalizeMobileEffectiveInstant('9999-12-31T23:59:59.9996Z').ok, + ).toBe(false) + }) + + it('serializes a canonical effectiveDateTime while preserving its source offset', () => { + const input = baseInput('withings', 'getmeas:11', { + ...heartRateMeasurement, + effective: { + kind: 'date-time', + value: instant('2026-08-20T08:30:00.251500001-07:00'), + }, + }) + const parsed = parseProviderMeasurementBundleInput(input) + expect(parsed.ok).toBe(true) + if (!parsed.ok) return + expect(parsed.value.measurements[0].effective).toEqual({ + kind: 'date-time', + value: '2026-08-20T08:30:00.252-07:00', + }) + + const result = buildProviderMeasurementBundle(input) + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(observation?.effectiveDateTime).toBe('2026-08-20T08:30:00.252-07:00') + }) + + it('derives every urn:uuid edge from a complete business identifier', () => { + const result = buildProviderMeasurementBundle( + baseInput('withings', 'getmeas:11', heartRateMeasurement), + ) + expect(result.ok).toBe(true) + if (!result.ok) return + + for (const entry of result.value.entry) { + expect(entry.fullUrl).toMatch(/^urn:uuid:/u) + const businessIdentifier = entry.extension?.[0]?.valueIdentifier + expect(businessIdentifier?.system).toBeDefined() + expect(businessIdentifier?.value).toBeDefined() + expect( + deriveEntryFullUrl({ + system: uri(businessIdentifier?.system ?? ''), + value: businessIdentifier?.value ?? '', + }), + ).toEqual({ ok: true, value: entry.fullUrl }) + } + }) + + it('constructs one composite Withings blood-pressure panel', () => { + const result = buildProviderMeasurementBundle( + baseInput('withings', 'getmeas:9+10', bloodPressureMeasurement), + ) + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(observation?.component).toHaveLength(2) + expect(observation?.valueQuantity).toBeUndefined() + }) + + it('uses optional Resource.id values only when supplied by a repository', () => { + const result = buildProviderMeasurementBundle({ + ...baseInput('withings', 'getmeas:11', heartRateMeasurement), + repositoryIds: { + bundle: unwrap(parseFhirId('bundle-42')), + observations: { + 'heart-rate': unwrap(parseFhirId('observation-42')), + }, + provenance: unwrap(parseFhirId('provenance-42')), + }, + }) + expect(result.ok).toBe(true) + if (!result.ok) return + expect(result.value.id).toBe('bundle-42') + expect( + resources(result).find( + (resource) => resource.resourceType === 'Observation', + )?.id, + ).toBe('observation-42') + }) + + it('omits optional attribution and device fields while preserving repository device ids', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const result = buildProviderMeasurementBundle({ + ...input, + application: { + identity: { + ...application.identity, + id: unwrap(parseFhirId('application-42')), + }, + name: application.name, + }, + source: { + adapter: input.source.adapter, + providerAccountIdentifier: input.source.providerAccountIdentifier, + sourceType: input.source.sourceType, + sourceNativeId: input.source.sourceNativeId, + dataOrigin: input.source.dataOrigin, + recordingDevice: { + identity: { + ...resourceIdentity( + 'https://example.org/deployments/recording-devices', + 'minimal-device', + ), + id: unwrap(parseFhirId('recording-device-42')), + }, + identityScope: 'deployment-scoped', + }, + }, + } as ProviderMeasurementBundleInput) + expect(result.ok).toBe(true) + if (!result.ok) return + + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(observation?.extension).toEqual( + expect.arrayContaining([ + { + url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type', + valueCode: 'withings/getmeas:11', + }, + ]), + ) + const minimalDevice = resources(result).find( + (resource) => resource.id === 'recording-device-42', + ) + expect(minimalDevice).not.toHaveProperty('deviceName') + expect(minimalDevice).not.toHaveProperty('manufacturer') + expect(minimalDevice).not.toHaveProperty('modelNumber') + }) + + it('rejects invalid scalar ranges at the strict runtime boundary', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + expect( + parseProviderMeasurementBundleInput({ + ...input, + measurements: [ + { + ...heartRateMeasurement, + effective: { kind: 'date-time', value: 'not-an-instant' }, + }, + ], + }).ok, + ).toBe(false) + expect( + parseProviderMeasurementBundleInput({ + ...input, + measurements: [{ ...heartRateMeasurement, value: 0 }], + }).ok, + ).toBe(false) + expect( + parseProviderMeasurementBundleInput({ + ...baseInput('google-health-api', 'steps', { + kind: 'step-count', + value: 1, + effective: { kind: 'period', start, end }, + }), + measurements: [ + { + kind: 'step-count', + value: 1.5, + effective: { kind: 'period', start, end }, + }, + ], + }).ok, + ).toBe(false) + expect( + parseProviderMeasurementBundleInput({ + ...baseInput('withings', 'getmeas:54', { + kind: 'oxygen-saturation', + value: 98, + effective: { kind: 'date-time', value: dateTime }, + }), + measurements: [ + { + kind: 'oxygen-saturation', + value: 101, + effective: { kind: 'date-time', value: dateTime }, + }, + ], + }).ok, + ).toBe(false) + }) + + it('rejects a source-native identity containing an isolated surrogate', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const invalid = { + ...input, + source: { ...input.source, sourceNativeId: 'invalid-\ud800' }, + } as ProviderMeasurementBundleInput + expect(parseProviderMeasurementBundleInput(invalid).ok).toBe(false) + expect(buildProviderMeasurementBundle(invalid).ok).toBe(false) + }) + + it('fails closed at every direct Provider identity primitive boundary', () => { + const valid = { + provider: 'withings', + providerAccountIdentifier: { + system: uri('https://example.org/provider-accounts'), + value: 'account-pseudonym', + }, + sourceType: 'getmeas:11', + sourceNativeId: 'source-record-1', + outputDiscriminators: ['heart-rate'], + eventSequence: positive(1), + graphIdentifierSystem: uri( + 'urn:grove:provider-graph:org.grovealliance.example', + ), + } as const + expect( + deriveProviderIdentities({ + ...valid, + providerAccountIdentifier: { + ...valid.providerAccountIdentifier, + system: '/relative' as never, + }, + }).ok, + ).toBe(false) + expect( + deriveProviderIdentities({ + ...valid, + outputDiscriminators: [' '], + }).ok, + ).toBe(false) + expect( + deriveProviderIdentities({ + ...valid, + outputDiscriminators: ['heart-rate', 'heart-rate'], + }).ok, + ).toBe(false) + expect( + deriveProviderIdentities({ + ...valid, + eventSequence: 0 as never, + }).ok, + ).toBe(false) + }) + + it.each([ + [ + 'source native id', + (input: ProviderMeasurementBundleInput) => ({ + ...input, + application: { + ...input.application, + manufacturer: `Leaked ${input.source.sourceNativeId}`, + }, + }), + ], + [ + 'provider account pseudonym', + (input: ProviderMeasurementBundleInput) => ({ + ...input, + source: { + ...input.source, + dataOrigin: { + ...input.source.dataOrigin, + name: `Origin ${input.source.providerAccountIdentifier.value}`, + }, + }, + }), + ], + ] as const)( + 'rejects %s leakage through emitted metadata', + (_name, mutate) => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const candidate = mutate(input) as ProviderMeasurementBundleInput + expect(parseProviderMeasurementBundleInput(candidate).ok).toBe(false) + expect(buildProviderMeasurementBundle(candidate).ok).toBe(false) + if (_name === 'provider account pseudonym') { + expect( + parseNormalizedProviderRecord({ + source: candidate.source, + measurements: candidate.measurements, + }).ok, + ).toBe(false) + } + }, + ) + + it.each([ + [ + 'provider account value', + (input: ProviderMeasurementBundleInput) => ({ + ...input, + source: { + ...input.source, + providerAccountIdentifier: { + ...input.source.providerAccountIdentifier, + value: ' \t ', + }, + }, + }), + ], + [ + 'source native id', + (input: ProviderMeasurementBundleInput) => ({ + ...input, + source: { ...input.source, sourceNativeId: '\n ' }, + }), + ], + [ + 'source type', + (input: ProviderMeasurementBundleInput) => ({ + ...input, + source: { ...input.source, sourceType: ' ' }, + }), + ], + [ + 'converter identifier', + (input: ProviderMeasurementBundleInput) => ({ + ...input, + application: { + ...input.application, + identity: { + ...input.application.identity, + identifier: { + ...input.application.identity.identifier, + value: '\t', + }, + }, + }, + }), + ], + [ + 'data-origin identifier', + (input: ProviderMeasurementBundleInput) => ({ + ...input, + source: { + ...input.source, + dataOrigin: { + ...input.source.dataOrigin, + identity: { + ...input.source.dataOrigin.identity, + identifier: { + ...input.source.dataOrigin.identity.identifier, + value: ' ', + }, + }, + }, + }, + }), + ], + ] as const)('rejects a whitespace-only %s', (_name, mutate) => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + expect(parseProviderMeasurementBundleInput(mutate(input)).ok).toBe(false) + }) + + it.each(['converter', 'gateway', 'data-origin', 'recording-device'] as const)( + 'rejects an invalid Unicode %s business identity', + (role) => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const invalidIdentity = resourceIdentity( + 'https://example.org/identifiers', + 'invalid-\ud800', + ) + let candidate: ProviderMeasurementBundleInput + if (role === 'converter') { + candidate = { + ...input, + application: { ...input.application, identity: invalidIdentity }, + } + } else if (role === 'gateway') { + candidate = { + ...input, + gatewayApplication: { + kind: 'distinct-application', + roleAssurance: 'mediated-or-routed-measurement', + application: { + identity: invalidIdentity, + name: 'Invalid gateway identity', + }, + }, + } + } else if (role === 'data-origin') { + candidate = { + ...input, + source: { + ...input.source, + dataOrigin: { + ...input.source.dataOrigin, + identity: invalidIdentity, + }, + }, + } as ProviderMeasurementBundleInput + } else { + candidate = { + ...input, + source: { + ...input.source, + recordingDevice: { + identity: invalidIdentity, + identityScope: 'deployment-scoped', + }, + }, + } as ProviderMeasurementBundleInput + } + expect(parseProviderMeasurementBundleInput(candidate).ok).toBe(false) + expect(buildProviderMeasurementBundle(candidate).ok).toBe(false) + }, + ) + + it.each([ + ['google-health-api', 'heart-rate', heartRateMeasurement], + [ + 'google-health-api', + 'blood-glucose', + { + kind: 'blood-glucose', + value: 94, + effective: { kind: 'date-time', value: dateTime }, + specimen: { + identity: resourceIdentity( + 'https://example.org/specimens', + 'whole-blood', + ), + }, + }, + ], + [ + 'oura', + 'sleep', + { + kind: 'sleep-stage', + stage: 'deep', + effective: { kind: 'period', start, end }, + }, + ], + ] as const)( + 'fails closed for non-scalar or unsupported %s/%s data', + (provider, sourceType, measurement) => { + const result = buildProviderMeasurementBundle( + baseInput( + provider, + sourceType, + measurement as unknown as MobileMeasurement, + ), + ) + expect(result.ok).toBe(false) + }, + ) + + it('rejects non-positive event sequences and duplicate graph identities', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + expect( + buildProviderMeasurementBundle({ + ...input, + eventSequence: 0, + } as ProviderMeasurementBundleInput).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + dataOrigin: { + ...input.source.dataOrigin, + identity: application.identity, + }, + }, + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + }) +}) + +describe('Exchange entry identity', () => { + it.each(groveFhirExchangeIdentity.vectors)( + 'matches the IG $case vector', + (vector) => { + const derived = deriveEntryFullUrl({ + system: uri(vector.system), + value: vector.value, + }) + // A rejection vector states no URN: the system carries a separator, so the name would be + // ambiguous and the derivation refuses it. + if (vector.fullUrl === null) { + expect(derived.ok).toBe(false) + return + } + expect(derived).toEqual({ ok: true, value: vector.fullUrl }) + }, + ) + + it('rejects isolated UTF-16 surrogates before UUID derivation', () => { + expect( + deriveEntryFullUrl({ + system: uri('https://example.org/identifiers'), + value: 'invalid-\ud800', + }).ok, + ).toBe(false) + expect( + deriveEntryFullUrl({ + system: uri('https://example.org/identifiers'), + value: 'invalid-\udc00', + }).ok, + ).toBe(false) + expect( + deriveEntryFullUrl({ + system: uri('https://example.org/identifiers'), + value: 'valid-😀', + }).ok, + ).toBe(true) + }) + + it('rejects incomplete entry identifiers and invalid repository ids', () => { + expect( + entryIdentifierName({ + system: '/relative' as never, + value: 'record-1', + }).ok, + ).toBe(false) + expect( + entryIdentifierName({ + system: uri('https://example.org/identifiers'), + value: '', + }).ok, + ).toBe(false) + expect( + entryIdentifierName({ + system: uri('https://example.org/identifiers'), + value: ' \t\n ', + }).ok, + ).toBe(false) + expect( + createEntryIdentity( + { + system: uri('https://example.org/identifiers'), + value: 'record-1', + }, + 'invalid/id' as never, + ).ok, + ).toBe(false) + }) +}) + +describe('Provider-neutral normalization contract', () => { + it.each([ + 'google-health-active-energy.json', + 'oura-daily-activity.json', + 'oura-sleep-duration.json', + 'withings-body-weight.json', + ])('accepts the normalized %s fixture', (name) => { + const fixture: unknown = JSON.parse( + readFileSync(new URL(`../fixtures/normalized/${name}`, import.meta.url), { + encoding: 'utf8', + }), + ) + expect(parseNormalizedProviderRecord(fixture).ok).toBe(true) + }) + + it('normalizes multi-output serialization order without mutating caller data', () => { + const fixture = JSON.parse( + readFileSync( + new URL( + '../fixtures/normalized/oura-daily-activity.json', + import.meta.url, + ), + { encoding: 'utf8' }, + ), + ) as { measurements: Array<{ kind: string }> } + const callerOrder = fixture.measurements.map(({ kind }) => kind) + const result = parseNormalizedProviderRecord(fixture) + expect(result.ok).toBe(true) + if (!result.ok) return + expect(result.value.measurements.map(({ kind }) => kind)).toEqual([ + 'step-count', + 'active-energy', + 'distance', + ]) + expect(fixture.measurements.map(({ kind }) => kind)).toEqual(callerOrder) + expect(Object.isFrozen(result.value.measurements)).toBe(true) + }) + + it('rejects raw response fields rather than silently stripping them', () => { + const fixture = JSON.parse( + readFileSync( + new URL( + '../fixtures/normalized/oura-sleep-duration.json', + import.meta.url, + ), + { encoding: 'utf8' }, + ), + ) as Record + expect( + parseNormalizedProviderRecord({ + ...fixture, + rawVendorResponse: { readiness: 97 }, + }).ok, + ).toBe(false) + }) + + it('requires the caller to attest that the account identifier is deployment-scoped and pseudonymous', () => { + const fixture = JSON.parse( + readFileSync( + new URL( + '../fixtures/normalized/oura-sleep-duration.json', + import.meta.url, + ), + { encoding: 'utf8' }, + ), + ) as { + source: { + providerAccountIdentifier: Record + } + } + const { system, value } = fixture.source.providerAccountIdentifier + expect( + parseNormalizedProviderRecord({ + ...fixture, + source: { + ...fixture.source, + providerAccountIdentifier: { system, value }, + }, + }).ok, + ).toBe(false) + }) + + it('strictly parses the complete graph input and preserves identity strings verbatim', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const result = parseProviderMeasurementBundleInput({ + ...input, + source: { + ...input.source, + providerAccountIdentifier: { + ...input.source.providerAccountIdentifier, + value: ' pseudonym-with-significant-spaces ', + }, + }, + }) + expect(result.ok).toBe(true) + if (!result.ok) return + expect(result.value.source.providerAccountIdentifier.value).toBe( + ' pseudonym-with-significant-spaces ', + ) + + expect( + buildProviderMeasurementBundle({ + ...input, + rawVendorResponse: { heart_rate: 64 }, + } as ProviderMeasurementBundleInput & { + rawVendorResponse: unknown + }).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + rawVendorResponse: { heart_rate: 64 }, + }, + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + }) + + it('rejects an arbitrary adapter and a mismatched source token', () => { + const dataOrigin = { + identity: resourceIdentity('https://example.org/data-origins', 'oura'), + name: 'Oura', + } + expect( + parseNormalizedProviderRecord({ + source: { + adapter: { kind: 'mobile', provider: 'oura' }, + providerAccountIdentifier: { + system: 'https://example.org/deployments/provider-accounts', + value: 'pseudonym-oura-001', + assurance: 'deployment-scoped-pseudonym', + }, + sourceType: 'sleep', + sourceNativeId: 'sleep-1', + dataOrigin, + }, + measurements: [ + { + kind: 'sleep-duration', + value: 7.4, + effective: { kind: 'period', start, end }, + }, + ], + }).ok, + ).toBe(false) + + expect( + parseNormalizedProviderRecord({ + source: { + adapter: { kind: 'providers', provider: 'oura' }, + providerAccountIdentifier: { + system: 'https://example.org/deployments/provider-accounts', + value: 'pseudonym-oura-001', + assurance: 'deployment-scoped-pseudonym', + }, + sourceType: 'daily_readiness', + sourceNativeId: 'readiness-1', + dataOrigin, + }, + measurements: [heartRateMeasurement], + }).ok, + ).toBe(false) + }) + + it('correlates provider, source token, and measurement at compile time', () => { + const withingsSource = { + adapter: { kind: 'providers', provider: 'withings' }, + providerAccountIdentifier: { + system: uri('https://example.org/deployments/provider-accounts'), + value: 'pseudonym-withings-001', + assurance: 'deployment-scoped-pseudonym', + }, + sourceType: 'getmeas:11', + sourceNativeId: 'heart-rate-1', + dataOrigin: { + identity: resourceIdentity( + 'https://example.org/data-origins', + 'withings', + ), + name: 'Withings', + }, + } as const + const valid: ConnectedProviderRecord<'withings', 'getmeas:11'> = { + source: withingsSource, + measurements: [heartRateMeasurement], + } + expect(valid.measurements[0].kind).toBe('heart-rate') + + type OuraSleep = ConnectedProviderRecord<'oura', 'sleep'> + const source = { + ...withingsSource, + adapter: { kind: 'providers', provider: 'oura' }, + sourceType: 'sleep', + } as const + const invalidMeasurement = { + kind: 'blood-glucose', + value: 95, + effective: { kind: 'date-time', value: dateTime }, + specimen: { + identity: resourceIdentity('https://example.org/specimens', 'blood'), + }, + } + // @ts-expect-error Oura sleep admits sleep-duration, never glucose. + const invalid: OuraSleep = { source, measurements: [invalidMeasurement] } + expect(invalid).toBeDefined() + }) + + it('generates only catalog-owned scalar source tokens', () => { + expect(Object.hasOwn(providerScalarMappings.withings, 'getmeas:9')).toBe( + false, + ) + expect(providerScalarMappings.withings['getmeas:9+10']).toEqual({ + 'blood-pressure': 'blood-pressure-panel', + }) + }) +}) + +describe('Provider builder properties', () => { + it('preserves every finite positive heart-rate value', () => { + assert( + property( + double({ + min: 0.01, + max: 400, + noNaN: true, + noDefaultInfinity: true, + }), + (value) => { + const result = buildProviderMeasurementBundle( + baseInput('withings', 'getmeas:11', { + kind: 'heart-rate', + value, + effective: { kind: 'date-time', value: dateTime }, + }), + ) + expect(result.ok).toBe(true) + if (!result.ok) return + expect( + resources(result).find( + (resource) => resource.resourceType === 'Observation', + )?.valueQuantity?.value, + ).toBe(value) + }, + ), + ) + }) + + it('rejects every oxygen saturation outside the percentage range', () => { + assert( + property( + oneof( + double({ min: -10_000, max: -Number.MIN_VALUE, noNaN: true }), + double({ min: 100.000_001, max: 10_000, noNaN: true }), + ), + (value) => { + expect( + buildProviderMeasurementBundle( + baseInput('withings', 'getmeas:54', { + kind: 'oxygen-saturation', + value, + effective: { kind: 'date-time', value: dateTime }, + }), + ).ok, + ).toBe(false) + }, + ), + ) + }) +}) diff --git a/packages/fhir/test/questionnaire.test.ts b/packages/fhir/test/questionnaire.test.ts new file mode 100644 index 00000000..e8b01d42 --- /dev/null +++ b/packages/fhir/test/questionnaire.test.ts @@ -0,0 +1,2937 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { expectTypeOf } from 'expect-type' +import { assert, property, string } from 'fast-check' +import { + buildQuestionnaire, + buildQuestionnaireResponse, + parseAbsoluteUri, + parseCanonical, + parseFhirId, + parseFhirInstant, + parsePatientReference, + parseQuestionnaire, + parseQuestionnaireResponse, + parseSemVer, + preflightQuestionnairePair, + type GroveQuestionnaire, + type QuestionnaireInput, + type QuestionnairePreflightOptions, + type QuestionnaireResponseAnswerInput, + type QuestionnaireResponseInput, + type Result, +} from '../src/index.js' +import { + compareR4Temporal, + isR4Date, + isR4DateTime, + isR4Time, +} from '../src/questionnaire/temporal.js' + +/* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 canonicals are normative HTTP URIs. */ + +const unwrap = (result: Result): T => { + if (!result.ok) { + throw new Error(result.issues.map((entry) => entry.message).join('\n')) + } + return result.value +} + +const url = unwrap( + parseAbsoluteUri('https://example.org/fhir/Questionnaire/pain'), +) +const version = unwrap(parseSemVer('2.0.0')) +const questionnaireCanonical = unwrap(parseCanonical(`${url}|${version}`)) +const authored = unwrap(parseFhirInstant('2026-08-20T12:00:00-07:00')) +const subject = unwrap(parsePatientReference('Patient/example')) +const codingSystem = unwrap( + parseAbsoluteUri('https://example.org/CodeSystem/pain'), +) + +const questionnaireExtensions = { + calculatedExpression: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression', + maxDecimalPlaces: 'http://hl7.org/fhir/StructureDefinition/maxDecimalPlaces', + maxOccurs: 'http://hl7.org/fhir/StructureDefinition/questionnaire-maxOccurs', + maxQuantity: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-maxQuantity', + maxSize: 'http://hl7.org/fhir/StructureDefinition/maxSize', + maxValue: 'http://hl7.org/fhir/StructureDefinition/maxValue', + hidden: 'http://hl7.org/fhir/StructureDefinition/questionnaire-hidden', + itemWeight: 'http://hl7.org/fhir/StructureDefinition/itemWeight', + mimeType: 'http://hl7.org/fhir/StructureDefinition/mimeType', + minLength: 'http://hl7.org/fhir/StructureDefinition/minLength', + minOccurs: 'http://hl7.org/fhir/StructureDefinition/questionnaire-minOccurs', + minQuantity: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-minQuantity', + minValue: 'http://hl7.org/fhir/StructureDefinition/minValue', + optionExclusive: + 'http://hl7.org/fhir/StructureDefinition/questionnaire-optionExclusive', + targetConstraint: 'http://hl7.org/fhir/StructureDefinition/targetConstraint', + unitOption: + 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption', + unitValueSet: + 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitValueSet', + variable: 'http://hl7.org/fhir/StructureDefinition/variable', +} as const + +const targetConstraint = ( + key: string, + severity: 'error' | 'warning' = 'error', +) => ({ + url: questionnaireExtensions.targetConstraint, + extension: [ + { url: 'key', valueId: key }, + { url: 'severity', valueCode: severity }, + { url: 'human', valueString: `Satisfy ${key}.` }, + { + url: 'expression', + valueExpression: { + language: 'text/fhirpath', + expression: 'true', + }, + }, + ], +}) + +const questionnaireInput = { + url, + version, + name: 'PainQuestionnaire', + title: 'Pain Questionnaire', + status: 'active', + subjectTypes: ['Patient'], + items: [ + { + linkId: 'health', + text: 'Health', + type: 'group', + required: true, + item: [ + { + linkId: 'has-pain', + text: 'Are you in pain?', + type: 'boolean', + required: true, + item: [ + { + linkId: 'pain-severity', + text: 'How severe is the pain?', + type: 'choice', + required: true, + enableWhen: [ + { + question: 'has-pain', + operator: '=', + answerBoolean: true, + }, + ], + answerOption: [ + { valueCoding: { system: codingSystem, code: 'mild' } }, + { valueCoding: { system: codingSystem, code: 'severe' } }, + ], + }, + ], + }, + ], + }, + { + linkId: 'notes', + text: 'Additional notes', + type: 'text', + }, + ], +} as const satisfies QuestionnaireInput + +const responseInput = (note = 'Rest helps.') => + ({ + questionnaire: questionnaireCanonical, + identifier: { + system: unwrap(parseAbsoluteUri('https://example.org/submissions')), + value: 'submission-1', + }, + status: 'completed', + subject, + authored, + items: [ + { + linkId: 'health', + text: 'Health', + item: [ + { + linkId: 'has-pain', + text: 'Are you in pain?', + answer: [ + { + valueBoolean: true, + item: [ + { + linkId: 'pain-severity', + text: 'How severe is the pain?', + answer: [ + { + valueCoding: { system: codingSystem, code: 'mild' }, + }, + ], + }, + ], + }, + ], + }, + ], + }, + { + linkId: 'notes', + text: 'Additional notes', + answer: [{ valueString: note }], + }, + ], + }) as const satisfies QuestionnaireResponseInput + +const questionnaire = unwrap(buildQuestionnaire(questionnaireInput)) +const response = unwrap(buildQuestionnaireResponse(responseInput())) + +const preflightItems = ( + questionnaireItems: ReadonlyArray>>, + responseItems: ReadonlyArray>>, + options: QuestionnairePreflightOptions = {}, + status: 'amended' | 'completed' | 'in-progress' = 'completed', +) => + preflightQuestionnairePair( + { ...questionnaire, item: questionnaireItems }, + { ...response, status, item: responseItems }, + options, + ) + +describe('Questionnaire R4 builders', () => { + it('stamps the exact Grove profiles and owned extensions', () => { + expect(questionnaire.meta?.profile).toEqual([ + 'https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire', + ]) + expect(questionnaire.extension).toEqual([ + { + url: 'http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm', + valueCoding: { + system: 'http://hl7.org/fhir/version-algorithm', + code: 'semver', + }, + }, + ]) + expect(response.meta?.profile).toEqual([ + 'https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response', + ]) + expect(response.extension).toEqual([ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode', + valueCodeableConcept: { + coding: [ + { + system: + 'http://terminology.hl7.org/CodeSystem/v3-ParticipationMode', + code: 'ELECTRONIC', + }, + ], + }, + }, + ]) + }) + + it('returns immutable, plain R4 JSON', () => { + expect(Object.isFrozen(questionnaire)).toBe(true) + expect(Object.isFrozen(questionnaire.item[0]?.item)).toBe(true) + expect(Object.isFrozen(response)).toBe(true) + expect(JSON.parse(JSON.stringify(questionnaire))).toEqual(questionnaire) + expectTypeOf(questionnaire).toExtend() + }) + + it('preserves admitted FHIR primitive metadata at every supported level', () => { + const input = { + ...questionnaire, + _title: { + extension: [ + { + url: 'https://example.org/fhir/StructureDefinition/localized-value', + valueString: 'en-US', + }, + ], + }, + item: questionnaire.item.map((item, index) => + index === 0 ? + { + ...item, + _text: { id: 'health-text-metadata' }, + } + : item, + ), + } + + const parsed = parseQuestionnaire(input) + expect(parsed.ok).toBe(true) + if (!parsed.ok) return + expect(parsed.value._title).toEqual(input._title) + expect(parsed.value.item[0]?._text).toEqual({ + id: 'health-text-metadata', + }) + }) + + it('rejects unknown fields and reference questions instead of stripping them', () => { + expect( + parseQuestionnaire({ ...questionnaire, vendorPayload: true }).ok, + ).toBe(false) + const referenceQuestionnaire = { + ...questionnaire, + item: [{ linkId: 'person', text: 'Person', type: 'reference' }], + } + expect(parseQuestionnaire(referenceQuestionnaire).ok).toBe(false) + }) + + it('rejects caller attempts to replace owned profile extensions', () => { + const result = buildQuestionnaire({ + ...questionnaireInput, + extensions: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm', + valueCoding: { + system: 'http://hl7.org/fhir/version-algorithm', + code: 'semver', + }, + }, + ], + }) + expect(result.ok).toBe(false) + }) + + it('rejects duplicated linkIds before construction', () => { + const result = buildQuestionnaire({ + ...questionnaireInput, + items: [questionnaireInput.items[0], questionnaireInput.items[0]], + }) + expect(result.ok).toBe(false) + if (result.ok) return + expect(result.issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ code: 'duplicate-identifier' }), + ]), + ) + }) + + it('returns Result failures for malformed JavaScript inputs without throwing', () => { + const malformedQuestionnaires = [ + undefined, + null, + {}, + { ...questionnaireInput, unknownRootField: true }, + { + ...questionnaireInput, + items: [ + { + linkId: 'strict-item', + text: 'Strict item', + type: 'string', + unknownItemField: true, + }, + ], + }, + ] + for (const candidate of malformedQuestionnaires) { + const invoke = () => buildQuestionnaire(candidate as QuestionnaireInput) + expect(invoke).not.toThrow() + expect(invoke().ok).toBe(false) + } + + const malformedResponses = [ + undefined, + null, + {}, + { ...responseInput(), unknownRootField: true }, + { + ...responseInput(), + items: [ + { + linkId: 'strict-item', + text: 'Strict item', + answer: [{ valueString: 'answer', unknownAnswerField: true }], + }, + ], + }, + ] + for (const candidate of malformedResponses) { + const invoke = () => + buildQuestionnaireResponse(candidate as QuestionnaireResponseInput) + expect(invoke).not.toThrow() + expect(invoke().ok).toBe(false) + } + }) + + it.each([ + [ + 'an empty group', + { linkId: 'empty-group', text: 'Empty group', type: 'group' }, + ], + [ + 'required on a display item', + { + linkId: 'required-display', + text: 'Display', + type: 'display', + required: false, + }, + ], + [ + 'children on a display item', + { + linkId: 'parent-display', + text: 'Display', + type: 'display', + item: [{ linkId: 'child', text: 'Child', type: 'string' }], + }, + ], + [ + 'answerOption together with initial', + { + linkId: 'choice', + text: 'Choice', + type: 'choice', + answerOption: [ + { valueCoding: { system: codingSystem, code: 'option' } }, + ], + initial: [{ valueCoding: { system: codingSystem, code: 'option' } }], + }, + ], + ] as const)('rejects %s', (_name, item) => { + const result = buildQuestionnaire({ + ...questionnaireInput, + items: [item], + } as unknown as QuestionnaireInput) + expect(result.ok).toBe(false) + }) + + it('enforces base R4 Questionnaire and response item invariants when parsing', () => { + expect( + parseQuestionnaire({ + ...questionnaire, + item: [{ linkId: 'empty-group', type: 'group' }], + }).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + item: [ + { + linkId: 'display', + text: 'Display', + type: 'display', + required: false, + }, + ], + }).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + item: [ + { + linkId: 'choice', + text: 'Choice', + type: 'choice', + answerOption: [{ valueString: 'option' }], + initial: [{ valueString: 'option' }], + }, + ], + }).ok, + ).toBe(false) + + const mixedItem = { + linkId: 'mixed', + text: 'Mixed', + answer: [{ valueString: 'answer' }], + item: [{ linkId: 'child', text: 'Child' }], + } + expect( + parseQuestionnaireResponse({ ...response, item: [mixedItem] }).ok, + ).toBe(false) + expect( + buildQuestionnaireResponse({ + ...responseInput(), + items: [mixedItem], + }).ok, + ).toBe(false) + }) + + it('requires the exact Grove profiles at every public parse boundary', () => { + const unprofiledQuestionnaire = { ...questionnaire, meta: undefined } + const unprofiledResponse = { ...response, meta: undefined } + expect(parseQuestionnaire(unprofiledQuestionnaire).ok).toBe(false) + expect(parseQuestionnaireResponse(unprofiledResponse).ok).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + meta: { + profile: [ + ...(questionnaire.meta?.profile ?? []), + 'https://example.org/fhir/StructureDefinition/extra', + ], + }, + }).ok, + ).toBe(false) + expect( + parseQuestionnaireResponse({ + ...response, + meta: { + profile: [ + ...(response.meta?.profile ?? []), + 'https://example.org/fhir/StructureDefinition/extra', + ], + }, + }).ok, + ).toBe(false) + expect( + preflightQuestionnairePair(unprofiledQuestionnaire, response).ok, + ).toBe(false) + expect( + preflightQuestionnairePair(questionnaire, unprofiledResponse).ok, + ).toBe(false) + }) + + it.each([ + [ + 'code on display', + { + linkId: 'display-code', + text: 'Display', + type: 'display', + code: [{ system: codingSystem, code: 'display' }], + }, + ], + [ + 'readOnly on display', + { + linkId: 'display-read-only', + text: 'Display', + type: 'display', + readOnly: false, + }, + ], + [ + 'answerOption on boolean', + { + linkId: 'boolean-options', + text: 'Boolean', + type: 'boolean', + answerOption: [{ valueString: 'not-admitted' }], + }, + ], + [ + 'answerValueSet on attachment', + { + linkId: 'attachment-value-set', + text: 'Attachment', + type: 'attachment', + answerValueSet: 'https://example.org/fhir/ValueSet/not-admitted', + }, + ], + [ + 'a non-boolean exists operand', + { + linkId: 'exists', + text: 'Exists', + type: 'string', + enableWhen: [ + { + question: 'exists', + operator: 'exists', + answerString: 'true', + }, + ], + }, + ], + [ + 'initial on group', + { + linkId: 'group-initial', + text: 'Group', + type: 'group', + initial: [{ valueString: 'not-admitted' }], + item: [{ linkId: 'child', text: 'Child', type: 'string' }], + }, + ], + [ + 'multiple initial values without repeats', + { + linkId: 'multiple-initial', + text: 'Multiple initial', + type: 'string', + initial: [{ valueString: 'one' }, { valueString: 'two' }], + }, + ], + [ + 'a linkId longer than 255 code points', + { + linkId: '😀'.repeat(256), + text: 'Long linkId', + type: 'string', + }, + ], + ] as const)('enforces %s', (_name, item) => { + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [item], + } as unknown as QuestionnaireInput).ok, + ).toBe(false) + }) + + it('requires authored initial and answerOption values to match item type', () => { + for (const item of [ + { + linkId: 'integer-initial', + text: 'Integer initial', + type: 'integer', + initial: [{ valueString: '1' }], + }, + { + linkId: 'date-initial', + text: 'Date initial', + type: 'date', + initial: [{ valueTime: '12:00:00' }], + }, + { + linkId: 'choice-option', + text: 'Choice option', + type: 'choice', + answerOption: [{ valueString: 'uncoded' }], + }, + { + linkId: 'integer-option', + text: 'Integer option', + type: 'integer', + answerOption: [{ valueDate: '2026-08-20' }], + }, + ] as const) { + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [item], + } as unknown as QuestionnaireInput).ok, + ).toBe(false) + } + + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'open-choice-option', + text: 'Open choice option', + type: 'open-choice', + answerOption: [{ valueString: 'uncoded' }], + }, + { + linkId: 'integer-initial', + text: 'Integer initial', + type: 'integer', + initial: [{ valueInteger: 1 }], + }, + ], + }).ok, + ).toBe(true) + }) + + it('limits answerValueSet to choice and open-choice items', () => { + for (const type of ['integer', 'date', 'string'] as const) { + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: `${type}-value-set`, + text: `${type} value set`, + type, + answerValueSet: 'https://example.org/fhir/ValueSet/options', + }, + ], + }).ok, + ).toBe(false) + } + + for (const type of ['choice', 'open-choice'] as const) { + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: `${type}-value-set`, + text: `${type} value set`, + type, + answerValueSet: 'https://example.org/fhir/ValueSet/options', + }, + ], + }).ok, + ).toBe(true) + } + + expect( + parseQuestionnaire({ + ...questionnaire, + item: [ + { + linkId: 'string-value-set', + text: 'String value set', + type: 'string', + answerValueSet: 'https://example.org/fhir/ValueSet/options', + }, + ], + }).ok, + ).toBe(false) + }) + + it('rejects valueless Extension objects throughout builder inputs', () => { + expect( + buildQuestionnaire({ + ...questionnaireInput, + extensions: [ + { url: 'https://example.org/fhir/StructureDefinition/empty' }, + ], + }).ok, + ).toBe(false) + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'empty-extension', + text: 'Empty extension', + type: 'string', + extension: [ + { url: 'https://example.org/fhir/StructureDefinition/empty' }, + ], + }, + ], + }).ok, + ).toBe(false) + expect( + buildQuestionnaireResponse({ + ...responseInput(), + extensions: [ + { url: 'https://example.org/fhir/StructureDefinition/empty' }, + ], + }).ok, + ).toBe(false) + }) + + it('enforces R4 ResourceType subject codes and signed integer bounds', () => { + expect( + buildQuestionnaire({ + ...questionnaireInput, + subjectTypes: ['Banana'], + }).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + subjectType: ['Banana'], + }).ok, + ).toBe(false) + + for (const valueInteger of [2_147_483_648, -2_147_483_649]) { + expect( + preflightItems( + [{ linkId: 'integer', text: 'Integer', type: 'integer' }], + [ + { + linkId: 'integer', + text: 'Integer', + answer: [{ valueInteger }], + }, + ], + ).ok, + ).toBe(false) + } + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'integer-option', + text: 'Integer option', + type: 'integer', + answerOption: [{ valueInteger: 2_147_483_648 }], + }, + ], + }).ok, + ).toBe(false) + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'long-text', + text: 'Long text', + type: 'string', + maxLength: 2_147_483_648, + }, + ], + }).ok, + ).toBe(false) + }) + + it('enforces hidden and itemWeight slice cardinality and value types', () => { + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'duplicate-hidden', + text: 'Duplicate hidden', + type: 'string', + extension: [ + { url: questionnaireExtensions.hidden, valueBoolean: true }, + { url: questionnaireExtensions.hidden, valueBoolean: false }, + ], + }, + ], + }).ok, + ).toBe(false) + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'wrong-hidden', + text: 'Wrong hidden', + type: 'string', + extension: [ + { url: questionnaireExtensions.hidden, valueString: 'true' }, + ], + }, + ], + }).ok, + ).toBe(false) + + expect( + buildQuestionnaireResponse({ + ...responseInput(), + items: [ + { + linkId: 'answer-weight', + text: 'Answer weight', + answer: [ + { + extension: [ + { + url: questionnaireExtensions.itemWeight, + valueInteger: 1, + }, + ], + valueCoding: { system: codingSystem, code: 'answer-weight' }, + }, + ], + }, + ], + }).ok, + ).toBe(false) + expect( + buildQuestionnaireResponse({ + ...responseInput(), + items: [ + { + linkId: 'duplicate-answer-weight', + text: 'Duplicate answer weight', + answer: [ + { + extension: [ + { + url: questionnaireExtensions.itemWeight, + valueDecimal: 1, + }, + { + url: questionnaireExtensions.itemWeight, + valueDecimal: 2, + }, + ], + valueCoding: { + system: codingSystem, + code: 'duplicate-answer-weight', + }, + }, + ], + }, + ], + }).ok, + ).toBe(false) + + const weightedCoding = { + system: codingSystem, + code: 'weighted', + extension: [{ url: questionnaireExtensions.itemWeight, valueInteger: 1 }], + } + const duplicateWeightedCoding = { + system: codingSystem, + code: 'duplicate-weight', + extension: [ + { url: questionnaireExtensions.itemWeight, valueDecimal: 1 }, + { url: questionnaireExtensions.itemWeight, valueDecimal: 2 }, + ], + } + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'wrong-weight', + text: 'Wrong weight', + type: 'choice', + answerOption: [{ valueCoding: weightedCoding }], + }, + ], + }).ok, + ).toBe(false) + expect( + buildQuestionnaireResponse({ + ...responseInput(), + items: [ + { + linkId: 'weighted', + text: 'Weighted', + answer: [{ valueCoding: weightedCoding }], + }, + ], + }).ok, + ).toBe(false) + expect( + buildQuestionnaireResponse({ + ...responseInput(), + items: [ + { + linkId: 'duplicate-weight', + text: 'Duplicate weight', + answer: [{ valueCoding: duplicateWeightedCoding }], + }, + ], + }).ok, + ).toBe(false) + }) + + it('rejects empty answer value elements', () => { + for (const [type, answer] of [ + ['string', { valueString: '' }], + ['url', { valueUri: '' }], + ['choice', { valueCoding: {} }], + ['quantity', { valueQuantity: {} }], + ['attachment', { valueAttachment: {} }], + ] as const) { + expect( + preflightItems( + [{ linkId: 'empty', text: 'Empty', type }], + [{ linkId: 'empty', text: 'Empty', answer: [answer] }], + ).ok, + ).toBe(false) + } + }) + + it('enforces answered text and qrs-2 outside pair preflight', () => { + const duplicateAnswers = [ + { + linkId: 'duplicate', + text: 'Duplicate', + answer: [{ valueString: 'one' }], + }, + { + linkId: 'duplicate', + text: 'Duplicate', + answer: [{ valueString: 'two' }], + }, + ] + expect( + buildQuestionnaireResponse({ + ...responseInput(), + items: duplicateAnswers, + }).ok, + ).toBe(false) + expect( + parseQuestionnaireResponse({ + ...response, + item: duplicateAnswers, + }).ok, + ).toBe(false) + + expect( + parseQuestionnaireResponse({ + ...response, + item: [ + { + linkId: 'answered-without-text', + answer: [{ valueString: 'answer' }], + }, + ], + }).ok, + ).toBe(false) + }) + + it('validates authored and answer temporal values as R4 primitives', () => { + for (const invalidAuthored of [ + '2026-08-20', + '2026-08-20T12:00:00', + '2026-02-29T12:00:00Z', + '2026-08-20T24:00:00Z', + ]) { + expect( + parseQuestionnaireResponse({ + ...response, + authored: invalidAuthored, + }).ok, + ).toBe(false) + } + + for (const [type, answer] of [ + ['date', { valueDate: '2026-02-29' }], + ['dateTime', { valueDateTime: '2026-08-20T12:00:00' }], + ['dateTime', { valueDateTime: '2026-13' }], + ['time', { valueTime: '24:00:00' }], + ] as const) { + expect( + preflightItems( + [{ linkId: 'temporal', text: 'Temporal', type }], + [ + { + linkId: 'temporal', + text: 'Temporal', + answer: [answer], + }, + ], + ).ok, + ).toBe(false) + } + + expect( + preflightItems( + [ + { linkId: 'partial-date', text: 'Date', type: 'date' }, + { + linkId: 'partial-date-time', + text: 'Date time', + type: 'dateTime', + }, + ], + [ + { + linkId: 'partial-date', + text: 'Date', + answer: [{ valueDate: '2026-08' }], + }, + { + linkId: 'partial-date-time', + text: 'Date time', + answer: [{ valueDateTime: '2026' }], + }, + ], + ).ok, + ).toBe(true) + }) + + it('covers R4 temporal validation and comparison boundaries', () => { + for (const candidate of [ + '0000', + '2026-00', + '2026-13', + '2026-02-29', + '2024-04-31', + 'not-a-date', + ]) { + expect(isR4Date(candidate)).toBe(false) + } + for (const candidate of [ + '2026', + '2026-08', + '2024-02-29', + '2026-04-30', + '2026-01-31', + ]) { + expect(isR4Date(candidate)).toBe(true) + } + expect(isR4Date(undefined)).toBe(false) + + for (const candidate of ['24:00:00', '12:60:00', '12:00:61', '12:00']) { + expect(isR4Time(candidate)).toBe(false) + } + expect(isR4Time(null)).toBe(false) + expect(isR4Time('00:00:00')).toBe(true) + expect(isR4Time('23:59:60.123')).toBe(true) + + expect(isR4DateTime(null)).toBe(false) + expect(isR4DateTime('2026-08')).toBe(true) + expect(isR4DateTime('2026-02-29T12:00:00Z')).toBe(false) + expect(isR4DateTime('2026-08-20T12:00:00+14:01')).toBe(false) + expect(isR4DateTime('2026-08-20T12:00:00Z')).toBe(true) + + expect(compareR4Temporal('bad', '2026', 'date')).toBeUndefined() + expect(compareR4Temporal('2026', 'bad', 'date')).toBeUndefined() + expect(compareR4Temporal('2026', '2027', 'date')).toBe(-1) + expect(compareR4Temporal('2027', '2026', 'date')).toBe(1) + expect(compareR4Temporal('2026', '2026', 'date')).toBe(0) + expect(compareR4Temporal('2026-02', '2026-03', 'date')).toBe(-1) + expect(compareR4Temporal('2026-03', '2026-02', 'date')).toBe(1) + expect(compareR4Temporal('2026-02-02', '2026-02-01', 'date')).toBe(1) + expect(compareR4Temporal('2026-02-01', '2026-02-01', 'date')).toBe(0) + expect(compareR4Temporal('2026', '2026-01', 'date')).toBeUndefined() + + expect(compareR4Temporal('bad', '12:00:00', 'time')).toBeUndefined() + expect(compareR4Temporal('12:00:00', 'bad', 'time')).toBeUndefined() + expect(compareR4Temporal('11:00:00', '12:00:00', 'time')).toBe(-1) + expect(compareR4Temporal('12:01:00', '12:00:00', 'time')).toBe(1) + expect(compareR4Temporal('12:00:01', '12:00:00', 'time')).toBe(1) + expect(compareR4Temporal('12:00:00.01', '12:00:00.1', 'time')).toBe(-1) + expect(compareR4Temporal('12:00:00.10', '12:00:00.1', 'time')).toBe(0) + + const instant = '2026-08-20T12:00:00Z' + expect(compareR4Temporal('bad', instant, 'dateTime')).toBeUndefined() + expect(compareR4Temporal(instant, 'bad', 'dateTime')).toBeUndefined() + expect(compareR4Temporal('2026-08', instant, 'dateTime')).toBeUndefined() + expect(compareR4Temporal(instant, '2026-08', 'dateTime')).toBeUndefined() + expect( + compareR4Temporal( + '2026-08-20T12:00:00+02:00', + '2026-08-20T10:30:00Z', + 'dateTime', + ), + ).toBe(-1) + expect(compareR4Temporal(instant, instant, 'dateTime')).toBe(0) + expect(compareR4Temporal('2026-08', '2026-09', 'dateTime')).toBe(-1) + expect(compareR4Temporal('2026', '2026-08', 'dateTime')).toBeUndefined() + }) + + it('validates Questionnaire lifecycle dates and effectivePeriod locally', () => { + expect( + parseQuestionnaire({ ...questionnaire, date: '2026-02-29' }).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + approvalDate: '2026-02-29', + }).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + lastReviewDate: '2026-08-20T12:00:00Z', + }).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + effectivePeriod: { start: 'not-a-date-time' }, + }).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + effectivePeriod: { + start: '2026-08-20T12:00:00Z', + end: '2026-08-20T13:00:00+02:00', + }, + }).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + date: '2026-08', + approvalDate: '2026-08-20', + lastReviewDate: '2026-08', + effectivePeriod: { + start: '2026-08-20T12:00:00+02:00', + end: '2026-08-20T11:00:00Z', + }, + }).ok, + ).toBe(true) + }) + + it('rejects duplicate and wrong-typed portable constraint extensions', () => { + const duplicate = buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'duplicate-minimum', + text: 'Duplicate minimum', + type: 'string', + extension: [ + { url: questionnaireExtensions.minLength, valueInteger: 1 }, + { url: questionnaireExtensions.minLength, valueInteger: 2 }, + ], + }, + ], + }) + expect(duplicate.ok).toBe(false) + if (!duplicate.ok) { + expect(duplicate.issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ code: 'duplicate-identifier' }), + ]), + ) + } + + for (const item of [ + { + linkId: 'wrong-minimum-length', + text: 'Wrong minimum length', + type: 'string', + extension: [ + { url: questionnaireExtensions.minLength, valueString: '1' }, + ], + }, + { + linkId: 'wrong-value-bound', + text: 'Wrong value bound', + type: 'integer', + extension: [ + { url: questionnaireExtensions.minValue, valueString: '1' }, + ], + }, + { + linkId: 'wrong-maximum-size', + text: 'Wrong maximum size', + type: 'attachment', + extension: [ + { url: questionnaireExtensions.maxSize, valueInteger: 1024 }, + ], + }, + { + linkId: 'wrong-unit-option', + text: 'Wrong unit option', + type: 'quantity', + extension: [ + { url: questionnaireExtensions.unitOption, valueString: 'kg' }, + ], + }, + { + linkId: 'wrong-exclusive-option', + text: 'Wrong exclusive option', + type: 'choice', + answerOption: [ + { + extension: [ + { + url: questionnaireExtensions.optionExclusive, + valueString: 'true', + }, + ], + valueCoding: { system: codingSystem, code: 'none' }, + }, + ], + }, + ] as const) { + const result = buildQuestionnaire({ + ...questionnaireInput, + items: [item], + } as unknown as QuestionnaireInput) + expect(result.ok).toBe(false) + if (!result.ok) { + expect(result.issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ code: 'invalid-type' }), + ]), + ) + } + } + }) + + it('accepts the complete portable constraint and expression surface', () => { + const result = buildQuestionnaire({ + ...questionnaireInput, + extensions: [ + { + url: questionnaireExtensions.variable, + valueExpression: { + language: 'text/fhirpath', + name: 'rootScore', + expression: '0', + }, + }, + targetConstraint('root-valid'), + ], + items: [ + { + linkId: 'text', + text: 'Text', + type: 'text', + maxLength: 8, + extension: [ + { url: questionnaireExtensions.minLength, valueInteger: 2 }, + ], + }, + { + linkId: 'decimal', + text: 'Decimal', + type: 'decimal', + extension: [ + { url: questionnaireExtensions.minValue, valueDecimal: 1 }, + { url: questionnaireExtensions.maxValue, valueDecimal: 5 }, + { + url: questionnaireExtensions.maxDecimalPlaces, + valueInteger: 2, + }, + ], + }, + { + linkId: 'quantity', + text: 'Quantity', + type: 'quantity', + extension: [ + { + url: questionnaireExtensions.minQuantity, + valueQuantity: { + value: 1, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + { + url: questionnaireExtensions.maxQuantity, + valueQuantity: { + value: 5, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + { + url: questionnaireExtensions.unitOption, + valueCoding: { + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + ], + }, + { + linkId: 'attachment', + text: 'Attachment', + type: 'attachment', + repeats: true, + extension: [ + { url: questionnaireExtensions.mimeType, valueCode: 'image/png' }, + { url: questionnaireExtensions.maxSize, valueDecimal: 1024 }, + { url: questionnaireExtensions.minOccurs, valueInteger: 1 }, + { url: questionnaireExtensions.maxOccurs, valueInteger: 2 }, + { + url: questionnaireExtensions.variable, + valueExpression: { + language: 'text/fhirpath', + name: 'localScore', + expression: '0', + }, + }, + targetConstraint('item-valid'), + ], + }, + ], + }) + expect(result.ok).toBe(true) + }) + + it.each([ + [ + 'text length placement', + { + linkId: 'invalid', + text: 'Invalid', + type: 'boolean', + extension: [ + { url: questionnaireExtensions.minLength, valueInteger: 1 }, + ], + }, + 'invalid-choice', + ], + [ + 'text bound order', + { + linkId: 'invalid', + text: 'Invalid', + type: 'string', + maxLength: 2, + extension: [ + { url: questionnaireExtensions.minLength, valueInteger: 3 }, + ], + }, + 'out-of-range', + ], + [ + 'decimal placement', + { + linkId: 'invalid', + text: 'Invalid', + type: 'integer', + extension: [ + { + url: questionnaireExtensions.maxDecimalPlaces, + valueInteger: 2, + }, + ], + }, + 'invalid-choice', + ], + [ + 'value bound datatype', + { + linkId: 'invalid', + text: 'Invalid', + type: 'date', + extension: [{ url: questionnaireExtensions.minValue, valueInteger: 1 }], + }, + 'invalid-type', + ], + [ + 'quantity bound order', + { + linkId: 'invalid', + text: 'Invalid', + type: 'quantity', + extension: [ + { + url: questionnaireExtensions.minQuantity, + valueQuantity: { + value: 5, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + { + url: questionnaireExtensions.maxQuantity, + valueQuantity: { + value: 1, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + ], + }, + 'out-of-range', + ], + [ + 'unit placement', + { + linkId: 'invalid', + text: 'Invalid', + type: 'string', + extension: [ + { + url: questionnaireExtensions.unitOption, + valueCoding: { + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + ], + }, + 'invalid-choice', + ], + [ + 'attachment placement', + { + linkId: 'invalid', + text: 'Invalid', + type: 'string', + extension: [ + { url: questionnaireExtensions.mimeType, valueCode: 'image/png' }, + ], + }, + 'invalid-choice', + ], + [ + 'occurrence placement', + { + linkId: 'invalid', + text: 'Invalid', + type: 'choice', + extension: [ + { url: questionnaireExtensions.minOccurs, valueInteger: 1 }, + ], + }, + 'invalid-choice', + ], + ] as const)('rejects invalid %s constraints', (_name, item, code) => { + const result = buildQuestionnaire({ + ...questionnaireInput, + items: [item], + } as unknown as QuestionnaireInput) + expect(result.ok).toBe(false) + if (result.ok) return + expect(result.issues).toEqual( + expect.arrayContaining([expect.objectContaining({ code })]), + ) + }) + + it('rejects malformed expressions, reserved variables, and duplicate target keys', () => { + const result = buildQuestionnaire({ + ...questionnaireInput, + extensions: [ + { + url: questionnaireExtensions.variable, + valueExpression: { + language: 'text/fhirpath', + name: 'context', + expression: '0', + }, + }, + targetConstraint('duplicate-key'), + ], + items: [ + { + linkId: 'invalid-expression', + text: 'Invalid expression', + type: 'string', + extension: [ + { + url: questionnaireExtensions.calculatedExpression, + valueExpression: { + language: 'text/cql', + expression: ' ', + }, + }, + targetConstraint('duplicate-key'), + ], + }, + ], + }) + expect(result.ok).toBe(false) + if (result.ok) return + expect(new Set(result.issues.map(({ code }) => code))).toEqual( + new Set(['invalid-code', 'invalid-type', 'duplicate-identifier']), + ) + }) + + it('reports the complete bounded Questionnaire input validation surface', () => { + const result = buildQuestionnaire({ + ...questionnaireInput, + url: '/relative', + version: '01.0.0', + id: 'invalid/id', + date: 'not-an-instant', + subjectTypes: ['Patient', ''], + extensions: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/rendering-styleSensitive', + valueBoolean: true, + }, + ], + items: [ + { linkId: '', type: 'string', repeats: true }, + { + linkId: 'exclusive', + text: 'Exclusive rules', + type: 'choice', + enableWhen: [ + { question: 'a', operator: 'exists', answerBoolean: true }, + { question: 'b', operator: 'exists', answerBoolean: true }, + ], + initial: [{ valueString: 'initial' }], + answerOption: [{ valueString: 'option' }], + answerValueSet: 'https://example.org/ValueSet/options', + extension: [ + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression', + valueExpression: { + language: 'text/fhirpath', + expression: 'true', + }, + }, + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', + valueExpression: { + language: 'text/fhirpath', + expression: "'x'", + }, + }, + { + url: 'http://hl7.org/fhir/StructureDefinition/rendering-styleSensitive', + valueBoolean: true, + }, + ], + }, + ], + } as unknown as QuestionnaireInput) + expect(result.ok).toBe(false) + if (result.ok) return + expect(new Set(result.issues.map(({ code }) => code))).toEqual( + new Set([ + 'invalid-uri', + 'invalid-code', + 'invalid-type', + 'invalid-identifier', + 'invalid-date-time', + 'missing-required', + 'invalid-choice', + 'invalid-reference', + ]), + ) + }) + + it('preserves every admitted optional Questionnaire and response field', () => { + const fullQuestionnaire = buildQuestionnaire({ + ...questionnaireInput, + id: unwrap(parseFhirId('questionnaire-1')), + date: authored, + description: 'A complete test instrument.', + purpose: 'Coverage of bounded optional fields.', + extensions: [ + { + url: 'https://example.org/fhir/StructureDefinition/instrument-note', + valueString: 'note', + }, + ], + }) + expect(fullQuestionnaire.ok).toBe(true) + if (!fullQuestionnaire.ok) return + expect(fullQuestionnaire.value.id).toBe('questionnaire-1') + expect(fullQuestionnaire.value.description).toBe( + 'A complete test instrument.', + ) + + const fullResponse = buildQuestionnaireResponse({ + ...responseInput(), + id: unwrap(parseFhirId('response-1')), + authorReference: 'Practitioner/author', + sourceReference: 'Patient/example', + extensions: [ + { + url: 'https://example.org/fhir/StructureDefinition/response-note', + valueString: 'note', + }, + ], + }) + expect(fullResponse.ok).toBe(true) + if (!fullResponse.ok) return + expect(fullResponse.value.author?.reference).toBe('Practitioner/author') + expect(fullResponse.value.source?.reference).toBe('Patient/example') + }) + + it('reports the complete bounded QuestionnaireResponse input validation surface', () => { + const result = buildQuestionnaireResponse({ + questionnaire: 'https://example.org/Questionnaire/pain', + identifier: { system: '/relative', value: ' ' }, + status: 'completed', + id: 'invalid/id', + subject: 'Patient/example/_history/2', + authored: 'not-an-instant', + authorReference: ' ', + sourceReference: '', + extensions: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode', + valueCodeableConcept: { text: 'duplicate' }, + }, + ], + items: [ + { + linkId: 'parent', + item: [ + { + linkId: 'direct-child', + answer: [{ valueString: 'missing repeated text' }], + }, + ], + answer: [ + { + valueString: 'parent answer', + item: [ + { + linkId: 'answer-child', + answer: [{ valueString: 'also missing repeated text' }], + }, + ], + }, + ], + }, + ], + } as unknown as QuestionnaireResponseInput) + expect(result.ok).toBe(false) + if (result.ok) return + expect(new Set(result.issues.map(({ code }) => code))).toEqual( + new Set([ + 'invalid-uri', + 'invalid-code', + 'invalid-identifier', + 'invalid-date-time', + 'invalid-reference', + 'duplicate-identifier', + 'missing-required', + ]), + ) + }) +}) + +describe('Questionnaire pair preflight', () => { + it('accepts an exact, nested Questionnaire/Response pair', () => { + const result = preflightQuestionnairePair(questionnaire, response) + expect(result.ok).toBe(true) + }) + + it('requires the authoritative version and completion envelopes', () => { + const incompleteQuestionnaire = { + ...questionnaire, + version: 'not-semver', + extension: [], + } + const incompleteResponse = { + ...response, + questionnaire: `${url}|not-semver`, + extension: [], + } + + const result = preflightQuestionnairePair( + incompleteQuestionnaire, + incompleteResponse, + ) + expect(result.ok).toBe(false) + if (result.ok) return + expect(new Set(result.issues.map(({ code }) => code))).toEqual( + new Set(['invalid-code', 'invalid-uri', 'missing-required']), + ) + expect( + result.issues.some( + ({ path, message }) => + path.join('.') === 'questionnaire.extension' && + message.includes('version-algorithm'), + ), + ).toBe(true) + expect( + result.issues.some( + ({ path, message }) => + path.join('.') === 'response.extension' && + message.includes('completion-mode'), + ), + ).toBe(true) + }) + + it('rejects a mismatched instrument version', () => { + const result = preflightQuestionnairePair(questionnaire, { + ...response, + questionnaire: `${url}|2.0.1`, + }) + expect(result.ok).toBe(false) + if (result.ok) return + expect(result.issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ code: 'value-mismatch' }), + ]), + ) + }) + + it('rejects an answer with the wrong datatype', () => { + const raw = structuredClone(response) + const answer = raw.item?.[0]?.item?.[0]?.answer?.[0] + if (answer === undefined) throw new Error('Missing test answer.') + const invalid = { ...answer, valueString: 'yes' } + const result = preflightQuestionnairePair(questionnaire, { + ...raw, + item: [ + { + ...raw.item?.[0], + item: [ + { + ...raw.item?.[0]?.item?.[0], + answer: [invalid], + }, + ], + }, + ], + }) + expect(result.ok).toBe(false) + }) + + it('rejects question children placed outside their answer context', () => { + const hasPain = response.item?.[0]?.item?.[0] + const nested = hasPain?.answer?.[0]?.item + if (hasPain === undefined || nested === undefined) { + throw new Error('Missing nested test items.') + } + const invalidResponse = { + ...response, + item: [ + { + ...response.item?.[0], + item: [ + { + ...hasPain, + answer: [{ valueBoolean: true }], + item: nested, + }, + ], + }, + response.item?.[1], + ], + } + expect(preflightQuestionnairePair(questionnaire, invalidResponse).ok).toBe( + false, + ) + }) + + it('rejects answers outside inline options', () => { + const health = response.item?.[0] + const hasPain = health?.item?.[0] + const hasPainAnswer = hasPain?.answer?.[0] + const severity = hasPainAnswer?.item?.[0] + const severityAnswer = severity?.answer?.[0] + const coding = severityAnswer?.valueCoding + if ( + health === undefined || + hasPain === undefined || + hasPainAnswer === undefined || + severity === undefined || + severityAnswer === undefined || + coding === undefined + ) { + throw new Error('Missing coded test answer.') + } + const invalidResponse = { + ...response, + item: [ + { + ...health, + item: [ + { + ...hasPain, + answer: [ + { + ...hasPainAnswer, + item: [ + { + ...severity, + answer: [ + { + ...severityAnswer, + valueCoding: { ...coding, code: 'moderate' }, + }, + ], + }, + ], + }, + ], + }, + ], + }, + response.item?.[1], + ], + } + expect(preflightQuestionnairePair(questionnaire, invalidResponse).ok).toBe( + false, + ) + }) + + it('requires enabled items in completed and amended responses', () => { + const missing = { ...response, item: response.item?.slice(1) } + expect(preflightQuestionnairePair(questionnaire, missing).ok).toBe(false) + expect( + preflightQuestionnairePair(questionnaire, { + ...missing, + status: 'amended', + }).ok, + ).toBe(false) + expect( + preflightQuestionnairePair(questionnaire, { + ...missing, + status: 'in-progress', + }).ok, + ).toBe(true) + }) + + it('evaluates conditional enablement and rejects disabled answers', () => { + const health = response.item?.[0] + const hasPain = health?.item?.[0] + if (health === undefined || hasPain === undefined) { + throw new Error('Missing conditional test items.') + } + const noPain = { + ...response, + item: [ + { + ...health, + item: [ + { + ...hasPain, + answer: [{ valueBoolean: false }], + }, + ], + }, + response.item?.[1], + ], + } + expect(preflightQuestionnairePair(questionnaire, noPain).ok).toBe(true) + + const disabledAnswer = { + ...noPain, + item: [ + { + ...noPain.item[0], + item: [ + { + ...noPain.item[0]?.item?.[0], + answer: [ + { + valueBoolean: false, + item: [ + { + linkId: 'pain-severity', + text: 'How severe is the pain?', + answer: [ + { + valueCoding: { system: codingSystem, code: 'mild' }, + }, + ], + }, + ], + }, + ], + }, + ], + }, + noPain.item[1], + ], + } + expect(preflightQuestionnairePair(questionnaire, disabledAnswer).ok).toBe( + false, + ) + }) + + it('fails closed for unresolved ValueSets and accepts normalized expansions', () => { + const valueSetCanonical = unwrap( + parseCanonical('https://example.org/fhir/ValueSet/mood|1.0.0'), + ) + const moodSystem = unwrap( + parseAbsoluteUri('https://example.org/fhir/CodeSystem/mood'), + ) + const valueSetQuestionnaire = unwrap( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'mood', + text: 'Mood', + type: 'choice', + required: true, + answerValueSet: valueSetCanonical, + }, + ], + }), + ) + const valueSetResponse = unwrap( + buildQuestionnaireResponse({ + ...responseInput(), + items: [ + { + linkId: 'mood', + text: 'Mood', + answer: [{ valueCoding: { system: moodSystem, code: 'calm' } }], + }, + ], + }), + ) + expect( + preflightQuestionnairePair(valueSetQuestionnaire, valueSetResponse).ok, + ).toBe(false) + expect( + preflightQuestionnairePair(valueSetQuestionnaire, valueSetResponse, { + valueSets: [ + { + canonical: valueSetCanonical, + concepts: [{ system: moodSystem, code: 'calm' }], + }, + ], + }).ok, + ).toBe(true) + }) + + it('enforces text length and decimal precision constraints', () => { + const textItem = { + linkId: 'text', + text: 'Text', + type: 'text', + maxLength: 4, + extension: [{ url: questionnaireExtensions.minLength, valueInteger: 2 }], + } as const + expect( + preflightItems( + [textItem], + [{ linkId: 'text', text: 'Text', answer: [{ valueString: 'x' }] }], + ).ok, + ).toBe(false) + expect( + preflightItems( + [textItem], + [ + { + linkId: 'text', + text: 'Text', + answer: [{ valueString: '12345' }], + }, + ], + ).ok, + ).toBe(false) + + const oneCodePointItem = { + linkId: 'unicode', + text: 'Unicode', + type: 'string', + maxLength: 1, + extension: [{ url: questionnaireExtensions.minLength, valueInteger: 1 }], + } as const + expect( + preflightItems( + [oneCodePointItem], + [ + { + linkId: 'unicode', + text: 'Unicode', + answer: [{ valueString: '😀' }], + }, + ], + ).ok, + ).toBe(true) + expect( + preflightItems( + [oneCodePointItem], + [ + { + linkId: 'unicode', + text: 'Unicode', + answer: [{ valueString: '😀x' }], + }, + ], + ).ok, + ).toBe(false) + + const decimalItem = { + linkId: 'decimal', + text: 'Decimal', + type: 'decimal', + extension: [ + { + url: questionnaireExtensions.maxDecimalPlaces, + valueInteger: 2, + }, + ], + } as const + expect( + preflightItems( + [decimalItem], + [ + { + linkId: 'decimal', + text: 'Decimal', + answer: [{ valueDecimal: 1.234 }], + }, + ], + ).ok, + ).toBe(false) + expect( + preflightItems( + [decimalItem], + [ + { + linkId: 'decimal', + text: 'Decimal', + answer: [{ valueDecimal: 1.25 }], + }, + ], + ).ok, + ).toBe(true) + }) + + it('orders dateTime bounds by instant and time bounds by clock value', () => { + const dateTimeItem = { + linkId: 'date-time', + text: 'Date time', + type: 'dateTime', + extension: [ + { + url: questionnaireExtensions.minValue, + valueDateTime: '2026-08-20T12:00:00+02:00', + }, + { + url: questionnaireExtensions.maxValue, + valueDateTime: '2026-08-20T11:00:00Z', + }, + ], + } as const + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [dateTimeItem], + }).ok, + ).toBe(true) + expect( + preflightItems( + [dateTimeItem], + [ + { + linkId: 'date-time', + text: 'Date time', + answer: [{ valueDateTime: '2026-08-20T10:30:00Z' }], + }, + ], + ).ok, + ).toBe(true) + + const timeItem = { + linkId: 'time', + text: 'Time', + type: 'time', + extension: [ + { + url: questionnaireExtensions.minValue, + valueTime: '12:00:00.0', + }, + { + url: questionnaireExtensions.maxValue, + valueTime: '12:00:00', + }, + ], + } as const + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [timeItem], + }).ok, + ).toBe(true) + expect( + preflightItems( + [timeItem], + [ + { + linkId: 'time', + text: 'Time', + answer: [{ valueTime: '12:00:00.000' }], + }, + ], + ).ok, + ).toBe(true) + }) + + it('evaluates temporal enableWhen values semantically', () => { + expect( + preflightItems( + [ + { linkId: 'source', text: 'Source', type: 'dateTime' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableWhen: [ + { + question: 'source', + operator: '>', + answerDateTime: '2026-08-20T12:00:00+02:00', + }, + ], + }, + ], + [ + { + linkId: 'source', + text: 'Source', + answer: [{ valueDateTime: '2026-08-20T10:30:00Z' }], + }, + { + linkId: 'dependent', + text: 'Dependent', + answer: [{ valueString: 'enabled' }], + }, + ], + ).ok, + ).toBe(true) + + expect( + preflightItems( + [ + { linkId: 'source', text: 'Source', type: 'time' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableWhen: [ + { + question: 'source', + operator: '=', + answerTime: '12:00:00.0', + }, + ], + }, + ], + [ + { + linkId: 'source', + text: 'Source', + answer: [{ valueTime: '12:00:00' }], + }, + { + linkId: 'dependent', + text: 'Dependent', + answer: [{ valueString: 'enabled' }], + }, + ], + ).ok, + ).toBe(true) + }) + + it('enforces scalar and comparable quantity bounds', () => { + expect( + preflightItems( + [ + { + linkId: 'integer', + text: 'Integer', + type: 'integer', + extension: [ + { url: questionnaireExtensions.minValue, valueInteger: 2 }, + { url: questionnaireExtensions.maxValue, valueInteger: 4 }, + ], + }, + ], + [ + { + linkId: 'integer', + text: 'Integer', + answer: [{ valueInteger: 5 }], + }, + ], + ).ok, + ).toBe(false) + + const quantityItem = { + linkId: 'quantity', + text: 'Quantity', + type: 'quantity', + extension: [ + { + url: questionnaireExtensions.minQuantity, + valueQuantity: { + value: 1, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + { + url: questionnaireExtensions.maxQuantity, + valueQuantity: { + value: 5, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + ], + } as const + expect( + preflightItems( + [quantityItem], + [ + { + linkId: 'quantity', + text: 'Quantity', + answer: [ + { + valueQuantity: { + value: 3, + system: 'http://unitsofmeasure.org', + code: 'lb', + }, + }, + ], + }, + ], + ).ok, + ).toBe(false) + expect( + preflightItems( + [quantityItem], + [ + { + linkId: 'quantity', + text: 'Quantity', + answer: [ + { + valueQuantity: { + value: 3, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + ], + }, + ], + ).ok, + ).toBe(true) + }) + + it('enforces inline and ValueSet-certified quantity units', () => { + const unitValueSet = unwrap( + parseCanonical('https://example.org/fhir/ValueSet/units|1.0.0'), + ) + const quantityAnswer = [ + { + linkId: 'quantity', + text: 'Quantity', + answer: [ + { + valueQuantity: { + value: 3, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + ], + }, + ] as const + const inline = [ + { + linkId: 'quantity', + text: 'Quantity', + type: 'quantity', + extension: [ + { + url: questionnaireExtensions.unitOption, + valueCoding: { + system: 'http://unitsofmeasure.org', + code: 'lb', + }, + }, + ], + }, + ] as const + expect(preflightItems(inline, quantityAnswer).ok).toBe(false) + + const valueSet = [ + { + linkId: 'quantity', + text: 'Quantity', + type: 'quantity', + extension: [ + { + url: questionnaireExtensions.unitValueSet, + valueCanonical: unitValueSet, + }, + ], + }, + ] as const + expect(preflightItems(valueSet, quantityAnswer).ok).toBe(false) + expect( + preflightItems(valueSet, quantityAnswer, { + valueSets: [ + { + canonical: unitValueSet, + concepts: [ + { + system: unwrap(parseAbsoluteUri('http://unitsofmeasure.org')), + code: 'kg', + }, + ], + }, + ], + }).ok, + ).toBe(true) + }) + + it('enforces attachment MIME type and declared maximum size', () => { + const item = { + linkId: 'attachment', + text: 'Attachment', + type: 'attachment', + extension: [ + { url: questionnaireExtensions.mimeType, valueCode: 'image/png' }, + { url: questionnaireExtensions.maxSize, valueDecimal: 4 }, + ], + } as const + expect( + preflightItems( + [item], + [ + { + linkId: 'attachment', + text: 'Attachment', + answer: [ + { + valueAttachment: { + contentType: 'text/plain', + size: 5, + data: 'dGVzdA==', + }, + }, + ], + }, + ], + ).ok, + ).toBe(false) + expect( + preflightItems( + [item], + [ + { + linkId: 'attachment', + text: 'Attachment', + answer: [ + { + valueAttachment: { + contentType: 'image/png', + size: 4, + data: 'dGVzdA==', + }, + }, + ], + }, + ], + ).ok, + ).toBe(true) + }) + + it('enforces answer occurrence limits and exclusive options', () => { + const item = { + linkId: 'choice', + text: 'Choice', + type: 'choice', + repeats: true, + extension: [ + { url: questionnaireExtensions.minOccurs, valueInteger: 2 }, + { url: questionnaireExtensions.maxOccurs, valueInteger: 2 }, + ], + answerOption: [ + { + extension: [ + { + url: questionnaireExtensions.optionExclusive, + valueBoolean: true, + }, + ], + valueCoding: { system: codingSystem, code: 'none' }, + }, + { valueCoding: { system: codingSystem, code: 'pain' } }, + { valueCoding: { system: codingSystem, code: 'fatigue' } }, + ], + } as const + const responseItem = (codes: readonly string[]) => [ + { + linkId: 'choice', + text: 'Choice', + answer: codes.map((code) => ({ + valueCoding: { system: codingSystem, code }, + })), + }, + ] + expect(preflightItems([item], responseItem(['pain'])).ok).toBe(false) + expect( + preflightItems([item], responseItem(['pain', 'fatigue', 'pain'])).ok, + ).toBe(false) + expect(preflightItems([item], responseItem(['none', 'pain'])).ok).toBe( + false, + ) + expect(preflightItems([item], responseItem(['pain', 'fatigue'])).ok).toBe( + true, + ) + }) + + it('returns non-blocking warnings for warning target constraints', () => { + const warningQuestionnaire = { + ...questionnaire, + extension: [ + ...(questionnaire.extension ?? []), + targetConstraint('warning-constraint', 'warning'), + ], + } + const result = preflightQuestionnairePair(warningQuestionnaire, response) + expect(result.ok).toBe(true) + if (!result.ok) return + expect(result.warnings).toEqual([ + expect.objectContaining({ + severity: 'warning', + code: 'external-validation-required', + }), + ]) + }) + + it('fails closed for calculated expressions while preserving mixed warnings', () => { + const expressionQuestionnaire = { + ...questionnaire, + extension: [ + ...(questionnaire.extension ?? []), + targetConstraint('warning-constraint', 'warning'), + targetConstraint('blocking-constraint'), + ], + item: questionnaire.item.map((item, index) => + index === 1 ? + { + ...item, + extension: [ + { + url: questionnaireExtensions.calculatedExpression, + valueExpression: { + language: 'text/fhirpath', + expression: "'calculated'", + }, + }, + ], + } + : item, + ), + } + expect( + preflightQuestionnairePair(expressionQuestionnaire, response).ok, + ).toBe(false) + const mixed = preflightQuestionnairePair(expressionQuestionnaire, response) + if (mixed.ok) return + expect(new Set(mixed.issues.map(({ severity }) => severity))).toEqual( + new Set(['error', 'warning']), + ) + expect( + preflightQuestionnairePair(expressionQuestionnaire, { + ...response, + status: 'in-progress', + }).ok, + ).toBe(true) + }) + + it('does not treat initialExpression population as completed validation', () => { + const initialExpressionItem = { + linkId: 'populated', + text: 'Populated', + type: 'string', + extension: [ + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', + valueExpression: { + language: 'text/fhirpath', + expression: "'suggested'", + }, + }, + ], + } as const + expect(preflightItems([initialExpressionItem], []).ok).toBe(true) + expect( + preflightItems([{ ...initialExpressionItem, required: true }], []).ok, + ).toBe(false) + }) + + it.each([ + ['boolean', { valueBoolean: false }], + ['decimal', { valueDecimal: 1.25 }], + ['integer', { valueInteger: 2 }], + ['date', { valueDate: '2026-08-20' }], + ['dateTime', { valueDateTime: '2026-08-20T12:00:00-07:00' }], + ['time', { valueTime: '12:00:00' }], + ['string', { valueString: 'short answer' }], + ['text', { valueString: 'long answer' }], + ['url', { valueUri: 'https://example.org/answer' }], + ['choice', { valueCoding: { system: codingSystem, code: 'coded-answer' } }], + [ + 'open-choice', + { valueCoding: { system: codingSystem, code: 'coded-answer' } }, + ], + ['open-choice', { valueString: 'free text' }], + [ + 'attachment', + { + valueAttachment: { + contentType: 'text/plain', + data: 'YW5zd2Vy', + title: 'answer.txt', + }, + }, + ], + [ + 'quantity', + { + valueQuantity: { + value: 3, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + ], + ] as const)( + 'accepts the supported %s answer representation', + (type, answer) => { + const result = preflightItems( + [{ linkId: 'answer', text: 'Answer', type }], + [{ linkId: 'answer', text: 'Answer', answer: [answer] }], + ) + expect(result.ok).toBe(true) + }, + ) + + it('admits free text for open-choice despite closed coded options', () => { + const result = preflightItems( + [ + { + linkId: 'answer', + text: 'Answer', + type: 'open-choice', + answerOption: [ + { valueCoding: { system: codingSystem, code: 'coded-answer' } }, + ], + }, + ], + [ + { + linkId: 'answer', + text: 'Answer', + answer: [{ valueString: 'free text' }], + }, + ], + ) + expect(result.ok).toBe(true) + }) + + it.each([ + ['<', 6], + ['<=', 5], + ['>', 4], + ['>=', 5], + ['!=', 4], + ] as const)('evaluates the %s enableWhen operator', (operator, expected) => { + const result = preflightItems( + [ + { linkId: 'source', text: 'Source', type: 'integer' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableWhen: [ + { question: 'source', operator, answerInteger: expected }, + ], + }, + ], + [ + { + linkId: 'source', + text: 'Source', + answer: [{ valueInteger: 5 }], + }, + { + linkId: 'dependent', + text: 'Dependent', + answer: [{ valueString: 'enabled' }], + }, + ], + ) + expect(result.ok).toBe(true) + }) + + it('compares Coding and Quantity enableWhen values semantically', () => { + const quantity = { + value: 3, + system: 'http://unitsofmeasure.org', + code: 'kg', + } + const result = preflightItems( + [ + { linkId: 'coded', text: 'Coded', type: 'choice' }, + { linkId: 'quantity', text: 'Quantity', type: 'quantity' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableBehavior: 'all', + enableWhen: [ + { + question: 'coded', + operator: '=', + answerCoding: { system: codingSystem, code: 'yes' }, + }, + { + question: 'quantity', + operator: '=', + answerQuantity: quantity, + }, + ], + }, + ], + [ + { + linkId: 'coded', + text: 'Coded', + answer: [{ valueCoding: { system: codingSystem, code: 'yes' } }], + }, + { + linkId: 'quantity', + text: 'Quantity', + answer: [{ valueQuantity: quantity }], + }, + { + linkId: 'dependent', + text: 'Dependent', + answer: [{ valueString: 'enabled' }], + }, + ], + ) + expect(result.ok).toBe(true) + }) + + it('supports exists and any enablement without treating absence as data', () => { + const result = preflightItems( + [ + { linkId: 'present', text: 'Present', type: 'boolean' }, + { linkId: 'absent', text: 'Absent', type: 'boolean' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableBehavior: 'any', + enableWhen: [ + { + question: 'present', + operator: 'exists', + answerBoolean: false, + }, + { + question: 'absent', + operator: 'exists', + answerBoolean: false, + }, + ], + }, + ], + [ + { + linkId: 'present', + text: 'Present', + answer: [{ valueBoolean: true }], + }, + { + linkId: 'dependent', + text: 'Dependent', + answer: [{ valueString: 'enabled' }], + }, + ], + ) + expect(result.ok).toBe(true) + }) + + it('fails closed when enableWhen cannot be evaluated', () => { + const invalidExists = preflightItems( + [ + { linkId: 'source', text: 'Source', type: 'string' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableWhen: [ + { + question: 'source', + operator: 'exists', + answerString: 'not-a-boolean', + }, + ], + }, + ], + [], + ) + expect(invalidExists.ok).toBe(false) + + const incomparable = preflightItems( + [ + { linkId: 'source', text: 'Source', type: 'string' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableWhen: [{ question: 'source', operator: '>', answerInteger: 2 }], + }, + ], + [ + { + linkId: 'source', + text: 'Source', + answer: [{ valueString: 'two' }], + }, + ], + ) + expect(incomparable.ok).toBe(false) + expect( + preflightItems( + [ + { linkId: 'source', text: 'Source', type: 'string' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableWhen: [ + { question: 'source', operator: '>', answerInteger: 2 }, + ], + }, + ], + [], + {}, + 'in-progress', + ).ok, + ).toBe(true) + }) + + it('requires external evaluation of completed FHIRPath enablement', () => { + const items = [ + { + linkId: 'conditional', + text: 'Conditional', + type: 'string', + extension: [ + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression', + valueExpression: { + language: 'text/fhirpath', + expression: "%resource.status = 'completed'", + }, + }, + ], + }, + ] as const + expect(preflightItems(items, []).ok).toBe(false) + expect(preflightItems(items, [], {}, 'in-progress').ok).toBe(true) + }) + + it('reports duplicate and invalid questionnaire topology', () => { + const duplicateAndUnknown = [ + { linkId: 'duplicate', text: 'First', type: 'string' }, + { + linkId: 'duplicate', + text: 'Second', + type: 'string', + enableWhen: [ + { question: 'missing', operator: '=', answerString: 'yes' }, + ], + }, + ] as const + const result = preflightItems(duplicateAndUnknown, []) + expect(result.ok).toBe(false) + if (result.ok) return + expect(new Set(result.issues.map(({ code }) => code))).toEqual( + new Set(['duplicate-identifier', 'invalid-reference']), + ) + }) + + it('reports response text, cardinality, grouping, and nesting failures', () => { + const result = preflightItems( + [ + { linkId: 'single', text: 'Single', type: 'string' }, + { + linkId: 'group', + text: 'Group', + type: 'group', + item: [{ linkId: 'child', text: 'Child', type: 'string' }], + }, + ], + [ + { + linkId: 'single', + text: 'Wrong text', + answer: [{ valueString: 'one' }, { valueString: 'two' }], + }, + { + linkId: 'group', + text: 'Group', + answer: [{ valueString: 'not admitted' }], + }, + { linkId: 'unknown', text: 'Unknown' }, + { linkId: 'single', text: 'Single' }, + ], + ) + expect(result.ok).toBe(false) + if (result.ok) return + expect(new Set(result.issues.map(({ code }) => code))).toEqual( + new Set([ + 'duplicate-identifier', + 'invalid-reference', + 'invalid-type', + 'out-of-range', + 'value-mismatch', + ]), + ) + }) + + it('rejects a resolved ValueSet nonmember and duplicate expansions', () => { + const valueSetCanonical = unwrap( + parseCanonical('https://example.org/fhir/ValueSet/mood|1.0.0'), + ) + const items = [ + { + linkId: 'mood', + text: 'Mood', + type: 'choice', + answerValueSet: valueSetCanonical, + }, + ] as const + const responseItems = [ + { + linkId: 'mood', + text: 'Mood', + answer: [ + { valueCoding: { system: codingSystem, code: 'not-admitted' } }, + ], + }, + ] as const + const expansion = { + canonical: valueSetCanonical, + concepts: [{ system: codingSystem, code: 'admitted' }], + } + const result = preflightItems(items, responseItems, { + valueSets: [expansion, expansion], + }) + expect(result.ok).toBe(false) + if (result.ok) return + expect(new Set(result.issues.map(({ code }) => code))).toEqual( + new Set(['duplicate-identifier', 'value-mismatch']), + ) + }) + + it('rejects entered-in-error answer data', () => { + expect( + preflightQuestionnairePair(questionnaire, { + ...response, + status: 'entered-in-error', + }).ok, + ).toBe(false) + }) + + it('preserves arbitrary textual answers under property testing', () => { + expect(() => + assert( + property(string({ minLength: 1 }), (value) => { + const candidate = unwrap( + buildQuestionnaireResponse(responseInput(value)), + ) + const result = preflightQuestionnairePair(questionnaire, candidate) + return result.ok + }), + ), + ).not.toThrow() + }) +}) + +describe('Questionnaire type contract', () => { + it('requires exactly one answer value at compile time', () => { + const answer: QuestionnaireResponseAnswerInput = { valueBoolean: true } + expect(answer.valueBoolean).toBe(true) + + // @ts-expect-error Multiple value[x] members are not a valid answer. + const invalid: QuestionnaireResponseAnswerInput = { + valueBoolean: true, + valueString: 'true', + } + expect(invalid).toBeDefined() + }) + + it.each([ + '0.0.0', + '1.2.3-alpha.1', + '1.2.3+build.0042', + '1.2.3-alpha.1+build.0042', + ])('accepts SemVer %s', (candidate) => { + expect(parseSemVer(candidate).ok).toBe(true) + }) + + it.each(['1.2', '01.2.3', '1.02.3', '1.2.3-01', '1.2.3+'])( + 'rejects non-SemVer %s', + (candidate) => { + expect(parseSemVer(candidate).ok).toBe(false) + }, + ) +}) diff --git a/packages/fhir/test/recording.test.ts b/packages/fhir/test/recording.test.ts new file mode 100644 index 00000000..8883b90b --- /dev/null +++ b/packages/fhir/test/recording.test.ts @@ -0,0 +1,709 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { readFileSync } from 'node:fs' +import { expectTypeOf } from 'expect-type' +import { assert, property, uint8Array } from 'fast-check' +import { + parseAbsoluteUri, + parseFhirId, + parseFhirInstant, + parsePatientReference, + parsePositiveInteger, + type FhirInstant, + type Result, +} from '../src/index.js' +import { + buildProviderRecordingBundle, + providerRawMappings, + encodeRecordingBytes, + parseCanonicalBase64, + parseProviderRecordingBundleInput, + parseImmutableRecordingUrl, + parseMediaType, + parseSha1Base64, + type ApplicationDeviceInput, + type ProviderRecordingBundleInput, + type ConnectedRawProvider, +} from '../src/providers/index.js' + +const unwrap = (result: Result): T => { + if (!result.ok) { + throw new Error(result.issues.map((issue) => issue.message).join('\n')) + } + return result.value +} + +const uri = (value: string) => unwrap(parseAbsoluteUri(value)) +const instant = (value: string): FhirInstant => unwrap(parseFhirInstant(value)) +const identity = (system: string, value: string) => ({ + identifier: { system: uri(system), value }, +}) + +const converter: ApplicationDeviceInput = { + identity: identity('https://example.org/applications', 'converter'), + name: 'Grove converter', + version: '0.0.0', +} + +const rawInput = ( + provider: ConnectedRawProvider, + sourceType: string, +): ProviderRecordingBundleInput => + ({ + source: { + adapter: { kind: 'providers', provider }, + providerAccountIdentifier: { + system: uri('https://provider.example.org/accounts'), + value: 'account-pseudonym-001', + assurance: 'deployment-scoped-pseudonym', + }, + sourceType, + sourceNativeId: `native-record-${provider}-${sourceType}`, + dataOrigin: { + identity: identity( + 'https://example.org/provider-applications', + `origin-${provider}`, + ), + name: `${provider} source`, + }, + }, + attachment: { + kind: 'embedded', + contentType: unwrap( + parseMediaType('application/vnd.grovealliance.provider+json'), + ), + title: 'Authorized minimized provider recording', + format: 'provider-recording', + payloadAssertion: 'caller-authorized-opaque-payload', + dataBase64: unwrap(encodeRecordingBytes(Uint8Array.of(1, 2, 3))), + }, + subject: unwrap(parsePatientReference('Patient/example')), + application: converter, + eventSequence: unwrap(parsePositiveInteger(1)), + graphIdentifierSystem: uri( + 'urn:grove:provider-graph:org.grovealliance.example', + ), + documentDate: instant('2026-08-20T17:00:01Z'), + recorded: instant('2026-08-20T17:00:02Z'), + }) as ProviderRecordingBundleInput + +const recordingSources = Object.entries(providerRawMappings).flatMap( + ([provider, sources]) => + Object.keys(sources).map((sourceType) => [provider, sourceType] as const), +) + +describe('Provider native recording graph', () => { + it.each([ + 'google-health-heart-rate-recording.json', + 'oura-heart-rate-recording.json', + 'withings-activity-intraday-recording.json', + 'withings-sleep-intraday-recording.json', + ])('accepts the provider-neutral normalized %s fixture', (name) => { + const fixture = JSON.parse( + readFileSync(new URL(`../fixtures/normalized/${name}`, import.meta.url), { + encoding: 'utf8', + }), + ) as Pick + const graph = rawInput('google-health-api', 'heart-rate') + expect( + buildProviderRecordingBundle({ + ...graph, + ...fixture, + }).ok, + ).toBe(true) + }) + + it.each(recordingSources)( + 'admits the exact catalogued %s/%s raw source', + (provider, sourceType) => { + const result = buildProviderRecordingBundle( + rawInput(provider as ConnectedRawProvider, sourceType), + ) + expect(result.ok).toBe(true) + if (!result.ok) return + + expect(result.value.entry).toHaveLength(4) + expect(Object.isFrozen(result.value)).toBe(true) + const document = result.value.entry.find( + ({ resource }) => resource.resourceType === 'DocumentReference', + )?.resource + expect(document?.resourceType).toBe('DocumentReference') + if (document?.resourceType !== 'DocumentReference') return + expect(document.meta?.profile).toEqual([ + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document', + ]) + expect(document.extension).toEqual([ + { + url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider', + valueCode: provider, + }, + { + url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type', + valueCode: `${provider}/${sourceType}`, + }, + ]) + // A recording document is a one-to-one conversion, so the source record identifies it and + // no second output namespace appears. + expect(document.identifier?.map(({ system }) => system)).toEqual([ + 'https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id', + ]) + expect(document.id).toBeUndefined() + }, + ) + + it('emits exact embedded bytes, computed SHA-1 integrity, and a complete audit graph', () => { + const input = rawInput('google-health-api', 'heart-rate') + const result = buildProviderRecordingBundle(input) + expect(result.ok).toBe(true) + if (!result.ok) return + + const documentEntry = result.value.entry.find( + ({ resource }) => resource.resourceType === 'DocumentReference', + ) + const provenance = result.value.entry.find( + ({ resource }) => resource.resourceType === 'Provenance', + )?.resource + expect(documentEntry?.resource.resourceType).toBe('DocumentReference') + if (documentEntry?.resource.resourceType !== 'DocumentReference') return + expect(documentEntry.resource.content).toEqual([ + { + attachment: { + contentType: 'application/vnd.grovealliance.provider+json', + data: 'AQID', + size: 3, + hash: 'cDeAcZjCKn0rCAc3HXY3eahP388=', + title: 'Authorized minimized provider recording', + }, + format: { + system: + 'https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format', + version: '0.5.0', + code: 'provider-recording', + display: 'Provider Recording', + }, + }, + ]) + expect(documentEntry.resource.status).toBe('current') + expect(documentEntry.resource.date).toBe(input.documentDate) + expect(documentEntry.resource.author).toHaveLength(1) + expect(provenance?.resourceType).toBe('Provenance') + if (provenance?.resourceType !== 'Provenance') return + expect(provenance.meta?.profile).toEqual([ + 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance', + ]) + expect(provenance.target).toEqual([{ reference: documentEntry.fullUrl }]) + expect(provenance.entity).toHaveLength(1) + expect(provenance.entity?.[0]?.what.identifier?.system).toBe( + 'https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id', + ) + const serialized = JSON.stringify(result.value) + // The provider's own record key travels in the clear; the caller's payload assertion does not. + expect(serialized).toContain(input.source.sourceNativeId) + expect(serialized).not.toContain(input.attachment.payloadAssertion) + expect(serialized).not.toContain('payloadAssertion') + }) + + it('matches the frozen raw source/output identity vectors', () => { + const input = rawInput('google-health-api', 'heart-rate') + const result = buildProviderRecordingBundle({ + ...input, + source: { + ...input.source, + providerAccountIdentifier: { + system: uri('https://provider.example.org/accounts'), + value: 'account-001', + assurance: 'deployment-scoped-pseudonym', + }, + sourceNativeId: 'heart-rate-2026-08-20', + }, + }) + expect(result.ok).toBe(true) + if (!result.ok) return + const document = result.value.entry.find( + ({ resource }) => resource.resourceType === 'DocumentReference', + )?.resource + if (document?.resourceType !== 'DocumentReference') { + throw new Error('Missing recording document') + } + // Google Health is account-scoped, and a recording document is a one-to-one conversion, so + // the source record is the document's only identity. + expect(document.identifier?.map(({ value }) => value)).toEqual([ + 'v1:google-health-api|account-001|heart-rate|heart-rate-2026-08-20', + ]) + }) + + it('supports an immutable external attachment without copying or fetching it', () => { + const input = rawInput('oura', 'heartrate') + const result = buildProviderRecordingBundle({ + ...input, + attachment: { + kind: 'external', + contentType: unwrap( + parseMediaType('application/vnd.grovealliance.provider+json'), + ), + title: 'Authorized minimized Oura recording', + format: 'provider-recording', + payloadAssertion: 'verified-sanitized-input', + url: unwrap( + parseImmutableRecordingUrl( + 'https://objects.example.org/recordings/version-42', + ), + ), + size: unwrap(parsePositiveInteger(3)), + hash: unwrap(parseSha1Base64('cDeAcZjCKn0rCAc3HXY3eahP388=')), + immutabilityAssurance: 'immutable-version-specific', + }, + }) + expect(result.ok).toBe(true) + if (!result.ok) return + const document = result.value.entry.find( + ({ resource }) => resource.resourceType === 'DocumentReference', + )?.resource + if (document?.resourceType !== 'DocumentReference') return + expect(document.content[0]?.attachment).toMatchObject({ + url: 'https://objects.example.org/recordings/version-42', + size: 3, + hash: 'cDeAcZjCKn0rCAc3HXY3eahP388=', + }) + expect(document.content[0]?.attachment.data).toBeUndefined() + const serialized = JSON.stringify(result.value) + expect(serialized).not.toContain('verified-sanitized-input') + expect(serialized).not.toContain('payloadAssertion') + }) + + it('retains repository-assigned ids while keeping digest identities out of Resource.id', () => { + const input = rawInput('withings', 'activityIntraday') + const result = buildProviderRecordingBundle({ + ...input, + repositoryIds: { + bundle: unwrap(parseFhirId('bundle-42')), + document: unwrap(parseFhirId('document-42')), + provenance: unwrap(parseFhirId('provenance-42')), + }, + }) + expect(result.ok).toBe(true) + if (!result.ok) return + expect(result.value.id).toBe('bundle-42') + expect( + result.value.entry.find( + ({ resource }) => resource.resourceType === 'DocumentReference', + )?.resource.id, + ).toBe('document-42') + }) + + it.each([ + ['google-health-api', 'steps'], + ['google-health-api', 'blood-glucose'], + ['oura', 'sleep'], + ['withings', 'getmeas:11'], + ] as const)('fails closed for non-raw %s/%s sources', (provider, source) => { + expect(buildProviderRecordingBundle(rawInput(provider, source)).ok).toBe( + false, + ) + }) + + it('rejects unknown provider fields rather than silently stripping them', () => { + const input = rawInput('google-health-api', 'heart-rate') + expect( + parseProviderRecordingBundleInput({ + ...input, + source: { ...input.source, vendorPayload: { bpm: 64 } }, + }).ok, + ).toBe(false) + expect( + parseProviderRecordingBundleInput({ + ...input, + attachment: { ...input.attachment, bearerToken: 'secret' }, + }).ok, + ).toBe(false) + }) + + it('fails closed for absent, ambiguous, or unsupported raw payload assertions', () => { + const input = rawInput('google-health-api', 'heart-rate') + const attachmentWithoutAssertion = { + kind: 'embedded', + contentType: input.attachment.contentType, + title: input.attachment.title, + dataBase64: + input.attachment.kind === 'embedded' ? + input.attachment.dataBase64 + : 'AQID', + } + for (const attachment of [ + attachmentWithoutAssertion, + { + ...attachmentWithoutAssertion, + payloadAssertion: [ + 'caller-authorized-opaque-payload', + 'verified-sanitized-input', + ], + }, + { ...attachmentWithoutAssertion, payloadAssertion: 'unreviewed' }, + ]) { + expect( + parseProviderRecordingBundleInput({ + ...input, + attachment, + }).ok, + ).toBe(false) + } + }) + + it.each([ + ['empty data', { dataBase64: '' }], + ['noncanonical base64', { dataBase64: 'AQI' }], + ['invalid media type', { contentType: 'application/json; charset=utf-8' }], + ])('rejects %s', (_name, change) => { + const input = rawInput('google-health-api', 'heart-rate') + expect( + buildProviderRecordingBundle({ + ...input, + attachment: { ...input.attachment, ...change }, + } as ProviderRecordingBundleInput).ok, + ).toBe(false) + }) + + it('rejects invalid external integrity and URL metadata', () => { + const input = rawInput('oura', 'heartrate') + const external = { + kind: 'external', + contentType: 'application/vnd.grovealliance.provider+json', + title: 'Authorized recording', + format: 'provider-recording', + payloadAssertion: 'caller-authorized-opaque-payload', + url: 'ftp://objects.example.org/recording', + size: 0, + hash: 'AQID', + immutabilityAssurance: 'immutable-version-specific', + } as const + expect( + buildProviderRecordingBundle({ + ...input, + attachment: external, + } as unknown as ProviderRecordingBundleInput).ok, + ).toBe(false) + + expect( + buildProviderRecordingBundle({ + ...input, + attachment: { + ...external, + url: 'https://objects.example.org/recording/version-42', + size: 2_147_483_648, + hash: 'cDeAcZjCKn0rCAc3HXY3eahP388=', + }, + } as unknown as ProviderRecordingBundleInput).ok, + ).toBe(false) + }) + + it('rejects a 32-byte SHA-256 digest where R4 requires a 20-byte SHA-1 hash', () => { + expect( + parseSha1Base64('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=').ok, + ).toBe(false) + }) + + it('fails closed across the base64, media-type, and immutable-URL primitive boundaries', () => { + expect(parseCanonicalBase64(42).ok).toBe(false) + expect(parseCanonicalBase64('AB==').ok).toBe(false) + expect(encodeRecordingBytes(new Uint8Array()).ok).toBe(false) + expect(encodeRecordingBytes('AQID' as unknown as Uint8Array).ok).toBe(false) + expect(parseSha1Base64(42).ok).toBe(false) + expect(parseMediaType(42).ok).toBe(false) + expect(parseImmutableRecordingUrl(42).ok).toBe(false) + expect( + parseImmutableRecordingUrl('https://objects.example.org/a b').ok, + ).toBe(false) + expect( + parseImmutableRecordingUrl('https://user@example.org/recording').ok, + ).toBe(false) + expect( + parseImmutableRecordingUrl('https://objects.example.org/%FF').ok, + ).toBe(false) + expect( + parseImmutableRecordingUrl('https://objects.example.org/%ZZ').ok, + ).toBe(false) + expect(parseImmutableRecordingUrl('::::').ok).toBe(false) + expect(parseImmutableRecordingUrl('not an absolute URL').ok).toBe(false) + }) + + it.each(['title', 'url', 'repository', 'application'] as const)( + 'rejects sourceNativeId leakage through %s metadata', + (location) => { + const input = rawInput('google-health-api', 'heart-rate') + const nativeId = input.source.sourceNativeId + let candidate: unknown + if (location === 'title') { + candidate = { + ...input, + attachment: { + ...input.attachment, + title: `Recording ${nativeId}`, + }, + } + } else if (location === 'url') { + candidate = { + ...input, + attachment: { + kind: 'external', + contentType: unwrap( + parseMediaType('application/vnd.grovealliance.provider+json'), + ), + title: 'Authorized recording', + format: 'provider-recording', + payloadAssertion: 'caller-authorized-opaque-payload', + url: `https://objects.example.org/${nativeId}`, + size: unwrap(parsePositiveInteger(3)), + hash: unwrap(parseSha1Base64('cDeAcZjCKn0rCAc3HXY3eahP388=')), + immutabilityAssurance: 'immutable-version-specific', + }, + } + } else if (location === 'repository') { + candidate = { ...input, repositoryIds: { document: nativeId } } + } else { + candidate = { + ...input, + application: { + ...input.application, + name: `Converter ${nativeId}`, + }, + } + } + expect( + buildProviderRecordingBundle(candidate as ProviderRecordingBundleInput) + .ok, + ).toBe(false) + }, + ) + + it('rejects provider-account pseudonym leakage through emitted metadata', () => { + const input = rawInput('google-health-api', 'heart-rate') + expect( + buildProviderRecordingBundle({ + ...input, + attachment: { + ...input.attachment, + title: `Recording ${input.source.providerAccountIdentifier.value}`, + }, + }).ok, + ).toBe(false) + }) + + it('rejects identity leakage through external and computed Attachment.hash metadata', () => { + const input = rawInput('google-health-api', 'heart-rate') + const emittedHash = 'cDeAcZjCKn0rCAc3HXY3eahP388=' + const privateSource = { + ...input.source, + sourceNativeId: emittedHash, + } + expect( + buildProviderRecordingBundle({ + ...input, + source: privateSource, + }).ok, + ).toBe(false) + expect( + buildProviderRecordingBundle({ + ...input, + source: privateSource, + attachment: { + kind: 'external', + contentType: unwrap( + parseMediaType('application/vnd.grovealliance.provider+json'), + ), + title: 'Authorized recording', + format: 'provider-recording', + payloadAssertion: 'verified-sanitized-input', + url: unwrap( + parseImmutableRecordingUrl( + 'https://objects.example.org/recordings/version-42', + ), + ), + size: unwrap(parsePositiveInteger(3)), + hash: unwrap(parseSha1Base64(emittedHash)), + immutabilityAssurance: 'immutable-version-specific', + }, + }).ok, + ).toBe(false) + }) + + it('rejects recursively percent-encoded identity leakage through attachment URLs', () => { + const input = rawInput('google-health-api', 'heart-rate') + const privateValue = 'native record/42' + const encoded = encodeURIComponent(encodeURIComponent(privateValue)) + expect( + buildProviderRecordingBundle({ + ...input, + source: { ...input.source, sourceNativeId: privateValue }, + attachment: { + kind: 'external', + contentType: unwrap( + parseMediaType('application/vnd.grovealliance.provider+json'), + ), + title: 'Authorized recording', + format: 'provider-recording', + payloadAssertion: 'verified-sanitized-input', + url: unwrap( + parseImmutableRecordingUrl( + `https://objects.example.org/recordings/${encoded}`, + ), + ), + size: unwrap(parsePositiveInteger(3)), + hash: unwrap(parseSha1Base64('cDeAcZjCKn0rCAc3HXY3eahP388=')), + immutabilityAssurance: 'immutable-version-specific', + }, + }).ok, + ).toBe(false) + }) + + it('rejects duplicate business identities in the resource graph', () => { + const input = rawInput('withings', 'sleepIntraday') + expect( + buildProviderRecordingBundle({ + ...input, + source: { ...input.source, dataOrigin: input.application }, + }).ok, + ).toBe(false) + }) + + it.each(['source', 'application', 'data-origin'] as const)( + 'rejects invalid Unicode identity input in the %s identity role', + (role) => { + const input = rawInput('google-health-api', 'heart-rate') + const invalid = '\ud800' + let candidate: ProviderRecordingBundleInput + if (role === 'source') { + candidate = { + ...input, + source: { ...input.source, sourceNativeId: invalid }, + } + } else if (role === 'application') { + candidate = { + ...input, + application: { + ...input.application, + identity: identity('https://example.org/applications', invalid), + }, + } + } else { + candidate = { + ...input, + source: { + ...input.source, + dataOrigin: { + ...input.source.dataOrigin, + identity: identity('https://example.org/data-origins', invalid), + }, + }, + } + } + expect(parseProviderRecordingBundleInput(candidate).ok).toBe(false) + expect(buildProviderRecordingBundle(candidate).ok).toBe(false) + }, + ) + + it.each([ + [ + 'provider account value', + (input: ProviderRecordingBundleInput) => ({ + ...input, + source: { + ...input.source, + providerAccountIdentifier: { + ...input.source.providerAccountIdentifier, + value: ' ', + }, + }, + }), + ], + [ + 'source native id', + (input: ProviderRecordingBundleInput) => ({ + ...input, + source: { ...input.source, sourceNativeId: '\t' }, + }), + ], + [ + 'source type', + (input: ProviderRecordingBundleInput) => ({ + ...input, + source: { ...input.source, sourceType: '\n ' }, + }), + ], + [ + 'converter identifier', + (input: ProviderRecordingBundleInput) => ({ + ...input, + application: { + ...input.application, + identity: { + ...input.application.identity, + identifier: { + ...input.application.identity.identifier, + value: ' ', + }, + }, + }, + }), + ], + [ + 'data-origin identifier', + (input: ProviderRecordingBundleInput) => ({ + ...input, + source: { + ...input.source, + dataOrigin: { + ...input.source.dataOrigin, + identity: { + ...input.source.dataOrigin.identity, + identifier: { + ...input.source.dataOrigin.identity.identifier, + value: ' \t ', + }, + }, + }, + }, + }), + ], + ] as const)('rejects a whitespace-only %s', (_name, mutate) => { + expect( + parseProviderRecordingBundleInput( + mutate(rawInput('google-health-api', 'heart-rate')), + ).ok, + ).toBe(false) + }) + + it('round-trips arbitrary non-empty byte arrays to canonical base64', () => { + assert( + property(uint8Array({ minLength: 1, maxLength: 512 }), (bytes) => { + const encoded = encodeRecordingBytes(bytes) + expect(encoded.ok).toBe(true) + if (!encoded.ok) return + expect( + parseProviderRecordingBundleInput({ + ...rawInput('google-health-api', 'heart-rate'), + attachment: { + ...rawInput('google-health-api', 'heart-rate').attachment, + dataBase64: encoded.value, + }, + }).ok, + ).toBe(true) + }), + ) + }) + + it('exposes a result-typed, closed recording facade', () => { + const result = buildProviderRecordingBundle( + rawInput('google-health-api', 'heart-rate'), + ) + expectTypeOf(result).toExtend< + Result + >() + }) +}) diff --git a/packages/fhir/test/resources/accounts.json b/packages/fhir/test/resources/accounts.json deleted file mode 100644 index edaf0a6f..00000000 --- a/packages/fhir/test/resources/accounts.json +++ /dev/null @@ -1,216 +0,0 @@ -{ - "0": { - "resourceType": "Account", - "id": "account-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T10:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Account"] - }, - "status": "active", - "name": "Patient Account for John Doe", - "subject": [ - { - "reference": "Patient/patient-001", - "display": "John Doe" - } - ] - }, - "1": { - "resourceType": "Account", - "id": "account-comprehensive", - "meta": { - "versionId": "2", - "lastUpdated": "2025-01-15T11:30:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Account"] - }, - "text": { - "status": "generated", - "div": "
Comprehensive billing account for Sarah Johnson
" - }, - "identifier": [ - { - "use": "official", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "AN", - "display": "Account number" - } - ] - }, - "system": "http://hospital.example.org/accounts", - "value": "ACC-12345678", - "period": { - "start": "2025-01-01" - } - } - ], - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "PBILLACCT", - "display": "patient billing account" - } - ], - "text": "Patient Billing Account" - }, - "name": "Sarah Johnson Medical Services Account", - "subject": [ - { - "reference": "Patient/patient-002", - "display": "Sarah Johnson" - } - ], - "servicePeriod": { - "start": "2025-01-01T00:00:00Z", - "end": "2025-12-31T23:59:59Z" - }, - "coverage": [ - { - "coverage": { - "reference": "Coverage/coverage-001", - "display": "Blue Cross Insurance" - }, - "priority": 1 - }, - { - "coverage": { - "reference": "Coverage/coverage-002", - "display": "Secondary Insurance" - }, - "priority": 2 - } - ], - "owner": { - "reference": "Organization/hospital-001", - "display": "General Hospital" - }, - "description": "Primary billing account for inpatient and outpatient services", - "guarantor": [ - { - "party": { - "reference": "Patient/patient-002", - "display": "Sarah Johnson" - }, - "onHold": false, - "period": { - "start": "2025-01-01" - } - } - ] - }, - "2": { - "resourceType": "Account", - "id": "account-on-hold", - "meta": { - "versionId": "3", - "lastUpdated": "2025-01-15T12:45:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Account"] - }, - "identifier": [ - { - "use": "official", - "system": "http://hospital.example.org/accounts", - "value": "ACC-87654321" - } - ], - "status": "on-hold", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "PBILLACCT", - "display": "patient billing account" - } - ] - }, - "name": "Robert Miller Account - Payment Dispute", - "subject": [ - { - "reference": "Patient/patient-003", - "display": "Robert Miller" - } - ], - "servicePeriod": { - "start": "2024-06-01T00:00:00Z", - "end": "2024-12-31T23:59:59Z" - }, - "coverage": [ - { - "coverage": { - "reference": "Coverage/coverage-003", - "display": "Aetna Health" - }, - "priority": 1 - } - ], - "owner": { - "reference": "Organization/hospital-001", - "display": "General Hospital" - }, - "description": "Account on hold pending insurance claim resolution", - "guarantor": [ - { - "party": { - "reference": "RelatedPerson/guarantor-001", - "display": "Mary Miller (Spouse)" - }, - "onHold": true, - "period": { - "start": "2024-06-01", - "end": "2025-06-01" - } - } - ] - }, - "3": { - "resourceType": "Account", - "id": "account-inactive", - "meta": { - "versionId": "4", - "lastUpdated": "2025-01-15T13:20:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Account"] - }, - "identifier": [ - { - "use": "official", - "system": "http://hospital.example.org/accounts", - "value": "ACC-99887766" - } - ], - "status": "inactive", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "PBILLACCT", - "display": "patient billing account" - } - ] - }, - "name": "Emily Chen Closed Account", - "subject": [ - { - "reference": "Patient/patient-004", - "display": "Emily Chen" - } - ], - "servicePeriod": { - "start": "2024-01-01T00:00:00Z", - "end": "2024-06-30T23:59:59Z" - }, - "owner": { - "reference": "Organization/hospital-001", - "display": "General Hospital" - }, - "description": "Account closed - all balances settled", - "partOf": { - "reference": "Account/parent-account-001", - "display": "Family Account" - } - } -} diff --git a/packages/fhir/test/resources/accounts.json.license b/packages/fhir/test/resources/accounts.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/accounts.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/accounts.test.ts b/packages/fhir/test/resources/accounts.test.ts deleted file mode 100644 index d7857465..00000000 --- a/packages/fhir/test/resources/accounts.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Account } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirAccount, type untypedAccountSchema } from '../../src/index.js' - -describe('Account Resource', () => { - it('should validate FHIR Accounts from accounts.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/accounts.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirAccount.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/activityDefinitions.json b/packages/fhir/test/resources/activityDefinitions.json deleted file mode 100644 index b3289f22..00000000 --- a/packages/fhir/test/resources/activityDefinitions.json +++ /dev/null @@ -1,195 +0,0 @@ -{ - "0": { - "resourceType": "ActivityDefinition", - "id": "activitydefinition-example-0", - "status": "draft", - "name": "BloodPressureMeasurement", - "title": "Blood Pressure Measurement Activity", - "description": "Activity definition for measuring blood pressure", - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "163020007", - "display": "Blood pressure taking" - } - ] - }, - "kind": "ServiceRequest" - }, - "1": { - "resourceType": "ActivityDefinition", - "id": "activitydefinition-example-1", - "url": "http://example.org/ActivityDefinition/medication-order", - "identifier": [ - { - "system": "http://example.org/identifiers", - "value": "AD-001" - } - ], - "version": "1.0.0", - "name": "MedicationOrderActivity", - "title": "Medication Order Activity Definition", - "subtitle": "For prescribing medications", - "status": "active", - "experimental": false, - "date": "2024-01-15T10:00:00Z", - "publisher": "Example Healthcare Organization", - "contact": [ - { - "name": "Clinical Operations", - "telecom": [ - { - "system": "email", - "value": "clinical@example.org" - } - ] - } - ], - "description": "This activity definition describes the process for ordering a medication", - "purpose": "To standardize medication ordering across the organization", - "copyright": "Copyright 2024 Example Healthcare Organization", - "kind": "MedicationRequest", - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "182817000", - "display": "Drug prescription" - } - ] - }, - "intent": "order", - "priority": "routine", - "doNotPerform": false, - "participant": [ - { - "type": "practitioner", - "role": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "158965000", - "display": "Medical practitioner" - } - ] - } - } - ] - }, - "2": { - "resourceType": "ActivityDefinition", - "id": "activitydefinition-example-2", - "status": "active", - "name": "LabTestOrder", - "title": "Laboratory Test Order", - "description": "Activity for ordering laboratory tests", - "kind": "ServiceRequest", - "code": { - "text": "Laboratory test order" - }, - "intent": "proposal", - "priority": "urgent", - "timingDateTime": "2024-11-20T09:00:00Z", - "participant": [ - { - "type": "practitioner" - }, - { - "type": "patient" - } - ], - "observationRequirement": [ - { - "reference": "ObservationDefinition/lab-test-def" - } - ] - }, - "3": { - "resourceType": "ActivityDefinition", - "id": "activitydefinition-example-3", - "url": "http://example.org/ActivityDefinition/referral-cardiology", - "version": "2.1.0", - "name": "CardiologyReferral", - "title": "Referral to Cardiology", - "status": "active", - "date": "2024-06-01T14:30:00Z", - "publisher": "Cardiology Department", - "description": "Activity definition for referring patients to cardiology services", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "approvalDate": "2024-05-15", - "lastReviewDate": "2024-10-01", - "effectivePeriod": { - "start": "2024-06-01", - "end": "2025-06-01" - }, - "topic": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "394579002", - "display": "Cardiology" - } - ] - } - ], - "author": [ - { - "name": "Dr. Smith", - "telecom": [ - { - "system": "email", - "value": "smith@example.org" - } - ] - } - ], - "kind": "ServiceRequest", - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "3457005", - "display": "Referral to cardiologist" - } - ] - }, - "intent": "order", - "priority": "routine", - "timingTiming": { - "repeat": { - "boundsDuration": { - "value": 30, - "unit": "days", - "system": "http://unitsofmeasure.org", - "code": "d" - } - } - }, - "participant": [ - { - "type": "practitioner", - "role": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "17561000", - "display": "Cardiologist" - } - ] - } - } - ] - } -} diff --git a/packages/fhir/test/resources/activityDefinitions.json.license b/packages/fhir/test/resources/activityDefinitions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/activityDefinitions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/activityDefinitions.test.ts b/packages/fhir/test/resources/activityDefinitions.test.ts deleted file mode 100644 index ba0a9d09..00000000 --- a/packages/fhir/test/resources/activityDefinitions.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ActivityDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirActivityDefinition, - type untypedActivityDefinitionSchema, -} from '../../src/index.js' - -describe('ActivityDefinition Resource', () => { - it('should validate FHIR activity definitions from activityDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/activityDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirActivityDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/additionalResourceMethods.test.ts b/packages/fhir/test/resources/additionalResourceMethods.test.ts deleted file mode 100644 index dc0d733e..00000000 --- a/packages/fhir/test/resources/additionalResourceMethods.test.ts +++ /dev/null @@ -1,235 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - type Condition, - type Encounter, - type MedicationRequest, -} from 'fhir/r4b.js' -import { - FhirCondition, - FhirEncounter, - FhirMedicationRequest, -} from '../../src/index.js' - -describe('FhirCondition', () => { - describe('onsetDate', () => { - it('should get onset date as Date object', () => { - const condition: Condition = { - resourceType: 'Condition', - subject: { reference: 'Patient/123' }, - onsetDateTime: '2024-01-15T10:00:00.000Z', - } - const fhirCondition = new FhirCondition(condition) - - const onsetDate = fhirCondition.onsetDate - expect(onsetDate).toBeInstanceOf(Date) - expect(onsetDate?.toISOString()).toBe('2024-01-15T10:00:00.000Z') - }) - }) - - describe('abatementDate', () => { - it('should get abatement date as Date object', () => { - const condition: Condition = { - resourceType: 'Condition', - subject: { reference: 'Patient/123' }, - abatementDateTime: '2024-02-20T10:00:00Z', - } - const fhirCondition = new FhirCondition(condition) - - const abatementDate = fhirCondition.abatementDate - expect(abatementDate).toBeInstanceOf(Date) - expect(abatementDate?.toISOString()).toBe('2024-02-20T10:00:00.000Z') - }) - }) - - describe('isActive', () => { - it('should return true when no abatement information', () => { - const condition: Condition = { - resourceType: 'Condition', - subject: { reference: 'Patient/123' }, - onsetDateTime: '2024-01-15T10:00:00Z', - } - const fhirCondition = new FhirCondition(condition) - - expect(fhirCondition.isActive).toBe(true) - }) - - it('should return false when abatement date exists', () => { - const condition: Condition = { - resourceType: 'Condition', - subject: { reference: 'Patient/123' }, - abatementDateTime: '2024-02-20T10:00:00Z', - } - const fhirCondition = new FhirCondition(condition) - - expect(fhirCondition.isActive).toBe(false) - }) - }) - - describe('isConfirmed', () => { - it('should return true when verification status is confirmed', () => { - const condition: Condition = { - resourceType: 'Condition', - subject: { reference: 'Patient/123' }, - verificationStatus: { - coding: [ - { - system: - 'http://terminology.hl7.org/CodeSystem/condition-ver-status', - code: 'confirmed', - }, - ], - }, - } - const fhirCondition = new FhirCondition(condition) - - expect(fhirCondition.isConfirmed).toBe(true) - }) - - it('should return false when verification status is not confirmed', () => { - const condition: Condition = { - resourceType: 'Condition', - subject: { reference: 'Patient/123' }, - verificationStatus: { - coding: [ - { - system: - 'http://terminology.hl7.org/CodeSystem/condition-ver-status', - code: 'provisional', - }, - ], - }, - } - const fhirCondition = new FhirCondition(condition) - - expect(fhirCondition.isConfirmed).toBe(false) - }) - }) -}) - -describe('FhirEncounter', () => { - describe('startDate and endDate', () => { - it('should get start and end dates as Date objects', () => { - const encounter: Encounter = { - resourceType: 'Encounter', - status: 'finished', - class: { - system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', - code: 'AMB', - }, - period: { - start: '2024-01-15T10:00:00Z', - end: '2024-01-15T11:00:00Z', - }, - } - const fhirEncounter = new FhirEncounter(encounter) - - const startDate = fhirEncounter.startDate - const endDate = fhirEncounter.endDate - expect(startDate).toBeInstanceOf(Date) - expect(endDate).toBeInstanceOf(Date) - expect(startDate?.toISOString()).toBe('2024-01-15T10:00:00.000Z') - expect(endDate?.toISOString()).toBe('2024-01-15T11:00:00.000Z') - }) - }) - - describe('isInProgress', () => { - it('should return true when encounter has start but no end', () => { - const encounter: Encounter = { - resourceType: 'Encounter', - status: 'in-progress', - class: { - system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', - code: 'AMB', - }, - period: { - start: '2024-01-15T10:00:00Z', - }, - } - const fhirEncounter = new FhirEncounter(encounter) - - expect(fhirEncounter.isInProgress).toBe(true) - }) - - it('should return false when encounter is finished', () => { - const encounter: Encounter = { - resourceType: 'Encounter', - status: 'finished', - class: { - system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', - code: 'AMB', - }, - period: { - start: '2024-01-15T10:00:00Z', - end: '2024-01-15T11:00:00Z', - }, - } - const fhirEncounter = new FhirEncounter(encounter) - - expect(fhirEncounter.isInProgress).toBe(false) - }) - }) - - describe('durationInMilliseconds', () => { - it('should calculate duration correctly', () => { - const encounter: Encounter = { - resourceType: 'Encounter', - status: 'finished', - class: { - system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', - code: 'AMB', - }, - period: { - start: '2024-01-15T10:00:00Z', - end: '2024-01-15T11:30:00Z', - }, - } - const fhirEncounter = new FhirEncounter(encounter) - - const duration = fhirEncounter.duration - expect(duration).toBe(90 * 60 * 1000) // 90 minutes in milliseconds - }) - - it('should return undefined when dates are incomplete', () => { - const encounter: Encounter = { - resourceType: 'Encounter', - status: 'in-progress', - class: { - system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', - code: 'AMB', - }, - period: { - start: '2024-01-15T10:00:00Z', - }, - } - const fhirEncounter = new FhirEncounter(encounter) - - expect(fhirEncounter.duration).toBeUndefined() - }) - }) -}) - -describe('FhirMedicationRequest', () => { - describe('authoredDate', () => { - it('should get authored date as Date object', () => { - const medicationRequest: MedicationRequest = { - resourceType: 'MedicationRequest', - status: 'active', - intent: 'order', - subject: { reference: 'Patient/123' }, - authoredOn: '2024-01-15T10:00:00Z', - } - const fhirMedRequest = new FhirMedicationRequest(medicationRequest) - - const authoredDate = fhirMedRequest.authoredDate - expect(authoredDate).toBeInstanceOf(Date) - expect(authoredDate?.toISOString()).toBe('2024-01-15T10:00:00.000Z') - }) - }) -}) diff --git a/packages/fhir/test/resources/administrableProductDefinitions.json b/packages/fhir/test/resources/administrableProductDefinitions.json deleted file mode 100644 index f78bddb4..00000000 --- a/packages/fhir/test/resources/administrableProductDefinitions.json +++ /dev/null @@ -1,231 +0,0 @@ -{ - "0": { - "resourceType": "AdministrableProductDefinition", - "id": "administrable-product-definition-example-0", - "status": "active", - "routeOfAdministration": [ - { - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "26643006", - "display": "Oral route" - } - ] - } - } - ] - }, - "1": { - "resourceType": "AdministrableProductDefinition", - "id": "administrable-product-definition-example-1", - "identifier": [ - { - "system": "http://example.org/administrable-products", - "value": "APD-001" - } - ], - "status": "active", - "formOf": [ - { - "reference": "MedicinalProductDefinition/example" - } - ], - "administrableDoseForm": { - "coding": [ - { - "system": "http://hl7.org/fhir/administrable-dose-form", - "code": "solution-for-injection", - "display": "Solution for Injection" - } - ] - }, - "unitOfPresentation": { - "coding": [ - { - "system": "http://hl7.org/fhir/unit-of-presentation", - "code": "syringe", - "display": "Syringe" - } - ] - }, - "routeOfAdministration": [ - { - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "47625008", - "display": "Intravenous route" - } - ] - }, - "maxSingleDose": { - "value": 100, - "unit": "mg" - }, - "maxDosePerDay": { - "value": 400, - "unit": "mg" - } - } - ] - }, - "2": { - "resourceType": "AdministrableProductDefinition", - "id": "administrable-product-definition-example-2", - "status": "active", - "formOf": [ - { - "reference": "MedicinalProductDefinition/tablet-product" - } - ], - "producedFrom": [ - { - "reference": "ManufacturedItemDefinition/tablet-500mg" - } - ], - "administrableDoseForm": { - "coding": [ - { - "system": "http://hl7.org/fhir/administrable-dose-form", - "code": "tablet", - "display": "Tablet" - } - ] - }, - "routeOfAdministration": [ - { - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "26643006", - "display": "Oral route" - } - ] - }, - "firstDose": { - "value": 500, - "unit": "mg" - }, - "maxDosePerTreatmentPeriod": { - "numerator": { - "value": 4000, - "unit": "mg" - }, - "denominator": { - "value": 1, - "unit": "day" - } - } - } - ], - "property": [ - { - "type": { - "coding": [ - { - "system": "http://example.org/characteristic-type", - "code": "dissolution-time", - "display": "Dissolution Time" - } - ] - }, - "valueQuantity": { - "value": 30, - "unit": "min" - } - } - ] - }, - "3": { - "resourceType": "AdministrableProductDefinition", - "id": "administrable-product-definition-example-3", - "identifier": [ - { - "system": "http://example.org/administrable-products", - "value": "APD-VET-001" - } - ], - "status": "active", - "formOf": [ - { - "reference": "MedicinalProductDefinition/veterinary-product" - } - ], - "administrableDoseForm": { - "coding": [ - { - "system": "http://hl7.org/fhir/administrable-dose-form", - "code": "solution-for-injection", - "display": "Solution for Injection" - } - ] - }, - "routeOfAdministration": [ - { - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "78421000", - "display": "Intramuscular route" - } - ] - }, - "targetSpecies": [ - { - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "388445009", - "display": "Bovine" - } - ] - }, - "withdrawalPeriod": [ - { - "tissue": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "226934003", - "display": "Meat" - } - ] - }, - "value": { - "value": 28, - "unit": "days", - "system": "http://unitsofmeasure.org", - "code": "d" - }, - "supportingInformation": "Do not use in animals producing milk for human consumption" - }, - { - "tissue": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "3718001", - "display": "Milk" - } - ] - }, - "value": { - "value": 96, - "unit": "hours", - "system": "http://unitsofmeasure.org", - "code": "h" - } - } - ] - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/administrableProductDefinitions.json.license b/packages/fhir/test/resources/administrableProductDefinitions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/administrableProductDefinitions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/administrableProductDefinitions.test.ts b/packages/fhir/test/resources/administrableProductDefinitions.test.ts deleted file mode 100644 index 4f739ca9..00000000 --- a/packages/fhir/test/resources/administrableProductDefinitions.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type AdministrableProductDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirAdministrableProductDefinition, - type untypedAdministrableProductDefinitionSchema, -} from '../../src/index.js' - -describe('AdministrableProductDefinition Resource', () => { - it('should validate FHIR administrableProductDefinition from administrableProductDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/administrableProductDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = - FhirAdministrableProductDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/adverseEvents.json b/packages/fhir/test/resources/adverseEvents.json deleted file mode 100644 index 28fe2a99..00000000 --- a/packages/fhir/test/resources/adverseEvents.json +++ /dev/null @@ -1,175 +0,0 @@ -{ - "0": { - "resourceType": "AdverseEvent", - "id": "example", - "identifier": { - "system": "http://acme.com/ids/patients/risks", - "value": "49476534" - }, - "actuality": "actual", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/adverse-event-category", - "code": "medication-mishap", - "display": "Medication Mishap" - } - ] - } - ], - "event": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "304386008", - "display": "O/E - itchy rash" - } - ], - "text": "This was a mild rash on the left forearm" - }, - "subject": { - "reference": "Patient/example" - }, - "date": "2017-01-29T12:34:56+00:00", - "seriousness": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/adverse-event-seriousness", - "code": "non-serious", - "display": "Non-serious" - } - ] - }, - "recorder": { - "reference": "Practitioner/example" - }, - "suspectEntity": [ - { - "instance": { - "reference": "Medication/example" - } - } - ] - }, - "1": { - "resourceType": "AdverseEvent", - "id": "example2", - "actuality": "potential", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/adverse-event-category", - "code": "product-problem", - "display": "Product Problem" - } - ] - } - ], - "event": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "39579001", - "display": "Anaphylaxis" - } - ] - }, - "subject": { - "reference": "Patient/example" - }, - "encounter": { - "reference": "Encounter/example" - }, - "detected": "2017-01-29", - "recordedDate": "2017-01-29", - "severity": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/adverse-event-severity", - "code": "severe", - "display": "Severe" - } - ] - }, - "outcome": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/adverse-event-outcome", - "code": "recovering", - "display": "Recovering" - } - ] - } - }, - "2": { - "resourceType": "AdverseEvent", - "id": "example3", - "actuality": "actual", - "subject": { - "reference": "Patient/example" - }, - "date": "2017-02-15T10:00:00+00:00", - "resultingCondition": [ - { - "reference": "Condition/example" - } - ], - "location": { - "reference": "Location/example" - }, - "contributor": [ - { - "reference": "Practitioner/example" - } - ], - "suspectEntity": [ - { - "instance": { - "reference": "Medication/example" - }, - "causality": [ - { - "assessment": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/adverse-event-causality-assess", - "code": "certain", - "display": "Certain" - } - ] - } - } - ] - } - ] - }, - "3": { - "resourceType": "AdverseEvent", - "id": "example4", - "actuality": "actual", - "subject": { - "reference": "Patient/example" - }, - "date": "2017-03-20", - "recorder": { - "reference": "Patient/example" - }, - "subjectMedicalHistory": [ - { - "reference": "Condition/example" - } - ], - "referenceDocument": [ - { - "reference": "DocumentReference/example" - } - ], - "study": [ - { - "reference": "ResearchStudy/example" - } - ] - } -} diff --git a/packages/fhir/test/resources/adverseEvents.json.license b/packages/fhir/test/resources/adverseEvents.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/adverseEvents.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/adverseEvents.test.ts b/packages/fhir/test/resources/adverseEvents.test.ts deleted file mode 100644 index aec67c12..00000000 --- a/packages/fhir/test/resources/adverseEvents.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type AdverseEvent } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirAdverseEvent, - type untypedAdverseEventSchema, -} from '../../src/index.js' - -describe('AdverseEvent Resource', () => { - it('should validate FHIR adverse event from adverseEvents.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/adverseEvents.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirAdverseEvent.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/allergyIntolerances.json b/packages/fhir/test/resources/allergyIntolerances.json deleted file mode 100644 index 4aa7481b..00000000 --- a/packages/fhir/test/resources/allergyIntolerances.json +++ /dev/null @@ -1,182 +0,0 @@ -{ - "0": { - "resourceType": "AllergyIntolerance", - "id": "allergy-example-0", - "clinicalStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", - "code": "active" - } - ] - }, - "verificationStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification", - "code": "confirmed" - } - ] - }, - "type": "allergy", - "category": ["food"], - "criticality": "high", - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "227493005", - "display": "Cashew nuts" - } - ] - }, - "patient": { - "reference": "Patient/patient-example" - }, - "onsetDateTime": "2020-05-12" - }, - "1": { - "resourceType": "AllergyIntolerance", - "id": "allergy-example-1", - "clinicalStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", - "code": "inactive" - } - ] - }, - "verificationStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification", - "code": "confirmed" - } - ] - }, - "type": "intolerance", - "category": ["medication"], - "criticality": "low", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "7980", - "display": "Penicillin" - } - ] - }, - "patient": { - "reference": "Patient/patient-example-2" - }, - "onsetAge": { - "value": 5, - "unit": "years" - }, - "recordedDate": "2018-03-15", - "reaction": [ - { - "substance": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "7980", - "display": "Penicillin" - } - ] - }, - "manifestation": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "271807003", - "display": "Rash" - } - ] - } - ], - "severity": "moderate" - } - ] - }, - "2": { - "resourceType": "AllergyIntolerance", - "id": "allergy-example-2", - "clinicalStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", - "code": "resolved" - } - ] - }, - "verificationStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification", - "code": "confirmed" - } - ] - }, - "type": "allergy", - "category": ["environment"], - "criticality": "unable-to-assess", - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "256277009", - "display": "Grass pollen" - } - ] - }, - "patient": { - "reference": "Patient/patient-example-3" - }, - "onsetString": "childhood", - "lastOccurrence": "2022-05-01" - }, - "3": { - "resourceType": "AllergyIntolerance", - "id": "allergy-example-3", - "clinicalStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", - "code": "active" - } - ] - }, - "verificationStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification", - "code": "unconfirmed" - } - ] - }, - "type": "allergy", - "category": ["biologic"], - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "111088007", - "display": "Latex" - } - ] - }, - "patient": { - "reference": "Patient/patient-xyz" - }, - "encounter": { - "reference": "Encounter/encounter-example" - }, - "note": [ - { - "text": "Patient reports itching and hives when exposed to latex gloves." - } - ] - } -} diff --git a/packages/fhir/test/resources/allergyIntolerances.json.license b/packages/fhir/test/resources/allergyIntolerances.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/allergyIntolerances.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/allergyIntolerances.test.ts b/packages/fhir/test/resources/allergyIntolerances.test.ts deleted file mode 100644 index 02641d82..00000000 --- a/packages/fhir/test/resources/allergyIntolerances.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type AllergyIntolerance } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirAllergyIntolerance, - type untypedAllergyIntoleranceSchema, -} from '../../src/index.js' - -describe('AllergyIntolerance Resource', () => { - it('should validate FHIR allergyIntolerance from allergyIntolerances.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/allergyIntolerances.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirAllergyIntolerance.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/appointmentResponses.json b/packages/fhir/test/resources/appointmentResponses.json deleted file mode 100644 index e0c9d845..00000000 --- a/packages/fhir/test/resources/appointmentResponses.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "0": { - "resourceType": "AppointmentResponse", - "id": "appointmentresponse-example-0", - "appointment": { - "reference": "Appointment/appointment-example-0" - }, - "participantType": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationType", - "code": "ATND", - "display": "attender" - } - ] - } - ], - "actor": { - "reference": "Patient/patient-example" - }, - "participantStatus": "accepted", - "comment": "I will be there on time" - }, - "1": { - "resourceType": "AppointmentResponse", - "id": "appointmentresponse-example-1", - "appointment": { - "reference": "Appointment/appointment-example-1" - }, - "start": "2024-11-20T14:30:00Z", - "end": "2024-11-20T15:00:00Z", - "participantType": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationType", - "code": "PPRF", - "display": "primary performer" - } - ] - } - ], - "actor": { - "reference": "Practitioner/practitioner-example" - }, - "participantStatus": "accepted" - }, - "2": { - "resourceType": "AppointmentResponse", - "id": "appointmentresponse-example-2", - "appointment": { - "reference": "Appointment/appointment-example-2" - }, - "actor": { - "reference": "Patient/patient-example-3" - }, - "participantStatus": "declined", - "comment": "Unable to attend due to prior commitment" - }, - "3": { - "resourceType": "AppointmentResponse", - "id": "appointmentresponse-example-3", - "identifier": [ - { - "system": "http://example.org/appointment-responses", - "value": "RESP-123" - } - ], - "appointment": { - "reference": "Appointment/appointment-example-3" - }, - "participantType": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationType", - "code": "PART", - "display": "Participation" - } - ] - } - ], - "actor": { - "reference": "Location/location-example" - }, - "participantStatus": "tentative", - "comment": "Room availability needs to be confirmed" - } -} diff --git a/packages/fhir/test/resources/appointmentResponses.json.license b/packages/fhir/test/resources/appointmentResponses.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/appointmentResponses.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/appointmentResponses.test.ts b/packages/fhir/test/resources/appointmentResponses.test.ts deleted file mode 100644 index 3f5fa592..00000000 --- a/packages/fhir/test/resources/appointmentResponses.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type AppointmentResponse } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirAppointmentResponse, - type untypedAppointmentResponseSchema, -} from '../../src/index.js' - -describe('AppointmentResponse Resource', () => { - it('should validate FHIR appointmentResponse from appointmentResponses.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/appointmentResponses.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirAppointmentResponse.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/appointments.json b/packages/fhir/test/resources/appointments.json deleted file mode 100644 index cdd0ab1f..00000000 --- a/packages/fhir/test/resources/appointments.json +++ /dev/null @@ -1,196 +0,0 @@ -{ - "0": { - "resourceType": "Appointment", - "id": "appointment-example-0", - "status": "booked", - "serviceCategory": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/service-category", - "code": "17", - "display": "General Practice" - } - ] - } - ], - "serviceType": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/service-type", - "code": "124", - "display": "General Practice" - } - ] - } - ], - "appointmentType": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0276", - "code": "ROUTINE", - "display": "Routine" - } - ] - }, - "start": "2024-12-15T09:00:00Z", - "end": "2024-12-15T10:00:00Z", - "participant": [ - { - "actor": { - "reference": "Patient/patient-example" - }, - "status": "accepted" - } - ] - }, - "1": { - "resourceType": "Appointment", - "id": "appointment-example-1", - "status": "fulfilled", - "serviceCategory": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/service-category", - "code": "8", - "display": "Counselling" - } - ] - } - ], - "priority": 5, - "description": "Follow-up consultation", - "start": "2024-11-20T14:30:00Z", - "end": "2024-11-20T15:00:00Z", - "minutesDuration": 30, - "created": "2024-11-01T10:00:00Z", - "participant": [ - { - "actor": { - "reference": "Patient/patient-example-2" - }, - "required": "required", - "status": "accepted" - }, - { - "actor": { - "reference": "Practitioner/practitioner-example" - }, - "status": "accepted" - } - ] - }, - "2": { - "resourceType": "Appointment", - "id": "appointment-example-2", - "status": "cancelled", - "cancelationReason": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/appointment-cancellation-reason", - "code": "pat", - "display": "Patient" - } - ] - }, - "serviceType": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/service-type", - "code": "57", - "display": "Immunization" - } - ] - } - ], - "start": "2024-10-10T08:00:00Z", - "end": "2024-10-10T08:15:00Z", - "comment": "Patient requested cancellation due to scheduling conflict", - "participant": [ - { - "actor": { - "reference": "Patient/patient-example-3" - }, - "status": "declined" - } - ] - }, - "3": { - "resourceType": "Appointment", - "id": "appointment-example-3", - "status": "proposed", - "serviceCategory": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/service-category", - "code": "27", - "display": "Specialist Medical" - } - ] - } - ], - "specialty": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "394579002", - "display": "Cardiology" - } - ] - } - ], - "appointmentType": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0276", - "code": "FOLLOWUP", - "display": "Follow-up" - } - ] - }, - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "313217001", - "display": "Cardiac stress testing" - } - ] - } - ], - "priority": 1, - "start": "2025-01-05T11:00:00Z", - "end": "2025-01-05T12:00:00Z", - "supportingInformation": [ - { - "reference": "DiagnosticReport/report-example" - } - ], - "participant": [ - { - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationType", - "code": "PPRF", - "display": "primary performer" - } - ] - } - ], - "actor": { - "reference": "Practitioner/practitioner-cardio" - }, - "required": "required", - "status": "needs-action" - } - ] - } -} diff --git a/packages/fhir/test/resources/appointments.json.license b/packages/fhir/test/resources/appointments.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/appointments.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/appointments.test.ts b/packages/fhir/test/resources/appointments.test.ts deleted file mode 100644 index 9b6acc6d..00000000 --- a/packages/fhir/test/resources/appointments.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Appointment } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirAppointment, - type untypedAppointmentSchema, -} from '../../src/index.js' - -describe('Appointment Resource', () => { - it('should validate FHIR appointment from appointments.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/appointments.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirAppointment.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/auditEvents.json b/packages/fhir/test/resources/auditEvents.json deleted file mode 100644 index 26025419..00000000 --- a/packages/fhir/test/resources/auditEvents.json +++ /dev/null @@ -1,301 +0,0 @@ -{ - "0": { - "resourceType": "AuditEvent", - "id": "audit-event-example-0", - "type": { - "system": "http://terminology.hl7.org/CodeSystem/audit-event-type", - "code": "rest", - "display": "RESTful Operation" - }, - "subtype": [ - { - "system": "http://hl7.org/fhir/restful-interaction", - "code": "read", - "display": "read" - } - ], - "action": "R", - "recorded": "2023-01-15T10:30:00Z", - "outcome": "0", - "agent": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", - "code": "humanuser" - } - ] - }, - "who": { - "reference": "Practitioner/example" - }, - "requestor": true, - "network": { - "address": "192.168.1.100", - "type": "2" - } - } - ], - "source": { - "observer": { - "reference": "Device/audit-system" - }, - "type": [ - { - "system": "http://terminology.hl7.org/CodeSystem/security-source-type", - "code": "4", - "display": "Application Server" - } - ] - }, - "entity": [ - { - "what": { - "reference": "Patient/example" - }, - "type": { - "system": "http://terminology.hl7.org/CodeSystem/audit-entity-type", - "code": "1", - "display": "Person" - }, - "role": { - "system": "http://terminology.hl7.org/CodeSystem/object-role", - "code": "1", - "display": "Patient" - } - } - ] - }, - "1": { - "resourceType": "AuditEvent", - "id": "audit-event-example-1", - "type": { - "system": "http://terminology.hl7.org/CodeSystem/audit-event-type", - "code": "rest", - "display": "RESTful Operation" - }, - "subtype": [ - { - "system": "http://hl7.org/fhir/restful-interaction", - "code": "update", - "display": "update" - } - ], - "action": "U", - "period": { - "start": "2023-02-20T14:00:00Z", - "end": "2023-02-20T14:01:00Z" - }, - "recorded": "2023-02-20T14:01:00Z", - "outcome": "0", - "outcomeDesc": "Successfully updated patient record", - "agent": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", - "code": "humanuser" - } - ] - }, - "role": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleClass", - "code": "ASSIGNED" - } - ] - } - ], - "who": { - "reference": "Practitioner/practitioner-1" - }, - "altId": "user123", - "name": "Dr. Jane Smith", - "requestor": true, - "network": { - "address": "192.168.1.101", - "type": "2" - } - } - ], - "source": { - "site": "Hospital Main Campus", - "observer": { - "reference": "Device/ehr-system" - }, - "type": [ - { - "system": "http://terminology.hl7.org/CodeSystem/security-source-type", - "code": "4" - } - ] - }, - "entity": [ - { - "what": { - "reference": "Patient/patient-1" - }, - "type": { - "system": "http://terminology.hl7.org/CodeSystem/audit-entity-type", - "code": "1" - }, - "role": { - "system": "http://terminology.hl7.org/CodeSystem/object-role", - "code": "1" - }, - "name": "John Doe" - } - ] - }, - "2": { - "resourceType": "AuditEvent", - "id": "audit-event-example-2", - "type": { - "system": "http://terminology.hl7.org/CodeSystem/audit-event-type", - "code": "rest", - "display": "RESTful Operation" - }, - "subtype": [ - { - "system": "http://hl7.org/fhir/restful-interaction", - "code": "create", - "display": "create" - } - ], - "action": "C", - "recorded": "2023-03-10T09:15:00Z", - "outcome": "0", - "purposeOfEvent": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", - "code": "TREAT" - } - ] - } - ], - "agent": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", - "code": "humanuser" - } - ] - }, - "who": { - "reference": "Practitioner/surgeon" - }, - "requestor": true, - "location": { - "reference": "Location/operating-room-1" - }, - "policy": ["http://example.org/fhir/policy/data-access"] - } - ], - "source": { - "observer": { - "reference": "Device/surgical-system" - } - }, - "entity": [ - { - "what": { - "reference": "Procedure/surgery-1" - }, - "type": { - "system": "http://terminology.hl7.org/CodeSystem/audit-entity-type", - "code": "2", - "display": "System Object" - }, - "lifecycle": { - "system": "http://terminology.hl7.org/CodeSystem/dicom-audit-lifecycle", - "code": "1", - "display": "Origination / Creation" - }, - "description": "New surgical procedure record created" - } - ] - }, - "3": { - "resourceType": "AuditEvent", - "id": "audit-event-example-3", - "type": { - "system": "http://terminology.hl7.org/CodeSystem/audit-event-type", - "code": "rest", - "display": "RESTful Operation" - }, - "subtype": [ - { - "system": "http://hl7.org/fhir/restful-interaction", - "code": "search-type", - "display": "search-type" - } - ], - "action": "E", - "recorded": "2023-04-05T16:45:00Z", - "outcome": "0", - "agent": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", - "code": "humanuser" - } - ] - }, - "who": { - "reference": "Practitioner/researcher" - }, - "requestor": true, - "network": { - "address": "203.0.113.42", - "type": "2" - }, - "purposeOfUse": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", - "code": "HRESCH", - "display": "healthcare research" - } - ] - } - ] - } - ], - "source": { - "observer": { - "reference": "Device/fhir-server" - } - }, - "entity": [ - { - "type": { - "system": "http://terminology.hl7.org/CodeSystem/audit-entity-type", - "code": "2" - }, - "role": { - "system": "http://terminology.hl7.org/CodeSystem/object-role", - "code": "3", - "display": "Report" - }, - "query": "UGF0aWVudD9nZW5kZXI9bWFsZSZfY291bnQ9MTA=", - "detail": [ - { - "type": "search-parameters", - "valueString": "gender=male&_count=10" - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/auditEvents.json.license b/packages/fhir/test/resources/auditEvents.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/auditEvents.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/auditEvents.test.ts b/packages/fhir/test/resources/auditEvents.test.ts deleted file mode 100644 index 0cd5d36b..00000000 --- a/packages/fhir/test/resources/auditEvents.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type AuditEvent } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirAuditEvent, - type untypedAuditEventSchema, -} from '../../src/index.js' - -describe('AuditEvent Resource', () => { - it('should validate FHIR AuditEvents from auditEvents.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/auditEvents.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirAuditEvent.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/basics.json b/packages/fhir/test/resources/basics.json deleted file mode 100644 index 08270f2e..00000000 --- a/packages/fhir/test/resources/basics.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "0": { - "resourceType": "Basic", - "id": "basic-example-1", - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/basic-resource-type", - "code": "consent", - "display": "Consent" - } - ], - "text": "Consent" - }, - "subject": { - "reference": "Patient/example" - }, - "created": "2023-01-15", - "author": { - "reference": "Practitioner/example" - } - }, - "1": { - "resourceType": "Basic", - "id": "basic-example-2", - "identifier": [ - { - "system": "http://example.org/identifiers", - "value": "12345" - } - ], - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/basic-resource-type", - "code": "referral", - "display": "Referral" - } - ] - }, - "subject": { - "reference": "Patient/example2" - } - }, - "2": { - "resourceType": "Basic", - "id": "basic-example-3", - "code": { - "coding": [ - { - "system": "http://example.org/basic-types", - "code": "special-authorization", - "display": "Special Authorization" - } - ] - }, - "created": "2023-06-01", - "author": { - "reference": "Organization/example" - } - }, - "3": { - "resourceType": "Basic", - "id": "basic-example-4", - "identifier": [ - { - "system": "http://hospital.example.org/basic", - "value": "ABC-789" - } - ], - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/basic-resource-type", - "code": "protocol", - "display": "Protocol" - } - ] - }, - "subject": { - "reference": "Patient/example3" - }, - "created": "2023-12-10", - "author": { - "reference": "Practitioner/example2" - } - } -} diff --git a/packages/fhir/test/resources/basics.json.license b/packages/fhir/test/resources/basics.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/basics.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/basics.test.ts b/packages/fhir/test/resources/basics.test.ts deleted file mode 100644 index b805e18c..00000000 --- a/packages/fhir/test/resources/basics.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Basic } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirBasic, type untypedBasicSchema } from '../../src/index.js' - -describe('Basic Resource', () => { - it('should validate FHIR basic from basics.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/basics.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirBasic.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/binaries.json b/packages/fhir/test/resources/binaries.json deleted file mode 100644 index 293fc42d..00000000 --- a/packages/fhir/test/resources/binaries.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "0": { - "resourceType": "Binary", - "id": "binary-example-1", - "contentType": "application/pdf", - "data": "JVBERi0xLjQKJeLjz9MKNCAwIG9iago8PC9GaWx0ZXIvRmxhdGVEZWNvZGUvTGVuZ3==" - }, - "1": { - "resourceType": "Binary", - "id": "binary-example-2", - "contentType": "image/png", - "securityContext": { - "reference": "Patient/example" - }, - "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" - }, - "3": { - "resourceType": "Binary", - "id": "binary-example-3", - "contentType": "text/plain", - "data": "VGhpcyBpcyBhIHNhbXBsZSB0ZXh0IGZpbGUgY29udGVudA==" - }, - "2": { - "resourceType": "Binary", - "id": "binary-example-4", - "contentType": "application/xml", - "securityContext": { - "reference": "DocumentReference/example" - }, - "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4=" - } -} diff --git a/packages/fhir/test/resources/binaries.json.license b/packages/fhir/test/resources/binaries.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/binaries.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/binaries.test.ts b/packages/fhir/test/resources/binaries.test.ts deleted file mode 100644 index 2dfd6234..00000000 --- a/packages/fhir/test/resources/binaries.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Binary } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirBinary, type untypedBinarySchema } from '../../src/index.js' - -describe('Binary Resource', () => { - it('should validate FHIR binary from binaries.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/binaries.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirBinary.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/binaryData.test.ts b/packages/fhir/test/resources/binaryData.test.ts deleted file mode 100644 index b7712448..00000000 --- a/packages/fhir/test/resources/binaryData.test.ts +++ /dev/null @@ -1,145 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Binary } from 'fhir/r4b.js' -import { FhirBinary } from '../../src/index.js' - -describe('FhirBinary', () => { - describe('getDataAsUint8Array', () => { - it('should decode base64 data correctly', () => { - // "Hello, World!" in base64 - const base64Data = 'SGVsbG8sIFdvcmxkIQ==' - const binary: Binary = { - resourceType: 'Binary', - contentType: 'text/plain', - data: base64Data, - } - - const fhirBinary = new FhirBinary(binary) - const data = fhirBinary.dataAsUint8Array - - expect(data).toBeInstanceOf(Uint8Array) - expect(data?.length).toBe(13) - - // Convert back to string to verify - const decoder = new TextDecoder() - const text = decoder.decode(data) - expect(text).toBe('Hello, World!') - }) - - it('should decode binary data correctly', () => { - // Create a simple binary sequence - const bytes = new Uint8Array([0, 1, 2, 3, 255, 254, 253]) - const base64Data = Buffer.from(bytes).toString('base64') - - const binary: Binary = { - resourceType: 'Binary', - contentType: 'application/octet-stream', - data: base64Data, - } - - const fhirBinary = new FhirBinary(binary) - const data = fhirBinary.dataAsUint8Array - - expect(data).toBeInstanceOf(Uint8Array) - expect(data?.length).toBe(7) - expect(Array.from(data ?? [])).toEqual([0, 1, 2, 3, 255, 254, 253]) - }) - - it('should return undefined when no data', () => { - const binary: Binary = { - resourceType: 'Binary', - contentType: 'text/plain', - } - - const fhirBinary = new FhirBinary(binary) - const data = fhirBinary.dataAsUint8Array - - expect(data).toBeUndefined() - }) - - it('should handle empty base64 string', () => { - const binary: Binary = { - resourceType: 'Binary', - contentType: 'text/plain', - data: '', - } - - const fhirBinary = new FhirBinary(binary) - const data = fhirBinary.dataAsUint8Array - - // Empty string is still valid base64, returns empty array - expect(data).toBeDefined() - expect(data?.length).toBe(0) - }) - - it('should handle invalid base64 gracefully', () => { - // Note: Node.js Buffer.from() is lenient and doesn't throw for many invalid inputs - // This test verifies the method doesn't crash - const binary: Binary = { - resourceType: 'Binary', - contentType: 'text/plain', - data: '!!!invalid!!!', - } - - const fhirBinary = new FhirBinary(binary) - - // Should not throw, but may return unexpected results - expect(() => fhirBinary.dataAsUint8Array).not.toThrow() - }) - - it('should handle large binary data', () => { - // Create a 1KB array - const bytes = new Uint8Array(1024) - for (let i = 0; i < bytes.length; i++) { - bytes[i] = i % 256 - } - const base64Data = Buffer.from(bytes).toString('base64') - - const binary: Binary = { - resourceType: 'Binary', - contentType: 'application/octet-stream', - data: base64Data, - } - - const fhirBinary = new FhirBinary(binary) - const data = fhirBinary.dataAsUint8Array - - expect(data).toBeInstanceOf(Uint8Array) - expect(data?.length).toBe(1024) - - // Verify the data matches - for (let i = 0; i < 1024; i++) { - expect(data?.[i]).toBe(i % 256) - } - }) - - it('should decode JSON data stored as base64', () => { - const jsonData = { name: 'Test', value: 42 } - const jsonString = JSON.stringify(jsonData) - const base64Data = Buffer.from(jsonString).toString('base64') - - const binary: Binary = { - resourceType: 'Binary', - contentType: 'application/json', - data: base64Data, - } - - const fhirBinary = new FhirBinary(binary) - const data = fhirBinary.dataAsUint8Array - - expect(data).toBeInstanceOf(Uint8Array) - - // Decode and parse the JSON - const decoder = new TextDecoder() - const decodedJson = JSON.parse(decoder.decode(data)) - - expect(decodedJson).toEqual(jsonData) - }) - }) -}) diff --git a/packages/fhir/test/resources/biologicallyDerivedProducts.json b/packages/fhir/test/resources/biologicallyDerivedProducts.json deleted file mode 100644 index b54724bf..00000000 --- a/packages/fhir/test/resources/biologicallyDerivedProducts.json +++ /dev/null @@ -1,148 +0,0 @@ -{ - "0": { - "resourceType": "BiologicallyDerivedProduct", - "id": "biologically-derived-product-example-0" - }, - "1": { - "resourceType": "BiologicallyDerivedProduct", - "id": "biologically-derived-product-example-1", - "productCategory": "cells", - "productCode": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "420420001", - "display": "Peripheral blood stem cells" - } - ] - }, - "status": "available", - "collection": { - "collector": { - "reference": "Practitioner/example" - }, - "source": { - "reference": "Patient/example" - }, - "collectedDateTime": "2024-01-15T09:00:00Z" - } - }, - "2": { - "resourceType": "BiologicallyDerivedProduct", - "id": "biologically-derived-product-example-2", - "identifier": [ - { - "system": "http://example.org/blood-bank", - "value": "BB-2024-001" - } - ], - "productCategory": "fluid", - "productCode": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "119297000", - "display": "Blood specimen" - } - ] - }, - "status": "available", - "quantity": 2, - "collection": { - "source": { - "reference": "Patient/donor123" - }, - "collectedPeriod": { - "start": "2024-01-10T08:00:00Z", - "end": "2024-01-10T08:30:00Z" - } - }, - "processing": [ - { - "description": "Leukoreduction", - "procedure": { - "coding": [ - { - "system": "http://example.org/blood-processing", - "code": "leukoreduction", - "display": "Leukoreduction" - } - ] - }, - "timeDateTime": "2024-01-10T10:00:00Z" - } - ], - "storage": [ - { - "description": "Refrigerated storage", - "temperature": 4, - "scale": "celsius", - "duration": { - "start": "2024-01-10T12:00:00Z" - } - } - ] - }, - "3": { - "resourceType": "BiologicallyDerivedProduct", - "id": "biologically-derived-product-example-3", - "identifier": [ - { - "system": "http://example.org/tissue-bank", - "value": "TB-2024-456" - } - ], - "productCategory": "tissue", - "productCode": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "272411007", - "display": "Bone tissue" - } - ] - }, - "status": "unavailable", - "collection": { - "collector": { - "reference": "Practitioner/surgeon" - }, - "source": { - "reference": "Patient/donor456" - }, - "collectedDateTime": "2023-12-01" - }, - "manipulation": { - "description": "Allograft processing and sterilization", - "timePeriod": { - "start": "2023-12-02", - "end": "2023-12-05" - } - }, - "processing": [ - { - "description": "Freeze-drying", - "timeDateTime": "2023-12-03" - }, - { - "description": "Gamma irradiation sterilization", - "timeDateTime": "2023-12-04" - } - ], - "storage": [ - { - "description": "Frozen storage", - "temperature": -80, - "scale": "celsius", - "duration": { - "start": "2023-12-06" - } - } - ], - "request": [ - { - "reference": "ServiceRequest/tissue-request" - } - ] - } -} diff --git a/packages/fhir/test/resources/biologicallyDerivedProducts.json.license b/packages/fhir/test/resources/biologicallyDerivedProducts.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/biologicallyDerivedProducts.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/biologicallyDerivedProducts.test.ts b/packages/fhir/test/resources/biologicallyDerivedProducts.test.ts deleted file mode 100644 index aae8311e..00000000 --- a/packages/fhir/test/resources/biologicallyDerivedProducts.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type BiologicallyDerivedProduct } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirBiologicallyDerivedProduct, - type untypedBiologicallyDerivedProductSchema, -} from '../../src/index.js' - -describe('BiologicallyDerivedProduct Resource', () => { - it('should validate FHIR biologicallyDerivedProduct from biologicallyDerivedProducts.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/biologicallyDerivedProducts.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = - FhirBiologicallyDerivedProduct.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/bodyStructures.json b/packages/fhir/test/resources/bodyStructures.json deleted file mode 100644 index 67a68bd3..00000000 --- a/packages/fhir/test/resources/bodyStructures.json +++ /dev/null @@ -1,164 +0,0 @@ -{ - "0": { - "resourceType": "BodyStructure", - "id": "example-0", - "identifier": [ - { - "system": "http://example.org/bodystructure-identifiers", - "value": "BS-001" - } - ], - "active": true, - "morphology": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "4147007", - "display": "Mass" - } - ] - }, - "location": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "78961009", - "display": "Splenic structure" - } - ] - }, - "description": "Mass in the splenic region", - "patient": { - "reference": "Patient/patient-001" - } - }, - "1": { - "resourceType": "BodyStructure", - "id": "example-1", - "active": true, - "morphology": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "399721002", - "display": "Tumor" - } - ] - }, - "location": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "80248007", - "display": "Left breast structure" - } - ] - }, - "locationQualifier": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "261183002", - "display": "Upper outer quadrant" - } - ] - } - ], - "description": "Tumor located in upper outer quadrant of left breast", - "patient": { - "reference": "Patient/patient-002", - "display": "Jane Doe" - } - }, - "2": { - "resourceType": "BodyStructure", - "id": "example-2", - "identifier": [ - { - "system": "http://example.org/bodystructure-identifiers", - "value": "BS-002" - } - ], - "active": false, - "location": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "39937001", - "display": "Skin structure" - } - ] - }, - "locationQualifier": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "419161000", - "display": "Unilateral left" - } - ] - }, - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "261122009", - "display": "Lower" - } - ] - } - ], - "description": "Previously marked surgical site on lower left skin area", - "patient": { - "reference": "Patient/patient-003" - } - }, - "3": { - "resourceType": "BodyStructure", - "id": "example-3", - "active": true, - "morphology": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "68566005", - "display": "Urinary tract infectious disease" - } - ] - }, - "location": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "113345001", - "display": "Abdominal structure" - } - ] - }, - "locationQualifier": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "255561001", - "display": "Medial" - } - ] - } - ], - "description": "Abdominal mass, medial location", - "image": [ - { - "contentType": "image/jpeg", - "url": "http://example.org/images/bodystructure-001.jpg", - "title": "Abdominal CT scan" - } - ], - "patient": { - "reference": "Patient/patient-004", - "display": "John Smith" - } - } -} diff --git a/packages/fhir/test/resources/bodyStructures.json.license b/packages/fhir/test/resources/bodyStructures.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/bodyStructures.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/bodyStructures.test.ts b/packages/fhir/test/resources/bodyStructures.test.ts deleted file mode 100644 index 1d9f2622..00000000 --- a/packages/fhir/test/resources/bodyStructures.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type BodyStructure } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirBodyStructure, - type untypedBodyStructureSchema, -} from '../../src/index.js' - -describe('BodyStructure Resource', () => { - it('should validate FHIR BodyStructures from bodyStructures.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/bodyStructures.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirBodyStructure.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/bundleFindResourcesWith.test.ts b/packages/fhir/test/resources/bundleFindResourcesWith.test.ts deleted file mode 100644 index af43352e..00000000 --- a/packages/fhir/test/resources/bundleFindResourcesWith.test.ts +++ /dev/null @@ -1,214 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Bundle, type Patient, type Observation } from 'fhir/r4b.js' -import { FhirBundle } from '../../src/index.js' - -const patient = (resource: Patient): Patient => resource -const observation = (resource: Observation): Observation => resource - -describe('FhirBundle - findResourcesWith', () => { - it('should find resources matching a predicate', () => { - const bundle: Bundle = { - resourceType: 'Bundle', - type: 'collection', - entry: [ - { - resource: patient({ - resourceType: 'Patient', - id: '1', - active: true, - name: [{ family: 'Smith' }], - }), - }, - { - resource: patient({ - resourceType: 'Patient', - id: '2', - active: false, - name: [{ family: 'Jones' }], - }), - }, - { - resource: observation({ - resourceType: 'Observation', - id: '3', - status: 'final', - code: { text: 'Blood pressure' }, - }), - }, - ], - } - - const fhirBundle = FhirBundle.parseGeneric(bundle) - - // Find all active patients - const activePatients = fhirBundle.findResources( - 'Patient', - (resource) => resource.active === true, - ) - - expect(activePatients).toHaveLength(1) - expect(activePatients.at(0)?.id).toBe('1') - }) - - it('should return empty array when no resources match', () => { - const bundle: Bundle = { - resourceType: 'Bundle', - type: 'collection', - entry: [ - { - resource: patient({ - resourceType: 'Patient', - id: '1', - active: false, - }), - }, - ], - } - - const fhirBundle = FhirBundle.parseGeneric(bundle) - - const activePatients = fhirBundle.findResources( - 'Patient', - (resource) => resource.active === true, - ) - - expect(activePatients).toHaveLength(0) - }) - - it('should handle bundles with no entries', () => { - const bundle: Bundle = { - resourceType: 'Bundle', - type: 'collection', - } - - const fhirBundle = FhirBundle.parseGeneric(bundle) - - const results = fhirBundle.findResources('Patient') - expect(results).toHaveLength(0) - }) - - it('should handle entries without resources', () => { - const bundle: Bundle = { - resourceType: 'Bundle', - type: 'collection', - entry: [ - { - fullUrl: 'http://example.com/Patient/1', - }, - { - resource: patient({ - resourceType: 'Patient', - id: '2', - active: true, - }), - }, - ], - } - - const fhirBundle = FhirBundle.parseGeneric(bundle) - - const patients = fhirBundle.findResources('Patient') - - expect(patients).toHaveLength(1) - expect(patients.at(0)?.id).toBe('2') - }) - - it('should find a resource by type and ID', () => { - const bundle: Bundle = { - resourceType: 'Bundle', - type: 'collection', - entry: [ - { - resource: patient({ - resourceType: 'Patient', - id: 'patient-1', - }), - }, - ], - } - - const fhirBundle = FhirBundle.parseGeneric(bundle) - - expect(fhirBundle.resourceById('Patient', 'patient-1')?.id).toBe( - 'patient-1', - ) - expect( - fhirBundle.resourceById('Patient', 'missing'), - ).toBeUndefined() - }) - - it('should work with complex predicates', () => { - const bundle: Bundle = { - resourceType: 'Bundle', - type: 'collection', - entry: [ - { - resource: observation({ - resourceType: 'Observation', - id: '1', - status: 'final', - code: { - coding: [ - { - system: 'http://loinc.org', - code: '8867-4', - }, - ], - }, - }), - }, - { - resource: observation({ - resourceType: 'Observation', - id: '2', - status: 'preliminary', - code: { - coding: [ - { - system: 'http://loinc.org', - code: '85354-9', - }, - ], - }, - }), - }, - { - resource: observation({ - resourceType: 'Observation', - id: '3', - status: 'final', - code: { - coding: [ - { - system: 'http://snomed.info/sct', - code: '12345', - }, - ], - }, - }), - }, - ], - } - - const fhirBundle = FhirBundle.parseGeneric(bundle) - - // Find final observations with LOINC codes - const loincObservations = fhirBundle.findResources( - 'Observation', - (resource) => - resource.status === 'final' && - (resource.code.coding?.some((c) => c.system === 'http://loinc.org') ?? - false), - ) - - expect(loincObservations).toHaveLength(1) - expect(loincObservations.at(0)?.id).toBe('1') - }) -}) diff --git a/packages/fhir/test/resources/bundleWithNewResources.test.ts b/packages/fhir/test/resources/bundleWithNewResources.test.ts deleted file mode 100644 index 4689bc2f..00000000 --- a/packages/fhir/test/resources/bundleWithNewResources.test.ts +++ /dev/null @@ -1,220 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { consentSchema, FhirBundle } from '../../src/index.js' - -describe('Bundle with new resource types', () => { - it('should validate a Bundle containing Consent resources', () => { - const bundleData = { - resourceType: 'Bundle', - type: 'collection', - entry: [ - { - resource: { - resourceType: 'Consent', - id: 'consent-1', - status: 'active', - scope: { - coding: [ - { - system: 'http://terminology.hl7.org/CodeSystem/consentscope', - code: 'patient-privacy', - }, - ], - }, - category: [ - { - coding: [ - { - system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', - code: 'INFA', - }, - ], - }, - ], - }, - }, - ], - } - - const consentBundle = FhirBundle.parse(bundleData, consentSchema) - expect(consentBundle.value.entry?.[0]?.resource?.resourceType).toBe( - 'Consent', - ) - - const genericBundle = FhirBundle.parseGeneric(bundleData) - expect(genericBundle.value.entry?.[0]?.resource?.resourceType).toBe( - 'Consent', - ) - }) - - it('should validate a Bundle containing Provenance resources', () => { - const bundleData = { - resourceType: 'Bundle', - type: 'collection', - entry: [ - { - resource: { - resourceType: 'Provenance', - id: 'provenance-1', - target: [{ reference: 'Patient/example' }], - recorded: '2023-01-15T10:30:00Z', - agent: [ - { - who: { reference: 'Practitioner/example' }, - }, - ], - }, - }, - ], - } - - const bundle = FhirBundle.parseGeneric(bundleData) - expect(bundle.value.entry?.[0]?.resource?.resourceType).toBe('Provenance') - }) - - it('should validate a Bundle containing AuditEvent resources', () => { - const bundleData = { - resourceType: 'Bundle', - type: 'collection', - entry: [ - { - resource: { - resourceType: 'AuditEvent', - id: 'audit-1', - type: { - system: 'http://terminology.hl7.org/CodeSystem/audit-event-type', - code: 'rest', - }, - recorded: '2023-01-15T10:30:00Z', - agent: [ - { - who: { reference: 'Practitioner/example' }, - requestor: true, - }, - ], - source: { - observer: { reference: 'Device/audit-system' }, - }, - }, - }, - ], - } - - const bundle = FhirBundle.parseGeneric(bundleData) - expect(bundle.value.entry?.[0]?.resource?.resourceType).toBe('AuditEvent') - }) - - it('should validate a Bundle containing Contract resources', () => { - const bundleData = { - resourceType: 'Bundle', - type: 'collection', - entry: [ - { - resource: { - resourceType: 'Contract', - id: 'contract-1', - status: 'executed', - issued: '2023-01-15T10:30:00Z', - type: { - coding: [ - { - system: 'http://terminology.hl7.org/CodeSystem/contract-type', - code: 'privacy', - }, - ], - }, - term: [ - { - offer: { - text: 'Patient agrees to privacy terms', - }, - }, - ], - }, - }, - ], - } - - const bundle = FhirBundle.parseGeneric(bundleData) - expect(bundle.value.entry?.[0]?.resource?.resourceType).toBe('Contract') - }) - - it('should validate a Bundle containing VerificationResult resources', () => { - const bundleData = { - resourceType: 'Bundle', - type: 'collection', - entry: [ - { - resource: { - resourceType: 'VerificationResult', - id: 'verification-1', - status: 'validated', - statusDate: '2023-01-15T10:30:00Z', - }, - }, - ], - } - - const bundle = FhirBundle.parseGeneric(bundleData) - expect(bundle.value.entry?.[0]?.resource?.resourceType).toBe( - 'VerificationResult', - ) - }) - - it('should validate a Bundle containing multiple new resource types', () => { - const bundleData = { - resourceType: 'Bundle', - type: 'collection', - entry: [ - { - resource: { - resourceType: 'Consent', - id: 'consent-1', - status: 'active', - scope: { - coding: [{ system: 'http://example.org', code: 'privacy' }], - }, - category: [ - { - coding: [{ system: 'http://example.org', code: 'INFA' }], - }, - ], - }, - }, - { - resource: { - resourceType: 'Provenance', - id: 'provenance-1', - target: [{ reference: 'Consent/consent-1' }], - recorded: '2023-01-15T10:30:00Z', - agent: [{ who: { reference: 'Practitioner/example' } }], - }, - }, - { - resource: { - resourceType: 'AuditEvent', - id: 'audit-1', - type: { system: 'http://example.org', code: 'rest' }, - recorded: '2023-01-15T10:30:00Z', - agent: [ - { who: { reference: 'Practitioner/example' }, requestor: true }, - ], - source: { observer: { reference: 'Device/system' } }, - }, - }, - ], - } - - const bundle = FhirBundle.parseGeneric(bundleData) - expect(bundle.value.entry?.length).toBe(3) - expect(bundle.value.entry?.[0]?.resource?.resourceType).toBe('Consent') - expect(bundle.value.entry?.[1]?.resource?.resourceType).toBe('Provenance') - expect(bundle.value.entry?.[2]?.resource?.resourceType).toBe('AuditEvent') - }) -}) diff --git a/packages/fhir/test/resources/bundles.json b/packages/fhir/test/resources/bundles.json deleted file mode 100644 index 0c3ed847..00000000 --- a/packages/fhir/test/resources/bundles.json +++ /dev/null @@ -1,766 +0,0 @@ -{ - "0": { - "resourceType": "Bundle", - "id": "bundle-example-0", - "type": "collection", - "entry": [ - { - "fullUrl": "https://example.org/Patient/patient-1", - "resource": { - "resourceType": "Patient", - "id": "patient-1", - "name": [ - { - "family": "Smith", - "given": ["John"] - } - ], - "gender": "male" - } - }, - { - "fullUrl": "https://example.org/Observation/obs-1", - "resource": { - "resourceType": "Observation", - "id": "obs-1", - "status": "final", - "code": { - "text": "Blood pressure" - }, - "subject": { - "reference": "Patient/patient-1" - } - } - } - ] - }, - "1": { - "resourceType": "Bundle", - "id": "bundle-example-1", - "type": "searchset", - "total": 3, - "entry": [ - { - "fullUrl": "https://example.org/Condition/cond-1", - "resource": { - "resourceType": "Condition", - "id": "cond-1", - "clinicalStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", - "code": "active" - } - ] - }, - "code": { - "text": "Diabetes" - }, - "subject": { - "reference": "Patient/patient-1" - } - }, - "search": { - "mode": "match" - } - }, - { - "fullUrl": "https://example.org/MedicationRequest/medreq-1", - "resource": { - "resourceType": "MedicationRequest", - "id": "medreq-1", - "status": "active", - "intent": "order", - "medicationCodeableConcept": { - "text": "Insulin" - }, - "subject": { - "reference": "Patient/patient-1" - } - }, - "search": { - "mode": "include" - } - } - ] - }, - "2": { - "resourceType": "Bundle", - "id": "bundle-example-2", - "type": "transaction", - "entry": [ - { - "fullUrl": "urn:uuid:61ebe359-bfdc-4613-8bf2-c5e300945f0a", - "resource": { - "resourceType": "Patient", - "name": [ - { - "family": "Transaction", - "given": ["Test"] - } - ] - }, - "request": { - "method": "POST", - "url": "Patient" - } - } - ] - }, - "3": { - "resourceType": "Bundle", - "id": "bundle-all-resources", - "type": "collection", - "timestamp": "2024-11-15T10:00:00Z", - "entry": [ - { - "fullUrl": "https://example.org/AllergyIntolerance/allergy-1", - "resource": { - "resourceType": "AllergyIntolerance", - "id": "allergy-1", - "clinicalStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", - "code": "active" - } - ] - }, - "type": "allergy", - "code": { - "text": "Peanuts" - }, - "patient": { - "reference": "Patient/patient-1" - } - } - }, - { - "fullUrl": "https://example.org/Appointment/appointment-1", - "resource": { - "resourceType": "Appointment", - "id": "appointment-1", - "status": "booked", - "start": "2024-12-15T09:00:00Z", - "end": "2024-12-15T10:00:00Z", - "participant": [ - { - "actor": { - "reference": "Patient/patient-1" - }, - "status": "accepted" - } - ] - } - }, - { - "fullUrl": "https://example.org/AppointmentResponse/apptresp-1", - "resource": { - "resourceType": "AppointmentResponse", - "id": "apptresp-1", - "appointment": { - "reference": "Appointment/appointment-1" - }, - "participantStatus": "accepted" - } - }, - { - "fullUrl": "https://example.org/Composition/comp-1", - "resource": { - "resourceType": "Composition", - "id": "comp-1", - "status": "final", - "type": { - "text": "Progress note" - }, - "subject": { - "reference": "Patient/patient-1" - }, - "date": "2024-11-15", - "author": [ - { - "reference": "Practitioner/practitioner-1" - } - ], - "title": "Patient Progress Note" - } - }, - { - "fullUrl": "https://example.org/Condition/cond-1", - "resource": { - "resourceType": "Condition", - "id": "cond-1", - "clinicalStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", - "code": "active" - } - ] - }, - "code": { - "text": "Hypertension" - }, - "subject": { - "reference": "Patient/patient-1" - } - } - }, - { - "fullUrl": "https://example.org/Coverage/coverage-1", - "resource": { - "resourceType": "Coverage", - "id": "coverage-1", - "status": "active", - "beneficiary": { - "reference": "Patient/patient-1" - }, - "payor": [ - { - "reference": "Organization/org-insurance" - } - ] - } - }, - { - "fullUrl": "https://example.org/Device/device-1", - "resource": { - "resourceType": "Device", - "id": "device-1", - "status": "active", - "type": { - "text": "Blood glucose meter" - } - } - }, - { - "fullUrl": "https://example.org/DiagnosticReport/report-1", - "resource": { - "resourceType": "DiagnosticReport", - "id": "report-1", - "status": "final", - "code": { - "text": "Lab results" - }, - "subject": { - "reference": "Patient/patient-1" - } - } - }, - { - "fullUrl": "https://example.org/Encounter/enc-1", - "resource": { - "resourceType": "Encounter", - "id": "enc-1", - "status": "finished", - "class": { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "AMB" - }, - "subject": { - "reference": "Patient/patient-1" - } - } - }, - { - "fullUrl": "https://example.org/Endpoint/endpoint-1", - "resource": { - "resourceType": "Endpoint", - "id": "endpoint-1", - "status": "active", - "connectionType": { - "system": "http://terminology.hl7.org/CodeSystem/endpoint-connection-type", - "code": "hl7-fhir-rest" - }, - "address": "https://example.org/fhir", - "payloadType": [ - { - "text": "FHIR" - } - ] - } - }, - { - "fullUrl": "https://example.org/EpisodeOfCare/episode-1", - "resource": { - "resourceType": "EpisodeOfCare", - "id": "episode-1", - "status": "active", - "patient": { - "reference": "Patient/patient-1" - } - } - }, - { - "fullUrl": "https://example.org/Group/group-1", - "resource": { - "resourceType": "Group", - "id": "group-1", - "type": "person", - "actual": true - } - }, - { - "fullUrl": "https://example.org/HealthcareService/service-1", - "resource": { - "resourceType": "HealthcareService", - "id": "service-1", - "active": true, - "name": "General Practice" - } - }, - { - "fullUrl": "https://example.org/ImagingStudy/imaging-1", - "resource": { - "resourceType": "ImagingStudy", - "id": "imaging-1", - "status": "available", - "subject": { - "reference": "Patient/patient-1" - } - } - }, - { - "fullUrl": "https://example.org/Immunization/imm-1", - "resource": { - "resourceType": "Immunization", - "id": "imm-1", - "status": "completed", - "vaccineCode": { - "text": "Flu vaccine" - }, - "patient": { - "reference": "Patient/patient-1" - }, - "occurrenceDateTime": "2024-10-01" - } - }, - { - "fullUrl": "https://example.org/ImmunizationEvaluation/immeval-1", - "resource": { - "resourceType": "ImmunizationEvaluation", - "id": "immeval-1", - "status": "completed", - "patient": { - "reference": "Patient/patient-1" - }, - "targetDisease": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "6142004", - "display": "Influenza" - } - ] - }, - "immunizationEvent": { - "reference": "Immunization/imm-1" - }, - "doseStatus": { - "text": "Valid" - } - } - }, - { - "fullUrl": "https://example.org/ImmunizationRecommendation/immrec-1", - "resource": { - "resourceType": "ImmunizationRecommendation", - "id": "immrec-1", - "patient": { - "reference": "Patient/patient-1" - }, - "date": "2024-11-15", - "recommendation": [ - { - "vaccineCode": [ - { - "text": "COVID-19 vaccine" - } - ], - "forecastStatus": { - "text": "Due" - } - } - ] - } - }, - { - "fullUrl": "https://example.org/Library/lib-1", - "resource": { - "resourceType": "Library", - "id": "lib-1", - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/library-type", - "code": "logic-library" - } - ] - } - } - }, - { - "fullUrl": "https://example.org/List/list-1", - "resource": { - "resourceType": "List", - "id": "list-1", - "status": "current", - "mode": "working", - "subject": { - "reference": "Patient/patient-1" - } - } - }, - { - "fullUrl": "https://example.org/Location/loc-1", - "resource": { - "resourceType": "Location", - "id": "loc-1", - "status": "active", - "name": "Main Hospital", - "mode": "instance" - } - }, - { - "fullUrl": "https://example.org/Medication/med-1", - "resource": { - "resourceType": "Medication", - "id": "med-1", - "status": "active", - "code": { - "text": "Aspirin" - } - } - }, - { - "fullUrl": "https://example.org/MedicationAdministration/medadmin-1", - "resource": { - "resourceType": "MedicationAdministration", - "id": "medadmin-1", - "status": "completed", - "medicationCodeableConcept": { - "text": "Aspirin" - }, - "subject": { - "reference": "Patient/patient-1" - }, - "effectiveDateTime": "2024-11-15T08:00:00Z" - } - }, - { - "fullUrl": "https://example.org/MedicationDispense/meddisp-1", - "resource": { - "resourceType": "MedicationDispense", - "id": "meddisp-1", - "status": "completed", - "medicationCodeableConcept": { - "text": "Aspirin" - }, - "subject": { - "reference": "Patient/patient-1" - } - } - }, - { - "fullUrl": "https://example.org/MedicationKnowledge/medknow-1", - "resource": { - "resourceType": "MedicationKnowledge", - "id": "medknow-1", - "status": "active", - "code": { - "text": "Aspirin" - } - } - }, - { - "fullUrl": "https://example.org/MedicationRequest/medreq-1", - "resource": { - "resourceType": "MedicationRequest", - "id": "medreq-1", - "status": "active", - "intent": "order", - "medicationCodeableConcept": { - "text": "Aspirin" - }, - "subject": { - "reference": "Patient/patient-1" - } - } - }, - { - "fullUrl": "https://example.org/MedicationStatement/medstat-1", - "resource": { - "resourceType": "MedicationStatement", - "id": "medstat-1", - "status": "active", - "medicationCodeableConcept": { - "text": "Aspirin" - }, - "subject": { - "reference": "Patient/patient-1" - } - } - }, - { - "fullUrl": "https://example.org/MessageHeader/msg-1", - "resource": { - "resourceType": "MessageHeader", - "id": "msg-1", - "eventCoding": { - "system": "http://example.org/events", - "code": "test" - }, - "source": { - "endpoint": "https://example.org" - } - } - }, - { - "fullUrl": "https://example.org/Observation/obs-1", - "resource": { - "resourceType": "Observation", - "id": "obs-1", - "status": "final", - "code": { - "text": "Blood pressure" - }, - "subject": { - "reference": "Patient/patient-1" - } - } - }, - { - "fullUrl": "https://example.org/OperationOutcome/opout-1", - "resource": { - "resourceType": "OperationOutcome", - "id": "opout-1", - "issue": [ - { - "severity": "information", - "code": "informational" - } - ] - } - }, - { - "fullUrl": "https://example.org/Organization/org-1", - "resource": { - "resourceType": "Organization", - "id": "org-1", - "active": true, - "name": "General Hospital" - } - }, - { - "fullUrl": "https://example.org/Parameters/params-1", - "resource": { - "resourceType": "Parameters", - "id": "params-1", - "parameter": [ - { - "name": "test", - "valueString": "value" - } - ] - } - }, - { - "fullUrl": "https://example.org/Patient/patient-1", - "resource": { - "resourceType": "Patient", - "id": "patient-1", - "name": [ - { - "family": "Doe", - "given": ["John"] - } - ], - "gender": "male" - } - }, - { - "fullUrl": "https://example.org/Person/person-1", - "resource": { - "resourceType": "Person", - "id": "person-1", - "active": true, - "name": [ - { - "family": "Doe", - "given": ["Jane"] - } - ] - } - }, - { - "fullUrl": "https://example.org/Practitioner/pract-1", - "resource": { - "resourceType": "Practitioner", - "id": "pract-1", - "active": true, - "name": [ - { - "family": "Smith", - "given": ["Dr."] - } - ] - } - }, - { - "fullUrl": "https://example.org/PractitionerRole/practrole-1", - "resource": { - "resourceType": "PractitionerRole", - "id": "practrole-1", - "active": true, - "practitioner": { - "reference": "Practitioner/pract-1" - } - } - }, - { - "fullUrl": "https://example.org/Procedure/proc-1", - "resource": { - "resourceType": "Procedure", - "id": "proc-1", - "status": "completed", - "code": { - "text": "Blood draw" - }, - "subject": { - "reference": "Patient/patient-1" - } - } - }, - { - "fullUrl": "https://example.org/Questionnaire/quest-1", - "resource": { - "resourceType": "Questionnaire", - "id": "quest-1", - "status": "active", - "item": [ - { - "linkId": "1", - "type": "boolean", - "text": "Question 1" - } - ] - } - }, - { - "fullUrl": "https://example.org/QuestionnaireResponse/questresp-1", - "resource": { - "resourceType": "QuestionnaireResponse", - "id": "questresp-1", - "status": "completed", - "authored": "2024-11-15" - } - }, - { - "fullUrl": "https://example.org/RelatedPerson/rel-1", - "resource": { - "resourceType": "RelatedPerson", - "id": "rel-1", - "active": true, - "patient": { - "reference": "Patient/patient-1" - }, - "relationship": [ - { - "text": "Spouse" - } - ] - } - }, - { - "fullUrl": "https://example.org/Schedule/sched-1", - "resource": { - "resourceType": "Schedule", - "id": "sched-1", - "active": true, - "actor": [ - { - "reference": "Practitioner/pract-1" - } - ] - } - }, - { - "fullUrl": "https://example.org/ServiceRequest/servreq-1", - "resource": { - "resourceType": "ServiceRequest", - "id": "servreq-1", - "status": "active", - "intent": "order", - "code": { - "text": "Lab test" - }, - "subject": { - "reference": "Patient/patient-1" - } - } - }, - { - "fullUrl": "https://example.org/Slot/slot-1", - "resource": { - "resourceType": "Slot", - "id": "slot-1", - "schedule": { - "reference": "Schedule/sched-1" - }, - "status": "free", - "start": "2024-12-15T09:00:00Z", - "end": "2024-12-15T10:00:00Z" - } - }, - { - "fullUrl": "https://example.org/Specimen/spec-1", - "resource": { - "resourceType": "Specimen", - "id": "spec-1", - "status": "available", - "type": { - "text": "Blood" - }, - "subject": { - "reference": "Patient/patient-1" - } - } - }, - { - "fullUrl": "https://example.org/Subscription/sub-1", - "resource": { - "resourceType": "Subscription", - "id": "sub-1", - "status": "active", - "reason": "Monitor updates", - "criteria": "Patient?", - "channel": { - "type": "rest-hook", - "endpoint": "https://example.org/hook" - } - } - }, - { - "fullUrl": "https://example.org/Substance/subst-1", - "resource": { - "resourceType": "Substance", - "id": "subst-1", - "status": "active", - "code": { - "text": "Aspirin" - } - } - }, - { - "fullUrl": "https://example.org/Task/task-1", - "resource": { - "resourceType": "Task", - "id": "task-1", - "status": "requested", - "intent": "order" - } - } - ] - } -} diff --git a/packages/fhir/test/resources/bundles.json.license b/packages/fhir/test/resources/bundles.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/bundles.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/bundles.test.ts b/packages/fhir/test/resources/bundles.test.ts deleted file mode 100644 index 9c7a1b74..00000000 --- a/packages/fhir/test/resources/bundles.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Bundle } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - bundleSchema, - FhirBundle, - fhirResourceSchema, - type untypedGenericBundleSchema, -} from '../../src/index.js' - -describe('Bundle Resource', () => { - it('should validate FHIR bundle from bundles.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/bundles.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResourceOwnSchemaBuild = - bundleSchema(fhirResourceSchema).parse(jsonValue) - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResourceOwnSchemaBuild), - ) - const parsedResource = FhirBundle.parseGeneric(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/capabilityStatements.json b/packages/fhir/test/resources/capabilityStatements.json deleted file mode 100644 index 5d4f9128..00000000 --- a/packages/fhir/test/resources/capabilityStatements.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "0": { - "resourceType": "CapabilityStatement", - "status": "active", - "date": "2023-01-15T10:00:00Z", - "kind": "instance", - "fhirVersion": "4.3.0", - "format": ["json", "xml"] - }, - "1": { - "resourceType": "CapabilityStatement", - "url": "http://example.org/fhir/CapabilityStatement/example", - "version": "1.0.0", - "name": "ExampleCapabilityStatement", - "title": "Example FHIR Server Capability Statement", - "status": "active", - "experimental": false, - "date": "2023-06-01T14:30:00Z", - "publisher": "Example Health Organization", - "description": "Capability statement for example FHIR server", - "kind": "instance", - "software": { - "name": "Example FHIR Server", - "version": "1.0.0" - }, - "fhirVersion": "4.3.0", - "format": ["json", "xml"] - }, - "2": { - "resourceType": "CapabilityStatement", - "url": "http://example.org/fhir/CapabilityStatement/patient-server", - "version": "2.0.0", - "name": "PatientServerCapability", - "status": "active", - "date": "2023-09-20T08:00:00Z", - "publisher": "Healthcare Systems Inc", - "contact": [ - { - "name": "System Administrator", - "telecom": [ - { - "system": "email", - "value": "admin@example.org" - } - ] - } - ], - "description": "Capability statement for patient management server", - "kind": "capability", - "fhirVersion": "4.3.0", - "format": ["json"], - "patchFormat": ["application/json-patch+json"] - }, - "3": { - "resourceType": "CapabilityStatement", - "url": "http://example.org/fhir/CapabilityStatement/requirements", - "name": "RequirementsStatement", - "title": "System Requirements", - "status": "draft", - "date": "2023-12-01T12:00:00Z", - "publisher": "Standards Body", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "purpose": "Define requirements for FHIR implementations", - "copyright": "Copyright 2023 Standards Body", - "kind": "requirements", - "fhirVersion": "4.3.0", - "format": ["json", "xml"], - "implementationGuide": [ - "http://hl7.org/fhir/us/core/ImplementationGuide/hl7.fhir.us.core" - ] - } -} diff --git a/packages/fhir/test/resources/capabilityStatements.json.license b/packages/fhir/test/resources/capabilityStatements.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/capabilityStatements.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/capabilityStatements.test.ts b/packages/fhir/test/resources/capabilityStatements.test.ts deleted file mode 100644 index 2926a058..00000000 --- a/packages/fhir/test/resources/capabilityStatements.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type CapabilityStatement } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirCapabilityStatement, - type untypedCapabilityStatementSchema, -} from '../../src/index.js' - -describe('CapabilityStatement Resource', () => { - it('should validate FHIR capability statement from capabilityStatements.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/capabilityStatements.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirCapabilityStatement.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/carePlans.json b/packages/fhir/test/resources/carePlans.json deleted file mode 100644 index 268ba127..00000000 --- a/packages/fhir/test/resources/carePlans.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "0": { - "resourceType": "CarePlan", - "id": "careplan-example-0", - "status": "active", - "intent": "plan", - "title": "Weight Loss Program", - "description": "Complete weight loss program including diet and exercise", - "subject": { - "reference": "Patient/patient-example" - }, - "period": { - "start": "2024-01-01", - "end": "2024-12-31" - }, - "created": "2024-01-01T10:00:00Z", - "category": [ - { - "coding": [ - { - "system": "http://hl7.org/fhir/care-plan-category", - "code": "assess-plan" - } - ] - } - ], - "activity": [ - { - "detail": { - "status": "in-progress", - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "229065009", - "display": "Exercise therapy" - } - ] - }, - "scheduledTiming": { - "repeat": { - "frequency": 3, - "period": 1, - "periodUnit": "wk" - } - } - } - } - ] - }, - "1": { - "resourceType": "CarePlan", - "id": "careplan-example-1", - "status": "draft", - "intent": "proposal", - "title": "Diabetes Management Plan", - "subject": { - "reference": "Patient/patient-example-2" - }, - "encounter": { - "reference": "Encounter/encounter-example" - } - }, - "2": { - "resourceType": "CarePlan", - "id": "careplan-example-2", - "status": "completed", - "intent": "order", - "subject": { - "reference": "Patient/patient-example-3" - }, - "period": { - "start": "2023-06-01", - "end": "2023-12-31" - } - }, - "3": { - "resourceType": "CarePlan", - "id": "careplan-example-3", - "status": "on-hold", - "intent": "option", - "title": "Cardiac Rehabilitation", - "subject": { - "reference": "Patient/patient-example-4" - }, - "goal": [ - { - "reference": "Goal/goal-example" - } - ] - } -} diff --git a/packages/fhir/test/resources/carePlans.json.license b/packages/fhir/test/resources/carePlans.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/carePlans.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/carePlans.test.ts b/packages/fhir/test/resources/carePlans.test.ts deleted file mode 100644 index ad22145a..00000000 --- a/packages/fhir/test/resources/carePlans.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type CarePlan } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirCarePlan, type untypedCarePlanSchema } from '../../src/index.js' - -describe('CarePlan Resource', () => { - it('should validate FHIR CarePlans from carePlans.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/carePlans.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirCarePlan.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/careTeams.json b/packages/fhir/test/resources/careTeams.json deleted file mode 100644 index de4225f5..00000000 --- a/packages/fhir/test/resources/careTeams.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "0": { - "resourceType": "CareTeam", - "id": "careteam-example-0", - "status": "active", - "name": "Diabetes Care Team", - "subject": { - "reference": "Patient/patient-example" - }, - "period": { - "start": "2024-01-01" - }, - "participant": [ - { - "role": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "17561000", - "display": "Cardiologist" - } - ] - } - ], - "member": { - "reference": "Practitioner/practitioner-example" - } - }, - { - "role": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "46255001", - "display": "Pharmacist" - } - ] - } - ], - "member": { - "reference": "Practitioner/practitioner-example-2" - } - } - ] - }, - "1": { - "resourceType": "CareTeam", - "id": "careteam-example-1", - "status": "proposed", - "subject": { - "reference": "Patient/patient-example-2" - }, - "managingOrganization": [ - { - "reference": "Organization/organization-example" - } - ] - }, - "2": { - "resourceType": "CareTeam", - "id": "careteam-example-2", - "status": "inactive", - "name": "Post-Surgery Team", - "subject": { - "reference": "Patient/patient-example-3" - }, - "encounter": { - "reference": "Encounter/encounter-example" - } - }, - "3": { - "resourceType": "CareTeam", - "id": "careteam-example-3", - "status": "suspended", - "subject": { - "reference": "Patient/patient-example-4" - } - } -} diff --git a/packages/fhir/test/resources/careTeams.json.license b/packages/fhir/test/resources/careTeams.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/careTeams.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/careTeams.test.ts b/packages/fhir/test/resources/careTeams.test.ts deleted file mode 100644 index f8128378..00000000 --- a/packages/fhir/test/resources/careTeams.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type CareTeam } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirCareTeam, type untypedCareTeamSchema } from '../../src/index.js' - -describe('CareTeam Resource', () => { - it('should validate FHIR CareTeams from careTeams.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/careTeams.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirCareTeam.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/catalogEntries.json b/packages/fhir/test/resources/catalogEntries.json deleted file mode 100644 index 70422fe4..00000000 --- a/packages/fhir/test/resources/catalogEntries.json +++ /dev/null @@ -1,127 +0,0 @@ -{ - "0": { - "resourceType": "CatalogEntry", - "id": "catalog-example-1", - "identifier": [ - { - "system": "http://example.org/catalog", - "value": "CAT-12345" - } - ], - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/catalogType", - "code": "medication", - "display": "Medication" - } - ] - }, - "orderable": true, - "referencedItem": { - "reference": "Medication/example" - }, - "status": "active", - "validityPeriod": { - "start": "2023-01-01", - "end": "2024-12-31" - } - }, - "1": { - "resourceType": "CatalogEntry", - "id": "catalog-example-2", - "identifier": [ - { - "system": "http://example.org/catalog", - "value": "CAT-67890" - } - ], - "orderable": false, - "referencedItem": { - "reference": "Device/example" - }, - "additionalIdentifier": [ - { - "system": "http://example.org/manufacturer", - "value": "MFG-123" - } - ], - "classification": [ - { - "coding": [ - { - "system": "http://example.org/device-classification", - "code": "implantable", - "display": "Implantable Device" - } - ] - } - ], - "status": "draft" - }, - "2": { - "resourceType": "CatalogEntry", - "id": "catalog-example-3", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/catalogType", - "code": "device", - "display": "Device" - } - ] - }, - "orderable": true, - "referencedItem": { - "reference": "Device/example2" - }, - "status": "retired", - "validTo": "2023-12-31", - "lastUpdated": "2023-11-15T10:30:00Z" - }, - "3": { - "resourceType": "CatalogEntry", - "id": "catalog-example-4", - "identifier": [ - { - "system": "http://hospital.example.org/catalog", - "value": "HOS-CAT-999" - } - ], - "orderable": true, - "referencedItem": { - "reference": "Medication/example2" - }, - "classification": [ - { - "coding": [ - { - "system": "http://www.whocc.no/atc", - "code": "J01CR02", - "display": "Amoxicillin and clavulanic acid" - } - ] - } - ], - "status": "active", - "additionalCharacteristic": [ - { - "coding": [ - { - "system": "http://example.org/characteristics", - "code": "refrigerated", - "display": "Requires Refrigeration" - } - ] - } - ], - "relatedEntry": [ - { - "relationtype": "triggers", - "item": { - "reference": "CatalogEntry/related-item" - } - } - ] - } -} diff --git a/packages/fhir/test/resources/catalogEntries.json.license b/packages/fhir/test/resources/catalogEntries.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/catalogEntries.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/catalogEntries.test.ts b/packages/fhir/test/resources/catalogEntries.test.ts deleted file mode 100644 index e269f9c1..00000000 --- a/packages/fhir/test/resources/catalogEntries.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type CatalogEntry } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirCatalogEntry, - type untypedCatalogEntrySchema, -} from '../../src/index.js' - -describe('CatalogEntry Resource', () => { - it('should validate FHIR catalog entry from catalogEntries.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/catalogEntries.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirCatalogEntry.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/chargeItemDefinitions.json b/packages/fhir/test/resources/chargeItemDefinitions.json deleted file mode 100644 index 63cb007e..00000000 --- a/packages/fhir/test/resources/chargeItemDefinitions.json +++ /dev/null @@ -1,154 +0,0 @@ -{ - "0": { - "resourceType": "ChargeItemDefinition", - "id": "chargeitemdefinition-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T10:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/ChargeItemDefinition" - ] - }, - "url": "http://hospital.example.org/ChargeItemDefinition/office-visit", - "status": "active" - }, - "1": { - "resourceType": "ChargeItemDefinition", - "id": "chargeitemdefinition-comprehensive", - "meta": { - "versionId": "2", - "lastUpdated": "2025-01-15T11:30:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/ChargeItemDefinition" - ] - }, - "text": { - "status": "generated", - "div": "
Comprehensive charge item definition for knee surgery
" - }, - "url": "http://hospital.example.org/ChargeItemDefinition/knee-surgery", - "identifier": [ - { - "system": "http://hospital.example.org/charge-definitions", - "value": "CID-27447" - } - ], - "version": "1.0.0", - "title": "Total Knee Arthroplasty Charge Definition", - "status": "active", - "experimental": false, - "date": "2025-01-01", - "publisher": "General Hospital Billing Department", - "contact": [ - { - "name": "Billing Support", - "telecom": [ - { - "system": "email", - "value": "billing@hospital.example.org" - } - ] - } - ], - "description": "Defines the billing structure for total knee arthroplasty procedures", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "code": { - "coding": [ - { - "system": "http://www.ama-assn.org/go/cpt", - "code": "27447", - "display": "Total knee arthroplasty" - } - ] - }, - "propertyGroup": [ - { - "priceComponent": [ - { - "type": "base", - "code": { - "coding": [ - { - "system": "http://hospital.example.org/price-components", - "code": "BASE", - "display": "Base Price" - } - ] - }, - "amount": { - "value": 20000.0, - "currency": "USD" - } - }, - { - "type": "surcharge", - "code": { - "coding": [ - { - "system": "http://hospital.example.org/price-components", - "code": "COMPLEX", - "display": "Complexity Surcharge" - } - ] - }, - "factor": 0.15, - "amount": { - "value": 3000.0, - "currency": "USD" - } - } - ] - } - ] - }, - "2": { - "resourceType": "ChargeItemDefinition", - "id": "chargeitemdefinition-draft", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T12:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/ChargeItemDefinition" - ] - }, - "url": "http://hospital.example.org/ChargeItemDefinition/new-procedure", - "version": "0.1.0", - "title": "New Experimental Procedure", - "status": "draft", - "experimental": true, - "date": "2025-01-15", - "publisher": "General Hospital Research Department", - "description": "Draft definition for a new experimental procedure" - }, - "3": { - "resourceType": "ChargeItemDefinition", - "id": "chargeitemdefinition-retired", - "meta": { - "versionId": "3", - "lastUpdated": "2025-01-15T13:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/ChargeItemDefinition" - ] - }, - "url": "http://hospital.example.org/ChargeItemDefinition/old-procedure", - "version": "2.0.0", - "title": "Retired Procedure Charge Definition", - "replaces": [ - "http://hospital.example.org/ChargeItemDefinition/old-procedure|1.0.0" - ], - "status": "retired", - "date": "2024-12-31", - "publisher": "General Hospital Billing Department", - "description": "This charge definition has been retired and replaced" - } -} diff --git a/packages/fhir/test/resources/chargeItemDefinitions.json.license b/packages/fhir/test/resources/chargeItemDefinitions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/chargeItemDefinitions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/chargeItemDefinitions.test.ts b/packages/fhir/test/resources/chargeItemDefinitions.test.ts deleted file mode 100644 index 80b79483..00000000 --- a/packages/fhir/test/resources/chargeItemDefinitions.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ChargeItemDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirChargeItemDefinition, - type untypedChargeItemDefinitionSchema, -} from '../../src/index.js' - -describe('ChargeItemDefinition Resource', () => { - it('should validate FHIR ChargeItemDefinitions from chargeItemDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/chargeItemDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirChargeItemDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/chargeItems.json b/packages/fhir/test/resources/chargeItems.json deleted file mode 100644 index e3a8a42f..00000000 --- a/packages/fhir/test/resources/chargeItems.json +++ /dev/null @@ -1,242 +0,0 @@ -{ - "0": { - "resourceType": "ChargeItem", - "id": "chargeitem-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T10:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/ChargeItem"] - }, - "status": "billable", - "code": { - "coding": [ - { - "system": "http://www.ama-assn.org/go/cpt", - "code": "99213", - "display": "Office/outpatient visit, established patient" - } - ] - }, - "subject": { - "reference": "Patient/patient-001", - "display": "John Doe" - } - }, - "1": { - "resourceType": "ChargeItem", - "id": "chargeitem-comprehensive", - "meta": { - "versionId": "2", - "lastUpdated": "2025-01-15T11:30:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/ChargeItem"] - }, - "text": { - "status": "generated", - "div": "
Comprehensive charge for surgical procedure
" - }, - "identifier": [ - { - "system": "http://hospital.example.org/charges", - "value": "CHG-123456" - } - ], - "definitionUri": [ - "http://hospital.example.org/ChargeItemDefinition/surgical-procedure-001" - ], - "status": "billed", - "code": { - "coding": [ - { - "system": "http://www.ama-assn.org/go/cpt", - "code": "27447", - "display": "Total knee arthroplasty" - } - ], - "text": "Total Knee Replacement Surgery" - }, - "subject": { - "reference": "Patient/patient-002", - "display": "Sarah Johnson" - }, - "context": { - "reference": "Encounter/encounter-001", - "display": "Inpatient Admission" - }, - "occurrenceDateTime": "2025-01-10T09:30:00Z", - "performer": [ - { - "function": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "304292004", - "display": "Surgeon" - } - ] - }, - "actor": { - "reference": "Practitioner/practitioner-001", - "display": "Dr. Jane Smith" - } - }, - { - "function": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "158965000", - "display": "Anesthesiologist" - } - ] - }, - "actor": { - "reference": "Practitioner/practitioner-002", - "display": "Dr. Robert Brown" - } - } - ], - "performingOrganization": { - "reference": "Organization/hospital-001", - "display": "General Hospital" - }, - "requestingOrganization": { - "reference": "Organization/hospital-001", - "display": "General Hospital" - }, - "costCenter": { - "reference": "Organization/dept-surgery", - "display": "Surgery Department" - }, - "quantity": { - "value": 1 - }, - "factorOverride": 1.15, - "priceOverride": { - "value": 25000.0, - "currency": "USD" - }, - "overrideReason": "Complex surgical case with extended operative time", - "enterer": { - "reference": "Practitioner/billing-clerk-001", - "display": "Mary Johnson" - }, - "enteredDate": "2025-01-11T14:00:00Z", - "reason": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "239873007", - "display": "Osteoarthritis of knee" - } - ] - } - ], - "service": [ - { - "reference": "Procedure/procedure-001", - "display": "Total knee arthroplasty procedure" - } - ], - "account": [ - { - "reference": "Account/account-002", - "display": "Sarah Johnson Medical Services Account" - } - ], - "note": [ - { - "text": "Patient required extended post-operative monitoring" - } - ] - }, - "2": { - "resourceType": "ChargeItem", - "id": "chargeitem-medication", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T13:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/ChargeItem"] - }, - "status": "billable", - "code": { - "coding": [ - { - "system": "http://www.ama-assn.org/go/cpt", - "code": "J1100", - "display": "Injection, dexamethasone sodium phosphate" - } - ] - }, - "subject": { - "reference": "Patient/patient-003", - "display": "Robert Miller" - }, - "context": { - "reference": "Encounter/encounter-002" - }, - "occurrencePeriod": { - "start": "2025-01-12T08:00:00Z", - "end": "2025-01-12T08:15:00Z" - }, - "quantity": { - "value": 8, - "unit": "mg", - "system": "http://unitsofmeasure.org", - "code": "mg" - }, - "productCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "3264", - "display": "Dexamethasone" - } - ] - }, - "performingOrganization": { - "reference": "Organization/pharmacy-001", - "display": "Hospital Pharmacy" - }, - "account": [ - { - "reference": "Account/account-003" - } - ] - }, - "3": { - "resourceType": "ChargeItem", - "id": "chargeitem-entered-in-error", - "meta": { - "versionId": "2", - "lastUpdated": "2025-01-15T14:30:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/ChargeItem"] - }, - "identifier": [ - { - "system": "http://hospital.example.org/charges", - "value": "CHG-999999" - } - ], - "status": "entered-in-error", - "code": { - "coding": [ - { - "system": "http://www.ama-assn.org/go/cpt", - "code": "99214", - "display": "Office/outpatient visit" - } - ] - }, - "subject": { - "reference": "Patient/patient-004", - "display": "Emily Chen" - }, - "occurrenceDateTime": "2025-01-13T10:00:00Z", - "note": [ - { - "text": "Charge entered in error - patient did not attend appointment" - } - ] - } -} diff --git a/packages/fhir/test/resources/chargeItems.json.license b/packages/fhir/test/resources/chargeItems.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/chargeItems.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/chargeItems.test.ts b/packages/fhir/test/resources/chargeItems.test.ts deleted file mode 100644 index e83736b8..00000000 --- a/packages/fhir/test/resources/chargeItems.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ChargeItem } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirChargeItem, - type untypedChargeItemSchema, -} from '../../src/index.js' - -describe('ChargeItem Resource', () => { - it('should validate FHIR ChargeItems from chargeItems.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/chargeItems.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirChargeItem.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/citations.json b/packages/fhir/test/resources/citations.json deleted file mode 100644 index 7172875c..00000000 --- a/packages/fhir/test/resources/citations.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "0": { - "resourceType": "Citation", - "id": "citation-example-0", - "status": "draft", - "title": "Example Citation" - }, - "1": { - "resourceType": "Citation", - "id": "citation-example-1", - "url": "http://example.org/fhir/Citation/diabetes-treatment-2024", - "identifier": [ - { - "system": "http://example.org/citations", - "value": "CIT-001" - } - ], - "version": "1.0.0", - "name": "DiabetesTreatmentCitation", - "title": "Diabetes Treatment Study 2024", - "status": "active", - "publisher": "Example Medical Journal", - "description": "Citation for a landmark diabetes treatment study", - "summary": [ - { - "text": "A randomized controlled trial showing improved outcomes with new diabetes treatment." - } - ] - }, - "2": { - "resourceType": "Citation", - "id": "citation-example-2", - "status": "active", - "title": "Cardiovascular Risk Factors Meta-Analysis", - "contact": [ - { - "name": "Dr. Research Author" - } - ], - "summary": [ - { - "text": "Meta-analysis of cardiovascular risk factors across 50 studies." - } - ], - "classification": [ - { - "type": { - "text": "Publication Type" - }, - "classifier": [ - { - "text": "Meta-Analysis" - } - ] - } - ] - }, - "3": { - "resourceType": "Citation", - "id": "citation-example-3", - "url": "http://example.org/fhir/Citation/cancer-immunotherapy", - "identifier": [ - { - "system": "http://doi.org", - "value": "10.1234/example.2024.001" - } - ], - "name": "CancerImmunotherapyCitation", - "title": "Efficacy of Immunotherapy in Advanced Cancer", - "status": "active", - "experimental": false, - "publisher": "Oncology Research Journal", - "contact": [ - { - "name": "Oncology Research Group" - } - ], - "description": "Comprehensive citation for the landmark immunotherapy trial in advanced cancer patients", - "summary": [ - { - "style": { - "text": "AMA" - }, - "text": "Smith J, et al. Efficacy of Immunotherapy in Advanced Cancer. Oncology Research Journal. 2024;123(4):567-590." - } - ], - "classification": [ - { - "type": { - "text": "Knowledge Artifact Type" - }, - "classifier": [ - { - "text": "Research Article" - } - ] - } - ], - "currentState": [ - { - "text": "Published" - } - ], - "citedArtifact": { - "identifier": [ - { - "system": "http://doi.org", - "value": "10.1234/example.2024.001" - } - ], - "title": [ - { - "text": "Efficacy of Immunotherapy in Advanced Cancer: A Randomized Controlled Trial" - } - ], - "abstract": [ - { - "text": "This randomized controlled trial evaluated the efficacy of a novel immunotherapy approach in patients with advanced cancer." - } - ] - } - } -} diff --git a/packages/fhir/test/resources/citations.test.ts b/packages/fhir/test/resources/citations.test.ts deleted file mode 100644 index 4b0303cd..00000000 --- a/packages/fhir/test/resources/citations.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Citation } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirCitation, type untypedCitationSchema } from '../../src/index.js' - -describe('Citation Resource', () => { - it('should validate FHIR citations from citations.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/citations.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirCitation.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/claimResponses.json b/packages/fhir/test/resources/claimResponses.json deleted file mode 100644 index 872a40ca..00000000 --- a/packages/fhir/test/resources/claimResponses.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "0": { - "resourceType": "ClaimResponse", - "id": "claimresponse-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T10:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/ClaimResponse"] - }, - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/claim-type", - "code": "institutional" - } - ] - }, - "use": "claim", - "patient": { - "reference": "Patient/patient-001" - }, - "created": "2025-01-15T10:00:00Z", - "insurer": { - "reference": "Organization/insurance-001" - }, - "outcome": "complete" - }, - "1": { - "resourceType": "ClaimResponse", - "id": "claimresponse-comprehensive", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T11:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/ClaimResponse"] - }, - "identifier": [ - { - "system": "http://insurance.example.org/claim-responses", - "value": "CR-001" - } - ], - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/claim-type", - "code": "professional" - } - ] - }, - "use": "claim", - "patient": { - "reference": "Patient/patient-002" - }, - "created": "2025-01-15T11:00:00Z", - "insurer": { - "reference": "Organization/insurance-001" - }, - "requestor": { - "reference": "Organization/hospital-001" - }, - "request": { - "reference": "Claim/claim-001" - }, - "outcome": "complete", - "disposition": "Claim processed successfully", - "preAuthRef": "PA-12345", - "total": [ - { - "category": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/adjudication", - "code": "submitted" - } - ] - }, - "amount": { - "value": 1000.0, - "currency": "USD" - } - } - ] - }, - "2": { - "resourceType": "ClaimResponse", - "id": "claimresponse-error", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T12:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/ClaimResponse"] - }, - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/claim-type", - "code": "pharmacy" - } - ] - }, - "use": "claim", - "patient": { - "reference": "Patient/patient-003" - }, - "created": "2025-01-15T12:00:00Z", - "insurer": { - "reference": "Organization/insurance-002" - }, - "outcome": "error", - "disposition": "Missing required documentation" - }, - "3": { - "resourceType": "ClaimResponse", - "id": "claimresponse-preauth", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T13:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/ClaimResponse"] - }, - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/claim-type", - "code": "professional" - } - ] - }, - "use": "preauthorization", - "patient": { - "reference": "Patient/patient-004" - }, - "created": "2025-01-15T13:00:00Z", - "insurer": { - "reference": "Organization/insurance-001" - }, - "outcome": "complete", - "disposition": "Preauthorization approved" - } -} diff --git a/packages/fhir/test/resources/claimResponses.json.license b/packages/fhir/test/resources/claimResponses.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/claimResponses.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/claimResponses.test.ts b/packages/fhir/test/resources/claimResponses.test.ts deleted file mode 100644 index 54851947..00000000 --- a/packages/fhir/test/resources/claimResponses.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ClaimResponse } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirClaimResponse, - type untypedClaimResponseSchema, -} from '../../src/index.js' - -describe('ClaimResponse Resource', () => { - it('should validate FHIR ClaimResponses from claimResponses.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/claimResponses.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirClaimResponse.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/claims.json b/packages/fhir/test/resources/claims.json deleted file mode 100644 index 7edff21a..00000000 --- a/packages/fhir/test/resources/claims.json +++ /dev/null @@ -1,201 +0,0 @@ -{ - "0": { - "resourceType": "Claim", - "id": "claim-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T10:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Claim"] - }, - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/claim-type", - "code": "institutional" - } - ] - }, - "use": "claim", - "patient": { - "reference": "Patient/patient-001" - }, - "created": "2025-01-15T10:00:00Z", - "provider": { - "reference": "Organization/hospital-001" - }, - "priority": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/processpriority", - "code": "normal" - } - ] - }, - "insurance": [ - { - "sequence": 1, - "focal": true, - "coverage": { - "reference": "Coverage/coverage-001" - } - } - ] - }, - "1": { - "resourceType": "Claim", - "id": "claim-comprehensive", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T11:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Claim"] - }, - "identifier": [ - { - "system": "http://hospital.example.org/claims", - "value": "CLM-001" - } - ], - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/claim-type", - "code": "professional" - } - ] - }, - "use": "claim", - "patient": { - "reference": "Patient/patient-002", - "display": "Sarah Johnson" - }, - "billablePeriod": { - "start": "2025-01-10", - "end": "2025-01-12" - }, - "created": "2025-01-15T11:00:00Z", - "insurer": { - "reference": "Organization/insurance-001" - }, - "provider": { - "reference": "Organization/hospital-001" - }, - "priority": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/processpriority", - "code": "normal" - } - ] - }, - "insurance": [ - { - "sequence": 1, - "focal": true, - "coverage": { - "reference": "Coverage/coverage-002" - } - } - ], - "item": [ - { - "sequence": 1, - "productOrService": { - "coding": [ - { - "system": "http://www.ama-assn.org/go/cpt", - "code": "99213" - } - ] - } - } - ] - }, - "2": { - "resourceType": "Claim", - "id": "claim-draft", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T12:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Claim"] - }, - "status": "draft", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/claim-type", - "code": "pharmacy" - } - ] - }, - "use": "claim", - "patient": { - "reference": "Patient/patient-003" - }, - "created": "2025-01-15T12:00:00Z", - "provider": { - "reference": "Organization/pharmacy-001" - }, - "priority": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/processpriority", - "code": "normal" - } - ] - }, - "insurance": [ - { - "sequence": 1, - "focal": true, - "coverage": { - "reference": "Coverage/coverage-003" - } - } - ] - }, - "3": { - "resourceType": "Claim", - "id": "claim-preauthorization", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T13:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Claim"] - }, - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/claim-type", - "code": "professional" - } - ] - }, - "use": "preauthorization", - "patient": { - "reference": "Patient/patient-004" - }, - "created": "2025-01-15T13:00:00Z", - "provider": { - "reference": "Organization/hospital-001" - }, - "priority": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/processpriority", - "code": "stat" - } - ] - }, - "insurance": [ - { - "sequence": 1, - "focal": true, - "coverage": { - "reference": "Coverage/coverage-004" - } - } - ] - } -} diff --git a/packages/fhir/test/resources/claims.json.license b/packages/fhir/test/resources/claims.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/claims.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/claims.test.ts b/packages/fhir/test/resources/claims.test.ts deleted file mode 100644 index ce582b73..00000000 --- a/packages/fhir/test/resources/claims.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Claim } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirClaim, type untypedClaimSchema } from '../../src/index.js' - -describe('Claim Resource', () => { - it('should validate FHIR Claims from claims.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/claims.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirClaim.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/clinicalImpressions.json b/packages/fhir/test/resources/clinicalImpressions.json deleted file mode 100644 index b9b0eaa3..00000000 --- a/packages/fhir/test/resources/clinicalImpressions.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "0": { - "resourceType": "ClinicalImpression", - "id": "clinicalimpression-example-0", - "status": "completed", - "subject": { - "reference": "Patient/patient-example" - }, - "effectiveDateTime": "2024-01-15T10:00:00Z", - "date": "2024-01-15T10:30:00Z", - "assessor": { - "reference": "Practitioner/practitioner-example" - }, - "description": "Patient presents with acute respiratory symptoms", - "summary": "Likely viral upper respiratory infection", - "finding": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "11833005", - "display": "Dry cough" - } - ] - } - }, - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "49727002", - "display": "Cough" - } - ] - }, - "basis": "Patient reported persistent cough for 3 days" - } - ] - }, - "1": { - "resourceType": "ClinicalImpression", - "id": "clinicalimpression-example-1", - "status": "in-progress", - "subject": { - "reference": "Patient/patient-example-2" - }, - "encounter": { - "reference": "Encounter/encounter-example" - }, - "effectivePeriod": { - "start": "2024-01-10", - "end": "2024-01-15" - } - }, - "2": { - "resourceType": "ClinicalImpression", - "id": "clinicalimpression-example-2", - "status": "completed", - "subject": { - "reference": "Patient/patient-example-3" - }, - "effectiveDateTime": "2024-02-01T14:30:00Z", - "investigation": [ - { - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "15220000", - "display": "Laboratory test" - } - ] - }, - "item": [ - { - "reference": "Observation/observation-example" - } - ] - } - ] - }, - "3": { - "resourceType": "ClinicalImpression", - "id": "clinicalimpression-example-3", - "status": "entered-in-error", - "subject": { - "reference": "Patient/patient-example-4" - }, - "effectiveDateTime": "2024-03-01T09:00:00Z" - } -} diff --git a/packages/fhir/test/resources/clinicalImpressions.json.license b/packages/fhir/test/resources/clinicalImpressions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/clinicalImpressions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/clinicalImpressions.test.ts b/packages/fhir/test/resources/clinicalImpressions.test.ts deleted file mode 100644 index d4ae246d..00000000 --- a/packages/fhir/test/resources/clinicalImpressions.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ClinicalImpression } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirClinicalImpression, - type untypedClinicalImpressionSchema, -} from '../../src/index.js' - -describe('ClinicalImpression Resource', () => { - it('should validate FHIR ClinicalImpressions from clinicalImpressions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/clinicalImpressions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirClinicalImpression.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/clinicalUseDefinitions.json b/packages/fhir/test/resources/clinicalUseDefinitions.json deleted file mode 100644 index c4e142b6..00000000 --- a/packages/fhir/test/resources/clinicalUseDefinitions.json +++ /dev/null @@ -1,223 +0,0 @@ -{ - "0": { - "resourceType": "ClinicalUseDefinition", - "id": "clinicalusedefinition-example-0", - "type": "indication", - "subject": [ - { - "reference": "MedicinalProductDefinition/example-insulin" - } - ], - "indication": { - "diseaseSymptomProcedure": { - "concept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "44054006", - "display": "Diabetes mellitus type 2" - } - ] - } - }, - "intendedEffect": { - "concept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "266918002", - "display": "Blood glucose control" - } - ] - } - } - } - }, - "1": { - "resourceType": "ClinicalUseDefinition", - "id": "clinicalusedefinition-example-1", - "identifier": [ - { - "system": "http://example.org/clinical-use", - "value": "CUD-001" - } - ], - "type": "contraindication", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/clinical-use-definition-category", - "code": "pregnancy", - "display": "Pregnancy and Lactation" - } - ] - } - ], - "subject": [ - { - "reference": "MedicinalProductDefinition/example-warfarin" - } - ], - "status": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/clinical-use-definition-status", - "code": "active", - "display": "Active" - } - ] - }, - "contraindication": { - "diseaseSymptomProcedure": { - "concept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "77386006", - "display": "Pregnancy" - } - ] - } - }, - "comorbidity": [ - { - "concept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "237238006", - "display": "Pregnancy with hemorrhage" - } - ] - } - } - ] - } - }, - "2": { - "resourceType": "ClinicalUseDefinition", - "id": "clinicalusedefinition-example-2", - "type": "interaction", - "subject": [ - { - "reference": "Medication/example-aspirin" - }, - { - "reference": "Medication/example-warfarin" - } - ], - "interaction": { - "interactant": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1191", - "display": "Aspirin" - } - ] - } - }, - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "11289", - "display": "Warfarin" - } - ] - } - } - ], - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "DRG", - "display": "Drug Interaction" - } - ] - }, - "effect": { - "concept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "370901008", - "display": "Increased bleeding risk" - } - ] - } - }, - "incidence": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/interaction-incidence", - "code": "high", - "display": "High" - } - ] - }, - "management": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "314530004", - "display": "Monitor coagulation parameters" - } - ] - } - ] - } - }, - "3": { - "resourceType": "ClinicalUseDefinition", - "id": "clinicalusedefinition-example-3", - "type": "undesirable-effect", - "subject": [ - { - "reference": "MedicinalProductDefinition/example-chemotherapy" - } - ], - "population": [ - { - "reference": "Group/cancer-patients" - } - ], - "undesirableEffect": { - "symptomConditionEffect": { - "concept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "422587007", - "display": "Nausea" - } - ] - } - }, - "classification": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/adverse-effect-classification", - "code": "common", - "display": "Common" - } - ] - }, - "frequencyOfOccurrence": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/frequency", - "code": "very-common", - "display": "Very Common (≥1/10)" - } - ] - } - } - } -} diff --git a/packages/fhir/test/resources/clinicalUseDefinitions.json.license b/packages/fhir/test/resources/clinicalUseDefinitions.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/clinicalUseDefinitions.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/clinicalUseDefinitions.test.ts b/packages/fhir/test/resources/clinicalUseDefinitions.test.ts deleted file mode 100644 index c7cc4d05..00000000 --- a/packages/fhir/test/resources/clinicalUseDefinitions.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ClinicalUseDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirClinicalUseDefinition, - type untypedClinicalUseDefinitionSchema, -} from '../../src/index.js' - -describe('ClinicalUseDefinition Resource', () => { - it('should validate FHIR clinical use definition from clinicalUseDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/clinicalUseDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirClinicalUseDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/codeSystems.json b/packages/fhir/test/resources/codeSystems.json deleted file mode 100644 index 30400758..00000000 --- a/packages/fhir/test/resources/codeSystems.json +++ /dev/null @@ -1,300 +0,0 @@ -{ - "0": { - "resourceType": "CodeSystem", - "id": "example-codesystem-0", - "url": "http://example.org/fhir/CodeSystem/example-0", - "identifier": [ - { - "system": "urn:ietf:rfc:3986", - "value": "urn:oid:2.16.840.1.113883.4.642.1.0" - } - ], - "version": "1.0.0", - "name": "ExampleCodeSystem0", - "title": "Example Code System 0", - "status": "active", - "experimental": true, - "date": "2023-01-01T00:00:00Z", - "publisher": "Example Organization", - "contact": [ - { - "name": "Example Contact", - "telecom": [ - { - "system": "email", - "value": "contact@example.org" - } - ] - } - ], - "description": "An example code system for testing purposes", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "purpose": "This code system is used for demonstration purposes", - "copyright": "Copyright 2023 Example Organization", - "caseSensitive": true, - "valueSet": "http://example.org/fhir/ValueSet/example-0", - "hierarchyMeaning": "is-a", - "compositional": false, - "versionNeeded": false, - "content": "complete", - "count": 3, - "property": [ - { - "code": "status", - "uri": "http://example.org/fhir/concept-properties#status", - "description": "The status of the concept", - "type": "code" - } - ], - "concept": [ - { - "code": "code1", - "display": "Code 1", - "definition": "The first example code", - "designation": [ - { - "language": "en", - "value": "Code One" - } - ], - "property": [ - { - "code": "status", - "valueCode": "active" - } - ] - }, - { - "code": "code2", - "display": "Code 2", - "definition": "The second example code", - "property": [ - { - "code": "status", - "valueCode": "active" - } - ] - }, - { - "code": "code3", - "display": "Code 3", - "definition": "The third example code", - "property": [ - { - "code": "status", - "valueCode": "deprecated" - } - ] - } - ] - }, - "1": { - "resourceType": "CodeSystem", - "id": "example-codesystem-1", - "url": "http://example.org/fhir/CodeSystem/example-1", - "version": "2.0.0", - "name": "ExampleCodeSystem1", - "title": "Example Code System 1 with Hierarchy", - "status": "draft", - "experimental": false, - "date": "2023-06-15T12:30:00Z", - "publisher": "Health Standards Organization", - "description": "A hierarchical code system example", - "caseSensitive": false, - "hierarchyMeaning": "part-of", - "content": "complete", - "count": 5, - "concept": [ - { - "code": "parent1", - "display": "Parent Concept 1", - "definition": "A parent concept", - "concept": [ - { - "code": "child1-1", - "display": "Child Concept 1-1", - "definition": "First child of parent 1" - }, - { - "code": "child1-2", - "display": "Child Concept 1-2", - "definition": "Second child of parent 1" - } - ] - }, - { - "code": "parent2", - "display": "Parent Concept 2", - "definition": "Another parent concept", - "concept": [ - { - "code": "child2-1", - "display": "Child Concept 2-1", - "definition": "First child of parent 2" - } - ] - } - ] - }, - "2": { - "resourceType": "CodeSystem", - "id": "example-codesystem-2", - "url": "http://example.org/fhir/CodeSystem/example-2", - "version": "1.5.0", - "name": "ExampleCodeSystem2", - "title": "Example Code System 2 with Filters", - "status": "active", - "date": "2023-09-20T08:15:00Z", - "publisher": "Medical Terminology Institute", - "contact": [ - { - "name": "Terminology Team", - "telecom": [ - { - "system": "url", - "value": "http://example.org/terminology" - } - ] - } - ], - "description": "Code system with filter definitions", - "useContext": [ - { - "code": { - "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", - "code": "focus" - }, - "valueCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "408475000", - "display": "Diabetic" - } - ] - } - } - ], - "caseSensitive": true, - "content": "fragment", - "count": 150, - "filter": [ - { - "code": "child", - "description": "Filter to find all child concepts", - "operator": ["=", "is-a"], - "value": "A code from the code system" - }, - { - "code": "regex", - "description": "Filter using regular expression", - "operator": ["regex"], - "value": "A regular expression pattern" - } - ], - "property": [ - { - "code": "deprecated", - "description": "Whether the concept is deprecated", - "type": "boolean" - }, - { - "code": "parent", - "description": "Parent concept code", - "type": "code" - } - ], - "concept": [ - { - "code": "concept-a", - "display": "Concept A", - "definition": "First filtered concept", - "property": [ - { - "code": "deprecated", - "valueBoolean": false - } - ] - }, - { - "code": "concept-b", - "display": "Concept B", - "definition": "Second filtered concept", - "designation": [ - { - "language": "es", - "value": "Concepto B" - }, - { - "language": "fr", - "value": "Concept B" - } - ], - "property": [ - { - "code": "deprecated", - "valueBoolean": false - }, - { - "code": "parent", - "valueCode": "concept-a" - } - ] - } - ] - }, - "3": { - "resourceType": "CodeSystem", - "id": "example-codesystem-3", - "url": "http://example.org/fhir/CodeSystem/example-3", - "version": "3.1.0", - "name": "ExampleCodeSystem3", - "title": "Example Code System 3 - Supplement", - "status": "retired", - "experimental": true, - "date": "2022-12-31T23:59:59Z", - "publisher": "Legacy Systems Inc", - "description": "A code system supplement example", - "content": "supplement", - "supplements": "http://example.org/fhir/CodeSystem/base-system", - "property": [ - { - "code": "translation", - "uri": "http://example.org/translation", - "description": "Translation to another language", - "type": "string" - }, - { - "code": "effectiveDate", - "description": "When the concept became effective", - "type": "dateTime" - } - ], - "concept": [ - { - "code": "supplemented-1", - "display": "Supplemented Concept 1", - "property": [ - { - "code": "translation", - "valueString": "Concepto Suplementado 1" - }, - { - "code": "effectiveDate", - "valueDateTime": "2022-01-01" - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/codeSystems.test.ts b/packages/fhir/test/resources/codeSystems.test.ts deleted file mode 100644 index 21d5ccd1..00000000 --- a/packages/fhir/test/resources/codeSystems.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type CodeSystem } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirCodeSystem, - type untypedCodeSystemSchema, -} from '../../src/index.js' - -describe('CodeSystem Resource', () => { - it('should validate FHIR CodeSystems from codeSystems.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/codeSystems.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirCodeSystem.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/communicationRequests.json b/packages/fhir/test/resources/communicationRequests.json deleted file mode 100644 index b864b5bd..00000000 --- a/packages/fhir/test/resources/communicationRequests.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "0": { - "resourceType": "CommunicationRequest", - "id": "communicationrequest-example-0", - "status": "active", - "priority": "routine", - "subject": { - "reference": "Patient/patient-example" - }, - "authoredOn": "2024-01-15T10:00:00Z", - "requester": { - "reference": "Practitioner/practitioner-example" - }, - "recipient": [ - { - "reference": "Patient/patient-example" - } - ], - "payload": [ - { - "contentString": "Please schedule your annual physical examination" - } - ], - "occurrenceDateTime": "2024-02-01T09:00:00Z" - }, - "1": { - "resourceType": "CommunicationRequest", - "id": "communicationrequest-example-1", - "status": "draft", - "priority": "urgent", - "subject": { - "reference": "Patient/patient-example-2" - }, - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/communication-category", - "code": "alert" - } - ] - } - ] - }, - "2": { - "resourceType": "CommunicationRequest", - "id": "communicationrequest-example-2", - "status": "completed", - "priority": "stat", - "subject": { - "reference": "Patient/patient-example-3" - }, - "encounter": { - "reference": "Encounter/encounter-example" - }, - "doNotPerform": false - }, - "3": { - "resourceType": "CommunicationRequest", - "id": "communicationrequest-example-3", - "status": "revoked", - "priority": "asap", - "subject": { - "reference": "Patient/patient-example-4" - }, - "medium": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", - "code": "PHONE", - "display": "telephone" - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/communicationRequests.json.license b/packages/fhir/test/resources/communicationRequests.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/communicationRequests.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/communicationRequests.test.ts b/packages/fhir/test/resources/communicationRequests.test.ts deleted file mode 100644 index 59938572..00000000 --- a/packages/fhir/test/resources/communicationRequests.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type CommunicationRequest } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirCommunicationRequest, - type untypedCommunicationRequestSchema, -} from '../../src/index.js' - -describe('CommunicationRequest Resource', () => { - it('should validate FHIR CommunicationRequests from communicationRequests.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/communicationRequests.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirCommunicationRequest.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/communications.json b/packages/fhir/test/resources/communications.json deleted file mode 100644 index 077c47ff..00000000 --- a/packages/fhir/test/resources/communications.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "0": { - "resourceType": "Communication", - "id": "communication-example-0", - "status": "completed", - "subject": { - "reference": "Patient/patient-example" - }, - "sent": "2024-01-15T14:30:00Z", - "received": "2024-01-15T14:35:00Z", - "sender": { - "reference": "Practitioner/practitioner-example" - }, - "recipient": [ - { - "reference": "Patient/patient-example" - } - ], - "payload": [ - { - "contentString": "Your lab results are ready. Please call to schedule a follow-up appointment." - } - ], - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/communication-category", - "code": "notification" - } - ] - } - ] - }, - "1": { - "resourceType": "Communication", - "id": "communication-example-1", - "status": "in-progress", - "subject": { - "reference": "Patient/patient-example-2" - }, - "topic": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/communication-topic", - "code": "prescription-refill" - } - ] - }, - "encounter": { - "reference": "Encounter/encounter-example" - } - }, - "2": { - "resourceType": "Communication", - "id": "communication-example-2", - "status": "not-done", - "statusReason": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/communication-not-done-reason", - "code": "invalid-phone-number" - } - ] - }, - "subject": { - "reference": "Patient/patient-example-3" - } - }, - "3": { - "resourceType": "Communication", - "id": "communication-example-3", - "status": "stopped", - "subject": { - "reference": "Patient/patient-example-4" - }, - "medium": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", - "code": "WRITTEN", - "display": "written" - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/communications.json.license b/packages/fhir/test/resources/communications.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/communications.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/communications.test.ts b/packages/fhir/test/resources/communications.test.ts deleted file mode 100644 index 054b0652..00000000 --- a/packages/fhir/test/resources/communications.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Communication } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirCommunication, - type untypedCommunicationSchema, -} from '../../src/index.js' - -describe('Communication Resource', () => { - it('should validate FHIR Communications from communications.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/communications.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirCommunication.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/compartmentDefinitions.json b/packages/fhir/test/resources/compartmentDefinitions.json deleted file mode 100644 index 416d4b04..00000000 --- a/packages/fhir/test/resources/compartmentDefinitions.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "0": { - "resourceType": "CompartmentDefinition", - "id": "patient", - "url": "http://hl7.org/fhir/CompartmentDefinition/patient", - "name": "PatientCompartment", - "status": "draft", - "code": "Patient", - "search": true, - "description": "Patient compartment definition", - "resource": [ - { - "code": "Account", - "param": ["subject"] - }, - { - "code": "Observation", - "param": ["subject", "performer"] - } - ] - }, - "1": { - "resourceType": "CompartmentDefinition", - "id": "encounter", - "url": "http://hl7.org/fhir/CompartmentDefinition/encounter", - "name": "EncounterCompartment", - "status": "active", - "code": "Encounter", - "search": true, - "experimental": false, - "date": "2023-01-15T10:00:00Z", - "publisher": "HL7 International", - "resource": [ - { - "code": "Procedure", - "param": ["encounter"] - } - ] - }, - "2": { - "resourceType": "CompartmentDefinition", - "id": "practitioner", - "url": "http://hl7.org/fhir/CompartmentDefinition/practitioner", - "name": "PractitionerCompartment", - "status": "active", - "code": "Practitioner", - "search": true, - "version": "4.3.0", - "description": "Practitioner compartment includes resources linked to a specific practitioner", - "purpose": "Define resources accessible within a practitioner context" - }, - "3": { - "resourceType": "CompartmentDefinition", - "id": "device", - "url": "http://example.org/fhir/CompartmentDefinition/device", - "name": "DeviceCompartment", - "status": "draft", - "code": "Device", - "search": false, - "experimental": true, - "contact": [ - { - "name": "Device Management Team", - "telecom": [ - { - "system": "email", - "value": "device@example.org" - } - ] - } - ], - "resource": [ - { - "code": "DeviceMetric", - "param": ["source"], - "documentation": "Device metrics are part of the device compartment" - } - ] - } -} diff --git a/packages/fhir/test/resources/compartmentDefinitions.json.license b/packages/fhir/test/resources/compartmentDefinitions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/compartmentDefinitions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/compartmentDefinitions.test.ts b/packages/fhir/test/resources/compartmentDefinitions.test.ts deleted file mode 100644 index 542304c9..00000000 --- a/packages/fhir/test/resources/compartmentDefinitions.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type CompartmentDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirCompartmentDefinition, - type untypedCompartmentDefinitionSchema, -} from '../../src/index.js' - -describe('CompartmentDefinition Resource', () => { - it('should validate FHIR compartment definition from compartmentDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/compartmentDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirCompartmentDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/compositions.json b/packages/fhir/test/resources/compositions.json deleted file mode 100644 index 8da3a272..00000000 --- a/packages/fhir/test/resources/compositions.json +++ /dev/null @@ -1,275 +0,0 @@ -{ - "0": { - "resourceType": "Composition", - "id": "comp-0", - "status": "final", - "type": { - "coding": [ - { - "system": "http://loinc.org", - "code": "11488-4", - "display": "Consult note" - } - ] - }, - "subject": { - "reference": "Patient/pat-0" - }, - "date": "2025-01-01T10:00:00Z", - "author": [ - { - "reference": "Practitioner/prac-0" - } - ], - "title": "Initial Consultation", - "confidentiality": "N", - "custodian": { - "reference": "Organization/org-0" - }, - "event": [ - { - "code": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "ENC", - "display": "Encounter" - } - ] - } - ], - "period": { - "start": "2025-01-01T09:00:00Z", - "end": "2025-01-01T10:00:00Z" - }, - "detail": [ - { - "reference": "Encounter/enc-0" - } - ] - } - ], - "section": [ - { - "title": "Medical Conditions", - "code": { - "coding": [ - { - "system": "http://loinc.org", - "code": "11450-4", - "display": "Problem list" - } - ] - }, - "entry": [ - { - "reference": "Condition/cond-0" - }, - { - "reference": "AllergyIntolerance/allergy-0" - } - ] - }, - { - "title": "Medications", - "code": { - "coding": [ - { - "system": "http://loinc.org", - "code": "10160-0", - "display": "Medication list" - } - ] - }, - "entry": [ - { - "reference": "Medication/med-0" - }, - { - "reference": "MedicationRequest/medreq-0" - }, - { - "reference": "MedicationStatement/medstat-0" - } - ] - } - ] - }, - "1": { - "resourceType": "Composition", - "id": "comp-1", - "status": "preliminary", - "type": { - "coding": [ - { - "system": "http://loinc.org", - "code": "18748-4", - "display": "Discharge summary" - } - ] - }, - "subject": { - "reference": "Patient/pat-1" - }, - "date": "2025-02-15T12:00:00Z", - "author": [ - { - "reference": "Practitioner/prac-1" - } - ], - "title": "Hospital Discharge Summary", - "attester": [ - { - "mode": "legal", - "time": "2025-02-15T12:30:00Z", - "party": { - "reference": "Practitioner/prac-1" - } - } - ], - "relatesTo": [ - { - "code": "replaces", - "targetReference": { - "reference": "Composition/comp-0" - } - } - ], - "section": [ - { - "title": "Procedures", - "entry": [ - { - "reference": "Procedure/proc-1" - } - ] - }, - { - "title": "Immunizations", - "entry": [ - { - "reference": "Immunization/imm-1" - }, - { - "reference": "ImmunizationEvaluation/immeval-1" - }, - { - "reference": "ImmunizationRecommendation/immrec-1" - } - ] - } - ] - }, - "2": { - "resourceType": "Composition", - "id": "comp-2", - "status": "amended", - "type": { - "coding": [ - { - "system": "http://loinc.org", - "code": "51847-2", - "display": "Evaluation note" - } - ] - }, - "subject": { - "reference": "Patient/pat-2" - }, - "date": "2025-03-10T09:00:00Z", - "author": [ - { - "reference": "Practitioner/prac-2" - } - ], - "title": "Follow-up Evaluation", - "event": [ - { - "detail": [ - { - "reference": "Appointment/appt-2" - } - ] - } - ], - "section": [ - { - "title": "Diagnostics", - "entry": [ - { - "reference": "DiagnosticReport/diagrep-2" - }, - { - "reference": "Observation/obs-2" - }, - { - "reference": "ServiceRequest/sr-2" - } - ] - }, - { - "title": "Medication Management", - "entry": [ - { - "reference": "MedicationAdministration/medadmin-2" - }, - { - "reference": "MedicationDispense/meddisp-2" - }, - { - "reference": "MedicationKnowledge/medknow-2" - } - ] - } - ] - }, - "3": { - "resourceType": "Composition", - "id": "comp-3", - "status": "entered-in-error", - "type": { - "coding": [ - { - "system": "http://loinc.org", - "code": "8648-8", - "display": "Patient insurance information" - } - ] - }, - "subject": { - "reference": "Patient/pat-3" - }, - "date": "2025-04-01T14:00:00Z", - "author": [ - { - "reference": "Practitioner/prac-3" - } - ], - "title": "Coverage and Questionnaire", - "section": [ - { - "title": "Coverage", - "entry": [ - { - "reference": "Coverage/cov-3" - }, - { - "reference": "Device/dev-3" - } - ] - }, - { - "title": "Patient-reported Data", - "entry": [ - { - "reference": "Questionnaire/quest-3" - }, - { - "reference": "QuestionnaireResponse/qres-3" - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/compositions.json.license b/packages/fhir/test/resources/compositions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/compositions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/compositions.test.ts b/packages/fhir/test/resources/compositions.test.ts deleted file mode 100644 index ff752ff8..00000000 --- a/packages/fhir/test/resources/compositions.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Composition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirComposition, - type untypedCompositionSchema, -} from '../../src/index.js' - -describe('Composition Resource', () => { - it('should validate FHIR Compositions from compositions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/compositions.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirComposition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/conceptMaps.json b/packages/fhir/test/resources/conceptMaps.json deleted file mode 100644 index e7328d96..00000000 --- a/packages/fhir/test/resources/conceptMaps.json +++ /dev/null @@ -1,275 +0,0 @@ -{ - "0": { - "resourceType": "ConceptMap", - "id": "example-conceptmap-0", - "url": "http://example.org/fhir/ConceptMap/example-0", - "identifier": { - "system": "urn:ietf:rfc:3986", - "value": "urn:oid:2.16.840.1.113883.4.642.1.0" - }, - "version": "1.0.0", - "name": "ExampleConceptMap0", - "title": "Example Concept Map 0", - "status": "active", - "experimental": true, - "date": "2023-01-15T10:00:00Z", - "publisher": "Example Health Organization", - "contact": [ - { - "name": "Terminology Team", - "telecom": [ - { - "system": "email", - "value": "terminology@example.org" - } - ] - } - ], - "description": "An example concept map for testing purposes", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "purpose": "Demonstrates mapping between two code systems", - "copyright": "Copyright 2023 Example Health Organization", - "sourceUri": "http://example.org/fhir/ValueSet/source-vs", - "targetUri": "http://example.org/fhir/ValueSet/target-vs", - "group": [ - { - "source": "http://example.org/fhir/CodeSystem/source-cs", - "sourceVersion": "1.0.0", - "target": "http://example.org/fhir/CodeSystem/target-cs", - "targetVersion": "2.0.0", - "element": [ - { - "code": "code1", - "display": "Source Code 1", - "target": [ - { - "code": "target-code1", - "display": "Target Code 1", - "equivalence": "equivalent" - } - ] - }, - { - "code": "code2", - "display": "Source Code 2", - "target": [ - { - "code": "target-code2a", - "display": "Target Code 2A", - "equivalence": "wider", - "comment": "This is a broader concept" - }, - { - "code": "target-code2b", - "display": "Target Code 2B", - "equivalence": "wider", - "comment": "This is also a broader concept" - } - ] - } - ] - } - ] - }, - "1": { - "resourceType": "ConceptMap", - "id": "example-conceptmap-1", - "url": "http://example.org/fhir/ConceptMap/example-1", - "version": "2.1.0", - "name": "ExampleConceptMap1", - "title": "Example Concept Map 1 with Dependencies", - "status": "draft", - "experimental": false, - "date": "2023-03-20T14:30:00Z", - "publisher": "Medical Standards Institute", - "description": "A concept map with dependencies and products", - "sourceCanonical": "http://example.org/fhir/ValueSet/conditions", - "targetCanonical": "http://snomed.info/sct", - "group": [ - { - "source": "http://example.org/fhir/CodeSystem/local-conditions", - "target": "http://snomed.info/sct", - "element": [ - { - "code": "diabetes-t2", - "display": "Type 2 Diabetes", - "target": [ - { - "code": "44054006", - "display": "Diabetes mellitus type 2", - "equivalence": "equal", - "dependsOn": [ - { - "property": "http://example.org/fhir/property/age", - "system": "http://example.org/fhir/CodeSystem/age-groups", - "value": "adult", - "display": "Adult" - } - ] - } - ] - }, - { - "code": "hypertension", - "display": "High Blood Pressure", - "target": [ - { - "code": "38341003", - "display": "Hypertensive disorder", - "equivalence": "equivalent", - "product": [ - { - "property": "http://example.org/fhir/property/severity", - "system": "http://example.org/fhir/CodeSystem/severity", - "value": "moderate", - "display": "Moderate" - } - ] - } - ] - } - ] - } - ] - }, - "2": { - "resourceType": "ConceptMap", - "id": "example-conceptmap-2", - "url": "http://example.org/fhir/ConceptMap/example-2", - "version": "1.5.0", - "name": "ExampleConceptMap2", - "title": "Example Concept Map 2 with Unmapped", - "status": "active", - "date": "2023-06-10T09:00:00Z", - "publisher": "Terminology Services Ltd", - "description": "Demonstrates unmapped codes handling", - "useContext": [ - { - "code": { - "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", - "code": "venue" - }, - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "AMB", - "display": "ambulatory" - } - ] - } - } - ], - "sourceUri": "http://example.org/fhir/ValueSet/medications-old", - "targetUri": "http://example.org/fhir/ValueSet/medications-new", - "group": [ - { - "source": "http://example.org/fhir/CodeSystem/medications-v1", - "target": "http://example.org/fhir/CodeSystem/medications-v2", - "element": [ - { - "code": "aspirin-100", - "display": "Aspirin 100mg", - "target": [ - { - "code": "asp-100", - "display": "Aspirin 100mg Tablet", - "equivalence": "equal" - } - ] - }, - { - "code": "paracetamol-500", - "display": "Paracetamol 500mg", - "target": [ - { - "code": "para-500", - "display": "Paracetamol 500mg Tablet", - "equivalence": "equivalent" - } - ] - } - ], - "unmapped": { - "mode": "fixed", - "code": "unmapped-medication", - "display": "Unmapped Medication" - } - } - ] - }, - "3": { - "resourceType": "ConceptMap", - "id": "example-conceptmap-3", - "url": "http://example.org/fhir/ConceptMap/example-3", - "version": "3.0.0", - "name": "ExampleConceptMap3", - "title": "Example Concept Map 3 - Multiple Groups", - "status": "retired", - "experimental": true, - "date": "2022-12-15T16:45:00Z", - "publisher": "Legacy Terminology Provider", - "description": "A retired concept map with multiple mapping groups", - "sourceCanonical": "http://example.org/fhir/ValueSet/all-codes", - "targetCanonical": "http://example.org/fhir/ValueSet/new-codes", - "group": [ - { - "source": "http://example.org/fhir/CodeSystem/group-a", - "target": "http://example.org/fhir/CodeSystem/unified", - "element": [ - { - "code": "a1", - "display": "Code A1", - "target": [ - { - "code": "unified-1", - "display": "Unified Code 1", - "equivalence": "equivalent" - } - ] - } - ], - "unmapped": { - "mode": "other-map", - "url": "http://example.org/fhir/ConceptMap/fallback-map" - } - }, - { - "source": "http://example.org/fhir/CodeSystem/group-b", - "target": "http://example.org/fhir/CodeSystem/unified", - "element": [ - { - "code": "b1", - "display": "Code B1", - "target": [ - { - "code": "unified-2", - "display": "Unified Code 2", - "equivalence": "narrower", - "comment": "The source concept is more specific" - } - ] - }, - { - "code": "b2", - "target": [ - { - "equivalence": "unmatched" - } - ] - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/conceptMaps.test.ts b/packages/fhir/test/resources/conceptMaps.test.ts deleted file mode 100644 index 1e636286..00000000 --- a/packages/fhir/test/resources/conceptMaps.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ConceptMap } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirConceptMap, - type untypedConceptMapSchema, -} from '../../src/index.js' - -describe('ConceptMap Resource', () => { - it('should validate FHIR ConceptMaps from conceptMaps.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/conceptMaps.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirConceptMap.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/conditions.json b/packages/fhir/test/resources/conditions.json deleted file mode 100644 index 4f2a147a..00000000 --- a/packages/fhir/test/resources/conditions.json +++ /dev/null @@ -1,202 +0,0 @@ -{ - "0": { - "resourceType": "Condition", - "id": "condition-example-0", - "clinicalStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", - "code": "active" - } - ] - }, - "verificationStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-ver-status", - "code": "confirmed" - } - ] - }, - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-category", - "code": "problem-list-item" - } - ] - } - ], - "severity": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "24484000", - "display": "Severe" - } - ] - }, - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "44054006", - "display": "Diabetes mellitus type 2" - } - ] - }, - "subject": { "reference": "Patient/patient-example" }, - "onsetDateTime": "2020-03-01", - "recordedDate": "2020-03-15", - "recorder": { "reference": "Practitioner/practitioner-example" } - }, - "1": { - "resourceType": "Condition", - "id": "condition-example-1", - "clinicalStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", - "code": "remission" - } - ] - }, - "verificationStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-ver-status", - "code": "provisional" - } - ] - }, - "code": { - "coding": [ - { - "system": "http://loinc.org", - "code": "LP416620-8", - "display": "COVID-19" - } - ] - }, - "subject": { "reference": "Patient/patient-another" }, - "encounter": { "reference": "Encounter/encounter-example" }, - "onsetAge": { "value": 45, "unit": "years" }, - "abatementDateTime": "2022-09-01", - "evidence": [ - { - "code": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "271327008", - "display": "Fever" - } - ] - } - ] - } - ], - "note": [{ "text": "Patient symptoms improving." }] - }, - "2": { - "resourceType": "Condition", - "id": "condition-example-2", - "clinicalStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", - "code": "inactive" - } - ] - }, - "verificationStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-ver-status", - "code": "entered-in-error" - } - ] - }, - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "38341003", - "display": "Hypertension" - } - ] - }, - "subject": { "reference": "Patient/patient-example" }, - "recordedDate": "2018-01-12", - "asserter": { "reference": "Practitioner/practitioner-assert" }, - "bodySite": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "38266002", - "display": "Entire body" - } - ] - } - ], - "stage": [ - { - "summary": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "258219007", - "display": "Stage 1" - } - ] - } - } - ] - }, - "3": { - "resourceType": "Condition", - "id": "condition-example-3", - "clinicalStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", - "code": "recurrence" - } - ] - }, - "verificationStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-ver-status", - "code": "confirmed" - } - ] - }, - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "109979007", - "display": "Asthma" - } - ] - }, - "subject": { "reference": "Patient/patient-xyz" }, - "encounter": { "reference": "Encounter/enc-xyz" }, - "onsetRange": { - "low": { "value": 5, "unit": "years" }, - "high": { "value": 12, "unit": "years" } - }, - "abatementString": "Resolved after treatment", - "note": [ - { - "authorString": "Dr. Smith", - "time": "2023-06-15", - "text": "Asthma under control with inhaler." - } - ] - } -} diff --git a/packages/fhir/test/resources/conditions.json.license b/packages/fhir/test/resources/conditions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/conditions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/conditions.test.ts b/packages/fhir/test/resources/conditions.test.ts deleted file mode 100644 index 11649bf5..00000000 --- a/packages/fhir/test/resources/conditions.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Condition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirCondition, type untypedConditionSchema } from '../../src/index.js' - -describe('Condition Resource', () => { - it('should validate FHIR Conditions from conditions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/conditions.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirCondition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/consents.json b/packages/fhir/test/resources/consents.json deleted file mode 100644 index 79f64765..00000000 --- a/packages/fhir/test/resources/consents.json +++ /dev/null @@ -1,207 +0,0 @@ -{ - "0": { - "resourceType": "Consent", - "id": "consent-example-0", - "status": "active", - "scope": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/consentscope", - "code": "patient-privacy" - } - ] - }, - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "INFA" - } - ] - } - ], - "patient": { - "reference": "Patient/example" - }, - "dateTime": "2023-01-15T10:30:00Z", - "performer": [ - { - "reference": "Patient/example" - } - ], - "organization": [ - { - "reference": "Organization/example" - } - ], - "policyRule": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "OPTIN" - } - ] - } - }, - "1": { - "resourceType": "Consent", - "id": "consent-example-1", - "status": "proposed", - "scope": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/consentscope", - "code": "research" - } - ] - }, - "category": [ - { - "coding": [ - { - "system": "http://loinc.org", - "code": "59284-0", - "display": "Patient Consent" - } - ] - } - ], - "patient": { - "reference": "Patient/example2" - }, - "dateTime": "2023-02-20T14:00:00Z", - "provision": { - "type": "permit", - "period": { - "start": "2023-02-20", - "end": "2024-02-20" - }, - "actor": [ - { - "role": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationType", - "code": "IRCP" - } - ] - }, - "reference": { - "reference": "Organization/research-org" - } - } - ], - "purpose": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", - "code": "HRESCH" - } - ] - } - }, - "2": { - "resourceType": "Consent", - "id": "consent-example-2", - "status": "active", - "scope": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/consentscope", - "code": "treatment" - } - ] - }, - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/consentcategorycodes", - "code": "acd" - } - ] - } - ], - "patient": { - "reference": "Patient/example3" - }, - "dateTime": "2023-03-10T09:15:00Z", - "verification": [ - { - "verified": true, - "verificationDate": "2023-03-10T09:15:00Z", - "verifiedWith": { - "reference": "RelatedPerson/family-member" - } - } - ], - "provision": { - "type": "deny", - "code": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "116859006", - "display": "Transfusion of blood product" - } - ] - } - ] - } - }, - "3": { - "resourceType": "Consent", - "id": "consent-example-3", - "status": "draft", - "scope": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/consentscope", - "code": "patient-privacy" - } - ] - }, - "category": [ - { - "coding": [ - { - "system": "http://loinc.org", - "code": "64292-6" - } - ] - } - ], - "patient": { - "reference": "Patient/example4" - }, - "dateTime": "2023-04-05T16:45:00Z", - "policy": [ - { - "authority": "http://example.org/fhir/policy", - "uri": "http://example.org/fhir/policy/consent-policy-v1" - } - ], - "provision": { - "type": "permit", - "data": [ - { - "meaning": "related", - "reference": { - "reference": "Observation/example" - } - } - ], - "action": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/consentaction", - "code": "access" - } - ] - } - ] - } - } -} diff --git a/packages/fhir/test/resources/consents.json.license b/packages/fhir/test/resources/consents.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/consents.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/consents.test.ts b/packages/fhir/test/resources/consents.test.ts deleted file mode 100644 index 536cfa15..00000000 --- a/packages/fhir/test/resources/consents.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Consent } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirConsent, type untypedConsentSchema } from '../../src/index.js' - -describe('Consent Resource', () => { - it('should validate FHIR Consents from consents.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/consents.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirConsent.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/contracts.json b/packages/fhir/test/resources/contracts.json deleted file mode 100644 index 3167d101..00000000 --- a/packages/fhir/test/resources/contracts.json +++ /dev/null @@ -1,293 +0,0 @@ -{ - "0": { - "resourceType": "Contract", - "id": "contract-example-0", - "status": "executed", - "issued": "2023-01-15T10:30:00Z", - "subject": [ - { - "reference": "Patient/example" - } - ], - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/contract-type", - "code": "privacy" - } - ] - }, - "term": [ - { - "offer": { - "text": "Patient agrees to privacy terms" - } - } - ] - }, - "1": { - "resourceType": "Contract", - "id": "contract-example-1", - "identifier": [ - { - "system": "http://example.org/fhir/contract", - "value": "CONTRACT-2023-001" - } - ], - "status": "negotiable", - "issued": "2023-02-20T14:00:00Z", - "applies": { - "start": "2023-03-01", - "end": "2024-03-01" - }, - "subject": [ - { - "reference": "Patient/patient-1" - } - ], - "authority": [ - { - "reference": "Organization/hospital" - } - ], - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/contract-type", - "code": "consent" - } - ] - }, - "subType": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/consentscope", - "code": "research" - } - ] - } - ], - "term": [ - { - "type": { - "coding": [ - { - "system": "http://example.org/fhir/contract-term-type", - "code": "statutory" - } - ] - }, - "offer": { - "party": [ - { - "reference": [ - { - "reference": "Patient/patient-1" - } - ], - "role": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/contractsignertypecodes", - "code": "CONSENTER" - } - ] - } - } - ], - "text": "Research participation consent" - } - } - ] - }, - "2": { - "resourceType": "Contract", - "id": "contract-example-2", - "status": "executable", - "contentDerivative": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/contract-content-derivative", - "code": "registration" - } - ] - }, - "issued": "2023-03-10T09:15:00Z", - "subject": [ - { - "reference": "Organization/clinic" - } - ], - "author": { - "reference": "Practitioner/legal-counsel" - }, - "scope": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/contract-scope", - "code": "policy" - } - ] - }, - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/contract-type", - "code": "disclosure" - } - ] - }, - "term": [ - { - "offer": { - "decision": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "OPTIN" - } - ] - }, - "text": "Disclosure authorization" - }, - "asset": [ - { - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/contract-assettype", - "code": "participation" - } - ] - } - ], - "period": [ - { - "start": "2023-03-10", - "end": "2024-03-10" - } - ] - } - ] - } - ], - "signer": [ - { - "type": { - "system": "http://terminology.hl7.org/CodeSystem/contractsignertypecodes", - "code": "CONSENTER" - }, - "party": { - "reference": "Patient/example" - }, - "signature": [ - { - "type": [ - { - "system": "urn:iso-astm:E1762-95:2013", - "code": "1.2.840.10065.1.12.1.1" - } - ], - "when": "2023-03-10T09:15:00Z", - "who": { - "reference": "Patient/example" - } - } - ] - } - ] - }, - "3": { - "resourceType": "Contract", - "id": "contract-example-3", - "status": "offered", - "legalState": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/contract-legalstate", - "code": "draft" - } - ] - }, - "issued": "2023-04-05T16:45:00Z", - "subject": [ - { - "reference": "Patient/patient-4" - } - ], - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/contract-type", - "code": "healthcare-privacy" - } - ] - }, - "name": "StandardPrivacyContract", - "title": "Standard Healthcare Privacy Contract", - "contentDefinition": { - "type": { - "coding": [ - { - "system": "http://example.org/contract-definition-type", - "code": "consent-directive" - } - ] - }, - "publicationStatus": "offered", - "publicationDate": "2023-04-05T16:45:00Z", - "publisher": { - "reference": "Organization/hospital" - } - }, - "term": [ - { - "identifier": { - "system": "http://example.org/contract-term", - "value": "TERM-001" - }, - "type": { - "coding": [ - { - "system": "http://example.org/contract-term-type", - "code": "statutory" - } - ] - }, - "offer": { - "text": "Standard privacy terms and conditions" - }, - "action": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/contractaction", - "code": "action-a" - } - ] - }, - "intent": { - "coding": [ - { - "system": "http://hl7.org/fhir/request-intent", - "code": "proposal" - } - ] - }, - "status": { - "coding": [ - { - "system": "http://hl7.org/fhir/contract-actionstatus", - "code": "pending" - } - ] - } - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/contracts.json.license b/packages/fhir/test/resources/contracts.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/contracts.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/contracts.test.ts b/packages/fhir/test/resources/contracts.test.ts deleted file mode 100644 index bffa6dab..00000000 --- a/packages/fhir/test/resources/contracts.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Contract } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirContract, type untypedContractSchema } from '../../src/index.js' - -describe('Contract Resource', () => { - it('should validate FHIR Contracts from contracts.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/contracts.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirContract.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/coverageEligibilityRequests.json b/packages/fhir/test/resources/coverageEligibilityRequests.json deleted file mode 100644 index 0b453a82..00000000 --- a/packages/fhir/test/resources/coverageEligibilityRequests.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "0": { - "resourceType": "CoverageEligibilityRequest", - "id": "coverageeligibilityrequest-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T10:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/CoverageEligibilityRequest" - ] - }, - "status": "active", - "purpose": ["validation"], - "patient": { - "reference": "Patient/patient-001" - }, - "created": "2025-01-15T10:00:00Z", - "insurer": { - "reference": "Organization/insurance-001" - } - }, - "1": { - "resourceType": "CoverageEligibilityRequest", - "id": "coverageeligibilityrequest-comprehensive", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T11:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/CoverageEligibilityRequest" - ] - }, - "identifier": [ - { - "system": "http://hospital.example.org/eligibility-requests", - "value": "ER-001" - } - ], - "status": "active", - "priority": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/processpriority", - "code": "normal" - } - ] - }, - "purpose": ["benefits", "validation"], - "patient": { - "reference": "Patient/patient-002" - }, - "servicedDate": "2025-01-20", - "created": "2025-01-15T11:00:00Z", - "provider": { - "reference": "Organization/hospital-001" - }, - "insurer": { - "reference": "Organization/insurance-001" - } - }, - "2": { - "resourceType": "CoverageEligibilityRequest", - "id": "coverageeligibilityrequest-draft", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T12:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/CoverageEligibilityRequest" - ] - }, - "status": "draft", - "purpose": ["discovery"], - "patient": { - "reference": "Patient/patient-003" - }, - "created": "2025-01-15T12:00:00Z", - "insurer": { - "reference": "Organization/insurance-002" - } - }, - "3": { - "resourceType": "CoverageEligibilityRequest", - "id": "coverageeligibilityrequest-auth", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T13:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/CoverageEligibilityRequest" - ] - }, - "status": "active", - "purpose": ["auth-requirements"], - "patient": { - "reference": "Patient/patient-004" - }, - "created": "2025-01-15T13:00:00Z", - "insurer": { - "reference": "Organization/insurance-001" - } - } -} diff --git a/packages/fhir/test/resources/coverageEligibilityRequests.json.license b/packages/fhir/test/resources/coverageEligibilityRequests.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/coverageEligibilityRequests.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/coverageEligibilityRequests.test.ts b/packages/fhir/test/resources/coverageEligibilityRequests.test.ts deleted file mode 100644 index c876151f..00000000 --- a/packages/fhir/test/resources/coverageEligibilityRequests.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type CoverageEligibilityRequest } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirCoverageEligibilityRequest, - type untypedCoverageEligibilityRequestSchema, -} from '../../src/index.js' - -describe('CoverageEligibilityRequest Resource', () => { - it('should validate FHIR CoverageEligibilityRequests from coverageEligibilityRequests.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/coverageEligibilityRequests.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = - FhirCoverageEligibilityRequest.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/coverageEligibilityResponses.json b/packages/fhir/test/resources/coverageEligibilityResponses.json deleted file mode 100644 index a9d54d5a..00000000 --- a/packages/fhir/test/resources/coverageEligibilityResponses.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "0": { - "resourceType": "CoverageEligibilityResponse", - "id": "coverageeligibilityresponse-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T10:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/CoverageEligibilityResponse" - ] - }, - "status": "active", - "purpose": ["validation"], - "patient": { - "reference": "Patient/patient-001" - }, - "created": "2025-01-15T10:00:00Z", - "request": { - "reference": "CoverageEligibilityRequest/coverageeligibilityrequest-minimal" - }, - "outcome": "complete", - "insurer": { - "reference": "Organization/insurance-001" - } - }, - "1": { - "resourceType": "CoverageEligibilityResponse", - "id": "coverageeligibilityresponse-comprehensive", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T11:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/CoverageEligibilityResponse" - ] - }, - "identifier": [ - { - "system": "http://insurance.example.org/eligibility-responses", - "value": "ER-RESP-001" - } - ], - "status": "active", - "purpose": ["benefits", "validation"], - "patient": { - "reference": "Patient/patient-002" - }, - "servicedDate": "2025-01-20", - "created": "2025-01-15T11:00:00Z", - "requestor": { - "reference": "Organization/hospital-001" - }, - "request": { - "reference": "CoverageEligibilityRequest/coverageeligibilityrequest-comprehensive" - }, - "outcome": "complete", - "disposition": "Patient is eligible for services", - "insurer": { - "reference": "Organization/insurance-001" - } - }, - "2": { - "resourceType": "CoverageEligibilityResponse", - "id": "coverageeligibilityresponse-error", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T12:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/CoverageEligibilityResponse" - ] - }, - "status": "active", - "purpose": ["discovery"], - "patient": { - "reference": "Patient/patient-003" - }, - "created": "2025-01-15T12:00:00Z", - "request": { - "reference": "CoverageEligibilityRequest/coverageeligibilityrequest-draft" - }, - "outcome": "error", - "disposition": "Coverage not found", - "insurer": { - "reference": "Organization/insurance-002" - } - }, - "3": { - "resourceType": "CoverageEligibilityResponse", - "id": "coverageeligibilityresponse-queued", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T13:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/CoverageEligibilityResponse" - ] - }, - "status": "active", - "purpose": ["auth-requirements"], - "patient": { - "reference": "Patient/patient-004" - }, - "created": "2025-01-15T13:00:00Z", - "request": { - "reference": "CoverageEligibilityRequest/coverageeligibilityrequest-auth" - }, - "outcome": "queued", - "disposition": "Request is under review", - "insurer": { - "reference": "Organization/insurance-001" - } - } -} diff --git a/packages/fhir/test/resources/coverageEligibilityResponses.json.license b/packages/fhir/test/resources/coverageEligibilityResponses.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/coverageEligibilityResponses.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/coverageEligibilityResponses.test.ts b/packages/fhir/test/resources/coverageEligibilityResponses.test.ts deleted file mode 100644 index ccfc2358..00000000 --- a/packages/fhir/test/resources/coverageEligibilityResponses.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type CoverageEligibilityResponse } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirCoverageEligibilityResponse, - type untypedCoverageEligibilityResponseSchema, -} from '../../src/index.js' - -describe('CoverageEligibilityResponse Resource', () => { - it('should validate FHIR CoverageEligibilityResponses from coverageEligibilityResponses.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/coverageEligibilityResponses.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = - FhirCoverageEligibilityResponse.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/coverages.json b/packages/fhir/test/resources/coverages.json deleted file mode 100644 index b9d70915..00000000 --- a/packages/fhir/test/resources/coverages.json +++ /dev/null @@ -1,512 +0,0 @@ -{ - "0": { - "resourceType": "Coverage", - "id": "coverage-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T14:30:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Coverage"] - }, - "status": "active", - "beneficiary": { - "reference": "Patient/patient-001", - "display": "John Smith" - }, - "payor": [ - { - "reference": "Organization/insurance-company-001", - "display": "Blue Cross Blue Shield" - } - ] - }, - "1": { - "resourceType": "Coverage", - "id": "coverage-comprehensive", - "meta": { - "versionId": "3", - "lastUpdated": "2025-01-15T15:45:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Coverage"], - "security": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", - "code": "HTEST", - "display": "test health data" - } - ], - "tag": [ - { - "system": "http://terminology.hl7.org/CodeSystem/common-tags", - "code": "actionable" - } - ] - }, - "text": { - "status": "generated", - "div": "
Comprehensive Health Insurance Coverage for Sarah Johnson
" - }, - "identifier": [ - { - "use": "official", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "MB", - "display": "Member Number" - } - ] - }, - "system": "http://bluecross.com/member-id", - "value": "BC-12345678", - "period": { - "start": "2024-01-01" - }, - "assigner": { - "reference": "Organization/blue-cross", - "display": "Blue Cross Blue Shield" - } - }, - { - "use": "secondary", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "SN", - "display": "Subscriber Number" - } - ] - }, - "system": "http://employer.com/employee-benefits", - "value": "EMP-987654321" - } - ], - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "EHCPOL", - "display": "extended healthcare" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/coverage-type", - "code": "medical", - "display": "Medical" - } - ], - "text": "Comprehensive Medical Coverage" - }, - "policyHolder": { - "reference": "Patient/patient-002", - "display": "Sarah Johnson" - }, - "subscriber": { - "reference": "Patient/patient-002", - "display": "Sarah Johnson" - }, - "subscriberId": "BC-12345678", - "beneficiary": { - "reference": "Patient/patient-002", - "display": "Sarah Johnson" - }, - "dependent": "00", - "relationship": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/subscriber-relationship", - "code": "self", - "display": "Self" - } - ] - }, - "period": { - "start": "2024-01-01T00:00:00Z", - "end": "2024-12-31T23:59:59Z" - }, - "class": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/coverage-class", - "code": "group", - "display": "Group" - } - ] - }, - "value": "EMP-GROUP-001", - "name": "Tech Corporation Employee Group" - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/coverage-class", - "code": "plan", - "display": "Plan" - } - ] - }, - "value": "GOLD-PLUS", - "name": "Gold Plus Plan" - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/coverage-class", - "code": "subplan", - "display": "SubPlan" - } - ] - }, - "value": "GOLD-PLUS-PREMIUM", - "name": "Gold Plus Premium Option" - } - ], - "order": 1, - "network": "BCBS-NETWORK-001", - "payor": [ - { - "reference": "Organization/blue-cross", - "display": "Blue Cross Blue Shield" - } - ], - "costToBeneficiary": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/coverage-copay-type", - "code": "copay", - "display": "Copay" - } - ] - }, - "valueMoney": { - "value": 25.0, - "currency": "USD" - } - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/coverage-copay-type", - "code": "deductible", - "display": "Deductible" - } - ] - }, - "valueMoney": { - "value": 1500.0, - "currency": "USD" - }, - "exception": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/ex-benefitcategory", - "code": "preventive", - "display": "Preventive Care" - } - ] - }, - "period": { - "start": "2024-01-01", - "end": "2024-12-31" - } - } - ] - } - ], - "subrogation": false, - "contract": [ - { - "reference": "Contract/employer-insurance-contract-001", - "display": "Tech Corporation Group Insurance Contract" - } - ] - }, - "2": { - "resourceType": "Coverage", - "id": "coverage-cancelled", - "meta": { - "versionId": "2", - "lastUpdated": "2025-01-15T16:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Coverage"] - }, - "text": { - "status": "generated", - "div": "
Cancelled Insurance Coverage for Robert Miller
" - }, - "identifier": [ - { - "use": "official", - "system": "http://aetna.com/member-id", - "value": "AET-98765432" - } - ], - "status": "cancelled", - - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/coverage-type", - "code": "medical", - "display": "Medical" - } - ] - }, - "subscriber": { - "reference": "Patient/patient-003", - "display": "Robert Miller" - }, - "subscriberId": "AET-98765432", - "beneficiary": { - "reference": "Patient/patient-003", - "display": "Robert Miller" - }, - "relationship": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/subscriber-relationship", - "code": "self", - "display": "Self" - } - ] - }, - "period": { - "start": "2024-01-01T00:00:00Z", - "end": "2024-06-30T23:59:59Z" - }, - "class": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/coverage-class", - "code": "plan", - "display": "Plan" - } - ] - }, - "value": "BASIC-001", - "name": "Basic Plan" - } - ], - "order": 1, - "payor": [ - { - "reference": "Organization/aetna", - "display": "Aetna Health Insurance" - } - ], - "costToBeneficiary": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/coverage-copay-type", - "code": "deductible", - "display": "Deductible" - } - ] - }, - "valueMoney": { - "value": 2500.0, - "currency": "USD" - } - } - ], - "subrogation": true - }, - "3": { - "resourceType": "Coverage", - "id": "coverage-dependent", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T17:15:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Coverage"] - }, - "text": { - "status": "generated", - "div": "
Dependent Coverage for Emily Chen under parent's plan
" - }, - "identifier": [ - { - "use": "official", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "MB", - "display": "Member Number" - } - ] - }, - "system": "http://unitedhealth.com/member-id", - "value": "UH-555666777" - } - ], - "status": "active", - - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/coverage-type", - "code": "medical", - "display": "Medical" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/coverage-type", - "code": "dental", - "display": "Dental" - } - ], - "text": "Medical and Dental Coverage" - }, - "policyHolder": { - "reference": "Patient/patient-004", - "display": "Alex Chen" - }, - "subscriber": { - "reference": "Patient/patient-004", - "display": "Alex Chen" - }, - "subscriberId": "UH-555666777-00", - "beneficiary": { - "reference": "Patient/patient-005", - "display": "Emily Chen" - }, - "dependent": "01", - "relationship": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/subscriber-relationship", - "code": "child", - "display": "Child" - } - ] - }, - "period": { - "start": "2024-01-01T00:00:00Z", - "end": "2026-12-31T23:59:59Z" - }, - "class": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/coverage-class", - "code": "group", - "display": "Group" - } - ] - }, - "value": "FAMILY-PLAN-001", - "name": "Family Plan Coverage" - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/coverage-class", - "code": "plan", - "display": "Plan" - } - ] - }, - "value": "PREMIUM-FAMILY", - "name": "Premium Family Plan" - } - ], - "order": 1, - "network": "UH-NETWORK-WEST", - "payor": [ - { - "reference": "Organization/unitedhealth", - "display": "UnitedHealth Group" - } - ], - "costToBeneficiary": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/coverage-copay-type", - "code": "copay", - "display": "Copay" - } - ] - }, - "valueMoney": { - "value": 15.0, - "currency": "USD" - } - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/coverage-copay-type", - "code": "coinsurance", - "display": "Coinsurance" - } - ] - }, - "valueQuantity": { - "value": 20, - "unit": "percent", - "system": "http://unitsofmeasure.org", - "code": "%" - }, - "exception": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/ex-benefitcategory", - "code": "oral-basic-dental", - "display": "Oral Health Basic Dental" - } - ] - }, - "period": { - "start": "2024-01-01", - "end": "2026-12-31" - } - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/ex-benefitcategory", - "code": "orthodontic", - "display": "Orthodontic" - } - ] - }, - "period": { - "start": "2024-01-01", - "end": "2026-12-31" - } - } - ] - } - ], - "subrogation": false, - "contract": [ - { - "reference": "Contract/family-insurance-contract-001", - "display": "Family Insurance Coverage Contract" - }, - { - "reference": "Contract/dental-rider-contract-001", - "display": "Dental Coverage Rider Contract" - } - ] - } -} diff --git a/packages/fhir/test/resources/coverages.json.license b/packages/fhir/test/resources/coverages.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/coverages.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/coverages.test.ts b/packages/fhir/test/resources/coverages.test.ts deleted file mode 100644 index e6ae5971..00000000 --- a/packages/fhir/test/resources/coverages.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Coverage } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirCoverage, type untypedCoverageSchema } from '../../src/index.js' - -describe('Coverage Resource', () => { - it('should validate FHIR Coverages from coverages.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/coverages.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirCoverage.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/detectedIssues.json b/packages/fhir/test/resources/detectedIssues.json deleted file mode 100644 index d6bb4bd4..00000000 --- a/packages/fhir/test/resources/detectedIssues.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "0": { - "resourceType": "DetectedIssue", - "id": "ddi", - "status": "final", - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "DRG", - "display": "Drug Interaction Alert" - } - ] - }, - "severity": "high", - "patient": { - "reference": "Patient/example" - }, - "identifiedDateTime": "2014-01-05", - "author": { - "reference": "Device/software" - }, - "implicated": [ - { - "reference": "MedicationRequest/example" - }, - { - "reference": "MedicationRequest/example2" - } - ], - "detail": "Potential interaction between warfarin and ibuprofen" - }, - "1": { - "resourceType": "DetectedIssue", - "id": "allergy", - "status": "final", - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "ALGY", - "display": "Allergy Alert" - } - ] - }, - "severity": "high", - "patient": { - "reference": "Patient/example" - }, - "identifiedDateTime": "2014-01-05", - "author": { - "reference": "Device/software" - }, - "implicated": [ - { - "reference": "MedicationRequest/example" - } - ], - "detail": "Patient has known allergy to penicillin", - "mitigation": [ - { - "action": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "13", - "display": "Stopped Concurrent Therapy" - } - ], - "text": "Cancelled the prescription" - }, - "date": "2014-01-05", - "author": { - "reference": "Practitioner/example" - } - } - ] - }, - "2": { - "resourceType": "DetectedIssue", - "id": "duplicate", - "identifier": [ - { - "use": "official", - "system": "http://example.org", - "value": "12345" - } - ], - "status": "preliminary", - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "DUPTHPY", - "display": "Duplicate Therapy Alert" - } - ] - }, - "severity": "moderate", - "patient": { - "reference": "Patient/example" - }, - "identifiedPeriod": { - "start": "2013-05-08", - "end": "2013-05-15" - }, - "implicated": [ - { - "reference": "MedicationRequest/example" - } - ], - "evidence": [ - { - "code": [ - { - "coding": [ - { - "system": "http://example.org/codesystem", - "code": "EVIDENCE", - "display": "Evidence" - } - ] - } - ], - "detail": [ - { - "reference": "Observation/example" - } - ] - } - ], - "reference": "http://example.org/evidence" - }, - "3": { - "resourceType": "DetectedIssue", - "id": "lab", - "status": "registered", - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "LAB", - "display": "Lab Alert" - } - ] - }, - "severity": "low", - "patient": { - "reference": "Patient/example" - }, - "identifiedDateTime": "2014-01-10T12:00:00+00:00", - "author": { - "reference": "Device/software" - }, - "implicated": [ - { - "reference": "ServiceRequest/example" - } - ], - "detail": "Creatinine levels elevated" - } -} diff --git a/packages/fhir/test/resources/detectedIssues.json.license b/packages/fhir/test/resources/detectedIssues.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/detectedIssues.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/detectedIssues.test.ts b/packages/fhir/test/resources/detectedIssues.test.ts deleted file mode 100644 index 99472c0a..00000000 --- a/packages/fhir/test/resources/detectedIssues.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type DetectedIssue } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirDetectedIssue, - type untypedDetectedIssueSchema, -} from '../../src/index.js' - -describe('DetectedIssue Resource', () => { - it('should validate FHIR detected issue from detectedIssues.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/detectedIssues.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirDetectedIssue.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/deviceDefinitions.json b/packages/fhir/test/resources/deviceDefinitions.json deleted file mode 100644 index d9a9ebdf..00000000 --- a/packages/fhir/test/resources/deviceDefinitions.json +++ /dev/null @@ -1,247 +0,0 @@ -{ - "0": { - "resourceType": "DeviceDefinition", - "id": "example-device-definition-1", - "identifier": [ - { - "system": "http://example.org/devices", - "value": "ABC123" - } - ], - "udiDeviceIdentifier": [ - { - "deviceIdentifier": "(01)00844588003288(17)220101(10)ABC123", - "issuer": "http://hl7.org/fhir/NamingSystem/gs1-di", - "jurisdiction": "http://hl7.org/fhir/NamingSystem/fda-udi" - } - ], - "manufacturerString": "Acme Medical Devices", - "deviceName": [ - { - "name": "Blood Glucose Monitor Model X", - "type": "user-friendly-name" - } - ], - "modelNumber": "BGM-X100", - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "43252007", - "display": "Blood glucose monitoring device" - } - ] - }, - "safety": [ - { - "coding": [ - { - "system": "urn:oid:2.16.840.1.113883.3.26.1.1", - "code": "C106046", - "display": "Not made with natural rubber latex" - } - ] - } - ], - "languageCode": [ - { - "coding": [ - { - "system": "urn:ietf:bcp:47", - "code": "en-US" - } - ] - } - ], - "note": [ - { - "text": "Requires calibration every 6 months" - } - ] - }, - "1": { - "resourceType": "DeviceDefinition", - "id": "example-device-definition-2", - "identifier": [ - { - "system": "http://example.org/devices", - "value": "XYZ789" - } - ], - "manufacturerString": "TechMed Inc", - "deviceName": [ - { - "name": "Pulse Oximeter Pro", - "type": "manufacturer-name" - }, - { - "name": "PulseOx Pro", - "type": "user-friendly-name" - } - ], - "modelNumber": "PO-2000", - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "448703006", - "display": "Pulse oximeter" - } - ] - }, - "capability": [ - { - "type": { - "coding": [ - { - "system": "http://example.org/capabilities", - "code": "spo2-measurement", - "display": "SpO2 Measurement" - } - ] - } - } - ], - "property": [ - { - "type": { - "coding": [ - { - "system": "http://example.org/properties", - "code": "battery-type", - "display": "Battery Type" - } - ] - }, - "valueCode": [ - { - "coding": [ - { - "system": "http://example.org/battery-types", - "code": "AAA", - "display": "AAA Battery" - } - ] - } - ] - } - ], - "contact": [ - { - "system": "url", - "value": "http://www.techmedinc.com" - } - ] - }, - "2": { - "resourceType": "DeviceDefinition", - "id": "example-device-definition-3", - "identifier": [ - { - "system": "http://example.org/devices", - "value": "DEF456" - } - ], - "manufacturerString": "CardioTech Systems", - "deviceName": [ - { - "name": "ECG Monitor Plus", - "type": "model-name" - } - ], - "modelNumber": "ECG-500", - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "258057004", - "display": "Electrocardiographic monitor" - } - ] - }, - "specialization": [ - { - "systemType": "12-lead ECG", - "version": "2.1" - } - ], - "version": ["2.1.0"], - "safety": [ - { - "coding": [ - { - "system": "urn:oid:2.16.840.1.113883.3.26.1.1", - "code": "C106045", - "display": "Contains latex" - } - ] - } - ], - "languageCode": [ - { - "coding": [ - { - "system": "urn:ietf:bcp:47", - "code": "en-US" - } - ] - } - ], - "material": [ - { - "substance": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "111088007", - "display": "Latex" - } - ] - }, - "allergenicIndicator": true - } - ] - }, - "3": { - "resourceType": "DeviceDefinition", - "id": "example-device-definition-4", - "identifier": [ - { - "system": "http://example.org/devices", - "value": "GHI321" - } - ], - "manufacturerString": "RespiCare Medical", - "deviceName": [ - { - "name": "Ventilator V3000", - "type": "manufacturer-name" - } - ], - "modelNumber": "V3000", - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "706172005", - "display": "Ventilator" - } - ] - }, - "version": ["3.0.1", "3.0.2"], - "url": "http://www.respicaremedical.com/devices/v3000", - "onlineInformation": "http://www.respicaremedical.com/support/v3000", - "note": [ - { - "text": "Recommended for adult patients only" - }, - { - "text": "Regular maintenance required every 1000 hours of operation" - } - ], - "quantity": { - "value": 1, - "unit": "unit" - } - } -} diff --git a/packages/fhir/test/resources/deviceDefinitions.json.license b/packages/fhir/test/resources/deviceDefinitions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/deviceDefinitions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/deviceDefinitions.test.ts b/packages/fhir/test/resources/deviceDefinitions.test.ts deleted file mode 100644 index 36c9a7cd..00000000 --- a/packages/fhir/test/resources/deviceDefinitions.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type DeviceDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirDeviceDefinition, - type untypedDeviceDefinitionSchema, -} from '../../src/index.js' - -describe('DeviceDefinition Resource', () => { - it('should validate FHIR DeviceDefinitions from deviceDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/deviceDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirDeviceDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/deviceMetrics.json b/packages/fhir/test/resources/deviceMetrics.json deleted file mode 100644 index 635a2b5d..00000000 --- a/packages/fhir/test/resources/deviceMetrics.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "0": { - "resourceType": "DeviceMetric", - "id": "example-device-metric-1", - "identifier": [ - { - "system": "http://example.org/metrics", - "value": "METRIC-001" - } - ], - "type": { - "coding": [ - { - "system": "http://loinc.org", - "code": "8867-4", - "display": "Heart rate" - } - ] - }, - "unit": { - "coding": [ - { - "system": "http://unitsofmeasure.org", - "code": "/min", - "display": "beats per minute" - } - ] - }, - "source": { - "reference": "Device/example-monitor" - }, - "operationalStatus": "on", - "color": "blue", - "category": "measurement", - "calibration": [ - { - "type": "unspecified", - "state": "calibrated", - "time": "2024-01-15T10:30:00Z" - } - ] - }, - "1": { - "resourceType": "DeviceMetric", - "id": "example-device-metric-2", - "identifier": [ - { - "system": "http://example.org/metrics", - "value": "METRIC-002" - } - ], - "type": { - "coding": [ - { - "system": "http://loinc.org", - "code": "2708-6", - "display": "Oxygen saturation in Arterial blood" - } - ] - }, - "unit": { - "coding": [ - { - "system": "http://unitsofmeasure.org", - "code": "%", - "display": "percent" - } - ] - }, - "source": { - "reference": "Device/example-pulse-oximeter" - }, - "parent": { - "reference": "Device/example-patient-monitor" - }, - "operationalStatus": "on", - "color": "magenta", - "category": "measurement" - }, - "2": { - "resourceType": "DeviceMetric", - "id": "example-device-metric-3", - "type": { - "coding": [ - { - "system": "http://loinc.org", - "code": "8480-6", - "display": "Systolic blood pressure" - } - ] - }, - "unit": { - "coding": [ - { - "system": "http://unitsofmeasure.org", - "code": "mm[Hg]", - "display": "millimeter of mercury" - } - ] - }, - "source": { - "reference": "Device/example-bp-monitor" - }, - "operationalStatus": "standby", - "color": "green", - "category": "measurement", - "calibration": [ - { - "type": "two-point", - "state": "calibration-required" - } - ] - }, - "3": { - "resourceType": "DeviceMetric", - "id": "example-device-metric-4", - "identifier": [ - { - "system": "http://example.org/metrics", - "value": "METRIC-004" - } - ], - "type": { - "coding": [ - { - "system": "urn:iso:std:iso:11073:10101", - "code": "150456", - "display": "MDC_TEMP" - } - ] - }, - "unit": { - "coding": [ - { - "system": "http://unitsofmeasure.org", - "code": "Cel", - "display": "degree Celsius" - } - ] - }, - "source": { - "reference": "Device/example-thermometer" - }, - "operationalStatus": "on", - "color": "red", - "category": "measurement", - "measurementPeriod": { - "repeat": { - "frequency": 1, - "period": 5, - "periodUnit": "min" - } - } - } -} diff --git a/packages/fhir/test/resources/deviceMetrics.json.license b/packages/fhir/test/resources/deviceMetrics.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/deviceMetrics.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/deviceMetrics.test.ts b/packages/fhir/test/resources/deviceMetrics.test.ts deleted file mode 100644 index 7c6c8d31..00000000 --- a/packages/fhir/test/resources/deviceMetrics.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type DeviceMetric } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirDeviceMetric, - type untypedDeviceMetricSchema, -} from '../../src/index.js' - -describe('DeviceMetric Resource', () => { - it('should validate FHIR DeviceMetrics from deviceMetrics.json', () => { - type DeviceMetricSchema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/deviceMetrics.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirDeviceMetric.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/deviceRequests.json b/packages/fhir/test/resources/deviceRequests.json deleted file mode 100644 index 185b6467..00000000 --- a/packages/fhir/test/resources/deviceRequests.json +++ /dev/null @@ -1,205 +0,0 @@ -{ - "0": { - "resourceType": "DeviceRequest", - "id": "example-device-request-1", - "identifier": [ - { - "system": "http://example.org/device-requests", - "value": "REQ-001" - } - ], - "status": "active", - "intent": "order", - "priority": "routine", - "codeCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "43252007", - "display": "Blood glucose monitoring device" - } - ], - "text": "Blood glucose monitor" - }, - "subject": { - "reference": "Patient/example-patient-1" - }, - "encounter": { - "reference": "Encounter/example-encounter-1" - }, - "occurrenceDateTime": "2024-02-01T10:00:00Z", - "authoredOn": "2024-01-25T09:30:00Z", - "requester": { - "reference": "Practitioner/example-practitioner-1" - }, - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "73211009", - "display": "Diabetes mellitus" - } - ] - } - ], - "note": [ - { - "text": "Patient requires home blood glucose monitoring" - } - ] - }, - "1": { - "resourceType": "DeviceRequest", - "id": "example-device-request-2", - "identifier": [ - { - "system": "http://example.org/device-requests", - "value": "REQ-002" - } - ], - "status": "completed", - "intent": "order", - "priority": "urgent", - "codeReference": { - "reference": "Device/example-wheelchair" - }, - "subject": { - "reference": "Patient/example-patient-2" - }, - "occurrencePeriod": { - "start": "2024-01-15", - "end": "2024-03-15" - }, - "authoredOn": "2024-01-10T14:20:00Z", - "requester": { - "reference": "Practitioner/example-practitioner-2" - }, - "performerType": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "224608005", - "display": "Administrative healthcare staff" - } - ] - }, - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "161874006", - "display": "Difficulty walking" - } - ] - } - ] - }, - "2": { - "resourceType": "DeviceRequest", - "id": "example-device-request-3", - "status": "draft", - "intent": "plan", - "codeCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "706172005", - "display": "Ventilator" - } - ] - }, - "parameter": [ - { - "code": { - "coding": [ - { - "system": "http://example.org/parameters", - "code": "tidal-volume", - "display": "Tidal Volume" - } - ] - }, - "valueQuantity": { - "value": 500, - "unit": "mL", - "system": "http://unitsofmeasure.org", - "code": "mL" - } - } - ], - "subject": { - "reference": "Patient/example-patient-3" - }, - "authoredOn": "2024-02-10T08:00:00Z", - "requester": { - "reference": "Practitioner/example-practitioner-3" - }, - "performer": { - "reference": "Practitioner/example-respiratory-therapist" - }, - "note": [ - { - "text": "Patient requires mechanical ventilation support" - } - ] - }, - "3": { - "resourceType": "DeviceRequest", - "id": "example-device-request-4", - "identifier": [ - { - "system": "http://example.org/device-requests", - "value": "REQ-004" - } - ], - "basedOn": [ - { - "reference": "ServiceRequest/example-service-request-1" - } - ], - "status": "on-hold", - "intent": "original-order", - "priority": "asap", - "codeCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "360300001", - "display": "Cardiac pacemaker" - } - ], - "text": "Cardiac pacemaker implantation" - }, - "subject": { - "reference": "Patient/example-patient-4" - }, - "occurrenceDateTime": "2024-03-01T09:00:00Z", - "authoredOn": "2024-02-15T11:45:00Z", - "requester": { - "reference": "Practitioner/example-cardiologist" - }, - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "426648003", - "display": "Symptomatic bradycardia" - } - ] - } - ], - "insurance": [ - { - "reference": "Coverage/example-coverage-1" - } - ], - "supportingInfo": [ - { - "reference": "Observation/example-ecg-observation" - } - ] - } -} diff --git a/packages/fhir/test/resources/deviceRequests.json.license b/packages/fhir/test/resources/deviceRequests.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/deviceRequests.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/deviceRequests.test.ts b/packages/fhir/test/resources/deviceRequests.test.ts deleted file mode 100644 index 73f7a384..00000000 --- a/packages/fhir/test/resources/deviceRequests.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type DeviceRequest } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirDeviceRequest, - type untypedDeviceRequestSchema, -} from '../../src/index.js' - -describe('DeviceRequest Resource', () => { - it('should validate FHIR DeviceRequests from deviceRequests.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/deviceRequests.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirDeviceRequest.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/deviceUseStatements.json b/packages/fhir/test/resources/deviceUseStatements.json deleted file mode 100644 index 8ee76c30..00000000 --- a/packages/fhir/test/resources/deviceUseStatements.json +++ /dev/null @@ -1,169 +0,0 @@ -{ - "0": { - "resourceType": "DeviceUseStatement", - "id": "example-device-use-1", - "identifier": [ - { - "system": "http://example.org/device-use-statements", - "value": "USE-001" - } - ], - "status": "active", - "subject": { - "reference": "Patient/example-patient-1" - }, - "device": { - "reference": "Device/example-glucose-monitor" - }, - "timingPeriod": { - "start": "2024-01-01", - "end": "2024-12-31" - }, - "recordedOn": "2024-01-05T10:30:00Z", - "source": { - "reference": "Patient/example-patient-1" - }, - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "73211009", - "display": "Diabetes mellitus" - } - ] - } - ], - "note": [ - { - "text": "Patient is using device daily for glucose monitoring" - } - ] - }, - "1": { - "resourceType": "DeviceUseStatement", - "id": "example-device-use-2", - "status": "completed", - "subject": { - "reference": "Patient/example-patient-2" - }, - "basedOn": [ - { - "reference": "DeviceRequest/example-device-request-2" - } - ], - "device": { - "reference": "Device/example-wheelchair" - }, - "timingPeriod": { - "start": "2024-01-15", - "end": "2024-03-15" - }, - "recordedOn": "2024-03-20T14:00:00Z", - "source": { - "reference": "Practitioner/example-practitioner-2" - }, - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "161874006", - "display": "Difficulty walking" - } - ] - } - ] - }, - "2": { - "resourceType": "DeviceUseStatement", - "id": "example-device-use-3", - "status": "active", - "subject": { - "reference": "Patient/example-patient-3" - }, - "device": { - "reference": "Device/example-cpap-machine" - }, - "timingTiming": { - "repeat": { - "frequency": 1, - "period": 1, - "periodUnit": "d", - "when": ["NIGHT"] - } - }, - "recordedOn": "2024-02-01T09:00:00Z", - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "78275009", - "display": "Obstructive sleep apnea syndrome" - } - ] - } - ], - "bodySite": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "123851003", - "display": "Mouth region structure" - } - ] - }, - "note": [ - { - "text": "Patient uses CPAP machine nightly" - } - ] - }, - "3": { - "resourceType": "DeviceUseStatement", - "id": "example-device-use-4", - "identifier": [ - { - "system": "http://example.org/device-use-statements", - "value": "USE-004" - } - ], - "status": "stopped", - "subject": { - "reference": "Patient/example-patient-4" - }, - "derivedFrom": [ - { - "reference": "Observation/example-device-observation" - } - ], - "device": { - "reference": "Device/example-insulin-pump" - }, - "timingPeriod": { - "start": "2023-06-01", - "end": "2024-01-31" - }, - "recordedOn": "2024-02-01T11:00:00Z", - "source": { - "reference": "Practitioner/example-endocrinologist" - }, - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "46635009", - "display": "Type 1 diabetes mellitus" - } - ] - } - ], - "note": [ - { - "text": "Device discontinued due to upgrade to newer model" - } - ] - } -} diff --git a/packages/fhir/test/resources/deviceUseStatements.json.license b/packages/fhir/test/resources/deviceUseStatements.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/deviceUseStatements.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/deviceUseStatements.test.ts b/packages/fhir/test/resources/deviceUseStatements.test.ts deleted file mode 100644 index 4c73ddd9..00000000 --- a/packages/fhir/test/resources/deviceUseStatements.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type DeviceUseStatement } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirDeviceUseStatement, - type untypedDeviceUseStatementSchema, -} from '../../src/index.js' - -describe('DeviceUseStatement Resource', () => { - it('should validate FHIR DeviceUseStatements from deviceUseStatements.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/deviceUseStatements.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirDeviceUseStatement.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/devices.json b/packages/fhir/test/resources/devices.json deleted file mode 100644 index ff188513..00000000 --- a/packages/fhir/test/resources/devices.json +++ /dev/null @@ -1,179 +0,0 @@ -{ - "0": { - "resourceType": "Device", - "id": "1c52ecc7-c65c-4e2b-bf47-a5633a0bc773", - "meta": { - "extension": [ - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-timestamp", - "valueDateTime": "2025-08-12T11:34:39-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-trigger", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0003", - "code": "R01" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-type", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0076", - "code": "ORU" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-timestamp", - "valueDateTime": "2025-03-23T05:27:00-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-id", - "valueString": "423896743" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-data-model-version", - "valueString": "2.6" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-facility", - "valueString": "TLK" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-client-id", - "valueString": "MED-5" - } - ] - }, - "identifier": [ - { - "value": "8943265" - } - ] - }, - "1": { - "resourceType": "Device", - "id": "68cc80cc-ee6e-4233-b79d-440e10491901", - "meta": { - "extension": [ - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-timestamp", - "valueDateTime": "2025-08-12T11:34:39-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-trigger", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0003", - "code": "R01" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-type", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0076", - "code": "ORU" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-timestamp", - "valueDateTime": "2025-03-23T05:27:00-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-id", - "valueString": "2348876" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-data-model-version", - "valueString": "2.6" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-facility", - "valueString": "TLK" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-client-id", - "valueString": "MED-5" - } - ] - }, - "identifier": [ - { - "value": "89734528" - } - ] - }, - "2": { - "resourceType": "Device", - "id": "afdc7998-6205-4fe1-afd2-0135914aa73a", - "meta": { - "extension": [ - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-timestamp", - "valueDateTime": "2025-08-12T11:34:39-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-trigger", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0003", - "code": "R01" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-type", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0076", - "code": "ORU" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-timestamp", - "valueDateTime": "2025-03-23T05:27:00-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-id", - "valueString": "435986543" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-data-model-version", - "valueString": "2.6" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-facility", - "valueString": "TLK" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-client-id", - "valueString": "MED-5" - } - ] - }, - "identifier": [ - { - "value": "234897" - } - ] - } -} diff --git a/packages/fhir/test/resources/devices.json.license b/packages/fhir/test/resources/devices.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/devices.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/devices.test.ts b/packages/fhir/test/resources/devices.test.ts deleted file mode 100644 index 2c32807b..00000000 --- a/packages/fhir/test/resources/devices.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Device } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirDevice, type untypedDeviceSchema } from '../../src/index.js' - -describe('Device Resource', () => { - it('should validate FHIR Devices from devices.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/devices.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirDevice.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/diagnosticReports.json b/packages/fhir/test/resources/diagnosticReports.json deleted file mode 100644 index 22f354fb..00000000 --- a/packages/fhir/test/resources/diagnosticReports.json +++ /dev/null @@ -1,338 +0,0 @@ -{ - "0": { - "resourceType": "DiagnosticReport", - "id": "98496390-c204-4eae-85fd-d8c298adc171", - "meta": { - "extension": [ - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-timestamp", - "valueDateTime": "2025-08-12T11:34:39-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-trigger", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0003", - "code": "R01" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-type", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0076", - "code": "ORU" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-timestamp", - "valueDateTime": "2025-03-23T05:27:00-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-id", - "valueString": "145" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-data-model-version", - "valueString": "2.6" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-facility", - "valueString": "XYZ" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-client-id", - "valueString": "ZZ" - } - ] - }, - "identifier": [ - { - "system": "urn:id:extID", - "value": "202503230527" - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "FILL", - "display": "Filler Identifier" - } - ] - }, - "value": "1356666" - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "PLAC", - "display": "Placer Identifier" - } - ] - }, - "value": "1356666" - } - ], - "basedOn": [ - { - "reference": "ServiceRequest/c028a8d3-1b75-4906-8bad-351bf37439ff" - } - ], - "status": "unknown", - "code": { - "coding": [ - { - "system": "urn:id:CUSTLAB", - "code": "AMP", - "display": "Amphetamine" - } - ], - "text": "Amphetamine" - }, - "subject": { - "reference": "Patient/9f747657-3cc6-4b9a-8371-640bd017501f" - }, - "encounter": { - "reference": "Encounter/9c76de44-7755-449c-9973-75babc0e5c87" - }, - "effectiveDateTime": "2025-03-23T05:26:00-01:00", - "result": [ - { - "reference": "Observation/b49c242a-6021-40b6-9354-a8b2ba2415b6" - } - ] - }, - "1": { - "resourceType": "DiagnosticReport", - "id": "18bbe1bf-fc46-44ea-adc7-ae102a77f658", - "meta": { - "extension": [ - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-timestamp", - "valueDateTime": "2025-08-12T11:34:39-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-trigger", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0003", - "code": "R01" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-type", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0076", - "code": "ORU" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-timestamp", - "valueDateTime": "2025-03-23T05:27:00-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-id", - "valueString": "244556" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-data-model-version", - "valueString": "2.6" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-facility", - "valueString": "XYZ" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-client-id", - "valueString": "ZZ" - } - ] - }, - "identifier": [ - { - "system": "urn:id:extID", - "value": "123" - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "FILL", - "display": "Filler Identifier" - } - ] - }, - "value": "42863" - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "PLAC", - "display": "Placer Identifier" - } - ] - }, - "value": "2934778" - } - ], - "basedOn": [ - { - "reference": "ServiceRequest/1bfb8e66-e62a-4e7a-aac1-14bec6e26e84" - } - ], - "status": "unknown", - "code": { - "coding": [ - { - "system": "urn:id:CUSTLAB", - "code": "BARB", - "display": "Barbiturate" - } - ], - "text": "Barbiturate" - }, - "subject": { - "reference": "Patient/9f747657-3cc6-4b9a-8371-640bd017501f" - }, - "encounter": { - "reference": "Encounter/9c76de44-7755-449c-9973-75babc0e5c87" - }, - "effectiveDateTime": "2025-03-23T05:26:00-01:00", - "result": [ - { - "reference": "Observation/55774520-7f7f-434e-81c6-5c5623a48161" - } - ] - }, - "2": { - "resourceType": "DiagnosticReport", - "id": "4cdf8dfa-7817-46bf-99d2-745865d534d4", - "meta": { - "extension": [ - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-timestamp", - "valueDateTime": "2025-08-12T11:34:39-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-trigger", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0003", - "code": "R01" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-type", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0076", - "code": "ORU" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-timestamp", - "valueDateTime": "2025-03-23T05:27:00-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-id", - "valueString": "248563" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-data-model-version", - "valueString": "2.6" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-facility", - "valueString": "XYZ" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-client-id", - "valueString": "ZZ" - } - ] - }, - "identifier": [ - { - "system": "urn:id:extID", - "value": "2359876" - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "FILL", - "display": "Filler Identifier" - } - ] - }, - "value": "23507927" - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "PLAC", - "display": "Placer Identifier" - } - ] - }, - "value": "89327598" - } - ], - "basedOn": [ - { - "reference": "ServiceRequest/19552e91-cccb-43c4-833e-fbcfdfde5da1" - } - ], - "status": "unknown", - "code": { - "coding": [ - { - "system": "urn:id:CUSTLAB", - "code": "CANN", - "display": "Cannabinoide" - } - ], - "text": "Cannabinoide" - }, - "subject": { - "reference": "Patient/9f747657-3cc6-4b9a-8371-640bd017501f" - }, - "encounter": { - "reference": "Encounter/9c76de44-7755-449c-9973-75babc0e5c87" - }, - "effectiveDateTime": "2025-03-23T05:26:00-01:00", - "result": [ - { - "reference": "Observation/9907be93-7487-4168-8371-7d74b20bcfa2" - } - ] - } -} diff --git a/packages/fhir/test/resources/diagnosticReports.json.license b/packages/fhir/test/resources/diagnosticReports.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/diagnosticReports.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/diagnosticReports.test.ts b/packages/fhir/test/resources/diagnosticReports.test.ts deleted file mode 100644 index 2af97a5e..00000000 --- a/packages/fhir/test/resources/diagnosticReports.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type DiagnosticReport } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirDiagnosticReport, - type untypedDiagnosticReportSchema, -} from '../../src/index.js' - -describe('DiagnosticReport Resource', () => { - it('should validate FHIR DiagnosticReports from diagnosticReports.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/diagnosticReports.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirDiagnosticReport.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/documentManifests.json b/packages/fhir/test/resources/documentManifests.json deleted file mode 100644 index f7c480e7..00000000 --- a/packages/fhir/test/resources/documentManifests.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "0": { - "resourceType": "DocumentManifest", - "id": "manifest-example-1", - "masterIdentifier": { - "system": "http://example.org/documents", - "value": "MANIFEST-123" - }, - "status": "current", - "content": [ - { - "reference": "DocumentReference/example1" - }, - { - "reference": "DocumentReference/example2" - } - ], - "created": "2023-01-15T09:30:00Z", - "author": [ - { - "reference": "Practitioner/example" - } - ], - "subject": { - "reference": "Patient/example" - } - }, - "1": { - "resourceType": "DocumentManifest", - "id": "manifest-example-2", - "identifier": [ - { - "system": "http://hospital.example.org/manifests", - "value": "DOC-MANIFEST-456" - } - ], - "status": "current", - "type": { - "coding": [ - { - "system": "http://loinc.org", - "code": "55112-7", - "display": "Document Summary" - } - ] - }, - "content": [ - { - "reference": "DocumentReference/doc1" - } - ], - "recipient": [ - { - "reference": "Practitioner/recipient" - } - ], - "source": "http://example.org/source-system" - }, - "2": { - "resourceType": "DocumentManifest", - "id": "manifest-example-3", - "status": "superseded", - "content": [ - { - "reference": "DocumentReference/old-doc1" - }, - { - "reference": "DocumentReference/old-doc2" - }, - { - "reference": "DocumentReference/old-doc3" - } - ], - "description": "Old version of patient discharge summary documents", - "related": [ - { - "identifier": { - "system": "http://example.org/related", - "value": "REL-789" - } - } - ] - }, - "3": { - "resourceType": "DocumentManifest", - "id": "manifest-example-4", - "masterIdentifier": { - "system": "http://example.org/documents", - "value": "MANIFEST-999" - }, - "identifier": [ - { - "system": "http://example.org/alternate", - "value": "ALT-MANIFEST-999" - } - ], - "status": "current", - "type": { - "coding": [ - { - "system": "http://loinc.org", - "code": "60591-5", - "display": "Patient summary Document" - } - ] - }, - "subject": { - "reference": "Patient/example2" - }, - "created": "2023-12-01T14:00:00Z", - "author": [ - { - "reference": "Practitioner/doc1" - }, - { - "reference": "Organization/hospital" - } - ], - "recipient": [ - { - "reference": "Patient/example2" - } - ], - "source": "urn:oid:1.2.3.4.5.6.7.8.9", - "description": "Comprehensive patient summary for transfer of care", - "content": [ - { - "reference": "DocumentReference/summary-doc" - }, - { - "reference": "List/medications" - } - ], - "related": [ - { - "ref": { - "reference": "Encounter/admission-encounter" - } - } - ] - } -} diff --git a/packages/fhir/test/resources/documentManifests.json.license b/packages/fhir/test/resources/documentManifests.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/documentManifests.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/documentManifests.test.ts b/packages/fhir/test/resources/documentManifests.test.ts deleted file mode 100644 index accd7d40..00000000 --- a/packages/fhir/test/resources/documentManifests.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type DocumentManifest } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirDocumentManifest, - type untypedDocumentManifestSchema, -} from '../../src/index.js' - -describe('DocumentManifest Resource', () => { - it('should validate FHIR document manifest from documentManifests.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/documentManifests.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirDocumentManifest.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/documentReferences.json b/packages/fhir/test/resources/documentReferences.json deleted file mode 100644 index 9dea2018..00000000 --- a/packages/fhir/test/resources/documentReferences.json +++ /dev/null @@ -1,284 +0,0 @@ -{ - "0": { - "resourceType": "DocumentReference", - "id": "docref-example-1", - "masterIdentifier": { - "system": "http://example.org/documents", - "value": "DOC-12345" - }, - "status": "current", - "type": { - "coding": [ - { - "system": "http://loinc.org", - "code": "34133-9", - "display": "Summary of episode note" - } - ] - }, - "category": [ - { - "coding": [ - { - "system": "http://hl7.org/fhir/us/core/CodeSystem/us-core-documentreference-category", - "code": "clinical-note", - "display": "Clinical Note" - } - ] - } - ], - "subject": { - "reference": "Patient/example" - }, - "date": "2023-01-15T10:30:00Z", - "author": [ - { - "reference": "Practitioner/example" - } - ], - "content": [ - { - "attachment": { - "contentType": "application/pdf", - "url": "http://example.org/documents/doc-12345.pdf", - "title": "Summary of Episode Note" - } - } - ] - }, - "1": { - "resourceType": "DocumentReference", - "id": "docref-example-2", - "identifier": [ - { - "system": "http://hospital.example.org/documents", - "value": "HOSP-DOC-789" - } - ], - "status": "current", - "docStatus": "final", - "type": { - "coding": [ - { - "system": "http://loinc.org", - "code": "11488-4", - "display": "Consult note" - } - ] - }, - "subject": { - "reference": "Patient/example2" - }, - "date": "2023-06-20T14:00:00Z", - "author": [ - { - "reference": "Practitioner/specialist" - } - ], - "authenticator": { - "reference": "Practitioner/specialist" - }, - "custodian": { - "reference": "Organization/hospital" - }, - "content": [ - { - "attachment": { - "contentType": "application/pdf", - "data": "JVBERi0xLjQKJeLjz9MK", - "hash": "SGVsbG8gV29ybGQh", - "title": "Consultation Note" - }, - "format": { - "system": "http://ihe.net/fhir/ValueSet/IHE.FormatCode.codesystem", - "code": "urn:ihe:pcc:xphr:2007", - "display": "Personal Health Records" - } - } - ], - "context": { - "encounter": [ - { - "reference": "Encounter/example" - } - ], - "period": { - "start": "2023-06-20T13:00:00Z", - "end": "2023-06-20T15:00:00Z" - } - } - }, - "2": { - "resourceType": "DocumentReference", - "id": "docref-example-3", - "status": "superseded", - "type": { - "coding": [ - { - "system": "http://loinc.org", - "code": "18842-5", - "display": "Discharge summary" - } - ] - }, - "category": [ - { - "coding": [ - { - "system": "http://hl7.org/fhir/us/core/CodeSystem/us-core-documentreference-category", - "code": "clinical-note" - } - ] - } - ], - "subject": { - "reference": "Patient/example3" - }, - "author": [ - { - "reference": "Practitioner/doctor1" - } - ], - "relatesTo": [ - { - "code": "replaces", - "target": { - "reference": "DocumentReference/old-discharge-summary" - } - } - ], - "description": "Discharge summary - superseded by newer version", - "content": [ - { - "attachment": { - "contentType": "text/html", - "url": "http://example.org/documents/discharge-v1.html" - } - } - ] - }, - "3": { - "resourceType": "DocumentReference", - "id": "docref-example-4", - "masterIdentifier": { - "system": "urn:ietf:rfc:3986", - "value": "urn:oid:1.2.3.4.5.6.7.8.9" - }, - "identifier": [ - { - "system": "http://example.org/documents", - "value": "DOC-ABC-999" - } - ], - "status": "current", - "docStatus": "preliminary", - "type": { - "coding": [ - { - "system": "http://loinc.org", - "code": "57133-1", - "display": "Referral note" - } - ] - }, - "category": [ - { - "coding": [ - { - "system": "http://hl7.org/fhir/us/core/CodeSystem/us-core-documentreference-category", - "code": "clinical-note" - } - ] - } - ], - "subject": { - "reference": "Patient/example4" - }, - "date": "2023-12-10T09:00:00Z", - "author": [ - { - "reference": "Practitioner/primarycare" - } - ], - "relatesTo": [ - { - "code": "appends", - "target": { - "reference": "DocumentReference/original-referral" - } - } - ], - "description": "Referral to cardiology with additional test results", - "securityLabel": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", - "code": "R", - "display": "Restricted" - } - ] - } - ], - "content": [ - { - "attachment": { - "contentType": "application/pdf", - "url": "http://example.org/secure/referral-ABC-999.pdf", - "size": 524288, - "hash": "2jmj7l5rSw0yVb/vlWAYkK/YBwk=", - "title": "Cardiology Referral with Test Results", - "creation": "2023-12-10" - }, - "format": { - "system": "http://ihe.net/fhir/ValueSet/IHE.FormatCode.codesystem", - "code": "urn:ihe:iti:xds:2017:mimeTypeSufficient", - "display": "MimeType sufficient" - } - } - ], - "context": { - "encounter": [ - { - "reference": "Encounter/office-visit" - } - ], - "event": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "REFERRAL", - "display": "Referral" - } - ] - } - ], - "period": { - "start": "2023-12-10T08:30:00Z", - "end": "2023-12-10T09:30:00Z" - }, - "facilityType": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode", - "code": "HOSP", - "display": "Hospital" - } - ] - }, - "practiceSetting": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "394579002", - "display": "Cardiology" - } - ] - }, - "sourcePatientInfo": { - "reference": "Patient/example4" - } - } - } -} diff --git a/packages/fhir/test/resources/documentReferences.json.license b/packages/fhir/test/resources/documentReferences.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/documentReferences.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/documentReferences.test.ts b/packages/fhir/test/resources/documentReferences.test.ts deleted file mode 100644 index 368fda3d..00000000 --- a/packages/fhir/test/resources/documentReferences.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type DocumentReference } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirDocumentReference, - type untypedDocumentReferenceSchema, -} from '../../src/index.js' - -describe('DocumentReference Resource', () => { - it('should validate FHIR document reference from documentReferences.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/documentReferences.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirDocumentReference.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/domainResourceClass.test.ts b/packages/fhir/test/resources/domainResourceClass.test.ts deleted file mode 100644 index ea9ee9cd..00000000 --- a/packages/fhir/test/resources/domainResourceClass.test.ts +++ /dev/null @@ -1,151 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Patient, type Reference } from 'fhir/r4b.js' -import { FhirPatient, FhirDomainResource } from '../../src/index.js' - -describe('FhirDomainResource', () => { - describe('isReferencedBy', () => { - it('should return true for matching relative reference', () => { - const patient: Patient = { - resourceType: 'Patient', - id: '123', - active: true, - } - const fhirPatient = new FhirPatient(patient) - - const reference: Reference = { - reference: 'Patient/123', - } - - expect(fhirPatient.isReferencedBy(reference)).toBe(true) - }) - - it('should return true for reference with just the id', () => { - const patient: Patient = { - resourceType: 'Patient', - id: '123', - active: true, - } - const fhirPatient = new FhirPatient(patient) - - const reference: Reference = { - reference: '123', - } - - expect(fhirPatient.isReferencedBy(reference)).toBe(true) - }) - - it('should return true for absolute URL reference', () => { - const patient: Patient = { - resourceType: 'Patient', - id: '123', - active: true, - } - const fhirPatient = new FhirPatient(patient) - - const reference: Reference = { - reference: 'https://example.com/fhir/Patient/123', - } - - expect(fhirPatient.isReferencedBy(reference)).toBe(true) - }) - - it('should return false for non-matching reference', () => { - const patient: Patient = { - resourceType: 'Patient', - id: '123', - active: true, - } - const fhirPatient = new FhirPatient(patient) - - const reference: Reference = { - reference: 'Patient/456', - } - - expect(fhirPatient.isReferencedBy(reference)).toBe(false) - }) - - it('should return false for reference without reference string', () => { - const patient: Patient = { - resourceType: 'Patient', - id: '123', - active: true, - } - const fhirPatient = new FhirPatient(patient) - - const reference: Reference = { - display: 'John Doe', - } - - expect(fhirPatient.isReferencedBy(reference)).toBe(false) - }) - - it('should return false for resource without id', () => { - const patient: Patient = { - resourceType: 'Patient', - active: true, - } - const fhirPatient = new FhirPatient(patient) - - const reference: Reference = { - reference: 'Patient/123', - } - - expect(fhirPatient.isReferencedBy(reference)).toBe(false) - }) - - it('should return false for wrong resource type', () => { - const patient: Patient = { - resourceType: 'Patient', - id: '123', - active: true, - } - const fhirPatient = new FhirPatient(patient) - - const reference: Reference = { - reference: 'Observation/123', - } - - expect(fhirPatient.isReferencedBy(reference)).toBe(false) - }) - }) - - describe('decodeBase64Binary', () => { - it('should decode base64 string correctly', () => { - const base64 = 'SGVsbG8sIFdvcmxkIQ==' // "Hello, World!" - const decoded = FhirDomainResource.decodeBase64Binary(base64) - - expect(decoded).toBeInstanceOf(Uint8Array) - expect(decoded?.length).toBe(13) - - const decoder = new TextDecoder() - expect(decoder.decode(decoded)).toBe('Hello, World!') - }) - - it('should return undefined for undefined input', () => { - const decoded = FhirDomainResource.decodeBase64Binary(undefined) - expect(decoded).toBeUndefined() - }) - - it('should handle empty string', () => { - const decoded = FhirDomainResource.decodeBase64Binary('') - expect(decoded).toBeDefined() - expect(decoded?.length).toBe(0) - }) - - it('should decode binary data correctly', () => { - const bytes = new Uint8Array([0, 1, 2, 3, 255, 254, 253]) - const base64 = Buffer.from(bytes).toString('base64') - const decoded = FhirDomainResource.decodeBase64Binary(base64) - - expect(decoded).toBeInstanceOf(Uint8Array) - expect(Array.from(decoded ?? [])).toEqual([0, 1, 2, 3, 255, 254, 253]) - }) - }) -}) diff --git a/packages/fhir/test/resources/encounters.json b/packages/fhir/test/resources/encounters.json deleted file mode 100644 index f3843a94..00000000 --- a/packages/fhir/test/resources/encounters.json +++ /dev/null @@ -1,251 +0,0 @@ -{ - "0": { - "resourceType": "Encounter", - "id": "9c76de44-7755-449c-9973-75babc0e5c87", - "meta": { - "extension": [ - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-timestamp", - "valueDateTime": "2025-06-12T11:12:39-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-trigger", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0003", - "code": "R01" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-type", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0076", - "code": "ORU" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-timestamp", - "valueDateTime": "2025-03-23T05:27:00-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-id", - "valueString": "1456" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-data-model-version", - "valueString": "2.6" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-facility", - "valueString": "PF1" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-client-id", - "valueString": "MED-5" - } - ] - }, - "identifier": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "VN", - "display": "Visit number" - } - ] - }, - "value": "673" - } - ], - "status": "arrived", - "class": { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "IMP", - "display": "inpatient encounter" - }, - "subject": { - "reference": "Patient/d0324c0b-ffa4-4412-b96b-39aab7b5b1ee" - }, - "period": { - "start": "2025-03-21T23:12:00-01:00" - }, - "serviceProvider": { - "reference": "Organization/17937211-3d53-4ed3-9f22-6643dbd5388c" - } - }, - "1": { - "resourceType": "Encounter", - "id": "1754999694664053920.2d01a606-90f9-4c89-bd35-59f2dc8df824", - "meta": { - "extension": [ - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-timestamp", - "valueDateTime": "2025-08-12T11:19:23-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-trigger", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0003", - "code": "R01" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-type", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0076", - "code": "ORU" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-timestamp", - "valueDateTime": "2025-03-17T07:56:00-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-id", - "valueString": "167" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-data-model-version", - "valueString": "2.6" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-facility", - "valueString": "ABC" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-client-id", - "valueString": "MED-5" - } - ] - }, - "identifier": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "VN", - "display": "Visit number" - } - ] - }, - "value": "178" - } - ], - "status": "arrived", - "class": { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "IMP", - "display": "inpatient encounter" - }, - "subject": { - "reference": "Patient/987654321" - }, - "period": { - "start": "2025-03-22T23:45:00-01:00" - }, - "serviceProvider": { - "reference": "Organization/ABC" - } - }, - "3": { - "resourceType": "Encounter", - "id": "e1b23f88-3c14-4b91-bb0a-cfd088afac11", - "meta": { - "extension": [ - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-timestamp", - "valueDateTime": "2025-08-12T12:10:00-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-trigger", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0003", - "code": "R01" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-type", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0076", - "code": "ORU" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-timestamp", - "valueDateTime": "2025-03-26T09:00:00-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-id", - "valueString": "123" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-data-model-version", - "valueString": "2.6" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-facility", - "valueString": "TLK" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-client-id", - "valueString": "MED-5" - } - ] - }, - "identifier": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "VN", - "display": "Visit number" - } - ] - }, - "value": "987654321" - } - ], - "status": "in-progress", - "class": { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "AMB", - "display": "ambulatory" - }, - "subject": { - "reference": "Patient/123abc456def" - }, - "period": { - "start": "2025-03-26T08:45:00-01:00" - }, - "serviceProvider": { - "reference": "Organization/clinic-456" - } - } -} diff --git a/packages/fhir/test/resources/encounters.json.license b/packages/fhir/test/resources/encounters.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/encounters.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/encounters.test.ts b/packages/fhir/test/resources/encounters.test.ts deleted file mode 100644 index 7bd09b4c..00000000 --- a/packages/fhir/test/resources/encounters.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Encounter } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirEncounter, type untypedEncounterSchema } from '../../src/index.js' - -describe('Encounter Resource', () => { - it('should validate FHIR Encounters from encounters.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/encounters.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirEncounter.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/endpoints.json b/packages/fhir/test/resources/endpoints.json deleted file mode 100644 index 4994b272..00000000 --- a/packages/fhir/test/resources/endpoints.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "0": { - "resourceType": "Endpoint", - "id": "endpoint-example-0", - "status": "active", - "connectionType": { - "system": "http://terminology.hl7.org/CodeSystem/endpoint-connection-type", - "code": "hl7-fhir-rest" - }, - "name": "FHIR Server Endpoint", - "address": "https://fhir.example.org/api/r4", - "payloadType": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/endpoint-payload-type", - "code": "any", - "display": "Any" - } - ] - } - ] - }, - "1": { - "resourceType": "Endpoint", - "id": "endpoint-example-1", - "status": "active", - "connectionType": { - "system": "http://terminology.hl7.org/CodeSystem/endpoint-connection-type", - "code": "dicom-wado-rs" - }, - "name": "PACS WADO-RS Endpoint", - "managingOrganization": { - "reference": "Organization/org-radiology" - }, - "contact": [ - { - "system": "email", - "value": "support@example.org" - } - ], - "address": "https://pacs.example.org/wado-rs", - "payloadType": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/endpoint-payload-type", - "code": "dicom", - "display": "DICOM" - } - ] - } - ], - "payloadMimeType": ["application/dicom"] - }, - "2": { - "resourceType": "Endpoint", - "id": "endpoint-example-2", - "status": "suspended", - "connectionType": { - "system": "http://terminology.hl7.org/CodeSystem/endpoint-connection-type", - "code": "hl7-v2-mllp" - }, - "name": "Legacy HL7 v2 Endpoint", - "address": "mllp://legacy.example.org:2575", - "payloadType": [ - { - "text": "HL7 v2 messages" - } - ], - "header": ["Authorization: Bearer token123"] - }, - "3": { - "resourceType": "Endpoint", - "id": "endpoint-example-3", - "identifier": [ - { - "system": "http://example.org/endpoints", - "value": "EP-789" - } - ], - "status": "active", - "connectionType": { - "system": "http://terminology.hl7.org/CodeSystem/endpoint-connection-type", - "code": "ihe-xds" - }, - "name": "XDS Document Repository", - "managingOrganization": { - "reference": "Organization/org-health-info-exchange" - }, - "period": { - "start": "2020-01-01" - }, - "address": "https://xds.example.org/repository", - "payloadType": [ - { - "coding": [ - { - "system": "urn:ihe:iti:xds:2013", - "code": "urn:ihe:iti:xds:2013:schema:ebRIM" - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/endpoints.json.license b/packages/fhir/test/resources/endpoints.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/endpoints.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/endpoints.test.ts b/packages/fhir/test/resources/endpoints.test.ts deleted file mode 100644 index 73ef2347..00000000 --- a/packages/fhir/test/resources/endpoints.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Endpoint } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirEndpoint, type untypedEndpointSchema } from '../../src/index.js' - -describe('Endpoint Resource', () => { - it('should validate FHIR endpoint from endpoints.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/endpoints.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirEndpoint.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/enrollmentRequests.json b/packages/fhir/test/resources/enrollmentRequests.json deleted file mode 100644 index 68a5a3e6..00000000 --- a/packages/fhir/test/resources/enrollmentRequests.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "0": { - "resourceType": "EnrollmentRequest", - "id": "enrollmentrequest-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T10:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/EnrollmentRequest"] - } - }, - "1": { - "resourceType": "EnrollmentRequest", - "id": "enrollmentrequest-comprehensive", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T11:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/EnrollmentRequest"] - }, - "text": { - "status": "generated", - "div": "
Enrollment request for new coverage
" - }, - "identifier": [ - { - "system": "http://hospital.example.org/enrollment-requests", - "value": "ENR-REQ-001" - } - ], - "status": "active", - "created": "2025-01-15T09:00:00Z", - "insurer": { - "reference": "Organization/insurance-company-001", - "display": "Blue Cross Insurance" - }, - "provider": { - "reference": "Organization/hospital-001", - "display": "General Hospital" - }, - "candidate": { - "reference": "Patient/patient-001", - "display": "John Doe" - }, - "coverage": { - "reference": "Coverage/coverage-001", - "display": "Primary Health Coverage" - } - }, - "2": { - "resourceType": "EnrollmentRequest", - "id": "enrollmentrequest-draft", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T12:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/EnrollmentRequest"] - }, - "identifier": [ - { - "system": "http://hospital.example.org/enrollment-requests", - "value": "ENR-REQ-002" - } - ], - "status": "draft", - "created": "2025-01-15T11:30:00Z", - "candidate": { - "reference": "Patient/patient-002", - "display": "Sarah Johnson" - } - }, - "3": { - "resourceType": "EnrollmentRequest", - "id": "enrollmentrequest-cancelled", - "meta": { - "versionId": "2", - "lastUpdated": "2025-01-15T13:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/EnrollmentRequest"] - }, - "identifier": [ - { - "system": "http://hospital.example.org/enrollment-requests", - "value": "ENR-REQ-003" - } - ], - "status": "cancelled", - "created": "2025-01-14T10:00:00Z", - "insurer": { - "reference": "Organization/insurance-company-002", - "display": "Aetna Insurance" - }, - "candidate": { - "reference": "Patient/patient-003", - "display": "Robert Miller" - } - } -} diff --git a/packages/fhir/test/resources/enrollmentRequests.json.license b/packages/fhir/test/resources/enrollmentRequests.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/enrollmentRequests.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/enrollmentRequests.test.ts b/packages/fhir/test/resources/enrollmentRequests.test.ts deleted file mode 100644 index df1f97dd..00000000 --- a/packages/fhir/test/resources/enrollmentRequests.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type EnrollmentRequest } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirEnrollmentRequest, - type untypedEnrollmentRequestSchema, -} from '../../src/index.js' - -describe('EnrollmentRequest Resource', () => { - it('should validate FHIR EnrollmentRequests from enrollmentRequests.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/enrollmentRequests.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirEnrollmentRequest.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/enrollmentResponses.json b/packages/fhir/test/resources/enrollmentResponses.json deleted file mode 100644 index 08b8405d..00000000 --- a/packages/fhir/test/resources/enrollmentResponses.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "0": { - "resourceType": "EnrollmentResponse", - "id": "enrollmentresponse-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T10:30:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/EnrollmentResponse"] - } - }, - "1": { - "resourceType": "EnrollmentResponse", - "id": "enrollmentresponse-complete", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T11:30:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/EnrollmentResponse"] - }, - "text": { - "status": "generated", - "div": "
Enrollment response - approved
" - }, - "identifier": [ - { - "system": "http://insurance.example.org/enrollment-responses", - "value": "ENR-RESP-001" - } - ], - "status": "active", - "request": { - "reference": "EnrollmentRequest/enrollmentrequest-comprehensive", - "display": "Enrollment Request for John Doe" - }, - "outcome": "complete", - "disposition": "Enrollment approved effective 2025-02-01", - "created": "2025-01-15T11:00:00Z", - "organization": { - "reference": "Organization/insurance-company-001", - "display": "Blue Cross Insurance" - }, - "requestProvider": { - "reference": "Organization/hospital-001", - "display": "General Hospital" - } - }, - "2": { - "resourceType": "EnrollmentResponse", - "id": "enrollmentresponse-error", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T12:30:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/EnrollmentResponse"] - }, - "identifier": [ - { - "system": "http://insurance.example.org/enrollment-responses", - "value": "ENR-RESP-002" - } - ], - "status": "active", - "request": { - "reference": "EnrollmentRequest/enrollmentrequest-002" - }, - "outcome": "error", - "disposition": "Missing required documentation - birth certificate", - "created": "2025-01-15T12:00:00Z", - "organization": { - "reference": "Organization/insurance-company-002", - "display": "Aetna Insurance" - } - }, - "3": { - "resourceType": "EnrollmentResponse", - "id": "enrollmentresponse-queued", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T13:30:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/EnrollmentResponse"] - }, - "identifier": [ - { - "system": "http://insurance.example.org/enrollment-responses", - "value": "ENR-RESP-003" - } - ], - "status": "active", - "request": { - "reference": "EnrollmentRequest/enrollmentrequest-003" - }, - "outcome": "queued", - "disposition": "Application is under review - pending background check", - "created": "2025-01-15T13:00:00Z", - "organization": { - "reference": "Organization/insurance-company-003", - "display": "UnitedHealth Group" - } - } -} diff --git a/packages/fhir/test/resources/enrollmentResponses.json.license b/packages/fhir/test/resources/enrollmentResponses.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/enrollmentResponses.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/enrollmentResponses.test.ts b/packages/fhir/test/resources/enrollmentResponses.test.ts deleted file mode 100644 index 4ac561d6..00000000 --- a/packages/fhir/test/resources/enrollmentResponses.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type EnrollmentResponse } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirEnrollmentResponse, - type untypedEnrollmentResponseSchema, -} from '../../src/index.js' - -describe('EnrollmentResponse Resource', () => { - it('should validate FHIR EnrollmentResponses from enrollmentResponses.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/enrollmentResponses.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirEnrollmentResponse.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/episodeOfCares.json b/packages/fhir/test/resources/episodeOfCares.json deleted file mode 100644 index f6f9771b..00000000 --- a/packages/fhir/test/resources/episodeOfCares.json +++ /dev/null @@ -1,127 +0,0 @@ -{ - "0": { - "resourceType": "EpisodeOfCare", - "id": "episode-example-0", - "status": "active", - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/episodeofcare-type", - "code": "hacc", - "display": "Home and Community Care" - } - ] - } - ], - "patient": { - "reference": "Patient/patient-example" - }, - "period": { - "start": "2024-01-15" - } - }, - "1": { - "resourceType": "EpisodeOfCare", - "id": "episode-example-1", - "identifier": [ - { - "system": "http://example.org/episodes", - "value": "E12345" - } - ], - "status": "finished", - "statusHistory": [ - { - "status": "active", - "period": { - "start": "2023-06-01", - "end": "2024-06-01" - } - } - ], - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/episodeofcare-type", - "code": "diab", - "display": "Diabetes management" - } - ] - } - ], - "diagnosis": [ - { - "condition": { - "reference": "Condition/condition-diabetes" - }, - "role": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/diagnosis-role", - "code": "CC", - "display": "Chief complaint" - } - ] - }, - "rank": 1 - } - ], - "patient": { - "reference": "Patient/patient-example-2" - }, - "managingOrganization": { - "reference": "Organization/org-endocrinology" - }, - "period": { - "start": "2023-06-01", - "end": "2024-06-01" - } - }, - "2": { - "resourceType": "EpisodeOfCare", - "id": "episode-example-2", - "status": "waitlist", - "patient": { - "reference": "Patient/patient-example-3" - }, - "careManager": { - "reference": "Practitioner/practitioner-example" - }, - "period": { - "start": "2024-11-01" - } - }, - "3": { - "resourceType": "EpisodeOfCare", - "id": "episode-example-3", - "status": "cancelled", - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/episodeofcare-type", - "code": "pac", - "display": "Post Acute Care" - } - ] - } - ], - "patient": { - "reference": "Patient/patient-xyz" - }, - "managingOrganization": { - "reference": "Organization/org-rehab" - }, - "period": { - "start": "2024-09-01", - "end": "2024-09-05" - }, - "referralRequest": [ - { - "reference": "ServiceRequest/referral-123" - } - ] - } -} diff --git a/packages/fhir/test/resources/episodeOfCares.json.license b/packages/fhir/test/resources/episodeOfCares.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/episodeOfCares.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/episodeOfCares.test.ts b/packages/fhir/test/resources/episodeOfCares.test.ts deleted file mode 100644 index 58ac1727..00000000 --- a/packages/fhir/test/resources/episodeOfCares.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type EpisodeOfCare } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirEpisodeOfCare, - type untypedEpisodeOfCareSchema, -} from '../../src/index.js' - -describe('EpisodeOfCare Resource', () => { - it('should validate FHIR episodeOfCare from episodeOfCares.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/episodeOfCares.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirEpisodeOfCare.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/eventDefinitions.json b/packages/fhir/test/resources/eventDefinitions.json deleted file mode 100644 index 9b77b84e..00000000 --- a/packages/fhir/test/resources/eventDefinitions.json +++ /dev/null @@ -1,153 +0,0 @@ -{ - "0": { - "resourceType": "EventDefinition", - "id": "eventdefinition-example-0", - "status": "draft", - "name": "AdmissionEvent", - "title": "Patient Admission Event", - "description": "Event triggered when a patient is admitted to the hospital", - "trigger": [ - { - "type": "named-event", - "name": "patient-admission" - } - ] - }, - "1": { - "resourceType": "EventDefinition", - "id": "eventdefinition-example-1", - "url": "http://example.org/EventDefinition/medication-dispensed", - "identifier": [ - { - "system": "http://example.org/identifiers", - "value": "ED-001" - } - ], - "version": "1.0.0", - "name": "MedicationDispensedEvent", - "title": "Medication Dispensed Event Definition", - "subtitle": "Triggered when medication is dispensed to patient", - "status": "active", - "experimental": false, - "date": "2024-02-10T15:30:00Z", - "publisher": "Example Pharmacy System", - "contact": [ - { - "name": "Pharmacy Operations", - "telecom": [ - { - "system": "email", - "value": "pharmacy@example.org" - } - ] - } - ], - "description": "This event definition describes when a medication is dispensed to a patient", - "purpose": "To enable automated workflows when medications are dispensed", - "copyright": "Copyright 2024 Example Healthcare Organization", - "trigger": [ - { - "type": "data-changed", - "name": "medication-dispensed" - } - ] - }, - "2": { - "resourceType": "EventDefinition", - "id": "eventdefinition-example-2", - "status": "active", - "name": "LabResultAvailable", - "title": "Laboratory Result Available Event", - "description": "Event triggered when lab results become available", - "subjectCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/resource-types", - "code": "Patient", - "display": "Patient" - } - ] - }, - "topic": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/definition-topic", - "code": "assessment", - "display": "Assessment" - } - ] - } - ], - "trigger": [ - { - "type": "data-added", - "name": "lab-result-available" - } - ] - }, - "3": { - "resourceType": "EventDefinition", - "id": "eventdefinition-example-3", - "url": "http://example.org/EventDefinition/appointment-reminder", - "version": "2.0.0", - "name": "AppointmentReminderEvent", - "title": "Appointment Reminder Event", - "status": "active", - "date": "2024-07-15T09:00:00Z", - "publisher": "Scheduling Department", - "description": "Event definition for sending appointment reminders", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "approvalDate": "2024-07-01", - "lastReviewDate": "2024-10-15", - "effectivePeriod": { - "start": "2024-07-15", - "end": "2025-07-15" - }, - "topic": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/definition-topic", - "code": "treatment", - "display": "Treatment" - } - ] - } - ], - "author": [ - { - "name": "Scheduling Team", - "telecom": [ - { - "system": "email", - "value": "scheduling@example.org" - } - ] - } - ], - "trigger": [ - { - "type": "periodic", - "name": "appointment-reminder-24h", - "timingTiming": { - "repeat": { - "frequency": 1, - "period": 24, - "periodUnit": "h" - } - } - } - ] - } -} diff --git a/packages/fhir/test/resources/eventDefinitions.json.license b/packages/fhir/test/resources/eventDefinitions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/eventDefinitions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/eventDefinitions.test.ts b/packages/fhir/test/resources/eventDefinitions.test.ts deleted file mode 100644 index b852e07b..00000000 --- a/packages/fhir/test/resources/eventDefinitions.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type EventDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirEventDefinition, - type untypedEventDefinitionSchema, -} from '../../src/index.js' - -describe('EventDefinition Resource', () => { - it('should validate FHIR event definitions from eventDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/eventDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirEventDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/evidenceReports.json b/packages/fhir/test/resources/evidenceReports.json deleted file mode 100644 index f410018e..00000000 --- a/packages/fhir/test/resources/evidenceReports.json +++ /dev/null @@ -1,207 +0,0 @@ -{ - "0": { - "resourceType": "EvidenceReport", - "id": "evidencereport-example-0", - "status": "draft", - "subject": { - "note": [ - { - "text": "Example evidence report subject" - } - ] - } - }, - "1": { - "resourceType": "EvidenceReport", - "id": "evidencereport-example-1", - "url": "http://example.org/fhir/EvidenceReport/diabetes-evidence-summary", - "identifier": [ - { - "system": "http://example.org/evidence-reports", - "value": "ER-001" - } - ], - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/evidence-report-type", - "code": "classification" - } - ] - }, - "publisher": "Example Evidence Research Group", - "subject": { - "characteristic": [ - { - "code": { - "text": "Population" - }, - "valueCodeableConcept": { - "text": "Adult patients with Type 2 Diabetes" - } - } - ] - }, - "section": [ - { - "title": "Summary", - "focus": { - "text": "Evidence Summary" - } - } - ] - }, - "2": { - "resourceType": "EvidenceReport", - "id": "evidencereport-example-2", - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/evidence-report-type", - "code": "search-results" - } - ] - }, - "publisher": "Research Institute", - "contact": [ - { - "name": "Evidence Team" - } - ], - "subject": { - "characteristic": [ - { - "code": { - "text": "Search Criteria" - }, - "valueCodeableConcept": { - "text": "Cardiovascular disease treatment" - } - } - ], - "note": [ - { - "text": "Systematic review search results" - } - ] - }, - "section": [ - { - "title": "Included Studies", - "focus": { - "text": "Study Selection" - } - }, - { - "title": "Results", - "focus": { - "text": "Evidence Synthesis" - } - } - ] - }, - "3": { - "resourceType": "EvidenceReport", - "id": "evidencereport-example-3", - "url": "http://example.org/fhir/EvidenceReport/cancer-treatment-evidence", - "identifier": [ - { - "system": "http://example.org/evidence-reports", - "value": "ER-CANCER-2024" - } - ], - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/evidence-report-type", - "code": "resources-compiled" - } - ] - }, - "publisher": "Oncology Evidence Network", - "contact": [ - { - "name": "Dr. Evidence Lead" - } - ], - "author": [ - { - "name": "Research Committee" - } - ], - "subject": { - "characteristic": [ - { - "code": { - "text": "Condition" - }, - "valueCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "363346000", - "display": "Malignant neoplastic disease" - } - ] - } - }, - { - "code": { - "text": "Intervention" - }, - "valueCodeableConcept": { - "text": "Immunotherapy" - } - } - ], - "note": [ - { - "text": "Comprehensive evidence report on cancer immunotherapy effectiveness" - } - ] - }, - "relatedArtifact": [ - { - "type": "citation", - "citation": "Key study reference" - } - ], - "section": [ - { - "title": "Background", - "focus": { - "text": "Clinical Context" - } - }, - { - "title": "Evidence Synthesis", - "focus": { - "text": "Main Findings" - }, - "section": [ - { - "title": "Efficacy", - "focus": { - "text": "Treatment Efficacy" - } - }, - { - "title": "Safety", - "focus": { - "text": "Adverse Events" - } - } - ] - }, - { - "title": "Conclusions", - "focus": { - "text": "Summary and Recommendations" - } - } - ] - } -} diff --git a/packages/fhir/test/resources/evidenceReports.test.ts b/packages/fhir/test/resources/evidenceReports.test.ts deleted file mode 100644 index da413419..00000000 --- a/packages/fhir/test/resources/evidenceReports.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type EvidenceReport } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirEvidenceReport, - type untypedEvidenceReportSchema, -} from '../../src/index.js' - -describe('EvidenceReport Resource', () => { - it('should validate FHIR evidence reports from evidenceReports.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/evidenceReports.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirEvidenceReport.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/evidenceVariables.json b/packages/fhir/test/resources/evidenceVariables.json deleted file mode 100644 index 604ef288..00000000 --- a/packages/fhir/test/resources/evidenceVariables.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "0": { - "resourceType": "EvidenceVariable", - "id": "evidencevariable-example-0", - "status": "draft", - "title": "Example Evidence Variable" - }, - "1": { - "resourceType": "EvidenceVariable", - "id": "evidencevariable-example-1", - "url": "http://example.org/fhir/EvidenceVariable/age-variable", - "identifier": [ - { - "system": "http://example.org/evidence-variables", - "value": "EV-001" - } - ], - "version": "1.0.0", - "name": "AgeVariable", - "title": "Patient Age Variable", - "status": "active", - "description": "Definition of patient age as an evidence variable", - "handling": "continuous", - "characteristic": [ - { - "description": "Patient age in years", - "definitionCodeableConcept": { - "coding": [ - { - "system": "http://loinc.org", - "code": "30525-0", - "display": "Age" - } - ] - } - } - ] - }, - "2": { - "resourceType": "EvidenceVariable", - "id": "evidencevariable-example-2", - "status": "active", - "title": "Blood Pressure Variable", - "handling": "continuous", - "characteristic": [ - { - "description": "Systolic blood pressure measurement", - "definitionCodeableConcept": { - "text": "Systolic Blood Pressure" - } - } - ], - "category": [ - { - "name": "Normal", - "valueRange": { - "low": { - "value": 90, - "unit": "mmHg" - }, - "high": { - "value": 120, - "unit": "mmHg" - } - } - }, - { - "name": "Hypertension", - "valueRange": { - "low": { - "value": 140, - "unit": "mmHg" - } - } - } - ] - }, - "3": { - "resourceType": "EvidenceVariable", - "id": "evidencevariable-example-3", - "url": "http://example.org/fhir/EvidenceVariable/smoking-status", - "name": "SmokingStatusVariable", - "title": "Smoking Status Evidence Variable", - "status": "active", - "publisher": "Example Health Research Organization", - "contact": [ - { - "name": "Research Data Team" - } - ], - "description": "Evidence variable for patient smoking status in clinical research", - "handling": "dichotomous", - "actual": true, - "characteristic": [ - { - "description": "Current smoking status", - "definitionCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "77176002", - "display": "Smoker" - } - ] - } - } - ], - "category": [ - { - "name": "Smoker", - "valueCodeableConcept": { - "text": "Current smoker" - } - }, - { - "name": "Non-smoker", - "valueCodeableConcept": { - "text": "Non-smoker" - } - } - ] - } -} diff --git a/packages/fhir/test/resources/evidenceVariables.test.ts b/packages/fhir/test/resources/evidenceVariables.test.ts deleted file mode 100644 index e728d79a..00000000 --- a/packages/fhir/test/resources/evidenceVariables.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type EvidenceVariable } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirEvidenceVariable, - type untypedEvidenceVariableSchema, -} from '../../src/index.js' - -describe('EvidenceVariable Resource', () => { - it('should validate FHIR evidence variables from evidenceVariables.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/evidenceVariables.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirEvidenceVariable.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/evidences.json b/packages/fhir/test/resources/evidences.json deleted file mode 100644 index 8fa18901..00000000 --- a/packages/fhir/test/resources/evidences.json +++ /dev/null @@ -1,193 +0,0 @@ -{ - "0": { - "resourceType": "Evidence", - "id": "evidence-example-0", - "status": "draft", - "variableDefinition": [ - { - "variableRole": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/variable-role", - "code": "population" - } - ] - }, - "observed": { - "reference": "EvidenceVariable/population-001" - } - } - ] - }, - "1": { - "resourceType": "Evidence", - "id": "evidence-example-1", - "url": "http://example.org/fhir/Evidence/diabetes-treatment-evidence", - "identifier": [ - { - "system": "http://example.org/evidence", - "value": "EVD-001" - } - ], - "version": "1.0.0", - "title": "Evidence for Diabetes Treatment Effectiveness", - "status": "active", - "description": "Evidence supporting the effectiveness of new diabetes treatment", - "variableDefinition": [ - { - "variableRole": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/variable-role", - "code": "population" - } - ] - }, - "observed": { - "reference": "EvidenceVariable/diabetes-population" - } - }, - { - "variableRole": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/variable-role", - "code": "exposure" - } - ] - }, - "observed": { - "reference": "EvidenceVariable/diabetes-treatment" - } - } - ] - }, - "2": { - "resourceType": "Evidence", - "id": "evidence-example-2", - "status": "active", - "title": "Blood Pressure Reduction Evidence", - "publisher": "Example Research Organization", - "description": "Evidence showing blood pressure reduction with intervention", - "variableDefinition": [ - { - "variableRole": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/variable-role", - "code": "population" - } - ] - }, - "observed": { - "reference": "EvidenceVariable/hypertensive-population" - } - }, - { - "variableRole": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/variable-role", - "code": "outcome" - } - ] - }, - "observed": { - "reference": "EvidenceVariable/blood-pressure-outcome" - } - } - ], - "statistic": [ - { - "description": "Mean blood pressure reduction", - "statisticType": { - "text": "Mean Difference" - }, - "quantity": { - "value": -10.5, - "unit": "mmHg" - } - } - ] - }, - "3": { - "resourceType": "Evidence", - "id": "evidence-example-3", - "url": "http://example.org/fhir/Evidence/cancer-survival", - "title": "Cancer Treatment Survival Evidence", - "status": "active", - "publisher": "Oncology Research Institute", - "contact": [ - { - "name": "Research Team" - } - ], - "description": "Evidence for improved survival with new cancer treatment", - "assertion": "The new treatment improves 5-year survival by 15%", - "variableDefinition": [ - { - "description": "Cancer patient population", - "variableRole": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/variable-role", - "code": "population" - } - ] - }, - "observed": { - "reference": "EvidenceVariable/cancer-population" - } - }, - { - "description": "New treatment intervention", - "variableRole": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/variable-role", - "code": "exposure" - } - ] - }, - "observed": { - "reference": "EvidenceVariable/new-treatment" - } - }, - { - "description": "5-year survival outcome", - "variableRole": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/variable-role", - "code": "outcome" - } - ] - }, - "observed": { - "reference": "EvidenceVariable/survival-outcome" - } - } - ], - "synthesisType": { - "text": "Meta-analysis" - }, - "studyType": { - "text": "Randomized Controlled Trial" - }, - "statistic": [ - { - "description": "Overall survival improvement", - "statisticType": { - "text": "Hazard Ratio" - }, - "quantity": { - "value": 0.75 - }, - "sampleSize": { - "numberOfStudies": 5, - "numberOfParticipants": 1250 - } - } - ] - } -} diff --git a/packages/fhir/test/resources/evidences.test.ts b/packages/fhir/test/resources/evidences.test.ts deleted file mode 100644 index 32f0247c..00000000 --- a/packages/fhir/test/resources/evidences.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Evidence } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirEvidence, type untypedEvidenceSchema } from '../../src/index.js' - -describe('Evidence Resource', () => { - it('should validate FHIR evidences from evidences.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/evidences.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirEvidence.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/exampleScenarios.json b/packages/fhir/test/resources/exampleScenarios.json deleted file mode 100644 index 421d93eb..00000000 --- a/packages/fhir/test/resources/exampleScenarios.json +++ /dev/null @@ -1,228 +0,0 @@ -{ - "0": { - "resourceType": "ExampleScenario", - "id": "example", - "status": "draft", - "actor": [ - { - "actorId": "client", - "type": "entity", - "name": "Client Application" - }, - { - "actorId": "server", - "type": "entity", - "name": "FHIR Server" - } - ], - "instance": [ - { - "resourceId": "patient1", - "resourceType": "Patient", - "name": "Test Patient" - } - ], - "process": [ - { - "title": "Patient Registration", - "step": [ - { - "operation": { - "number": "1", - "name": "Create Patient", - "initiator": "client", - "receiver": "server" - } - } - ] - } - ] - }, - "1": { - "resourceType": "ExampleScenario", - "id": "medication-workflow", - "status": "active", - "name": "MedicationWorkflow", - "experimental": false, - "date": "2023-05-15T10:00:00Z", - "actor": [ - { - "actorId": "prescriber", - "type": "person", - "name": "Prescribing Physician", - "description": "The physician who prescribes medication" - }, - { - "actorId": "pharmacy", - "type": "entity", - "name": "Pharmacy System" - }, - { - "actorId": "ehr", - "type": "entity", - "name": "Electronic Health Record" - } - ], - "instance": [ - { - "resourceId": "med-request-1", - "resourceType": "MedicationRequest", - "name": "Prescription", - "description": "A prescription for antibiotics" - }, - { - "resourceId": "patient-123", - "resourceType": "Patient", - "name": "John Doe" - } - ], - "process": [ - { - "title": "Prescribe and Dispense Medication", - "preConditions": "Patient has been diagnosed", - "postConditions": "Medication has been dispensed", - "step": [ - { - "operation": { - "number": "1.0", - "name": "Create Prescription", - "initiator": "prescriber", - "receiver": "ehr", - "initiatorActive": true, - "receiverActive": true - } - }, - { - "operation": { - "number": "2.0", - "name": "Send to Pharmacy", - "initiator": "ehr", - "receiver": "pharmacy" - } - } - ] - } - ] - }, - "2": { - "resourceType": "ExampleScenario", - "id": "nested-workflow", - "status": "draft", - "actor": [ - { - "actorId": "app", - "type": "entity", - "name": "Mobile App" - } - ], - "instance": [ - { - "resourceId": "obs-1", - "resourceType": "Observation", - "name": "Blood Pressure", - "version": [ - { - "versionId": "v1", - "description": "Initial reading" - } - ], - "containedInstance": [ - { - "resourceId": "patient-1" - } - ] - } - ], - "process": [ - { - "title": "Main Process", - "step": [ - { - "process": [ - { - "title": "Sub-Process", - "step": [ - { - "operation": { - "number": "1.1", - "name": "Sub-operation", - "initiator": "app", - "receiver": "app" - } - } - ] - } - ] - } - ] - } - ] - }, - "3": { - "resourceType": "ExampleScenario", - "id": "alternative-paths", - "status": "active", - "actor": [ - { - "actorId": "system", - "type": "entity", - "name": "Healthcare System" - } - ], - "instance": [ - { - "resourceId": "appointment-1", - "resourceType": "Appointment", - "name": "Scheduled Appointment" - } - ], - "process": [ - { - "title": "Appointment Scheduling", - "step": [ - { - "operation": { - "number": "1", - "name": "Check Availability", - "initiator": "system", - "receiver": "system" - } - }, - { - "alternative": [ - { - "title": "Slot Available", - "description": "When a time slot is available", - "step": [ - { - "operation": { - "number": "2a", - "name": "Book Appointment", - "initiator": "system", - "receiver": "system" - } - } - ] - }, - { - "title": "Slot Not Available", - "description": "When no time slot is available", - "step": [ - { - "operation": { - "number": "2b", - "name": "Suggest Alternative", - "initiator": "system", - "receiver": "system" - } - } - ] - } - ] - } - ] - } - ], - "workflow": ["http://example.org/fhir/ExampleScenario/related-workflow"] - } -} diff --git a/packages/fhir/test/resources/exampleScenarios.json.license b/packages/fhir/test/resources/exampleScenarios.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/exampleScenarios.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/exampleScenarios.test.ts b/packages/fhir/test/resources/exampleScenarios.test.ts deleted file mode 100644 index c4ac34f2..00000000 --- a/packages/fhir/test/resources/exampleScenarios.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ExampleScenario } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirExampleScenario, - type untypedExampleScenarioSchema, -} from '../../src/index.js' - -describe('ExampleScenario Resource', () => { - it('should validate FHIR example scenario from exampleScenarios.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/exampleScenarios.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirExampleScenario.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/explanationOfBenefits.json b/packages/fhir/test/resources/explanationOfBenefits.json deleted file mode 100644 index df7e04bb..00000000 --- a/packages/fhir/test/resources/explanationOfBenefits.json +++ /dev/null @@ -1,196 +0,0 @@ -{ - "0": { - "resourceType": "ExplanationOfBenefit", - "id": "explanationofbenefit-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T10:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/ExplanationOfBenefit" - ] - }, - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/claim-type", - "code": "institutional" - } - ] - }, - "use": "claim", - "patient": { - "reference": "Patient/patient-001" - }, - "created": "2025-01-15T10:00:00Z", - "insurer": { - "reference": "Organization/insurance-001" - }, - "provider": { - "reference": "Organization/hospital-001" - }, - "outcome": "complete", - "insurance": [ - { - "focal": true, - "coverage": { - "reference": "Coverage/coverage-001" - } - } - ] - }, - "1": { - "resourceType": "ExplanationOfBenefit", - "id": "explanationofbenefit-comprehensive", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T11:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/ExplanationOfBenefit" - ] - }, - "identifier": [ - { - "system": "http://insurance.example.org/eobs", - "value": "EOB-001" - } - ], - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/claim-type", - "code": "professional" - } - ] - }, - "use": "claim", - "patient": { - "reference": "Patient/patient-002" - }, - "billablePeriod": { - "start": "2025-01-10", - "end": "2025-01-12" - }, - "created": "2025-01-15T11:00:00Z", - "insurer": { - "reference": "Organization/insurance-001" - }, - "provider": { - "reference": "Organization/hospital-001" - }, - "claim": { - "reference": "Claim/claim-001" - }, - "claimResponse": { - "reference": "ClaimResponse/claimresponse-001" - }, - "outcome": "complete", - "disposition": "Claim processed and benefits determined", - "insurance": [ - { - "focal": true, - "coverage": { - "reference": "Coverage/coverage-002" - } - } - ], - "total": [ - { - "category": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/adjudication", - "code": "submitted" - } - ] - }, - "amount": { - "value": 1000.0, - "currency": "USD" - } - } - ] - }, - "2": { - "resourceType": "ExplanationOfBenefit", - "id": "explanationofbenefit-draft", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T12:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/ExplanationOfBenefit" - ] - }, - "status": "draft", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/claim-type", - "code": "pharmacy" - } - ] - }, - "use": "claim", - "patient": { - "reference": "Patient/patient-003" - }, - "created": "2025-01-15T12:00:00Z", - "insurer": { - "reference": "Organization/insurance-002" - }, - "provider": { - "reference": "Organization/pharmacy-001" - }, - "outcome": "queued", - "insurance": [ - { - "focal": true, - "coverage": { - "reference": "Coverage/coverage-003" - } - } - ] - }, - "3": { - "resourceType": "ExplanationOfBenefit", - "id": "explanationofbenefit-error", - "meta": { - "versionId": "2", - "lastUpdated": "2025-01-15T13:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/ExplanationOfBenefit" - ] - }, - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/claim-type", - "code": "professional" - } - ] - }, - "use": "claim", - "patient": { - "reference": "Patient/patient-004" - }, - "created": "2025-01-15T13:00:00Z", - "insurer": { - "reference": "Organization/insurance-001" - }, - "provider": { - "reference": "Organization/hospital-002" - }, - "outcome": "error", - "disposition": "Coverage is not active for the service period", - "insurance": [ - { - "focal": true, - "coverage": { - "reference": "Coverage/coverage-004" - } - } - ] - } -} diff --git a/packages/fhir/test/resources/explanationOfBenefits.json.license b/packages/fhir/test/resources/explanationOfBenefits.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/explanationOfBenefits.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/explanationOfBenefits.test.ts b/packages/fhir/test/resources/explanationOfBenefits.test.ts deleted file mode 100644 index 286eb77d..00000000 --- a/packages/fhir/test/resources/explanationOfBenefits.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ExplanationOfBenefit } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirExplanationOfBenefit, - type untypedExplanationOfBenefitSchema, -} from '../../src/index.js' - -describe('ExplanationOfBenefit Resource', () => { - it('should validate FHIR ExplanationOfBenefits from explanationOfBenefits.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/explanationOfBenefits.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirExplanationOfBenefit.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/familyMemberHistories.json b/packages/fhir/test/resources/familyMemberHistories.json deleted file mode 100644 index ba24e289..00000000 --- a/packages/fhir/test/resources/familyMemberHistories.json +++ /dev/null @@ -1,241 +0,0 @@ -{ - "0": { - "resourceType": "FamilyMemberHistory", - "id": "example-0", - "identifier": [ - { - "system": "http://example.org/family-history-identifiers", - "value": "FMH-001" - } - ], - "status": "completed", - "patient": { - "reference": "Patient/patient-001", - "display": "John Doe" - }, - "date": "2023-06-15T10:30:00Z", - "relationship": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode", - "code": "FTH", - "display": "father" - } - ] - }, - "sex": { - "coding": [ - { - "system": "http://hl7.org/fhir/administrative-gender", - "code": "male", - "display": "Male" - } - ] - }, - "deceasedAge": { - "value": 74, - "unit": "years", - "system": "http://unitsofmeasure.org", - "code": "a" - }, - "condition": [ - { - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "22298006", - "display": "Myocardial infarction" - } - ] - }, - "onsetAge": { - "value": 65, - "unit": "years", - "system": "http://unitsofmeasure.org", - "code": "a" - }, - "contributedToDeath": true - } - ] - }, - "1": { - "resourceType": "FamilyMemberHistory", - "id": "example-1", - "status": "partial", - "patient": { - "reference": "Patient/patient-002" - }, - "relationship": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode", - "code": "MTH", - "display": "mother" - } - ] - }, - "sex": { - "coding": [ - { - "system": "http://hl7.org/fhir/administrative-gender", - "code": "female", - "display": "Female" - } - ] - }, - "ageAge": { - "value": 68, - "unit": "years", - "system": "http://unitsofmeasure.org", - "code": "a" - }, - "condition": [ - { - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "44054006", - "display": "Diabetes mellitus type 2" - } - ] - }, - "onsetAge": { - "value": 50, - "unit": "years", - "system": "http://unitsofmeasure.org", - "code": "a" - }, - "note": [ - { - "text": "Managed with oral medications" - } - ] - }, - { - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "38341003", - "display": "Hypertensive disorder" - } - ] - }, - "onsetAge": { - "value": 45, - "unit": "years", - "system": "http://unitsofmeasure.org", - "code": "a" - } - } - ] - }, - "2": { - "resourceType": "FamilyMemberHistory", - "id": "example-2", - "status": "completed", - "patient": { - "reference": "Patient/patient-003", - "display": "Jane Smith" - }, - "date": "2024-02-10", - "name": "Aunt Susan", - "relationship": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode", - "code": "AUNT", - "display": "aunt" - } - ] - }, - "sex": { - "coding": [ - { - "system": "http://hl7.org/fhir/administrative-gender", - "code": "female", - "display": "Female" - } - ] - }, - "bornDate": "1955-03-12", - "deceasedBoolean": false, - "ageAge": { - "value": 69, - "unit": "years", - "system": "http://unitsofmeasure.org", - "code": "a" - }, - "condition": [ - { - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "363346000", - "display": "Malignant neoplastic disease" - } - ], - "text": "Breast cancer" - }, - "outcome": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "370996005", - "display": "Patient condition improved" - } - ] - }, - "onsetAge": { - "value": 55, - "unit": "years", - "system": "http://unitsofmeasure.org", - "code": "a" - } - } - ] - }, - "3": { - "resourceType": "FamilyMemberHistory", - "id": "example-3", - "status": "health-unknown", - "patient": { - "reference": "Patient/patient-004" - }, - "relationship": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode", - "code": "BRO", - "display": "brother" - } - ] - }, - "sex": { - "coding": [ - { - "system": "http://hl7.org/fhir/administrative-gender", - "code": "male", - "display": "Male" - } - ] - }, - "dataAbsentReason": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/history-absent-reason", - "code": "subject-unknown", - "display": "Subject Unknown" - } - ] - }, - "note": [ - { - "text": "Patient does not know brother's health history" - } - ] - } -} diff --git a/packages/fhir/test/resources/familyMemberHistories.json.license b/packages/fhir/test/resources/familyMemberHistories.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/familyMemberHistories.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/familyMemberHistories.test.ts b/packages/fhir/test/resources/familyMemberHistories.test.ts deleted file mode 100644 index 1b62f0cd..00000000 --- a/packages/fhir/test/resources/familyMemberHistories.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type FamilyMemberHistory } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirFamilyMemberHistory, - type untypedFamilyMemberHistorySchema, -} from '../../src/index.js' - -describe('FamilyMemberHistory Resource', () => { - it('should validate FHIR FamilyMemberHistories from familyMemberHistories.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/familyMemberHistories.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirFamilyMemberHistory.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/flags.json b/packages/fhir/test/resources/flags.json deleted file mode 100644 index a547fb15..00000000 --- a/packages/fhir/test/resources/flags.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "0": { - "resourceType": "Flag", - "id": "flag-example-0", - "status": "active", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/flag-category", - "code": "safety", - "display": "Safety" - } - ] - } - ], - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/flag-code", - "code": "drug-allergy", - "display": "Drug allergy" - } - ], - "text": "Penicillin allergy" - }, - "subject": { - "reference": "Patient/patient-example" - }, - "period": { - "start": "2024-01-01" - }, - "author": { - "reference": "Practitioner/practitioner-example" - } - }, - "1": { - "resourceType": "Flag", - "id": "flag-example-1", - "status": "inactive", - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/flag-code", - "code": "other" - } - ], - "text": "Fall risk" - }, - "subject": { - "reference": "Patient/patient-example-2" - }, - "period": { - "start": "2023-06-01", - "end": "2024-01-01" - } - }, - "2": { - "resourceType": "Flag", - "id": "flag-example-2", - "status": "active", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/flag-category", - "code": "clinical" - } - ] - } - ], - "code": { - "text": "Latex allergy" - }, - "subject": { - "reference": "Patient/patient-example-3" - }, - "encounter": { - "reference": "Encounter/encounter-example" - } - }, - "3": { - "resourceType": "Flag", - "id": "flag-example-3", - "status": "entered-in-error", - "code": { - "text": "Do not resuscitate" - }, - "subject": { - "reference": "Patient/patient-example-4" - } - } -} diff --git a/packages/fhir/test/resources/flags.json.license b/packages/fhir/test/resources/flags.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/flags.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/flags.test.ts b/packages/fhir/test/resources/flags.test.ts deleted file mode 100644 index 0d5b2aa2..00000000 --- a/packages/fhir/test/resources/flags.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Flag } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirFlag, type untypedFlagSchema } from '../../src/index.js' - -describe('Flag Resource', () => { - it('should validate FHIR Flags from flags.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/flags.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirFlag.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/goals.json b/packages/fhir/test/resources/goals.json deleted file mode 100644 index 78233cbf..00000000 --- a/packages/fhir/test/resources/goals.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "0": { - "resourceType": "Goal", - "id": "goal-example-0", - "lifecycleStatus": "active", - "description": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "289169006", - "display": "Weight loss" - } - ], - "text": "Target weight loss of 20 pounds" - }, - "subject": { - "reference": "Patient/patient-example" - }, - "startDate": "2024-01-01", - "target": [ - { - "measure": { - "coding": [ - { - "system": "http://loinc.org", - "code": "29463-7", - "display": "Body weight" - } - ] - }, - "detailQuantity": { - "value": 180, - "unit": "lbs", - "system": "http://unitsofmeasure.org", - "code": "[lb_av]" - }, - "dueDate": "2024-12-31" - } - ] - }, - "1": { - "resourceType": "Goal", - "id": "goal-example-1", - "lifecycleStatus": "proposed", - "description": { - "text": "Improve blood glucose control" - }, - "subject": { - "reference": "Patient/patient-example-2" - }, - "priority": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/goal-priority", - "code": "high-priority" - } - ] - } - }, - "2": { - "resourceType": "Goal", - "id": "goal-example-2", - "lifecycleStatus": "completed", - "description": { - "text": "Smoking cessation" - }, - "subject": { - "reference": "Patient/patient-example-3" - }, - "statusDate": "2024-06-01", - "achievementStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/goal-achievement", - "code": "achieved" - } - ] - } - }, - "3": { - "resourceType": "Goal", - "id": "goal-example-3", - "lifecycleStatus": "on-hold", - "description": { - "text": "Reduce blood pressure" - }, - "subject": { - "reference": "Patient/patient-example-4" - }, - "target": [ - { - "measure": { - "coding": [ - { - "system": "http://loinc.org", - "code": "85354-9", - "display": "Blood pressure" - } - ] - }, - "detailString": "Less than 120/80 mmHg" - } - ] - } -} diff --git a/packages/fhir/test/resources/goals.json.license b/packages/fhir/test/resources/goals.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/goals.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/goals.test.ts b/packages/fhir/test/resources/goals.test.ts deleted file mode 100644 index e9ca8f30..00000000 --- a/packages/fhir/test/resources/goals.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Goal } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirGoal, type untypedGoalSchema } from '../../src/index.js' - -describe('Goal Resource', () => { - it('should validate FHIR Goals from goals.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/goals.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirGoal.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/graphDefinitions.json b/packages/fhir/test/resources/graphDefinitions.json deleted file mode 100644 index 6f682831..00000000 --- a/packages/fhir/test/resources/graphDefinitions.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "0": { - "resourceType": "GraphDefinition", - "id": "example", - "url": "http://example.org/fhir/GraphDefinition/example", - "name": "ExampleGraphDefinition", - "status": "draft", - "start": "Patient", - "link": [ - { - "path": "Patient.managingOrganization", - "target": [ - { - "type": "Organization" - } - ] - } - ] - }, - "1": { - "resourceType": "GraphDefinition", - "id": "patient-everything", - "url": "http://example.org/fhir/GraphDefinition/patient-everything", - "name": "PatientEverything", - "status": "active", - "start": "Patient", - "link": [ - { - "path": "Patient.generalPractitioner", - "min": 0, - "max": "*", - "description": "Link to patient's general practitioners", - "target": [ - { - "type": "Practitioner", - "compartment": [ - { - "use": "requirement", - "code": "Patient", - "rule": "identical" - } - ] - } - ] - }, - { - "path": "Patient.link.other", - "target": [ - { - "type": "Patient" - } - ] - } - ] - }, - "2": { - "resourceType": "GraphDefinition", - "id": "encounter-graph", - "url": "http://example.org/fhir/GraphDefinition/encounter-graph", - "name": "EncounterGraph", - "status": "active", - "start": "Encounter", - "link": [ - { - "path": "Encounter.subject", - "target": [ - { - "type": "Patient", - "link": [ - { - "path": "Patient.managingOrganization", - "target": [ - { - "type": "Organization" - } - ] - } - ] - } - ] - } - ] - }, - "3": { - "resourceType": "GraphDefinition", - "id": "medication-graph", - "url": "http://example.org/fhir/GraphDefinition/medication-graph", - "name": "MedicationGraph", - "status": "draft", - "experimental": true, - "start": "MedicationRequest", - "profile": "http://hl7.org/fhir/StructureDefinition/MedicationRequest", - "link": [ - { - "path": "MedicationRequest.subject", - "description": "Link to the patient", - "target": [ - { - "type": "Patient", - "compartment": [ - { - "use": "condition", - "code": "Patient", - "rule": "matching", - "expression": "subject.resolve().id = %context.id" - } - ] - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/graphDefinitions.json.license b/packages/fhir/test/resources/graphDefinitions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/graphDefinitions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/graphDefinitions.test.ts b/packages/fhir/test/resources/graphDefinitions.test.ts deleted file mode 100644 index 2c3604fe..00000000 --- a/packages/fhir/test/resources/graphDefinitions.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type GraphDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirGraphDefinition, - type untypedGraphDefinitionSchema, -} from '../../src/index.js' - -describe('GraphDefinition Resource', () => { - it('should validate FHIR graph definition from graphDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/graphDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirGraphDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/groups.json b/packages/fhir/test/resources/groups.json deleted file mode 100644 index bf89959c..00000000 --- a/packages/fhir/test/resources/groups.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "0": { - "resourceType": "Group", - "id": "group-0", - "identifier": [ - { - "system": "http://hospital.org/groups", - "value": "g-000" - } - ], - "active": true, - "type": "person", - "actual": true, - "name": "Diabetes Patients", - "quantity": 2, - "member": [ - { - "entity": { - "reference": "Patient/pat-0a" - }, - "period": { - "start": "2025-01-01" - }, - "inactive": false - }, - { - "entity": { - "reference": "Patient/pat-0b" - }, - "period": { - "start": "2025-01-02", - "end": "2025-01-10" - }, - "inactive": true - } - ] - }, - "1": { - "resourceType": "Group", - "id": "group-1", - "active": false, - "type": "animal", - "actual": true, - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "387961004", - "display": "Dog" - } - ] - }, - "name": "Canine Research Cohort", - "quantity": 3, - "member": [ - { - "entity": { - "reference": "Patient/dog-1" - } - }, - { - "entity": { - "reference": "Patient/dog-2" - } - }, - { - "entity": { - "reference": "Patient/dog-3" - } - } - ] - }, - "2": { - "resourceType": "Group", - "id": "group-2", - "active": true, - "type": "device", - "actual": false, - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/group-type", - "code": "device", - "display": "Device" - } - ] - }, - "name": "Cardiac Implants", - "quantity": 1, - "characteristic": [ - { - "code": { - "coding": [ - { - "system": "http://example.org/criteria", - "code": "pacemaker" - } - ] - }, - "valueCodeableConcept": { - "coding": [ - { - "system": "http://example.org/devices", - "code": "PM123", - "display": "Pacemaker Model 123" - } - ] - }, - "exclude": false, - "period": { - "start": "2025-01-01" - } - } - ] - }, - "3": { - "resourceType": "Group", - "id": "group-3", - "active": true, - "type": "practitioner", - "actual": true, - "name": "Cardiology Team", - "managingEntity": { - "reference": "Organization/org-3" - }, - "quantity": 2, - "member": [ - { - "entity": { - "reference": "Practitioner/prac-3a" - } - }, - { - "entity": { - "reference": "Practitioner/prac-3b" - } - } - ], - "characteristic": [ - { - "code": { - "coding": [ - { - "system": "http://example.org/criteria", - "code": "specialty" - } - ] - }, - "valueCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "394579002", - "display": "Cardiology" - } - ] - }, - "exclude": false - } - ] - } -} diff --git a/packages/fhir/test/resources/groups.json.license b/packages/fhir/test/resources/groups.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/groups.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/groups.test.ts b/packages/fhir/test/resources/groups.test.ts deleted file mode 100644 index 672daffb..00000000 --- a/packages/fhir/test/resources/groups.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Group } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirGroup, type untypedGroupSchema } from '../../src/index.js' - -describe('Group Resource', () => { - it('should validate FHIR Groups from groups.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/groups.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirGroup.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/guidanceResponses.json b/packages/fhir/test/resources/guidanceResponses.json deleted file mode 100644 index f37dc6b5..00000000 --- a/packages/fhir/test/resources/guidanceResponses.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "0": { - "resourceType": "GuidanceResponse", - "id": "guidanceresponse-example-0", - "moduleUri": "http://example.org/guidance/treatment-protocol", - "status": "success", - "subject": { - "reference": "Patient/example" - } - }, - "1": { - "resourceType": "GuidanceResponse", - "id": "guidanceresponse-example-1", - "requestIdentifier": { - "system": "http://example.org/requests", - "value": "REQ-001" - }, - "identifier": [ - { - "system": "http://example.org/guidanceresponses", - "value": "GR-001" - } - ], - "moduleCanonical": "http://example.org/PlanDefinition/diabetes-management", - "status": "success", - "subject": { - "reference": "Patient/patient-diabetes-001" - }, - "encounter": { - "reference": "Encounter/encounter-001" - }, - "occurrenceDateTime": "2024-11-15T10:30:00Z", - "performer": { - "reference": "Device/clinical-decision-support-system", - "display": "Clinical Decision Support System" - }, - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "73211009", - "display": "Diabetes mellitus" - } - ] - } - ], - "note": [ - { - "text": "Patient meets criteria for intensified diabetes management" - } - ], - "result": { - "reference": "CarePlan/diabetes-intensified-plan" - } - }, - "2": { - "resourceType": "GuidanceResponse", - "id": "guidanceresponse-example-2", - "requestIdentifier": { - "system": "http://example.org/requests", - "value": "REQ-456" - }, - "moduleCodeableConcept": { - "coding": [ - { - "system": "http://example.org/guidance-modules", - "code": "antibiotic-selection", - "display": "Antibiotic Selection Guidance" - } - ] - }, - "status": "data-required", - "subject": { - "reference": "Patient/patient-infection-001" - }, - "encounter": { - "reference": "Encounter/encounter-er-001" - }, - "occurrenceDateTime": "2024-11-20T14:15:00Z", - "reasonCode": [ - { - "text": "Suspected bacterial infection" - } - ], - "evaluationMessage": [ - { - "reference": "OperationOutcome/missing-lab-data", - "display": "Culture results required for optimal antibiotic selection" - } - ], - "note": [ - { - "text": "Additional laboratory culture data required for optimal antibiotic selection" - } - ] - }, - "3": { - "resourceType": "GuidanceResponse", - "id": "guidanceresponse-example-3", - "requestIdentifier": { - "system": "http://example.org/requests", - "value": "REQ-789" - }, - "identifier": [ - { - "system": "http://example.org/guidanceresponses", - "value": "GR-789" - } - ], - "moduleCanonical": "http://example.org/ActivityDefinition/medication-order", - "status": "success", - "subject": { - "reference": "Patient/patient-hypertension-001" - }, - "encounter": { - "reference": "Encounter/encounter-cardiology-001" - }, - "occurrenceDateTime": "2024-11-25T09:00:00Z", - "performer": { - "reference": "Practitioner/practitioner-cardiologist", - "display": "Dr. Cardiology" - }, - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "38341003", - "display": "Hypertensive disorder" - } - ] - } - ], - "reasonReference": [ - { - "reference": "Observation/blood-pressure-high" - } - ], - "note": [ - { - "authorReference": { - "reference": "Practitioner/practitioner-cardiologist" - }, - "time": "2024-11-25T09:05:00Z", - "text": "Patient requires medication adjustment based on recent BP readings" - } - ], - "result": { - "reference": "RequestGroup/medication-adjustment-plan" - }, - "outputParameters": { - "reference": "Parameters/output-recommendations" - } - } -} diff --git a/packages/fhir/test/resources/guidanceResponses.json.license b/packages/fhir/test/resources/guidanceResponses.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/guidanceResponses.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/guidanceResponses.test.ts b/packages/fhir/test/resources/guidanceResponses.test.ts deleted file mode 100644 index b7acd85a..00000000 --- a/packages/fhir/test/resources/guidanceResponses.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type GuidanceResponse } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirGuidanceResponse, - type untypedGuidanceResponseSchema, -} from '../../src/index.js' - -describe('GuidanceResponse Resource', () => { - it('should validate FHIR guidance responses from guidanceResponses.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/guidanceResponses.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirGuidanceResponse.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/healthcareServices.json b/packages/fhir/test/resources/healthcareServices.json deleted file mode 100644 index f6f56923..00000000 --- a/packages/fhir/test/resources/healthcareServices.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "0": { - "resourceType": "HealthcareService", - "id": "service-example-0", - "active": true, - "providedBy": { - "reference": "Organization/org-example" - }, - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/service-category", - "code": "8", - "display": "Counselling" - } - ] - } - ], - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/service-type", - "code": "124", - "display": "General Practice" - } - ] - } - ], - "name": "General Practice Services" - }, - "1": { - "resourceType": "HealthcareService", - "id": "service-example-1", - "identifier": [ - { - "system": "http://example.org/services", - "value": "SVC-001" - } - ], - "active": true, - "providedBy": { - "reference": "Organization/org-cardiology" - }, - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/service-category", - "code": "35", - "display": "Specialist Medical" - } - ] - } - ], - "specialty": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "394579002", - "display": "Cardiology" - } - ] - } - ], - "name": "Cardiac Services", - "telecom": [ - { - "system": "phone", - "value": "+1-555-111-2222" - } - ], - "appointmentRequired": true - }, - "2": { - "resourceType": "HealthcareService", - "id": "service-example-2", - "active": false, - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/service-category", - "code": "17", - "display": "General Practice" - } - ] - } - ], - "name": "Retired Service", - "comment": "This service is no longer available" - }, - "3": { - "resourceType": "HealthcareService", - "id": "service-example-3", - "active": true, - "providedBy": { - "reference": "Organization/org-hospital" - }, - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/service-category", - "code": "1", - "display": "Adoption/Permanent Care Info/Support" - } - ] - } - ], - "name": "24/7 Emergency Department", - "extraDetails": "Walk-ins welcome at any time", - "availableTime": [ - { - "allDay": true - } - ], - "availabilityExceptions": "None" - } -} diff --git a/packages/fhir/test/resources/healthcareServices.json.license b/packages/fhir/test/resources/healthcareServices.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/healthcareServices.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/healthcareServices.test.ts b/packages/fhir/test/resources/healthcareServices.test.ts deleted file mode 100644 index 69940285..00000000 --- a/packages/fhir/test/resources/healthcareServices.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type HealthcareService } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirHealthcareService, - type untypedHealthcareServiceSchema, -} from '../../src/index.js' - -describe('HealthcareService Resource', () => { - it('should validate FHIR healthcareService from healthcareServices.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/healthcareServices.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirHealthcareService.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/imagingStudies.json b/packages/fhir/test/resources/imagingStudies.json deleted file mode 100644 index c3fe173e..00000000 --- a/packages/fhir/test/resources/imagingStudies.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "0": { - "resourceType": "ImagingStudy", - "id": "imaging-example-0", - "status": "available", - "subject": { - "reference": "Patient/patient-example" - } - }, - "1": { - "resourceType": "ImagingStudy", - "id": "imaging-example-1", - "identifier": [ - { - "system": "urn:dicom:uid", - "value": "urn:oid:1.2.840.113619.2.1.1.1" - } - ], - "status": "available", - "modality": [ - { - "system": "http://dicom.nema.org/resources/ontology/DCM", - "code": "CT" - } - ], - "subject": { - "reference": "Patient/patient-example-2" - }, - "started": "2024-11-15T10:30:00Z", - "numberOfSeries": 1, - "numberOfInstances": 50 - }, - "2": { - "resourceType": "ImagingStudy", - "id": "imaging-example-2", - "status": "cancelled", - "subject": { - "reference": "Patient/patient-example-3" - }, - "note": [ - { - "text": "Study cancelled due to patient discomfort" - } - ] - }, - "3": { - "resourceType": "ImagingStudy", - "id": "imaging-example-3", - "status": "available", - "modality": [ - { - "system": "http://dicom.nema.org/resources/ontology/DCM", - "code": "MR" - } - ], - "subject": { - "reference": "Patient/patient-xyz" - }, - "started": "2024-10-20T14:00:00Z", - "endpoint": [ - { - "reference": "Endpoint/endpoint-pacs" - } - ], - "numberOfSeries": 2, - "numberOfInstances": 120, - "procedureReference": { - "reference": "Procedure/procedure-mri" - }, - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "25064002", - "display": "Headache" - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/imagingStudies.json.license b/packages/fhir/test/resources/imagingStudies.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/imagingStudies.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/imagingStudies.test.ts b/packages/fhir/test/resources/imagingStudies.test.ts deleted file mode 100644 index d432c178..00000000 --- a/packages/fhir/test/resources/imagingStudies.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ImagingStudy } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirImagingStudy, - type untypedImagingStudySchema, -} from '../../src/index.js' - -describe('ImagingStudy Resource', () => { - it('should validate FHIR imagingStudy from imagingStudies.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/imagingStudies.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirImagingStudy.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/immunizationEvaluations.json b/packages/fhir/test/resources/immunizationEvaluations.json deleted file mode 100644 index 4cdd8693..00000000 --- a/packages/fhir/test/resources/immunizationEvaluations.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "0": { - "resourceType": "ImmunizationEvaluation", - "id": "eval-0", - "identifier": [ - { "system": "http://hospital.example.org/immevals", "value": "EVAL-0001" } - ], - "status": "completed", - "patient": { "reference": "Patient/123" }, - "date": "2024-10-01", - "authority": { "reference": "Organization/cdc", "display": "CDC" }, - "targetDisease": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "6142004", - "display": "Influenza" - } - ] - }, - "immunizationEvent": { "reference": "Immunization/imm-0" }, - "doseStatus": { "text": "Valid" } - }, - "1": { - "resourceType": "ImmunizationEvaluation", - "id": "eval-1", - "status": "completed", - "patient": { "reference": "Patient/456" }, - "date": "2023-06-15", - "authority": { "display": "WHO" }, - "targetDisease": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "840539006", - "display": "COVID-19" - } - ] - }, - "immunizationEvent": { "reference": "Immunization/imm-1" }, - "doseStatus": { "text": "Valid" }, - "description": "Dose meets recommended timing and interval criteria", - "series": "COVID primary series", - "doseNumberPositiveInt": 2, - "seriesDosesPositiveInt": 2 - }, - "2": { - "resourceType": "ImmunizationEvaluation", - "id": "eval-2", - "status": "completed", - "patient": { "reference": "Patient/789" }, - "date": "2022-08-20", - "authority": { - "reference": "Organization/local", - "display": "Local Health Authority" - }, - "targetDisease": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "36653000", - "display": "Measles" - } - ] - }, - "immunizationEvent": { "reference": "Immunization/imm-2" }, - "doseStatus": { "text": "Invalid" }, - "doseStatusReason": [ - { "coding": [{ "code": "EXPIRED" }], "text": "Expired lot" }, - { "coding": [{ "code": "TOO_EARLY" }], "text": "Administered too early" } - ], - "description": "Invalid because administered prior to minimum recommended age" - }, - "3": { - "resourceType": "ImmunizationEvaluation", - "id": "eval-3", - "status": "completed", - "patient": { "reference": "Patient/999" }, - "date": "2021-02-25", - "authority": { - "reference": "Organization/who", - "display": "World Health Organization" - }, - "targetDisease": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "40468003", - "display": "Viral hepatitis type B" - } - ] - }, - "immunizationEvent": { "reference": "Immunization/imm-3" }, - "doseStatus": { "text": "Valid" }, - "doseStatusReason": [{ "text": "Meets all criteria" }], - "description": "Second dose administered on time, series progressing appropriately", - "series": "HepB series", - "doseNumberPositiveInt": 2, - "seriesDosesPositiveInt": 3 - } -} diff --git a/packages/fhir/test/resources/immunizationEvaluations.json.license b/packages/fhir/test/resources/immunizationEvaluations.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/immunizationEvaluations.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/immunizationEvaluations.test.ts b/packages/fhir/test/resources/immunizationEvaluations.test.ts deleted file mode 100644 index e58434d3..00000000 --- a/packages/fhir/test/resources/immunizationEvaluations.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ImmunizationEvaluation } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirImmunizationEvaluation, - type untypedImmunizationEvaluationSchema, -} from '../../src/index.js' - -describe('ImmunizationEvaluation Resource', () => { - it('should validate FHIR immunization evaluations from immunizationEvaluations.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/immunizationEvaluations.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirImmunizationEvaluation.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/immunizationRecommendations.json b/packages/fhir/test/resources/immunizationRecommendations.json deleted file mode 100644 index c66d2396..00000000 --- a/packages/fhir/test/resources/immunizationRecommendations.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "0": { - "resourceType": "ImmunizationRecommendation", - "id": "rec-0", - "identifier": [ - { "system": "http://hospital.example.org/immrecs", "value": "REC-0001" } - ], - "patient": { "reference": "Patient/123" }, - "date": "2024-11-01", - "authority": { "reference": "Organization/cdc", "display": "CDC" }, - "recommendation": [ - { - "vaccineCode": [ - { - "coding": [ - { - "system": "http://hl7.org/fhir/sid/cvx", - "code": "140", - "display": "Influenza" - } - ] - } - ], - "forecastStatus": { "text": "Due" } - } - ] - }, - "1": { - "resourceType": "ImmunizationRecommendation", - "id": "rec-1", - "patient": { "reference": "Patient/456" }, - "date": "2023-06-10", - "recommendation": [ - { - "vaccineCode": [ - { - "coding": [ - { - "system": "http://hl7.org/fhir/sid/cvx", - "code": "207", - "display": "COVID-19" - } - ] - } - ], - "forecastStatus": { "text": "Overdue" }, - "dateCriterion": [ - { "code": { "text": "Earliest date" }, "value": "2023-05-01" }, - { "code": { "text": "Latest date" }, "value": "2023-06-01" } - ], - "doseNumberPositiveInt": 2, - "seriesDosesPositiveInt": 3 - } - ] - }, - "2": { - "resourceType": "ImmunizationRecommendation", - "id": "rec-2", - "patient": { "reference": "Patient/789" }, - "date": "2022-08-15", - "recommendation": [ - { - "vaccineCode": [ - { - "coding": [ - { - "system": "http://hl7.org/fhir/sid/cvx", - "code": "03", - "display": "MMR" - } - ] - } - ], - "forecastStatus": { "text": "Not recommended" }, - "description": "Patient has documented immunity", - "supportingImmunization": [{ "reference": "Immunization/imm-2" }], - "supportingPatientInformation": [ - { "reference": "Observation/obs-immunity", "display": "IgG positive" } - ], - "dateCriterion": [ - { "code": { "text": "Earliest date" }, "value": "2022-07-01" } - ] - } - ] - }, - "3": { - "resourceType": "ImmunizationRecommendation", - "id": "rec-3", - "patient": { "reference": "Patient/999" }, - "date": "2021-02-20", - "authority": { "display": "WHO" }, - "recommendation": [ - { - "vaccineCode": [ - { - "coding": [ - { - "system": "http://hl7.org/fhir/sid/cvx", - "code": "08", - "display": "Hepatitis B" - } - ] - } - ], - "forecastStatus": { "text": "Due soon" }, - "series": "HepB series", - "targetDisease": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "40468003", - "display": "Viral hepatitis type B" - } - ] - }, - "doseNumberPositiveInt": 3, - "seriesDosesPositiveInt": 3, - "supportingImmunization": [{ "reference": "Immunization/imm-3" }], - "supportingPatientInformation": [ - { - "reference": "Condition/chronic-liver", - "display": "Chronic liver disease" - } - ], - "description": "Final dose of series recommended" - } - ] - } -} diff --git a/packages/fhir/test/resources/immunizationRecommendations.json.license b/packages/fhir/test/resources/immunizationRecommendations.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/immunizationRecommendations.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/immunizationRecommendations.test.ts b/packages/fhir/test/resources/immunizationRecommendations.test.ts deleted file mode 100644 index 2b0af8b3..00000000 --- a/packages/fhir/test/resources/immunizationRecommendations.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ImmunizationRecommendation } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirImmunizationRecommendation, - type untypedImmunizationRecommendationSchema, -} from '../../src/index.js' - -describe('ImmunizationRecommendation Resource', () => { - it('should validate FHIR immunization recommendations from immunizationRecommendations.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/immunizationRecommendations.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = - FhirImmunizationRecommendation.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) - - it('should return vaccine displays and ignore recommendations without vaccine codes', () => { - const recommendation = FhirImmunizationRecommendation.parse({ - resourceType: 'ImmunizationRecommendation', - patient: { reference: 'Patient/123' }, - date: '2026-08-06', - recommendation: [ - { - vaccineCode: [{ text: 'Influenza' }], - forecastStatus: { text: 'Due' }, - }, - { - forecastStatus: { text: 'Complete' }, - }, - ], - }) - - expect(recommendation.vaccineCodeDisplays).toEqual(['Influenza']) - }) -}) diff --git a/packages/fhir/test/resources/immunizations.json b/packages/fhir/test/resources/immunizations.json deleted file mode 100644 index 1d789285..00000000 --- a/packages/fhir/test/resources/immunizations.json +++ /dev/null @@ -1,147 +0,0 @@ -{ - "0": { - "resourceType": "Immunization", - "id": "imm-0", - "identifier": [ - { - "system": "http://hospital.example.org/immunizations", - "value": "IMM-0001" - } - ], - "status": "completed", - "statusReason": { "text": "Routine vaccination" }, - "vaccineCode": { - "coding": [ - { - "system": "http://hl7.org/fhir/sid/cvx", - "code": "140", - "display": "Influenza, seasonal" - } - ], - "text": "Flu shot" - }, - "patient": { "reference": "Patient/123" }, - "encounter": { "reference": "Encounter/enc-001" }, - "occurrenceDateTime": "2024-10-01", - "recorded": "2024-10-01T10:00:00Z", - "primarySource": true, - "reportOrigin": { "text": "Provider report" }, - "location": { - "reference": "Location/clinic1", - "display": "Downtown Clinic" - }, - "manufacturer": { "reference": "Organization/org1", "display": "Pfizer" }, - "lotNumber": "LOT12345", - "expirationDate": "2025-05-01" - }, - "1": { - "resourceType": "Immunization", - "id": "imm-1", - "status": "completed", - "vaccineCode": { - "coding": [ - { - "system": "http://hl7.org/fhir/sid/cvx", - "code": "208", - "display": "COVID-19 mRNA" - } - ] - }, - "patient": { "reference": "Patient/456" }, - "occurrenceDateTime": "2023-06-12", - "site": { "coding": [{ "code": "LA" }], "text": "Left arm" }, - "route": { "coding": [{ "code": "IM" }], "text": "Intramuscular" }, - "doseQuantity": { "value": 0.5, "unit": "mL" }, - "performer": [ - { - "function": { "text": "Administering provider" }, - "actor": { "reference": "Practitioner/111", "display": "Dr. Smith" } - }, - { - "function": { "text": "Ordering provider" }, - "actor": { "reference": "Practitioner/222", "display": "Dr. Jones" } - } - ], - "note": [{ "text": "Observed 15 min post-vaccination. No reaction." }], - "isSubpotent": false, - "programEligibility": [{ "text": "VFC eligible" }], - "fundingSource": { "text": "Government supplied" } - }, - "2": { - "resourceType": "Immunization", - "id": "imm-2", - "status": "entered-in-error", - "vaccineCode": { - "coding": [ - { - "system": "http://hl7.org/fhir/sid/cvx", - "code": "03", - "display": "MMR" - } - ] - }, - "patient": { "reference": "Patient/789" }, - "occurrenceDateTime": "2022-08-15", - "site": { "text": "Right thigh" }, - "route": { "text": "Subcutaneous" }, - "doseQuantity": { "value": 0.5, "unit": "mL" }, - "performer": [ - { - "function": { "text": "Administering provider" }, - "actor": { "display": "Nurse Jane" } - } - ], - "note": [{ "text": "Dose entered in error, not administered" }], - "reasonCode": [{ "text": "System data entry error" }], - "reaction": [ - { - "date": "2022-08-15T12:00:00Z", - "detail": { "reference": "Observation/obs-reaction-1" }, - "reported": true - } - ], - "isSubpotent": true, - "subpotentReason": [{ "text": "Expired lot" }] - }, - "3": { - "resourceType": "Immunization", - "id": "imm-3", - "status": "completed", - "vaccineCode": { - "coding": [ - { - "system": "http://hl7.org/fhir/sid/cvx", - "code": "08", - "display": "Hepatitis B" - } - ] - }, - "patient": { "reference": "Patient/999" }, - "occurrenceDateTime": "2021-02-20", - "doseQuantity": { "value": 1.0, "unit": "mL" }, - "protocolApplied": [ - { - "series": "HepB series", - "authority": { "reference": "Organization/cdc", "display": "CDC" }, - "targetDisease": [ - { - "coding": [ - { "code": "40468003", "display": "Viral hepatitis type B" } - ] - } - ], - "doseNumberPositiveInt": 2, - "seriesDosesPositiveInt": 3 - } - ], - "reportOrigin": { "text": "Immunization registry" }, - "location": { "display": "Regional Health Center" }, - "manufacturer": { "display": "GlaxoSmithKline" }, - "lotNumber": "HBV123", - "expirationDate": "2023-01-01", - "reasonCode": [{ "text": "Routine infant schedule" }], - "programEligibility": [{ "text": "Medicaid" }], - "fundingSource": { "text": "Insurance" }, - "note": [{ "text": "Second dose administered on schedule" }] - } -} diff --git a/packages/fhir/test/resources/immunizations.json.license b/packages/fhir/test/resources/immunizations.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/immunizations.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/immunizations.test.ts b/packages/fhir/test/resources/immunizations.test.ts deleted file mode 100644 index b13e2f9c..00000000 --- a/packages/fhir/test/resources/immunizations.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Immunization } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirImmunization, - type untypedImmunizationSchema, -} from '../../src/index.js' - -describe('Immunization Resource', () => { - it('should validate FHIR immunizations from immunizations.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/immunizations.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirImmunization.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/implementationGuides.json b/packages/fhir/test/resources/implementationGuides.json deleted file mode 100644 index f50fbca7..00000000 --- a/packages/fhir/test/resources/implementationGuides.json +++ /dev/null @@ -1,126 +0,0 @@ -{ - "0": { - "resourceType": "ImplementationGuide", - "id": "ig-example-0", - "url": "http://example.org/fhir/ImplementationGuide/example", - "name": "ExampleImplementationGuide", - "status": "draft", - "packageId": "example.fhir.guide", - "fhirVersion": ["4.3.0"] - }, - "1": { - "resourceType": "ImplementationGuide", - "id": "ig-example-1", - "url": "http://example.org/fhir/ImplementationGuide/us-core", - "version": "1.0.0", - "name": "USCoreImplementationGuide", - "title": "US Core Implementation Guide", - "status": "active", - "experimental": false, - "date": "2023-01-15T10:00:00Z", - "publisher": "Example Health Organization", - "description": "Implementation guide for US Core FHIR profiles", - "packageId": "hl7.fhir.us.core", - "license": "CC0-1.0", - "fhirVersion": ["4.3.0"], - "dependsOn": [ - { - "uri": "http://hl7.org/fhir/us/core/ImplementationGuide/hl7.fhir.us.core", - "packageId": "hl7.fhir.us.core", - "version": "5.0.1" - } - ] - }, - "2": { - "resourceType": "ImplementationGuide", - "id": "ig-example-2", - "url": "http://example.org/fhir/ImplementationGuide/patient-guide", - "version": "2.0.0", - "name": "PatientDataGuide", - "title": "Patient Data Implementation Guide", - "status": "active", - "publisher": "Healthcare Standards Authority", - "contact": [ - { - "name": "Standards Team", - "telecom": [ - { - "system": "email", - "value": "standards@example.org" - } - ] - } - ], - "packageId": "example.patient.guide", - "fhirVersion": ["4.3.0"], - "global": [ - { - "type": "Patient", - "profile": "http://example.org/fhir/StructureDefinition/patient-profile" - } - ], - "definition": { - "resource": [ - { - "reference": { - "reference": "StructureDefinition/patient-profile" - }, - "name": "Patient Profile", - "description": "Profile for patient resource", - "exampleBoolean": false - } - ] - } - }, - "3": { - "resourceType": "ImplementationGuide", - "id": "ig-example-3", - "url": "http://example.org/fhir/ImplementationGuide/medication-guide", - "version": "1.2.0", - "name": "MedicationGuide", - "title": "Medication Management Implementation Guide", - "status": "active", - "experimental": false, - "date": "2023-09-20T08:00:00Z", - "publisher": "Pharmacy Standards Body", - "description": "Guidelines for medication management in FHIR", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "copyright": "Copyright 2023 Example Organization", - "packageId": "example.medication.guide", - "license": "Apache-2.0", - "fhirVersion": ["4.3.0"], - "definition": { - "grouping": [ - { - "name": "Medication Profiles", - "description": "Core medication resource profiles" - } - ], - "resource": [ - { - "reference": { - "reference": "StructureDefinition/medication-profile" - }, - "name": "Medication Profile", - "exampleBoolean": false, - "groupingId": "medication-profiles" - } - ], - "page": { - "nameUrl": "http://example.org/ig/index.html", - "title": "Home Page", - "generation": "html" - } - } - } -} diff --git a/packages/fhir/test/resources/implementationGuides.json.license b/packages/fhir/test/resources/implementationGuides.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/implementationGuides.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/implementationGuides.test.ts b/packages/fhir/test/resources/implementationGuides.test.ts deleted file mode 100644 index 8d5237da..00000000 --- a/packages/fhir/test/resources/implementationGuides.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ImplementationGuide } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirImplementationGuide, - type untypedImplementationGuideSchema, -} from '../../src/index.js' - -describe('ImplementationGuide Resource', () => { - it('should validate FHIR implementation guide from implementationGuides.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/implementationGuides.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirImplementationGuide.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/ingredients.json b/packages/fhir/test/resources/ingredients.json deleted file mode 100644 index cd4d4b6d..00000000 --- a/packages/fhir/test/resources/ingredients.json +++ /dev/null @@ -1,183 +0,0 @@ -{ - "0": { - "resourceType": "Ingredient", - "id": "ingredient-example-0", - "status": "active", - "role": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleClass", - "code": "ACTIB", - "display": "active ingredient - basis of strength" - } - ] - }, - "substance": { - "code": { - "concept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "387207008", - "display": "Caffeine" - } - ] - } - } - } - }, - "1": { - "resourceType": "Ingredient", - "id": "ingredient-example-1", - "status": "active", - "identifier": { - "system": "http://example.org/ingredients", - "value": "ING-001" - }, - "role": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleClass", - "code": "IACT", - "display": "inactive ingredient" - } - ] - }, - "manufacturer": [ - { - "role": "actual", - "manufacturer": { - "reference": "Organization/example" - } - } - ], - "substance": { - "code": { - "concept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "161", - "display": "Acetaminophen" - } - ] - } - }, - "strength": [ - { - "presentationRatio": { - "numerator": { - "value": 500, - "unit": "mg" - }, - "denominator": { - "value": 1, - "unit": "tablet" - } - } - } - ] - } - }, - "2": { - "resourceType": "Ingredient", - "id": "ingredient-example-2", - "status": "draft", - "role": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleClass", - "code": "ACTIB", - "display": "active ingredient - basis of strength" - } - ] - }, - "allergenicIndicator": true, - "function": [ - { - "coding": [ - { - "system": "http://example.org/ingredient-function", - "code": "stabilizer", - "display": "Stabilizer" - } - ] - } - ], - "substance": { - "code": { - "concept": { - "text": "Custom ingredient" - } - } - } - }, - "3": { - "resourceType": "Ingredient", - "id": "ingredient-example-3", - "status": "retired", - "role": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleClass", - "code": "IACT", - "display": "inactive ingredient" - } - ] - }, - "for": [ - { - "reference": "MedicinalProductDefinition/example" - } - ], - "manufacturer": [ - { - "role": "allowed", - "manufacturer": { - "reference": "Organization/manufacturer1" - } - }, - { - "role": "possible", - "manufacturer": { - "reference": "Organization/manufacturer2" - } - } - ], - "substance": { - "code": { - "reference": { - "reference": "SubstanceDefinition/example" - } - }, - "strength": [ - { - "concentrationRatio": { - "numerator": { - "value": 10, - "unit": "mg" - }, - "denominator": { - "value": 1, - "unit": "mL" - } - }, - "referenceStrength": [ - { - "strengthRatio": { - "numerator": { - "value": 8, - "unit": "mg" - }, - "denominator": { - "value": 1, - "unit": "mL" - } - } - } - ] - } - ] - } - } -} diff --git a/packages/fhir/test/resources/ingredients.json.license b/packages/fhir/test/resources/ingredients.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/ingredients.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/ingredients.test.ts b/packages/fhir/test/resources/ingredients.test.ts deleted file mode 100644 index 938ed8c3..00000000 --- a/packages/fhir/test/resources/ingredients.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Ingredient } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirIngredient, - type untypedIngredientSchema, -} from '../../src/index.js' - -describe('Ingredient Resource', () => { - it('should validate FHIR ingredient from ingredients.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/ingredients.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirIngredient.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/insurancePlans.json b/packages/fhir/test/resources/insurancePlans.json deleted file mode 100644 index 40ef45ed..00000000 --- a/packages/fhir/test/resources/insurancePlans.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "0": { - "resourceType": "InsurancePlan", - "id": "insuranceplan-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T10:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/InsurancePlan"] - } - }, - "1": { - "resourceType": "InsurancePlan", - "id": "insuranceplan-comprehensive", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T11:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/InsurancePlan"] - }, - "identifier": [ - { - "system": "http://insurance.example.org/plans", - "value": "PLAN-GOLD-001" - } - ], - "status": "active", - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/insurance-plan-type", - "code": "medical", - "display": "Medical" - } - ] - } - ], - "name": "Gold Plus Health Plan", - "alias": ["Gold Plan", "Premium Health"], - "period": { - "start": "2025-01-01", - "end": "2025-12-31" - }, - "ownedBy": { - "reference": "Organization/insurance-001", - "display": "Blue Cross Insurance" - }, - "administeredBy": { - "reference": "Organization/admin-001" - } - }, - "2": { - "resourceType": "InsurancePlan", - "id": "insuranceplan-draft", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T12:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/InsurancePlan"] - }, - "status": "draft", - "name": "New Basic Plan" - }, - "3": { - "resourceType": "InsurancePlan", - "id": "insuranceplan-retired", - "meta": { - "versionId": "2", - "lastUpdated": "2025-01-15T13:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/InsurancePlan"] - }, - "status": "retired", - "name": "Old Silver Plan" - } -} diff --git a/packages/fhir/test/resources/insurancePlans.json.license b/packages/fhir/test/resources/insurancePlans.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/insurancePlans.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/insurancePlans.test.ts b/packages/fhir/test/resources/insurancePlans.test.ts deleted file mode 100644 index 1580fa6f..00000000 --- a/packages/fhir/test/resources/insurancePlans.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type InsurancePlan } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirInsurancePlan, - type untypedInsurancePlanSchema, -} from '../../src/index.js' - -describe('InsurancePlan Resource', () => { - it('should validate FHIR InsurancePlans from insurancePlans.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/insurancePlans.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirInsurancePlan.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/invoices.json b/packages/fhir/test/resources/invoices.json deleted file mode 100644 index 404bbe0e..00000000 --- a/packages/fhir/test/resources/invoices.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "0": { - "resourceType": "Invoice", - "id": "invoice-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T10:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Invoice"] - }, - "status": "draft" - }, - "1": { - "resourceType": "Invoice", - "id": "invoice-issued", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T11:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Invoice"] - }, - "identifier": [ - { - "system": "http://hospital.example.org/invoices", - "value": "INV-001" - } - ], - "status": "issued", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0001", - "code": "INVOICE", - "display": "Invoice" - } - ] - }, - "subject": { - "reference": "Patient/patient-001", - "display": "John Doe" - }, - "recipient": { - "reference": "Patient/patient-001" - }, - "date": "2025-01-15", - "participant": [ - { - "actor": { - "reference": "Practitioner/practitioner-001", - "display": "Dr. Jane Smith" - } - } - ], - "issuer": { - "reference": "Organization/hospital-001", - "display": "General Hospital" - }, - "account": { - "reference": "Account/account-001" - }, - "totalNet": { - "value": 1000.0, - "currency": "USD" - }, - "totalGross": { - "value": 1100.0, - "currency": "USD" - } - }, - "2": { - "resourceType": "Invoice", - "id": "invoice-balanced", - "meta": { - "versionId": "2", - "lastUpdated": "2025-01-15T12:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Invoice"] - }, - "status": "balanced" - }, - "3": { - "resourceType": "Invoice", - "id": "invoice-cancelled", - "meta": { - "versionId": "3", - "lastUpdated": "2025-01-15T13:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Invoice"] - }, - "status": "cancelled", - "cancelledReason": "Patient deceased before services rendered" - } -} diff --git a/packages/fhir/test/resources/invoices.json.license b/packages/fhir/test/resources/invoices.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/invoices.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/invoices.test.ts b/packages/fhir/test/resources/invoices.test.ts deleted file mode 100644 index ad5d50c0..00000000 --- a/packages/fhir/test/resources/invoices.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Invoice } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirInvoice, type untypedInvoiceSchema } from '../../src/index.js' - -describe('Invoice Resource', () => { - it('should validate FHIR Invoices from invoices.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/invoices.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirInvoice.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/libraries.json b/packages/fhir/test/resources/libraries.json deleted file mode 100644 index 2915bb75..00000000 --- a/packages/fhir/test/resources/libraries.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "0": { - "resourceType": "Library", - "id": "library-example-0", - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/library-type", - "code": "logic-library" - } - ] - }, - "title": "Common Functions", - "description": "A library of common clinical functions" - }, - "1": { - "resourceType": "Library", - "id": "library-example-1", - "url": "http://example.org/fhir/Library/diabetes-logic", - "identifier": [ - { - "system": "http://example.org/libraries", - "value": "LIB-001" - } - ], - "version": "1.0.0", - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/library-type", - "code": "logic-library" - } - ] - }, - "title": "Diabetes Management Logic", - "description": "Clinical decision support logic for diabetes care", - "date": "2024-01-15" - }, - "2": { - "resourceType": "Library", - "id": "library-example-2", - "status": "retired", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/library-type", - "code": "model-definition" - } - ] - }, - "title": "Deprecated Model", - "description": "This library is no longer in use" - }, - "3": { - "resourceType": "Library", - "id": "library-example-3", - "url": "http://example.org/fhir/Library/hypertension", - "version": "2.1.0", - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/library-type", - "code": "logic-library" - } - ] - }, - "title": "Hypertension Management", - "subtitle": "Guidelines for blood pressure control", - "experimental": false, - "publisher": "Example Healthcare Organization", - "contact": [ - { - "name": "Clinical Support Team" - } - ], - "description": "Evidence-based guidelines for managing hypertension", - "useContext": [ - { - "code": { - "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", - "code": "focus" - }, - "valueCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "38341003", - "display": "Hypertension" - } - ] - } - } - ], - "topic": [ - { - "text": "Hypertension" - } - ] - } -} diff --git a/packages/fhir/test/resources/libraries.json.license b/packages/fhir/test/resources/libraries.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/libraries.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/libraries.test.ts b/packages/fhir/test/resources/libraries.test.ts deleted file mode 100644 index c270db7b..00000000 --- a/packages/fhir/test/resources/libraries.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Library } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirLibrary, type untypedLibrarySchema } from '../../src/index.js' - -describe('Library Resource', () => { - it('should validate FHIR library from libraries.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/libraries.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirLibrary.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/linkages.json b/packages/fhir/test/resources/linkages.json deleted file mode 100644 index c9325d8e..00000000 --- a/packages/fhir/test/resources/linkages.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "0": { - "resourceType": "Linkage", - "id": "example-0", - "active": true, - "author": { - "reference": "Practitioner/practitioner-001", - "display": "Dr. Smith" - }, - "item": [ - { - "resource": { - "reference": "Patient/patient-001" - }, - "type": "source" - }, - { - "resource": { - "reference": "Patient/patient-002" - }, - "type": "alternate" - } - ] - }, - "1": { - "resourceType": "Linkage", - "id": "example-1", - "active": true, - "author": { - "reference": "Organization/org-001" - }, - "item": [ - { - "resource": { - "reference": "Observation/obs-001" - }, - "type": "source" - }, - { - "resource": { - "reference": "Observation/obs-002" - }, - "type": "alternate" - }, - { - "resource": { - "reference": "Observation/obs-003" - }, - "type": "historical" - } - ] - }, - "2": { - "resourceType": "Linkage", - "id": "example-2", - "active": false, - "author": { - "reference": "Practitioner/practitioner-002", - "display": "Dr. Johnson" - }, - "item": [ - { - "resource": { - "reference": "Condition/condition-001" - }, - "type": "source" - }, - { - "resource": { - "reference": "Condition/condition-002" - }, - "type": "alternate" - } - ] - }, - "3": { - "resourceType": "Linkage", - "id": "example-3", - "active": true, - "item": [ - { - "resource": { - "reference": "Encounter/encounter-001" - }, - "type": "source" - }, - { - "resource": { - "reference": "Encounter/encounter-002" - }, - "type": "alternate" - }, - { - "resource": { - "reference": "Encounter/encounter-003" - }, - "type": "alternate" - } - ] - } -} diff --git a/packages/fhir/test/resources/linkages.json.license b/packages/fhir/test/resources/linkages.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/linkages.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/linkages.test.ts b/packages/fhir/test/resources/linkages.test.ts deleted file mode 100644 index 07396b06..00000000 --- a/packages/fhir/test/resources/linkages.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Linkage } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirLinkage, type untypedLinkageSchema } from '../../src/index.js' - -describe('Linkage Resource', () => { - it('should validate FHIR Linkages from linkages.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/linkages.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirLinkage.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/lists.json b/packages/fhir/test/resources/lists.json deleted file mode 100644 index ef56419a..00000000 --- a/packages/fhir/test/resources/lists.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "0": { - "resourceType": "List", - "id": "list-0", - "identifier": [ - { - "system": "http://hospital.org/lists", - "value": "list-000" - } - ], - "status": "current", - "mode": "working", - "title": "Active Problem List", - "code": { - "coding": [ - { - "system": "http://loinc.org", - "code": "11450-4", - "display": "Problem list" - } - ] - }, - "subject": { - "reference": "Patient/pat-0" - }, - "encounter": { - "reference": "Encounter/enc-0" - }, - "date": "2025-01-10T09:00:00Z", - "source": { - "reference": "Practitioner/prac-0" - }, - "orderedBy": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/list-order", - "code": "event-date", - "display": "Sorted by Event Date" - } - ] - }, - "entry": [ - { - "flag": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", - "code": "HTN", - "display": "Hypertension" - } - ] - }, - "date": "2025-01-01T00:00:00Z", - "deleted": false, - "item": { - "reference": "Condition/cond-0" - } - }, - { - "item": { - "reference": "Condition/cond-1" - }, - "deleted": true - } - ], - "note": [ - { - "text": "This list captures currently active medical problems." - } - ] - }, - "1": { - "resourceType": "List", - "id": "list-1", - "status": "retired", - "mode": "snapshot", - "title": "Discontinued Medications", - "subject": { - "reference": "Patient/pat-1" - }, - "date": "2025-02-01T12:00:00Z", - "source": { - "reference": "Practitioner/prac-1" - }, - "entry": [ - { - "item": { - "reference": "MedicationRequest/medreq-1" - }, - "deleted": true, - "date": "2025-01-20T00:00:00Z" - }, - { - "item": { - "reference": "MedicationStatement/medstat-1" - } - } - ], - "note": [ - { - "text": "Medications no longer taken by patient." - } - ] - }, - "2": { - "resourceType": "List", - "id": "list-2", - "status": "current", - "mode": "changes", - "title": "Lab Results List", - "subject": { - "reference": "Patient/pat-2" - }, - "date": "2025-03-15T08:30:00Z", - "source": { - "reference": "Practitioner/prac-2" - }, - "orderedBy": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/list-order", - "code": "priority", - "display": "Sorted by Priority" - } - ] - }, - "entry": [ - { - "flag": { - "text": "Critical" - }, - "item": { - "reference": "Observation/obs-2a" - }, - "date": "2025-03-14T07:00:00Z" - }, - { - "item": { - "reference": "Observation/obs-2b" - } - }, - { - "item": { - "reference": "DiagnosticReport/diagrep-2" - } - } - ] - }, - "3": { - "resourceType": "List", - "id": "list-3", - "status": "entered-in-error", - "mode": "working", - "title": "Erroneous Appointment List", - "subject": { - "reference": "Patient/pat-3" - }, - "date": "2025-04-01T10:00:00Z", - "source": { - "reference": "Practitioner/prac-3" - }, - "emptyReason": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/list-empty-reason", - "code": "notasked", - "display": "Not Asked" - } - ], - "text": "List created in error." - } - } -} diff --git a/packages/fhir/test/resources/lists.json.license b/packages/fhir/test/resources/lists.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/lists.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/lists.test.ts b/packages/fhir/test/resources/lists.test.ts deleted file mode 100644 index 9047920a..00000000 --- a/packages/fhir/test/resources/lists.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type List } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirList, type untypedListSchema } from '../../src/index.js' - -describe('List Resource', () => { - it('should validate FHIR Lists from lists.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/lists.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirList.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/locations.json b/packages/fhir/test/resources/locations.json deleted file mode 100644 index 8a34027c..00000000 --- a/packages/fhir/test/resources/locations.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "0": { - "resourceType": "Location", - "id": "location-example-0", - "status": "active", - "name": "Main Hospital Building", - "mode": "instance", - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode", - "code": "HOSP", - "display": "Hospital" - } - ] - } - ], - "address": { - "line": ["123 Medical Center Drive"], - "city": "Springfield", - "state": "IL", - "postalCode": "62701" - } - }, - "1": { - "resourceType": "Location", - "id": "location-example-1", - "identifier": [ - { - "system": "http://example.org/locations", - "value": "LOC-ROOM-301" - } - ], - "status": "active", - "name": "Examination Room 301", - "alias": ["Exam Room 301", "ER-301"], - "description": "Standard examination room on third floor", - "mode": "instance", - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode", - "code": "ER", - "display": "Emergency room" - } - ] - } - ], - "telecom": [ - { - "system": "phone", - "value": "ext. 301" - } - ], - "physicalType": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", - "code": "ro", - "display": "Room" - } - ] - }, - "partOf": { - "reference": "Location/location-example-0" - } - }, - "2": { - "resourceType": "Location", - "id": "location-example-2", - "status": "suspended", - "name": "Building B - Under Renovation", - "mode": "instance", - "operationalStatus": { - "system": "http://terminology.hl7.org/CodeSystem/v2-0116", - "code": "C", - "display": "Closed" - } - }, - "3": { - "resourceType": "Location", - "id": "location-example-3", - "status": "active", - "name": "Satellite Clinic - Downtown", - "mode": "instance", - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode", - "code": "COMM", - "display": "Community Location" - } - ] - } - ], - "address": { - "line": ["456 Main Street", "Suite 100"], - "city": "Boston", - "state": "MA", - "postalCode": "02101", - "country": "USA" - }, - "position": { - "longitude": -71.0589, - "latitude": 42.3601, - "altitude": 10.0 - }, - "managingOrganization": { - "reference": "Organization/org-example" - }, - "hoursOfOperation": [ - { - "daysOfWeek": ["mon", "tue", "wed", "thu", "fri"], - "openingTime": "08:00:00", - "closingTime": "17:00:00" - } - ] - } -} diff --git a/packages/fhir/test/resources/locations.json.license b/packages/fhir/test/resources/locations.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/locations.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/locations.test.ts b/packages/fhir/test/resources/locations.test.ts deleted file mode 100644 index 9a75322e..00000000 --- a/packages/fhir/test/resources/locations.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Location } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirLocation, type untypedLocationSchema } from '../../src/index.js' - -describe('Location Resource', () => { - it('should validate FHIR location from locations.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/locations.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirLocation.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/manufacturedItemDefinitions.json b/packages/fhir/test/resources/manufacturedItemDefinitions.json deleted file mode 100644 index 4d2ff777..00000000 --- a/packages/fhir/test/resources/manufacturedItemDefinitions.json +++ /dev/null @@ -1,205 +0,0 @@ -{ - "0": { - "resourceType": "ManufacturedItemDefinition", - "id": "manufactured-item-example-0", - "status": "active", - "manufacturedDoseForm": { - "coding": [ - { - "system": "http://hl7.org/fhir/manufactured-dose-form", - "code": "tablet", - "display": "Tablet" - } - ] - } - }, - "1": { - "resourceType": "ManufacturedItemDefinition", - "id": "manufactured-item-example-1", - "identifier": [ - { - "system": "http://example.org/manufactured-items", - "value": "MI-12345" - } - ], - "status": "active", - "manufacturedDoseForm": { - "coding": [ - { - "system": "http://hl7.org/fhir/manufactured-dose-form", - "code": "capsule", - "display": "Capsule" - } - ] - }, - "unitOfPresentation": { - "coding": [ - { - "system": "http://hl7.org/fhir/unit-of-presentation", - "code": "capsule", - "display": "Capsule" - } - ] - }, - "manufacturer": [ - { - "reference": "Organization/pharma-manufacturer" - } - ], - "ingredient": [ - { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "161", - "display": "Acetaminophen" - } - ] - } - ] - }, - "2": { - "resourceType": "ManufacturedItemDefinition", - "id": "manufactured-item-example-2", - "status": "draft", - "manufacturedDoseForm": { - "coding": [ - { - "system": "http://hl7.org/fhir/manufactured-dose-form", - "code": "solution-for-injection", - "display": "Solution for Injection" - } - ] - }, - "unitOfPresentation": { - "coding": [ - { - "system": "http://hl7.org/fhir/unit-of-presentation", - "code": "vial", - "display": "Vial" - } - ] - }, - "property": [ - { - "type": { - "coding": [ - { - "system": "http://example.org/property-type", - "code": "color", - "display": "Color" - } - ] - }, - "valueCodeableConcept": { - "text": "Clear colorless" - } - }, - { - "type": { - "coding": [ - { - "system": "http://example.org/property-type", - "code": "ph", - "display": "pH" - } - ] - }, - "valueQuantity": { - "value": 7.4, - "unit": "pH" - } - } - ] - }, - "3": { - "resourceType": "ManufacturedItemDefinition", - "id": "manufactured-item-example-3", - "identifier": [ - { - "system": "http://example.org/manufactured-items", - "value": "MI-TABLET-500MG" - } - ], - "status": "active", - "manufacturedDoseForm": { - "coding": [ - { - "system": "http://hl7.org/fhir/manufactured-dose-form", - "code": "film-coated-tablet", - "display": "Film-coated Tablet" - } - ] - }, - "unitOfPresentation": { - "coding": [ - { - "system": "http://hl7.org/fhir/unit-of-presentation", - "code": "tablet", - "display": "Tablet" - } - ] - }, - "manufacturer": [ - { - "reference": "Organization/manufacturer1" - }, - { - "reference": "Organization/manufacturer2" - } - ], - "ingredient": [ - { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1191", - "display": "Aspirin" - } - ] - } - ], - "property": [ - { - "type": { - "coding": [ - { - "system": "http://example.org/property-type", - "code": "shape", - "display": "Shape" - } - ] - }, - "valueCodeableConcept": { - "text": "Round" - } - }, - { - "type": { - "coding": [ - { - "system": "http://example.org/property-type", - "code": "scoring", - "display": "Scoring" - } - ] - }, - "valueBoolean": true - }, - { - "type": { - "coding": [ - { - "system": "http://example.org/property-type", - "code": "release-profile", - "display": "Release Profile" - } - ] - }, - "valueCodeableConcept": { - "text": "Immediate release" - } - } - ] - } -} diff --git a/packages/fhir/test/resources/manufacturedItemDefinitions.json.license b/packages/fhir/test/resources/manufacturedItemDefinitions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/manufacturedItemDefinitions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/manufacturedItemDefinitions.test.ts b/packages/fhir/test/resources/manufacturedItemDefinitions.test.ts deleted file mode 100644 index 507569e3..00000000 --- a/packages/fhir/test/resources/manufacturedItemDefinitions.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ManufacturedItemDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirManufacturedItemDefinition, - type untypedManufacturedItemDefinitionSchema, -} from '../../src/index.js' - -describe('ManufacturedItemDefinition Resource', () => { - it('should validate FHIR manufacturedItemDefinition from manufacturedItemDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/manufacturedItemDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = - FhirManufacturedItemDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/measureReports.json b/packages/fhir/test/resources/measureReports.json deleted file mode 100644 index 90a640a7..00000000 --- a/packages/fhir/test/resources/measureReports.json +++ /dev/null @@ -1,324 +0,0 @@ -{ - "0": { - "resourceType": "MeasureReport", - "id": "measurereport-example-0", - "status": "complete", - "type": "summary", - "measure": "Measure/diabetes-quality", - "period": { - "start": "2024-01-01", - "end": "2024-12-31" - } - }, - "1": { - "resourceType": "MeasureReport", - "id": "measurereport-example-1", - "identifier": [ - { - "system": "http://example.org/identifiers", - "value": "MR-001" - } - ], - "status": "complete", - "type": "individual", - "measure": "http://example.org/Measure/hba1c-control", - "subject": { - "reference": "Patient/patient-example-1" - }, - "date": "2024-11-15T10:00:00Z", - "reporter": { - "reference": "Organization/reporting-org" - }, - "period": { - "start": "2024-01-01", - "end": "2024-12-31" - }, - "improvementNotation": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-improvement-notation", - "code": "increase", - "display": "Increased score indicates improvement" - } - ] - }, - "group": [ - { - "population": [ - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "initial-population", - "display": "Initial Population" - } - ] - }, - "count": 1 - }, - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "numerator", - "display": "Numerator" - } - ] - }, - "count": 1 - }, - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "denominator", - "display": "Denominator" - } - ] - }, - "count": 1 - } - ], - "measureScore": { - "value": 1.0 - } - } - ] - }, - "2": { - "resourceType": "MeasureReport", - "id": "measurereport-example-2", - "status": "complete", - "type": "summary", - "measure": "Measure/screening-compliance", - "date": "2024-11-20T14:00:00Z", - "period": { - "start": "2024-07-01", - "end": "2024-09-30" - }, - "group": [ - { - "code": { - "text": "Q3 2024 Screening Rates" - }, - "population": [ - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "initial-population", - "display": "Initial Population" - } - ] - }, - "count": 1000 - }, - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "numerator", - "display": "Numerator" - } - ] - }, - "count": 850 - } - ], - "measureScore": { - "value": 0.85 - }, - "stratifier": [ - { - "code": [ - { - "text": "Age Group" - } - ], - "stratum": [ - { - "value": { - "text": "50-64" - }, - "population": [ - { - "code": { - "text": "numerator" - }, - "count": 500 - } - ], - "measureScore": { - "value": 0.83 - } - }, - { - "value": { - "text": "65-75" - }, - "population": [ - { - "code": { - "text": "numerator" - }, - "count": 350 - } - ], - "measureScore": { - "value": 0.88 - } - } - ] - } - ] - } - ] - }, - "3": { - "resourceType": "MeasureReport", - "id": "measurereport-example-3", - "identifier": [ - { - "system": "http://example.org/measurereports", - "value": "MR-Q4-2024" - } - ], - "status": "complete", - "type": "summary", - "measure": "http://example.org/Measure/readmission-rate", - "date": "2024-11-30T16:00:00Z", - "reporter": { - "reference": "Organization/hospital-quality-dept", - "display": "Hospital Quality Department" - }, - "period": { - "start": "2024-10-01", - "end": "2024-12-31" - }, - "improvementNotation": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-improvement-notation", - "code": "decrease", - "display": "Decreased score indicates improvement" - } - ] - }, - "group": [ - { - "code": { - "text": "30-Day Readmission Rate" - }, - "population": [ - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "initial-population", - "display": "Initial Population" - } - ] - }, - "count": 5000 - }, - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "numerator", - "display": "Numerator" - } - ] - }, - "count": 450 - }, - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "denominator", - "display": "Denominator" - } - ] - }, - "count": 5000 - } - ], - "measureScore": { - "value": 0.09 - }, - "stratifier": [ - { - "code": [ - { - "text": "Service Line" - } - ], - "stratum": [ - { - "value": { - "text": "Cardiology" - }, - "population": [ - { - "code": { - "text": "denominator" - }, - "count": 1200 - }, - { - "code": { - "text": "numerator" - }, - "count": 96 - } - ], - "measureScore": { - "value": 0.08 - } - }, - { - "value": { - "text": "Orthopedics" - }, - "population": [ - { - "code": { - "text": "denominator" - }, - "count": 800 - }, - { - "code": { - "text": "numerator" - }, - "count": 64 - } - ], - "measureScore": { - "value": 0.08 - } - } - ] - } - ] - } - ], - "evaluatedResource": [ - { - "reference": "Encounter/encounter-1" - }, - { - "reference": "Encounter/encounter-2" - } - ] - } -} diff --git a/packages/fhir/test/resources/measureReports.json.license b/packages/fhir/test/resources/measureReports.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/measureReports.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/measureReports.test.ts b/packages/fhir/test/resources/measureReports.test.ts deleted file mode 100644 index 10ce7165..00000000 --- a/packages/fhir/test/resources/measureReports.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type MeasureReport } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirMeasureReport, - type untypedMeasureReportSchema, -} from '../../src/index.js' - -describe('MeasureReport Resource', () => { - it('should validate FHIR measure reports from measureReports.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/measureReports.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirMeasureReport.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/measures.json b/packages/fhir/test/resources/measures.json deleted file mode 100644 index 76886f9b..00000000 --- a/packages/fhir/test/resources/measures.json +++ /dev/null @@ -1,327 +0,0 @@ -{ - "0": { - "resourceType": "Measure", - "id": "measure-example-0", - "status": "draft", - "name": "DiabetesQualityMeasure", - "title": "Diabetes Quality Measure", - "description": "Measure for diabetes care quality" - }, - "1": { - "resourceType": "Measure", - "id": "measure-example-1", - "url": "http://example.org/Measure/hba1c-control", - "identifier": [ - { - "system": "http://example.org/identifiers", - "value": "M-001" - } - ], - "version": "1.0.0", - "name": "HbA1cControlMeasure", - "title": "HbA1c Control Measure", - "subtitle": "Diabetes Management Quality Measure", - "status": "active", - "experimental": false, - "date": "2024-04-01T12:00:00Z", - "publisher": "Quality Improvement Team", - "contact": [ - { - "name": "Quality Measures Team", - "telecom": [ - { - "system": "email", - "value": "quality@example.org" - } - ] - } - ], - "description": "Percentage of patients with diabetes whose HbA1c is under control (< 7%)", - "purpose": "To measure the quality of diabetes management", - "copyright": "Copyright 2024 Example Healthcare Organization", - "approvalDate": "2024-03-15", - "lastReviewDate": "2024-10-01", - "effectivePeriod": { - "start": "2024-04-01", - "end": "2025-04-01" - }, - "topic": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/definition-topic", - "code": "assessment", - "display": "Assessment" - } - ] - } - ], - "author": [ - { - "name": "Dr. Quality", - "telecom": [ - { - "system": "email", - "value": "drquality@example.org" - } - ] - } - ], - "scoring": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-scoring", - "code": "proportion", - "display": "Proportion" - } - ] - }, - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-type", - "code": "process", - "display": "Process" - } - ] - } - ], - "improvementNotation": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-improvement-notation", - "code": "increase", - "display": "Increased score indicates improvement" - } - ] - }, - "group": [ - { - "population": [ - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "initial-population", - "display": "Initial Population" - } - ] - }, - "criteria": { - "language": "text/cql", - "expression": "Initial Population" - } - }, - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "numerator", - "display": "Numerator" - } - ] - }, - "criteria": { - "language": "text/cql", - "expression": "Numerator" - } - }, - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "denominator", - "display": "Denominator" - } - ] - }, - "criteria": { - "language": "text/cql", - "expression": "Denominator" - } - } - ] - } - ] - }, - "2": { - "resourceType": "Measure", - "id": "measure-example-2", - "status": "active", - "name": "ScreeningComplianceMeasure", - "title": "Cancer Screening Compliance Measure", - "description": "Measure for tracking cancer screening compliance", - "scoring": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-scoring", - "code": "proportion", - "display": "Proportion" - } - ] - }, - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-type", - "code": "process", - "display": "Process" - } - ] - } - ], - "subjectCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/resource-types", - "code": "Patient", - "display": "Patient" - } - ] - }, - "group": [ - { - "description": "Patients aged 50-75 who received screening", - "population": [ - { - "code": { - "text": "Initial Population" - }, - "criteria": { - "language": "text/fhirpath", - "expression": "Patient.age >= 50 and Patient.age <= 75" - } - } - ] - } - ] - }, - "3": { - "resourceType": "Measure", - "id": "measure-example-3", - "url": "http://example.org/Measure/readmission-rate", - "version": "2.0.0", - "name": "HospitalReadmissionRate", - "title": "30-Day Hospital Readmission Rate", - "status": "active", - "date": "2024-09-01T14:00:00Z", - "publisher": "Hospital Quality Department", - "description": "Measure of 30-day all-cause hospital readmission rate", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "approvalDate": "2024-08-15", - "lastReviewDate": "2024-11-15", - "effectivePeriod": { - "start": "2024-09-01", - "end": "2025-09-01" - }, - "topic": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/definition-topic", - "code": "treatment", - "display": "Treatment" - } - ] - } - ], - "author": [ - { - "name": "Quality Analytics Team", - "telecom": [ - { - "system": "email", - "value": "analytics@example.org" - } - ] - } - ], - "disclaimer": "This measure is intended for quality improvement purposes only", - "scoring": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-scoring", - "code": "proportion", - "display": "Proportion" - } - ] - }, - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-type", - "code": "outcome", - "display": "Outcome" - } - ] - } - ], - "rationale": "Hospital readmissions are costly and often preventable with appropriate discharge planning", - "clinicalRecommendationStatement": "Effective discharge planning and follow-up care can reduce readmission rates", - "improvementNotation": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-improvement-notation", - "code": "decrease", - "display": "Decreased score indicates improvement" - } - ] - }, - "guidance": "Calculate based on all hospital admissions during the measurement period", - "group": [ - { - "description": "All-cause readmissions within 30 days", - "population": [ - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "initial-population", - "display": "Initial Population" - } - ] - }, - "description": "All inpatient admissions", - "criteria": { - "language": "text/cql", - "expression": "Initial Population" - } - }, - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "numerator", - "display": "Numerator" - } - ] - }, - "description": "Readmissions within 30 days", - "criteria": { - "language": "text/cql", - "expression": "Numerator" - } - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/measures.json.license b/packages/fhir/test/resources/measures.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/measures.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/measures.test.ts b/packages/fhir/test/resources/measures.test.ts deleted file mode 100644 index dcd00a7c..00000000 --- a/packages/fhir/test/resources/measures.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Measure } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirMeasure, type untypedMeasureSchema } from '../../src/index.js' - -describe('Measure Resource', () => { - it('should validate FHIR measures from measures.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/measures.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirMeasure.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/media.json b/packages/fhir/test/resources/media.json deleted file mode 100644 index e2d478f9..00000000 --- a/packages/fhir/test/resources/media.json +++ /dev/null @@ -1,240 +0,0 @@ -{ - "0": { - "resourceType": "Media", - "id": "example-media-1", - "identifier": [ - { - "system": "http://example.org/media", - "value": "MEDIA-001" - } - ], - "status": "completed", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/media-type", - "code": "image", - "display": "Image" - } - ] - }, - "modality": { - "coding": [ - { - "system": "http://dicom.nema.org/resources/ontology/DCM", - "code": "DX", - "display": "Digital Radiography" - } - ] - }, - "view": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "399067008", - "display": "Lateral view" - } - ] - }, - "subject": { - "reference": "Patient/example-patient-1" - }, - "encounter": { - "reference": "Encounter/example-encounter-1" - }, - "createdDateTime": "2024-01-15T10:30:00Z", - "issued": "2024-01-15T10:35:00Z", - "operator": { - "reference": "Practitioner/example-radiologist" - }, - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "267336000", - "display": "Chest pain" - } - ] - } - ], - "bodySite": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "51185008", - "display": "Thorax" - } - ] - }, - "deviceName": "Acme Digital X-Ray System", - "height": 1024, - "width": 1024, - "content": { - "contentType": "image/jpeg", - "url": "https://example.org/media/chest-xray-001.jpg", - "title": "Chest X-Ray Lateral View", - "creation": "2024-01-15T10:30:00Z" - } - }, - "1": { - "resourceType": "Media", - "id": "example-media-2", - "status": "completed", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/media-type", - "code": "video", - "display": "Video" - } - ] - }, - "subject": { - "reference": "Patient/example-patient-2" - }, - "createdPeriod": { - "start": "2024-02-01T14:00:00Z", - "end": "2024-02-01T14:05:00Z" - }, - "operator": { - "reference": "Practitioner/example-surgeon" - }, - "deviceName": "Olympus Endoscope", - "frames": 9000, - "duration": 300, - "content": { - "contentType": "video/mp4", - "url": "https://example.org/media/endoscopy-video-001.mp4", - "title": "Upper GI Endoscopy", - "creation": "2024-02-01T14:00:00Z" - }, - "note": [ - { - "text": "Normal mucosa observed throughout examination" - } - ] - }, - "2": { - "resourceType": "Media", - "id": "example-media-3", - "identifier": [ - { - "system": "http://example.org/media", - "value": "MEDIA-003" - } - ], - "basedOn": [ - { - "reference": "ServiceRequest/example-ultrasound-request" - } - ], - "status": "completed", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/media-type", - "code": "image", - "display": "Image" - } - ] - }, - "modality": { - "coding": [ - { - "system": "http://dicom.nema.org/resources/ontology/DCM", - "code": "US", - "display": "Ultrasound" - } - ] - }, - "subject": { - "reference": "Patient/example-patient-3" - }, - "encounter": { - "reference": "Encounter/example-encounter-3" - }, - "createdDateTime": "2024-03-10T09:15:00Z", - "operator": { - "reference": "Practitioner/example-sonographer" - }, - "bodySite": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "113345001", - "display": "Abdomen" - } - ] - }, - "deviceName": "GE Ultrasound System", - "device": { - "reference": "Device/example-ultrasound-device" - }, - "height": 512, - "width": 512, - "content": { - "contentType": "image/dicom", - "data": "BASE64_ENCODED_DICOM_DATA_HERE", - "title": "Abdominal Ultrasound" - } - }, - "3": { - "resourceType": "Media", - "id": "example-media-4", - "status": "completed", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/media-type", - "code": "audio", - "display": "Audio" - } - ] - }, - "subject": { - "reference": "Patient/example-patient-4" - }, - "encounter": { - "reference": "Encounter/example-encounter-4" - }, - "createdDateTime": "2024-04-05T11:00:00Z", - "issued": "2024-04-05T11:30:00Z", - "operator": { - "reference": "Practitioner/example-cardiologist" - }, - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "301113001", - "display": "Finding of heart sounds" - } - ] - } - ], - "bodySite": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "80891009", - "display": "Heart" - } - ] - }, - "deviceName": "Electronic Stethoscope Model-X", - "duration": 30, - "content": { - "contentType": "audio/mp4", - "url": "https://example.org/media/heart-sounds-001.mp4", - "title": "Heart Sounds Recording", - "creation": "2024-04-05T11:00:00Z" - }, - "note": [ - { - "text": "S1 and S2 heart sounds clearly audible, no murmurs detected" - } - ] - } -} diff --git a/packages/fhir/test/resources/media.json.license b/packages/fhir/test/resources/media.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/media.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/media.test.ts b/packages/fhir/test/resources/media.test.ts deleted file mode 100644 index 54bdb3fa..00000000 --- a/packages/fhir/test/resources/media.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Media } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirMedia, type untypedMediaSchema } from '../../src/index.js' - -describe('Media Resource', () => { - it('should validate FHIR Media from media.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/media.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirMedia.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/medicationAdministrations.json b/packages/fhir/test/resources/medicationAdministrations.json deleted file mode 100644 index dc48450b..00000000 --- a/packages/fhir/test/resources/medicationAdministrations.json +++ /dev/null @@ -1,235 +0,0 @@ -{ - "0": { - "resourceType": "MedicationAdministration", - "id": "example-complete", - "meta": { - "versionId": "1", - "lastUpdated": "2021-04-05T10:30:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/MedicationAdministration" - ] - }, - "status": "completed", - "category": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/medication-admin-category", - "code": "inpatient", - "display": "Inpatient" - } - ] - }, - "medicationCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1049630", - "display": "Acetaminophen 325 MG Oral Tablet" - } - ], - "text": "Acetaminophen 325mg tablet" - }, - "subject": { - "reference": "Patient/example", - "display": "John Doe" - }, - "context": { - "reference": "Encounter/example" - }, - "supportingInformation": [ - { - "reference": "Observation/example" - } - ], - "effectivePeriod": { - "start": "2021-04-05T09:00:00Z", - "end": "2021-04-05T09:30:00Z" - }, - "performer": [ - { - "function": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/med-admin-perform-function", - "code": "performer", - "display": "Performer" - } - ] - }, - "actor": { - "reference": "Practitioner/example", - "display": "Dr. Jane Smith" - } - } - ], - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "25064002", - "display": "Headache" - } - ] - } - ], - "reasonReference": [ - { - "reference": "Condition/example" - } - ], - "request": { - "reference": "MedicationRequest/example" - }, - "dosage": { - "text": "325mg orally every 6 hours as needed for pain", - "site": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "181220002", - "display": "Entire oral cavity" - } - ] - }, - "route": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "26643006", - "display": "Oral Route" - } - ] - }, - "method": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/med-admin-method", - "code": "swallow", - "display": "Swallow" - } - ] - }, - "dose": { - "value": 325, - "unit": "mg", - "system": "http://unitsofmeasure.org", - "code": "mg" - }, - "rateRatio": { - "numerator": { - "value": 1, - "unit": "tablet", - "system": "http://unitsofmeasure.org", - "code": "{tablet}" - }, - "denominator": { - "value": 6, - "unit": "h", - "system": "http://unitsofmeasure.org", - "code": "h" - } - } - }, - "eventHistory": [ - { - "reference": "Provenance/example" - } - ] - }, - "1": { - "resourceType": "MedicationAdministration", - "id": "example-in-progress", - "status": "in-progress", - "medicationReference": { - "reference": "Medication/med1" - }, - "subject": { - "reference": "Patient/example2" - }, - "effectiveDateTime": "2021-04-06T14:00:00Z", - "performer": [ - { - "actor": { - "reference": "Practitioner/example2", - "display": "Nurse Alice" - } - } - ], - "dosage": { - "text": "5 mL orally every 8 hours", - "route": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "26643006", - "display": "Oral Route" - } - ] - }, - "dose": { - "value": 5, - "unit": "mL", - "system": "http://unitsofmeasure.org", - "code": "mL" - } - } - }, - "2": { - "resourceType": "MedicationAdministration", - "id": "example-on-hold", - "status": "on-hold", - "medicationCodeableConcept": { - "text": "Ibuprofen 200mg tablet" - }, - "subject": { - "reference": "Patient/example3" - }, - "effectiveDateTime": "2021-04-07T10:15:00Z", - "reasonCode": [ - { - "text": "Patient requested hold due to nausea" - } - ], - "request": { - "reference": "MedicationRequest/ibuprofen" - } - }, - "3": { - "resourceType": "MedicationAdministration", - "id": "example-not-done", - "status": "not-done", - "statusReason": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/medication-admin-status-reason", - "code": "patient-refusal", - "display": "Patient Refusal" - } - ] - } - ], - "medicationCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "197361", - "display": "Amoxicillin 250mg" - } - ] - }, - "subject": { - "reference": "Patient/example4" - }, - "effectiveDateTime": "2021-04-08T08:00:00Z", - "performer": [ - { - "actor": { - "reference": "Practitioner/example3", - "display": "Dr. Robert Brown" - } - } - ] - } -} diff --git a/packages/fhir/test/resources/medicationAdministrations.json.license b/packages/fhir/test/resources/medicationAdministrations.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/medicationAdministrations.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/medicationAdministrations.test.ts b/packages/fhir/test/resources/medicationAdministrations.test.ts deleted file mode 100644 index 8e4ee60f..00000000 --- a/packages/fhir/test/resources/medicationAdministrations.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type MedicationAdministration } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirMedicationAdministration, - type untypedMedicationAdministrationSchema, -} from '../../src/index.js' - -describe('MedicationAdministration Resource', () => { - it('should validate FHIR medication administrations from medicationAdministrations.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/medicationAdministrations.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirMedicationAdministration.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/medicationDispenses.json b/packages/fhir/test/resources/medicationDispenses.json deleted file mode 100644 index 29913efb..00000000 --- a/packages/fhir/test/resources/medicationDispenses.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "0": { - "resourceType": "MedicationDispense", - "id": "example-complete", - "meta": { - "versionId": "1", - "lastUpdated": "2021-04-05T12:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/MedicationDispense"] - }, - "identifier": [ - { - "system": "http://hospital.org/dispense", - "value": "123456" - } - ], - "status": "completed", - "statusReasonCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", - "code": "ALG", - "display": "Allergy" - } - ] - }, - "category": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/medicationdispense-category", - "code": "outpatient", - "display": "Outpatient" - } - ] - }, - "medicationCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1049630", - "display": "Acetaminophen 325 MG Oral Tablet" - } - ], - "text": "Acetaminophen 325mg tablet" - }, - "subject": { - "reference": "Patient/example", - "display": "John Doe" - }, - "context": { - "reference": "Encounter/example" - }, - "supportingInformation": [ - { - "reference": "Observation/example" - } - ], - "performer": [ - { - "function": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/med-dispense-perform-function", - "code": "pharmacist", - "display": "Pharmacist" - } - ] - }, - "actor": { - "reference": "Practitioner/example", - "display": "Dr. Jane Smith" - } - } - ], - "location": { - "reference": "Location/pharmacy" - }, - "authorizingPrescription": [ - { - "reference": "MedicationRequest/example" - } - ], - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "RFP", - "display": "Refill - Part Fill" - } - ] - }, - "quantity": { - "value": 30, - "unit": "tablet", - "system": "http://unitsofmeasure.org", - "code": "{tablet}" - }, - "daysSupply": { - "value": 10, - "unit": "days", - "system": "http://unitsofmeasure.org", - "code": "d" - }, - "whenPrepared": "2021-04-05T11:00:00Z", - "whenHandedOver": "2021-04-05T12:00:00Z", - "destination": { - "reference": "Location/patient-home" - }, - "receiver": [ - { - "reference": "Patient/example", - "display": "John Doe" - } - ], - "note": [ - { - "text": "Patient advised to take with food." - } - ], - "dosageInstruction": [ - { - "text": "Take 1 tablet every 6 hours as needed for pain", - "timing": { - "repeat": { - "frequency": 1, - "period": 6, - "periodUnit": "h" - } - }, - "asNeededBoolean": true, - "route": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "26643006", - "display": "Oral Route" - } - ] - }, - "doseAndRate": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/dose-rate-type", - "code": "ordered", - "display": "Ordered" - } - ] - }, - "doseQuantity": { - "value": 325, - "unit": "mg", - "system": "http://unitsofmeasure.org", - "code": "mg" - } - } - ] - } - ], - "substitution": { - "wasSubstituted": false, - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActSubstanceAdminSubstitutionCode", - "code": "E", - "display": "equivalent" - } - ] - }, - "reason": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", - "code": "FP", - "display": "Formulary Policy" - } - ] - } - ] - }, - "eventHistory": [ - { - "reference": "Provenance/example" - } - ] - }, - "1": { - "resourceType": "MedicationDispense", - "id": "example-in-progress", - "status": "in-progress", - "medicationReference": { - "reference": "Medication/med1" - }, - "subject": { - "reference": "Patient/example2" - }, - "quantity": { - "value": 100, - "unit": "mL", - "system": "http://unitsofmeasure.org", - "code": "mL" - }, - "daysSupply": { - "value": 7, - "unit": "days", - "system": "http://unitsofmeasure.org", - "code": "d" - }, - "whenPrepared": "2021-04-06T09:00:00Z", - "performer": [ - { - "actor": { - "reference": "Practitioner/example2", - "display": "Nurse Alice" - } - } - ] - }, - "2": { - "resourceType": "MedicationDispense", - "id": "example-on-hold", - "status": "on-hold", - "medicationCodeableConcept": { - "text": "Ibuprofen 200mg tablet" - }, - "subject": { - "reference": "Patient/example3" - }, - "authorizingPrescription": [ - { - "reference": "MedicationRequest/ibuprofen" - } - ], - "quantity": { - "value": 20, - "unit": "tablet", - "system": "http://unitsofmeasure.org", - "code": "{tablet}" - }, - "note": [ - { - "text": "Patient requested to delay dispense until next week." - } - ] - }, - "3": { - "resourceType": "MedicationDispense", - "id": "example-declined", - "status": "declined", - "statusReasonCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/medicationdispense-status-reason", - "code": "patient-request", - "display": "Patient Request" - } - ] - }, - "medicationCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "197361", - "display": "Amoxicillin 250mg" - } - ] - }, - "subject": { - "reference": "Patient/example4" - }, - "performer": [ - { - "actor": { - "reference": "Practitioner/example3", - "display": "Dr. Robert Brown" - } - } - ], - "note": [ - { - "text": "Patient refused medication pickup." - } - ] - } -} diff --git a/packages/fhir/test/resources/medicationDispenses.json.license b/packages/fhir/test/resources/medicationDispenses.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/medicationDispenses.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/medicationDispenses.test.ts b/packages/fhir/test/resources/medicationDispenses.test.ts deleted file mode 100644 index 33b32063..00000000 --- a/packages/fhir/test/resources/medicationDispenses.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type MedicationDispense } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirMedicationDispense, - type untypedMedicationDispenseSchema, -} from '../../src/index.js' - -describe('MedicationDispense Resource', () => { - it('should validate FHIR medication dispenses from medicationDispenses.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/medicationDispenses.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirMedicationDispense.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/medicationKnowledges.json b/packages/fhir/test/resources/medicationKnowledges.json deleted file mode 100644 index a383e008..00000000 --- a/packages/fhir/test/resources/medicationKnowledges.json +++ /dev/null @@ -1,175 +0,0 @@ -{ - "0": { - "resourceType": "MedicationKnowledge", - "id": "med-001", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1049630", - "display": "Amoxicillin 250mg capsule" - } - ], - "text": "Amoxicillin 250mg capsule" - }, - "status": "active", - "manufacturer": { "reference": "Organization/123" }, - "doseForm": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "385055001", - "display": "Capsule" - } - ], - "text": "Capsule" - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "7070", - "display": "Amoxicillin" - } - ] - }, - "isActive": true, - "strength": { - "numerator": { "value": 250, "unit": "mg" }, - "denominator": { "value": 1, "unit": "capsule" } - } - } - ], - "regulatory": [ - { - "regulatoryAuthority": { "reference": "Organization/456" } - } - ] - }, - "1": { - "resourceType": "MedicationKnowledge", - "id": "example", - "text": { - "status": "generated", - "div": "

Generated Narrative

Resource "example"

code: Vancomycin Hydrochloride (VANCOMYCIN HYDROCHLORIDE) (National drug codes#0409-6531-02)

status: active

manufacturer:

Resource "org4"

name: Pfizer Laboratories Div Pfizer Inc

doseForm: Oral Dose Form (qualifier value) (SNOMED CT#385268001)

amount: 50 mg/ml

synonym: Vancomycin Hydrochloride (VANCOMYCIN HYDROCHLORIDE)

" - }, - "contained": [ - { - "resourceType": "Organization", - "id": "org4", - "name": "Pfizer Laboratories Div Pfizer Inc" - } - ], - "code": { - "coding": [ - { - "system": "http://hl7.org/fhir/sid/ndc", - "code": "0409-6531-02", - "display": "Vancomycin Hydrochloride (VANCOMYCIN HYDROCHLORIDE)" - } - ] - }, - "status": "active", - "manufacturer": { - "reference": "#org4" - }, - "doseForm": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "385268001", - "display": "Oral Dose Form (qualifier value)" - } - ] - }, - "amount": { - "value": 50, - "unit": "mg/ml" - }, - "synonym": ["Vancomycin Hydrochloride (VANCOMYCIN HYDROCHLORIDE)"] - }, - "2": { - "resourceType": "MedicationKnowledge", - "id": "med-003", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1049629", - "display": "Azithromycin 500mg tablet" - } - ], - "text": "Azithromycin 500mg tablet" - }, - "status": "active", - "manufacturer": { "reference": "Organization/321" }, - "doseForm": { "text": "Tablet" }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1979", - "display": "Azithromycin" - } - ] - }, - "isActive": true, - "strength": { - "numerator": { "value": 500, "unit": "mg" }, - "denominator": { "value": 1, "unit": "tablet" } - } - } - ], - "monitoringProgram": [ - { - "type": { "text": "Hepatic function" }, - "name": "Liver enzyme monitoring" - } - ] - }, - "3": { - "resourceType": "MedicationKnowledge", - "id": "med-004", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "861013", - "display": "Paracetamol 500mg tablet" - } - ], - "text": "Paracetamol 500mg tablet" - }, - "status": "active", - "manufacturer": { "reference": "Organization/654" }, - "doseForm": { "text": "Tablet" }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "161", - "display": "Paracetamol" - } - ] - }, - "isActive": true, - "strength": { - "numerator": { "value": 500, "unit": "mg" }, - "denominator": { "value": 1, "unit": "tablet" } - } - } - ], - "kinetics": [ - { - "areaUnderCurve": [{ "value": 25, "unit": "mg*h/L" }], - "halfLifePeriod": { "value": 2, "unit": "h" } - } - ] - } -} diff --git a/packages/fhir/test/resources/medicationKnowledges.json.license b/packages/fhir/test/resources/medicationKnowledges.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/medicationKnowledges.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/medicationKnowledges.test.ts b/packages/fhir/test/resources/medicationKnowledges.test.ts deleted file mode 100644 index bbf55d22..00000000 --- a/packages/fhir/test/resources/medicationKnowledges.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type MedicationKnowledge } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirMedicationKnowledge, - type untypedMedicationKnowledgeSchema, -} from '../../src/index.js' - -describe('MedicationKnowledge Resource', () => { - it('should validate FHIR medication knowledges from medicationKnowledges.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/medicationKnowledges.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirMedicationKnowledge.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/medicationRequests.json b/packages/fhir/test/resources/medicationRequests.json deleted file mode 100644 index fe5645bb..00000000 --- a/packages/fhir/test/resources/medicationRequests.json +++ /dev/null @@ -1,175 +0,0 @@ -{ - "0": { - "resourceType": "MedicationRequest", - "id": "medreq-example-0", - "status": "active", - "intent": "order", - "medicationCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "313782", - "display": "Acetaminophen 325 MG Oral Tablet" - } - ] - }, - "subject": { - "reference": "Patient/patient-example" - }, - "authoredOn": "2024-11-15" - }, - "1": { - "resourceType": "MedicationRequest", - "id": "medreq-example-1", - "identifier": [ - { - "system": "http://example.org/prescriptions", - "value": "RX-12345" - } - ], - "status": "active", - "intent": "order", - "priority": "routine", - "medicationCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "197361", - "display": "Lisinopril 10 MG Oral Tablet" - } - ] - }, - "subject": { - "reference": "Patient/patient-example-2" - }, - "encounter": { - "reference": "Encounter/encounter-example" - }, - "authoredOn": "2024-10-20T14:30:00Z", - "requester": { - "reference": "Practitioner/practitioner-example" - }, - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "38341003", - "display": "Hypertension" - } - ] - } - ], - "dosageInstruction": [ - { - "text": "Take one tablet by mouth once daily", - "timing": { - "repeat": { - "frequency": 1, - "period": 1, - "periodUnit": "d" - } - }, - "route": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "26643006", - "display": "Oral route" - } - ] - } - } - ], - "dispenseRequest": { - "numberOfRepeatsAllowed": 3, - "quantity": { - "value": 30, - "unit": "tablet" - }, - "expectedSupplyDuration": { - "value": 30, - "unit": "days" - } - } - }, - "2": { - "resourceType": "MedicationRequest", - "id": "medreq-example-2", - "status": "stopped", - "statusReason": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/medicationrequest-status-reason", - "code": "altchoice", - "display": "Try another treatment first" - } - ] - }, - "intent": "order", - "medicationCodeableConcept": { - "text": "Medication X" - }, - "subject": { - "reference": "Patient/patient-example-3" - }, - "authoredOn": "2024-09-10" - }, - "3": { - "resourceType": "MedicationRequest", - "id": "medreq-example-3", - "status": "completed", - "intent": "order", - "priority": "urgent", - "medicationReference": { - "reference": "Medication/med-insulin" - }, - "subject": { - "reference": "Patient/patient-diabetes" - }, - "authoredOn": "2024-11-01T08:00:00Z", - "requester": { - "reference": "Practitioner/practitioner-endo" - }, - "reasonReference": [ - { - "reference": "Condition/condition-diabetes" - } - ], - "note": [ - { - "text": "Adjust dose based on blood glucose monitoring" - } - ], - "dosageInstruction": [ - { - "sequence": 1, - "text": "Inject 10 units subcutaneously before breakfast", - "timing": { - "repeat": { - "when": ["ACM"] - } - }, - "route": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "34206005", - "display": "Subcutaneous route" - } - ] - }, - "doseAndRate": [ - { - "doseQuantity": { - "value": 10, - "unit": "units", - "system": "http://unitsofmeasure.org", - "code": "U" - } - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/medicationRequests.json.license b/packages/fhir/test/resources/medicationRequests.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/medicationRequests.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/medicationRequests.test.ts b/packages/fhir/test/resources/medicationRequests.test.ts deleted file mode 100644 index f4b0dcc9..00000000 --- a/packages/fhir/test/resources/medicationRequests.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type MedicationRequest } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirMedicationRequest, - type untypedMedicationRequestSchema, -} from '../../src/index.js' - -describe('MedicationRequest Resource', () => { - it('should validate FHIR medicationRequest from medicationRequests.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/medicationRequests.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirMedicationRequest.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/medicationStatements.json b/packages/fhir/test/resources/medicationStatements.json deleted file mode 100644 index 8c0f330c..00000000 --- a/packages/fhir/test/resources/medicationStatements.json +++ /dev/null @@ -1,210 +0,0 @@ -{ - "0": { - "resourceType": "MedicationStatement", - "id": "example-complete", - "meta": { - "versionId": "1", - "lastUpdated": "2021-04-05T15:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/MedicationStatement"] - }, - "identifier": [ - { - "system": "http://hospital.org/medicationstatement", - "value": "ms-12345" - } - ], - "status": "active", - "statusReason": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/medication-statement-status-reason", - "code": "medication-authorized", - "display": "Medication Authorized" - } - ] - } - ], - "category": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/medication-statement-category", - "code": "inpatient", - "display": "Inpatient" - } - ] - }, - "medicationCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1049630", - "display": "Acetaminophen 325 MG Oral Tablet" - } - ], - "text": "Acetaminophen 325mg tablet" - }, - "subject": { - "reference": "Patient/example", - "display": "John Doe" - }, - "context": { - "reference": "Encounter/example" - }, - "effectivePeriod": { - "start": "2021-04-01T09:00:00Z", - "end": "2021-04-10T09:00:00Z" - }, - "dateAsserted": "2021-04-01T09:00:00Z", - "informationSource": { - "reference": "Practitioner/example", - "display": "Dr. Jane Smith" - }, - "derivedFrom": [ - { - "reference": "MedicationRequest/example" - } - ], - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "25064002", - "display": "Headache" - } - ] - } - ], - "note": [ - { - "text": "Patient reports occasional use for headaches." - } - ], - "dosage": [ - { - "text": "325mg orally every 6 hours as needed for pain", - "timing": { - "repeat": { - "frequency": 1, - "period": 6, - "periodUnit": "h" - } - }, - "asNeededBoolean": true, - "site": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "181220002", - "display": "Entire oral cavity" - } - ] - }, - "route": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "26643006", - "display": "Oral Route" - } - ] - }, - "doseAndRate": [ - { - "doseQuantity": { - "value": 325, - "unit": "mg", - "system": "http://unitsofmeasure.org", - "code": "mg" - } - } - ] - } - ] - }, - "1": { - "resourceType": "MedicationStatement", - "id": "example-completed", - "status": "completed", - "medicationReference": { - "reference": "Medication/med1" - }, - "subject": { - "reference": "Patient/example2" - }, - "effectiveDateTime": "2021-04-06T10:00:00Z", - "informationSource": { - "reference": "Patient/example2" - }, - "dosage": [ - { - "text": "5 mL orally every 8 hours", - "route": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "26643006", - "display": "Oral Route" - } - ] - }, - "doseAndRate": [ - { - "doseQuantity": { - "value": 5, - "unit": "mL", - "system": "http://unitsofmeasure.org", - "code": "mL" - } - } - ] - } - ] - }, - "2": { - "resourceType": "MedicationStatement", - "id": "example-intended", - "status": "intended", - "medicationCodeableConcept": { - "text": "Ibuprofen 200mg tablet" - }, - "subject": { - "reference": "Patient/example3" - }, - "effectiveDateTime": "2021-04-07T08:00:00Z", - "reasonCode": [ - { - "text": "Patient intends to use for minor joint pain." - } - ], - "note": [ - { - "text": "Patient has not started taking yet." - } - ] - }, - "3": { - "resourceType": "MedicationStatement", - "id": "example-entered-in-error", - "status": "entered-in-error", - "medicationCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "197361", - "display": "Amoxicillin 250mg" - } - ] - }, - "subject": { - "reference": "Patient/example4" - }, - "dateAsserted": "2021-04-08T12:00:00Z", - "note": [ - { - "text": "Entered in error, patient never took this medication." - } - ] - } -} diff --git a/packages/fhir/test/resources/medicationStatements.json.license b/packages/fhir/test/resources/medicationStatements.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/medicationStatements.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/medicationStatements.test.ts b/packages/fhir/test/resources/medicationStatements.test.ts deleted file mode 100644 index b5fbd004..00000000 --- a/packages/fhir/test/resources/medicationStatements.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type MedicationStatement } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirMedicationStatement, - type untypedMedicationStatementSchema, -} from '../../src/index.js' - -describe('MedicationStatement Resource', () => { - it('should validate FHIR medication statements from medicationStatements.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/medicationStatements.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirMedicationStatement.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/medications.json b/packages/fhir/test/resources/medications.json deleted file mode 100644 index c820ca5d..00000000 --- a/packages/fhir/test/resources/medications.json +++ /dev/null @@ -1,5728 +0,0 @@ -{ - "1808": { - "197417": { - "id": "197417", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "197417", - "display": "Bumetanide (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1808", - "display": "Bumetanide" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 0.5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "197418": { - "id": "197418", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "197418", - "display": "Bumetanide (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1808", - "display": "Bumetanide" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 1, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "197419": { - "id": "197419", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "197419", - "display": "Bumetanide (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1808", - "display": "Bumetanide" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 2, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "1998": { - "308962": { - "id": "308962", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "308962", - "display": "Captopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1998", - "display": "Captopril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 100, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "308963": { - "id": "308963", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "308963", - "display": "Captopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1998", - "display": "Captopril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 12.5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "308964": { - "id": "308964", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "308964", - "display": "Captopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1998", - "display": "Captopril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 50, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "317173": { - "id": "317173", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "317173", - "display": "Captopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1998", - "display": "Captopril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 25, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "3827": { - "858804": { - "id": "858804", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "858804", - "display": "Enalapril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "3827", - "display": "Enalapril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 2.5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "858810": { - "id": "858810", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "858810", - "display": "Enalapril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "3827", - "display": "Enalapril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 20, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "858813": { - "id": "858813", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "858813", - "display": "Enalapril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "3827", - "display": "Enalapril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "858817": { - "id": "858817", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "858817", - "display": "Enalapril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "3827", - "display": "Enalapril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 10, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "4109": { - "1251903": { - "id": "1251903", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1251903", - "display": "Ethacrynate (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "4109", - "display": "Ethacrynic Acid" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 25, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "1251907": { - "id": "1251907", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1251907", - "display": "Ethacrynate (Oral Tablet)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "4109", - "display": "Ethacrynic Acid" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 50, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "4603": { - "197732": { - "id": "197732", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "197732", - "display": "Furosemide (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "4603", - "display": "Furosemide" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 80, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "310429": { - "id": "310429", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "310429", - "display": "Furosemide (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "4603", - "display": "Furosemide" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 20, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "313988": { - "id": "313988", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "313988", - "display": "Furosemide (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "4603", - "display": "Furosemide" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 40, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "9997": { - "198222": { - "id": "198222", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "198222", - "display": "Spironolactone (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "9997", - "display": "Spironolactone" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 100, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "198223": { - "id": "198223", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "198223", - "display": "Spironolactone (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "9997", - "display": "Spironolactone" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 50, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "313096": { - "id": "313096", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "313096", - "display": "Spironolactone (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "9997", - "display": "Spironolactone" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 25, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "18867": { - "898687": { - "id": "898687", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "898687", - "display": "Benazepril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "18867", - "display": "Benazepril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 10, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "898690": { - "id": "898690", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "898690", - "display": "Benazepril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "18867", - "display": "Benazepril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 20, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "898719": { - "id": "898719", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "898719", - "display": "Benazepril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "18867", - "display": "Benazepril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 40, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "898723": { - "id": "898723", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "898723", - "display": "Benazepril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "18867", - "display": "Benazepril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "19484": { - "854901": { - "id": "854901", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "854901", - "display": "Bisoprolol (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "19484", - "display": "Bisoprolol" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 10, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "854905": { - "id": "854905", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "854905", - "display": "Bisoprolol (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "19484", - "display": "Bisoprolol" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "20352": { - "200031": { - "id": "200031", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "200031", - "display": "Carvedilol (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "20352", - "display": "Carvedilol" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 6.25, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "200032": { - "id": "200032", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "200032", - "display": "Carvedilol (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "20352", - "display": "Carvedilol" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 12.5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "200033": { - "id": "200033", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "200033", - "display": "Carvedilol (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "20352", - "display": "Carvedilol" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 25, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "686924": { - "id": "686924", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "686924", - "display": "Carvedilol (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "20352", - "display": "Carvedilol" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 3.125, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "29046": { - "197884": { - "id": "197884", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "197884", - "display": "Lisinopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "29046", - "display": "Lisinopril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 40, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "205326": { - "id": "205326", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "205326", - "display": "Lisinopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "29046", - "display": "Lisinopril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 30, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "311353": { - "id": "311353", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "311353", - "display": "Lisinopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "29046", - "display": "Lisinopril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 2.5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "311354": { - "id": "311354", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "311354", - "display": "Lisinopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "29046", - "display": "Lisinopril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "314076": { - "id": "314076", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "314076", - "display": "Lisinopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "29046", - "display": "Lisinopril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 10, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "314077": { - "id": "314077", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "314077", - "display": "Lisinopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "29046", - "display": "Lisinopril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 20, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "30131": { - "1299896": { - "id": "1299896", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1299896", - "display": "Moexipril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "30131", - "display": "Moexipril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 15, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "1299897": { - "id": "1299897", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1299897", - "display": "Moexipril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "30131", - "display": "Moexipril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 7.5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "35208": { - "312748": { - "id": "312748", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "312748", - "display": "Quinapril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "35208", - "display": "Quinapril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 10, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "312749": { - "id": "312749", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "312749", - "display": "Quinapril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "35208", - "display": "Quinapril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 20, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "312750": { - "id": "312750", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "312750", - "display": "Quinapril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "35208", - "display": "Quinapril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "314203": { - "id": "314203", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "314203", - "display": "Quinapril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "35208", - "display": "Quinapril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 40, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "35296": { - "198188": { - "id": "198188", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "198188", - "display": "Ramipril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "35296", - "display": "Ramipril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 2.5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Capsule", - "display": "Oral Capsule" - } - ], - "text": "Oral Capsule" - } - }, - "198189": { - "id": "198189", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "198189", - "display": "Ramipril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "35296", - "display": "Ramipril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Capsule", - "display": "Oral Capsule" - } - ], - "text": "Oral Capsule" - } - }, - "251856": { - "id": "251856", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "251856", - "display": "Ramipril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "35296", - "display": "Ramipril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 2.5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "251857": { - "id": "251857", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "251857", - "display": "Ramipril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "35296", - "display": "Ramipril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "261962": { - "id": "261962", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "261962", - "display": "Ramipril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "35296", - "display": "Ramipril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 10, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Capsule", - "display": "Oral Capsule" - } - ], - "text": "Oral Capsule" - } - }, - "401965": { - "id": "401965", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "401965", - "display": "Ramipril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "35296", - "display": "Ramipril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 1.25, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "401968": { - "id": "401968", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "401968", - "display": "Ramipril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "35296", - "display": "Ramipril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 10, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "845488": { - "id": "845488", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "845488", - "display": "Ramipril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "35296", - "display": "Ramipril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 1.25, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Capsule", - "display": "Oral Capsule" - } - ], - "text": "Oral Capsule" - } - } - }, - "38413": { - "198369": { - "id": "198369", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "198369", - "display": "Torsemide (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "38413", - "display": "Torsemide" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 10, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "198370": { - "id": "198370", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "198370", - "display": "Torsemide (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "38413", - "display": "Torsemide" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 100, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "198371": { - "id": "198371", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "198371", - "display": "Torsemide (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "38413", - "display": "Torsemide" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 20, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "198372": { - "id": "198372", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "198372", - "display": "Torsemide (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "38413", - "display": "Torsemide" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "2589881": { - "id": "2589881", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "2589881", - "display": "Torsemide (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "38413", - "display": "Torsemide" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 40, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "2589885": { - "id": "2589885", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "2589885", - "display": "Torsemide (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "38413", - "display": "Torsemide" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 60, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "38454": { - "199351": { - "id": "199351", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "199351", - "display": "Trandolapril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "38454", - "display": "Trandolapril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 2, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "199352": { - "id": "199352", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "199352", - "display": "Trandolapril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "38454", - "display": "Trandolapril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 4, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "199353": { - "id": "199353", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "199353", - "display": "Trandolapril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "38454", - "display": "Trandolapril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 1, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "54552": { - "854925": { - "id": "854925", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "854925", - "display": "Perindopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "54552", - "display": "Perindopril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 8, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "854984": { - "id": "854984", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "854984", - "display": "Perindopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "54552", - "display": "Perindopril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 2, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "854988": { - "id": "854988", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "854988", - "display": "Perindopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "54552", - "display": "Perindopril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 4, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "69749": { - "349199": { - "id": "349199", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "349199", - "display": "Valsartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "69749", - "display": "Valsartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 80, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "349200": { - "id": "349200", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "349200", - "display": "Valsartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "69749", - "display": "Valsartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 320, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "349201": { - "id": "349201", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "349201", - "display": "Valsartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "69749", - "display": "Valsartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 160, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "349483": { - "id": "349483", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "349483", - "display": "Valsartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "69749", - "display": "Valsartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 40, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "73494": { - "205304": { - "id": "205304", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "205304", - "display": "Telmisartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "73494", - "display": "Telmisartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 40, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "205305": { - "id": "205305", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "205305", - "display": "Telmisartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "73494", - "display": "Telmisartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 80, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "282755": { - "id": "282755", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "282755", - "display": "Telmisartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "73494", - "display": "Telmisartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 20, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "83515": { - "310139": { - "id": "310139", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "310139", - "display": "Eprosartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "83515", - "display": "Eprosartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 400, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "310140": { - "id": "310140", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "310140", - "display": "Eprosartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "83515", - "display": "Eprosartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 600, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "83818": { - "200094": { - "id": "200094", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "200094", - "display": "Irbesartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "83818", - "display": "Irbesartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 75, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "200095": { - "id": "200095", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "200095", - "display": "Irbesartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "83818", - "display": "Irbesartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 150, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "200096": { - "id": "200096", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "200096", - "display": "Irbesartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "83818", - "display": "Irbesartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 300, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "118463": { - "349373": { - "id": "349373", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "349373", - "display": "Olmesartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "118463", - "display": "Olmesartan Medoxomil" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "349401": { - "id": "349401", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "349401", - "display": "Olmesartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "118463", - "display": "Olmesartan Medoxomil" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 20, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "349405": { - "id": "349405", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "349405", - "display": "Olmesartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "118463", - "display": "Olmesartan Medoxomil" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 40, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "203160": { - "979480": { - "id": "979480", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "979480", - "display": "Losartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "203160", - "display": "Losartan Potassium" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 100, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "979485": { - "id": "979485", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "979485", - "display": "Losartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "203160", - "display": "Losartan Potassium" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 25, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "979492": { - "id": "979492", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "979492", - "display": "Losartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "203160", - "display": "Losartan Potassium" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 50, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "214354": { - "153822": { - "id": "153822", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "153822", - "display": "Candesartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "214354", - "display": "Candesartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 4, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "153823": { - "id": "153823", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "153823", - "display": "Candesartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "214354", - "display": "Candesartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 8, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "577776": { - "id": "577776", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "577776", - "display": "Candesartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "214354", - "display": "Candesartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 16, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "639537": { - "id": "639537", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "639537", - "display": "Candesartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "214354", - "display": "Candesartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 32, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "221124": { - "866412": { - "id": "866412", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "866412", - "display": "Metoprolol XR (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "221124", - "display": "Metoprolol Succinate" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 100, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Extended Release Oral Tablet", - "display": "Extended Release Oral Tablet" - } - ], - "text": "Extended Release Oral Tablet" - } - }, - "866419": { - "id": "866419", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "866419", - "display": "Metoprolol XR (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "221124", - "display": "Metoprolol Succinate" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 200, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Extended Release Oral Tablet", - "display": "Extended Release Oral Tablet" - } - ], - "text": "Extended Release Oral Tablet" - } - }, - "866427": { - "id": "866427", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "866427", - "display": "Metoprolol XR (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "221124", - "display": "Metoprolol Succinate" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 25, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Extended Release Oral Tablet", - "display": "Extended Release Oral Tablet" - } - ], - "text": "Extended Release Oral Tablet" - } - }, - "866436": { - "id": "866436", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "866436", - "display": "Metoprolol XR (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "221124", - "display": "Metoprolol Succinate" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 50, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Extended Release Oral Tablet", - "display": "Extended Release Oral Tablet" - } - ], - "text": "Extended Release Oral Tablet" - } - }, - "1999031": { - "id": "1999031", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1999031", - "display": "Metoprolol XR (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "221124", - "display": "Metoprolol Succinate" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 100, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Extended Release Oral Capsule", - "display": "Extended Release Oral Capsule" - } - ], - "text": "Extended Release Oral Capsule" - } - }, - "1999033": { - "id": "1999033", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1999033", - "display": "Metoprolol XR (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "221124", - "display": "Metoprolol Succinate" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 200, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Extended Release Oral Capsule", - "display": "Extended Release Oral Capsule" - } - ], - "text": "Extended Release Oral Capsule" - } - }, - "1999035": { - "id": "1999035", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1999035", - "display": "Metoprolol XR (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "221124", - "display": "Metoprolol Succinate" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 25, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Extended Release Oral Capsule", - "display": "Extended Release Oral Capsule" - } - ], - "text": "Extended Release Oral Capsule" - } - }, - "1999037": { - "id": "1999037", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1999037", - "display": "Metoprolol XR (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "221124", - "display": "Metoprolol Succinate" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 50, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Extended Release Oral Capsule", - "display": "Extended Release Oral Capsule" - } - ], - "text": "Extended Release Oral Capsule" - } - } - }, - "227278": { - "857169": { - "id": "857169", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "857169", - "display": "Fosinopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "227278", - "display": "Fosinopril Sodium" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 10, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "857183": { - "id": "857183", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "857183", - "display": "Fosinopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "227278", - "display": "Fosinopril Sodium" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 20, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "857187": { - "id": "857187", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "857187", - "display": "Fosinopril (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "227278", - "display": "Fosinopril Sodium" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 40, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "298869": { - "351256": { - "id": "351256", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "351256", - "display": "Eplerenone (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "298869", - "display": "Eplerenone" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 25, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "351257": { - "id": "351257", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "351257", - "display": "Eplerenone (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "298869", - "display": "Eplerenone" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 50, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "668310": { - "860510": { - "id": "860510", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "860510", - "display": "Carvedilol XR (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "668310", - "display": "Carvedilol Phosphate" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 10, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Extended Release Oral Capsule", - "display": "Extended Release Oral Capsule" - } - ], - "text": "Extended Release Oral Capsule" - } - }, - "860516": { - "id": "860516", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "860516", - "display": "Carvedilol XR (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "668310", - "display": "Carvedilol Phosphate" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 20, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Extended Release Oral Capsule", - "display": "Extended Release Oral Capsule" - } - ], - "text": "Extended Release Oral Capsule" - } - }, - "860522": { - "id": "860522", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "860522", - "display": "Carvedilol XR (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "668310", - "display": "Carvedilol Phosphate" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 40, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Extended Release Oral Capsule", - "display": "Extended Release Oral Capsule" - } - ], - "text": "Extended Release Oral Capsule" - } - }, - "860532": { - "id": "860532", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "860532", - "display": "Carvedilol XR (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "668310", - "display": "Carvedilol Phosphate" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 80, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Extended Release Oral Capsule", - "display": "Extended Release Oral Capsule" - } - ], - "text": "Extended Release Oral Capsule" - } - } - }, - "1091642": { - "1091646": { - "id": "1091646", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1091646", - "display": "Azilsartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1091642", - "display": "Azilsartan Medoxomil" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 40, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "1091652": { - "id": "1091652", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1091652", - "display": "Azilsartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1091642", - "display": "Azilsartan Medoxomil" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 80, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "1373458": { - "1373463": { - "id": "1373463", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1373463", - "display": "Canagliflozin (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1373458", - "display": "Canagliflozin" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 100, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "1373471": { - "id": "1373471", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1373471", - "display": "Canagliflozin (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1373458", - "display": "Canagliflozin" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 300, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "1488564": { - "1488569": { - "id": "1488569", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1488569", - "display": "Dapagliflozin (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1488564", - "display": "Dapagliflozin" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 10, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "1488574": { - "id": "1488574", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1488574", - "display": "Dapagliflozin (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1488564", - "display": "Dapagliflozin" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "1545653": { - "1545658": { - "id": "1545658", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1545658", - "display": "Empagliflozin (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1545653", - "display": "Empagliflozin" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 10, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "1545666": { - "id": "1545666", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1545666", - "display": "Empagliflozin (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1545653", - "display": "Empagliflozin" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 25, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "1656339": { - "1656340": { - "id": "1656340", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1656340", - "display": "Sacubitril/Valsartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1656328", - "display": "Sacubitril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 24, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - }, - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "69749", - "display": "Valsartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 26, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "1656349": { - "id": "1656349", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1656349", - "display": "Sacubitril/Valsartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1656328", - "display": "Sacubitril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 49, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - }, - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "69749", - "display": "Valsartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 51, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "1656354": { - "id": "1656354", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1656354", - "display": "Sacubitril/Valsartan (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1656328", - "display": "Sacubitril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 97, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - }, - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "69749", - "display": "Valsartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 103, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "2679793": { - "id": "2679793", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "2679793", - "display": "Sacubitril/Valsartan (Oral Pellet)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1656328", - "display": "Sacubitril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 15, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - }, - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "69749", - "display": "Valsartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 16, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Pellet", - "display": "Oral Pellet" - } - ], - "text": "Oral Pellet" - } - }, - "2679800": { - "id": "2679800", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "2679800", - "display": "Sacubitril/Valsartan (Oral Pellet)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1656328", - "display": "Sacubitril" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 6, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - }, - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "69749", - "display": "Valsartan" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 6, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Pellet", - "display": "Oral Pellet" - } - ], - "text": "Oral Pellet" - } - } - }, - "1992672": { - "1992810": { - "id": "1992810", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1992810", - "display": "Ertugliflozin (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1992672", - "display": "Ertugliflozin" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 5, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "1992819": { - "id": "1992819", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1992819", - "display": "Ertugliflozin (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "1992672", - "display": "Ertugliflozin" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 15, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "2627044": { - "2637859": { - "id": "2637859", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "2637859", - "display": "Bexagliflozin (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "2627044", - "display": "Bexagliflozin" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 20, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - }, - "2638675": { - "2638683": { - "id": "2638683", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "2638683", - "display": "Sotagliflozin (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "2638675", - "display": "Sotagliflozin" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 200, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - }, - "2638691": { - "id": "2638691", - "resourceType": "Medication", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "2638691", - "display": "Sotagliflozin (Oral Pill)" - } - ] - }, - "ingredient": [ - { - "itemCodeableConcept": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "2638675", - "display": "Sotagliflozin" - } - ] - }, - "strength": { - "numerator": { - "system": "http://unitsofmeasure.org", - "code": "mg", - "value": 400, - "unit": "mg" - }, - "denominator": { - "value": 1 - } - } - } - ], - "form": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "Oral Tablet", - "display": "Oral Tablet" - } - ], - "text": "Oral Tablet" - } - } - } -} diff --git a/packages/fhir/test/resources/medications.json.license b/packages/fhir/test/resources/medications.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/medications.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/medications.test.ts b/packages/fhir/test/resources/medications.test.ts deleted file mode 100644 index ec5af9ba..00000000 --- a/packages/fhir/test/resources/medications.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Medication } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirMedication, - type untypedMedicationSchema, -} from '../../src/index.js' - -describe('Medication Resource', () => { - it('should validate FHIR medications from medications.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/medications.json', 'utf-8') - const decodedJson: unknown = JSON.parse(data) - - if ( - typeof decodedJson !== 'object' || - decodedJson === null || - Array.isArray(decodedJson) - ) { - throw new TypeError('Expected medication categories to be an object') - } - - // drugs.json contains nested structure: {categoryId: {drugId: medicationResource}} - Object.values(decodedJson).forEach((categoryData: unknown) => { - if ( - typeof categoryData !== 'object' || - categoryData === null || - Array.isArray(categoryData) - ) { - throw new TypeError('Expected a medication category to be an object') - } - - Object.values(categoryData).forEach((medicationData: unknown) => { - const fhirResource = FhirMedication.parse(medicationData).value - expect(jsonStringifyDeterministically(medicationData)).toBe( - jsonStringifyDeterministically(fhirResource), - ) - }) - }) - }) -}) diff --git a/packages/fhir/test/resources/medicinalProductDefinitions.json b/packages/fhir/test/resources/medicinalProductDefinitions.json deleted file mode 100644 index fcd12818..00000000 --- a/packages/fhir/test/resources/medicinalProductDefinitions.json +++ /dev/null @@ -1,159 +0,0 @@ -{ - "0": { - "resourceType": "MedicinalProductDefinition", - "id": "medicinal-product-example-0", - "name": [ - { - "productName": "Aspirin 100mg Tablets" - } - ] - }, - "1": { - "resourceType": "MedicinalProductDefinition", - "id": "medicinal-product-example-1", - "identifier": [ - { - "system": "http://example.org/medicinal-products", - "value": "MP-12345" - } - ], - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/medicinal-product-type", - "code": "MedicinalProduct", - "display": "Medicinal Product" - } - ] - }, - "name": [ - { - "productName": "Paracetamol 500mg Tablets", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/medicinal-product-name-type", - "code": "full", - "display": "Full name" - } - ] - } - } - ], - "classification": [ - { - "coding": [ - { - "system": "http://www.whocc.no/atc", - "code": "N02BE01", - "display": "paracetamol" - } - ] - } - ] - }, - "2": { - "resourceType": "MedicinalProductDefinition", - "id": "medicinal-product-example-2", - "identifier": [ - { - "system": "http://example.org/medicinal-products", - "value": "MP-67890" - } - ], - "domain": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/medicinal-product-domain", - "code": "Human", - "display": "Human use" - } - ] - }, - "description": "Insulin injection for diabetes management", - "combinedPharmaceuticalDoseForm": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/dose-form", - "code": "385219001", - "display": "Injection solution" - } - ] - }, - "name": [ - { - "productName": "Insulin Human 100 IU/ml Solution for Injection" - } - ], - "code": [ - { - "system": "http://snomed.info/sct", - "code": "325072002", - "display": "Insulin preparation" - } - ] - }, - "3": { - "resourceType": "MedicinalProductDefinition", - "id": "medicinal-product-example-3", - "identifier": [ - { - "system": "http://example.org/medicinal-products", - "value": "MP-54321" - } - ], - "status": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/medicinal-product-status", - "code": "active", - "display": "Active" - } - ] - }, - "name": [ - { - "productName": "Amoxicillin 250mg/5ml Oral Suspension", - "namePart": [ - { - "part": "Amoxicillin", - "type": { - "coding": [ - { - "code": "INN", - "display": "International Nonproprietary Name" - } - ] - } - }, - { - "part": "250mg/5ml", - "type": { - "coding": [ - { - "code": "STR", - "display": "Strength" - } - ] - } - } - ] - } - ], - "contact": [ - { - "type": { - "coding": [ - { - "code": "manufacturer", - "display": "Manufacturer" - } - ] - }, - "contact": { - "reference": "Organization/pharma-co" - } - } - ] - } -} diff --git a/packages/fhir/test/resources/medicinalProductDefinitions.json.license b/packages/fhir/test/resources/medicinalProductDefinitions.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/medicinalProductDefinitions.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/medicinalProductDefinitions.test.ts b/packages/fhir/test/resources/medicinalProductDefinitions.test.ts deleted file mode 100644 index deeaa67d..00000000 --- a/packages/fhir/test/resources/medicinalProductDefinitions.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type MedicinalProductDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirMedicinalProductDefinition, - type untypedMedicinalProductDefinitionSchema, -} from '../../src/index.js' - -describe('MedicinalProductDefinition Resource', () => { - it('should validate FHIR medicinal product definition from medicinalProductDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/medicinalProductDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = - FhirMedicinalProductDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/messageDefinitions.json b/packages/fhir/test/resources/messageDefinitions.json deleted file mode 100644 index 66b279f4..00000000 --- a/packages/fhir/test/resources/messageDefinitions.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "0": { - "resourceType": "MessageDefinition", - "id": "patient-link", - "url": "http://example.org/fhir/MessageDefinition/patient-link", - "name": "PatientLink", - "status": "active", - "date": "2023-01-15T10:00:00Z", - "eventCoding": { - "system": "http://example.org/fhir/message-events", - "code": "admin-notify" - }, - "category": "notification", - "focus": [ - { - "code": "Patient", - "min": 2, - "max": "2" - } - ] - }, - "1": { - "resourceType": "MessageDefinition", - "id": "observation-provide", - "url": "http://example.org/fhir/MessageDefinition/observation-provide", - "name": "ObservationProvide", - "title": "Observation Provide Message", - "status": "draft", - "date": "2023-06-01T14:30:00Z", - "publisher": "Example Health System", - "eventUri": "http://example.org/events/observation-provide", - "category": "consequence", - "responseRequired": "always", - "focus": [ - { - "code": "Observation", - "profile": "http://hl7.org/fhir/StructureDefinition/vitalsigns", - "min": 1, - "max": "*" - } - ] - }, - "2": { - "resourceType": "MessageDefinition", - "id": "communication-request", - "url": "http://example.org/fhir/MessageDefinition/communication-request", - "name": "CommunicationRequest", - "status": "active", - "date": "2023-03-20T09:15:00Z", - "eventCoding": { - "system": "http://terminology.hl7.org/CodeSystem/message-events", - "code": "communication-request" - }, - "category": "currency", - "responseRequired": "on-error", - "focus": [ - { - "code": "CommunicationRequest", - "min": 1, - "max": "1" - } - ], - "allowedResponse": [ - { - "message": "http://example.org/fhir/MessageDefinition/communication-response" - } - ] - }, - "3": { - "resourceType": "MessageDefinition", - "id": "medication-administration", - "url": "http://example.org/fhir/MessageDefinition/medication-administration", - "name": "MedicationAdministration", - "status": "active", - "experimental": true, - "date": "2023-09-10T16:45:00Z", - "publisher": "Clinical Systems Inc", - "contact": [ - { - "name": "Support Team", - "telecom": [ - { - "system": "email", - "value": "support@example.org" - } - ] - } - ], - "eventCoding": { - "system": "http://example.org/message-events", - "code": "medication-admin" - }, - "category": "notification", - "responseRequired": "never", - "focus": [ - { - "code": "MedicationAdministration", - "min": 1, - "max": "*" - }, - { - "code": "Patient", - "min": 1, - "max": "1" - } - ], - "graph": ["http://example.org/fhir/GraphDefinition/medication-context"] - } -} diff --git a/packages/fhir/test/resources/messageDefinitions.json.license b/packages/fhir/test/resources/messageDefinitions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/messageDefinitions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/messageDefinitions.test.ts b/packages/fhir/test/resources/messageDefinitions.test.ts deleted file mode 100644 index a49ea594..00000000 --- a/packages/fhir/test/resources/messageDefinitions.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type MessageDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirMessageDefinition, - type untypedMessageDefinitionSchema, -} from '../../src/index.js' - -describe('MessageDefinition Resource', () => { - it('should validate FHIR message definition from messageDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/messageDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirMessageDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/messageHeaders.json b/packages/fhir/test/resources/messageHeaders.json deleted file mode 100644 index 6f2f556f..00000000 --- a/packages/fhir/test/resources/messageHeaders.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "0": { - "resourceType": "MessageHeader", - "id": "msgheader-example-0", - "eventCoding": { - "system": "http://example.org/fhir/message-events", - "code": "patient-link" - }, - "source": { - "endpoint": "https://example.org/fhir" - } - }, - "1": { - "resourceType": "MessageHeader", - "id": "msgheader-example-1", - "eventCoding": { - "system": "http://terminology.hl7.org/CodeSystem/message-events", - "code": "notification", - "display": "Notification" - }, - "destination": [ - { - "name": "Central System", - "endpoint": "https://central.example.org/fhir" - } - ], - "sender": { - "reference": "Organization/org-example" - }, - "source": { - "name": "Source System", - "software": "FHIR Server v2.0", - "version": "2.0.1", - "endpoint": "https://source.example.org/fhir" - }, - "reason": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/message-reasons-encounter", - "code": "admit", - "display": "Admit" - } - ] - } - }, - "2": { - "resourceType": "MessageHeader", - "id": "msgheader-example-2", - "eventCoding": { - "system": "http://example.org/events", - "code": "test-event" - }, - "source": { - "endpoint": "https://test.example.org" - }, - "response": { - "identifier": "msg-12345", - "code": "ok" - } - }, - "3": { - "resourceType": "MessageHeader", - "id": "msgheader-example-3", - "eventCoding": { - "system": "http://terminology.hl7.org/CodeSystem/message-events", - "code": "communication-request" - }, - "destination": [ - { - "name": "Destination A", - "target": { - "reference": "Device/device-example" - }, - "endpoint": "https://dest-a.example.org/fhir", - "receiver": { - "reference": "Practitioner/practitioner-example" - } - } - ], - "sender": { - "reference": "Organization/org-sender" - }, - "author": { - "reference": "Practitioner/practitioner-author" - }, - "source": { - "name": "Clinical System", - "software": "EMR v3.5", - "version": "3.5.2", - "endpoint": "https://emr.example.org/fhir" - }, - "responsible": { - "reference": "Organization/org-responsible" - }, - "focus": [ - { - "reference": "Patient/patient-example" - } - ] - } -} diff --git a/packages/fhir/test/resources/messageHeaders.json.license b/packages/fhir/test/resources/messageHeaders.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/messageHeaders.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/messageHeaders.test.ts b/packages/fhir/test/resources/messageHeaders.test.ts deleted file mode 100644 index 327b0337..00000000 --- a/packages/fhir/test/resources/messageHeaders.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type MessageHeader } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirMessageHeader, - type untypedMessageHeaderSchema, -} from '../../src/index.js' - -describe('MessageHeader Resource', () => { - it('should validate FHIR messageHeader from messageHeaders.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/messageHeaders.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirMessageHeader.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/molecularSequences.json b/packages/fhir/test/resources/molecularSequences.json deleted file mode 100644 index 09d934f2..00000000 --- a/packages/fhir/test/resources/molecularSequences.json +++ /dev/null @@ -1,187 +0,0 @@ -{ - "0": { - "resourceType": "MolecularSequence", - "id": "example-molecular-sequence-1", - "identifier": [ - { - "system": "http://example.org/molecular-sequences", - "value": "SEQ-001" - } - ], - "type": "dna", - "coordinateSystem": 0, - "patient": { - "reference": "Patient/example-patient-1" - }, - "specimen": { - "reference": "Specimen/example-specimen-1" - }, - "device": { - "reference": "Device/example-sequencer" - }, - "performer": { - "reference": "Organization/example-lab" - }, - "observedSeq": "ACGTACGTACGT", - "readCoverage": 50, - "referenceSeq": { - "chromosome": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/chromosome-human", - "code": "1", - "display": "chromosome 1" - } - ] - }, - "genomeBuild": "GRCh38", - "referenceSeqId": { - "coding": [ - { - "system": "http://www.ncbi.nlm.nih.gov/nuccore", - "code": "NC_000001.11" - } - ] - }, - "strand": "watson", - "windowStart": 1000, - "windowEnd": 1012 - } - }, - "1": { - "resourceType": "MolecularSequence", - "id": "example-molecular-sequence-2", - "type": "rna", - "coordinateSystem": 1, - "patient": { - "reference": "Patient/example-patient-2" - }, - "observedSeq": "ACGUACGUACGU", - "referenceSeq": { - "referenceSeqId": { - "coding": [ - { - "system": "http://www.ncbi.nlm.nih.gov/nuccore", - "code": "NM_001005484.1" - } - ] - }, - "strand": "watson", - "windowStart": 1, - "windowEnd": 13 - }, - "variant": [ - { - "start": 5, - "end": 5, - "observedAllele": "G", - "referenceAllele": "A" - } - ] - }, - "2": { - "resourceType": "MolecularSequence", - "id": "example-molecular-sequence-3", - "identifier": [ - { - "system": "http://example.org/molecular-sequences", - "value": "SEQ-003" - } - ], - "type": "aa", - "coordinateSystem": 1, - "patient": { - "reference": "Patient/example-patient-3" - }, - "specimen": { - "reference": "Specimen/example-protein-specimen" - }, - "observedSeq": "MTEYKLVVVGAGGVGKSALTIQLIQNHFVDEYDPTIEDSYRKQVVIDGETCLLDILDTAGQEEYSAMRDQYMRTGEGFLCVFAINNTKSFEDIHQYREQIKRVKDSDDVPMVLVGNKCDLAARTVESRQAQDLARSYGIPYIETSAKTRQGVEDAFYTLVREIRQHKLRKLNPPDESGPGCMSCKCVLS", - "referenceSeq": { - "referenceSeqId": { - "coding": [ - { - "system": "http://www.ncbi.nlm.nih.gov/protein", - "code": "NP_004976.2" - } - ] - }, - "windowStart": 1, - "windowEnd": 189 - } - }, - "3": { - "resourceType": "MolecularSequence", - "id": "example-molecular-sequence-4", - "type": "dna", - "coordinateSystem": 0, - "patient": { - "reference": "Patient/example-patient-4" - }, - "specimen": { - "reference": "Specimen/example-blood-specimen" - }, - "performer": { - "reference": "Organization/example-genomics-lab" - }, - "observedSeq": "ATGC", - "readCoverage": 100, - "referenceSeq": { - "chromosome": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/chromosome-human", - "code": "X", - "display": "chromosome X" - } - ] - }, - "genomeBuild": "GRCh38", - "referenceSeqId": { - "coding": [ - { - "system": "http://www.ncbi.nlm.nih.gov/nuccore", - "code": "NC_000023.11" - } - ] - }, - "strand": "crick", - "windowStart": 5000, - "windowEnd": 5004 - }, - "quality": [ - { - "type": "snp", - "standardSequence": { - "coding": [ - { - "system": "http://www.ncbi.nlm.nih.gov/nuccore", - "code": "NC_000023.11" - } - ] - }, - "start": 5000, - "end": 5004, - "score": { - "value": 30 - }, - "method": { - "coding": [ - { - "system": "http://loinc.org", - "code": "LA6364-4", - "display": "Quality score" - } - ] - } - } - ], - "repository": [ - { - "type": "openapi", - "url": "https://example.org/sequence-repository", - "name": "Example Sequence Repository" - } - ] - } -} diff --git a/packages/fhir/test/resources/molecularSequences.json.license b/packages/fhir/test/resources/molecularSequences.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/molecularSequences.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/molecularSequences.test.ts b/packages/fhir/test/resources/molecularSequences.test.ts deleted file mode 100644 index 84c071af..00000000 --- a/packages/fhir/test/resources/molecularSequences.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type MolecularSequence } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirMolecularSequence, - type untypedMolecularSequenceSchema, -} from '../../src/index.js' - -describe('MolecularSequence Resource', () => { - it('should validate FHIR MolecularSequences from molecularSequences.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/molecularSequences.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirMolecularSequence.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/namingSystems.json b/packages/fhir/test/resources/namingSystems.json deleted file mode 100644 index 0e38f403..00000000 --- a/packages/fhir/test/resources/namingSystems.json +++ /dev/null @@ -1,203 +0,0 @@ -{ - "0": { - "resourceType": "NamingSystem", - "id": "example-namingsystem-0", - "name": "ExampleNamingSystem0", - "status": "active", - "kind": "codesystem", - "date": "2023-01-01T00:00:00Z", - "publisher": "Example Standards Organization", - "contact": [ - { - "name": "Standards Team", - "telecom": [ - { - "system": "email", - "value": "standards@example.org" - } - ] - } - ], - "responsible": "Example Standards Organization", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "NPI", - "display": "National Provider Identifier" - } - ] - }, - "description": "An example naming system for a code system identifier", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "usage": "Use this identifier for all provider references in the system", - "uniqueId": [ - { - "type": "oid", - "value": "2.16.840.1.113883.4.642.1.0", - "preferred": true - }, - { - "type": "uri", - "value": "http://example.org/fhir/NamingSystem/example-0", - "preferred": false, - "comment": "This is the FHIR URI representation" - } - ] - }, - "1": { - "resourceType": "NamingSystem", - "id": "example-namingsystem-1", - "name": "ExampleNamingSystem1", - "status": "draft", - "kind": "identifier", - "date": "2023-06-15T12:30:00Z", - "publisher": "Healthcare Identifier Authority", - "description": "A draft naming system for patient identifiers", - "useContext": [ - { - "code": { - "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", - "code": "focus" - }, - "valueCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "116154003", - "display": "Patient" - } - ] - } - } - ], - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "GB", - "display": "United Kingdom" - } - ] - } - ], - "uniqueId": [ - { - "type": "uuid", - "value": "550e8400-e29b-41d4-a716-446655440000", - "preferred": true, - "period": { - "start": "2023-01-01" - } - }, - { - "type": "oid", - "value": "2.16.840.1.113883.4.642.1.1", - "preferred": false, - "comment": "Legacy OID identifier" - } - ] - }, - "2": { - "resourceType": "NamingSystem", - "id": "example-namingsystem-2", - "name": "ExampleNamingSystem2", - "status": "active", - "kind": "root", - "date": "2023-09-20T08:15:00Z", - "publisher": "International Health Data Exchange", - "contact": [ - { - "name": "Technical Support", - "telecom": [ - { - "system": "url", - "value": "http://example.org/support" - }, - { - "system": "phone", - "value": "+1-555-0123" - } - ] - } - ], - "responsible": "Global Healthcare Standards Committee", - "description": "A root naming system for organizational identifiers", - "usage": "This identifier system should be used for all organization references in cross-border healthcare data exchanges", - "uniqueId": [ - { - "type": "uri", - "value": "http://example.org/fhir/org-identifiers", - "preferred": true, - "comment": "Primary URI for this naming system" - }, - { - "type": "oid", - "value": "2.16.840.1.113883.3.4642.2.0", - "preferred": false, - "period": { - "start": "2020-01-01", - "end": "2025-12-31" - }, - "comment": "OID valid until end of 2025" - }, - { - "type": "other", - "value": "IHDE-ORG-v2", - "preferred": false, - "comment": "Version 2 organizational code" - } - ] - }, - "3": { - "resourceType": "NamingSystem", - "id": "example-namingsystem-3", - "name": "ExampleNamingSystem3", - "status": "retired", - "kind": "codesystem", - "date": "2022-12-31T23:59:59Z", - "publisher": "Legacy Health Systems Inc", - "description": "A retired naming system that was replaced by a newer version", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "CA", - "display": "Canada" - } - ] - } - ], - "usage": "This naming system is retired. Please use ExampleNamingSystem4 instead.", - "uniqueId": [ - { - "type": "oid", - "value": "2.16.840.1.113883.4.642.1.999", - "preferred": true, - "period": { - "start": "2015-01-01", - "end": "2022-12-31" - }, - "comment": "Retired as of December 31, 2022" - }, - { - "type": "uri", - "value": "http://example.org/fhir/legacy/naming-system-3", - "preferred": false, - "comment": "Legacy URI maintained for backward compatibility" - } - ] - } -} diff --git a/packages/fhir/test/resources/namingSystems.test.ts b/packages/fhir/test/resources/namingSystems.test.ts deleted file mode 100644 index 43681ef9..00000000 --- a/packages/fhir/test/resources/namingSystems.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type NamingSystem } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirNamingSystem, - type untypedNamingSystemSchema, -} from '../../src/index.js' - -describe('NamingSystem Resource', () => { - it('should validate FHIR NamingSystems from namingSystems.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/namingSystems.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirNamingSystem.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/nutritionOrders.json b/packages/fhir/test/resources/nutritionOrders.json deleted file mode 100644 index b9e14308..00000000 --- a/packages/fhir/test/resources/nutritionOrders.json +++ /dev/null @@ -1,213 +0,0 @@ -{ - "0": { - "resourceType": "NutritionOrder", - "id": "nutrition-order-example-0", - "status": "active", - "intent": "order", - "patient": { - "reference": "Patient/example" - }, - "dateTime": "2024-01-15T10:00:00Z" - }, - "1": { - "resourceType": "NutritionOrder", - "id": "nutrition-order-example-1", - "identifier": [ - { - "system": "http://example.org/nutrition-orders", - "value": "NO-2024-001" - } - ], - "status": "active", - "intent": "order", - "patient": { - "reference": "Patient/diabetic-patient" - }, - "encounter": { - "reference": "Encounter/inpatient-stay" - }, - "dateTime": "2024-01-15T08:00:00Z", - "orderer": { - "reference": "Practitioner/dietitian" - }, - "oralDiet": { - "type": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "386619000", - "display": "Diabetic diet" - } - ] - } - ], - "nutrient": [ - { - "modifier": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "2331003", - "display": "Carbohydrate" - } - ] - }, - "amount": { - "value": 100, - "unit": "grams", - "system": "http://unitsofmeasure.org", - "code": "g" - } - } - ], - "instruction": "Take meals at regular intervals" - } - }, - "2": { - "resourceType": "NutritionOrder", - "id": "nutrition-order-example-2", - "status": "active", - "intent": "order", - "patient": { - "reference": "Patient/elderly-patient" - }, - "dateTime": "2024-01-15T12:00:00Z", - "oralDiet": { - "type": [ - { - "text": "Soft diet" - } - ], - "texture": [ - { - "modifier": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "228059006", - "display": "Pureed" - } - ] - }, - "foodType": { - "text": "All solids" - } - } - ], - "fluidConsistencyType": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "439021000124105", - "display": "Nectar thick liquid" - } - ] - } - ] - }, - "supplement": [ - { - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "442991000124104", - "display": "High protein supplement" - } - ] - }, - "productName": "Ensure Plus", - "quantity": { - "value": 1, - "unit": "bottle" - } - } - ] - }, - "3": { - "resourceType": "NutritionOrder", - "id": "nutrition-order-example-3", - "status": "active", - "intent": "order", - "patient": { - "reference": "Patient/tube-fed-patient" - }, - "dateTime": "2024-01-15T14:00:00Z", - "allergyIntolerance": [ - { - "reference": "AllergyIntolerance/lactose" - } - ], - "foodPreferenceModifier": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/diet", - "code": "kosher", - "display": "Kosher" - } - ] - } - ], - "enteralFormula": { - "baseFormulaType": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "443051000124104", - "display": "Adult high protein formula" - } - ] - }, - "baseFormulaProductName": "Osmolite HN", - "additiveType": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/entformula-additive", - "code": "fiber", - "display": "Fiber" - } - ] - }, - "caloricDensity": { - "value": 1.2, - "unit": "calories per milliliter", - "system": "http://unitsofmeasure.org", - "code": "cal/mL" - }, - "routeofAdministration": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "385219001", - "display": "Nasogastric route" - } - ] - }, - "administration": [ - { - "rateQuantity": { - "value": 75, - "unit": "mL/hr", - "system": "http://unitsofmeasure.org", - "code": "mL/h" - } - } - ], - "maxVolumeToDeliver": { - "value": 1800, - "unit": "mL/day", - "system": "http://unitsofmeasure.org", - "code": "mL/d" - }, - "administrationInstruction": "Continuous feeding via pump" - }, - "note": [ - { - "text": "Monitor for signs of intolerance" - } - ] - } -} diff --git a/packages/fhir/test/resources/nutritionOrders.test.ts b/packages/fhir/test/resources/nutritionOrders.test.ts deleted file mode 100644 index 0b185c6b..00000000 --- a/packages/fhir/test/resources/nutritionOrders.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type NutritionOrder } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirNutritionOrder, - type untypedNutritionOrderSchema, -} from '../../src/index.js' - -describe('NutritionOrder Resource', () => { - it('should validate FHIR nutritionOrder from nutritionOrders.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/nutritionOrders.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirNutritionOrder.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/nutritionProducts.json b/packages/fhir/test/resources/nutritionProducts.json deleted file mode 100644 index 7b863eb0..00000000 --- a/packages/fhir/test/resources/nutritionProducts.json +++ /dev/null @@ -1,231 +0,0 @@ -{ - "0": { - "resourceType": "NutritionProduct", - "id": "nutrition-product-example-0", - "status": "active" - }, - "1": { - "resourceType": "NutritionProduct", - "id": "nutrition-product-example-1", - "status": "active", - "code": { - "coding": [ - { - "system": "http://example.org/nutrition-products", - "code": "protein-shake", - "display": "Protein Shake" - } - ] - }, - "category": [ - { - "coding": [ - { - "system": "http://example.org/nutrition-category", - "code": "supplement", - "display": "Nutritional Supplement" - } - ] - } - ], - "manufacturer": [ - { - "reference": "Organization/nutrition-company" - } - ], - "nutrient": [ - { - "item": { - "concept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "88878007", - "display": "Protein" - } - ] - } - }, - "amount": [ - { - "numerator": { - "value": 25, - "unit": "g" - }, - "denominator": { - "value": 1, - "unit": "serving" - } - } - ] - } - ] - }, - "2": { - "resourceType": "NutritionProduct", - "id": "nutrition-product-example-2", - "status": "active", - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "443735", - "display": "Enteral nutrition formula" - } - ] - }, - "category": [ - { - "coding": [ - { - "system": "http://example.org/nutrition-category", - "code": "enteral-formula", - "display": "Enteral Formula" - } - ] - } - ], - "ingredient": [ - { - "item": { - "concept": { - "text": "Whey protein isolate" - } - }, - "amount": [ - { - "numerator": { - "value": 15, - "unit": "g" - }, - "denominator": { - "value": 100, - "unit": "mL" - } - } - ] - }, - { - "item": { - "concept": { - "text": "Maltodextrin" - } - } - } - ], - "nutrient": [ - { - "item": { - "concept": { - "text": "Calories" - } - }, - "amount": [ - { - "numerator": { - "value": 150, - "unit": "kcal" - }, - "denominator": { - "value": 100, - "unit": "mL" - } - } - ] - }, - { - "item": { - "concept": { - "text": "Carbohydrates" - } - }, - "amount": [ - { - "numerator": { - "value": 20, - "unit": "g" - }, - "denominator": { - "value": 100, - "unit": "mL" - } - } - ] - } - ], - "instance": { - "quantity": { - "value": 500, - "unit": "mL" - }, - "lotNumber": "LOT-2024-001", - "expiry": "2025-12-31" - } - }, - "3": { - "resourceType": "NutritionProduct", - "id": "nutrition-product-example-3", - "status": "active", - "code": { - "text": "Pediatric formula" - }, - "category": [ - { - "text": "Pediatric nutrition" - } - ], - "knownAllergen": [ - { - "concept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "3718001", - "display": "Cow milk protein" - } - ] - } - } - ], - "productCharacteristic": [ - { - "type": { - "coding": [ - { - "system": "http://example.org/characteristic-type", - "code": "flavor", - "display": "Flavor" - } - ] - }, - "valueString": "Vanilla" - }, - { - "type": { - "coding": [ - { - "system": "http://example.org/characteristic-type", - "code": "osmolality", - "display": "Osmolality" - } - ] - }, - "valueQuantity": { - "value": 300, - "unit": "mOsm/kg" - } - }, - { - "type": { - "text": "Ready to use" - }, - "valueBoolean": true - } - ], - "note": [ - { - "text": "Suitable for children over 1 year of age" - } - ] - } -} diff --git a/packages/fhir/test/resources/nutritionProducts.test.ts b/packages/fhir/test/resources/nutritionProducts.test.ts deleted file mode 100644 index c578171f..00000000 --- a/packages/fhir/test/resources/nutritionProducts.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type NutritionProduct } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirNutritionProduct, - type untypedNutritionProductSchema, -} from '../../src/index.js' - -describe('NutritionProduct Resource', () => { - it('should validate FHIR nutritionProduct from nutritionProducts.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/nutritionProducts.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirNutritionProduct.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/observationDefinitions.json b/packages/fhir/test/resources/observationDefinitions.json deleted file mode 100644 index 2dcbb1c2..00000000 --- a/packages/fhir/test/resources/observationDefinitions.json +++ /dev/null @@ -1,312 +0,0 @@ -{ - "0": { - "resourceType": "ObservationDefinition", - "id": "example-observation-definition-1", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "vital-signs", - "display": "Vital Signs" - } - ] - } - ], - "code": { - "coding": [ - { - "system": "http://loinc.org", - "code": "8867-4", - "display": "Heart rate" - } - ] - }, - "identifier": [ - { - "system": "http://example.org/observation-definitions", - "value": "OBS-DEF-001" - } - ], - "permittedDataType": ["Quantity"], - "multipleResultsAllowed": false, - "preferredReportName": "Heart Rate", - "quantitativeDetails": { - "unit": { - "coding": [ - { - "system": "http://unitsofmeasure.org", - "code": "/min", - "display": "beats per minute" - } - ] - }, - "decimalPrecision": 0 - }, - "qualifiedInterval": [ - { - "category": "reference", - "range": { - "low": { - "value": 60, - "unit": "/min", - "system": "http://unitsofmeasure.org", - "code": "/min" - }, - "high": { - "value": 100, - "unit": "/min", - "system": "http://unitsofmeasure.org", - "code": "/min" - } - }, - "context": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/referencerange-meaning", - "code": "normal", - "display": "Normal Range" - } - ] - } - } - ] - }, - "1": { - "resourceType": "ObservationDefinition", - "id": "example-observation-definition-2", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "laboratory", - "display": "Laboratory" - } - ] - } - ], - "code": { - "coding": [ - { - "system": "http://loinc.org", - "code": "2339-0", - "display": "Glucose [Mass/volume] in Blood" - } - ] - }, - "permittedDataType": ["Quantity"], - "method": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "83561000052101", - "display": "Glucose measurement, blood" - } - ] - }, - "preferredReportName": "Blood Glucose", - "quantitativeDetails": { - "unit": { - "coding": [ - { - "system": "http://unitsofmeasure.org", - "code": "mg/dL", - "display": "milligrams per deciliter" - } - ] - }, - "customaryUnit": { - "coding": [ - { - "system": "http://unitsofmeasure.org", - "code": "mmol/L", - "display": "millimoles per liter" - } - ] - }, - "conversionFactor": 0.0555, - "decimalPrecision": 1 - }, - "qualifiedInterval": [ - { - "category": "reference", - "range": { - "low": { - "value": 70, - "unit": "mg/dL", - "system": "http://unitsofmeasure.org", - "code": "mg/dL" - }, - "high": { - "value": 100, - "unit": "mg/dL", - "system": "http://unitsofmeasure.org", - "code": "mg/dL" - } - }, - "context": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/referencerange-meaning", - "code": "normal", - "display": "Normal Range" - } - ] - }, - "condition": "Fasting" - } - ] - }, - "2": { - "resourceType": "ObservationDefinition", - "id": "example-observation-definition-3", - "code": { - "coding": [ - { - "system": "http://loinc.org", - "code": "8480-6", - "display": "Systolic blood pressure" - } - ] - }, - "permittedDataType": ["Quantity"], - "multipleResultsAllowed": false, - "preferredReportName": "Systolic BP", - "quantitativeDetails": { - "unit": { - "coding": [ - { - "system": "http://unitsofmeasure.org", - "code": "mm[Hg]", - "display": "millimeter of mercury" - } - ] - }, - "decimalPrecision": 0 - }, - "qualifiedInterval": [ - { - "category": "reference", - "range": { - "low": { - "value": 90, - "unit": "mm[Hg]", - "system": "http://unitsofmeasure.org", - "code": "mm[Hg]" - }, - "high": { - "value": 120, - "unit": "mm[Hg]", - "system": "http://unitsofmeasure.org", - "code": "mm[Hg]" - } - }, - "appliesTo": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "133936004", - "display": "Adult" - } - ] - } - ] - }, - { - "category": "critical", - "range": { - "high": { - "value": 180, - "unit": "mm[Hg]", - "system": "http://unitsofmeasure.org", - "code": "mm[Hg]" - } - } - } - ] - }, - "3": { - "resourceType": "ObservationDefinition", - "id": "example-observation-definition-4", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "laboratory", - "display": "Laboratory" - } - ] - } - ], - "code": { - "coding": [ - { - "system": "http://loinc.org", - "code": "789-8", - "display": "Erythrocytes [#/volume] in Blood by Automated count" - } - ] - }, - "identifier": [ - { - "system": "http://example.org/observation-definitions", - "value": "OBS-DEF-004" - } - ], - "permittedDataType": ["Quantity"], - "preferredReportName": "RBC Count", - "quantitativeDetails": { - "unit": { - "coding": [ - { - "system": "http://unitsofmeasure.org", - "code": "10*6/uL", - "display": "million per microliter" - } - ] - }, - "decimalPrecision": 2 - }, - "qualifiedInterval": [ - { - "category": "reference", - "range": { - "low": { - "value": 4.5, - "unit": "10*6/uL", - "system": "http://unitsofmeasure.org", - "code": "10*6/uL" - }, - "high": { - "value": 5.9, - "unit": "10*6/uL", - "system": "http://unitsofmeasure.org", - "code": "10*6/uL" - } - }, - "gender": "male" - }, - { - "category": "reference", - "range": { - "low": { - "value": 4.0, - "unit": "10*6/uL", - "system": "http://unitsofmeasure.org", - "code": "10*6/uL" - }, - "high": { - "value": 5.2, - "unit": "10*6/uL", - "system": "http://unitsofmeasure.org", - "code": "10*6/uL" - } - }, - "gender": "female" - } - ] - } -} diff --git a/packages/fhir/test/resources/observationDefinitions.json.license b/packages/fhir/test/resources/observationDefinitions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/observationDefinitions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/observationDefinitions.test.ts b/packages/fhir/test/resources/observationDefinitions.test.ts deleted file mode 100644 index 1bb615c6..00000000 --- a/packages/fhir/test/resources/observationDefinitions.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ObservationDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirObservationDefinition, - type untypedObservationDefinitionSchema, -} from '../../src/index.js' - -describe('ObservationDefinition Resource', () => { - it('should validate FHIR ObservationDefinitions from observationDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/observationDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirObservationDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/observationEffective.test.ts b/packages/fhir/test/resources/observationEffective.test.ts deleted file mode 100644 index dc520636..00000000 --- a/packages/fhir/test/resources/observationEffective.test.ts +++ /dev/null @@ -1,251 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Observation } from 'fhir/r4b.js' -import { FhirObservation } from '../../src/index.js' - -describe('FhirObservation', () => { - describe('effectiveDate', () => { - it('should return Date from effectiveDateTime', () => { - const observation: Observation = { - resourceType: 'Observation', - status: 'final', - code: { text: 'Test' }, - effectiveDateTime: '2024-03-15T10:30:00Z', - } - - const fhirObs = new FhirObservation(observation) - const effectiveDate = fhirObs.effectiveDate - - expect(effectiveDate).toBeInstanceOf(Date) - expect(effectiveDate?.toISOString()).toBe('2024-03-15T10:30:00.000Z') - }) - - it('should return Date from effectiveInstant', () => { - const observation: Observation = { - resourceType: 'Observation', - status: 'final', - code: { text: 'Test' }, - effectiveInstant: '2024-03-15T10:30:00.123Z', - } - - const fhirObs = new FhirObservation(observation) - const effectiveDate = fhirObs.effectiveDate - - expect(effectiveDate).toBeInstanceOf(Date) - expect(effectiveDate?.toISOString()).toBe('2024-03-15T10:30:00.123Z') - }) - - it('should return undefined when no effective date', () => { - const observation: Observation = { - resourceType: 'Observation', - status: 'final', - code: { text: 'Test' }, - } - - const fhirObs = new FhirObservation(observation) - expect(fhirObs.effectiveDate).toBeUndefined() - }) - }) - - describe('effectivePeriodStart', () => { - it('should return start date from effectivePeriod', () => { - const observation: Observation = { - resourceType: 'Observation', - status: 'final', - code: { text: 'Test' }, - effectivePeriod: { - start: '2024-03-15T10:00:00Z', - }, - } - - const fhirObs = new FhirObservation(observation) - const startDate = fhirObs.effectivePeriodStart - - expect(startDate).toBeInstanceOf(Date) - expect(startDate?.toISOString()).toBe('2024-03-15T10:00:00.000Z') - }) - - it('should return undefined when no period', () => { - const observation: Observation = { - resourceType: 'Observation', - status: 'final', - code: { text: 'Test' }, - } - - const fhirObs = new FhirObservation(observation) - expect(fhirObs.effectivePeriodStart).toBeUndefined() - }) - }) - - describe('effectivePeriodEnd', () => { - it('should return end date from effectivePeriod', () => { - const observation: Observation = { - resourceType: 'Observation', - status: 'final', - code: { text: 'Test' }, - effectivePeriod: { - end: '2024-03-15T11:00:00Z', - }, - } - - const fhirObs = new FhirObservation(observation) - const endDate = fhirObs.effectivePeriodEnd - - expect(endDate).toBeInstanceOf(Date) - expect(endDate?.toISOString()).toBe('2024-03-15T11:00:00.000Z') - }) - - it('should return undefined when no period', () => { - const observation: Observation = { - resourceType: 'Observation', - status: 'final', - code: { text: 'Test' }, - } - - const fhirObs = new FhirObservation(observation) - expect(fhirObs.effectivePeriodEnd).toBeUndefined() - }) - }) - - describe('effectiveOverlaps', () => { - it('should return true when effectiveDateTime is in range', () => { - const observation: Observation = { - resourceType: 'Observation', - status: 'final', - code: { text: 'Test' }, - effectiveDateTime: '2024-06-15T10:00:00Z', - } - - const fhirObs = new FhirObservation(observation) - const rangeStart = new Date('2024-01-01T00:00:00Z') - const rangeEnd = new Date('2024-12-31T23:59:59Z') - - expect(fhirObs.effectiveOverlaps(rangeStart, rangeEnd)).toBe(true) - }) - - it('should return false when effectiveDateTime is outside range', () => { - const observation: Observation = { - resourceType: 'Observation', - status: 'final', - code: { text: 'Test' }, - effectiveDateTime: '2023-06-15T10:00:00Z', - } - - const fhirObs = new FhirObservation(observation) - const rangeStart = new Date('2024-01-01T00:00:00Z') - const rangeEnd = new Date('2024-12-31T23:59:59Z') - - expect(fhirObs.effectiveOverlaps(rangeStart, rangeEnd)).toBe(false) - }) - - it('should return true when effectivePeriod overlaps range', () => { - const observation: Observation = { - resourceType: 'Observation', - status: 'final', - code: { text: 'Test' }, - effectivePeriod: { - start: '2024-05-01T00:00:00Z', - end: '2024-07-31T23:59:59Z', - }, - } - - const fhirObs = new FhirObservation(observation) - const rangeStart = new Date('2024-06-01T00:00:00Z') - const rangeEnd = new Date('2024-06-30T23:59:59Z') - - expect(fhirObs.effectiveOverlaps(rangeStart, rangeEnd)).toBe(true) - }) - - it('should return true when effectivePeriod partially overlaps range', () => { - const observation: Observation = { - resourceType: 'Observation', - status: 'final', - code: { text: 'Test' }, - effectivePeriod: { - start: '2024-05-01T00:00:00Z', - end: '2024-06-15T00:00:00Z', - }, - } - - const fhirObs = new FhirObservation(observation) - const rangeStart = new Date('2024-06-01T00:00:00Z') - const rangeEnd = new Date('2024-06-30T23:59:59Z') - - expect(fhirObs.effectiveOverlaps(rangeStart, rangeEnd)).toBe(true) - }) - - it('should return false when effectivePeriod does not overlap range', () => { - const observation: Observation = { - resourceType: 'Observation', - status: 'final', - code: { text: 'Test' }, - effectivePeriod: { - start: '2024-01-01T00:00:00Z', - end: '2024-02-28T23:59:59Z', - }, - } - - const fhirObs = new FhirObservation(observation) - const rangeStart = new Date('2024-06-01T00:00:00Z') - const rangeEnd = new Date('2024-06-30T23:59:59Z') - - expect(fhirObs.effectiveOverlaps(rangeStart, rangeEnd)).toBe(false) - }) - - it('should handle effectivePeriod with only start date', () => { - const observation: Observation = { - resourceType: 'Observation', - status: 'final', - code: { text: 'Test' }, - effectivePeriod: { - start: '2024-05-01T00:00:00Z', - }, - } - - const fhirObs = new FhirObservation(observation) - const rangeStart = new Date('2024-06-01T00:00:00Z') - const rangeEnd = new Date('2024-06-30T23:59:59Z') - - // Period without end extends infinitely, so it should overlap - expect(fhirObs.effectiveOverlaps(rangeStart, rangeEnd)).toBe(true) - }) - - it('should handle effectivePeriod with only end date', () => { - const observation: Observation = { - resourceType: 'Observation', - status: 'final', - code: { text: 'Test' }, - effectivePeriod: { - end: '2024-07-31T23:59:59Z', - }, - } - - const fhirObs = new FhirObservation(observation) - const rangeStart = new Date('2024-06-01T00:00:00Z') - const rangeEnd = new Date('2024-06-30T23:59:59Z') - - // Period without start extends infinitely into past, so it should overlap - expect(fhirObs.effectiveOverlaps(rangeStart, rangeEnd)).toBe(true) - }) - - it('should return false when no effective date information', () => { - const observation: Observation = { - resourceType: 'Observation', - status: 'final', - code: { text: 'Test' }, - } - - const fhirObs = new FhirObservation(observation) - const rangeStart = new Date('2024-06-01T00:00:00Z') - const rangeEnd = new Date('2024-06-30T23:59:59Z') - - expect(fhirObs.effectiveOverlaps(rangeStart, rangeEnd)).toBe(false) - }) - }) -}) diff --git a/packages/fhir/test/resources/observations.json b/packages/fhir/test/resources/observations.json deleted file mode 100644 index 6da258e8..00000000 --- a/packages/fhir/test/resources/observations.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "0": { - "resourceType": "Observation", - "id": "f001", - "identifier": [ - { - "use": "official", - "system": "http://www.bmc.nl/zorgportal/identifiers/observations", - "value": "6323" - } - ], - "status": "final", - "code": { - "coding": [ - { - "system": "http://loinc.org", - "code": "15074-8", - "display": "Glucose [Moles/volume] in Blood" - } - ] - }, - "subject": { - "reference": "Patient/f001", - "display": "P. van de Heuvel" - }, - "effectiveDateTime": "2013-04-02T09:30:10+01:00", - "issued": "2013-04-03T15:30:10+01:00", - "performer": [ - { - "reference": "Practitioner/f005", - "display": "A. Langeveld" - } - ], - "valueQuantity": { - "value": 6.3, - "unit": "mmol/l", - "system": "http://unitsofmeasure.org", - "code": "mmol/L" - }, - "interpretation": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", - "code": "H", - "display": "High" - } - ] - } - ], - "referenceRange": [ - { - "low": { - "value": 3.1, - "unit": "mmol/l", - "system": "http://unitsofmeasure.org", - "code": "mmol/L" - }, - "high": { - "value": 6.2, - "unit": "mmol/l", - "system": "http://unitsofmeasure.org", - "code": "mmol/L" - } - } - ] - } -} diff --git a/packages/fhir/test/resources/observations.json.license b/packages/fhir/test/resources/observations.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/observations.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/observations.test.ts b/packages/fhir/test/resources/observations.test.ts deleted file mode 100644 index d91a1615..00000000 --- a/packages/fhir/test/resources/observations.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Observation } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirObservation, - type untypedObservationSchema, -} from '../../src/index.js' - -describe('Observation Resource', () => { - it('should validate FHIR observation from observation.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/observations.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirObservation.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/operationDefinitions.json b/packages/fhir/test/resources/operationDefinitions.json deleted file mode 100644 index 79a20076..00000000 --- a/packages/fhir/test/resources/operationDefinitions.json +++ /dev/null @@ -1,176 +0,0 @@ -{ - "0": { - "resourceType": "OperationDefinition", - "id": "example", - "url": "http://example.org/fhir/OperationDefinition/example", - "name": "ExampleOperation", - "status": "draft", - "kind": "operation", - "code": "example", - "system": false, - "type": true, - "instance": false - }, - "1": { - "resourceType": "OperationDefinition", - "id": "patient-everything", - "url": "http://hl7.org/fhir/OperationDefinition/Patient-everything", - "name": "PatientEverything", - "title": "Fetch Patient Record", - "status": "active", - "kind": "operation", - "code": "everything", - "description": "This operation is used to return all the information related to a patient", - "resource": ["Patient"], - "system": false, - "type": false, - "instance": true, - "parameter": [ - { - "name": "start", - "use": "in", - "min": 0, - "max": "1", - "documentation": "The date range relates to care dates, not record currency dates", - "type": "date" - }, - { - "name": "end", - "use": "in", - "min": 0, - "max": "1", - "type": "date" - }, - { - "name": "return", - "use": "out", - "min": 1, - "max": "1", - "type": "Bundle" - } - ] - }, - "2": { - "resourceType": "OperationDefinition", - "id": "observation-stats", - "url": "http://example.org/fhir/OperationDefinition/Observation-stats", - "name": "ObservationStats", - "status": "active", - "kind": "query", - "code": "stats", - "description": "Calculate statistics for observations", - "resource": ["Observation"], - "system": false, - "type": true, - "instance": false, - "affectsState": false, - "parameter": [ - { - "name": "subject", - "use": "in", - "min": 1, - "max": "1", - "type": "Reference", - "targetProfile": ["http://hl7.org/fhir/StructureDefinition/Patient"] - }, - { - "name": "code", - "use": "in", - "min": 1, - "max": "*", - "type": "string", - "searchType": "token" - }, - { - "name": "result", - "use": "out", - "min": 1, - "max": "1", - "type": "Parameters", - "part": [ - { - "name": "average", - "use": "out", - "min": 0, - "max": "1", - "type": "decimal" - }, - { - "name": "min", - "use": "out", - "min": 0, - "max": "1", - "type": "decimal" - }, - { - "name": "max", - "use": "out", - "min": 0, - "max": "1", - "type": "decimal" - } - ] - } - ] - }, - "3": { - "resourceType": "OperationDefinition", - "id": "valueset-expand", - "url": "http://hl7.org/fhir/OperationDefinition/ValueSet-expand", - "name": "ValueSetExpand", - "title": "Value Set Expansion", - "status": "active", - "kind": "operation", - "code": "expand", - "description": "Expand a value set to return the list of codes", - "resource": ["ValueSet"], - "system": false, - "type": true, - "instance": true, - "affectsState": false, - "inputProfile": "http://hl7.org/fhir/StructureDefinition/Parameters", - "outputProfile": "http://hl7.org/fhir/StructureDefinition/ValueSet", - "parameter": [ - { - "name": "url", - "use": "in", - "min": 0, - "max": "1", - "type": "uri" - }, - { - "name": "filter", - "use": "in", - "min": 0, - "max": "1", - "type": "string" - }, - { - "name": "return", - "use": "out", - "min": 1, - "max": "1", - "type": "ValueSet", - "binding": { - "strength": "example", - "valueSet": "http://hl7.org/fhir/ValueSet/example" - }, - "referencedFrom": [ - { - "source": "url", - "sourceId": "expansion" - } - ] - } - ], - "overload": [ - { - "parameterName": ["url"] - }, - { - "parameterName": ["url", "filter"], - "comment": "Expand with filter" - } - ] - } -} diff --git a/packages/fhir/test/resources/operationDefinitions.json.license b/packages/fhir/test/resources/operationDefinitions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/operationDefinitions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/operationDefinitions.test.ts b/packages/fhir/test/resources/operationDefinitions.test.ts deleted file mode 100644 index 568b0d55..00000000 --- a/packages/fhir/test/resources/operationDefinitions.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type OperationDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirOperationDefinition, - type untypedOperationDefinitionSchema, -} from '../../src/index.js' - -describe('OperationDefinition Resource', () => { - it('should validate FHIR operation definition from operationDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/operationDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirOperationDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/operationOutcomes.json b/packages/fhir/test/resources/operationOutcomes.json deleted file mode 100644 index 9dbb699f..00000000 --- a/packages/fhir/test/resources/operationOutcomes.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "0": { - "resourceType": "OperationOutcome", - "id": "opout-example-0", - "issue": [ - { - "severity": "information", - "code": "informational", - "details": { - "text": "Operation completed successfully" - } - } - ] - }, - "1": { - "resourceType": "OperationOutcome", - "id": "opout-example-1", - "issue": [ - { - "severity": "error", - "code": "required", - "details": { - "coding": [ - { - "system": "http://example.org/errors", - "code": "ERR-001", - "display": "Missing required field" - } - ], - "text": "The field 'patient' is required" - }, - "diagnostics": "Resource validation failed", - "expression": ["Patient.identifier"] - } - ] - }, - "2": { - "resourceType": "OperationOutcome", - "id": "opout-example-2", - "issue": [ - { - "severity": "warning", - "code": "code-invalid", - "details": { - "text": "Code not found in value set" - }, - "diagnostics": "The code '12345' was not found in the bound value set", - "location": ["Observation.code"] - } - ] - }, - "3": { - "resourceType": "OperationOutcome", - "id": "opout-example-3", - "issue": [ - { - "severity": "fatal", - "code": "exception", - "details": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/operation-outcome", - "code": "MSG_TIMEOUT", - "display": "Timeout" - } - ], - "text": "Request timeout" - }, - "diagnostics": "The operation timed out after 30 seconds" - }, - { - "severity": "information", - "code": "informational", - "details": { - "text": "Retry recommended" - } - } - ] - } -} diff --git a/packages/fhir/test/resources/operationOutcomes.json.license b/packages/fhir/test/resources/operationOutcomes.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/operationOutcomes.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/operationOutcomes.test.ts b/packages/fhir/test/resources/operationOutcomes.test.ts deleted file mode 100644 index 5c294513..00000000 --- a/packages/fhir/test/resources/operationOutcomes.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type OperationOutcome } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirOperationOutcome, - type untypedOperationOutcomeSchema, -} from '../../src/index.js' - -describe('OperationOutcome Resource', () => { - it('should validate FHIR operationOutcome from operationOutcomes.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/operationOutcomes.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirOperationOutcome.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/organizationAffiliations.json b/packages/fhir/test/resources/organizationAffiliations.json deleted file mode 100644 index 2d9854c8..00000000 --- a/packages/fhir/test/resources/organizationAffiliations.json +++ /dev/null @@ -1,196 +0,0 @@ -{ - "0": { - "resourceType": "OrganizationAffiliation", - "id": "example-0", - "identifier": [ - { - "system": "http://example.org/affiliation-identifiers", - "value": "AFF-001" - } - ], - "active": true, - "period": { - "start": "2020-01-01" - }, - "organization": { - "reference": "Organization/org-001", - "display": "Health Level Seven International" - }, - "participatingOrganization": { - "reference": "Organization/org-002", - "display": "ABC Medical Center" - }, - "code": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/organization-role", - "code": "provider", - "display": "Provider" - } - ] - } - ], - "specialty": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "394802001", - "display": "General medicine" - } - ] - } - ], - "location": [ - { - "reference": "Location/location-001", - "display": "Main Campus" - } - ], - "telecom": [ - { - "system": "phone", - "value": "+1-555-0100" - }, - { - "system": "email", - "value": "affiliation@example.org" - } - ] - }, - "1": { - "resourceType": "OrganizationAffiliation", - "id": "example-1", - "active": true, - "organization": { - "reference": "Organization/org-003" - }, - "participatingOrganization": { - "reference": "Organization/org-004" - }, - "network": [ - { - "reference": "Organization/network-001", - "display": "Regional Health Network" - } - ], - "code": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/organization-role", - "code": "member", - "display": "Member" - } - ] - } - ] - }, - "2": { - "resourceType": "OrganizationAffiliation", - "id": "example-2", - "identifier": [ - { - "system": "http://example.org/affiliation-identifiers", - "value": "AFF-002" - } - ], - "active": false, - "period": { - "start": "2018-01-01", - "end": "2019-12-31" - }, - "organization": { - "reference": "Organization/org-005" - }, - "participatingOrganization": { - "reference": "Organization/org-006" - }, - "code": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/organization-role", - "code": "HIE/HIO", - "display": "HIE/HIO" - } - ] - } - ], - "healthcareService": [ - { - "reference": "HealthcareService/service-001" - } - ], - "endpoint": [ - { - "reference": "Endpoint/endpoint-001" - } - ] - }, - "3": { - "resourceType": "OrganizationAffiliation", - "id": "example-3", - "active": true, - "period": { - "start": "2021-06-01" - }, - "organization": { - "reference": "Organization/org-007", - "display": "Community Hospital" - }, - "participatingOrganization": { - "reference": "Organization/org-008", - "display": "Specialty Clinic" - }, - "code": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/organization-role", - "code": "vendor", - "display": "Vendor" - } - ] - } - ], - "specialty": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "394585009", - "display": "Obstetrics and gynecology" - } - ] - }, - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "394537008", - "display": "Pediatric specialty" - } - ] - } - ], - "location": [ - { - "reference": "Location/location-002", - "display": "East Wing" - }, - { - "reference": "Location/location-003", - "display": "West Wing" - } - ], - "telecom": [ - { - "system": "phone", - "value": "+1-555-0200", - "use": "work" - } - ] - } -} diff --git a/packages/fhir/test/resources/organizationAffiliations.json.license b/packages/fhir/test/resources/organizationAffiliations.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/organizationAffiliations.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/organizationAffiliations.test.ts b/packages/fhir/test/resources/organizationAffiliations.test.ts deleted file mode 100644 index ab76bc1e..00000000 --- a/packages/fhir/test/resources/organizationAffiliations.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type OrganizationAffiliation } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirOrganizationAffiliation, - type untypedOrganizationAffiliationSchema, -} from '../../src/index.js' - -describe('OrganizationAffiliation Resource', () => { - it('should validate FHIR OrganizationAffiliations from organizationAffiliations.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/organizationAffiliations.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirOrganizationAffiliation.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/organizations.json b/packages/fhir/test/resources/organizations.json deleted file mode 100644 index f21aa5b8..00000000 --- a/packages/fhir/test/resources/organizations.json +++ /dev/null @@ -1,154 +0,0 @@ -{ - "0": { - "resourceType": "Organization", - "id": "org-example-0", - "active": true, - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/organization-type", - "code": "prov", - "display": "Healthcare Provider" - } - ] - } - ], - "name": "General Hospital", - "telecom": [ - { - "system": "phone", - "value": "+1-555-100-2000", - "use": "work" - } - ], - "address": [ - { - "line": ["100 Hospital Drive"], - "city": "Springfield", - "state": "IL", - "postalCode": "62701" - } - ] - }, - "1": { - "resourceType": "Organization", - "id": "org-example-1", - "identifier": [ - { - "system": "http://hl7.org/fhir/sid/us-npi", - "value": "1234567890" - } - ], - "active": true, - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/organization-type", - "code": "dept", - "display": "Hospital Department" - } - ] - } - ], - "name": "Cardiology Department", - "alias": ["Cardiac Care", "Heart Center"], - "telecom": [ - { - "system": "phone", - "value": "+1-555-100-2222" - }, - { - "system": "email", - "value": "cardiology@hospital.example.org" - } - ], - "partOf": { - "reference": "Organization/org-example-0" - } - }, - "2": { - "resourceType": "Organization", - "id": "org-example-2", - "active": false, - "name": "Closed Clinic", - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/organization-type", - "code": "prov", - "display": "Healthcare Provider" - } - ] - } - ] - }, - "3": { - "resourceType": "Organization", - "id": "org-example-3", - "identifier": [ - { - "system": "http://example.org/organizations", - "value": "ORG-789" - } - ], - "active": true, - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/organization-type", - "code": "ins", - "display": "Insurance Company" - } - ] - } - ], - "name": "HealthCare Insurance Co.", - "telecom": [ - { - "system": "phone", - "value": "+1-800-555-1234", - "use": "work" - }, - { - "system": "url", - "value": "https://www.healthcareins.example.org" - } - ], - "address": [ - { - "type": "both", - "line": ["500 Insurance Plaza"], - "city": "New York", - "state": "NY", - "postalCode": "10001", - "country": "USA" - } - ], - "contact": [ - { - "purpose": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/contactentity-type", - "code": "ADMIN", - "display": "Administrative" - } - ] - }, - "name": { - "text": "Customer Service" - }, - "telecom": [ - { - "system": "phone", - "value": "+1-800-555-5678" - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/organizations.json.license b/packages/fhir/test/resources/organizations.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/organizations.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/organizations.test.ts b/packages/fhir/test/resources/organizations.test.ts deleted file mode 100644 index 12859086..00000000 --- a/packages/fhir/test/resources/organizations.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Organization } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirOrganization, - type untypedOrganizationSchema, -} from '../../src/index.js' - -describe('Organization Resource', () => { - it('should validate FHIR organization from organizations.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/organizations.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirOrganization.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/packagedProductDefinitions.json b/packages/fhir/test/resources/packagedProductDefinitions.json deleted file mode 100644 index 85ae0785..00000000 --- a/packages/fhir/test/resources/packagedProductDefinitions.json +++ /dev/null @@ -1,248 +0,0 @@ -{ - "0": { - "resourceType": "PackagedProductDefinition", - "id": "packaged-product-definition-example-0" - }, - "1": { - "resourceType": "PackagedProductDefinition", - "id": "packaged-product-definition-example-1", - "identifier": [ - { - "system": "http://example.org/packaged-products", - "value": "PPD-001" - } - ], - "name": "Aspirin 500mg Tablets - Blister Pack of 20", - "type": { - "coding": [ - { - "system": "http://hl7.org/fhir/package-type", - "code": "medicinal-product-pack", - "display": "Medicinal Product Pack" - } - ] - }, - "packageFor": [ - { - "reference": "MedicinalProductDefinition/aspirin" - } - ], - "package": { - "type": { - "coding": [ - { - "system": "http://example.org/package-type", - "code": "box", - "display": "Box" - } - ] - }, - "quantity": 1, - "package": [ - { - "type": { - "coding": [ - { - "system": "http://example.org/package-type", - "code": "blister", - "display": "Blister" - } - ] - }, - "quantity": 2, - "containedItem": [ - { - "item": { - "reference": { - "reference": "ManufacturedItemDefinition/aspirin-tablet" - } - }, - "amount": { - "value": 10, - "unit": "tablet" - } - } - ] - } - ] - } - }, - "2": { - "resourceType": "PackagedProductDefinition", - "id": "packaged-product-definition-example-2", - "name": "Insulin Injection Solution - 5 Pre-filled Pens", - "packageFor": [ - { - "reference": "MedicinalProductDefinition/insulin" - } - ], - "containedItemQuantity": [ - { - "value": 5, - "unit": "pen" - } - ], - "manufacturer": [ - { - "reference": "Organization/insulin-manufacturer" - } - ], - "package": { - "type": { - "coding": [ - { - "system": "http://example.org/package-type", - "code": "carton", - "display": "Carton" - } - ] - }, - "containedItem": [ - { - "item": { - "reference": { - "reference": "DeviceDefinition/insulin-pen" - } - }, - "amount": { - "value": 5, - "unit": "pen" - } - } - ], - "property": [ - { - "type": { - "text": "Temperature control" - }, - "valueBoolean": true - } - ], - "shelfLifeStorage": [ - { - "type": { - "text": "Refrigerated storage" - }, - "periodDuration": { - "value": 24, - "unit": "months" - } - } - ] - } - }, - "3": { - "resourceType": "PackagedProductDefinition", - "id": "packaged-product-definition-example-3", - "identifier": [ - { - "system": "http://example.org/gtin", - "value": "05000471234567" - } - ], - "name": "Eye Drops - 10mL Bottle", - "description": "Sterile eye drops solution", - "packageFor": [ - { - "reference": "MedicinalProductDefinition/eye-drops" - } - ], - "status": { - "coding": [ - { - "system": "http://hl7.org/fhir/publication-status", - "code": "active", - "display": "Active" - } - ] - }, - "marketingStatus": [ - { - "country": { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "US", - "display": "United States" - } - ] - }, - "status": { - "coding": [ - { - "system": "http://example.org/marketing-status", - "code": "marketed", - "display": "Marketed" - } - ] - } - } - ], - "package": { - "type": { - "coding": [ - { - "system": "http://example.org/package-type", - "code": "bottle", - "display": "Bottle" - } - ] - }, - "material": [ - { - "coding": [ - { - "system": "http://example.org/material", - "code": "plastic", - "display": "Plastic" - } - ] - } - ], - "containedItem": [ - { - "item": { - "concept": { - "text": "Eye drops solution" - } - }, - "amount": { - "value": 10, - "unit": "mL", - "system": "http://unitsofmeasure.org", - "code": "mL" - } - } - ], - "shelfLifeStorage": [ - { - "type": { - "text": "Shelf life" - }, - "periodDuration": { - "value": 36, - "unit": "months" - }, - "specialPrecautionsForStorage": [ - { - "text": "Do not freeze" - } - ] - } - ] - }, - "legalStatusOfSupply": [ - { - "code": { - "coding": [ - { - "system": "http://example.org/legal-status", - "code": "OTC", - "display": "Over the counter" - } - ] - } - } - ] - } -} diff --git a/packages/fhir/test/resources/packagedProductDefinitions.json.license b/packages/fhir/test/resources/packagedProductDefinitions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/packagedProductDefinitions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/packagedProductDefinitions.test.ts b/packages/fhir/test/resources/packagedProductDefinitions.test.ts deleted file mode 100644 index 67d07e62..00000000 --- a/packages/fhir/test/resources/packagedProductDefinitions.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type PackagedProductDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirPackagedProductDefinition, - type untypedPackagedProductDefinitionSchema, -} from '../../src/index.js' - -describe('PackagedProductDefinition Resource', () => { - it('should validate FHIR packagedProductDefinition from packagedProductDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/packagedProductDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = - FhirPackagedProductDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/parameters.json b/packages/fhir/test/resources/parameters.json deleted file mode 100644 index 18cd4569..00000000 --- a/packages/fhir/test/resources/parameters.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "0": { - "resourceType": "Parameters", - "parameter": [ - { - "name": "param1", - "valueString": "value1" - } - ] - }, - "1": { - "resourceType": "Parameters", - "id": "params-example-1", - "parameter": [ - { - "name": "code", - "valueCode": "active" - }, - { - "name": "system", - "valueUri": "http://example.org/codes" - }, - { - "name": "exact", - "valueBoolean": true - } - ] - }, - "2": { - "resourceType": "Parameters", - "id": "params-example-2", - "parameter": [ - { - "name": "patient", - "resource": { - "resourceType": "Patient", - "id": "param-patient", - "name": [ - { - "family": "Test", - "given": ["Patient"] - } - ] - } - } - ] - }, - "3": { - "resourceType": "Parameters", - "id": "params-example-3", - "parameter": [ - { - "name": "input", - "part": [ - { - "name": "name", - "valueString": "John Doe" - }, - { - "name": "age", - "valueInteger": 35 - }, - { - "name": "active", - "valueBoolean": true - } - ] - }, - { - "name": "count", - "valueInteger": 10 - }, - { - "name": "date", - "valueDate": "2024-11-15" - } - ] - } -} diff --git a/packages/fhir/test/resources/parameters.json.license b/packages/fhir/test/resources/parameters.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/parameters.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/parameters.test.ts b/packages/fhir/test/resources/parameters.test.ts deleted file mode 100644 index 3671ae26..00000000 --- a/packages/fhir/test/resources/parameters.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Parameters } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirParameters, - type untypedParametersSchema, -} from '../../src/index.js' - -describe('Parameters Resource', () => { - it('should validate FHIR parameters from parameters.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/parameters.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirParameters.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/patientMethods.test.ts b/packages/fhir/test/resources/patientMethods.test.ts deleted file mode 100644 index 31e12bf2..00000000 --- a/packages/fhir/test/resources/patientMethods.test.ts +++ /dev/null @@ -1,171 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { type Patient } from 'fhir/r4b.js' -import { FhirPatient } from '../../src/index.js' - -describe('FhirPatient', () => { - describe('birthDate', () => { - it('should get birth date as Date object', () => { - const patient: Patient = { - resourceType: 'Patient', - birthDate: '1990-05-15', - } - const fhirPatient = new FhirPatient(patient) - - const birthDate = fhirPatient.birthDate - expect(birthDate).toBeInstanceOf(Date) - expect(birthDate?.getUTCFullYear()).toBe(1990) - expect(birthDate?.getUTCMonth()).toBe(4) // 0-indexed - expect(birthDate?.getUTCDate()).toBe(15) - }) - - it('should return undefined when no birth date', () => { - const patient: Patient = { - resourceType: 'Patient', - } - const fhirPatient = new FhirPatient(patient) - - expect(fhirPatient.birthDate).toBeUndefined() - }) - }) - - describe('deceasedDate', () => { - it('should get deceased date as Date object', () => { - const patient: Patient = { - resourceType: 'Patient', - deceasedDateTime: '2020-03-15T10:30:00Z', - } - const fhirPatient = new FhirPatient(patient) - - const deceasedDate = fhirPatient.deceasedDate - expect(deceasedDate).toBeInstanceOf(Date) - expect(deceasedDate?.toISOString()).toBe('2020-03-15T10:30:00.000Z') - }) - }) - - describe('isDeceased', () => { - it('should return true when deceasedBoolean is true', () => { - const patient: Patient = { - resourceType: 'Patient', - deceasedBoolean: true, - } - const fhirPatient = new FhirPatient(patient) - - expect(fhirPatient.isDeceased).toBe(true) - }) - - it('should return true when deceasedDateTime is set', () => { - const patient: Patient = { - resourceType: 'Patient', - deceasedDateTime: '2020-03-15T10:30:00Z', - } - const fhirPatient = new FhirPatient(patient) - - expect(fhirPatient.isDeceased).toBe(true) - }) - - it('should return false when not deceased', () => { - const patient: Patient = { - resourceType: 'Patient', - } - const fhirPatient = new FhirPatient(patient) - - expect(fhirPatient.isDeceased).toBe(false) - }) - }) - - describe('identifiersBySystem', () => { - it('should return identifiers matching system', () => { - const patient: Patient = { - resourceType: 'Patient', - identifier: [ - { system: 'http://hospital.org/mrn', value: 'MRN123' }, - { system: 'http://hospital.org/mrn', value: 'MRN456' }, - { system: 'http://other.org/id', value: 'OTHER789' }, - ], - } - const fhirPatient = new FhirPatient(patient) - - const mrns = fhirPatient.identifiersBySystem('http://hospital.org/mrn') - expect(mrns).toEqual(['MRN123', 'MRN456']) - }) - - it('should return empty array when no matching identifiers', () => { - const patient: Patient = { - resourceType: 'Patient', - identifier: [{ system: 'http://other.org/id', value: 'OTHER789' }], - } - const fhirPatient = new FhirPatient(patient) - - const mrns = fhirPatient.identifiersBySystem('http://hospital.org/mrn') - expect(mrns).toEqual([]) - }) - }) - - describe('primaryPhone and primaryEmail', () => { - it('should return primary phone', () => { - const patient: Patient = { - resourceType: 'Patient', - telecom: [ - { system: 'phone', value: '555-1234' }, - { system: 'email', value: 'patient@example.com' }, - ], - } - const fhirPatient = new FhirPatient(patient) - - expect(fhirPatient.phoneNumbers.at(0)).toBe('555-1234') - expect(fhirPatient.emailAddresses.at(0)).toBe('patient@example.com') - }) - - it('should return undefined when no contact info', () => { - const patient: Patient = { - resourceType: 'Patient', - } - const fhirPatient = new FhirPatient(patient) - - expect(fhirPatient.phoneNumbers.at(0)).toBeUndefined() - expect(fhirPatient.emailAddresses.at(0)).toBeUndefined() - }) - }) - - describe('calculateAge', () => { - it('should calculate age correctly', () => { - const patient: Patient = { - resourceType: 'Patient', - birthDate: '1990-05-15', - } - const fhirPatient = new FhirPatient(patient) - - const asOfDate = new Date('2024-06-20') - const age = fhirPatient.ageInYears(asOfDate) - expect(age).toBe(34) - }) - - it('should handle age before birthday this year', () => { - const patient: Patient = { - resourceType: 'Patient', - birthDate: '1990-08-15', - } - const fhirPatient = new FhirPatient(patient) - - const asOfDate = new Date('2024-06-20') - const age = fhirPatient.ageInYears(asOfDate) - expect(age).toBe(33) - }) - - it('should return undefined when no birth date', () => { - const patient: Patient = { - resourceType: 'Patient', - } - const fhirPatient = new FhirPatient(patient) - - expect(fhirPatient.ageInYears()).toBeUndefined() - }) - }) -}) diff --git a/packages/fhir/test/resources/patients.json b/packages/fhir/test/resources/patients.json deleted file mode 100644 index a621153e..00000000 --- a/packages/fhir/test/resources/patients.json +++ /dev/null @@ -1,205 +0,0 @@ -{ - "0": { - "resourceType": "Patient", - "id": "patient-example-0", - "identifier": [ - { - "system": "http://hospital.example.org/patients", - "value": "12345" - } - ], - "active": true, - "name": [ - { - "use": "official", - "family": "Smith", - "given": ["John", "Robert"] - } - ], - "telecom": [ - { - "system": "phone", - "value": "+1-555-123-4567", - "use": "home" - } - ], - "gender": "male", - "birthDate": "1980-05-15", - "address": [ - { - "use": "home", - "line": ["123 Main St"], - "city": "Springfield", - "state": "IL", - "postalCode": "62701", - "country": "USA" - } - ] - }, - "1": { - "resourceType": "Patient", - "id": "patient-example-1", - "identifier": [ - { - "use": "official", - "system": "http://hospital.example.org/patients", - "value": "67890" - } - ], - "active": false, - "name": [ - { - "use": "official", - "family": "Johnson", - "given": ["Emily"] - }, - { - "use": "nickname", - "given": ["Em"] - } - ], - "telecom": [ - { - "system": "email", - "value": "emily.johnson@example.com", - "use": "work" - } - ], - "gender": "female", - "birthDate": "1992-11-23", - "deceasedBoolean": false, - "maritalStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-MaritalStatus", - "code": "M", - "display": "Married" - } - ] - }, - "contact": [ - { - "relationship": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0131", - "code": "C", - "display": "Emergency Contact" - } - ] - } - ], - "name": { - "family": "Johnson", - "given": ["Michael"] - }, - "telecom": [ - { - "system": "phone", - "value": "+1-555-987-6543" - } - ] - } - ] - }, - "2": { - "resourceType": "Patient", - "id": "patient-example-2", - "identifier": [ - { - "system": "urn:oid:1.2.36.146.595.217.0.1", - "value": "MRN-111222" - } - ], - "active": true, - "name": [ - { - "family": "Williams", - "given": ["Sarah", "Anne"], - "prefix": ["Dr."] - } - ], - "gender": "female", - "birthDate": "1975-03-10", - "deceasedDateTime": "2023-06-15T10:30:00Z", - "address": [ - { - "line": ["456 Oak Avenue", "Apt 7B"], - "city": "Boston", - "state": "MA", - "postalCode": "02101" - } - ], - "multipleBirthBoolean": false, - "communication": [ - { - "language": { - "coding": [ - { - "system": "urn:ietf:bcp:47", - "code": "en-US", - "display": "English (United States)" - } - ] - }, - "preferred": true - } - ] - }, - "3": { - "resourceType": "Patient", - "id": "patient-example-3", - "identifier": [ - { - "system": "http://example.org/fhir/patients", - "value": "PAT-999" - } - ], - "active": true, - "name": [ - { - "use": "official", - "family": "Garcia", - "given": ["Carlos", "Miguel"], - "suffix": ["Jr."] - } - ], - "telecom": [ - { - "system": "phone", - "value": "+1-555-777-8888", - "use": "mobile" - }, - { - "system": "email", - "value": "carlos.garcia@example.com" - } - ], - "gender": "male", - "birthDate": "2000-07-20", - "address": [ - { - "use": "home", - "type": "both", - "line": ["789 Elm Street"], - "city": "Los Angeles", - "state": "CA", - "postalCode": "90001", - "country": "USA", - "period": { - "start": "2015-01-01" - } - } - ], - "multipleBirthInteger": 2, - "generalPractitioner": [ - { - "reference": "Practitioner/practitioner-example" - } - ], - "managingOrganization": { - "reference": "Organization/org-example" - } - } -} diff --git a/packages/fhir/test/resources/patients.json.license b/packages/fhir/test/resources/patients.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/patients.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/patients.test.ts b/packages/fhir/test/resources/patients.test.ts deleted file mode 100644 index e553f6c7..00000000 --- a/packages/fhir/test/resources/patients.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Patient } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirPatient, type untypedPatientSchema } from '../../src/index.js' - -describe('Patient Resource', () => { - it('should validate FHIR patient from patients.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/patients.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirPatient.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/paymentNotices.json b/packages/fhir/test/resources/paymentNotices.json deleted file mode 100644 index 1f277255..00000000 --- a/packages/fhir/test/resources/paymentNotices.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "0": { - "resourceType": "PaymentNotice", - "id": "paymentnotice-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T10:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/PaymentNotice"] - }, - "status": "active", - "created": "2025-01-15T10:00:00Z", - "payment": { - "reference": "PaymentReconciliation/payment-001" - }, - "recipient": { - "reference": "Organization/hospital-001" - }, - "amount": { - "value": 5000.0, - "currency": "USD" - } - }, - "1": { - "resourceType": "PaymentNotice", - "id": "paymentnotice-comprehensive", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T11:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/PaymentNotice"] - }, - "identifier": [ - { - "system": "http://insurance.example.org/payment-notices", - "value": "PN-001" - } - ], - "status": "active", - "request": { - "reference": "Claim/claim-001" - }, - "response": { - "reference": "ClaimResponse/claimresponse-001" - }, - "created": "2025-01-15T11:00:00Z", - "provider": { - "reference": "Organization/hospital-001" - }, - "payment": { - "reference": "PaymentReconciliation/payment-001" - }, - "paymentDate": "2025-01-14", - "payee": { - "reference": "Organization/hospital-001" - }, - "recipient": { - "reference": "Organization/insurance-001" - }, - "amount": { - "value": 10000.0, - "currency": "USD" - }, - "paymentStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/paymentstatus", - "code": "paid" - } - ] - } - }, - "2": { - "resourceType": "PaymentNotice", - "id": "paymentnotice-draft", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T12:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/PaymentNotice"] - }, - "status": "draft", - "created": "2025-01-15T12:00:00Z", - "payment": { - "reference": "PaymentReconciliation/payment-002" - }, - "recipient": { - "reference": "Organization/hospital-002" - }, - "amount": { - "value": 2500.0, - "currency": "USD" - } - }, - "3": { - "resourceType": "PaymentNotice", - "id": "paymentnotice-cancelled", - "meta": { - "versionId": "2", - "lastUpdated": "2025-01-15T13:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/PaymentNotice"] - }, - "status": "cancelled", - "created": "2025-01-15T10:00:00Z", - "payment": { - "reference": "PaymentReconciliation/payment-003" - }, - "recipient": { - "reference": "Organization/hospital-003" - }, - "amount": { - "value": 7500.0, - "currency": "USD" - } - } -} diff --git a/packages/fhir/test/resources/paymentNotices.json.license b/packages/fhir/test/resources/paymentNotices.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/paymentNotices.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/paymentNotices.test.ts b/packages/fhir/test/resources/paymentNotices.test.ts deleted file mode 100644 index b84b9612..00000000 --- a/packages/fhir/test/resources/paymentNotices.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type PaymentNotice } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirPaymentNotice, - type untypedPaymentNoticeSchema, -} from '../../src/index.js' - -describe('PaymentNotice Resource', () => { - it('should validate FHIR PaymentNotices from paymentNotices.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/paymentNotices.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirPaymentNotice.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/paymentReconciliations.json b/packages/fhir/test/resources/paymentReconciliations.json deleted file mode 100644 index fe8bce74..00000000 --- a/packages/fhir/test/resources/paymentReconciliations.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "0": { - "resourceType": "PaymentReconciliation", - "id": "paymentreconciliation-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T10:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/PaymentReconciliation" - ] - }, - "status": "active", - "created": "2025-01-15T10:00:00Z", - "paymentDate": "2025-01-14", - "paymentAmount": { - "value": 15000.0, - "currency": "USD" - } - }, - "1": { - "resourceType": "PaymentReconciliation", - "id": "paymentreconciliation-complete", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T11:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/PaymentReconciliation" - ] - }, - "identifier": [ - { - "system": "http://insurance.example.org/payment-reconciliations", - "value": "PR-001" - } - ], - "status": "active", - "period": { - "start": "2025-01-01", - "end": "2025-01-31" - }, - "created": "2025-01-15T11:00:00Z", - "paymentIssuer": { - "reference": "Organization/insurance-001" - }, - "request": { - "reference": "Claim/claim-001" - }, - "requestor": { - "reference": "Organization/hospital-001" - }, - "outcome": "complete", - "disposition": "All claims processed successfully", - "paymentDate": "2025-01-14", - "paymentAmount": { - "value": 25000.0, - "currency": "USD" - }, - "paymentIdentifier": { - "system": "http://insurance.example.org/payment-ids", - "value": "PAY-12345" - } - }, - "2": { - "resourceType": "PaymentReconciliation", - "id": "paymentreconciliation-draft", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T12:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/PaymentReconciliation" - ] - }, - "status": "draft", - "created": "2025-01-15T12:00:00Z", - "paymentDate": "2025-01-15", - "paymentAmount": { - "value": 8000.0, - "currency": "USD" - } - }, - "3": { - "resourceType": "PaymentReconciliation", - "id": "paymentreconciliation-error", - "meta": { - "versionId": "2", - "lastUpdated": "2025-01-15T13:00:00Z", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/PaymentReconciliation" - ] - }, - "status": "active", - "created": "2025-01-15T10:00:00Z", - "outcome": "error", - "disposition": "Payment processing failed - invalid account number", - "paymentDate": "2025-01-14", - "paymentAmount": { - "value": 12000.0, - "currency": "USD" - } - } -} diff --git a/packages/fhir/test/resources/paymentReconciliations.json.license b/packages/fhir/test/resources/paymentReconciliations.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/paymentReconciliations.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/paymentReconciliations.test.ts b/packages/fhir/test/resources/paymentReconciliations.test.ts deleted file mode 100644 index d4e7325e..00000000 --- a/packages/fhir/test/resources/paymentReconciliations.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type PaymentReconciliation } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirPaymentReconciliation, - type untypedPaymentReconciliationSchema, -} from '../../src/index.js' - -describe('PaymentReconciliation Resource', () => { - it('should validate FHIR PaymentReconciliations from paymentReconciliations.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/paymentReconciliations.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirPaymentReconciliation.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/persons.json b/packages/fhir/test/resources/persons.json deleted file mode 100644 index e7c5621a..00000000 --- a/packages/fhir/test/resources/persons.json +++ /dev/null @@ -1,132 +0,0 @@ -{ - "0": { - "resourceType": "Person", - "id": "person-example-0", - "active": true, - "name": [ - { - "use": "official", - "family": "Smith", - "given": ["John"] - } - ], - "telecom": [ - { - "system": "phone", - "value": "+1-555-123-4567" - } - ], - "gender": "male", - "birthDate": "1980-05-15" - }, - "1": { - "resourceType": "Person", - "id": "person-example-1", - "identifier": [ - { - "system": "http://example.org/persons", - "value": "PERSON-123" - } - ], - "active": true, - "name": [ - { - "use": "official", - "family": "Johnson", - "given": ["Emily", "Rose"] - }, - { - "use": "maiden", - "family": "Williams" - } - ], - "telecom": [ - { - "system": "email", - "value": "emily.johnson@example.com", - "use": "work" - } - ], - "gender": "female", - "birthDate": "1992-11-23", - "address": [ - { - "use": "home", - "line": ["789 Oak Street"], - "city": "Boston", - "state": "MA", - "postalCode": "02101" - } - ], - "link": [ - { - "target": { - "reference": "Patient/patient-example-1" - }, - "assurance": "level3" - } - ] - }, - "2": { - "resourceType": "Person", - "id": "person-example-2", - "active": false, - "name": [ - { - "family": "Brown", - "given": ["Michael"] - } - ], - "gender": "male" - }, - "3": { - "resourceType": "Person", - "id": "person-example-3", - "active": true, - "name": [ - { - "use": "official", - "family": "Garcia", - "given": ["Maria"], - "prefix": ["Dr."] - } - ], - "telecom": [ - { - "system": "phone", - "value": "+1-555-777-8888", - "use": "mobile" - }, - { - "system": "email", - "value": "maria.garcia@hospital.example.org", - "use": "work" - } - ], - "gender": "female", - "birthDate": "1978-03-10", - "address": [ - { - "line": ["123 Medical Plaza", "Suite 200"], - "city": "Los Angeles", - "state": "CA", - "postalCode": "90001" - } - ], - "photo": { - "contentType": "image/jpeg", - "url": "https://example.org/photos/garcia.jpg" - }, - "link": [ - { - "target": { - "reference": "Practitioner/practitioner-garcia" - }, - "assurance": "level4" - } - ], - "managingOrganization": { - "reference": "Organization/org-example-0" - } - } -} diff --git a/packages/fhir/test/resources/persons.json.license b/packages/fhir/test/resources/persons.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/persons.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/persons.test.ts b/packages/fhir/test/resources/persons.test.ts deleted file mode 100644 index 7df07815..00000000 --- a/packages/fhir/test/resources/persons.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Person } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirPerson, type untypedPersonSchema } from '../../src/index.js' - -describe('Person Resource', () => { - it('should validate FHIR person from persons.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/persons.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirPerson.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/planDefinitions.json b/packages/fhir/test/resources/planDefinitions.json deleted file mode 100644 index ec13c498..00000000 --- a/packages/fhir/test/resources/planDefinitions.json +++ /dev/null @@ -1,293 +0,0 @@ -{ - "0": { - "resourceType": "PlanDefinition", - "id": "plandefinition-example-0", - "status": "draft", - "name": "DiabetesCarePlan", - "title": "Diabetes Care Plan", - "description": "A care plan for managing diabetes" - }, - "1": { - "resourceType": "PlanDefinition", - "id": "plandefinition-example-1", - "url": "http://example.org/PlanDefinition/hypertension-management", - "identifier": [ - { - "system": "http://example.org/identifiers", - "value": "PD-001" - } - ], - "version": "1.0.0", - "name": "HypertensionManagementPlan", - "title": "Hypertension Management Plan", - "subtitle": "Evidence-based plan for managing hypertension", - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/plan-definition-type", - "code": "clinical-protocol", - "display": "Clinical Protocol" - } - ] - }, - "experimental": false, - "date": "2024-03-01T10:00:00Z", - "publisher": "Cardiology Department", - "contact": [ - { - "name": "Clinical Guidelines Team", - "telecom": [ - { - "system": "email", - "value": "guidelines@example.org" - } - ] - } - ], - "description": "This plan definition describes the recommended management approach for patients with hypertension", - "purpose": "To provide evidence-based guidance for hypertension management", - "copyright": "Copyright 2024 Example Healthcare Organization", - "goal": [ - { - "category": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/goal-category", - "code": "physiological", - "display": "Physiological" - } - ] - }, - "description": { - "text": "Achieve blood pressure control" - }, - "target": [ - { - "measure": { - "coding": [ - { - "system": "http://loinc.org", - "code": "85354-9", - "display": "Blood pressure panel" - } - ] - }, - "detailString": "Systolic < 140 mmHg, Diastolic < 90 mmHg" - } - ] - } - ], - "action": [ - { - "title": "Assess Blood Pressure", - "description": "Measure and record blood pressure", - "code": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "163020007", - "display": "Blood pressure taking" - } - ] - } - ], - "timingTiming": { - "repeat": { - "frequency": 1, - "period": 3, - "periodUnit": "mo" - } - }, - "participant": [ - { - "type": "practitioner", - "role": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "158965000", - "display": "Medical practitioner" - } - ] - } - } - ] - } - ] - }, - "2": { - "resourceType": "PlanDefinition", - "id": "plandefinition-example-2", - "status": "active", - "name": "PreventiveCareProtocol", - "title": "Adult Preventive Care Protocol", - "description": "Protocol for preventive care services", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/plan-definition-type", - "code": "eca-rule", - "display": "ECA Rule" - } - ] - }, - "subjectCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/resource-types", - "code": "Patient", - "display": "Patient" - } - ] - }, - "topic": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/definition-topic", - "code": "treatment", - "display": "Treatment" - } - ] - } - ], - "action": [ - { - "title": "Annual Physical Examination", - "description": "Schedule and perform annual physical examination", - "code": [ - { - "text": "Annual physical" - } - ], - "priority": "routine", - "timingTiming": { - "repeat": { - "frequency": 1, - "period": 1, - "periodUnit": "a" - } - } - }, - { - "title": "Immunization Review", - "description": "Review and update immunizations", - "priority": "routine", - "action": [ - { - "title": "Check Flu Vaccine", - "description": "Verify annual flu vaccination status" - } - ] - } - ] - }, - "3": { - "resourceType": "PlanDefinition", - "id": "plandefinition-example-3", - "url": "http://example.org/PlanDefinition/postoperative-care", - "version": "2.5.0", - "name": "PostOperativeCarePlan", - "title": "Post-Operative Care Plan", - "status": "active", - "date": "2024-08-10T16:00:00Z", - "publisher": "Surgical Services", - "description": "Standardized plan for post-operative care management", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "approvalDate": "2024-08-01", - "lastReviewDate": "2024-11-01", - "effectivePeriod": { - "start": "2024-08-10", - "end": "2025-08-10" - }, - "topic": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/definition-topic", - "code": "treatment", - "display": "Treatment" - } - ] - } - ], - "author": [ - { - "name": "Surgical Quality Team", - "telecom": [ - { - "system": "email", - "value": "surgery@example.org" - } - ] - } - ], - "goal": [ - { - "description": { - "text": "Prevent post-operative complications" - }, - "priority": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/goal-priority", - "code": "high-priority", - "display": "High Priority" - } - ] - } - } - ], - "action": [ - { - "title": "Pain Management", - "description": "Assess and manage post-operative pain", - "code": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "278414003", - "display": "Pain management" - } - ] - } - ], - "priority": "urgent", - "timingTiming": { - "repeat": { - "frequency": 4, - "period": 1, - "periodUnit": "d" - } - }, - "participant": [ - { - "type": "practitioner" - } - ] - }, - { - "title": "Wound Assessment", - "description": "Monitor surgical wound for signs of infection", - "priority": "routine", - "groupingBehavior": "logical-group", - "selectionBehavior": "all", - "requiredBehavior": "must", - "cardinalityBehavior": "single" - } - ] - } -} diff --git a/packages/fhir/test/resources/planDefinitions.json.license b/packages/fhir/test/resources/planDefinitions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/planDefinitions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/planDefinitions.test.ts b/packages/fhir/test/resources/planDefinitions.test.ts deleted file mode 100644 index ebded5d4..00000000 --- a/packages/fhir/test/resources/planDefinitions.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type PlanDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirPlanDefinition, - type untypedPlanDefinitionSchema, -} from '../../src/index.js' - -describe('PlanDefinition Resource', () => { - it('should validate FHIR plan definitions from planDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/planDefinitions.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirPlanDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/practitionerRoles.json b/packages/fhir/test/resources/practitionerRoles.json deleted file mode 100644 index de336867..00000000 --- a/packages/fhir/test/resources/practitionerRoles.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "0": { - "resourceType": "PractitionerRole", - "id": "practrole-example-0", - "active": true, - "practitioner": { - "reference": "Practitioner/practitioner-example" - }, - "code": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0286", - "code": "RP", - "display": "Referring Provider" - } - ] - } - ] - }, - "1": { - "resourceType": "PractitionerRole", - "id": "practrole-example-1", - "identifier": [ - { - "system": "http://example.org/practitioner-roles", - "value": "ROLE-123" - } - ], - "active": true, - "period": { - "start": "2020-01-01" - }, - "practitioner": { - "reference": "Practitioner/practitioner-cardio" - }, - "organization": { - "reference": "Organization/org-cardiology" - }, - "code": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "17561000", - "display": "Cardiologist" - } - ] - } - ], - "specialty": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "394579002", - "display": "Cardiology" - } - ] - } - ], - "location": [ - { - "reference": "Location/location-example" - } - ], - "telecom": [ - { - "system": "phone", - "value": "+1-555-222-3333", - "use": "work" - }, - { - "system": "email", - "value": "cardio@example.org" - } - ], - "availableTime": [ - { - "daysOfWeek": ["mon", "tue", "wed", "thu", "fri"], - "availableStartTime": "08:00:00", - "availableEndTime": "17:00:00" - } - ] - }, - "2": { - "resourceType": "PractitionerRole", - "id": "practrole-example-2", - "active": false, - "practitioner": { - "reference": "Practitioner/practitioner-retired" - }, - "code": [ - { - "text": "Retired physician" - } - ] - }, - "3": { - "resourceType": "PractitionerRole", - "id": "practrole-example-3", - "active": true, - "period": { - "start": "2023-06-01", - "end": "2025-06-01" - }, - "practitioner": { - "reference": "Practitioner/practitioner-nurse" - }, - "organization": { - "reference": "Organization/org-example" - }, - "code": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "224535009", - "display": "Registered Nurse" - } - ] - } - ], - "location": [ - { - "reference": "Location/location-ward-3" - } - ], - "healthcareService": [ - { - "reference": "HealthcareService/service-nursing" - } - ], - "telecom": [ - { - "system": "pager", - "value": "555-1234" - } - ], - "availableTime": [ - { - "daysOfWeek": ["mon", "wed", "fri"], - "allDay": true - } - ], - "notAvailable": [ - { - "description": "On vacation", - "during": { - "start": "2024-12-20", - "end": "2025-01-05" - } - } - ] - } -} diff --git a/packages/fhir/test/resources/practitionerRoles.json.license b/packages/fhir/test/resources/practitionerRoles.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/practitionerRoles.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/practitionerRoles.test.ts b/packages/fhir/test/resources/practitionerRoles.test.ts deleted file mode 100644 index 20977a40..00000000 --- a/packages/fhir/test/resources/practitionerRoles.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type PractitionerRole } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirPractitionerRole, - type untypedPractitionerRoleSchema, -} from '../../src/index.js' - -describe('PractitionerRole Resource', () => { - it('should validate FHIR practitionerRole from practitionerRoles.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/practitionerRoles.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirPractitionerRole.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/practitioners.json b/packages/fhir/test/resources/practitioners.json deleted file mode 100644 index b3a20c9e..00000000 --- a/packages/fhir/test/resources/practitioners.json +++ /dev/null @@ -1,573 +0,0 @@ -{ - "0": { - "resourceType": "Practitioner", - "id": "practitioner-minimal", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T14:30:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Practitioner"] - }, - "active": true, - "name": [ - { - "use": "official", - "family": "Smith", - "given": ["John", "Michael"], - "prefix": ["Dr."], - "suffix": ["MD"] - } - ] - }, - "1": { - "resourceType": "Practitioner", - "id": "practitioner-comprehensive", - "meta": { - "versionId": "2", - "lastUpdated": "2025-01-15T15:45:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Practitioner"], - "security": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", - "code": "HTEST", - "display": "test health data" - } - ], - "tag": [ - { - "system": "http://terminology.hl7.org/CodeSystem/common-tags", - "code": "actionable" - } - ] - }, - "text": { - "status": "generated", - "div": "
Dr. Sarah Johnson, MD, PhD - Cardiologist
" - }, - "identifier": [ - { - "use": "official", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "PRN", - "display": "Provider number" - } - ] - }, - "system": "http://hospital.example.org/practitioners", - "value": "PRAC-001234", - "period": { - "start": "2020-01-01" - }, - "assigner": { - "display": "General Hospital" - } - }, - { - "use": "secondary", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "NPI", - "display": "National Provider Identifier" - } - ] - }, - "system": "http://hl7.org/fhir/sid/us-npi", - "value": "1234567890" - } - ], - "active": true, - "name": [ - { - "use": "official", - "family": "Johnson", - "given": ["Sarah", "Elizabeth"], - "prefix": ["Dr."], - "suffix": ["MD", "PhD"], - "period": { - "start": "2020-01-01" - } - }, - { - "use": "maiden", - "family": "Williams", - "given": ["Sarah", "Elizabeth"] - } - ], - "birthDate": "1985-03-15", - "telecom": [ - { - "system": "phone", - "value": "+1-555-123-4567", - "use": "work", - "rank": 1, - "period": { - "start": "2020-01-01" - } - }, - { - "system": "email", - "value": "sarah.johnson@hospital.example.org", - "use": "work", - "rank": 1 - }, - { - "system": "phone", - "value": "+1-555-987-6543", - "use": "mobile", - "rank": 2 - }, - { - "system": "fax", - "value": "+1-555-123-4568", - "use": "work" - } - ], - "gender": "female", - "address": [ - { - "use": "work", - "type": "physical", - "line": ["123 Medical Center Drive", "Suite 400"], - "city": "Boston", - "state": "MA", - "postalCode": "02101", - "country": "US", - "period": { - "start": "2020-01-01" - } - }, - { - "use": "home", - "type": "physical", - "line": ["456 Residential Street"], - "city": "Cambridge", - "state": "MA", - "postalCode": "02138", - "country": "US" - } - ], - "photo": [ - { - "contentType": "image/jpeg", - "url": "https://hospital.example.org/photos/practitioners/sarah-johnson.jpg", - "title": "Dr. Sarah Johnson Photo", - "creation": "2024-01-15" - } - ], - "qualification": [ - { - "identifier": [ - { - "system": "http://medical-board.example.org/licenses", - "value": "MD-12345" - } - ], - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0360", - "code": "MD", - "display": "Doctor of Medicine" - } - ], - "text": "Doctor of Medicine" - }, - "period": { - "start": "2010-05-15" - }, - "issuer": { - "display": "Harvard Medical School" - } - }, - { - "identifier": [ - { - "system": "http://medical-board.example.org/licenses", - "value": "PHD-67890" - } - ], - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0360", - "code": "PHD", - "display": "Doctor of Philosophy" - } - ], - "text": "Doctor of Philosophy in Biomedical Sciences" - }, - "period": { - "start": "2014-06-20", - "end": "2014-06-20" - }, - "issuer": { - "display": "MIT" - } - }, - { - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "408443003", - "display": "General medical practice" - } - ] - }, - "period": { - "start": "2015-07-01" - }, - "issuer": { - "display": "American Board of Internal Medicine" - } - } - ], - "communication": [ - { - "coding": [ - { - "system": "urn:ietf:bcp:47", - "code": "en-US", - "display": "English (United States)" - } - ] - }, - { - "coding": [ - { - "system": "urn:ietf:bcp:47", - "code": "es", - "display": "Spanish" - } - ] - }, - { - "coding": [ - { - "system": "urn:ietf:bcp:47", - "code": "fr", - "display": "French" - } - ] - } - ] - }, - "2": { - "resourceType": "Practitioner", - "id": "practitioner-deceased", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T16:00:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Practitioner"] - }, - "text": { - "status": "generated", - "div": "
Dr. Robert Miller (deceased)
" - }, - "identifier": [ - { - "use": "official", - "system": "http://hospital.example.org/practitioners", - "value": "PRAC-005678" - } - ], - "active": false, - "name": [ - { - "use": "official", - "family": "Miller", - "given": ["Robert", "James"], - "prefix": ["Dr."], - "suffix": ["MD"], - "period": { - "start": "1975-01-01", - "end": "2023-12-15" - } - } - ], - "birthDate": "1950-08-22", - "telecom": [ - { - "system": "phone", - "value": "+1-555-234-5678", - "use": "work" - } - ], - "gender": "male", - "address": [ - { - "use": "work", - "line": ["789 Old Medical Building"], - "city": "Springfield", - "state": "IL", - "postalCode": "62701", - "country": "US", - "period": { - "start": "1980-01-01", - "end": "2023-12-15" - } - } - ], - "qualification": [ - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0360", - "code": "MD", - "display": "Doctor of Medicine" - } - ] - }, - "period": { - "start": "1975-05-20", - "end": "2023-12-15" - }, - "issuer": { - "display": "University of Illinois Medical School" - } - } - ], - "communication": [ - { - "coding": [ - { - "system": "urn:ietf:bcp:47", - "code": "en-US", - "display": "English (United States)" - } - ] - } - ] - }, - "3": { - "resourceType": "Practitioner", - "id": "practitioner-edge-cases", - "meta": { - "versionId": "1", - "lastUpdated": "2025-01-15T17:15:00Z", - "profile": ["http://hl7.org/fhir/StructureDefinition/Practitioner"] - }, - "text": { - "status": "generated", - "div": "
Alex Chen, RN - Nurse Practitioner with multiple specialties
" - }, - "identifier": [ - { - "use": "official", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "PRN", - "display": "Provider number" - } - ] - }, - "system": "http://nursing-board.example.org/licenses", - "value": "RN-999888", - "period": { - "start": "2018-01-01" - } - }, - { - "use": "secondary", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "DEA", - "display": "Drug Enforcement Administration registration number" - } - ] - }, - "system": "http://dea.gov", - "value": "BC1234567" - } - ], - "active": true, - "name": [ - { - "use": "official", - "family": "Chen", - "given": ["Alex"], - "suffix": ["RN", "MSN"] - }, - { - "use": "nickname", - "given": ["A.C."] - } - ], - "birthDate": "1990-11-30", - "telecom": [ - { - "system": "phone", - "value": "+1-555-345-6789", - "use": "work", - "rank": 1 - }, - { - "system": "email", - "value": "alex.chen@clinic.example.org", - "use": "work" - }, - { - "system": "sms", - "value": "+1-555-345-6789", - "use": "mobile" - }, - { - "system": "url", - "value": "https://www.linkedin.com/in/alex-chen-rn", - "use": "work" - } - ], - "gender": "other", - "address": [ - { - "use": "work", - "type": "physical", - "text": "Community Health Clinic, 456 Healthcare Plaza, Floor 2, Room 201, Downtown Medical District", - "line": ["456 Healthcare Plaza", "Floor 2, Room 201"], - "city": "Portland", - "district": "Downtown Medical District", - "state": "OR", - "postalCode": "97201", - "country": "US", - "period": { - "start": "2022-03-01" - } - }, - { - "use": "temp", - "type": "postal", - "line": ["PO Box 12345"], - "city": "Portland", - "state": "OR", - "postalCode": "97208", - "country": "US", - "period": { - "start": "2024-01-01", - "end": "2024-12-31" - } - } - ], - "photo": [ - { - "contentType": "image/png", - "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==", - "title": "Profile Photo", - "creation": "2024-06-01" - } - ], - "qualification": [ - { - "identifier": [ - { - "system": "http://nursing-board.example.org/licenses", - "value": "RN-999888" - } - ], - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "224571005", - "display": "Nurse practitioner" - } - ], - "text": "Registered Nurse" - }, - "period": { - "start": "2018-05-15" - }, - "issuer": { - "display": "Oregon State Board of Nursing" - } - }, - { - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "224535009", - "display": "Registered nurse" - } - ], - "text": "Master of Science in Nursing" - }, - "period": { - "start": "2020-12-15", - "end": "2020-12-15" - }, - "issuer": { - "display": "Oregon Health & Science University" - } - }, - { - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "394802001", - "display": "General medicine" - } - ] - }, - "period": { - "start": "2021-02-01" - }, - "issuer": { - "display": "American Nurses Credentialing Center" - } - }, - { - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "408443003", - "display": "General medical practice" - } - ] - }, - "period": { - "start": "2022-03-01" - } - } - ], - "communication": [ - { - "coding": [ - { - "system": "urn:ietf:bcp:47", - "code": "en-US", - "display": "English (United States)" - } - ] - }, - { - "coding": [ - { - "system": "urn:ietf:bcp:47", - "code": "zh-CN", - "display": "Chinese (China)" - } - ] - }, - { - "coding": [ - { - "system": "urn:ietf:bcp:47", - "code": "ko", - "display": "Korean" - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/practitioners.json.license b/packages/fhir/test/resources/practitioners.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/practitioners.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/practitioners.test.ts b/packages/fhir/test/resources/practitioners.test.ts deleted file mode 100644 index 7224e767..00000000 --- a/packages/fhir/test/resources/practitioners.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Practitioner } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirPractitioner, - type untypedPractitionerSchema, -} from '../../src/index.js' - -describe('Practitioner Resource', () => { - it('should validate FHIR Practitioners from practitioners.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/practitioners.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirPractitioner.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/procedures.json b/packages/fhir/test/resources/procedures.json deleted file mode 100644 index 84be1270..00000000 --- a/packages/fhir/test/resources/procedures.json +++ /dev/null @@ -1,154 +0,0 @@ -{ - "0": { - "resourceType": "Procedure", - "id": "procedure-example-0", - "status": "completed", - "category": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "103693007", - "display": "Diagnostic procedure" - } - ] - }, - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "80146002", - "display": "Appendectomy" - } - ] - }, - "subject": { "reference": "Patient/patient-example" }, - "performedDateTime": "2019-07-22", - "performer": [ - { "actor": { "reference": "Practitioner/practitioner-example" } } - ], - "recorder": { "reference": "Practitioner/practitioner-recorder" } - }, - "1": { - "resourceType": "Procedure", - "id": "procedure-example-1", - "status": "in-progress", - "code": { - "coding": [ - { - "system": "http://loinc.org", - "code": "LP12345-6", - "display": "Chemotherapy" - } - ] - }, - "subject": { "reference": "Patient/patient-another" }, - "encounter": { "reference": "Encounter/encounter-example" }, - "performedPeriod": { "start": "2021-05-01", "end": "2021-08-01" }, - "performer": [ - { "actor": { "reference": "Practitioner/practitioner-oncologist" } } - ], - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "363346000", - "display": "Malignant tumor" - } - ] - } - ] - }, - "2": { - "resourceType": "Procedure", - "id": "procedure-example-2", - "status": "not-done", - "statusReason": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/procedure-not-performed-reason", - "code": "PATOBJ" - } - ] - }, - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "73761001", - "display": "Colonoscopy" - } - ] - }, - "subject": { "reference": "Patient/patient-refusal" }, - "note": [{ "text": "Patient refused the procedure." }] - }, - "3": { - "resourceType": "Procedure", - "id": "procedure-example-3", - "status": "completed", - "category": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "387713003", - "display": "Surgical procedure" - } - ] - }, - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "174041007", - "display": "Coronary artery bypass graft" - } - ] - }, - "subject": { "reference": "Patient/patient-heart" }, - "encounter": { "reference": "Encounter/enc-surgery" }, - "performedDateTime": "2022-02-15", - "performer": [ - { - "actor": { "reference": "Practitioner/practitioner-surgeon" }, - "onBehalfOf": { "reference": "Organization/org-hospital" } - } - ], - "bodySite": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "302509004", - "display": "Coronary artery" - } - ] - } - ], - "focalDevice": [ - { - "action": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "129303008", - "display": "Implantation" - } - ] - }, - "manipulated": { "reference": "Device/device-stent" } - } - ], - "complication": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "233604007", - "display": "Surgical wound infection" - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/procedures.json.license b/packages/fhir/test/resources/procedures.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/procedures.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/procedures.test.ts b/packages/fhir/test/resources/procedures.test.ts deleted file mode 100644 index 22477547..00000000 --- a/packages/fhir/test/resources/procedures.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Procedure } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirProcedure, type untypedProcedureSchema } from '../../src/index.js' - -describe('Procedure Resource', () => { - it('should validate FHIR Procedures from procedures.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/procedures.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirProcedure.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/provenances.json b/packages/fhir/test/resources/provenances.json deleted file mode 100644 index 93d7d039..00000000 --- a/packages/fhir/test/resources/provenances.json +++ /dev/null @@ -1,186 +0,0 @@ -{ - "0": { - "resourceType": "Provenance", - "id": "provenance-example-0", - "target": [ - { - "reference": "Patient/example" - } - ], - "recorded": "2023-01-15T10:30:00Z", - "agent": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", - "code": "author" - } - ] - }, - "who": { - "reference": "Practitioner/example" - } - } - ], - "reason": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", - "code": "TREAT" - } - ] - } - ] - }, - "1": { - "resourceType": "Provenance", - "id": "provenance-example-1", - "target": [ - { - "reference": "Observation/example" - } - ], - "occurredDateTime": "2023-02-20T14:00:00Z", - "recorded": "2023-02-20T14:15:00Z", - "policy": ["http://example.org/fhir/policy/data-collection"], - "agent": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", - "code": "enterer" - } - ] - }, - "who": { - "reference": "Practitioner/practitioner-1" - } - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", - "code": "verifier" - } - ] - }, - "who": { - "reference": "Practitioner/practitioner-2" - }, - "onBehalfOf": { - "reference": "Organization/hospital" - } - } - ], - "entity": [ - { - "role": "source", - "what": { - "reference": "Device/measurement-device" - } - } - ] - }, - "2": { - "resourceType": "Provenance", - "id": "provenance-example-2", - "target": [ - { - "reference": "MedicationRequest/example" - } - ], - "occurredPeriod": { - "start": "2023-03-10T09:00:00Z", - "end": "2023-03-10T09:30:00Z" - }, - "recorded": "2023-03-10T09:35:00Z", - "location": { - "reference": "Location/clinic" - }, - "agent": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", - "code": "author" - } - ] - }, - "role": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleClass", - "code": "ASSIGNED" - } - ] - } - ], - "who": { - "reference": "Practitioner/prescriber" - } - } - ], - "activity": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", - "code": "CREATE" - } - ] - } - }, - "3": { - "resourceType": "Provenance", - "id": "provenance-example-3", - "target": [ - { - "reference": "Procedure/example" - } - ], - "recorded": "2023-04-05T16:45:00Z", - "agent": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", - "code": "performer" - } - ] - }, - "who": { - "reference": "Practitioner/surgeon" - } - } - ], - "entity": [ - { - "role": "revision", - "what": { - "reference": "Procedure/original-procedure" - }, - "agent": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", - "code": "author" - } - ] - }, - "who": { - "reference": "Practitioner/original-surgeon" - } - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/provenances.json.license b/packages/fhir/test/resources/provenances.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/provenances.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/provenances.test.ts b/packages/fhir/test/resources/provenances.test.ts deleted file mode 100644 index 0e48bf24..00000000 --- a/packages/fhir/test/resources/provenances.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Provenance } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirProvenance, - type untypedProvenanceSchema, -} from '../../src/index.js' - -describe('Provenance Resource', () => { - it('should validate FHIR Provenances from provenances.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/provenances.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirProvenance.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/questionnaireResponses.json b/packages/fhir/test/resources/questionnaireResponses.json deleted file mode 100644 index becb5b51..00000000 --- a/packages/fhir/test/resources/questionnaireResponses.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "0": { - "resourceType": "QuestionnaireResponse", - "id": "qr-example-0", - "status": "completed", - "authored": "2024-11-15T10:30:00Z", - "item": [ - { - "linkId": "1", - "text": "Do you smoke?", - "answer": [ - { - "valueBoolean": false - } - ] - } - ] - }, - "1": { - "resourceType": "QuestionnaireResponse", - "id": "qr-example-1", - "identifier": { - "system": "http://example.org/questionnaire-responses", - "value": "QR-001" - }, - "questionnaire": "http://example.org/Questionnaire/health-survey", - "status": "completed", - "subject": { - "reference": "Patient/patient-example" - }, - "authored": "2024-11-10T14:20:00Z", - "author": { - "reference": "Patient/patient-example" - }, - "item": [ - { - "linkId": "1", - "text": "General health", - "answer": [ - { - "valueCoding": { - "system": "http://example.org/health-status", - "code": "good", - "display": "Good" - } - } - ] - }, - { - "linkId": "2", - "text": "Exercise frequency", - "answer": [ - { - "valueString": "3 times per week" - } - ] - } - ] - }, - "2": { - "resourceType": "QuestionnaireResponse", - "id": "qr-example-2", - "status": "in-progress", - "authored": "2024-11-15T09:00:00Z", - "item": [ - { - "linkId": "1", - "text": "Name", - "answer": [ - { - "valueString": "John Smith" - } - ] - } - ] - }, - "3": { - "resourceType": "QuestionnaireResponse", - "id": "qr-example-3", - "questionnaire": "http://example.org/Questionnaire/pain-assessment", - "status": "completed", - "subject": { - "reference": "Patient/patient-example-2" - }, - "encounter": { - "reference": "Encounter/encounter-example" - }, - "authored": "2024-11-01T11:45:00Z", - "author": { - "reference": "Practitioner/practitioner-example" - }, - "source": { - "reference": "Patient/patient-example-2" - }, - "item": [ - { - "linkId": "1", - "text": "Pain level (0-10)", - "answer": [ - { - "valueInteger": 7 - } - ] - }, - { - "linkId": "2", - "text": "Pain location", - "answer": [ - { - "valueString": "Lower back" - } - ] - }, - { - "linkId": "3", - "text": "Pain duration", - "item": [ - { - "linkId": "3.1", - "text": "Duration in days", - "answer": [ - { - "valueInteger": 5 - } - ] - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/questionnaireResponses.json.license b/packages/fhir/test/resources/questionnaireResponses.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/questionnaireResponses.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/questionnaireResponses.test.ts b/packages/fhir/test/resources/questionnaireResponses.test.ts deleted file mode 100644 index 21dc90aa..00000000 --- a/packages/fhir/test/resources/questionnaireResponses.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type QuestionnaireResponse } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirQuestionnaireResponse, - type untypedQuestionnaireResponseSchema, -} from '../../src/index.js' - -describe('QuestionnaireResponse Resource', () => { - it('should validate FHIR questionnaireResponse from questionnaireResponses.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/questionnaireResponses.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirQuestionnaireResponse.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/questionnaires.json b/packages/fhir/test/resources/questionnaires.json deleted file mode 100644 index 22c10a16..00000000 --- a/packages/fhir/test/resources/questionnaires.json +++ /dev/null @@ -1,7183 +0,0 @@ -{ - "0": { - "id": "9528ccc2-d1be-4c4c-9c3c-19f78e51ec19", - "resourceType": "Questionnaire", - "meta": { - "profile": [ - "http://schmiedmayerlab.github.io/grove-ts/fhir/StructureDefinition/sdf-Questionnaire" - ], - "tag": [ - { - "system": "urn:ietf:bcp:47", - "code": "en-US", - "display": "English" - } - ] - }, - "title": "KCCQ-12", - "language": "en-US", - "status": "active", - "publisher": "Grove Project", - "subjectType": ["Patient"], - "useContext": [ - { - "code": { - "system": "http://hl7.org/fhir/ValueSet/usage-context-type", - "code": "focus", - "display": "Clinical Focus" - }, - "valueCodeableConcept": { - "coding": [ - { - "system": "urn:oid:2.16.578.1.12.4.1.1.8655", - "display": "KCCQ-12" - } - ] - } - } - ], - "contact": [ - { - "name": "http://schmiedmayerlab.github.io/grove-ts" - } - ], - "url": "http://schmiedmayerlab.github.io/grove-ts/fhir/questionnaire/9528ccc2-d1be-4c4c-9c3c-19f78e51ec19", - "item": [ - { - "linkId": "73490535-203f-44b2-d1b7-7c0a786c16f9", - "type": "display", - "text": "The following questions refer to your heart failure and how it may affect your life. Please read and complete the following questions. There are no right or wrong answers. Please mark the answer that best applies to you.\n\nThese results will be send to your care team.", - "required": false - }, - { - "linkId": "c0b3bef6-1e2d-4621-d82e-b73069574dc4", - "type": "group", - "text": "Heart failure affects different people in different ways. Some feel shortness of breath while others feel fatigue. Please indicate how much you are limited by heart failure (shortness of breath or fatigue) in your ability to do the following activities over the past 2 weeks.", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "a459b804-35bf-4792-f1eb-0b52c4e176e1", - "type": "choice", - "text": "Showering/bathing", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "c973b297-4561-4b8c-c8c6-fb559ca15169", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "1", - "display": "Extremely Limited" - } - }, - { - "valueCoding": { - "id": "1a8c0f4f-12a7-458d-8881-0a0750117ff0", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "2", - "display": "Quite a bit Limited" - } - }, - { - "valueCoding": { - "id": "1e372729-1834-41c4-8cda-9a8196a77971", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "3", - "display": "Moderately Limited" - } - }, - { - "valueCoding": { - "id": "cb3c686a-42a0-4c68-8b8d-f2621129adef", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "4", - "display": "Slightly Limited" - } - }, - { - "valueCoding": { - "id": "4c04162f-e529-406b-a0fa-d636ad1bf6d7", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "5", - "display": "Not at all Limited" - } - }, - { - "valueCoding": { - "id": "c4f351f5-33d6-477a-f9ba-f68a1ae0df6d", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "6", - "display": "Limited for other reasons or did not do the activity" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - }, - { - "linkId": "cf9c5031-1ed5-438a-fc7d-dc69234015a0", - "type": "choice", - "text": "Walking 1 block on level ground", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "c973b297-4561-4b8c-c8c6-fb559ca15169", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "1", - "display": "Extremely Limited" - } - }, - { - "valueCoding": { - "id": "1a8c0f4f-12a7-458d-8881-0a0750117ff0", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "2", - "display": "Quite a bit Limited" - } - }, - { - "valueCoding": { - "id": "1e372729-1834-41c4-8cda-9a8196a77971", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "3", - "display": "Moderately Limited" - } - }, - { - "valueCoding": { - "id": "cb3c686a-42a0-4c68-8b8d-f2621129adef", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "4", - "display": "Slightly Limited" - } - }, - { - "valueCoding": { - "id": "4c04162f-e529-406b-a0fa-d636ad1bf6d7", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "5", - "display": "Not at all Limited" - } - }, - { - "valueCoding": { - "id": "c4f351f5-33d6-477a-f9ba-f68a1ae0df6d", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "6", - "display": "Limited for other reasons or did not do the activity" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - }, - { - "linkId": "1fad0f81-b2a9-4c8f-9a78-4b2a5d7aef07", - "type": "choice", - "text": "Hurrying or jogging (as if to catch a bus)", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "c973b297-4561-4b8c-c8c6-fb559ca15169", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "1", - "display": "Extremely Limited" - } - }, - { - "valueCoding": { - "id": "1a8c0f4f-12a7-458d-8881-0a0750117ff0", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "2", - "display": "Quite a bit Limited" - } - }, - { - "valueCoding": { - "id": "1e372729-1834-41c4-8cda-9a8196a77971", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "3", - "display": "Moderately Limited" - } - }, - { - "valueCoding": { - "id": "cb3c686a-42a0-4c68-8b8d-f2621129adef", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "4", - "display": "Slightly Limited" - } - }, - { - "valueCoding": { - "id": "4c04162f-e529-406b-a0fa-d636ad1bf6d7", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "5", - "display": "Not at all Limited" - } - }, - { - "valueCoding": { - "id": "c4f351f5-33d6-477a-f9ba-f68a1ae0df6d", - "system": "urn:uuid:8290e1d8-8141-4982-deb9-57f9d2e13a14", - "code": "6", - "display": "Limited for other reasons or did not do the activity" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "692bda7d-a616-43d1-8dc6-8291f6460ab2", - "type": "choice", - "text": "Over the past 2 weeks, how many times did you have swelling in your feet, ankles or legs when you woke up in the morning?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "80df26e0-55a6-4d50-90de-b60cd851c4fb", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "1", - "display": "Every morning" - } - }, - { - "valueCoding": { - "id": "473f99f2-b9d2-4e92-85ed-ca78f3e0141c", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "2", - "display": "3 or more times per week but not every day" - } - }, - { - "valueCoding": { - "id": "e3eab124-44f9-43a4-b1f6-e42b0199e39b", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "3", - "display": "1-2 times per week" - } - }, - { - "valueCoding": { - "id": "253f487a-f26b-442f-8757-8a2401465fed", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "4", - "display": "Less than once a week" - } - }, - { - "valueCoding": { - "id": "7de45367-76b2-4b9b-8e4e-050bae6b309c", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "5", - "display": "Never over the past 2 weeks" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - }, - { - "linkId": "b1734b9e-1d16-4238-8556-5ae3fa0ba913", - "type": "choice", - "text": "Over the past 2 weeks, on average, how many times has fatigue limited your ability to do what you wanted", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "80df26e0-55a6-4d50-90de-b60cd851c4fb", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "1", - "display": "All of the time" - } - }, - { - "valueCoding": { - "id": "473f99f2-b9d2-4e92-85ed-ca78f3e0141c", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "2", - "display": "Several times per day" - } - }, - { - "valueCoding": { - "id": "e3eab124-44f9-43a4-b1f6-e42b0199e39b", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "3", - "display": "At least once a day" - } - }, - { - "valueCoding": { - "id": "253f487a-f26b-442f-8757-8a2401465fed", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "4", - "display": "3 or more times per week but not every day" - } - }, - { - "valueCoding": { - "id": "7de45367-76b2-4b9b-8e4e-050bae6b309c", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "5", - "display": "1-2 times per week" - } - }, - { - "valueCoding": { - "id": "e174ffbc-23d4-4c44-94c5-1864bf2afa18", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "6", - "display": "Less than once a week" - } - }, - { - "valueCoding": { - "id": "853fb552-dd2c-48f6-86b9-1bb923d7fd2d", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "7", - "display": "Never over the past 2 weeks" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - }, - { - "linkId": "57f37fb3-a0ad-4b1f-844e-3f67d9b76946", - "type": "choice", - "text": "Over the past 2 weeks, on average, how many times has shortness of breath limited your ability to do what you wanted", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "80df26e0-55a6-4d50-90de-b60cd851c4fb", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "1", - "display": "All of the time" - } - }, - { - "valueCoding": { - "id": "473f99f2-b9d2-4e92-85ed-ca78f3e0141c", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "2", - "display": "Several times per day" - } - }, - { - "valueCoding": { - "id": "e3eab124-44f9-43a4-b1f6-e42b0199e39b", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "3", - "display": "At least once a day" - } - }, - { - "valueCoding": { - "id": "253f487a-f26b-442f-8757-8a2401465fed", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "4", - "display": "3 or more times per week but not every day" - } - }, - { - "valueCoding": { - "id": "7de45367-76b2-4b9b-8e4e-050bae6b309c", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "5", - "display": "1-2 times per week" - } - }, - { - "valueCoding": { - "id": "e174ffbc-23d4-4c44-94c5-1864bf2afa18", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "6", - "display": "Less than once a week" - } - }, - { - "valueCoding": { - "id": "853fb552-dd2c-48f6-86b9-1bb923d7fd2d", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "7", - "display": "Never over the past 2 weeks" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - }, - { - "linkId": "396164df-d045-4c56-d710-513297bdc6f2", - "type": "choice", - "text": "Over the past 2 weeks, on average, how many times have you been forced to sleep sitting up in a chair or with at least 3 pillows to prop you up because of shortness of breath?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "80df26e0-55a6-4d50-90de-b60cd851c4fb", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "1", - "display": "Every night" - } - }, - { - "valueCoding": { - "id": "473f99f2-b9d2-4e92-85ed-ca78f3e0141c", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "2", - "display": "3 or more times per week but not every day" - } - }, - { - "valueCoding": { - "id": "e3eab124-44f9-43a4-b1f6-e42b0199e39b", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "3", - "display": "1-2 times per week" - } - }, - { - "valueCoding": { - "id": "253f487a-f26b-442f-8757-8a2401465fed", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "4", - "display": "Less than once a week" - } - }, - { - "valueCoding": { - "id": "7de45367-76b2-4b9b-8e4e-050bae6b309c", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "5", - "display": "Never over the past 2 weeks" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - }, - { - "linkId": "75e3f62e-e37d-48a2-f4d9-af2db8922da0", - "type": "choice", - "text": "Over the past 2 weeks, how much has your heart failure limited your enjoyment of life?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "80df26e0-55a6-4d50-90de-b60cd851c4fb", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "1", - "display": "It has extremely limited my enjoyment of life" - } - }, - { - "valueCoding": { - "id": "473f99f2-b9d2-4e92-85ed-ca78f3e0141c", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "2", - "display": "It has limited my enjoyment of life quite a bit" - } - }, - { - "valueCoding": { - "id": "e3eab124-44f9-43a4-b1f6-e42b0199e39b", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "3", - "display": "It has moderately limited my enjoyment of life" - } - }, - { - "valueCoding": { - "id": "253f487a-f26b-442f-8757-8a2401465fed", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "4", - "display": "It has slightly limited my enjoyment of life" - } - }, - { - "valueCoding": { - "id": "7de45367-76b2-4b9b-8e4e-050bae6b309c", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "5", - "display": "It has not limited my enjoyment of life" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - }, - { - "linkId": "fce3a16e-c6d8-4bac-8ab5-8f4aee4adc08", - "type": "choice", - "text": "If you had to spend the rest of your life with your heart failure the way it is right now, how would you feel about this?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "80df26e0-55a6-4d50-90de-b60cd851c4fb", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "1", - "display": "Not at all satisfied" - } - }, - { - "valueCoding": { - "id": "473f99f2-b9d2-4e92-85ed-ca78f3e0141c", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "2", - "display": "Mostly dissatisfied" - } - }, - { - "valueCoding": { - "id": "e3eab124-44f9-43a4-b1f6-e42b0199e39b", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "3", - "display": "Somewhat satisfied" - } - }, - { - "valueCoding": { - "id": "253f487a-f26b-442f-8757-8a2401465fed", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "4", - "display": "Mostly satisfied" - } - }, - { - "valueCoding": { - "id": "7de45367-76b2-4b9b-8e4e-050bae6b309c", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "5", - "display": "Completely satisfied" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - }, - { - "linkId": "8649bc8c-f908-487d-87a4-a97106b1a4c3", - "type": "group", - "text": "How much does your heart failure affect your lifestyle? Please indicate how your heart failure may have limited your participation in the following activities over the past 2 weeks.", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "8b022e69-127d-4447-8190-39ac645e60e1", - "type": "choice", - "text": "Hobbies, recreational activities", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "5df54746-3a58-4153-8f07-1f13fdc09902", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "1", - "display": "Severely Limited" - } - }, - { - "valueCoding": { - "id": "a57b3a46-a005-4204-a07f-b2d535a6d5ab", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "2", - "display": "Limited quite a bit" - } - }, - { - "valueCoding": { - "id": "632d08fb-b521-4511-8887-b7c375d901f3", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "3", - "display": "Moderately limited" - } - }, - { - "valueCoding": { - "id": "fcbe715a-b86c-4fdc-8855-8aaa353d06d7", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "4", - "display": "Slightly limited" - } - }, - { - "valueCoding": { - "id": "d9cf6b00-5fed-48ac-a2be-a2e29688c5fa", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "5", - "display": "Did not limit at all" - } - }, - { - "valueCoding": { - "id": "8a20b488-aad4-4676-86d3-c494b979db3f", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "6", - "display": "Does not apply or did not do for other reasons" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - }, - { - "linkId": "1eee7259-da1c-4cba-80a9-e67e684573a1", - "type": "choice", - "text": "Working or doing household chores", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "5df54746-3a58-4153-8f07-1f13fdc09902", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "1", - "display": "Severely Limited" - } - }, - { - "valueCoding": { - "id": "a57b3a46-a005-4204-a07f-b2d535a6d5ab", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "2", - "display": "Limited quite a bit" - } - }, - { - "valueCoding": { - "id": "632d08fb-b521-4511-8887-b7c375d901f3", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "3", - "display": "Moderately limited" - } - }, - { - "valueCoding": { - "id": "fcbe715a-b86c-4fdc-8855-8aaa353d06d7", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "4", - "display": "Slightly limited" - } - }, - { - "valueCoding": { - "id": "d9cf6b00-5fed-48ac-a2be-a2e29688c5fa", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "5", - "display": "Did not limit at all" - } - }, - { - "valueCoding": { - "id": "8a20b488-aad4-4676-86d3-c494b979db3f", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "6", - "display": "Does not apply or did not do for other reasons" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - }, - { - "linkId": "883a22a8-2f6e-4b41-84b7-0028ed543192", - "type": "choice", - "text": "Visiting family or friends out of your home", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "5df54746-3a58-4153-8f07-1f13fdc09902", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "1", - "display": "Severely Limited" - } - }, - { - "valueCoding": { - "id": "a57b3a46-a005-4204-a07f-b2d535a6d5ab", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "2", - "display": "Limited quite a bit" - } - }, - { - "valueCoding": { - "id": "632d08fb-b521-4511-8887-b7c375d901f3", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "3", - "display": "Moderately limited" - } - }, - { - "valueCoding": { - "id": "fcbe715a-b86c-4fdc-8855-8aaa353d06d7", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "4", - "display": "Slightly limited" - } - }, - { - "valueCoding": { - "id": "d9cf6b00-5fed-48ac-a2be-a2e29688c5fa", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "5", - "display": "Did not limit at all" - } - }, - { - "valueCoding": { - "id": "8a20b488-aad4-4676-86d3-c494b979db3f", - "system": "urn:uuid:90ab9a5a-0ed7-43e0-9131-75ab9d8b94cf", - "code": "6", - "display": "Does not apply or did not do for other reasons" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "24108967-2ff3-40d0-c54f-a7b97bb84d05", - "type": "choice", - "text": "In the last two weeks, how much has your dizziness affected you?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "80df26e0-55a6-4d50-90de-b60cd851c4fb", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "5", - "display": "Extremely bothersome" - } - }, - { - "valueCoding": { - "id": "473f99f2-b9d2-4e92-85ed-ca78f3e0141c", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "4", - "display": "Quite a bit bothersome" - } - }, - { - "valueCoding": { - "id": "e3eab124-44f9-43a4-b1f6-e42b0199e39b", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "3", - "display": "Moderately bothersome" - } - }, - { - "valueCoding": { - "id": "253f487a-f26b-442f-8757-8a2401465fed", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "2", - "display": "Slightly bothersome" - } - }, - { - "valueCoding": { - "id": "7de45367-76b2-4b9b-8e4e-050bae6b309c", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "1", - "display": "Not at all bothersome" - } - }, - { - "valueCoding": { - "id": "3d1a8f9b-e666-4ad4-9202-571313a2b5d1", - "system": "urn:uuid:2b2f9a9a-e721-495a-82d6-fbc1b22a27b2", - "code": "0", - "display": "I've had no dizziness" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - "registration_en_US": { - "id": "grove-registration", - "resourceType": "Questionnaire", - "meta": { - "profile": [ - "http://schmiedmayerlab.github.io/grove-ts/fhir/StructureDefinition/sdf-Questionnaire" - ], - "tag": [ - { - "system": "urn:ietf:bcp:47", - "code": "en-US", - "display": "English" - } - ] - }, - "title": "Registration Survey", - "language": "en-US", - "status": "active", - "publisher": "Grove Project", - "subjectType": ["Patient"], - "useContext": [], - "contact": [ - { - "name": "http://schmiedmayerlab.github.io/grove-ts" - } - ], - "url": "http://schmiedmayerlab.github.io/grove-ts/fhir/questionnaire/grove-registration", - "item": [ - { - "linkId": "welcome", - "type": "display", - "text": "Welcome to the Grove study! Please complete the following survey to help us understand your health and well-being.", - "required": false - }, - { - "linkId": "personal-information", - "type": "group", - "text": "Personal information", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "personal-information.description", - "type": "display", - "text": "Please provide the following information to help us understand your health and well-being.", - "required": false - }, - { - "linkId": "personal-information.dateOfBirth", - "type": "date", - "text": "Date of Birth", - "required": true - }, - { - "linkId": "personal-information.sex", - "type": "choice", - "text": "Select your sex assigned at birth:", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "male", - "system": "grove-sex", - "code": "male", - "display": "Male" - } - }, - { - "valueCoding": { - "id": "female", - "system": "grove-sex", - "code": "female", - "display": "Female" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "lab.2160-0.exists", - "type": "group", - "text": "Creatinine", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.2160-0.exists-description", - "type": "display", - "text": "The creatinine level in your body helps understand how your kidneys handle the drugs you are taking.", - "required": false - }, - { - "linkId": "lab.2160-0.exists", - "type": "boolean", - "text": "Have you recently received a new creatinine value?", - "required": true - } - ] - }, - { - "linkId": "lab.2160-0.page1", - "type": "group", - "text": "Creatinine", - "required": false, - "enableWhen": [ - { - "question": "lab.2160-0.exists", - "operator": "=", - "answerBoolean": true - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.2160-0.description", - "type": "display", - "text": "The creatinine level in your body helps understand how your kidneys handle the drugs you are taking.", - "required": false - }, - { - "linkId": "lab.2160-0.value", - "type": "decimal", - "text": "Creatinine (mg/dL):", - "required": true - }, - { - "linkId": "lab.2160-0.dateTime", - "type": "dateTime", - "text": "Date:", - "required": true - } - ] - }, - { - "linkId": "lab.6298-4.exists", - "type": "group", - "text": "Potassium", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.6298-4.exists-description", - "type": "display", - "text": "The potassium level in your body helps understand how your liver handles the drugs you are taking.", - "required": false - }, - { - "linkId": "lab.6298-4.exists", - "type": "boolean", - "text": "Have you recently received a new potassium value?", - "required": true - } - ] - }, - { - "linkId": "lab.6298-4.page1", - "type": "group", - "text": "Potassium", - "required": false, - "enableWhen": [ - { - "question": "lab.6298-4.exists", - "operator": "=", - "answerBoolean": true - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.6298-4.description", - "type": "display", - "text": "The potassium level in your body helps understand how your liver handles the drugs you are taking.", - "required": false - }, - { - "linkId": "lab.6298-4.value", - "type": "decimal", - "text": "Potassium (mEq/L):", - "required": true - }, - { - "linkId": "lab.6298-4.dateTime", - "type": "dateTime", - "text": "Date:", - "required": true - } - ] - }, - { - "linkId": "lab.8340-2.exists", - "type": "group", - "text": "Dry Weight", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.8340-2.exists-description", - "type": "display", - "text": "The dry weight is useful to set a baseline to check that your weight does not increase unnoticed.", - "required": false - }, - { - "linkId": "lab.8340-2.exists", - "type": "boolean", - "text": "Have you recently received a new dry weight value?", - "required": true - } - ] - }, - { - "linkId": "lab.8340-2.page1", - "type": "group", - "text": "Dry Weight", - "required": false, - "enableWhen": [ - { - "question": "lab.8340-2.exists", - "operator": "=", - "answerBoolean": true - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.8340-2.description", - "type": "display", - "text": "The dry weight is useful to set a baseline to check that your weight does not increase unnoticed.", - "required": false - }, - { - "linkId": "lab.8340-2.value", - "type": "decimal", - "text": "Dry Weight (lbs):", - "required": true - }, - { - "linkId": "lab.8340-2.dateTime", - "type": "dateTime", - "text": "Date:", - "required": true - } - ] - }, - { - "linkId": "medication.betablockers.exists", - "type": "group", - "text": "Beta Blockers", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.betablockers.exists-description", - "type": "display", - "text": "Do you take any of the following medications?\n\nBisoprolol (Zebeta)\nCarvedilol (Coreg)\nMetoprolol Succinate (Toprol XL)\nCarvedilol Phosphate (Coreg CR)", - "required": false - }, - { - "linkId": "medication.betablockers.exists", - "type": "choice", - "text": "Do you take any medication from the above list?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "yes", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-registration", - "code": "yes", - "display": "Yes" - } - }, - { - "valueCoding": { - "id": "no", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-registration", - "code": "no", - "display": "No" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.betablockers.page1", - "type": "group", - "text": "Beta Blockers", - "required": false, - "enableWhen": [ - { - "question": "medication.betablockers.exists", - "operator": "=", - "answerCoding": { - "id": "yes", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-registration", - "code": "yes", - "display": "Yes" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.betablockers.description", - "type": "display", - "text": "Please enter which drug you are taking, how often you take it per day and how many pills/tablets you take per intake.\n\nDo not enter the total amount of pills/tablets you take per day.", - "required": false - }, - { - "linkId": "medication.betablockers.frequency", - "type": "decimal", - "text": "Intake frequency (per day):", - "required": true - }, - { - "linkId": "medication.betablockers.quantity", - "type": "decimal", - "text": "Pills/tablets per intake:", - "required": true - }, - { - "linkId": "medication.betablockers.drug", - "type": "choice", - "text": "Which pill/tablet do you take?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "medications/19484/drugs/854901", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/19484/drugs/854901", - "display": "Bisoprolol (Zebeta)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/19484/drugs/854905", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/19484/drugs/854905", - "display": "Bisoprolol (Zebeta)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/20352/drugs/200032", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/20352/drugs/200032", - "display": "Carvedilol (Coreg)\n12.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/20352/drugs/200033", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/20352/drugs/200033", - "display": "Carvedilol (Coreg)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/20352/drugs/686924", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/20352/drugs/686924", - "display": "Carvedilol (Coreg)\n3.125 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/20352/drugs/200031", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/20352/drugs/200031", - "display": "Carvedilol (Coreg)\n6.25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/668310/drugs/860510", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/668310/drugs/860510", - "display": "Carvedilol Phosphate (Coreg CR)\n10 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/668310/drugs/860516", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/668310/drugs/860516", - "display": "Carvedilol Phosphate (Coreg CR)\n20 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/668310/drugs/860522", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/668310/drugs/860522", - "display": "Carvedilol Phosphate (Coreg CR)\n40 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/668310/drugs/860532", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/668310/drugs/860532", - "display": "Carvedilol Phosphate (Coreg CR)\n80 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/1999031", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/1999031", - "display": "Metoprolol Succinate (Toprol XL)\n100 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/866412", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/866412", - "display": "Metoprolol Succinate (Toprol XL)\n100 mg Extended Release Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/1999033", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/1999033", - "display": "Metoprolol Succinate (Toprol XL)\n200 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/866419", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/866419", - "display": "Metoprolol Succinate (Toprol XL)\n200 mg Extended Release Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/1999035", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/1999035", - "display": "Metoprolol Succinate (Toprol XL)\n25 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/866427", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/866427", - "display": "Metoprolol Succinate (Toprol XL)\n25 mg Extended Release Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/1999037", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/1999037", - "display": "Metoprolol Succinate (Toprol XL)\n50 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/866436", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/866436", - "display": "Metoprolol Succinate (Toprol XL)\n50 mg Extended Release Oral Tablet" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.rasi.exists", - "type": "group", - "text": "Renin-Angiotensin System Inhibitors (RASI)", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.rasi.exists-description", - "type": "display", - "text": "Do you take any of the following medications?\n\nCaptopril (Capoten)\nEnalapril (Vasotec)\nBenazepril (Lotensin)\nLisinopril (Zestril)\nMoexipril (Univasc)\nQuinapril (Accupril)\nRamipril (Altace)\nTrandolapril (Mavik)\nPerindopril (Aceon)\nValsartan (Diovan)\nTelmisartan (Micardis)\nEprosartan (Teveten)\nIrbesartan (Avapro)\nOlmesartan Medoxomil (Benicar)\nLosartan Potassium (Cozaar)\nCandesartan (Atacand)\nFosinopril Sodium (Monopril)\nAzilsartan Medoxomil (Edarbi)\nSacubitril / Valsartan (Entresto)", - "required": false - }, - { - "linkId": "medication.rasi.exists", - "type": "choice", - "text": "Do you take any medication from the above list?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "yes", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-registration", - "code": "yes", - "display": "Yes" - } - }, - { - "valueCoding": { - "id": "no", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-registration", - "code": "no", - "display": "No" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.rasi.page1", - "type": "group", - "text": "Renin-Angiotensin System Inhibitors (RASI)", - "required": false, - "enableWhen": [ - { - "question": "medication.rasi.exists", - "operator": "=", - "answerCoding": { - "id": "yes", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-registration", - "code": "yes", - "display": "Yes" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.rasi.description", - "type": "display", - "text": "Please enter which drug you are taking, how often you take it per day and how many pills/tablets you take per intake.\n\nDo not enter the total amount of pills/tablets you take per day.", - "required": false - }, - { - "linkId": "medication.rasi.frequency", - "type": "decimal", - "text": "Intake frequency (per day):", - "required": true - }, - { - "linkId": "medication.rasi.quantity", - "type": "decimal", - "text": "Pills/tablets per intake:", - "required": true - }, - { - "linkId": "medication.rasi.drug", - "type": "choice", - "text": "Which pill/tablet do you take?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "medications/1091642/drugs/1091646", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1091642/drugs/1091646", - "display": "Azilsartan Medoxomil (Edarbi)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1091642/drugs/1091652", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1091642/drugs/1091652", - "display": "Azilsartan Medoxomil (Edarbi)\n80 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/18867/drugs/898687", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/18867/drugs/898687", - "display": "Benazepril (Lotensin)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/18867/drugs/898690", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/18867/drugs/898690", - "display": "Benazepril (Lotensin)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/18867/drugs/898719", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/18867/drugs/898719", - "display": "Benazepril (Lotensin)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/18867/drugs/898723", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/18867/drugs/898723", - "display": "Benazepril (Lotensin)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/214354/drugs/577776", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/214354/drugs/577776", - "display": "Candesartan (Atacand)\n16 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/214354/drugs/639537", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/214354/drugs/639537", - "display": "Candesartan (Atacand)\n32 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/214354/drugs/153822", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/214354/drugs/153822", - "display": "Candesartan (Atacand)\n4 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/214354/drugs/153823", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/214354/drugs/153823", - "display": "Candesartan (Atacand)\n8 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1998/drugs/308962", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1998/drugs/308962", - "display": "Captopril (Capoten)\n100 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1998/drugs/308963", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1998/drugs/308963", - "display": "Captopril (Capoten)\n12.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1998/drugs/317173", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1998/drugs/317173", - "display": "Captopril (Capoten)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1998/drugs/308964", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1998/drugs/308964", - "display": "Captopril (Capoten)\n50 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/3827/drugs/858817", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/3827/drugs/858817", - "display": "Enalapril (Vasotec)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/3827/drugs/858804", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/3827/drugs/858804", - "display": "Enalapril (Vasotec)\n2.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/3827/drugs/858810", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/3827/drugs/858810", - "display": "Enalapril (Vasotec)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/3827/drugs/858813", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/3827/drugs/858813", - "display": "Enalapril (Vasotec)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/83515/drugs/310139", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/83515/drugs/310139", - "display": "Eprosartan (Teveten)\n400 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/83515/drugs/310140", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/83515/drugs/310140", - "display": "Eprosartan (Teveten)\n600 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/227278/drugs/857169", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/227278/drugs/857169", - "display": "Fosinopril Sodium (Monopril)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/227278/drugs/857183", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/227278/drugs/857183", - "display": "Fosinopril Sodium (Monopril)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/227278/drugs/857187", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/227278/drugs/857187", - "display": "Fosinopril Sodium (Monopril)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/83818/drugs/200095", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/83818/drugs/200095", - "display": "Irbesartan (Avapro)\n150 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/83818/drugs/200096", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/83818/drugs/200096", - "display": "Irbesartan (Avapro)\n300 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/83818/drugs/200094", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/83818/drugs/200094", - "display": "Irbesartan (Avapro)\n75 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/314076", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/314076", - "display": "Lisinopril (Zestril)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/311353", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/311353", - "display": "Lisinopril (Zestril)\n2.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/314077", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/314077", - "display": "Lisinopril (Zestril)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/205326", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/205326", - "display": "Lisinopril (Zestril)\n30 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/197884", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/197884", - "display": "Lisinopril (Zestril)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/311354", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/311354", - "display": "Lisinopril (Zestril)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/203160/drugs/979480", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/203160/drugs/979480", - "display": "Losartan Potassium (Cozaar)\n100 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/203160/drugs/979485", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/203160/drugs/979485", - "display": "Losartan Potassium (Cozaar)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/203160/drugs/979492", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/203160/drugs/979492", - "display": "Losartan Potassium (Cozaar)\n50 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/30131/drugs/1299896", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/30131/drugs/1299896", - "display": "Moexipril (Univasc)\n15 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/30131/drugs/1299897", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/30131/drugs/1299897", - "display": "Moexipril (Univasc)\n7.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/118463/drugs/349401", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/118463/drugs/349401", - "display": "Olmesartan Medoxomil (Benicar)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/118463/drugs/349405", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/118463/drugs/349405", - "display": "Olmesartan Medoxomil (Benicar)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/118463/drugs/349373", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/118463/drugs/349373", - "display": "Olmesartan Medoxomil (Benicar)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/54552/drugs/854984", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/54552/drugs/854984", - "display": "Perindopril (Aceon)\n2 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/54552/drugs/854988", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/54552/drugs/854988", - "display": "Perindopril (Aceon)\n4 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/54552/drugs/854925", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/54552/drugs/854925", - "display": "Perindopril (Aceon)\n8 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35208/drugs/312748", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35208/drugs/312748", - "display": "Quinapril (Accupril)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35208/drugs/312749", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35208/drugs/312749", - "display": "Quinapril (Accupril)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35208/drugs/314203", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35208/drugs/314203", - "display": "Quinapril (Accupril)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35208/drugs/312750", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35208/drugs/312750", - "display": "Quinapril (Accupril)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/845488", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/845488", - "display": "Ramipril (Altace)\n1.25 mg Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/401965", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/401965", - "display": "Ramipril (Altace)\n1.25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/261962", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/261962", - "display": "Ramipril (Altace)\n10 mg Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/401968", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/401968", - "display": "Ramipril (Altace)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/198188", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/198188", - "display": "Ramipril (Altace)\n2.5 mg Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/251856", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/251856", - "display": "Ramipril (Altace)\n2.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/198189", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/198189", - "display": "Ramipril (Altace)\n5 mg Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/251857", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/251857", - "display": "Ramipril (Altace)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1656339/drugs/2679793", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1656339/drugs/2679793", - "display": "Sacubitril / Valsartan (Entresto)\n15/16 mg Oral Pellet" - } - }, - { - "valueCoding": { - "id": "medications/1656339/drugs/1656340", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1656339/drugs/1656340", - "display": "Sacubitril / Valsartan (Entresto)\n24/26 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1656339/drugs/1656349", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1656339/drugs/1656349", - "display": "Sacubitril / Valsartan (Entresto)\n49/51 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1656339/drugs/2679800", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1656339/drugs/2679800", - "display": "Sacubitril / Valsartan (Entresto)\n6/6 mg Oral Pellet" - } - }, - { - "valueCoding": { - "id": "medications/1656339/drugs/1656354", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1656339/drugs/1656354", - "display": "Sacubitril / Valsartan (Entresto)\n97/103 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/73494/drugs/282755", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/73494/drugs/282755", - "display": "Telmisartan (Micardis)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/73494/drugs/205304", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/73494/drugs/205304", - "display": "Telmisartan (Micardis)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/73494/drugs/205305", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/73494/drugs/205305", - "display": "Telmisartan (Micardis)\n80 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38454/drugs/199353", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38454/drugs/199353", - "display": "Trandolapril (Mavik)\n1 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38454/drugs/199351", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38454/drugs/199351", - "display": "Trandolapril (Mavik)\n2 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38454/drugs/199352", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38454/drugs/199352", - "display": "Trandolapril (Mavik)\n4 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/69749/drugs/349201", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/69749/drugs/349201", - "display": "Valsartan (Diovan)\n160 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/69749/drugs/349200", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/69749/drugs/349200", - "display": "Valsartan (Diovan)\n320 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/69749/drugs/349483", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/69749/drugs/349483", - "display": "Valsartan (Diovan)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/69749/drugs/349199", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/69749/drugs/349199", - "display": "Valsartan (Diovan)\n80 mg Oral Tablet" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.mra.exists", - "type": "group", - "text": "Mineralocorticoid Receptor Antagonists (MRA)", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.mra.exists-description", - "type": "display", - "text": "Do you take any of the following medications?\n\nSpironolactone (Aldactone)\nEplerenone (Inspra)", - "required": false - }, - { - "linkId": "medication.mra.exists", - "type": "choice", - "text": "Do you take any medication from the above list?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "yes", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-registration", - "code": "yes", - "display": "Yes" - } - }, - { - "valueCoding": { - "id": "no", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-registration", - "code": "no", - "display": "No" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.mra.page1", - "type": "group", - "text": "Mineralocorticoid Receptor Antagonists (MRA)", - "required": false, - "enableWhen": [ - { - "question": "medication.mra.exists", - "operator": "=", - "answerCoding": { - "id": "yes", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-registration", - "code": "yes", - "display": "Yes" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.mra.description", - "type": "display", - "text": "Please enter which drug you are taking, how often you take it per day and how many pills/tablets you take per intake.\n\nDo not enter the total amount of pills/tablets you take per day.", - "required": false - }, - { - "linkId": "medication.mra.frequency", - "type": "decimal", - "text": "Intake frequency (per day):", - "required": true - }, - { - "linkId": "medication.mra.quantity", - "type": "decimal", - "text": "Pills/tablets per intake:", - "required": true - }, - { - "linkId": "medication.mra.drug", - "type": "choice", - "text": "Which pill/tablet do you take?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "medications/298869/drugs/351256", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/298869/drugs/351256", - "display": "Eplerenone (Inspra)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/298869/drugs/351257", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/298869/drugs/351257", - "display": "Eplerenone (Inspra)\n50 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/9997/drugs/198222", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/9997/drugs/198222", - "display": "Spironolactone (Aldactone)\n100 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/9997/drugs/313096", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/9997/drugs/313096", - "display": "Spironolactone (Aldactone)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/9997/drugs/198223", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/9997/drugs/198223", - "display": "Spironolactone (Aldactone)\n50 mg Oral Tablet" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.sglt2i.exists", - "type": "group", - "text": "SGLT2 Inhibitors (SGLT2i)", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.sglt2i.exists-description", - "type": "display", - "text": "Do you take any of the following medications?\n\nCanagliflozin (Invokana)\nDapagliflozin (Farxiga)\nEmpagliflozin (Jardiance)\nErtugliflozin (Steglatro)\nBexagliflozin (Brenzavvy)\nSotagliflozin (Inpefa)", - "required": false - }, - { - "linkId": "medication.sglt2i.exists", - "type": "choice", - "text": "Do you take any medication from the above list?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "yes", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-registration", - "code": "yes", - "display": "Yes" - } - }, - { - "valueCoding": { - "id": "no", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-registration", - "code": "no", - "display": "No" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.sglt2i.page1", - "type": "group", - "text": "SGLT2 Inhibitors (SGLT2i)", - "required": false, - "enableWhen": [ - { - "question": "medication.sglt2i.exists", - "operator": "=", - "answerCoding": { - "id": "yes", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-registration", - "code": "yes", - "display": "Yes" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.sglt2i.description", - "type": "display", - "text": "Please enter which drug you are taking, how often you take it per day and how many pills/tablets you take per intake.\n\nDo not enter the total amount of pills/tablets you take per day.", - "required": false - }, - { - "linkId": "medication.sglt2i.frequency", - "type": "decimal", - "text": "Intake frequency (per day):", - "required": true - }, - { - "linkId": "medication.sglt2i.quantity", - "type": "decimal", - "text": "Pills/tablets per intake:", - "required": true - }, - { - "linkId": "medication.sglt2i.drug", - "type": "choice", - "text": "Which pill/tablet do you take?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "medications/2627044/drugs/2637859", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/2627044/drugs/2637859", - "display": "Bexagliflozin (Brenzavvy)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1373458/drugs/1373463", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1373458/drugs/1373463", - "display": "Canagliflozin (Invokana)\n100 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1373458/drugs/1373471", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1373458/drugs/1373471", - "display": "Canagliflozin (Invokana)\n300 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1488564/drugs/1488569", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1488564/drugs/1488569", - "display": "Dapagliflozin (Farxiga)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1488564/drugs/1488574", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1488564/drugs/1488574", - "display": "Dapagliflozin (Farxiga)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1545653/drugs/1545658", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1545653/drugs/1545658", - "display": "Empagliflozin (Jardiance)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1545653/drugs/1545666", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1545653/drugs/1545666", - "display": "Empagliflozin (Jardiance)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1992672/drugs/1992819", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1992672/drugs/1992819", - "display": "Ertugliflozin (Steglatro)\n15 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1992672/drugs/1992810", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1992672/drugs/1992810", - "display": "Ertugliflozin (Steglatro)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/2638675/drugs/2638683", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/2638675/drugs/2638683", - "display": "Sotagliflozin (Inpefa)\n200 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/2638675/drugs/2638691", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/2638675/drugs/2638691", - "display": "Sotagliflozin (Inpefa)\n400 mg Oral Tablet" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.diuretics.exists", - "type": "group", - "text": "Diuretics", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.diuretics.exists-description", - "type": "display", - "text": "Do you take any of the following medications?\n\nBumetanide (Bumex)\nEthacrynic Acid (Edecrin)\nFurosemide (Lasix)\nTorsemide (Soaanz)", - "required": false - }, - { - "linkId": "medication.diuretics.exists", - "type": "choice", - "text": "Do you take any medication from the above list?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "yes", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-registration", - "code": "yes", - "display": "Yes" - } - }, - { - "valueCoding": { - "id": "no", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-registration", - "code": "no", - "display": "No" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.diuretics.page1", - "type": "group", - "text": "Diuretics", - "required": false, - "enableWhen": [ - { - "question": "medication.diuretics.exists", - "operator": "=", - "answerCoding": { - "id": "yes", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-registration", - "code": "yes", - "display": "Yes" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.diuretics.description", - "type": "display", - "text": "Please enter which drug you are taking, how often you take it per day and how many pills/tablets you take per intake.\n\nDo not enter the total amount of pills/tablets you take per day.", - "required": false - }, - { - "linkId": "medication.diuretics.frequency", - "type": "decimal", - "text": "Intake frequency (per day):", - "required": true - }, - { - "linkId": "medication.diuretics.quantity", - "type": "decimal", - "text": "Pills/tablets per intake:", - "required": true - }, - { - "linkId": "medication.diuretics.drug", - "type": "choice", - "text": "Which pill/tablet do you take?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "medications/1808/drugs/197417", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1808/drugs/197417", - "display": "Bumetanide (Bumex)\n0.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1808/drugs/197418", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1808/drugs/197418", - "display": "Bumetanide (Bumex)\n1 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1808/drugs/197419", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1808/drugs/197419", - "display": "Bumetanide (Bumex)\n2 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/4109/drugs/1251903", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/4109/drugs/1251903", - "display": "Ethacrynic Acid (Edecrin)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/4109/drugs/1251907", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/4109/drugs/1251907", - "display": "Ethacrynic Acid (Edecrin)\n50 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/4603/drugs/310429", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/4603/drugs/310429", - "display": "Furosemide (Lasix)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/4603/drugs/313988", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/4603/drugs/313988", - "display": "Furosemide (Lasix)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/4603/drugs/197732", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/4603/drugs/197732", - "display": "Furosemide (Lasix)\n80 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/198369", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/198369", - "display": "Torsemide (Soaanz)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/198370", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/198370", - "display": "Torsemide (Soaanz)\n100 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/198371", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/198371", - "display": "Torsemide (Soaanz)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/2589881", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/2589881", - "display": "Torsemide (Soaanz)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/198372", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/198372", - "display": "Torsemide (Soaanz)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/2589885", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/2589885", - "display": "Torsemide (Soaanz)\n60 mg Oral Tablet" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "appointment.exists", - "type": "group", - "text": "Next appointment", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "appointment.exists-description", - "type": "display", - "text": "Next appointment", - "required": false - }, - { - "linkId": "appointment.exists", - "type": "boolean", - "text": "Do you already have a new appointment scheduled?", - "required": true - } - ] - }, - { - "linkId": "appointment.page1", - "type": "group", - "text": "Next appointment", - "required": false, - "enableWhen": [ - { - "question": "appointment.exists", - "operator": "=", - "answerBoolean": true - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "appointment.description", - "type": "display", - "text": "Upcoming appointment", - "required": false - }, - { - "linkId": "appointment.dateTime", - "type": "dateTime", - "text": "Date:", - "required": true - } - ] - } - ] - }, - "dataUpdate_en_US": { - "id": "grove-data-update", - "resourceType": "Questionnaire", - "meta": { - "profile": [ - "http://schmiedmayerlab.github.io/grove-ts/fhir/StructureDefinition/sdf-Questionnaire" - ], - "tag": [ - { - "system": "urn:ietf:bcp:47", - "code": "en-US", - "display": "English" - } - ] - }, - "title": "Update Survey", - "language": "en-US", - "status": "active", - "publisher": "Grove Project", - "subjectType": ["Patient"], - "useContext": [], - "contact": [ - { - "name": "http://schmiedmayerlab.github.io/grove-ts" - } - ], - "url": "http://schmiedmayerlab.github.io/grove-ts/fhir/questionnaire/grove-data-update", - "item": [ - { - "linkId": "lab.2160-0.exists", - "type": "group", - "text": "Creatinine", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.2160-0.exists-description", - "type": "display", - "text": "The creatinine level in your body helps understand how your kidneys handle the drugs you are taking.", - "required": false - }, - { - "linkId": "lab.2160-0.exists", - "type": "boolean", - "text": "Have you recently received a new creatinine value?", - "required": true - } - ] - }, - { - "linkId": "lab.2160-0.page1", - "type": "group", - "text": "Creatinine", - "required": false, - "enableWhen": [ - { - "question": "lab.2160-0.exists", - "operator": "=", - "answerBoolean": true - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.2160-0.description", - "type": "display", - "text": "The creatinine level in your body helps understand how your kidneys handle the drugs you are taking.", - "required": false - }, - { - "linkId": "lab.2160-0.value", - "type": "decimal", - "text": "Creatinine (mg/dL):", - "required": true - }, - { - "linkId": "lab.2160-0.dateTime", - "type": "dateTime", - "text": "Date:", - "required": true - } - ] - }, - { - "linkId": "lab.6298-4.exists", - "type": "group", - "text": "Potassium", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.6298-4.exists-description", - "type": "display", - "text": "The potassium level in your body helps understand how your liver handles the drugs you are taking.", - "required": false - }, - { - "linkId": "lab.6298-4.exists", - "type": "boolean", - "text": "Have you recently received a new potassium value?", - "required": true - } - ] - }, - { - "linkId": "lab.6298-4.page1", - "type": "group", - "text": "Potassium", - "required": false, - "enableWhen": [ - { - "question": "lab.6298-4.exists", - "operator": "=", - "answerBoolean": true - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.6298-4.description", - "type": "display", - "text": "The potassium level in your body helps understand how your liver handles the drugs you are taking.", - "required": false - }, - { - "linkId": "lab.6298-4.value", - "type": "decimal", - "text": "Potassium (mEq/L):", - "required": true - }, - { - "linkId": "lab.6298-4.dateTime", - "type": "dateTime", - "text": "Date:", - "required": true - } - ] - }, - { - "linkId": "lab.8340-2.exists", - "type": "group", - "text": "Dry Weight", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.8340-2.exists-description", - "type": "display", - "text": "The dry weight is useful to set a baseline to check that your weight does not increase unnoticed.", - "required": false - }, - { - "linkId": "lab.8340-2.exists", - "type": "boolean", - "text": "Have you recently received a new dry weight value?", - "required": true - } - ] - }, - { - "linkId": "lab.8340-2.page1", - "type": "group", - "text": "Dry Weight", - "required": false, - "enableWhen": [ - { - "question": "lab.8340-2.exists", - "operator": "=", - "answerBoolean": true - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.8340-2.description", - "type": "display", - "text": "The dry weight is useful to set a baseline to check that your weight does not increase unnoticed.", - "required": false - }, - { - "linkId": "lab.8340-2.value", - "type": "decimal", - "text": "Dry Weight (lbs):", - "required": true - }, - { - "linkId": "lab.8340-2.dateTime", - "type": "dateTime", - "text": "Date:", - "required": true - } - ] - }, - { - "linkId": "medication.betablockers.exists", - "type": "group", - "text": "Beta Blockers", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.betablockers.exists-description", - "type": "display", - "text": "Do you take any of the following medications?\n\nBisoprolol (Zebeta)\nCarvedilol (Coreg)\nMetoprolol Succinate (Toprol XL)\nCarvedilol Phosphate (Coreg CR)", - "required": false - }, - { - "linkId": "medication.betablockers.exists", - "type": "choice", - "text": "Do you take any medication from the above list?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - }, - { - "valueCoding": { - "id": "yes-unchanged", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-unchanged", - "display": "Yes, unchanged since last update" - } - }, - { - "valueCoding": { - "id": "no", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "no", - "display": "No" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.betablockers.page1", - "type": "group", - "text": "Beta Blockers", - "required": false, - "enableWhen": [ - { - "question": "medication.betablockers.exists", - "operator": "=", - "answerCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.betablockers.description", - "type": "display", - "text": "Please enter which drug you are taking, how often you take it per day and how many pills/tablets you take per intake.\n\nDo not enter the total amount of pills/tablets you take per day.", - "required": false - }, - { - "linkId": "medication.betablockers.frequency", - "type": "decimal", - "text": "Intake frequency (per day):", - "required": true - }, - { - "linkId": "medication.betablockers.quantity", - "type": "decimal", - "text": "Pills/tablets per intake:", - "required": true - }, - { - "linkId": "medication.betablockers.drug", - "type": "choice", - "text": "Which pill/tablet do you take?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "medications/19484/drugs/854901", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/19484/drugs/854901", - "display": "Bisoprolol (Zebeta)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/19484/drugs/854905", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/19484/drugs/854905", - "display": "Bisoprolol (Zebeta)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/20352/drugs/200032", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/20352/drugs/200032", - "display": "Carvedilol (Coreg)\n12.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/20352/drugs/200033", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/20352/drugs/200033", - "display": "Carvedilol (Coreg)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/20352/drugs/686924", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/20352/drugs/686924", - "display": "Carvedilol (Coreg)\n3.125 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/20352/drugs/200031", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/20352/drugs/200031", - "display": "Carvedilol (Coreg)\n6.25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/668310/drugs/860510", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/668310/drugs/860510", - "display": "Carvedilol Phosphate (Coreg CR)\n10 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/668310/drugs/860516", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/668310/drugs/860516", - "display": "Carvedilol Phosphate (Coreg CR)\n20 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/668310/drugs/860522", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/668310/drugs/860522", - "display": "Carvedilol Phosphate (Coreg CR)\n40 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/668310/drugs/860532", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/668310/drugs/860532", - "display": "Carvedilol Phosphate (Coreg CR)\n80 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/1999031", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/1999031", - "display": "Metoprolol Succinate (Toprol XL)\n100 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/866412", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/866412", - "display": "Metoprolol Succinate (Toprol XL)\n100 mg Extended Release Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/1999033", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/1999033", - "display": "Metoprolol Succinate (Toprol XL)\n200 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/866419", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/866419", - "display": "Metoprolol Succinate (Toprol XL)\n200 mg Extended Release Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/1999035", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/1999035", - "display": "Metoprolol Succinate (Toprol XL)\n25 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/866427", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/866427", - "display": "Metoprolol Succinate (Toprol XL)\n25 mg Extended Release Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/1999037", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/1999037", - "display": "Metoprolol Succinate (Toprol XL)\n50 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/866436", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/866436", - "display": "Metoprolol Succinate (Toprol XL)\n50 mg Extended Release Oral Tablet" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.rasi.exists", - "type": "group", - "text": "Renin-Angiotensin System Inhibitors (RASI)", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.rasi.exists-description", - "type": "display", - "text": "Do you take any of the following medications?\n\nCaptopril (Capoten)\nEnalapril (Vasotec)\nBenazepril (Lotensin)\nLisinopril (Zestril)\nMoexipril (Univasc)\nQuinapril (Accupril)\nRamipril (Altace)\nTrandolapril (Mavik)\nPerindopril (Aceon)\nValsartan (Diovan)\nTelmisartan (Micardis)\nEprosartan (Teveten)\nIrbesartan (Avapro)\nOlmesartan Medoxomil (Benicar)\nLosartan Potassium (Cozaar)\nCandesartan (Atacand)\nFosinopril Sodium (Monopril)\nAzilsartan Medoxomil (Edarbi)\nSacubitril / Valsartan (Entresto)", - "required": false - }, - { - "linkId": "medication.rasi.exists", - "type": "choice", - "text": "Do you take any medication from the above list?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - }, - { - "valueCoding": { - "id": "yes-unchanged", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-unchanged", - "display": "Yes, unchanged since last update" - } - }, - { - "valueCoding": { - "id": "no", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "no", - "display": "No" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.rasi.page1", - "type": "group", - "text": "Renin-Angiotensin System Inhibitors (RASI)", - "required": false, - "enableWhen": [ - { - "question": "medication.rasi.exists", - "operator": "=", - "answerCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.rasi.description", - "type": "display", - "text": "Please enter which drug you are taking, how often you take it per day and how many pills/tablets you take per intake.\n\nDo not enter the total amount of pills/tablets you take per day.", - "required": false - }, - { - "linkId": "medication.rasi.frequency", - "type": "decimal", - "text": "Intake frequency (per day):", - "required": true - }, - { - "linkId": "medication.rasi.quantity", - "type": "decimal", - "text": "Pills/tablets per intake:", - "required": true - }, - { - "linkId": "medication.rasi.drug", - "type": "choice", - "text": "Which pill/tablet do you take?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "medications/1091642/drugs/1091646", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1091642/drugs/1091646", - "display": "Azilsartan Medoxomil (Edarbi)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1091642/drugs/1091652", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1091642/drugs/1091652", - "display": "Azilsartan Medoxomil (Edarbi)\n80 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/18867/drugs/898687", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/18867/drugs/898687", - "display": "Benazepril (Lotensin)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/18867/drugs/898690", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/18867/drugs/898690", - "display": "Benazepril (Lotensin)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/18867/drugs/898719", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/18867/drugs/898719", - "display": "Benazepril (Lotensin)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/18867/drugs/898723", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/18867/drugs/898723", - "display": "Benazepril (Lotensin)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/214354/drugs/577776", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/214354/drugs/577776", - "display": "Candesartan (Atacand)\n16 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/214354/drugs/639537", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/214354/drugs/639537", - "display": "Candesartan (Atacand)\n32 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/214354/drugs/153822", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/214354/drugs/153822", - "display": "Candesartan (Atacand)\n4 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/214354/drugs/153823", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/214354/drugs/153823", - "display": "Candesartan (Atacand)\n8 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1998/drugs/308962", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1998/drugs/308962", - "display": "Captopril (Capoten)\n100 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1998/drugs/308963", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1998/drugs/308963", - "display": "Captopril (Capoten)\n12.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1998/drugs/317173", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1998/drugs/317173", - "display": "Captopril (Capoten)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1998/drugs/308964", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1998/drugs/308964", - "display": "Captopril (Capoten)\n50 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/3827/drugs/858817", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/3827/drugs/858817", - "display": "Enalapril (Vasotec)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/3827/drugs/858804", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/3827/drugs/858804", - "display": "Enalapril (Vasotec)\n2.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/3827/drugs/858810", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/3827/drugs/858810", - "display": "Enalapril (Vasotec)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/3827/drugs/858813", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/3827/drugs/858813", - "display": "Enalapril (Vasotec)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/83515/drugs/310139", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/83515/drugs/310139", - "display": "Eprosartan (Teveten)\n400 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/83515/drugs/310140", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/83515/drugs/310140", - "display": "Eprosartan (Teveten)\n600 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/227278/drugs/857169", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/227278/drugs/857169", - "display": "Fosinopril Sodium (Monopril)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/227278/drugs/857183", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/227278/drugs/857183", - "display": "Fosinopril Sodium (Monopril)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/227278/drugs/857187", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/227278/drugs/857187", - "display": "Fosinopril Sodium (Monopril)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/83818/drugs/200095", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/83818/drugs/200095", - "display": "Irbesartan (Avapro)\n150 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/83818/drugs/200096", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/83818/drugs/200096", - "display": "Irbesartan (Avapro)\n300 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/83818/drugs/200094", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/83818/drugs/200094", - "display": "Irbesartan (Avapro)\n75 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/314076", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/314076", - "display": "Lisinopril (Zestril)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/311353", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/311353", - "display": "Lisinopril (Zestril)\n2.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/314077", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/314077", - "display": "Lisinopril (Zestril)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/205326", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/205326", - "display": "Lisinopril (Zestril)\n30 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/197884", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/197884", - "display": "Lisinopril (Zestril)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/311354", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/311354", - "display": "Lisinopril (Zestril)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/203160/drugs/979480", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/203160/drugs/979480", - "display": "Losartan Potassium (Cozaar)\n100 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/203160/drugs/979485", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/203160/drugs/979485", - "display": "Losartan Potassium (Cozaar)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/203160/drugs/979492", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/203160/drugs/979492", - "display": "Losartan Potassium (Cozaar)\n50 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/30131/drugs/1299896", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/30131/drugs/1299896", - "display": "Moexipril (Univasc)\n15 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/30131/drugs/1299897", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/30131/drugs/1299897", - "display": "Moexipril (Univasc)\n7.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/118463/drugs/349401", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/118463/drugs/349401", - "display": "Olmesartan Medoxomil (Benicar)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/118463/drugs/349405", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/118463/drugs/349405", - "display": "Olmesartan Medoxomil (Benicar)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/118463/drugs/349373", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/118463/drugs/349373", - "display": "Olmesartan Medoxomil (Benicar)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/54552/drugs/854984", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/54552/drugs/854984", - "display": "Perindopril (Aceon)\n2 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/54552/drugs/854988", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/54552/drugs/854988", - "display": "Perindopril (Aceon)\n4 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/54552/drugs/854925", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/54552/drugs/854925", - "display": "Perindopril (Aceon)\n8 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35208/drugs/312748", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35208/drugs/312748", - "display": "Quinapril (Accupril)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35208/drugs/312749", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35208/drugs/312749", - "display": "Quinapril (Accupril)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35208/drugs/314203", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35208/drugs/314203", - "display": "Quinapril (Accupril)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35208/drugs/312750", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35208/drugs/312750", - "display": "Quinapril (Accupril)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/845488", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/845488", - "display": "Ramipril (Altace)\n1.25 mg Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/401965", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/401965", - "display": "Ramipril (Altace)\n1.25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/261962", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/261962", - "display": "Ramipril (Altace)\n10 mg Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/401968", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/401968", - "display": "Ramipril (Altace)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/198188", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/198188", - "display": "Ramipril (Altace)\n2.5 mg Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/251856", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/251856", - "display": "Ramipril (Altace)\n2.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/198189", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/198189", - "display": "Ramipril (Altace)\n5 mg Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/251857", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/251857", - "display": "Ramipril (Altace)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1656339/drugs/2679793", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1656339/drugs/2679793", - "display": "Sacubitril / Valsartan (Entresto)\n15/16 mg Oral Pellet" - } - }, - { - "valueCoding": { - "id": "medications/1656339/drugs/1656340", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1656339/drugs/1656340", - "display": "Sacubitril / Valsartan (Entresto)\n24/26 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1656339/drugs/1656349", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1656339/drugs/1656349", - "display": "Sacubitril / Valsartan (Entresto)\n49/51 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1656339/drugs/2679800", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1656339/drugs/2679800", - "display": "Sacubitril / Valsartan (Entresto)\n6/6 mg Oral Pellet" - } - }, - { - "valueCoding": { - "id": "medications/1656339/drugs/1656354", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1656339/drugs/1656354", - "display": "Sacubitril / Valsartan (Entresto)\n97/103 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/73494/drugs/282755", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/73494/drugs/282755", - "display": "Telmisartan (Micardis)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/73494/drugs/205304", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/73494/drugs/205304", - "display": "Telmisartan (Micardis)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/73494/drugs/205305", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/73494/drugs/205305", - "display": "Telmisartan (Micardis)\n80 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38454/drugs/199353", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38454/drugs/199353", - "display": "Trandolapril (Mavik)\n1 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38454/drugs/199351", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38454/drugs/199351", - "display": "Trandolapril (Mavik)\n2 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38454/drugs/199352", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38454/drugs/199352", - "display": "Trandolapril (Mavik)\n4 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/69749/drugs/349201", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/69749/drugs/349201", - "display": "Valsartan (Diovan)\n160 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/69749/drugs/349200", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/69749/drugs/349200", - "display": "Valsartan (Diovan)\n320 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/69749/drugs/349483", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/69749/drugs/349483", - "display": "Valsartan (Diovan)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/69749/drugs/349199", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/69749/drugs/349199", - "display": "Valsartan (Diovan)\n80 mg Oral Tablet" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.mra.exists", - "type": "group", - "text": "Mineralocorticoid Receptor Antagonists (MRA)", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.mra.exists-description", - "type": "display", - "text": "Do you take any of the following medications?\n\nSpironolactone (Aldactone)\nEplerenone (Inspra)", - "required": false - }, - { - "linkId": "medication.mra.exists", - "type": "choice", - "text": "Do you take any medication from the above list?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - }, - { - "valueCoding": { - "id": "yes-unchanged", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-unchanged", - "display": "Yes, unchanged since last update" - } - }, - { - "valueCoding": { - "id": "no", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "no", - "display": "No" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.mra.page1", - "type": "group", - "text": "Mineralocorticoid Receptor Antagonists (MRA)", - "required": false, - "enableWhen": [ - { - "question": "medication.mra.exists", - "operator": "=", - "answerCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.mra.description", - "type": "display", - "text": "Please enter which drug you are taking, how often you take it per day and how many pills/tablets you take per intake.\n\nDo not enter the total amount of pills/tablets you take per day.", - "required": false - }, - { - "linkId": "medication.mra.frequency", - "type": "decimal", - "text": "Intake frequency (per day):", - "required": true - }, - { - "linkId": "medication.mra.quantity", - "type": "decimal", - "text": "Pills/tablets per intake:", - "required": true - }, - { - "linkId": "medication.mra.drug", - "type": "choice", - "text": "Which pill/tablet do you take?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "medications/298869/drugs/351256", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/298869/drugs/351256", - "display": "Eplerenone (Inspra)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/298869/drugs/351257", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/298869/drugs/351257", - "display": "Eplerenone (Inspra)\n50 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/9997/drugs/198222", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/9997/drugs/198222", - "display": "Spironolactone (Aldactone)\n100 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/9997/drugs/313096", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/9997/drugs/313096", - "display": "Spironolactone (Aldactone)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/9997/drugs/198223", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/9997/drugs/198223", - "display": "Spironolactone (Aldactone)\n50 mg Oral Tablet" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.sglt2i.exists", - "type": "group", - "text": "SGLT2 Inhibitors (SGLT2i)", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.sglt2i.exists-description", - "type": "display", - "text": "Do you take any of the following medications?\n\nCanagliflozin (Invokana)\nDapagliflozin (Farxiga)\nEmpagliflozin (Jardiance)\nErtugliflozin (Steglatro)\nBexagliflozin (Brenzavvy)\nSotagliflozin (Inpefa)", - "required": false - }, - { - "linkId": "medication.sglt2i.exists", - "type": "choice", - "text": "Do you take any medication from the above list?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - }, - { - "valueCoding": { - "id": "yes-unchanged", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-unchanged", - "display": "Yes, unchanged since last update" - } - }, - { - "valueCoding": { - "id": "no", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "no", - "display": "No" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.sglt2i.page1", - "type": "group", - "text": "SGLT2 Inhibitors (SGLT2i)", - "required": false, - "enableWhen": [ - { - "question": "medication.sglt2i.exists", - "operator": "=", - "answerCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.sglt2i.description", - "type": "display", - "text": "Please enter which drug you are taking, how often you take it per day and how many pills/tablets you take per intake.\n\nDo not enter the total amount of pills/tablets you take per day.", - "required": false - }, - { - "linkId": "medication.sglt2i.frequency", - "type": "decimal", - "text": "Intake frequency (per day):", - "required": true - }, - { - "linkId": "medication.sglt2i.quantity", - "type": "decimal", - "text": "Pills/tablets per intake:", - "required": true - }, - { - "linkId": "medication.sglt2i.drug", - "type": "choice", - "text": "Which pill/tablet do you take?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "medications/2627044/drugs/2637859", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/2627044/drugs/2637859", - "display": "Bexagliflozin (Brenzavvy)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1373458/drugs/1373463", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1373458/drugs/1373463", - "display": "Canagliflozin (Invokana)\n100 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1373458/drugs/1373471", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1373458/drugs/1373471", - "display": "Canagliflozin (Invokana)\n300 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1488564/drugs/1488569", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1488564/drugs/1488569", - "display": "Dapagliflozin (Farxiga)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1488564/drugs/1488574", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1488564/drugs/1488574", - "display": "Dapagliflozin (Farxiga)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1545653/drugs/1545658", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1545653/drugs/1545658", - "display": "Empagliflozin (Jardiance)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1545653/drugs/1545666", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1545653/drugs/1545666", - "display": "Empagliflozin (Jardiance)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1992672/drugs/1992819", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1992672/drugs/1992819", - "display": "Ertugliflozin (Steglatro)\n15 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1992672/drugs/1992810", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1992672/drugs/1992810", - "display": "Ertugliflozin (Steglatro)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/2638675/drugs/2638683", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/2638675/drugs/2638683", - "display": "Sotagliflozin (Inpefa)\n200 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/2638675/drugs/2638691", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/2638675/drugs/2638691", - "display": "Sotagliflozin (Inpefa)\n400 mg Oral Tablet" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.diuretics.exists", - "type": "group", - "text": "Diuretics", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.diuretics.exists-description", - "type": "display", - "text": "Do you take any of the following medications?\n\nBumetanide (Bumex)\nEthacrynic Acid (Edecrin)\nFurosemide (Lasix)\nTorsemide (Soaanz)", - "required": false - }, - { - "linkId": "medication.diuretics.exists", - "type": "choice", - "text": "Do you take any medication from the above list?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - }, - { - "valueCoding": { - "id": "yes-unchanged", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-unchanged", - "display": "Yes, unchanged since last update" - } - }, - { - "valueCoding": { - "id": "no", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "no", - "display": "No" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.diuretics.page1", - "type": "group", - "text": "Diuretics", - "required": false, - "enableWhen": [ - { - "question": "medication.diuretics.exists", - "operator": "=", - "answerCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.diuretics.description", - "type": "display", - "text": "Please enter which drug you are taking, how often you take it per day and how many pills/tablets you take per intake.\n\nDo not enter the total amount of pills/tablets you take per day.", - "required": false - }, - { - "linkId": "medication.diuretics.frequency", - "type": "decimal", - "text": "Intake frequency (per day):", - "required": true - }, - { - "linkId": "medication.diuretics.quantity", - "type": "decimal", - "text": "Pills/tablets per intake:", - "required": true - }, - { - "linkId": "medication.diuretics.drug", - "type": "choice", - "text": "Which pill/tablet do you take?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "medications/1808/drugs/197417", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1808/drugs/197417", - "display": "Bumetanide (Bumex)\n0.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1808/drugs/197418", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1808/drugs/197418", - "display": "Bumetanide (Bumex)\n1 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1808/drugs/197419", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1808/drugs/197419", - "display": "Bumetanide (Bumex)\n2 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/4109/drugs/1251903", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/4109/drugs/1251903", - "display": "Ethacrynic Acid (Edecrin)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/4109/drugs/1251907", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/4109/drugs/1251907", - "display": "Ethacrynic Acid (Edecrin)\n50 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/4603/drugs/310429", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/4603/drugs/310429", - "display": "Furosemide (Lasix)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/4603/drugs/313988", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/4603/drugs/313988", - "display": "Furosemide (Lasix)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/4603/drugs/197732", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/4603/drugs/197732", - "display": "Furosemide (Lasix)\n80 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/198369", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/198369", - "display": "Torsemide (Soaanz)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/198370", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/198370", - "display": "Torsemide (Soaanz)\n100 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/198371", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/198371", - "display": "Torsemide (Soaanz)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/2589881", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/2589881", - "display": "Torsemide (Soaanz)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/198372", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/198372", - "display": "Torsemide (Soaanz)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/2589885", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/2589885", - "display": "Torsemide (Soaanz)\n60 mg Oral Tablet" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "appointment.exists", - "type": "group", - "text": "Next appointment", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "appointment.exists-description", - "type": "display", - "text": "Next appointment", - "required": false - }, - { - "linkId": "appointment.exists", - "type": "boolean", - "text": "Do you already have a new appointment scheduled?", - "required": true - } - ] - }, - { - "linkId": "appointment.page1", - "type": "group", - "text": "Next appointment", - "required": false, - "enableWhen": [ - { - "question": "appointment.exists", - "operator": "=", - "answerBoolean": true - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "appointment.description", - "type": "display", - "text": "Upcoming appointment", - "required": false - }, - { - "linkId": "appointment.dateTime", - "type": "dateTime", - "text": "Date:", - "required": true - } - ] - } - ] - }, - "postAppointment_en_US": { - "id": "grove-post-appointment", - "resourceType": "Questionnaire", - "meta": { - "profile": [ - "http://schmiedmayerlab.github.io/grove-ts/fhir/StructureDefinition/sdf-Questionnaire" - ], - "tag": [ - { - "system": "urn:ietf:bcp:47", - "code": "en-US", - "display": "English" - } - ] - }, - "title": "Post-Appointment Survey", - "language": "en-US", - "status": "active", - "publisher": "Grove Project", - "subjectType": ["Patient"], - "useContext": [], - "contact": [ - { - "name": "http://schmiedmayerlab.github.io/grove-ts" - } - ], - "url": "http://schmiedmayerlab.github.io/grove-ts/fhir/questionnaire/grove-post-appointment", - "item": [ - { - "linkId": "lab.2160-0.exists", - "type": "group", - "text": "Creatinine", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.2160-0.exists-description", - "type": "display", - "text": "The creatinine level in your body helps understand how your kidneys handle the drugs you are taking.", - "required": false - }, - { - "linkId": "lab.2160-0.exists", - "type": "boolean", - "text": "Have you recently received a new creatinine value?", - "required": true - } - ] - }, - { - "linkId": "lab.2160-0.page1", - "type": "group", - "text": "Creatinine", - "required": false, - "enableWhen": [ - { - "question": "lab.2160-0.exists", - "operator": "=", - "answerBoolean": true - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.2160-0.description", - "type": "display", - "text": "The creatinine level in your body helps understand how your kidneys handle the drugs you are taking.", - "required": false - }, - { - "linkId": "lab.2160-0.value", - "type": "decimal", - "text": "Creatinine (mg/dL):", - "required": true - }, - { - "linkId": "lab.2160-0.dateTime", - "type": "dateTime", - "text": "Date:", - "required": true - } - ] - }, - { - "linkId": "lab.6298-4.exists", - "type": "group", - "text": "Potassium", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.6298-4.exists-description", - "type": "display", - "text": "The potassium level in your body helps understand how your liver handles the drugs you are taking.", - "required": false - }, - { - "linkId": "lab.6298-4.exists", - "type": "boolean", - "text": "Have you recently received a new potassium value?", - "required": true - } - ] - }, - { - "linkId": "lab.6298-4.page1", - "type": "group", - "text": "Potassium", - "required": false, - "enableWhen": [ - { - "question": "lab.6298-4.exists", - "operator": "=", - "answerBoolean": true - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.6298-4.description", - "type": "display", - "text": "The potassium level in your body helps understand how your liver handles the drugs you are taking.", - "required": false - }, - { - "linkId": "lab.6298-4.value", - "type": "decimal", - "text": "Potassium (mEq/L):", - "required": true - }, - { - "linkId": "lab.6298-4.dateTime", - "type": "dateTime", - "text": "Date:", - "required": true - } - ] - }, - { - "linkId": "lab.8340-2.exists", - "type": "group", - "text": "Dry Weight", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.8340-2.exists-description", - "type": "display", - "text": "The dry weight is useful to set a baseline to check that your weight does not increase unnoticed.", - "required": false - }, - { - "linkId": "lab.8340-2.exists", - "type": "boolean", - "text": "Have you recently received a new dry weight value?", - "required": true - } - ] - }, - { - "linkId": "lab.8340-2.page1", - "type": "group", - "text": "Dry Weight", - "required": false, - "enableWhen": [ - { - "question": "lab.8340-2.exists", - "operator": "=", - "answerBoolean": true - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "lab.8340-2.description", - "type": "display", - "text": "The dry weight is useful to set a baseline to check that your weight does not increase unnoticed.", - "required": false - }, - { - "linkId": "lab.8340-2.value", - "type": "decimal", - "text": "Dry Weight (lbs):", - "required": true - }, - { - "linkId": "lab.8340-2.dateTime", - "type": "dateTime", - "text": "Date:", - "required": true - } - ] - }, - { - "linkId": "medication.betablockers.exists", - "type": "group", - "text": "Beta Blockers", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.betablockers.exists-description", - "type": "display", - "text": "Do you take any of the following medications?\n\nBisoprolol (Zebeta)\nCarvedilol (Coreg)\nMetoprolol Succinate (Toprol XL)\nCarvedilol Phosphate (Coreg CR)", - "required": false - }, - { - "linkId": "medication.betablockers.exists", - "type": "choice", - "text": "Do you take any medication from the above list?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - }, - { - "valueCoding": { - "id": "yes-unchanged", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-unchanged", - "display": "Yes, unchanged since last update" - } - }, - { - "valueCoding": { - "id": "no", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "no", - "display": "No" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.betablockers.page1", - "type": "group", - "text": "Beta Blockers", - "required": false, - "enableWhen": [ - { - "question": "medication.betablockers.exists", - "operator": "=", - "answerCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.betablockers.description", - "type": "display", - "text": "Please enter which drug you are taking, how often you take it per day and how many pills/tablets you take per intake.\n\nDo not enter the total amount of pills/tablets you take per day.", - "required": false - }, - { - "linkId": "medication.betablockers.frequency", - "type": "decimal", - "text": "Intake frequency (per day):", - "required": true - }, - { - "linkId": "medication.betablockers.quantity", - "type": "decimal", - "text": "Pills/tablets per intake:", - "required": true - }, - { - "linkId": "medication.betablockers.drug", - "type": "choice", - "text": "Which pill/tablet do you take?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "medications/19484/drugs/854901", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/19484/drugs/854901", - "display": "Bisoprolol (Zebeta)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/19484/drugs/854905", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/19484/drugs/854905", - "display": "Bisoprolol (Zebeta)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/20352/drugs/200032", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/20352/drugs/200032", - "display": "Carvedilol (Coreg)\n12.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/20352/drugs/200033", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/20352/drugs/200033", - "display": "Carvedilol (Coreg)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/20352/drugs/686924", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/20352/drugs/686924", - "display": "Carvedilol (Coreg)\n3.125 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/20352/drugs/200031", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/20352/drugs/200031", - "display": "Carvedilol (Coreg)\n6.25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/668310/drugs/860510", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/668310/drugs/860510", - "display": "Carvedilol Phosphate (Coreg CR)\n10 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/668310/drugs/860516", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/668310/drugs/860516", - "display": "Carvedilol Phosphate (Coreg CR)\n20 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/668310/drugs/860522", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/668310/drugs/860522", - "display": "Carvedilol Phosphate (Coreg CR)\n40 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/668310/drugs/860532", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/668310/drugs/860532", - "display": "Carvedilol Phosphate (Coreg CR)\n80 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/1999031", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/1999031", - "display": "Metoprolol Succinate (Toprol XL)\n100 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/866412", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/866412", - "display": "Metoprolol Succinate (Toprol XL)\n100 mg Extended Release Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/1999033", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/1999033", - "display": "Metoprolol Succinate (Toprol XL)\n200 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/866419", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/866419", - "display": "Metoprolol Succinate (Toprol XL)\n200 mg Extended Release Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/1999035", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/1999035", - "display": "Metoprolol Succinate (Toprol XL)\n25 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/866427", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/866427", - "display": "Metoprolol Succinate (Toprol XL)\n25 mg Extended Release Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/1999037", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/1999037", - "display": "Metoprolol Succinate (Toprol XL)\n50 mg Extended Release Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/221124/drugs/866436", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/221124/drugs/866436", - "display": "Metoprolol Succinate (Toprol XL)\n50 mg Extended Release Oral Tablet" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.rasi.exists", - "type": "group", - "text": "Renin-Angiotensin System Inhibitors (RASI)", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.rasi.exists-description", - "type": "display", - "text": "Do you take any of the following medications?\n\nCaptopril (Capoten)\nEnalapril (Vasotec)\nBenazepril (Lotensin)\nLisinopril (Zestril)\nMoexipril (Univasc)\nQuinapril (Accupril)\nRamipril (Altace)\nTrandolapril (Mavik)\nPerindopril (Aceon)\nValsartan (Diovan)\nTelmisartan (Micardis)\nEprosartan (Teveten)\nIrbesartan (Avapro)\nOlmesartan Medoxomil (Benicar)\nLosartan Potassium (Cozaar)\nCandesartan (Atacand)\nFosinopril Sodium (Monopril)\nAzilsartan Medoxomil (Edarbi)\nSacubitril / Valsartan (Entresto)", - "required": false - }, - { - "linkId": "medication.rasi.exists", - "type": "choice", - "text": "Do you take any medication from the above list?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - }, - { - "valueCoding": { - "id": "yes-unchanged", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-unchanged", - "display": "Yes, unchanged since last update" - } - }, - { - "valueCoding": { - "id": "no", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "no", - "display": "No" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.rasi.page1", - "type": "group", - "text": "Renin-Angiotensin System Inhibitors (RASI)", - "required": false, - "enableWhen": [ - { - "question": "medication.rasi.exists", - "operator": "=", - "answerCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.rasi.description", - "type": "display", - "text": "Please enter which drug you are taking, how often you take it per day and how many pills/tablets you take per intake.\n\nDo not enter the total amount of pills/tablets you take per day.", - "required": false - }, - { - "linkId": "medication.rasi.frequency", - "type": "decimal", - "text": "Intake frequency (per day):", - "required": true - }, - { - "linkId": "medication.rasi.quantity", - "type": "decimal", - "text": "Pills/tablets per intake:", - "required": true - }, - { - "linkId": "medication.rasi.drug", - "type": "choice", - "text": "Which pill/tablet do you take?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "medications/1091642/drugs/1091646", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1091642/drugs/1091646", - "display": "Azilsartan Medoxomil (Edarbi)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1091642/drugs/1091652", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1091642/drugs/1091652", - "display": "Azilsartan Medoxomil (Edarbi)\n80 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/18867/drugs/898687", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/18867/drugs/898687", - "display": "Benazepril (Lotensin)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/18867/drugs/898690", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/18867/drugs/898690", - "display": "Benazepril (Lotensin)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/18867/drugs/898719", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/18867/drugs/898719", - "display": "Benazepril (Lotensin)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/18867/drugs/898723", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/18867/drugs/898723", - "display": "Benazepril (Lotensin)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/214354/drugs/577776", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/214354/drugs/577776", - "display": "Candesartan (Atacand)\n16 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/214354/drugs/639537", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/214354/drugs/639537", - "display": "Candesartan (Atacand)\n32 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/214354/drugs/153822", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/214354/drugs/153822", - "display": "Candesartan (Atacand)\n4 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/214354/drugs/153823", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/214354/drugs/153823", - "display": "Candesartan (Atacand)\n8 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1998/drugs/308962", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1998/drugs/308962", - "display": "Captopril (Capoten)\n100 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1998/drugs/308963", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1998/drugs/308963", - "display": "Captopril (Capoten)\n12.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1998/drugs/317173", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1998/drugs/317173", - "display": "Captopril (Capoten)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1998/drugs/308964", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1998/drugs/308964", - "display": "Captopril (Capoten)\n50 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/3827/drugs/858817", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/3827/drugs/858817", - "display": "Enalapril (Vasotec)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/3827/drugs/858804", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/3827/drugs/858804", - "display": "Enalapril (Vasotec)\n2.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/3827/drugs/858810", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/3827/drugs/858810", - "display": "Enalapril (Vasotec)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/3827/drugs/858813", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/3827/drugs/858813", - "display": "Enalapril (Vasotec)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/83515/drugs/310139", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/83515/drugs/310139", - "display": "Eprosartan (Teveten)\n400 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/83515/drugs/310140", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/83515/drugs/310140", - "display": "Eprosartan (Teveten)\n600 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/227278/drugs/857169", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/227278/drugs/857169", - "display": "Fosinopril Sodium (Monopril)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/227278/drugs/857183", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/227278/drugs/857183", - "display": "Fosinopril Sodium (Monopril)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/227278/drugs/857187", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/227278/drugs/857187", - "display": "Fosinopril Sodium (Monopril)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/83818/drugs/200095", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/83818/drugs/200095", - "display": "Irbesartan (Avapro)\n150 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/83818/drugs/200096", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/83818/drugs/200096", - "display": "Irbesartan (Avapro)\n300 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/83818/drugs/200094", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/83818/drugs/200094", - "display": "Irbesartan (Avapro)\n75 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/314076", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/314076", - "display": "Lisinopril (Zestril)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/311353", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/311353", - "display": "Lisinopril (Zestril)\n2.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/314077", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/314077", - "display": "Lisinopril (Zestril)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/205326", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/205326", - "display": "Lisinopril (Zestril)\n30 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/197884", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/197884", - "display": "Lisinopril (Zestril)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/29046/drugs/311354", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/29046/drugs/311354", - "display": "Lisinopril (Zestril)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/203160/drugs/979480", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/203160/drugs/979480", - "display": "Losartan Potassium (Cozaar)\n100 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/203160/drugs/979485", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/203160/drugs/979485", - "display": "Losartan Potassium (Cozaar)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/203160/drugs/979492", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/203160/drugs/979492", - "display": "Losartan Potassium (Cozaar)\n50 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/30131/drugs/1299896", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/30131/drugs/1299896", - "display": "Moexipril (Univasc)\n15 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/30131/drugs/1299897", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/30131/drugs/1299897", - "display": "Moexipril (Univasc)\n7.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/118463/drugs/349401", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/118463/drugs/349401", - "display": "Olmesartan Medoxomil (Benicar)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/118463/drugs/349405", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/118463/drugs/349405", - "display": "Olmesartan Medoxomil (Benicar)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/118463/drugs/349373", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/118463/drugs/349373", - "display": "Olmesartan Medoxomil (Benicar)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/54552/drugs/854984", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/54552/drugs/854984", - "display": "Perindopril (Aceon)\n2 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/54552/drugs/854988", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/54552/drugs/854988", - "display": "Perindopril (Aceon)\n4 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/54552/drugs/854925", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/54552/drugs/854925", - "display": "Perindopril (Aceon)\n8 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35208/drugs/312748", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35208/drugs/312748", - "display": "Quinapril (Accupril)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35208/drugs/312749", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35208/drugs/312749", - "display": "Quinapril (Accupril)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35208/drugs/314203", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35208/drugs/314203", - "display": "Quinapril (Accupril)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35208/drugs/312750", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35208/drugs/312750", - "display": "Quinapril (Accupril)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/845488", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/845488", - "display": "Ramipril (Altace)\n1.25 mg Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/401965", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/401965", - "display": "Ramipril (Altace)\n1.25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/261962", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/261962", - "display": "Ramipril (Altace)\n10 mg Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/401968", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/401968", - "display": "Ramipril (Altace)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/198188", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/198188", - "display": "Ramipril (Altace)\n2.5 mg Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/251856", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/251856", - "display": "Ramipril (Altace)\n2.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/198189", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/198189", - "display": "Ramipril (Altace)\n5 mg Oral Capsule" - } - }, - { - "valueCoding": { - "id": "medications/35296/drugs/251857", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/35296/drugs/251857", - "display": "Ramipril (Altace)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1656339/drugs/2679793", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1656339/drugs/2679793", - "display": "Sacubitril / Valsartan (Entresto)\n15/16 mg Oral Pellet" - } - }, - { - "valueCoding": { - "id": "medications/1656339/drugs/1656340", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1656339/drugs/1656340", - "display": "Sacubitril / Valsartan (Entresto)\n24/26 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1656339/drugs/1656349", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1656339/drugs/1656349", - "display": "Sacubitril / Valsartan (Entresto)\n49/51 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1656339/drugs/2679800", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1656339/drugs/2679800", - "display": "Sacubitril / Valsartan (Entresto)\n6/6 mg Oral Pellet" - } - }, - { - "valueCoding": { - "id": "medications/1656339/drugs/1656354", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1656339/drugs/1656354", - "display": "Sacubitril / Valsartan (Entresto)\n97/103 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/73494/drugs/282755", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/73494/drugs/282755", - "display": "Telmisartan (Micardis)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/73494/drugs/205304", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/73494/drugs/205304", - "display": "Telmisartan (Micardis)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/73494/drugs/205305", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/73494/drugs/205305", - "display": "Telmisartan (Micardis)\n80 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38454/drugs/199353", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38454/drugs/199353", - "display": "Trandolapril (Mavik)\n1 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38454/drugs/199351", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38454/drugs/199351", - "display": "Trandolapril (Mavik)\n2 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38454/drugs/199352", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38454/drugs/199352", - "display": "Trandolapril (Mavik)\n4 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/69749/drugs/349201", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/69749/drugs/349201", - "display": "Valsartan (Diovan)\n160 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/69749/drugs/349200", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/69749/drugs/349200", - "display": "Valsartan (Diovan)\n320 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/69749/drugs/349483", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/69749/drugs/349483", - "display": "Valsartan (Diovan)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/69749/drugs/349199", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/69749/drugs/349199", - "display": "Valsartan (Diovan)\n80 mg Oral Tablet" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.mra.exists", - "type": "group", - "text": "Mineralocorticoid Receptor Antagonists (MRA)", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.mra.exists-description", - "type": "display", - "text": "Do you take any of the following medications?\n\nSpironolactone (Aldactone)\nEplerenone (Inspra)", - "required": false - }, - { - "linkId": "medication.mra.exists", - "type": "choice", - "text": "Do you take any medication from the above list?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - }, - { - "valueCoding": { - "id": "yes-unchanged", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-unchanged", - "display": "Yes, unchanged since last update" - } - }, - { - "valueCoding": { - "id": "no", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "no", - "display": "No" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.mra.page1", - "type": "group", - "text": "Mineralocorticoid Receptor Antagonists (MRA)", - "required": false, - "enableWhen": [ - { - "question": "medication.mra.exists", - "operator": "=", - "answerCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.mra.description", - "type": "display", - "text": "Please enter which drug you are taking, how often you take it per day and how many pills/tablets you take per intake.\n\nDo not enter the total amount of pills/tablets you take per day.", - "required": false - }, - { - "linkId": "medication.mra.frequency", - "type": "decimal", - "text": "Intake frequency (per day):", - "required": true - }, - { - "linkId": "medication.mra.quantity", - "type": "decimal", - "text": "Pills/tablets per intake:", - "required": true - }, - { - "linkId": "medication.mra.drug", - "type": "choice", - "text": "Which pill/tablet do you take?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "medications/298869/drugs/351256", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/298869/drugs/351256", - "display": "Eplerenone (Inspra)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/298869/drugs/351257", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/298869/drugs/351257", - "display": "Eplerenone (Inspra)\n50 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/9997/drugs/198222", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/9997/drugs/198222", - "display": "Spironolactone (Aldactone)\n100 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/9997/drugs/313096", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/9997/drugs/313096", - "display": "Spironolactone (Aldactone)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/9997/drugs/198223", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/9997/drugs/198223", - "display": "Spironolactone (Aldactone)\n50 mg Oral Tablet" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.sglt2i.exists", - "type": "group", - "text": "SGLT2 Inhibitors (SGLT2i)", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.sglt2i.exists-description", - "type": "display", - "text": "Do you take any of the following medications?\n\nCanagliflozin (Invokana)\nDapagliflozin (Farxiga)\nEmpagliflozin (Jardiance)\nErtugliflozin (Steglatro)\nBexagliflozin (Brenzavvy)\nSotagliflozin (Inpefa)", - "required": false - }, - { - "linkId": "medication.sglt2i.exists", - "type": "choice", - "text": "Do you take any medication from the above list?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - }, - { - "valueCoding": { - "id": "yes-unchanged", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-unchanged", - "display": "Yes, unchanged since last update" - } - }, - { - "valueCoding": { - "id": "no", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "no", - "display": "No" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.sglt2i.page1", - "type": "group", - "text": "SGLT2 Inhibitors (SGLT2i)", - "required": false, - "enableWhen": [ - { - "question": "medication.sglt2i.exists", - "operator": "=", - "answerCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.sglt2i.description", - "type": "display", - "text": "Please enter which drug you are taking, how often you take it per day and how many pills/tablets you take per intake.\n\nDo not enter the total amount of pills/tablets you take per day.", - "required": false - }, - { - "linkId": "medication.sglt2i.frequency", - "type": "decimal", - "text": "Intake frequency (per day):", - "required": true - }, - { - "linkId": "medication.sglt2i.quantity", - "type": "decimal", - "text": "Pills/tablets per intake:", - "required": true - }, - { - "linkId": "medication.sglt2i.drug", - "type": "choice", - "text": "Which pill/tablet do you take?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "medications/2627044/drugs/2637859", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/2627044/drugs/2637859", - "display": "Bexagliflozin (Brenzavvy)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1373458/drugs/1373463", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1373458/drugs/1373463", - "display": "Canagliflozin (Invokana)\n100 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1373458/drugs/1373471", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1373458/drugs/1373471", - "display": "Canagliflozin (Invokana)\n300 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1488564/drugs/1488569", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1488564/drugs/1488569", - "display": "Dapagliflozin (Farxiga)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1488564/drugs/1488574", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1488564/drugs/1488574", - "display": "Dapagliflozin (Farxiga)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1545653/drugs/1545658", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1545653/drugs/1545658", - "display": "Empagliflozin (Jardiance)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1545653/drugs/1545666", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1545653/drugs/1545666", - "display": "Empagliflozin (Jardiance)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1992672/drugs/1992819", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1992672/drugs/1992819", - "display": "Ertugliflozin (Steglatro)\n15 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1992672/drugs/1992810", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1992672/drugs/1992810", - "display": "Ertugliflozin (Steglatro)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/2638675/drugs/2638683", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/2638675/drugs/2638683", - "display": "Sotagliflozin (Inpefa)\n200 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/2638675/drugs/2638691", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/2638675/drugs/2638691", - "display": "Sotagliflozin (Inpefa)\n400 mg Oral Tablet" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.diuretics.exists", - "type": "group", - "text": "Diuretics", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.diuretics.exists-description", - "type": "display", - "text": "Do you take any of the following medications?\n\nBumetanide (Bumex)\nEthacrynic Acid (Edecrin)\nFurosemide (Lasix)\nTorsemide (Soaanz)", - "required": false - }, - { - "linkId": "medication.diuretics.exists", - "type": "choice", - "text": "Do you take any medication from the above list?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - }, - { - "valueCoding": { - "id": "yes-unchanged", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-unchanged", - "display": "Yes, unchanged since last update" - } - }, - { - "valueCoding": { - "id": "no", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "no", - "display": "No" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "medication.diuretics.page1", - "type": "group", - "text": "Diuretics", - "required": false, - "enableWhen": [ - { - "question": "medication.diuretics.exists", - "operator": "=", - "answerCoding": { - "id": "yes-changed", - "system": "http://example.com/grove/fhir/ValueSet/medication-exists-update", - "code": "yes-changed", - "display": "Yes, changed since last update" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "medication.diuretics.description", - "type": "display", - "text": "Please enter which drug you are taking, how often you take it per day and how many pills/tablets you take per intake.\n\nDo not enter the total amount of pills/tablets you take per day.", - "required": false - }, - { - "linkId": "medication.diuretics.frequency", - "type": "decimal", - "text": "Intake frequency (per day):", - "required": true - }, - { - "linkId": "medication.diuretics.quantity", - "type": "decimal", - "text": "Pills/tablets per intake:", - "required": true - }, - { - "linkId": "medication.diuretics.drug", - "type": "choice", - "text": "Which pill/tablet do you take?", - "required": true, - "answerOption": [ - { - "valueCoding": { - "id": "medications/1808/drugs/197417", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1808/drugs/197417", - "display": "Bumetanide (Bumex)\n0.5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1808/drugs/197418", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1808/drugs/197418", - "display": "Bumetanide (Bumex)\n1 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/1808/drugs/197419", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/1808/drugs/197419", - "display": "Bumetanide (Bumex)\n2 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/4109/drugs/1251903", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/4109/drugs/1251903", - "display": "Ethacrynic Acid (Edecrin)\n25 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/4109/drugs/1251907", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/4109/drugs/1251907", - "display": "Ethacrynic Acid (Edecrin)\n50 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/4603/drugs/310429", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/4603/drugs/310429", - "display": "Furosemide (Lasix)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/4603/drugs/313988", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/4603/drugs/313988", - "display": "Furosemide (Lasix)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/4603/drugs/197732", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/4603/drugs/197732", - "display": "Furosemide (Lasix)\n80 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/198369", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/198369", - "display": "Torsemide (Soaanz)\n10 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/198370", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/198370", - "display": "Torsemide (Soaanz)\n100 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/198371", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/198371", - "display": "Torsemide (Soaanz)\n20 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/2589881", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/2589881", - "display": "Torsemide (Soaanz)\n40 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/198372", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/198372", - "display": "Torsemide (Soaanz)\n5 mg Oral Tablet" - } - }, - { - "valueCoding": { - "id": "medications/38413/drugs/2589885", - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "medications/38413/drugs/2589885", - "display": "Torsemide (Soaanz)\n60 mg Oral Tablet" - } - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "radio-button", - "display": "Radio Button" - } - ] - } - } - ] - } - ] - }, - { - "linkId": "appointment.exists", - "type": "group", - "text": "Next appointment", - "required": false, - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "appointment.exists-description", - "type": "display", - "text": "Next appointment", - "required": false - }, - { - "linkId": "appointment.exists", - "type": "boolean", - "text": "Do you already have a new appointment scheduled?", - "required": true - } - ] - }, - { - "linkId": "appointment.page1", - "type": "group", - "text": "Next appointment", - "required": false, - "enableWhen": [ - { - "question": "appointment.exists", - "operator": "=", - "answerBoolean": true - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://hl7.org/fhir/questionnaire-item-control", - "code": "page", - "display": "Page" - } - ], - "text": "Page" - } - } - ], - "item": [ - { - "linkId": "appointment.description", - "type": "display", - "text": "Upcoming appointment", - "required": false - }, - { - "linkId": "appointment.dateTime", - "type": "dateTime", - "text": "Date:", - "required": true - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/questionnaires.json.license b/packages/fhir/test/resources/questionnaires.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/questionnaires.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/questionnaires.test.ts b/packages/fhir/test/resources/questionnaires.test.ts deleted file mode 100644 index d5cdc58e..00000000 --- a/packages/fhir/test/resources/questionnaires.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Questionnaire } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirQuestionnaire, - type untypedQuestionnaireSchema, -} from '../../src/index.js' - -describe('Questionnaire Resource', () => { - it('should validate FHIR questionnaires from questionnaires.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/questionnaires.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const fhirResource = FhirQuestionnaire.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(fhirResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/regulatedAuthorizations.json b/packages/fhir/test/resources/regulatedAuthorizations.json deleted file mode 100644 index b708d889..00000000 --- a/packages/fhir/test/resources/regulatedAuthorizations.json +++ /dev/null @@ -1,190 +0,0 @@ -{ - "0": { - "resourceType": "RegulatedAuthorization", - "id": "regulated-authorization-example-0" - }, - "1": { - "resourceType": "RegulatedAuthorization", - "id": "regulated-authorization-example-1", - "identifier": [ - { - "system": "http://example.org/marketing-authorizations", - "value": "MA-12345" - } - ], - "subject": [ - { - "reference": "MedicinalProductDefinition/example" - } - ], - "type": { - "coding": [ - { - "system": "http://example.org/authorization-type", - "code": "marketing-auth", - "display": "Marketing Authorization" - } - ] - }, - "status": { - "coding": [ - { - "system": "http://hl7.org/fhir/publication-status", - "code": "active", - "display": "Active" - } - ] - }, - "statusDate": "2023-01-15", - "holder": { - "reference": "Organization/pharma-company" - }, - "regulator": { - "reference": "Organization/fda" - } - }, - "2": { - "resourceType": "RegulatedAuthorization", - "id": "regulated-authorization-example-2", - "identifier": [ - { - "system": "http://ema.europa.eu/marketing-authorizations", - "value": "EU/1/23/1234" - } - ], - "subject": [ - { - "reference": "MedicinalProductDefinition/vaccine" - } - ], - "type": { - "coding": [ - { - "system": "http://example.org/authorization-type", - "code": "conditional-approval", - "display": "Conditional Approval" - } - ] - }, - "description": "Conditional marketing authorization for novel vaccine", - "region": [ - { - "coding": [ - { - "system": "http://unstats.un.org/unsd/methods/m49/m49.htm", - "code": "150", - "display": "Europe" - } - ] - } - ], - "indication": { - "concept": { - "text": "Prevention of infectious disease" - } - }, - "validityPeriod": { - "start": "2023-06-01", - "end": "2024-06-01" - }, - "case": { - "identifier": { - "system": "http://example.org/cases", - "value": "CASE-2023-001" - }, - "type": { - "coding": [ - { - "system": "http://example.org/case-type", - "code": "initial-application", - "display": "Initial Application" - } - ] - }, - "dateDateTime": "2023-01-01" - } - }, - "3": { - "resourceType": "RegulatedAuthorization", - "id": "regulated-authorization-example-3", - "identifier": [ - { - "system": "http://example.org/orphan-drug-designations", - "value": "ODD-2023-789" - } - ], - "subject": [ - { - "reference": "MedicinalProductDefinition/rare-disease-drug" - } - ], - "type": { - "coding": [ - { - "system": "http://example.org/authorization-type", - "code": "orphan-drug", - "display": "Orphan Drug Designation" - } - ] - }, - "holder": { - "reference": "Organization/biotech-startup" - }, - "basis": [ - { - "text": "Rare disease affecting fewer than 200,000 patients" - } - ], - "intendedUse": { - "coding": [ - { - "system": "http://example.org/intended-use", - "code": "treatment", - "display": "Treatment" - } - ] - }, - "case": { - "identifier": { - "system": "http://example.org/cases", - "value": "CASE-2023-456" - }, - "type": { - "text": "Orphan designation application" - }, - "datePeriod": { - "start": "2022-11-01", - "end": "2023-02-28" - }, - "status": { - "coding": [ - { - "system": "http://example.org/case-status", - "code": "completed", - "display": "Completed" - } - ] - }, - "application": [ - { - "identifier": { - "value": "APP-2022-001" - }, - "type": { - "text": "Initial submission" - }, - "dateDateTime": "2022-11-01" - }, - { - "identifier": { - "value": "APP-2023-002" - }, - "type": { - "text": "Response to questions" - }, - "dateDateTime": "2023-01-15" - } - ] - } - } -} diff --git a/packages/fhir/test/resources/regulatedAuthorizations.json.license b/packages/fhir/test/resources/regulatedAuthorizations.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/regulatedAuthorizations.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/regulatedAuthorizations.test.ts b/packages/fhir/test/resources/regulatedAuthorizations.test.ts deleted file mode 100644 index 83ccf658..00000000 --- a/packages/fhir/test/resources/regulatedAuthorizations.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type RegulatedAuthorization } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirRegulatedAuthorization, - type untypedRegulatedAuthorizationSchema, -} from '../../src/index.js' - -describe('RegulatedAuthorization Resource', () => { - it('should validate FHIR regulatedAuthorization from regulatedAuthorizations.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/regulatedAuthorizations.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirRegulatedAuthorization.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/relatedPersons.json b/packages/fhir/test/resources/relatedPersons.json deleted file mode 100644 index 3c2ccc83..00000000 --- a/packages/fhir/test/resources/relatedPersons.json +++ /dev/null @@ -1,175 +0,0 @@ -{ - "0": { - "resourceType": "RelatedPerson", - "id": "related-example-0", - "active": true, - "patient": { - "reference": "Patient/patient-example" - }, - "relationship": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode", - "code": "WIFE", - "display": "wife" - } - ] - } - ], - "name": [ - { - "family": "Smith", - "given": ["Jane"] - } - ] - }, - "1": { - "resourceType": "RelatedPerson", - "id": "related-example-1", - "identifier": [ - { - "system": "http://example.org/related-persons", - "value": "REL-123" - } - ], - "active": true, - "patient": { - "reference": "Patient/patient-example-2" - }, - "relationship": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode", - "code": "FTH", - "display": "father" - } - ] - } - ], - "name": [ - { - "use": "official", - "family": "Johnson", - "given": ["Michael", "Robert"] - } - ], - "telecom": [ - { - "system": "phone", - "value": "+1-555-888-9999", - "use": "mobile" - } - ], - "gender": "male", - "birthDate": "1970-03-15", - "address": [ - { - "use": "home", - "line": ["456 Oak Street"], - "city": "Boston", - "state": "MA", - "postalCode": "02101" - } - ] - }, - "2": { - "resourceType": "RelatedPerson", - "id": "related-example-2", - "active": false, - "patient": { - "reference": "Patient/patient-example-3" - }, - "relationship": [ - { - "text": "Former guardian" - } - ] - }, - "3": { - "resourceType": "RelatedPerson", - "id": "related-example-3", - "active": true, - "patient": { - "reference": "Patient/patient-child" - }, - "relationship": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode", - "code": "MTH", - "display": "mother" - } - ] - }, - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0131", - "code": "C", - "display": "Emergency Contact" - } - ] - } - ], - "name": [ - { - "use": "official", - "family": "Garcia", - "given": ["Maria"] - } - ], - "telecom": [ - { - "system": "phone", - "value": "+1-555-111-2222", - "use": "home", - "rank": 1 - }, - { - "system": "phone", - "value": "+1-555-333-4444", - "use": "mobile", - "rank": 2 - }, - { - "system": "email", - "value": "maria.garcia@example.com" - } - ], - "gender": "female", - "address": [ - { - "line": ["789 Main Street", "Apt 3B"], - "city": "Los Angeles", - "state": "CA", - "postalCode": "90001" - } - ], - "photo": [ - { - "contentType": "image/jpeg", - "url": "https://example.org/photos/garcia-m.jpg" - } - ], - "period": { - "start": "2020-01-01" - }, - "communication": [ - { - "language": { - "coding": [ - { - "system": "urn:ietf:bcp:47", - "code": "es", - "display": "Spanish" - } - ] - }, - "preferred": true - } - ] - } -} diff --git a/packages/fhir/test/resources/relatedPersons.json.license b/packages/fhir/test/resources/relatedPersons.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/relatedPersons.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/relatedPersons.test.ts b/packages/fhir/test/resources/relatedPersons.test.ts deleted file mode 100644 index 570802cf..00000000 --- a/packages/fhir/test/resources/relatedPersons.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type RelatedPerson } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirRelatedPerson, - type untypedRelatedPersonSchema, -} from '../../src/index.js' - -describe('RelatedPerson Resource', () => { - it('should validate FHIR relatedPerson from relatedPersons.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/relatedPersons.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirRelatedPerson.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/requestGroups.json b/packages/fhir/test/resources/requestGroups.json deleted file mode 100644 index 76c7e997..00000000 --- a/packages/fhir/test/resources/requestGroups.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "0": { - "resourceType": "RequestGroup", - "id": "requestgroup-example-0", - "status": "active", - "intent": "order", - "priority": "routine", - "subject": { - "reference": "Patient/patient-example" - }, - "encounter": { - "reference": "Encounter/encounter-example" - }, - "authoredOn": "2024-01-15T10:00:00Z", - "author": { - "reference": "Practitioner/practitioner-example" - }, - "action": [ - { - "title": "Order Lab Tests", - "description": "Complete metabolic panel and lipid panel", - "priority": "routine", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/action-type", - "code": "create" - } - ] - } - } - ] - }, - "1": { - "resourceType": "RequestGroup", - "id": "requestgroup-example-1", - "status": "draft", - "intent": "proposal", - "subject": { - "reference": "Patient/patient-example-2" - }, - "action": [ - { - "prefix": "1", - "title": "Administer medication", - "selectionBehavior": "exactly-one", - "action": [ - { - "title": "Option A" - }, - { - "title": "Option B" - } - ] - } - ] - }, - "2": { - "resourceType": "RequestGroup", - "id": "requestgroup-example-2", - "status": "completed", - "intent": "plan", - "priority": "urgent", - "subject": { - "reference": "Patient/patient-example-3" - }, - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "386053000", - "display": "Evaluation procedure" - } - ] - } - }, - "3": { - "resourceType": "RequestGroup", - "id": "requestgroup-example-3", - "status": "revoked", - "intent": "directive", - "subject": { - "reference": "Patient/patient-example-4" - }, - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "254837009", - "display": "Malignant neoplasm of breast" - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/requestGroups.json.license b/packages/fhir/test/resources/requestGroups.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/requestGroups.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/requestGroups.test.ts b/packages/fhir/test/resources/requestGroups.test.ts deleted file mode 100644 index a09c9a20..00000000 --- a/packages/fhir/test/resources/requestGroups.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type RequestGroup } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirRequestGroup, - type untypedRequestGroupSchema, -} from '../../src/index.js' - -describe('RequestGroup Resource', () => { - it('should validate FHIR RequestGroups from requestGroups.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/requestGroups.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirRequestGroup.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/researchDefinitions.json b/packages/fhir/test/resources/researchDefinitions.json deleted file mode 100644 index 307b84b9..00000000 --- a/packages/fhir/test/resources/researchDefinitions.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "0": { - "resourceType": "ResearchDefinition", - "id": "researchdefinition-example-0", - "status": "draft", - "population": { - "reference": "ResearchElementDefinition/population-001" - } - }, - "1": { - "resourceType": "ResearchDefinition", - "id": "researchdefinition-example-1", - "url": "http://example.org/fhir/ResearchDefinition/diabetes-study", - "identifier": [ - { - "system": "http://example.org/research-definitions", - "value": "RD-001" - } - ], - "version": "1.0.0", - "name": "DiabetesResearchDefinition", - "title": "Diabetes Treatment Research Definition", - "status": "active", - "description": "Research definition for diabetes treatment effectiveness study", - "population": { - "reference": "ResearchElementDefinition/diabetes-population" - }, - "exposure": { - "reference": "ResearchElementDefinition/diabetes-treatment" - }, - "outcome": { - "reference": "ResearchElementDefinition/hba1c-outcome" - } - }, - "2": { - "resourceType": "ResearchDefinition", - "id": "researchdefinition-example-2", - "status": "active", - "title": "Cardiovascular Risk Study Definition", - "publisher": "Example Research Institute", - "contact": [ - { - "name": "Dr. Research Lead" - } - ], - "population": { - "reference": "ResearchElementDefinition/cardiac-population" - }, - "exposure": { - "reference": "ResearchElementDefinition/smoking-exposure" - }, - "exposureAlternative": { - "reference": "ResearchElementDefinition/non-smoking-exposure" - }, - "outcome": { - "reference": "ResearchElementDefinition/cardiac-event-outcome" - } - }, - "3": { - "resourceType": "ResearchDefinition", - "id": "researchdefinition-example-3", - "url": "http://example.org/fhir/ResearchDefinition/cancer-immunotherapy", - "name": "CancerImmunotherapyDefinition", - "title": "Cancer Immunotherapy Research Definition", - "status": "active", - "experimental": false, - "description": "Comprehensive research definition for cancer immunotherapy clinical trials", - "purpose": "To standardize the population, exposure, and outcome definitions for cancer immunotherapy research", - "effectivePeriod": { - "start": "2024-01-01" - }, - "topic": [ - { - "text": "Oncology" - }, - { - "text": "Immunotherapy" - } - ], - "author": [ - { - "name": "Oncology Research Group" - } - ], - "population": { - "reference": "ResearchElementDefinition/cancer-population" - }, - "exposure": { - "reference": "ResearchElementDefinition/immunotherapy-exposure" - }, - "outcome": { - "reference": "ResearchElementDefinition/survival-outcome" - } - } -} diff --git a/packages/fhir/test/resources/researchDefinitions.json.license b/packages/fhir/test/resources/researchDefinitions.json.license deleted file mode 100644 index 3e8d46d1..00000000 --- a/packages/fhir/test/resources/researchDefinitions.json.license +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/packages/fhir/test/resources/researchDefinitions.test.ts b/packages/fhir/test/resources/researchDefinitions.test.ts deleted file mode 100644 index 09dd6e61..00000000 --- a/packages/fhir/test/resources/researchDefinitions.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ResearchDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirResearchDefinition, - type untypedResearchDefinitionSchema, -} from '../../src/index.js' - -describe('ResearchDefinition Resource', () => { - it('should validate FHIR research definitions from researchDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/researchDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirResearchDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/researchElementDefinitions.json b/packages/fhir/test/resources/researchElementDefinitions.json deleted file mode 100644 index b8a75276..00000000 --- a/packages/fhir/test/resources/researchElementDefinitions.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "0": { - "resourceType": "ResearchElementDefinition", - "id": "researchelementdefinition-example-0", - "status": "draft", - "type": "population", - "characteristic": [ - { - "definitionCodeableConcept": { - "text": "Adult patients" - } - } - ] - }, - "1": { - "resourceType": "ResearchElementDefinition", - "id": "researchelementdefinition-example-1", - "url": "http://example.org/fhir/ResearchElementDefinition/diabetes-exposure", - "identifier": [ - { - "system": "http://example.org/research-elements", - "value": "RED-001" - } - ], - "version": "1.0.0", - "name": "DiabetesExposure", - "title": "Diabetes Exposure Definition", - "status": "active", - "type": "exposure", - "variableType": "dichotomous", - "description": "Definition for diabetes mellitus exposure in clinical studies", - "characteristic": [ - { - "definitionCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "73211009", - "display": "Diabetes mellitus" - } - ] - }, - "studyEffectiveDescription": "Diagnosed during study period" - } - ] - }, - "2": { - "resourceType": "ResearchElementDefinition", - "id": "researchelementdefinition-example-2", - "status": "active", - "type": "outcome", - "variableType": "continuous", - "title": "Blood Pressure Outcome", - "characteristic": [ - { - "definitionCodeableConcept": { - "text": "Systolic blood pressure measurement" - }, - "unitOfMeasure": { - "coding": [ - { - "system": "http://unitsofmeasure.org", - "code": "mm[Hg]" - } - ] - } - } - ] - }, - "3": { - "resourceType": "ResearchElementDefinition", - "id": "researchelementdefinition-example-3", - "url": "http://example.org/fhir/ResearchElementDefinition/cardiac-population", - "name": "CardiacPatientPopulation", - "title": "Cardiac Patient Population", - "status": "active", - "experimental": false, - "publisher": "Example Research Organization", - "contact": [ - { - "name": "Research Team" - } - ], - "description": "Definition of the cardiac patient population for clinical research", - "type": "population", - "characteristic": [ - { - "definitionCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "49601007", - "display": "Disorder of cardiovascular system" - } - ] - }, - "studyEffectivePeriod": { - "start": "2024-01-01" - } - } - ] - } -} diff --git a/packages/fhir/test/resources/researchElementDefinitions.json.license b/packages/fhir/test/resources/researchElementDefinitions.json.license deleted file mode 100644 index 3e8d46d1..00000000 --- a/packages/fhir/test/resources/researchElementDefinitions.json.license +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/packages/fhir/test/resources/researchElementDefinitions.test.ts b/packages/fhir/test/resources/researchElementDefinitions.test.ts deleted file mode 100644 index 868c1942..00000000 --- a/packages/fhir/test/resources/researchElementDefinitions.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ResearchElementDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirResearchElementDefinition, - type untypedResearchElementDefinitionSchema, -} from '../../src/index.js' - -describe('ResearchElementDefinition Resource', () => { - it('should validate FHIR research element definitions from researchElementDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/researchElementDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = - FhirResearchElementDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/researchStudies.json b/packages/fhir/test/resources/researchStudies.json deleted file mode 100644 index e97fd76b..00000000 --- a/packages/fhir/test/resources/researchStudies.json +++ /dev/null @@ -1,140 +0,0 @@ -{ - "0": { - "resourceType": "ResearchStudy", - "id": "researchstudy-example-0", - "status": "active", - "title": "Diabetes Management Study" - }, - "1": { - "resourceType": "ResearchStudy", - "id": "researchstudy-example-1", - "identifier": [ - { - "system": "http://clinicaltrials.gov", - "value": "NCT12345678" - } - ], - "title": "Cancer Treatment Trial", - "status": "active", - "phase": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/research-study-phase", - "code": "phase-3" - } - ] - }, - "primaryPurposeType": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/research-study-prim-purp-type", - "code": "treatment" - } - ] - }, - "category": [ - { - "text": "Interventional" - } - ], - "focus": [ - { - "text": "Lung Cancer" - } - ], - "description": "A randomized controlled trial for advanced lung cancer treatment" - }, - "2": { - "resourceType": "ResearchStudy", - "id": "researchstudy-example-2", - "status": "closed-to-accrual", - "title": "Cardiovascular Health Observation Study", - "period": { - "start": "2023-01-01", - "end": "2023-12-31" - }, - "contact": [ - { - "name": "Dr. Jane Smith" - } - ], - "sponsor": { - "reference": "Organization/sponsor-001" - } - }, - "3": { - "resourceType": "ResearchStudy", - "id": "researchstudy-example-3", - "identifier": [ - { - "system": "http://example.org/research-studies", - "value": "STUDY-2024-001" - } - ], - "title": "Multi-center Alzheimer's Disease Study", - "status": "active", - "phase": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/research-study-phase", - "code": "phase-2" - } - ] - }, - "description": "A phase 2 study evaluating novel treatments for Alzheimer's disease", - "period": { - "start": "2024-01-01" - }, - "principalInvestigator": { - "reference": "Practitioner/investigator-001" - }, - "arm": [ - { - "name": "Experimental Drug A", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/research-study-arm-type", - "code": "experimental" - } - ] - } - }, - { - "name": "Placebo", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/research-study-arm-type", - "code": "placebo-comparator" - } - ] - } - } - ], - "objective": [ - { - "name": "Primary Outcome", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/research-study-objective-type", - "code": "primary" - } - ] - } - }, - { - "name": "Secondary Outcome", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/research-study-objective-type", - "code": "secondary" - } - ] - } - } - ] - } -} diff --git a/packages/fhir/test/resources/researchStudies.json.license b/packages/fhir/test/resources/researchStudies.json.license deleted file mode 100644 index 3e8d46d1..00000000 --- a/packages/fhir/test/resources/researchStudies.json.license +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/packages/fhir/test/resources/researchStudies.test.ts b/packages/fhir/test/resources/researchStudies.test.ts deleted file mode 100644 index 556264be..00000000 --- a/packages/fhir/test/resources/researchStudies.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ResearchStudy } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirResearchStudy, - type untypedResearchStudySchema, -} from '../../src/index.js' - -describe('ResearchStudy Resource', () => { - it('should validate FHIR research studies from researchStudies.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/researchStudies.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirResearchStudy.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/researchSubjects.json b/packages/fhir/test/resources/researchSubjects.json deleted file mode 100644 index af8be38f..00000000 --- a/packages/fhir/test/resources/researchSubjects.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "0": { - "resourceType": "ResearchSubject", - "id": "researchsubject-example-0", - "status": "on-study", - "study": { - "reference": "ResearchStudy/study-001" - }, - "individual": { - "reference": "Patient/patient-001" - } - }, - "1": { - "resourceType": "ResearchSubject", - "id": "researchsubject-example-1", - "identifier": [ - { - "system": "http://example.org/research-subjects", - "value": "RS-12345" - } - ], - "status": "on-study-intervention", - "study": { - "reference": "ResearchStudy/cancer-trial" - }, - "individual": { - "reference": "Patient/patient-002" - }, - "assignedArm": "Treatment Arm A", - "actualArm": "Treatment Arm A", - "consent": { - "reference": "Consent/consent-001" - } - }, - "2": { - "resourceType": "ResearchSubject", - "id": "researchsubject-example-2", - "status": "screening", - "period": { - "start": "2024-01-10" - }, - "study": { - "reference": "ResearchStudy/diabetes-study" - }, - "individual": { - "reference": "Patient/patient-003" - } - }, - "3": { - "resourceType": "ResearchSubject", - "id": "researchsubject-example-3", - "identifier": [ - { - "system": "http://example.org/research-subjects", - "value": "RS-67890" - } - ], - "status": "off-study", - "period": { - "start": "2023-06-01", - "end": "2024-01-15" - }, - "study": { - "reference": "ResearchStudy/cardio-study" - }, - "individual": { - "reference": "Patient/patient-004" - }, - "assignedArm": "Control Group", - "actualArm": "Control Group" - } -} diff --git a/packages/fhir/test/resources/researchSubjects.json.license b/packages/fhir/test/resources/researchSubjects.json.license deleted file mode 100644 index 3e8d46d1..00000000 --- a/packages/fhir/test/resources/researchSubjects.json.license +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/packages/fhir/test/resources/researchSubjects.test.ts b/packages/fhir/test/resources/researchSubjects.test.ts deleted file mode 100644 index 80432713..00000000 --- a/packages/fhir/test/resources/researchSubjects.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ResearchSubject } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirResearchSubject, - type untypedResearchSubjectSchema, -} from '../../src/index.js' - -describe('ResearchSubject Resource', () => { - it('should validate FHIR research subjects from researchSubjects.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/researchSubjects.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirResearchSubject.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/riskAssessments.json b/packages/fhir/test/resources/riskAssessments.json deleted file mode 100644 index e19cf742..00000000 --- a/packages/fhir/test/resources/riskAssessments.json +++ /dev/null @@ -1,221 +0,0 @@ -{ - "0": { - "resourceType": "RiskAssessment", - "id": "cardiac", - "status": "final", - "subject": { - "reference": "Patient/example" - }, - "encounter": { - "reference": "Encounter/example" - }, - "occurrenceDateTime": "2014-07-19T16:04:00Z", - "performer": { - "reference": "Practitioner/example" - }, - "basis": [ - { - "reference": "Observation/example" - } - ], - "prediction": [ - { - "outcome": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "414545008", - "display": "Ischemic heart disease" - } - ], - "text": "Heart Attack" - }, - "probabilityDecimal": 0.02, - "whenRange": { - "low": { - "value": 39, - "unit": "years", - "system": "http://unitsofmeasure.org", - "code": "a" - }, - "high": { - "value": 49, - "unit": "years", - "system": "http://unitsofmeasure.org", - "code": "a" - } - } - } - ] - }, - "1": { - "resourceType": "RiskAssessment", - "id": "genetic", - "identifier": [ - { - "use": "official", - "system": "http://example.org", - "value": "risk-123" - } - ], - "status": "final", - "method": { - "coding": [ - { - "code": "BRCAPRO" - } - ] - }, - "subject": { - "reference": "Patient/example" - }, - "occurrenceDateTime": "2006-01-13T23:01:00Z", - "basis": [ - { - "reference": "FamilyMemberHistory/mother" - } - ], - "prediction": [ - { - "outcome": { - "text": "Breast Cancer" - }, - "probabilityDecimal": 0.000168, - "whenRange": { - "high": { - "value": 53, - "unit": "years", - "system": "http://unitsofmeasure.org", - "code": "a" - } - } - }, - { - "outcome": { - "text": "Breast Cancer" - }, - "probabilityDecimal": 0.000368, - "whenRange": { - "low": { - "value": 54, - "unit": "years", - "system": "http://unitsofmeasure.org", - "code": "a" - }, - "high": { - "value": 57, - "unit": "years", - "system": "http://unitsofmeasure.org", - "code": "a" - } - } - } - ], - "note": [ - { - "text": "High degree of certainty" - } - ] - }, - "2": { - "resourceType": "RiskAssessment", - "id": "population", - "status": "final", - "code": { - "coding": [ - { - "system": "http://browser.ihtsdotools.org/", - "code": "709510002", - "display": "Assessment of risk for disease" - } - ] - }, - "subject": { - "reference": "Patient/example" - }, - "occurrencePeriod": { - "start": "2014-07-19", - "end": "2014-07-20" - }, - "condition": { - "reference": "Condition/example" - }, - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "444814009", - "display": "Viral sinusitis" - } - ] - } - ], - "prediction": [ - { - "outcome": { - "text": "Complications" - }, - "qualitativeRisk": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/risk-probability", - "code": "moderate", - "display": "Moderate likelihood" - } - ] - } - } - ], - "mitigation": "Careful monitoring and early intervention" - }, - "3": { - "resourceType": "RiskAssessment", - "id": "prognosis", - "basedOn": { - "reference": "ServiceRequest/example" - }, - "parent": { - "reference": "Procedure/example" - }, - "status": "preliminary", - "subject": { - "reference": "Patient/example" - }, - "occurrenceDateTime": "2014-08-15T09:00:00Z", - "performer": { - "reference": "Practitioner/example" - }, - "reasonReference": [ - { - "reference": "Observation/example" - } - ], - "prediction": [ - { - "outcome": { - "text": "Complete Recovery" - }, - "probabilityRange": { - "low": { - "value": 0.6, - "unit": "%", - "system": "http://unitsofmeasure.org", - "code": "%" - }, - "high": { - "value": 0.8, - "unit": "%", - "system": "http://unitsofmeasure.org", - "code": "%" - } - }, - "whenPeriod": { - "start": "2014-09-01", - "end": "2014-12-01" - }, - "rationale": "Based on similar patient outcomes" - } - ] - } -} diff --git a/packages/fhir/test/resources/riskAssessments.json.license b/packages/fhir/test/resources/riskAssessments.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/riskAssessments.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/riskAssessments.test.ts b/packages/fhir/test/resources/riskAssessments.test.ts deleted file mode 100644 index 05d12109..00000000 --- a/packages/fhir/test/resources/riskAssessments.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type RiskAssessment } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirRiskAssessment, - type untypedRiskAssessmentSchema, -} from '../../src/index.js' - -describe('RiskAssessment Resource', () => { - it('should validate FHIR risk assessment from riskAssessments.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/riskAssessments.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirRiskAssessment.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/schedules.json b/packages/fhir/test/resources/schedules.json deleted file mode 100644 index 8fc29457..00000000 --- a/packages/fhir/test/resources/schedules.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "0": { - "resourceType": "Schedule", - "id": "schedule-example-0", - "active": true, - "actor": [ - { - "reference": "Practitioner/practitioner-example" - } - ] - }, - "1": { - "resourceType": "Schedule", - "id": "schedule-example-1", - "identifier": [ - { - "system": "http://example.org/schedules", - "value": "SCH-001" - } - ], - "active": true, - "serviceCategory": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/service-category", - "code": "17", - "display": "General Practice" - } - ] - } - ], - "serviceType": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/service-type", - "code": "124", - "display": "General Practice" - } - ] - } - ], - "specialty": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "394814009", - "display": "General practice" - } - ] - } - ], - "actor": [ - { - "reference": "Practitioner/practitioner-example" - }, - { - "reference": "Location/location-example" - } - ], - "planningHorizon": { - "start": "2024-12-01T00:00:00Z", - "end": "2024-12-31T23:59:59Z" - }, - "comment": "Regular clinic hours" - }, - "2": { - "resourceType": "Schedule", - "id": "schedule-example-2", - "active": false, - "actor": [ - { - "reference": "HealthcareService/service-example" - } - ] - }, - "3": { - "resourceType": "Schedule", - "id": "schedule-example-3", - "active": true, - "serviceCategory": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/service-category", - "code": "27", - "display": "Specialist Medical" - } - ] - } - ], - "actor": [ - { - "reference": "Practitioner/practitioner-cardio" - }, - { - "reference": "Location/location-cardio-clinic" - } - ], - "planningHorizon": { - "start": "2025-01-01T00:00:00Z", - "end": "2025-03-31T23:59:59Z" - }, - "comment": "Cardiology consultation schedule for Q1 2025" - } -} diff --git a/packages/fhir/test/resources/schedules.json.license b/packages/fhir/test/resources/schedules.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/schedules.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/schedules.test.ts b/packages/fhir/test/resources/schedules.test.ts deleted file mode 100644 index d80a75a7..00000000 --- a/packages/fhir/test/resources/schedules.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Schedule } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirSchedule, type untypedScheduleSchema } from '../../src/index.js' - -describe('Schedule Resource', () => { - it('should validate FHIR schedule from schedules.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/schedules.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirSchedule.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/searchParameters.json b/packages/fhir/test/resources/searchParameters.json deleted file mode 100644 index f0e9f9d7..00000000 --- a/packages/fhir/test/resources/searchParameters.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "0": { - "resourceType": "SearchParameter", - "id": "patient-identifier", - "url": "http://example.org/fhir/SearchParameter/patient-identifier", - "name": "PatientIdentifier", - "status": "active", - "description": "Search for patients by identifier", - "code": "identifier", - "base": ["Patient"], - "type": "token", - "expression": "Patient.identifier" - }, - "1": { - "resourceType": "SearchParameter", - "id": "observation-value-quantity", - "url": "http://hl7.org/fhir/SearchParameter/Observation-value-quantity", - "name": "ObservationValueQuantity", - "status": "active", - "description": "The value of the observation, if the value is a Quantity", - "code": "value-quantity", - "base": ["Observation"], - "type": "quantity", - "expression": "Observation.value.ofType(Quantity)", - "comparator": ["eq", "ne", "gt", "lt", "ge", "le"], - "modifier": ["missing"] - }, - "2": { - "resourceType": "SearchParameter", - "id": "encounter-patient", - "url": "http://example.org/fhir/SearchParameter/encounter-patient", - "name": "EncounterPatient", - "status": "draft", - "description": "The patient present at the encounter", - "code": "patient", - "base": ["Encounter"], - "type": "reference", - "expression": "Encounter.subject.where(resolve() is Patient)", - "target": ["Patient"], - "multipleOr": true, - "multipleAnd": true - }, - "3": { - "resourceType": "SearchParameter", - "id": "medication-ingredient", - "url": "http://example.org/fhir/SearchParameter/medication-ingredient", - "name": "MedicationIngredient", - "status": "active", - "description": "Reference to a resource that is a component of the medication", - "code": "ingredient", - "base": ["Medication"], - "type": "reference", - "expression": "Medication.ingredient.item.reference", - "target": ["Substance", "Medication"], - "component": [ - { - "definition": "http://example.org/fhir/SearchParameter/substance-code", - "expression": "Medication.ingredient.item.resolve().ofType(Substance).code" - } - ] - } -} diff --git a/packages/fhir/test/resources/searchParameters.json.license b/packages/fhir/test/resources/searchParameters.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/searchParameters.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/searchParameters.test.ts b/packages/fhir/test/resources/searchParameters.test.ts deleted file mode 100644 index 1f132618..00000000 --- a/packages/fhir/test/resources/searchParameters.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type SearchParameter } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirSearchParameter, - type untypedSearchParameterSchema, -} from '../../src/index.js' - -describe('SearchParameter Resource', () => { - it('should validate FHIR search parameter from searchParameters.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/searchParameters.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirSearchParameter.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/serviceRequests.json b/packages/fhir/test/resources/serviceRequests.json deleted file mode 100644 index fdbb6e70..00000000 --- a/packages/fhir/test/resources/serviceRequests.json +++ /dev/null @@ -1,308 +0,0 @@ -{ - "0": { - "resourceType": "ServiceRequest", - "id": "c028a8d3-1b75-4906-8bad-351bf37439ff", - "meta": { - "extension": [ - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-timestamp", - "valueDateTime": "2025-08-13T16:56:56-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-trigger", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0003", - "code": "R01" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-type", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0076", - "code": "ORU" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-timestamp", - "valueDateTime": "2015-01-23T08:13:24-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-id", - "valueString": "156" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-data-model-version", - "valueString": "2.6" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-facility", - "valueString": "TLK" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-client-id", - "valueString": "ABC" - } - ] - }, - "identifier": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "VN", - "display": "Visit number" - } - ] - }, - "value": "456" - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "FILL", - "display": "Filler Identifier" - } - ] - }, - "system": "urn:id:ORD", - "value": "13" - } - ], - "status": "active", - "intent": "order", - "code": { - "coding": [ - { - "system": "urn:id:CUSTLAB", - "code": "HBEL", - "display": "Hb-Elektrophorese" - } - ], - "text": "Hb-Elektrophorese" - }, - "subject": { - "reference": "Patient/9f747657-3cc6-4b9a-8371-640bd017501f" - }, - "occurrenceDateTime": "2015-02-16T08:11:23-01:00", - "authoredOn": "2015-04-16T08:11:23-01:00" - }, - "1": { - "resourceType": "ServiceRequest", - "id": "1bfb8e66-e62a-4e7a-aac1-14bec6e26e84", - "meta": { - "extension": [ - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-timestamp", - "valueDateTime": "2025-12-12T11:34:39-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-trigger", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0003", - "code": "R01" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-type", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0076", - "code": "ORU" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-timestamp", - "valueDateTime": "2025-08-23T05:27:00-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-id", - "valueString": "4367" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-data-model-version", - "valueString": "2.6" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-facility", - "valueString": "EHR" - } - ] - }, - "identifier": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "VN", - "display": "Visit number" - } - ] - }, - "value": "123" - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "FILL", - "display": "Filler Identifier" - } - ] - }, - "value": "246" - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "PLAC", - "display": "Placer Identifier" - } - ] - }, - "value": "24568" - } - ], - "status": "unknown", - "intent": "order", - "code": { - "coding": [ - { - "system": "urn:id:CUSTLAB", - "code": "BARB", - "display": "Barbiturate" - } - ], - "text": "Barbiturate" - }, - "subject": { - "reference": "Patient/9f747657-3cc6-4b9a-8371-640bd017501f" - }, - "occurrenceDateTime": "2025-02-23T05:26:00-01:00" - }, - "2": { - "resourceType": "ServiceRequest", - "id": "b2800f7c-f741-479f-bc6f-5e7a065f4d2b", - "meta": { - "extension": [ - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-timestamp", - "valueDateTime": "2025-06-12T11:34:39-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-trigger", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0003", - "code": "R01" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-type", - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0076", - "code": "ORU" - } - ] - } - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-event-timestamp", - "valueDateTime": "2025-03-23T05:27:00-01:00" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-record-id", - "valueString": "34567" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/source-data-model-version", - "valueString": "2.6" - }, - { - "url": "http://ibm.com/fhir/cdm/StructureDefinition/process-facility", - "valueString": "EHR" - } - ] - }, - "identifier": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "VN", - "display": "Visit number" - } - ] - }, - "value": "12345678" - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "FILL", - "display": "Filler Identifier" - } - ] - }, - "value": "87654321" - }, - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "PLAC", - "display": "Placer Identifier" - } - ] - }, - "value": "13579246" - } - ], - "status": "unknown", - "intent": "order", - "code": { - "coding": [ - { - "system": "urn:id:CUSTLAB", - "code": "BENZ", - "display": "Benzodiazepine" - } - ], - "text": "Benzodiazepine" - }, - "subject": { - "reference": "Patient/9f747657-3cc6-4b9a-8371-640bd017501f" - }, - "occurrenceDateTime": "2025-03-23T05:26:00-01:00" - } -} diff --git a/packages/fhir/test/resources/serviceRequests.json.license b/packages/fhir/test/resources/serviceRequests.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/serviceRequests.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/serviceRequests.test.ts b/packages/fhir/test/resources/serviceRequests.test.ts deleted file mode 100644 index d03647bf..00000000 --- a/packages/fhir/test/resources/serviceRequests.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ServiceRequest } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirServiceRequest, - type untypedServiceRequestSchema, -} from '../../src/index.js' - -describe('ServiceRequest Resource', () => { - it('should validate FHIR ServiceRequest from serviceRequests.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/serviceRequests.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirServiceRequest.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/slots.json b/packages/fhir/test/resources/slots.json deleted file mode 100644 index 94bee974..00000000 --- a/packages/fhir/test/resources/slots.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "0": { - "resourceType": "Slot", - "id": "slot-example-0", - "schedule": { - "reference": "Schedule/schedule-example-0" - }, - "status": "free", - "start": "2024-12-15T09:00:00Z", - "end": "2024-12-15T09:30:00Z" - }, - "1": { - "resourceType": "Slot", - "id": "slot-example-1", - "identifier": [ - { - "system": "http://example.org/slots", - "value": "SLOT-001" - } - ], - "serviceCategory": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/service-category", - "code": "17", - "display": "General Practice" - } - ] - } - ], - "serviceType": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/service-type", - "code": "124", - "display": "General Practice" - } - ] - } - ], - "specialty": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "394814009", - "display": "General practice" - } - ] - } - ], - "appointmentType": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0276", - "code": "ROUTINE", - "display": "Routine" - } - ] - }, - "schedule": { - "reference": "Schedule/schedule-example-1" - }, - "status": "busy", - "start": "2024-12-15T10:00:00Z", - "end": "2024-12-15T10:30:00Z", - "comment": "Appointment booked" - }, - "2": { - "resourceType": "Slot", - "id": "slot-example-2", - "schedule": { - "reference": "Schedule/schedule-example-2" - }, - "status": "busy-unavailable", - "start": "2024-12-15T14:00:00Z", - "end": "2024-12-15T15:00:00Z" - }, - "3": { - "resourceType": "Slot", - "id": "slot-example-3", - "schedule": { - "reference": "Schedule/schedule-example-3" - }, - "status": "free", - "start": "2025-01-05T11:00:00Z", - "end": "2025-01-05T12:00:00Z", - "overbooked": false, - "comment": "Available for cardiology consultation" - } -} diff --git a/packages/fhir/test/resources/slots.json.license b/packages/fhir/test/resources/slots.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/slots.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/slots.test.ts b/packages/fhir/test/resources/slots.test.ts deleted file mode 100644 index 512ffa86..00000000 --- a/packages/fhir/test/resources/slots.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Slot } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirSlot, type untypedSlotSchema } from '../../src/index.js' - -describe('Slot Resource', () => { - it('should validate FHIR slot from slots.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/slots.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirSlot.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) - - it('exposes validated slot boundaries as dates', () => { - const slot = FhirSlot.parse({ - resourceType: 'Slot', - schedule: { reference: 'Schedule/example' }, - status: 'free', - start: '2024-12-15T09:00:00Z', - end: '2024-12-15T09:30:00Z', - }) - - expect(slot.startDate.toISOString()).toBe('2024-12-15T09:00:00.000Z') - expect(slot.endDate.toISOString()).toBe('2024-12-15T09:30:00.000Z') - }) -}) diff --git a/packages/fhir/test/resources/specimenDefinitions.json b/packages/fhir/test/resources/specimenDefinitions.json deleted file mode 100644 index c73beeeb..00000000 --- a/packages/fhir/test/resources/specimenDefinitions.json +++ /dev/null @@ -1,345 +0,0 @@ -{ - "0": { - "resourceType": "SpecimenDefinition", - "id": "example-specimen-definition-1", - "identifier": { - "system": "http://example.org/specimen-definitions", - "value": "SPEC-DEF-001" - }, - "typeCollected": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "119297000", - "display": "Blood specimen" - } - ] - }, - "patientPreparation": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "263678003", - "display": "Fasting" - } - ] - } - ], - "timeAspect": "preferably morning time", - "collection": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "28520004", - "display": "Venipuncture" - } - ] - } - ], - "typeTested": [ - { - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "119361006", - "display": "Plasma specimen" - } - ] - }, - "preference": "preferred", - "container": { - "material": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "32039001", - "display": "Glass" - } - ] - }, - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "706053007", - "display": "Evacuated blood collection tube" - } - ] - }, - "cap": { - "coding": [ - { - "system": "http://example.org/container-cap", - "code": "green", - "display": "Green cap" - } - ] - }, - "minimumVolumeQuantity": { - "value": 2, - "unit": "mL", - "system": "http://unitsofmeasure.org", - "code": "mL" - } - } - } - ] - }, - "1": { - "resourceType": "SpecimenDefinition", - "id": "example-specimen-definition-2", - "typeCollected": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "122575003", - "display": "Urine specimen" - } - ] - }, - "collection": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "258574006", - "display": "Mid-stream urine sample" - } - ] - } - ], - "typeTested": [ - { - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "122575003", - "display": "Urine specimen" - } - ] - }, - "preference": "preferred", - "container": { - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "706054001", - "display": "Urine specimen container" - } - ] - }, - "minimumVolumeQuantity": { - "value": 10, - "unit": "mL", - "system": "http://unitsofmeasure.org", - "code": "mL" - } - }, - "handling": [ - { - "temperatureQualifier": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/handling-condition", - "code": "room", - "display": "room temperature" - } - ] - }, - "temperatureRange": { - "low": { - "value": 15, - "unit": "°C", - "system": "http://unitsofmeasure.org", - "code": "Cel" - }, - "high": { - "value": 25, - "unit": "°C", - "system": "http://unitsofmeasure.org", - "code": "Cel" - } - } - } - ] - } - ] - }, - "2": { - "resourceType": "SpecimenDefinition", - "id": "example-specimen-definition-3", - "identifier": { - "system": "http://example.org/specimen-definitions", - "value": "SPEC-DEF-003" - }, - "typeCollected": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "119376003", - "display": "Tissue specimen" - } - ] - }, - "collection": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "129314006", - "display": "Biopsy" - } - ] - } - ], - "typeTested": [ - { - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "119376003", - "display": "Tissue specimen" - } - ] - }, - "preference": "preferred", - "container": { - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "434746001", - "display": "Specimen container" - } - ] - }, - "additive": [ - { - "additiveCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0371", - "code": "BACTM", - "display": "Bacterial Transport Medium" - } - ] - } - } - ] - }, - "handling": [ - { - "temperatureQualifier": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/handling-condition", - "code": "refrigerated", - "display": "refrigerated" - } - ] - }, - "temperatureRange": { - "low": { - "value": 2, - "unit": "°C", - "system": "http://unitsofmeasure.org", - "code": "Cel" - }, - "high": { - "value": 8, - "unit": "°C", - "system": "http://unitsofmeasure.org", - "code": "Cel" - } - }, - "instruction": "Keep refrigerated during transport" - } - ] - } - ] - }, - "3": { - "resourceType": "SpecimenDefinition", - "id": "example-specimen-definition-4", - "typeCollected": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "119342007", - "display": "Saliva specimen" - } - ] - }, - "patientPreparation": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "225372009", - "display": "No oral intake for 30 minutes" - } - ], - "text": "Do not eat or drink for 30 minutes before collection" - } - ], - "timeAspect": "morning specimen preferred", - "collection": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "278450005", - "display": "Collection of saliva" - } - ] - } - ], - "typeTested": [ - { - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "119342007", - "display": "Saliva specimen" - } - ] - }, - "preference": "preferred", - "container": { - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "706055000", - "display": "Saliva specimen container" - } - ] - }, - "cap": { - "coding": [ - { - "system": "http://example.org/container-cap", - "code": "blue", - "display": "Blue cap" - } - ] - }, - "minimumVolumeQuantity": { - "value": 1, - "unit": "mL", - "system": "http://unitsofmeasure.org", - "code": "mL" - } - }, - "requirement": "Specimen should be collected in the morning" - } - ] - } -} diff --git a/packages/fhir/test/resources/specimenDefinitions.json.license b/packages/fhir/test/resources/specimenDefinitions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/specimenDefinitions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/specimenDefinitions.test.ts b/packages/fhir/test/resources/specimenDefinitions.test.ts deleted file mode 100644 index b783da18..00000000 --- a/packages/fhir/test/resources/specimenDefinitions.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type SpecimenDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirSpecimenDefinition, - type untypedSpecimenDefinitionSchema, -} from '../../src/index.js' - -describe('SpecimenDefinition Resource', () => { - it('should validate FHIR SpecimenDefinitions from specimenDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/specimenDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirSpecimenDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/specimens.json b/packages/fhir/test/resources/specimens.json deleted file mode 100644 index 0e6406fc..00000000 --- a/packages/fhir/test/resources/specimens.json +++ /dev/null @@ -1,181 +0,0 @@ -{ - "0": { - "resourceType": "Specimen", - "id": "specimen-example-0", - "status": "available", - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "119297000", - "display": "Blood specimen" - } - ] - }, - "subject": { - "reference": "Patient/patient-example" - }, - "receivedTime": "2024-11-15T08:30:00Z" - }, - "1": { - "resourceType": "Specimen", - "id": "specimen-example-1", - "identifier": [ - { - "system": "http://example.org/specimens", - "value": "SPEC-12345" - } - ], - "accessionIdentifier": { - "system": "http://lab.example.org/specimens", - "value": "LAB-67890" - }, - "status": "available", - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "122555007", - "display": "Venous blood specimen" - } - ] - }, - "subject": { - "reference": "Patient/patient-example-2" - }, - "receivedTime": "2024-11-10T09:00:00Z", - "request": [ - { - "reference": "ServiceRequest/lab-order-123" - } - ], - "collection": { - "collector": { - "reference": "Practitioner/practitioner-phlebotomist" - }, - "collectedDateTime": "2024-11-10T08:45:00Z", - "quantity": { - "value": 10, - "unit": "mL" - }, - "method": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "129316008", - "display": "Venipuncture" - } - ] - }, - "bodySite": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "368208006", - "display": "Left arm" - } - ] - } - }, - "processing": [ - { - "description": "Centrifuged", - "timeDateTime": "2024-11-10T09:15:00Z" - } - ], - "container": [ - { - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "702268002", - "display": "Blood collection tube" - } - ] - } - } - ] - }, - "2": { - "resourceType": "Specimen", - "id": "specimen-example-2", - "status": "unavailable", - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "258580003", - "display": "Urine specimen" - } - ] - }, - "subject": { - "reference": "Patient/patient-example-3" - }, - "note": [ - { - "text": "Specimen was contaminated" - } - ] - }, - "3": { - "resourceType": "Specimen", - "id": "specimen-example-3", - "status": "available", - "type": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "119376003", - "display": "Tissue specimen" - } - ] - }, - "subject": { - "reference": "Patient/patient-biopsy" - }, - "receivedTime": "2024-11-01T14:30:00Z", - "collection": { - "collectedDateTime": "2024-11-01T11:00:00Z", - "method": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "129314006", - "display": "Biopsy" - } - ] - }, - "bodySite": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "78961009", - "display": "Splenic structure" - } - ] - }, - "fastingStatusCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0916", - "code": "NF", - "display": "Not fasting" - } - ] - } - }, - "condition": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0493", - "code": "ROOM", - "display": "Room temperature" - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/specimens.json.license b/packages/fhir/test/resources/specimens.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/specimens.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/specimens.test.ts b/packages/fhir/test/resources/specimens.test.ts deleted file mode 100644 index a7b804dd..00000000 --- a/packages/fhir/test/resources/specimens.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Specimen } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirSpecimen, type untypedSpecimenSchema } from '../../src/index.js' - -describe('Specimen Resource', () => { - it('should validate FHIR specimen from specimens.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/specimens.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirSpecimen.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/structureDefinitions.json b/packages/fhir/test/resources/structureDefinitions.json deleted file mode 100644 index 81afd2d2..00000000 --- a/packages/fhir/test/resources/structureDefinitions.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "0": { - "resourceType": "StructureDefinition", - "url": "http://example.org/fhir/StructureDefinition/custom-patient", - "name": "CustomPatient", - "status": "draft", - "kind": "resource", - "abstract": false, - "type": "http://hl7.org/fhir/StructureDefinition/Patient" - }, - "1": { - "resourceType": "StructureDefinition", - "url": "http://example.org/fhir/StructureDefinition/patient-birthPlace", - "name": "PatientBirthPlace", - "title": "Patient Birth Place Extension", - "status": "active", - "date": "2023-01-15T10:00:00Z", - "publisher": "Example Organization", - "description": "An extension to capture the place of birth for a patient", - "kind": "complex-type", - "abstract": false, - "context": [ - { - "type": "element", - "expression": "Patient" - } - ], - "type": "http://hl7.org/fhir/StructureDefinition/Extension", - "baseDefinition": "http://hl7.org/fhir/StructureDefinition/Extension", - "derivation": "constraint" - }, - "2": { - "resourceType": "StructureDefinition", - "url": "http://example.org/fhir/StructureDefinition/observation-profile", - "name": "ObservationProfile", - "title": "Custom Observation Profile", - "status": "active", - "experimental": true, - "date": "2023-06-01T14:30:00Z", - "publisher": "Health Data Standards Organization", - "contact": [ - { - "name": "Standards Team", - "telecom": [ - { - "system": "email", - "value": "standards@example.org" - } - ] - } - ], - "description": "A profile for laboratory observations", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "purpose": "To constrain Observation resources for laboratory use cases", - "copyright": "Copyright 2023 Example Organization", - "kind": "resource", - "abstract": false, - "type": "http://hl7.org/fhir/StructureDefinition/Observation", - "baseDefinition": "http://hl7.org/fhir/StructureDefinition/Observation", - "derivation": "constraint", - "mapping": [ - { - "identity": "v2", - "uri": "http://hl7.org/v2", - "name": "HL7 v2 Mapping" - } - ] - }, - "3": { - "resourceType": "StructureDefinition", - "url": "http://example.org/fhir/StructureDefinition/medication-extended", - "name": "MedicationExtended", - "title": "Extended Medication Resource", - "status": "draft", - "experimental": false, - "date": "2023-09-20T08:00:00Z", - "publisher": "Pharmacy Standards Body", - "description": "Extended medication resource with additional constraints", - "fhirVersion": "4.3.0", - "kind": "resource", - "abstract": false, - "type": "http://hl7.org/fhir/StructureDefinition/Medication", - "baseDefinition": "http://hl7.org/fhir/StructureDefinition/Medication", - "derivation": "constraint", - "keyword": [ - { - "system": "http://snomed.info/sct", - "code": "410942007", - "display": "Drug or medicament" - } - ] - } -} diff --git a/packages/fhir/test/resources/structureDefinitions.json.license b/packages/fhir/test/resources/structureDefinitions.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/structureDefinitions.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/structureDefinitions.test.ts b/packages/fhir/test/resources/structureDefinitions.test.ts deleted file mode 100644 index 3af0951f..00000000 --- a/packages/fhir/test/resources/structureDefinitions.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type StructureDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirStructureDefinition, - type untypedStructureDefinitionSchema, -} from '../../src/index.js' - -describe('StructureDefinition Resource', () => { - it('should validate FHIR structure definition from structureDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/structureDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirStructureDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/structureMaps.json b/packages/fhir/test/resources/structureMaps.json deleted file mode 100644 index db9fb2b7..00000000 --- a/packages/fhir/test/resources/structureMaps.json +++ /dev/null @@ -1,241 +0,0 @@ -{ - "0": { - "resourceType": "StructureMap", - "id": "example", - "url": "http://example.org/fhir/StructureMap/example", - "name": "ExampleMap", - "status": "draft", - "group": [ - { - "name": "main", - "typeMode": "none", - "input": [ - { - "name": "source", - "mode": "source" - }, - { - "name": "target", - "mode": "target" - } - ], - "rule": [ - { - "name": "rule1", - "source": [ - { - "context": "source" - } - ] - } - ] - } - ] - }, - "1": { - "resourceType": "StructureMap", - "id": "patient-transform", - "url": "http://example.org/fhir/StructureMap/patient-transform", - "name": "PatientTransform", - "title": "Patient Data Transformation", - "status": "active", - "experimental": false, - "date": "2023-05-15T10:00:00Z", - "structure": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/Patient", - "mode": "source", - "alias": "PatientSource" - }, - { - "url": "http://example.org/fhir/StructureDefinition/PatientTarget", - "mode": "target", - "alias": "PatientTarget" - } - ], - "group": [ - { - "name": "patient", - "typeMode": "none", - "input": [ - { - "name": "src", - "type": "Patient", - "mode": "source" - }, - { - "name": "tgt", - "type": "Patient", - "mode": "target" - } - ], - "rule": [ - { - "name": "name", - "source": [ - { - "context": "src", - "element": "name", - "variable": "vname" - } - ], - "target": [ - { - "context": "tgt", - "contextType": "variable", - "element": "name", - "transform": "copy" - } - ] - } - ] - } - ] - }, - "2": { - "resourceType": "StructureMap", - "id": "nested-rules", - "url": "http://example.org/fhir/StructureMap/nested-rules", - "name": "NestedRulesMap", - "status": "active", - "group": [ - { - "name": "main", - "typeMode": "types", - "input": [ - { - "name": "source", - "mode": "source", - "documentation": "Source resource" - }, - { - "name": "target", - "mode": "target", - "documentation": "Target resource" - } - ], - "rule": [ - { - "name": "root", - "source": [ - { - "context": "source", - "element": "identifier", - "variable": "id", - "defaultValueString": "default-id" - } - ], - "target": [ - { - "context": "target", - "contextType": "variable", - "element": "id", - "transform": "copy", - "parameter": [ - { - "valueId": "id" - } - ] - } - ], - "rule": [ - { - "name": "nested", - "source": [ - { - "context": "id", - "element": "system" - } - ], - "target": [ - { - "context": "target", - "element": "system", - "transform": "copy" - } - ] - } - ] - } - ] - } - ] - }, - "3": { - "resourceType": "StructureMap", - "id": "complex-transform", - "url": "http://example.org/fhir/StructureMap/complex-transform", - "name": "ComplexTransform", - "status": "active", - "publisher": "Example Organization", - "contact": [ - { - "name": "Support Team" - } - ], - "import": ["http://example.org/fhir/StructureMap/base-transform"], - "structure": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/Observation", - "mode": "source" - }, - { - "url": "http://example.org/fhir/StructureDefinition/CustomObs", - "mode": "target", - "documentation": "Custom observation format" - } - ], - "group": [ - { - "name": "transform", - "typeMode": "type-and-types", - "input": [ - { - "name": "src", - "type": "Observation", - "mode": "source" - }, - { - "name": "tgt", - "mode": "target" - } - ], - "rule": [ - { - "name": "value", - "source": [ - { - "context": "src", - "element": "value", - "listMode": "first", - "variable": "val", - "condition": "value.exists()" - } - ], - "target": [ - { - "context": "tgt", - "element": "result", - "variable": "result", - "listMode": ["share"], - "transform": "create", - "parameter": [ - { - "valueString": "decimal" - } - ] - } - ], - "dependent": [ - { - "name": "processValue", - "variable": ["val", "result"] - } - ], - "documentation": "Transform observation value" - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/structureMaps.json.license b/packages/fhir/test/resources/structureMaps.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/structureMaps.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/structureMaps.test.ts b/packages/fhir/test/resources/structureMaps.test.ts deleted file mode 100644 index ce7d1223..00000000 --- a/packages/fhir/test/resources/structureMaps.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type StructureMap } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirStructureMap, - type untypedStructureMapSchema, -} from '../../src/index.js' - -describe('StructureMap Resource', () => { - it('should validate FHIR structure map from structureMaps.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/structureMaps.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirStructureMap.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/subscriptionStatuses.json b/packages/fhir/test/resources/subscriptionStatuses.json deleted file mode 100644 index 7e9941f7..00000000 --- a/packages/fhir/test/resources/subscriptionStatuses.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "0": { - "resourceType": "SubscriptionStatus", - "id": "example", - "subscription": { - "reference": "Subscription/example" - }, - "type": "event-notification", - "eventsSinceSubscriptionStart": "42" - }, - "1": { - "resourceType": "SubscriptionStatus", - "id": "handshake", - "subscription": { - "reference": "Subscription/12345" - }, - "type": "handshake", - "status": "requested", - "eventsSinceSubscriptionStart": "0", - "topic": "http://example.org/fhir/SubscriptionTopic/patient-admission" - }, - "2": { - "resourceType": "SubscriptionStatus", - "id": "heartbeat", - "subscription": { - "reference": "Subscription/monitoring" - }, - "type": "heartbeat", - "status": "active", - "eventsSinceSubscriptionStart": "125", - "topic": "http://example.org/fhir/SubscriptionTopic/vitals-monitoring", - "notificationEvent": [ - { - "eventNumber": "125", - "timestamp": "2023-06-15T14:30:00Z", - "focus": { - "reference": "Observation/12345" - } - } - ] - }, - "3": { - "resourceType": "SubscriptionStatus", - "id": "error-status", - "subscription": { - "reference": "Subscription/failed-sub" - }, - "type": "query-status", - "status": "error", - "eventsSinceSubscriptionStart": "10", - "topic": "http://example.org/fhir/SubscriptionTopic/medication-dispense", - "error": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/subscription-error", - "code": "unreachable", - "display": "Endpoint unreachable" - } - ] - } - ], - "notificationEvent": [ - { - "eventNumber": "10", - "timestamp": "2023-06-15T10:00:00Z", - "focus": { - "reference": "MedicationDispense/67890" - }, - "additionalContext": [ - { - "reference": "Patient/example" - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/subscriptionStatuses.json.license b/packages/fhir/test/resources/subscriptionStatuses.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/subscriptionStatuses.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/subscriptionStatuses.test.ts b/packages/fhir/test/resources/subscriptionStatuses.test.ts deleted file mode 100644 index be08511c..00000000 --- a/packages/fhir/test/resources/subscriptionStatuses.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type SubscriptionStatus } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirSubscriptionStatus, - type untypedSubscriptionStatusSchema, -} from '../../src/index.js' - -describe('SubscriptionStatus Resource', () => { - it('should validate FHIR subscription status from subscriptionStatuses.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/subscriptionStatuses.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirSubscriptionStatus.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/subscriptionTopics.json b/packages/fhir/test/resources/subscriptionTopics.json deleted file mode 100644 index 46d5ee5a..00000000 --- a/packages/fhir/test/resources/subscriptionTopics.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "0": { - "resourceType": "SubscriptionTopic", - "id": "patient-admission", - "url": "http://example.org/fhir/SubscriptionTopic/patient-admission", - "status": "active", - "resourceTrigger": [ - { - "resource": "http://hl7.org/fhir/StructureDefinition/Encounter", - "supportedInteraction": ["create", "update"], - "fhirPathCriteria": "(%previous.empty() | (%previous.status != 'in-progress')) and (%current.status = 'in-progress')" - } - ] - }, - "1": { - "resourceType": "SubscriptionTopic", - "id": "vitals-monitoring", - "url": "http://example.org/fhir/SubscriptionTopic/vitals-monitoring", - "status": "draft", - "title": "Vital Signs Monitoring", - "date": "2023-05-20T12:00:00Z", - "publisher": "Example Health System", - "resourceTrigger": [ - { - "description": "Monitor for new vital signs observations", - "resource": "http://hl7.org/fhir/StructureDefinition/Observation", - "supportedInteraction": ["create"], - "queryCriteria": { - "current": "category=vital-signs" - } - } - ], - "canFilterBy": [ - { - "filterParameter": "patient", - "resource": "http://hl7.org/fhir/StructureDefinition/Observation" - }, - { - "filterParameter": "code", - "resource": "http://hl7.org/fhir/StructureDefinition/Observation", - "modifier": ["=", "in"] - } - ], - "notificationShape": [ - { - "resource": "http://hl7.org/fhir/StructureDefinition/Observation", - "include": ["Observation:patient"] - } - ] - }, - "2": { - "resourceType": "SubscriptionTopic", - "id": "medication-dispense", - "url": "http://example.org/fhir/SubscriptionTopic/medication-dispense", - "status": "active", - "experimental": false, - "eventTrigger": [ - { - "description": "Notification when medication is dispensed", - "resource": "http://hl7.org/fhir/StructureDefinition/MedicationDispense", - "event": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "DISPENSE" - } - ] - } - } - ], - "canFilterBy": [ - { - "filterParameter": "patient", - "resource": "http://hl7.org/fhir/StructureDefinition/MedicationDispense", - "description": "Filter by patient" - } - ] - }, - "3": { - "resourceType": "SubscriptionTopic", - "id": "lab-results", - "url": "http://example.org/fhir/SubscriptionTopic/lab-results", - "status": "active", - "approvalDate": "2023-01-01", - "lastReviewDate": "2023-06-01", - "resourceTrigger": [ - { - "description": "Lab results available", - "resource": "http://hl7.org/fhir/StructureDefinition/DiagnosticReport", - "supportedInteraction": ["create", "update"], - "queryCriteria": { - "current": "status=final", - "previous": "status!=final", - "requireBoth": false, - "resultForCreate": "test-passes" - } - } - ], - "canFilterBy": [ - { - "filterParameter": "category", - "resource": "http://hl7.org/fhir/StructureDefinition/DiagnosticReport", - "filterDefinition": "http://example.org/fhir/SearchParameter/diagnostic-report-category", - "modifier": ["=", "in", "not-in"] - } - ], - "notificationShape": [ - { - "resource": "http://hl7.org/fhir/StructureDefinition/DiagnosticReport", - "include": ["DiagnosticReport:subject", "DiagnosticReport:result"], - "revInclude": ["Observation:focus"] - } - ] - } -} diff --git a/packages/fhir/test/resources/subscriptionTopics.json.license b/packages/fhir/test/resources/subscriptionTopics.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/subscriptionTopics.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/subscriptionTopics.test.ts b/packages/fhir/test/resources/subscriptionTopics.test.ts deleted file mode 100644 index 17846603..00000000 --- a/packages/fhir/test/resources/subscriptionTopics.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type SubscriptionTopic } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirSubscriptionTopic, - type untypedSubscriptionTopicSchema, -} from '../../src/index.js' - -describe('SubscriptionTopic Resource', () => { - it('should validate FHIR subscription topic from subscriptionTopics.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/subscriptionTopics.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirSubscriptionTopic.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/subscriptions.json b/packages/fhir/test/resources/subscriptions.json deleted file mode 100644 index 3441d231..00000000 --- a/packages/fhir/test/resources/subscriptions.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "0": { - "resourceType": "Subscription", - "id": "subscription-example-0", - "status": "active", - "reason": "Monitor patient updates", - "criteria": "Patient?", - "channel": { - "type": "rest-hook", - "endpoint": "https://example.org/webhook", - "payload": "application/fhir+json" - } - }, - "1": { - "resourceType": "Subscription", - "id": "subscription-example-1", - "status": "requested", - "contact": [ - { - "system": "email", - "value": "admin@example.org" - } - ], - "end": "2025-12-31T23:59:59Z", - "reason": "Track new observations", - "criteria": "Observation?code=http://loinc.org|15074-8", - "channel": { - "type": "rest-hook", - "endpoint": "https://example.org/fhir/subscription", - "payload": "application/fhir+json", - "header": ["Authorization: Bearer secret-token-here"] - } - }, - "2": { - "resourceType": "Subscription", - "id": "subscription-example-2", - "status": "off", - "reason": "Temporary suspension", - "criteria": "Encounter?", - "channel": { - "type": "email", - "endpoint": "mailto:notifications@example.org", - "payload": "application/fhir+json" - } - }, - "3": { - "resourceType": "Subscription", - "id": "subscription-example-3", - "status": "active", - "contact": [ - { - "system": "phone", - "value": "+1-555-999-0000" - } - ], - "end": "2025-06-30T23:59:59Z", - "reason": "Alert for critical results", - "criteria": "DiagnosticReport?status=final", - "error": "Connection timeout on 2024-11-01", - "channel": { - "type": "websocket", - "payload": "application/fhir+json" - } - } -} diff --git a/packages/fhir/test/resources/subscriptions.json.license b/packages/fhir/test/resources/subscriptions.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/subscriptions.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/subscriptions.test.ts b/packages/fhir/test/resources/subscriptions.test.ts deleted file mode 100644 index 5b9937ca..00000000 --- a/packages/fhir/test/resources/subscriptions.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Subscription } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirSubscription, - type untypedSubscriptionSchema, -} from '../../src/index.js' - -describe('Subscription Resource', () => { - it('should validate FHIR subscription from subscriptions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/subscriptions.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirSubscription.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/substanceDefinitions.json b/packages/fhir/test/resources/substanceDefinitions.json deleted file mode 100644 index 7c625f47..00000000 --- a/packages/fhir/test/resources/substanceDefinitions.json +++ /dev/null @@ -1,153 +0,0 @@ -{ - "0": { - "resourceType": "SubstanceDefinition", - "id": "substance-example-0", - "name": [ - { - "name": "Aspirin" - } - ] - }, - "1": { - "resourceType": "SubstanceDefinition", - "id": "substance-example-1", - "identifier": [ - { - "system": "http://example.org/substances", - "value": "SUB-12345" - } - ], - "status": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/substance-status", - "code": "active", - "display": "Active" - } - ] - }, - "name": [ - { - "name": "Acetylsalicylic acid", - "type": { - "coding": [ - { - "code": "scientific", - "display": "Scientific Name" - } - ] - }, - "preferred": true - } - ], - "structure": { - "molecularFormula": "C9H8O4" - } - }, - "2": { - "resourceType": "SubstanceDefinition", - "id": "substance-example-2", - "identifier": [ - { - "system": "http://example.org/substances", - "value": "SUB-67890" - } - ], - "description": "An anticoagulant substance", - "name": [ - { - "name": "Warfarin", - "preferred": true - }, - { - "name": "Coumadin", - "type": { - "coding": [ - { - "code": "brand", - "display": "Brand Name" - } - ] - } - } - ], - "classification": [ - { - "coding": [ - { - "system": "http://www.whocc.no/atc", - "code": "B01AA03", - "display": "warfarin" - } - ] - } - ], - "property": [ - { - "type": { - "coding": [ - { - "code": "molecular-weight", - "display": "Molecular Weight" - } - ] - }, - "valueCodeableConcept": { - "coding": [ - { - "code": "308.33", - "display": "308.33 g/mol" - } - ] - } - } - ] - }, - "3": { - "resourceType": "SubstanceDefinition", - "id": "substance-example-3", - "identifier": [ - { - "system": "http://example.org/substances", - "value": "SUB-54321" - } - ], - "version": "1.0", - "status": { - "coding": [ - { - "code": "active" - } - ] - }, - "name": [ - { - "name": "Insulin Human", - "type": { - "coding": [ - { - "code": "INN", - "display": "International Nonproprietary Name" - } - ] - }, - "preferred": true - } - ], - "manufacturer": [ - { - "reference": "Organization/pharma-manufacturer" - } - ], - "sourceMaterial": { - "type": { - "coding": [ - { - "code": "human", - "display": "Human derived" - } - ] - } - } - } -} diff --git a/packages/fhir/test/resources/substanceDefinitions.json.license b/packages/fhir/test/resources/substanceDefinitions.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/substanceDefinitions.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/substanceDefinitions.test.ts b/packages/fhir/test/resources/substanceDefinitions.test.ts deleted file mode 100644 index ca526ac6..00000000 --- a/packages/fhir/test/resources/substanceDefinitions.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type SubstanceDefinition } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirSubstanceDefinition, - type untypedSubstanceDefinitionSchema, -} from '../../src/index.js' - -describe('SubstanceDefinition Resource', () => { - it('should validate FHIR substance definition from substanceDefinitions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/substanceDefinitions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirSubstanceDefinition.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/substances.json b/packages/fhir/test/resources/substances.json deleted file mode 100644 index 1d1f90ef..00000000 --- a/packages/fhir/test/resources/substances.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "0": { - "resourceType": "Substance", - "id": "substance-example-0", - "status": "active", - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "387207008", - "display": "Caffeine" - } - ] - } - }, - "1": { - "resourceType": "Substance", - "id": "substance-example-1", - "identifier": [ - { - "system": "http://example.org/substances", - "value": "SUB-001" - } - ], - "status": "active", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/substance-category", - "code": "drug", - "display": "Drug or Medicament" - } - ] - } - ], - "code": { - "coding": [ - { - "system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "161", - "display": "Acetaminophen" - } - ] - }, - "description": "White crystalline powder" - }, - "2": { - "resourceType": "Substance", - "id": "substance-example-2", - "status": "inactive", - "code": { - "text": "Discontinued substance" - } - }, - "3": { - "resourceType": "Substance", - "id": "substance-example-3", - "status": "active", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/substance-category", - "code": "allergen", - "display": "Allergen" - } - ] - } - ], - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "227493005", - "display": "Cashew nut" - } - ] - }, - "description": "Tree nut allergen", - "ingredient": [ - { - "quantity": { - "numerator": { - "value": 100, - "unit": "%" - }, - "denominator": { - "value": 1, - "unit": "%" - } - }, - "substanceCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "227493005", - "display": "Cashew nut" - } - ] - } - } - ] - } -} diff --git a/packages/fhir/test/resources/substances.json.license b/packages/fhir/test/resources/substances.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/substances.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/substances.test.ts b/packages/fhir/test/resources/substances.test.ts deleted file mode 100644 index ca03dc85..00000000 --- a/packages/fhir/test/resources/substances.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Substance } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirSubstance, type untypedSubstanceSchema } from '../../src/index.js' - -describe('Substance Resource', () => { - it('should validate FHIR substance from substances.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/substances.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirSubstance.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/supplyDeliveries.json b/packages/fhir/test/resources/supplyDeliveries.json deleted file mode 100644 index 5400d47c..00000000 --- a/packages/fhir/test/resources/supplyDeliveries.json +++ /dev/null @@ -1,186 +0,0 @@ -{ - "0": { - "resourceType": "SupplyDelivery", - "id": "supplydelivery-example-0", - "status": "completed", - "patient": { - "reference": "Patient/example", - "display": "John Smith" - }, - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/supply-item-type", - "code": "medication", - "display": "Medication" - } - ] - }, - "suppliedItem": { - "quantity": { - "value": 10, - "unit": "vials" - }, - "itemCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "325072002", - "display": "Insulin preparation" - } - ] - } - }, - "occurrenceDateTime": "2023-01-20T14:30:00Z", - "supplier": { - "reference": "Organization/pharmacy" - }, - "destination": { - "reference": "Location/diabetes-clinic" - } - }, - "1": { - "resourceType": "SupplyDelivery", - "id": "supplydelivery-example-1", - "identifier": [ - { - "system": "http://hospital.example.org/supplydelivery", - "value": "SD-2023-001" - } - ], - "basedOn": [ - { - "reference": "SupplyRequest/SR-2023-001" - } - ], - "status": "in-progress", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/supply-item-type", - "code": "device", - "display": "Device" - } - ] - }, - "suppliedItem": { - "quantity": { - "value": 3, - "unit": "units" - }, - "itemReference": { - "reference": "Device/oxygen-cylinder-e" - } - }, - "occurrencePeriod": { - "start": "2023-02-10T08:00:00Z", - "end": "2023-02-10T16:00:00Z" - }, - "supplier": { - "reference": "Organization/medical-supply-vendor" - }, - "destination": { - "reference": "Location/icu-3" - }, - "receiver": [ - { - "reference": "Practitioner/respiratory-therapist-1", - "display": "Dr. Alice Johnson" - } - ] - }, - "2": { - "resourceType": "SupplyDelivery", - "id": "supplydelivery-example-2", - "status": "completed", - "patient": { - "reference": "Patient/patient-123" - }, - "suppliedItem": { - "quantity": { - "value": 100, - "unit": "masks", - "system": "http://unitsofmeasure.org", - "code": "{masks}" - }, - "itemCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "412307009", - "display": "Surgical mask" - } - ] - } - }, - "occurrenceDateTime": "2023-03-15T10:00:00Z", - "destination": { - "reference": "Location/ward-1" - }, - "receiver": [ - { - "reference": "Practitioner/nurse-1" - } - ] - }, - "3": { - "resourceType": "SupplyDelivery", - "id": "supplydelivery-example-3", - "identifier": [ - { - "use": "official", - "system": "http://hospital.example.org/supplydelivery", - "value": "SD-2023-099" - } - ], - "basedOn": [ - { - "reference": "SupplyRequest/SR-2023-042" - } - ], - "partOf": [ - { - "reference": "SupplyDelivery/parent-delivery" - } - ], - "status": "entered-in-error", - "patient": { - "reference": "Patient/patient-456" - }, - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/supply-item-type", - "code": "central", - "display": "Central Supply" - } - ] - }, - "suppliedItem": { - "quantity": { - "value": 50, - "unit": "units" - }, - "itemCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "19830006", - "display": "Bandage" - } - ], - "text": "Sterile gauze bandages" - } - }, - "occurrencePeriod": { - "start": "2023-04-01T08:00:00Z", - "end": "2023-04-01T12:00:00Z" - }, - "supplier": { - "reference": "Organization/central-supply" - }, - "destination": { - "reference": "Location/emergency-room" - } - } -} diff --git a/packages/fhir/test/resources/supplyDeliveries.json.license b/packages/fhir/test/resources/supplyDeliveries.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/supplyDeliveries.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/supplyDeliveries.test.ts b/packages/fhir/test/resources/supplyDeliveries.test.ts deleted file mode 100644 index 5e4d56fd..00000000 --- a/packages/fhir/test/resources/supplyDeliveries.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type SupplyDelivery } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirSupplyDelivery, - type untypedSupplyDeliverySchema, -} from '../../src/index.js' - -describe('SupplyDelivery Resource', () => { - it('should validate FHIR supply delivery from supplyDeliveries.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/supplyDeliveries.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirSupplyDelivery.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/supplyRequests.json b/packages/fhir/test/resources/supplyRequests.json deleted file mode 100644 index f5348feb..00000000 --- a/packages/fhir/test/resources/supplyRequests.json +++ /dev/null @@ -1,228 +0,0 @@ -{ - "0": { - "resourceType": "SupplyRequest", - "id": "supplyrequest-example-0", - "status": "active", - "category": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/supply-kind", - "code": "central", - "display": "Central Supply" - } - ] - }, - "priority": "routine", - "itemCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "412307009", - "display": "Surgical mask" - } - ] - }, - "quantity": { - "value": 100, - "unit": "masks", - "system": "http://unitsofmeasure.org", - "code": "{masks}" - }, - "occurrenceDateTime": "2023-01-15T10:00:00Z", - "authoredOn": "2023-01-10T14:30:00Z", - "requester": { - "reference": "Practitioner/example", - "display": "Dr. Jane Smith" - }, - "deliverTo": { - "reference": "Location/ward-1", - "display": "Ward 1" - } - }, - "1": { - "resourceType": "SupplyRequest", - "id": "supplyrequest-example-1", - "identifier": [ - { - "system": "http://hospital.example.org/supplyrequest", - "value": "SR-2023-001" - } - ], - "status": "completed", - "category": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/supply-kind", - "code": "nonstock", - "display": "Non-Stock" - } - ] - }, - "priority": "urgent", - "itemReference": { - "reference": "Medication/example-insulin" - }, - "quantity": { - "value": 50, - "unit": "vials", - "system": "http://unitsofmeasure.org", - "code": "{vials}" - }, - "parameter": [ - { - "code": { - "coding": [ - { - "system": "http://example.org/supply-parameter", - "code": "temperature", - "display": "Storage Temperature" - } - ] - }, - "valueRange": { - "low": { - "value": 2, - "unit": "degrees Celsius", - "system": "http://unitsofmeasure.org", - "code": "Cel" - }, - "high": { - "value": 8, - "unit": "degrees Celsius", - "system": "http://unitsofmeasure.org", - "code": "Cel" - } - } - } - ], - "occurrencePeriod": { - "start": "2023-02-01T08:00:00Z", - "end": "2023-02-03T17:00:00Z" - }, - "authoredOn": "2023-01-28T09:00:00Z", - "requester": { - "reference": "Practitioner/pharmacist-1" - }, - "supplier": [ - { - "reference": "Organization/pharmacy-dept" - } - ], - "reasonCode": [ - { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "44054006", - "display": "Diabetes mellitus type 2" - } - ] - } - ], - "deliverFrom": { - "reference": "Location/central-pharmacy" - }, - "deliverTo": { - "reference": "Location/diabetes-clinic" - } - }, - "2": { - "resourceType": "SupplyRequest", - "id": "supplyrequest-example-2", - "status": "draft", - "priority": "asap", - "itemCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "19830006", - "display": "Bandage" - } - ], - "text": "Sterile gauze bandages" - }, - "quantity": { - "value": 200, - "unit": "rolls" - }, - "occurrenceDateTime": "2023-03-20T12:00:00Z", - "requester": { - "reference": "Practitioner/nurse-1" - } - }, - "3": { - "resourceType": "SupplyRequest", - "id": "supplyrequest-example-3", - "identifier": [ - { - "use": "official", - "system": "http://hospital.example.org/supplyrequest", - "value": "SR-2023-042" - } - ], - "status": "suspended", - "category": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/supply-kind", - "code": "central" - } - ] - }, - "priority": "stat", - "itemCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "336623009", - "display": "Oxygen cylinder" - } - ] - }, - "quantity": { - "value": 3, - "unit": "cylinders", - "system": "http://unitsofmeasure.org", - "code": "{cylinders}" - }, - "parameter": [ - { - "code": { - "coding": [ - { - "system": "http://example.org/supply-parameter", - "code": "size", - "display": "Cylinder Size" - } - ] - }, - "valueCodeableConcept": { - "coding": [ - { - "system": "http://example.org/cylinder-size", - "code": "E", - "display": "Size E" - } - ] - } - } - ], - "authoredOn": "2023-04-05T16:45:00Z", - "requester": { - "reference": "Practitioner/respiratory-therapist-1" - }, - "supplier": [ - { - "reference": "Organization/medical-supply-vendor" - } - ], - "reasonReference": [ - { - "reference": "Condition/respiratory-failure" - } - ], - "deliverTo": { - "reference": "Location/icu-3" - } - } -} diff --git a/packages/fhir/test/resources/supplyRequests.json.license b/packages/fhir/test/resources/supplyRequests.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/supplyRequests.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/supplyRequests.test.ts b/packages/fhir/test/resources/supplyRequests.test.ts deleted file mode 100644 index 31af2ba7..00000000 --- a/packages/fhir/test/resources/supplyRequests.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type SupplyRequest } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirSupplyRequest, - type untypedSupplyRequestSchema, -} from '../../src/index.js' - -describe('SupplyRequest Resource', () => { - it('should validate FHIR supply request from supplyRequests.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/supplyRequests.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirSupplyRequest.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/tasks.json b/packages/fhir/test/resources/tasks.json deleted file mode 100644 index 74e18a47..00000000 --- a/packages/fhir/test/resources/tasks.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "0": { - "resourceType": "Task", - "id": "task-example-0", - "status": "requested", - "intent": "order", - "code": { - "text": "Review lab results" - } - }, - "1": { - "resourceType": "Task", - "id": "task-example-1", - "identifier": [ - { - "system": "http://example.org/tasks", - "value": "TASK-123" - } - ], - "status": "in-progress", - "statusReason": { - "text": "Awaiting additional information" - }, - "intent": "order", - "priority": "routine", - "code": { - "coding": [ - { - "system": "http://hl7.org/fhir/CodeSystem/task-code", - "code": "approve", - "display": "Activate/approve the focal resource" - } - ] - }, - "description": "Approve medication request", - "for": { - "reference": "Patient/patient-example" - }, - "authoredOn": "2024-11-15T10:00:00Z", - "requester": { - "reference": "Practitioner/practitioner-example" - }, - "owner": { - "reference": "Practitioner/practitioner-reviewer" - }, - "reasonCode": { - "text": "High risk medication requires approval" - } - }, - "2": { - "resourceType": "Task", - "id": "task-example-2", - "status": "cancelled", - "intent": "order", - "code": { - "text": "Schedule follow-up" - }, - "for": { - "reference": "Patient/patient-example-2" - }, - "note": [ - { - "text": "Patient requested cancellation" - } - ] - }, - "3": { - "resourceType": "Task", - "id": "task-example-3", - "status": "completed", - "intent": "order", - "priority": "urgent", - "code": { - "coding": [ - { - "system": "http://hl7.org/fhir/CodeSystem/task-code", - "code": "fulfill", - "display": "Fulfill the focal request" - } - ] - }, - "focus": { - "reference": "ServiceRequest/lab-order-456" - }, - "for": { - "reference": "Patient/patient-urgent" - }, - "executionPeriod": { - "start": "2024-11-01T08:00:00Z", - "end": "2024-11-01T10:30:00Z" - }, - "authoredOn": "2024-11-01T07:30:00Z", - "lastModified": "2024-11-01T10:35:00Z", - "requester": { - "reference": "Practitioner/practitioner-ordering" - }, - "owner": { - "reference": "Organization/lab-department" - }, - "output": [ - { - "type": { - "text": "Lab result" - }, - "valueReference": { - "reference": "DiagnosticReport/report-456" - } - } - ] - } -} diff --git a/packages/fhir/test/resources/tasks.json.license b/packages/fhir/test/resources/tasks.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/tasks.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/tasks.test.ts b/packages/fhir/test/resources/tasks.test.ts deleted file mode 100644 index df53d3d2..00000000 --- a/packages/fhir/test/resources/tasks.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type Task } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirTask, type untypedTaskSchema } from '../../src/index.js' - -describe('Task Resource', () => { - it('should validate FHIR task from tasks.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/tasks.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirTask.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/terminologyCapabilities.json b/packages/fhir/test/resources/terminologyCapabilities.json deleted file mode 100644 index 29c918cc..00000000 --- a/packages/fhir/test/resources/terminologyCapabilities.json +++ /dev/null @@ -1,267 +0,0 @@ -{ - "0": { - "resourceType": "TerminologyCapabilities", - "id": "example-terminologycapabilities-0", - "url": "http://example.org/fhir/TerminologyCapabilities/example-0", - "version": "1.0.0", - "name": "ExampleTerminologyCapabilities0", - "title": "Example Terminology Capabilities 0", - "status": "active", - "experimental": true, - "date": "2023-01-15T10:00:00Z", - "publisher": "Example Health Organization", - "contact": [ - { - "name": "Terminology Services Team", - "telecom": [ - { - "system": "email", - "value": "terminology@example.org" - } - ] - } - ], - "description": "An example terminology capabilities statement", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "purpose": "Describes the terminology service capabilities", - "copyright": "Copyright 2023 Example Health Organization", - "kind": "instance", - "software": { - "name": "Example Terminology Server", - "version": "2.5.0" - }, - "implementation": { - "description": "Example terminology service implementation", - "url": "http://example.org/fhir/terminology" - }, - "lockedDate": true, - "codeSystem": [ - { - "uri": "http://snomed.info/sct", - "subsumption": true - }, - { - "uri": "http://loinc.org", - "version": [ - { - "code": "2.73", - "isDefault": true, - "compositional": false, - "language": ["en", "es"] - } - ] - } - ], - "expansion": { - "hierarchical": true, - "paging": true, - "incomplete": false, - "parameter": [ - { - "name": "includeDesignations", - "documentation": "Controls whether concept designations are included" - } - ] - }, - "codeSearch": "explicit", - "validateCode": { - "translations": true - } - }, - "1": { - "resourceType": "TerminologyCapabilities", - "id": "example-terminologycapabilities-1", - "url": "http://example.org/fhir/TerminologyCapabilities/example-1", - "version": "2.0.0", - "name": "ExampleTerminologyCapabilities1", - "title": "Example Terminology Capabilities 1 with Filters", - "status": "draft", - "experimental": false, - "date": "2023-03-20T14:30:00Z", - "publisher": "Medical Standards Institute", - "description": "A terminology capabilities statement with filter support", - "kind": "capability", - "codeSystem": [ - { - "uri": "http://example.org/fhir/CodeSystem/conditions", - "version": [ - { - "code": "1.0.0", - "isDefault": true, - "compositional": true, - "language": ["en"], - "filter": [ - { - "code": "concept", - "op": ["is-a", "descendent-of"] - }, - { - "code": "property", - "op": ["=", "in"] - } - ], - "property": ["status", "effectiveDate"] - } - ], - "subsumption": true - } - ], - "expansion": { - "hierarchical": false, - "paging": false, - "textFilter": "Supports text search in displays" - }, - "codeSearch": "all", - "validateCode": { - "translations": false - }, - "translation": { - "needsMap": true - } - }, - "2": { - "resourceType": "TerminologyCapabilities", - "id": "example-terminologycapabilities-2", - "url": "http://example.org/fhir/TerminologyCapabilities/example-2", - "version": "1.5.0", - "name": "ExampleTerminologyCapabilities2", - "title": "Example Terminology Capabilities 2 - Multi-System", - "status": "active", - "date": "2023-06-10T09:00:00Z", - "publisher": "Terminology Services Ltd", - "description": "A capabilities statement describing support for multiple code systems", - "useContext": [ - { - "code": { - "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", - "code": "workflow" - }, - "valueCodeableConcept": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", - "code": "CLINNOTEE", - "display": "clinical note entry" - } - ] - } - } - ], - "kind": "instance", - "software": { - "name": "MultiTerm Server", - "version": "3.2.1" - }, - "implementation": { - "description": "Production terminology service", - "url": "https://terminology.example.org/fhir" - }, - "lockedDate": false, - "codeSystem": [ - { - "uri": "http://snomed.info/sct", - "version": [ - { - "code": "20230301", - "isDefault": true, - "compositional": true, - "language": ["en", "es", "fr"], - "filter": [ - { - "code": "concept", - "op": ["is-a", "descendent-of", "generalizes"] - } - ] - } - ], - "subsumption": true - }, - { - "uri": "http://loinc.org", - "version": [ - { - "code": "2.73", - "isDefault": true, - "language": ["en"] - } - ], - "subsumption": false - }, - { - "uri": "http://www.nlm.nih.gov/research/umls/rxnorm", - "subsumption": false - } - ], - "expansion": { - "hierarchical": true, - "paging": true, - "incomplete": true, - "parameter": [ - { - "name": "activeOnly", - "documentation": "Only include active concepts" - }, - { - "name": "displayLanguage", - "documentation": "Preferred language for displays" - } - ], - "textFilter": "Full text search on concept display and synonyms" - }, - "validateCode": { - "translations": true - }, - "translation": { - "needsMap": false - }, - "closure": { - "translation": true - } - }, - "3": { - "resourceType": "TerminologyCapabilities", - "id": "example-terminologycapabilities-3", - "url": "http://example.org/fhir/TerminologyCapabilities/example-3", - "version": "3.0.0", - "name": "ExampleTerminologyCapabilities3", - "title": "Example Terminology Capabilities 3 - Requirements", - "status": "retired", - "experimental": true, - "date": "2022-12-15T16:45:00Z", - "publisher": "Legacy Terminology Provider", - "description": "A retired capabilities statement describing requirements", - "kind": "requirements", - "lockedDate": false, - "codeSystem": [ - { - "uri": "http://example.org/fhir/CodeSystem/legacy", - "version": [ - { - "code": "1.0.0", - "isDefault": true, - "compositional": false, - "language": ["en"], - "property": ["deprecated", "effectiveDate", "parent"] - } - ] - } - ], - "expansion": { - "hierarchical": false, - "paging": false - }, - "validateCode": { - "translations": false - } - } -} diff --git a/packages/fhir/test/resources/terminologyCapabilities.test.ts b/packages/fhir/test/resources/terminologyCapabilities.test.ts deleted file mode 100644 index cde891cc..00000000 --- a/packages/fhir/test/resources/terminologyCapabilities.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type TerminologyCapabilities } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirTerminologyCapabilities, - type untypedTerminologyCapabilitiesSchema, -} from '../../src/index.js' - -describe('TerminologyCapabilities Resource', () => { - it('should validate FHIR TerminologyCapabilities from terminologyCapabilities.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/terminologyCapabilities.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirTerminologyCapabilities.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/testHelpers.ts b/packages/fhir/test/resources/testHelpers.ts deleted file mode 100644 index 847ead6a..00000000 --- a/packages/fhir/test/resources/testHelpers.ts +++ /dev/null @@ -1,30 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -/** - * Stringify an object with deterministically sorted keys - * @param object The object to stringify - * @returns JSON string with sorted keys - */ -export const jsonStringifyDeterministically = (object: unknown): string => - JSON.stringify( - object, - (_, value: unknown) => { - if (value && typeof value === 'object' && !Array.isArray(value)) { - const record = value as Record - return Object.keys(record) - .sort() - .reduce>((sorted, key) => { - sorted[key] = record[key] - return sorted - }, {}) - } - return value - }, - 2, - ) diff --git a/packages/fhir/test/resources/testReports.json b/packages/fhir/test/resources/testReports.json deleted file mode 100644 index 7272b5d4..00000000 --- a/packages/fhir/test/resources/testReports.json +++ /dev/null @@ -1,145 +0,0 @@ -{ - "0": { - "resourceType": "TestReport", - "id": "example", - "status": "completed", - "testScript": { - "reference": "TestScript/example" - }, - "result": "pass", - "score": 100.0, - "tester": "HL7 Execution Engine" - }, - "1": { - "resourceType": "TestReport", - "id": "failed-test", - "identifier": { - "system": "urn:ietf:rfc:3986", - "value": "urn:uuid:12345678-1234-1234-1234-123456789abc" - }, - "name": "PatientSearchTest", - "status": "completed", - "testScript": { - "reference": "TestScript/patient-search" - }, - "result": "fail", - "score": 75.5, - "tester": "Test System v1.0", - "issued": "2023-06-15T14:30:00Z", - "participant": [ - { - "type": "test-engine", - "uri": "http://example.org/test-engine", - "display": "FHIR Test Engine" - }, - { - "type": "server", - "uri": "http://example.org/fhir", - "display": "Example FHIR Server" - } - ] - }, - "2": { - "resourceType": "TestReport", - "id": "setup-test", - "status": "completed", - "testScript": { - "reference": "TestScript/setup-example" - }, - "result": "pass", - "score": 100.0, - "setup": { - "action": [ - { - "operation": { - "result": "pass", - "message": "Successfully created test patient" - } - }, - { - "assert": { - "result": "pass", - "message": "Patient created with correct ID" - } - } - ] - }, - "test": [ - { - "name": "Read Patient Test", - "action": [ - { - "operation": { - "result": "pass" - } - } - ] - } - ], - "teardown": { - "action": [ - { - "operation": { - "result": "pass", - "message": "Successfully deleted test patient" - } - } - ] - } - }, - "3": { - "resourceType": "TestReport", - "id": "complex-test", - "status": "in-progress", - "testScript": { - "reference": "TestScript/comprehensive-test" - }, - "result": "pending", - "tester": "Automated Test Suite", - "issued": "2023-06-15T10:00:00Z", - "participant": [ - { - "type": "test-engine", - "uri": "http://example.org/test-engine", - "display": "Test Engine" - }, - { - "type": "client", - "uri": "http://example.org/test-client", - "display": "Test Client" - }, - { - "type": "server", - "uri": "http://example.org/fhir-server", - "display": "FHIR Server Under Test" - } - ], - "test": [ - { - "name": "Patient CRUD Operations", - "description": "Test create, read, update, delete operations", - "action": [ - { - "operation": { - "result": "pass", - "message": "Create operation successful" - } - }, - { - "assert": { - "result": "pass", - "message": "Patient ID matches expected value" - } - }, - { - "operation": { - "result": "fail", - "message": "Update operation failed", - "detail": "http://example.org/fhir/OperationOutcome/12345" - } - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/testReports.json.license b/packages/fhir/test/resources/testReports.json.license deleted file mode 100644 index 53d8663c..00000000 --- a/packages/fhir/test/resources/testReports.json.license +++ /dev/null @@ -1,6 +0,0 @@ - -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/testReports.test.ts b/packages/fhir/test/resources/testReports.test.ts deleted file mode 100644 index 1e100827..00000000 --- a/packages/fhir/test/resources/testReports.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type TestReport } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirTestReport, - type untypedTestReportSchema, -} from '../../src/index.js' - -describe('TestReport Resource', () => { - it('should validate FHIR test report from testReports.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/testReports.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirTestReport.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/testScripts.json b/packages/fhir/test/resources/testScripts.json deleted file mode 100644 index f3c4f33b..00000000 --- a/packages/fhir/test/resources/testScripts.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "0": { - "resourceType": "TestScript", - "url": "http://example.org/fhir/TestScript/example", - "name": "ExampleTestScript", - "status": "draft" - }, - "1": { - "resourceType": "TestScript", - "url": "http://example.org/fhir/TestScript/patient-read", - "name": "PatientReadTest", - "title": "Patient Read Test Script", - "status": "active", - "date": "2023-01-15T10:00:00Z", - "publisher": "Example Organization", - "description": "Test script for reading patient resources" - }, - "2": { - "resourceType": "TestScript", - "url": "http://example.org/fhir/TestScript/comprehensive", - "version": "1.0.0", - "name": "ComprehensiveTest", - "title": "Comprehensive FHIR Test Suite", - "status": "active", - "experimental": false, - "date": "2023-06-01T14:30:00Z", - "publisher": "Healthcare Standards Body", - "contact": [ - { - "name": "Testing Team", - "telecom": [ - { - "system": "email", - "value": "testing@example.org" - } - ] - } - ], - "description": "Comprehensive test suite for FHIR server", - "purpose": "Validate FHIR server implementation" - }, - "3": { - "resourceType": "TestScript", - "url": "http://example.org/fhir/TestScript/medication-test", - "version": "2.0.0", - "name": "MedicationTest", - "title": "Medication Management Test", - "status": "active", - "date": "2023-09-20T08:00:00Z", - "publisher": "Pharmacy Systems", - "description": "Test medication resource operations", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "copyright": "Copyright 2023 Pharmacy Systems" - } -} diff --git a/packages/fhir/test/resources/testScripts.json.license b/packages/fhir/test/resources/testScripts.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/testScripts.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/testScripts.test.ts b/packages/fhir/test/resources/testScripts.test.ts deleted file mode 100644 index 5181e5d6..00000000 --- a/packages/fhir/test/resources/testScripts.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type TestScript } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirTestScript, - type untypedTestScriptSchema, -} from '../../src/index.js' - -describe('TestScript Resource', () => { - it('should validate FHIR test script from testScripts.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/testScripts.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirTestScript.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/valueSets.json b/packages/fhir/test/resources/valueSets.json deleted file mode 100644 index c40c51ad..00000000 --- a/packages/fhir/test/resources/valueSets.json +++ /dev/null @@ -1,285 +0,0 @@ -{ - "0": { - "resourceType": "ValueSet", - "id": "example-valueset-0", - "url": "http://example.org/fhir/ValueSet/example-0", - "identifier": [ - { - "system": "urn:ietf:rfc:3986", - "value": "urn:oid:2.16.840.1.113883.4.642.3.0" - } - ], - "version": "1.0.0", - "name": "ExampleValueSet0", - "title": "Example Value Set 0", - "status": "active", - "experimental": true, - "date": "2023-01-15T10:00:00Z", - "publisher": "Example Health Organization", - "contact": [ - { - "name": "Terminology Team", - "telecom": [ - { - "system": "email", - "value": "terminology@example.org" - } - ] - } - ], - "description": "An example value set with specific concepts", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "purpose": "Demonstrates a simple value set with enumerated concepts", - "copyright": "Copyright 2023 Example Health Organization", - "compose": { - "include": [ - { - "system": "http://example.org/fhir/CodeSystem/conditions", - "version": "1.0.0", - "concept": [ - { - "code": "diabetes", - "display": "Diabetes Mellitus" - }, - { - "code": "hypertension", - "display": "Hypertension", - "designation": [ - { - "language": "es", - "value": "Hipertensión" - } - ] - }, - { - "code": "asthma", - "display": "Asthma" - } - ] - } - ] - } - }, - "1": { - "resourceType": "ValueSet", - "id": "example-valueset-1", - "url": "http://example.org/fhir/ValueSet/example-1", - "version": "2.0.0", - "name": "ExampleValueSet1", - "title": "Example Value Set 1 with Filters", - "status": "draft", - "experimental": false, - "date": "2023-03-20T14:30:00Z", - "publisher": "Medical Standards Institute", - "description": "A value set using filters to select concepts", - "immutable": false, - "compose": { - "lockedDate": "2023-03-01", - "inactive": false, - "include": [ - { - "system": "http://snomed.info/sct", - "filter": [ - { - "property": "concept", - "op": "is-a", - "value": "64572001" - } - ] - }, - { - "system": "http://example.org/fhir/CodeSystem/local-codes", - "concept": [ - { - "code": "local-1", - "display": "Local Code 1" - } - ] - } - ], - "exclude": [ - { - "system": "http://snomed.info/sct", - "concept": [ - { - "code": "12345678", - "display": "Excluded Concept" - } - ] - } - ] - } - }, - "2": { - "resourceType": "ValueSet", - "id": "example-valueset-2", - "url": "http://example.org/fhir/ValueSet/example-2", - "version": "1.5.0", - "name": "ExampleValueSet2", - "title": "Example Value Set 2 with Expansion", - "status": "active", - "date": "2023-06-10T09:00:00Z", - "publisher": "Terminology Services Ltd", - "description": "A value set with expansion included", - "useContext": [ - { - "code": { - "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", - "code": "focus" - }, - "valueCodeableConcept": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "408475000", - "display": "Diabetic" - } - ] - } - } - ], - "compose": { - "include": [ - { - "system": "http://example.org/fhir/CodeSystem/medications", - "concept": [ - { - "code": "metformin", - "display": "Metformin" - }, - { - "code": "insulin", - "display": "Insulin" - } - ] - } - ] - }, - "expansion": { - "identifier": "urn:uuid:550e8400-e29b-41d4-a716-446655440000", - "timestamp": "2023-06-10T09:00:00Z", - "total": 2, - "offset": 0, - "parameter": [ - { - "name": "version", - "valueString": "1.5.0" - } - ], - "contains": [ - { - "system": "http://example.org/fhir/CodeSystem/medications", - "version": "1.0.0", - "code": "metformin", - "display": "Metformin", - "designation": [ - { - "language": "es", - "value": "Metformina" - } - ] - }, - { - "system": "http://example.org/fhir/CodeSystem/medications", - "version": "1.0.0", - "code": "insulin", - "display": "Insulin" - } - ] - } - }, - "3": { - "resourceType": "ValueSet", - "id": "example-valueset-3", - "url": "http://example.org/fhir/ValueSet/example-3", - "version": "3.0.0", - "name": "ExampleValueSet3", - "title": "Example Value Set 3 - Complex Hierarchy", - "status": "retired", - "experimental": true, - "date": "2022-12-15T16:45:00Z", - "publisher": "Legacy Terminology Provider", - "description": "A retired value set with hierarchical expansion", - "immutable": true, - "compose": { - "include": [ - { - "system": "http://example.org/fhir/CodeSystem/hierarchy", - "valueSet": [ - "http://example.org/fhir/ValueSet/parent-set-1", - "http://example.org/fhir/ValueSet/parent-set-2" - ] - } - ] - }, - "expansion": { - "timestamp": "2022-12-15T16:45:00Z", - "total": 4, - "parameter": [ - { - "name": "includeDesignations", - "valueBoolean": true - }, - { - "name": "limitedExpansion", - "valueBoolean": false - } - ], - "contains": [ - { - "system": "http://example.org/fhir/CodeSystem/hierarchy", - "abstract": false, - "inactive": false, - "code": "parent-1", - "display": "Parent Concept 1", - "contains": [ - { - "system": "http://example.org/fhir/CodeSystem/hierarchy", - "code": "child-1-1", - "display": "Child Concept 1-1" - }, - { - "system": "http://example.org/fhir/CodeSystem/hierarchy", - "code": "child-1-2", - "display": "Child Concept 1-2" - } - ] - }, - { - "system": "http://example.org/fhir/CodeSystem/hierarchy", - "abstract": false, - "code": "parent-2", - "display": "Parent Concept 2", - "contains": [ - { - "system": "http://example.org/fhir/CodeSystem/hierarchy", - "code": "child-2-1", - "display": "Child Concept 2-1", - "designation": [ - { - "language": "fr", - "use": { - "system": "http://snomed.info/sct", - "code": "900000000000013009", - "display": "Synonym" - }, - "value": "Concept Enfant 2-1" - } - ] - } - ] - } - ] - } - } -} diff --git a/packages/fhir/test/resources/valueSets.test.ts b/packages/fhir/test/resources/valueSets.test.ts deleted file mode 100644 index 5903369f..00000000 --- a/packages/fhir/test/resources/valueSets.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type ValueSet } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { FhirValueSet, type untypedValueSetSchema } from '../../src/index.js' - -describe('ValueSet Resource', () => { - it('should validate FHIR ValueSets from valueSets.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync('test/resources/valueSets.json', 'utf-8') - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirValueSet.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/verificationResults.json b/packages/fhir/test/resources/verificationResults.json deleted file mode 100644 index 1755511c..00000000 --- a/packages/fhir/test/resources/verificationResults.json +++ /dev/null @@ -1,293 +0,0 @@ -{ - "0": { - "resourceType": "VerificationResult", - "id": "verification-result-example-0", - "status": "validated", - "statusDate": "2023-01-15T10:30:00Z", - "validationType": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/validation-type", - "code": "primary" - } - ] - }, - "target": [ - { - "reference": "Practitioner/example" - } - ], - "primarySource": [ - { - "who": { - "reference": "Organization/medical-board" - }, - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/primary-source-type", - "code": "lic-board" - } - ] - } - ], - "validationStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/validation-status", - "code": "successful" - } - ] - }, - "validationDate": "2023-01-15T10:30:00Z" - } - ] - }, - "1": { - "resourceType": "VerificationResult", - "id": "verification-result-example-1", - "status": "attested", - "statusDate": "2023-02-20T14:00:00Z", - "validationType": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/validation-type", - "code": "multiple" - } - ] - }, - "validationProcess": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/validation-process", - "code": "primary" - } - ] - } - ], - "lastPerformed": "2023-02-20T14:00:00Z", - "nextScheduled": "2024-02-20", - "target": [ - { - "reference": "Practitioner/practitioner-1" - } - ], - "targetLocation": ["Practitioner.qualification[0]"], - "need": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/need", - "code": "periodic" - } - ] - }, - "primarySource": [ - { - "who": { - "reference": "Organization/university" - }, - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/primary-source-type", - "code": "prim-ed" - } - ] - } - ], - "communicationMethod": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/verificationresult-communication-method", - "code": "manual" - } - ] - } - ], - "validationStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/validation-status", - "code": "successful" - } - ] - }, - "validationDate": "2023-02-20T14:00:00Z" - } - ], - "attestation": { - "who": { - "reference": "Practitioner/practitioner-1" - }, - "communicationMethod": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/verificationresult-communication-method", - "code": "manual" - } - ] - }, - "date": "2023-02-20" - } - }, - "2": { - "resourceType": "VerificationResult", - "id": "verification-result-example-2", - "status": "req-revalid", - "statusDate": "2023-03-10T09:15:00Z", - "validationType": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/validation-type", - "code": "primary" - } - ] - }, - "target": [ - { - "reference": "Organization/clinic" - } - ], - "need": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/need", - "code": "periodic" - } - ] - }, - "lastPerformed": "2022-03-10T09:00:00Z", - "nextScheduled": "2023-03-10", - "failureAction": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/failure-action", - "code": "warning" - } - ] - }, - "primarySource": [ - { - "who": { - "reference": "Organization/accreditation-body" - }, - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/primary-source-type", - "code": "auth-source" - } - ] - } - ], - "validationStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/validation-status", - "code": "req-revalid" - } - ] - }, - "validationDate": "2022-03-10T09:00:00Z", - "canPushUpdates": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/can-push-updates", - "code": "yes" - } - ] - }, - "pushTypeAvailable": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/push-type-available", - "code": "specific" - } - ] - } - ] - } - ] - }, - "3": { - "resourceType": "VerificationResult", - "id": "verification-result-example-3", - "status": "validated", - "statusDate": "2023-04-05T16:45:00Z", - "validationType": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/validation-type", - "code": "multiple" - } - ] - }, - "validationProcess": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/validation-process", - "code": "multi" - } - ] - } - ], - "target": [ - { - "reference": "Medication/example" - } - ], - "targetLocation": ["Medication.ingredient"], - "lastPerformed": "2023-04-05T16:45:00Z", - "validator": [ - { - "organization": { - "reference": "Organization/fda" - }, - "identityCertificate": "MIICdTCCAd4..." - } - ], - "primarySource": [ - { - "who": { - "reference": "Organization/manufacturer" - }, - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/primary-source-type", - "code": "issuing-source" - } - ] - } - ], - "communicationMethod": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/verificationresult-communication-method", - "code": "pull" - } - ] - } - ], - "validationStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/validation-status", - "code": "successful" - } - ] - }, - "validationDate": "2023-04-05T16:45:00Z" - } - ] - } -} diff --git a/packages/fhir/test/resources/verificationResults.json.license b/packages/fhir/test/resources/verificationResults.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/verificationResults.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/verificationResults.test.ts b/packages/fhir/test/resources/verificationResults.test.ts deleted file mode 100644 index 4ac81ca9..00000000 --- a/packages/fhir/test/resources/verificationResults.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type VerificationResult } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirVerificationResult, - type untypedVerificationResultSchema, -} from '../../src/index.js' - -describe('VerificationResult Resource', () => { - it('should validate FHIR VerificationResults from verificationResults.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/verificationResults.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirVerificationResult.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/resources/visionPrescriptions.json b/packages/fhir/test/resources/visionPrescriptions.json deleted file mode 100644 index b2c741ff..00000000 --- a/packages/fhir/test/resources/visionPrescriptions.json +++ /dev/null @@ -1,145 +0,0 @@ -{ - "0": { - "resourceType": "VisionPrescription", - "id": "visionprescription-example-0", - "status": "active", - "created": "2024-01-15T10:00:00Z", - "patient": { - "reference": "Patient/patient-example" - }, - "dateWritten": "2024-01-15T10:00:00Z", - "prescriber": { - "reference": "Practitioner/practitioner-example" - }, - "lensSpecification": [ - { - "product": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/ex-visionprescriptionproduct", - "code": "lens" - } - ] - }, - "eye": "right", - "sphere": -2.0, - "cylinder": -0.5, - "axis": 180, - "add": 2.0 - }, - { - "product": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/ex-visionprescriptionproduct", - "code": "lens" - } - ] - }, - "eye": "left", - "sphere": -2.25, - "cylinder": -0.75, - "axis": 175, - "add": 2.0 - } - ] - }, - "1": { - "resourceType": "VisionPrescription", - "id": "visionprescription-example-1", - "status": "draft", - "created": "2024-02-01T14:30:00Z", - "patient": { - "reference": "Patient/patient-example-2" - }, - "dateWritten": "2024-02-01T14:30:00Z", - "prescriber": { - "reference": "Practitioner/practitioner-example-2" - }, - "lensSpecification": [ - { - "product": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/ex-visionprescriptionproduct", - "code": "contact" - } - ] - }, - "eye": "right", - "sphere": -3.5, - "backCurve": 8.7, - "diameter": 14.0, - "duration": { - "value": 30, - "unit": "days", - "system": "http://unitsofmeasure.org", - "code": "d" - } - } - ] - }, - "2": { - "resourceType": "VisionPrescription", - "id": "visionprescription-example-2", - "status": "cancelled", - "created": "2024-03-01T09:00:00Z", - "patient": { - "reference": "Patient/patient-example-3" - }, - "dateWritten": "2024-03-01T09:00:00Z", - "prescriber": { - "reference": "Practitioner/practitioner-example" - }, - "encounter": { - "reference": "Encounter/encounter-example" - }, - "lensSpecification": [ - { - "product": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/ex-visionprescriptionproduct", - "code": "lens" - } - ] - }, - "eye": "right", - "sphere": -1.5 - } - ] - }, - "3": { - "resourceType": "VisionPrescription", - "id": "visionprescription-example-3", - "status": "entered-in-error", - "created": "2024-04-01T11:00:00Z", - "patient": { - "reference": "Patient/patient-example-4" - }, - "dateWritten": "2024-04-01T11:00:00Z", - "prescriber": { - "reference": "Practitioner/practitioner-example-3" - }, - "lensSpecification": [ - { - "product": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/ex-visionprescriptionproduct", - "code": "lens" - } - ] - }, - "eye": "left", - "power": -2.5, - "prism": [ - { - "amount": 0.5, - "base": "out" - } - ] - } - ] - } -} diff --git a/packages/fhir/test/resources/visionPrescriptions.json.license b/packages/fhir/test/resources/visionPrescriptions.json.license deleted file mode 100644 index 2a87de45..00000000 --- a/packages/fhir/test/resources/visionPrescriptions.json.license +++ /dev/null @@ -1,5 +0,0 @@ -This source file is part of the Grove open-source project - -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/resources/visionPrescriptions.test.ts b/packages/fhir/test/resources/visionPrescriptions.test.ts deleted file mode 100644 index 9e4c7f72..00000000 --- a/packages/fhir/test/resources/visionPrescriptions.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import fs from 'fs' -import { expectTypeOf } from 'expect-type' -import { type VisionPrescription } from 'fhir/r4b.js' -import { type z } from 'zod' -import { jsonStringifyDeterministically } from './testHelpers.js' -import { - FhirVisionPrescription, - type untypedVisionPrescriptionSchema, -} from '../../src/index.js' - -describe('VisionPrescription Resource', () => { - it('should validate FHIR VisionPrescriptions from visionPrescriptions.json', () => { - type Schema = z.infer - expectTypeOf().toExtend() - expectTypeOf().toExtend() - - const data = fs.readFileSync( - 'test/resources/visionPrescriptions.json', - 'utf-8', - ) - const decodedJson = JSON.parse(data) - - Object.values(decodedJson).forEach((jsonValue: unknown) => { - const parsedResource = FhirVisionPrescription.parse(jsonValue).value - expect(jsonStringifyDeterministically(jsonValue)).toBe( - jsonStringifyDeterministically(parsedResource), - ) - }) - }) -}) diff --git a/packages/fhir/test/tsconfig.json b/packages/fhir/test/tsconfig.json index 95795ed6..446ee81c 100644 --- a/packages/fhir/test/tsconfig.json +++ b/packages/fhir/test/tsconfig.json @@ -5,9 +5,6 @@ "moduleResolution": "NodeNext", "noEmit": true, "types": ["jest", "node"], - "paths": { - "@schmiedmayerlab/grove-firebase-utils": ["../../firebase-utils/src"] - }, "rootDir": "../" }, "include": ["./**/*.ts", "../src/**/*.ts"], diff --git a/packages/fhir/test/tsconfig.json.license b/packages/fhir/test/tsconfig.json.license index 53d8663c..fa4e8d92 100644 --- a/packages/fhir/test/tsconfig.json.license +++ b/packages/fhir/test/tsconfig.json.license @@ -1,6 +1,3 @@ - -This source file is part of the Grove open-source project - SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/valueSets/valueSets.test.ts b/packages/fhir/test/valueSets/valueSets.test.ts deleted file mode 100644 index 0a3ac68c..00000000 --- a/packages/fhir/test/valueSets/valueSets.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { expectTypeOf } from 'expect-type' -import { - type CapabilityStatement, - type DataRequirement, - type ExampleScenarioInstance, - type ImplementationGuide, - type TestScriptSetupActionOperation, - type TimingRepeat, -} from 'fhir/r4b.js' -import { type z } from 'zod' -import { - type CodeSearchSupport, - codeSearchSupportSchema, - eventTimingSchema, - fhirDefinedTypeSchema, - fhirLicenseSchema, - fhirResourceTypeSchema, - fhirTypeSchema, - fhirVersionSchema, -} from '../../src/index.js' - -describe('Value set exports', () => { - it('exports terminology capability search support from the public entry point', () => { - expect(codeSearchSupportSchema.options).toEqual(['explicit', 'all']) - expectTypeOf().toEqualTypeOf<'explicit' | 'all'>() - }) - - it('matches the required FHIR R4B bindings', () => { - expectTypeOf>().toEqualTypeOf< - ExampleScenarioInstance['resourceType'] - >() - expectTypeOf>().toEqualTypeOf< - DataRequirement['type'] - >() - expectTypeOf>().toEqualTypeOf< - NonNullable - >() - expectTypeOf>().toEqualTypeOf< - CapabilityStatement['fhirVersion'] - >() - expectTypeOf>().toEqualTypeOf< - NonNullable - >() - expectTypeOf>().toEqualTypeOf< - NonNullable[number] - >() - }) - - it('rejects values outside required FHIR R4B bindings', () => { - expect(fhirResourceTypeSchema.safeParse('UnknownResource').success).toBe( - false, - ) - expect(fhirTypeSchema.safeParse('UnknownType').success).toBe(false) - expect(fhirDefinedTypeSchema.safeParse('Type').success).toBe(false) - expect(fhirVersionSchema.safeParse('9.9.9').success).toBe(false) - expect(fhirLicenseSchema.safeParse('Unknown-License').success).toBe(false) - expect(eventTimingSchema.safeParse('UNKNOWN').success).toBe(false) - }) -}) diff --git a/packages/fhir/test/zod-fixtures.test.ts b/packages/fhir/test/zod-fixtures.test.ts new file mode 100644 index 00000000..d9bc60a2 --- /dev/null +++ b/packages/fhir/test/zod-fixtures.test.ts @@ -0,0 +1,53 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { readFileSync, readdirSync } from 'node:fs' +import { join } from 'node:path' +import * as r4 from '../src/zod/r4/index.js' + +const DIRECTORY = join(process.cwd(), 'fixtures/conformance/resources') + +/** The schema for a resource type, when the generated surface covers it. */ +const schemaFor = ( + resourceType: string, +): { parse: (value: unknown) => unknown } | undefined => { + const name = `${resourceType.charAt(0).toLowerCase()}${resourceType.slice(1)}Schema` + const exports = r4 as unknown as Record + const schema = exports[name] + return schema === undefined ? undefined : ( + (schema as { parse: (value: unknown) => unknown }) + ) +} + +describe('generated schemas against the published conformance fixtures', () => { + const files = readdirSync(DIRECTORY).filter((file) => file.endsWith('.json')) + + it('has fixtures to check', () => { + expect(files.length).toBeGreaterThan(0) + }) + + it.each(files)('accepts %s', (file) => { + const bundle = JSON.parse(readFileSync(join(DIRECTORY, file), 'utf8')) as { + resourceType: string + entry?: Array<{ resource: unknown }> + } + const resources = + bundle.resourceType === 'Bundle' ? + (bundle.entry ?? []).map( + (entry: { resource: unknown }) => entry.resource, + ) + : [bundle] + for (const resource of resources) { + const schema = schemaFor( + (resource as { resourceType: string }).resourceType, + ) + if (!schema) continue + expect(() => schema.parse(resource)).not.toThrow() + } + }) +}) diff --git a/packages/fhir/test/zod-generation-invariants.test.ts b/packages/fhir/test/zod-generation-invariants.test.ts new file mode 100644 index 00000000..8f599b4e --- /dev/null +++ b/packages/fhir/test/zod-generation-invariants.test.ts @@ -0,0 +1,567 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { readFileSync } from 'node:fs' +import { join } from 'node:path' + +const RELEASES = ['r4', 'r4b'] as const + +/** The invariants each pinned release states that the generator does not check. */ +const UNCHECKED_KEYS: Readonly> = { + r4: [ + 'adf-0', + 'age-1', + 'ait-1', + 'ait-2', + 'app-1', + 'app-2', + 'app-3', + 'app-4', + 'apr-1', + 'att-1', + 'bdl-1', + 'bdl-10', + 'bdl-11', + 'bdl-12', + 'bdl-2', + 'bdl-3', + 'bdl-4', + 'bdl-5', + 'bdl-7', + 'bdl-8', + 'bdl-9', + 'ces-1', + 'cid-0', + 'cmd-0', + 'cmd-1', + 'cmd-2', + 'cmd-3', + 'cmp-1', + 'cmp-2', + 'cnt-3', + 'con-1', + 'con-2', + 'con-3', + 'con-4', + 'con-5', + 'cpb-0', + 'cpb-1', + 'cpb-12', + 'cpb-14', + 'cpb-15', + 'cpb-16', + 'cpb-2', + 'cpb-3', + 'cpb-7', + 'cpb-9', + 'cpd-0', + 'cpl-3', + 'cpt-2', + 'csd-0', + 'csd-1', + 'ctm-1', + 'dis-1', + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'drq-1', + 'drq-2', + 'drt-1', + 'ees-0', + 'eld-1', + 'eld-11', + 'eld-12', + 'eld-13', + 'eld-14', + 'eld-15', + 'eld-16', + 'eld-17', + 'eld-18', + 'eld-19', + 'eld-2', + 'eld-20', + 'eld-21', + 'eld-22', + 'eld-3', + 'eld-4', + 'eld-5', + 'eld-6', + 'eld-7', + 'eld-8', + 'esc-0', + 'evd-0', + 'evi-0', + 'evv-0', + 'exp-1', + 'fhs-1', + 'fhs-2', + 'gdf-0', + 'gol-1', + 'grp-1', + 'ig-0', + 'ig-1', + 'ig-2', + 'imm-1', + 'imr-1', + 'inv-1', + 'inv-2', + 'ipn-1', + 'lib-0', + 'lnk-1', + 'lst-1', + 'lst-2', + 'lst-3', + 'mad-1', + 'md-1', + 'mdd-1', + 'mea-0', + 'mea-1', + 'mrp-1', + 'mrp-2', + 'msd-0', + 'msq-3', + 'msq-5', + 'msq-6', + 'nor-1', + 'nsd-0', + 'nsd-1', + 'nsd-2', + 'obs-7', + 'opd-0', + 'opd-1', + 'opd-2', + 'opd-3', + 'org-1', + 'org-2', + 'org-3', + 'pat-1', + 'pdf-0', + 'ppc-1', + 'ppc-2', + 'ppc-3', + 'ppc-4', + 'ppc-5', + 'prr-1', + 'qrs-1', + 'qty-3', + 'que-0', + 'que-1', + 'que-10', + 'que-11', + 'que-12', + 'que-13', + 'que-2', + 'que-3', + 'que-4', + 'que-5', + 'que-6', + 'que-7', + 'que-8', + 'que-9', + 'ras-1', + 'ras-2', + 'rat-1', + 'red-0', + 'ref-1', + 'rng-2', + 'rqg-1', + 'rsd-0', + 'rvs-0', + 'sdf-0', + 'sdf-1', + 'sdf-10', + 'sdf-11', + 'sdf-14', + 'sdf-15', + 'sdf-16', + 'sdf-17', + 'sdf-18', + 'sdf-19', + 'sdf-2', + 'sdf-20', + 'sdf-21', + 'sdf-22', + 'sdf-23', + 'sdf-3', + 'sdf-4', + 'sdf-5', + 'sdf-6', + 'sdf-8', + 'sdf-9', + 'sev-1', + 'smp-0', + 'smp-1', + 'smp-2', + 'spd-0', + 'spd-1', + 'spd-2', + 'tcp-0', + 'tcp-1', + 'tcp-2', + 'tcp-3', + 'tcp-4', + 'tcp-5', + 'tim-1', + 'tim-10', + 'tim-2', + 'tim-4', + 'tim-5', + 'tim-6', + 'tim-7', + 'tim-8', + 'tim-9', + 'trd-1', + 'trd-2', + 'trd-3', + 'tst-0', + 'tst-1', + 'tst-10', + 'tst-11', + 'tst-12', + 'tst-13', + 'tst-2', + 'tst-3', + 'tst-4', + 'tst-5', + 'tst-6', + 'tst-7', + 'tst-8', + 'tst-9', + 'txt-1', + 'txt-2', + 'vsd-0', + 'vsd-1', + 'vsd-10', + 'vsd-2', + 'vsd-3', + 'vsd-6', + 'vsd-9', + ], + r4b: [ + 'age-1', + 'ait-1', + 'ait-2', + 'apd-1', + 'app-1', + 'app-2', + 'app-3', + 'app-4', + 'apr-1', + 'att-1', + 'bdl-1', + 'bdl-10', + 'bdl-11', + 'bdl-12', + 'bdl-2', + 'bdl-3', + 'bdl-4', + 'bdl-5', + 'bdl-7', + 'bdl-8', + 'bdl-9', + 'ces-1', + 'cid-0', + 'cmd-0', + 'cmd-1', + 'cmd-2', + 'cmd-3', + 'cmp-1', + 'cmp-2', + 'cnl-0', + 'cnt-3', + 'con-1', + 'con-2', + 'con-3', + 'con-4', + 'con-5', + 'cpb-0', + 'cpb-1', + 'cpb-12', + 'cpb-14', + 'cpb-15', + 'cpb-16', + 'cpb-2', + 'cpb-3', + 'cpb-7', + 'cpb-9', + 'cpd-0', + 'cpl-3', + 'cpt-2', + 'csd-0', + 'csd-1', + 'ctm-1', + 'cud-1', + 'dis-1', + 'dom-2', + 'dom-3', + 'dom-4', + 'dom-5', + 'dom-6', + 'drq-1', + 'drq-2', + 'drt-1', + 'eld-1', + 'eld-11', + 'eld-12', + 'eld-13', + 'eld-14', + 'eld-15', + 'eld-16', + 'eld-17', + 'eld-18', + 'eld-19', + 'eld-2', + 'eld-20', + 'eld-21', + 'eld-22', + 'eld-3', + 'eld-4', + 'eld-5', + 'eld-6', + 'eld-7', + 'eld-8', + 'esc-0', + 'evd-0', + 'exp-1', + 'fhs-1', + 'fhs-2', + 'gdf-0', + 'gol-1', + 'grp-1', + 'ig-0', + 'ig-1', + 'ig-2', + 'imm-1', + 'imr-1', + 'ing-1', + 'inv-1', + 'inv-2', + 'ipn-1', + 'lnk-1', + 'lst-1', + 'lst-2', + 'lst-3', + 'mad-1', + 'md-1', + 'mdd-1', + 'mea-1', + 'mrp-1', + 'mrp-2', + 'msd-0', + 'msq-3', + 'msq-5', + 'msq-6', + 'nor-1', + 'nsd-0', + 'nsd-1', + 'nsd-2', + 'obs-7', + 'opd-0', + 'opd-1', + 'opd-2', + 'opd-3', + 'org-1', + 'org-2', + 'org-3', + 'pat-1', + 'ppc-1', + 'ppc-2', + 'ppc-3', + 'ppc-4', + 'ppc-5', + 'prr-1', + 'qrs-1', + 'qty-3', + 'que-0', + 'que-10', + 'que-11', + 'que-12', + 'que-13', + 'que-2', + 'que-3', + 'que-4', + 'que-5', + 'que-6', + 'que-7', + 'que-8', + 'que-9', + 'ras-1', + 'ras-2', + 'rat-1', + 'red-0', + 'ref-1', + 'rng-2', + 'rqg-1', + 'rsd-0', + 'sdf-0', + 'sdf-1', + 'sdf-10', + 'sdf-11', + 'sdf-14', + 'sdf-15', + 'sdf-16', + 'sdf-17', + 'sdf-18', + 'sdf-19', + 'sdf-2', + 'sdf-20', + 'sdf-21', + 'sdf-22', + 'sdf-23', + 'sdf-24', + 'sdf-25', + 'sdf-3', + 'sdf-4', + 'sdf-5', + 'sdf-6', + 'sdf-8', + 'sdf-9', + 'sev-1', + 'smp-0', + 'smp-1', + 'smp-2', + 'spd-0', + 'spd-1', + 'spd-2', + 'sst-1', + 'tcp-0', + 'tcp-1', + 'tcp-2', + 'tcp-3', + 'tcp-4', + 'tcp-5', + 'tim-1', + 'tim-10', + 'tim-2', + 'tim-4', + 'tim-5', + 'tim-6', + 'tim-7', + 'tim-8', + 'tim-9', + 'trd-1', + 'trd-2', + 'trd-3', + 'tst-0', + 'tst-1', + 'tst-10', + 'tst-11', + 'tst-12', + 'tst-13', + 'tst-2', + 'tst-3', + 'tst-4', + 'tst-5', + 'tst-6', + 'tst-7', + 'tst-8', + 'tst-9', + 'txt-1', + 'txt-2', + 'vsd-0', + 'vsd-1', + 'vsd-10', + 'vsd-2', + 'vsd-3', + 'vsd-6', + 'vsd-9', + ], +} + +const source = (release: string): string => + readFileSync(join(process.cwd(), 'src/zod', release, 'schemas.ts'), 'utf8') + +describe.each(RELEASES)('generated %s schemas', (release) => { + const text = source(release) + + it('leaves no element unvalidated', () => { + // An escape to z.unknown() means an element lost its constraint; every one of these was a + // real gap when the generator was written. + expect(text).not.toContain('z.unknown()') + }) + + it('states a type for every exported schema', () => { + // Without the annotation the schema is only cast to the release's type, never checked + // against it, and a mismatch reaches consumers as a silent `any`. + const untyped = [...text.matchAll(/^export const (\w+Schema)(?!: )/gm)].map( + (m) => m[1], + ) + expect(untyped).toEqual([]) + }) + + it('rejects properties the release does not define', () => { + // FHIR JSON is closed; a plain object would strip an unmodelled property silently. + expect(text).not.toMatch(/z\.object\(/) + expect(text).toContain('z.strictObject(') + }) + + it('keeps the extensions a primitive may carry', () => { + // Every primitive-valued element has a `_`-prefixed sibling for its id and extensions. + expect((text.match(/(?:^|\s)_[A-Za-z]\w*:/g) ?? []).length).toBeGreaterThan( + 300, + ) + }) + + it('closes every required binding it can enumerate', () => { + expect((text.match(/z\.enum\(/g) ?? []).length).toBeGreaterThan(30) + }) + + it('constrains the primitives the release gives a pattern', () => { + expect((text.match(/new RegExp\(/g) ?? []).length).toBeGreaterThan(10) + }) + + it('enforces the invariants the release states', () => { + for (const message of [ + 'ext-1', + 'obs-6', + 'Period.start must not be later', + ]) { + expect(text).toContain(message) + } + expect(text).toContain( + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + ) + }) + + it('publishes the invariants it does not check', () => { + // A validator that silently ignores an invariant overstates itself. The map makes the gap + // inspectable, and this assertion makes a new release's new invariants fail the build + // rather than disappear. + expect(text).toContain('export const UNCHECKED_CONSTRAINTS') + const map = text.slice(text.indexOf('UNCHECKED_CONSTRAINTS')) + const keys = new Set( + [...(map.match(/'[a-z]+-\d+'/g) ?? [])].map((key) => key.slice(1, -1)), + ) + // Pinned, not bounded: these are facts about a pinned release, so a release that states a + // new invariant must fail here rather than silently widening the gap. + expect([...keys].sort((a, b) => a.localeCompare(b))).toEqual( + UNCHECKED_KEYS[release], + ) + // Anything implemented must not also be reported as unchecked. + for (const implemented of ['ext-1', 'per-1', 'obs-3', 'obs-6']) { + expect(keys.has(implemented)).toBe(false) + } + }) + + it('is generated, and says so', () => { + expect(text).toContain('GENERATED FILE') + expect(text).toMatch(/Generated from hl7\.fhir\.\w+\.core#/) + }) +}) + +describe('release coverage', () => { + it('covers both releases from one generator', () => { + // R4B is a minimal delta over R4 for this surface; if that stops being true the generator is + // producing something release-specific that deserves a look. + const r4 = source('r4').split('\n').length + const r4b = source('r4b').split('\n').length + expect(Math.abs(r4b - r4)).toBeLessThan(r4 * 0.1) + }) +}) diff --git a/packages/fhir/test/zod-official-examples.test.ts b/packages/fhir/test/zod-official-examples.test.ts new file mode 100644 index 00000000..e94e0443 --- /dev/null +++ b/packages/fhir/test/zod-official-examples.test.ts @@ -0,0 +1,144 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { readFileSync, readdirSync } from 'node:fs' +import { join } from 'node:path' +import { + EXAMPLE_PACKAGES, + fhirPackage, +} from '../scripts/generate-zod-schemas.mjs' +import * as r4 from '../src/zod/r4/index.js' +import * as r4b from '../src/zod/r4b/index.js' + +/** + * Examples the release publishes that do not satisfy the release's own definitions. + * + * Each entry states why, and is a claim about the corpus rather than a licence to weaken a + * schema. The list is asserted to stay small so it cannot quietly become a place to put + * failures whose cause has not been established. + */ +// Defects in the published corpora themselves, keyed by the defect and listing the files it +// spans. Keyed by cause rather than by file so the cap below counts distinct specification +// defects, not the number of files each happens to touch; `*` matches any run of characters. +const NONCONFORMANT_EXAMPLES: Readonly> = { + // Questionnaire.item.linkId is 1..1, and these nested items omit it. + 'nested items omit the required linkId': ['Questionnaire-qs1.json'], + // SearchParameter.base is 1..*, and the generated extension stubs carry none. + 'extension stubs omit the required base': [ + 'SearchParameter-*-extensions-*.json', + ], + // ImplementationGuide.name and .status are both 1..1 and this descriptor states neither. + 'the core IG descriptor omits name and status': [ + 'ImplementationGuide-fhir.json', + 'ig-r4*.json', + ], + // status is 1..1 on both CodeSystem and ValueSet; the catalogType pair states neither, and + // the value-set bundle carries that same pair. + 'the catalogType terminology omits the required status': [ + '*catalogType.json', + 'Bundle-valuesets.json', + ], +} + +const matches = (pattern: string, file: string): boolean => + pattern.includes('*') ? + new RegExp( + `^${pattern + .split('*') + .map((part) => part.replaceAll(/[.*+?^${}()|[\]\\]/gu, String.raw`\$&`)) + .join('.*')}$`, + 'u', + ).test(file) + : pattern === file + +const isNonconformant = (file: string): boolean => + Object.values(NONCONFORMANT_EXAMPLES).some((patterns) => + patterns.some((pattern) => matches(pattern, file)), + ) + +const MODULES: { readonly r4: unknown; readonly r4b: unknown } = { r4, r4b } + +interface Schema { + safeParse: (value: unknown) => { success: boolean } +} + +/** + * Validate each release against its own published examples. + * + * A corpus this project did not author is the only check that the generated schemas describe + * FHIR rather than describing the fixtures. Every schema defect found in review — an abstract + * `Resource` stamped with a literal `resourceType`, a dropped `id` on backbones, a required + * array satisfied by `[]` — was invisible to the project's own fixtures and immediately visible + * here. + */ +// The count travels with the release: it is the lowest number of resources each corpus is known +// to cover, so a naming change that stopped resolving most schemas fails rather than passing. +describe.each([ + ['r4', 130], + ['r4b', 138], +] as const)('the %s published examples', (release, expectedCoverage) => { + let directory: string + const schemas = MODULES[release] as Record + + beforeAll(async () => { + directory = await fhirPackage(EXAMPLE_PACKAGES[release]) + }, 180_000) + + const schemaFor = (resourceType: string): Schema | undefined => { + const name = `${resourceType.charAt(0).toLowerCase()}${resourceType.slice(1)}Schema` + const schema = schemas[name] + return schema === undefined ? undefined : (schema as Schema) + } + + /** A resource and everything nested inside it that carries its own resourceType. */ + const nested = (resource: unknown): Array<{ resourceType: string }> => { + if (typeof resource !== 'object' || resource === null) return [] + const value = resource as { + resourceType?: string + entry?: Array<{ resource?: unknown }> + contained?: unknown[] + } + const found = + value.resourceType === undefined ? + [] + : [value as { resourceType: string }] + for (const entry of value.entry ?? []) found.push(...nested(entry.resource)) + for (const contained of value.contained ?? []) + found.push(...nested(contained)) + return found + } + + it('accepts every example of a resource type the package models', () => { + const failures: string[] = [] + let checked = 0 + for (const file of readdirSync(directory)) { + if (!file.endsWith('.json')) continue + let resource: unknown + try { + resource = JSON.parse(readFileSync(join(directory, file), 'utf8')) + } catch { + continue + } + // Bundle entries and contained resources count: fix 1 made those slots open, so a defect + // inside one would otherwise never be exercised. + for (const candidate of nested(resource)) { + const schema = schemaFor(candidate.resourceType) + if (schema === undefined) continue + checked += 1 + if (schema.safeParse(candidate).success) continue + if (isNonconformant(file)) continue + failures.push(`${file} (${candidate.resourceType})`) + } + } + expect(failures).toEqual([]) + // Pinned, not a floor: a naming change that stopped resolving most schemas would otherwise + // leave this green while checking almost nothing. + expect(checked).toBeGreaterThanOrEqual(expectedCoverage) + expect(Object.keys(NONCONFORMANT_EXAMPLES).length).toBeLessThan(5) + }, 180_000) +}) diff --git a/packages/fhir/test/zod-schemas.test.ts b/packages/fhir/test/zod-schemas.test.ts new file mode 100644 index 00000000..8cd0816b --- /dev/null +++ b/packages/fhir/test/zod-schemas.test.ts @@ -0,0 +1,318 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + bundleSchema, + medicationAdministrationSchema, + medicationStatementSchema, + observationSchema, + documentReferenceSchema, + patientSchema, + positiveIntSchema, + resourceSchema, + unsignedIntSchema, + visionPrescriptionSchema, +} from '../src/zod/r4/index.js' +import { + bundleSchema as r4bBundle, + observationSchema as r4bObservation, +} from '../src/zod/r4b/index.js' + +const observation = { + resourceType: 'Observation', + id: 'example', + status: 'final', + code: { + coding: [ + // LOINC's canonical FHIR system URI is http by definition: it identifies a code system + // and is never fetched. + // eslint-disable-next-line sonarjs/no-clear-text-protocols + { system: 'http://loinc.org', code: '8867-4', display: 'Heart rate' }, + ], + }, + subject: { reference: 'Patient/example' }, + effectiveDateTime: '2026-08-20T17:31:01Z', + valueQuantity: { + value: 72, + unit: '/min', + system: 'http://unitsofmeasure.org', + code: '/min', + }, +} + +describe('generated Zod schemas', () => { + it('accepts a well-formed Observation', () => { + expect(observationSchema.parse(observation)).toBeTruthy() + }) + + it('rejects a status outside the required binding', () => { + // status binds required to ObservationStatus; the enum comes from the release's own ValueSet. + expect(() => + observationSchema.parse({ ...observation, status: 'complete' }), + ).toThrow() + }) + + it('rejects a malformed dateTime', () => { + // The regex is the one the release publishes for the dateTime primitive. + expect(() => + observationSchema.parse({ + ...observation, + effectiveDateTime: '20-08-2026', + }), + ).toThrow() + }) + + it('rejects a missing required element', () => { + const withoutStatus: Record = { ...observation } + delete withoutStatus.status + expect(() => observationSchema.parse(withoutStatus)).toThrow() + }) + + it('rejects the wrong resourceType', () => { + expect(() => + observationSchema.parse({ ...observation, resourceType: 'Patient' }), + ).toThrow() + }) + + it('validates a recursive backbone to its full depth', () => { + // Questionnaire.item.item is a contentReference back to Questionnaire.item; a nested item + // with a bad linkId must still be rejected rather than passing as an open record. + const document = { + resourceType: 'DocumentReference', + status: 'current', + content: [{ attachment: { contentType: 'text/csv' } }], + } + expect(documentReferenceSchema.parse(document)).toBeTruthy() + expect(() => + documentReferenceSchema.parse({ ...document, status: 'draft' }), + ).toThrow() + }) + + it('keeps the extensions a primitive carries', () => { + // FHIR puts an id or extension on a primitive in a `_`-prefixed sibling. Dropping it would + // lose data that a conforming producer deliberately sent. + const withExtension = { + ...observation, + _effectiveDateTime: { + extension: [ + { url: 'http://example.org/precision', valueString: 'second' }, + ], + }, + } + const parsed = observationSchema.parse(withExtension) as unknown as Record< + string, + unknown + > + expect(parsed._effectiveDateTime).toBeDefined() + }) + + it('rejects a property the specification does not define', () => { + // FHIR JSON is closed: an unmodelled property is an error, not an extension. Stripping it + // silently would let a misspelled element pass validation. + expect(() => + observationSchema.parse({ ...observation, statuss: 'final' }), + ).toThrow() + }) + + it('bounds the integer primitives the specification bounds', () => { + // positiveInt and unsignedInt state their range as a lexical pattern rather than a min. + expect(() => positiveIntSchema.parse(0)).toThrow() + expect(positiveIntSchema.parse(1)).toBe(1) + expect(() => unsignedIntSchema.parse(-1)).toThrow() + expect(unsignedIntSchema.parse(0)).toBe(0) + }) + + it('parses the same instance under R4B', () => { + // R4B is a minimal delta over R4 for this surface; an instance valid in one is valid in both. + expect(r4bObservation.parse(observation)).toBeTruthy() + }) +}) + +/** A Bundle entry carrying whatever the test wants to put in a polymorphic resource slot. */ +const bundleCarrying = (resource: unknown): unknown => ({ + resourceType: 'Bundle', + type: 'collection', + entry: [{ resource }], +}) + +describe('polymorphic resource slots', () => { + it('rejects a malformed resource inside a Bundle entry', () => { + // The slot admits any resource, which is not the same as admitting any object: an + // Observation nested here is the same Observation the package validates anywhere else. + const result = bundleSchema.safeParse( + bundleCarrying({ ...observation, status: 'complete' }), + ) + expect(result.success).toBe(false) + }) + + it('names the element the fault is in, not the entry that carries it', () => { + const result = bundleSchema.safeParse( + bundleCarrying({ ...observation, status: 'complete' }), + ) + expect(result.success).toBe(false) + expect(result.error?.issues[0]?.path).toEqual([ + 'entry', + 0, + 'resource', + 'status', + ]) + }) + + it('accepts a well-formed resource inside a Bundle entry', () => { + expect(bundleSchema.parse(bundleCarrying(observation))).toBeTruthy() + }) + + it('rejects a malformed contained resource', () => { + const result = observationSchema.safeParse({ + ...observation, + contained: [{ resourceType: 'Patient', gender: 'undisclosed' }], + }) + expect(result.success).toBe(false) + expect(result.error?.issues[0]?.path).toEqual(['contained', 0, 'gender']) + }) + + it('rejects a malformed entry response outcome', () => { + const result = bundleSchema.safeParse({ + resourceType: 'Bundle', + type: 'transaction-response', + entry: [ + { + response: { + status: '201 Created', + outcome: { ...observation, status: 'complete' }, + }, + }, + ], + }) + expect(result.success).toBe(false) + }) + + it('rejects a resource slot holding something that is not a resource', () => { + expect(bundleSchema.safeParse(bundleCarrying(42)).success).toBe(false) + expect(bundleSchema.safeParse(bundleCarrying({ id: 'x' })).success).toBe( + false, + ) + }) + + it('checks a resource type the release publishes', () => { + // Every resource R4 defines is modelled, so a CarePlan is checked rather than waved through: + // this one is missing the required intent and subject. + expect( + bundleSchema.safeParse( + bundleCarrying({ resourceType: 'CarePlan', status: 'draft' }), + ).success, + ).toBe(false) + }) + + it('leaves a resource type the release does not publish open', () => { + // A type outside the release is the only remaining unmodelled case, and rejecting it would + // refuse a bundle over a gap in this package rather than a fault in the data. + expect( + bundleSchema.safeParse( + bundleCarrying({ resourceType: 'SomethingNotInR4', status: 'draft' }), + ).success, + ).toBe(true) + }) + + it('validates a Bundle nested inside a Bundle', () => { + const nested = bundleCarrying( + bundleCarrying({ ...observation, status: 'complete' }), + ) + expect(bundleSchema.safeParse(nested).success).toBe(false) + expect( + bundleSchema.safeParse(bundleCarrying(bundleCarrying(observation))) + .success, + ).toBe(true) + }) + + it('terminates on a resource that contains itself', () => { + // JSON cannot express this, but an in-memory object handed to safeParse can, and the + // dispatch would otherwise recur until the stack ran out. + const cyclic: Record = { + resourceType: 'Bundle', + type: 'collection', + entry: [] as unknown[], + } + ;(cyclic.entry as unknown[]).push({ resource: cyclic }) + expect(() => bundleSchema.safeParse(cyclic)).not.toThrow() + }) + + it('dispatches the same way from the exported resource schema', () => { + expect(resourceSchema.safeParse(observation).success).toBe(true) + expect( + resourceSchema.safeParse({ ...observation, status: 'complete' }).success, + ).toBe(false) + }) + + it('dispatches under R4B too', () => { + expect( + r4bBundle.safeParse( + bundleCarrying({ ...observation, status: 'complete' }), + ).success, + ).toBe(false) + expect(r4bBundle.parse(bundleCarrying(observation))).toBeTruthy() + }) +}) + +describe('the resources the guides emit', () => { + const patient = { resourceType: 'Patient', id: 'example' } + const medicationAdministration = { + resourceType: 'MedicationAdministration', + status: 'completed', + medicationCodeableConcept: { text: 'Aspirin 81 mg' }, + subject: { reference: 'Patient/example' }, + effectiveDateTime: '2026-08-20T12:00:00Z', + } + const medicationStatement = { + resourceType: 'MedicationStatement', + status: 'active', + medicationCodeableConcept: { text: 'Aspirin 81 mg' }, + subject: { reference: 'Patient/example' }, + } + const visionPrescription = { + resourceType: 'VisionPrescription', + status: 'active', + created: '2026-08-20T12:00:00Z', + patient: { reference: 'Patient/example' }, + dateWritten: '2026-08-20T12:00:00Z', + prescriber: { reference: 'Practitioner/example' }, + lensSpecification: [{ product: { text: 'lens' }, eye: 'right' }], + } + + it.each([ + ['Patient', patientSchema, patient, { gender: 'undisclosed' }], + [ + 'MedicationAdministration', + medicationAdministrationSchema, + medicationAdministration, + { status: 'finished' }, + ], + [ + 'MedicationStatement', + medicationStatementSchema, + medicationStatement, + { status: 'ongoing' }, + ], + [ + 'VisionPrescription', + visionPrescriptionSchema, + visionPrescription, + { status: 'live' }, + ], + ])( + 'validates %s and the slots that carry it', + (_name, schema, valid, fault) => { + expect(schema.safeParse(valid).success).toBe(true) + expect(schema.safeParse({ ...valid, ...fault }).success).toBe(false) + expect(bundleSchema.safeParse(bundleCarrying(valid)).success).toBe(true) + expect( + bundleSchema.safeParse(bundleCarrying({ ...valid, ...fault })).success, + ).toBe(false) + }, + ) +}) diff --git a/packages/fhir/tsconfig.json b/packages/fhir/tsconfig.json index c512814c..afd17fb4 100644 --- a/packages/fhir/tsconfig.json +++ b/packages/fhir/tsconfig.json @@ -4,17 +4,17 @@ "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", - "esModuleInterop": true, "declaration": true, + "declarationMap": true, "rootDir": "src", "outDir": "dist", + "isolatedDeclarations": true, "isolatedModules": true, "sourceMap": true, - "resolveJsonModule": true, - "types": ["node"], - "paths": { - "@schmiedmayerlab/grove-firebase-utils": ["../firebase-utils/src"] - } + "exactOptionalPropertyTypes": true, + "verbatimModuleSyntax": true, + "erasableSyntaxOnly": true, + "lib": ["ES2022", "DOM"] }, "include": ["src/**/*.ts"], "exclude": ["node_modules", "dist", "test"] From 4e29cc73387353981207a86e4a52ddb1700cc8b9 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Fri, 28 Aug 2026 13:48:18 -0700 Subject: [PATCH 02/31] Adopt the completed Grove FHIR 0.6.0 contract --- .github/workflows/fhir-conformance.yml | 10 +- package-lock.json | 8 +- packages/fhir/README.md | 338 +- .../catalog/measurement-capabilities.json | 278 +- .../fhir/fixtures/conformance/manifest.json | 166 +- .../conformance/mobile-exchange/corpus.json | 600 + .../corpus.json.license} | 0 .../mobile-exchange/exchange-bundle.json | 284 + .../exchange-bundle.json.license | 3 + .../mobile-exchange/retraction-bundle.json | 140 + .../retraction-bundle.json.license | 3 + .../resources/mobile-active-energy.json | 170 +- .../resources/mobile-basal-energy.json | 170 +- ...le-blood-glucose-unspecified-specimen.json | 170 +- .../resources/mobile-blood-pressure.json | 158 +- .../resources/mobile-body-fat-percentage.json | 170 +- .../resources/mobile-body-height.json | 158 +- .../resources/mobile-body-temperature.json | 158 +- .../resources/mobile-body-water-mass.json | 170 +- .../resources/mobile-body-weight.json | 158 +- .../resources/mobile-bone-mass.json | 170 +- .../resources/mobile-deep-sleep-duration.json | 170 +- .../resources/mobile-distance.json | 170 +- .../mobile-electrodermal-activity.json | 170 +- .../resources/mobile-flights-climbed.json | 170 +- .../mobile-heart-rate-variability-rmssd.json | 170 +- .../mobile-heart-rate-variability-sdnn.json | 170 +- .../resources/mobile-heart-rate.json | 158 +- .../resources/mobile-lean-body-mass.json | 170 +- .../mobile-light-sleep-duration.json | 170 +- ...obile-oxygen-saturation-daily-average.json | 170 +- .../resources/mobile-oxygen-saturation.json | 158 +- .../resources/mobile-rem-sleep-duration.json | 170 +- .../mobile-respiratory-rate-average.json | 170 +- .../resources/mobile-skin-temperature.json | 170 +- .../mobile-sleep-awake-duration.json | 174 +- .../resources/mobile-sleep-duration.json | 174 +- .../resources/mobile-sleep-heart-rate.json | 176 +- .../resources/mobile-step-count.json | 170 +- .../conformance/resources/mobile-vo2-max.json | 174 +- .../conformance/resources/mobile-workout.json | 174 +- ...-api-resting-heart-rate-daily-average.json | 340 + ...ting-heart-rate-daily-average.json.license | 3 + .../provider-oura-daily-activity.json | 300 +- ...provider-oura-oura-cardiovascular-age.json | 330 + ...-oura-oura-cardiovascular-age.json.license | 3 + .../provider-oura-oura-readiness-score.json | 330 + ...der-oura-oura-readiness-score.json.license | 3 + .../provider-primary-output-retraction.json | 207 + ...der-primary-output-retraction.json.license | 3 + .../provider-source-artifact-retraction.json | 207 + ...er-source-artifact-retraction.json.license | 3 + .../provider-withings-body-fat-mass.json | 322 + ...ovider-withings-body-fat-mass.json.license | 3 + ...ovider-withings-corrected-qt-interval.json | 322 + ...ithings-corrected-qt-interval.json.license | 3 + ...der-withings-extracellular-water-mass.json | 327 + ...ings-extracellular-water-mass.json.license | 3 + ...der-withings-intracellular-water-mass.json | 327 + ...ings-intracellular-water-mass.json.license | 3 + .../provider-withings-muscle-mass.json | 322 + ...provider-withings-muscle-mass.json.license | 3 + .../provider-withings-pr-interval.json | 322 + ...provider-withings-pr-interval.json.license | 3 + .../provider-withings-qrs-duration.json | 322 + ...rovider-withings-qrs-duration.json.license | 3 + .../provider-withings-qt-interval.json | 322 + ...provider-withings-qt-interval.json.license | 3 + ...-withings-sleeping-heart-rate-average.json | 340 + ...s-sleeping-heart-rate-average.json.license | 3 + ...-atrial-fibrillation-notification-ecg.json | 329 + ...fibrillation-notification-ecg.json.license | 3 + ...-atrial-fibrillation-notification-ppg.json | 329 + ...fibrillation-notification-ppg.json.license | 3 + ...-withings-withings-nerve-health-score.json | 327 + ...s-withings-nerve-health-score.json.license | 3 + ...withings-withings-pulse-wave-velocity.json | 327 + ...-withings-pulse-wave-velocity.json.license | 3 + ...ovider-withings-withings-vascular-age.json | 327 + ...ithings-withings-vascular-age.json.license | 3 + ...-withings-withings-visceral-fat-index.json | 327 + ...s-withings-visceral-fat-index.json.license | 3 + .../providers-oura-daily-activity.json | 415 - .../resources/questionnaire-response.json | 2 +- ...ecording-google-health-api-heart-rate.json | 175 +- .../resources/recording-oura-heartrate.json | 175 +- .../recording-withings-activityintraday.json | 175 +- .../recording-withings-sleepintraday.json | 175 +- .../google-health-active-energy.json | 11 +- .../google-health-heart-rate-recording.json | 11 +- .../normalized/oura-daily-activity.json | 15 +- .../normalized/oura-heart-rate-recording.json | 15 +- .../normalized/oura-sleep-duration.json | 15 +- .../withings-activity-intraday-recording.json | 11 +- .../normalized/withings-body-weight.json | 11 +- .../withings-sleep-intraday-recording.json | 11 +- packages/fhir/grove-fhir.json | 3 +- packages/fhir/package.json | 3 +- packages/fhir/scripts/browser-smoke.mjs | 143 +- packages/fhir/scripts/fetch-grove-fhir.mjs | 76 +- .../scripts/generate-conformance-fixtures.mjs | 528 +- .../scripts/generate-measurement-catalog.mjs | 1153 +- .../fhir/scripts/generate-zod-schemas.mjs | 422 +- .../scripts/measurement-catalog-inputs.mjs | 105 + .../scripts/measurement-catalog-renderer.mjs | 164 + packages/fhir/scripts/package-smoke.mjs | 20 +- .../fhir/scripts/validate-conformance.mjs | 48 +- .../fhir/scripts/zod-generator-input.d.mts | 29 + packages/fhir/scripts/zod-generator-input.mjs | 375 + packages/fhir/src/core/freeze.ts | 20 +- packages/fhir/src/core/index.ts | 1 + packages/fhir/src/core/json.ts | 152 + packages/fhir/src/core/primitives.ts | 11 +- packages/fhir/src/core/quantity.ts | 11 +- packages/fhir/src/core/result.ts | 86 +- packages/fhir/src/index.ts | 42 +- packages/fhir/src/mobile/contract.ts | 106 + packages/fhir/src/mobile/identity.ts | 664 +- packages/fhir/src/mobile/index.ts | 26 +- .../mobile/measurement-catalog.generated.ts | 2503 +- packages/fhir/src/mobile/types.ts | 73 +- packages/fhir/src/providers/builder.ts | 615 +- .../fhir/src/providers/contract.generated.ts | 1653 +- packages/fhir/src/providers/graph.ts | 170 +- packages/fhir/src/providers/identity.ts | 648 +- packages/fhir/src/providers/index.ts | 45 +- .../src/providers/measurement-definition.ts | 127 + packages/fhir/src/providers/privacy.ts | 41 - packages/fhir/src/providers/profiles.ts | 35 +- .../src/providers/provider-input-schemas.ts | 319 + packages/fhir/src/providers/provider.ts | 679 +- packages/fhir/src/providers/recording.ts | 479 +- packages/fhir/src/providers/retraction.ts | 529 + packages/fhir/src/providers/types.ts | 299 +- packages/fhir/src/questionnaire/builder.ts | 74 +- .../src/questionnaire/contract.generated.ts | 4 +- packages/fhir/src/questionnaire/contract.ts | 615 +- packages/fhir/src/questionnaire/index.ts | 3 + packages/fhir/src/questionnaire/parse.ts | 95 +- .../preflight-answer-validation.ts | 680 + .../questionnaire/preflight-diagnostics.ts | 27 + packages/fhir/src/questionnaire/preflight.ts | 724 +- .../questionnaire/questionnaire-extensions.ts | 497 + .../src/questionnaire/r4-resource-types.ts | 9 +- packages/fhir/src/questionnaire/references.ts | 160 + packages/fhir/src/questionnaire/schemas.ts | 131 +- packages/fhir/src/questionnaire/types.ts | 41 +- .../fhir/src/r4/active-graph-semantics.ts | 615 + .../src/r4/active-observation-semantics.ts | 674 + packages/fhir/src/r4/base-schemas.ts | 131 + packages/fhir/src/r4/diagnostics.ts | 167 + packages/fhir/src/r4/exchange-envelope.ts | 608 + packages/fhir/src/r4/graph-schema-utils.ts | 436 + packages/fhir/src/r4/index.ts | 13 +- packages/fhir/src/r4/json.ts | 51 + packages/fhir/src/r4/parse.ts | 304 +- packages/fhir/src/r4/profile-semantics.ts | 517 + .../fhir/src/r4/retraction-graph-semantics.ts | 339 + packages/fhir/src/r4/schemas.ts | 175 +- packages/fhir/src/r4/types.ts | 103 +- packages/fhir/src/zod/r4/schemas.ts | 62105 +++++++++------ packages/fhir/src/zod/r4b/schemas.ts | 63862 ++++++++++------ packages/fhir/src/zod/support.ts | 97 +- packages/fhir/test/exchange-corpus.test.ts | 629 + packages/fhir/test/exchange-protocol.test.ts | 438 + packages/fhir/test/exports.test.ts | 62 +- packages/fhir/test/fhir-json.test.ts | 253 + .../fhir/test/foundation-boundaries.test.ts | 30 + packages/fhir/test/foundation.test.ts | 125 +- packages/fhir/test/mobile.test.ts | 79 +- packages/fhir/test/provider-contract.test.ts | 479 + packages/fhir/test/provider-identity.test.ts | 667 + packages/fhir/test/provider-mapping.test.ts | 593 + packages/fhir/test/provider-semantics.test.ts | 741 + packages/fhir/test/provider-test-support.ts | 268 + packages/fhir/test/providers.test.ts | 1772 - .../fhir/test/questionnaire-builder.test.ts | 637 + .../questionnaire-extension-contract.test.ts | 756 + .../test/questionnaire-input-surface.test.ts | 286 + ...uestionnaire-preflight-constraints.test.ts | 508 + ...uestionnaire-preflight-expressions.test.ts | 500 + .../fhir/test/questionnaire-preflight.test.ts | 347 + .../fhir/test/questionnaire-test-support.ts | 238 + .../fhir/test/questionnaire-types.test.ts | 42 + packages/fhir/test/questionnaire.test.ts | 2937 - packages/fhir/test/recording.test.ts | 823 +- packages/fhir/test/retraction.test.ts | 526 + .../test/zod-generation-invariants.test.ts | 15 +- .../fhir/test/zod-generator-boundary.test.ts | 160 + packages/fhir/test/zod-schemas.test.ts | 89 +- 190 files changed, 118545 insertions(+), 55878 deletions(-) create mode 100644 packages/fhir/fixtures/conformance/mobile-exchange/corpus.json rename packages/fhir/fixtures/conformance/{resources/providers-oura-daily-activity.json.license => mobile-exchange/corpus.json.license} (100%) create mode 100644 packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json create mode 100644 packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json.license create mode 100644 packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json create mode 100644 packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json.license delete mode 100644 packages/fhir/fixtures/conformance/resources/providers-oura-daily-activity.json create mode 100644 packages/fhir/scripts/measurement-catalog-inputs.mjs create mode 100644 packages/fhir/scripts/measurement-catalog-renderer.mjs create mode 100644 packages/fhir/scripts/zod-generator-input.d.mts create mode 100644 packages/fhir/scripts/zod-generator-input.mjs create mode 100644 packages/fhir/src/core/json.ts create mode 100644 packages/fhir/src/mobile/contract.ts create mode 100644 packages/fhir/src/providers/measurement-definition.ts delete mode 100644 packages/fhir/src/providers/privacy.ts create mode 100644 packages/fhir/src/providers/provider-input-schemas.ts create mode 100644 packages/fhir/src/providers/retraction.ts create mode 100644 packages/fhir/src/questionnaire/preflight-answer-validation.ts create mode 100644 packages/fhir/src/questionnaire/preflight-diagnostics.ts create mode 100644 packages/fhir/src/questionnaire/questionnaire-extensions.ts create mode 100644 packages/fhir/src/questionnaire/references.ts create mode 100644 packages/fhir/src/r4/active-graph-semantics.ts create mode 100644 packages/fhir/src/r4/active-observation-semantics.ts create mode 100644 packages/fhir/src/r4/base-schemas.ts create mode 100644 packages/fhir/src/r4/diagnostics.ts create mode 100644 packages/fhir/src/r4/exchange-envelope.ts create mode 100644 packages/fhir/src/r4/graph-schema-utils.ts create mode 100644 packages/fhir/src/r4/json.ts create mode 100644 packages/fhir/src/r4/profile-semantics.ts create mode 100644 packages/fhir/src/r4/retraction-graph-semantics.ts create mode 100644 packages/fhir/test/exchange-corpus.test.ts create mode 100644 packages/fhir/test/exchange-protocol.test.ts create mode 100644 packages/fhir/test/fhir-json.test.ts create mode 100644 packages/fhir/test/provider-contract.test.ts create mode 100644 packages/fhir/test/provider-identity.test.ts create mode 100644 packages/fhir/test/provider-mapping.test.ts create mode 100644 packages/fhir/test/provider-semantics.test.ts create mode 100644 packages/fhir/test/provider-test-support.ts delete mode 100644 packages/fhir/test/providers.test.ts create mode 100644 packages/fhir/test/questionnaire-builder.test.ts create mode 100644 packages/fhir/test/questionnaire-extension-contract.test.ts create mode 100644 packages/fhir/test/questionnaire-input-surface.test.ts create mode 100644 packages/fhir/test/questionnaire-preflight-constraints.test.ts create mode 100644 packages/fhir/test/questionnaire-preflight-expressions.test.ts create mode 100644 packages/fhir/test/questionnaire-preflight.test.ts create mode 100644 packages/fhir/test/questionnaire-test-support.ts create mode 100644 packages/fhir/test/questionnaire-types.test.ts delete mode 100644 packages/fhir/test/questionnaire.test.ts create mode 100644 packages/fhir/test/retraction.test.ts create mode 100644 packages/fhir/test/zod-generator-boundary.test.ts diff --git a/.github/workflows/fhir-conformance.yml b/.github/workflows/fhir-conformance.yml index c62da95b..4193e8c5 100644 --- a/.github/workflows/fhir-conformance.yml +++ b/.github/workflows/fhir-conformance.yml @@ -58,7 +58,7 @@ jobs: - name: Read the pinned Grove FHIR commit id: grove-fhir run: >- - echo "sha=$(node -p "const p=require('./packages/fhir/grove-fhir.json');p.ref??p.sha")" + echo "sha=$(node -p "require('./packages/fhir/grove-fhir.json').ref")" >> "$GITHUB_OUTPUT" - name: Checkout the pinned Grove FHIR commit @@ -145,12 +145,14 @@ jobs: --ig "$GITHUB_WORKSPACE/.build/grove-fhir" - name: Build only the required IG packages - if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + if: always() && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) working-directory: .build/grove-fhir - run: ./Scripts/build-guides.sh mobile sensor questionnaire providers + run: >- + ./Scripts/build-guides.sh + mobile sensor questionnaire providers google-health oura withings - name: Run official FHIR R4 Validator conformance - if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + if: always() && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) run: >- npm run conformance --workspace @schmiedmayerlab/grove-fhir -- --ig "$GITHUB_WORKSPACE/.build/grove-fhir" diff --git a/package-lock.json b/package-lock.json index e328882a..d8653e03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19534,9 +19534,9 @@ "optional": true }, "node_modules/uuid": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz", - "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==", + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.2.tgz", + "integrity": "sha512-xZe/16rV4aa+HGSOCiY2YeLT1OybRLrrkL/Rqaq7p7GMVXjFh+6wN4oMYgjFmnSnhY8t6Xpdl2l9qmnHYuMHwQ==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -20844,7 +20844,7 @@ "dependencies": { "@noble/hashes": "2.3.0", "@types/fhir": "^0.0.44", - "uuid": "^14.0.1", + "uuid": "^14.0.2", "zod": "^4.4.3" }, "devDependencies": { diff --git a/packages/fhir/README.md b/packages/fhir/README.md index 2dc09e1a..99560806 100644 --- a/packages/fhir/README.md +++ b/packages/fhir/README.md @@ -37,8 +37,7 @@ One measurement, turned into a complete Grove exchange graph: import { parseAbsoluteUri, parseFhirInstant, - parsePatientReference, - parsePositiveInteger, + type DeploymentIdentityInput, type Result, } from '@schmiedmayerlab/grove-fhir' import { buildProviderMeasurementBundle } from '@schmiedmayerlab/grove-fhir/providers' @@ -51,8 +50,52 @@ const unwrap = (result: Result): Value => { const measuredAt = unwrap(parseFhirInstant('2026-08-19T17:30:00Z')) +const identitySecret = process.env.GROVE_IDENTITY_SECRET_BASE64URL +if (identitySecret === undefined) { + throw new Error('Load the Grove HMAC key from the deployment secret store.') +} + +const identitySystem = (kind: string) => + unwrap( + parseAbsoluteUri( + `https://mystudy.example.org/fhir/identity/${kind}/study-key/1`, + ), + ) + +const deploymentIdentity: DeploymentIdentityInput = { + opaqueIdentifierSystems: { + 'source-record': identitySystem('source-record'), + 'source-output': identitySystem('source-output'), + 'writer-record': identitySystem('writer-record'), + 'provider-record': identitySystem('provider-record'), + 'provider-output': identitySystem('provider-output'), + 'source-artifact': identitySystem('source-artifact'), + 'provider-artifact': identitySystem('provider-artifact'), + 'source-context': identitySystem('source-context'), + 'recording-device': identitySystem('recording-device'), + 'device-snapshot': identitySystem('device-snapshot'), + }, + eventIdentifierSystem: identitySystem('event'), + entryNodeIdentifierSystem: identitySystem('entry-node'), + keyId: 'study-key', + keyEpoch: '1', + secretBase64Url: identitySecret, + producerInstance: 'd59d7bda-7c5a-48f9-a5ac-0eb87ec04cb0', +} + const result = buildProviderMeasurementBundle({ - subject: unwrap(parsePatientReference('Patient/participant-01')), + subject: { + type: 'Patient', + identifier: { + system: unwrap( + parseAbsoluteUri( + 'https://mystudy.example.org/fhir/identifiers/patient-pseudonyms', + ), + ), + value: 'pseudonym-participant-01', + assurance: 'deployment-scoped-pseudonym', + }, + }, measurements: [ { kind: 'body-weight', @@ -62,45 +105,34 @@ const result = buildProviderMeasurementBundle({ ], source: { adapter: { kind: 'providers', provider: 'google-health-api' }, - providerAccountIdentifier: { + providerScopeIdentifier: { system: unwrap( parseAbsoluteUri( 'https://mystudy.example.org/fhir/identifiers/provider-accounts', ), ), - value: 'pseudonym-participant-01', - assurance: 'deployment-scoped-pseudonym', + value: 'provider-account-pseudonym-7f3a', + assurance: 'deployment-scoped-account-pseudonym', }, sourceType: 'weight', sourceNativeId: 'ghapi-weight-20260819', recordingMethod: 'automatically-recorded', dataOrigin: { - identity: { - identifier: { - system: unwrap( - parseAbsoluteUri('https://mystudy.example.org/data-origins'), - ), - value: 'google-health-api', - }, - }, - name: 'google-health-api', + sourceDeviceToken: 'google-health-api-2026-08', + name: 'Google Health API', }, }, application: { - identity: { - identifier: { - system: unwrap( - parseAbsoluteUri('https://mystudy.example.org/applications'), - ), - value: 'mystudy', - }, - }, + sourceDeviceToken: 'org.example.mystudy/2.1.0/42', name: 'MyStudy', version: '2.1.0', + build: '42', }, - eventSequence: unwrap(parsePositiveInteger(1)), - issued: unwrap(parseFhirInstant('2026-08-20T12:01:00Z')), + deploymentIdentity, + eventSequence: '1042', + occurred: measuredAt, recorded: unwrap(parseFhirInstant('2026-08-20T12:02:00Z')), + assembled: unwrap(parseFhirInstant('2026-08-20T12:03:00Z')), }) if (!result.ok) { @@ -110,19 +142,21 @@ if (!result.ok) { } ``` -A success gives you a frozen `collection` Bundle holding the Observation, the two Devices, and the Provenance. +A success gives you a frozen `collection` Bundle that has passed the package's profile-aware producer preflight, holding the Observation, application snapshots, and Provenance. Release conformance still uses the official validator as described below. Four conventions are visible in that call and hold throughout the package: - **Nothing throws for bad input.** Every parser and builder returns a `Result`: either `{ ok: true, value }` or `{ ok: false, issues }`, where each issue carries a stable `code` and a `path` into your input. + Unknown JavaScript input first crosses a strict JSON-domain snapshot boundary, so cycles, accessors, sparse arrays, special objects, and non-JSON values are reported rather than reaching schema code or becoming shared mutable state. Reserve `try`/`catch` for genuine programming errors. - **Validated strings are branded types.** An `AbsoluteUri` or a `FhirInstant` can only be produced by its parser, so a malformed timestamp is caught where it enters rather than at the receiving server. This is why the example parses its literals instead of passing raw strings. -- **You supply identity; the package derives structure.** - The participant reference, the account pseudonym, the source's native id, and the timestamps are yours. - Resource identifiers and the Bundle's internal `urn:uuid` links are derived from them, so the same input always produces the same graph and a re-send deduplicates rather than landing twice. +- **You supply governed identity inputs; the package derives the graph.** + The deployment owns the key, key-epoch-specific Identifier systems, producer instance, monotonic event sequence, account pseudonym, source-native id, and all event times. + Source and output identifiers use the Grove v2 domain-separated HMAC contract; internal `urn:uuid` links use UUIDv5 over the selected complete Identifier pair. + An exact retry reuses the event sequence, `occurred`, `recorded`, `assembled`, and payload. A changed source revision or payload is a new event with a new sequence. - **Results are frozen plain JSON.** Serialize them, store them, send them; there are no classes and no hidden state. @@ -141,14 +175,17 @@ The published JavaScript uses portable ES2022 and Web Platform APIs. - The supported R4 closure is intentionally bounded to the resources Grove constructs and validates. - Expected input failures use a discriminated `Result` with stable issue codes and paths; successful results may carry non-blocking warnings. - Validated strings use branded types for instants, canonical URLs, FHIR ids, Patient references, and `urn:uuid` full URLs. -- Provider-account pseudonym, source token/native id, durable event sequence, and all times are caller-owned identity inputs. -- Connected source/output/conversion/exchange identifiers and every Bundle full URL are derived internally from the frozen JCS/SHA-256 and UUID-v5 contracts. +- Provider scope, native source id, device snapshot token, durable producer-wide event sequence, key lifecycle, and all times are caller-owned inputs. Account-scoped providers require a deployment pseudonym; a provider with authoritative global record keys instead requires one documented global key-space pair, so identity cannot fragment across accounts. +- Source/output/artifact/writer/device identifiers use HMAC-SHA-256 over unsigned-32-bit length-framed UTF-8 fields with an explicit protocol domain, key id, and positive key epoch. +- `Bundle.identifier` is the sole business identifier for one immutable source-record revision event; Provenance uses a typed event-scoped node key because R4 Provenance has no business-identifier element. +- Every Bundle `fullUrl` is lowercase UUIDv5 over the length-framed selected Identifier `(system, value)` pair. UUIDv5 formats an internal link; it is not a privacy mechanism. +- Every emitted output carries matching typed `source-record` and `source-output` identifiers. Source-preservation documents additionally carry one typed `source-artifact` identifier. - `Resource.id` is omitted unless the caller supplies a repository-assigned id. ## Provider measurements -The Grove Mobile catalog defines 84 measurements whose semantics are shared by -at least two evidenced sources; `sharedMobileMeasurementCatalog` exposes them +The generated Grove Mobile catalog defines the measurements whose semantics are +shared by at least two evidenced sources; `sharedMobileMeasurementCatalog` exposes them with their value kind, coded-result vocabulary, method, and member contracts. Owner-exclusive measurements (HealthKit, Health Connect, and connected-provider platform exclusives) are deliberately absent from the @@ -160,7 +197,7 @@ Health API, Oura, and Withings inventories. It includes point and period quantities, closed coded results such as workout classification, and composite blood pressure. Shared profiles without a semantically exact provider source remain profiled but are not constructible through this facade, and -owner-exclusive scalar mappings stay contract data outside it. BMI reuses the +owner-exclusive scalar mappings are exposed only through their exact owner profile and provider source row. BMI reuses the standard R4 profile with a HealthKit adapter claim; the four specimen-specific glucose profiles belong to Health Connect. Neither is incorrectly presented as a shared Mobile profile. Google glucose without specimen evidence maps only to @@ -176,8 +213,6 @@ import { parseNormalizedProviderRecord, type ProviderMeasurementBundleInput, } from '@schmiedmayerlab/grove-fhir/providers' - -import { parsePositiveInteger } from '@schmiedmayerlab/grove-fhir' ``` `parseNormalizedProviderRecord(unknown)` is the strict boundary for one source @@ -200,7 +235,7 @@ or fetching behavior. complete graph input. `buildProviderMeasurementBundle` also runs that strict parser itself, so JavaScript callers cannot bypass the boundary or add fields that would be silently ignored. Identity strings are validated without trimming or otherwise -changing their digest input. +changing their HMAC preimage. Every scalar `effectiveDateTime` and aggregate `effectivePeriod` endpoint is canonicalized with the generated Mobile IG contract: exact offset-bearing @@ -246,36 +281,87 @@ ambiguous, or unsupported format or assertion fails closed. The assertion is pro FHIR authorization or consent, and is separate from recording-hardware identity authorization. -`sourceNativeId` remains digest-only for raw recordings too. It is rejected if -it leaks through caller-controlled FHIR metadata such as an attachment title, -URL, Device display, business identifier, or repository id. Opaque attachment -bytes are intentionally not inspected or sanitized by Grove; their disclosure -and retention remain an explicit deployment responsibility, not authorization -granted by the digest or Attachment hash. +For an admitted HealthKit clinical-record document, the parser consumes the +HealthKit catalog's `clinicalRecordAdmission.fhirRepresentation` object. It +requires exactly one extension at the published URL, exactly the published +FHIR value element, and the fixed admitted release. Missing, duplicate, DSTU2, +unknown, or differently shaped release metadata fails closed; Grove preserves +the provider-issued R4 payload bytes without asserting issuer conformance. + +`sourceNativeId` remains HMAC-input-only by default for raw recordings too. +An application that deliberately needs round-trip traceability may opt in with +`nativeIdentifierDisclosure`; its absolute `system` must name the governed +repository/account/store key space and its `nativeId` must exactly equal +`source.sourceNativeId`. The builder then adds that Identifier only to the sole +primary `DocumentReference`. It never copies it to the companion artifact, +Devices, Provenance, entry keys, titles, URLs, or payload metadata. Opaque +attachment bytes are not inspected or sanitized by Grove; neither an HMAC nor +an Attachment hash grants authorization to disclose or retain them. The high-level builder accepts a validated `ProviderMeasurementBundleInput` and returns a -`Result`. The collection contains the Observation, converter -and data-origin application Devices, an optional recording Device, an optional -distinct gateway application, and conversion Provenance. Every internal edge -uses a derived `urn:uuid` Bundle full URL. Each entry carries the complete -business Identifier from which that URL was derived. +`Result`. The collection contains every output, +immutable converter and data-origin application snapshots, optional host and +recording-device snapshots, an optional distinct gateway application, and one +conversion Provenance. Every internal edge uses a derived `urn:uuid` Bundle +full URL. Each entry carries the selected complete Identifier pair from which +that URL was derived. FHIR conversion does not make the converter a gateway. The gateway extension is absent by default and appears only when `GatewayApplicationInput` carries the explicit `mediated-or-routed-measurement` role assurance. The gateway may be the converter in that additional role or a separately identified application. Recording-device identity likewise declares either a deployment scope or an -explicitly authorized hardware scope. Serial numbers are not accepted by the -producer API. - -The source record input contains a deployment-scoped pseudonymous provider -account Identifier, an exact catalog source token, and the provider-native id. -The native id and account pseudonym are digest inputs only and are never emitted -in the FHIR graph. The output discriminator is selected by the generated -catalog—callers cannot provide arbitrary text. The positive durable event -sequence distinguishes conversion/exchange events without changing source or -clinical-output identity. The builder never generates times, event sequences, -or repository ids. +explicitly authorized hardware scope. It requires a governed stable per-unit +token and emits both a stable `recording-device` identity and an event-scoped +`device-snapshot` identity; without that evidence the Device is omitted. Serial +numbers are not accepted by the producer API. Application and host snapshots +are separate Devices linked through `Device.parent`. A single snapshot can +fill multiple Provenance or gateway roles and is emitted only once. + +The source record input contains a complete `providerScopeIdentifier`, an exact +catalog source token, and the provider-native id. Google Health API and Withings +require a deployment-scoped account pseudonym; Oura's authoritative globally +unique document ids use one documented global key-space pair rather than an +account coordinate. The native id and provider scope are HMAC inputs only unless +the caller uses the explicit native-Identifier disclosure above; the provider +scope is never emitted. A scalar record may disclose the native Identifier only when its +generated Provider source row designates one unique one-to-one Observation: + +```typescript +import { parseAbsoluteUri } from '@schmiedmayerlab/grove-fhir' +import type { GovernedSourceIdentifierInput } from '@schmiedmayerlab/grove-fhir/providers' + +const nativeIdentifierDisclosure: GovernedSourceIdentifierInput = { + system: unwrap( + parseAbsoluteUri( + 'https://example.org/repositories/withings-account-7/heart-rate-records', + ), + ), + nativeId: 'withings-heart-rate-2026-08-20', // exactly source.sourceNativeId + type: { text: 'Withings account-scoped heart-rate id' }, +} +``` + +Provider disclosure is admitted only when the generated source row has one +unique supported measurement and the event contains that sole Observation. +Grouped rows such as Oura `daily_activity` have no unambiguous primary and must +omit it. The system cannot be a Grove graph-role, event, entry, or opaque identity +namespace, and `Identifier.type` cannot use the Grove identifier-role code +system. Omission remains conformant. The output role and discriminator are +selected by the generated catalog—callers cannot provide arbitrary text. +Scalar outputs use their exact catalog output role with discriminator `single`; +native provider recordings use role `native-recording` with discriminator +`single`. The positive decimal +event sequence changes the event and its Device snapshots without changing +logical source or output identity. The builder never generates keys, times, +event sequences, or repository ids. + +An optional `writerRecord` always carries its separately derived +`writer-record` Identifier. Its canonical non-negative `version` remains +optional because some sources expose a stable writer record without a revision; +when absent, Grove emits no writer-version extension and makes no supersession +ordering claim. A writer-version extension is therefore never emitted without +the identity it qualifies. `createEntryIdentity(identifier, id?)` remains available for deriving the normative UUID-v5 full URL for caller-owned Device or Specimen business @@ -294,12 +380,40 @@ only `ResearchStudy/{FHIR-id}` or an absolute HTTP(S) resource URL ending in and duplicate literal references fail closed. The synchronized normative catalog and its resolved development reference live -under `catalog/grove-fhir`. `catalog/measurement-capabilities.json` -distinguishes the 31 constructible Provider scalar and coded measurements, -four constructible native-recording source tokens, the shared profiles not -admitted by this facade, adapter-profiled BMI/glucose outputs, -platform-exclusive adapter measurements, and reviewed future, adapter-only, -Sensor, and out-of-scope candidates. +under `.grove-fhir`. `catalog/measurement-capabilities.json` distinguishes the +constructible Provider scalar/coded measurements and native-recording sources +from shared profiles not admitted by this facade, adapter-profiled outputs, +platform-exclusive measurements, and reviewed future or out-of-scope candidates. + +## Retractions + +A source removal is a new append-only Provenance event, never a mutation of the +previous clinical resource: + +```typescript +import { + buildProviderRetractionBundle, + parseProviderRetractionInput, +} from '@schmiedmayerlab/grove-fhir/providers' +``` + +`buildProviderRetractionBundle` accepts only logical targets the Provider facade can itself emit: primary Observations, native-recording documents, and Device snapshots. +Each target has a closed role and a typed Identifier; it never carries a literal +reference or a copied prior resource. Primary and source-artifact targets use +the exact prior Provider `provider-output` identity (carried with the FHIR +`source-output` role). A recording document additionally retains its distinct +`provider-artifact` identity with the `source-artifact` role, while the selected +entry and retraction key remain the Provider output identity. Device retirement +targets use the prior event's `device-snapshot` identity. The resulting Bundle +contains one retraction Provenance plus its immutable assembler application and +optional host snapshots. It never emits `entered-in-error`, a FHIR DELETE, or a +receiver retention instruction. + +Receivers resolve complete Identifier pairs and apply their own idempotent, +atomic lifecycle policy. `parseProviderRetractionInput(unknown)` is the strict +JavaScript boundary and rejects unknown fields, unsupported target/type pairs, +malformed key material, and a device target that names the retraction event +itself. ## Questionnaires @@ -365,6 +479,7 @@ entry identity, and effective-time canonicalization: ```typescript import { canonicalizeMobileEffectiveInstant, + groveExchangeProtocol, groveMobilePackageMetadata, sharedMobileMeasurementCatalog, type MobileMeasurement, @@ -378,6 +493,7 @@ and Withings mapping and graph builders: import { buildProviderMeasurementBundle, buildProviderRecordingBundle, + buildProviderRetractionBundle, groveProviderPackageMetadata, } from '@schmiedmayerlab/grove-fhir/providers' ``` @@ -398,11 +514,33 @@ Use the R4 entry point for the bounded resource schemas and types: ```typescript import { - parseCollectionBundle, - type CollectionBundle, + parseGroveMobileExchangeBundle, + parseGroveMobileRetractionBundle, + parseR4CollectionBundle, + type GroveMobileExchangeBundle, } from '@schmiedmayerlab/grove-fhir/r4' ``` +`parseR4CollectionBundle` checks a bounded base-R4 collection Bundle and makes +no Grove profile claim. `parseGroveMobileExchangeBundle` and +`parseGroveMobileRetractionBundle` additionally enforce event identity, +entry-key selection and digest, deterministic UUIDv5 full URLs, reference +closure, output identities, exact Provenance coverage, event times, and the +active-versus-retraction graph shape. Active events admit only the catalog's +closed output/support/lifecycle resource set, prohibit contained resources, +require exact direct profile modes for outputs, Device, +QuestionnaireResponse, and Provenance, and reject disconnected supporting +nodes. Health Connect `DataOrigin.packageName` remains an identifier-only +logical Device agent on Provenance; it is never synthesized as an unprofiled +Bundle Device. The deliberately explicit parser names prevent callers from +mistaking base-R4 parsing for Grove profile validation. + +For a shared producer-rule failure, `Issue.code` is the exact normative rule +identifier and `Issue.reason` plus `Issue.location` reproduce the stable +corpus diagnostic. `Issue.path` remains the parser-facing array path useful for +programmatic input handling; non-protocol schema failures continue to use +`schema-invalid` without inventing a Grove rule. + Parsing never returns a partially accepted resource: ```typescript @@ -479,19 +617,42 @@ Prefer `parseObservation` and its siblings for R4: they return a `Result` with t ## R4 boundary The runtime schemas are generated from a FHIR release's own `StructureDefinition` resources, not written by hand. -Cardinality, choice types, required-binding enumerations, and even the primitive patterns come from the published definitions, so a schema cannot drift from the specification it claims to implement. - -Each schema states the release's own TypeScript type, so TypeScript checks that the schema produces that type rather than trusting a cast. +Cardinality, choice types, offline-enumerable required bindings, and primitive +patterns come from the published definitions, so these structural rules do not +depend on hand-maintained copies. + +Each schema exposes the release's TypeScript type corrected by the package's +`FhirJson` wire representation. Recursive Zod schemas require internal type +assertions, so generator parity tests and runtime fixtures—not those assertions +alone—guard the emitted property surface. Primitive metadata such as `_effectiveDateTime` is preserved, and a property the release does not define is rejected rather than silently dropped: FHIR JSON is a closed format, so an unmodelled property is an error and not an extension. +Repeated primitive arrays admit legal `null` value slots for extension-only +elements and require their parallel `_field` metadata arrays to remain aligned. +Required singular and repeated primitives may likewise be represented by their +`_field` metadata alone, while omitting both value and metadata still fails. +Primitive-only choice extensions count as the corresponding `value[x]` +alternative, and empty emitted string primitives are rejected. + +These schemas validate already-materialized JavaScript values. JavaScript +`number` cannot retain a FHIR decimal's original lexical scale or arbitrary +precision after ordinary `JSON.parse`, so this package does not promise +lossless decimal wire round-tripping. Preserve/tokenize wire JSON with a +lossless decimal representation when lexical precision matters, and use the +official validator at that boundary. Grove builders emit only finite numeric +values inside JavaScript's supported producer scope. A slot that may hold _any_ resource — `contained`, `Bundle.entry.resource`, and `Bundle.entry.response.outcome` — discriminates on `resourceType` and validates the resource against the schema for that type. A malformed Observation nested in a bundle entry is rejected exactly where a standalone one would be, and the reported path names the element at fault rather than the entry that carries it. -A resource type the package does not model keeps the open shape, because there is nothing better to check it against; rejecting it would refuse a conformant bundle over a gap here rather than a fault in the data. Every resource the release publishes is modelled — 146 in R4 and 141 in R4B — so a resource arriving contained, bundled, or referenced is checked field by field rather than waved through. -The seed comes from the release's own StructureDefinitions, so a resource cannot be missing because nobody remembered to add it; only a `resourceType` outside the release keeps the open shape. +The seed comes from the release's own StructureDefinitions, so a resource cannot be missing because nobody remembered to add it. A `resourceType` outside the pinned release is nonconformant and fails closed. -Four FHIR invariants are enforced structurally — `ext-1`, `per-1`, `obs-3`, and `obs-6` — along with cardinality, required-array minimums, and the rule that a choice element carries exactly one alternative when required and at most one otherwise. +Seven FHIR invariants are enforced structurally — `ele-1`, `ext-1`, `per-1`, +`obs-3`, `obs-6`, `ref-1`, and `qty-3` — along with cardinality, +required-array minimums, and the rule that a choice element carries exactly one +alternative when required and at most one otherwise. Every other invariant the release states is listed in the generated `UNCHECKED_CONSTRAINTS` map, keyed by the path it governs, so the boundary is inspectable rather than implied. +`STRUCTURAL_SCHEMA_SOURCE` publishes the exact package id, package/FHIR version, +archive SHA-512, and generated structure count behind each R4 or R4B surface. Passing a runtime schema is therefore an application preflight, not a declaration of profile conformance. Normative conformance is established by validating generated fixtures with the official HL7 FHIR Validator and the pinned Grove implementation-guide packages. @@ -517,25 +678,28 @@ npm run conformance:structural -- --ig /path/to/grove-fhir ``` `npm run conformance` additionally requires the checksum-pinned official FHIR -Validator and the Mobile, Sensor dependency, Provider, and Questionnaire -packages built in that checkout. It validates all 10 admitted Mobile -measurement graphs, all four admitted native-recording graphs, and the -Questionnaire/Response pair. Every admitted scalar fixture is generated from +Validator and the Mobile, Sensor, Provider, Questionnaire, Google Health, +Oura, and Withings packages built in that checkout. It validates the generated Mobile measurement +graphs, every admitted native-recording graph, the shared active/retraction +exchange corpus, and the Questionnaire/Response pair. Every admitted scalar fixture is generated from the IG's versioned Mobile semantic corpus and bound through an exact RFC 6901 resource pointer, so the generic producer kit compares the emitted profile, clinical code, effective time, and result projection—not only the resource shape. -During coordinated 0.2 development, CI consumes -`feature/fhir-v020-adapters`. `catalog/grove-fhir/source-ref.json` records the -resolved review snapshot for traceability; SHA drift is reported but is not by -itself a failure. Catalog or emitted-resource drift is a failure. Before merge, -the consumer branch must refresh the catalogs and recorded snapshot to the -reviewed IG head, be marked ready for review, and pass the repository's full -readiness CI. Draft pull requests run explicitly labeled scoped FHIR and fast -standards checks; the release-readiness aggregate stays fail-closed. Marking a -pull request ready reruns the complete repository suite and official Validator -workflow. +These committed demonstration fixtures deliberately use `example.org` +deployment systems. The official-validator runner enables the root validator's +explicit example-URL fixture mode for this lane only; it does not change the +runtime parsers or admit example systems in production data. + +`grove-fhir.json` pins the exact immutable IG commit and the SHA-256 of the +corresponding GitHub archive consumed by generation. Fetching verifies that +archive before extraction and binds both values in its cache marker; +catalog, corpus, generated TypeScript, or emitted-resource drift fails CI. +For any future contract update, refresh both the immutable commit and archive +digest, regenerate from a clean checkout, and run unit, package, browser, +structural, and official validator checks. The validator steps use failure-independent conditions so a +browser failure cannot suppress their evidence. ## License diff --git a/packages/fhir/catalog/measurement-capabilities.json b/packages/fhir/catalog/measurement-capabilities.json index f78ab726..ed06cade 100644 --- a/packages/fhir/catalog/measurement-capabilities.json +++ b/packages/fhir/catalog/measurement-capabilities.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "igVersion": "0.5.0", + "igVersion": "0.6.0", "canonicalBase": "https://grovealliance.org/fhir", "recordConversion": { "input": "One connected-provider source record with a non-empty unique subset of the catalog-admitted outputs actually present.", @@ -10,15 +10,15 @@ "fetchBoundary": "Provider clients, authentication, pagination, webhooks, and raw response models remain outside grove-fhir." }, "statuses": { - "implemented": "A closed public builder emits resources conforming to the listed v0.2 profile.", + "implemented": "A closed public builder emits resources conforming to the listed v0.6.0 profile.", "implemented-recording": "The closed native-recording builder emits the exact Sensor + Provider DocumentReference graph for this catalogued provider source token.", "profiled-not-admitted": "The shared profile exists, but this adapter facade has no supported provider source.", - "adapter-profiled": "Version 0.2 defines an adapter-owned or standard-plus-adapter profile claim; it is intentionally not a shared Mobile profile and is outside this Provider facade.", - "planned-shared": "The concept is shared by multiple sources but has no admitted v0.2 Grove builder.", + "adapter-profiled": "Version 0.6.0 defines an adapter-owned or standard-plus-adapter profile claim; it is intentionally not a shared Mobile profile and is outside this Provider facade.", + "planned-shared": "The concept is shared by multiple sources but has no admitted v0.6.0 Grove builder.", "adapter-specific": "The semantics are source-specific and must not be collapsed into a shared concept.", "sensor-ig": "The concept belongs to the Sensor and Waveform contract.", "not-in-scope": "The source exposes the record, but it is not a mobile measurement admitted by this package.", - "platform-exclusive": "The measurement is owned by a single platform adapter and its adapter-exclusive profile; it is outside the shared Mobile facade and this Provider facade." + "platform-exclusive": "The measurement is owned by one adapter and is emitted only through that adapter's exact Provider facade row and owner-specific semantic profile; it is outside the shared Mobile facade." }, "measurements": [ { @@ -58,7 +58,7 @@ }, "effective": "dateTime", "sources": ["healthkit", "health-connect"], - "reason": "None of the Google Health API, Oura, or Withings mappings admits this shared measurement in v0.2." + "reason": "None of the Google Health API, Oura, or Withings mappings admits this shared measurement in v0.6.0." }, { "key": "basal-energy", @@ -1013,7 +1013,7 @@ "code": "/min", "display": "beats/minute" }, - "effective": "dateTime", + "effective": "dateTime-or-Period", "sources": ["healthkit", "health-connect", "sensorkit", "withings"] }, { @@ -1242,19 +1242,20 @@ }, { "key": "resting-heart-rate", - "status": "implemented", + "status": "profiled-not-admitted", "profile": "grove-mobile-resting-heart-rate", "code": { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", - "code": "resting-heart-rate", - "display": "Resting heart rate" + "system": "http://loinc.org", + "code": "40443-4", + "display": "Heart rate --resting" }, "unit": { "code": "/min", "display": "beats/minute" }, - "effective": "Period", - "sources": ["healthkit", "health-connect", "google-health-api"] + "effective": "dateTime", + "sources": ["healthkit", "health-connect"], + "reason": "The connected Provider sources expose a daily aggregate, not this instantaneous resting-heart-rate meaning." }, { "key": "sexual-activity", @@ -1473,7 +1474,7 @@ }, "effective": "dateTime", "sources": ["healthkit"], - "reason": "FHIR R4 BMI plus the HealthKit adapter profile is the exact v0.2 claim; a one-source shared Mobile wrapper is prohibited." + "reason": "FHIR R4 BMI plus the HealthKit adapter profile is the exact v0.6.0 claim; a one-source shared Mobile wrapper is prohibited." }, { "key": "blood-glucose", @@ -1548,9 +1549,9 @@ "status": "implemented-recording", "provider": "google-health-api", "sourceType": "heart-rate", - "profile": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document", + "profile": "https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document", "sourceNeutralProfile": "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", - "outputDiscriminator": "native-recording", + "outputRole": "native-recording", "sources": ["google-health-api"] }, { @@ -1558,9 +1559,9 @@ "status": "implemented-recording", "provider": "oura", "sourceType": "heartrate", - "profile": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document", + "profile": "https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document", "sourceNeutralProfile": "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", - "outputDiscriminator": "native-recording", + "outputRole": "native-recording", "sources": ["oura"] }, { @@ -1568,9 +1569,9 @@ "status": "implemented-recording", "provider": "withings", "sourceType": "activityIntraday", - "profile": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document", + "profile": "https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document", "sourceNeutralProfile": "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", - "outputDiscriminator": "native-recording", + "outputRole": "native-recording", "sources": ["withings"] }, { @@ -1578,9 +1579,9 @@ "status": "implemented-recording", "provider": "withings", "sourceType": "sleepIntraday", - "profile": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document", + "profile": "https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document", "sourceNeutralProfile": "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", - "outputDiscriminator": "native-recording", + "outputRole": "native-recording", "sources": ["withings"] }, { @@ -1777,7 +1778,7 @@ { "key": "body-fat-mass", "status": "platform-exclusive", - "profile": "provider-body-fat-mass", + "profile": "withings-body-fat-mass", "code": { "system": "http://loinc.org", "code": "73708-0", @@ -1788,13 +1789,13 @@ "display": "kg" }, "effective": "dateTime", - "owner": "providers", + "owner": "withings", "sources": ["withings"] }, { "key": "extracellular-water-mass", "status": "platform-exclusive", - "profile": "provider-extracellular-water-mass", + "profile": "withings-extracellular-water-mass", "code": { "system": "https://grovealliance.org/fhir/providers/CodeSystem/provider-measurement", "code": "extracellular-water-mass", @@ -1805,13 +1806,13 @@ "display": "kg" }, "effective": "dateTime", - "owner": "providers", + "owner": "withings", "sources": ["withings"] }, { "key": "intracellular-water-mass", "status": "platform-exclusive", - "profile": "provider-intracellular-water-mass", + "profile": "withings-intracellular-water-mass", "code": { "system": "https://grovealliance.org/fhir/providers/CodeSystem/provider-measurement", "code": "intracellular-water-mass", @@ -1822,13 +1823,13 @@ "display": "kg" }, "effective": "dateTime", - "owner": "providers", + "owner": "withings", "sources": ["withings"] }, { "key": "muscle-mass", "status": "platform-exclusive", - "profile": "provider-muscle-mass", + "profile": "withings-muscle-mass", "code": { "system": "http://loinc.org", "code": "73964-9", @@ -1839,13 +1840,13 @@ "display": "kg" }, "effective": "dateTime", - "owner": "providers", + "owner": "withings", "sources": ["withings"] }, { "key": "sleeping-heart-rate-average", "status": "platform-exclusive", - "profile": "provider-sleeping-heart-rate-average", + "profile": "withings-sleeping-heart-rate-average", "code": { "system": "https://grovealliance.org/fhir/providers/CodeSystem/provider-measurement", "code": "sleeping-heart-rate-average", @@ -1856,7 +1857,220 @@ "display": "beats/minute" }, "effective": "Period", - "owner": "providers", + "owner": "withings", + "sources": ["withings"] + }, + { + "key": "corrected-qt-interval", + "status": "platform-exclusive", + "profile": "withings-corrected-qt-interval", + "code": { + "system": "http://loinc.org", + "code": "8636-3", + "display": "Q-T interval corrected" + }, + "unit": { + "code": "ms", + "display": "ms" + }, + "effective": "dateTime", + "owner": "withings", + "sources": ["withings"] + }, + { + "key": "oura-cardiovascular-age", + "status": "platform-exclusive", + "profile": "oura-cardiovascular-age", + "code": { + "system": "https://grovealliance.org/fhir/oura/CodeSystem/oura-measurement", + "code": "oura-cardiovascular-age", + "display": "Oura cardiovascular age" + }, + "unit": { + "code": "a", + "display": "years" + }, + "effective": "Period", + "owner": "oura", + "sources": ["oura"] + }, + { + "key": "oura-readiness-score", + "status": "platform-exclusive", + "profile": "oura-readiness-score", + "code": { + "system": "https://grovealliance.org/fhir/oura/CodeSystem/oura-measurement", + "code": "oura-readiness-score", + "display": "Oura readiness score" + }, + "unit": { + "code": "{score}", + "display": "score" + }, + "effective": "Period", + "owner": "oura", + "sources": ["oura"] + }, + { + "key": "pr-interval", + "status": "platform-exclusive", + "profile": "withings-pr-interval", + "code": { + "system": "http://loinc.org", + "code": "8625-6", + "display": "P-R Interval" + }, + "unit": { + "code": "ms", + "display": "ms" + }, + "effective": "dateTime", + "owner": "withings", + "sources": ["withings"] + }, + { + "key": "qrs-duration", + "status": "platform-exclusive", + "profile": "withings-qrs-duration", + "code": { + "system": "http://loinc.org", + "code": "8633-0", + "display": "QRS duration" + }, + "unit": { + "code": "ms", + "display": "ms" + }, + "effective": "dateTime", + "owner": "withings", + "sources": ["withings"] + }, + { + "key": "qt-interval", + "status": "platform-exclusive", + "profile": "withings-qt-interval", + "code": { + "system": "http://loinc.org", + "code": "8634-8", + "display": "Q-T interval" + }, + "unit": { + "code": "ms", + "display": "ms" + }, + "effective": "dateTime", + "owner": "withings", + "sources": ["withings"] + }, + { + "key": "resting-heart-rate-daily-average", + "status": "platform-exclusive", + "profile": "google-health-daily-resting-heart-rate", + "code": { + "system": "https://grovealliance.org/fhir/google-health/CodeSystem/google-health-measurement", + "code": "resting-heart-rate-daily-average", + "display": "Resting heart rate daily average" + }, + "unit": { + "code": "/min", + "display": "beats/minute" + }, + "effective": "Period", + "owner": "google-health", + "sources": ["google-health-api"] + }, + { + "key": "withings-atrial-fibrillation-notification-ecg", + "status": "platform-exclusive", + "profile": "withings-atrial-fibrillation-notification-ecg", + "code": { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", + "code": "withings-atrial-fibrillation-notification-ecg", + "display": "Withings atrial fibrillation notification (ECG)" + }, + "effective": "dateTime", + "owner": "withings", + "sources": ["withings"] + }, + { + "key": "withings-atrial-fibrillation-notification-ppg", + "status": "platform-exclusive", + "profile": "withings-atrial-fibrillation-notification-ppg", + "code": { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", + "code": "withings-atrial-fibrillation-notification-ppg", + "display": "Withings atrial fibrillation notification (PPG)" + }, + "effective": "dateTime", + "owner": "withings", + "sources": ["withings"] + }, + { + "key": "withings-nerve-health-score", + "status": "platform-exclusive", + "profile": "withings-nerve-health-score", + "code": { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", + "code": "withings-nerve-health-score", + "display": "Withings nerve health score" + }, + "unit": { + "code": "{score}", + "display": "score" + }, + "effective": "dateTime", + "owner": "withings", + "sources": ["withings"] + }, + { + "key": "withings-pulse-wave-velocity", + "status": "platform-exclusive", + "profile": "withings-pulse-wave-velocity", + "code": { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", + "code": "withings-pulse-wave-velocity", + "display": "Withings pulse wave velocity" + }, + "unit": { + "code": "m/s", + "display": "m/s" + }, + "effective": "dateTime", + "owner": "withings", + "sources": ["withings"] + }, + { + "key": "withings-vascular-age", + "status": "platform-exclusive", + "profile": "withings-vascular-age", + "code": { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", + "code": "withings-vascular-age", + "display": "Withings vascular age" + }, + "unit": { + "code": "a", + "display": "years" + }, + "effective": "dateTime", + "owner": "withings", + "sources": ["withings"] + }, + { + "key": "withings-visceral-fat-index", + "status": "platform-exclusive", + "profile": "withings-visceral-fat-index", + "code": { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", + "code": "withings-visceral-fat-index", + "display": "Withings visceral fat index" + }, + "unit": { + "code": "{score}", + "display": "score" + }, + "effective": "dateTime", + "owner": "withings", "sources": ["withings"] } ] diff --git a/packages/fhir/fixtures/conformance/manifest.json b/packages/fhir/fixtures/conformance/manifest.json index 6027108b..52f69305 100644 --- a/packages/fhir/fixtures/conformance/manifest.json +++ b/packages/fhir/fixtures/conformance/manifest.json @@ -3,25 +3,40 @@ "fhirVersion": "4.0.1", "producer": { "name": "Grove TypeScript", "version": "0.0.0" }, "packages": [ + { + "alias": "google-health", + "packageId": "org.grovealliance.fhir.google-health", + "version": "0.6.0" + }, { "alias": "mobile", "packageId": "org.grovealliance.fhir.mobile", - "version": "0.5.0" + "version": "0.6.0" + }, + { + "alias": "oura", + "packageId": "org.grovealliance.fhir.oura", + "version": "0.6.0" }, { "alias": "providers", "packageId": "org.grovealliance.fhir.providers", - "version": "0.5.0" + "version": "0.6.0" }, { "alias": "sensor", "packageId": "org.grovealliance.fhir.sensor", - "version": "0.5.0" + "version": "0.6.0" }, { "alias": "questionnaire", "packageId": "org.grovealliance.fhir.questionnaire", - "version": "0.5.0" + "version": "0.6.0" + }, + { + "alias": "withings", + "packageId": "org.grovealliance.fhir.withings", + "version": "0.6.0" } ], "resources": [ @@ -163,12 +178,6 @@ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" ] }, - { - "path": "resources/mobile-resting-heart-rate.json", - "requiredProfiles": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, { "path": "resources/mobile-skin-temperature.json", "requiredProfiles": [ @@ -211,6 +220,114 @@ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" ] }, + { + "path": "resources/provider-google-health-api-resting-heart-rate-daily-average.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-oura-oura-cardiovascular-age.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-oura-oura-readiness-score.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-withings-withings-vascular-age.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-withings-sleeping-heart-rate-average.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-withings-body-fat-mass.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-withings-muscle-mass.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-withings-withings-pulse-wave-velocity.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-withings-qrs-duration.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-withings-pr-interval.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-withings-qt-interval.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-withings-corrected-qt-interval.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-withings-withings-nerve-health-score.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-withings-extracellular-water-mass.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-withings-intracellular-water-mass.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "resources/provider-withings-withings-visceral-fat-index.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, { "path": "resources/provider-oura-daily-activity.json", "requiredProfiles": [ @@ -241,6 +358,30 @@ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" ] }, + { + "path": "resources/provider-primary-output-retraction.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle" + ] + }, + { + "path": "resources/provider-source-artifact-retraction.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle" + ] + }, + { + "path": "mobile-exchange/exchange-bundle.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + { + "path": "mobile-exchange/retraction-bundle.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle" + ] + }, { "path": "resources/questionnaire.json", "requiredProfiles": [ @@ -370,11 +511,6 @@ "path": "resources/mobile-respiratory-rate-average.json", "resourcePointer": "/entry/0/resource" }, - { - "id": "resting-heart-rate", - "path": "resources/mobile-resting-heart-rate.json", - "resourcePointer": "/entry/0/resource" - }, { "id": "skin-temperature", "path": "resources/mobile-skin-temperature.json", diff --git a/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json b/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json new file mode 100644 index 00000000..bf1b5f96 --- /dev/null +++ b/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json @@ -0,0 +1,600 @@ +{ + "schemaVersion": 1, + "version": "0.6.0", + "bases": [ + { "id": "mobile-exchange", "path": "exchange-bundle.json" }, + { "id": "mobile-retraction", "path": "retraction-bundle.json" } + ], + "cases": [ + { + "id": "missing-entry-node-key", + "base": "mobile-exchange", + "patch": [{ "op": "remove", "path": "/entry/0/extension" }], + "expectedRule": { + "code": "mobile-exchange.entry-node-key", + "reason": "Every Bundle entry must carry exactly one complete Grove exchange entry node key.", + "location": "Bundle.entry[0]", + "severity": "error" + } + }, + { + "id": "non-deterministic-full-url", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/1/fullUrl", + "value": "urn:uuid:00000000-0000-5000-8000-000000000000" + } + ], + "expectedRule": { + "code": "mobile-exchange.deterministic-full-url", + "reason": "Bundle.entry.fullUrl must be the UUID version 5 value derived from its complete entry identifier.", + "location": "Bundle.entry[1].fullUrl", + "severity": "error" + } + }, + { + "id": "unresolved-internal-reference", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/2/resource/subject/reference", + "value": "urn:uuid:00000000-0000-5000-8000-000000000001" + } + ], + "expectedRule": { + "code": "mobile-exchange.resolved-reference", + "reason": "Every internal UUID URN reference must resolve to a Bundle entry fullUrl.", + "location": "Bundle.entry[2].resource.subject.reference", + "severity": "error" + } + }, + { + "id": "wrong-heart-rate-unit", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/2/resource/valueQuantity/code", + "value": "kg" + } + ], + "expectedRule": { + "code": "mobile-output.fixed-quantity-unit", + "reason": "Every Quantity-valued catalog measurement uses the exact fixed system and code declared by its semantic profile contract.", + "location": "Bundle.entry[2].resource.valueQuantity.code", + "severity": "error" + } + }, + { + "id": "non-canonical-event-identity", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/identifier/value", + "value": "exchange-20260820-001" + } + ], + "expectedRule": { + "code": "mobile-exchange.event-identity", + "reason": "Bundle.identifier.value must be the canonical e2 producer UUID and positive sequence form.", + "location": "Bundle.identifier.value", + "severity": "error" + } + }, + { + "id": "tampered-entry-node-digest", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/0/extension/0/valueIdentifier/value", + "value": "n2:patient:0:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + } + ], + "expectedRule": { + "code": "mobile-exchange.entry-node-digest", + "reason": "An entry-node digest must be derived from the enclosing event identifier, role, and ordinal.", + "location": "Bundle.entry[0].extension.valueIdentifier.value", + "severity": "error" + } + }, + { + "id": "missing-source-output-identity", + "base": "mobile-exchange", + "patch": [{ "op": "remove", "path": "/entry/2/resource/identifier/1" }], + "expectedRule": { + "code": "mobile-output.source-output-required", + "reason": "Every active clinical output must carry its exact typed source-output identity in addition to source-record identity.", + "location": "Bundle.entry[2].resource.identifier", + "severity": "error" + } + }, + { + "id": "identity-system-changes-role", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/2/resource/identifier/1/system", + "value": "https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1" + } + ], + "expectedRule": { + "code": "mobile-exchange.identity-system-role", + "reason": "Within one event graph, each Grove Identifier.system names exactly one Grove identifier role; one namespace cannot change meaning between nodes.", + "location": "Bundle", + "severity": "error" + } + }, + { + "id": "missing-transform-provenance", + "base": "mobile-exchange", + "patch": [{ "op": "remove", "path": "/entry/3" }], + "expectedRule": { + "code": "mobile-exchange.transform-provenance", + "reason": "An active event must contain exactly one transform Provenance and no retraction Provenance.", + "location": "Bundle.entry", + "severity": "error" + } + }, + { + "id": "retraction-literal-target", + "base": "mobile-retraction", + "patch": [ + { + "op": "add", + "path": "/entry/0/resource/target/0/reference", + "value": "Observation/prior-output" + } + ], + "expectedRule": { + "code": "mobile-retraction.logical-target", + "reason": "A retraction target must be a typed logical Reference without a literal reference.", + "location": "Provenance.target[0]", + "severity": "error" + } + }, + { + "id": "retraction-unknown-target-role", + "base": "mobile-retraction", + "patch": [ + { + "op": "replace", + "path": "/entry/0/resource/target/0/extension/0/valueCode", + "value": "delete-command" + } + ], + "expectedRule": { + "code": "mobile-retraction.target-role", + "reason": "Every retraction target must carry exactly one closed Grove target-role code.", + "location": "Provenance.target[0].extension", + "severity": "error" + } + }, + { + "id": "retraction-clear-target-identity", + "base": "mobile-retraction", + "patch": [ + { + "op": "replace", + "path": "/entry/0/resource/target/0/identifier/value", + "value": "prior-output-001" + } + ], + "expectedRule": { + "code": "mobile-retraction.opaque-target", + "reason": "A retraction target must use the exact canonical v2 HMAC identity previously emitted.", + "location": "Provenance.target[0].identifier.value", + "severity": "error" + } + }, + { + "id": "retraction-copied-clinical-resource", + "base": "mobile-retraction", + "patch": [ + { + "op": "replace", + "path": "/entry/0/resource/resourceType", + "value": "Observation" + } + ], + "expectedRule": { + "code": "mobile-retraction.no-clinical-copy", + "reason": "A retraction event contains its lifecycle Provenance and optional Device agents, never a copied or mutilated clinical resource.", + "location": "Bundle.entry[0].resource", + "severity": "error" + } + }, + { + "id": "ambiguous-active-lifecycle-coding", + "base": "mobile-exchange", + "patch": [ + { + "op": "add", + "path": "/entry/3/resource/activity/coding/-", + "value": { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "amend" + } + } + ], + "expectedRule": { + "code": "mobile-exchange.lifecycle-coding", + "reason": "A lifecycle Provenance must carry exactly one coding across the ISO transform and Grove retraction lifecycle systems; translations from other systems remain open.", + "location": "Provenance.activity.coding", + "severity": "error" + } + }, + { + "id": "contradictory-retraction-lifecycle-coding", + "base": "mobile-retraction", + "patch": [ + { + "op": "add", + "path": "/entry/0/resource/activity/coding/-", + "value": { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform" + } + } + ], + "expectedRule": { + "code": "mobile-exchange.lifecycle-coding", + "reason": "A lifecycle Provenance must carry exactly one coding across the ISO transform and Grove retraction lifecycle systems; translations from other systems remain open.", + "location": "Provenance.activity.coding", + "severity": "error" + } + }, + { + "id": "unprofiled-active-observation", + "base": "mobile-exchange", + "patch": [{ "op": "remove", "path": "/entry/2/resource/meta/profile" }], + "expectedRule": { + "code": "mobile-output.semantic-profile", + "reason": "Every active Observation must directly claim one admitted Grove semantic profile shape; an empty claim cannot bypass semantic validation.", + "location": "Observation.meta.profile", + "severity": "error" + } + }, + { + "id": "wrong-subject-target-type", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/2/resource/subject/reference", + "value": "urn:uuid:8f87a88a-8744-5116-8901-9274f62472ac" + } + ], + "expectedRule": { + "code": "mobile-exchange.reference-target-type", + "reason": "An Observation subject resolves to a Patient entry, not merely to any existing fullUrl.", + "location": "Observation.subject.reference", + "severity": "error" + } + }, + { + "id": "false-reference-declared-type", + "base": "mobile-exchange", + "patch": [ + { + "op": "add", + "path": "/entry/2/resource/subject/type", + "value": "Device" + } + ], + "expectedRule": { + "code": "mobile-exchange.reference-declared-type", + "reason": "When Reference.type is present it must equal the referenced entry's actual resourceType token.", + "location": "Observation.subject.type", + "severity": "error" + } + }, + { + "id": "transform-literal-source-entity", + "base": "mobile-exchange", + "patch": [ + { + "op": "add", + "path": "/entry/3/resource/entity/0/what/reference", + "value": "urn:uuid:3314ab4c-4ab3-536f-a556-e3b6ff97762d" + } + ], + "expectedRule": { + "code": "mobile-exchange.logical-source-entity", + "reason": "Lifecycle Provenance carries exactly one logical source-record Identifier entity and never a literal source Reference.", + "location": "Provenance.entity[0].what", + "severity": "error" + } + }, + { + "id": "retraction-role-target-type-mismatch", + "base": "mobile-retraction", + "patch": [ + { + "op": "replace", + "path": "/entry/0/resource/target/0/type", + "value": "Device" + } + ], + "expectedRule": { + "code": "mobile-retraction.role-target-type", + "reason": "Every retraction target role fixes its admitted resource type and Identifier role.", + "location": "Provenance.target[0].type", + "severity": "error" + } + }, + { + "id": "retraction-additional-source-entity", + "base": "mobile-retraction", + "patch": [ + { + "op": "add", + "path": "/entry/0/resource/entity/-", + "value": { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1", + "value": "v2:test-key:1:1y-KjnPSgOBDI5pE69EVN8U7Oaen_WM4cPL-sBxDKgw" + } + } + } + } + ], + "expectedRule": { + "code": "mobile-exchange.single-source-entity", + "reason": "A lifecycle Provenance identifies exactly one source-record entity.", + "location": "Provenance.entity", + "severity": "error" + } + }, + { + "id": "mixed-literal-logical-patient-reference", + "base": "mobile-exchange", + "patch": [ + { + "op": "add", + "path": "/entry/2/resource/subject/identifier", + "value": { + "system": "https://deployment.example/fhir/NamingSystem/patient-pseudonym", + "value": "participant-42" + } + } + ], + "expectedRule": { + "code": "mobile-exchange.reference-shape", + "reason": "A governed Reference is exclusively resolving-literal or identifier-only logical, never both.", + "location": "Observation.subject", + "severity": "error" + } + }, + { + "id": "untyped-logical-patient-reference", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/2/resource/subject", + "value": { + "identifier": { + "system": "https://deployment.example/fhir/NamingSystem/patient-pseudonym", + "value": "participant-42" + } + } + } + ], + "expectedRule": { + "code": "mobile-exchange.logical-patient-reference", + "reason": "An identifier-only logical Patient Reference carries the exact Patient type and one complete absolute-system pseudonym Identifier.", + "location": "Observation.subject", + "severity": "error" + } + }, + { + "id": "unadmitted-condition-resource", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/0/resource/resourceType", + "value": "Condition" + } + ], + "expectedRule": { + "code": "mobile-exchange.entry-resource-type", + "reason": "An active event admits only its closed output, supporting, and lifecycle resource type set.", + "location": "Bundle.entry[0].resource.resourceType", + "severity": "error" + } + }, + { + "id": "adapter-only-output-without-adapter-profile", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/2/resource/resourceType", + "value": "Specimen" + } + ], + "expectedRule": { + "code": "mobile-output.adapter-only-profile", + "reason": "An adapter-only active output type must directly claim exactly its one admitted adapter profile.", + "location": "Specimen.meta.profile", + "severity": "error" + } + }, + { + "id": "contained-resource-prohibited", + "base": "mobile-exchange", + "patch": [ + { + "op": "add", + "path": "/entry/2/resource/contained", + "value": [{ "resourceType": "Patient", "id": "hidden-patient" }] + } + ], + "expectedRule": { + "code": "mobile-exchange.contained-resource-prohibited", + "reason": "Mobile exchange events prohibit contained resources; every graph node must be an addressable Bundle entry.", + "location": "Bundle.entry[2].resource.contained", + "severity": "error" + } + }, + { + "id": "unadmitted-device-metric-resource", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/0/resource/resourceType", + "value": "DeviceMetric" + } + ], + "expectedRule": { + "code": "mobile-exchange.entry-resource-type", + "reason": "An active event admits only its closed output, supporting, and lifecycle resource type set.", + "location": "Bundle.entry[0].resource.resourceType", + "severity": "error" + } + }, + { + "id": "unprofiled-active-document-reference", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/2/resource", + "value": { + "resourceType": "DocumentReference", + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1", + "value": "v2:test-key:1:1y-KjnPSgOBDI5pE69EVN8U7Oaen_WM4cPL-sBxDKgw" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v2/test-key/1", + "value": "v2:test-key:1:_sudqhgnrCDWXSytfxliI_fzd0qjFUMFobOIMP6Z5gw" + } + ], + "status": "current", + "subject": { + "reference": "urn:uuid:3314ab4c-4ab3-536f-a556-e3b6ff97762d" + }, + "date": "2026-08-19T17:30:00Z", + "content": [ + { + "attachment": { + "contentType": "application/octet-stream", + "url": "https://example.org/recording.bin" + } + } + ] + } + } + ], + "expectedRule": { + "code": "mobile-output.document-profile", + "reason": "Every active DocumentReference must directly claim exactly one admitted recording or clinical-document profile mode.", + "location": "DocumentReference.meta.profile", + "severity": "error" + } + }, + { + "id": "unprofiled-active-device", + "base": "mobile-exchange", + "patch": [{ "op": "remove", "path": "/entry/1/resource/meta/profile" }], + "expectedRule": { + "code": "mobile-support.device-profile", + "reason": "Every active Device must directly claim exactly one admitted Grove Device profile mode.", + "location": "Device.meta.profile", + "severity": "error" + } + }, + { + "id": "unprofiled-active-provenance", + "base": "mobile-exchange", + "patch": [{ "op": "remove", "path": "/entry/3/resource/meta/profile" }], + "expectedRule": { + "code": "mobile-exchange.provenance-profile", + "reason": "The sole active lifecycle Provenance must directly claim exactly one admitted Mobile or adapter conversion profile.", + "location": "Provenance.meta.profile", + "severity": "error" + } + }, + { + "id": "disconnected-supporting-patient", + "base": "mobile-exchange", + "patch": [ + { + "op": "add", + "path": "/entry/-", + "value": { + "fullUrl": "urn:uuid:19e3b5cf-5c03-57a3-a9ff-6c0cb3025341", + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v2", + "value": "n2:patient:1:wTdrAaKPt3xdV3VbfjLhG5fqVbRBmQe1q_UMGSBeopU" + } + } + ], + "resource": { + "resourceType": "Patient", + "identifier": [ + { + "system": "https://deployment.example/fhir/NamingSystem/patient-pseudonym", + "value": "participant-99" + } + ] + } + } + } + ], + "expectedRule": { + "code": "mobile-support.connected", + "reason": "Every supporting resource must be connected to an output or the lifecycle Provenance.", + "location": "Bundle.entry", + "severity": "error" + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/providers-oura-daily-activity.json.license b/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json.license similarity index 100% rename from packages/fhir/fixtures/conformance/resources/providers-oura-daily-activity.json.license rename to packages/fhir/fixtures/conformance/mobile-exchange/corpus.json.license diff --git a/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json b/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json new file mode 100644 index 00000000..24e5c74a --- /dev/null +++ b/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json @@ -0,0 +1,284 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v2", + "value": "e2:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v2", + "value": "n2:patient:0:wMhqO_kIRVFR8etQrCbgX0FXhz7h3f-LnsfUhRcrC9A" + } + } + ], + "fullUrl": "urn:uuid:3314ab4c-4ab3-536f-a556-e3b6ff97762d", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v2/test-key/1", + "value": "v2:test-key:1:UEdJx7eiKElpKK_oPmjTu3fK072oUz8JKwgtF0CBe8A" + } + } + ], + "fullUrl": "urn:uuid:8f87a88a-8744-5116-8901-9274f62472ac", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v2/test-key/1", + "value": "v2:test-key:1:UEdJx7eiKElpKK_oPmjTu3fK072oUz8JKwgtF0CBe8A" + } + ], + "deviceName": [ + { "type": "user-friendly-name", "name": "Mobile Study" } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source output" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v2/test-key/1", + "value": "v2:test-key:1:_sudqhgnrCDWXSytfxliI_fzd0qjFUMFobOIMP6Z5gw" + } + } + ], + "fullUrl": "urn:uuid:13bc1990-e0d8-57cf-8772-8f959664241d", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1", + "value": "v2:test-key:1:1y-KjnPSgOBDI5pE69EVN8U7Oaen_WM4cPL-sBxDKgw" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v2/test-key/1", + "value": "v2:test-key:1:_sudqhgnrCDWXSytfxliI_fzd0qjFUMFobOIMP6Z5gw" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:8f87a88a-8744-5116-8901-9274f62472ac" + } + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:3314ab4c-4ab3-536f-a556-e3b6ff97762d" + }, + "performer": [ + { "reference": "urn:uuid:3314ab4c-4ab3-536f-a556-e3b6ff97762d" } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v2", + "value": "n2:conversion-provenance:0:SwGD7C4DT5_9kgIOQ9h7W8I4UdwJPuEOnkh2TgQVwko" + } + } + ], + "fullUrl": "urn:uuid:9908feb7-0370-5f06-a689-f8afa210eb41", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:8f87a88a-8744-5116-8901-9274f62472ac" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1", + "value": "v2:test-key:1:1y-KjnPSgOBDI5pE69EVN8U7Oaen_WM4cPL-sBxDKgw" + } + } + } + ], + "target": [ + { "reference": "urn:uuid:13bc1990-e0d8-57cf-8772-8f959664241d" } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json.license b/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json b/packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json new file mode 100644 index 00000000..85752411 --- /dev/null +++ b/packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json @@ -0,0 +1,140 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileRetractionBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v2", + "value": "e2:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:43" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v2", + "value": "n2:retraction-provenance:0:kry9tA8wKNb5VGuzulTG-msyl6iRqHJuR2y1eK-7VOc" + } + } + ], + "fullUrl": "urn:uuid:39f1693a-d1b3-597c-998f-0837dbd1d6d1", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileRetractionProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "source-record-retracted", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-lifecycle-event", + "display": "Source record retracted" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "type": "Device", + "identifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v2/test-key/1", + "value": "v2:test-key:1:4hdZeDHmzYotDYvBYG1H5YZPDtfGbMiEftruwDvait4" + } + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1", + "value": "v2:test-key:1:1y-KjnPSgOBDI5pE69EVN8U7Oaen_WM4cPL-sBxDKgw" + } + } + } + ], + "target": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-role", + "valueCode": "primary-output" + } + ], + "type": "Observation", + "identifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source output" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v2/test-key/1", + "value": "v2:test-key:1:_sudqhgnrCDWXSytfxliI_fzd0qjFUMFobOIMP6Z5gw" + } + } + ], + "occurredDateTime": "2026-08-21T08:00:00Z", + "recorded": "2026-08-21T08:00:01Z" + } + } + ], + "timestamp": "2026-08-21T08:00:01Z" +} diff --git a/packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json.license b/packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json b/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json index 4f241454..877fadac 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|1|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:1" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|active-energy-burned|source-active-energy" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:hUNfkRjsHis9rWj4K6qNLC-jFLQKKWL2Uh6UYxYBy9I" } } ], - "fullUrl": "urn:uuid:4fa8c437-0d58-5efd-b9da-a69f1b0e4ac3", + "fullUrl": "urn:uuid:623544dc-4d5f-5814-9d3d-3064f83c39fa", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-active-energy", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation" ] }, "extension": [ @@ -50,23 +66,31 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|active-energy-burned|source-active-energy" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:Vu0a2YcqoF0_Kmt53rvO840SVdAxu8nPfHv7sZnTABw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:hUNfkRjsHis9rWj4K6qNLC-jFLQKKWL2Uh6UYxYBy9I", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [ { @@ -75,12 +99,17 @@ } ] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-20T08:00:00.000-07:00", "end": "2026-08-20T09:00:00.000-07:00" }, - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 312.5, "unit": "kcal", @@ -92,14 +121,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:likPDb5FjrpWDRZ0rPo5XUmjfcOT7rGpsg5T6dCWODs" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:2c249665-5425-548b-a009-422bd516b0e0", "resource": { "resourceType": "Device", "meta": { @@ -109,8 +146,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:likPDb5FjrpWDRZ0rPo5XUmjfcOT7rGpsg5T6dCWODs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -125,14 +170,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:e2j1gdQw2SwNKr5tOCv1YnM8xF-NxUolN2oVdQ9vrMI" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:f6cd5dc4-f09e-56b0-a457-afd61347504e", "resource": { "resourceType": "Device", "meta": { @@ -142,8 +195,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:e2j1gdQw2SwNKr5tOCv1YnM8xF-NxUolN2oVdQ9vrMI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -172,24 +233,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|1|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:BzzQh80b6pPy829ESNyCiTXdG-DRf0tbuj1NZT0GvtA" } } ], - "fullUrl": "urn:uuid:31ec9e26-8e34-5117-b7d5-3caf0dc60afc", + "fullUrl": "urn:uuid:1bb2312b-a95b-5608-9fa7-61308ca3d104", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:4fa8c437-0d58-5efd-b9da-a69f1b0e4ac3" } + { "reference": "urn:uuid:623544dc-4d5f-5814-9d3d-3064f83c39fa" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -214,7 +284,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:f6cd5dc4-f09e-56b0-a457-afd61347504e" } } ], @@ -223,8 +293,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|active-energy-burned|source-active-energy" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:Vu0a2YcqoF0_Kmt53rvO840SVdAxu8nPfHv7sZnTABw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -240,7 +318,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:2c249665-5425-548b-a009-422bd516b0e0" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json b/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json index c753f9d1..e953257e 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|2|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:2" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|basal-energy-burned|source-basal-energy" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:tof8kHTSRnd3Qox8cyYy7Trj59RBB5ctSu7xwykXafk" } } ], - "fullUrl": "urn:uuid:6ec71145-b100-5578-8823-2ef0091d0c15", + "fullUrl": "urn:uuid:ba77bf34-3274-5ee3-85b3-0846d7870737", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-basal-energy", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation" ] }, "extension": [ @@ -50,23 +66,31 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|basal-energy-burned|source-basal-energy" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:Nc4nBJazF3IhkS_aoV_cRugPi_PauEGuVoAeb3JUJ48", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:tof8kHTSRnd3Qox8cyYy7Trj59RBB5ctSu7xwykXafk", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [ { @@ -75,12 +99,17 @@ } ] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-20T08:00:00.000-07:00", "end": "2026-08-20T09:00:00.000-07:00" }, - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 68.4, "unit": "kcal", @@ -92,14 +121,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:rOfDhVWbEC3J_msDqtXEJ2Qb5M-0HSfHFMAeOvkC0qs" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:420ade87-e452-505a-a153-f069b0e88265", "resource": { "resourceType": "Device", "meta": { @@ -109,8 +146,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:rOfDhVWbEC3J_msDqtXEJ2Qb5M-0HSfHFMAeOvkC0qs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -125,14 +170,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:qEp85FoL6U1CRgvupplIUAz4Fo1KjSRFndZLUU8kTRU" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:927c2fbb-a5c2-5ea7-aa96-08b3f15d66e9", "resource": { "resourceType": "Device", "meta": { @@ -142,8 +195,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:qEp85FoL6U1CRgvupplIUAz4Fo1KjSRFndZLUU8kTRU", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -172,24 +233,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|2|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:b4gGw5G3fzDLigIZMN9EC50VyLvstLXvjY2l3oy_sGw" } } ], - "fullUrl": "urn:uuid:a5c908b9-295f-5609-8720-4a413f3212c6", + "fullUrl": "urn:uuid:4916b7cd-f8d1-5ddb-9a14-a55731877f53", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:6ec71145-b100-5578-8823-2ef0091d0c15" } + { "reference": "urn:uuid:ba77bf34-3274-5ee3-85b3-0846d7870737" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -214,7 +284,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:927c2fbb-a5c2-5ea7-aa96-08b3f15d66e9" } } ], @@ -223,8 +293,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|basal-energy-burned|source-basal-energy" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:Nc4nBJazF3IhkS_aoV_cRugPi_PauEGuVoAeb3JUJ48", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -240,7 +318,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:420ade87-e452-505a-a153-f069b0e88265" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json b/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json index 6f92a0a2..40a04db4 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|3|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:3" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|blood-glucose|source-blood-glucose-unspecified-specimen" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:wU-7VZxFRZGNCrwlBOECjEPYmjl7mAsK_kyXmHtaG68" } } ], - "fullUrl": "urn:uuid:d22a19d8-ac4e-5b01-a3d8-2e30a4fd4072", + "fullUrl": "urn:uuid:5695bce2-b1c9-50c9-9b24-1f74cfd2defa", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-blood-glucose-unspecified-specimen", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation" ] }, "extension": [ @@ -50,29 +66,42 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|blood-glucose|source-blood-glucose-unspecified-specimen" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:_fLMU3KSLn8Iu61CRQYpPS9H2OBRz_7V1QWJXSnD5Eo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:wU-7VZxFRZGNCrwlBOECjEPYmjl7mAsK_kyXmHtaG68", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "2339-0" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-20T07:45:00.120-07:00", - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 104, "unit": "mg/dL", @@ -84,14 +113,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:TV7SWW_WZgyeo8sglzZ0jExkXwIoRn8k2JuNLXWBkvo" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:b52e371d-2d28-5eff-8423-775113d08e3c", "resource": { "resourceType": "Device", "meta": { @@ -101,8 +138,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:TV7SWW_WZgyeo8sglzZ0jExkXwIoRn8k2JuNLXWBkvo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -117,14 +162,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:urPyM4-DaIvxKij6cJYUVuk_Af2lFfrZ60SlvcVCEUI" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:aba81b80-2d1e-5661-a24b-da63951e0c5f", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +187,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:urPyM4-DaIvxKij6cJYUVuk_Af2lFfrZ60SlvcVCEUI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -164,24 +225,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|3|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:RAfPzgJF0Ued-HNqD6NgoKm4LXPmhrIXjiswttjVAp4" } } ], - "fullUrl": "urn:uuid:3fdb1234-47b7-5454-9afb-3c7e88f44bfe", + "fullUrl": "urn:uuid:e550c720-a79b-5fea-952b-26807d890f23", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:d22a19d8-ac4e-5b01-a3d8-2e30a4fd4072" } + { "reference": "urn:uuid:5695bce2-b1c9-50c9-9b24-1f74cfd2defa" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -206,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:aba81b80-2d1e-5661-a24b-da63951e0c5f" } } ], @@ -215,8 +285,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|blood-glucose|source-blood-glucose-unspecified-specimen" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:_fLMU3KSLn8Iu61CRQYpPS9H2OBRz_7V1QWJXSnD5Eo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -232,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:b52e371d-2d28-5eff-8423-775113d08e3c" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json b/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json index 3d026296..6ced73a5 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|4|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:4" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:9+10|source-blood-pressure" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:KjMs_5Z98EM7M1K6bocf3dhsJaVnjtZ5pzvqNerZCgk" } } ], - "fullUrl": "urn:uuid:1f8fa372-3649-5278-adb4-8ca22fe44df2", + "fullUrl": "urn:uuid:7b8c648b-2755-5849-a83e-a3c2f79a10b3", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-blood-pressure", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" ] }, "extension": [ @@ -50,8 +66,28 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:9+10|source-blood-pressure" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:lW2AnLDPykLNrwVliVjBURtgQxUmjX14dyMmjwJjvXE", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:KjMs_5Z98EM7M1K6bocf3dhsJaVnjtZ5pzvqNerZCgk", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], "status": "final", @@ -70,9 +106,14 @@ "code": { "coding": [{ "system": "http://loinc.org", "code": "85354-9" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-20T08:10:00.000-07:00", - "issued": "2026-08-20T12:01:00Z", "component": [ { "code": { @@ -102,14 +143,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:0pxSLYyzgHNNruap6eMTXhN-D9vApkbHKf6UOCkWeAA" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:5d0c9510-347f-59ad-8cc5-12b5551dfb7a", "resource": { "resourceType": "Device", "meta": { @@ -119,8 +168,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:0pxSLYyzgHNNruap6eMTXhN-D9vApkbHKf6UOCkWeAA", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -135,14 +192,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:NGLBYjP9Dq9zj_e4R4WphOIbfaj2rO1IKS5xlMjOMXo" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:c4ae7203-7b58-54c2-9169-35a2deecadda", "resource": { "resourceType": "Device", "meta": { @@ -152,8 +217,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:NGLBYjP9Dq9zj_e4R4WphOIbfaj2rO1IKS5xlMjOMXo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -182,24 +255,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|4|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:O906sk_10OKvpUfAlH82r4aVcwSp-nvQW9AmjNVVczw" } } ], - "fullUrl": "urn:uuid:d09ac50b-9a38-523a-aff4-b5ca6491beee", + "fullUrl": "urn:uuid:acb0d854-54f9-57ae-b2f2-9d176e85df51", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:1f8fa372-3649-5278-adb4-8ca22fe44df2" } + { "reference": "urn:uuid:7b8c648b-2755-5849-a83e-a3c2f79a10b3" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -224,7 +306,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:c4ae7203-7b58-54c2-9169-35a2deecadda" } } ], @@ -233,8 +315,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:9+10|source-blood-pressure" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:lW2AnLDPykLNrwVliVjBURtgQxUmjX14dyMmjwJjvXE", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -250,7 +340,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:5d0c9510-347f-59ad-8cc5-12b5551dfb7a" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json b/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json index 57b5e0eb..13fba565 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|5|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:5" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:6|source-body-fat-percentage" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:qcWNB4ioRgosOhigXqLo236vVq8UmpeMjxz5ftYVVO0" } } ], - "fullUrl": "urn:uuid:b9e2bc87-bd00-5e44-9cc8-a0248ee1aa6c", + "fullUrl": "urn:uuid:64c1ff29-36a9-5284-aba0-d243656cd1c8", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-fat-percentage", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" ] }, "extension": [ @@ -50,29 +66,42 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:6|source-body-fat-percentage" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:sQp4MBeBxJ8xNX6Ab5Nrs1AVbUT_mkjrLUGAcraOIAw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:qcWNB4ioRgosOhigXqLo236vVq8UmpeMjxz5ftYVVO0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "41982-0" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-21T07:12:04.000-07:00", - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 23.5, "unit": "%", @@ -84,14 +113,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:V-eifyyiDduzD3EwBiqbiC4OBL8skWdZdKR8wWlUjgE" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:7406c83a-c51d-513e-98c4-2d9e85d3ffef", "resource": { "resourceType": "Device", "meta": { @@ -101,8 +138,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:V-eifyyiDduzD3EwBiqbiC4OBL8skWdZdKR8wWlUjgE", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -117,14 +162,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:hHFSobV3E0PtndBhIBT_buq1Zk0VXreuN_3z0ZnKWII" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:62183acb-8e9d-5e2f-a65b-7229964065df", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +187,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:hHFSobV3E0PtndBhIBT_buq1Zk0VXreuN_3z0ZnKWII", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -164,24 +225,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|5|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:y9p0wl98ovA-2fnCmvTJypjA5Vlrx-SVxkrXrzQcryw" } } ], - "fullUrl": "urn:uuid:ac2fbd76-4c3e-5a05-bcad-4270e25678fd", + "fullUrl": "urn:uuid:a809c0d3-d9d2-5d86-8d78-39031a41dc35", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:b9e2bc87-bd00-5e44-9cc8-a0248ee1aa6c" } + { "reference": "urn:uuid:64c1ff29-36a9-5284-aba0-d243656cd1c8" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -206,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:62183acb-8e9d-5e2f-a65b-7229964065df" } } ], @@ -215,8 +285,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:6|source-body-fat-percentage" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:sQp4MBeBxJ8xNX6Ab5Nrs1AVbUT_mkjrLUGAcraOIAw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -232,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:7406c83a-c51d-513e-98c4-2d9e85d3ffef" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-height.json b/packages/fhir/fixtures/conformance/resources/mobile-body-height.json index 0aa95f5e..f46d326b 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-height.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-height.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|6|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:6" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:4|source-body-height" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:d9Fng8711pHYFae9CkmqLfUQ9sDxtP_JVAbEzQyJCkY" } } ], - "fullUrl": "urn:uuid:f6f3a537-b5ff-50a1-b657-b4e968a96adf", + "fullUrl": "urn:uuid:de5c75b0-e861-5954-9378-0f603d75a2ad", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-height", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" ] }, "extension": [ @@ -50,8 +66,28 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:4|source-body-height" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:hoaya6AcIo6qhTyA_H_REs0Ru4wkdnTVGAvabB6eGZs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:d9Fng8711pHYFae9CkmqLfUQ9sDxtP_JVAbEzQyJCkY", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], "status": "final", @@ -70,9 +106,14 @@ "code": { "coding": [{ "system": "http://loinc.org", "code": "8302-2" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-20T08:15:00.000-07:00", - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 171.2, "unit": "cm", @@ -84,14 +125,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:Roe1aB4-W2k8fuYsrh-IoJwUdt26cVWv9NRthHBNn_U" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:9ba44d16-edab-5b14-96c5-6c306777cedf", "resource": { "resourceType": "Device", "meta": { @@ -101,8 +150,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:Roe1aB4-W2k8fuYsrh-IoJwUdt26cVWv9NRthHBNn_U", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -117,14 +174,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:gAR8INBvNzlTMKzvBj2ghpzHndeTFSEalg-VJPykKhY" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:11353867-ec18-5c06-b35c-93f4e9710098", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +199,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:gAR8INBvNzlTMKzvBj2ghpzHndeTFSEalg-VJPykKhY", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -164,24 +237,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|6|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:zP9yHJniUtAMG2YAmoyy26umVdVxnOOX9xBoboL0Ql8" } } ], - "fullUrl": "urn:uuid:39b72318-032b-5b02-b66d-629f24b9c48c", + "fullUrl": "urn:uuid:30a63761-a7f6-55e5-9850-5a5f47c77629", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:f6f3a537-b5ff-50a1-b657-b4e968a96adf" } + { "reference": "urn:uuid:de5c75b0-e861-5954-9378-0f603d75a2ad" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -206,7 +288,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:11353867-ec18-5c06-b35c-93f4e9710098" } } ], @@ -215,8 +297,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:4|source-body-height" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:hoaya6AcIo6qhTyA_H_REs0Ru4wkdnTVGAvabB6eGZs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -232,7 +322,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:9ba44d16-edab-5b14-96c5-6c306777cedf" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json b/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json index 3fdbf90e..d67ddb7c 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|7|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:7" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|core-body-temperature|source-body-temperature" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:yoER3Zd7tX6f4ByZmKSfaeqYkkTEE-rPc2Qt59XTclw" } } ], - "fullUrl": "urn:uuid:e26a6d6a-e280-536b-b7e0-87776416a1bd", + "fullUrl": "urn:uuid:521ec2f4-8138-5538-9a7f-df0690eb0b6a", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-temperature", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation" ] }, "extension": [ @@ -50,8 +66,28 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|core-body-temperature|source-body-temperature" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:E84Nt2m-BegHKDiHFkVDfyWk3AVMdm12xG1ODcKbVZk", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:yoER3Zd7tX6f4ByZmKSfaeqYkkTEE-rPc2Qt59XTclw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], "status": "final", @@ -70,9 +106,14 @@ "code": { "coding": [{ "system": "http://loinc.org", "code": "8310-5" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-20T08:20:00.000-07:00", - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 37.1, "unit": "Cel", @@ -84,14 +125,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:FedpU6eibK7zsoMuQdcOL6RNBo39KXsBTI5QU7xDzKc" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:c0980002-c5b7-5c31-98ff-f4a484408c56", "resource": { "resourceType": "Device", "meta": { @@ -101,8 +150,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:FedpU6eibK7zsoMuQdcOL6RNBo39KXsBTI5QU7xDzKc", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -117,14 +174,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:YYAwvFTpHuMZtZr_Wcsu4RaZ2q-2TTylSrkKdx67Rdw" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:6d00c90c-e221-5856-9cdc-8b0174a6abb3", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +199,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:YYAwvFTpHuMZtZr_Wcsu4RaZ2q-2TTylSrkKdx67Rdw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -164,24 +237,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|7|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:M-J3CJ8bnST_su7f0CJSdRhl4krNugSlFE3mUX_nb9I" } } ], - "fullUrl": "urn:uuid:ae1ec112-8951-516a-a166-069e5b8ea3a9", + "fullUrl": "urn:uuid:30383105-87de-58b7-884a-5e06fee030bc", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:e26a6d6a-e280-536b-b7e0-87776416a1bd" } + { "reference": "urn:uuid:521ec2f4-8138-5538-9a7f-df0690eb0b6a" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -206,7 +288,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:6d00c90c-e221-5856-9cdc-8b0174a6abb3" } } ], @@ -215,8 +297,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|core-body-temperature|source-body-temperature" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:E84Nt2m-BegHKDiHFkVDfyWk3AVMdm12xG1ODcKbVZk", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -232,7 +322,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:c0980002-c5b7-5c31-98ff-f4a484408c56" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json b/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json index cdf385d7..56d4b10b 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|8|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:8" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:77|source-body-water-mass" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:h9kGKBCHIgGjAcm9EiiOm6sfAbCpmB-GQy1oykjz8wQ" } } ], - "fullUrl": "urn:uuid:e6a0b057-eddb-5412-a920-2eab118ef502", + "fullUrl": "urn:uuid:b7f5136c-eea4-5438-aa80-2fb29ef82696", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-water-mass", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" ] }, "extension": [ @@ -50,29 +66,42 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:77|source-body-water-mass" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:5XQDUzVcIcQNjBpRWfS-I_BT2KpigimyXXwA682S-5s", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:h9kGKBCHIgGjAcm9EiiOm6sfAbCpmB-GQy1oykjz8wQ", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "101683-1" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-21T07:12:04.000-07:00", - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 38.2, "unit": "kg", @@ -84,14 +113,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:-2zVZ4txzSTB6-XppERPQK6oqqL0SwJTW9J7jOh0EIc" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:a2c48014-937d-5005-bed8-e7ef0b0bacda", "resource": { "resourceType": "Device", "meta": { @@ -101,8 +138,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:-2zVZ4txzSTB6-XppERPQK6oqqL0SwJTW9J7jOh0EIc", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -117,14 +162,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:GaBqtWuQqnijksVa1NTG0qeo1smeMumnpwkkQCGTa9M" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:aff37b9c-be2f-5c75-8259-05362029c74d", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +187,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:GaBqtWuQqnijksVa1NTG0qeo1smeMumnpwkkQCGTa9M", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -164,24 +225,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|8|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:_YhVUy6nf5scLFK7jX-qoNE217b3rRBucnx8z1tKU30" } } ], - "fullUrl": "urn:uuid:e450870a-7a09-513a-ae56-8e4c93b6e867", + "fullUrl": "urn:uuid:861a9ac4-08d6-5415-8df7-1d5e9fe82d0b", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:e6a0b057-eddb-5412-a920-2eab118ef502" } + { "reference": "urn:uuid:b7f5136c-eea4-5438-aa80-2fb29ef82696" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -206,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:aff37b9c-be2f-5c75-8259-05362029c74d" } } ], @@ -215,8 +285,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:77|source-body-water-mass" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:5XQDUzVcIcQNjBpRWfS-I_BT2KpigimyXXwA682S-5s", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -232,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:a2c48014-937d-5005-bed8-e7ef0b0bacda" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json b/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json index 3727ae93..5fdbb11f 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|9|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:9" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:1|source-body-weight" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:ViQ5nBSYkWaGjgIOfrC0Cs6GI6uRnUKVuVs8dsOUYJo" } } ], - "fullUrl": "urn:uuid:0ed69292-8608-512b-8f9c-322acca73207", + "fullUrl": "urn:uuid:7fc5da96-9f22-5cde-8cc9-b889045f5390", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-weight", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" ] }, "extension": [ @@ -50,8 +66,28 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:1|source-body-weight" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:n430W8JBTayb8eVy_kI9tGK9M6ZDc2Dey0A5KzyXdyA", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:ViQ5nBSYkWaGjgIOfrC0Cs6GI6uRnUKVuVs8dsOUYJo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], "status": "final", @@ -70,9 +106,14 @@ "code": { "coding": [{ "system": "http://loinc.org", "code": "29463-7" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-20T08:25:00.000-07:00", - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 68.4, "unit": "kg", @@ -84,14 +125,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:pGpKK7LYGcor-3wzfgIdlwR28wJC2e-xMoTGNcmAQpw" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:d4fb1bec-f360-5dff-a871-80c79236b34e", "resource": { "resourceType": "Device", "meta": { @@ -101,8 +150,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:pGpKK7LYGcor-3wzfgIdlwR28wJC2e-xMoTGNcmAQpw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -117,14 +174,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:7_O0HF3rl6mXRn1oPfGTdra8dFBKlNiCuAJL_fNIrwo" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:9dc94f05-5c77-525c-99a7-e94f92715b33", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +199,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:7_O0HF3rl6mXRn1oPfGTdra8dFBKlNiCuAJL_fNIrwo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -164,24 +237,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|9|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:IHeCdNY_GBiWa47yEPhQw4qD35VQEtNQvlosx5HgZq4" } } ], - "fullUrl": "urn:uuid:3ca982c7-7eb0-5b00-ba98-1721fa019d72", + "fullUrl": "urn:uuid:05d228db-9f2f-5252-9b7a-e45dc027cad7", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:0ed69292-8608-512b-8f9c-322acca73207" } + { "reference": "urn:uuid:7fc5da96-9f22-5cde-8cc9-b889045f5390" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -206,7 +288,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:9dc94f05-5c77-525c-99a7-e94f92715b33" } } ], @@ -215,8 +297,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:1|source-body-weight" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:n430W8JBTayb8eVy_kI9tGK9M6ZDc2Dey0A5KzyXdyA", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -232,7 +322,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:d4fb1bec-f360-5dff-a871-80c79236b34e" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json b/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json index e740ab31..ee01fe59 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|10|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:10" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:88|source-bone-mass" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:3rbLqhNYeNnDB1dGdK0zMIURMBrsGPVl9tKRpYJuo6I" } } ], - "fullUrl": "urn:uuid:f1ac51fd-7ee2-5e4a-85b7-2e290d709def", + "fullUrl": "urn:uuid:7ee96abb-9672-5924-9053-7b8055044d12", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-bone-mass", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" ] }, "extension": [ @@ -50,29 +66,42 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:88|source-bone-mass" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:IS4eY2DFXzYkNRSYFYU_FS3PV5xgR7Zi-F0Ze6ro7yQ", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:3rbLqhNYeNnDB1dGdK0zMIURMBrsGPVl9tKRpYJuo6I", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "101685-6" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-21T07:12:04.000-07:00", - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 3.1, "unit": "kg", @@ -84,14 +113,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:AFhh8uFcvjFZUUjP2BbK6rQBbl8GVMtrSXoRkKADwW0" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:9aafe683-ff70-5e6f-8f2e-0d47708f715a", "resource": { "resourceType": "Device", "meta": { @@ -101,8 +138,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:AFhh8uFcvjFZUUjP2BbK6rQBbl8GVMtrSXoRkKADwW0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -117,14 +162,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:DI3p3QmuQOm_TqIxevxlk4n1IKGz6xKSz--LPt8C8uM" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:b890e4a8-8ed9-5c35-a930-90da5a89137b", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +187,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:DI3p3QmuQOm_TqIxevxlk4n1IKGz6xKSz--LPt8C8uM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -164,24 +225,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|10|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:4JYtJvg5s5dqWMDSu9_6kH1CJUkPDC5HoErSVze-_kw" } } ], - "fullUrl": "urn:uuid:660769ce-8d69-50fe-a69c-cb23b89f9482", + "fullUrl": "urn:uuid:88fa466b-024a-5d36-867a-eab9fc7fcc06", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:f1ac51fd-7ee2-5e4a-85b7-2e290d709def" } + { "reference": "urn:uuid:7ee96abb-9672-5924-9053-7b8055044d12" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -206,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:b890e4a8-8ed9-5c35-a930-90da5a89137b" } } ], @@ -215,8 +285,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:88|source-bone-mass" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:IS4eY2DFXzYkNRSYFYU_FS3PV5xgR7Zi-F0Ze6ro7yQ", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -232,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:9aafe683-ff70-5e6f-8f2e-0d47708f715a" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json index 7057ed51..40ecb6cb 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "oura|11|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:11" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-deep-sleep-duration" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:OhgkKcSIJTZQDgzR07JK97OEHcNiYmO-1lTQwjJ0ytc" } } ], - "fullUrl": "urn:uuid:3f88044c-89be-5cee-916e-1fcd1e737688", + "fullUrl": "urn:uuid:a3fdc563-2b7a-5c7c-ac3b-e40563166e02", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-deep-sleep-duration", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/oura/StructureDefinition/oura-observation" ] }, "extension": [ @@ -50,32 +66,45 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-deep-sleep-duration" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:t8d7IXwysPPh1De_qhKiAfN3dXsC9VwI6zZp6bD0l40", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:OhgkKcSIJTZQDgzR07JK97OEHcNiYmO-1lTQwjJ0ytc", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "93831-6" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-19T22:40:00.000-07:00", "end": "2026-08-20T06:55:00.000-07:00" }, - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 92, "unit": "min", @@ -97,14 +126,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:6vVZncgNn4pPXCqtE4LOWMgYCbfQYiefnOfilHR0EeU" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:41176715-cf6c-5230-998c-d9d68409bbd4", "resource": { "resourceType": "Device", "meta": { @@ -114,8 +151,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:6vVZncgNn4pPXCqtE4LOWMgYCbfQYiefnOfilHR0EeU", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -130,14 +175,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:SxKWGhLJZtu5LyrKvoAwvXfMJWLPrVR94fgdHyeVL5I" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:7284f88f-d0d5-50c7-84ba-c4658ec92c62", "resource": { "resourceType": "Device", "meta": { @@ -147,8 +200,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:SxKWGhLJZtu5LyrKvoAwvXfMJWLPrVR94fgdHyeVL5I", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -177,24 +238,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "oura|11|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:2begnAju9fHwtX1cps3EiMVhsqJYkbWkrUbKDDMFnUc" } } ], - "fullUrl": "urn:uuid:b3e9d1c8-59f7-5e95-a4bd-78850fef03af", + "fullUrl": "urn:uuid:38412fcd-f787-5fcc-b0a8-4cdc77728152", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:3f88044c-89be-5cee-916e-1fcd1e737688" } + { "reference": "urn:uuid:a3fdc563-2b7a-5c7c-ac3b-e40563166e02" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -219,7 +289,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:7284f88f-d0d5-50c7-84ba-c4658ec92c62" } } ], @@ -228,8 +298,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-deep-sleep-duration" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:t8d7IXwysPPh1De_qhKiAfN3dXsC9VwI6zZp6bD0l40", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -245,7 +323,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:41176715-cf6c-5230-998c-d9d68409bbd4" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-distance.json b/packages/fhir/fixtures/conformance/resources/mobile-distance.json index ec8fbf41..6cf7f27c 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-distance.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-distance.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|12|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:12" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|distance|source-distance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:4Xm4N-HGeL0wfT796USMczzqIt2_UYHgOgsym9eE3vs" } } ], - "fullUrl": "urn:uuid:a13fa60c-3127-59cf-9b97-a5450c67010a", + "fullUrl": "urn:uuid:285f706e-8106-5bc1-8968-b04f30f89a39", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-distance", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation" ] }, "extension": [ @@ -50,32 +66,45 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|distance|source-distance" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:ORDRv8X7Kiwng77Sq9Iy_lho8jcPec7cnXVvaevWFEc", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:4Xm4N-HGeL0wfT796USMczzqIt2_UYHgOgsym9eE3vs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "103208-5" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-20T07:00:00.000-07:00", "end": "2026-08-20T07:30:00.000-07:00" }, - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 4820.5, "unit": "m", @@ -87,14 +116,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:ljmH930Ln2GwRNJ8zxNH1GQ7UCL80JMwS1vxs7042fQ" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:9bd84bff-ca3b-5efe-b0d3-eac985a86209", "resource": { "resourceType": "Device", "meta": { @@ -104,8 +141,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:ljmH930Ln2GwRNJ8zxNH1GQ7UCL80JMwS1vxs7042fQ", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -120,14 +165,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:IfolXmU541fCYhtMJPmNDtLZzlUd_T7C1mUq7gOMVfQ" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:a7b17eb4-dca8-5357-ae47-f316ba331af1", "resource": { "resourceType": "Device", "meta": { @@ -137,8 +190,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:IfolXmU541fCYhtMJPmNDtLZzlUd_T7C1mUq7gOMVfQ", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -167,24 +228,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|12|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:3lMkEryrw0OilBdBD1v0h0IKbzsZmJOLiRi8-hTKXSg" } } ], - "fullUrl": "urn:uuid:c8a6fefb-2618-5988-aa79-2c39298a732b", + "fullUrl": "urn:uuid:2440341d-6a4c-5495-afb5-7bbd12bed542", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:a13fa60c-3127-59cf-9b97-a5450c67010a" } + { "reference": "urn:uuid:285f706e-8106-5bc1-8968-b04f30f89a39" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -209,7 +279,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:a7b17eb4-dca8-5357-ae47-f316ba331af1" } } ], @@ -218,8 +288,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|distance|source-distance" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:ORDRv8X7Kiwng77Sq9Iy_lho8jcPec7cnXVvaevWFEc", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -235,7 +313,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:9bd84bff-ca3b-5efe-b0d3-eac985a86209" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json b/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json index cd513e99..9923e319 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|13|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:13" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:196|source-electrodermal-activity" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:QaFq1UO4YCJMyKZETAa_k_mO7XWhwkPAPbA3hcsGo8o" } } ], - "fullUrl": "urn:uuid:6fc9d8dd-20dc-574b-a091-d5d6d87504ea", + "fullUrl": "urn:uuid:c13bb15f-8146-544c-8606-efcff6713d2f", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-electrodermal-activity", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" ] }, "extension": [ @@ -50,23 +66,31 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:196|source-electrodermal-activity" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:5Z-4dkeRWqP7Othp8dWZkFLwHhlt-6uPKljYJjYJ3PA", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:QaFq1UO4YCJMyKZETAa_k_mO7XWhwkPAPbA3hcsGo8o", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [ { @@ -75,9 +99,14 @@ } ] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-20T10:02:30.000-07:00", - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 2.4, "unit": "uS", @@ -89,14 +118,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:gH9QaLt9-FrRUuQwSFDiko_20-FUCzw_Qlc33lpfJqc" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:6b18ef20-a8e1-5263-a763-47b8e9f781c5", "resource": { "resourceType": "Device", "meta": { @@ -106,8 +143,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:gH9QaLt9-FrRUuQwSFDiko_20-FUCzw_Qlc33lpfJqc", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -122,14 +167,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:QAlMzQ1sodRgk8FLUo9kPs0ZiQCwdGwP2HOO4lqHSvs" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:04a5de5e-805e-5c55-9816-79186de3730b", "resource": { "resourceType": "Device", "meta": { @@ -139,8 +192,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:QAlMzQ1sodRgk8FLUo9kPs0ZiQCwdGwP2HOO4lqHSvs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -169,24 +230,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|13|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:2mMqPMmYFerH2KE9y48zxMXF-nP8yKqHHmY-AQnr9VY" } } ], - "fullUrl": "urn:uuid:9fd254a2-55b3-5c33-ae0a-bc2f9143d38d", + "fullUrl": "urn:uuid:83781f3b-a79d-57d5-876b-34897fc5e1d4", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:6fc9d8dd-20dc-574b-a091-d5d6d87504ea" } + { "reference": "urn:uuid:c13bb15f-8146-544c-8606-efcff6713d2f" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -211,7 +281,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:04a5de5e-805e-5c55-9816-79186de3730b" } } ], @@ -220,8 +290,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:196|source-electrodermal-activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:5Z-4dkeRWqP7Othp8dWZkFLwHhlt-6uPKljYJjYJ3PA", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -237,7 +315,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:6b18ef20-a8e1-5263-a763-47b8e9f781c5" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json b/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json index 089bbbee..17a9ed57 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|14|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:14" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|floors|source-flights-climbed" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:9F2FoOz7Fr0tgc4_x8ANXITQ-OOtmLTEO36VAPyq41w" } } ], - "fullUrl": "urn:uuid:e038a46f-4731-5083-9688-9d42886b755b", + "fullUrl": "urn:uuid:3f7f1483-cb67-5dd5-a801-f1cdc6ebd7fe", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-flights-climbed", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation" ] }, "extension": [ @@ -50,32 +66,45 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|floors|source-flights-climbed" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:KonZRzCBLyhG1NY4_mJRRtS4BIv4M3OnvYtdJ9Dwxs0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:9F2FoOz7Fr0tgc4_x8ANXITQ-OOtmLTEO36VAPyq41w", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "100304-5" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-20T08:00:00.000-07:00", "end": "2026-08-20T09:00:00.000-07:00" }, - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 6, "unit": "flights", @@ -87,14 +116,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:NHdlpVZDWK6LWi_l9gk-fjVVXVE2D1MOxg6WxZd2_wY" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:af49e404-2505-5279-92af-e801873a52eb", "resource": { "resourceType": "Device", "meta": { @@ -104,8 +141,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:NHdlpVZDWK6LWi_l9gk-fjVVXVE2D1MOxg6WxZd2_wY", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -120,14 +165,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:-FvHhIPZp52KT3IzFTM307FK_UaiyUvjk2AjHfLzc3Q" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:cf0df766-31c3-57f1-a1e8-fb71badac697", "resource": { "resourceType": "Device", "meta": { @@ -137,8 +190,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:-FvHhIPZp52KT3IzFTM307FK_UaiyUvjk2AjHfLzc3Q", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -167,24 +228,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|14|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:RsJoNU8RpAmAFZIgxNlT-RN4Bfyx5ZdJwVqkQMGv8rU" } } ], - "fullUrl": "urn:uuid:6a7a199b-318d-5d03-85cb-2f1ef8e7a76e", + "fullUrl": "urn:uuid:c76286dc-1f59-573d-8038-78b66e559228", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:e038a46f-4731-5083-9688-9d42886b755b" } + { "reference": "urn:uuid:3f7f1483-cb67-5dd5-a801-f1cdc6ebd7fe" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -209,7 +279,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:cf0df766-31c3-57f1-a1e8-fb71badac697" } } ], @@ -218,8 +288,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|floors|source-flights-climbed" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:KonZRzCBLyhG1NY4_mJRRtS4BIv4M3OnvYtdJ9Dwxs0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -235,7 +313,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:af49e404-2505-5279-92af-e801873a52eb" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json index a5d7cf86..754efa85 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|16|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:16" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|heart-rate-variability|source-heart-rate-variability-rmssd" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:ZnLIL6SSF3Gja1m0o306OZkJGQul41LPQn7hA5pWDM8" } } ], - "fullUrl": "urn:uuid:64bdd3cb-444f-5795-aa69-646b868dc687", + "fullUrl": "urn:uuid:0586ab21-3183-575d-9b79-ca7655102535", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate-variability-rmssd", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation" ] }, "extension": [ @@ -50,23 +66,31 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|heart-rate-variability|source-heart-rate-variability-rmssd" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:QKU7Mn9Azdb0uXzKWXJKx_4AIhS_qsa2-XVtYaelQKM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:ZnLIL6SSF3Gja1m0o306OZkJGQul41LPQn7hA5pWDM8", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [ { @@ -75,9 +99,14 @@ } ] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-20T06:58:12.500-07:00", - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 38, "unit": "ms", @@ -89,14 +118,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:o1xxMg_I2PM2GIkxuit_iyHr4nudEulGfq56MGinQTw" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:73e7cee5-b56e-52a1-b897-d7a2a9c2b4de", "resource": { "resourceType": "Device", "meta": { @@ -106,8 +143,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:o1xxMg_I2PM2GIkxuit_iyHr4nudEulGfq56MGinQTw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -122,14 +167,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:uA_D8Zy9-9gEoM-xCNwgrwaH7P60a3t2EkRQ1Tq1sf0" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:16aa054b-a1b0-56e8-bd63-ece074f4913b", "resource": { "resourceType": "Device", "meta": { @@ -139,8 +192,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:uA_D8Zy9-9gEoM-xCNwgrwaH7P60a3t2EkRQ1Tq1sf0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -169,24 +230,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|16|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:ggTth36Vc35tjDEjq0uTZwVVhE30YDaza0XeATNjWho" } } ], - "fullUrl": "urn:uuid:7466a142-fd83-5310-97e9-762516b22cd1", + "fullUrl": "urn:uuid:1d782e60-75b2-58de-b0f9-bbebb745af55", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:64bdd3cb-444f-5795-aa69-646b868dc687" } + { "reference": "urn:uuid:0586ab21-3183-575d-9b79-ca7655102535" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -211,7 +281,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:16aa054b-a1b0-56e8-bd63-ece074f4913b" } } ], @@ -220,8 +290,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|heart-rate-variability|source-heart-rate-variability-rmssd" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:QKU7Mn9Azdb0uXzKWXJKx_4AIhS_qsa2-XVtYaelQKM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -237,7 +315,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:73e7cee5-b56e-52a1-b897-d7a2a9c2b4de" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json index f6a49b1b..52dbc1a2 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|17|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:17" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|heart-rate-variability|source-heart-rate-variability-sdnn" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:60eY9xJ1BGUKk02Wzxgbw8I-KsJkvA6QmeqDHUea5cI" } } ], - "fullUrl": "urn:uuid:c3c2cb69-302f-527d-83bd-28b86cf4b78a", + "fullUrl": "urn:uuid:137fe5e0-b993-59e2-9bbb-d01b6822b871", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate-variability-sdnn", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation" ] }, "extension": [ @@ -50,29 +66,42 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|heart-rate-variability|source-heart-rate-variability-sdnn" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:3SuDnTSHb_y1kwQTccpLSh5rdP0Qva9KPCWHyEPmNvo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:60eY9xJ1BGUKk02Wzxgbw8I-KsJkvA6QmeqDHUea5cI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "112429-6" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-20T03:12:44.000-07:00", - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 54, "unit": "ms", @@ -84,14 +113,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:SH0IL9WB76tc2lwGI9J70EPhdFxAisS-WQ4aSdsR1Qg" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:9c3b7af2-e57b-5aa4-a641-f803c710cabd", "resource": { "resourceType": "Device", "meta": { @@ -101,8 +138,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:SH0IL9WB76tc2lwGI9J70EPhdFxAisS-WQ4aSdsR1Qg", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -117,14 +162,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:aONwM6Z7mj9j_sHLBy2z7oJ1BRb0j2uummA3OVRLzxs" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:a9c59fa6-4464-5e8a-a09b-34f4e6cb7597", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +187,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:aONwM6Z7mj9j_sHLBy2z7oJ1BRb0j2uummA3OVRLzxs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -164,24 +225,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|17|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:F8Eww65FNK0JXLReGmb39_Jelr5uRaoowR8DRk3D5TI" } } ], - "fullUrl": "urn:uuid:3faae20d-209d-5ab8-a149-480c4763567f", + "fullUrl": "urn:uuid:2114ba63-5ea2-5848-826a-94999484e6a2", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:c3c2cb69-302f-527d-83bd-28b86cf4b78a" } + { "reference": "urn:uuid:137fe5e0-b993-59e2-9bbb-d01b6822b871" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -206,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:a9c59fa6-4464-5e8a-a09b-34f4e6cb7597" } } ], @@ -215,8 +285,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|heart-rate-variability|source-heart-rate-variability-sdnn" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:3SuDnTSHb_y1kwQTccpLSh5rdP0Qva9KPCWHyEPmNvo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -232,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:9c3b7af2-e57b-5aa4-a641-f803c710cabd" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json index d3046262..41dcc64f 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|15|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:15" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:11|source-heart-rate" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:dyDbUB4KgbwSNmbNun1rnvosFUVDw3nUfZAShMJWJOw" } } ], - "fullUrl": "urn:uuid:f7304f6c-beb8-53aa-9d35-555432f7c9f6", + "fullUrl": "urn:uuid:20edb4a3-51d4-53a7-8d85-4232af3926f0", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" ] }, "extension": [ @@ -50,8 +66,28 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:11|source-heart-rate" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:i8sTWTfdx-WRqKbe-SxdHQSgUpcCHr-uHI7HrL08BQo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:dyDbUB4KgbwSNmbNun1rnvosFUVDw3nUfZAShMJWJOw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], "status": "final", @@ -70,9 +106,14 @@ "code": { "coding": [{ "system": "http://loinc.org", "code": "8867-4" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 72, "unit": "beats/minute", @@ -84,14 +125,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:rcaBJJ9K36ymlV0oqVjf6ocLjvTjeGh399SuNGTcBQ4" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:1989133d-9c27-565d-9aee-2aeaf956b28e", "resource": { "resourceType": "Device", "meta": { @@ -101,8 +150,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:rcaBJJ9K36ymlV0oqVjf6ocLjvTjeGh399SuNGTcBQ4", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -117,14 +174,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:8LYUDLu5wN6d4xiDgy4_fvWIsv-wpn3c9o6YNNhoreA" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:1c8ecea2-d107-5698-bddf-b038400227e2", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +199,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:8LYUDLu5wN6d4xiDgy4_fvWIsv-wpn3c9o6YNNhoreA", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -164,24 +237,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|15|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:kq4uOeM8fb1oCMVFL_tw7zK3r7M4GGjQltJcGikZA9U" } } ], - "fullUrl": "urn:uuid:d231c7e8-0b4b-58a1-a51b-da727a2b1361", + "fullUrl": "urn:uuid:d7cf8988-42eb-532b-a46b-652943a1fa88", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:f7304f6c-beb8-53aa-9d35-555432f7c9f6" } + { "reference": "urn:uuid:20edb4a3-51d4-53a7-8d85-4232af3926f0" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -206,7 +288,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:1c8ecea2-d107-5698-bddf-b038400227e2" } } ], @@ -215,8 +297,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:11|source-heart-rate" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:i8sTWTfdx-WRqKbe-SxdHQSgUpcCHr-uHI7HrL08BQo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -232,7 +322,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:1989133d-9c27-565d-9aee-2aeaf956b28e" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json b/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json index 372841b4..a5669c0c 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|18|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:18" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:5|source-lean-body-mass" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:M3mCXstdoV5mPI_uEj3BK2M2mx8ZwQNQqnhlH-3-RrA" } } ], - "fullUrl": "urn:uuid:7077f001-9cec-5e08-b3c6-a7ba5096025d", + "fullUrl": "urn:uuid:d8a72ccb-5213-5c4d-b1b6-3e0ef81351af", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-lean-body-mass", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" ] }, "extension": [ @@ -50,29 +66,42 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:5|source-lean-body-mass" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:VJf8NJTHb8NA1xWAzyXlFWzi3sDnTuY-trFQHK4STpY", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:M3mCXstdoV5mPI_uEj3BK2M2mx8ZwQNQqnhlH-3-RrA", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "91557-9" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-21T07:12:04.000-07:00", - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 55.3, "unit": "kg", @@ -84,14 +113,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:c9MTJ_MumzoBlID_WBUSFTXVLhhvmFPjMSlsZKDED5w" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:80c7119e-d3ad-5817-a256-b5dca5738a6c", "resource": { "resourceType": "Device", "meta": { @@ -101,8 +138,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:c9MTJ_MumzoBlID_WBUSFTXVLhhvmFPjMSlsZKDED5w", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -117,14 +162,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:gQzM9idbGlLqdXLn6VDj-P4JqwtgD1lkCR8t5VcAr2w" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:1f5c2740-cb04-5cc5-8350-07aaaf3e54d9", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +187,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:gQzM9idbGlLqdXLn6VDj-P4JqwtgD1lkCR8t5VcAr2w", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -164,24 +225,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|18|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:bXfTWoMfhRdyksa4P96FvJq43jM_vpSTy6IjL-ux9oQ" } } ], - "fullUrl": "urn:uuid:28c4d1cd-eef9-51fe-a87c-2c7f67f80a27", + "fullUrl": "urn:uuid:017be1e1-c42e-555d-b461-023ed631084e", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:7077f001-9cec-5e08-b3c6-a7ba5096025d" } + { "reference": "urn:uuid:d8a72ccb-5213-5c4d-b1b6-3e0ef81351af" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -206,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:1f5c2740-cb04-5cc5-8350-07aaaf3e54d9" } } ], @@ -215,8 +285,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:5|source-lean-body-mass" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:VJf8NJTHb8NA1xWAzyXlFWzi3sDnTuY-trFQHK4STpY", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -232,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:80c7119e-d3ad-5817-a256-b5dca5738a6c" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json index 09932528..27e05b20 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|19|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:19" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|sleep|source-light-sleep-duration" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:Kh4-pKwP8yYYMcNe6yowsX20rPUPq8QE-ewi7iJZiiQ" } } ], - "fullUrl": "urn:uuid:a8efa58e-ebb6-5388-8969-b24a60edd198", + "fullUrl": "urn:uuid:287eaf2f-5bed-5969-9ce1-034ade0e29c9", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-light-sleep-duration", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation" ] }, "extension": [ @@ -50,32 +66,45 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|sleep|source-light-sleep-duration" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:iQf-YAFLu0qdwElSD1qMoEnP6d-8L0ppBg4yoERcn6o", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:Kh4-pKwP8yYYMcNe6yowsX20rPUPq8QE-ewi7iJZiiQ", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "93830-8" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-19T22:40:00.000-07:00", "end": "2026-08-20T06:55:00.000-07:00" }, - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 214, "unit": "min", @@ -97,14 +126,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:KgMuJEdOw-zlw_Xe9dwVYDLfykPVUNqcl2imwF29i10" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:167ce386-5528-5010-80c2-f1925b415f32", "resource": { "resourceType": "Device", "meta": { @@ -114,8 +151,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:KgMuJEdOw-zlw_Xe9dwVYDLfykPVUNqcl2imwF29i10", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -130,14 +175,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:Hvt9NljrBk6a-V8nRriNE-BHIxfdnq7NIhNrOahXMTM" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:83a2b7a5-7a74-5e9d-94e8-86823f3662ca", "resource": { "resourceType": "Device", "meta": { @@ -147,8 +200,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:Hvt9NljrBk6a-V8nRriNE-BHIxfdnq7NIhNrOahXMTM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -177,24 +238,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|19|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:iQV6LGuHNuQ4oq-2qw8zD3mFvpJe_uwR5j7SkYy3FPE" } } ], - "fullUrl": "urn:uuid:68a25a2a-0ae8-5806-8fba-c0edefe00b03", + "fullUrl": "urn:uuid:8cc3ef28-3e9b-5647-a2bd-67061231df49", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:a8efa58e-ebb6-5388-8969-b24a60edd198" } + { "reference": "urn:uuid:287eaf2f-5bed-5969-9ce1-034ade0e29c9" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -219,7 +289,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:83a2b7a5-7a74-5e9d-94e8-86823f3662ca" } } ], @@ -228,8 +298,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|sleep|source-light-sleep-duration" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:iQf-YAFLu0qdwElSD1qMoEnP6d-8L0ppBg4yoERcn6o", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -245,7 +323,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:167ce386-5528-5010-80c2-f1925b415f32" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json index 03f56643..a522017d 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|21|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:21" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|daily-oxygen-saturation|source-oxygen-saturation-daily-average" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:pz2bc_14nzpN1nf7SeqIO64j0IeZx5pne3cGLUt9EvI" } } ], - "fullUrl": "urn:uuid:88490f63-9e39-5af8-a8d0-447fbeaade5e", + "fullUrl": "urn:uuid:ae129fff-cc5c-57ed-b2c9-e7b5251a5dc6", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-oxygen-saturation-daily-average", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation" ] }, "extension": [ @@ -50,32 +66,45 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|daily-oxygen-saturation|source-oxygen-saturation-daily-average" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:LJpPA6SgcBh3mS33fNozvvsvFZ7oF8s8BGlqzNRtHXc", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:pz2bc_14nzpN1nf7SeqIO64j0IeZx5pne3cGLUt9EvI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "103209-3" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-19T22:40:00.000-07:00", "end": "2026-08-20T06:55:00.000-07:00" }, - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 96.5, "unit": "%", @@ -97,14 +126,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:ExeKDTle-oh9jLx-X9p8xW5HikI4oKs7NlOS-7xTSjc" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:6e12b116-b0a3-54f0-88c1-6d1d7f3613af", "resource": { "resourceType": "Device", "meta": { @@ -114,8 +151,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:ExeKDTle-oh9jLx-X9p8xW5HikI4oKs7NlOS-7xTSjc", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -130,14 +175,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:V86cGZ2kLwl_AHtTzOq8rdDxjc7VlQ9ikQC3BSEkhes" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:b269432e-d07b-5e50-b2de-711a4e636220", "resource": { "resourceType": "Device", "meta": { @@ -147,8 +200,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:V86cGZ2kLwl_AHtTzOq8rdDxjc7VlQ9ikQC3BSEkhes", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -177,24 +238,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|21|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:m0hsqsMK-mI8buNMlPn7jXuJZeWsw38i-VBhiKzZtRs" } } ], - "fullUrl": "urn:uuid:9530191a-d2b8-5c01-b751-9ddf0fb03ad1", + "fullUrl": "urn:uuid:36b2b622-fd3f-58b0-9462-9933872d0def", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:88490f63-9e39-5af8-a8d0-447fbeaade5e" } + { "reference": "urn:uuid:ae129fff-cc5c-57ed-b2c9-e7b5251a5dc6" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -219,7 +289,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:b269432e-d07b-5e50-b2de-711a4e636220" } } ], @@ -228,8 +298,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|daily-oxygen-saturation|source-oxygen-saturation-daily-average" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:LJpPA6SgcBh3mS33fNozvvsvFZ7oF8s8BGlqzNRtHXc", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -245,7 +323,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:6e12b116-b0a3-54f0-88c1-6d1d7f3613af" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json index 98318da5..a5f1f1f5 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|20|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:20" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:54|source-oxygen-saturation" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:uC4-CLTnHTiVl6yISs2FQ53EwZmM56mWAxoFQzB3PZ0" } } ], - "fullUrl": "urn:uuid:0b637261-2d92-53f5-b4df-3f27b4a0fb6b", + "fullUrl": "urn:uuid:982583dd-9d31-5624-9952-fbc8218182c9", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-oxygen-saturation", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" ] }, "extension": [ @@ -50,8 +66,28 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:54|source-oxygen-saturation" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:rBfmxEHQDXZC8-jWUzQz8czUa64bqdkH8NckUE-RiR0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:uC4-CLTnHTiVl6yISs2FQ53EwZmM56mWAxoFQzB3PZ0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], "status": "final", @@ -70,9 +106,14 @@ "code": { "coding": [{ "system": "http://loinc.org", "code": "2708-6" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-20T08:35:00.000-07:00", - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 98, "unit": "%", @@ -84,14 +125,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:K3HkBVarnKxvVz-ItYI6QIobe5p66qLbAbgyRGj3ft4" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:504ac48f-1656-51fe-81ac-7757ecc4b958", "resource": { "resourceType": "Device", "meta": { @@ -101,8 +150,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:K3HkBVarnKxvVz-ItYI6QIobe5p66qLbAbgyRGj3ft4", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -117,14 +174,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:aBUSMXl5wcjkpazCl7Y_HAP7U3xodq9qgk76F91KxfQ" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:c72b00b2-52cb-5dc2-8a42-f9d8db034b1c", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +199,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:aBUSMXl5wcjkpazCl7Y_HAP7U3xodq9qgk76F91KxfQ", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -164,24 +237,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|20|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:axbqoCfibKd_O3FuzXZCr82AHhKET3ABs5qBRZEsLGM" } } ], - "fullUrl": "urn:uuid:eecf4b79-25b7-54c6-9338-4a9daf24bd6f", + "fullUrl": "urn:uuid:d31566dd-6f8c-5797-98e8-7e8bba8a52c3", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:0b637261-2d92-53f5-b4df-3f27b4a0fb6b" } + { "reference": "urn:uuid:982583dd-9d31-5624-9952-fbc8218182c9" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -206,7 +288,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:c72b00b2-52cb-5dc2-8a42-f9d8db034b1c" } } ], @@ -215,8 +297,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:54|source-oxygen-saturation" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:rBfmxEHQDXZC8-jWUzQz8czUa64bqdkH8NckUE-RiR0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -232,7 +322,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:504ac48f-1656-51fe-81ac-7757ecc4b958" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json index b4e9a271..08899c5a 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|22|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:22" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|sleep|source-rem-sleep-duration" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:74do0jSkuDfKuenVUUcy0Haqjndk7yEFyWA9053M-eY" } } ], - "fullUrl": "urn:uuid:d8ad0e63-29d8-5c65-aa7a-41e7cd9a63ac", + "fullUrl": "urn:uuid:02194ed4-cc34-5427-8086-fcc96af34e45", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-rem-sleep-duration", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation" ] }, "extension": [ @@ -50,32 +66,45 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|sleep|source-rem-sleep-duration" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:PzR6lkMO5KVY-dmv14Rd-vSvQe-8fjsMGWYQF3HT0fw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:74do0jSkuDfKuenVUUcy0Haqjndk7yEFyWA9053M-eY", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "93829-0" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-19T22:40:00.000-07:00", "end": "2026-08-20T06:55:00.000-07:00" }, - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 104, "unit": "min", @@ -97,14 +126,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:YCE1SS0miZABgACiuZAaNJojg0lELFd6skkEVCZAAB4" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:f02ebbf1-0679-56a7-bcb5-9cafc2ad8a61", "resource": { "resourceType": "Device", "meta": { @@ -114,8 +151,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:YCE1SS0miZABgACiuZAaNJojg0lELFd6skkEVCZAAB4", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -130,14 +175,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:BQ4HlGVpPu3nubyFC0IRbCDMaKBVW0K5MQ8fPLhGZw0" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:971871ab-2052-50d7-a122-c6bdbc8ccc9c", "resource": { "resourceType": "Device", "meta": { @@ -147,8 +200,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:BQ4HlGVpPu3nubyFC0IRbCDMaKBVW0K5MQ8fPLhGZw0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -177,24 +238,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|22|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:VjIqz9kMJoV2559rrdJqlPf1G0QnsAMsDZ3O3X_e9H8" } } ], - "fullUrl": "urn:uuid:51dbdcfc-a144-58dd-a2e9-90f331ad6b39", + "fullUrl": "urn:uuid:c48d93ce-bd44-52f3-bafd-22fb07c28c65", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:d8ad0e63-29d8-5c65-aa7a-41e7cd9a63ac" } + { "reference": "urn:uuid:02194ed4-cc34-5427-8086-fcc96af34e45" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -219,7 +289,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:971871ab-2052-50d7-a122-c6bdbc8ccc9c" } } ], @@ -228,8 +298,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|sleep|source-rem-sleep-duration" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:PzR6lkMO5KVY-dmv14Rd-vSvQe-8fjsMGWYQF3HT0fw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -245,7 +323,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:f02ebbf1-0679-56a7-bcb5-9cafc2ad8a61" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json b/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json index 5bae7c6e..abedd530 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "oura|23|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:23" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-respiratory-rate-average" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:wy9-Uph5fs_HJNnUTps5cFe8iYuZlikYcoNL7hgIOXk" } } ], - "fullUrl": "urn:uuid:c758f721-c73b-51f5-b81d-eadafad45a58", + "fullUrl": "urn:uuid:fc7e37fe-b91c-5bf1-a159-e6c33dd63f44", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-respiratory-rate-average", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/oura/StructureDefinition/oura-observation" ] }, "extension": [ @@ -50,32 +66,45 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-respiratory-rate-average" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:1rFsF0Psn65i5MhnBzXWzIJ__WF67rlZycsEC2JTkdg", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:wy9-Uph5fs_HJNnUTps5cFe8iYuZlikYcoNL7hgIOXk", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "103217-6" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-19T22:40:00.000-07:00", "end": "2026-08-20T06:55:00.000-07:00" }, - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 14.8, "unit": "breaths/minute", @@ -95,14 +124,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:qI6tkrqoQ3Kan47MQpPJX26Ad5HRTnk91pRTHWhIPD4" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:063ecd77-4aa0-5be2-ad80-1f9dd90394ec", "resource": { "resourceType": "Device", "meta": { @@ -112,8 +149,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:qI6tkrqoQ3Kan47MQpPJX26Ad5HRTnk91pRTHWhIPD4", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -128,14 +173,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:1dLX2SKN2UKqG7-cYWKWag_gvU92cRMTi5hXWQqbasM" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:bb6720e1-6b84-5c3e-95a1-da0f08b03cc5", "resource": { "resourceType": "Device", "meta": { @@ -145,8 +198,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:1dLX2SKN2UKqG7-cYWKWag_gvU92cRMTi5hXWQqbasM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -175,24 +236,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "oura|23|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:o3IalVxfclA_0ZXvjerPqhbrJGkXuRut15sEGBvzpTo" } } ], - "fullUrl": "urn:uuid:71f210b4-e20d-5f50-92c5-ee2a6e0eb06f", + "fullUrl": "urn:uuid:74b4fda5-d211-5795-9fad-98f795eb773b", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:c758f721-c73b-51f5-b81d-eadafad45a58" } + { "reference": "urn:uuid:fc7e37fe-b91c-5bf1-a159-e6c33dd63f44" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -217,7 +287,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:bb6720e1-6b84-5c3e-95a1-da0f08b03cc5" } } ], @@ -226,8 +296,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-respiratory-rate-average" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:1rFsF0Psn65i5MhnBzXWzIJ__WF67rlZycsEC2JTkdg", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -243,7 +321,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:063ecd77-4aa0-5be2-ad80-1f9dd90394ec" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json b/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json index 394f1f24..c73f03db 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|25|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:24" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:73|source-skin-temperature" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:OPgVVQo-3vB0P8wfU6EZgrmsFPE3ASKqJLQMzoZ6f_4" } } ], - "fullUrl": "urn:uuid:d92d95f4-c8f7-5a2d-9ed2-b64c71e2d17e", + "fullUrl": "urn:uuid:2306c7c3-ee62-52b9-a381-75b067203094", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-skin-temperature", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" ] }, "extension": [ @@ -50,29 +66,42 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:73|source-skin-temperature" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:rYPq44R2whk-DQQA4WbtFjoPYLBRjkVfi3j9Odic-Cw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:OPgVVQo-3vB0P8wfU6EZgrmsFPE3ASKqJLQMzoZ6f_4", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "61008-9" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-21T03:41:12.000-07:00", - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 34.9, "unit": "Cel", @@ -84,14 +113,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:7DRSkHTWPTJrwR7ZE_cfDazlFcNJv3aO5LEkTe9Qy8M" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:5e361e72-9af6-5a43-8982-e8e72d3acaa7", "resource": { "resourceType": "Device", "meta": { @@ -101,8 +138,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:7DRSkHTWPTJrwR7ZE_cfDazlFcNJv3aO5LEkTe9Qy8M", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -117,14 +162,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:lZ6KGumnLTW38-i20z8u_8kqp66JTj4af-1HVhejeFs" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:7026fc91-3968-58d8-8afa-4486d0d5f643", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +187,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:lZ6KGumnLTW38-i20z8u_8kqp66JTj4af-1HVhejeFs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -164,24 +225,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|25|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:xP9z_G8IUzJ4hRcWa8rTuns4Tr9rYS1bQM1r_I5YsCQ" } } ], - "fullUrl": "urn:uuid:c74fdd10-b50a-548d-be24-cb20feb4e1ca", + "fullUrl": "urn:uuid:1044c0b8-4e09-5ef5-ac6d-b6d44d86cb6a", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:d92d95f4-c8f7-5a2d-9ed2-b64c71e2d17e" } + { "reference": "urn:uuid:2306c7c3-ee62-52b9-a381-75b067203094" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -206,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:7026fc91-3968-58d8-8afa-4486d0d5f643" } } ], @@ -215,8 +285,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:73|source-skin-temperature" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:rYPq44R2whk-DQQA4WbtFjoPYLBRjkVfi3j9Odic-Cw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -232,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:5e361e72-9af6-5a43-8982-e8e72d3acaa7" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json index eb4ab86a..505189d6 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json @@ -6,39 +6,55 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "oura|26|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:25" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-sleep-awake-duration" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:sJcUcs4VwEVIa3dqXjiSo_vdAXo8cxdCDBwc4EdxSXo" } } ], - "fullUrl": "urn:uuid:780fbb29-081f-5df2-a97a-24960267baae", + "fullUrl": "urn:uuid:850e3576-5190-5c99-a859-2797a7263c8e", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-sleep-awake-duration", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation" ] }, "extension": [ { "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", - "valueCode": "oura" + "valueCode": "google-health-api" }, { "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", - "valueCode": "oura/sleep" + "valueCode": "google-health-api/sleep" }, { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", @@ -50,32 +66,45 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-sleep-awake-duration" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:m95PvVsdr0AKhMfUZt1K9o_5zlz9OmHPQLbHLBEsaIs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:sJcUcs4VwEVIa3dqXjiSo_vdAXo8cxdCDBwc4EdxSXo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "93828-2" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-19T22:40:00.000-07:00", "end": "2026-08-20T06:55:00.000-07:00" }, - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 31, "unit": "min", @@ -97,14 +126,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:RmXP5foemvhNM5kgvSo-8h9fhQD-abNyQ1-N5Na8-D8" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:5af1e779-4b55-5b83-afe0-f01908ec7d0f", "resource": { "resourceType": "Device", "meta": { @@ -114,8 +151,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:RmXP5foemvhNM5kgvSo-8h9fhQD-abNyQ1-N5Na8-D8", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -130,14 +175,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:LxTJK0oYfZ0GnmSQ7cNedoLg1Ug4k1UMiwRAU92ala0" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:2d89e8f7-ba76-5501-b905-0e045507e2ca", "resource": { "resourceType": "Device", "meta": { @@ -147,8 +200,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:LxTJK0oYfZ0GnmSQ7cNedoLg1Ug4k1UMiwRAU92ala0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -177,24 +238,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "oura|26|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:tnLyeUCtJuyPfvuAobQ0YR_MRoaluLng_oni4JWltng" } } ], - "fullUrl": "urn:uuid:c1317ed0-fcdb-5cfc-aaec-f74fef1e80e6", + "fullUrl": "urn:uuid:46525a86-3466-5731-88ff-5da588b95285", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:780fbb29-081f-5df2-a97a-24960267baae" } + { "reference": "urn:uuid:850e3576-5190-5c99-a859-2797a7263c8e" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -219,7 +289,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:2d89e8f7-ba76-5501-b905-0e045507e2ca" } } ], @@ -228,8 +298,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-sleep-awake-duration" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:m95PvVsdr0AKhMfUZt1K9o_5zlz9OmHPQLbHLBEsaIs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -245,7 +323,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:5af1e779-4b55-5b83-afe0-f01908ec7d0f" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json index 166d7a96..622cb759 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json @@ -6,39 +6,55 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|27|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:26" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|sleep|source-sleep-duration" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:NLUKdxH2Bu9L0FILQLNJCtIhFMmwOFfAJbLBVNdTgqU" } } ], - "fullUrl": "urn:uuid:2196ef69-d631-5ca9-b4f8-09c36888ddae", + "fullUrl": "urn:uuid:05244da9-1825-5826-b0a1-0c71861a6842", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-sleep-duration", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/oura/StructureDefinition/oura-observation" ] }, "extension": [ { "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", - "valueCode": "google-health-api" + "valueCode": "oura" }, { "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", - "valueCode": "google-health-api/sleep" + "valueCode": "oura/sleep" }, { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", @@ -50,32 +66,45 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|sleep|source-sleep-duration" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:ngybYnAViMXOMr2U5sQ4vlDLVyCqV8tjmEoLFIbtAd0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:NLUKdxH2Bu9L0FILQLNJCtIhFMmwOFfAJbLBVNdTgqU", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [{ "system": "http://loinc.org", "code": "93832-4" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-19T23:00:00.000-07:00", "end": "2026-08-20T06:30:00.000-07:00" }, - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 7.5, "unit": "h", @@ -87,14 +116,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:F-jVtzp1WAoTIXEgsgTOIS9j4hhepV_Di4WqS7_bSXM" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:1703fa05-5a33-5d03-8c55-b11fb257f88e", "resource": { "resourceType": "Device", "meta": { @@ -104,8 +141,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:F-jVtzp1WAoTIXEgsgTOIS9j4hhepV_Di4WqS7_bSXM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -120,14 +165,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:zPcfG7xgqixhjIIJ2woXGyq4HhuCrwEbG2VYjvBVdO0" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:6dc3aacc-4ed2-5935-868b-683217dd45ee", "resource": { "resourceType": "Device", "meta": { @@ -137,8 +190,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:zPcfG7xgqixhjIIJ2woXGyq4HhuCrwEbG2VYjvBVdO0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -167,24 +228,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|27|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:Xzycdixp8jYP3zAiTHoccUKLp0l2zUcUUzoyxaNHIdU" } } ], - "fullUrl": "urn:uuid:127ca2f2-f330-50e4-9834-8ad85f792dc2", + "fullUrl": "urn:uuid:9f1fb7d0-9f73-5bb0-abaa-2179683a9481", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:2196ef69-d631-5ca9-b4f8-09c36888ddae" } + { "reference": "urn:uuid:05244da9-1825-5826-b0a1-0c71861a6842" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -209,7 +279,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:6dc3aacc-4ed2-5935-868b-683217dd45ee" } } ], @@ -218,8 +288,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|sleep|source-sleep-duration" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:ngybYnAViMXOMr2U5sQ4vlDLVyCqV8tjmEoLFIbtAd0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -235,7 +313,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:1703fa05-5a33-5d03-8c55-b11fb257f88e" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json b/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json index 165e3ce3..4d86dab2 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json @@ -6,39 +6,55 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "oura|28|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:27" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-sleep-heart-rate" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:-ydU3t1w_BNle9TqOcbDNcouP-BX5H8BcRZo-twcKGk" } } ], - "fullUrl": "urn:uuid:d0725ae0-d782-5e01-8707-3297dccaa124", + "fullUrl": "urn:uuid:f4350c5c-addb-5b37-a6e4-f70b70c7c204", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-sleep-heart-rate", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" ] }, "extension": [ { "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", - "valueCode": "oura" + "valueCode": "withings" }, { "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", - "valueCode": "oura/sleep" + "valueCode": "withings/getsummary:hr_average" }, { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", @@ -50,23 +66,31 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-sleep-heart-rate" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:xbJ-iE9UQxhPtbJcTMFNlvl2E1rw_RFHGl-cR_afGps", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:-ydU3t1w_BNle9TqOcbDNcouP-BX5H8BcRZo-twcKGk", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [ { @@ -75,12 +99,17 @@ } ] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-19T22:40:00.000-07:00", "end": "2026-08-20T06:55:00.000-07:00" }, - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 52, "unit": "beats/minute", @@ -91,7 +120,7 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-aggregation-method", - "code": "session-minimum" + "code": "session-mean" } ] } @@ -100,14 +129,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:m4yPR6-T33YEk187BDUDcWabUI_5Wwu452efX4AaLm0" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:260fcfea-e93b-5a8f-adb7-20fe6a38b754", "resource": { "resourceType": "Device", "meta": { @@ -117,8 +154,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:m4yPR6-T33YEk187BDUDcWabUI_5Wwu452efX4AaLm0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -133,14 +178,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:CNHz0YMev5m3ShuC3Kebp4EwElYtojeBTFJYEIrg-D8" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:a9a32d06-04d9-5278-a924-670d7bc766b4", "resource": { "resourceType": "Device", "meta": { @@ -150,8 +203,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:CNHz0YMev5m3ShuC3Kebp4EwElYtojeBTFJYEIrg-D8", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -180,24 +241,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "oura|28|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:vrW_O3cLSq7pn0a-04qbdRgIM4yITIaOddDA4Qp1uOY" } } ], - "fullUrl": "urn:uuid:aa110c46-e856-54b7-8b81-1929ca1696ce", + "fullUrl": "urn:uuid:d8607389-4279-54a2-8f2e-2c8a2842e7fa", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:d0725ae0-d782-5e01-8707-3297dccaa124" } + { "reference": "urn:uuid:f4350c5c-addb-5b37-a6e4-f70b70c7c204" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -222,7 +292,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:a9a32d06-04d9-5278-a924-670d7bc766b4" } } ], @@ -231,8 +301,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-sleep-heart-rate" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:xbJ-iE9UQxhPtbJcTMFNlvl2E1rw_RFHGl-cR_afGps", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -248,7 +326,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:260fcfea-e93b-5a8f-adb7-20fe6a38b754" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-step-count.json b/packages/fhir/fixtures/conformance/resources/mobile-step-count.json index df99a710..69bbddaf 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-step-count.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-step-count.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|29|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:28" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|steps|source-step-count" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:WGqZAqQU-biHsH5k-uG4BKM95u30CHO8TRD8tQx-52o" } } ], - "fullUrl": "urn:uuid:4c2aa01f-1bf4-5a25-9200-6a54f3b9d695", + "fullUrl": "urn:uuid:438d43e3-26cf-55be-bc89-96acd497ede4", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-step-count", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation" ] }, "extension": [ @@ -50,23 +66,31 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|steps|source-step-count" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:zjV-47AY0X438_8imhgO2vHu27AE-x8kUCjG-nGh74o", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:WGqZAqQU-biHsH5k-uG4BKM95u30CHO8TRD8tQx-52o", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [ { @@ -75,12 +99,17 @@ } ] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-20T08:00:00.000-07:00", "end": "2026-08-20T09:00:00.000-07:00" }, - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 1042, "unit": "steps", @@ -92,14 +121,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:hj-500Omt5CToIwO7T2ZIXacSAreh3YcnPVvKvLxNII" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:d56441eb-cf67-585f-ba35-d3c329b65add", "resource": { "resourceType": "Device", "meta": { @@ -109,8 +146,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:hj-500Omt5CToIwO7T2ZIXacSAreh3YcnPVvKvLxNII", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -125,14 +170,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:VqZKJe_tfkaYF7ANsPk3PxPhF5EPlZGBfXkY6NyqMXg" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:0e2180df-172f-5273-a8d9-4705487cc527", "resource": { "resourceType": "Device", "meta": { @@ -142,8 +195,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:VqZKJe_tfkaYF7ANsPk3PxPhF5EPlZGBfXkY6NyqMXg", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -172,24 +233,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|29|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:p4Bb3RHFQNPqKMfld4OQ9J-nXopJz5GdWOIxaLug0R0" } } ], - "fullUrl": "urn:uuid:e1c60032-30a8-56b0-baa1-8e8437a0dc1d", + "fullUrl": "urn:uuid:5be6bdd9-d758-50dc-ae46-8252511af490", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:4c2aa01f-1bf4-5a25-9200-6a54f3b9d695" } + { "reference": "urn:uuid:438d43e3-26cf-55be-bc89-96acd497ede4" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -214,7 +284,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:0e2180df-172f-5273-a8d9-4705487cc527" } } ], @@ -223,8 +293,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|steps|source-step-count" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:zjV-47AY0X438_8imhgO2vHu27AE-x8kUCjG-nGh74o", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -240,7 +318,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:d56441eb-cf67-585f-ba35-d3c329b65add" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json b/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json index ba8a46f3..e9018251 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json @@ -6,39 +6,55 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|30|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:29" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:123|source-vo2-max" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:y1QuJ7hfGk7zwtwTHj5_Fwg8BipVuY9aQKIGY-YmGxY" } } ], - "fullUrl": "urn:uuid:015be0f5-1929-59b0-9ac6-8023f6af5079", + "fullUrl": "urn:uuid:027e2c38-29b6-5209-bf70-10f7c4948814", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-vo2-max", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/oura/StructureDefinition/oura-observation" ] }, "extension": [ { "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", - "valueCode": "withings" + "valueCode": "oura" }, { "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", - "valueCode": "withings/getmeas:123" + "valueCode": "oura/vO2_max" }, { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", @@ -50,23 +66,31 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:123|source-vo2-max" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:ezvIpUivUDmQBnlRULi9SnbNSB3d96yhaPRlBF21pjE", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:y1QuJ7hfGk7zwtwTHj5_Fwg8BipVuY9aQKIGY-YmGxY", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [ { @@ -75,9 +99,14 @@ } ] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectiveDateTime": "2026-08-20T09:15:00.000-07:00", - "issued": "2026-08-20T12:01:00Z", "valueQuantity": { "value": 41.2, "unit": "mL/kg/min", @@ -89,14 +118,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:_Y2QNP-2XNnfie17gelY7kaafP-2ltXEvQ76QhRr98w" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:addc23e6-931e-5948-81ad-c1361ccc3420", "resource": { "resourceType": "Device", "meta": { @@ -106,8 +143,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:_Y2QNP-2XNnfie17gelY7kaafP-2ltXEvQ76QhRr98w", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -122,14 +167,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:jLj49odbMlRoLaVLJX5O6SK82PjRttC5InrGJPz9zNk" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:f96648d5-5733-58cc-ae77-6ca173f0f942", "resource": { "resourceType": "Device", "meta": { @@ -139,8 +192,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:jLj49odbMlRoLaVLJX5O6SK82PjRttC5InrGJPz9zNk", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -169,24 +230,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|30|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:g-h8ZNobhWcYYUOfsE0AqLdRYDHq1Kq0imymekcXVLk" } } ], - "fullUrl": "urn:uuid:eada2e49-6c62-51d7-8027-046c4fc13408", + "fullUrl": "urn:uuid:920158b5-ec2c-5c17-9516-99dc7970345b", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:015be0f5-1929-59b0-9ac6-8023f6af5079" } + { "reference": "urn:uuid:027e2c38-29b6-5209-bf70-10f7c4948814" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -211,7 +281,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:f96648d5-5733-58cc-ae77-6ca173f0f942" } } ], @@ -220,8 +290,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|getmeas:123|source-vo2-max" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:ezvIpUivUDmQBnlRULi9SnbNSB3d96yhaPRlBF21pjE", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -237,7 +315,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:addc23e6-931e-5948-81ad-c1361ccc3420" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-workout.json b/packages/fhir/fixtures/conformance/resources/mobile-workout.json index 87ffeefc..4595e6ee 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-workout.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-workout.json @@ -6,39 +6,55 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|31|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:30" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|exercise|source-workout" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:bkcjuqFK9r7EYstavDxeB12JSLv5F6KNeLRYIkIadCA" } } ], - "fullUrl": "urn:uuid:f0c37ae9-349c-5bae-8a04-d53238902db3", + "fullUrl": "urn:uuid:113c473d-3cf3-57b5-bcf5-f19b16e4e8fc", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-workout", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" ] }, "extension": [ { "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", - "valueCode": "google-health-api" + "valueCode": "withings" }, { "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", - "valueCode": "google-health-api/exercise" + "valueCode": "withings/getworkouts:interval" }, { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", @@ -50,23 +66,31 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|exercise|source-workout" - } - ], - "status": "final", - "category": [ + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:thqKd9HD2EQem0OiJCH3nlctn93jJBEyqSdl78-66CM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:bkcjuqFK9r7EYstavDxeB12JSLv5F6KNeLRYIkIadCA", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], + "status": "final", "code": { "coding": [ { @@ -75,12 +99,17 @@ } ] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-20T17:00:00.000-07:00", "end": "2026-08-20T17:45:00.000-07:00" }, - "issued": "2026-08-20T12:01:00Z", "valueCodeableConcept": { "coding": [ { @@ -94,14 +123,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:BGuFhqjFeFfs0-Qgf00AVbu7_r1z5MDTmXMJNj3uCsc" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:3408d591-5cf1-501c-a88b-799d372ae788", "resource": { "resourceType": "Device", "meta": { @@ -111,8 +148,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:BGuFhqjFeFfs0-Qgf00AVbu7_r1z5MDTmXMJNj3uCsc", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -127,14 +172,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:EioXEBFCGCgE99RBqs323faTsPOkRTnlxatrnLU0tUg" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:86a0ab55-0f00-5d71-98e1-8a01504f10e1", "resource": { "resourceType": "Device", "meta": { @@ -144,8 +197,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:EioXEBFCGCgE99RBqs323faTsPOkRTnlxatrnLU0tUg", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -174,24 +235,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|31|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:UyapMnpmGzNs5utTNKfeWbDCVAmyEtlbo8r-rZ5J-Os" } } ], - "fullUrl": "urn:uuid:d41f5be1-4cdc-5796-8613-45b96964cc2d", + "fullUrl": "urn:uuid:09f73288-ea2d-58cb-afe5-56d5686e8f66", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:f0c37ae9-349c-5bae-8a04-d53238902db3" } + { "reference": "urn:uuid:113c473d-3cf3-57b5-bcf5-f19b16e4e8fc" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -216,7 +286,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:86a0ab55-0f00-5d71-98e1-8a01504f10e1" } } ], @@ -225,8 +295,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|exercise|source-workout" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:thqKd9HD2EQem0OiJCH3nlctn93jJBEyqSdl78-66CM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -242,7 +320,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:3408d591-5cf1-501c-a88b-799d372ae788" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json b/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json new file mode 100644 index 00000000..10b4a9d2 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json @@ -0,0 +1,340 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:31" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:-vWNbpGxC9yPeIH3Rb8iHCw6D2FqZ3VB-KfE2P0OtnI" + } + } + ], + "fullUrl": "urn:uuid:538b4b82-cb94-53fb-9ab2-b5ce4d3415d1", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-daily-resting-heart-rate", + "https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "google-health-api" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "google-health-api/daily-resting-heart-rate" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:jczOkpWlxBPJaIzeniJl_W2BzHPfkzt3WgRMZ7Dgo-A", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:-vWNbpGxC9yPeIH3Rb8iHCw6D2FqZ3VB-KfE2P0OtnI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/google-health/CodeSystem/google-health-measurement", + "code": "resting-heart-rate-daily-average" + } + ] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectivePeriod": { + "start": "2026-08-20T00:00:00.000-07:00", + "end": "2026-08-21T00:00:00.000-07:00" + }, + "valueQuantity": { + "value": 58, + "unit": "beats/minute", + "system": "http://unitsofmeasure.org", + "code": "/min" + }, + "method": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-aggregation-method", + "code": "daily-mean", + "display": "Daily mean" + } + ], + "text": "Daily mean" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:XD4XTH_UnJ7EfAUTg1oEF7qRPhx44wcJtlXM5oPuXA8" + } + } + ], + "fullUrl": "urn:uuid:070da471-11e4-5fe7-8cdd-17356844348c", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:XD4XTH_UnJ7EfAUTg1oEF7qRPhx44wcJtlXM5oPuXA8", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:ek76hvS0ALQ26mLLbodG0X0OsK2rCEqg5UCwDYex0WQ" + } + } + ], + "fullUrl": "urn:uuid:f3ac0fad-b36c-5381-b298-aa566cc1dda5", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:ek76hvS0ALQ26mLLbodG0X0OsK2rCEqg5UCwDYex0WQ", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:A_WStZ8unmNnjz-GX3YoyC80P2XUWHcih_dybPzOwak" + } + } + ], + "fullUrl": "urn:uuid:0ec4d89c-79f4-5e2b-8ebe-1cee16f8a08d", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:538b4b82-cb94-53fb-9ab2-b5ce4d3415d1" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:f3ac0fad-b36c-5381-b298-aa566cc1dda5" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:jczOkpWlxBPJaIzeniJl_W2BzHPfkzt3WgRMZ7Dgo-A", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:070da471-11e4-5fe7-8cdd-17356844348c" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json.license b/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json b/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json index e47f515a..8bb2deb2 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "oura|32|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:49" }, "type": "collection", - "timestamp": "2026-08-21T07:02:00Z", + "timestamp": "2026-08-21T07:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", - "value": "v1:source-oura-daily-activity|step-count" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:9LAbJZScNRkDfjciUcutA0SIA09N6yzddW6_40K7p04" } } ], - "fullUrl": "urn:uuid:8847e52b-0da0-5755-a027-e33cc367c692", + "fullUrl": "urn:uuid:60573ccb-7eba-55f7-84f5-bd080cd7a096", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-step-count", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/oura/StructureDefinition/oura-observation" ] }, "extension": [ @@ -50,27 +66,31 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-oura-daily-activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:0roAYJCspncl8UFF8gvN2_DKuBKmJsbXIBZsHKtAXks", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } }, { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", - "value": "v1:source-oura-daily-activity|step-count" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:9LAbJZScNRkDfjciUcutA0SIA09N6yzddW6_40K7p04", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], "status": "final", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" - } - ], "code": { "coding": [ { @@ -79,12 +99,17 @@ } ] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-20T00:00:00.000-07:00", "end": "2026-08-21T00:00:00.000-07:00" }, - "issued": "2026-08-21T07:01:00Z", "valueQuantity": { "value": 1042, "unit": "steps", @@ -96,20 +121,28 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", - "value": "v1:source-oura-daily-activity|active-energy" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:l6a6EFuUCQFFQSM6ea2u6YHgDGbN3H_GmOu8ulZc1Mg" } } ], - "fullUrl": "urn:uuid:c99ba8fc-e79e-5b15-bf68-bee5a5e5934b", + "fullUrl": "urn:uuid:fa3b3a4e-e36e-5866-ad5e-e44008b2a012", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-active-energy", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/oura/StructureDefinition/oura-observation" ] }, "extension": [ @@ -131,27 +164,31 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-oura-daily-activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:0roAYJCspncl8UFF8gvN2_DKuBKmJsbXIBZsHKtAXks", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } }, { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", - "value": "v1:source-oura-daily-activity|active-energy" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:l6a6EFuUCQFFQSM6ea2u6YHgDGbN3H_GmOu8ulZc1Mg", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], "status": "final", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" - } - ], "code": { "coding": [ { @@ -160,12 +197,17 @@ } ] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-20T00:00:00.000-07:00", "end": "2026-08-21T00:00:00.000-07:00" }, - "issued": "2026-08-21T07:01:00Z", "valueQuantity": { "value": 312.5, "unit": "kcal", @@ -177,20 +219,28 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", - "value": "v1:source-oura-daily-activity|distance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:ECIMIs_0Lq43DhjyaeYN6y9JmqtdNm92Ak311sANg_g" } } ], - "fullUrl": "urn:uuid:0aa056e7-092f-5cc6-901d-86a9427c89e0", + "fullUrl": "urn:uuid:34b3ee3a-7727-5e4b-a8a1-0d69e23ea85f", "resource": { "resourceType": "Observation", "meta": { "profile": [ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-distance", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" + "https://grovealliance.org/fhir/oura/StructureDefinition/oura-observation" ] }, "extension": [ @@ -212,36 +262,45 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-oura-daily-activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:0roAYJCspncl8UFF8gvN2_DKuBKmJsbXIBZsHKtAXks", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } }, { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", - "value": "v1:source-oura-daily-activity|distance" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:ECIMIs_0Lq43DhjyaeYN6y9JmqtdNm92Ak311sANg_g", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } } ], "status": "final", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" - } - ], "code": { "coding": [{ "system": "http://loinc.org", "code": "103208-5" }] }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "effectivePeriod": { "start": "2026-08-20T00:00:00.000-07:00", "end": "2026-08-21T00:00:00.000-07:00" }, - "issued": "2026-08-21T07:01:00Z", "valueQuantity": { "value": 4820.5, "unit": "m", @@ -253,14 +312,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:PuQZ4Xis65pRj9JXayG7RPqZX1y0_-MgpBJwt6Y6nPY" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:3977f4a9-344f-50a7-9bbb-5ece3b9fd015", "resource": { "resourceType": "Device", "meta": { @@ -270,8 +337,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:PuQZ4Xis65pRj9JXayG7RPqZX1y0_-MgpBJwt6Y6nPY", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -286,14 +361,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:BiLe29RLAJjeGcFc8Ya3zb-yEsOAsoKE_W8c4MaVQAE" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:05ae6de1-cddd-5b61-82e7-c12c0c35c749", "resource": { "resourceType": "Device", "meta": { @@ -303,8 +386,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:BiLe29RLAJjeGcFc8Ya3zb-yEsOAsoKE_W8c4MaVQAE", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -333,26 +424,35 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "oura|32|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:072e7erAPHzJqp18Iecwyt80VRYaOxcVfgeBpxHZBV4" } } ], - "fullUrl": "urn:uuid:8da79180-b7a2-5851-9af5-75e3b41a0d2f", + "fullUrl": "urn:uuid:a8fb3286-c6f3-54f9-ac8f-b749fb87a45c", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:8847e52b-0da0-5755-a027-e33cc367c692" }, - { "reference": "urn:uuid:c99ba8fc-e79e-5b15-bf68-bee5a5e5934b" }, - { "reference": "urn:uuid:0aa056e7-092f-5cc6-901d-86a9427c89e0" } + { "reference": "urn:uuid:60573ccb-7eba-55f7-84f5-bd080cd7a096" }, + { "reference": "urn:uuid:fa3b3a4e-e36e-5866-ad5e-e44008b2a012" }, + { "reference": "urn:uuid:34b3ee3a-7727-5e4b-a8a1-0d69e23ea85f" } ], + "occurredDateTime": "2026-08-21T07:00:00Z", "recorded": "2026-08-21T07:02:00Z", "activity": { "coding": [ @@ -377,7 +477,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:05ae6de1-cddd-5b61-82e7-c12c0c35c749" } } ], @@ -386,8 +486,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "source-oura-daily-activity" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:0roAYJCspncl8UFF8gvN2_DKuBKmJsbXIBZsHKtAXks", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -403,7 +511,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:3977f4a9-344f-50a7-9bbb-5ece3b9fd015" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json new file mode 100644 index 00000000..de065ef8 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json @@ -0,0 +1,330 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:32" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:w22OQpSd6BHV11gV3gGmjfypjG9h0hMzXj2HE0HMcK8" + } + } + ], + "fullUrl": "urn:uuid:98b9c214-2168-51ee-ba2a-c7b5c818d61e", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/oura/StructureDefinition/oura-cardiovascular-age", + "https://grovealliance.org/fhir/oura/StructureDefinition/oura-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "oura" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "oura/daily_cardiovascular_age" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:bMJIOFm8WX8UdsKhlujt9hPADkeh8DNMV-baupj_kKc", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:w22OQpSd6BHV11gV3gGmjfypjG9h0hMzXj2HE0HMcK8", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/oura/CodeSystem/oura-measurement", + "code": "oura-cardiovascular-age" + } + ] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectivePeriod": { + "start": "2026-08-20T00:00:00.000-07:00", + "end": "2026-08-21T00:00:00.000-07:00" + }, + "valueQuantity": { + "value": 38, + "unit": "years", + "system": "http://unitsofmeasure.org", + "code": "a" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:bA77wMxXOHtLwzFovIWuUCHgNM4OYv47m2GhPR2M9mk" + } + } + ], + "fullUrl": "urn:uuid:7978f9e9-eae2-547f-8313-10c3751a57c1", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:bA77wMxXOHtLwzFovIWuUCHgNM4OYv47m2GhPR2M9mk", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:HwWqIZL5n6dXYm-Cyg4WnPdjiB8LKJDQMaiiFFowQSU" + } + } + ], + "fullUrl": "urn:uuid:89b496ef-4b88-5d28-97b5-ab36d8b3136b", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:HwWqIZL5n6dXYm-Cyg4WnPdjiB8LKJDQMaiiFFowQSU", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:y1SHC2zDcK4svQBUYRcAFOtqE5Ls-qnowOMrIpPlhv4" + } + } + ], + "fullUrl": "urn:uuid:e37256bc-4b0a-5b06-9c2a-5bd9828e0652", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:98b9c214-2168-51ee-ba2a-c7b5c818d61e" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:89b496ef-4b88-5d28-97b5-ab36d8b3136b" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:bMJIOFm8WX8UdsKhlujt9hPADkeh8DNMV-baupj_kKc", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:7978f9e9-eae2-547f-8313-10c3751a57c1" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json.license b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json new file mode 100644 index 00000000..7ec2443e --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json @@ -0,0 +1,330 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:33" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:DhscoNlcMhvVGag5mNyUR5jZm81lFbCq5n4MYQw5oKY" + } + } + ], + "fullUrl": "urn:uuid:a1ffb3e5-3084-50fc-bb53-0432be9bf875", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/oura/StructureDefinition/oura-readiness-score", + "https://grovealliance.org/fhir/oura/StructureDefinition/oura-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "oura" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "oura/daily_readiness" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:3350h9qJT8w5YTMTSx7uhmcLA_4y5BtXnZFqtC6dufY", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:DhscoNlcMhvVGag5mNyUR5jZm81lFbCq5n4MYQw5oKY", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/oura/CodeSystem/oura-measurement", + "code": "oura-readiness-score" + } + ] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectivePeriod": { + "start": "2026-08-20T00:00:00.000-07:00", + "end": "2026-08-21T00:00:00.000-07:00" + }, + "valueQuantity": { + "value": 78, + "unit": "score", + "system": "http://unitsofmeasure.org", + "code": "{score}" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:PE3zgguOXvysbgnbG3pvgjmQmiab7WnkZYVAdiMhzGo" + } + } + ], + "fullUrl": "urn:uuid:1918f022-0cb1-5f71-b3c2-8b8409bffda4", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:PE3zgguOXvysbgnbG3pvgjmQmiab7WnkZYVAdiMhzGo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:MFvvceR7vuUbC4RDz6rnnbMhpYMwvyFCVyoxlSHblLs" + } + } + ], + "fullUrl": "urn:uuid:b1cda0b7-4158-538a-866f-4ec26427da1c", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:MFvvceR7vuUbC4RDz6rnnbMhpYMwvyFCVyoxlSHblLs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:hItGY9DfpHn8mUw3L6ZmtEBO8RCzDHmJH2KNL6OmKrU" + } + } + ], + "fullUrl": "urn:uuid:4bbeccf8-5a03-5054-a2dd-bb2094999b3a", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:a1ffb3e5-3084-50fc-bb53-0432be9bf875" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:b1cda0b7-4158-538a-866f-4ec26427da1c" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:3350h9qJT8w5YTMTSx7uhmcLA_4y5BtXnZFqtC6dufY", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:1918f022-0cb1-5f71-b3c2-8b8409bffda4" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json.license b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json b/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json new file mode 100644 index 00000000..e727f2cc --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json @@ -0,0 +1,207 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:54" + }, + "type": "collection", + "timestamp": "2026-08-22T12:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:p7ISqEJUX3HlUFGXpiEAAVaVoTJJxy2Lv3b45PaxdSI" + } + } + ], + "fullUrl": "urn:uuid:b9531c09-6f04-5de0-8675-11c561b531f2", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:p7ISqEJUX3HlUFGXpiEAAVaVoTJJxy2Lv3b45PaxdSI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:retraction-provenance:0:9L7-Yd2G07JqjIf8iCbf4uJUAYkiVssyT3BjYu9tI-U" + } + } + ], + "fullUrl": "urn:uuid:971dab9f-d023-5cf5-b51f-d061f6b127a3", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-provenance" + ] + }, + "target": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-role", + "valueCode": "device-snapshot" + } + ], + "type": "Device", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:e2j1gdQw2SwNKr5tOCv1YnM8xF-NxUolN2oVdQ9vrMI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-role", + "valueCode": "primary-output" + } + ], + "type": "Observation", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:hUNfkRjsHis9rWj4K6qNLC-jFLQKKWL2Uh6UYxYBy9I", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + } + ], + "occurredDateTime": "2026-08-22T12:00:00Z", + "recorded": "2026-08-22T12:01:00Z", + "activity": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-lifecycle-event", + "code": "source-record-retracted", + "display": "Source record retracted" + } + ], + "text": "Source record retracted" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:b9531c09-6f04-5de0-8675-11c561b531f2" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:Vu0a2YcqoF0_Kmt53rvO840SVdAxu8nPfHv7sZnTABw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + } + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json.license b/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json b/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json new file mode 100644 index 00000000..f1163b70 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json @@ -0,0 +1,207 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:55" + }, + "type": "collection", + "timestamp": "2026-08-22T13:02:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:33a-BZk3SZFIl0sZdByOUvq2dAR-nqSkxKy12Xfe6_4" + } + } + ], + "fullUrl": "urn:uuid:64fa1ccd-2ebb-5b5b-b195-ab88ed10b39d", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:33a-BZk3SZFIl0sZdByOUvq2dAR-nqSkxKy12Xfe6_4", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:retraction-provenance:0:n96he5rGWnKTKdpNDUxomZ1sZBTWwtRTuCahy_gEVcI" + } + } + ], + "fullUrl": "urn:uuid:dc3860eb-4aad-5cef-b6aa-abf719ff7f84", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-provenance" + ] + }, + "target": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-role", + "valueCode": "device-snapshot" + } + ], + "type": "Device", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:WclJMCQ6CUAslia5K2NFCa4BEWetQW0V2edf9a-EN_w", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-role", + "valueCode": "source-artifact" + } + ], + "type": "DocumentReference", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:dQm59jLLiwD0Zs3uVioH3S-V4hOzA3izmLiS1S94se4", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + } + ], + "occurredDateTime": "2026-08-22T13:00:00Z", + "recorded": "2026-08-22T13:01:00Z", + "activity": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-lifecycle-event", + "code": "source-record-retracted", + "display": "Source record retracted" + } + ], + "text": "Source record retracted" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:64fa1ccd-2ebb-5b5b-b195-ab88ed10b39d" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:RDmUrHr5N6bLAoEeTx0TRfsl2wf739glATitaeC3zlM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + } + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json.license b/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json new file mode 100644 index 00000000..0c439a33 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json @@ -0,0 +1,322 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:36" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:v-8pEBoDCpdxHB1EduRyPoyGXEc3pHIOPR2zGkc9oRM" + } + } + ], + "fullUrl": "urn:uuid:1ffd2014-0a78-5bff-b0f4-33b8c8fd6ef1", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-body-fat-mass", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:8" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:F3IWWxP6dTqYRs3P7KAs2vDdLPXma4GZxbyOuzxdgxE", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:v-8pEBoDCpdxHB1EduRyPoyGXEc3pHIOPR2zGkc9oRM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [{ "system": "http://loinc.org", "code": "73708-0" }] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectiveDateTime": "2026-08-20T12:00:00.000Z", + "valueQuantity": { + "value": 16.4, + "unit": "kg", + "system": "http://unitsofmeasure.org", + "code": "kg" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:gpUAhUBpb1SAO0tUA43n9NAneCosysH1I1S1QmW4Q_M" + } + } + ], + "fullUrl": "urn:uuid:0f67d388-b785-528d-bc7c-613df1a0c060", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:gpUAhUBpb1SAO0tUA43n9NAneCosysH1I1S1QmW4Q_M", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:nJQ0h6JsVl1eewTp7U2SNJ7_orKFQSR_lY2vw9ePkPQ" + } + } + ], + "fullUrl": "urn:uuid:0ca89bd1-0baf-537a-b308-ffb39af276f2", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:nJQ0h6JsVl1eewTp7U2SNJ7_orKFQSR_lY2vw9ePkPQ", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:UqkZ9h9kZT40Q-aG_nMJqKWoClDOYfrg6e0d3F9q3xo" + } + } + ], + "fullUrl": "urn:uuid:d781494c-ffbe-5f2b-bf3f-2d577dd03bd1", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:1ffd2014-0a78-5bff-b0f4-33b8c8fd6ef1" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:0ca89bd1-0baf-537a-b308-ffb39af276f2" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:F3IWWxP6dTqYRs3P7KAs2vDdLPXma4GZxbyOuzxdgxE", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:0f67d388-b785-528d-bc7c-613df1a0c060" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json b/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json new file mode 100644 index 00000000..bc114024 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json @@ -0,0 +1,322 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:43" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:Y_f5paLes1rrzJNq8zx_GrReUzpj2KYUPTrpXkL_bJE" + } + } + ], + "fullUrl": "urn:uuid:fb5ea01a-3278-568f-8e56-b0f6aa8de5d6", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-corrected-qt-interval", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:138" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:2KGZsgh2IbY6WKZvPoL3p0IxBcRnbFe0PuhBvkxkjnw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:Y_f5paLes1rrzJNq8zx_GrReUzpj2KYUPTrpXkL_bJE", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [{ "system": "http://loinc.org", "code": "8636-3" }] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectiveDateTime": "2026-08-20T12:00:00.000Z", + "valueQuantity": { + "value": 410, + "unit": "ms", + "system": "http://unitsofmeasure.org", + "code": "ms" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:Sdq2HZgzFAkzJWOp27GRuRf7f1_0rWpxT2W6lzRrt5g" + } + } + ], + "fullUrl": "urn:uuid:71f8e6b9-b929-5789-90a0-b8a2c35fc39f", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:Sdq2HZgzFAkzJWOp27GRuRf7f1_0rWpxT2W6lzRrt5g", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:GVSBZVw6mAYUk10Aby73QqtkVAuHIsdAWndd2Gi8Y-M" + } + } + ], + "fullUrl": "urn:uuid:6cdc1d6f-381f-50b2-b020-411e2984f81d", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:GVSBZVw6mAYUk10Aby73QqtkVAuHIsdAWndd2Gi8Y-M", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:wqnT_bJnHqJ3cIRz8F47Y6RFgvxjkmUC7Rde7rXxlQ4" + } + } + ], + "fullUrl": "urn:uuid:7125ec33-ab85-5760-9c15-7ad81d197b2f", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:fb5ea01a-3278-568f-8e56-b0f6aa8de5d6" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:6cdc1d6f-381f-50b2-b020-411e2984f81d" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:2KGZsgh2IbY6WKZvPoL3p0IxBcRnbFe0PuhBvkxkjnw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:71f8e6b9-b929-5789-90a0-b8a2c35fc39f" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json new file mode 100644 index 00000000..a0748faa --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json @@ -0,0 +1,327 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:46" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:aBHmnn17uPQJ1FOYWLjRJMYAsi-UoPoWectNN5p4YeI" + } + } + ], + "fullUrl": "urn:uuid:cb17c0b1-7dc2-51b8-a405-8d638c9e1137", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-extracellular-water-mass", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:168" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:Vf23EVGsuE6LzFmG94yu4Sbl68oSJPaC7ns20tgOTbE", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:aBHmnn17uPQJ1FOYWLjRJMYAsi-UoPoWectNN5p4YeI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", + "code": "extracellular-water-mass" + } + ] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectiveDateTime": "2026-08-20T12:00:00.000Z", + "valueQuantity": { + "value": 17, + "unit": "kg", + "system": "http://unitsofmeasure.org", + "code": "kg" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:wB83fOKGefr0oW9mSLAS-E4hfeiKpocLDy2z3UyF-Pg" + } + } + ], + "fullUrl": "urn:uuid:998938b2-c583-5020-842d-cd2ebcc2898a", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:wB83fOKGefr0oW9mSLAS-E4hfeiKpocLDy2z3UyF-Pg", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:xnn3tYFITW6OjbABhqa6GMrWWG34Vo0qTouz2wRhfcg" + } + } + ], + "fullUrl": "urn:uuid:80f93679-3b19-5963-9df6-d83960ee26df", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:xnn3tYFITW6OjbABhqa6GMrWWG34Vo0qTouz2wRhfcg", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:R5jJi9xKjjoAPm0B6cXb9MRGAPCmuWq6oTu0dnclFTY" + } + } + ], + "fullUrl": "urn:uuid:12e55e29-54eb-52c2-af3d-83345c5a4742", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:cb17c0b1-7dc2-51b8-a405-8d638c9e1137" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:80f93679-3b19-5963-9df6-d83960ee26df" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:Vf23EVGsuE6LzFmG94yu4Sbl68oSJPaC7ns20tgOTbE", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:998938b2-c583-5020-842d-cd2ebcc2898a" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json new file mode 100644 index 00000000..f090f52b --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json @@ -0,0 +1,327 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:47" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:qnp8sw7U_5k1jcdZ3Q5_WhNSk66zG6ResQPZGuIZNZM" + } + } + ], + "fullUrl": "urn:uuid:3999714a-bd10-5630-bff0-358ebb9066f0", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-intracellular-water-mass", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:169" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:67j8aU_KeUiEsRGYcYxStm8w4S96HpxbH1T82wX4YA0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:qnp8sw7U_5k1jcdZ3Q5_WhNSk66zG6ResQPZGuIZNZM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", + "code": "intracellular-water-mass" + } + ] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectiveDateTime": "2026-08-20T12:00:00.000Z", + "valueQuantity": { + "value": 25, + "unit": "kg", + "system": "http://unitsofmeasure.org", + "code": "kg" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:nobmhD2bLkWt4vIo-3U2581ybvFqZNMALB-nryW7XW4" + } + } + ], + "fullUrl": "urn:uuid:b0c56bad-8848-5bc3-ad2e-0c8dbe17a38f", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:nobmhD2bLkWt4vIo-3U2581ybvFqZNMALB-nryW7XW4", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:ialp3WiZS9pCIWlLwKySmhNIo2pN6D_IgaI_el6nYGU" + } + } + ], + "fullUrl": "urn:uuid:1638f3f5-1bd8-5781-a868-c9f0c1ee08d7", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:ialp3WiZS9pCIWlLwKySmhNIo2pN6D_IgaI_el6nYGU", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:qW4VHoMZF4T4_JhTUzAsWRxiO4JHnvi8KVp2N8aNRxM" + } + } + ], + "fullUrl": "urn:uuid:99011c44-9a29-53f6-8b76-d08299fff7fc", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:3999714a-bd10-5630-bff0-358ebb9066f0" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:1638f3f5-1bd8-5781-a868-c9f0c1ee08d7" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:67j8aU_KeUiEsRGYcYxStm8w4S96HpxbH1T82wX4YA0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:b0c56bad-8848-5bc3-ad2e-0c8dbe17a38f" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json new file mode 100644 index 00000000..573fc78c --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json @@ -0,0 +1,322 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:37" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:-plJxc895a7YvoL-kDFuPK_cEK-XXsMl7gcOgxhXDXQ" + } + } + ], + "fullUrl": "urn:uuid:1a18d23d-d6c7-55ef-bc0f-3c510d32db32", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-muscle-mass", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:76" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:vrz0pgc6vEQRz5vCFQ0zFUeVkbDKISXZ5jXSw2Sf2lI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:-plJxc895a7YvoL-kDFuPK_cEK-XXsMl7gcOgxhXDXQ", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [{ "system": "http://loinc.org", "code": "73964-9" }] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectiveDateTime": "2026-08-20T12:00:00.000Z", + "valueQuantity": { + "value": 31.5, + "unit": "kg", + "system": "http://unitsofmeasure.org", + "code": "kg" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:eg3cIAyec7MkCkm5Us4Na0GcK3jrnVQ4pMGAARybdrI" + } + } + ], + "fullUrl": "urn:uuid:0f8a9455-1b5e-525b-b08c-06a6125ac41a", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:eg3cIAyec7MkCkm5Us4Na0GcK3jrnVQ4pMGAARybdrI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:JMPo1bm4W-tUPoCj0950nF1Ctxy1mY-tk-xBhx7v7Mg" + } + } + ], + "fullUrl": "urn:uuid:98f4eb28-c7b2-56a6-bcc3-72811b9be3c1", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:JMPo1bm4W-tUPoCj0950nF1Ctxy1mY-tk-xBhx7v7Mg", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:ZI1k1uS2uXaZEf535CiSb8EAqdYMmpsTqNoe6sg8IUA" + } + } + ], + "fullUrl": "urn:uuid:5add0b87-845c-5acf-99e1-d473ac399eff", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:1a18d23d-d6c7-55ef-bc0f-3c510d32db32" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:98f4eb28-c7b2-56a6-bcc3-72811b9be3c1" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:vrz0pgc6vEQRz5vCFQ0zFUeVkbDKISXZ5jXSw2Sf2lI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:0f8a9455-1b5e-525b-b08c-06a6125ac41a" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json b/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json new file mode 100644 index 00000000..307f8d67 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json @@ -0,0 +1,322 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:41" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:8HT96eYiLbWjtbtVIwWUDixBE8r-i6SuUXCqEWs1IRk" + } + } + ], + "fullUrl": "urn:uuid:659fa6df-ec45-5897-8a85-b68df925ee29", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-pr-interval", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:136" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:OD56zfXSltzOVd2cSE1aOMqphMiPvMtEav-SEPxrxhk", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:8HT96eYiLbWjtbtVIwWUDixBE8r-i6SuUXCqEWs1IRk", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [{ "system": "http://loinc.org", "code": "8625-6" }] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectiveDateTime": "2026-08-20T12:00:00.000Z", + "valueQuantity": { + "value": 160, + "unit": "ms", + "system": "http://unitsofmeasure.org", + "code": "ms" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:Ba-kuQQnIk98NEVsqQQe7O-S5JbbkdSMJ8kE9yAl2Ho" + } + } + ], + "fullUrl": "urn:uuid:5ba8549c-cc98-5c68-b5c7-85824ba0cc8b", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:Ba-kuQQnIk98NEVsqQQe7O-S5JbbkdSMJ8kE9yAl2Ho", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:f_GbpxT669C97lP8rd5ol_9HzDfbAqnhPoC-fU16FXQ" + } + } + ], + "fullUrl": "urn:uuid:9a1660b3-378f-51e0-8b8b-6a4648072516", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:f_GbpxT669C97lP8rd5ol_9HzDfbAqnhPoC-fU16FXQ", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:QwHAjAj49BSmxEwHifopjWWYBgp4fiyGLl1HYHljgrE" + } + } + ], + "fullUrl": "urn:uuid:b65d3f35-883c-524a-a7cb-e48718b42409", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:659fa6df-ec45-5897-8a85-b68df925ee29" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:9a1660b3-378f-51e0-8b8b-6a4648072516" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:OD56zfXSltzOVd2cSE1aOMqphMiPvMtEav-SEPxrxhk", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:5ba8549c-cc98-5c68-b5c7-85824ba0cc8b" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json b/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json new file mode 100644 index 00000000..98f743d1 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json @@ -0,0 +1,322 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:40" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:-VrehvMePEy4QTFL6eqYg1_o1JaTVnaj2whlu5_56Eo" + } + } + ], + "fullUrl": "urn:uuid:ad5adfe1-83a9-5445-b973-a10e2ce6a637", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-qrs-duration", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:135" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:XgJtiUCD0cuBpIIXA5R3z4CYwW4K2fdEKeZwMJq--bc", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:-VrehvMePEy4QTFL6eqYg1_o1JaTVnaj2whlu5_56Eo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [{ "system": "http://loinc.org", "code": "8633-0" }] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectiveDateTime": "2026-08-20T12:00:00.000Z", + "valueQuantity": { + "value": 92, + "unit": "ms", + "system": "http://unitsofmeasure.org", + "code": "ms" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:5OysBeno7l6iCOCst3Uw4XDyQBea8geSJCEZEBRXDaI" + } + } + ], + "fullUrl": "urn:uuid:4e6093ee-1107-544d-8450-c4c51f156641", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:5OysBeno7l6iCOCst3Uw4XDyQBea8geSJCEZEBRXDaI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:QLLqe7qyiVlalpsljgKO_UNSsrDYq5ZBDbJhkV2oNXo" + } + } + ], + "fullUrl": "urn:uuid:51ad7b35-f4ab-57c5-a0ba-8a1e38717a98", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:QLLqe7qyiVlalpsljgKO_UNSsrDYq5ZBDbJhkV2oNXo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:zRFH4nYMDVzX2kUwWWNZvgTf-jEM6KiMTPVgdV6qFh4" + } + } + ], + "fullUrl": "urn:uuid:d5201df4-07ca-5c11-b841-9d19f4f7333a", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:ad5adfe1-83a9-5445-b973-a10e2ce6a637" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:51ad7b35-f4ab-57c5-a0ba-8a1e38717a98" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:XgJtiUCD0cuBpIIXA5R3z4CYwW4K2fdEKeZwMJq--bc", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:4e6093ee-1107-544d-8450-c4c51f156641" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json b/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json new file mode 100644 index 00000000..4a1cebf7 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json @@ -0,0 +1,322 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:42" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:NgpKwjtbacLLLIALkH5ljPqgtMspwLQHN0ih-LQeYkY" + } + } + ], + "fullUrl": "urn:uuid:0c0f2d86-7809-529e-8f39-4af267e8ee85", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-qt-interval", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:137" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:zE-GrKPuCrUM4HFNERcyX-hoimDBHlhm0g5nEnz8338", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:NgpKwjtbacLLLIALkH5ljPqgtMspwLQHN0ih-LQeYkY", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [{ "system": "http://loinc.org", "code": "8634-8" }] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectiveDateTime": "2026-08-20T12:00:00.000Z", + "valueQuantity": { + "value": 390, + "unit": "ms", + "system": "http://unitsofmeasure.org", + "code": "ms" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:k-WKPQzpF_j3Hw_bWyYnbA185sdWTT_kl-9sTR0JZ2o" + } + } + ], + "fullUrl": "urn:uuid:23362b25-5dcd-5d21-abed-93d18aba8c2a", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:k-WKPQzpF_j3Hw_bWyYnbA185sdWTT_kl-9sTR0JZ2o", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:oh1U7xktK50hOpwTsCdz_pWXcp1eaLsRrDTbjIdlgWs" + } + } + ], + "fullUrl": "urn:uuid:a78ce352-3518-5d01-bba4-514d95ab8260", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:oh1U7xktK50hOpwTsCdz_pWXcp1eaLsRrDTbjIdlgWs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:_ZMjCrqh1oRUDYwUpdo6mFJNt7n9lKQ1ijluft3sv6Y" + } + } + ], + "fullUrl": "urn:uuid:3b3c0a16-43e8-586b-80e9-e5f170de5224", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:0c0f2d86-7809-529e-8f39-4af267e8ee85" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:a78ce352-3518-5d01-bba4-514d95ab8260" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:zE-GrKPuCrUM4HFNERcyX-hoimDBHlhm0g5nEnz8338", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:23362b25-5dcd-5d21-abed-93d18aba8c2a" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json b/packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json new file mode 100644 index 00000000..f8e2c4da --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json @@ -0,0 +1,340 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:35" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:wfMYZsydK0NY4MYx71uNA3EwZ7vfQ4qsTWrfEH0aXnU" + } + } + ], + "fullUrl": "urn:uuid:f2b1fd23-c157-528e-814e-3e89d8f80d78", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-sleeping-heart-rate-average", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getsummary:hr_average" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:CZ2cmfO6offumxbR-gXU9SJ2_8Rk3AazW-0xdsjKQKs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:wfMYZsydK0NY4MYx71uNA3EwZ7vfQ4qsTWrfEH0aXnU", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", + "code": "sleeping-heart-rate-average" + } + ] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectivePeriod": { + "start": "2026-08-20T00:00:00.000-07:00", + "end": "2026-08-21T00:00:00.000-07:00" + }, + "valueQuantity": { + "value": 55, + "unit": "beats/minute", + "system": "http://unitsofmeasure.org", + "code": "/min" + }, + "method": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-aggregation-method", + "code": "session-mean", + "display": "Session mean" + } + ], + "text": "Session mean" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:-auiTPVO34UPW2ganrhpldZ8AZIo-OgZ2PX2ypqIBBk" + } + } + ], + "fullUrl": "urn:uuid:e013c037-4170-57d9-843e-9c4b51cbb6e8", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:-auiTPVO34UPW2ganrhpldZ8AZIo-OgZ2PX2ypqIBBk", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:GIryRZlH8NN1hVjxX6i0LR1Pp_kOBt0IbNQ7AYhCwnI" + } + } + ], + "fullUrl": "urn:uuid:d4fbec3e-a3d3-5c26-b821-0c8573aa7d92", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:GIryRZlH8NN1hVjxX6i0LR1Pp_kOBt0IbNQ7AYhCwnI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:I1GTBoaSyqEh7xIu6Ymum1Lt4REZ0iMmTuT7i3x9UyE" + } + } + ], + "fullUrl": "urn:uuid:2625ae16-19f8-5ade-b459-c026e2137adc", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:f2b1fd23-c157-528e-814e-3e89d8f80d78" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:d4fbec3e-a3d3-5c26-b821-0c8573aa7d92" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:CZ2cmfO6offumxbR-gXU9SJ2_8Rk3AazW-0xdsjKQKs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:e013c037-4170-57d9-843e-9c4b51cbb6e8" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json new file mode 100644 index 00000000..b1e60022 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json @@ -0,0 +1,329 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:39" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:emH-NR1xqg_vUxVL0hRTwagb5U_a19915i0i1-R6A2o" + } + } + ], + "fullUrl": "urn:uuid:8833e73d-b370-5a45-a5bb-25a1dae8416f", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-atrial-fibrillation-notification-ecg", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:130" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:mILmtdrsmxCMZfQbvWplAFWwisalKkizN8iGpnpOhko", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:emH-NR1xqg_vUxVL0hRTwagb5U_a19915i0i1-R6A2o", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", + "code": "withings-atrial-fibrillation-notification-ecg" + } + ] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectiveDateTime": "2026-08-20T12:00:00.000Z", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-atrial-fibrillation-notification-ecg", + "code": "signs-detected" + } + ] + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:Il9XmM9hhHHcq2duFtsKSaGGjcHi93bXcxP7PHWQqR0" + } + } + ], + "fullUrl": "urn:uuid:fe81dcbb-4115-5986-92e6-8cc9c77c8063", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:Il9XmM9hhHHcq2duFtsKSaGGjcHi93bXcxP7PHWQqR0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:tUlqK6yLqPm92UDowyv74VPf3boNLpZlH1b4cdpcewg" + } + } + ], + "fullUrl": "urn:uuid:cf01f8b8-9d69-5d62-9e8f-2da6ea54ad0c", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:tUlqK6yLqPm92UDowyv74VPf3boNLpZlH1b4cdpcewg", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:kXrlAZ8HIs0Hv-Ug_Za1nV5kPCTUMEQa_rbUDFAi3S0" + } + } + ], + "fullUrl": "urn:uuid:d831037e-f4f8-5e2f-b1dd-78b7ea7bf940", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:8833e73d-b370-5a45-a5bb-25a1dae8416f" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:cf01f8b8-9d69-5d62-9e8f-2da6ea54ad0c" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:mILmtdrsmxCMZfQbvWplAFWwisalKkizN8iGpnpOhko", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:fe81dcbb-4115-5986-92e6-8cc9c77c8063" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json new file mode 100644 index 00000000..54bd3f83 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json @@ -0,0 +1,329 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:44" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:zMUpq6PKQ7KQzFV8uvbij_qpG9P5KuVYCsi2UtsSWLo" + } + } + ], + "fullUrl": "urn:uuid:103b8762-3db0-5b9e-a96a-5c99e60c364a", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-atrial-fibrillation-notification-ppg", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:139" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:TyYVQi5gWz12InQQ1q-4jM-vE0zcxdOjPgZ96X9IToM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:zMUpq6PKQ7KQzFV8uvbij_qpG9P5KuVYCsi2UtsSWLo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", + "code": "withings-atrial-fibrillation-notification-ppg" + } + ] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectiveDateTime": "2026-08-20T12:00:00.000Z", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-atrial-fibrillation-notification-ppg", + "code": "signs-detected" + } + ] + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:wPHa9mRDNZ2mxN1Gr98bVc1AJsa3MfGoSU9OZ2rdSMI" + } + } + ], + "fullUrl": "urn:uuid:7386126f-fd74-5776-add6-742a2794a810", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:wPHa9mRDNZ2mxN1Gr98bVc1AJsa3MfGoSU9OZ2rdSMI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:dICuwOuxbMBV5mOe-F_OWanjaUBgCr24EmZj5UFx_0U" + } + } + ], + "fullUrl": "urn:uuid:9e0b38fe-b155-5efb-8f7d-4be794b626ef", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:dICuwOuxbMBV5mOe-F_OWanjaUBgCr24EmZj5UFx_0U", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:UHm6_5bDGuYRkyIs7yvhQoa9Hck6OcZncYXbYRKhVIk" + } + } + ], + "fullUrl": "urn:uuid:96053cec-a187-50c8-9865-3f450665fc15", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:103b8762-3db0-5b9e-a96a-5c99e60c364a" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:9e0b38fe-b155-5efb-8f7d-4be794b626ef" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:TyYVQi5gWz12InQQ1q-4jM-vE0zcxdOjPgZ96X9IToM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:7386126f-fd74-5776-add6-742a2794a810" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json new file mode 100644 index 00000000..376d50c3 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json @@ -0,0 +1,327 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:45" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:Nupjb6-6LRKMFNKdJZ_a8fpCGzOvIxiYPsXz1LCZx70" + } + } + ], + "fullUrl": "urn:uuid:69e4617e-0a17-5728-b431-3e326ee00b8f", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-nerve-health-score", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:167" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:e1WLlbtbT-04i-Z-dr6wkLIwmNjjc8wmlFH6AqzrSSA", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:Nupjb6-6LRKMFNKdJZ_a8fpCGzOvIxiYPsXz1LCZx70", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", + "code": "withings-nerve-health-score" + } + ] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectiveDateTime": "2026-08-20T12:00:00.000Z", + "valueQuantity": { + "value": 80, + "unit": "score", + "system": "http://unitsofmeasure.org", + "code": "{score}" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:hLXwYN5X8Ktb5SnrmBBsZt817xz7mTyLealZ933VboI" + } + } + ], + "fullUrl": "urn:uuid:cb6bbeb6-adbb-562b-aadd-d369bf9bd355", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:hLXwYN5X8Ktb5SnrmBBsZt817xz7mTyLealZ933VboI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:EDLAes5DVhwOatmQGgLPL6kCWu1Mi5dZYnxSIeZvyFw" + } + } + ], + "fullUrl": "urn:uuid:7711f83b-c805-5c21-a65b-1d9dfb63c36c", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:EDLAes5DVhwOatmQGgLPL6kCWu1Mi5dZYnxSIeZvyFw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:ulz23Bd4CnBDvPiT9TxdmtBhEg3PCfoPCQGyBPYZbDA" + } + } + ], + "fullUrl": "urn:uuid:e4510a70-4e18-5b49-9114-77eb0615051c", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:69e4617e-0a17-5728-b431-3e326ee00b8f" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:7711f83b-c805-5c21-a65b-1d9dfb63c36c" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:e1WLlbtbT-04i-Z-dr6wkLIwmNjjc8wmlFH6AqzrSSA", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:cb6bbeb6-adbb-562b-aadd-d369bf9bd355" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json new file mode 100644 index 00000000..01f0f51c --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json @@ -0,0 +1,327 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:38" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:FqQDl6lwjbaRTytFX2ebkYzxZVe6I2BvlaJ8l9WapRQ" + } + } + ], + "fullUrl": "urn:uuid:3743f2a7-4635-512e-8a7f-85f613ce5c51", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-pulse-wave-velocity", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:91" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:8V-A1z7HIta0HdzglUhddFLCMuke0tj4Jqalih8E1x4", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:FqQDl6lwjbaRTytFX2ebkYzxZVe6I2BvlaJ8l9WapRQ", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", + "code": "withings-pulse-wave-velocity" + } + ] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectiveDateTime": "2026-08-20T12:00:00.000Z", + "valueQuantity": { + "value": 7.5, + "unit": "m/s", + "system": "http://unitsofmeasure.org", + "code": "m/s" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:yYOwkwpAwZ1YIrHn2yudC_JTipVjnM09wA8xw08qJ60" + } + } + ], + "fullUrl": "urn:uuid:4ad85f24-e1e9-5944-8651-5bcc971c8a31", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:yYOwkwpAwZ1YIrHn2yudC_JTipVjnM09wA8xw08qJ60", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:9ObTGmgNyzr1T-rsn6TA3oVgR8ylKy35MLs-TWCHvXE" + } + } + ], + "fullUrl": "urn:uuid:a106321e-50be-5095-a4ec-12fe69677052", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:9ObTGmgNyzr1T-rsn6TA3oVgR8ylKy35MLs-TWCHvXE", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:OfCIr0vxkZEoRDf5mnkqOVlQQbZnYsvJgj4lmxoIg4o" + } + } + ], + "fullUrl": "urn:uuid:9cca95a8-a441-5a16-a7e6-524540e019d9", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:3743f2a7-4635-512e-8a7f-85f613ce5c51" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:a106321e-50be-5095-a4ec-12fe69677052" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:8V-A1z7HIta0HdzglUhddFLCMuke0tj4Jqalih8E1x4", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:4ad85f24-e1e9-5944-8651-5bcc971c8a31" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json new file mode 100644 index 00000000..409c62f8 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json @@ -0,0 +1,327 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:34" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:2iMBjOsXnAQIT45pZU9H5ypPRzY153KkVupDOHIKLWY" + } + } + ], + "fullUrl": "urn:uuid:90c159c5-309d-5400-99cf-4b1807c92576", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-vascular-age", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:155" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:C7K-KSVzTkMWqHwgrTYgZZCYsd9iE2EikLLsGcS4vJM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:2iMBjOsXnAQIT45pZU9H5ypPRzY153KkVupDOHIKLWY", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", + "code": "withings-vascular-age" + } + ] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectiveDateTime": "2026-08-20T12:00:00.000Z", + "valueQuantity": { + "value": 45, + "unit": "years", + "system": "http://unitsofmeasure.org", + "code": "a" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:VxnD6ESPP8cXKJKDtr5Q2bKTvbWorcFHs8-UJRtBgIQ" + } + } + ], + "fullUrl": "urn:uuid:af293fce-c17c-51f6-a74f-3820ff9dee04", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:VxnD6ESPP8cXKJKDtr5Q2bKTvbWorcFHs8-UJRtBgIQ", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:SSVrBCM7t7d-X19tAz_m4mRTShckrfCJdM-MHPil3eo" + } + } + ], + "fullUrl": "urn:uuid:e07b3c6b-6ee1-56e6-b378-0ab3b463b833", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:SSVrBCM7t7d-X19tAz_m4mRTShckrfCJdM-MHPil3eo", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:aB9HfOQ1q8VFIiwTHa2P4omQsDz3e2Hivq8hrsGsoII" + } + } + ], + "fullUrl": "urn:uuid:ce7089de-11a7-54aa-a2cb-d089f625c747", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:90c159c5-309d-5400-99cf-4b1807c92576" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:e07b3c6b-6ee1-56e6-b378-0ab3b463b833" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:C7K-KSVzTkMWqHwgrTYgZZCYsd9iE2EikLLsGcS4vJM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:af293fce-c17c-51f6-a74f-3820ff9dee04" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json new file mode 100644 index 00000000..8990a1f5 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json @@ -0,0 +1,327 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:48" + }, + "type": "collection", + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:tZvXJhhF-AF2LfpkVJT-2y113D1Uk4qI7gMYq8octas" + } + } + ], + "fullUrl": "urn:uuid:c36c4917-50f3-5369-a417-824e74dbfc2a", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-visceral-fat-index", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:170" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + } + ], + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:Mz7eIgHMyN-svW-aZcm0pfRUcHEGUeyhueiSGXYyToI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:tZvXJhhF-AF2LfpkVJT-2y113D1Uk4qI7gMYq8octas", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + } + ], + "status": "final", + "code": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", + "code": "withings-visceral-fat-index" + } + ] + }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, + "effectiveDateTime": "2026-08-20T12:00:00.000Z", + "valueQuantity": { + "value": 8, + "unit": "score", + "system": "http://unitsofmeasure.org", + "code": "{score}" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:grWuDgnsPw60xb8ySPI9U1FukEnmnHpQNMFkf0p0P_0" + } + } + ], + "fullUrl": "urn:uuid:1860727d-2576-55d5-95b1-c044cb1df722", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:grWuDgnsPw60xb8ySPI9U1FukEnmnHpQNMFkf0p0P_0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:TFt-5upyqZDRPM32U7kvf6w4yiNxc1rsvPizF1L4_Ug" + } + } + ], + "fullUrl": "urn:uuid:4d922b3a-5d55-5950-9be9-292492a0b071", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:TFt-5upyqZDRPM32U7kvf6w4yiNxc1rsvPizF1L4_Ug", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:pYi0YCuMa86bbjk85SR0AMiqhh_YQiX0vi0up-bemtk" + } + } + ], + "fullUrl": "urn:uuid:a8db880a-9ee1-5e06-a7ac-f3881280b431", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:c36c4917-50f3-5369-a417-824e74dbfc2a" } + ], + "occurredDateTime": "2026-08-20T12:00:00Z", + "recorded": "2026-08-20T12:02:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:4d922b3a-5d55-5950-9be9-292492a0b071" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:Mz7eIgHMyN-svW-aZcm0pfRUcHEGUeyhueiSGXYyToI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:1860727d-2576-55d5-95b1-c044cb1df722" + } + } + ] + } + ] + } + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/providers-oura-daily-activity.json b/packages/fhir/fixtures/conformance/resources/providers-oura-daily-activity.json deleted file mode 100644 index 3c968702..00000000 --- a/packages/fhir/fixtures/conformance/resources/providers-oura-daily-activity.json +++ /dev/null @@ -1,415 +0,0 @@ -{ - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, - "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-exchange-id", - "value": "v1:60bb2b3ed72a0994b1b52c43534fa8cbfbfb44c88dabcbb409143a1cad7738e1" - }, - "type": "collection", - "timestamp": "2026-08-21T07:02:00Z", - "entry": [ - { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", - "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", - "value": "v1:14fd42a645502a68c3c9b71d35118e0f7ffa0ba41c42277049caadcad4b72ec3" - } - } - ], - "fullUrl": "urn:uuid:3869cc0d-5ae0-5f2f-8cad-3733d3eb979a", - "resource": { - "resourceType": "Observation", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-step-count", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" - ] - }, - "extension": [ - { - "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", - "valueCode": "oura" - }, - { - "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", - "valueCode": "oura/daily_activity" - }, - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", - "valueCoding": { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", - "code": "automatically-recorded" - } - } - ], - "identifier": [ - { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:7826f42abcd8a2cb292e4cafeb55b072adeae56b36f781265f2cb29f201641fa" - }, - { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", - "value": "v1:14fd42a645502a68c3c9b71d35118e0f7ffa0ba41c42277049caadcad4b72ec3" - } - ], - "status": "final", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" - } - ], - "code": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", - "code": "step-count-total" - } - ] - }, - "subject": { "reference": "Patient/example" }, - "effectivePeriod": { - "start": "2026-08-20T00:00:00.000-07:00", - "end": "2026-08-21T00:00:00.000-07:00" - }, - "issued": "2026-08-21T07:01:00Z", - "valueQuantity": { - "value": 1042, - "unit": "steps", - "system": "http://unitsofmeasure.org", - "code": "{steps}" - } - } - }, - { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", - "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", - "value": "v1:2904c733b341853749925ef31880f8d374d001e19f29e996c7cbf82bdd9cc389" - } - } - ], - "fullUrl": "urn:uuid:c1692b07-4621-579e-b0b4-b672b34e0101", - "resource": { - "resourceType": "Observation", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-active-energy", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" - ] - }, - "extension": [ - { - "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", - "valueCode": "oura" - }, - { - "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", - "valueCode": "oura/daily_activity" - }, - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", - "valueCoding": { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", - "code": "automatically-recorded" - } - } - ], - "identifier": [ - { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:7826f42abcd8a2cb292e4cafeb55b072adeae56b36f781265f2cb29f201641fa" - }, - { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", - "value": "v1:2904c733b341853749925ef31880f8d374d001e19f29e996c7cbf82bdd9cc389" - } - ], - "status": "final", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" - } - ], - "code": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", - "code": "active-energy-burned" - } - ] - }, - "subject": { "reference": "Patient/example" }, - "effectivePeriod": { - "start": "2026-08-20T00:00:00.000-07:00", - "end": "2026-08-21T00:00:00.000-07:00" - }, - "issued": "2026-08-21T07:01:00Z", - "valueQuantity": { - "value": 312.5, - "unit": "kcal", - "system": "http://unitsofmeasure.org", - "code": "kcal" - } - } - }, - { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", - "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", - "value": "v1:aeadc4dcb2cbd9685a2668e343c954b65569d1cb67cd80261debe7eed4fff0e0" - } - } - ], - "fullUrl": "urn:uuid:4c76c5cc-cc6e-583e-824b-82ba0b6a8e94", - "resource": { - "resourceType": "Observation", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-distance", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" - ] - }, - "extension": [ - { - "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", - "valueCode": "oura" - }, - { - "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", - "valueCode": "oura/daily_activity" - }, - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", - "valueCoding": { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", - "code": "automatically-recorded" - } - } - ], - "identifier": [ - { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:7826f42abcd8a2cb292e4cafeb55b072adeae56b36f781265f2cb29f201641fa" - }, - { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id", - "value": "v1:aeadc4dcb2cbd9685a2668e343c954b65569d1cb67cd80261debe7eed4fff0e0" - } - ], - "status": "final", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" - } - ], - "code": { - "coding": [{ "system": "http://loinc.org", "code": "103208-5" }] - }, - "subject": { "reference": "Patient/example" }, - "effectivePeriod": { - "start": "2026-08-20T00:00:00.000-07:00", - "end": "2026-08-21T00:00:00.000-07:00" - }, - "issued": "2026-08-21T07:01:00Z", - "valueQuantity": { - "value": 4820.5, - "unit": "m", - "system": "http://unitsofmeasure.org", - "code": "m" - } - } - }, - { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", - "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" - } - } - ], - "fullUrl": "urn:uuid:7c06980e-c131-564a-9d90-26a23d9c1264", - "resource": { - "resourceType": "Device", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" - ] - }, - "identifier": [ - { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" - } - ], - "status": "active", - "deviceName": [ - { - "name": "Synthetic connected provider", - "type": "user-friendly-name" - } - ] - } - }, - { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", - "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" - } - } - ], - "fullUrl": "urn:uuid:ed2738ad-fee1-53b8-976d-0fd5b9512aa8", - "resource": { - "resourceType": "Device", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" - ] - }, - "identifier": [ - { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" - } - ], - "status": "active", - "deviceName": [ - { - "name": "Grove TypeScript conformance producer", - "type": "user-friendly-name" - } - ], - "version": [ - { - "type": { - "coding": [ - { - "system": "urn:iso:std:iso:11073:10101", - "code": "531975", - "display": "Software revision" - } - ] - }, - "value": "0.0.0" - } - ] - } - }, - { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", - "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-conversion-id", - "value": "v1:60bb2b3ed72a0994b1b52c43534fa8cbfbfb44c88dabcbb409143a1cad7738e1" - } - } - ], - "fullUrl": "urn:uuid:4eccfa53-5bce-5842-9ce5-cccf46447031", - "resource": { - "resourceType": "Provenance", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" - ] - }, - "target": [ - { "reference": "urn:uuid:3869cc0d-5ae0-5f2f-8cad-3733d3eb979a" }, - { "reference": "urn:uuid:c1692b07-4621-579e-b0b4-b672b34e0101" }, - { "reference": "urn:uuid:4c76c5cc-cc6e-583e-824b-82ba0b6a8e94" } - ], - "recorded": "2026-08-21T07:02:00Z", - "activity": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", - "code": "transform", - "display": "Transform/Translate Record Lifecycle Event" - } - ], - "text": "Transform/Translate Record Lifecycle Event" - }, - "agent": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", - "code": "assembler", - "display": "Assembler" - } - ], - "text": "Assembler" - }, - "who": { - "reference": "urn:uuid:ed2738ad-fee1-53b8-976d-0fd5b9512aa8" - } - } - ], - "entity": [ - { - "role": "source", - "what": { - "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:7826f42abcd8a2cb292e4cafeb55b072adeae56b36f781265f2cb29f201641fa" - } - }, - "agent": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", - "code": "enterer", - "display": "Enterer" - } - ], - "text": "Enterer" - }, - "who": { - "reference": "urn:uuid:7c06980e-c131-564a-9d90-26a23d9c1264" - } - } - ] - } - ] - } - } - ] -} diff --git a/packages/fhir/fixtures/conformance/resources/questionnaire-response.json b/packages/fhir/fixtures/conformance/resources/questionnaire-response.json index e15d377f..4ac2d32c 100644 --- a/packages/fhir/fixtures/conformance/resources/questionnaire-response.json +++ b/packages/fhir/fixtures/conformance/resources/questionnaire-response.json @@ -24,7 +24,7 @@ }, "questionnaire": "https://grovealliance.org/fhir/testing/Questionnaire/grove-ts-conformance|1.0.0", "status": "completed", - "subject": { "reference": "Patient/example" }, + "subject": { "reference": "Patient/example", "type": "Patient" }, "authored": "2026-08-20T12:00:00Z", "item": [ { diff --git a/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json b/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json index 1dbba174..1d4a19fe 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json +++ b/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|32|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:50" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|heart-rate|raw-source-google-health-api-1" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:dQm59jLLiwD0Zs3uVioH3S-V4hOzA3izmLiS1S94se4" } } ], - "fullUrl": "urn:uuid:310feab7-2f06-5d67-bbd1-989df3b9081b", + "fullUrl": "urn:uuid:7a8fa1d7-37d4-5dbd-a1f1-3b63629b18e0", "resource": { "resourceType": "DocumentReference", "meta": { "profile": [ "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document" ] }, "extension": [ @@ -43,16 +59,54 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|heart-rate|raw-source-google-health-api-1" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:RDmUrHr5N6bLAoEeTx0TRfsl2wf739glATitaeC3zlM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:dQm59jLLiwD0Zs3uVioH3S-V4hOzA3izmLiS1S94se4", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", + "value": "v2:fixture-key:1:F2IrGFyNF7wRiEXA_f_VCSiIVV_35FCfAj1wU3lnwXg", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-artifact" + } + ] + } } ], "status": "current", "type": { "text": "Google Health API heart-rate archive" }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "date": "2026-08-20T12:01:00Z", "author": [ - { "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" } + { "reference": "urn:uuid:f90fcdf9-1d5d-5f4d-8759-ce24ee11ecbf" } ], "content": [ { @@ -65,9 +119,9 @@ }, "format": { "system": "https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format", - "version": "0.5.0", "code": "provider-recording", - "display": "Provider Recording" + "display": "Provider Recording", + "version": "0.6.0" } } ] @@ -76,14 +130,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:QlkY7bt_QZvWGB92pst3f3Bmlv0vPJ9p12NzL-WcOIw" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:7b317e95-cd66-5207-9263-1192c80dd69d", "resource": { "resourceType": "Device", "meta": { @@ -93,8 +155,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:QlkY7bt_QZvWGB92pst3f3Bmlv0vPJ9p12NzL-WcOIw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -109,14 +179,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:WclJMCQ6CUAslia5K2NFCa4BEWetQW0V2edf9a-EN_w" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:f90fcdf9-1d5d-5f4d-8759-ce24ee11ecbf", "resource": { "resourceType": "Device", "meta": { @@ -126,8 +204,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:WclJMCQ6CUAslia5K2NFCa4BEWetQW0V2edf9a-EN_w", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -156,24 +242,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|32|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:tVYB3e_dIl_B-kam9oOpu0sMNeUG_mjlIHE00kv-jWk" } } ], - "fullUrl": "urn:uuid:c350fe22-82c5-5488-abcc-329047852480", + "fullUrl": "urn:uuid:341ccbad-8942-5258-835a-a930be090079", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:310feab7-2f06-5d67-bbd1-989df3b9081b" } + { "reference": "urn:uuid:7a8fa1d7-37d4-5dbd-a1f1-3b63629b18e0" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -198,7 +293,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:f90fcdf9-1d5d-5f4d-8759-ce24ee11ecbf" } } ], @@ -207,8 +302,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|heart-rate|raw-source-google-health-api-1" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:RDmUrHr5N6bLAoEeTx0TRfsl2wf739glATitaeC3zlM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -224,7 +327,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:7b317e95-cd66-5207-9263-1192c80dd69d" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json b/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json index 5fa64daf..8c0b0321 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json +++ b/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "oura|33|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:51" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "raw-source-oura-2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:q_Tx9qoh1QsMf_QtcyEXDvej3MG40dckUnPrtKrXVJg" } } ], - "fullUrl": "urn:uuid:780e25b2-19ee-5f0f-8d41-61d02be16019", + "fullUrl": "urn:uuid:b27eb87d-b169-54f7-bf36-08a7bef04a65", "resource": { "resourceType": "DocumentReference", "meta": { "profile": [ "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document" ] }, "extension": [ @@ -43,16 +59,54 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "raw-source-oura-2" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:ds91lAVuNGWeEI1UGr6VCMVH8PUszhnZV8nHUtrWeKs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:q_Tx9qoh1QsMf_QtcyEXDvej3MG40dckUnPrtKrXVJg", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", + "value": "v2:fixture-key:1:B2LqptJ5yQlG2j9eikA5NC0q7v_mnoEGTqlpOsRJS_4", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-artifact" + } + ] + } } ], "status": "current", "type": { "text": "Oura heartrate archive" }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "date": "2026-08-20T12:01:00Z", "author": [ - { "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" } + { "reference": "urn:uuid:3683e54f-a0b6-5883-aa38-fd65fea05471" } ], "content": [ { @@ -65,9 +119,9 @@ }, "format": { "system": "https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format", - "version": "0.5.0", "code": "provider-recording", - "display": "Provider Recording" + "display": "Provider Recording", + "version": "0.6.0" } } ] @@ -76,14 +130,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:9SAOPDcfMDEycjmgjHuzq3AwaXpGgcWTUfzvcka1clU" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:23210e76-49c2-5503-8a4e-f5a6ce47f389", "resource": { "resourceType": "Device", "meta": { @@ -93,8 +155,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:9SAOPDcfMDEycjmgjHuzq3AwaXpGgcWTUfzvcka1clU", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -109,14 +179,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:VQ3pqzf0ae4VagpWXQAO6_pSrmE6qUIJIK5gRgKEiz4" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:3683e54f-a0b6-5883-aa38-fd65fea05471", "resource": { "resourceType": "Device", "meta": { @@ -126,8 +204,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:VQ3pqzf0ae4VagpWXQAO6_pSrmE6qUIJIK5gRgKEiz4", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -156,24 +242,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "oura|33|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:YSj0Ah1T_5LZPm78bAPuwtt5bd86Bm6dY9NL7gkw_QI" } } ], - "fullUrl": "urn:uuid:becb8a0c-bec4-50be-a103-c2b155504067", + "fullUrl": "urn:uuid:f4706ee9-b5ed-577e-9035-9adaab428906", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:780e25b2-19ee-5f0f-8d41-61d02be16019" } + { "reference": "urn:uuid:b27eb87d-b169-54f7-bf36-08a7bef04a65" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -198,7 +293,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:3683e54f-a0b6-5883-aa38-fd65fea05471" } } ], @@ -207,8 +302,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "raw-source-oura-2" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:ds91lAVuNGWeEI1UGr6VCMVH8PUszhnZV8nHUtrWeKs", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -224,7 +327,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:23210e76-49c2-5503-8a4e-f5a6ce47f389" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json b/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json index 5f8ded11..69712a7a 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json +++ b/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|34|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:52" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|activityIntraday|raw-source-withings-3" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:SIl4FZ2kZAPZ15QYM9VnCgBCfK3yg6bWqkwIQYNo9gw" } } ], - "fullUrl": "urn:uuid:47cd8df0-eadc-529c-ad67-dcbe7895752e", + "fullUrl": "urn:uuid:8c0d21d1-440a-573c-9d9c-b6e05601198c", "resource": { "resourceType": "DocumentReference", "meta": { "profile": [ "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document" ] }, "extension": [ @@ -43,16 +59,54 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|activityIntraday|raw-source-withings-3" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:4im449uv3-Tg03jydL0lwfnVFYQc_2LP-j7mXBCkGzI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:SIl4FZ2kZAPZ15QYM9VnCgBCfK3yg6bWqkwIQYNo9gw", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", + "value": "v2:fixture-key:1:KWEc4JvtiEpZ4-q2O09yqsca7DLECOcIObGtz8E1P-o", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-artifact" + } + ] + } } ], "status": "current", "type": { "text": "Withings activityIntraday archive" }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "date": "2026-08-20T12:01:00Z", "author": [ - { "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" } + { "reference": "urn:uuid:c832b2d5-de29-5fa4-90b4-00d14a429d35" } ], "content": [ { @@ -65,9 +119,9 @@ }, "format": { "system": "https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format", - "version": "0.5.0", "code": "provider-recording", - "display": "Provider Recording" + "display": "Provider Recording", + "version": "0.6.0" } } ] @@ -76,14 +130,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:eBOHrN0OUNB6VbrRGeaTzLMa24c22LK7ToMSYhVjGqM" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:129d84e9-fbb5-5d6e-87b3-eba8cace7863", "resource": { "resourceType": "Device", "meta": { @@ -93,8 +155,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:eBOHrN0OUNB6VbrRGeaTzLMa24c22LK7ToMSYhVjGqM", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -109,14 +179,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:6OlNGWgUEqrs5upUH80PMNkuOiB3FN-Giy1WxsvxwPE" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:c832b2d5-de29-5fa4-90b4-00d14a429d35", "resource": { "resourceType": "Device", "meta": { @@ -126,8 +204,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:6OlNGWgUEqrs5upUH80PMNkuOiB3FN-Giy1WxsvxwPE", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -156,24 +242,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|34|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:4jm6yHHA_sU8uDIbkGAE2w5gRxpDPdYsqZuivs-I9XE" } } ], - "fullUrl": "urn:uuid:3c83d787-9632-5fff-9282-e76ca3969288", + "fullUrl": "urn:uuid:dc289d38-6058-5c14-92b2-da6f4a0bca22", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:47cd8df0-eadc-529c-ad67-dcbe7895752e" } + { "reference": "urn:uuid:8c0d21d1-440a-573c-9d9c-b6e05601198c" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -198,7 +293,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:c832b2d5-de29-5fa4-90b4-00d14a429d35" } } ], @@ -207,8 +302,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|activityIntraday|raw-source-withings-3" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:4im449uv3-Tg03jydL0lwfnVFYQc_2LP-j7mXBCkGzI", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -224,7 +327,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:129d84e9-fbb5-5d6e-87b3-eba8cace7863" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json b/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json index 44f81c09..a3466122 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json +++ b/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json @@ -6,29 +6,45 @@ ] }, "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|35|exchange-bundle" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/event", + "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:53" }, "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", + "timestamp": "2026-08-20T12:03:00Z", "entry": [ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|sleepIntraday|raw-source-withings-4" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:J-y_R7It9boHzh5qac_KBiUMKZuXf6PRmrIVhdaKZzg" } } ], - "fullUrl": "urn:uuid:8c829d33-1a6d-5aa4-9007-6df3509aa5a1", + "fullUrl": "urn:uuid:61a3eeb8-40b8-5948-8d99-5113e83bb9d0", "resource": { "resourceType": "DocumentReference", "meta": { "profile": [ "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document" ] }, "extension": [ @@ -43,16 +59,54 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|sleepIntraday|raw-source-withings-4" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:i983RVmugbV7mBvlWBJ1bLXFa9dv7Dz8lOg8Yx9ovgQ", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v2:fixture-key:1:J-y_R7It9boHzh5qac_KBiUMKZuXf6PRmrIVhdaKZzg", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + } + }, + { + "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", + "value": "v2:fixture-key:1:MAWqcZLaMAzYRUhy1NsjyKp5XoOBoFmZfwxUey9YJS4", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-artifact" + } + ] + } } ], "status": "current", "type": { "text": "Withings sleepIntraday archive" }, - "subject": { "reference": "Patient/example" }, + "subject": { + "type": "Patient", + "identifier": { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + }, "date": "2026-08-20T12:01:00Z", "author": [ - { "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" } + { "reference": "urn:uuid:a313be7b-70d3-5963-a1bb-690c7654d007" } ], "content": [ { @@ -65,9 +119,9 @@ }, "format": { "system": "https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format", - "version": "0.5.0", "code": "provider-recording", - "display": "Provider Recording" + "display": "Provider Recording", + "version": "0.6.0" } } ] @@ -76,14 +130,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:Z0ADfd9fY__cR5QlT_hmNkEqiqOoD2UG1BreVu__DkU" } } ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", + "fullUrl": "urn:uuid:c760430c-cd6f-53da-b631-f81c20646d74", "resource": { "resourceType": "Device", "meta": { @@ -93,8 +155,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:Z0ADfd9fY__cR5QlT_hmNkEqiqOoD2UG1BreVu__DkU", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -109,14 +179,22 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:d3o65QGoz2ek2Z9w-JW7wMVlXk2Qu_xWsMM2-OQ62T0" } } ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", + "fullUrl": "urn:uuid:a313be7b-70d3-5963-a1bb-690c7654d007", "resource": { "resourceType": "Device", "meta": { @@ -126,8 +204,16 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v2:fixture-key:1:d3o65QGoz2ek2Z9w-JW7wMVlXk2Qu_xWsMM2-OQ62T0", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + } } ], "status": "active", @@ -156,24 +242,33 @@ { "extension": [ { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "withings|35|conversion-provenance" + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n2:conversion-provenance:0:XVxtHKA7DWFrwHFPUWzabP00tnU3q4o8_-qXaCPqJxc" } } ], - "fullUrl": "urn:uuid:64cdab01-9ce7-528c-9dc3-902eb32aead5", + "fullUrl": "urn:uuid:7a04c3c6-3267-5250-82c6-5d72a764d86f", "resource": { "resourceType": "Provenance", "meta": { "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" ] }, "target": [ - { "reference": "urn:uuid:8c829d33-1a6d-5aa4-9007-6df3509aa5a1" } + { "reference": "urn:uuid:61a3eeb8-40b8-5948-8d99-5113e83bb9d0" } ], + "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", "activity": { "coding": [ @@ -198,7 +293,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" + "reference": "urn:uuid:a313be7b-70d3-5963-a1bb-690c7654d007" } } ], @@ -207,8 +302,16 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:withings|account-withings|sleepIntraday|raw-source-withings-4" + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v2:fixture-key:1:i983RVmugbV7mBvlWBJ1bLXFa9dv7Dz8lOg8Yx9ovgQ", + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } } }, "agent": [ @@ -224,7 +327,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" + "reference": "urn:uuid:c760430c-cd6f-53da-b631-f81c20646d74" } } ] diff --git a/packages/fhir/fixtures/normalized/google-health-active-energy.json b/packages/fhir/fixtures/normalized/google-health-active-energy.json index 5820505c..5b32b94f 100644 --- a/packages/fhir/fixtures/normalized/google-health-active-energy.json +++ b/packages/fhir/fixtures/normalized/google-health-active-energy.json @@ -1,21 +1,16 @@ { "source": { "adapter": { "kind": "providers", "provider": "google-health-api" }, - "providerAccountIdentifier": { + "providerScopeIdentifier": { "system": "https://example.org/deployments/provider-accounts", "value": "pseudonym-google-001", - "assurance": "deployment-scoped-pseudonym" + "assurance": "deployment-scoped-account-pseudonym" }, "sourceType": "active-energy-burned", "sourceNativeId": "synthetic-active-energy-2026-08-20", "recordingMethod": "automatically-recorded", "dataOrigin": { - "identity": { - "identifier": { - "system": "https://example.org/data-origins", - "value": "google-health-api" - } - }, + "sourceDeviceToken": "origin-instance-google-001", "name": "Google Health" } }, diff --git a/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json b/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json index 379e2fb3..25182562 100644 --- a/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json +++ b/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json @@ -4,20 +4,15 @@ "kind": "providers", "provider": "google-health-api" }, - "providerAccountIdentifier": { + "providerScopeIdentifier": { "system": "https://example.org/deployments/provider-accounts", "value": "pseudonym-google-001", - "assurance": "deployment-scoped-pseudonym" + "assurance": "deployment-scoped-account-pseudonym" }, "sourceType": "heart-rate", "sourceNativeId": "synthetic-google-heart-rate-series-001", "dataOrigin": { - "identity": { - "identifier": { - "system": "https://example.org/data-origins", - "value": "google-health-api" - } - }, + "sourceDeviceToken": "origin-instance-google-001", "name": "Google Health API" } }, diff --git a/packages/fhir/fixtures/normalized/oura-daily-activity.json b/packages/fhir/fixtures/normalized/oura-daily-activity.json index e4f7d744..3b03df5a 100644 --- a/packages/fhir/fixtures/normalized/oura-daily-activity.json +++ b/packages/fhir/fixtures/normalized/oura-daily-activity.json @@ -1,21 +1,16 @@ { "source": { "adapter": { "kind": "providers", "provider": "oura" }, - "providerAccountIdentifier": { - "system": "https://example.org/deployments/provider-accounts", - "value": "pseudonym-oura-001", - "assurance": "deployment-scoped-pseudonym" + "providerScopeIdentifier": { + "system": "https://example.org/provider-key-spaces", + "value": "oura-document-uuid-global", + "assurance": "documented-global-key-space" }, "sourceType": "daily_activity", "sourceNativeId": "synthetic-daily-activity-2026-08-20", "recordingMethod": "automatically-recorded", "dataOrigin": { - "identity": { - "identifier": { - "system": "https://example.org/data-origins", - "value": "oura" - } - }, + "sourceDeviceToken": "origin-instance-oura-001", "name": "Oura" } }, diff --git a/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json b/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json index 2c43764d..abf1c89a 100644 --- a/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json +++ b/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json @@ -4,20 +4,15 @@ "kind": "providers", "provider": "oura" }, - "providerAccountIdentifier": { - "system": "https://example.org/deployments/provider-accounts", - "value": "pseudonym-oura-001", - "assurance": "deployment-scoped-pseudonym" + "providerScopeIdentifier": { + "system": "https://example.org/provider-key-spaces", + "value": "oura-document-uuid-global", + "assurance": "documented-global-key-space" }, "sourceType": "heartrate", "sourceNativeId": "synthetic-oura-heart-rate-series-001", "dataOrigin": { - "identity": { - "identifier": { - "system": "https://example.org/data-origins", - "value": "oura" - } - }, + "sourceDeviceToken": "origin-instance-oura-001", "name": "Oura" } }, diff --git a/packages/fhir/fixtures/normalized/oura-sleep-duration.json b/packages/fhir/fixtures/normalized/oura-sleep-duration.json index d600337b..bbd0470d 100644 --- a/packages/fhir/fixtures/normalized/oura-sleep-duration.json +++ b/packages/fhir/fixtures/normalized/oura-sleep-duration.json @@ -1,21 +1,16 @@ { "source": { "adapter": { "kind": "providers", "provider": "oura" }, - "providerAccountIdentifier": { - "system": "https://example.org/deployments/provider-accounts", - "value": "pseudonym-oura-001", - "assurance": "deployment-scoped-pseudonym" + "providerScopeIdentifier": { + "system": "https://example.org/provider-key-spaces", + "value": "oura-document-uuid-global", + "assurance": "documented-global-key-space" }, "sourceType": "sleep", "sourceNativeId": "synthetic-sleep-2026-08-19", "recordingMethod": "automatically-recorded", "dataOrigin": { - "identity": { - "identifier": { - "system": "https://example.org/data-origins", - "value": "oura" - } - }, + "sourceDeviceToken": "origin-instance-oura-001", "name": "Oura" } }, diff --git a/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json b/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json index fda64ad0..5478705b 100644 --- a/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json +++ b/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json @@ -4,20 +4,15 @@ "kind": "providers", "provider": "withings" }, - "providerAccountIdentifier": { + "providerScopeIdentifier": { "system": "https://example.org/deployments/provider-accounts", "value": "pseudonym-withings-001", - "assurance": "deployment-scoped-pseudonym" + "assurance": "deployment-scoped-account-pseudonym" }, "sourceType": "activityIntraday", "sourceNativeId": "synthetic-withings-activity-series-001", "dataOrigin": { - "identity": { - "identifier": { - "system": "https://example.org/data-origins", - "value": "withings" - } - }, + "sourceDeviceToken": "origin-instance-withings-001", "name": "Withings" } }, diff --git a/packages/fhir/fixtures/normalized/withings-body-weight.json b/packages/fhir/fixtures/normalized/withings-body-weight.json index 786668a5..b9078412 100644 --- a/packages/fhir/fixtures/normalized/withings-body-weight.json +++ b/packages/fhir/fixtures/normalized/withings-body-weight.json @@ -1,21 +1,16 @@ { "source": { "adapter": { "kind": "providers", "provider": "withings" }, - "providerAccountIdentifier": { + "providerScopeIdentifier": { "system": "https://example.org/deployments/provider-accounts", "value": "pseudonym-withings-001", - "assurance": "deployment-scoped-pseudonym" + "assurance": "deployment-scoped-account-pseudonym" }, "sourceType": "getmeas:1", "sourceNativeId": "synthetic-weight-2026-08-20", "recordingMethod": "automatically-recorded", "dataOrigin": { - "identity": { - "identifier": { - "system": "https://example.org/data-origins", - "value": "withings" - } - }, + "sourceDeviceToken": "origin-instance-withings-001", "name": "Withings" } }, diff --git a/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json b/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json index d87fa9eb..6582c777 100644 --- a/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json +++ b/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json @@ -4,20 +4,15 @@ "kind": "providers", "provider": "withings" }, - "providerAccountIdentifier": { + "providerScopeIdentifier": { "system": "https://example.org/deployments/provider-accounts", "value": "pseudonym-withings-001", - "assurance": "deployment-scoped-pseudonym" + "assurance": "deployment-scoped-account-pseudonym" }, "sourceType": "sleepIntraday", "sourceNativeId": "synthetic-withings-sleep-series-001", "dataOrigin": { - "identity": { - "identifier": { - "system": "https://example.org/data-origins", - "value": "withings" - } - }, + "sourceDeviceToken": "origin-instance-withings-001", "name": "Withings" } }, diff --git a/packages/fhir/grove-fhir.json b/packages/fhir/grove-fhir.json index c4871d67..51df19fe 100644 --- a/packages/fhir/grove-fhir.json +++ b/packages/fhir/grove-fhir.json @@ -1,4 +1,5 @@ { "repository": "https://github.com/SchmiedmayerLab/grove-fhir", - "ref": "85eb20a6b19a688f42e66e9ad3de96b0d1a5263f" + "ref": "31197e876161e9c6d207760d472343231da527e2", + "archiveSha256": "1397dbd3daf591697ead3c54db6a18bd076967bf8489955115176e95d5b6cd68" } diff --git a/packages/fhir/package.json b/packages/fhir/package.json index a9b321d0..5a747aed 100644 --- a/packages/fhir/package.json +++ b/packages/fhir/package.json @@ -88,6 +88,7 @@ "files": [ "dist", "catalog", + "fixtures/conformance", "fixtures/normalized", "README.md", "LICENSE" @@ -95,7 +96,7 @@ "dependencies": { "@noble/hashes": "2.3.0", "@types/fhir": "^0.0.44", - "uuid": "^14.0.1", + "uuid": "^14.0.2", "zod": "^4.4.3" }, "devDependencies": { diff --git a/packages/fhir/scripts/browser-smoke.mjs b/packages/fhir/scripts/browser-smoke.mjs index ef71a1f7..5e22cf52 100644 --- a/packages/fhir/scripts/browser-smoke.mjs +++ b/packages/fhir/scripts/browser-smoke.mjs @@ -104,8 +104,42 @@ try { system: 'https://study.example.org/fhir/identifiers/mobile-observation', value: 'heart-rate-20260820-001', }) + const deploymentIdentity = { + opaqueIdentifierSystems: Object.fromEntries( + [ + 'source-record', + 'source-output', + 'writer-record', + 'provider-record', + 'provider-output', + 'source-artifact', + 'provider-artifact', + 'source-context', + 'recording-device', + 'device-snapshot', + ].map((kind) => [ + kind, + `https://example.org/browser/identity/${kind}/browser-key/1`, + ]), + ), + eventIdentifierSystem: 'https://example.org/browser/identity/event', + entryNodeIdentifierSystem: + 'https://example.org/browser/identity/entry-node', + keyId: 'browser-key', + keyEpoch: '1', + secretBase64Url: 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', + producerInstance: '9738c1a2-4258-4ba7-a9f7-e7f56e2996d8', + } + const providerSubject = { + type: 'Patient', + identifier: { + system: 'https://example.org/browser/patient-pseudonyms', + value: 'patient-browser', + assurance: 'deployment-scoped-pseudonym', + }, + } const measurementGraph = provider.buildProviderMeasurementBundle({ - subject: 'Patient/browser', + subject: providerSubject, measurements: [ { kind: 'heart-rate', @@ -115,55 +149,76 @@ try { ], source: { adapter: { kind: 'providers', provider: 'withings' }, - providerAccountIdentifier: { + providerScopeIdentifier: { system: 'https://example.org/provider-account-pseudonyms', value: 'browser-account', - assurance: 'deployment-scoped-pseudonym', + assurance: 'deployment-scoped-account-pseudonym', }, sourceType: 'getmeas:11', sourceNativeId: 'browser-heart-rate', dataOrigin: { - identity: { - identifier: { - system: 'https://example.org/data-origins', - value: 'withings', - }, - }, + sourceDeviceToken: 'browser-withings-origin', name: 'Withings', }, }, application: { - identity: { - identifier: { - system: 'https://example.org/applications', - value: 'browser-converter', - }, - }, + sourceDeviceToken: 'browser-converter', name: 'Browser converter', }, - eventSequence: 1, - graphIdentifierSystem: - 'urn:grove:provider-graph:org.grovealliance.example', - issued: '2026-08-20T12:01:00Z', + eventSequence: '1', + deploymentIdentity, + occurred: '2026-08-20T12:00:00Z', recorded: '2026-08-20T12:02:00Z', + assembled: '2026-08-20T12:03:00Z', }) + const outputCoordinates = provider.providerOutputCoordinates( + 'withings', + 'getmeas:11', + 'heart-rate', + ) + const retraction = + outputCoordinates === undefined ? + { ok: false } + : provider.buildProviderRetractionBundle({ + source: { + provider: 'withings', + providerScopeIdentifier: { + system: 'https://example.org/provider-account-pseudonyms', + value: 'browser-account', + assurance: 'deployment-scoped-account-pseudonym', + }, + sourceType: 'getmeas:11', + sourceNativeId: 'browser-heart-rate', + }, + targets: [ + { + role: 'primary-output', + resourceType: 'Observation', + ...outputCoordinates, + }, + ], + application: { + sourceDeviceToken: 'browser-converter', + name: 'Browser converter', + }, + eventSequence: '3', + deploymentIdentity, + occurred: '2026-08-21T12:00:00Z', + recorded: '2026-08-21T12:01:00Z', + assembled: '2026-08-21T12:02:00Z', + }) const recording = provider.buildProviderRecordingBundle({ source: { adapter: { kind: 'providers', provider: 'google-health-api' }, - providerAccountIdentifier: { + providerScopeIdentifier: { system: 'https://example.org/provider-account-pseudonyms', value: 'browser-raw-account', - assurance: 'deployment-scoped-pseudonym', + assurance: 'deployment-scoped-account-pseudonym', }, sourceType: 'heart-rate', sourceNativeId: 'browser-native-recording-42', dataOrigin: { - identity: { - identifier: { - system: 'https://example.org/data-origins', - value: 'google-health-api', - }, - }, + sourceDeviceToken: 'browser-google-origin', name: 'Google Health API', }, }, @@ -175,21 +230,17 @@ try { payloadAssertion: 'caller-authorized-opaque-payload', dataBase64: 'AQID', }, - subject: 'Patient/browser', + subject: providerSubject, application: { - identity: { - identifier: { - system: 'https://example.org/applications', - value: 'browser-raw-converter', - }, - }, + sourceDeviceToken: 'browser-raw-converter', name: 'Browser raw converter', }, - eventSequence: 2, - graphIdentifierSystem: - 'urn:grove:provider-graph:org.grovealliance.example', + eventSequence: '2', + deploymentIdentity, documentDate: '2026-08-20T12:01:00Z', + occurred: '2026-08-20T12:00:00Z', recorded: '2026-08-20T12:02:00Z', + assembled: '2026-08-20T12:03:00Z', }) const instrument = questionnaire.buildQuestionnaire({ url: 'https://example.org/Questionnaire/browser', @@ -234,12 +285,15 @@ try { .length, measurementGraph: measurementGraph.ok, recordingGraph: recording.ok, - rawSourceCount: Object.values(provider.providerRawMappings).reduce( + retractionGraph: + retraction.ok && + !JSON.stringify(retraction.value).includes('entered-in-error'), + rawSourceCount: Object.values(provider.providerRawOutputRoles).reduce( (count, mappings) => count + Object.keys(mappings).length, 0, ), scalarMeasurementCount: new Set( - Object.values(provider.providerScalarMappings).flatMap( + Object.values(provider.providerScalarOutputRoles).flatMap( (sourceMappings) => Object.values(sourceMappings).flatMap((mapping) => Object.keys(mapping), @@ -248,10 +302,12 @@ try { ).size, providerApiVisibleFromMobile: 'buildProviderMeasurementBundle' in mobile || - 'buildProviderRecordingBundle' in mobile, + 'buildProviderRecordingBundle' in mobile || + 'buildProviderRetractionBundle' in mobile, providerApiVisibleFromRoot: 'buildProviderMeasurementBundle' in grove || - 'buildProviderRecordingBundle' in grove, + 'buildProviderRecordingBundle' in grove || + 'buildProviderRetractionBundle' in grove, internalGraphVisible: 'groveFhirPackageGraph' in grove || 'groveFhirProfileClaims' in grove || @@ -262,13 +318,14 @@ try { }, origin) if ( - result.fullUrl !== 'urn:uuid:72a64652-0bad-517d-8a36-39e3b6adccac' || + result.fullUrl !== 'urn:uuid:9ca77ee5-421b-5f0c-8206-7e9e65485ae5' || result.hasNodeProcess || result.measurementCount !== 84 || result.measurementGraph !== true || result.recordingGraph !== true || + result.retractionGraph !== true || result.rawSourceCount !== 4 || - result.scalarMeasurementCount !== 36 || + result.scalarMeasurementCount !== 48 || result.providerApiVisibleFromMobile || result.providerApiVisibleFromRoot || result.internalGraphVisible || diff --git a/packages/fhir/scripts/fetch-grove-fhir.mjs b/packages/fhir/scripts/fetch-grove-fhir.mjs index 266b77a5..ab1b4fe1 100644 --- a/packages/fhir/scripts/fetch-grove-fhir.mjs +++ b/packages/fhir/scripts/fetch-grove-fhir.mjs @@ -12,7 +12,8 @@ import { Buffer } from 'node:buffer' import { execFile } from 'node:child_process' -import { mkdir, readFile, rm, writeFile } from 'node:fs/promises' +import { createHash } from 'node:crypto' +import { mkdtemp, readFile, rename, rm, writeFile } from 'node:fs/promises' import { dirname, resolve } from 'node:path' import { stdout } from 'node:process' import { fileURLToPath } from 'node:url' @@ -23,51 +24,70 @@ const run = promisify(execFile) const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..') const destination = resolve(packageRoot, '.grove-fhir') const marker = resolve(destination, '.ref') +const repository = 'https://github.com/SchmiedmayerLab/grove-fhir' const pin = JSON.parse( await readFile(resolve(packageRoot, 'grove-fhir.json'), 'utf8'), ) -// A released version is pinned by its tag so the reference stays readable in a diff; a bare -// commit stays valid for pinning work that predates a release. -const ref = pin.ref ?? pin.sha +const ref = pin.ref +const archiveSha256 = pin.archiveSha256 if ( + pin.repository !== repository || typeof ref !== 'string' || - !/^(?:[\da-f]{40}|\d+\.\d+\.\d+(?:-[\dA-Za-z]+(?:[.-][\dA-Za-z]+)*)?)$/u.test( - ref, - ) + !/^[\da-f]{40}$/u.test(ref) || + typeof archiveSha256 !== 'string' || + !/^[\da-f]{64}$/u.test(archiveSha256) ) { throw new Error( - `grove-fhir.json must pin a release tag or a complete commit SHA, got "${ref}"`, + 'grove-fhir.json must pin the Grove repository by complete commit SHA and exact archiveSha256.', ) } +const markerValue = `${JSON.stringify({ ref, archiveSha256 })}\n` const present = await readFile(marker, 'utf8').catch(() => undefined) // Every generator and checker depends on this, so repeated runs are the common case. -if (present !== ref) { - const archive = `${pin.repository}/archive/${ref}.tar.gz` +if (present !== markerValue) { + const archive = `${repository}/archive/${ref}.tar.gz` const response = await fetch(archive) if (!response.ok) { throw new Error(`Could not fetch ${archive}: ${response.status}`) } - await rm(destination, { recursive: true, force: true }) - await mkdir(destination, { recursive: true }) - const tarball = resolve(destination, 'grove-fhir.tar.gz') - await writeFile(tarball, Buffer.from(await response.arrayBuffer())) + const bytes = Buffer.from(await response.arrayBuffer()) + const actualSha256 = createHash('sha256').update(bytes).digest('hex') + if (actualSha256 !== archiveSha256) { + throw new Error( + `Grove FHIR archive digest mismatch.\n expected ${archiveSha256}\n actual ${actualSha256}`, + ) + } + + const staging = await mkdtemp(resolve(packageRoot, '.grove-fhir-fetch-')) + try { + const tarball = resolve(staging, 'grove-fhir.tar.gz') + await writeFile(tarball, bytes) - // The archive root is named for the commit, so strip it and keep only what is read. - await run('tar', [ - '--extract', - '--file', - tarball, - '--directory', - destination, - '--strip-components', - '1', - `grove-fhir-${ref}/catalog`, - `grove-fhir-${ref}/Conformance/corpora/mobile-semantics`, - ]) - await rm(tarball) - await writeFile(marker, ref) + // The archive root is named for the commit, so strip it and keep only what is read. + await run('tar', [ + '--extract', + '--file', + tarball, + '--directory', + staging, + '--strip-components', + '1', + '--no-same-owner', + '--no-same-permissions', + `grove-fhir-${ref}/catalog`, + `grove-fhir-${ref}/Conformance/corpora/mobile-semantics`, + `grove-fhir-${ref}/Conformance/corpora/mobile-exchange`, + ]) + await rm(tarball) + await writeFile(resolve(staging, '.ref'), markerValue) + await rm(destination, { recursive: true, force: true }) + await rename(staging, destination) + } catch (error) { + await rm(staging, { recursive: true, force: true }) + throw error + } stdout.write(`Fetched Grove FHIR catalogs at ${ref}\n`) } diff --git a/packages/fhir/scripts/generate-conformance-fixtures.mjs b/packages/fhir/scripts/generate-conformance-fixtures.mjs index 4de44c2d..73313e65 100644 --- a/packages/fhir/scripts/generate-conformance-fixtures.mjs +++ b/packages/fhir/scripts/generate-conformance-fixtures.mjs @@ -21,26 +21,34 @@ const providerApi = await import(`${distRoot}/providers/index.js`) const { buildQuestionnaire, buildQuestionnaireResponse, + groveExchangeProtocol, groveFhirContractVersion, parseAbsoluteUri, parseCanonical, parseFhirInstant, - parsePatientReference, - parsePositiveInteger, parseSemVer, sharedMobileMeasurementCatalog, } = rootApi const { + adapterMeasurementCatalog, buildProviderRecordingBundle, buildProviderMeasurementBundle, - providerRawMappings, + buildProviderRetractionBundle, + providerAdapterCatalog, + providerRawOutputRoles, providerRecordEffectiveRules, - providerScalarMappings, + providerScalarOutputDiscriminators, + providerScalarOutputRoles, encodeRecordingBytes, parseMediaType, } = providerApi const root = resolve(dirname(fileURLToPath(import.meta.url)), '..') +const localIgIndex = argv.indexOf('--ig') +const upstreamRoot = + localIgIndex === -1 ? + resolve(root, '.grove-fhir') + : resolve(argv[localIgIndex + 1] ?? '') const fixtureRoot = resolve(root, 'fixtures/conformance') const resourceRoot = resolve(fixtureRoot, 'resources') const check = argv.includes('--check') @@ -49,29 +57,45 @@ const packageMetadata = JSON.parse( ) const semanticCorpus = JSON.parse( await readFile( - resolve( - root, - '.grove-fhir/Conformance/corpora/mobile-semantics/corpus.json', - ), + resolve(upstreamRoot, 'Conformance/corpora/mobile-semantics/corpus.json'), 'utf8', ), ) -const packageGraph = JSON.parse( - await readFile( - resolve(root, '.grove-fhir/catalog/package-graph.json'), - 'utf8', +const mobileExchangeCorpusRoot = resolve( + upstreamRoot, + 'Conformance/corpora/mobile-exchange', +) +const sharedMobileExchangeFiles = [ + 'exchange-bundle.json', + 'retraction-bundle.json', + 'corpus.json', +] +const sharedMobileExchange = new Map( + await Promise.all( + sharedMobileExchangeFiles.map(async (name) => [ + `mobile-exchange/${name}`, + JSON.parse( + await readFile(resolve(mobileExchangeCorpusRoot, name), 'utf8'), + ), + ]), ), ) +const packageGraph = JSON.parse( + await readFile(resolve(upstreamRoot, 'catalog/package-graph.json'), 'utf8'), +) const producerVersion = packageMetadata.version if (typeof producerVersion !== 'string' || producerVersion.length === 0) { throw new Error('The FHIR package must declare a producer version.') } const conformancePackageSources = [ + 'google-health', 'mobile', + 'oura', 'providers', 'sensor', 'questionnaire', + 'withings', ] const packageBySource = new Map( packageGraph.packages.map((entry) => [entry.source, entry]), @@ -106,6 +130,10 @@ const exchangeBundleProfile = profileCanonical( 'mobile', 'grove-mobile-exchange-bundle', ) +const retractionBundleProfile = profileCanonical( + 'mobile', + 'grove-mobile-retraction-bundle', +) const questionnaireProfile = profileCanonical( 'questionnaire', 'grove-questionnaire', @@ -124,32 +152,77 @@ const unwrap = (result) => { const uri = (value) => unwrap(parseAbsoluteUri(value)) const instant = (value) => unwrap(parseFhirInstant(value)) -const resourceIdentity = (system, value) => ({ - identifier: { system: uri(system), value }, -}) +const providerSubject = { + type: 'Patient', + identifier: { + system: uri('https://grovealliance.org/fhir/testing/patient-pseudonyms'), + value: 'patient-example', + assurance: 'deployment-scoped-pseudonym', + }, +} const application = { - identity: resourceIdentity( - 'https://grovealliance.org/fhir/testing/applications', - 'grove-ts-0.2', - ), + sourceDeviceToken: 'grove-ts-conformance-producer', name: 'Grove TypeScript conformance producer', version: producerVersion, } const dataOrigin = { - identity: resourceIdentity( - 'https://grovealliance.org/fhir/testing/data-origins', - 'connected-provider', - ), + sourceDeviceToken: 'synthetic-connected-provider', name: 'Synthetic connected provider', } -// Owner-exclusive scalar mappings stay outside the shared facade and the corpus. +const providerScopeIdentifier = (provider) => { + const scope = providerAdapterCatalog.providers.find( + ({ id }) => id === provider, + )?.identifierScope + if (scope === 'account') { + return { + system: uri( + 'https://example.org/deployments/provider-account-pseudonyms', + ), + value: `account-${provider}`, + assurance: 'deployment-scoped-account-pseudonym', + } + } + if (scope === 'global' || scope === 'none') { + return { + system: uri('https://example.org/provider-key-spaces'), + value: `${provider}-document-id-global`, + assurance: 'documented-global-key-space', + } + } + throw new Error(`Provider ${provider} has no closed identifier scope.`) +} +const deploymentIdentity = { + opaqueIdentifierSystems: Object.fromEntries( + groveExchangeProtocol.opaqueIdentity.identityKinds.map(({ kind }) => [ + kind, + uri( + `https://grovealliance.org/fhir/testing/identity/${kind}/fixture-key/1`, + ), + ]), + ), + eventIdentifierSystem: uri( + 'https://grovealliance.org/fhir/testing/identity/event', + ), + entryNodeIdentifierSystem: uri( + 'https://grovealliance.org/fhir/testing/identity/entry-node', + ), + keyId: 'fixture-key', + keyEpoch: '1', + secretBase64Url: 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', + producerInstance: '33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc', +} +// The shared semantic corpus covers Mobile semantics; owner-exclusive Provider +// measurements are generated separately from their exact catalog definitions below. +const providerAdmittedMeasurementIds = new Set( + Object.values(providerScalarOutputRoles).flatMap((sourceMappings) => + Object.values(sourceMappings).flatMap((mapping) => Object.keys(mapping)), + ), +) const admittedMeasurementIds = new Set( - Object.values(providerScalarMappings) - .flatMap((sourceMappings) => - Object.values(sourceMappings).flatMap((mapping) => Object.keys(mapping)), - ) - .filter((id) => Object.hasOwn(sharedMobileMeasurementCatalog, id)), + [...providerAdmittedMeasurementIds].filter((id) => + Object.hasOwn(sharedMobileMeasurementCatalog, id), + ), ) const semanticVectors = semanticCorpus.vectors.filter((vector) => admittedMeasurementIds.has(vector.id), @@ -203,9 +276,96 @@ const measurementFromVector = (vector) => { } const measurements = semanticVectors.map(measurementFromVector) +const eventSequenceAt = (zeroBasedOrdinal) => String(zeroBasedOrdinal + 1) +const measurementEventSequences = measurements.map((_, index) => + eventSequenceAt(index), +) + +const providerRows = new Map( + providerAdapterCatalog.providers.map((provider) => [provider.id, provider]), +) +const exclusiveMeasurementCases = [] +const includedExclusiveMeasurements = new Set() +for (const [provider, sourceMappings] of Object.entries( + providerScalarOutputRoles, +)) { + const providerRow = providerRows.get(provider) + if ( + providerRow === undefined || + typeof providerRow.measurementOwner !== 'string' + ) { + throw new Error(`Provider ${provider} has no measurement owner.`) + } + for (const [sourceType, mapping] of Object.entries(sourceMappings)) { + for (const kind of Object.keys(mapping)) { + if ( + Object.hasOwn(sharedMobileMeasurementCatalog, kind) || + includedExclusiveMeasurements.has(kind) + ) { + continue + } + const definition = + adapterMeasurementCatalog[providerRow.measurementOwner]?.[kind] + if ( + definition === undefined || + definition.owner !== providerRow.measurementOwner + ) { + throw new Error( + `Provider ${provider}/${sourceType} has no exact owner definition for ${kind}.`, + ) + } + const recordRule = providerRecordEffectiveRules[provider]?.[sourceType] + const effective = + recordRule !== undefined || definition.effective === 'Period' ? + { + kind: 'period', + start: instant('2026-08-20T00:00:00-07:00'), + end: instant('2026-08-21T00:00:00-07:00'), + } + : { kind: 'date-time', value: instant('2026-08-20T12:00:00Z') } + const value = + definition.valueKind === 'quantity' ? definition.quantity?.example + : definition.valueKind === 'codeableConcept' ? + definition.allowedValues?.[0] + : undefined + if (typeof value !== 'number' && typeof value !== 'string') { + throw new Error( + `Provider ${provider}/${sourceType}/${kind} has no scalar example.`, + ) + } + exclusiveMeasurementCases.push({ + provider, + sourceType, + measurement: { kind, value, effective }, + }) + includedExclusiveMeasurements.add(kind) + } + } +} +const admittedExclusiveMeasurementIds = new Set( + [...providerAdmittedMeasurementIds].filter( + (id) => !Object.hasOwn(sharedMobileMeasurementCatalog, id), + ), +) +if ( + exclusiveMeasurementCases.length !== admittedExclusiveMeasurementIds.size || + [...admittedExclusiveMeasurementIds].some( + (id) => !includedExclusiveMeasurements.has(id), + ) +) { + throw new Error( + 'Every admitted owner-exclusive Provider scalar measurement requires one fixture.', + ) +} +const exclusiveMeasurementEventSequences = exclusiveMeasurementCases.map( + (_, index) => eventSequenceAt(measurements.length + index), +) +const ouraDailyActivityEventSequence = eventSequenceAt( + measurements.length + exclusiveMeasurementCases.length, +) const providerFor = (measurement, index) => { - const providers = Object.entries(providerScalarMappings) + const providers = Object.entries(providerScalarOutputRoles) const ordered = [ ...providers.slice(index % providers.length), ...providers.slice(0, index % providers.length), @@ -228,36 +388,54 @@ const providerMeasurementBundle = (measurement, index) => { const provider = providerFor(measurement, index) return unwrap( buildProviderMeasurementBundle({ - subject: unwrap(parsePatientReference('Patient/example')), + subject: providerSubject, measurements: [measurement], source: { adapter: { kind: 'providers', provider: provider.provider, }, - providerAccountIdentifier: { - system: uri( - 'https://example.org/deployments/provider-account-pseudonyms', - ), - value: `account-${provider.provider}`, - assurance: 'deployment-scoped-pseudonym', - }, + providerScopeIdentifier: providerScopeIdentifier(provider.provider), sourceType: provider.sourceType, sourceNativeId: `source-${measurement.kind}`, recordingMethod: 'automatically-recorded', dataOrigin, }, application, - eventSequence: unwrap(parsePositiveInteger(index + 1)), - graphIdentifierSystem: uri( - 'urn:grove:provider-graph:org.grovealliance.example', - ), - issued: instant('2026-08-20T12:01:00Z'), + eventSequence: measurementEventSequences[index], + deploymentIdentity, + occurred: instant('2026-08-20T12:00:00Z'), recorded: instant('2026-08-20T12:02:00Z'), + assembled: instant('2026-08-20T12:03:00Z'), }), ) } +const exclusiveProviderMeasurementBundle = ( + { provider, sourceType, measurement }, + index, +) => + unwrap( + buildProviderMeasurementBundle({ + subject: providerSubject, + measurements: [measurement], + source: { + adapter: { kind: 'providers', provider }, + providerScopeIdentifier: providerScopeIdentifier(provider), + sourceType, + sourceNativeId: `source-${measurement.kind}`, + recordingMethod: 'automatically-recorded', + dataOrigin, + }, + application, + eventSequence: exclusiveMeasurementEventSequences[index], + deploymentIdentity, + occurred: instant('2026-08-20T12:00:00Z'), + recorded: instant('2026-08-20T12:02:00Z'), + assembled: instant('2026-08-20T12:03:00Z'), + }), + ) + const measurementByKind = new Map( measurements.map((measurement) => [measurement.kind, measurement]), ) @@ -279,36 +457,34 @@ const ouraDailyActivityMeasurements = [ }) const ouraDailyActivityBundle = unwrap( buildProviderMeasurementBundle({ - subject: unwrap(parsePatientReference('Patient/example')), + subject: providerSubject, measurements: ouraDailyActivityMeasurements, source: { adapter: { kind: 'providers', provider: 'oura' }, - providerAccountIdentifier: { - system: uri( - 'https://example.org/deployments/provider-account-pseudonyms', - ), - value: 'account-oura-daily-activity', - assurance: 'deployment-scoped-pseudonym', - }, + providerScopeIdentifier: providerScopeIdentifier('oura'), sourceType: 'daily_activity', sourceNativeId: 'source-oura-daily-activity', recordingMethod: 'automatically-recorded', dataOrigin, }, application, - eventSequence: unwrap(parsePositiveInteger(measurements.length + 1)), - graphIdentifierSystem: uri( - 'urn:grove:provider-graph:org.grovealliance.example', - ), - issued: instant('2026-08-21T07:01:00Z'), + eventSequence: ouraDailyActivityEventSequence, + deploymentIdentity, + occurred: instant('2026-08-21T07:00:00Z'), recorded: instant('2026-08-21T07:02:00Z'), + assembled: instant('2026-08-21T07:03:00Z'), }), ) -const recordingSources = Object.entries(providerRawMappings).flatMap( +const recordingSources = Object.entries(providerRawOutputRoles).flatMap( ([provider, sources]) => Object.keys(sources).map((sourceType) => ({ provider, sourceType })), ) +const recordingEventSequences = recordingSources.map((_, index) => + eventSequenceAt( + measurements.length + exclusiveMeasurementCases.length + 1 + index, + ), +) const recordingPath = ({ provider, sourceType }) => `resources/recording-${provider}-${sourceType.replaceAll(/[^A-Za-z0-9]+/gu, '-').toLowerCase()}.json` @@ -318,13 +494,7 @@ const recordingBundle = ({ provider, sourceType }, index) => buildProviderRecordingBundle({ source: { adapter: { kind: 'providers', provider }, - providerAccountIdentifier: { - system: uri( - 'https://example.org/deployments/provider-account-pseudonyms', - ), - value: `account-${provider}`, - assurance: 'deployment-scoped-pseudonym', - }, + providerScopeIdentifier: providerScopeIdentifier(provider), sourceType, sourceNativeId: `raw-source-${provider}-${String(index + 1)}`, dataOrigin, @@ -341,19 +511,126 @@ const recordingBundle = ({ provider, sourceType }, index) => encodeRecordingBytes(Buffer.from('{"synthetic":true}', 'utf8')), ), }, - subject: unwrap(parsePatientReference('Patient/example')), + subject: providerSubject, application, - eventSequence: unwrap( - parsePositiveInteger(measurements.length + index + 1), - ), - graphIdentifierSystem: uri( - 'urn:grove:provider-graph:org.grovealliance.example', - ), + eventSequence: recordingEventSequences[index], + deploymentIdentity, documentDate: instant('2026-08-20T12:01:00Z'), + occurred: instant('2026-08-20T12:00:00Z'), recorded: instant('2026-08-20T12:02:00Z'), + assembled: instant('2026-08-20T12:03:00Z'), }), ) +const primaryRetractionMeasurement = measurements[0] +if (primaryRetractionMeasurement === undefined) { + throw new Error( + 'A primary-output retraction fixture requires one measurement.', + ) +} +const primaryRetractionProvider = providerFor(primaryRetractionMeasurement, 0) +const primaryRetractionOutputRole = + providerScalarOutputRoles[primaryRetractionProvider.provider]?.[ + primaryRetractionProvider.sourceType + ]?.[primaryRetractionMeasurement.kind] +if (primaryRetractionOutputRole === undefined) { + throw new Error('The primary-output retraction role is not catalog-owned.') +} +const primaryRetractionOutputDiscriminator = + providerScalarOutputDiscriminators[primaryRetractionProvider.provider]?.[ + primaryRetractionProvider.sourceType + ]?.[primaryRetractionMeasurement.kind] +if (primaryRetractionOutputDiscriminator === undefined) { + throw new Error( + 'The primary-output retraction discriminator is not catalog-owned.', + ) +} +const primaryRetractionEventSequence = eventSequenceAt( + measurements.length + + exclusiveMeasurementCases.length + + 1 + + recordingSources.length, +) +const primaryRetractionBundle = unwrap( + buildProviderRetractionBundle({ + source: { + provider: primaryRetractionProvider.provider, + providerScopeIdentifier: providerScopeIdentifier( + primaryRetractionProvider.provider, + ), + sourceType: primaryRetractionProvider.sourceType, + sourceNativeId: `source-${primaryRetractionMeasurement.kind}`, + }, + targets: [ + { + role: 'primary-output', + resourceType: 'Observation', + outputRole: primaryRetractionOutputRole, + outputDiscriminator: primaryRetractionOutputDiscriminator, + }, + { + role: 'device-snapshot', + resourceType: 'Device', + priorEventSequence: measurementEventSequences[0], + deviceRole: 'application', + sourceDeviceToken: application.sourceDeviceToken, + }, + ], + application, + eventSequence: primaryRetractionEventSequence, + deploymentIdentity, + occurred: instant('2026-08-22T12:00:00Z'), + recorded: instant('2026-08-22T12:01:00Z'), + assembled: instant('2026-08-22T12:02:00Z'), + }), +) + +const artifactRetractionSource = recordingSources[0] +if (artifactRetractionSource === undefined) { + throw new Error( + 'A source-artifact retraction fixture requires one recording.', + ) +} +const artifactRetractionEventSequence = eventSequenceAt( + measurements.length + + exclusiveMeasurementCases.length + + 2 + + recordingSources.length, +) +const artifactRetractionBundle = unwrap( + buildProviderRetractionBundle({ + source: { + provider: artifactRetractionSource.provider, + providerScopeIdentifier: providerScopeIdentifier( + artifactRetractionSource.provider, + ), + sourceType: artifactRetractionSource.sourceType, + sourceNativeId: `raw-source-${artifactRetractionSource.provider}-1`, + }, + targets: [ + { + role: 'source-artifact', + resourceType: 'DocumentReference', + formatCode: 'provider-recording', + partIndex: '0', + }, + { + role: 'device-snapshot', + resourceType: 'Device', + priorEventSequence: recordingEventSequences[0], + deviceRole: 'application', + sourceDeviceToken: application.sourceDeviceToken, + }, + ], + application, + eventSequence: artifactRetractionEventSequence, + deploymentIdentity, + occurred: instant('2026-08-22T13:00:00Z'), + recorded: instant('2026-08-22T13:01:00Z'), + assembled: instant('2026-08-22T13:02:00Z'), + }), +) + const questionnaireUrl = uri( 'https://grovealliance.org/fhir/testing/Questionnaire/grove-ts-conformance', ) @@ -408,7 +685,7 @@ const questionnaireResponse = unwrap( value: 'grove-ts-conformance-response', }, status: 'completed', - subject: unwrap(parsePatientReference('Patient/example')), + subject: { type: 'Patient', reference: 'Patient/example' }, authored: instant('2026-08-20T12:00:00Z'), items: [ { @@ -436,13 +713,67 @@ const resources = new Map( providerMeasurementBundle(measurement, index), ]), ) +const exclusiveMeasurementPath = ({ provider, measurement }) => + `resources/provider-${provider}-${measurement.kind}.json` +for (const [index, entry] of exclusiveMeasurementCases.entries()) { + resources.set( + exclusiveMeasurementPath(entry), + exclusiveProviderMeasurementBundle(entry, index), + ) +} const ouraDailyActivityPath = 'resources/provider-oura-daily-activity.json' resources.set(ouraDailyActivityPath, ouraDailyActivityBundle) for (const [index, source] of recordingSources.entries()) { resources.set(recordingPath(source), recordingBundle(source, index)) } +resources.set( + 'resources/provider-primary-output-retraction.json', + primaryRetractionBundle, +) +resources.set( + 'resources/provider-source-artifact-retraction.json', + artifactRetractionBundle, +) resources.set('resources/questionnaire.json', questionnaire) resources.set('resources/questionnaire-response.json', questionnaireResponse) +for (const [path, value] of sharedMobileExchange) resources.set(path, value) + +const abstractProviderObservationProfile = providerAdapterCatalog.adapterProfile +for (const [path, value] of resources) { + const candidates = + value?.resourceType === 'Bundle' ? + (value.entry ?? []).map(({ resource }) => resource) + : [value] + if ( + candidates.some( + (resource) => + resource?.resourceType === 'Observation' && + resource.meta?.profile?.includes(abstractProviderObservationProfile), + ) + ) { + throw new Error( + `${path} directly claims the abstract ProvidersObservation parent instead of an exact provider envelope.`, + ) + } +} + +const eventIdentifiers = new Map() +for (const [path, value] of resources) { + if (value?.resourceType !== 'Bundle' || value.identifier === undefined) + continue + const { system, value: identifierValue } = value.identifier + if (typeof system !== 'string' || typeof identifierValue !== 'string') { + throw new Error(`${path} has an incomplete event identifier.`) + } + const key = `${system.length}:${system}${identifierValue.length}:${identifierValue}` + const prior = eventIdentifiers.get(key) + if (prior !== undefined) { + throw new Error( + `${path} reuses the event identifier already emitted by ${prior}.`, + ) + } + eventIdentifiers.set(key, path) +} const manifest = { schemaVersion: 1, @@ -454,6 +785,10 @@ const manifest = { path: `resources/mobile-${measurement.kind}.json`, requiredProfiles: [exchangeBundleProfile], })), + ...exclusiveMeasurementCases.map((entry) => ({ + path: exclusiveMeasurementPath(entry), + requiredProfiles: [exchangeBundleProfile], + })), { path: ouraDailyActivityPath, requiredProfiles: [exchangeBundleProfile], @@ -462,6 +797,22 @@ const manifest = { path: recordingPath(source), requiredProfiles: [exchangeBundleProfile], })), + { + path: 'resources/provider-primary-output-retraction.json', + requiredProfiles: [retractionBundleProfile], + }, + { + path: 'resources/provider-source-artifact-retraction.json', + requiredProfiles: [retractionBundleProfile], + }, + { + path: 'mobile-exchange/exchange-bundle.json', + requiredProfiles: [exchangeBundleProfile], + }, + { + path: 'mobile-exchange/retraction-bundle.json', + requiredProfiles: [retractionBundleProfile], + }, { path: 'resources/questionnaire.json', requiredProfiles: [questionnaireProfile], @@ -497,30 +848,18 @@ for (const relative of [...serialized.keys()]) { // Validate what we are about to publish, against the release's own model, before it is written. // The official Validator checks these later in CI; catching a malformed emission here names the // resource and the element rather than surfacing as a validator report a build later. -const generated = await import('@schmiedmayerlab/grove-fhir/zod/r4') -const schemaFor = (resourceType) => - generated[ - `${resourceType.charAt(0).toLowerCase()}${resourceType.slice(1)}Schema` - ] +const { resourceSchema } = await import('@schmiedmayerlab/grove-fhir/zod/r4') +const nonResourceArtifacts = new Set(['mobile-exchange/corpus.json']) const invalid = [] for (const [relative, resource] of resources) { - const candidates = - resource.resourceType === 'Bundle' ? - (resource.entry ?? []).map((entry) => entry.resource) - : [resource] - for (const candidate of candidates) { - const schema = schemaFor(candidate.resourceType) - if (!schema) continue - const result = schema.safeParse(candidate) - if (!result.success) { - invalid.push( - `${relative} (${candidate.resourceType}): ${result.error.issues - .map( - (issue) => `${issue.path.join('.') || ''} ${issue.message}`, - ) - .join('; ')}`, - ) - } + if (nonResourceArtifacts.has(relative)) continue + const result = resourceSchema.safeParse(resource) + if (!result.success) { + invalid.push( + `${relative}: ${result.error.issues + .map((issue) => `${issue.path.join('.') || ''} ${issue.message}`) + .join('; ')}`, + ) } } if (invalid.length > 0) { @@ -530,6 +869,7 @@ if (invalid.length > 0) { } await mkdir(resourceRoot, { recursive: true }) +await mkdir(resolve(fixtureRoot, 'mobile-exchange'), { recursive: true }) for (const [relative, value] of serialized) { const path = resolve(fixtureRoot, relative) if (check) { diff --git a/packages/fhir/scripts/generate-measurement-catalog.mjs b/packages/fhir/scripts/generate-measurement-catalog.mjs index ec82f372..585a6b24 100644 --- a/packages/fhir/scripts/generate-measurement-catalog.mjs +++ b/packages/fhir/scripts/generate-measurement-catalog.mjs @@ -7,75 +7,49 @@ // import { readFile, writeFile } from 'node:fs/promises' -import { dirname, resolve } from 'node:path' import { argv } from 'node:process' -import { fileURLToPath } from 'node:url' +import { URL } from 'node:url' import { format } from 'prettier' -const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..') -const catalogRoot = resolve(packageRoot, '.grove-fhir/catalog') -const capabilityPath = resolve( - packageRoot, - 'catalog/measurement-capabilities.json', -) -const inputPath = resolve(catalogRoot, 'measurement-catalog.json') -const packageGraphPath = resolve(catalogRoot, 'package-graph.json') -const exchangeIdentityPath = resolve(catalogRoot, 'exchange-identity.json') -const profileClaimsPath = resolve(catalogRoot, 'profile-claims.json') -const sensorCatalogPath = resolve(catalogRoot, 'sensor-catalog.json') -const healthConnectAdapterPath = resolve( - catalogRoot, - 'health-connect-adapter.json', -) -const healthConnectIdentityPath = resolve( - catalogRoot, - 'health-connect-identity.json', -) -const healthKitAdapterPath = resolve(catalogRoot, 'healthkit-adapter.json') -const sensorKitAdapterPath = resolve(catalogRoot, 'sensorkit-adapter.json') -const formatRegistryPath = resolve(catalogRoot, 'format-registry.json') -const sourceRefPath = resolve(packageRoot, 'grove-fhir.json') -const semanticCorpusPath = resolve( - packageRoot, - '.grove-fhir/Conformance/corpora/mobile-semantics/corpus.json', -) -const outputPaths = { - mobile: resolve(packageRoot, 'src/mobile/measurement-catalog.generated.ts'), - questionnaire: resolve( - packageRoot, - 'src/questionnaire/contract.generated.ts', - ), - provider: resolve(packageRoot, 'src/providers/contract.generated.ts'), -} +import { loadMeasurementCatalogInputs } from './measurement-catalog-inputs.mjs' +import { renderMeasurementCatalogSources } from './measurement-catalog-renderer.mjs' + +const { + capabilities, + catalog, + exchangeCorpus, + exchangeProtocol, + formatRegistry, + healthConnectAdapter, + healthKitAdapter, + outputPaths, + packageGraph, + profileClaims, + providerAdapter, + semanticCorpus, + sensorCatalog, + sensorKitAdapter, + sourceRef, +} = await loadMeasurementCatalogInputs(import.meta.url, argv) -const adapterOwners = ['healthkit', 'health-connect', 'sensorkit', 'providers'] +const adapterOwners = [ + 'google-health', + 'healthkit', + 'health-connect', + 'oura', + 'providers', + 'sensorkit', + 'withings', +] +const uniqueNonemptyStrings = (values) => + Array.isArray(values) && + values.length > 0 && + values.every((value) => typeof value === 'string' && value.length > 0) && + new Set(values).size === values.length const isMobileOwned = (measurement) => measurement.owner === undefined || measurement.owner === 'mobile' -const catalog = JSON.parse(await readFile(inputPath, 'utf8')) -const packageGraph = JSON.parse(await readFile(packageGraphPath, 'utf8')) -const exchangeIdentity = JSON.parse( - await readFile(exchangeIdentityPath, 'utf8'), -) -const profileClaims = JSON.parse(await readFile(profileClaimsPath, 'utf8')) -const sensorCatalog = JSON.parse(await readFile(sensorCatalogPath, 'utf8')) -const healthConnectAdapter = JSON.parse( - await readFile(healthConnectAdapterPath, 'utf8'), -) -const healthConnectIdentity = JSON.parse( - await readFile(healthConnectIdentityPath, 'utf8'), -) -const healthKitAdapter = JSON.parse( - await readFile(healthKitAdapterPath, 'utf8'), -) -const sensorKitAdapter = JSON.parse( - await readFile(sensorKitAdapterPath, 'utf8'), -) -const formatRegistry = JSON.parse(await readFile(formatRegistryPath, 'utf8')) -const sourceRef = JSON.parse(await readFile(sourceRefPath, 'utf8')) -const semanticCorpus = JSON.parse(await readFile(semanticCorpusPath, 'utf8')) -const capabilities = JSON.parse(await readFile(capabilityPath, 'utf8')) const measurements = catalog.measurements if (!Array.isArray(measurements) || measurements.length === 0) { throw new Error('Measurement catalog must contain measurements.') @@ -148,6 +122,22 @@ if ( } const keys = new Set() +const standardVitalProfiles = new Set( + [ + 'bp', + 'bodyheight', + 'bodytemp', + 'bodyweight', + 'heartrate', + 'oxygensat', + 'resprate', + ].map((id) => `http://hl7.org/fhir/StructureDefinition/${id}`), +) +const vitalSignsCategory = { + system: 'http://terminology.hl7.org/CodeSystem/observation-category', + code: 'vital-signs', + display: 'Vital Signs', +} for (const measurement of measurements) { if (typeof measurement.id !== 'string' || keys.has(measurement.id)) { throw new Error( @@ -171,6 +161,28 @@ for (const measurement of measurements) { `Normative measurement ${measurement.id} declares unknown owner ${String(measurement.owner)}.`, ) } + if ( + measurement.category !== undefined && + (typeof measurement.category !== 'object' || + measurement.category === null || + ['system', 'code', 'display'].some( + (field) => + typeof measurement.category[field] !== 'string' || + measurement.category[field].trim() === '', + )) + ) { + throw new Error( + `Normative measurement ${measurement.id} has an incomplete Observation category.`, + ) + } + if ( + standardVitalProfiles.has(measurement.standardProfile) && + JSON.stringify(measurement.category) !== JSON.stringify(vitalSignsCategory) + ) { + throw new Error( + `Standard vital-sign measurement ${measurement.id} must materialize its inherited vital-signs category.`, + ) + } if (measurement.valueKind === 'quantity') { if (measurement.quantity === null) { throw new Error( @@ -189,6 +201,12 @@ for (const measurement of measurements) { `Normative measurement ${measurement.id} requires a closed coded-result contract.`, ) } + } else if (measurement.valueKind === 'dateTime') { + if (measurement.quantity !== null) { + throw new Error( + `Date-time measurement ${measurement.id} must not declare a quantity.`, + ) + } } else if (measurement.valueKind === 'grouping') { // A panel states its members rather than a value of its own, so it declares neither a quantity // nor a coded result; the members are complete Observations that stand alone. @@ -209,12 +227,15 @@ for (const measurement of measurements) { const supportedSources = Object.values(measurement.coverage).filter( (status) => status === 'supported', ) + const evidencedSources = Object.values(measurement.coverage).filter( + (status) => ['platform-exclusive', 'supported'].includes(status), + ) if (isMobileOwned(measurement) && supportedSources.length < 2) { throw new Error( `Shared Mobile measurement ${measurement.id} requires at least two evidenced supported sources.`, ) } - if (supportedSources.length === 0) { + if (evidencedSources.length === 0) { throw new Error( `Normative measurement ${measurement.id} requires at least one evidenced supported source.`, ) @@ -271,7 +292,6 @@ if (packageGraph.fhirVersion !== '4.0.1' || catalog.fhirVersion !== '4.0.1') { for (const consumedCatalog of [ sensorCatalog, healthConnectAdapter, - healthConnectIdentity, healthKitAdapter, sensorKitAdapter, ]) { @@ -285,18 +305,16 @@ for (const consumedCatalog of [ } } -// A released version is pinned by its tag so the reference stays readable; a bare commit stays -// valid for pinning work that predates a release. -const pinnedRef = sourceRef.ref ?? sourceRef.sha +const pinnedRef = sourceRef.ref if ( sourceRef.repository !== 'https://github.com/SchmiedmayerLab/grove-fhir' || typeof pinnedRef !== 'string' || - !/^(?:[\da-f]{40}|\d+\.\d+\.\d+(?:-[\dA-Za-z]+(?:[.-][\dA-Za-z]+)*)?)$/u.test( - pinnedRef, - ) + !/^[\da-f]{40}$/u.test(pinnedRef) || + typeof sourceRef.archiveSha256 !== 'string' || + !/^[\da-f]{64}$/u.test(sourceRef.archiveSha256) ) { throw new Error( - 'The IG release these catalogs were read from must be pinned.', + 'The IG catalogs must be pinned by an immutable commit and exact archive SHA-256.', ) } @@ -365,15 +383,43 @@ const questionnaireProfiles = selectEntries(profiles, [ 'grove-questionnaire', 'grove-questionnaire-response', ]) +const mobileProfiles = selectEntries(profiles, [ + 'grove-application-device', + 'grove-host-device', + 'grove-mobile-conversion-provenance', + 'grove-mobile-exchange-bundle', + 'grove-mobile-retraction-bundle', + 'grove-mobile-retraction-provenance', + 'grove-recording-device', + 'grove-sensor-recording-document', +]) +const providerObservationProfileIds = providerAdapter.providers.map( + ({ id, observationProfile }) => { + const profile = Object.entries(profiles).find( + ([, canonical]) => canonical === observationProfile, + )?.[0] + if (profile === undefined) { + throw new Error( + `Provider ${id} refers to unknown Observation profile ${String(observationProfile)}.`, + ) + } + return profile + }, +) const providerProfiles = selectEntries(profiles, [ ...measurements.map((measurement) => measurement.profile), + ...providerObservationProfileIds, 'grove-application-device', + 'grove-host-device', + 'grove-mobile-conversion-provenance', 'grove-mobile-exchange-bundle', + 'grove-mobile-retraction-bundle', + 'grove-mobile-retraction-provenance', 'grove-recording-device', 'grove-sensor-recording-document', - 'provider-conversion-provenance', - 'provider-observation', - 'provider-recording-document', + 'providers-conversion-provenance', + 'providers-observation', + 'providers-recording-document', ]) const providerPackageCanonicals = selectEntries(packageCanonicals, [ 'mobile', @@ -386,8 +432,13 @@ if ( profileClaims.observationAdapterClaim?.cardinality !== 2 || profileClaims.observationAdapterClaim?.inheritedProfilesAreNotDeclared !== true || - !profileClaims.observationAdapterClaim?.adapterProfiles?.includes( - profiles['provider-observation'], + profileClaims.observationAdapterClaim?.adapterProfiles?.includes( + profiles['providers-observation'], + ) || + !providerAdapter.providers.every(({ observationProfile }) => + profileClaims.observationAdapterClaim?.adapterProfiles?.includes( + observationProfile, + ), ) || !profileClaims.observationAdapterClaim?.forbiddenExplicitProfiles?.includes( profiles['grove-mobile-observation'], @@ -419,9 +470,9 @@ if ( connectedRecordingClaim.profiles?.[0] !== profiles['grove-sensor-recording-document'] || connectedRecordingClaim.profiles?.[1] !== - profiles['provider-recording-document'] || + profiles['providers-recording-document'] || connectedProvenanceClaim?.profile !== - profiles['provider-conversion-provenance'] + profiles['providers-conversion-provenance'] ) { throw new Error('Adapter-specific profile claims are incomplete.') } @@ -513,13 +564,23 @@ if ( throw new Error('Health Connect sleep-stage mapping is incomplete.') } +const healthConnectDataOriginApplication = + healthConnectAdapter.dataOriginApplication if ( - healthConnectIdentity.composition?.separator !== '|' || - healthConnectIdentity.composition?.versionPrefix !== 'v1' || - healthConnectIdentity.vectors?.length !== 7 + healthConnectDataOriginApplication?.sourceField !== + 'Metadata.dataOrigin.packageName' || + healthConnectDataOriginApplication.r4Element !== + 'Provenance.entity.agent.who' || + healthConnectDataOriginApplication.referenceType !== 'Device' || + healthConnectDataOriginApplication.referenceMode !== 'identifier-only' || + healthConnectDataOriginApplication.identifierSystem !== + 'https://grovealliance.org/fhir/health-connect/NamingSystem/android-package-name' || + healthConnectDataOriginApplication.literalReferenceAllowed !== false || + healthConnectDataOriginApplication.eventBundleEntryRequired !== false || + healthConnectDataOriginApplication.profileClaimRequired !== false ) { throw new Error( - 'Health Connect deterministic identity vectors are incomplete.', + 'Health Connect DataOrigin application must remain a typed identifier-only logical Device Reference.', ) } @@ -531,6 +592,87 @@ const healthKitDerivedAggregates = healthKitAdapter.derivedAggregates ?? [] const healthKitStatusVocabulary = new Set( Object.keys(healthKitAdapter.statusVocabulary ?? {}), ) +const healthKitClinicalRecordAdmission = + healthKitAdapter.clinicalRecordAdmission +const healthKitClinicalFhirRepresentation = + healthKitClinicalRecordAdmission?.fhirRepresentation +const healthKitApplicationDeviceIdentity = + healthKitAdapter.applicationDeviceIdentity +const healthKitBundleIdentifier = + healthKitApplicationDeviceIdentity?.bundleIdentifier +if ( + typeof healthKitClinicalRecordAdmission !== 'object' || + healthKitClinicalRecordAdmission === null || + Array.isArray(healthKitClinicalRecordAdmission) || + Object.keys(healthKitClinicalRecordAdmission).sort().join(',') !== + 'admittedFHIRRelease,fhirRepresentation,payloadFormat,profile,rejectedFHIRReleases,rule,sourceFHIRReleaseField' || + healthKitClinicalRecordAdmission.profile !== + profiles['healthkit-clinical-record-document'] || + typeof healthKitClinicalRecordAdmission.payloadFormat !== 'string' || + formatRegistry.formats?.[healthKitClinicalRecordAdmission.payloadFormat] + ?.status !== 'active' || + formatRegistry.formats[healthKitClinicalRecordAdmission.payloadFormat] + .contentType !== 'application/fhir+json' || + typeof healthKitClinicalRecordAdmission.sourceFHIRReleaseField !== 'string' || + healthKitClinicalRecordAdmission.sourceFHIRReleaseField.trim() === '' || + typeof healthKitClinicalRecordAdmission.admittedFHIRRelease !== 'string' || + healthKitClinicalRecordAdmission.admittedFHIRRelease.trim() === '' || + !uniqueNonemptyStrings( + healthKitClinicalRecordAdmission.rejectedFHIRReleases, + ) || + healthKitClinicalRecordAdmission.rejectedFHIRReleases.includes( + healthKitClinicalRecordAdmission.admittedFHIRRelease, + ) || + typeof healthKitClinicalRecordAdmission.rule !== 'string' || + healthKitClinicalRecordAdmission.rule.trim() === '' || + typeof healthKitClinicalFhirRepresentation !== 'object' || + healthKitClinicalFhirRepresentation === null || + Array.isArray(healthKitClinicalFhirRepresentation) || + Object.keys(healthKitClinicalFhirRepresentation).sort().join(',') !== + 'cardinality,extensionUrl,fixedValue,resourceType,valueElement' || + healthKitClinicalFhirRepresentation.resourceType !== 'DocumentReference' || + typeof healthKitClinicalFhirRepresentation.extensionUrl !== 'string' || + !URL.canParse(healthKitClinicalFhirRepresentation.extensionUrl) || + healthKitClinicalFhirRepresentation.valueElement !== 'valueCode' || + healthKitClinicalFhirRepresentation.cardinality?.min !== 1 || + healthKitClinicalFhirRepresentation.cardinality.max !== 1 || + healthKitClinicalFhirRepresentation.fixedValue !== + healthKitClinicalRecordAdmission.admittedFHIRRelease +) { + throw new Error( + 'HealthKit clinical-record FHIR admission must remain complete and closed.', + ) +} +if ( + typeof healthKitApplicationDeviceIdentity !== 'object' || + healthKitApplicationDeviceIdentity === null || + Array.isArray(healthKitApplicationDeviceIdentity) || + Object.keys(healthKitApplicationDeviceIdentity).sort().join(',') !== + 'bundleIdentifier,classificationRule,profile,snapshotIdentifierRole' || + healthKitApplicationDeviceIdentity.profile !== + profiles['healthkit-application-device'] || + healthKitApplicationDeviceIdentity.snapshotIdentifierRole !== + 'device-snapshot' || + typeof healthKitApplicationDeviceIdentity.classificationRule !== 'string' || + healthKitApplicationDeviceIdentity.classificationRule.trim() === '' || + typeof healthKitBundleIdentifier !== 'object' || + healthKitBundleIdentifier === null || + Array.isArray(healthKitBundleIdentifier) || + Object.keys(healthKitBundleIdentifier).sort().join(',') !== + 'cardinality,meaning,system,typeCode,typeSystem' || + healthKitBundleIdentifier.system !== + 'https://grovealliance.org/fhir/healthkit/NamingSystem/apple-bundle-id' || + healthKitBundleIdentifier.typeSystem !== + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-identifier-type' || + healthKitBundleIdentifier.typeCode !== 'apple-bundle-id' || + healthKitBundleIdentifier.cardinality !== '1..1' || + typeof healthKitBundleIdentifier.meaning !== 'string' || + healthKitBundleIdentifier.meaning.trim() === '' +) { + throw new Error( + 'HealthKit application Device identity must remain complete and closed.', + ) +} if ( !Number.isInteger(healthKitSourceRowCount) || healthKitSourceRowCount <= 0 || @@ -575,25 +717,23 @@ if ( } const expectedConnectedProviders = ['google-health-api', 'oura', 'withings'] -const providerAdapterPath = resolve(catalogRoot, 'providers-adapter.json') -const providerAdapter = JSON.parse(await readFile(providerAdapterPath, 'utf8')) if ( providerAdapter.fhirVersion !== '4.0.1' || providerAdapter.version !== packageGraph.version || providerAdapter.packageId !== 'org.grovealliance.fhir.providers' || providerAdapter.canonical !== packageCanonicals['providers'] || - providerAdapter.adapterProfile !== profiles['provider-observation'] || + providerAdapter.adapterProfile !== profiles['providers-observation'] || providerAdapter.recordingDocument?.sourceNeutralProfile !== profiles['grove-sensor-recording-document'] || providerAdapter.recordingDocument?.adapterProfile !== - profiles['provider-recording-document'] || - providerAdapter.recordingDocument?.outputDiscriminator !== - 'native-recording' || + profiles['providers-recording-document'] || + providerAdapter.recordingDocument?.outputRole !== 'native-recording' || + providerAdapter.recordingDocument?.outputDiscriminator !== 'single' || providerAdapter.rawPayloadAdmission?.allowedAssertions?.join(',') !== rawPayloadAssertions.join(',') || providerAdapter.rawPayloadAdmission?.notFHIRAuthorization !== true || providerAdapter.conversionProvenanceProfile !== - profiles['provider-conversion-provenance'] || + profiles['providers-conversion-provenance'] || providerAdapter.sourceTypeExtension?.url !== `${packageCanonicals['providers']}/StructureDefinition/provider-source-type` || providerAdapter.sourceTypeExtension?.codeSystem !== @@ -610,13 +750,143 @@ if ( throw new Error('Provider providers must be the exact closed provider set.') } -const providerScalarMappings = {} -const providerRawMappings = {} +const markerResourceTypes = (definition, kind) => { + if ( + kind === 'extension-url' && + definition?.valueElement === 'valueCode' && + Array.isArray(definition.contexts) + ) { + const resourceTypes = definition.contexts + if ( + resourceTypes.length === 0 || + resourceTypes.some( + (resourceType) => + typeof resourceType !== 'string' || + !/^[A-Z][A-Za-z]+$/u.test(resourceType), + ) || + new Set(resourceTypes).size !== resourceTypes.length + ) { + throw new Error( + 'The extension-url adapter source marker contexts are incomplete.', + ) + } + return resourceTypes + } + if ( + typeof definition !== 'object' || + definition === null || + Array.isArray(definition) || + definition.cardinality !== 'exactly one' || + typeof definition.r4Element !== 'string' + ) { + throw new Error(`The ${kind} adapter source marker is incomplete.`) + } + const terminal = + kind === 'coding-system' ? 'code.coding' : 'extension.valueCode' + const resourceTypes = definition.r4Element.split(' or ').map((path) => { + const match = /^([A-Z][A-Za-z]+)\.(.+)$/u.exec(path) + if (match?.[1] === undefined || match[2] !== terminal) { + throw new Error( + `The ${kind} adapter source marker has an unsupported R4 element path ${path}.`, + ) + } + return match[1] + }) + if ( + resourceTypes.length === 0 || + new Set(resourceTypes).size !== resourceTypes.length + ) { + throw new Error(`The ${kind} adapter source marker paths are not unique.`) + } + return resourceTypes +} + +const adapterClaim = (catalogDefinition) => { + const adapter = catalogDefinition.identity?.adapterId + const claim = profileClaims.adapterConversionProvenanceClaims?.find( + (candidate) => candidate.adapter === adapter, + ) + if ( + typeof adapter !== 'string' || + claim === undefined || + !Array.isArray(claim.targetAdapterProfiles) || + claim.targetAdapterProfiles.length === 0 || + new Set(claim.targetAdapterProfiles).size !== + claim.targetAdapterProfiles.length + ) { + throw new Error('An adapter source marker has no closed profile claim.') + } + return claim +} + +const extensionMarker = (definition) => { + const valueSystem = definition?.codeSystem ?? definition?.valueSystem + if ( + typeof definition?.url !== 'string' || + definition.url.trim() === '' || + typeof valueSystem !== 'string' || + valueSystem.trim() === '' + ) { + throw new Error('An adapter source extension marker is incomplete.') + } + return { + kind: 'extension-url', + url: definition.url, + resourceTypes: markerResourceTypes(definition, 'extension-url'), + } +} + +const adapterSourceMarkerClaims = [ + { + catalog: healthKitAdapter, + markers: [extensionMarker(healthKitAdapter.sourceTypeExtension)], + }, + { + catalog: healthConnectAdapter, + markers: [extensionMarker(healthConnectAdapter.sourceTypeExtension)], + }, + { + catalog: providerAdapter, + markers: [ + extensionMarker(providerAdapter.sourceTypeExtension), + extensionMarker(providerAdapter.providerExtension), + ], + }, + { + catalog: sensorKitAdapter, + markers: [extensionMarker(sensorKitAdapter.sourceTypeExtension)], + }, +].map(({ catalog: catalogDefinition, markers }) => { + const claim = adapterClaim(catalogDefinition) + return { + adapter: claim.adapter, + profiles: claim.targetAdapterProfiles, + markers, + } +}) + +const ownedAdapterProfiles = adapterSourceMarkerClaims.flatMap( + ({ profiles: ownedProfiles }) => ownedProfiles, +) +if (new Set(ownedAdapterProfiles).size !== ownedAdapterProfiles.length) { + throw new Error('Adapter source-marker profile ownership must be disjoint.') +} + +const providerScalarOutputRoles = {} +const providerScalarOutputDiscriminators = {} +const providerRawOutputRoles = {} +const providerRawOutputDiscriminators = {} const providerRecordEffectiveRules = {} const completeCivilDayEffectiveRule = 'the source civil day represented as a complete day Period; midpoint substitution is forbidden' for (const provider of providerAdapter.providers) { if ( + typeof provider.measurementOwner !== 'string' || + provider.measurementOwner.length === 0 || + typeof provider.observationProfile !== 'string' || + !profileClaims.observationAdapterClaim.adapterProfiles.includes( + provider.observationProfile, + ) || provider.sourceTypes?.length !== provider.sourceTypeCount || new Set(provider.sourceTypes.map((entry) => entry.token)).size !== provider.sourceTypeCount @@ -625,18 +895,23 @@ for (const provider of providerAdapter.providers) { } const sourceMappings = {} + const sourceDiscriminators = {} const rawSourceMappings = {} + const rawSourceDiscriminators = {} const recordEffectiveRules = {} for (const sourceType of provider.sourceTypes) { - const supportedElements = (sourceType.elements ?? []).filter( - (element) => element.status === 'supported', + const structuredElements = (sourceType.elements ?? []).filter( + (element) => + element.status === 'supported' || + element.status === 'platform-exclusive', ) const measurementIds = [ ...new Set( (sourceType.elements ?? []) .filter( (element) => - element.status === 'supported' && + (element.status === 'supported' || + element.status === 'platform-exclusive') && element.groupedMapping === undefined, ) .flatMap((element) => element.measurementIds ?? []), @@ -653,10 +928,13 @@ for (const provider of providerAdapter.providers) { sourceMappings[sourceType.token] = Object.fromEntries( measurementIds.map((measurementId) => [measurementId, measurementId]), ) + sourceDiscriminators[sourceType.token] = Object.fromEntries( + measurementIds.map((measurementId) => [measurementId, 'single']), + ) } const effectiveRules = [ ...new Set( - supportedElements + structuredElements .map((element) => element.effective) .filter((rule) => typeof rule === 'string'), ), @@ -664,7 +942,7 @@ for (const provider of providerAdapter.providers) { if (effectiveRules.includes(completeCivilDayEffectiveRule)) { if ( effectiveRules.length !== 1 || - supportedElements.some( + structuredElements.some( (element) => element.effective !== completeCivilDayEffectiveRule, ) ) { @@ -694,6 +972,8 @@ for (const provider of providerAdapter.providers) { ) } rawSourceMappings[sourceType.token] = + providerAdapter.recordingDocument.outputRole + rawSourceDiscriminators[sourceType.token] = providerAdapter.recordingDocument.outputDiscriminator } } @@ -701,7 +981,11 @@ for (const provider of providerAdapter.providers) { if ( grouped.status !== 'supported' || typeof grouped.token !== 'string' || + grouped.token.length === 0 || + typeof grouped.outputRole !== 'string' || + grouped.outputRole.length === 0 || typeof grouped.outputDiscriminator !== 'string' || + grouped.outputDiscriminator.length === 0 || !Array.isArray(grouped.measurementIds) || grouped.measurementIds.length === 0 ) { @@ -715,6 +999,12 @@ for (const provider of providerAdapter.providers) { } } sourceMappings[grouped.token] = Object.fromEntries( + grouped.measurementIds.map((measurementId) => [ + measurementId, + grouped.outputRole, + ]), + ) + sourceDiscriminators[grouped.token] = Object.fromEntries( grouped.measurementIds.map((measurementId) => [ measurementId, grouped.outputDiscriminator, @@ -722,15 +1012,42 @@ for (const provider of providerAdapter.providers) { ) } for (const [sourceType, mappings] of Object.entries(sourceMappings)) { - const discriminators = Object.values(mappings) - if (new Set(discriminators).size !== discriminators.length) { + const discriminators = sourceDiscriminators[sourceType] + if ( + discriminators === undefined || + JSON.stringify(Object.keys(discriminators)) !== + JSON.stringify(Object.keys(mappings)) + ) { + throw new Error( + `Provider ${provider.id}/${sourceType} output coordinate keys are inconsistent.`, + ) + } + const coordinates = Object.entries(mappings).map( + ([measurementId, outputRole]) => + `${outputRole.length}:${outputRole}${discriminators[measurementId].length}:${discriminators[measurementId]}`, + ) + if (new Set(coordinates).size !== coordinates.length) { throw new Error( - `Provider ${provider.id}/${sourceType} output discriminators must be unique.`, + `Provider ${provider.id}/${sourceType} output coordinates must be unique.`, ) } + for (const measurementId of Object.keys(mappings)) { + const definition = implemented[measurementId] + if ( + !isMobileOwned(definition) && + (definition?.owner !== provider.measurementOwner || + !['codeableConcept', 'quantity'].includes(definition.valueKind)) + ) { + throw new Error( + `Provider ${provider.id}/${sourceType} cannot admit owner-exclusive measurement ${measurementId} outside its exact scalar facade.`, + ) + } + } } - providerScalarMappings[provider.id] = sourceMappings - providerRawMappings[provider.id] = rawSourceMappings + providerScalarOutputRoles[provider.id] = sourceMappings + providerScalarOutputDiscriminators[provider.id] = sourceDiscriminators + providerRawOutputRoles[provider.id] = rawSourceMappings + providerRawOutputDiscriminators[provider.id] = rawSourceDiscriminators if (Object.keys(recordEffectiveRules).length > 0) { providerRecordEffectiveRules[provider.id] = recordEffectiveRules } @@ -745,6 +1062,16 @@ if ( measurementIds: ['step-count', 'active-energy', 'distance'], outputsShareEffective: true, }, + daily_cardiovascular_age: { + kind: 'complete-civil-day-period', + measurementIds: ['oura-cardiovascular-age'], + outputsShareEffective: true, + }, + daily_readiness: { + kind: 'complete-civil-day-period', + measurementIds: ['oura-readiness-score'], + outputsShareEffective: true, + }, }, withings: { 'getactivity:steps': { @@ -771,40 +1098,81 @@ if ( } if ( - Object.values(providerRawMappings).flatMap((mapping) => Object.keys(mapping)) - .length !== 4 + Object.values(providerRawOutputRoles).flatMap((mapping) => + Object.keys(mapping), + ).length !== 4 ) { throw new Error('Provider must admit exactly four raw source tokens.') } +if ( + JSON.stringify( + Object.fromEntries( + Object.entries(providerRawOutputRoles).map(([provider, mappings]) => [ + provider, + Object.keys(mappings), + ]), + ), + ) !== + JSON.stringify( + Object.fromEntries( + Object.entries(providerRawOutputDiscriminators).map( + ([provider, mappings]) => [provider, Object.keys(mappings)], + ), + ), + ) +) { + throw new Error( + 'Provider raw output role/discriminator keys are inconsistent.', + ) +} const providerIdentity = providerAdapter.identity +const expectedProviderSourceComponents = [ + 'provider-code', + 'source-type', + 'provider-scope-system', + 'provider-scope-value', + 'native-record-id', +] +const expectedProviderOutputComponents = [ + ...expectedProviderSourceComponents, + 'output-role', + 'output-discriminator', +] +const expectedProviderArtifactComponents = [ + ...expectedProviderSourceComponents, + 'format-code', + 'part-index', +] if ( - providerIdentity?.digest !== undefined || - providerIdentity.sourceRecord?.system !== - 'https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id' || - providerIdentity.output?.system !== - 'https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id' || - providerIdentity.output?.outputDiscriminatorRule - ?.ordinarySupportedMeasurement !== - 'the exact measurementId string from the supported element mapping' || - providerIdentity.output?.outputDiscriminatorRule?.groupedMapping !== - 'the exact outputDiscriminator declared on that groupedMappings row' || - providerIdentity.output?.outputDiscriminatorRule?.mappedStandardRaw !== - 'native-recording' || - providerIdentity.output?.outputDiscriminatorRule?.noFallback !== true || - providerIdentity.vectors?.length !== 9 || - providerIdentity.vectors.filter((vector) => vector.role === 'sourceRecord') - .length !== 3 || - providerIdentity.vectors.filter((vector) => vector.role === 'output') - .length !== 2 || - providerIdentity.vectors.filter((vector) => vector.role === 'contentDerived') - .length !== 1 || - providerIdentity.vectors.filter((vector) => vector.role === 'writerRecord') - .length !== 1 || - providerIdentity.composition?.separator !== '|' || - // Export-created nodes moved to the deployment's namespace, so this guide owns neither. - providerIdentity.conversion !== undefined || - providerIdentity.exchange !== undefined + providerIdentity?.contract !== 'catalog/exchange-protocol.json' || + providerIdentity.protocolVersion !== 2 || + providerIdentity.adapterId !== 'providers' || + providerIdentity.sourceRecord?.identityKind !== 'provider-record' || + providerIdentity.sourceRecord?.identifierRole !== 'source-record' || + JSON.stringify(providerIdentity.sourceRecord?.components) !== + JSON.stringify(expectedProviderSourceComponents) || + providerIdentity.sourceOutput?.identityKind !== 'provider-output' || + providerIdentity.sourceOutput?.identifierRole !== 'source-output' || + JSON.stringify(providerIdentity.sourceOutput?.components) !== + JSON.stringify(expectedProviderOutputComponents) || + providerIdentity.writerRecord?.identityKind !== 'writer-record' || + providerIdentity.writerRecord?.identifierRole !== 'writer-record' || + providerIdentity.sourceArtifact?.identityKind !== 'provider-artifact' || + providerIdentity.sourceArtifact?.identifierRole !== 'source-artifact' || + JSON.stringify(providerIdentity.sourceArtifact?.components) !== + JSON.stringify(expectedProviderArtifactComponents) || + providerAdapter.providers.some( + ({ identifierScope, providerScopeMode, identifierScopeReason }) => + !['account', 'global'].includes(identifierScope) || + providerScopeMode !== + (identifierScope === 'account' ? + 'deployment-scoped-account-pseudonym' + : 'documented-global-key-space') || + typeof identifierScopeReason !== 'string' || + identifierScopeReason.trim() === '', + ) || + typeof providerIdentity.resourceIdPolicy !== 'string' ) { throw new Error('Provider deterministic identity contract is incomplete.') } @@ -814,7 +1182,14 @@ if ( formatRegistry.version !== packageGraph.version || typeof formatRegistry.codeSystem !== 'string' || typeof formatRegistry.valueSet !== 'string' || - Object.keys(formatRegistry.formats ?? {}).length === 0 + Object.keys(formatRegistry.formats ?? {}).length === 0 || + Object.hasOwn(formatRegistry.formats ?? {}, 'fhir-resource-array') || + formatRegistry.formats?.['fhir-collection-bundle']?.contentType !== + 'application/fhir+json' || + formatRegistry.formats?.['fhir-r4-resource']?.contentType !== + 'application/fhir+json' || + formatRegistry.formats?.['provider-recording']?.contentType !== + 'application/vnd.grovealliance.provider+json' ) { throw new Error('Recording format registry is incomplete.') } @@ -823,6 +1198,7 @@ if ( const recordingFormatRegistry = { codeSystem: formatRegistry.codeSystem, valueSet: formatRegistry.valueSet, + version: formatRegistry.version, formats: Object.fromEntries( Object.entries(formatRegistry.formats).map(([code, entry]) => { if ( @@ -845,12 +1221,10 @@ const recordingFormatRegistry = { } const providerAdmittedMeasurements = new Set( - Object.values(providerScalarMappings).flatMap((sourceMappings) => + Object.values(providerScalarOutputRoles).flatMap((sourceMappings) => Object.values(sourceMappings).flatMap((mapping) => Object.keys(mapping)), ), ) -// The Provider facade emits shared Mobile profiles only; owner-exclusive -// mappings stay in the contract data but are outside the implemented surface. const sharedAdmittedMeasurements = new Set( [...providerAdmittedMeasurements].filter((id) => isMobileOwned(implemented[id]), @@ -882,27 +1256,352 @@ for (const row of healthKitAdapter.rows) { } } +const protocolIdentityKinds = exchangeProtocol.opaqueIdentity?.identityKinds +const identityKindsByName = new Map( + (protocolIdentityKinds ?? []).map((definition) => [ + definition.kind, + definition, + ]), +) +const protocolIdentifierRoles = new Set( + (protocolIdentityKinds ?? []).map(({ identifierRole }) => identifierRole), +) +const identityVectors = exchangeProtocol.testVectors?.identities +const invalidIdentityVectors = exchangeProtocol.testVectors?.invalidIdentities +const providerCodes = new Set(providerAdapter.providers.map(({ id }) => id)) +const providerIdentityKinds = new Set([ + 'provider-record', + 'provider-output', + 'provider-artifact', +]) +const genericSourceIdentityKinds = new Set([ + 'source-record', + 'source-output', + 'source-artifact', +]) +const sourceOutputVector = identityVectors?.find( + ({ id }) => id === 'multi-output-sample', +) +const sourceContextVector = identityVectors?.find( + ({ id }) => id === 'healthkit-medication-source-context', +) +const opaqueIdentityValue = + /^v2:[A-Za-z0-9._-]+:[1-9][0-9]*:[A-Za-z0-9_-]{43}$/u +const retractionTargetRules = + exchangeProtocol.lifecycle?.retraction?.targetRoles +const retractionTargetEntries = + ( + typeof retractionTargetRules === 'object' && + retractionTargetRules !== null && + !Array.isArray(retractionTargetRules) + ) ? + Object.entries(retractionTargetRules) + : [] +const adapterOnlyOutputProfileClaims = + exchangeProtocol.lifecycle?.active?.adapterOnlyOutputProfileClaims +const activeEntryResourcePolicy = + exchangeProtocol.lifecycle?.active?.entryResourcePolicy +const adapterOnlyClaimRows = [ + profileClaims.healthConnectSpecimenClaim, + ...(profileClaims.healthKitPlatformExclusiveResourceClaims ?? []), +] +const adapterOnlyClaimTypes = adapterOnlyClaimRows.map( + ({ resourceType }) => resourceType, +) +const expectedHealthKitSingleProfiles = measurements + .filter((measurement) => measurement.owner === 'healthkit') + .map((measurement) => profiles[measurement.profile]) +const healthKitSingleProfiles = + profileClaims.healthKitSingleProfileObservationClaims?.profiles +const documentProfileClaimRows = [ + profileClaims.sensorRecordingDocumentClaim, + profileClaims.healthKitRecordingDocumentClaim, + profileClaims.healthKitClinicalRecordDocumentClaim, + profileClaims.providerRecordingDocumentClaim, + profileClaims.sensorKitRecordingDocumentClaim, +] +const exactSet = (left, right) => + Array.isArray(left) && + Array.isArray(right) && + left.length === right.length && + new Set(left).size === left.length && + left.every((value) => right.includes(value)) +const activeOutputResourceTypes = [ + 'Observation', + 'DocumentReference', + 'Specimen', + 'VisionPrescription', + 'MedicationAdministration', + 'MedicationStatement', +] +const activeSupportingResourceTypes = [ + 'Patient', + 'Device', + 'ResearchStudy', + 'ResearchSubject', + 'PlanDefinition', + 'QuestionnaireResponse', +] +const activeDeviceClaims = profileClaims.activeDeviceClaims +const activeQuestionnaireResponseClaim = + profileClaims.activeQuestionnaireResponseClaim +const expectedReferencePaths = [ + ['Observation', 'subject', ['Patient']], + ['Observation', 'device', ['Device']], + ['Observation', 'specimen', ['Specimen']], + ['Observation', 'focus', ['Location']], + ['Observation', 'hasMember', ['Observation']], + [ + 'Observation', + 'derivedFrom', + ['Observation', 'DocumentReference', 'QuestionnaireResponse'], + ], + ['DocumentReference', 'subject', ['Patient']], + ['Specimen', 'subject', ['Patient']], + ['MedicationAdministration', 'subject', ['Patient']], + ['MedicationStatement', 'subject', ['Patient']], + ['VisionPrescription', 'patient', ['Patient']], + ['ResearchSubject', 'individual', ['Patient']], + ['ResearchSubject', 'study', ['ResearchStudy']], + ['ResearchStudy', 'protocol', ['PlanDefinition']], + ['Device', 'parent', ['Device']], +] +const expectedExtensionTargets = [ + [ + 'http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice', + ['Device'], + ], + [ + 'http://hl7.org/fhir/StructureDefinition/workflow-researchStudy', + ['ResearchStudy'], + ], +] +const referencePaths = exchangeProtocol.referencePolicy?.paths?.map( + ({ resourceType, path, targetTypes }) => [resourceType, path, targetTypes], +) +const extensionTargets = + exchangeProtocol.referencePolicy?.extensionTargets?.map( + ({ url, targetTypes }) => [url, targetTypes], + ) +const exchangeRuleDiagnostics = Object.fromEntries( + (exchangeCorpus.cases ?? []).map(({ expectedRule }) => [ + expectedRule?.code, + { + reason: expectedRule?.reason, + severity: expectedRule?.severity, + }, + ]), +) +const exchangeRuleRows = exchangeCorpus.cases?.map( + ({ expectedRule }) => expectedRule, +) if ( - exchangeIdentity.profile !== profiles['grove-mobile-exchange-bundle'] || - typeof exchangeIdentity.entryIdentifierExtension !== 'string' || - exchangeIdentity.fullUrlAlgorithm?.name !== 'uuid-v5-composed-identifier-v1' + exchangeProtocol.schemaVersion !== 1 || + exchangeProtocol.version !== packageGraph.version || + exchangeProtocol.protocolVersion !== 2 || + exchangeProtocol.releaseVersion !== packageGraph.version || + exchangeProtocol.fhirVersion !== '4.0.1' || + exchangeProtocol.profiles?.activeBundle !== + profiles['grove-mobile-exchange-bundle'] || + exchangeProtocol.profiles?.conversionProvenance !== + profiles['grove-mobile-conversion-provenance'] || + exchangeProtocol.profiles?.retractionBundle !== + profiles['grove-mobile-retraction-bundle'] || + exchangeProtocol.profiles?.retractionProvenance !== + profiles['grove-mobile-retraction-provenance'] || + exchangeProtocol.opaqueIdentity?.algorithm !== 'HMAC-SHA-256' || + exchangeProtocol.opaqueIdentity?.keyRequirements?.minimumBytes !== 32 || + exchangeProtocol.opaqueIdentity?.domain !== + 'org.grovealliance.fhir.identity.v2' || + exchangeProtocol.entryIdentity?.entryNode?.domain !== + 'org.grovealliance.fhir.entry-node.v2' || + exchangeProtocol.entryIdentity?.fullUrl?.namespace !== + '43df4575-bff7-5a57-9a80-2472cd2b0623' || + !uniqueNonemptyStrings( + exchangeProtocol.entryIdentity?.resourceIdentifierPriority, + ) || + exchangeProtocol.entryIdentity.resourceIdentifierPriority.some( + (role) => !protocolIdentifierRoles.has(role), + ) || + exchangeProtocol.extensions?.entryNodeKey !== + `${packageCanonicals.mobile}/StructureDefinition/grove-exchange-entry-node-key` || + exchangeProtocol.extensions?.retractionTargetRole !== + `${packageCanonicals.mobile}/StructureDefinition/grove-retraction-target-role` || + exchangeProtocol.codeSystems?.identifierRole !== + `${packageCanonicals.mobile}/CodeSystem/grove-identifier-role` || + exchangeProtocol.codeSystems?.lifecycleEvent !== + `${packageCanonicals.mobile}/CodeSystem/grove-lifecycle-event` || + exchangeProtocol.codeSystems?.retractionTargetRole !== + `${packageCanonicals.mobile}/CodeSystem/grove-retraction-target-role` || + exchangeProtocol.lifecycle?.retraction?.activityCode !== + 'source-record-retracted' || + adapterOnlyOutputProfileClaims?.authority !== 'catalog/profile-claims.json' || + !exactSet( + adapterOnlyOutputProfileClaims?.resourceTypes, + adapterOnlyClaimTypes, + ) || + !exactSet( + activeEntryResourcePolicy?.outputResourceTypes, + activeOutputResourceTypes, + ) || + !exactSet( + activeEntryResourcePolicy?.supportingResourceTypes, + activeSupportingResourceTypes, + ) || + activeEntryResourcePolicy?.lifecycleResourceType !== 'Provenance' || + activeEntryResourcePolicy.otherResourceTypesAllowed !== false || + activeEntryResourcePolicy.containedResourcesAllowed !== false || + activeEntryResourcePolicy.supportingResourcesMustBeConnected !== true || + activeEntryResourcePolicy.profileClaimAuthority !== + 'catalog/profile-claims.json' || + adapterOnlyClaimRows.some( + (claim) => + claim?.cardinality !== 1 || + claim.otherProfilesAllowed !== false || + typeof claim.profile !== 'string' || + !uniqueNonemptyStrings(claim.requiredIdentifierRoles), + ) || + !exactSet(healthKitSingleProfiles, expectedHealthKitSingleProfiles) || + profileClaims.healthKitSingleProfileObservationClaims?.cardinality !== 1 || + profileClaims.healthKitSingleProfileObservationClaims + ?.otherProfilesAllowed !== false || + documentProfileClaimRows.some( + (claim) => + typeof claim !== 'object' || + claim === null || + claim.otherProfilesAllowed !== false || + claim.cardinality !== claim.profiles?.length || + !uniqueNonemptyStrings(claim.profiles) || + !uniqueNonemptyStrings(claim.requiredIdentifierRoles), + ) || + !Array.isArray(activeDeviceClaims) || + activeDeviceClaims.length !== 4 || + activeDeviceClaims.some( + (claim) => + claim?.resourceType !== 'Device' || + claim.cardinality !== 1 || + claim.otherProfilesAllowed !== false || + claim.profiles?.length !== 1 || + !uniqueNonemptyStrings(claim.profiles) || + !uniqueNonemptyStrings(claim.requiredIdentifierRoles), + ) || + activeQuestionnaireResponseClaim?.resourceType !== 'QuestionnaireResponse' || + activeQuestionnaireResponseClaim.cardinality !== 1 || + activeQuestionnaireResponseClaim.otherProfilesAllowed !== false || + activeQuestionnaireResponseClaim.profiles?.length !== 1 || + !uniqueNonemptyStrings(activeQuestionnaireResponseClaim.profiles) || + JSON.stringify(referencePaths) !== JSON.stringify(expectedReferencePaths) || + JSON.stringify(extensionTargets) !== + JSON.stringify(expectedExtensionTargets) || + typeof exchangeProtocol.referencePolicy?.literalClosure !== 'string' || + typeof exchangeProtocol.referencePolicy?.declaredType !== 'string' || + typeof exchangeProtocol.referencePolicy?.governedShape !== 'string' || + exchangeCorpus.schemaVersion !== 1 || + !Array.isArray(exchangeRuleRows) || + exchangeRuleRows.length !== 31 || + Object.keys(exchangeRuleDiagnostics).length !== + new Set(exchangeRuleRows.map(({ code }) => code)).size || + exchangeRuleRows.some( + (rule) => + typeof rule?.code !== 'string' || + !rule.code.startsWith('mobile-') || + typeof rule.reason !== 'string' || + rule.reason.length === 0 || + typeof rule.location !== 'string' || + rule.location.length === 0 || + rule.severity !== 'error' || + exchangeRuleDiagnostics[rule.code]?.reason !== rule.reason || + exchangeRuleDiagnostics[rule.code]?.severity !== rule.severity, + ) || + typeof exchangeProtocol.recordingDevice?.instanceRule !== 'string' || + typeof exchangeProtocol.recordingDevice?.unknownInstance !== 'string' || + typeof exchangeProtocol.recordingDevice?.snapshots !== 'string' || + typeof exchangeProtocol.recordingDevice?.roles !== 'string' || + !['application', 'host', 'recording-device'].every((role) => + exchangeProtocol.recordingDevice.roles.includes(role), + ) || + retractionTargetEntries.length === 0 || + retractionTargetEntries.some( + ([role, rule]) => + role.length === 0 || + typeof rule !== 'object' || + rule === null || + !protocolIdentifierRoles.has(rule.identifierRole) || + !uniqueNonemptyStrings(rule.resourceTypes), + ) || + !Array.isArray(protocolIdentityKinds) || + protocolIdentityKinds.length === 0 || + identityKindsByName.size !== protocolIdentityKinds.length || + protocolIdentityKinds?.some( + ({ kind, identifierRole, components }) => + typeof kind !== 'string' || + kind.length === 0 || + typeof identifierRole !== 'string' || + identifierRole.length === 0 || + !uniqueNonemptyStrings(components), + ) || + !Array.isArray(identityVectors) || + identityVectors.length === 0 || + new Set(identityVectors.map(({ id }) => id)).size !== + identityVectors.length || + identityVectors.some((vector) => { + const definition = identityKindsByName.get(vector.identityKind) + return ( + typeof vector.id !== 'string' || + vector.id.length === 0 || + definition === undefined || + !Array.isArray(vector.components) || + vector.components.length !== definition.components.length || + vector.components.some( + (component) => typeof component !== 'string' || component.length === 0, + ) || + (providerIdentityKinds.has(vector.identityKind) && + !providerCodes.has(vector.components[0])) || + (genericSourceIdentityKinds.has(vector.identityKind) && + providerCodes.has(vector.components[0])) || + !opaqueIdentityValue.test(vector.value) + ) + }) || + !Array.isArray(invalidIdentityVectors) || + invalidIdentityVectors.length !== 4 || + new Set(invalidIdentityVectors.map(({ id }) => id)).size !== + invalidIdentityVectors.length || + invalidIdentityVectors.some((vector) => { + const definition = identityKindsByName.get(vector.identityKind) + const emptyComponentCount = + Array.isArray(vector.components) ? + vector.components.filter((component) => component === '').length + : 0 + const providerKindRequired = + vector.expectedError === 'provider-kind-required' && + genericSourceIdentityKinds.has(vector.identityKind) && + providerCodes.has(vector.components?.[0]) + return ( + typeof vector.id !== 'string' || + vector.id.length === 0 || + definition === undefined || + !Array.isArray(vector.components) || + vector.components.length !== definition.components.length || + vector.components.some((component) => typeof component !== 'string') || + !( + (vector.expectedError === 'empty-component' && + emptyComponentCount === 1) || + providerKindRequired + ) + ) + }) || + new Set( + invalidIdentityVectors + .filter(({ expectedError }) => expectedError === 'provider-kind-required') + .map(({ identityKind }) => identityKind), + ).size !== genericSourceIdentityKinds.size || + sourceOutputVector?.identityKind !== 'source-output' || + sourceOutputVector.components?.length !== 7 || + sourceOutputVector.value !== + 'v2:test-key:1:PQCWz9dZSrJm-KrbhbkckGeowkjhSSwWDRCVuF3VfXw' || + sourceContextVector?.identityKind !== 'source-context' ) { - throw new Error('Exchange identity catalog is incomplete or inconsistent.') -} - -for (const vector of exchangeIdentity.vectors ?? []) { - // A rejection vector states no name and no URN: it exists to record what is refused. - if (vector.input === null && vector.fullUrl === null) { - continue - } - if ( - typeof vector.system !== 'string' || - typeof vector.value !== 'string' || - typeof vector.input !== 'string' || - typeof vector.fullUrl !== 'string' - ) { - throw new Error('Exchange identity vectors must be complete.') - } + throw new Error('Exchange protocol catalog is incomplete or inconsistent.') } const implementedCapabilities = capabilities.measurements?.filter( @@ -937,18 +1636,27 @@ if ( } if ( !Array.isArray(implementedCapabilities) || - implementedCapabilities.length !== sharedAdmittedMeasurements.size + implementedCapabilities.length !== sharedAdmittedMeasurements.size || + [...providerAdmittedMeasurements].some((key) => { + const capability = capabilities.measurements.find( + (entry) => entry.key === key, + ) + return ( + capability === undefined || + !['implemented', 'platform-exclusive'].includes(capability.status) + ) + }) ) { throw new Error( - 'Capability matrix implemented rows must exactly match the Provider facade.', + 'Capability matrix shared and platform-exclusive rows must exactly match the Provider facade.', ) } -const rawMappingRows = Object.entries(providerRawMappings).flatMap( +const rawMappingRows = Object.entries(providerRawOutputRoles).flatMap( ([provider, mappings]) => - Object.entries(mappings).map(([sourceType, outputDiscriminator]) => ({ + Object.entries(mappings).map(([sourceType, outputRole]) => ({ provider, sourceType, - outputDiscriminator, + outputRole, })), ) if ( @@ -960,8 +1668,8 @@ if ( (capability) => capability.provider === mapping.provider && capability.sourceType === mapping.sourceType && - capability.outputDiscriminator === mapping.outputDiscriminator && - capability.profile === profiles['provider-recording-document'] && + capability.outputRole === mapping.outputRole && + capability.profile === profiles['providers-recording-document'] && capability.sourceNeutralProfile === profiles['grove-sensor-recording-document'], ), @@ -1006,7 +1714,7 @@ for (const normative of measurements) { capability.profile !== normative.profile) ) { throw new Error( - `Owner-exclusive capability ${normative.id} must stay outside the shared facade.`, + `Owner-exclusive capability ${normative.id} must retain its exact platform-exclusive contract.`, ) } continue @@ -1059,7 +1767,8 @@ const generated = { mobilePackageMetadata, questionnairePackageMetadata, providerPackageMetadata, - exchangeIdentity, + exchangeProtocol, + exchangeRuleDiagnostics, profileClaims, packageCanonicals, profiles, @@ -1067,13 +1776,20 @@ const generated = { effectiveCanonicalization, effectiveCanonicalizationVectors: semanticEffectiveCanonicalization.vectors, providerAdapter, - providerScalarMappings, - providerRawMappings, + providerScalarOutputDiscriminators, + providerScalarOutputRoles, + providerRawOutputRoles, + providerRawOutputDiscriminators, providerRecordEffectiveRules, questionnaireProfiles, + mobileProfiles, providerProfiles, providerPackageCanonicals, adapterMeasurementCatalog, + adapterSourceMarkerClaims, + healthKitClinicalRecordAdmission, + healthKitApplicationDeviceIdentity, + healthConnectDataOriginApplication, recordingFormatRegistry, } @@ -1083,91 +1799,34 @@ for (const value of Object.values(generated)) { } } -const frozenExport = (name, value) => { - const valueName = `${name}Value` - return `const ${valueName} = ${JSON.stringify(value, null, 2)} as const - -export const ${name}: typeof ${valueName} = deepFreeze(${valueName})` -} - -const header = (disableClearText) => `// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// -// Generated by scripts/generate-measurement-catalog.mjs. Do not edit directly. - -${disableClearText ? '/* eslint-disable sonarjs/no-clear-text-protocols */\n' : ''} - -import { deepFreeze } from '../core/index.js' - -` -const versions = `export const groveFhirContractVersion = ${JSON.stringify(packageGraph.version)} as const - -export const groveFhirVersion = ${JSON.stringify(packageGraph.fhirVersion)} as const -` - -const mobileUnformattedOutput = `${header(true)}${versions} - -${frozenExport('groveMobilePackageMetadata', mobilePackageMetadata)} - -${frozenExport('groveFhirExchangeIdentity', exchangeIdentity)} - -${frozenExport('sharedMobileMeasurementCatalog', sharedMobileSemanticCatalog)} - -${frozenExport('mobileEffectiveCanonicalization', effectiveCanonicalization)} - -${frozenExport('mobileEffectiveCanonicalizationVectors', semanticEffectiveCanonicalization.vectors)} - -export type SharedMobileMeasurementKind = keyof typeof sharedMobileMeasurementCatalog -` - -const questionnaireUnformattedOutput = `${header(false)}${versions} - -${frozenExport('groveQuestionnairePackageMetadata', questionnairePackageMetadata)} - -${frozenExport('groveQuestionnaireProfileCanonicals', questionnaireProfiles)} -` - -const providerUnformattedOutput = `${header(true)}${versions} - -${frozenExport('groveProviderPackageMetadata', providerPackageMetadata)} - -${frozenExport('groveProviderPackageCanonicals', providerPackageCanonicals)} - -${frozenExport('groveProviderProfileCanonicals', providerProfiles)} - -${frozenExport('providerAdapterCatalog', providerAdapter)} - -${frozenExport('adapterMeasurementCatalog', adapterMeasurementCatalog)} - -${frozenExport('providerScalarMappings', providerScalarMappings)} - -${frozenExport('providerRecordEffectiveRules', providerRecordEffectiveRules)} - -${frozenExport('providerRawMappings', providerRawMappings)} - -${frozenExport('groveRecordingFormatRegistry', recordingFormatRegistry)} - -export type AdapterMeasurementCatalog = typeof adapterMeasurementCatalog - -export type ProviderRecordingFormat = - keyof typeof groveRecordingFormatRegistry.formats - -export type ProviderScalarMappings = typeof providerScalarMappings - -export type ProviderRecordEffectiveRules = typeof providerRecordEffectiveRules - -export type ProviderRawMappings = typeof providerRawMappings -` - -const unformattedOutputs = { - mobile: mobileUnformattedOutput, - questionnaire: questionnaireUnformattedOutput, - provider: providerUnformattedOutput, -} +const unformattedOutputs = renderMeasurementCatalogSources({ + adapterMeasurementCatalog, + adapterSourceMarkerClaims, + effectiveCanonicalization, + effectiveCanonicalizationVectors: semanticEffectiveCanonicalization.vectors, + exchangeProtocol, + exchangeRuleDiagnostics, + healthConnectDataOriginApplication, + healthKitApplicationDeviceIdentity, + healthKitClinicalRecordAdmission, + mobilePackageMetadata, + mobileProfiles, + packageGraph, + profileClaims, + providerAdapter, + providerScalarOutputDiscriminators, + providerPackageCanonicals, + providerPackageMetadata, + providerProfiles, + providerRawOutputRoles, + providerRawOutputDiscriminators, + providerRecordEffectiveRules, + providerScalarOutputRoles, + questionnairePackageMetadata, + questionnaireProfiles, + recordingFormatRegistry, + sharedMobileSemanticCatalog, +}) for (const [scope, unformattedOutput] of Object.entries(unformattedOutputs)) { const output = await format(unformattedOutput, { parser: 'typescript', diff --git a/packages/fhir/scripts/generate-zod-schemas.mjs b/packages/fhir/scripts/generate-zod-schemas.mjs index 0cf9e577..597dbede 100644 --- a/packages/fhir/scripts/generate-zod-schemas.mjs +++ b/packages/fhir/scripts/generate-zod-schemas.mjs @@ -20,7 +20,15 @@ */ import { createHash } from 'node:crypto' -import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises' +import { + mkdir, + mkdtemp, + readFile, + readdir, + rename, + rm, + writeFile, +} from 'node:fs/promises' import { existsSync } from 'node:fs' import { dirname, join, resolve } from 'node:path' import { fileURLToPath, pathToFileURL } from 'node:url' @@ -28,6 +36,13 @@ import { spawnSync } from 'node:child_process' import { format, resolveConfig } from 'prettier' import { Buffer } from 'node:buffer' import process, { argv, stdout } from 'node:process' +import { + assertSafeGeneratorInput, + fhirTypeAlias, + pathSchemaIdentifier, + resourceDispatchIdentifiers, + structureSchemaIdentifier, +} from './zod-generator-input.mjs' const { fetch } = globalThis @@ -104,16 +119,43 @@ const resourceRoots = (structures) => /** Downloads and unpacks a FHIR core package once, then reuses it. */ export async function fhirPackage({ packageId, version, integrity }) { const target = join(CACHE_ROOT, `${packageId}#${version}`) - if (existsSync(join(target, 'package'))) { - return join(target, 'package') + const packageDirectory = join(target, 'package') + const archivePath = join(target, 'package.tgz') + const markerPath = join(target, '.integrity') + const markerValue = `${JSON.stringify({ packageId, version, integrity })}\n` + const digestMatches = (bytes) => + integrity === undefined || + createHash('sha512').update(bytes).digest('hex') === integrity + const cachedArchive = await readFile(archivePath).catch(() => undefined) + const cachedMarker = await readFile(markerPath, 'utf8').catch(() => undefined) + const cachedMetadata = await readFile( + join(packageDirectory, 'package.json'), + 'utf8', + ) + .then((value) => JSON.parse(value)) + .catch(() => undefined) + if ( + cachedArchive !== undefined && + digestMatches(cachedArchive) && + cachedMarker === markerValue && + cachedMetadata?.name === packageId && + cachedMetadata.version === version + ) { + return packageDirectory } - await mkdir(target, { recursive: true }) - const url = `${REGISTRY}/${packageId}/${version}` - const response = await fetch(url) - if (!response.ok) { - throw new Error(`Could not fetch ${url}: ${response.status}`) + + let bytes = + cachedArchive !== undefined && digestMatches(cachedArchive) ? + cachedArchive + : undefined + if (bytes === undefined) { + const url = `${REGISTRY}/${packageId}/${version}` + const response = await fetch(url) + if (!response.ok) { + throw new Error(`Could not fetch ${url}: ${response.status}`) + } + bytes = Buffer.from(await response.arrayBuffer()) } - const bytes = Buffer.from(await response.arrayBuffer()) // A registry tarball is mutable in principle; the digest pins what this generator was built // against, so a substituted archive fails the build rather than silently changing the output. const digest = createHash('sha512').update(bytes).digest('hex') @@ -122,19 +164,43 @@ export async function fhirPackage({ packageId, version, integrity }) { `${packageId}#${version} does not match its pinned digest.\n expected ${integrity}\n actual ${digest}`, ) } - const archive = join(target, 'package.tgz') - await writeFile(archive, bytes) - const result = spawnSync( - 'tar', - ['xzf', archive, '--no-same-owner', '-C', target], - { - stdio: 'inherit', - }, - ) - if (result.status !== 0) { - throw new Error(`Could not unpack ${archive}`) + + await mkdir(CACHE_ROOT, { recursive: true }) + const staging = await mkdtemp(join(CACHE_ROOT, '.package-fetch-')) + try { + const stagedArchive = join(staging, 'package.tgz') + await writeFile(stagedArchive, bytes) + const result = spawnSync( + 'tar', + [ + 'xzf', + stagedArchive, + '--no-same-owner', + '--no-same-permissions', + '-C', + staging, + ], + { stdio: 'inherit' }, + ) + if (result.status !== 0) { + throw new Error(`Could not unpack ${packageId}#${version}.`) + } + const metadata = JSON.parse( + await readFile(join(staging, 'package/package.json'), 'utf8'), + ) + if (metadata.name !== packageId || metadata.version !== version) { + throw new Error( + `FHIR package metadata does not match ${packageId}#${version}.`, + ) + } + await writeFile(join(staging, '.integrity'), markerValue) + await rm(target, { recursive: true, force: true }) + await rename(staging, target) + } catch (error) { + await rm(staging, { recursive: true, force: true }) + throw error } - return join(target, 'package') + return packageDirectory } /** Every StructureDefinition and ValueSet the release publishes, keyed by name/url. */ @@ -148,8 +214,13 @@ async function loadDefinitions(packageDir) { let resource try { resource = JSON.parse(await readFile(join(packageDir, entry), 'utf8')) - } catch { - continue + } catch (error) { + throw new Error( + `Could not parse pinned FHIR package resource ${entry}.`, + { + cause: error, + }, + ) } if (resource.resourceType === 'StructureDefinition') { if (resource.derivation === 'constraint') continue @@ -291,21 +362,12 @@ function childrenOf(elements, path) { ) } -const SCHEMA_NAMES = new Map() -const schemaName = (name) => { - if (!SCHEMA_NAMES.has(name)) { - SCHEMA_NAMES.set( - name, - `${name.charAt(0).toLowerCase()}${name.slice(1)}Schema`, - ) - } - return SCHEMA_NAMES.get(name) -} +const schemaName = structureSchemaIdentifier /** The bounds a repeating element states, so a 1..* element cannot be satisfied by `[]`. */ function arrayBounds(element) { - let rendered = '.array()' - if (element.min > 0) rendered += `.min(${element.min})` + // FHIR JSON arrays are omitted when absent and SHALL never be serialized empty. + let rendered = `.array().min(${Math.max(1, element.min)})` const max = Number(element.max) if (Number.isFinite(max) && max > 1) rendered += `.max(${max})` return rendered @@ -424,8 +486,12 @@ function renderElement(element, context, emit) { } else { expression = single(types[0].code, types[0]) } + if (isArray && sawPrimitive) expression = `${expression}.nullable()` if (isArray) expression = `${expression}${arrayBounds(element)}` - if (optional) expression = `${expression}.optional()` + // A primitive may exist through its `_shadow` Element alone (for example a + // data-absent-reason extension). Object-level refinements enforce min cardinality across the + // value and shadow together, so the value property itself must remain optional. + if (optional || sawPrimitive) expression = `${expression}.optional()` return { expression, primitiveSibling: sawPrimitive, @@ -455,7 +521,7 @@ const IMPLEMENTED_CONSTRAINTS = { ` (value) => {\n` + ` const observation = value as Record\n` + ` const hasValue = Object.keys(observation).some(\n` + - ` (key) => key.startsWith('value') && observation[key] !== undefined,\n` + + ` (key) => (key.startsWith('value') || key.startsWith('_value')) && observation[key] !== undefined,\n` + ` )\n` + ` return observation.dataAbsentReason === undefined || !hasValue\n` + ` },\n` + @@ -464,8 +530,8 @@ const IMPLEMENTED_CONSTRAINTS = { 'obs-3': (schema) => `${schema}.refine(\n` + ` (value) => {\n` + - ` const range = value as { low?: unknown; high?: unknown; text?: unknown }\n` + - ` return range.low !== undefined || range.high !== undefined || range.text !== undefined\n` + + ` const range = value as { low?: unknown; high?: unknown; text?: unknown; _text?: unknown }\n` + + ` return range.low !== undefined || range.high !== undefined || range.text !== undefined || range._text !== undefined\n` + ` },\n` + ` { message: 'obs-3: a reference range states a low, a high, or text.' },\n` + ` )`, @@ -474,32 +540,57 @@ const IMPLEMENTED_CONSTRAINTS = { ` (value) => {\n` + ` const period = value as { start?: string; end?: string }\n` + ` if (period.start === undefined || period.end === undefined) return true\n` + - ` const start = fhirDateTimeToEpoch(period.start)\n` + - ` const end = fhirDateTimeToEpoch(period.end)\n` + + ` const ordering = compareFhirDateTimes(period.start, period.end)\n` + ` // A malformed endpoint is a malformed date, not an ordering fault; the release says\n` + ` // as much with start.hasValue(). Reporting both would name the wrong problem.\n` + - ` if (Number.isNaN(start) || Number.isNaN(end)) return true\n` + - ` return start <= end\n` + + ` if (ordering === undefined) return true\n` + + ` return ordering <= 0\n` + ` },\n` + ` { message: 'Period.start must not be later than Period.end.' },\n` + ` )`, + 'ref-1': (schema) => + `${schema}.refine(\n` + + ` (value) => {\n` + + ` const reference = value as Record\n` + + ` return ['reference', '_reference', 'identifier', 'display', '_display'].some(\n` + + ` (name) => reference[name] !== undefined,\n` + + ` )\n` + + ` },\n` + + ` { message: 'ref-1: a Reference states a literal reference, identifier, or display.' },\n` + + ` )`, + 'qty-3': (schema) => + `${schema}.refine(\n` + + ` (value) => {\n` + + ` const quantity = value as Record\n` + + ` const hasCode = quantity.code !== undefined || quantity._code !== undefined\n` + + ` return !hasCode || quantity.system !== undefined || quantity._system !== undefined\n` + + ` },\n` + + ` { message: 'qty-3: a coded Quantity requires its unit system.' },\n` + + ` )`, } -/** At most one property of a choice may be present, which is what `[x]` means. */ +/** At most one choice alternative may be present; a primitive value/shadow pair is one choice. */ function choiceRefinement(groups) { if (groups.length === 0) return '' const rendered = groups .map( (group) => - `{ names: [${group.names.map((name) => JSON.stringify(name)).join(', ')}], required: ${group.required} }`, + `{ alternatives: [${group.alternatives + .map( + (alternative) => + `[${alternative.map((name) => JSON.stringify(name)).join(', ')}]`, + ) + .join(', ')}], required: ${group.required} }`, ) .join(', ') return ( `.refine(\n` + ` (value) =>\n` + ` [${rendered}].every((group) => {\n` + - ` const present = group.names.filter(\n` + - ` (name) => (value as Record)[name] !== undefined,\n` + + ` const present = group.alternatives.filter((alternative) =>\n` + + ` alternative.some(\n` + + ` (name) => (value as Record)[name] !== undefined,\n` + + ` ),\n` + ` ).length\n` + ` return group.required ? present === 1 : present <= 1\n` + ` }),\n` + @@ -511,6 +602,78 @@ function choiceRefinement(groups) { ) } +/** Positional rules for repeating primitive value and `_shadow` arrays in FHIR JSON. */ +function primitiveArrayRefinement(groups) { + if (groups.length === 0) return '' + const rendered = groups + .map( + ({ name, required }) => + `{ name: ${JSON.stringify(name)}, shadow: ${JSON.stringify(`_${name}`)}, required: ${required} }`, + ) + .join(', ') + return ( + `.superRefine((value, context) => {\n` + + ` for (const group of [${rendered}]) {\n` + + ` const record = value as Record\n` + + ` const values = record[group.name] as unknown[] | undefined\n` + + ` const shadows = record[group.shadow] as unknown[] | undefined\n` + + ` if (group.required && values === undefined && shadows === undefined) {\n` + + ` context.addIssue({ code: 'custom', path: [group.name], message: 'A required repeating primitive needs values or primitive metadata.' })\n` + + ` continue\n` + + ` }\n` + + ` if (values !== undefined && shadows !== undefined && values.length !== shadows.length) {\n` + + ` context.addIssue({ code: 'custom', path: [group.shadow], message: 'FHIR primitive value and metadata arrays must have equal length.' })\n` + + ` continue\n` + + ` }\n` + + ` const length = values?.length ?? shadows?.length ?? 0\n` + + ` for (let index = 0; index < length; index += 1) {\n` + + ` if ((values?.[index] ?? null) === null && (shadows?.[index] ?? null) === null) {\n` + + ` context.addIssue({ code: 'custom', path: [group.name, index], message: 'A repeating primitive slot needs a value or primitive metadata.' })\n` + + ` }\n` + + ` }\n` + + ` }\n` + + ` })` + ) +} + +/** Required singular primitives may be represented by their `_shadow` Element alone. */ +function primitiveScalarRefinement(groups) { + const required = groups.filter((group) => group.required) + if (required.length === 0) return '' + const rendered = required + .map( + ({ name }) => + `{ name: ${JSON.stringify(name)}, shadow: ${JSON.stringify(`_${name}`)} }`, + ) + .join(', ') + return ( + `.superRefine((value, context) => {\n` + + ` const record = value as Record\n` + + ` for (const group of [${rendered}]) {\n` + + ` if (record[group.name] === undefined && record[group.shadow] === undefined) {\n` + + ` context.addIssue({ code: 'custom', path: [group.name], message: 'A required primitive needs a value or primitive metadata.' })\n` + + ` }\n` + + ` }\n` + + ` })` + ) +} + +const elementContentRefinement = () => + `.refine(\n` + + ` (value) => Object.keys(value as object).some((key) => key !== 'id'),\n` + + ` { message: 'ele-1: an element must have a value or children beyond id.' },\n` + + ` )` + +const objectRefinements = ({ choices, primitiveArrays, primitiveScalars }) => + `${choiceRefinement(choices)}${primitiveScalarRefinement(primitiveScalars)}${primitiveArrayRefinement(primitiveArrays)}${elementContentRefinement()}` + +const newObjectRefinements = () => ({ + choices: [], + emittedPaths: [], + primitiveArrays: [], + primitiveScalars: [], +}) + /** * The constraints that apply to an object, as the release states applicability. * @@ -589,14 +752,11 @@ function applicableConstraints(elements, path) { * involves no type system, and it is what would have caught `id` being dropped from every * backbone while every test stayed green. */ -function assertEveryElementEmitted(root, elements, rendered) { - const missing = [] - for (const element of childrenOf(elements, root)) { - const name = element.path.slice(root.length + 1).replace('[x]', '') - if (!new RegExp(`(^|\\s)_?${name}[A-Za-z]*:`, 'm').test(rendered)) { - missing.push(element.path) - } - } +function assertEveryElementEmitted(root, elements, emittedPaths) { + const emitted = new Set(emittedPaths) + const missing = childrenOf(elements, root) + .map(({ path }) => path) + .filter((path) => !emitted.has(path)) if (missing.length > 0) { throw new Error( `${root}: the release names elements the generated schema does not emit: ` + @@ -629,7 +789,10 @@ function renderStructure(structure, context, pending) { constrained = `${base}.regex(new RegExp(${JSON.stringify(`^[0-9a-zA-Z+/=${XSD_SPACE}]*$`)}), 'Expected a FHIR base64Binary.')` + `.refine(\n` + - ` (value) => value.replace(/[${XSD_SPACE}]/gu, '').length % 4 === 0,\n` + + ` (value) => {\n` + + ` const length = value.replace(/[${XSD_SPACE}]/gu, '').length\n` + + ` return length > 0 && length % 4 === 0\n` + + ` },\n` + ` { message: 'Expected a FHIR base64Binary.' },\n` + ` )` } else if (regex && base === 'z.string()') { @@ -640,6 +803,12 @@ function renderStructure(structure, context, pending) { if (bounds.max !== undefined) constrained = `${constrained}.max(${bounds.max})` } + if (['date', 'dateTime', 'instant'].includes(root)) { + constrained = `${constrained}.refine(hasValidFhirCalendarDate, { message: ${JSON.stringify(`Expected a real Gregorian calendar date for FHIR ${root}.`)} })` + } + if (['canonical', 'uri', 'url', 'xhtml'].includes(root)) { + constrained = `${constrained}.min(1)` + } if (regex && base.startsWith('z.number()')) { // positiveInt and unsignedInt state their bound as a lexical pattern; testing the string // form applies the specification's own constraint rather than a hand-chosen one. @@ -658,20 +827,12 @@ function renderStructure(structure, context, pending) { // A backbone another element points at by contentReference is hoisted to its own schema: // inlining a self-referential one would not terminate. const hoisted = new Map() - const pathSchemaName = (path) => { - const parts = path.split('.') - return `${parts[0].charAt(0).toLowerCase()}${parts[0].slice(1)}${parts - .slice(1) - .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) - .join('')}Schema` - } - const lines = [] const emit = { require: (name) => pending.add(name), reference: (path) => { - hoisted.set(path, pathSchemaName(path)) - return pathSchemaName(path) + hoisted.set(path, pathSchemaIdentifier(path)) + return pathSchemaIdentifier(path) }, primitive: (name) => { pending.add(name) @@ -683,34 +844,39 @@ function renderStructure(structure, context, pending) { .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join('') if (context.declaredTypes.has(declared)) { - hoisted.set(element.path, pathSchemaName(element.path)) - return pathSchemaName(element.path) + hoisted.set(element.path, pathSchemaIdentifier(element.path)) + return pathSchemaIdentifier(element.path) } // Anything the release does not name stays inline: giving it a name would invent a type // the specification does not publish. The same emitter is used so a deeper backbone is // validated rather than falling back to an open record. - const nestedChoices = [] + const nestedRefinements = newObjectRefinements() const nested = renderProperties( element.path, elements, context, pending, emit, - nestedChoices, + nestedRefinements, ) - return `z.strictObject({\n${nested}\n })${choiceRefinement(nestedChoices)}` + assertEveryElementEmitted( + element.path, + elements, + nestedRefinements.emittedPaths, + ) + return `z.strictObject({\n${nested}\n })${objectRefinements(nestedRefinements)}` }, } recordSkippedConstraints(elements) - const rootChoices = [] + const rootRefinements = newObjectRefinements() const body = renderProperties( root, elements, context, pending, emit, - rootChoices, + rootRefinements, ) const rootConstraints = applicableConstraints(elements, root) // Rendering a backbone can discover further ones, so drain until the set stops growing. @@ -719,14 +885,14 @@ function renderStructure(structure, context, pending) { for (const [path, name] of [...hoisted]) { if (emitted.has(path)) continue emitted.add(path) - const nestedChoices = [] + const nestedRefinements = newObjectRefinements() const nested = renderProperties( path, elements, context, pending, emit, - nestedChoices, + nestedRefinements, ) const declared = path .split('.') @@ -734,13 +900,13 @@ function renderStructure(structure, context, pending) { .join('') const nestedAnnotation = context.declaredTypes.has(declared) ? - `z.ZodType` + `z.ZodType>` : 'z.ZodTypeAny' if (context.declaredTypes.has(declared)) context.usedTypes.add(declared) // A hoisted backbone carries its own choices and its own constraints; applying them only // at the resource root would leave the named type weaker than the inline form. const nestedConstraints = applicableConstraints(elements, path) - let nestedClosing = ` })${choiceRefinement(nestedChoices)}` + let nestedClosing = ` })${objectRefinements(nestedRefinements)}` for (const key of nestedConstraints) { nestedClosing = IMPLEMENTED_CONSTRAINTS[key](nestedClosing) } @@ -748,13 +914,15 @@ function renderStructure(structure, context, pending) { lines.push(` z.strictObject({`) lines.push(nested) lines.push(`${nestedClosing},`) - lines.push(`)`) + lines.push(`) as unknown as ${nestedAnnotation}`) lines.push('') - assertEveryElementEmitted(path, elements, nested) + assertEveryElementEmitted(path, elements, nestedRefinements.emittedPaths) } } const annotation = - context.declaredTypes.has(root) ? `z.ZodType` : 'z.ZodTypeAny' + context.declaredTypes.has(root) ? + `z.ZodType>` + : 'z.ZodTypeAny' if (context.declaredTypes.has(root)) context.usedTypes.add(root) lines.push(`export const ${schemaName(root)}: ${annotation} = z.lazy(() =>`) lines.push(` z.strictObject({`) @@ -762,14 +930,14 @@ function renderStructure(structure, context, pending) { lines.push(` resourceType: z.literal(${JSON.stringify(root)}),`) } lines.push(body) - let closing = ` })${choiceRefinement(rootChoices)}` + let closing = ` })${objectRefinements(rootRefinements)}` for (const key of rootConstraints) { closing = IMPLEMENTED_CONSTRAINTS[key](closing) } lines.push(`${closing},`) - lines.push(`)`) + lines.push(`) as unknown as ${annotation}`) const rendered = lines.join('\n') + '\n' - assertEveryElementEmitted(root, elements, body) + assertEveryElementEmitted(root, elements, rootRefinements.emittedPaths) return rendered } @@ -785,31 +953,38 @@ function renderProperties(path, elements, context, pending, emit, collector) { } const rows = [] const choiceGroups = [] + const primitiveArrayGroups = [] + const primitiveScalarGroups = [] for (const child of childrenOf(elements, path)) { const name = child.path.slice(path.length + 1) const rendered = renderElement(child, context, localEmit) if (!rendered) continue + collector?.emittedPaths.push(child.path) if (rendered.choice) { choiceGroups.push({ - names: rendered.choice.map((option) => option.name), + alternatives: rendered.choice.map((option) => [ + option.name, + ...(option.primitiveSibling ? [`_${option.name}`] : []), + ]), required: rendered.optional === false, }) for (const option of rendered.choice) { - rows.push(` ${option.name}: ${option.expression}.optional(),`) + rows.push( + ` ${JSON.stringify(option.name)}: ${option.expression}.optional(),`, + ) if ( option.primitiveSibling && !(child.representation ?? []).includes('xmlAttr') ) { pending.add('Element') rows.push( - ` _${option.name}: z.lazy(() => ${schemaName('Element')}).optional(),`, + ` ${JSON.stringify(`_${option.name}`)}: z.lazy(() => ${schemaName('Element')}).optional(),`, ) } } continue } - const key = /^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name) - rows.push(` ${key}: ${rendered.expression},`) + rows.push(` ${JSON.stringify(name)}: ${rendered.expression},`) if ( rendered.primitiveSibling && !(child.representation ?? []).includes('xmlAttr') @@ -817,12 +992,21 @@ function renderProperties(path, elements, context, pending, emit, collector) { pending.add('Element') const sibling = rendered.siblingIsArray ? - `z.lazy(() => ${schemaName('Element')}).array()` + `z.lazy(() => ${schemaName('Element')}).nullable()${arrayBounds(child)}` : `z.lazy(() => ${schemaName('Element')})` - rows.push(` _${name}: ${sibling}.optional(),`) + rows.push(` ${JSON.stringify(`_${name}`)}: ${sibling}.optional(),`) + if (rendered.siblingIsArray) { + primitiveArrayGroups.push({ name, required: child.min > 0 }) + } else { + primitiveScalarGroups.push({ name, required: child.min > 0 }) + } } } - if (collector) collector.push(...choiceGroups) + if (collector) { + collector.choices.push(...choiceGroups) + collector.primitiveArrays.push(...primitiveArrayGroups) + collector.primitiveScalars.push(...primitiveScalarGroups) + } return rows.join('\n') } @@ -843,26 +1027,26 @@ function specialises(name, ancestor, structures) { * * `Bundle.entry.resource`, `Bundle.entry.response.outcome` and every `contained` array admit any * resource, which is not the same as admitting any object. The slot discriminates on the - * `resourceType` every resource carries and validates against the schema for that type; only a - * type this package does not model keeps the open shape, because there is nothing better to - * check it against. + * `resourceType` every resource carries and validates against the schema for that type. An + * unknown resource type is not part of the pinned FHIR release and therefore fails closed. * * Dispatching rather than unioning is what keeps the recursion finite: a resource may carry a * Bundle that carries it again, and the members are read at parse time from a table rather than * expanded into the type of every slot that admits a resource. */ function renderResourceDispatch(typeCode, members, context) { - const table = `modelled${typeCode}s` + const { table, inFlight } = resourceDispatchIdentifiers(typeCode) const output = context.declaredTypes.has(typeCode) ? - `Fhir${typeCode}` + `FhirJson<${fhirTypeAlias(typeCode)}>` : '{ resourceType: string }' if (context.declaredTypes.has(typeCode)) context.usedTypes.add(typeCode) const annotation = `z.ZodType<${output}>` - const inFlight = `dispatching${typeCode}` return ( `const ${table}: Readonly> = {\n` + - members.map((name) => ` ${name}: ${schemaName(name)},`).join('\n') + + members + .map((name) => ` ${JSON.stringify(name)}: ${schemaName(name)},`) + .join('\n') + `\n}\n\n` + `const ${inFlight} = new WeakSet()\n\n` + `export const ${schemaName(typeCode)}: ${annotation} = z\n` + @@ -878,9 +1062,10 @@ function renderResourceDispatch(typeCode, members, context) { ` )\n` + ` .superRefine((value, ctx) => {\n` + ` const modelled = ${table}[value.resourceType]\n` + - ` // A resource type the release defines but this package does not model keeps the open\n` + - ` // shape; reporting it as invalid would reject a conformant bundle.\n` + - ` if (modelled === undefined) return\n` + + ` if (modelled === undefined) {\n` + + ` ctx.addIssue({ code: 'custom', path: ['resourceType'], message: 'Resource type is not published by this pinned FHIR release.' })\n` + + ` return\n` + + ` }\n` + ` // JSON cannot describe a resource that contains itself, but an in-memory object can,\n` + ` // and re-entering one would recur until the stack ran out.\n` + ` if (${inFlight}.has(value)) return\n` + @@ -908,7 +1093,7 @@ const HEADER = `// // GENERATED FILE. Run \`npm run generate:zod\` after changing the generator or the release pin. // -/* eslint-disable sonarjs/regex-complexity, sonarjs/concise-regex, sonarjs/single-char-in-character-classes, sonarjs/single-character-alternation */ +/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion, sonarjs/no-nested-functions, sonarjs/regex-complexity, sonarjs/concise-regex, sonarjs/single-char-in-character-classes, sonarjs/single-character-alternation */ ` @@ -945,6 +1130,12 @@ async function main(argv) { (match) => match[1], ), ) + assertSafeGeneratorInput({ + structures, + valueSets, + codeSystems, + declaredTypes, + }) const context = { structures, valueSets, @@ -964,7 +1155,11 @@ async function main(argv) { if (done.has(name)) continue done.add(name) const structure = structures.get(name) - if (!structure) continue + if (!structure) { + throw new Error( + `The pinned FHIR package does not define required structure ${name}.`, + ) + } if (structure.abstract === true && structure.kind === 'resource') continue chunks.set(name, renderStructure(structure, context, pending)) } @@ -989,7 +1184,21 @@ async function main(argv) { .map(([path, keys]) => [path, [...keys].sort()]) .sort(([a], [b]) => a.localeCompare(b)) const skippedExport = - `\n/**\n * Invariants the release states for these paths that this module does not check.\n *\n` + + `\n/** Exact pinned release material and generated closure behind this module. */\n` + + `export const STRUCTURAL_SCHEMA_SOURCE = {\n` + + ` packageId: ${JSON.stringify(release.packageId)},\n` + + ` packageVersion: ${JSON.stringify(release.version)},\n` + + ` fhirVersion: ${JSON.stringify(release.fhirVersion)},\n` + + ` archiveSha512: ${JSON.stringify(release.integrity)},\n` + + ` structureCount: ${String(ordered.length)},\n` + + `} as const\n\n` + + `/** Structural wire-boundary capabilities and deliberate limits. */\n` + + `export const STRUCTURAL_SCHEMA_CAPABILITIES = {\n` + + ` fullFhirPath: false,\n` + + ` preservesDecimalLexemesAfterJsonParse: false,\n` + + ` normativeConformanceValidator: 'official-fhir-validator',\n` + + `} as const\n\n` + + `/**\n * Invariants the release states for these paths that this module does not check.\n *\n` + ` * Structural rules are enforced; a rule expressed only as FHIRPath is not evaluated here.\n` + ` * Normative conformance is established by the official FHIR Validator, not by this module.\n */\n` + `export const UNCHECKED_CONSTRAINTS: Readonly> = {\n` + @@ -1004,10 +1213,11 @@ async function main(argv) { const imports = [...context.usedTypes].sort() const typeImport = imports.length > 0 ? - `import type {\n${imports.map((name) => ` ${name} as Fhir${name},`).join('\n')}\n} from 'fhir/${releaseKey}.js'\n` + `import type {\n${imports.map((name) => ` ${name} as ${fhirTypeAlias(name)},`).join('\n')}\n} from 'fhir/${releaseKey}.js'\n` : '' const rendered = `${HEADER}${typeImport}import { z } from 'zod' -import { fhirDateTimeToEpoch } from '../support.js' +import type { FhirJson } from '../../r4/json.js' +import { compareFhirDateTimes, hasValidFhirCalendarDate } from '../support.js' // Generated from ${release.packageId}#${release.version} (FHIR ${release.fhirVersion}). // ${ordered.length} structures: every type the exposed roots reach. diff --git a/packages/fhir/scripts/measurement-catalog-inputs.mjs b/packages/fhir/scripts/measurement-catalog-inputs.mjs new file mode 100644 index 00000000..23b68a36 --- /dev/null +++ b/packages/fhir/scripts/measurement-catalog-inputs.mjs @@ -0,0 +1,105 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { readFile } from 'node:fs/promises' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' + +const readJson = async (path) => JSON.parse(await readFile(path, 'utf8')) + +/** Resolve the local/pinned IG layout and load every generator input once. */ +export const loadMeasurementCatalogInputs = async (scriptUrl, arguments_) => { + const packageRoot = resolve(dirname(fileURLToPath(scriptUrl)), '..') + const localIgIndex = arguments_.indexOf('--ig') + const upstreamRoot = + localIgIndex === -1 ? + resolve(packageRoot, '.grove-fhir') + : resolve(arguments_[localIgIndex + 1] ?? '') + const catalogRoot = resolve(upstreamRoot, 'catalog') + const paths = { + capability: resolve(packageRoot, 'catalog/measurement-capabilities.json'), + catalog: resolve(catalogRoot, 'measurement-catalog.json'), + exchangeCorpus: resolve( + upstreamRoot, + 'Conformance/corpora/mobile-exchange/corpus.json', + ), + exchangeProtocol: resolve(catalogRoot, 'exchange-protocol.json'), + formatRegistry: resolve(catalogRoot, 'format-registry.json'), + healthConnectAdapter: resolve(catalogRoot, 'health-connect-adapter.json'), + healthKitAdapter: resolve(catalogRoot, 'healthkit-adapter.json'), + packageGraph: resolve(catalogRoot, 'package-graph.json'), + profileClaims: resolve(catalogRoot, 'profile-claims.json'), + providerAdapter: resolve(catalogRoot, 'providers-adapter.json'), + semanticCorpus: resolve( + upstreamRoot, + 'Conformance/corpora/mobile-semantics/corpus.json', + ), + sensorCatalog: resolve(catalogRoot, 'sensor-catalog.json'), + sensorKitAdapter: resolve(catalogRoot, 'sensorkit-adapter.json'), + sourceRef: resolve(packageRoot, 'grove-fhir.json'), + } + const [ + capabilities, + catalog, + exchangeCorpus, + exchangeProtocol, + formatRegistry, + healthConnectAdapter, + healthKitAdapter, + packageGraph, + profileClaims, + providerAdapter, + semanticCorpus, + sensorCatalog, + sensorKitAdapter, + sourceRef, + ] = await Promise.all([ + readJson(paths.capability), + readJson(paths.catalog), + readJson(paths.exchangeCorpus), + readJson(paths.exchangeProtocol), + readJson(paths.formatRegistry), + readJson(paths.healthConnectAdapter), + readJson(paths.healthKitAdapter), + readJson(paths.packageGraph), + readJson(paths.profileClaims), + readJson(paths.providerAdapter), + readJson(paths.semanticCorpus), + readJson(paths.sensorCatalog), + readJson(paths.sensorKitAdapter), + readJson(paths.sourceRef), + ]) + + return { + capabilities, + catalog, + exchangeCorpus, + exchangeProtocol, + formatRegistry, + healthConnectAdapter, + healthKitAdapter, + outputPaths: { + mobile: resolve( + packageRoot, + 'src/mobile/measurement-catalog.generated.ts', + ), + provider: resolve(packageRoot, 'src/providers/contract.generated.ts'), + questionnaire: resolve( + packageRoot, + 'src/questionnaire/contract.generated.ts', + ), + }, + packageGraph, + profileClaims, + providerAdapter, + semanticCorpus, + sensorCatalog, + sensorKitAdapter, + sourceRef, + } +} diff --git a/packages/fhir/scripts/measurement-catalog-renderer.mjs b/packages/fhir/scripts/measurement-catalog-renderer.mjs new file mode 100644 index 00000000..e8bae567 --- /dev/null +++ b/packages/fhir/scripts/measurement-catalog-renderer.mjs @@ -0,0 +1,164 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/** Render the three stable TypeScript catalog modules from validated IG values. */ +export const renderMeasurementCatalogSources = ({ + adapterMeasurementCatalog, + adapterSourceMarkerClaims, + effectiveCanonicalization, + effectiveCanonicalizationVectors, + exchangeProtocol, + exchangeRuleDiagnostics, + healthConnectDataOriginApplication, + healthKitApplicationDeviceIdentity, + healthKitClinicalRecordAdmission, + mobilePackageMetadata, + mobileProfiles, + packageGraph, + profileClaims, + providerAdapter, + providerPackageCanonicals, + providerPackageMetadata, + providerProfiles, + providerRawOutputDiscriminators, + providerRawOutputRoles, + providerRecordEffectiveRules, + providerScalarOutputDiscriminators, + providerScalarOutputRoles, + questionnairePackageMetadata, + questionnaireProfiles, + recordingFormatRegistry, + sharedMobileSemanticCatalog, +}) => { + const frozenExport = (name, value) => { + const valueName = `${name}Value` + return `const ${valueName} = ${JSON.stringify(value, null, 2)} as const + + export const ${name}: typeof ${valueName} = deepFreeze(${valueName})` + } + + const header = (disableClearText) => `// + // This source file is part of the Grove open-source project + // + // SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + // + // SPDX-License-Identifier: MIT + // + // Generated by scripts/generate-measurement-catalog.mjs. Do not edit directly. + + ${disableClearText ? '/* eslint-disable sonarjs/no-clear-text-protocols */\n' : ''} + + import { deepFreeze } from '../core/index.js' + + ` + const versions = `export const groveFhirContractVersion = ${JSON.stringify(packageGraph.version)} as const + + export const groveFhirVersion = ${JSON.stringify(packageGraph.fhirVersion)} as const + ` + + const mobileUnformattedOutput = `${header(true)}${versions} + + ${frozenExport('groveMobilePackageMetadata', mobilePackageMetadata)} + + ${frozenExport('groveMobileProfileCanonicals', mobileProfiles)} + + ${frozenExport('groveExchangeProtocol', exchangeProtocol)} + + ${frozenExport('groveExchangeRuleDiagnostics', exchangeRuleDiagnostics)} + + ${frozenExport('groveProfileClaims', profileClaims)} + + ${frozenExport('adapterSourceMarkerClaims', adapterSourceMarkerClaims)} + + ${frozenExport('healthConnectDataOriginApplication', healthConnectDataOriginApplication)} + + ${frozenExport('groveRecordingFormatRegistry', recordingFormatRegistry)} + + ${frozenExport('sharedMobileMeasurementCatalog', sharedMobileSemanticCatalog)} + + ${frozenExport('mobileEffectiveCanonicalization', effectiveCanonicalization)} + + ${frozenExport('mobileEffectiveCanonicalizationVectors', effectiveCanonicalizationVectors)} + + export type SharedMobileMeasurementKind = keyof typeof sharedMobileMeasurementCatalog + + export type GroveRecordingFormat = + keyof typeof groveRecordingFormatRegistry.formats + + export type ProviderRecordingFormat = Extract< + GroveRecordingFormat, + 'provider-recording' + > + ` + + const questionnaireUnformattedOutput = `${header(false)}${versions} + + ${frozenExport('groveQuestionnairePackageMetadata', questionnairePackageMetadata)} + + ${frozenExport('groveQuestionnaireProfileCanonicals', questionnaireProfiles)} + ` + + const providerUnformattedOutput = `${header(true)}${versions} + + ${frozenExport('groveProviderPackageMetadata', providerPackageMetadata)} + + ${frozenExport('groveProviderPackageCanonicals', providerPackageCanonicals)} + + ${frozenExport('groveProviderProfileCanonicals', providerProfiles)} + + ${frozenExport('providerAdapterCatalog', providerAdapter)} + + ${frozenExport('adapterMeasurementCatalog', adapterMeasurementCatalog)} + + ${frozenExport('healthKitApplicationDeviceIdentity', healthKitApplicationDeviceIdentity)} + + ${frozenExport('healthKitClinicalRecordAdmission', healthKitClinicalRecordAdmission)} + + ${frozenExport('providerScalarOutputRoles', providerScalarOutputRoles)} + + ${frozenExport('providerScalarOutputDiscriminators', providerScalarOutputDiscriminators)} + + ${frozenExport('providerRecordEffectiveRules', providerRecordEffectiveRules)} + + ${frozenExport('providerRawOutputRoles', providerRawOutputRoles)} + + ${frozenExport('providerRawOutputDiscriminators', providerRawOutputDiscriminators)} + + export type AdapterMeasurementCatalog = typeof adapterMeasurementCatalog + + export type HealthKitApplicationDeviceIdentity = + typeof healthKitApplicationDeviceIdentity + + export type HealthKitClinicalRecordAdmission = + typeof healthKitClinicalRecordAdmission + + export { + groveRecordingFormatRegistry, + type GroveRecordingFormat, + type ProviderRecordingFormat, + } from '../mobile/measurement-catalog.generated.js' + + export type ProviderScalarOutputRoles = typeof providerScalarOutputRoles + + export type ProviderScalarOutputDiscriminators = + typeof providerScalarOutputDiscriminators + + export type ProviderRecordEffectiveRules = typeof providerRecordEffectiveRules + + export type ProviderRawOutputRoles = typeof providerRawOutputRoles + + export type ProviderRawOutputDiscriminators = + typeof providerRawOutputDiscriminators + ` + + return { + mobile: mobileUnformattedOutput, + questionnaire: questionnaireUnformattedOutput, + provider: providerUnformattedOutput, + } +} diff --git a/packages/fhir/scripts/package-smoke.mjs b/packages/fhir/scripts/package-smoke.mjs index ed1526aa..a67c9fc3 100644 --- a/packages/fhir/scripts/package-smoke.mjs +++ b/packages/fhir/scripts/package-smoke.mjs @@ -23,10 +23,15 @@ const [root, mobile, provider, provenance, questionnaire, r4] = const providerExports = [ 'buildProviderMeasurementBundle', 'buildProviderRecordingBundle', - 'providerRawMappings', - 'providerScalarMappings', + 'buildProviderRetractionBundle', + 'healthKitApplicationDeviceIdentity', + 'providerRawOutputDiscriminators', + 'providerRawOutputRoles', + 'providerScalarOutputDiscriminators', + 'providerScalarOutputRoles', ] const internalGraphExports = [ + 'deriveConformanceVectorOpaqueIdentifier', 'groveFhirPackageCanonicals', 'groveFhirPackageGraph', 'groveFhirProfileCanonicals', @@ -75,7 +80,9 @@ for (const name of internalGraphExports) { assert.equal(typeof mobile.canonicalizeMobileEffectiveInstant, 'function') assert.equal(root.groveFhirVersion, '4.0.1') -assert.equal(root.groveFhirContractVersion, '0.5.0') +assert.equal(root.groveFhirContractVersion, '0.6.0') +assert.equal(root.groveExchangeProtocol.protocolVersion, 2) +assert.equal(root.groveMobileContract.version, '0.6.0') assert.equal( mobile.groveMobilePackageMetadata.packageId, 'org.grovealliance.fhir.mobile', @@ -89,7 +96,12 @@ assert.equal( 'org.grovealliance.fhir.questionnaire', ) assert.equal(typeof root.parseFhirInstant, 'function') -assert.equal(typeof r4.parseCollectionBundle, 'function') +assert.equal(typeof r4.parseR4CollectionBundle, 'function') +assert.equal(typeof r4.parseGroveMobileExchangeBundle, 'function') +assert.equal(typeof r4.parseGroveMobileRetractionBundle, 'function') +assert.equal(typeof provider.parseProviderRetractionInput, 'function') +assert.equal(typeof provider.providerOutputCoordinates, 'function') +assert.equal(typeof provider.providerOutputRole, 'function') assert.equal(typeof provenance.parseProvenance, 'function') assert.equal(typeof questionnaire.buildQuestionnaire, 'function') diff --git a/packages/fhir/scripts/validate-conformance.mjs b/packages/fhir/scripts/validate-conformance.mjs index 2996ee02..f0d703be 100644 --- a/packages/fhir/scripts/validate-conformance.mjs +++ b/packages/fhir/scripts/validate-conformance.mjs @@ -89,8 +89,34 @@ await requireByteIdentical( resolve(igRoot, 'Conformance/corpora/mobile-semantics/corpus.json'), 'Conformance/corpora/mobile-semantics/corpus.json', ) +for (const name of [ + 'corpus.json', + 'exchange-bundle.json', + 'retraction-bundle.json', +]) { + await requireByteIdentical( + resolve( + packageRoot, + '.grove-fhir/Conformance/corpora/mobile-exchange', + name, + ), + resolve(igRoot, 'Conformance/corpora/mobile-exchange', name), + `Conformance/corpora/mobile-exchange/${name}`, + ) +} const pin = await readJson(resolve(packageRoot, 'grove-fhir.json')) +if ( + pin.repository !== 'https://github.com/SchmiedmayerLab/grove-fhir' || + typeof pin.ref !== 'string' || + !/^[\da-f]{40}$/u.test(pin.ref) || + typeof pin.archiveSha256 !== 'string' || + !/^[\da-f]{64}$/u.test(pin.archiveSha256) +) { + throw new Error( + 'The Grove FHIR dependency pin must contain one complete commit SHA and archive SHA-256.', + ) +} const revision = spawnSync('git', ['-C', igRoot, 'rev-parse', 'HEAD'], { encoding: 'utf8', }) @@ -98,22 +124,13 @@ if (revision.status !== 0) { throw new Error('Cannot resolve the Grove FHIR checkout revision.') } const resolvedSha = revision.stdout.trim() -const pinnedRef = pin.ref ?? pin.sha -// A tag is resolved to the commit it names, so a moved tag is caught rather than trusted. -const pinnedSha = - /^[\da-f]{40}$/u.test(pinnedRef) ? pinnedRef : ( - spawnSync('git', ['-C', igRoot, 'rev-list', '-n', '1', pinnedRef], { - encoding: 'utf8', - }).stdout.trim() - ) +const pinnedRef = pin.ref stdout.write(`Grove FHIR pinned ref: ${pinnedRef}\n`) stdout.write(`Grove FHIR checkout SHA: ${resolvedSha}\n`) // The guide checkout and the catalogs this package generated from must be one commit. -if (!structuralOnly && resolvedSha !== pinnedSha) { +if (!structuralOnly && resolvedSha !== pinnedRef) { throw new Error( - `The Grove FHIR checkout is ${resolvedSha}, but this package is pinned to ${pinnedRef}` + - (pinnedSha && pinnedSha !== pinnedRef ? ` (${pinnedSha})` : '') + - '.', + `The Grove FHIR checkout is ${resolvedSha}, but this package is pinned to ${pinnedRef}.`, ) } @@ -131,16 +148,23 @@ if (structuralOnly) { producerArguments.push('--structural-only') } else { producerArguments.push( + '--allow-example-urls', '--validator', validator, '--package', + `google-health=${resolve(igRoot, 'google-health/output/package.tgz')}`, + '--package', `mobile=${resolve(igRoot, 'mobile/output/package.tgz')}`, '--package', + `oura=${resolve(igRoot, 'oura/output/package.tgz')}`, + '--package', `providers=${resolve(igRoot, 'providers/output/package.tgz')}`, '--package', `sensor=${resolve(igRoot, 'sensor/output/package.tgz')}`, '--package', `questionnaire=${resolve(igRoot, 'questionnaire/output/package.tgz')}`, + '--package', + `withings=${resolve(igRoot, 'withings/output/package.tgz')}`, ) } run('python3', producerArguments, igRoot) diff --git a/packages/fhir/scripts/zod-generator-input.d.mts b/packages/fhir/scripts/zod-generator-input.d.mts new file mode 100644 index 00000000..bd465060 --- /dev/null +++ b/packages/fhir/scripts/zod-generator-input.d.mts @@ -0,0 +1,29 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +export declare function fhirTypeName(value: unknown, label?: string): string + +export declare function structureSchemaIdentifier(name: unknown): string + +export declare function fhirTypeAlias(name: unknown): string + +export declare function pathSchemaIdentifier(path: unknown): string + +export declare function resourceDispatchIdentifiers(name: unknown): Readonly<{ + table: string + inFlight: string +}> + +export declare function assertSafeGeneratorInput( + input: Readonly<{ + structures: Map + valueSets: Map + codeSystems: Map + declaredTypes: Set + }>, +): void diff --git a/packages/fhir/scripts/zod-generator-input.mjs b/packages/fhir/scripts/zod-generator-input.mjs new file mode 100644 index 00000000..41b26658 --- /dev/null +++ b/packages/fhir/scripts/zod-generator-input.mjs @@ -0,0 +1,375 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +const FHIR_NAME = /^[A-Za-z][A-Za-z0-9]*$/u +const ELEMENT_SEGMENT = /^[A-Za-z][A-Za-z0-9]*(?:\[x\])?$/u +const CARDINALITY = /^(?:0|[1-9]\d*)$/u +const FHIR_PATH_SYSTEM_TYPE = + /^http:\/\/hl7\.org\/fhirpath\/System\.[A-Za-z][A-Za-z0-9]*$/u + +const RESERVED_WORDS = new Set([ + 'await', + 'break', + 'case', + 'catch', + 'class', + 'const', + 'continue', + 'debugger', + 'default', + 'delete', + 'do', + 'else', + 'enum', + 'export', + 'extends', + 'false', + 'finally', + 'for', + 'function', + 'if', + 'implements', + 'import', + 'in', + 'instanceof', + 'interface', + 'let', + 'new', + 'null', + 'package', + 'private', + 'protected', + 'public', + 'return', + 'static', + 'super', + 'switch', + 'this', + 'throw', + 'true', + 'try', + 'typeof', + 'var', + 'void', + 'while', + 'with', + 'yield', +]) + +const fail = (label, value, expectation) => { + throw new Error(`${label} ${JSON.stringify(value)} ${expectation}.`) +} + +/** A release-owned FHIR type name that is safe to derive TypeScript identifiers from. */ +export function fhirTypeName(value, label = 'FHIR type name') { + if ( + typeof value !== 'string' || + !FHIR_NAME.test(value) || + RESERVED_WORDS.has(value) + ) { + fail(label, value, 'must be a non-reserved ASCII FHIR name') + } + return value +} + +const typeScriptIdentifier = (value, label) => { + if (!FHIR_NAME.test(value) || RESERVED_WORDS.has(value)) { + fail(label, value, 'must produce a non-reserved TypeScript identifier') + } + return value +} + +/** The value-space identifier used for one generated structure schema. */ +export const structureSchemaIdentifier = (name) => { + const safe = fhirTypeName(name) + return typeScriptIdentifier( + `${safe.charAt(0).toLowerCase()}${safe.slice(1)}Schema`, + `schema identifier derived from ${safe}`, + ) +} + +/** The type-space alias used for one declaration imported from @types/fhir. */ +export const fhirTypeAlias = (name) => + typeScriptIdentifier( + `Fhir${fhirTypeName(name)}`, + `type alias derived from ${String(name)}`, + ) + +const pathSegments = (path, label = 'FHIR element path') => { + if (typeof path !== 'string') { + fail(label, path, 'must be a dotted FHIR path') + } + const segments = path.split('.') + if ( + segments.length === 0 || + segments.some((segment) => !ELEMENT_SEGMENT.test(segment)) + ) { + fail(label, path, 'must contain only ASCII FHIR path segments') + } + return segments +} + +/** The value-space identifier used when a backbone path must be hoisted. */ +export const pathSchemaIdentifier = (path) => { + const parts = pathSegments(path).map((part) => part.replace('[x]', '')) + return typeScriptIdentifier( + `${parts[0].charAt(0).toLowerCase()}${parts[0].slice(1)}${parts + .slice(1) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join('')}Schema`, + `schema identifier derived from ${path}`, + ) +} + +/** Identifiers used by the closed dispatcher for an abstract resource type. */ +export const resourceDispatchIdentifiers = (name) => { + const safe = fhirTypeName(name) + return { + table: typeScriptIdentifier( + `modelled${safe}s`, + `dispatch table derived from ${safe}`, + ), + inFlight: typeScriptIdentifier( + `dispatching${safe}`, + `dispatch guard derived from ${safe}`, + ), + } +} + +const cardinalityMaximum = (value, label) => { + if (value === '*') return Number.POSITIVE_INFINITY + if (typeof value !== 'string' || !CARDINALITY.test(value)) { + fail(label, value, 'must be * or a canonical non-negative integer string') + } + const parsed = Number(value) + if (!Number.isSafeInteger(parsed)) { + fail(label, value, 'must fit in a JavaScript safe integer') + } + return parsed +} + +const finiteInteger = (value, label, { minimum } = {}) => { + if ( + typeof value !== 'number' || + !Number.isSafeInteger(value) || + (minimum !== undefined && value < minimum) + ) { + fail(label, value, 'must be a finite safe integer') + } + return value +} + +const registerIdentifier = (identifiers, identifier, origin) => { + const previous = identifiers.get(identifier) + if (previous !== undefined && previous !== origin) { + throw new Error( + `Generated TypeScript identifier ${identifier} collides for ${previous} and ${origin}.`, + ) + } + identifiers.set(identifier, origin) +} + +const declaredBackboneName = (path) => + pathSegments(path) + .map((part) => { + const normalized = part.replace('[x]', '') + return normalized.charAt(0).toUpperCase() + normalized.slice(1) + }) + .join('') + +const validateType = (type, label) => { + if (typeof type !== 'object' || type === null || Array.isArray(type)) { + fail(label, type, 'must be a FHIR element type object') + } + if (typeof type.code !== 'string') { + fail( + `${label}.code`, + type.code, + 'must be a FHIR type name or FHIRPath system type', + ) + } + if (!FHIR_PATH_SYSTEM_TYPE.test(type.code)) { + fhirTypeName(type.code, `${label}.code`) + } + for (const [index, extension] of (type.extension ?? []).entries()) { + if ( + extension?.url?.endsWith('/structuredefinition-fhir-type') && + extension.valueUrl !== undefined + ) { + fhirTypeName(extension.valueUrl, `${label}.extension[${index}].valueUrl`) + } + } +} + +const validateStructure = ( + mapName, + key, + structure, + declaredTypes, + identifiers, +) => { + if ( + typeof structure !== 'object' || + structure === null || + Array.isArray(structure) + ) { + fail( + `${mapName}[${String(key)}]`, + structure, + 'must be a StructureDefinition object', + ) + } + const name = fhirTypeName(structure.name, `${mapName} structure.name`) + if (key !== name) { + throw new Error( + `${mapName} key ${JSON.stringify(key)} must equal structure.name ${JSON.stringify(name)}.`, + ) + } + if ( + !['primitive-type', 'complex-type', 'resource', 'logical'].includes( + structure.kind, + ) + ) { + fail( + `${name}.kind`, + structure.kind, + 'must be a supported StructureDefinition kind', + ) + } + registerIdentifier( + identifiers, + structureSchemaIdentifier(name), + `structure ${name}`, + ) + if (declaredTypes.has(name)) { + registerIdentifier(identifiers, fhirTypeAlias(name), `FHIR type ${name}`) + } + if (structure.abstract === true && structure.kind === 'resource') { + const dispatch = resourceDispatchIdentifiers(name) + registerIdentifier( + identifiers, + dispatch.table, + `dispatch table for ${name}`, + ) + registerIdentifier( + identifiers, + dispatch.inFlight, + `dispatch guard for ${name}`, + ) + } + + const elements = structure.snapshot?.element + if (!Array.isArray(elements) || elements.length === 0) { + fail(`${name}.snapshot.element`, elements, 'must be a non-empty array') + } + for (const [index, element] of elements.entries()) { + const label = `${name}.snapshot.element[${index}]` + if ( + typeof element !== 'object' || + element === null || + Array.isArray(element) + ) { + fail(label, element, 'must be an element object') + } + const segments = pathSegments(element.path, `${label}.path`) + if (segments[0] !== name) { + fail(`${label}.path`, element.path, `must be rooted at ${name}`) + } + const min = finiteInteger(element.min, `${label}.min`, { minimum: 0 }) + const max = cardinalityMaximum(element.max, `${label}.max`) + if (max < min) { + throw new Error( + `${label} cardinality ${min}..${element.max} is inverted.`, + ) + } + for (const field of ['minValueInteger', 'maxValueInteger']) { + if (element[field] !== undefined) + finiteInteger(element[field], `${label}.${field}`) + } + for (const [typeIndex, type] of (element.type ?? []).entries()) { + validateType(type, `${label}.type[${typeIndex}]`) + } + if (element.contentReference !== undefined) { + if ( + typeof element.contentReference !== 'string' || + !element.contentReference.startsWith('#') + ) { + fail( + `${label}.contentReference`, + element.contentReference, + 'must be a local #path', + ) + } + const target = element.contentReference.slice(1) + const targetSegments = pathSegments(target, `${label}.contentReference`) + if (targetSegments[0] !== name) { + fail( + `${label}.contentReference`, + element.contentReference, + `must stay inside ${name}`, + ) + } + registerIdentifier( + identifiers, + pathSchemaIdentifier(target), + `path ${target}`, + ) + } + const isBackbone = (element.type ?? []).some(({ code }) => + ['BackboneElement', 'Element'].includes(code), + ) + if (isBackbone && declaredTypes.has(declaredBackboneName(element.path))) { + registerIdentifier( + identifiers, + pathSchemaIdentifier(element.path), + `path ${element.path}`, + ) + } + } +} + +/** + * Rejects a malformed or code-generating package AST before any release-owned string reaches a + * TypeScript template. Rendering still quotes property/table keys; this gate protects the few + * positions that must be identifiers and catches generator-name collisions deterministically. + */ +export function assertSafeGeneratorInput({ + structures, + valueSets, + codeSystems, + declaredTypes, +}) { + if (!(structures instanceof Map)) + fail('structures', structures, 'must be a Map') + if (!(valueSets instanceof Map)) fail('valueSets', valueSets, 'must be a Map') + if (!(codeSystems instanceof Map)) + fail('codeSystems', codeSystems, 'must be a Map') + if (!(declaredTypes instanceof Set)) + fail('declaredTypes', declaredTypes, 'must be a Set') + + const identifiers = new Map() + for (const name of declaredTypes) { + const safe = fhirTypeName(name, 'declared FHIR type') + registerIdentifier(identifiers, fhirTypeAlias(safe), `FHIR type ${safe}`) + } + for (const [key, structure] of structures) { + validateStructure('structures', key, structure, declaredTypes, identifiers) + } + for (const [mapName, resources] of [ + ['valueSets', valueSets], + ['codeSystems', codeSystems], + ]) { + for (const [key, resource] of resources) { + if (typeof key !== 'string' || resource?.url !== key) { + throw new Error( + `${mapName} key ${JSON.stringify(key)} must equal its resource URL.`, + ) + } + } + } +} diff --git a/packages/fhir/src/core/freeze.ts b/packages/fhir/src/core/freeze.ts index e9d2921c..5292df7f 100644 --- a/packages/fhir/src/core/freeze.ts +++ b/packages/fhir/src/core/freeze.ts @@ -17,16 +17,20 @@ const isObject = (value: unknown): value is Record => typeof value === 'object' && value !== null export const deepFreeze = (value: T): ReadonlyDeep => { - if (!isObject(value) || Object.isFrozen(value)) { - return value as ReadonlyDeep - } + const visited = new WeakSet() + const visit = (candidate: unknown): void => { + if (!isObject(candidate) || visited.has(candidate)) return + visited.add(candidate) - // Freeze before descending: the frozen check above is what stops a cycle, so a node that - // reaches itself through its own children must already be frozen by the time it is revisited. - Object.freeze(value) - for (const nested of Object.values(value)) { - deepFreeze(nested) + // Read descriptors, not properties: freezing a data structure must not invoke getters. + for (const descriptor of Object.values( + Object.getOwnPropertyDescriptors(candidate), + )) { + if ('value' in descriptor) visit(descriptor.value) + } + if (!Object.isFrozen(candidate)) Object.freeze(candidate) } + visit(value) return value as ReadonlyDeep } diff --git a/packages/fhir/src/core/index.ts b/packages/fhir/src/core/index.ts index 861588ac..c944d8f3 100644 --- a/packages/fhir/src/core/index.ts +++ b/packages/fhir/src/core/index.ts @@ -7,6 +7,7 @@ // export { deepFreeze, type ReadonlyDeep } from './freeze.js' +export { cloneJsonValue, type JsonValue } from './json.js' export { compareFhirInstants, fhirDateTimeToDate, diff --git a/packages/fhir/src/core/json.ts b/packages/fhir/src/core/json.ts new file mode 100644 index 00000000..575491cb --- /dev/null +++ b/packages/fhir/src/core/json.ts @@ -0,0 +1,152 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { err, ok, type Result } from './result.js' + +export type JsonValue = + | boolean + | number + | string + | null + | readonly JsonValue[] + | { readonly [key: string]: JsonValue } + +const invalidJson = (path: ReadonlyArray): Result => + err( + 'invalid-type', + 'Expected an acyclic JSON value made only of null, booleans, finite numbers, strings, dense arrays, and plain data-property objects.', + path, + ) + +const enumerableDataProperty = ( + object: object, + key: PropertyKey, + path: ReadonlyArray, +): Result => { + const descriptor = Object.getOwnPropertyDescriptor(object, key) + if ( + descriptor === undefined || + !('value' in descriptor) || + descriptor.enumerable !== true + ) { + return invalidJson(path) + } + const propertyValue: unknown = descriptor.value + return ok(propertyValue) +} + +const cloneJsonArray = ( + value: readonly unknown[], + keys: readonly PropertyKey[], + ancestors: WeakSet, + path: ReadonlyArray, +): Result => { + if ( + keys.some((key) => typeof key !== 'string') || + keys.length !== value.length + 1 || + !keys.includes('length') + ) { + return invalidJson(path) + } + const clone: JsonValue[] = [] + for (let index = 0; index < value.length; index += 1) { + const property = enumerableDataProperty(value, String(index), [ + ...path, + index, + ]) + if (!property.ok) return property + const nested = cloneJsonNode(property.value, ancestors, [...path, index]) + if (!nested.ok) return nested + clone.push(nested.value) + } + return ok(clone) +} + +const cloneJsonObject = ( + value: object, + keys: readonly PropertyKey[], + ancestors: WeakSet, + path: ReadonlyArray, +): Result => { + const clone: Record = Object.create(null) as Record< + string, + JsonValue + > + for (const key of keys) { + if (typeof key !== 'string') return invalidJson(path) + const property = enumerableDataProperty(value, key, [...path, key]) + if (!property.ok) return property + const nested = cloneJsonNode(property.value, ancestors, [...path, key]) + if (!nested.ok) return nested + Object.defineProperty(clone, key, { + value: nested.value, + enumerable: true, + configurable: true, + writable: true, + }) + } + return ok(clone) +} + +const cloneJsonNode = ( + value: unknown, + ancestors: WeakSet, + path: ReadonlyArray, +): Result => { + if ( + value === null || + typeof value === 'boolean' || + typeof value === 'string' + ) { + return ok(value) + } + if (typeof value === 'number') { + return Number.isFinite(value) ? ok(value) : invalidJson(path) + } + if (typeof value !== 'object' || ancestors.has(value)) { + return invalidJson(path) + } + + const array = Array.isArray(value) + const prototype: unknown = Object.getPrototypeOf(value) + // Realm-local identity is not a JSON property: browser frames, workers, and + // VM-backed test runners give ordinary objects a different Object.prototype. + // A direct prototype whose own prototype is null is still a plain record; + // class instances retain an extra prototype level and remain rejected. + const plainRecord = + prototype === null || + (typeof prototype === 'object' && Object.getPrototypeOf(prototype) === null) + if (!array && !plainRecord) { + return invalidJson(path) + } + + ancestors.add(value) + try { + const keys = Reflect.ownKeys(value) + return array ? + cloneJsonArray(value, keys, ancestors, path) + : cloneJsonObject(value, keys, ancestors, path) + } finally { + ancestors.delete(value) + } +} + +/** + * Takes an isolated snapshot of caller-owned JSON before validation. + * + * Accessors, proxies that throw, cycles, sparse arrays, special object instances, and values + * JSON cannot represent are rejected. Shared acyclic subgraphs are copied independently, just + * as they are after a JSON round trip. The function itself never throws for hostile input. + */ +export const cloneJsonValue = (value: unknown): Result => { + try { + return cloneJsonNode(value, new WeakSet(), []) + } catch { + return invalidJson([]) + } +} diff --git a/packages/fhir/src/core/primitives.ts b/packages/fhir/src/core/primitives.ts index e9999b8b..539f9331 100644 --- a/packages/fhir/src/core/primitives.ts +++ b/packages/fhir/src/core/primitives.ts @@ -80,10 +80,14 @@ const isSemVer = (value: string): boolean => { ) } +const ABSOLUTE_URI = + /^[A-Za-z][A-Za-z\d+.-]*:(?:[A-Za-z\d._~:/?#[\]@!$&'()*+,;=-]|%[A-Fa-f\d]{2})*$/u + const isAbsoluteUri = (value: string): boolean => { + if (!ABSOLUTE_URI.test(value)) return false try { const parsed = new URL(value) - return parsed.protocol.length > 1 && !/\s/u.test(value) + return parsed.protocol.length > 1 } catch { return false } @@ -197,7 +201,10 @@ const compareFractions = (left: string, right: string): -1 | 0 | 1 => { export const parseAbsoluteUri = (value: unknown): Result => { if (typeof value !== 'string' || !isAbsoluteUri(value)) { - return err('invalid-uri', 'Expected an absolute URI.') + return err( + 'invalid-uri', + 'Expected an absolute ASCII RFC 3986 URI with valid percent encoding.', + ) } return ok(value as AbsoluteUri) } diff --git a/packages/fhir/src/core/quantity.ts b/packages/fhir/src/core/quantity.ts index 3823d1e0..2e240353 100644 --- a/packages/fhir/src/core/quantity.ts +++ b/packages/fhir/src/core/quantity.ts @@ -6,6 +6,7 @@ // SPDX-License-Identifier: MIT // +import { cloneJsonValue } from './json.js' import { err, ok, type Result } from './result.js' /** The bounds a Quantity may state in place of an exact value. */ @@ -32,10 +33,16 @@ const COMPARATORS: readonly string[] = ['<', '<=', '>=', '>'] * rather than reduced to a number whose meaning is unknown. */ export const fhirQuantityToValue = (value: unknown): Result => { - if (typeof value !== 'object' || value === null) { + const snapshot = cloneJsonValue(value) + if (!snapshot.ok) return snapshot + if ( + typeof snapshot.value !== 'object' || + snapshot.value === null || + Array.isArray(snapshot.value) + ) { return err('invalid-type', 'Expected a FHIR Quantity.') } - const quantity = value as { + const quantity = snapshot.value as { readonly value?: unknown readonly unit?: unknown readonly code?: unknown diff --git a/packages/fhir/src/core/result.ts b/packages/fhir/src/core/result.ts index 22831aef..e7492a5d 100644 --- a/packages/fhir/src/core/result.ts +++ b/packages/fhir/src/core/result.ts @@ -23,12 +23,17 @@ export type IssueCode = | 'schema-invalid' | 'unsupported-measurement' | 'value-mismatch' + | `mobile-${string}` export interface Issue { readonly severity: IssueSeverity readonly code: IssueCode readonly path: ReadonlyArray readonly message: string + /** Exact normative producer-rule reason when this is a Grove protocol diagnostic. */ + readonly reason?: string + /** Stable FHIR-facing producer-rule location, independent of parser internals. */ + readonly location?: string } export type Result = @@ -57,25 +62,92 @@ export const issues = (entries: readonly Issue[]): Result => ({ issues: entries, }) +type InspectedResult = + | { + readonly ok: true + readonly value: T + readonly warnings: readonly Issue[] + } + | { readonly ok: false; readonly issues: readonly Issue[] } + +const isIssue = (value: unknown): value is Issue => { + if (typeof value !== 'object' || value === null) return false + const candidate = value as Partial + return ( + ['error', 'warning'].includes(String(candidate.severity)) && + typeof candidate.code === 'string' && + Array.isArray(candidate.path) && + candidate.path.every( + (component) => + typeof component === 'string' || typeof component === 'number', + ) && + typeof candidate.message === 'string' && + (candidate.reason === undefined || typeof candidate.reason === 'string') && + (candidate.location === undefined || typeof candidate.location === 'string') + ) +} + +const inspectResult = (value: unknown): InspectedResult | undefined => { + try { + if (typeof value !== 'object' || value === null) return undefined + const candidate = value as { + readonly ok?: unknown + readonly value?: T + readonly warnings?: unknown + readonly issues?: unknown + } + if (candidate.ok === true) { + const warnings = candidate.warnings ?? [] + return Array.isArray(warnings) && warnings.every(isIssue) ? + { ok: true, value: candidate.value as T, warnings } + : undefined + } + return ( + candidate.ok === false && + Array.isArray(candidate.issues) && + candidate.issues.every(isIssue) + ) ? + { ok: false, issues: candidate.issues } + : undefined + } catch { + return undefined + } +} + +const invalidResult = ( + path: ReadonlyArray = [], +): Result => + err('invalid-type', 'Expected a well-formed Grove Result value.', path) + export const mapResult = ( result: Result, transform: (value: T) => U, -): Result => - result.ok ? ok(transform(result.value), result.warnings) : result +): Result => { + const inspected = inspectResult(result) + if (inspected === undefined) return invalidResult() + return inspected.ok ? + ok(transform(inspected.value), inspected.warnings) + : inspected +} export const collectResults = ( results: ReadonlyArray>, ): Result => { + if (!Array.isArray(results)) return invalidResult() const values: T[] = [] const failures: Issue[] = [] const warnings: Issue[] = [] - for (const result of results) { - if (result.ok) { - values.push(result.value) - warnings.push(...(result.warnings ?? [])) + for (const [index, result] of results.entries()) { + const inspected = inspectResult(result) + if (inspected === undefined) { + const invalid = invalidResult([index]) + if (!invalid.ok) failures.push(...invalid.issues) + } else if (inspected.ok) { + values.push(inspected.value) + warnings.push(...inspected.warnings) } else { - failures.push(...result.issues) + failures.push(...inspected.issues) } } diff --git a/packages/fhir/src/index.ts b/packages/fhir/src/index.ts index 597b4f16..bc1e045f 100644 --- a/packages/fhir/src/index.ts +++ b/packages/fhir/src/index.ts @@ -45,16 +45,21 @@ export { export * from './extract/index.js' export { - parseCollectionBundle, + parseGroveMobileExchangeBundle, + parseGroveMobileRetractionBundle, + parseR4CollectionBundle, parseDevice, parseDocumentReference, parseObservation, parseProvenance, parseSpecimen, parseSupportedR4Resource, - type CollectionBundle, + type R4CollectionBundle, type Device, type DocumentReference, + type FhirJson, + type GroveMobileExchangeBundle, + type GroveMobileRetractionBundle, type Observation, type Provenance, type Specimen, @@ -66,22 +71,50 @@ export { entryIdentifierName, createEntryIdentity, deriveEntryFullUrl, + deriveEntryNodeIdentifier, + deriveEntryNodeValue, + deriveEventIdentifier, + deriveOpaqueIdentifier, + encodeLengthFramedUtf8, groveFhirContractVersion, - groveFhirExchangeIdentity, + groveExchangeProtocol, + groveProfileClaims, groveFhirVersion, + groveMobileContract, mobileEffectiveCanonicalization, mobileEffectiveCanonicalizationVectors, + groveMobilePackageMetadata, + groveMobileProfileCanonicals, + groveRecordingFormatRegistry, + isEntryNodeIdentityValue, + isEventIdentityValue, + isOpaqueIdentityValue, sharedMobileMeasurementCatalog, + validateDeploymentIdentity, type ApplicationDeviceInput, type BloodPressureMeasurement, + type ChoiceQuantityMeasurement, + type ChoiceQuantityMeasurementKind, type CompleteIdentifierInput, + type DeploymentIdentityInput, type GatewayApplicationInput, + type GroveIdentifierRole, + type GroveOpaqueIdentityComponents, + type GroveOpaqueIdentityKind, + type GroveRecordingFormat, + type GroveOpaqueIdentitySystems, + type GroveMobileProtocolContract, + type HostDeviceInput, type IdentifiedEntryIdentityInput, + type InstantCodedMeasurement, + type InstantCodedMeasurementKind, type InstantEffectiveTime, type InstantQuantityMeasurement, type InstantQuantityMeasurementKind, type MobileMeasurement, type PeriodEffectiveTime, + type PeriodCodedMeasurement, + type PeriodCodedMeasurementKind, type PeriodQuantityMeasurement, type PeriodQuantityMeasurementKind, type RecordingDeviceInput, @@ -106,7 +139,10 @@ export { type QuestionnairePair, type QuestionnairePreflightOptions, type QuestionnaireResponseAnswerInput, + type QuestionnaireResponseAuthorInput, type QuestionnaireResponseInput, type QuestionnaireResponseItemInput, + type QuestionnaireResponseSourceInput, + type QuestionnaireResponseSubjectInput, type ResolvedQuestionnaireValueSetInput, } from './questionnaire/index.js' diff --git a/packages/fhir/src/mobile/contract.ts b/packages/fhir/src/mobile/contract.ts new file mode 100644 index 00000000..0384b571 --- /dev/null +++ b/packages/fhir/src/mobile/contract.ts @@ -0,0 +1,106 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + groveExchangeProtocol, + groveMobilePackageMetadata, + groveMobileProfileCanonicals, + groveRecordingFormatRegistry, +} from './measurement-catalog.generated.js' +import { deepFreeze } from '../core/index.js' + +export interface GroveMobileProtocolContract { + readonly canonical: string + readonly version: string + readonly fhirVersion: string + readonly protocolVersion: number + readonly profiles: Readonly<{ + exchangeBundle: string + conversionProvenance: string + recordingDevice: string + applicationDevice: string + hostDevice: string + sensorRecordingDocument: string + retractionBundle: string + retractionProvenance: string + }> + readonly extensions: Readonly<{ + entryNodeKey: string + retractionTargetRole: string + }> + readonly systems: Readonly<{ + identifierRole: string + lifecycleEvent: string + retractionTargetRole: string + }> + readonly identity: Readonly<{ + domain: string + entryNodeDomain: string + fullUrlNamespace: string + opaqueIdentifierRoles: readonly string[] + resourceIdentifierPriority: readonly string[] + }> + readonly lifecycle: Readonly<{ + sourceRecordRetracted: string + retractionTargets: typeof groveExchangeProtocol.lifecycle.retraction.targetRoles + adapterOnlyOutputProfileClaims: typeof groveExchangeProtocol.lifecycle.active.adapterOnlyOutputProfileClaims + activeEntryResourcePolicy: typeof groveExchangeProtocol.lifecycle.active.entryResourcePolicy + }> + readonly referencePolicy: typeof groveExchangeProtocol.referencePolicy + readonly recordingFormats: typeof groveRecordingFormatRegistry +} + +const groveMobileContractValue: GroveMobileProtocolContract = { + canonical: groveMobilePackageMetadata.canonical, + version: groveMobilePackageMetadata.version, + fhirVersion: groveMobilePackageMetadata.fhirVersion, + protocolVersion: groveExchangeProtocol.protocolVersion, + profiles: { + exchangeBundle: groveExchangeProtocol.profiles.activeBundle, + conversionProvenance: groveExchangeProtocol.profiles.conversionProvenance, + recordingDevice: groveMobileProfileCanonicals['grove-recording-device'], + applicationDevice: groveMobileProfileCanonicals['grove-application-device'], + hostDevice: groveMobileProfileCanonicals['grove-host-device'], + sensorRecordingDocument: + groveMobileProfileCanonicals['grove-sensor-recording-document'], + retractionBundle: groveExchangeProtocol.profiles.retractionBundle, + retractionProvenance: groveExchangeProtocol.profiles.retractionProvenance, + }, + extensions: groveExchangeProtocol.extensions, + systems: groveExchangeProtocol.codeSystems, + identity: { + domain: groveExchangeProtocol.opaqueIdentity.domain, + entryNodeDomain: groveExchangeProtocol.entryIdentity.entryNode.domain, + fullUrlNamespace: groveExchangeProtocol.entryIdentity.fullUrl.namespace, + opaqueIdentifierRoles: [ + ...new Set( + groveExchangeProtocol.opaqueIdentity.identityKinds.map( + ({ identifierRole }) => identifierRole, + ), + ), + ], + resourceIdentifierPriority: + groveExchangeProtocol.entryIdentity.resourceIdentifierPriority, + }, + lifecycle: { + activeEntryResourcePolicy: + groveExchangeProtocol.lifecycle.active.entryResourcePolicy, + adapterOnlyOutputProfileClaims: + groveExchangeProtocol.lifecycle.active.adapterOnlyOutputProfileClaims, + sourceRecordRetracted: + groveExchangeProtocol.lifecycle.retraction.activityCode, + retractionTargets: groveExchangeProtocol.lifecycle.retraction.targetRoles, + }, + referencePolicy: groveExchangeProtocol.referencePolicy, + recordingFormats: groveRecordingFormatRegistry, +} + +/** Runtime constants projected from the pinned, generated Grove 0.6 protocol catalog. */ +export const groveMobileContract: GroveMobileProtocolContract = deepFreeze( + groveMobileContractValue, +) diff --git a/packages/fhir/src/mobile/identity.ts b/packages/fhir/src/mobile/identity.ts index 4b0e0e9d..0dbd5174 100644 --- a/packages/fhir/src/mobile/identity.ts +++ b/packages/fhir/src/mobile/identity.ts @@ -6,13 +6,20 @@ // SPDX-License-Identifier: MIT // +import { hmac } from '@noble/hashes/hmac.js' +import { sha256 } from '@noble/hashes/sha2.js' import { v5 as uuidV5 } from 'uuid' -import { groveFhirExchangeIdentity } from './measurement-catalog.generated.js' +import { groveMobileContract } from './contract.js' +import { groveExchangeProtocol } from './measurement-catalog.generated.js' import type { CompleteIdentifierInput, + DeploymentIdentityInput, + GroveIdentifierRole, + GroveOpaqueIdentityKind, IdentifiedEntryIdentityInput, } from './types.js' import { + cloneJsonValue, deepFreeze, err, ok, @@ -20,11 +27,81 @@ import { parseFhirId, parseUrnUuid, type FhirId, + type JsonValue, type Result, type UrnUuid, } from '../core/index.js' +import { providerAdapterCatalog } from '../providers/contract.generated.js' + +const ASCII_TOKEN = /^[A-Za-z\d._-]+$/u +const POSITIVE_DECIMAL = /^[1-9]\d*$/u +const UNSIGNED_DECIMAL = /^(?:0|[1-9]\d*)$/u +const LOWERCASE_ROLE = /^[a-z][a-z\d-]*$/u +const CANONICAL_PRODUCER_UUID = + /^[\da-f]{8}-[\da-f]{4}-[1-5][\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}$/u +const OPAQUE_VALUE = /^v2:[A-Za-z\d._-]+:[1-9]\d*:[A-Za-z\d_-]{43}$/u +const EVENT_VALUE = + /^e2:[\da-f]{8}-[\da-f]{4}-[1-5][\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}:[1-9]\d*$/u +const ENTRY_NODE_VALUE = /^n2:[a-z][a-z\d-]*:(?:0|[1-9]\d*):[A-Za-z\d_-]{43}$/u +const BASE64_URL = /^[A-Za-z\d_-]+$/u +const MINIMUM_HMAC_KEY_BYTES = 32 + +const PROVIDER_CODES: ReadonlySet = new Set( + providerAdapterCatalog.providers.map(({ id }) => id), +) +const PROVIDER_IDENTITY_KINDS: ReadonlySet = new Set([ + 'provider-record', + 'provider-output', + 'provider-artifact', +]) +const GENERIC_SOURCE_IDENTITY_KINDS: ReadonlySet = new Set([ + 'source-record', + 'source-output', + 'source-artifact', +]) + +const identityKinds = groveExchangeProtocol.opaqueIdentity.identityKinds.map( + ({ kind }) => kind, +) as readonly GroveOpaqueIdentityKind[] + +const identityComponentCounts = Object.fromEntries( + groveExchangeProtocol.opaqueIdentity.identityKinds.map( + ({ kind, components }) => [kind, components.length], + ), +) as Readonly> + +const identifierRoleByKind: Readonly< + Record< + GroveOpaqueIdentityKind, + Exclude + > +> = Object.fromEntries( + groveExchangeProtocol.opaqueIdentity.identityKinds.map( + ({ kind, identifierRole }) => [kind, identifierRole], + ), +) as Readonly< + Record< + GroveOpaqueIdentityKind, + Exclude + > +> + +type OpaqueIdentityDefinition = + (typeof groveExchangeProtocol)['opaqueIdentity']['identityKinds'][number] + +type StringComponentTuple = Readonly<{ + [Index in keyof Components]: string +}> + +/** Exact ordered component tuples projected from the pinned protocol catalog. */ +export type GroveOpaqueIdentityComponents = Readonly<{ + [Kind in GroveOpaqueIdentityKind]: StringComponentTuple< + Extract['components'] + > +}> export const containsIsolatedSurrogate = (value: string): boolean => { + if (typeof value !== 'string') return true for (let index = 0; index < value.length; index += 1) { const code = value.charCodeAt(index) if (code >= 0xd800 && code <= 0xdbff) { @@ -38,60 +115,569 @@ export const containsIsolatedSurrogate = (value: string): boolean => { return false } +/** Grove's unsigned-32-bit big-endian length-prefixed UTF-8 field encoding. */ +export const encodeLengthFramedUtf8 = ( + components: readonly string[], +): Result => { + const snapshot = cloneJsonValue(components) + if (!snapshot.ok) return snapshot + if (!Array.isArray(snapshot.value)) { + return err('invalid-type', 'Framed fields must be supplied as an array.') + } + let encoder: TextEncoder + try { + encoder = new TextEncoder() + } catch { + return err( + 'out-of-range', + 'The runtime could not initialize UTF-8 framing.', + ) + } + const encoded: Uint8Array[] = [] + let size = 0 + for (const [index, component] of snapshot.value.entries()) { + if (typeof component !== 'string' || containsIsolatedSurrogate(component)) { + return err( + 'invalid-identifier', + 'Framed fields must be strings containing Unicode scalar values only.', + [index], + ) + } + let bytes: Uint8Array + try { + bytes = encoder.encode(component) + } catch { + return err( + 'out-of-range', + 'The runtime could not allocate a UTF-8 framed field.', + [index], + ) + } + if ( + bytes.length > 0xffff_ffff || + size > Number.MAX_SAFE_INTEGER - bytes.length - 4 + ) { + return err( + 'out-of-range', + 'A framed field is too large for unsigned-32-bit length framing.', + [index], + ) + } + encoded.push(bytes) + size += 4 + bytes.length + } + + try { + const framed = new Uint8Array(size) + const view = new DataView(framed.buffer) + let offset = 0 + for (const bytes of encoded) { + view.setUint32(offset, bytes.length, false) + offset += 4 + framed.set(bytes, offset) + offset += bytes.length + } + return ok(framed) + } catch { + return err( + 'out-of-range', + 'The runtime could not allocate the complete UTF-8 frame.', + ) + } +} + +const base64UrlWithoutPadding = (bytes: Uint8Array): string => { + const alphabet = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_' + let result = '' + for (let index = 0; index < bytes.length; index += 3) { + const first = bytes[index] ?? 0 + const second = bytes[index + 1] + const third = bytes[index + 2] + const block = (first << 16) | ((second ?? 0) << 8) | (third ?? 0) + result += alphabet.charAt((block >>> 18) & 63) + result += alphabet.charAt((block >>> 12) & 63) + if (second !== undefined) result += alphabet.charAt((block >>> 6) & 63) + if (third !== undefined) result += alphabet.charAt(block & 63) + } + return result +} + +const decodeBase64UrlWithoutPadding = ( + value: string, +): Uint8Array | undefined => { + if ( + typeof value !== 'string' || + !BASE64_URL.test(value) || + value.length % 4 === 1 + ) { + return undefined + } + const alphabet = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_' + const bytes: number[] = [] + let accumulator = 0 + let bits = 0 + for (const character of value) { + const digit = alphabet.indexOf(character) + if (digit < 0) return undefined + accumulator = (accumulator << 6) | digit + bits += 6 + if (bits >= 8) { + bits -= 8 + bytes.push((accumulator >>> bits) & 0xff) + } + } + if (bits > 0 && (accumulator & ((1 << bits) - 1)) !== 0) return undefined + const decoded = Uint8Array.from(bytes) + return base64UrlWithoutPadding(decoded) === value ? decoded : undefined +} + +const isPublicConformanceKey = (key: Uint8Array): boolean => + key.length === 32 && key.every((value, index) => value === index) + +const DEPLOYMENT_IDENTITY_KEYS = [ + 'entryNodeIdentifierSystem', + 'eventIdentifierSystem', + 'keyEpoch', + 'keyId', + 'opaqueIdentifierSystems', + 'producerInstance', + 'secretBase64Url', +] as const + +const validateDeploymentIdentityInternal = ( + input: unknown, + allowPublicConformanceKey: boolean, +): Result => { + const snapshot = cloneJsonValue(input) + if (!snapshot.ok) return snapshot + if ( + typeof snapshot.value !== 'object' || + snapshot.value === null || + Array.isArray(snapshot.value) + ) { + return err( + 'missing-required', + 'Deployment identity configuration must be a complete object.', + ) + } + const rawCandidate = snapshot.value as Readonly> + const deploymentKeys = Object.keys(rawCandidate) + if ( + deploymentKeys.length !== DEPLOYMENT_IDENTITY_KEYS.length || + deploymentKeys.some( + (key) => !(DEPLOYMENT_IDENTITY_KEYS as readonly string[]).includes(key), + ) + ) { + return err( + 'schema-invalid', + 'Deployment identity configuration contains missing or unknown fields.', + ) + } + const rawSystems = rawCandidate.opaqueIdentifierSystems + if ( + typeof rawSystems !== 'object' || + rawSystems === null || + Array.isArray(rawSystems) + ) { + return err( + 'missing-required', + 'Deployment identity configuration must include its Identifier systems.', + ['opaqueIdentifierSystems'], + ) + } + const candidate = rawCandidate as unknown as DeploymentIdentityInput + const systemEntries = Object.entries(rawSystems) + if ( + systemEntries.length !== identityKinds.length || + identityKinds.some((kind) => !Object.hasOwn(rawSystems, kind)) || + systemEntries.some( + ([kind]) => !identityKinds.includes(kind as GroveOpaqueIdentityKind), + ) || + systemEntries.some(([, system]) => !parseAbsoluteUri(system).ok) || + !parseAbsoluteUri(candidate.eventIdentifierSystem).ok || + !parseAbsoluteUri(candidate.entryNodeIdentifierSystem).ok + ) { + return err( + 'invalid-uri', + 'Every deployment identity system must be an absolute URI.', + ['opaqueIdentifierSystems'], + ) + } + const systems = [ + ...systemEntries.map(([, system]) => system), + candidate.eventIdentifierSystem, + candidate.entryNodeIdentifierSystem, + ] + if (new Set(systems).size !== systems.length) { + return err( + 'duplicate-identifier', + 'Each identity kind, event, and entry-node key space requires its own Identifier.system.', + ['opaqueIdentifierSystems'], + ) + } + if ( + typeof candidate.keyId !== 'string' || + !ASCII_TOKEN.test(candidate.keyId) + ) { + return err( + 'invalid-identifier', + 'Identity keyId must be a nonempty ASCII token using A-Z, a-z, 0-9, dot, underscore, or hyphen.', + ['keyId'], + ) + } + if ( + typeof candidate.keyEpoch !== 'string' || + !POSITIVE_DECIMAL.test(candidate.keyEpoch) + ) { + return err( + 'invalid-identifier', + 'Identity keyEpoch must be a canonical positive decimal string.', + ['keyEpoch'], + ) + } + if ( + typeof candidate.producerInstance !== 'string' || + !CANONICAL_PRODUCER_UUID.test(candidate.producerInstance) + ) { + return err( + 'invalid-identifier', + 'producerInstance must be a canonical lowercase RFC 4122 UUID version 1 through 5.', + ['producerInstance'], + ) + } + const secret = decodeBase64UrlWithoutPadding(candidate.secretBase64Url) + if (secret === undefined || secret.byteLength < MINIMUM_HMAC_KEY_BYTES) { + return err( + 'invalid-identifier', + `Identity secret must be canonical unpadded base64url containing at least ${MINIMUM_HMAC_KEY_BYTES} bytes.`, + ['secretBase64Url'], + ) + } + if (!allowPublicConformanceKey && isPublicConformanceKey(secret)) { + return err( + 'invalid-identifier', + 'The public Grove conformance key is prohibited at public runtime boundaries.', + ['secretBase64Url'], + ) + } + return ok(candidate) +} + +/** Validates deployment identity material for every public production/runtime operation. */ +export const validateDeploymentIdentity = ( + input: unknown, +): Result => + validateDeploymentIdentityInternal(input, false) + +const deriveOpaqueIdentifierInternal = ( + deployment: DeploymentIdentityInput, + identityKind: Kind, + components: GroveOpaqueIdentityComponents[Kind], + allowPublicConformanceKey: boolean, +): Result => { + if ( + typeof identityKind !== 'string' || + !Object.hasOwn(identityComponentCounts, identityKind) + ) { + return err( + 'invalid-code', + 'Identity kind is not part of the closed Grove v2 identity contract.', + ['identityKind'], + ) + } + const validated = validateDeploymentIdentityInternal( + deployment, + allowPublicConformanceKey, + ) + if (!validated.ok) return validated + const componentSnapshot = cloneJsonValue(components) + if (!componentSnapshot.ok) return componentSnapshot + if ( + !Array.isArray(componentSnapshot.value) || + componentSnapshot.value.length !== identityComponentCounts[identityKind] + ) { + return err( + 'value-mismatch', + `${identityKind} requires exactly ${identityComponentCounts[identityKind]} ordered components.`, + ['components'], + ) + } + if ( + !componentSnapshot.value.every( + (component): component is string => typeof component === 'string', + ) + ) { + return err( + 'invalid-identifier', + 'Opaque identity components must all be strings.', + ['components'], + ) + } + const invalidComponentIndex = componentSnapshot.value.findIndex( + (component) => + component.length === 0 || containsIsolatedSurrogate(component), + ) + if (invalidComponentIndex >= 0) { + return err( + 'invalid-identifier', + 'Opaque identity components must be nonempty Unicode-scalar strings.', + ['components', invalidComponentIndex], + ) + } + const firstComponent = componentSnapshot.value[0] + if ( + typeof firstComponent !== 'string' || + (PROVIDER_IDENTITY_KINDS.has(identityKind) && + !PROVIDER_CODES.has(firstComponent)) || + (GENERIC_SOURCE_IDENTITY_KINDS.has(identityKind) && + PROVIDER_CODES.has(firstComponent)) + ) { + return err( + 'invalid-code', + PROVIDER_IDENTITY_KINDS.has(identityKind) ? + 'A Provider identity kind requires one exact catalog provider code as its first component.' + : 'Provider coordinates require the matching provider-record, provider-output, or provider-artifact identity kind.', + ['components', 0], + ) + } + const preimage = encodeLengthFramedUtf8([ + groveMobileContract.identity.domain, + identityKind, + ...componentSnapshot.value, + ]) + if (!preimage.ok) return preimage + const secret = decodeBase64UrlWithoutPadding(validated.value.secretBase64Url) + if (secret === undefined) { + return err( + 'invalid-identifier', + 'Identity secret is not canonical base64url.', + ) + } + const digest = hmac(sha256, secret, preimage.value) + return ok({ + system: validated.value.opaqueIdentifierSystems[identityKind], + value: `v2:${validated.value.keyId}:${validated.value.keyEpoch}:${base64UrlWithoutPadding(digest)}`, + role: identifierRoleByKind[identityKind], + }) +} + +/** Derives one deployment-owned, role-typed Grove v2 HMAC Identifier. */ +export const deriveOpaqueIdentifier = ( + deployment: DeploymentIdentityInput, + identityKind: Kind, + components: GroveOpaqueIdentityComponents[Kind], +): Result => + deriveOpaqueIdentifierInternal(deployment, identityKind, components, false) + /** - * The UUID-v5 name for one identifier: the system, a vertical bar, then the value. + * Internal test seam for the exact published normative vector key. * - * Only the system is barred from containing a vertical bar, so the name splits at the first one. - * A value may contain them, because a composed identifier is built from them. + * Deliberately omitted from every package entry point; application code cannot opt into it. */ -export const entryIdentifierName = ( - input: CompleteIdentifierInput, +export const deriveConformanceVectorOpaqueIdentifier = < + Kind extends GroveOpaqueIdentityKind, +>( + deployment: DeploymentIdentityInput, + identityKind: Kind, + components: GroveOpaqueIdentityComponents[Kind], +): Result => { + const secret = decodeBase64UrlWithoutPadding(deployment.secretBase64Url) + if ( + secret === undefined || + !isPublicConformanceKey(secret) || + deployment.keyId !== groveExchangeProtocol.testVectors.keyId || + deployment.keyEpoch !== groveExchangeProtocol.testVectors.epoch + ) { + return err( + 'invalid-identifier', + 'The internal conformance seam accepts only the exact published vector key.', + ['secretBase64Url'], + ) + } + return deriveOpaqueIdentifierInternal( + deployment, + identityKind, + components, + true, + ) +} + +/** Creates the sole event business Identifier for one immutable exchange assertion. */ +export const deriveEventIdentifier = ( + deployment: DeploymentIdentityInput, + sequence: string, +): Result => { + const validated = validateDeploymentIdentity(deployment) + if (!validated.ok) return validated + if (typeof sequence !== 'string' || !POSITIVE_DECIMAL.test(sequence)) { + return err( + 'invalid-identifier', + 'Event sequence must be a canonical positive decimal string.', + ['sequence'], + ) + } + return ok({ + system: validated.value.eventIdentifierSystem, + value: `e2:${validated.value.producerInstance}:${sequence}`, + role: 'event', + }) +} + +/** Creates an unkeyed, event-scoped graph-node Identifier for a resource without business ID. */ +export const deriveEntryNodeValue = ( + eventSystem: string, + eventValue: string, + role: string, + ordinal: string, ): Result => { - if (!parseAbsoluteUri(input.system).ok) { - return err('invalid-uri', 'Identifier.system must be an absolute URI.', [ - 'system', - ]) + if ( + typeof eventSystem !== 'string' || + typeof eventValue !== 'string' || + !parseAbsoluteUri(eventSystem).ok || + !EVENT_VALUE.test(eventValue) + ) { + return err( + 'invalid-identifier', + 'Entry-node derivation requires a complete canonical event Identifier.', + ['event'], + ) } - if (input.value.trim() === '') { - return err('invalid-identifier', 'Identifier.value must not be blank.', [ - 'value', - ]) + if (typeof role !== 'string' || !LOWERCASE_ROLE.test(role)) { + return err( + 'invalid-code', + 'Entry-node role must be a lowercase code token.', + ['role'], + ) } + if (typeof ordinal !== 'string' || !UNSIGNED_DECIMAL.test(ordinal)) { + return err( + 'invalid-identifier', + 'Entry-node ordinal must be a canonical unsigned decimal string.', + ['ordinal'], + ) + } + const preimage = encodeLengthFramedUtf8([ + groveMobileContract.identity.entryNodeDomain, + eventSystem, + eventValue, + role, + ordinal, + ]) + if (!preimage.ok) return preimage + return ok( + `n2:${role}:${ordinal}:${base64UrlWithoutPadding(sha256(preimage.value))}`, + ) +} + +/** Creates an unkeyed, event-scoped graph-node Identifier for a resource without business ID. */ +export const deriveEntryNodeIdentifier = ( + deployment: DeploymentIdentityInput, + event: CompleteIdentifierInput, + role: string, + ordinal: string, +): Result => { + const validated = validateDeploymentIdentity(deployment) + if (!validated.ok) return validated + const eventSnapshot = cloneJsonValue(event) if ( - containsIsolatedSurrogate(input.system) || - containsIsolatedSurrogate(input.value) + !eventSnapshot.ok || + typeof eventSnapshot.value !== 'object' || + eventSnapshot.value === null || + Array.isArray(eventSnapshot.value) ) { return err( 'invalid-identifier', - 'Identifier strings must contain Unicode scalar values only.', + "Entry-node derivation requires this producer's complete typed event Identifier.", + ['event'], ) } - if (input.system.includes('|')) { + const eventValue = eventSnapshot.value as unknown as CompleteIdentifierInput + if ( + eventValue.system !== validated.value.eventIdentifierSystem || + eventValue.role !== 'event' || + typeof eventValue.value !== 'string' || + !EVENT_VALUE.test(eventValue.value) || + !eventValue.value.startsWith(`e2:${validated.value.producerInstance}:`) + ) { return err( 'invalid-identifier', - 'Identifier.system must not contain a vertical bar, so the name splits at the first one.', - ['system'], + "Entry-node derivation requires this producer's complete typed event Identifier.", + ['event'], ) } - return ok(`${input.system}|${input.value}`) + const value = deriveEntryNodeValue( + eventValue.system, + eventValue.value, + role, + ordinal, + ) + if (!value.ok) return value + return ok({ + system: validated.value.entryNodeIdentifierSystem, + value: value.value, + role: 'entry-node', + }) +} + +const hasCanonicalSha256DigestSuffix = (value: string): boolean => { + const digest = value.slice(value.lastIndexOf(':') + 1) + return decodeBase64UrlWithoutPadding(digest)?.length === 32 +} + +export const isOpaqueIdentityValue = (value: string): boolean => + typeof value === 'string' && + OPAQUE_VALUE.test(value) && + hasCanonicalSha256DigestSuffix(value) + +export const isEventIdentityValue = (value: string): boolean => + typeof value === 'string' && EVENT_VALUE.test(value) + +export const isEntryNodeIdentityValue = (value: string): boolean => + typeof value === 'string' && + ENTRY_NODE_VALUE.test(value) && + hasCanonicalSha256DigestSuffix(value) + +/** The UUID-v5 byte name for one identifier: length-framed UTF-8 `[system, value]`. */ +export const entryIdentifierName = ( + input: CompleteIdentifierInput, +): Result => { + const snapshot = cloneJsonValue(input) + if ( + !snapshot.ok || + typeof snapshot.value !== 'object' || + snapshot.value === null || + Array.isArray(snapshot.value) + ) { + return err('invalid-type', 'Expected one complete Identifier.') + } + const identifier = snapshot.value as unknown as CompleteIdentifierInput + if (!parseAbsoluteUri(identifier.system).ok) { + return err('invalid-uri', 'Identifier.system must be an absolute URI.', [ + 'system', + ]) + } + if (typeof identifier.value !== 'string' || identifier.value === '') { + return err('invalid-identifier', 'Identifier.value must not be empty.', [ + 'value', + ]) + } + return encodeLengthFramedUtf8([identifier.system, identifier.value]) } -/** Derives the IG-mandated lowercase UUID-v5 Bundle entry fullUrl. */ +/** Derives the protocol-mandated lowercase UUID-v5 Bundle entry fullUrl. */ export const deriveEntryFullUrl = ( input: CompleteIdentifierInput, ): Result => { const name = entryIdentifierName(input) if (!name.ok) return name - return parseUrnUuid( - `urn:uuid:${uuidV5( - name.value, - groveFhirExchangeIdentity.fullUrlAlgorithm.namespace, - )}`, + `urn:uuid:${uuidV5(name.value, groveMobileContract.identity.fullUrlNamespace)}`, ) } -/** Pairs a business identifier with its deterministic exchange identity. */ +/** Pairs a complete entry key with its deterministic exchange UUID URN. */ export const createEntryIdentity = ( identifier: CompleteIdentifierInput, id?: FhirId, @@ -101,13 +687,29 @@ export const createEntryIdentity = ( 'id', ]) } - const fullUrl = deriveEntryFullUrl(identifier) + const identifierSnapshot = cloneJsonValue(identifier) + if ( + !identifierSnapshot.ok || + typeof identifierSnapshot.value !== 'object' || + identifierSnapshot.value === null || + Array.isArray(identifierSnapshot.value) + ) { + return err('invalid-type', 'Expected one complete entry Identifier.') + } + const safeIdentifier = + identifierSnapshot.value as unknown as CompleteIdentifierInput + const fullUrl = deriveEntryFullUrl(safeIdentifier) if (!fullUrl.ok) return fullUrl - return ok( deepFreeze({ fullUrl: fullUrl.value, - identifier: { system: identifier.system, value: identifier.value }, + identifier: { + system: safeIdentifier.system, + value: safeIdentifier.value, + ...(safeIdentifier.role === undefined ? + {} + : { role: safeIdentifier.role }), + }, ...(id === undefined ? {} : { id }), }) as unknown as IdentifiedEntryIdentityInput, ) diff --git a/packages/fhir/src/mobile/index.ts b/packages/fhir/src/mobile/index.ts index 694fef4d..7be209b9 100644 --- a/packages/fhir/src/mobile/index.ts +++ b/packages/fhir/src/mobile/index.ts @@ -10,23 +10,46 @@ export { entryIdentifierName, createEntryIdentity, deriveEntryFullUrl, + deriveEntryNodeIdentifier, + deriveEntryNodeValue, + deriveEventIdentifier, + deriveOpaqueIdentifier, + encodeLengthFramedUtf8, + isEntryNodeIdentityValue, + isEventIdentityValue, + isOpaqueIdentityValue, + validateDeploymentIdentity, } from './identity.js' +export { groveMobileContract } from './contract.js' +export type { GroveMobileProtocolContract } from './contract.js' export { - groveFhirExchangeIdentity, + groveExchangeProtocol, + groveProfileClaims, groveFhirContractVersion, groveFhirVersion, groveMobilePackageMetadata, + groveMobileProfileCanonicals, + groveRecordingFormatRegistry, mobileEffectiveCanonicalization, mobileEffectiveCanonicalizationVectors, sharedMobileMeasurementCatalog, + type GroveRecordingFormat, + type ProviderRecordingFormat, type SharedMobileMeasurementKind, } from './measurement-catalog.generated.js' export { canonicalizeMobileEffectiveInstant } from './time.js' export type { ApplicationDeviceInput, BloodPressureMeasurement, + ChoiceQuantityMeasurement, + ChoiceQuantityMeasurementKind, CompleteIdentifierInput, + DeploymentIdentityInput, + GroveIdentifierRole, + GroveOpaqueIdentityKind, + GroveOpaqueIdentitySystems, GatewayApplicationInput, + HostDeviceInput, IdentifiedEntryIdentityInput, InstantCodedMeasurement, InstantCodedMeasurementKind, @@ -46,3 +69,4 @@ export type { SleepStageMeasurement, SleepStageSourceCodingInput, } from './types.js' +export type { GroveOpaqueIdentityComponents } from './identity.js' diff --git a/packages/fhir/src/mobile/measurement-catalog.generated.ts b/packages/fhir/src/mobile/measurement-catalog.generated.ts index 7a6955aa..842d3592 100644 --- a/packages/fhir/src/mobile/measurement-catalog.generated.ts +++ b/packages/fhir/src/mobile/measurement-catalog.generated.ts @@ -11,13 +11,13 @@ import { deepFreeze } from '../core/index.js' -export const groveFhirContractVersion = '0.5.0' as const +export const groveFhirContractVersion = '0.6.0' as const export const groveFhirVersion = '4.0.1' as const const groveMobilePackageMetadataValue = { fhirVersion: '4.0.1', - version: '0.5.0', + version: '0.6.0', packageId: 'org.grovealliance.fhir.mobile', canonical: 'https://grovealliance.org/fhir/mobile', dependencies: ['hl7.terminology.r4#7.3.0', 'hl7.fhir.uv.extensions.r4#5.3.0'], @@ -26,140 +26,2301 @@ const groveMobilePackageMetadataValue = { export const groveMobilePackageMetadata: typeof groveMobilePackageMetadataValue = deepFreeze(groveMobilePackageMetadataValue) -const groveFhirExchangeIdentityValue = { +const groveMobileProfileCanonicalsValue = { + 'grove-application-device': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device', + 'grove-host-device': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device', + 'grove-mobile-conversion-provenance': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance', + 'grove-mobile-exchange-bundle': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle', + 'grove-mobile-retraction-bundle': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle', + 'grove-mobile-retraction-provenance': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-provenance', + 'grove-recording-device': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-device', + 'grove-sensor-recording-document': + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', +} as const + +export const groveMobileProfileCanonicals: typeof groveMobileProfileCanonicalsValue = + deepFreeze(groveMobileProfileCanonicalsValue) + +const groveExchangeProtocolValue = { + $schema: + 'https://grovealliance.org/fhir/catalog/schemas/exchange-protocol.schema.json', schemaVersion: 1, - version: '0.5.0', + version: '0.6.0', + protocolVersion: 2, + releaseVersion: '0.6.0', fhirVersion: '4.0.1', - profile: - 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle', - entryIdentifierExtension: - 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier', - fullUrlAlgorithm: { - name: 'uuid-v5-composed-identifier-v1', - namespace: 'a9a39cf1-c944-5d15-a3c2-c395969ea101', - input: - 'UTF-8 bytes of the system, one U+007C VERTICAL LINE, then the value', - rejects: - 'The system must not contain a vertical bar, so the input splits unambiguously at the first one. The value may contain vertical bars, because a composed identifier is built from them. An isolated UTF-16 surrogate in either is rejected. Nothing is escaped: an escaping rule is the conformance surface this scheme exists to remove.', - output: 'urn:uuid:', - whyADigestHere: - 'Version 5 turns an identifier into the URN a Bundle fullUrl must be. It is a format conversion, not concealment: the identifier it is built from travels in the clear on the same resource.', - }, - resourceId: { - role: 'repository logical id', - required: false, - sourceIdentityAllowed: false, - }, - internalReferences: { - containedReferencesAllowed: true, - bundleEntryReferences: 'must use an entry fullUrl UUID URN', - unresolvedUuidUrnsAllowed: false, - }, - recordingDeviceIdentity: { + profiles: { + activeBundle: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle', + conversionProvenance: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance', + retractionBundle: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle', + retractionProvenance: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-provenance', + }, + extensions: { + entryNodeKey: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key', + retractionTargetRole: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-role', + writerRecordVersion: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version', + }, + codeSystems: { + identifierRole: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role', + lifecycleEvent: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-lifecycle-event', + retractionTargetRole: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-retraction-target-role', + }, + conformanceLevels: [ + { + code: 'fhir-r4', + authority: 'FHIR R4 and the exact packages in the release manifest', + validator: 'HL7 FHIR Validator', + }, + { + code: 'grove-profile', + authority: + 'Published Grove StructureDefinitions, ValueSets, CodeSystems, and FHIRPath invariants', + validator: 'HL7 FHIR Validator with the exact Grove packages', + }, + { + code: 'grove-producer', + authority: + 'This protocol, its closed catalogs, and the shared positive and negative corpora', + validator: + 'Any implementation that reports the exact stable Grove rule identifiers', + }, + ], + producerDiagnostics: [ + { + code: 'mobile-exchange.unclassified', + reason: + 'A producer-contract failure reached the exchange diagnostic boundary without a more specific registered rule; validation fails closed and the conformance kit must classify the gap.', + }, + { + code: 'mobile-exchange.entry-node-key', + reason: + 'Every Bundle entry must carry exactly one complete Grove exchange entry node key.', + }, + { + code: 'mobile-exchange.deterministic-full-url', + reason: + 'Bundle.entry.fullUrl must be the UUID version 5 value derived from its complete entry identifier.', + }, + { + code: 'mobile-exchange.resolved-reference', + reason: + 'Every internal UUID URN reference must resolve to a Bundle entry fullUrl.', + }, + { + code: 'mobile-exchange.event-identity', + reason: + 'Bundle.identifier.value must be the canonical e2 producer UUID and positive sequence form.', + }, + { + code: 'mobile-exchange.entry-node-digest', + reason: + 'An entry-node digest must be derived from the enclosing event identifier, role, and ordinal.', + }, + { + code: 'mobile-exchange.identity-system-role', + reason: + 'Within one event graph, each Grove Identifier.system names exactly one Grove identifier role; one namespace cannot change meaning between nodes.', + }, + { + code: 'mobile-output.source-output-required', + reason: + 'Every active clinical output must carry its exact typed source-output identity in addition to source-record identity.', + }, + { + code: 'mobile-output.hybrid-companion', + reason: + 'A hybrid-required output and its exact source-preservation companion must form the catalog-declared closed, same-source, bidirectionally linked graph.', + }, + { + code: 'healthkit-ecg.output-graph', + reason: + 'A HealthKit ECG event must satisfy the catalog-owned waveform, optional average-heart-rate, symptom-member, relationship-direction, identity, and effective-period graph contract.', + }, + { + code: 'mobile-exchange.transform-provenance', + reason: + 'An active event must contain exactly one transform Provenance and no retraction Provenance.', + }, + { + code: 'mobile-retraction.logical-target', + reason: + 'A retraction target must be a typed logical Reference without a literal reference.', + }, + { + code: 'mobile-retraction.target-role', + reason: + 'Every retraction target must carry exactly one closed Grove target-role code.', + }, + { + code: 'mobile-retraction.opaque-target', + reason: + 'A retraction target must use the exact canonical v2 HMAC identity previously emitted.', + }, + { + code: 'mobile-retraction.no-clinical-copy', + reason: + 'A retraction event contains its lifecycle Provenance and optional Device agents, never a copied or mutilated clinical resource.', + }, + { + code: 'mobile-exchange.lifecycle-coding', + reason: + 'A lifecycle Provenance must carry exactly one coding across the ISO transform and Grove retraction lifecycle systems; translations from other systems remain open.', + }, + { + code: 'mobile-output.semantic-profile', + reason: + 'Every active Observation must directly claim one admitted Grove semantic profile shape; an empty claim cannot bypass semantic validation.', + }, + { + code: 'mobile-output.fixed-quantity-unit', + reason: + 'Every Quantity-valued catalog measurement uses the exact fixed system and code declared by its semantic profile contract.', + }, + { + code: 'mobile-output.quantity-value-domain', + reason: + 'Every Quantity-valued catalog measurement stays within its reviewed representational minimum, maximum, and integer-only domain without inventing a physiologic range.', + }, + { + code: 'mobile-exchange.reference-target-type', + reason: + 'An Observation subject resolves to a Patient entry, not merely to any existing fullUrl.', + }, + { + code: 'mobile-exchange.reference-declared-type', + reason: + "When Reference.type is present it must equal the referenced entry's actual resourceType token.", + }, + { + code: 'mobile-exchange.logical-source-entity', + reason: + 'Lifecycle Provenance carries exactly one logical source-record Identifier entity and never a literal source Reference.', + }, + { + code: 'mobile-retraction.role-target-type', + reason: + 'Every retraction target role fixes its admitted resource type and Identifier role.', + }, + { + code: 'mobile-exchange.single-source-entity', + reason: + 'A lifecycle Provenance identifies exactly one source-record entity.', + }, + { + code: 'mobile-exchange.reference-shape', + reason: + 'A governed Reference is exclusively resolving-literal or identifier-only logical, never both.', + }, + { + code: 'mobile-exchange.logical-patient-reference', + reason: + 'An identifier-only logical Patient Reference carries the exact Patient type and one complete absolute-system pseudonym Identifier.', + }, + { + code: 'mobile-output.adapter-only-profile', + reason: + 'An adapter-only active output type must directly claim exactly its one admitted adapter profile.', + }, + { + code: 'mobile-exchange.entry-resource-type', + reason: + 'An active event admits only its closed output, supporting, and lifecycle resource type set.', + }, + { + code: 'mobile-exchange.contained-resource-prohibited', + reason: + 'Mobile exchange events prohibit contained resources; every graph node must be an addressable Bundle entry.', + }, + { + code: 'mobile-output.document-profile', + reason: + 'Every active DocumentReference must directly claim exactly one admitted recording or clinical-document profile mode.', + }, + { + code: 'mobile-support.device-profile', + reason: + 'Every active Device must directly claim exactly one admitted Grove Device profile mode.', + }, + { + code: 'mobile-support.connected', + reason: + 'Every supporting resource must be connected to an output or the lifecycle Provenance.', + }, + { + code: 'mobile-exchange.provenance-profile', + reason: + 'The sole active lifecycle Provenance must directly claim exactly one admitted Mobile or adapter conversion profile.', + }, + ], + event: { + semanticUnit: + 'Exactly one immutable source record revision or one retraction assertion. A transport may batch complete event Bundles without changing this unit.', + bundleIdentifier: { + system: + 'A deployment-owned absolute URI, stable for the producer instance and never reused for another key space.', + valueForm: + 'e2::', + soleEventBusinessIdentifier: true, + }, + times: { + occurred: + 'The source activity or retraction-detection time, carried on Provenance.occurred[x].', + recorded: + 'The time the Provenance assertion was recorded; mandatory Provenance.recorded.', + assembled: + 'The time the immutable exchange graph was assembled; mandatory Bundle.timestamp.', + }, + retry: + 'An exact retry reuses the Bundle identifier, all three times, entry keys, and payload. Any source revision or content change receives a new sequence.', + transport: + 'Bundle.type is collection. Bundle.entry.request and Bundle.entry.response are prohibited; repository application and atomicity are separate sink behavior.', + }, + opaqueIdentity: { + algorithm: 'HMAC-SHA-256', + domain: 'org.grovealliance.fhir.identity.v2', + preimage: + "The domain, one identity kind from the closed identityKinds list, and exactly that kind's typed components in declared order. Every typed component is a non-empty Unicode-scalar string; missing, empty, additional, reordered, or non-scalar components are errors. Every field is encoded as an unsigned 32-bit big-endian byte length followed by its exact UTF-8 bytes.", + valueForm: + 'v2:::<43-character-base64url-digest-without-padding>', + identifierSystem: + 'Deployment-owned and immutable for one identity kind, deployment scope, key id, and key epoch. A rotated key uses a new system and epoch.', + keyRequirements: { + minimumBytes: 32, + managedSecret: true, + testKeysProhibited: true, + }, + componentRequirements: { + valueType: 'unicode-scalar-string', + nonEmpty: true, + arity: 'exactly-kind-components', + }, + privacy: + 'HMAC reduces disclosure of native identifiers. It does not de-identify subjects, times, clinical data, graph relationships, or attachments.', + informationPreservation: { + hmacGuarantee: 'equality-and-reconciliation', + reversible: false, + exactNativeRoundTrip: + 'optional-governed-source-identifier-on-catalog-designated-primary', + potentiallyInternalCoordinates: [ + 'output-role', + 'output-discriminator', + 'child-index', + ], + internalCoordinateRule: + 'A coordinate may exist only in the HMAC preimage when it has no standalone source or clinical meaning.', + semanticSourceFactRule: + 'Every meaningful source order, time, multiplicity, ordinal, or other source fact must also be preserved in a FHIR element or registered source payload; a HMAC preimage is never its sole representation.', + }, + rotation: + 'A deployment must retain old key epochs while identifiers from them can be replayed or retracted. Losing a key ends deterministic reconciliation for that epoch; a new key must never be presented under the old system.', + identityKinds: [ + { + kind: 'source-record', + identifierRole: 'source-record', + components: [ + 'adapter-id', + 'source-type', + 'repository-scope-system', + 'repository-scope-value', + 'native-record-id', + ], + }, + { + kind: 'source-output', + identifierRole: 'source-output', + components: [ + 'adapter-id', + 'source-type', + 'repository-scope-system', + 'repository-scope-value', + 'native-record-id', + 'output-role', + 'output-discriminator', + ], + }, + { + kind: 'writer-record', + identifierRole: 'writer-record', + components: [ + 'writer-application-system', + 'writer-application-value', + 'writer-record-id', + ], + }, + { + kind: 'provider-record', + identifierRole: 'source-record', + components: [ + 'provider-code', + 'source-type', + 'provider-scope-system', + 'provider-scope-value', + 'native-record-id', + ], + }, + { + kind: 'provider-output', + identifierRole: 'source-output', + components: [ + 'provider-code', + 'source-type', + 'provider-scope-system', + 'provider-scope-value', + 'native-record-id', + 'output-role', + 'output-discriminator', + ], + }, + { + kind: 'source-artifact', + identifierRole: 'source-artifact', + components: [ + 'adapter-id', + 'source-type', + 'repository-scope-system', + 'repository-scope-value', + 'native-record-id', + 'format-code', + 'part-index', + ], + }, + { + kind: 'provider-artifact', + identifierRole: 'source-artifact', + components: [ + 'provider-code', + 'source-type', + 'provider-scope-system', + 'provider-scope-value', + 'native-record-id', + 'format-code', + 'part-index', + ], + }, + { + kind: 'source-context', + identifierRole: 'source-context', + components: [ + 'adapter-id', + 'context-type', + 'repository-scope-system', + 'repository-scope-value', + 'native-context-id', + ], + }, + { + kind: 'recording-device', + identifierRole: 'recording-device', + components: [ + 'adapter-id', + 'subject-system', + 'subject-value', + 'stable-unit-token', + ], + }, + { + kind: 'device-snapshot', + identifierRole: 'device-snapshot', + components: [ + 'event-system', + 'event-value', + 'device-role', + 'source-device-token', + ], + }, + ], + }, + governedSourceIdentifier: { purpose: - "A recording Device deduplicates across every Observation it produced, so one participant's recorder is stored once instead of once per sample.", - subjectParticipates: - 'A wearable belongs to a person, so the subject partitions the key: two participants wearing the same model are two devices, which is what Device means in R4. This is the one identity in the family that admits a subject reference; every other identity derives from source data alone and must not.', - versionsExcluded: - "Firmware and software change over a recorder's life and are therefore not part of its identity. They are not carried on the deduplicated Device either, because a backfill converts samples out of order and would leave an arbitrary value behind. Each Observation states the versions in force when it was recorded.", - failClosed: - 'The key requires a non-empty manufacturer and at least one of model or hardwareVersion. A source that states less has no admitted device identity; the producer emits its per-sample derived identity instead of asserting a shared device.', - prohibitedInputs: [ - 'vendor per-unit identifiers and serial numbers, including a digest of one', - 'firmware, software, Resource.id, repository ids, and processing timestamps', + 'Optional source-native round-trip or traceability identity on the one primary source-derived output. It supplements, and never replaces, the mandatory opaque Grove source-record and source-output identifiers.', + placement: + 'Use Resource.identifier only on the designated one-to-one primary representation of the source record. Do not repeat one source-native identifier on child outputs, synthesized specimens, source artifacts, device snapshots, or other supporting nodes. A source-only conversion may designate its DocumentReference as primary.', + system: + 'Identifier.system is a deployment- or source-authority-owned absolute URI naming the exact native key space. When the native value is not globally unique, the system must identify the governed repository/account/store scope; an underspecified vendor-wide system is nonconformant.', + value: + 'Identifier.value is the exact source-native value. It is emitted only under an explicit deployment purpose and disclosure policy; omission remains conformant and does not weaken Grove reconciliation.', + type: 'Identifier.type is optional when system fully defines the key space, as in FHIR R4. A present type may be text-only. Every present Coding has an absolute system and a code in the FHIR code lexical form; no Coding may use the Grove identifier-role CodeSystem because source-native identifiers are not Grove graph keys.', + prohibitions: + 'Never use a source-native value as Resource.id, a Bundle entry key, retraction address, arbitrary Observation component, or untyped metadata. Do not incidentally copy it into Attachment.title, Attachment.url, transport/object names, or logs; a deployment that deliberately uses it in a separate non-FHIR storage contract must govern that use explicitly.', + closedProfiles: + 'A profile with a closed or exact Identifier set admits no governed source identifier unless that profile explicitly adds such a slice. Supporting nodes whose identity is synthesized from another source record remain closed.', + validation: { + optionality: 'optional', + designatedNode: { + lifecycle: 'active', + r4Element: 'Resource.identifier', + designationMode: 'catalog-output-selector', + maxPerPrimaryOutput: 1, + maxPrimaryOutputsPerEvent: 1, + ambiguityRule: 'omit-native-identifier', + outputModeRules: { + 'semantic-only': 'sole-source-derived-output', + 'source-only': 'sole-document-reference-output', + 'hybrid-required': 'one-structured-primary-never-companion-artifact', + choice: 'one-selected-one-to-one-output', + }, + adapterSelectors: { + healthkit: { + catalog: 'catalog/healthkit-adapter.json', + rowCollection: 'rows', + rowKey: 'sourceTypeIdentifier', + sourceMarker: { + kind: 'extension', + url: 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-source-type-extension', + valueElement: 'valueCode', + }, + strategy: 'unique-output-intersecting-source-row-profiles', + excludedProfiles: [ + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-ecg-average-heart-rate-observation', + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-workout-segment', + ], + }, + 'health-connect': { + catalog: 'catalog/health-connect-adapter.json', + rowCollection: 'recordTypes', + rowKey: 'token', + sourceMarker: { + kind: 'extension', + url: 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-record-type', + valueElement: 'valueCode', + }, + strategy: 'unique-output-selected-by-source-row-output-contract', + eligibleCountRules: ['exactly-one'], + eligibleGraphRules: ['exactly-one-admitted-specimen-output'], + excludedResourceTypes: ['Specimen'], + }, + providers: { + catalog: 'catalog/providers-adapter.json', + rowCollection: 'providers.sourceTypes', + rowKey: 'provider-id+token', + sourceMarker: { + kind: 'extension', + url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type', + valueElement: 'valueCode', + }, + providerMarker: { + kind: 'extension', + url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider', + valueElement: 'valueCode', + }, + strategy: 'unique-supported-measurement-or-sole-source-document', + }, + sensorkit: { + catalog: 'catalog/sensorkit-adapter.json', + rowCollection: 'entries', + rowKey: 'sourceTypeCode', + sourceMarker: { + kind: 'extension', + url: 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-source-type', + valueElement: 'valueCode', + }, + strategy: 'unique-structured-row-profile-or-sole-source-document', + }, + }, + }, + identifier: { + classification: { + activeOutputNonGroveIdentifier: 'governed-source-identifier', + supportNodeIdentifier: 'profile-defined-non-source-identifier', + supportLeakageDetection: + 'exact-system-and-value-pair-match-to-active-governed-identifier', + }, + system: { + required: true, + absoluteUri: true, + scope: 'deployment-or-exact-source-authority-key-space', + }, + value: { + required: true, + nonEmpty: true, + exactSourceValue: true, + }, + type: { + required: false, + textOnlyAllowed: true, + coding: { + systemRequired: true, + systemAbsoluteUri: true, + codeRequired: true, + codeLexicalForm: 'fhir-code', + groveRoleCodingAllowed: false, + }, + }, + }, + forbiddenIdentityUses: [ + 'resource-id', + 'bundle-entry-key', + 'retraction-address', + 'observation-component', + 'untyped-metadata', + 'attachment-title', + 'attachment-url', + ], + forbiddenIdentityUseEnforcement: { + exactIdentifierPair: { + r4Paths: ['**.Identifier'], + matchRule: 'exact-system-and-value', + enforcement: 'event-validator', + }, + resourceId: { + r4Paths: ['**.Resource.id'], + matchRule: 'no-value-only-heuristic', + enforcement: 'producer-obligation-and-profile-review', + }, + bundleEntryKey: { + r4Paths: [ + 'Bundle.entry.fullUrl', + 'Bundle.entry.extension.valueIdentifier.value', + ], + matchRule: + 'identifier-pair-structural-validation; no-fullUrl-value-heuristic', + enforcement: 'exchange-entry-key-validator-and-producer-obligation', + }, + retractionAddress: { + r4Paths: [ + 'Provenance.target.reference', + 'Provenance.target.identifier.value', + ], + matchRule: + 'identifier-pair-structural-validation; no-reference-value-heuristic', + enforcement: 'retraction-profile-validator-and-producer-obligation', + }, + observationComponent: { + r4Paths: ['Observation.component.value[x]'], + matchRule: 'no-value-only-heuristic', + enforcement: + 'closed-adapter-component-semantics-and-producer-obligation', + }, + untypedMetadata: { + r4Paths: [], + matchRule: 'no-generic-carrier-admitted', + enforcement: + 'closed-adapter-metadata-allowlists-and-no-grove-generic-extension', + }, + attachmentPresentation: { + r4Paths: [ + 'DocumentReference.content.attachment.title', + 'DocumentReference.content.attachment.url', + ], + matchRule: 'no-substring-heuristic', + enforcement: 'deployment-governance-and-producer-obligation', + }, + }, + duplicateElsewhereInEventAllowed: false, + neverResourceTypes: [ + 'Specimen', + 'Patient', + 'Device', + 'ResearchStudy', + 'ResearchSubject', + 'PlanDefinition', + 'QuestionnaireResponse', + 'Provenance', + 'Bundle', + ], + neverProfiles: [ + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-ecg-average-heart-rate-observation', + ], + healthConnectChildCountRules: [ + 'one-per-sample', + 'one-per-stage', + 'one-per-present-field', + ], + healthConnectChildGraphRules: [ + 'one-per-source-segment-or-lap', + 'one-per-source-delta', + ], + recordingDocumentRule: + "A DocumentReference may carry the governed source Identifier only when it is the active event's sole source-derived output; a companion source artifact in a hybrid graph is not primary.", + }, + }, + entryIdentity: { + selection: + 'Use the selected complete business Identifier pair for a resource that has one. Otherwise use the entry-node Identifier carried by the Grove entry-node-key extension.', + resourceIdentifierPriority: [ + 'source-output', + 'source-artifact', + 'source-record', + 'writer-record', + 'device-snapshot', + 'recording-device', + ], + entryNode: { + system: 'A deployment-owned absolute URI for event-scoped graph nodes.', + domain: 'org.grovealliance.fhir.entry-node.v2', + components: [ + 'event-system', + 'event-value', + 'node-role', + 'zero-based-ordinal', + ], + valueForm: + 'n2:::<43-character-base64url-sha256-without-padding>', + digest: + 'Unkeyed SHA-256 over the same unsigned 32-bit length-framed UTF-8 encoding. It is a deterministic graph key, not a privacy control.', + }, + fullUrl: { + algorithm: + 'UUID version 5 over the unsigned 32-bit length-framed UTF-8 fields [Identifier.system, Identifier.value]. Identifier.system is an absolute ASCII RFC 3986 URI; an IRI must first be represented as its canonical URI form (for example IDNA plus percent-encoding) before it enters FHIR or the identity preimage.', + namespace: '43df4575-bff7-5a57-9a80-2472cd2b0623', + output: 'urn:uuid:', + privacy: + 'UUID version 5 is formatting only and makes no concealment claim.', + }, + }, + lifecycle: { + active: { + activitySystem: + 'http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle', + activityCode: 'transform', + codingCardinality: + 'Exactly one coding from the ISO lifecycle system and zero codings from the Grove lifecycle system; translations from unrelated systems remain open.', + shape: + 'One active Bundle contains every output derived from exactly one source record revision and exactly one conversion Provenance targeting every output.', + entryResourcePolicy: { + outputResourceTypes: [ + 'Observation', + 'DocumentReference', + 'Specimen', + 'VisionPrescription', + 'MedicationAdministration', + 'MedicationStatement', + ], + supportingResourceTypes: [ + 'Patient', + 'Device', + 'ResearchStudy', + 'ResearchSubject', + 'PlanDefinition', + 'QuestionnaireResponse', + ], + lifecycleResourceType: 'Provenance', + otherResourceTypesAllowed: false, + containedResourcesAllowed: false, + supportingResourcesMustBeConnected: true, + profileClaimAuthority: 'catalog/profile-claims.json', + rule: 'Every top-level entry is one admitted source-derived output, the sole conversion Provenance, or a connected supporting resource. Every output and each Device or QuestionnaireResponse matches one exact direct-profile mode. Contained resources are prohibited so clinical output cannot bypass event identity and Provenance targeting.', + }, + adapterOnlyOutputProfileClaims: { + authority: 'catalog/profile-claims.json', + resourceTypes: [ + 'Specimen', + 'VisionPrescription', + 'MedicationAdministration', + 'MedicationStatement', + ], + rule: 'Each listed active output type is admitted only under the exact one-profile adapter claim for that resourceType. An unprofiled or source-neutral output of one of these types is prohibited.', + }, + }, + retraction: { + activitySystem: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-lifecycle-event', + activityCode: 'source-record-retracted', + codingCardinality: + 'Exactly one coding from the Grove lifecycle system and zero codings from the ISO lifecycle system; translations from unrelated systems remain open.', + meaning: + 'The producer records that its source no longer exposes the record and identifies the exact previously emitted graph nodes. It neither asserts clinical error nor instructs a FHIR server to delete anything.', + target: + 'Each Provenance.target is a typed logical Reference with exactly one complete Identifier and one Grove target-role extension. Literal references and copied active clinical resources are prohibited.', + targetRoles: { + 'primary-output': { + identifierRole: 'source-output', + resourceTypes: [ + 'Observation', + 'VisionPrescription', + 'MedicationAdministration', + 'MedicationStatement', + ], + }, + 'source-artifact': { + identifierRole: 'source-output', + resourceTypes: ['DocumentReference'], + }, + 'child-output': { + identifierRole: 'source-output', + resourceTypes: ['Observation'], + }, + specimen: { + identifierRole: 'source-output', + resourceTypes: ['Specimen'], + }, + 'device-snapshot': { + identifierRole: 'device-snapshot', + resourceTypes: ['Device'], + }, + }, + sink: 'A receiver must resolve every target unambiguously under its indexed complete Identifier pair before applying its separately configured idempotent and atomic lifecycle policy.', + }, + }, + referencePolicy: { + literalClosure: + 'Every literal Reference in an exchange Bundle resolves to exactly one Bundle.entry.fullUrl. Contained resources and #id references are prohibited in Mobile active and retraction event Bundles.', + declaredType: + 'When Reference.type is present on a literal reference, it is the exact target resourceType token.', + governedShape: + 'Every reference at a governed path is either one resolving Bundle fullUrl with no logical identifier, or one identifier-only logical reference with no literal, an exact allowed Reference.type, and one complete absolute-system Identifier. Identifier-only Patient references carry a deployment-scoped pseudonym and are not fabricated Bundle nodes.', + paths: [ + { + resourceType: 'Observation', + path: 'subject', + targetTypes: ['Patient'], + }, + { + resourceType: 'Observation', + path: 'device', + targetTypes: ['Device'], + }, + { + resourceType: 'Observation', + path: 'specimen', + targetTypes: ['Specimen'], + }, + { + resourceType: 'Observation', + path: 'focus', + targetTypes: ['Location'], + }, + { + resourceType: 'Observation', + path: 'hasMember', + targetTypes: ['Observation'], + }, + { + resourceType: 'Observation', + path: 'derivedFrom', + targetTypes: [ + 'Observation', + 'DocumentReference', + 'QuestionnaireResponse', + ], + }, + { + resourceType: 'DocumentReference', + path: 'subject', + targetTypes: ['Patient'], + }, + { + resourceType: 'Specimen', + path: 'subject', + targetTypes: ['Patient'], + }, + { + resourceType: 'MedicationAdministration', + path: 'subject', + targetTypes: ['Patient'], + }, + { + resourceType: 'MedicationStatement', + path: 'subject', + targetTypes: ['Patient'], + }, + { + resourceType: 'VisionPrescription', + path: 'patient', + targetTypes: ['Patient'], + }, + { + resourceType: 'ResearchSubject', + path: 'individual', + targetTypes: ['Patient'], + }, + { + resourceType: 'ResearchSubject', + path: 'study', + targetTypes: ['ResearchStudy'], + }, + { + resourceType: 'ResearchStudy', + path: 'protocol', + targetTypes: ['PlanDefinition'], + }, + { + resourceType: 'Device', + path: 'parent', + targetTypes: ['Device'], + }, + ], + extensionTargets: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice', + targetTypes: ['Device'], + }, + { + url: 'http://hl7.org/fhir/StructureDefinition/workflow-researchStudy', + targetTypes: ['ResearchStudy'], + }, + ], + }, + outputModes: [ + { + code: 'semantic-only', + meaning: + 'A reviewed FHIR semantic projection is the complete admitted output.', + }, + { + code: 'source-only', + meaning: + 'No safe semantic projection is admitted; emit the registered source-preservation artifact.', + }, + { + code: 'hybrid-required', + meaning: + 'Emit both the semantic projection and exact source-preservation artifact in one validated graph.', + }, + { + code: 'choice', + meaning: + 'The catalog explicitly admits equivalent alternatives and names every allowed choice.', + }, + ], + outputCountRules: [ + 'exactly-one', + 'zero-or-one', + 'one-per-sample', + 'one-per-stage', + 'one-per-present-field', + 'graph-specific', + ], + recordingDevice: { + instanceRule: + 'A shared FHIR Device requires a deployment-governed stable per-unit token. Manufacturer, model, hardware version, subject, or their digest cannot establish physical-instance identity.', + unknownInstance: + 'If no stable per-unit token is available, omit the shared Device and retain model/source description only where the selected resource permits it.', + snapshots: + 'A Grove recording Device carries both its stable per-unit recording-device identifier and a distinct event-scoped device-snapshot identifier. The snapshot is the selected entry key. Event-time firmware, software, operating-system, and application build facts are immutable snapshot data; never mutate one shared Device while importing historical events out of order.', + roles: + 'The device-snapshot component device-role is the resource kind only: application, host, or recording-device. Participation roles such as assembler, enterer, gateway, or author never enter identity. One Device resource may participate in several roles; graph construction deduplicates the resource and retains every role relationship.', + }, + payload: { + attachmentSize: + 'When a Grove profile requires Attachment.size it is the exact pre-base64 byte count and must be between 0 and 2147483647 inclusive. Larger payloads are rejected unless a future segmented manifest profile is used.', + attachmentHash: + 'FHIR R4 Attachment.hash is base64 SHA-1 over the pre-base64 payload bytes. Stronger integrity uses a separately defined manifest element and never changes Attachment.hash semantics.', + semanticComparison: + 'FHIR graphs compare semantically through lossless JSON tokens that preserve decimal lexemes. Whole-Bundle byte equality is not required.', + byteComparison: + 'Exact bytes are required for identity preimages and registered opaque, CSV, or binary payloads.', + }, + testVectors: { + keyHex: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', + keyId: 'test-key', + epoch: '1', + warning: + 'Public conformance key only. A production deployment must reject this key.', + identitySystems: [ + { + identityKind: 'source-record', + system: + 'https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1', + }, + { + identityKind: 'source-output', + system: + 'https://study.example.org/fhir/NamingSystem/grove-source-output-v2/test-key/1', + }, + { + identityKind: 'writer-record', + system: + 'https://study.example.org/fhir/NamingSystem/grove-writer-record-v2/test-key/1', + }, + { + identityKind: 'provider-record', + system: + 'https://study.example.org/fhir/NamingSystem/grove-provider-record-v2/test-key/1', + }, + { + identityKind: 'provider-output', + system: + 'https://study.example.org/fhir/NamingSystem/grove-provider-output-v2/test-key/1', + }, + { + identityKind: 'source-artifact', + system: + 'https://study.example.org/fhir/NamingSystem/grove-source-artifact-v2/test-key/1', + }, + { + identityKind: 'provider-artifact', + system: + 'https://study.example.org/fhir/NamingSystem/grove-provider-artifact-v2/test-key/1', + }, + { + identityKind: 'source-context', + system: + 'https://study.example.org/fhir/NamingSystem/grove-source-context-v2/test-key/1', + }, + { + identityKind: 'recording-device', + system: + 'https://study.example.org/fhir/NamingSystem/grove-recording-device-v2/test-key/1', + }, + { + identityKind: 'device-snapshot', + system: + 'https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v2/test-key/1', + }, ], - lexicalRules: { - absentField: - 'An absent source field is the empty string; it is never omitted from the composition.', - normalization: - 'Source strings are used exactly as the platform states them; no trimming, case folding, or Unicode normalization is applied.', - noSeparatorInComponents: - 'No component may contain U+007C VERTICAL LINE. A source value carrying one is rejected rather than escaped.', + measurementExamples: { + effectiveInstant: '2026-08-19T10:30:00-07:00', + effectivePeriodStart: '2026-08-19T00:00:00-07:00', + effectivePeriodEnd: '2026-08-20T00:00:00-07:00', + healthConnectLastModifiedTime: '2026-08-20T08:00:00Z', + nativeRecordPrefix: 'record-', + repositoryScope: { + system: 'https://study.example.org/fhir/NamingSystem/source-repository', + value: '1f5c58aa-6ec6-4e79-a682-829a9debd3f5', + }, + providerScope: { + system: 'https://study.example.org/fhir/NamingSystem/provider-account', + value: 'acct-7f3a9c', + }, + globalProviderScope: { + system: + 'https://study.example.org/fhir/NamingSystem/provider-key-space', + value: 'oura-document-id-global', + }, + coordinates: { + canonicalInstant: '2026-08-19T17:30:00.000000000Z', + canonicalPeriodStart: '2026-08-19T07:00:00.000000000Z', + canonicalPeriodEnd: '2026-08-20T07:00:00.000000000Z', + occurrence: '0', + sleepStageToken: 'STAGE_TYPE_LIGHT', + exerciseSegmentToken: 'EXERCISE_LAP', + }, }, - vectors: [ + identities: [ + { + id: 'unicode-and-separator-source-record', + identityKind: 'source-record', + components: [ + 'health-connect', + 'RestingHeartRateRecord', + 'urn:uuid:1f5c58aa-6ec6-4e79-a682-829a9debd3f5', + 'default', + 'record|東京', + ], + value: 'v2:test-key:1:UKY2qgzSB8--SueGxEfOhpElzHTVJ6usIUWV_KUTD6o', + }, { - case: 'apple-watch', + id: 'multi-output-sample', + identityKind: 'source-output', + components: [ + 'health-connect', + 'HeartRateRecord', + 'urn:uuid:1f5c58aa-6ec6-4e79-a682-829a9debd3f5', + 'default', + 'record-heart-001', + 'sample', + '2026-08-19T10:30:00.000000000Z|0', + ], + value: 'v2:test-key:1:PQCWz9dZSrJm-KrbhbkckGeowkjhSSwWDRCVuF3VfXw', + }, + { + id: 'provider-account-complete-pair', + identityKind: 'provider-record', + components: [ + 'withings', + 'measure', + 'https://accounts.example.org', + 'patient|α', + '17348211', + ], + value: 'v2:test-key:1:p3NFdQ-hmHon98JG7cmCbLncbAmNkjkBa5sYocSr6pw', + }, + { + id: 'provider-output-domain-separated', + identityKind: 'provider-output', + components: [ + 'withings', + 'getmeas:9+10', + 'https://accounts.example.org', + 'patient|α', + '17348211', + 'blood-pressure-panel', + 'single', + ], + value: 'v2:test-key:1:HjBwHRt0W3-CbhJbc7hGpWRp92zug70gt5m626T4Y2U', + }, + { + id: 'provider-artifact-domain-separated', + identityKind: 'provider-artifact', + components: [ + 'google-health-api', + 'heart-rate', + 'https://accounts.example.org', + 'patient|α', + 'recording-001', + 'provider-recording', + '0', + ], + value: 'v2:test-key:1:CxLpZ4NQee12xCyJCGimrxMLEKvbRt54Kl6RTh9UsrU', + }, + { + id: 'generic-source-artifact', + identityKind: 'source-artifact', + components: [ + 'sensorkit', + 'device-usage', + 'https://store.example.org', + 'default', + 'record-001', + 'native-recording', + '0', + ], + value: 'v2:test-key:1:7QkJwJojYb7w6BlKMy3TyoX-sgiPzjGJHL1euY-4SKk', + }, + { + id: 'writer-record-complete-application-pair', + identityKind: 'writer-record', + components: [ + 'https://applications.example.org', + 'com.withings.wiscale2', + 'logical-record-001', + ], + value: 'v2:test-key:1:N4QSlWU6sNp9ahfyfSRTUO0K_VIZZoy-Lw3JTNrDzP4', + }, + { + id: 'healthkit-medication-source-context', + identityKind: 'source-context', components: [ - 'Patient/1a2b3c', 'healthkit', - 'Apple Inc.', - 'Watch', - 'Watch7,12', + 'medication-health-concept', + 'urn:uuid:1f5c58aa-6ec6-4e79-a682-829a9debd3f5', + 'default', + '2f8a51c6-9d34-4e07-b2f1-63c8ad905e12', + ], + value: 'v2:test-key:1:nq3ZogmXHSznC1LC1wNMm7KTQChgapPzmjmGeB9RHcw', + }, + { + id: 'recording-device-per-unit', + identityKind: 'recording-device', + components: [ + 'healthkit', + 'https://study.example.org/participants', + 'participant-001', + 'watch-unit-token-001', + ], + value: 'v2:test-key:1:MWGV3Vfk0jfLIr0nowr_I7TAwoqGtpSkxUi1d8FxTnE', + }, + { + id: 'device-snapshot-per-event', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v2', + 'e2:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42', + 'application', + 'com.example.app|1.2.3', ], - value: 'v1:Patient/1a2b3c|healthkit|Apple Inc.|Watch|Watch7,12', + value: 'v2:test-key:1:TUaZacIuSMAFl_WbhxvMBX4AgxIKcHw_KvbntQdXYxQ', }, + ], + invalidIdentities: [ { - case: 'no-hardware-version', + id: 'empty-typed-component', + identityKind: 'source-record', components: [ - 'Patient/1a2b3c', 'health-connect', - 'Google', - 'Pixel Watch', + 'RestingHeartRateRecord', + 'urn:uuid:1f5c58aa-6ec6-4e79-a682-829a9debd3f5', '', + 'record-001', ], - value: 'v1:Patient/1a2b3c|health-connect|Google|Pixel Watch|', + expectedError: 'empty-component', }, { - case: 'different-participant-same-model', + id: 'provider-components-under-generic-record-kind', + identityKind: 'source-record', components: [ - 'Patient/9z8y7x', - 'healthkit', - 'Apple Inc.', - 'Watch', - 'Watch7,12', + 'oura', + 'daily_activity', + 'https://accounts.example.org', + 'patient-001', + 'activity-001', + ], + expectedError: 'provider-kind-required', + }, + { + id: 'provider-components-under-generic-output-kind', + identityKind: 'source-output', + components: [ + 'withings', + 'getmeas:9+10', + 'https://accounts.example.org', + 'patient-001', + '17348211', + 'blood-pressure-panel', + 'single', ], - value: 'v1:Patient/9z8y7x|healthkit|Apple Inc.|Watch|Watch7,12', + expectedError: 'provider-kind-required', + }, + { + id: 'provider-components-under-generic-artifact-kind', + identityKind: 'source-artifact', + components: [ + 'google-health-api', + 'heart-rate', + 'https://accounts.example.org', + 'patient-001', + 'recording-001', + 'provider-recording', + '0', + ], + expectedError: 'provider-kind-required', + }, + ], + event: { + system: 'https://study.example.org/fhir/NamingSystem/grove-event-v2', + producerInstance: '1f5c58aa-6ec6-4e79-a682-829a9debd3f5', + sequence: '42', + value: 'e2:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42', + }, + entryNode: { + system: 'https://study.example.org/fhir/NamingSystem/grove-entry-node-v2', + role: 'conversion-provenance', + ordinal: '0', + value: + 'n2:conversion-provenance:0:SwGD7C4DT5_9kgIOQ9h7W8I4UdwJPuEOnkh2TgQVwko', + fullUrl: 'urn:uuid:9908feb7-0370-5f06-a689-f8afa210eb41', + }, + fullUrls: [ + { + id: 'unicode-value-and-escaped-system', + system: 'https://xn--fsq.example/%E8%AD%98%E5%88%A5%E5%AD%90', + value: 'café|東京', + fullUrl: 'urn:uuid:d35e4203-71f6-595c-bd1b-306b8414974e', + }, + ], + }, +} as const + +export const groveExchangeProtocol: typeof groveExchangeProtocolValue = + deepFreeze(groveExchangeProtocolValue) + +const groveExchangeRuleDiagnosticsValue = { + 'mobile-exchange.entry-node-key': { + reason: + 'Every Bundle entry must carry exactly one complete Grove exchange entry node key.', + severity: 'error', + }, + 'mobile-exchange.deterministic-full-url': { + reason: + 'Bundle.entry.fullUrl must be the UUID version 5 value derived from its complete entry identifier.', + severity: 'error', + }, + 'mobile-exchange.resolved-reference': { + reason: + 'Every internal UUID URN reference must resolve to a Bundle entry fullUrl.', + severity: 'error', + }, + 'mobile-output.fixed-quantity-unit': { + reason: + 'Every Quantity-valued catalog measurement uses the exact fixed system and code declared by its semantic profile contract.', + severity: 'error', + }, + 'mobile-exchange.event-identity': { + reason: + 'Bundle.identifier.value must be the canonical e2 producer UUID and positive sequence form.', + severity: 'error', + }, + 'mobile-exchange.entry-node-digest': { + reason: + 'An entry-node digest must be derived from the enclosing event identifier, role, and ordinal.', + severity: 'error', + }, + 'mobile-output.source-output-required': { + reason: + 'Every active clinical output must carry its exact typed source-output identity in addition to source-record identity.', + severity: 'error', + }, + 'mobile-exchange.identity-system-role': { + reason: + 'Within one event graph, each Grove Identifier.system names exactly one Grove identifier role; one namespace cannot change meaning between nodes.', + severity: 'error', + }, + 'mobile-exchange.transform-provenance': { + reason: + 'An active event must contain exactly one transform Provenance and no retraction Provenance.', + severity: 'error', + }, + 'mobile-retraction.logical-target': { + reason: + 'A retraction target must be a typed logical Reference without a literal reference.', + severity: 'error', + }, + 'mobile-retraction.target-role': { + reason: + 'Every retraction target must carry exactly one closed Grove target-role code.', + severity: 'error', + }, + 'mobile-retraction.opaque-target': { + reason: + 'A retraction target must use the exact canonical v2 HMAC identity previously emitted.', + severity: 'error', + }, + 'mobile-retraction.no-clinical-copy': { + reason: + 'A retraction event contains its lifecycle Provenance and optional Device agents, never a copied or mutilated clinical resource.', + severity: 'error', + }, + 'mobile-exchange.lifecycle-coding': { + reason: + 'A lifecycle Provenance must carry exactly one coding across the ISO transform and Grove retraction lifecycle systems; translations from other systems remain open.', + severity: 'error', + }, + 'mobile-output.semantic-profile': { + reason: + 'Every active Observation must directly claim one admitted Grove semantic profile shape; an empty claim cannot bypass semantic validation.', + severity: 'error', + }, + 'mobile-exchange.reference-target-type': { + reason: + 'An Observation subject resolves to a Patient entry, not merely to any existing fullUrl.', + severity: 'error', + }, + 'mobile-exchange.reference-declared-type': { + reason: + "When Reference.type is present it must equal the referenced entry's actual resourceType token.", + severity: 'error', + }, + 'mobile-exchange.logical-source-entity': { + reason: + 'Lifecycle Provenance carries exactly one logical source-record Identifier entity and never a literal source Reference.', + severity: 'error', + }, + 'mobile-retraction.role-target-type': { + reason: + 'Every retraction target role fixes its admitted resource type and Identifier role.', + severity: 'error', + }, + 'mobile-exchange.single-source-entity': { + reason: + 'A lifecycle Provenance identifies exactly one source-record entity.', + severity: 'error', + }, + 'mobile-exchange.reference-shape': { + reason: + 'A governed Reference is exclusively resolving-literal or identifier-only logical, never both.', + severity: 'error', + }, + 'mobile-exchange.logical-patient-reference': { + reason: + 'An identifier-only logical Patient Reference carries the exact Patient type and one complete absolute-system pseudonym Identifier.', + severity: 'error', + }, + 'mobile-exchange.entry-resource-type': { + reason: + 'An active event admits only its closed output, supporting, and lifecycle resource type set.', + severity: 'error', + }, + 'mobile-output.adapter-only-profile': { + reason: + 'An adapter-only active output type must directly claim exactly its one admitted adapter profile.', + severity: 'error', + }, + 'mobile-exchange.contained-resource-prohibited': { + reason: + 'Mobile exchange events prohibit contained resources; every graph node must be an addressable Bundle entry.', + severity: 'error', + }, + 'mobile-output.document-profile': { + reason: + 'Every active DocumentReference must directly claim exactly one admitted recording or clinical-document profile mode.', + severity: 'error', + }, + 'mobile-support.device-profile': { + reason: + 'Every active Device must directly claim exactly one admitted Grove Device profile mode.', + severity: 'error', + }, + 'mobile-exchange.provenance-profile': { + reason: + 'The sole active lifecycle Provenance must directly claim exactly one admitted Mobile or adapter conversion profile.', + severity: 'error', + }, + 'mobile-support.connected': { + reason: + 'Every supporting resource must be connected to an output or the lifecycle Provenance.', + severity: 'error', + }, +} as const + +export const groveExchangeRuleDiagnostics: typeof groveExchangeRuleDiagnosticsValue = + deepFreeze(groveExchangeRuleDiagnosticsValue) + +const groveProfileClaimsValue = { + $schema: + 'https://grovealliance.org/fhir/catalog/schemas/catalog-contracts.schema.json', + schemaVersion: 1, + fhirVersion: '4.0.1', + version: '0.6.0', + observationAdapterClaim: { + cardinality: 2, + members: [ + 'exactly one semantic profile from catalog/measurement-catalog.json (shared or owner-specific), sharedSensorProfiles, or standardAdapterClaims', + 'exactly one adapter profile listed below', + ], + inheritedProfilesAreNotDeclared: true, + adapterProfiles: [ + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-observation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-ecg-observation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-ecg-average-heart-rate-observation', + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-observation', + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation', + 'https://grovealliance.org/fhir/oura/StructureDefinition/oura-observation', + 'https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-ecg-observation', + ], + sharedSensorProfiles: [ + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-ecg-observation', + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-sampled-data-observation', + ], + standardAdapterClaims: [ + { + id: 'healthkit-body-mass-index', + semanticProfile: 'http://hl7.org/fhir/StructureDefinition/bmi', + adapterProfile: + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-observation', + cardinality: 2, + reason: + 'The authoritative R4 BMI profile is reused directly; BMI is not a one-source Mobile profile.', + }, + ], + forbiddenExplicitProfiles: [ + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-observation', + 'http://hl7.org/fhir/StructureDefinition/bp', + 'http://hl7.org/fhir/StructureDefinition/bodyheight', + 'http://hl7.org/fhir/StructureDefinition/bodytemp', + 'http://hl7.org/fhir/StructureDefinition/bodyweight', + 'http://hl7.org/fhir/StructureDefinition/heartrate', + 'http://hl7.org/fhir/StructureDefinition/oxygensat', + 'http://hl7.org/fhir/StructureDefinition/resprate', + ], + }, + healthKitSingleProfileObservationClaims: { + cardinality: 1, + profiles: [ + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-exercise-time', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-move-time', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-stand-hour', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-stand-time', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-atrial-fibrillation-burden', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-audiogram-panel', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-biological-sex', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-bladder-incontinence', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-bleeding-after-pregnancy', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-bleeding-during-pregnancy', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-blood-alcohol-content', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-blood-type', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-contraceptive-use', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-cycling-functional-threshold-power', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-date-of-birth', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-environmental-audio-exposure', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-environmental-audio-exposure-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-environmental-sound-reduction', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-fitzpatrick-skin-type', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-food-correlation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-forced-expiratory-volume-1', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-forced-vital-capacity', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-gad7-assessment', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-handwashing-session', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-headphone-audio-exposure', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-headphone-audio-exposure-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-heart-rate-recovery-one-minute', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-high-heart-rate-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-hypertension-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-infrequent-menstrual-cycles', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-inhaler-usage', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-insulin-delivery', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-irregular-heart-rhythm-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-irregular-menstrual-cycles', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-lactation-status', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-low-cardio-fitness-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-low-heart-rate-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-number-of-alcoholic-beverages', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-number-of-times-fallen', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-peak-expiratory-flow-rate', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-peripheral-perfusion-index', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-persistent-intermenstrual-bleeding', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-phq9-assessment', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-physical-effort', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-pregnancy-status', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-pregnancy-test-result', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-progesterone-test-result', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-prolonged-menstrual-periods', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-running-ground-contact-time', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-running-stride-length', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-running-vertical-oscillation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-six-minute-walk-test-distance', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-sleep-apnea-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-sleeping-breathing-disturbances', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-stair-ascent-speed', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-stair-descent-speed', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-state-of-mind', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-swimming-stroke-count', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-abdominal-cramps', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-acne', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-appetite-changes', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-bloating', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-breast-pain', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-chest-tightness-or-pain', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-chills', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-constipation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-coughing', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-diarrhea', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-dizziness', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-dry-skin', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-fainting', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-fatigue', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-fever', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-generalized-body-ache', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-hair-loss', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-headache', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-heartburn', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-hot-flashes', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-loss-of-smell', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-loss-of-taste', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-lower-back-pain', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-memory-lapse', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-mood-changes', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-nausea', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-night-sweats', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-pelvic-pain', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-rapid-pounding-or-fluttering-heartbeat', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-runny-nose', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-shortness-of-breath', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-sinus-congestion', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-skipped-heartbeat', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-sleep-changes', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-sore-throat', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-vomiting', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-wheezing', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-time-in-daylight', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-toothbrushing-session', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-underwater-depth', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-uv-exposure', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-vaginal-dryness', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-waist-circumference', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-asymmetry', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-double-support', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-heart-rate-average', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-speed', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-steadiness', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-steadiness-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-step-length', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-water-temperature', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-wheelchair-use', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-workout-effort-score', + ], + otherProfilesAllowed: false, + }, + healthKitEcgGraphClaim: { + waveform: { + cardinality: { + min: 1, + max: 1, + }, + profiles: [ + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-ecg-observation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-ecg-observation', + ], + outputIdentity: { + role: 'electrocardiogram', + discriminator: 'single', + }, + }, + averageHeartRate: { + cardinality: { + min: 0, + max: 1, + }, + profiles: [ + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-ecg-average-heart-rate-observation', + ], + code: { + system: 'http://loinc.org', + code: '8867-4', + display: 'Heart rate', + }, + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: 'beats/minute', }, + outputIdentity: { + role: 'average-heart-rate', + discriminator: 'single', + }, + sameSourceRecord: true, + sameEffectivePeriod: true, + reference: { + sourceOutput: 'average-heart-rate', + r4Path: 'Observation.derivedFrom', + targetOutput: 'electrocardiogram', + targetType: 'Observation', + referenceShape: 'resolving-literal', + min: 1, + max: 1, + }, + }, + correlatedSymptoms: { + sourceOutput: 'electrocardiogram', + r4Path: 'Observation.hasMember', + targetOutputClass: 'admitted-healthkit-symptom', + targetProfiles: [ + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-chest-tightness-or-pain', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-dizziness', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-fainting', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-fatigue', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-rapid-pounding-or-fluttering-heartbeat', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-shortness-of-breath', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-skipped-heartbeat', + ], + targetType: 'Observation', + referenceShape: 'identifier-only', + identifierRole: 'source-output', + sameEventBundle: false, + cardinality: { + min: 0, + max: '*', + }, + statusConditionedCardinality: [ + { + status: 'notSet', + min: 0, + max: 0, + }, + { + status: 'none', + min: 0, + max: 0, + }, + { + status: 'present', + min: 1, + max: '*', + }, + ], + distinctIdentifiers: true, + preserveDuplicateSourceSamples: true, + }, + provenance: { + profile: + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-conversion-provenance', + rule: 'The ECG conversion Provenance targets the waveform and the optional average-heart-rate output. Each correlated symptom has its own source-record event and normal HealthKit conversion Provenance.', + }, + }, + sensorRecordingDocumentClaim: { + resourceType: 'DocumentReference', + cardinality: 1, + profiles: [ + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + ], + otherProfilesAllowed: false, + requiredIdentifierRoles: [ + 'source-record', + 'source-output', + 'source-artifact', + ], + }, + healthKitRecordingDocumentClaim: { + cardinality: 2, + profiles: [ + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-recording-document', + ], + otherProfilesAllowed: false, + requiredIdentifierRoles: [ + 'source-record', + 'source-output', + 'source-artifact', + ], + }, + healthKitClinicalRecordDocumentClaim: { + cardinality: 1, + profiles: [ + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-clinical-record-document', ], - composition: [ - 'subjectReference', - 'adapterId', - 'manufacturer', - 'model', - 'hardwareVersion', + otherProfilesAllowed: false, + requiredIdentifierRoles: [ + 'source-record', + 'source-output', + 'source-artifact', ], - form: 'v1:||||', - fixedArity: - 'Exactly five components, so an absent source field is an empty component rather than an omitted one. The arity is fixed, so an empty component is unambiguous.', }, - vectors: [ + healthKitPlatformExclusiveResourceClaims: [ { - case: 'ascii', - system: 'https://study.example.org/fhir/identifiers/mobile-observation', - value: 'heart-rate-20260820-001', - input: - 'https://study.example.org/fhir/identifiers/mobile-observation|heart-rate-20260820-001', - fullUrl: 'urn:uuid:72a64652-0bad-517d-8a36-39e3b6adccac', + resourceType: 'VisionPrescription', + profile: + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-vision-prescription', + cardinality: 1, + otherProfilesAllowed: false, + requiredIdentifierRoles: ['source-record', 'source-output'], }, { - case: 'non-ascii', - system: 'https://例.example/識別子', - value: 'café-東京', - input: 'https://例.example/識別子|café-東京', - fullUrl: 'urn:uuid:31acad95-5e9a-5b0f-b5b7-4f4627825b6b', + resourceType: 'MedicationAdministration', + profile: + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-medication-dose-event', + cardinality: 1, + otherProfilesAllowed: false, + requiredIdentifierRoles: ['source-record', 'source-output'], }, { - case: 'composed-value', - system: - 'https://grovealliance.org/fhir/mobile/NamingSystem/grove-writer-record-id', - value: 'v1:com.withings.wiscale2|17348211', - input: - 'https://grovealliance.org/fhir/mobile/NamingSystem/grove-writer-record-id|v1:com.withings.wiscale2|17348211', - fullUrl: 'urn:uuid:68db0fd4-0146-59c9-86cf-934f00881095', + resourceType: 'MedicationStatement', + profile: + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-user-annotated-medication', + cardinality: 1, + otherProfilesAllowed: false, + requiredIdentifierRoles: [ + 'source-record', + 'source-output', + 'source-context', + ], + }, + ], + healthConnectPlatformExclusiveClaims: { + cardinality: 1, + profiles: [ + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-whole-blood-glucose', + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-capillary-blood-glucose', + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-serum-plasma-glucose', + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-interstitial-glucose', + ], + otherProfilesAllowed: false, + }, + sensorKitRecordingDocumentClaim: { + cardinality: 2, + profiles: [ + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-recording-document', + ], + otherProfilesAllowed: false, + requiredIdentifierRoles: [ + 'source-record', + 'source-output', + 'source-artifact', + ], + }, + providerRecordingDocumentClaim: { + cardinality: 2, + profiles: [ + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document', + ], + otherProfilesAllowed: false, + requiredIdentifierRoles: [ + 'source-record', + 'source-output', + 'source-artifact', + ], + }, + activeDeviceClaims: [ + { + id: 'mobile-recording-device', + resourceType: 'Device', + cardinality: 1, + profiles: [ + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-device', + ], + otherProfilesAllowed: false, + requiredIdentifierRoles: ['recording-device', 'device-snapshot'], + }, + { + id: 'mobile-application-device', + resourceType: 'Device', + cardinality: 1, + profiles: [ + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device', + ], + otherProfilesAllowed: false, + requiredIdentifierRoles: ['device-snapshot'], }, { - case: 'rejected-separator-in-system', - system: 'https://example.org/a|b', - value: 'x', - input: null, - fullUrl: null, - note: 'A vertical bar in the system would make the split ambiguous, so it is rejected. A bar in the value is ordinary, as the composed-value case shows.', + id: 'mobile-host-device', + resourceType: 'Device', + cardinality: 1, + profiles: [ + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device', + ], + otherProfilesAllowed: false, + requiredIdentifierRoles: ['device-snapshot'], + }, + { + id: 'healthkit-application-device', + resourceType: 'Device', + cardinality: 1, + profiles: [ + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-application-device', + ], + otherProfilesAllowed: false, + requiredIdentifierRoles: ['device-snapshot'], }, ], + activeQuestionnaireResponseClaim: { + resourceType: 'QuestionnaireResponse', + cardinality: 1, + profiles: [ + 'https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response', + ], + otherProfilesAllowed: false, + }, + adapterConversionProvenanceClaims: [ + { + adapter: 'healthkit', + profile: + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-conversion-provenance', + targetAdapterProfiles: [ + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-exercise-time', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-move-time', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-stand-hour', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-stand-time', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-atrial-fibrillation-burden', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-audiogram-panel', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-biological-sex', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-bladder-incontinence', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-bleeding-after-pregnancy', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-bleeding-during-pregnancy', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-blood-alcohol-content', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-blood-type', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-clinical-record-document', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-contraceptive-use', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-cycling-functional-threshold-power', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-date-of-birth', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-ecg-average-heart-rate-observation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-ecg-observation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-environmental-audio-exposure', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-environmental-audio-exposure-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-environmental-sound-reduction', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-fitzpatrick-skin-type', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-food-correlation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-forced-expiratory-volume-1', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-forced-vital-capacity', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-gad7-assessment', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-handwashing-session', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-headphone-audio-exposure', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-headphone-audio-exposure-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-heart-rate-recovery-one-minute', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-high-heart-rate-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-hypertension-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-infrequent-menstrual-cycles', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-inhaler-usage', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-insulin-delivery', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-irregular-heart-rhythm-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-irregular-menstrual-cycles', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-lactation-status', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-low-cardio-fitness-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-low-heart-rate-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-medication-dose-event', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-number-of-alcoholic-beverages', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-number-of-times-fallen', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-observation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-peak-expiratory-flow-rate', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-peripheral-perfusion-index', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-persistent-intermenstrual-bleeding', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-phq9-assessment', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-physical-effort', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-pregnancy-status', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-pregnancy-test-result', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-progesterone-test-result', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-prolonged-menstrual-periods', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-recording-document', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-running-ground-contact-time', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-running-stride-length', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-running-vertical-oscillation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-six-minute-walk-test-distance', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-sleep-apnea-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-sleeping-breathing-disturbances', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-stair-ascent-speed', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-stair-descent-speed', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-state-of-mind', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-swimming-stroke-count', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-abdominal-cramps', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-acne', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-appetite-changes', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-bloating', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-breast-pain', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-chest-tightness-or-pain', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-chills', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-constipation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-coughing', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-diarrhea', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-dizziness', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-dry-skin', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-fainting', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-fatigue', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-fever', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-generalized-body-ache', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-hair-loss', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-headache', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-heartburn', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-hot-flashes', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-loss-of-smell', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-loss-of-taste', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-lower-back-pain', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-memory-lapse', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-mood-changes', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-nausea', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-night-sweats', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-pelvic-pain', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-rapid-pounding-or-fluttering-heartbeat', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-runny-nose', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-shortness-of-breath', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-sinus-congestion', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-skipped-heartbeat', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-sleep-changes', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-sore-throat', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-vomiting', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-wheezing', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-time-in-daylight', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-toothbrushing-session', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-underwater-depth', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-user-annotated-medication', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-uv-exposure', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-vaginal-dryness', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-vision-prescription', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-waist-circumference', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-asymmetry', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-double-support', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-heart-rate-average', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-speed', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-steadiness', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-steadiness-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-step-length', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-water-temperature', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-wheelchair-use', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-workout-effort-score', + ], + rule: 'One Provenance in the active exchange graph targets every admitted HealthKit structured or byte-preserving output produced from its exact sole typed source-record Identifier, including child-only Observation, recording, clinical-document, vision, dose, and tracked-medication resources; every target is an internal UUID reference. An active conversion graph never encodes retraction by mutating output status. A separate retraction Bundle with retraction Provenance asserts source removal and carries no conversion Provenance.', + sourceIdentifierRole: 'source-record', + sourceIdentityKind: 'source-record', + }, + { + adapter: 'health-connect', + profile: + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-conversion-provenance', + targetAdapterProfiles: [ + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-observation', + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-whole-blood-glucose', + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-capillary-blood-glucose', + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-serum-plasma-glucose', + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-interstitial-glucose', + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-specimen', + ], + rule: 'One Provenance in the active exchange graph targets every Health Connect Observation and synthesized Specimen produced from its exact sole typed source-record Identifier; every target is an internal UUID reference. The Provenance directly declares only the Health Connect conversion profile; its inherited Mobile profile is not repeated. An active conversion graph never encodes retraction by mutating output status. A separate retraction Bundle with retraction Provenance asserts source removal and carries no conversion Provenance.', + sourceIdentifierRole: 'source-record', + sourceIdentityKind: 'source-record', + }, + { + adapter: 'providers', + profile: + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance', + targetAdapterProfiles: [ + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document', + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation', + 'https://grovealliance.org/fhir/oura/StructureDefinition/oura-observation', + 'https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation', + ], + rule: 'One Provenance in the active exchange graph targets every connected-provider Observation carrying its exact provider-owned adapter envelope and every Provider Recording Document carrying its exact sole typed source-record Identifier; the abstract ProvidersObservation parent is never a direct claim. Every target is an internal UUID reference. An active conversion graph never encodes retraction by mutating output status. A separate retraction Bundle with retraction Provenance asserts source removal and carries no conversion Provenance.', + sourceIdentifierRole: 'source-record', + sourceIdentityKind: 'provider-record', + }, + { + adapter: 'sensorkit', + profile: + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-conversion-provenance', + targetAdapterProfiles: [ + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-accelerometer-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-device-usage-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-ecg-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-keyboard-metrics-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-messages-usage-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-on-wrist-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-phone-usage-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-ppg-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-recording-document', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-sleep-session-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-visit-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-wrist-temperature-observation', + ], + rule: 'One Provenance in the active exchange graph targets every SensorKit structured and raw output carrying its exact sole typed source-record Identifier; every target is an internal UUID reference. An active conversion graph never encodes retraction by mutating output status. A separate retraction Bundle with retraction Provenance asserts source removal and carries no conversion Provenance.', + sourceIdentifierRole: 'source-record', + sourceIdentityKind: 'source-record', + }, + ], + sensorKitPlatformExclusiveClaims: { + cardinality: 1, + profiles: [ + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-accelerometer-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-device-usage-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-keyboard-metrics-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-messages-usage-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-on-wrist-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-phone-usage-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-ppg-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-sleep-session-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-visit-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-wrist-temperature-observation', + ], + otherProfilesAllowed: false, + }, + sensorKitHybridObservationClaims: { + cardinality: 2, + profiles: [ + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-ecg-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-ecg-observation', + ], + requiredCompanionProfile: + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-recording-document', + rule: 'The ECG Observation and exact native Recording Document are linked in one exchange graph, share the exact source-record Identifier, and are both targeted by one SensorKit conversion Provenance.', + }, + healthConnectSpecimenClaim: { + resourceType: 'Specimen', + cardinality: 1, + profile: + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-specimen', + otherProfilesAllowed: false, + requiredIdentifierRoles: ['source-record', 'source-output'], + }, +} as const + +export const groveProfileClaims: typeof groveProfileClaimsValue = deepFreeze( + groveProfileClaimsValue, +) + +const adapterSourceMarkerClaimsValue = [ + { + adapter: 'healthkit', + profiles: [ + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-exercise-time', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-move-time', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-stand-hour', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-apple-stand-time', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-atrial-fibrillation-burden', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-audiogram-panel', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-biological-sex', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-bladder-incontinence', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-bleeding-after-pregnancy', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-bleeding-during-pregnancy', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-blood-alcohol-content', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-blood-type', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-clinical-record-document', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-contraceptive-use', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-cycling-functional-threshold-power', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-date-of-birth', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-ecg-average-heart-rate-observation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-ecg-observation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-environmental-audio-exposure', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-environmental-audio-exposure-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-environmental-sound-reduction', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-fitzpatrick-skin-type', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-food-correlation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-forced-expiratory-volume-1', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-forced-vital-capacity', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-gad7-assessment', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-handwashing-session', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-headphone-audio-exposure', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-headphone-audio-exposure-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-heart-rate-recovery-one-minute', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-high-heart-rate-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-hypertension-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-infrequent-menstrual-cycles', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-inhaler-usage', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-insulin-delivery', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-irregular-heart-rhythm-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-irregular-menstrual-cycles', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-lactation-status', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-low-cardio-fitness-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-low-heart-rate-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-medication-dose-event', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-number-of-alcoholic-beverages', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-number-of-times-fallen', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-observation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-peak-expiratory-flow-rate', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-peripheral-perfusion-index', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-persistent-intermenstrual-bleeding', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-phq9-assessment', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-physical-effort', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-pregnancy-status', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-pregnancy-test-result', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-progesterone-test-result', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-prolonged-menstrual-periods', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-recording-document', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-running-ground-contact-time', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-running-stride-length', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-running-vertical-oscillation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-six-minute-walk-test-distance', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-sleep-apnea-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-sleeping-breathing-disturbances', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-stair-ascent-speed', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-stair-descent-speed', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-state-of-mind', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-swimming-stroke-count', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-abdominal-cramps', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-acne', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-appetite-changes', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-bloating', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-breast-pain', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-chest-tightness-or-pain', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-chills', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-constipation', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-coughing', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-diarrhea', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-dizziness', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-dry-skin', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-fainting', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-fatigue', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-fever', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-generalized-body-ache', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-hair-loss', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-headache', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-heartburn', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-hot-flashes', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-loss-of-smell', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-loss-of-taste', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-lower-back-pain', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-memory-lapse', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-mood-changes', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-nausea', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-night-sweats', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-pelvic-pain', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-rapid-pounding-or-fluttering-heartbeat', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-runny-nose', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-shortness-of-breath', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-sinus-congestion', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-skipped-heartbeat', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-sleep-changes', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-sore-throat', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-vomiting', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-symptom-wheezing', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-time-in-daylight', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-toothbrushing-session', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-underwater-depth', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-user-annotated-medication', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-uv-exposure', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-vaginal-dryness', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-vision-prescription', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-waist-circumference', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-asymmetry', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-double-support', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-heart-rate-average', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-speed', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-steadiness', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-steadiness-notification', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-walking-step-length', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-water-temperature', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-wheelchair-use', + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-workout-effort-score', + ], + markers: [ + { + kind: 'extension-url', + url: 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-source-type-extension', + resourceTypes: [ + 'Observation', + 'DocumentReference', + 'VisionPrescription', + 'MedicationAdministration', + 'MedicationStatement', + ], + }, + ], + }, + { + adapter: 'health-connect', + profiles: [ + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-observation', + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-whole-blood-glucose', + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-capillary-blood-glucose', + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-serum-plasma-glucose', + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-interstitial-glucose', + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-specimen', + ], + markers: [ + { + kind: 'extension-url', + url: 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-record-type', + resourceTypes: ['Observation'], + }, + ], + }, + { + adapter: 'providers', + profiles: [ + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document', + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation', + 'https://grovealliance.org/fhir/oura/StructureDefinition/oura-observation', + 'https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation', + ], + markers: [ + { + kind: 'extension-url', + url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type', + resourceTypes: ['Observation', 'DocumentReference'], + }, + { + kind: 'extension-url', + url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider', + resourceTypes: ['Observation', 'DocumentReference'], + }, + ], + }, + { + adapter: 'sensorkit', + profiles: [ + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-accelerometer-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-device-usage-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-ecg-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-keyboard-metrics-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-messages-usage-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-on-wrist-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-phone-usage-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-ppg-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-recording-document', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-sleep-session-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-visit-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-wrist-temperature-observation', + ], + markers: [ + { + kind: 'extension-url', + url: 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-source-type', + resourceTypes: ['Observation', 'DocumentReference'], + }, + ], + }, +] as const + +export const adapterSourceMarkerClaims: typeof adapterSourceMarkerClaimsValue = + deepFreeze(adapterSourceMarkerClaimsValue) + +const healthConnectDataOriginApplicationValue = { + sourceField: 'Metadata.dataOrigin.packageName', + r4Element: 'Provenance.entity.agent.who', + referenceType: 'Device', + referenceMode: 'identifier-only', + identifierSystem: + 'https://grovealliance.org/fhir/health-connect/NamingSystem/android-package-name', + identifierValue: 'exact non-blank Metadata.dataOrigin.packageName', + literalReferenceAllowed: false, + eventBundleEntryRequired: false, + profileClaimRequired: false, + rule: 'Carry one complete absolute-system Identifier on the typed logical Device Reference. The package name identifies an application product, not an installation, host, account, person, or physical recording device.', } as const -export const groveFhirExchangeIdentity: typeof groveFhirExchangeIdentityValue = - deepFreeze(groveFhirExchangeIdentityValue) +export const healthConnectDataOriginApplication: typeof healthConnectDataOriginApplicationValue = + deepFreeze(healthConnectDataOriginApplicationValue) + +const groveRecordingFormatRegistryValue = { + codeSystem: + 'https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format', + valueSet: + 'https://grovealliance.org/fhir/sensor/ValueSet/grove-recording-format', + version: '0.6.0', + formats: { + 'heart-rate-samples': { + title: 'Heart Rate Samples', + contentType: 'text/csv', + status: 'active', + }, + 'triaxial-acceleration-samples': { + title: 'Triaxial Acceleration Samples', + contentType: 'text/csv', + status: 'active', + }, + 'ambient-light-samples': { + title: 'Ambient Light Samples', + contentType: 'text/csv', + status: 'active', + }, + 'ambient-pressure-samples': { + title: 'Ambient Pressure Samples', + contentType: 'text/csv', + status: 'active', + }, + 'pedometer-samples': { + title: 'Pedometer Samples', + contentType: 'text/csv', + status: 'active', + }, + 'wrist-temperature-samples': { + title: 'Wrist Temperature Samples', + contentType: 'text/csv', + status: 'active', + }, + 'triaxial-rotation-samples': { + title: 'Triaxial Rotation Samples', + contentType: 'text/csv', + status: 'active', + }, + 'odometer-samples': { + title: 'Odometer Samples', + contentType: 'text/csv', + status: 'active', + }, + 'beat-interval-series': { + title: 'Beat Interval Series', + contentType: 'text/csv', + status: 'active', + }, + 'location-track-samples': { + title: 'Location Track Samples', + contentType: 'text/csv', + status: 'active', + }, + 'fhir-collection-bundle': { + title: 'FHIR R4 Collection Bundle', + contentType: 'application/fhir+json', + status: 'active', + }, + 'fhir-r4-resource': { + title: 'FHIR R4 Resource', + contentType: 'application/fhir+json', + status: 'active', + }, + 'clinical-document': { + title: 'Clinical Document', + contentType: 'application/hl7-cda+xml', + status: 'active', + }, + 'native-recording': { + title: 'Native Recording', + contentType: 'application/vnd.grovealliance.native+json', + status: 'active', + }, + 'provider-recording': { + title: 'Provider Recording', + contentType: 'application/vnd.grovealliance.provider+json', + status: 'active', + }, + 'photoplethysmogram-samples': { + title: 'Photoplethysmogram Samples', + contentType: 'application/vnd.grovealliance.ppg', + status: 'active', + }, + }, +} as const + +export const groveRecordingFormatRegistry: typeof groveRecordingFormatRegistryValue = + deepFreeze(groveRecordingFormatRegistryValue) const sharedMobileMeasurementCatalogValue = { 'active-energy': { @@ -271,6 +2432,7 @@ const sharedMobileMeasurementCatalogValue = { }, }, effective: 'dateTime', + forbiddenElements: ['specimen'], }, 'blood-pressure': { id: 'blood-pressure', @@ -283,6 +2445,11 @@ const sharedMobileMeasurementCatalogValue = { system: 'http://loinc.org', code: '85354-9', }, + category: { + system: 'http://terminology.hl7.org/CodeSystem/observation-category', + code: 'vital-signs', + display: 'Vital Signs', + }, valueKind: 'components', components: [ { @@ -340,6 +2507,17 @@ const sharedMobileMeasurementCatalogValue = { unit: '%', example: 22, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + maximum: { + value: 100, + inclusive: true, + }, + integerOnly: false, + }, }, effective: 'dateTime', }, @@ -354,6 +2532,11 @@ const sharedMobileMeasurementCatalogValue = { system: 'http://loinc.org', code: '8302-2', }, + category: { + system: 'http://terminology.hl7.org/CodeSystem/observation-category', + code: 'vital-signs', + display: 'Vital Signs', + }, valueKind: 'quantity', quantity: { system: 'http://unitsofmeasure.org', @@ -377,6 +2560,11 @@ const sharedMobileMeasurementCatalogValue = { system: 'http://loinc.org', code: '8310-5', }, + category: { + system: 'http://terminology.hl7.org/CodeSystem/observation-category', + code: 'vital-signs', + display: 'Vital Signs', + }, valueKind: 'quantity', quantity: { system: 'http://unitsofmeasure.org', @@ -424,6 +2612,11 @@ const sharedMobileMeasurementCatalogValue = { system: 'http://loinc.org', code: '29463-7', }, + category: { + system: 'http://terminology.hl7.org/CodeSystem/observation-category', + code: 'vital-signs', + display: 'Vital Signs', + }, valueKind: 'quantity', quantity: { system: 'http://unitsofmeasure.org', @@ -1703,6 +3896,13 @@ const sharedMobileMeasurementCatalogValue = { unit: 'flights', example: 12, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + integerOnly: true, + }, }, effective: 'Period', }, @@ -1741,6 +3941,11 @@ const sharedMobileMeasurementCatalogValue = { system: 'http://loinc.org', code: '8867-4', }, + category: { + system: 'http://terminology.hl7.org/CodeSystem/observation-category', + code: 'vital-signs', + display: 'Vital Signs', + }, valueKind: 'quantity', quantity: { system: 'http://unitsofmeasure.org', @@ -1751,7 +3956,7 @@ const sharedMobileMeasurementCatalogValue = { s: -1, }, }, - effective: 'dateTime', + effective: 'dateTime-or-Period', }, 'heart-rate-variability-rmssd': { id: 'heart-rate-variability-rmssd', @@ -2055,6 +4260,11 @@ const sharedMobileMeasurementCatalogValue = { system: 'http://loinc.org', code: '2708-6', }, + category: { + system: 'http://terminology.hl7.org/CodeSystem/observation-category', + code: 'vital-signs', + display: 'Vital Signs', + }, valueKind: 'quantity', quantity: { system: 'http://unitsofmeasure.org', @@ -2062,6 +4272,17 @@ const sharedMobileMeasurementCatalogValue = { unit: '%', example: 97, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + maximum: { + value: 100, + inclusive: true, + }, + integerOnly: false, + }, }, effective: 'dateTime', }, @@ -2084,6 +4305,17 @@ const sharedMobileMeasurementCatalogValue = { unit: '%', example: 96, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + maximum: { + value: 100, + inclusive: true, + }, + integerOnly: false, + }, }, method: { code: 'daily-mean', @@ -2159,6 +4391,11 @@ const sharedMobileMeasurementCatalogValue = { system: 'http://loinc.org', code: '9279-1', }, + category: { + system: 'http://terminology.hl7.org/CodeSystem/observation-category', + code: 'vital-signs', + display: 'Vital Signs', + }, valueKind: 'quantity', quantity: { system: 'http://unitsofmeasure.org', @@ -2199,17 +4436,27 @@ const sharedMobileMeasurementCatalogValue = { 'resting-heart-rate': { id: 'resting-heart-rate', profile: 'grove-mobile-resting-heart-rate', - standardProfile: null, + standardProfile: 'http://hl7.org/fhir/StructureDefinition/heartrate', title: 'Resting Heart Rate', description: - 'A windowed estimate of the heart rate while at rest, normalized to UCUM beats per minute over the exact estimation window. It is semantically distinct from the shared point heart-rate measurement and is implemented by the phase-2 aggregate design. LOINC 40443-4 is deliberately not used: it denotes a heart rate measured at rest, which R4 classifies as a vital sign, whereas this result is a derived daily estimate that must not be surfaced as a measured vital sign.', + 'One instantaneous heart-rate measurement explicitly classified by its source as taken at rest, normalized to UCUM beats per minute. This is not an aggregate result: any daily or windowed estimate uses the separate resting-heart-rate-daily-average profile.', code: { - system: - 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', - code: 'resting-heart-rate', - display: 'Resting heart rate', - definition: - 'The estimated heart rate at rest across the exact Observation effective Period.', + system: 'http://loinc.org', + code: '40443-4', + display: 'Heart rate --resting', + }, + requiredCodings: [ + { + slice: 'heartRate', + system: 'http://loinc.org', + code: '8867-4', + display: 'Heart rate', + }, + ], + category: { + system: 'http://terminology.hl7.org/CodeSystem/observation-category', + code: 'vital-signs', + display: 'Vital Signs', }, valueKind: 'quantity', quantity: { @@ -2221,11 +4468,7 @@ const sharedMobileMeasurementCatalogValue = { s: -1, }, }, - method: { - code: 'daily-mean', - display: 'Daily mean', - }, - effective: 'Period', + effective: 'dateTime', }, 'sexual-activity': { id: 'sexual-activity', @@ -2455,14 +4698,17 @@ const sharedMobileMeasurementCatalogValue = { unit: 'steps', example: 8432, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + integerOnly: true, + }, unitMustSupport: true, }, effective: 'Period', - obeys: [ - 'grove-step-count-result-1', - 'grove-step-count-period-1', - 'grove-step-count-value-1', - ], + obeys: ['grove-step-count-result-1', 'grove-step-count-period-1'], }, 'vo2-max': { id: 'vo2-max', @@ -2512,6 +4758,13 @@ const sharedMobileMeasurementCatalogValue = { unit: 'pushes', example: 1240, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + integerOnly: true, + }, }, effective: 'Period', }, @@ -2925,3 +5178,11 @@ export const mobileEffectiveCanonicalizationVectors: typeof mobileEffectiveCanon export type SharedMobileMeasurementKind = keyof typeof sharedMobileMeasurementCatalog + +export type GroveRecordingFormat = + keyof typeof groveRecordingFormatRegistry.formats + +export type ProviderRecordingFormat = Extract< + GroveRecordingFormat, + 'provider-recording' +> diff --git a/packages/fhir/src/mobile/types.ts b/packages/fhir/src/mobile/types.ts index 3059f49c..ee483b42 100644 --- a/packages/fhir/src/mobile/types.ts +++ b/packages/fhir/src/mobile/types.ts @@ -17,6 +17,9 @@ import type { type SharedCatalog = typeof import('./measurement-catalog.generated.js').sharedMobileMeasurementCatalog +type OpaqueIdentityDefinition = + (typeof import('./measurement-catalog.generated.js').groveExchangeProtocol)['opaqueIdentity']['identityKinds'][number] + /** Kinds whose catalog definition declares the given value kind and effective. */ type MeasurementKindsWhere< ValueKind extends string, @@ -29,14 +32,20 @@ type MeasurementKindsWhere< : never }[SharedMobileMeasurementKind] +/** Kinds whose profile explicitly admits either an instant or bounded Period. */ +export type ChoiceQuantityMeasurementKind = MeasurementKindsWhere< + 'quantity', + 'dateTime-or-Period' +> + export type InstantQuantityMeasurementKind = MeasurementKindsWhere< 'quantity', - 'dateTime' + 'dateTime' | 'dateTime-or-Period' > export type PeriodQuantityMeasurementKind = MeasurementKindsWhere< 'quantity', - 'Period' + 'Period' | 'dateTime-or-Period' > export type InstantCodedMeasurementKind = MeasurementKindsWhere< @@ -79,6 +88,11 @@ export type PeriodQuantityMeasurement = { } }[PeriodQuantityMeasurementKind] +export type ChoiceQuantityMeasurement = Extract< + InstantQuantityMeasurement | PeriodQuantityMeasurement, + { readonly kind: ChoiceQuantityMeasurementKind } +> + export type InstantCodedMeasurement = { readonly [Kind in InstantCodedMeasurementKind]: { readonly kind: Kind @@ -124,6 +138,38 @@ export interface SleepStageMeasurement { export interface CompleteIdentifierInput { readonly system: AbsoluteUri readonly value: string + /** Grove role carried in Identifier.type for deployment-owned v2 identities. */ + readonly role?: GroveIdentifierRole +} + +/** Closed roles used to distinguish deployment-owned v2 identifier key spaces. */ +export type GroveIdentifierRole = + OpaqueIdentityDefinition['identifierRole'] | 'entry-node' | 'event' + +/** Closed HMAC domains from the Grove 0.6 exchange protocol. */ +export type GroveOpaqueIdentityKind = OpaqueIdentityDefinition['kind'] + +export type GroveOpaqueIdentitySystems = Readonly< + Record +> + +/** + * Deployment-owned identity material. + * + * Every opaque system names exactly one deployment, identity kind, key id, and key epoch. The + * secret is used only while a graph is built and is never retained in, or emitted with, FHIR. + */ +export interface DeploymentIdentityInput { + readonly opaqueIdentifierSystems: GroveOpaqueIdentitySystems + readonly eventIdentifierSystem: AbsoluteUri + readonly entryNodeIdentifierSystem: AbsoluteUri + readonly keyId: string + /** Canonical positive decimal string; represented lexically to avoid JavaScript integer loss. */ + readonly keyEpoch: string + /** Canonical unpadded base64url key material containing at least 32 bytes. */ + readonly secretBase64Url: string + /** Canonical lowercase RFC 4122 UUID (versions 1 through 5). */ + readonly producerInstance: string } /** Caller-owned business identity with an optional repository-assigned Resource.id. */ @@ -147,14 +193,33 @@ export interface IdentifiedEntryIdentityInput extends ResourceIdentityInput { } export interface ApplicationDeviceInput { - readonly identity: ResourceIdentityInput + /** Deployment-governed token for this source application/build; never emitted in clear. */ + readonly sourceDeviceToken: string + readonly id?: FhirId readonly name: string readonly version?: string + readonly build?: string + readonly manufacturer?: string + /** Optional immutable snapshot of the hardware/OS hosting this application. */ + readonly host?: HostDeviceInput +} + +export interface HostDeviceInput { + /** Event-time source token for this host snapshot; never emitted in clear. */ + readonly sourceDeviceToken: string + readonly id?: FhirId + readonly name?: string readonly manufacturer?: string + readonly modelNumber?: string + readonly operatingSystemVersion: string } interface RecordingDeviceBaseInput { - readonly identity: ResourceIdentityInput + /** Stable token for one physical unit; model, manufacturer, or subject are not substitutes. */ + readonly stableUnitToken: string + /** Complete logical subject identity used only in the HMAC preimage. */ + readonly subjectIdentifier: CompleteIdentifierInput + readonly id?: FhirId readonly name?: string readonly manufacturer?: string readonly modelNumber?: string diff --git a/packages/fhir/src/providers/builder.ts b/packages/fhir/src/providers/builder.ts index b795dd0a..69f04593 100644 --- a/packages/fhir/src/providers/builder.ts +++ b/packages/fhir/src/providers/builder.ts @@ -6,53 +6,55 @@ // SPDX-License-Identifier: MIT // -import { - groveProviderProfileCanonicals, - providerAdapterCatalog, -} from './contract.generated.js' +import { providerAdapterCatalog } from './contract.generated.js' import { assemblerAgent, coding, concept, + deduplicateIdentifiedEntries, + governedSourceIdentifier, identifiedEntry, identifier, makeApplicationDevice, + makeHostDevice, makeRecordingDevice, provenanceActivity, resourceId, sourceEntityAgent, } from './graph.js' -import { deriveProviderIdentities } from './identity.js' +import { + deriveApplicationEntryIdentity, + deriveDeviceSnapshotEntryIdentity, + deriveProviderIdentities, + deriveRecordingDeviceEntryIdentity, + deriveWriterRecordIdentifier, + type RecordingDeviceGraphIdentity, +} from './identity.js' +import { + providerMeasurementDefinition, + providerMeasurementProfile, + providerObservationProfile, + type ProviderMeasurementDefinition, +} from './measurement-definition.js' import { EXTENSIONS, PROFILES, SYSTEMS } from './profiles.js' import { - providerOutputDiscriminator, + providerOutputCoordinates, + type ProviderOutputCoordinates, parseProviderMeasurementBundleInput, } from './provider.js' import type { ProviderMeasurementBundleInput } from './types.js' import { issues, ok, type Result, type UrnUuid } from '../core/index.js' import { createEntryIdentity } from '../mobile/identity.js' -import { sharedMobileMeasurementCatalog } from '../mobile/measurement-catalog.generated.js' import type { CompleteIdentifierInput, IdentifiedEntryIdentityInput, - ResourceIdentityInput, } from '../mobile/types.js' import { - parseCollectionBundle, + parseGroveMobileExchangeBundle, type CodeableConcept, - type CollectionBundle, + type GroveMobileExchangeBundle, } from '../r4/index.js' -const VITAL_SIGNS = new Set([ - 'blood-pressure', - 'body-height', - 'body-temperature', - 'body-weight', - 'heart-rate', - 'oxygen-saturation', - 'respiratory-rate', -]) - type ConnectedMeasurement = ProviderMeasurementBundleInput['measurements'][number] @@ -72,31 +74,74 @@ const quantity = ( interface ResolvedGraphIdentities { readonly sourceRecord: CompleteIdentifierInput - readonly exchange: CompleteIdentifierInput + readonly event: CompleteIdentifierInput readonly observations: readonly IdentifiedEntryIdentityInput[] readonly provenance: IdentifiedEntryIdentityInput readonly application: IdentifiedEntryIdentityInput + readonly applicationHost?: IdentifiedEntryIdentityInput readonly dataOrigin: IdentifiedEntryIdentityInput + readonly dataOriginHost?: IdentifiedEntryIdentityInput readonly gatewayReference?: UrnUuid readonly distinctGatewayApplication?: IdentifiedEntryIdentityInput - readonly recordingDevice?: IdentifiedEntryIdentityInput + readonly distinctGatewayHost?: IdentifiedEntryIdentityInput + readonly recordingDevice?: RecordingDeviceGraphIdentity + readonly writerRecord?: CompleteIdentifierInput } -const categoryFor = (kind: ConnectedMeasurement['kind']) => { - if (VITAL_SIGNS.has(kind)) { - return [concept(SYSTEMS.observationCategory, 'vital-signs', 'Vital Signs')] +type ProviderGraphEntry = GroveMobileExchangeBundle['entry'][number] +type ProviderGraphResource = Parameters[1] + +const definitionFor = ( + input: ProviderMeasurementBundleInput, + kind: ConnectedMeasurement['kind'], +): ProviderMeasurementDefinition => { + const definition = providerMeasurementDefinition( + input.source.adapter.provider, + kind, + ) + if (definition === undefined) { + throw new Error( + `Validated Provider input has no definition for ${input.source.adapter.provider}/${kind}.`, + ) } - return [concept(SYSTEMS.observationCategory, 'activity', 'Activity')] + return definition } -const profileFor = (kind: ConnectedMeasurement['kind']): readonly string[] => { - const definition = sharedMobileMeasurementCatalog[kind] +const categoryFor = (definition: ProviderMeasurementDefinition) => { + if (definition.category === undefined) return undefined return [ - groveProviderProfileCanonicals[definition.profile], - PROFILES.providerObservation, + concept( + definition.category.system, + definition.category.code, + definition.category.display, + ), ] } +const profileFor = ( + input: ProviderMeasurementBundleInput, + kind: ConnectedMeasurement['kind'], +): readonly string[] => { + const semanticProfile = providerMeasurementProfile( + input.source.adapter.provider, + kind, + ) + if (semanticProfile === undefined) { + throw new Error( + `Validated Provider input has no semantic profile for ${input.source.adapter.provider}/${kind}.`, + ) + } + const adapterProfile = providerObservationProfile( + input.source.adapter.provider, + ) + if (adapterProfile === undefined) { + throw new Error( + `Validated Provider input has no adapter profile for ${input.source.adapter.provider}.`, + ) + } + return [semanticProfile, adapterProfile] +} + // A measurement with a methodChoice states its aggregation per adapter source element, // because the same shared meaning is a daily mean for one provider and a session // statistic for another. @@ -122,15 +167,15 @@ const aggregationMethodFor = ( input: ProviderMeasurementBundleInput, kind: ConnectedMeasurement['kind'], ): CodeableConcept | undefined => { - const definition = sharedMobileMeasurementCatalog[kind] - if ('method' in definition) { + const definition = definitionFor(input, kind) + if (definition.method !== undefined) { return concept( SYSTEMS.groveAggregationMethod, definition.method.code, definition.method.display, ) } - if (!('methodChoice' in definition)) { + if (definition.methodChoice === undefined) { return undefined } const declared = declaredAggregationMethods.get( @@ -150,7 +195,7 @@ const missingAggregationMethod = ( input: ProviderMeasurementBundleInput, kind: ConnectedMeasurement['kind'], ): boolean => - 'methodChoice' in sharedMobileMeasurementCatalog[kind] && + definitionFor(input, kind).methodChoice !== undefined && aggregationMethodFor(input, kind) === undefined const effectiveFor = (measurement: ConnectedMeasurement) => @@ -163,11 +208,19 @@ const effectiveFor = (measurement: ConnectedMeasurement) => }, } -const resultFor = (measurement: ConnectedMeasurement) => { +const resultFor = ( + input: ProviderMeasurementBundleInput, + measurement: ConnectedMeasurement, +) => { if (measurement.kind === 'blood-pressure') { - const definition = sharedMobileMeasurementCatalog['blood-pressure'] - const systolic = definition.components[0] - const diastolic = definition.components[1] + const definition = definitionFor(input, 'blood-pressure') + const systolic = definition.components?.[0] + const diastolic = definition.components?.[1] + if (systolic?.quantity === undefined || diastolic?.quantity === undefined) { + throw new Error( + 'The validated blood-pressure definition has no two-component result.', + ) + } return { component: [ { @@ -182,7 +235,7 @@ const resultFor = (measurement: ConnectedMeasurement) => { } } - const definition = sharedMobileMeasurementCatalog[measurement.kind] + const definition = definitionFor(input, measurement.kind) if (typeof measurement.value === 'string') { const { resultCodeSystem } = definition as { @@ -211,8 +264,8 @@ const makeObservation = ( identities: ResolvedGraphIdentities, method: CodeableConcept | undefined, ) => { - const definition = sharedMobileMeasurementCatalog[measurement.kind] - const category = categoryFor(measurement.kind) + const definition = definitionFor(input, measurement.kind) + const category = categoryFor(definition) const extensions = [ { url: EXTENSIONS.provider, @@ -243,40 +296,59 @@ const makeObservation = ( ]), ...(input.researchStudyReferences ?? []).map((reference) => ({ url: EXTENSIONS.researchStudy, - valueReference: { reference }, + valueReference: { + type: reference.type, + identifier: { + system: reference.identifier.system, + value: reference.identifier.value, + }, + }, })), ] return { resourceType: 'Observation' as const, ...resourceId(observationIdentity), - meta: { profile: profileFor(measurement.kind) }, - extension: extensions, - // A one-to-one conversion identifies its Observation by the source record, so the entry - // identity is that same pair; listing it twice would repeat one identifier on the resource. + meta: { profile: profileFor(input, measurement.kind) }, + extension: [ + ...extensions, + ...(input.source.writerRecord?.version === undefined ? + [] + : [ + { + url: EXTENSIONS.writerRecordVersion, + valueString: input.source.writerRecord.version, + }, + ]), + ], identifier: [ identifier(identities.sourceRecord), - ...(( - observationIdentity.identifier.system === - identities.sourceRecord.system && - observationIdentity.identifier.value === identities.sourceRecord.value - ) ? + identifier(observationIdentity.identifier), + ...(identities.writerRecord === undefined ? + [] + : [identifier(identities.writerRecord)]), + ...(input.nativeIdentifierDisclosure === undefined ? [] - : [identifier(observationIdentity.identifier)]), + : [governedSourceIdentifier(input.nativeIdentifierDisclosure)]), ], status: 'final' as const, - category, + ...(category === undefined ? {} : { category }), code: { coding: [coding(definition.code.system, definition.code.code)], }, - subject: { reference: input.subject }, + subject: { + type: input.subject.type, + identifier: { + system: input.subject.identifier.system, + value: input.subject.identifier.value, + }, + }, ...effectiveFor(measurement), - issued: input.issued, - ...resultFor(measurement), + ...resultFor(input, measurement), ...(method === undefined ? {} : { method }), ...(input.source.recordingDevice === undefined ? {} - : { device: { reference: identities.recordingDevice?.fullUrl } }), + : { device: { reference: identities.recordingDevice?.snapshot.fullUrl } }), } } @@ -287,10 +359,13 @@ const makeProvenance = ( return { resourceType: 'Provenance' as const, ...resourceId(identities.provenance), - meta: { profile: [PROFILES.providerConversionProvenance] }, + meta: { + profile: [PROFILES.providerConversionProvenance], + }, target: identities.observations.map(({ fullUrl }) => ({ reference: fullUrl, })), + occurredDateTime: input.occurred, recorded: input.recorded, activity: provenanceActivity(), agent: [assemblerAgent(identities.application.fullUrl)], @@ -306,91 +381,104 @@ const makeProvenance = ( } } -const optionalEntryIdentity = ( - input: ResourceIdentityInput | undefined, -): Result => +const optionalRecordingDeviceIdentity = ( + input: ProviderMeasurementBundleInput['source']['recordingDevice'], + deployment: ProviderMeasurementBundleInput['deploymentIdentity'], + event: CompleteIdentifierInput, + adapterId: string, +): Result => input === undefined ? ok(undefined) - : createEntryIdentity(input.identifier, input.id) + : deriveRecordingDeviceEntryIdentity(deployment, event, adapterId, input) type ResolvedGatewayIdentity = Pick< ResolvedGraphIdentities, - 'distinctGatewayApplication' | 'gatewayReference' + 'distinctGatewayApplication' | 'distinctGatewayHost' | 'gatewayReference' > +const resolveHostIdentity = ( + application: ProviderMeasurementBundleInput['application'], + deployment: ProviderMeasurementBundleInput['deploymentIdentity'], + event: CompleteIdentifierInput, +): Result => + application.host === undefined ? + ok(undefined) + : deriveDeviceSnapshotEntryIdentity( + deployment, + event, + application.host.sourceDeviceToken, + 'host', + application.host.id, + ) + const resolveGatewayIdentity = ( input: ProviderMeasurementBundleInput['gatewayApplication'], converter: IdentifiedEntryIdentityInput, + deployment: ProviderMeasurementBundleInput['deploymentIdentity'], + event: CompleteIdentifierInput, ): Result => { if (input === undefined) return ok({}) if (input.kind === 'converter-application') { return ok({ gatewayReference: converter.fullUrl }) } - const application = createEntryIdentity( - input.application.identity.identifier, - input.application.identity.id, + const application = deriveApplicationEntryIdentity( + deployment, + event, + input.application, ) if (!application.ok) return application + const host = resolveHostIdentity(input.application, deployment, event) + if (!host.ok) return host return ok({ gatewayReference: application.value.fullUrl, distinctGatewayApplication: application.value, + ...(host.value === undefined ? {} : { distinctGatewayHost: host.value }), }) } -const resolveGraphIdentities = ( +const resolveProviderOutputCoordinates = ( input: ProviderMeasurementBundleInput, -): Result => { - const outputDiscriminators = input.measurements.map(({ kind }) => - providerOutputDiscriminator( +): Result => { + for (const [index, measurement] of input.measurements.entries()) { + if (missingAggregationMethod(input, measurement.kind)) { + return issues([ + { + severity: 'error', + code: 'unsupported-measurement', + path: ['measurements', index], + message: + `${input.source.adapter.provider}/${input.source.sourceType} declares no aggregation ` + + `for ${measurement.kind}, so its Observation would omit the method its profile requires.`, + }, + ]) + } + } + const outputCoordinates = input.measurements.map(({ kind }) => + providerOutputCoordinates( input.source.adapter.provider, input.source.sourceType, kind, ), ) - for (const [index, measurement] of input.measurements.entries()) { - if (!missingAggregationMethod(input, measurement.kind)) { - continue - } - return issues([ - { - severity: 'error', - code: 'unsupported-measurement', - path: ['measurements', index], - message: - `${input.source.adapter.provider}/${input.source.sourceType} declares no aggregation ` + - `for ${measurement.kind}, so its Observation would omit the method its profile requires.`, - }, - ]) - } - if (outputDiscriminators.some((value) => value === undefined)) { + if (outputCoordinates.some((coordinates) => coordinates === undefined)) { return issues([ { severity: 'error', code: 'unsupported-measurement', path: ['measurements'], - message: 'No catalog-owned Provider output discriminator exists.', + message: 'No catalog-owned Provider output role exists.', }, ]) } - const connected = deriveProviderIdentities({ - provider: input.source.adapter.provider, - providerAccountIdentifier: input.source.providerAccountIdentifier, - sourceType: input.source.sourceType, - sourceNativeId: input.source.sourceNativeId, - outputDiscriminators: outputDiscriminators as [string, ...string[]], - eventSequence: input.eventSequence, - graphIdentifierSystem: input.graphIdentifierSystem, - }) - if (!connected.ok) return connected + return ok(outputCoordinates as readonly ProviderOutputCoordinates[]) +} - // A one-to-one conversion mints no output identity, so its single Observation is identified by - // the source record itself. - const outputIdentities = - connected.value.outputs.length > 0 ? - connected.value.outputs - : [connected.value.sourceRecord] +const resolveObservationIdentities = ( + input: ProviderMeasurementBundleInput, + outputs: readonly CompleteIdentifierInput[], +): Result => { const observations: IdentifiedEntryIdentityInput[] = [] - for (const [index, output] of outputIdentities.entries()) { + for (const [index, output] of outputs.entries()) { const measurement = input.measurements[index] if (measurement === undefined) { return issues([ @@ -409,86 +497,120 @@ const resolveGraphIdentities = ( if (!observation.ok) return observation observations.push(observation.value) } + return ok(observations) +} + +const resolveGraphIdentities = ( + input: ProviderMeasurementBundleInput, +): Result => { + const outputCoordinates = resolveProviderOutputCoordinates(input) + if (!outputCoordinates.ok) return outputCoordinates + const connected = deriveProviderIdentities({ + provider: input.source.adapter.provider, + providerScopeIdentifier: input.source.providerScopeIdentifier, + sourceType: input.source.sourceType, + sourceNativeId: input.source.sourceNativeId, + outputs: outputCoordinates.value.map( + ({ outputRole, outputDiscriminator }) => ({ + kind: 'provider-output' as const, + outputRole, + outputDiscriminator, + }), + ), + eventSequence: input.eventSequence, + deployment: input.deploymentIdentity, + }) + if (!connected.ok) return connected + + const observations = resolveObservationIdentities( + input, + connected.value.outputs, + ) + if (!observations.ok) return observations const provenance = createEntryIdentity( - connected.value.conversion, + connected.value.provenanceNode, input.repositoryIds?.provenance, ) if (!provenance.ok) return provenance - const application = createEntryIdentity( - input.application.identity.identifier, - input.application.identity.id, + const application = deriveApplicationEntryIdentity( + input.deploymentIdentity, + connected.value.event, + input.application, ) if (!application.ok) return application + const applicationHost = resolveHostIdentity( + input.application, + input.deploymentIdentity, + connected.value.event, + ) + if (!applicationHost.ok) return applicationHost const gateway = resolveGatewayIdentity( input.gatewayApplication, application.value, + input.deploymentIdentity, + connected.value.event, ) if (!gateway.ok) return gateway - const dataOrigin = createEntryIdentity( - input.source.dataOrigin.identity.identifier, - input.source.dataOrigin.identity.id, + const dataOrigin = deriveApplicationEntryIdentity( + input.deploymentIdentity, + connected.value.event, + input.source.dataOrigin, ) if (!dataOrigin.ok) return dataOrigin + const dataOriginHost = resolveHostIdentity( + input.source.dataOrigin, + input.deploymentIdentity, + connected.value.event, + ) + if (!dataOriginHost.ok) return dataOriginHost - const recordingDevice = optionalEntryIdentity( - input.source.recordingDevice?.identity, + const recordingDevice = optionalRecordingDeviceIdentity( + input.source.recordingDevice, + input.deploymentIdentity, + connected.value.event, + input.source.adapter.provider, ) if (!recordingDevice.ok) return recordingDevice + const writerRecord = + input.source.writerRecord === undefined ? + undefined + : deriveWriterRecordIdentifier( + input.deploymentIdentity, + input.source.writerRecord.applicationIdentifier, + input.source.writerRecord.nativeRecordId, + ) + if (writerRecord !== undefined && !writerRecord.ok) return writerRecord + const resolved: ResolvedGraphIdentities = { sourceRecord: connected.value.sourceRecord, - exchange: connected.value.exchange, - observations, + event: connected.value.event, + observations: observations.value, provenance: provenance.value, application: application.value, dataOrigin: dataOrigin.value, + ...(applicationHost.value === undefined ? + {} + : { applicationHost: applicationHost.value }), + ...(dataOriginHost.value === undefined ? + {} + : { dataOriginHost: dataOriginHost.value }), ...gateway.value, + ...(writerRecord === undefined ? {} : { writerRecord: writerRecord.value }), ...(recordingDevice.value === undefined ? {} : { recordingDevice: recordingDevice.value }), } - const fullUrls = [ - ...resolved.observations.map(({ fullUrl }) => fullUrl), - resolved.provenance.fullUrl, - resolved.application.fullUrl, - resolved.dataOrigin.fullUrl, - resolved.distinctGatewayApplication?.fullUrl, - resolved.recordingDevice?.fullUrl, - ].filter((value) => value !== undefined) - if (new Set(fullUrls).size !== fullUrls.length) { - return issues([ - { - severity: 'error', - code: 'duplicate-identifier', - path: [], - message: 'Every Bundle entry requires a distinct business identifier.', - }, - ]) - } return { ok: true, value: resolved } } -/** - * Builds one deterministic, profile-stamped R4 resource graph for the non-empty - * subset of catalog-admitted outputs present in a connected-provider source - * record. Every output shares one conversion Provenance. Raw native ids are - * used only as digest input and are never emitted. Times, repository ids, and - * the durable event sequence remain caller-owned; malformed inputs return - * structured issues and no partial graph. - */ -export const buildProviderMeasurementBundle = ( +const buildProviderObservationEntries = ( input: ProviderMeasurementBundleInput, -): Result => { - const parsed = parseProviderMeasurementBundleInput(input) - if (!parsed.ok) return parsed - const validatedInput = parsed.value - - const identities = resolveGraphIdentities(validatedInput) - if (!identities.ok) return identities - - const observationEntries = [] - for (const [index, measurement] of validatedInput.measurements.entries()) { - const identity = identities.value.observations[index] + identities: ResolvedGraphIdentities, +): Result => { + const entries: ProviderGraphEntry[] = [] + for (const [index, measurement] of input.measurements.entries()) { + const identity = identities.observations[index] if (identity === undefined) { return issues([ { @@ -499,70 +621,171 @@ export const buildProviderMeasurementBundle = ( }, ]) } - observationEntries.push( + entries.push( identifiedEntry( identity, makeObservation( - validatedInput, + input, measurement, identity, - identities.value, - aggregationMethodFor(validatedInput, measurement.kind), + identities, + aggregationMethodFor(input, measurement.kind), ), ), ) } - const application = makeApplicationDevice(validatedInput.application) - const distinctGatewayApplicationInput = - validatedInput.gatewayApplication?.kind === 'distinct-application' ? - validatedInput.gatewayApplication.application + return ok(entries) +} + +const optionalIdentifiedEntry = ( + identity: IdentifiedEntryIdentityInput | undefined, + resource: ProviderGraphResource | undefined, +): readonly ProviderGraphEntry[] => + identity === undefined || resource === undefined ? + [] + : [identifiedEntry(identity, resource)] + +const buildProviderSupportingEntries = ( + input: ProviderMeasurementBundleInput, + identities: ResolvedGraphIdentities, +): readonly ProviderGraphEntry[] => { + const applicationHostInput = input.application.host + const applicationHost = + ( + applicationHostInput === undefined || + identities.applicationHost === undefined + ) ? + undefined + : makeHostDevice({ + ...applicationHostInput, + identity: identities.applicationHost, + }) + const application = makeApplicationDevice({ + ...input.application, + identity: identities.application, + ...(identities.applicationHost === undefined ? + {} + : { parentReference: identities.applicationHost.fullUrl }), + }) + const gatewayApplicationInput = + input.gatewayApplication?.kind === 'distinct-application' ? + input.gatewayApplication.application : undefined - const distinctGatewayApplication = - distinctGatewayApplicationInput === undefined ? undefined : ( - makeApplicationDevice(distinctGatewayApplicationInput) - ) - const recordingDeviceInput = validatedInput.source.recordingDevice + const gatewayApplication = + ( + gatewayApplicationInput === undefined || + identities.distinctGatewayApplication === undefined + ) ? + undefined + : makeApplicationDevice({ + ...gatewayApplicationInput, + identity: identities.distinctGatewayApplication, + ...(identities.distinctGatewayHost === undefined ? + {} + : { parentReference: identities.distinctGatewayHost.fullUrl }), + }) + const gatewayHostInput = gatewayApplicationInput?.host + const gatewayHost = + ( + gatewayHostInput === undefined || + identities.distinctGatewayHost === undefined + ) ? + undefined + : makeHostDevice({ + ...gatewayHostInput, + identity: identities.distinctGatewayHost, + }) + const recordingDeviceInput = input.source.recordingDevice const recordingDevice = - recordingDeviceInput === undefined ? undefined : ( - makeRecordingDevice(recordingDeviceInput) - ) - const dataOrigin = makeApplicationDevice(validatedInput.source.dataOrigin) - const provenance = makeProvenance(validatedInput, identities.value) - const recordingDeviceEntries = ( - recordingDevice === undefined || - identities.value.recordingDevice === undefined + recordingDeviceInput === undefined || + identities.recordingDevice === undefined ) ? - [] - : [identifiedEntry(identities.value.recordingDevice, recordingDevice)] - const entry = [ - ...observationEntries, - ...recordingDeviceEntries, - identifiedEntry(identities.value.dataOrigin, dataOrigin), - ...(( - distinctGatewayApplication === undefined || - identities.value.distinctGatewayApplication === undefined + undefined + : makeRecordingDevice({ + ...recordingDeviceInput, + identity: identities.recordingDevice.snapshot, + stableIdentifier: identities.recordingDevice.stableIdentifier, + }) + const dataOriginHostInput = input.source.dataOrigin.host + const dataOriginHost = + ( + dataOriginHostInput === undefined || + identities.dataOriginHost === undefined ) ? - [] - : [ - identifiedEntry( - identities.value.distinctGatewayApplication, - distinctGatewayApplication, - ), - ]), - identifiedEntry(identities.value.application, application), - identifiedEntry(identities.value.provenance, provenance), + undefined + : makeHostDevice({ + ...dataOriginHostInput, + identity: identities.dataOriginHost, + }) + const dataOrigin = makeApplicationDevice({ + ...input.source.dataOrigin, + identity: identities.dataOrigin, + ...(identities.dataOriginHost === undefined ? + {} + : { parentReference: identities.dataOriginHost.fullUrl }), + }) + const provenance = makeProvenance(input, identities) + return [ + ...optionalIdentifiedEntry( + identities.recordingDevice?.snapshot, + recordingDevice, + ), + ...optionalIdentifiedEntry(identities.dataOriginHost, dataOriginHost), + identifiedEntry(identities.dataOrigin, dataOrigin), + ...optionalIdentifiedEntry(identities.distinctGatewayHost, gatewayHost), + ...optionalIdentifiedEntry( + identities.distinctGatewayApplication, + gatewayApplication, + ), + ...optionalIdentifiedEntry(identities.applicationHost, applicationHost), + identifiedEntry(identities.application, application), + identifiedEntry(identities.provenance, provenance), ] +} + +const buildProviderGraphEntries = ( + input: ProviderMeasurementBundleInput, + identities: ResolvedGraphIdentities, +): Result => { + const observations = buildProviderObservationEntries(input, identities) + if (!observations.ok) return observations + return deduplicateIdentifiedEntries([ + ...observations.value, + ...buildProviderSupportingEntries(input, identities), + ]) +} + +/** + * Builds one deterministic, profile-stamped R4 resource graph for the non-empty + * subset of catalog-admitted outputs present in a connected-provider source + * record. Every output shares one conversion Provenance. Native ids are digest + * input by default and may appear only through the explicit governed disclosure + * policy. Times, repository ids, and the durable event sequence remain + * caller-owned; malformed inputs return structured issues and no partial graph. + */ +export const buildProviderMeasurementBundle = ( + input: ProviderMeasurementBundleInput, +): Result => { + const parsed = parseProviderMeasurementBundleInput(input) + if (!parsed.ok) return parsed + const validatedInput = parsed.value + + const identities = resolveGraphIdentities(validatedInput) + if (!identities.ok) return identities + + const entry = buildProviderGraphEntries(validatedInput, identities.value) + if (!entry.ok) return entry - return parseCollectionBundle({ + return parseGroveMobileExchangeBundle({ resourceType: 'Bundle', ...(validatedInput.repositoryIds?.bundle === undefined ? {} : { id: validatedInput.repositoryIds.bundle }), meta: { profile: [PROFILES.mobileBundle] }, - identifier: identifier(identities.value.exchange), + identifier: identifier(identities.value.event), type: 'collection', - timestamp: validatedInput.recorded, - entry, + timestamp: validatedInput.assembled, + entry: entry.value, }) } diff --git a/packages/fhir/src/providers/contract.generated.ts b/packages/fhir/src/providers/contract.generated.ts index 50ad29e9..7b636bd2 100644 --- a/packages/fhir/src/providers/contract.generated.ts +++ b/packages/fhir/src/providers/contract.generated.ts @@ -11,18 +11,20 @@ import { deepFreeze } from '../core/index.js' -export const groveFhirContractVersion = '0.5.0' as const +export const groveFhirContractVersion = '0.6.0' as const export const groveFhirVersion = '4.0.1' as const const groveProviderPackageMetadataValue = { fhirVersion: '4.0.1', - version: '0.5.0', + version: '0.6.0', packageId: 'org.grovealliance.fhir.providers', canonical: 'https://grovealliance.org/fhir/providers', dependencies: [ - 'org.grovealliance.fhir.mobile#0.5.0', - 'org.grovealliance.fhir.sensor#0.5.0', + 'hl7.terminology.r4#7.3.0', + 'hl7.fhir.uv.extensions.r4#5.3.0', + 'org.grovealliance.fhir.mobile#0.6.0', + 'org.grovealliance.fhir.sensor#0.6.0', ], } as const @@ -59,6 +61,8 @@ const groveProviderProfileCanonicalsValue = { 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-basal-energy', 'health-connect-basal-metabolic-rate': 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-basal-metabolic-rate', + 'healthkit-biological-sex': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-biological-sex', 'healthkit-bladder-incontinence': 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-bladder-incontinence', 'healthkit-bleeding-after-pregnancy': @@ -73,8 +77,8 @@ const groveProviderProfileCanonicalsValue = { 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-blood-pressure', 'healthkit-blood-type': 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-blood-type', - 'provider-body-fat-mass': - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-body-fat-mass', + 'withings-body-fat-mass': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-body-fat-mass', 'grove-mobile-body-fat-percentage': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-body-fat-percentage', 'grove-mobile-body-height': @@ -91,10 +95,14 @@ const groveProviderProfileCanonicalsValue = { 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-cervical-mucus-quality', 'healthkit-contraceptive-use': 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-contraceptive-use', + 'withings-corrected-qt-interval': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-corrected-qt-interval', 'grove-mobile-cycling-cadence': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-cycling-cadence', 'healthkit-cycling-functional-threshold-power': 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-cycling-functional-threshold-power', + 'healthkit-date-of-birth': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-date-of-birth', 'grove-mobile-deep-sleep-duration': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-deep-sleep-duration', 'grove-mobile-dietary-biotin': @@ -193,8 +201,10 @@ const groveProviderProfileCanonicalsValue = { 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-environmental-audio-exposure-notification', 'healthkit-environmental-sound-reduction': 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-environmental-sound-reduction', - 'provider-extracellular-water-mass': - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-extracellular-water-mass', + 'withings-extracellular-water-mass': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-extracellular-water-mass', + 'healthkit-fitzpatrick-skin-type': + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-fitzpatrick-skin-type', 'grove-mobile-flights-climbed': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-flights-climbed', 'grove-mobile-fluid-intake': @@ -233,8 +243,8 @@ const groveProviderProfileCanonicalsValue = { 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-insulin-delivery', 'grove-mobile-intermenstrual-bleeding': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-intermenstrual-bleeding', - 'provider-intracellular-water-mass': - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-intracellular-water-mass', + 'withings-intracellular-water-mass': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-intracellular-water-mass', 'healthkit-irregular-heart-rhythm-notification': 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-irregular-heart-rhythm-notification', 'healthkit-irregular-menstrual-cycles': @@ -255,12 +265,16 @@ const groveProviderProfileCanonicalsValue = { 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-menstruation-period', 'grove-mobile-mindfulness-session': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-mindfulness-session', - 'provider-muscle-mass': - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-muscle-mass', + 'withings-muscle-mass': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-muscle-mass', 'healthkit-number-of-alcoholic-beverages': 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-number-of-alcoholic-beverages', 'healthkit-number-of-times-fallen': 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-number-of-times-fallen', + 'oura-cardiovascular-age': + 'https://grovealliance.org/fhir/oura/StructureDefinition/oura-cardiovascular-age', + 'oura-readiness-score': + 'https://grovealliance.org/fhir/oura/StructureDefinition/oura-readiness-score', 'grove-mobile-ovulation-test-result': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-ovulation-test-result', 'grove-mobile-oxygen-saturation': @@ -279,6 +293,8 @@ const groveProviderProfileCanonicalsValue = { 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-physical-effort', 'grove-mobile-power': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-power', + 'withings-pr-interval': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-pr-interval', 'healthkit-pregnancy-status': 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-pregnancy-status', 'healthkit-pregnancy-test-result': @@ -287,6 +303,10 @@ const groveProviderProfileCanonicalsValue = { 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-progesterone-test-result', 'healthkit-prolonged-menstrual-periods': 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-prolonged-menstrual-periods', + 'withings-qrs-duration': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-qrs-duration', + 'withings-qt-interval': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-qt-interval', 'grove-mobile-rem-sleep-duration': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-rem-sleep-duration', 'grove-mobile-respiratory-rate': @@ -295,6 +315,8 @@ const groveProviderProfileCanonicalsValue = { 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-respiratory-rate-average', 'grove-mobile-resting-heart-rate': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-resting-heart-rate', + 'google-health-daily-resting-heart-rate': + 'https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-daily-resting-heart-rate', 'healthkit-running-ground-contact-time': 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-running-ground-contact-time', 'healthkit-running-stride-length': @@ -319,8 +341,8 @@ const groveProviderProfileCanonicalsValue = { 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-sleep-stage', 'healthkit-sleeping-breathing-disturbances': 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-sleeping-breathing-disturbances', - 'provider-sleeping-heart-rate-average': - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-sleeping-heart-rate-average', + 'withings-sleeping-heart-rate-average': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-sleeping-heart-rate-average', 'grove-mobile-speed': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-speed', 'healthkit-stair-ascent-speed': @@ -445,39 +467,67 @@ const groveProviderProfileCanonicalsValue = { 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-wheelchair-push-count', 'healthkit-wheelchair-use': 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-wheelchair-use', + 'withings-atrial-fibrillation-notification-ecg': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-atrial-fibrillation-notification-ecg', + 'withings-atrial-fibrillation-notification-ppg': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-atrial-fibrillation-notification-ppg', + 'withings-nerve-health-score': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-nerve-health-score', + 'withings-pulse-wave-velocity': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-pulse-wave-velocity', + 'withings-vascular-age': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-vascular-age', + 'withings-visceral-fat-index': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-visceral-fat-index', 'grove-mobile-workout': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-workout', 'healthkit-workout-effort-score': 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-workout-effort-score', 'grove-mobile-workout-segment': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-workout-segment', + 'google-health-observation': + 'https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation', + 'oura-observation': + 'https://grovealliance.org/fhir/oura/StructureDefinition/oura-observation', + 'withings-observation': + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation', 'grove-application-device': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device', + 'grove-host-device': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device', + 'grove-mobile-conversion-provenance': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance', 'grove-mobile-exchange-bundle': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle', + 'grove-mobile-retraction-bundle': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle', + 'grove-mobile-retraction-provenance': + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-provenance', 'grove-recording-device': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-device', 'grove-sensor-recording-document': 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', - 'provider-conversion-provenance': - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance', - 'provider-observation': - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation', - 'provider-recording-document': - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document', + 'providers-conversion-provenance': + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance', + 'providers-observation': + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-observation', + 'providers-recording-document': + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document', } as const export const groveProviderProfileCanonicals: typeof groveProviderProfileCanonicalsValue = deepFreeze(groveProviderProfileCanonicalsValue) const providerAdapterCatalogValue = { + $schema: + 'https://grovealliance.org/fhir/catalog/schemas/catalog-contracts.schema.json', schemaVersion: 1, fhirVersion: '4.0.1', - version: '0.5.0', + version: '0.6.0', packageId: 'org.grovealliance.fhir.providers', canonical: 'https://grovealliance.org/fhir/providers', adapterProfile: - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation', + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-observation', sourceTypeExtension: { url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type', codeSystem: @@ -489,13 +539,22 @@ const providerAdapterCatalogValue = { "provider id + '/' + exact source token; the atomic Withings blood-pressure output uses withings/getmeas:9+10", rule: 'Every admitted output states the exact catalog dispatch token in its own identifier value.', }, + providerExtension: { + url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider', + codeSystem: 'https://grovealliance.org/fhir/providers/CodeSystem/provider', + r4Element: + 'Observation.extension.valueCode or DocumentReference.extension.valueCode', + cardinality: 'exactly one', + rule: 'Every admitted connected-provider output states the exact provider id that owns its catalog row.', + }, recordingDocument: { sourceNeutralProfile: 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', adapterProfile: - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document', + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document', claimMode: 'exactly-source-neutral-plus-adapter', - outputDiscriminator: 'native-recording', + outputRole: 'native-recording', + outputDiscriminator: 'single', encoding: 'caller-supplied exact already-obtained provider bytes', }, rawPayloadAdmission: { @@ -510,7 +569,7 @@ const providerAdapterCatalogValue = { notFHIRAuthorization: true, }, conversionProvenanceProfile: - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance', + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance', statusVocabulary: [ 'supported', 'mapped-standard', @@ -521,24 +580,30 @@ const providerAdapterCatalogValue = { ], statusDefinitions: { supported: - 'Version 0.5.0 defines an exact conversion to every shared profile listed for the source element.', + 'Version 0.6.0 defines an exact conversion to every semantic profile listed for the source element, whether the profile is shared or provider-owned.', 'mapped-standard': - 'Version 0.5.0 admits only the listed source-neutral Sensor recording contract and does not assert a scalar clinical result.', + 'Version 0.6.0 admits only the listed source-neutral Sensor recording contract and does not assert a scalar clinical result.', 'platform-exclusive': - 'Version 0.5.0 defines the listed provider-scoped structured profile because no exact shared profile exists.', + 'Version 0.6.0 defines the listed provider-scoped structured profile because no exact shared profile exists.', unmodeled: - 'The source element is inventoried, but no shared or provider-scoped profile models it in version 0.5.0 and no output is admitted.', + 'The source element is inventoried, but no shared or provider-scoped profile models it in version 0.6.0 and no output is admitted.', deferred: - 'A plausible shared mapping is known, but the source evidence, time semantics, or result shape is insufficient for a conformant version 0.5.0 output.', + 'A plausible shared mapping is known, but the source evidence, time semantics, or result shape is insufficient for a conformant version 0.6.0 output.', 'intentionally-unsupported': - 'Version 0.5.0 deliberately refuses a normalized conversion because doing so would be misleading or diagnostic-adjacent.', + 'Version 0.6.0 deliberately refuses a normalized conversion because doing so would be misleading or diagnostic-adjacent.', + }, + observationIssued: { + cardinality: '0..0', + source: + 'only an exact provider field explicitly declared by its catalog row; no such field is declared in 0.6.0', + rule: 'Version 0.6.0 declares no authoritative provider row field for result availability or current-version modification, so every Provider Observation omits Observation.issued. Conversion, retrieval, assembly, and upload times are prohibited substitutes.', }, sourceEvidence: { accessed: '2026-08-20', scope: 'Exact provider data-type tokens and consumed fields frozen in providers[].sourceTypes; conversion operates only on already-obtained payloads and defines no provider fetching.', tokenBinding: - 'Each providers[].sourceTypes[].token and elements[].path is part of the exact v0.5.0 consumed source surface.', + 'Each providers[].sourceTypes[].token and elements[].path is part of the exact v0.6.0 consumed source surface.', providers: [ { id: 'google-health-api', @@ -567,199 +632,77 @@ const providerAdapterCatalogValue = { ], }, identity: { - composition: { - form: 'v1:|...', - versionPrefix: 'v1', - separator: '|', - rule: "Components join with a vertical bar in the order the composition lists them, behind the scheme version. No component may be empty or contain a vertical bar. Every component is the source's own text, unaltered: nothing is hashed, truncated, normalized, or re-encoded.", - passThrough: - 'A provider whose native record key is unique across the whole provider carries that key verbatim, with no scheme version and no components joined to it: the system already says which provider and which key space it belongs to. Composition is used only where the key alone would not identify one record, which is why an account-scoped provider carries its account pseudonym.', - }, - identifierScope: { - meaning: - "Whether a provider's native record key is unique across the whole provider, or only within one account. A provider scoped to `account` carries its deployment-scoped account pseudonym as a component, so the identifier still stands alone and a receiver deduplicates by comparing system and value with no side channel.", - default: 'account', - relaxationRule: - "A provider moves to `none` only when the vendor documents that its native record key is unique across all accounts. Inference from the shape of an observed value is not sufficient, because the failure it guards against is silently merging two participants' measurements.", - }, - providerAccountIdentifier: { - requirement: - 'A complete deployment-scoped pseudonymous Identifier pair. It must be stable for the provider-account scope needed for deduplication.', - prohibitedByDefault: [ - 'vendor email address', - 'vendor account id', - 'vendor member id', - 'OAuth subject or token', - ], - exception: - 'A raw vendor account value may be used only when an explicit deployment privacy policy authorizes that disclosure; this package grants no such authorization.', - emission: - "For a provider scoped to `account`, the pseudonymous value is a component of the source-record identifier. It is a deployment-minted pseudonym, never a vendor-side account key, and it discloses nothing the resource's own subject reference does not already carry.", - }, - sourceNativeId: { - requirement: - 'The exact stable native record or group key supplied by the already-obtained provider payload.', - emission: - 'Carried in the identifier value as the provider supplied it. It is a vendor row key, not participant data.', - absent: - 'A provider source type that supplies no native key uses the contentDerived strategy below.', - }, + contract: 'catalog/exchange-protocol.json', + protocolVersion: 2, + adapterId: 'providers', sourceRecord: { - system: - 'https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id', - composition: [ - 'providerCode', - 'providerAccountPseudonym', - 'sourceType', - 'sourceNativeId', + identityKind: 'provider-record', + identifierRole: 'source-record', + components: [ + 'provider-code', + 'source-type', + 'provider-scope-system', + 'provider-scope-value', + 'native-record-id', ], - appliesWhen: 'identifierScope is account', - passThroughWhen: - 'identifierScope is none: the value is sourceNativeId verbatim.', - }, - contentDerived: { - whenApplicable: - 'A provider source type that supplies no stable native record key.', - composition: [ - 'providerCode', - 'providerAccountPseudonym', - 'sourceType', - 'content', - 'startInstant', - 'endInstant', - 'measuredValue', + scopeRule: + "The provider scope is always a complete Identifier pair. Use a deployment-scoped account pseudonym when the native key is account-scoped; use the provider's documented global key-space pair only when global uniqueness is authoritative. Never omit the system or infer scope from an observed key.", + absentNativeId: + 'Assign and persist an opaque import-record key before conversion. It must be stable for exact retries and for revisions the connector can correlate. Never derive it from measured values, times, or serialized content.', + }, + sourceOutput: { + identityKind: 'provider-output', + identifierRole: 'source-output', + components: [ + 'provider-code', + 'source-type', + 'provider-scope-system', + 'provider-scope-value', + 'native-record-id', + 'output-role', + 'output-discriminator', ], - discriminator: 'content', - accountScopeRequired: true, - accountScopeReason: - 'Two participants can record identical values over the same window, so content alone collides across people. The account pseudonym is what keeps them apart and must never be omitted for this strategy.', - revisionLimitation: - 'A corrected value or a corrected time yields a different identifier, so a correction appears as a new Observation rather than a revision. The `content` discriminator makes that visible to a receiver rather than leaving it to be inferred per provider.', - rationale: - 'The alternative of keying on the time window alone would survive a value correction but silently merge two distinct measurements sharing a window. A missed deduplication is recoverable; a false merge is not.', + outputRoleRule: + 'Use the exact measurement id, grouped mapping outputRole, or native-recording outputRole declared by the source row.', + outputDiscriminatorRule: + "Use the exact catalog outputDiscriminator; it is single for a sole output. A repeated output appends only the catalog's canonical non-value occurrence coordinate. No fallback or content-derived discriminator is permitted.", }, writerRecord: { - system: - 'https://grovealliance.org/fhir/mobile/NamingSystem/grove-writer-record-id', - composition: ['providerCode', 'sourceNativeId'], - note: 'Carries no account component on any provider, so it can match the identical measurement arriving through HealthKit or Health Connect. A receiver must not assume the match holds: nothing obliges a vendor to use one identifier space across its app and its API.', - }, - output: { - system: - 'https://grovealliance.org/fhir/providers/NamingSystem/provider-output-id', - composition: ['', 'outputDiscriminator'], - outputDiscriminatorRule: { - ordinarySupportedMeasurement: - 'the exact measurementId string from the supported element mapping', - groupedMapping: - 'the exact outputDiscriminator declared on that groupedMappings row', - mappedStandardRaw: 'native-recording', - noFallback: true, - }, - cardinality: '0..1', - appliesWhen: - 'Only where one source record yields more than one Observation. A one-to-one conversion carries the source-record identifier alone; a second namespace repeating the same value identifies nothing new.', - }, - deploymentOwnedIdentities: { - rule: "The conversion Provenance and the exchange Bundle are created by the export, not read from the provider, so they are identified in the deployment's own namespace. This retires provider-conversion-id and provider-exchange-id.", - roles: ['conversion-provenance', 'exchange-bundle'], - value: '||', + identityKind: 'writer-record', + identifierRole: 'writer-record', + components: [ + 'writer-application-system', + 'writer-application-value', + 'writer-record-id', + ], + rule: 'Emit only when the payload supplies a stable writer-application Identifier pair and logical writer record id. Provider code plus native record id is not evidence of cross-channel writer identity.', + }, + sourceArtifact: { + identityKind: 'provider-artifact', + identifierRole: 'source-artifact', + components: [ + 'provider-code', + 'source-type', + 'provider-scope-system', + 'provider-scope-value', + 'native-record-id', + 'format-code', + 'part-index', + ], + rule: 'A native Recording Document uses its registered format code and zero-based part index in addition to the source-record coordinates.', }, resourceIdPolicy: - 'Resource.id is optional and repository-assigned; no business identifier is copied into Resource.id.', - positiveEventSequence: - 'A durable positive event sequence written as a non-zero ASCII digit followed by zero or more ASCII digits; no sign, no leading zero, no other characters. A byte-identical retry reuses the sequence; changed content allocates a higher value.', - singleConversionRule: - "A conversion Provenance has exactly one source-record pair. When a Bundle carries exactly one conversion event, the exchange event is that conversion's emission and reuses its sequence, so the exchange identifier value equals the conversion identifier value byte for byte and only the NamingSystem distinguishes them.", - vectors: [ - { - role: 'sourceRecord', - scope: 'account', - components: ['withings', 'acct-7f3a9c', 'measure', '17348211'], - identifierValue: 'v1:withings|acct-7f3a9c|measure|17348211', - }, - { - role: 'sourceRecord', - scope: 'none', - components: ['oura', 'sleep', '8f9a5221-639e-4a85-81cb-4065ef23f979'], - identifierValue: 'v1:oura|sleep|8f9a5221-639e-4a85-81cb-4065ef23f979', - }, - { - role: 'sourceRecord', - scope: 'account', - components: [ - 'withings', - 'acct-7f3a9c', - 'heart-rate', - 'hr-2026-08-19-001', - ], - identifierValue: 'v1:withings|acct-7f3a9c|heart-rate|hr-2026-08-19-001', - }, - { - role: 'output', - scope: 'account', - components: [ - 'withings', - 'acct-7f3a9c', - 'measure', - '17348211', - 'body-weight', - ], - identifierValue: 'v1:withings|acct-7f3a9c|measure|17348211|body-weight', - }, - { - role: 'output', - scope: 'account', - components: [ - 'withings', - 'acct-7f3a9c', - 'heart-rate', - 'hr-2026-08-19-001', - 'native-recording', - ], - identifierValue: - 'v1:withings|acct-7f3a9c|heart-rate|hr-2026-08-19-001|native-recording', - }, - { - role: 'contentDerived', - scope: 'account', - components: [ - 'google-health-api', - 'acct-7f3a9c', - 'steps', - 'content', - '2026-08-19T00:00:00Z', - '2026-08-20T00:00:00Z', - '8412', - ], - identifierValue: - 'v1:google-health-api|acct-7f3a9c|steps|content|2026-08-19T00:00:00Z|2026-08-20T00:00:00Z|8412', - }, - { - role: 'writerRecord', - components: ['withings', '17348211'], - identifierValue: 'v1:withings|17348211', - note: "Identical to the value the same measurement carries when it arrives through HealthKit or Health Connect, where the writer slot is the application's own bundle or package name.", - }, - { - role: 'deploymentOwnedExchangeBundle', - components: ['withings', '1', 'exchange-bundle'], - identifierValue: 'withings|1|exchange-bundle', - note: "Minted in the deployment's namespace, so it carries no scheme-version prefix from this guide.", - }, - { - role: 'rejectedSeparatorInComponent', - components: ['withings', 'acct-7f3a9c', 'measure', '173|48211'], - identifierValue: null, - note: 'A native key containing a vertical bar is rejected, never escaped.', - }, - ], + 'Resource.id is optional and repository-assigned. Source, output, writer, and artifact business identifiers are never copied into Resource.id.', }, providers: [ { id: 'google-health-api', title: 'Google Health API', + measurementOwner: 'google-health', + observationProfile: + 'https://grovealliance.org/fhir/google-health/StructureDefinition/google-health-observation', sourceTypeCount: 18, + providerScopeMode: 'deployment-scoped-account-pseudonym', sourceTypes: [ { token: 'steps', @@ -885,7 +828,7 @@ const providerAdapterCatalogValue = { { path: 'dailyRestingHeartRate.beatsPerMinute', status: 'supported', - measurementIds: ['resting-heart-rate'], + measurementIds: ['resting-heart-rate-daily-average'], }, ], }, @@ -1001,8 +944,9 @@ const providerAdapterCatalogValue = { sourceNeutralProfile: 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', adapterProfile: - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document', - outputDiscriminator: 'native-recording', + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document', + outputRole: 'native-recording', + outputDiscriminator: 'single', }, elements: [ { @@ -1018,11 +962,14 @@ const providerAdapterCatalogValue = { ], identifierScope: 'account', identifierScopeReason: - 'The dataPoint segment of the record name is documented as unique only within a user and a dataType, and may be client-provided, so two accounts can hold the same key. Most Google data types carry no record key at all and use the contentDerived strategy.', + 'The dataPoint segment of the record name is documented as unique only within a user and a dataType, and may be client-provided, so two accounts can hold the same key. When a source row has no durable native key, assign and persist the opaque import-record key required by identity.sourceRecord.absentNativeId before conversion; content-derived identity is prohibited.', }, { id: 'oura', title: 'Oura', + measurementOwner: 'oura', + observationProfile: + 'https://grovealliance.org/fhir/oura/StructureDefinition/oura-observation', sourceTypeCount: 8, sourceTypes: [ { @@ -1146,25 +1093,40 @@ const providerAdapterCatalogValue = { }, { token: 'daily_cardiovascular_age', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + profiles: [ + 'https://grovealliance.org/fhir/oura/StructureDefinition/oura-cardiovascular-age', + ], elements: [ { path: 'vascular_age', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + measurementIds: ['oura-cardiovascular-age'], + unitConversion: 'provider integer years to UCUM a', + effective: + 'the source civil day represented as a complete day Period; midpoint substitution is forbidden', reason: - "Although two providers report it, each value is the output of an undisclosed proprietary algorithm over different inputs (Withings pulse-wave velocity versus Oura PPG features), so the numbers are not comparable and no shared physiological definition exists; normalizing them under one code would fabricate clinical comparability, and the diagnostic-adjacent 'age' framing makes a shared measurement unsafe. Both provider catalog rows already record this refusal.", + "Version 0.6.0 carries the Oura cardiovascular-age figure as the provider-scoped oura-cardiovascular-age profile, on an age scale in UCUM years over the civil-day Period. The profile names the vendor in its code and description, so the figure cannot be mistaken for a chronological age, for a clinical vascular assessment, or for Withings' vascular age, which stays a separate measurement because the two are undisclosed algorithms over different inputs. No comparability between vendors and no diagnosis is asserted.", }, ], }, { token: 'daily_readiness', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + profiles: [ + 'https://grovealliance.org/fhir/oura/StructureDefinition/oura-readiness-score', + ], elements: [ { path: 'score', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + measurementIds: ['oura-readiness-score'], + unitConversion: + 'unitless provider value to the dimensionless UCUM {score} annotation', + effective: + 'the source civil day represented as a complete day Period; midpoint substitution is forbidden', reason: - "A proprietary vendor score with no physiologic unit is not a comparable measurement; normalizing it as a dimensionless quantity would misrepresent an undisclosed composite algorithm as an observable. The same verdict applies to Oura's daily sleep and activity scores, which are not yet inventoried in providers-adapter.json.", + "Version 0.6.0 carries the Oura readiness figure as the provider-scoped oura-readiness-score profile over the civil-day Period, on the dimensionless UCUM {score} annotation because the vendor publishes no physical unit for it. The code names the vendor and the profile description states the scale, so the figure cannot be read as an observable quantity. Nothing about the composite's inputs, weighting, or comparability across people is asserted, and Oura's daily sleep and activity scores remain outside the inventoried source surface.", }, ], }, @@ -1176,8 +1138,9 @@ const providerAdapterCatalogValue = { sourceNeutralProfile: 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', adapterProfile: - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document', - outputDiscriminator: 'native-recording', + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document', + outputRole: 'native-recording', + outputDiscriminator: 'single', }, elements: [ { @@ -1191,13 +1154,17 @@ const providerAdapterCatalogValue = { ], }, ], - identifierScope: 'none', + identifierScope: 'global', + providerScopeMode: 'documented-global-key-space', identifierScopeReason: - 'Document ids are UUIDs, unique by construction across all accounts.', + 'Document ids are UUIDs documented as globally unique. The provider-scope pair names that stable global document-id key space and is never replaced with a per-account value.', }, { id: 'withings', title: 'Withings', + measurementOwner: 'withings', + observationProfile: + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation', sourceTypeCount: 42, groupedMappings: [ { @@ -1206,7 +1173,8 @@ const providerAdapterCatalogValue = { members: ['getmeas:9', 'getmeas:10'], sourceNativeId: 'the common measure-group grpid', measurementIds: ['blood-pressure'], - outputDiscriminator: 'blood-pressure-panel', + outputRole: 'blood-pressure-panel', + outputDiscriminator: 'single', rule: 'Emit exactly one panel only when one type 9 value and one type 10 value occur in the same measure group; otherwise emit neither component as a normalized Observation. Use getmeas:9+10, not either member token, in the source-record identity preimage.', }, ], @@ -1330,13 +1298,19 @@ const providerAdapterCatalogValue = { }, { token: 'getmeas:155', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + profiles: [ + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-vascular-age', + ], elements: [ { path: 'measure.value*10^unit', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + measurementIds: ['withings-vascular-age'], + unitConversion: 'provider SI value to UCUM a', + effective: 'measure group date', reason: - "Although two providers report it, each value is the output of an undisclosed proprietary algorithm over different inputs (Withings pulse-wave velocity versus Oura PPG features), so the numbers are not comparable and no shared physiological definition exists; normalizing them under one code would fabricate clinical comparability, and the diagnostic-adjacent 'age' framing makes a shared measurement unsafe. Both provider catalog rows already record this refusal.", + "Version 0.6.0 carries the Withings vascular-age figure as the provider-scoped withings-vascular-age profile, on an age scale in UCUM years. The profile names the vendor in its code and description, so the figure cannot be mistaken for a chronological age, for a clinical vascular assessment, or for Oura's cardiovascular age, which stays a separate measurement because the two are undisclosed algorithms over different inputs. No comparability between vendors and no diagnosis is asserted.", }, ], }, @@ -1476,8 +1450,9 @@ const providerAdapterCatalogValue = { sourceNeutralProfile: 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', adapterProfile: - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document', - outputDiscriminator: 'native-recording', + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document', + outputRole: 'native-recording', + outputDiscriminator: 'single', }, elements: [ { @@ -1526,8 +1501,9 @@ const providerAdapterCatalogValue = { sourceNeutralProfile: 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', adapterProfile: - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document', - outputDiscriminator: 'native-recording', + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document', + outputRole: 'native-recording', + outputDiscriminator: 'single', }, elements: [ { @@ -1630,97 +1606,144 @@ const providerAdapterCatalogValue = { }, { token: 'getmeas:91', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + profiles: [ + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-pulse-wave-velocity', + ], elements: [ { path: 'measure.value*10^unit', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + measurementIds: ['withings-pulse-wave-velocity'], + unitConversion: 'provider SI value to UCUM m/s', + effective: 'measure group date', reason: - 'Diagnostic-adjacent cardiovascular assessment: Withings PWV is an aortic-stiffness screening value produced by a proprietary single-vendor scale algorithm (and was subject to regulatory withdrawal and reintroduction), with no second evidencing source and no safe wellness normalization, so admitting it as a normalized measurement would misrepresent a clinical vascular assessment.', + 'Version 0.6.0 carries the Withings aortic pulse-wave velocity estimate as the provider-scoped withings-pulse-wave-velocity profile in UCUM metres per second. The code names the vendor rather than a shared arterial-stiffness concept, because no second inventoried source evidences it and a scale-derived estimate is not interchangeable with a tonometric measurement taken in clinic. No arterial-stiffness finding and no diagnosis is asserted.', }, ], }, { token: 'getmeas:130', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + profiles: [ + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-atrial-fibrillation-notification-ecg', + ], elements: [ { path: 'measure.value', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + measurementIds: ['withings-atrial-fibrillation-notification-ecg'], + effective: 'measure group date', reason: - 'An AFib classification is diagnostic-adjacent and is not represented as an untyped scalar Observation.', + "Version 0.6.0 carries the fact that Withings' electrocardiogram screening algorithm flagged signs of atrial fibrillation, as the provider-scoped withings-atrial-fibrillation-notification-ecg profile with a closed single-code result, on the same basis as the HealthKit irregular-heart-rhythm notification. An Observation is admitted only for the vendor's positive screening classification: Withings publishes no encoding for the numeric measure.value, so a negative or inconclusive classification produces no output. No rhythm finding, atrial-fibrillation burden, or diagnosis is asserted, and the code is taken from the providers code system so no receiver can read it as one.", }, ], }, { token: 'getmeas:135', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + profiles: [ + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-qrs-duration', + ], elements: [ { path: 'measure.value*10^unit', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + measurementIds: ['qrs-duration'], + unitConversion: 'provider SI value to UCUM ms', + effective: 'measure group date', reason: - 'A QRS interval requires an ECG-specific interpretation contract not present in version 0.5.0.', + 'Version 0.6.0 carries the QRS duration the Withings algorithm measured from its own electrocardiogram, as the provider-scoped withings-qrs-duration profile under LOINC 8633-0. The profile is provider-scoped because Withings is the only inventoried source that reports the interval as a discrete measure. No rhythm interpretation, conduction finding, or diagnosis is asserted; the electrocardiogram recording remains the rhythm evidence.', }, ], }, { token: 'getmeas:136', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + profiles: [ + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-pr-interval', + ], elements: [ { path: 'measure.value*10^unit', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + measurementIds: ['pr-interval'], + unitConversion: 'provider SI value to UCUM ms', + effective: 'measure group date', reason: - 'A PR interval requires an ECG-specific interpretation contract not present in version 0.5.0.', + 'Version 0.6.0 carries the PR interval the Withings algorithm measured from its own electrocardiogram, as the provider-scoped withings-pr-interval profile under LOINC 8625-6. The profile is provider-scoped because Withings is the only inventoried source that reports the interval as a discrete measure. No rhythm interpretation, conduction finding, or diagnosis is asserted; the electrocardiogram recording remains the rhythm evidence.', }, ], }, { token: 'getmeas:137', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + profiles: [ + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-qt-interval', + ], elements: [ { path: 'measure.value*10^unit', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + measurementIds: ['qt-interval'], + unitConversion: 'provider SI value to UCUM ms', + effective: 'measure group date', reason: - 'A QT interval requires an ECG-specific interpretation contract not present in version 0.5.0.', + 'Version 0.6.0 carries the uncorrected QT interval the Withings algorithm measured from its own electrocardiogram, as the provider-scoped withings-qt-interval profile under LOINC 8634-8. The profile is provider-scoped because Withings is the only inventoried source that reports the interval as a discrete measure. No repolarization finding, rhythm interpretation, or diagnosis is asserted, and the value is never substituted for the rate-corrected interval.', }, ], }, { token: 'getmeas:138', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + profiles: [ + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-corrected-qt-interval', + ], elements: [ { path: 'measure.value*10^unit', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + measurementIds: ['corrected-qt-interval'], + unitConversion: 'provider SI value to UCUM ms', + effective: 'measure group date', reason: - 'A corrected QT interval requires an ECG-specific interpretation contract not present in version 0.5.0.', + 'Version 0.6.0 carries the rate-corrected QT interval Withings reports, as the provider-scoped withings-corrected-qt-interval profile under LOINC 8636-3. The profile is provider-scoped because Withings is the only inventoried source that reports the correction as a discrete measure and does not publish which correction formula it applied, so the value is deliberately not asserted to be interchangeable with a QTc read from a clinical twelve-lead electrocardiogram. No repolarization finding and no diagnosis is asserted.', }, ], }, { token: 'getmeas:139', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + profiles: [ + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-atrial-fibrillation-notification-ppg', + ], elements: [ { path: 'measure.value', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + measurementIds: ['withings-atrial-fibrillation-notification-ppg'], + effective: 'measure group date', reason: - 'An AFib classification is diagnostic-adjacent and is not represented as an untyped scalar Observation.', + "Version 0.6.0 carries the fact that Withings' photoplethysmography screening algorithm flagged signs of atrial fibrillation, as the provider-scoped withings-atrial-fibrillation-notification-ppg profile with a closed single-code result. It is kept separate from the electrocardiogram notification because it screens a different signal. An Observation is admitted only for the vendor's positive screening classification: Withings publishes no encoding for the numeric measure.value, so a negative or inconclusive classification produces no output. No rhythm finding, atrial-fibrillation burden, or diagnosis is asserted.", }, ], }, { token: 'getmeas:167', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + profiles: [ + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-nerve-health-score', + ], elements: [ { path: 'measure.value*10^unit', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + measurementIds: ['withings-nerve-health-score'], + unitConversion: + 'unitless provider value to the dimensionless UCUM {score} annotation', + effective: 'measure group date', reason: - 'A proprietary vendor score with no physiologic unit, and additionally diagnostic-adjacent: Withings positions it as small-fiber-neuropathy screening, so an untyped scalar Observation would be misleading.', + 'Version 0.6.0 carries the Withings nerve-health figure as the provider-scoped withings-nerve-health-score profile, on the dimensionless UCUM {score} annotation because the vendor publishes no physical unit for it. The code names the vendor and the profile description states the scale. Withings positions the figure as small-fiber-neuropathy screening, so the profile deliberately asserts no neuropathy finding, no nerve-conduction measurement, and no diagnosis.', }, ], }, @@ -1748,13 +1771,20 @@ const providerAdapterCatalogValue = { }, { token: 'getmeas:170', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + profiles: [ + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-visceral-fat-index', + ], elements: [ { path: 'measure.value*10^unit', - status: 'intentionally-unsupported', + status: 'platform-exclusive', + measurementIds: ['withings-visceral-fat-index'], + unitConversion: + 'unitless provider value to the dimensionless UCUM {score} annotation', + effective: 'measure group date', reason: - 'The value is a unitless proprietary rating, not a mass or area; emitting it as a dimensionless UCUM quantity would assert a measurand and comparability the vendor does not define.', + "Version 0.6.0 carries the Withings visceral-fat figure as the provider-scoped withings-visceral-fat-index profile, on the dimensionless UCUM {score} annotation because the API returns the value without a unit and it is a rating rather than a mass or an area. The code names the vendor, so the figure cannot be folded into a shared body-composition measurement, and no measurand, no mass, and no comparability with another vendor's visceral-fat figure is asserted.", }, ], }, @@ -1795,15 +1825,20 @@ const providerAdapterCatalogValue = { }, ], identifierScope: 'account', + providerScopeMode: 'deployment-scoped-account-pseudonym', identifierScopeReason: 'grpid is a server-assigned integer that appears to come from a global sequence, but Withings does not document its scope. Scoped by account until they do.', }, ], recordingDeviceIdentity: { adapterId: 'providers', - note: "A connected provider's device metadata lives on a separate vendor endpoint the adapter does not consume, and its per-unit identifiers are refused outright. Provider Observations therefore carry a per-sample recording Device until that input exists.", - sourceFields: {}, - status: 'unavailable', + status: 'stable-token-required', + stableUnitToken: + 'A caller-governed, deployment-scoped per-unit token from already-authorized provider device data.', + fallback: + 'Omit Observation.device when no stable per-unit token exists. Manufacturer, model, payload group, account, and event identity do not prove a physical device instance.', + snapshot: + 'When emitted, the Device carries both recording-device and event-scoped device-snapshot v2 identifiers.', }, } as const @@ -1958,6 +1993,17 @@ const adapterMeasurementCatalogValue = { unit: '%', example: 2, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + maximum: { + value: 100, + inclusive: true, + }, + integerOnly: false, + }, }, method: { code: 'percentage-of-time', @@ -2272,6 +2318,45 @@ const adapterMeasurementCatalogValue = { }, ], }, + 'biological-sex': { + id: 'biological-sex', + profile: 'healthkit-biological-sex', + standardProfile: null, + title: 'Grove HealthKit Biological Sex', + description: + "Biological sex as a coded Observation. LOINC 46098-0 'Sex' is the context-free concept (verified ACTIVE, PROPERTY LP6886-8 Type, SCALE_TYP LP7750-5 Nom). 76689-9 'Sex assigned at birth' was rejected: the HealthKit characteristic states only a sex, never that it is the one assigned at birth, and adopting that code would fabricate a provenance the source does not carry. Value is a Grove-coded concept with the HKBiologicalSex token retained as secondary coding per the sleep-stage absorption pattern. HKBiologicalSex.notSet emits no Observation.", + code: { + system: 'http://loinc.org', + code: '46098-0', + display: 'Sex', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-biological-sex', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-biological-sex', + allowedValues: ['female', 'male', 'other'], + resultCodes: [ + { + code: 'female', + display: 'Female', + definition: 'HKBiologicalSex female.', + }, + { + code: 'male', + display: 'Male', + definition: 'HKBiologicalSex male.', + }, + { + code: 'other', + display: 'Other', + definition: 'HKBiologicalSex other.', + }, + ], + effective: 'dateTime', + }, 'bladder-incontinence': { id: 'bladder-incontinence', profile: 'healthkit-bladder-incontinence', @@ -2421,6 +2506,17 @@ const adapterMeasurementCatalogValue = { unit: '%', example: 0.04, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + maximum: { + value: 100, + inclusive: true, + }, + integerOnly: false, + }, }, effective: 'dateTime', }, @@ -2594,6 +2690,24 @@ const adapterMeasurementCatalogValue = { }, effective: 'dateTime', }, + 'date-of-birth': { + id: 'date-of-birth', + profile: 'healthkit-date-of-birth', + standardProfile: null, + title: 'Grove HealthKit Date of Birth', + description: + "Date of birth as an Observation. LOINC 21112-8 'Birth date' is the exact concept (verified ACTIVE, PROPERTY LP6882-7 TmStp, SCALE_TYP LP7753-9 Qn). A date of birth identifies a person across systems, so the adapter withholds it unless the deployment authorizes disclosure; a deployment that already knows its participant's demographics from enrollment should prefer that authoritative record over this assertion.", + code: { + system: 'http://loinc.org', + code: '21112-8', + display: 'Birth date', + }, + valueKind: 'dateTime', + owner: 'healthkit', + quantity: null, + effective: 'dateTime', + example: '1985-04-12', + }, 'environmental-audio-exposure': { id: 'environmental-audio-exposure', profile: 'healthkit-environmental-audio-exposure', @@ -2687,6 +2801,67 @@ const adapterMeasurementCatalogValue = { }, effective: 'Period', }, + 'fitzpatrick-skin-type': { + id: 'fitzpatrick-skin-type', + profile: 'healthkit-fitzpatrick-skin-type', + standardProfile: null, + title: 'Grove HealthKit Fitzpatrick Skin Type', + description: + "Fitzpatrick sun-reactive skin type as a coded Observation. LOINC 66555-4 'Skin type [Fitzpatrick Classification Scale]' is the exact concept (verified ACTIVE, PROPERTY LP6886-8 Type, SCALE_TYP LP7750-5 Nom). The scale materially conditions UV-exposure interpretation, which is why it is modelled rather than left to the deployment. HKFitzpatrickSkinType.notSet emits no Observation.", + code: { + system: 'http://loinc.org', + code: '66555-4', + display: 'Skin type [Fitzpatrick Classification Scale]', + }, + valueKind: 'codeableConcept', + owner: 'healthkit', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/healthkit/ValueSet/healthkit-fitzpatrick-skin-type', + resultCodeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-fitzpatrick-skin-type', + allowedValues: [ + 'type-i', + 'type-ii', + 'type-iii', + 'type-iv', + 'type-v', + 'type-vi', + ], + resultCodes: [ + { + code: 'type-i', + display: 'Type I', + definition: 'Always burns, never tans.', + }, + { + code: 'type-ii', + display: 'Type II', + definition: 'Usually burns, tans minimally.', + }, + { + code: 'type-iii', + display: 'Type III', + definition: 'Sometimes burns mildly, tans uniformly.', + }, + { + code: 'type-iv', + display: 'Type IV', + definition: 'Burns minimally, always tans well.', + }, + { + code: 'type-v', + display: 'Type V', + definition: 'Very rarely burns, tans very easily.', + }, + { + code: 'type-vi', + display: 'Type VI', + definition: 'Never burns, deeply pigmented.', + }, + ], + effective: 'dateTime', + }, 'food-correlation': { id: 'food-correlation', profile: 'healthkit-food-correlation', @@ -3052,6 +3227,13 @@ const adapterMeasurementCatalogValue = { unit: 'puffs', example: 2, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + integerOnly: true, + }, }, effective: 'Period', }, @@ -3318,6 +3500,13 @@ const adapterMeasurementCatalogValue = { unit: 'drinks', example: 2, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + integerOnly: true, + }, }, effective: 'Period', }, @@ -3344,6 +3533,13 @@ const adapterMeasurementCatalogValue = { unit: 'falls', example: 1, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + integerOnly: true, + }, }, effective: 'Period', }, @@ -3393,6 +3589,17 @@ const adapterMeasurementCatalogValue = { unit: '%', example: 3.5, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + maximum: { + value: 100, + inclusive: true, + }, + integerOnly: false, + }, }, effective: 'dateTime', }, @@ -3891,6 +4098,17 @@ const adapterMeasurementCatalogValue = { unit: 'valence', example: 0.4, dimension: {}, + valueDomain: { + minimum: { + value: -1, + inclusive: true, + }, + maximum: { + value: 1, + inclusive: true, + }, + integerOnly: false, + }, }, effective: 'Period', components: [ @@ -4308,6 +4526,13 @@ const adapterMeasurementCatalogValue = { unit: 'strokes', example: 640, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + integerOnly: true, + }, }, effective: 'Period', }, @@ -5433,6 +5658,17 @@ const adapterMeasurementCatalogValue = { unit: '%', example: 1.4, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + maximum: { + value: 100, + inclusive: true, + }, + integerOnly: false, + }, }, effective: 'dateTime', }, @@ -5456,6 +5692,17 @@ const adapterMeasurementCatalogValue = { unit: '%', example: 27, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + maximum: { + value: 100, + inclusive: true, + }, + integerOnly: false, + }, }, effective: 'dateTime', }, @@ -5543,6 +5790,17 @@ const adapterMeasurementCatalogValue = { unit: '%', example: 82, dimension: {}, + valueDomain: { + minimum: { + value: 0, + inclusive: true, + }, + maximum: { + value: 100, + inclusive: true, + }, + integerOnly: false, + }, }, method: { code: 'rolling-mean', @@ -6001,10 +6259,10 @@ const adapterMeasurementCatalogValue = { effective: 'Period', }, }, - providers: { + withings: { 'body-fat-mass': { id: 'body-fat-mass', - profile: 'provider-body-fat-mass', + profile: 'withings-body-fat-mass', standardProfile: null, title: 'Body Fat Mass', description: @@ -6015,7 +6273,7 @@ const adapterMeasurementCatalogValue = { display: 'Body fat [Mass] Calculated', }, valueKind: 'quantity', - owner: 'providers', + owner: 'withings', quantity: { system: 'http://unitsofmeasure.org', code: 'kg', @@ -6027,23 +6285,48 @@ const adapterMeasurementCatalogValue = { }, effective: 'dateTime', }, + 'corrected-qt-interval': { + id: 'corrected-qt-interval', + profile: 'withings-corrected-qt-interval', + standardProfile: null, + title: 'Corrected QT Interval', + description: + "The rate-corrected QT interval a Withings device reports alongside its own electrocardiogram, normalized to UCUM milliseconds. Only Withings reports the correction as a discrete measure, so the profile is provider-scoped. The interval is the vendor algorithm's reading of its own recording; no rhythm interpretation, repolarization finding, or diagnosis is asserted, and the correction formula the vendor applied is not published, so the value is not interchangeable with a QTc read from a clinical twelve-lead electrocardiogram.", + code: { + system: 'http://loinc.org', + code: '8636-3', + display: 'Q-T interval corrected', + }, + valueKind: 'quantity', + owner: 'withings', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ms', + unit: 'ms', + example: 410, + dimension: { + s: 1, + }, + }, + effective: 'dateTime', + }, 'extracellular-water-mass': { id: 'extracellular-water-mass', - profile: 'provider-extracellular-water-mass', + profile: 'withings-extracellular-water-mass', standardProfile: null, title: 'Extracellular Water Mass', description: 'Mass of extracellular body water estimated by bioelectrical impedance, normalized to UCUM kilograms. Only Withings evidences the compartmentalized value, so the profile is provider-scoped and is distinct from total body-water mass.', code: { system: - 'https://grovealliance.org/fhir/providers/CodeSystem/provider-measurement', + 'https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement', code: 'extracellular-water-mass', display: 'Extracellular water mass', definition: 'The mass of body water outside cells as estimated by bioelectrical impedance analysis.', }, valueKind: 'quantity', - owner: 'providers', + owner: 'withings', quantity: { system: 'http://unitsofmeasure.org', code: 'kg', @@ -6057,21 +6340,21 @@ const adapterMeasurementCatalogValue = { }, 'intracellular-water-mass': { id: 'intracellular-water-mass', - profile: 'provider-intracellular-water-mass', + profile: 'withings-intracellular-water-mass', standardProfile: null, title: 'Intracellular Water Mass', description: 'Mass of intracellular body water estimated by bioelectrical impedance, normalized to UCUM kilograms. Only Withings evidences the compartmentalized value, so the profile is provider-scoped and is distinct from total body-water mass.', code: { system: - 'https://grovealliance.org/fhir/providers/CodeSystem/provider-measurement', + 'https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement', code: 'intracellular-water-mass', display: 'Intracellular water mass', definition: 'The mass of body water inside cells as estimated by bioelectrical impedance analysis.', }, valueKind: 'quantity', - owner: 'providers', + owner: 'withings', quantity: { system: 'http://unitsofmeasure.org', code: 'kg', @@ -6085,7 +6368,7 @@ const adapterMeasurementCatalogValue = { }, 'muscle-mass': { id: 'muscle-mass', - profile: 'provider-muscle-mass', + profile: 'withings-muscle-mass', standardProfile: null, title: 'Muscle Mass', description: @@ -6096,7 +6379,7 @@ const adapterMeasurementCatalogValue = { display: 'Body muscle mass Calculated', }, valueKind: 'quantity', - owner: 'providers', + owner: 'withings', quantity: { system: 'http://unitsofmeasure.org', code: 'kg', @@ -6108,23 +6391,98 @@ const adapterMeasurementCatalogValue = { }, effective: 'dateTime', }, + 'pr-interval': { + id: 'pr-interval', + profile: 'withings-pr-interval', + standardProfile: null, + title: 'PR Interval', + description: + "The PR interval a Withings device measures from its own electrocardiogram, normalized to UCUM milliseconds. Only Withings reports it as a discrete measure, so the profile is provider-scoped. The interval is the vendor algorithm's reading of its own recording; no conduction finding, rhythm interpretation, or diagnosis is asserted.", + code: { + system: 'http://loinc.org', + code: '8625-6', + display: 'P-R Interval', + }, + valueKind: 'quantity', + owner: 'withings', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ms', + unit: 'ms', + example: 160, + dimension: { + s: 1, + }, + }, + effective: 'dateTime', + }, + 'qrs-duration': { + id: 'qrs-duration', + profile: 'withings-qrs-duration', + standardProfile: null, + title: 'QRS Duration', + description: + "The QRS complex duration a Withings device measures from its own electrocardiogram, normalized to UCUM milliseconds. Only Withings reports it as a discrete measure, so the profile is provider-scoped. The interval is the vendor algorithm's reading of its own recording; no conduction finding, rhythm interpretation, or diagnosis is asserted.", + code: { + system: 'http://loinc.org', + code: '8633-0', + display: 'QRS duration', + }, + valueKind: 'quantity', + owner: 'withings', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ms', + unit: 'ms', + example: 92, + dimension: { + s: 1, + }, + }, + effective: 'dateTime', + }, + 'qt-interval': { + id: 'qt-interval', + profile: 'withings-qt-interval', + standardProfile: null, + title: 'QT Interval', + description: + "The uncorrected QT interval a Withings device measures from its own electrocardiogram, normalized to UCUM milliseconds. Only Withings reports it as a discrete measure, so the profile is provider-scoped. The interval is the vendor algorithm's reading of its own recording; no repolarization finding, rhythm interpretation, or diagnosis is asserted, and the rate correction is a separate measurement.", + code: { + system: 'http://loinc.org', + code: '8634-8', + display: 'Q-T interval', + }, + valueKind: 'quantity', + owner: 'withings', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'ms', + unit: 'ms', + example: 390, + dimension: { + s: 1, + }, + }, + effective: 'dateTime', + }, 'sleeping-heart-rate-average': { id: 'sleeping-heart-rate-average', - profile: 'provider-sleeping-heart-rate-average', + profile: 'withings-sleeping-heart-rate-average', standardProfile: null, title: 'Sleeping Heart Rate Average', description: 'The average heart rate across one sleep session, normalized to UCUM beats per minute. It is a session-windowed average, distinct from both the shared point heart-rate measurement and the daily resting-heart-rate estimate, and is implemented by the phase-2 aggregate design.', code: { system: - 'https://grovealliance.org/fhir/providers/CodeSystem/provider-measurement', + 'https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement', code: 'sleeping-heart-rate-average', display: 'Sleeping heart rate average', definition: 'The mean heart rate across the exact sleep-session Observation effective Period.', }, valueKind: 'quantity', - owner: 'providers', + owner: 'withings', quantity: { system: 'http://unitsofmeasure.org', code: '/min', @@ -6140,24 +6498,334 @@ const adapterMeasurementCatalogValue = { }, effective: 'Period', }, - }, -} as const - -export const adapterMeasurementCatalog: typeof adapterMeasurementCatalogValue = - deepFreeze(adapterMeasurementCatalogValue) - -const providerScalarMappingsValue = { - 'google-health-api': { - steps: { - 'step-count': 'step-count', - }, - distance: { - distance: 'distance', - }, - 'active-energy-burned': { - 'active-energy': 'active-energy', - }, - weight: { + 'withings-atrial-fibrillation-notification-ecg': { + id: 'withings-atrial-fibrillation-notification-ecg', + profile: 'withings-atrial-fibrillation-notification-ecg', + standardProfile: null, + title: 'Withings Atrial Fibrillation Notification (ECG)', + description: + "Withings meastype 130: a screening notification from the vendor's proprietary electrocardiogram algorithm. It is emitted as a notification and never as a rhythm finding, exactly as the HealthKit irregular-heart-rhythm notification is: the recording itself remains the rhythm evidence. The adapter admits an Observation only for the vendor's positive screening classification; Withings publishes no encoding for the numeric measure.value, so no negative result, no inconclusive result, and no diagnosis is asserted. The code is deliberately taken from the providers code system so no receiver can read it as an atrial-fibrillation finding.", + code: { + system: + 'https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement', + code: 'withings-atrial-fibrillation-notification-ecg', + display: 'Withings atrial fibrillation notification (ECG)', + definition: + "A Withings screening notification stating that the vendor's electrocardiogram algorithm flagged signs of atrial fibrillation in the recording taken at the Observation effective instant.", + }, + valueKind: 'codeableConcept', + owner: 'withings', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/withings/ValueSet/withings-atrial-fibrillation-notification-ecg', + resultCodeSystem: + 'https://grovealliance.org/fhir/withings/CodeSystem/withings-atrial-fibrillation-notification-ecg', + allowedValues: ['signs-detected'], + resultCodes: [ + { + code: 'signs-detected', + display: 'Signs Detected', + definition: + "The vendor's electrocardiogram screening algorithm flagged signs of atrial fibrillation in this recording. Nothing beyond the notification is asserted.", + }, + ], + effective: 'dateTime', + }, + 'withings-atrial-fibrillation-notification-ppg': { + id: 'withings-atrial-fibrillation-notification-ppg', + profile: 'withings-atrial-fibrillation-notification-ppg', + standardProfile: null, + title: 'Withings Atrial Fibrillation Notification (PPG)', + description: + "Withings meastype 139: a screening notification from the vendor's proprietary photoplethysmography algorithm, kept separate from the electrocardiogram notification because it screens a different signal. It is emitted as a notification and never as a rhythm finding. The adapter admits an Observation only for the vendor's positive screening classification; Withings publishes no encoding for the numeric measure.value, so no negative result, no inconclusive result, and no diagnosis is asserted. The code is deliberately taken from the providers code system so no receiver can read it as an atrial-fibrillation finding.", + code: { + system: + 'https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement', + code: 'withings-atrial-fibrillation-notification-ppg', + display: 'Withings atrial fibrillation notification (PPG)', + definition: + "A Withings screening notification stating that the vendor's photoplethysmography algorithm flagged signs of atrial fibrillation in the reading taken at the Observation effective instant.", + }, + valueKind: 'codeableConcept', + owner: 'withings', + quantity: null, + valueSet: + 'https://grovealliance.org/fhir/withings/ValueSet/withings-atrial-fibrillation-notification-ppg', + resultCodeSystem: + 'https://grovealliance.org/fhir/withings/CodeSystem/withings-atrial-fibrillation-notification-ppg', + allowedValues: ['signs-detected'], + resultCodes: [ + { + code: 'signs-detected', + display: 'Signs Detected', + definition: + "The vendor's photoplethysmography screening algorithm flagged signs of atrial fibrillation in this reading. Nothing beyond the notification is asserted.", + }, + ], + effective: 'dateTime', + }, + 'withings-nerve-health-score': { + id: 'withings-nerve-health-score', + profile: 'withings-nerve-health-score', + standardProfile: null, + title: 'Withings Nerve Health Score', + description: + "Withings meastype 167: the vendor's nerve-health figure, computed by an undisclosed algorithm from electrochemical skin conductance measured at the feet. The vendor publishes no physical unit for it, so it carries the dimensionless UCUM {score} annotation rather than an invented unit or the conductance it derives from, and the profile description is the only statement of its scale. Withings positions the figure as small-fiber-neuropathy screening, so it is deliberately coded from the providers code system: it is a vendor screening figure and never a neuropathy finding or a diagnosis.", + code: { + system: + 'https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement', + code: 'withings-nerve-health-score', + display: 'Withings nerve health score', + definition: + "The bounded figure Withings' proprietary nerve-health algorithm computes from electrochemical skin conductance at the feet, at the Observation effective instant.", + }, + valueKind: 'quantity', + owner: 'withings', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{score}', + unit: 'score', + example: 80, + dimension: {}, + }, + effective: 'dateTime', + }, + 'withings-pulse-wave-velocity': { + id: 'withings-pulse-wave-velocity', + profile: 'withings-pulse-wave-velocity', + standardProfile: null, + title: 'Withings Pulse Wave Velocity', + description: + "Withings meastype 91: the vendor's aortic pulse-wave velocity estimate, produced by an undisclosed single-vendor scale algorithm and normalized to UCUM metres per second. It is deliberately coded from the providers code system rather than a shared arterial-stiffness code, because no second source evidences it and the estimate is not interchangeable with a tonometric pulse-wave velocity measured in clinic. It is a wellness estimate and never an arterial-stiffness diagnosis.", + code: { + system: + 'https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement', + code: 'withings-pulse-wave-velocity', + display: 'Withings pulse wave velocity', + definition: + "The aortic pulse-wave velocity Withings' proprietary scale algorithm estimates at the Observation effective instant, in metres per second.", + }, + valueKind: 'quantity', + owner: 'withings', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'm/s', + unit: 'm/s', + example: 7.5, + dimension: { + m: 1, + s: -1, + }, + }, + effective: 'dateTime', + }, + 'withings-vascular-age': { + id: 'withings-vascular-age', + profile: 'withings-vascular-age', + standardProfile: null, + title: 'Withings Vascular Age', + description: + "Withings meastype 155: the vendor's vascular-age figure, derived by an undisclosed algorithm from the pulse-wave velocity the same scale estimates, and reported on an age scale in UCUM years. It is a vendor score expressed in years, not a chronological age and not a vascular assessment. It is deliberately a separate measurement from the Oura cardiovascular age: the two are undisclosed algorithms over different inputs, so a shared code would fabricate a comparability neither vendor defines.", + code: { + system: + 'https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement', + code: 'withings-vascular-age', + display: 'Withings vascular age', + definition: + "The age-scaled figure Withings' proprietary vascular-age algorithm derives from its own pulse-wave velocity estimate, at the Observation effective instant.", + }, + valueKind: 'quantity', + owner: 'withings', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'a', + unit: 'years', + example: 45, + dimension: { + s: 1, + }, + }, + effective: 'dateTime', + }, + 'withings-visceral-fat-index': { + id: 'withings-visceral-fat-index', + profile: 'withings-visceral-fat-index', + standardProfile: null, + title: 'Withings Visceral Fat Index', + description: + "Withings meastype 170: the vendor's visceral-fat figure, which the API returns without a unit. It is a bounded rating on the vendor's own scale rather than a mass or an area, so it carries the dimensionless UCUM {score} annotation instead of an invented mass or area unit, and it is kept apart from every shared body-composition measurement. The profile description is the only statement of its scale, and no comparability with another vendor's visceral-fat figure is asserted.", + code: { + system: + 'https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement', + code: 'withings-visceral-fat-index', + display: 'Withings visceral fat index', + definition: + "The bounded figure Withings' proprietary body-composition algorithm reports for abdominal visceral fat at the Observation effective instant.", + }, + valueKind: 'quantity', + owner: 'withings', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{score}', + unit: 'score', + example: 8, + dimension: {}, + }, + effective: 'dateTime', + }, + }, + oura: { + 'oura-cardiovascular-age': { + id: 'oura-cardiovascular-age', + profile: 'oura-cardiovascular-age', + standardProfile: null, + title: 'Oura Cardiovascular Age', + description: + "Oura's proprietary cardiovascular-age figure over a civil-day effective Period, reported on an age scale in UCUM years. It is a vendor score expressed in years, not a chronological age and not a vascular assessment. It is deliberately a separate measurement from the Withings vascular age: the two are undisclosed algorithms over different inputs, so a shared code would fabricate a comparability neither vendor defines.", + code: { + system: + 'https://grovealliance.org/fhir/oura/CodeSystem/oura-measurement', + code: 'oura-cardiovascular-age', + display: 'Oura cardiovascular age', + definition: + "The age-scaled figure Oura's proprietary cardiovascular-age algorithm derives from its own photoplethysmography features over the exact civil-day effective Period.", + }, + valueKind: 'quantity', + owner: 'oura', + quantity: { + system: 'http://unitsofmeasure.org', + code: 'a', + unit: 'years', + example: 38, + dimension: { + s: 1, + }, + }, + effective: 'Period', + }, + 'oura-readiness-score': { + id: 'oura-readiness-score', + profile: 'oura-readiness-score', + standardProfile: null, + title: 'Oura Readiness Score', + description: + "Oura's proprietary daily readiness figure over a civil-day effective Period. The vendor publishes no physical unit for it and it measures no observable quantity, so it carries the dimensionless UCUM {score} annotation rather than an invented unit, and the profile description is the only statement of its scale. It is a vendor composite, not a physiological measurement, and nothing about the inputs, weighting, or comparability across people or firmware versions is asserted.", + code: { + system: + 'https://grovealliance.org/fhir/oura/CodeSystem/oura-measurement', + code: 'oura-readiness-score', + display: 'Oura readiness score', + definition: + "The bounded composite figure Oura's proprietary readiness algorithm reports for the exact civil-day effective Period.", + }, + valueKind: 'quantity', + owner: 'oura', + quantity: { + system: 'http://unitsofmeasure.org', + code: '{score}', + unit: 'score', + example: 78, + dimension: {}, + }, + effective: 'Period', + }, + }, + 'google-health': { + 'resting-heart-rate-daily-average': { + id: 'resting-heart-rate-daily-average', + profile: 'google-health-daily-resting-heart-rate', + owner: 'google-health', + standardProfile: null, + title: 'Resting Heart Rate Daily Average', + description: + 'A source-supplied mean resting heart rate over an exact civil-day Period, normalized to UCUM beats per minute. It is an aggregate estimate and is never substituted for a point RestingHeartRateRecord or HealthKit resting-heart-rate sample.', + code: { + system: + 'https://grovealliance.org/fhir/google-health/CodeSystem/google-health-measurement', + code: 'resting-heart-rate-daily-average', + display: 'Resting heart rate daily average', + definition: + 'The mean resting heart-rate estimate reported for the exact civil-day Period.', + }, + valueKind: 'quantity', + quantity: { + system: 'http://unitsofmeasure.org', + code: '/min', + unit: 'beats/minute', + example: 58, + dimension: { + s: -1, + }, + }, + method: { + code: 'daily-mean', + display: 'Daily mean', + }, + effective: 'Period', + }, + }, +} as const + +export const adapterMeasurementCatalog: typeof adapterMeasurementCatalogValue = + deepFreeze(adapterMeasurementCatalogValue) + +const healthKitApplicationDeviceIdentityValue = { + profile: + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-application-device', + snapshotIdentifierRole: 'device-snapshot', + bundleIdentifier: { + system: + 'https://grovealliance.org/fhir/healthkit/NamingSystem/apple-bundle-id', + typeSystem: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-identifier-type', + typeCode: 'apple-bundle-id', + cardinality: '1..1', + meaning: + 'Exact Apple application product bundle identifier; never an installation, host, account, or person identifier.', + }, + classificationRule: + 'The converting application always uses this profile. An HKSourceRevision author uses it only when the caller explicitly classifies that source as an application; never infer actor class from name, bundle text, UUID shape, or productType.', +} as const + +export const healthKitApplicationDeviceIdentity: typeof healthKitApplicationDeviceIdentityValue = + deepFreeze(healthKitApplicationDeviceIdentityValue) + +const healthKitClinicalRecordAdmissionValue = { + profile: + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-clinical-record-document', + payloadFormat: 'fhir-r4-resource', + sourceFHIRReleaseField: 'HKFHIRVersion.fhirRelease', + admittedFHIRRelease: 'r4', + fhirRepresentation: { + resourceType: 'DocumentReference', + extensionUrl: + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-clinical-fhir-release', + valueElement: 'valueCode', + cardinality: { + min: 1, + max: 1, + }, + fixedValue: 'r4', + }, + rejectedFHIRReleases: ['dstu2', 'unknown'], + rule: 'Admit a HealthKit clinical record only when HKFHIRVersion.fhirRelease is r4. Reject dstu2, unknown, a missing release, and every future release before emitting a DocumentReference; the payload is preserved byte-for-byte and never upgraded, downgraded, or inferred from its JSON shape.', +} as const + +export const healthKitClinicalRecordAdmission: typeof healthKitClinicalRecordAdmissionValue = + deepFreeze(healthKitClinicalRecordAdmissionValue) + +const providerScalarOutputRolesValue = { + 'google-health-api': { + steps: { + 'step-count': 'step-count', + }, + distance: { + distance: 'distance', + }, + 'active-energy-burned': { + 'active-energy': 'active-energy', + }, + weight: { 'body-weight': 'body-weight', }, 'body-fat': { @@ -6177,7 +6845,7 @@ const providerScalarMappingsValue = { 'heart-rate-variability-sdnn': 'heart-rate-variability-sdnn', }, 'daily-resting-heart-rate': { - 'resting-heart-rate': 'resting-heart-rate', + 'resting-heart-rate-daily-average': 'resting-heart-rate-daily-average', }, 'daily-respiratory-rate': { 'respiratory-rate-average': 'respiratory-rate-average', @@ -6230,6 +6898,12 @@ const providerScalarMappingsValue = { vO2_max: { 'vo2-max': 'vo2-max', }, + daily_cardiovascular_age: { + 'oura-cardiovascular-age': 'oura-cardiovascular-age', + }, + daily_readiness: { + 'oura-readiness-score': 'oura-readiness-score', + }, }, withings: { 'getmeas:1': { @@ -6253,6 +6927,9 @@ const providerScalarMappingsValue = { 'getmeas:123': { 'vo2-max': 'vo2-max', }, + 'getmeas:155': { + 'withings-vascular-age': 'withings-vascular-age', + }, 'getactivity:steps': { 'step-count': 'step-count', }, @@ -6302,12 +6979,41 @@ const providerScalarMappingsValue = { 'getmeas:88': { 'bone-mass': 'bone-mass', }, + 'getmeas:91': { + 'withings-pulse-wave-velocity': 'withings-pulse-wave-velocity', + }, + 'getmeas:130': { + 'withings-atrial-fibrillation-notification-ecg': + 'withings-atrial-fibrillation-notification-ecg', + }, + 'getmeas:135': { + 'qrs-duration': 'qrs-duration', + }, + 'getmeas:136': { + 'pr-interval': 'pr-interval', + }, + 'getmeas:137': { + 'qt-interval': 'qt-interval', + }, + 'getmeas:138': { + 'corrected-qt-interval': 'corrected-qt-interval', + }, + 'getmeas:139': { + 'withings-atrial-fibrillation-notification-ppg': + 'withings-atrial-fibrillation-notification-ppg', + }, + 'getmeas:167': { + 'withings-nerve-health-score': 'withings-nerve-health-score', + }, 'getmeas:168': { 'extracellular-water-mass': 'extracellular-water-mass', }, 'getmeas:169': { 'intracellular-water-mass': 'intracellular-water-mass', }, + 'getmeas:170': { + 'withings-visceral-fat-index': 'withings-visceral-fat-index', + }, 'getmeas:196': { 'electrodermal-activity': 'electrodermal-activity', }, @@ -6317,8 +7023,217 @@ const providerScalarMappingsValue = { }, } as const -export const providerScalarMappings: typeof providerScalarMappingsValue = - deepFreeze(providerScalarMappingsValue) +export const providerScalarOutputRoles: typeof providerScalarOutputRolesValue = + deepFreeze(providerScalarOutputRolesValue) + +const providerScalarOutputDiscriminatorsValue = { + 'google-health-api': { + steps: { + 'step-count': 'single', + }, + distance: { + distance: 'single', + }, + 'active-energy-burned': { + 'active-energy': 'single', + }, + weight: { + 'body-weight': 'single', + }, + 'body-fat': { + 'body-fat-percentage': 'single', + }, + height: { + 'body-height': 'single', + }, + 'vo2-max': { + 'vo2-max': 'single', + }, + 'daily-oxygen-saturation': { + 'oxygen-saturation-daily-average': 'single', + }, + 'heart-rate-variability': { + 'heart-rate-variability-rmssd': 'single', + 'heart-rate-variability-sdnn': 'single', + }, + 'daily-resting-heart-rate': { + 'resting-heart-rate-daily-average': 'single', + }, + 'daily-respiratory-rate': { + 'respiratory-rate-average': 'single', + }, + 'blood-glucose': { + 'blood-glucose-unspecified-specimen': 'single', + }, + 'core-body-temperature': { + 'body-temperature': 'single', + }, + floors: { + 'flights-climbed': 'single', + }, + 'basal-energy-burned': { + 'basal-energy': 'single', + }, + sleep: { + 'sleep-duration': 'single', + 'deep-sleep-duration': 'single', + 'rem-sleep-duration': 'single', + 'light-sleep-duration': 'single', + 'sleep-awake-duration': 'single', + }, + exercise: { + workout: 'single', + }, + }, + oura: { + daily_activity: { + 'step-count': 'single', + 'active-energy': 'single', + distance: 'single', + }, + sleep: { + 'sleep-duration': 'single', + 'deep-sleep-duration': 'single', + 'rem-sleep-duration': 'single', + 'light-sleep-duration': 'single', + 'sleep-awake-duration': 'single', + 'sleep-heart-rate': 'single', + 'respiratory-rate-average': 'single', + }, + daily_spo2: { + 'oxygen-saturation-daily-average': 'single', + }, + workout: { + workout: 'single', + }, + vO2_max: { + 'vo2-max': 'single', + }, + daily_cardiovascular_age: { + 'oura-cardiovascular-age': 'single', + }, + daily_readiness: { + 'oura-readiness-score': 'single', + }, + }, + withings: { + 'getmeas:1': { + 'body-weight': 'single', + }, + 'getmeas:4': { + 'body-height': 'single', + }, + 'getmeas:6': { + 'body-fat-percentage': 'single', + }, + 'getmeas:11': { + 'heart-rate': 'single', + }, + 'getmeas:54': { + 'oxygen-saturation': 'single', + }, + 'getmeas:71': { + 'body-temperature': 'single', + }, + 'getmeas:123': { + 'vo2-max': 'single', + }, + 'getmeas:155': { + 'withings-vascular-age': 'single', + }, + 'getactivity:steps': { + 'step-count': 'single', + }, + 'getactivity:distance': { + distance: 'single', + }, + 'getactivity:calories': { + 'active-energy': 'single', + }, + 'getsummary:deepsleepduration': { + 'deep-sleep-duration': 'single', + }, + 'getsummary:remsleepduration': { + 'rem-sleep-duration': 'single', + }, + 'getsummary:lightsleepduration': { + 'light-sleep-duration': 'single', + }, + 'getsummary:wakeupduration': { + 'sleep-awake-duration': 'single', + }, + 'getsummary:hr_average': { + 'sleep-heart-rate': 'single', + 'sleeping-heart-rate-average': 'single', + }, + 'getsummary:rr_average': { + 'respiratory-rate-average': 'single', + }, + 'getworkouts:interval': { + workout: 'single', + }, + 'getmeas:5': { + 'lean-body-mass': 'single', + }, + 'getmeas:8': { + 'body-fat-mass': 'single', + }, + 'getmeas:73': { + 'skin-temperature': 'single', + }, + 'getmeas:76': { + 'muscle-mass': 'single', + }, + 'getmeas:77': { + 'body-water-mass': 'single', + }, + 'getmeas:88': { + 'bone-mass': 'single', + }, + 'getmeas:91': { + 'withings-pulse-wave-velocity': 'single', + }, + 'getmeas:130': { + 'withings-atrial-fibrillation-notification-ecg': 'single', + }, + 'getmeas:135': { + 'qrs-duration': 'single', + }, + 'getmeas:136': { + 'pr-interval': 'single', + }, + 'getmeas:137': { + 'qt-interval': 'single', + }, + 'getmeas:138': { + 'corrected-qt-interval': 'single', + }, + 'getmeas:139': { + 'withings-atrial-fibrillation-notification-ppg': 'single', + }, + 'getmeas:167': { + 'withings-nerve-health-score': 'single', + }, + 'getmeas:168': { + 'extracellular-water-mass': 'single', + }, + 'getmeas:169': { + 'intracellular-water-mass': 'single', + }, + 'getmeas:170': { + 'withings-visceral-fat-index': 'single', + }, + 'getmeas:196': { + 'electrodermal-activity': 'single', + }, + 'getmeas:9+10': { + 'blood-pressure': 'single', + }, + }, +} as const + +export const providerScalarOutputDiscriminators: typeof providerScalarOutputDiscriminatorsValue = + deepFreeze(providerScalarOutputDiscriminatorsValue) const providerRecordEffectiveRulesValue = { oura: { @@ -6327,6 +7242,16 @@ const providerRecordEffectiveRulesValue = { measurementIds: ['step-count', 'active-energy', 'distance'], outputsShareEffective: true, }, + daily_cardiovascular_age: { + kind: 'complete-civil-day-period', + measurementIds: ['oura-cardiovascular-age'], + outputsShareEffective: true, + }, + daily_readiness: { + kind: 'complete-civil-day-period', + measurementIds: ['oura-readiness-score'], + outputsShareEffective: true, + }, }, withings: { 'getactivity:steps': { @@ -6350,7 +7275,7 @@ const providerRecordEffectiveRulesValue = { export const providerRecordEffectiveRules: typeof providerRecordEffectiveRulesValue = deepFreeze(providerRecordEffectiveRulesValue) -const providerRawMappingsValue = { +const providerRawOutputRolesValue = { 'google-health-api': { 'heart-rate': 'native-recording', }, @@ -6363,109 +7288,47 @@ const providerRawMappingsValue = { }, } as const -export const providerRawMappings: typeof providerRawMappingsValue = deepFreeze( - providerRawMappingsValue, -) +export const providerRawOutputRoles: typeof providerRawOutputRolesValue = + deepFreeze(providerRawOutputRolesValue) -const groveRecordingFormatRegistryValue = { - codeSystem: - 'https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format', - valueSet: - 'https://grovealliance.org/fhir/sensor/ValueSet/grove-recording-format', - formats: { - 'heart-rate-samples': { - title: 'Heart Rate Samples', - contentType: 'text/csv', - status: 'active', - }, - 'triaxial-acceleration-samples': { - title: 'Triaxial Acceleration Samples', - contentType: 'text/csv', - status: 'active', - }, - 'ambient-light-samples': { - title: 'Ambient Light Samples', - contentType: 'text/csv', - status: 'active', - }, - 'ambient-pressure-samples': { - title: 'Ambient Pressure Samples', - contentType: 'text/csv', - status: 'active', - }, - 'pedometer-samples': { - title: 'Pedometer Samples', - contentType: 'text/csv', - status: 'active', - }, - 'wrist-temperature-samples': { - title: 'Wrist Temperature Samples', - contentType: 'text/csv', - status: 'active', - }, - 'triaxial-rotation-samples': { - title: 'Triaxial Rotation Samples', - contentType: 'text/csv', - status: 'active', - }, - 'odometer-samples': { - title: 'Odometer Samples', - contentType: 'text/csv', - status: 'active', - }, - 'beat-interval-series': { - title: 'Beat Interval Series', - contentType: 'text/csv', - status: 'active', - }, - 'location-track-samples': { - title: 'Location Track Samples', - contentType: 'text/csv', - status: 'active', - }, - 'fhir-resource-array': { - title: 'FHIR Resource Array', - contentType: 'application/vnd.grovealliance.fhir-array+json', - status: 'active', - }, - 'fhir-resource': { - title: 'FHIR Resource', - contentType: 'application/fhir+json', - status: 'active', - }, - 'clinical-document': { - title: 'Clinical Document', - contentType: 'application/hl7-cda+xml', - status: 'active', - }, - 'native-recording': { - title: 'Native Recording', - contentType: 'application/vnd.grovealliance.native+json', - status: 'active', - }, - 'provider-recording': { - title: 'Provider Recording', - contentType: 'application/vnd.grovealliance.provider+json', - status: 'active', - }, - 'photoplethysmogram-samples': { - title: 'Photoplethysmogram Samples', - contentType: 'application/vnd.grovealliance.ppg', - status: 'active', - }, +const providerRawOutputDiscriminatorsValue = { + 'google-health-api': { + 'heart-rate': 'single', + }, + oura: { + heartrate: 'single', + }, + withings: { + activityIntraday: 'single', + sleepIntraday: 'single', }, } as const -export const groveRecordingFormatRegistry: typeof groveRecordingFormatRegistryValue = - deepFreeze(groveRecordingFormatRegistryValue) +export const providerRawOutputDiscriminators: typeof providerRawOutputDiscriminatorsValue = + deepFreeze(providerRawOutputDiscriminatorsValue) export type AdapterMeasurementCatalog = typeof adapterMeasurementCatalog -export type ProviderRecordingFormat = - keyof typeof groveRecordingFormatRegistry.formats +export type HealthKitApplicationDeviceIdentity = + typeof healthKitApplicationDeviceIdentity + +export type HealthKitClinicalRecordAdmission = + typeof healthKitClinicalRecordAdmission + +export { + groveRecordingFormatRegistry, + type GroveRecordingFormat, + type ProviderRecordingFormat, +} from '../mobile/measurement-catalog.generated.js' -export type ProviderScalarMappings = typeof providerScalarMappings +export type ProviderScalarOutputRoles = typeof providerScalarOutputRoles + +export type ProviderScalarOutputDiscriminators = + typeof providerScalarOutputDiscriminators export type ProviderRecordEffectiveRules = typeof providerRecordEffectiveRules -export type ProviderRawMappings = typeof providerRawMappings +export type ProviderRawOutputRoles = typeof providerRawOutputRoles + +export type ProviderRawOutputDiscriminators = + typeof providerRawOutputDiscriminators diff --git a/packages/fhir/src/providers/graph.ts b/packages/fhir/src/providers/graph.ts index 413a2573..2dc8b752 100644 --- a/packages/fhir/src/providers/graph.ts +++ b/packages/fhir/src/providers/graph.ts @@ -7,9 +7,12 @@ // import { EXTENSIONS, PROFILES, SYSTEMS } from './profiles.js' +import type { GovernedSourceIdentifierInput } from './types.js' +import { err, ok, type Result } from '../core/index.js' import type { ApplicationDeviceInput, CompleteIdentifierInput, + HostDeviceInput, IdentifiedEntryIdentityInput, RecordingDeviceInput, ResourceIdentityInput, @@ -17,14 +20,14 @@ import type { import type { CodeableConcept, Coding, - CollectionBundle, Device, GraphResource, + GroveMobileExchangeBundle, Identifier, Provenance, } from '../r4/index.js' -type GraphEntry = CollectionBundle['entry'][number] +type GraphEntry = GroveMobileExchangeBundle['entry'][number] type ProvenanceAgent = Provenance['agent'][number] export const resourceId = ( @@ -35,6 +38,38 @@ export const resourceId = ( export const identifier = (input: CompleteIdentifierInput): Identifier => ({ system: input.system, value: input.value, + ...(input.role === undefined ? + {} + : { + type: concept(SYSTEMS.groveIdentifierRole, input.role), + }), +}) + +/** + * Emits the caller-governed, source-native traceability Identifier verbatim. + * It deliberately has no Grove graph-role coding and never participates in identity. + */ +export const governedSourceIdentifier = ( + input: GovernedSourceIdentifierInput, +): Identifier => ({ + system: input.system, + value: input.nativeId, + ...(input.type === undefined ? + {} + : { + type: { + ...(input.type.coding === undefined ? + {} + : { + coding: input.type.coding.map(({ system, code, display }) => ({ + system, + code, + ...(display === undefined ? {} : { display }), + })), + }), + ...(input.type.text === undefined ? {} : { text: input.type.text }), + }, + }), }) export const coding = ( @@ -57,21 +92,15 @@ export const concept = ( }) export const makeApplicationDevice = ( - input: ApplicationDeviceInput, -): Device => ({ - resourceType: 'Device' as const, - ...resourceId(input.identity), - meta: { profile: [PROFILES.applicationDevice] }, - identifier: [identifier(input.identity.identifier)], - status: 'active' as const, - ...(input.manufacturer === undefined ? - {} - : { manufacturer: input.manufacturer }), - deviceName: [{ name: input.name, type: 'user-friendly-name' as const }], - ...(input.version === undefined ? - {} - : { - version: [ + input: Omit & { + readonly identity: IdentifiedEntryIdentityInput + readonly parentReference?: string + }, +): Device => { + const versions = [ + ...(input.version === undefined ? + [] + : [ { type: { coding: [ @@ -84,15 +113,90 @@ export const makeApplicationDevice = ( }, value: input.version, }, - ], + ]), + ...(input.build === undefined ? + [] + : [ + { + type: { + coding: [ + coding( + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type', + 'build', + 'Application build', + ), + ], + }, + value: input.build, + }, + ]), + ] + return { + resourceType: 'Device' as const, + ...resourceId(input.identity), + meta: { profile: [PROFILES.applicationDevice] }, + identifier: [identifier(input.identity.identifier)], + status: 'active' as const, + ...(input.manufacturer === undefined ? + {} + : { manufacturer: input.manufacturer }), + deviceName: [{ name: input.name, type: 'user-friendly-name' as const }], + ...(versions.length === 0 ? {} : { version: versions }), + ...(input.parentReference === undefined ? + {} + : { parent: { reference: input.parentReference } }), + } +} + +export const makeHostDevice = ( + input: Omit & { + readonly identity: IdentifiedEntryIdentityInput + }, +): Device => ({ + resourceType: 'Device' as const, + ...resourceId(input.identity), + meta: { profile: [PROFILES.hostDevice] }, + identifier: [identifier(input.identity.identifier)], + status: 'active' as const, + ...(input.name === undefined ? + {} + : { + deviceName: [{ name: input.name, type: 'user-friendly-name' as const }], }), + ...(input.manufacturer === undefined ? + {} + : { manufacturer: input.manufacturer }), + ...(input.modelNumber === undefined ? + {} + : { modelNumber: input.modelNumber }), + version: [ + { + type: concept( + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type', + 'os-version', + 'Operating system version', + ), + value: input.operatingSystemVersion, + }, + ], }) -export const makeRecordingDevice = (input: RecordingDeviceInput): Device => ({ +export const makeRecordingDevice = ( + input: Omit< + RecordingDeviceInput, + 'id' | 'stableUnitToken' | 'subjectIdentifier' + > & { + readonly identity: IdentifiedEntryIdentityInput + readonly stableIdentifier: CompleteIdentifierInput + }, +): Device => ({ resourceType: 'Device' as const, ...resourceId(input.identity), meta: { profile: [PROFILES.recordingDevice] }, - identifier: [identifier(input.identity.identifier)], + identifier: [ + identifier(input.stableIdentifier), + identifier(input.identity.identifier), + ], status: 'active' as const, ...(input.name === undefined ? {} @@ -114,13 +218,37 @@ export const identifiedEntry = ( fullUrl: identity.fullUrl, extension: [ { - url: EXTENSIONS.exchangeEntryIdentifier, + url: EXTENSIONS.entryNodeKey, valueIdentifier: identifier(identity.identifier), }, ], resource, }) +/** + * Reuses one identical resource when it legitimately fills multiple graph roles. + * A repeated fullUrl carrying conflicting resource facts remains an identity collision. + */ +export const deduplicateIdentifiedEntries = ( + entries: readonly GraphEntry[], +): Result => { + const byFullUrl = new Map() + for (const entry of entries) { + const existing = byFullUrl.get(entry.fullUrl) + if (existing === undefined) { + byFullUrl.set(entry.fullUrl, entry) + continue + } + if (JSON.stringify(existing.resource) !== JSON.stringify(entry.resource)) { + return err( + 'duplicate-identifier', + 'One Bundle fullUrl resolves to conflicting resource snapshots.', + ) + } + } + return ok([...byFullUrl.values()]) +} + export const provenanceActivity = (): CodeableConcept => concept( SYSTEMS.isoLifecycle, diff --git a/packages/fhir/src/providers/identity.ts b/packages/fhir/src/providers/identity.ts index f5338e26..cf776c85 100644 --- a/packages/fhir/src/providers/identity.ts +++ b/packages/fhir/src/providers/identity.ts @@ -6,192 +6,576 @@ // SPDX-License-Identifier: MIT // -import { providerAdapterCatalog } from './contract.generated.js' -import type { ConnectedProvider } from './types.js' import { + providerAdapterCatalog, + providerRawOutputDiscriminators, + providerRawOutputRoles, + providerScalarOutputDiscriminators, + providerScalarOutputRoles, +} from './contract.generated.js' +import type { + ConnectedProvider, + ProviderScopeIdentifierInput, +} from './types.js' +import { + cloneJsonValue, deepFreeze, err, ok, parseAbsoluteUri, - parseFhirId, - parsePositiveInteger, - type AbsoluteUri, - type PositiveInteger, + type FhirId, + type JsonValue, type Result, } from '../core/index.js' import { - containsIsolatedSurrogate, createEntryIdentity, + deriveEntryNodeIdentifier, + deriveEventIdentifier, + deriveOpaqueIdentifier, + isEventIdentityValue, + validateDeploymentIdentity, } from '../mobile/identity.js' +import { groveProfileClaims } from '../mobile/measurement-catalog.generated.js' import type { + ApplicationDeviceInput, CompleteIdentifierInput, + DeploymentIdentityInput, IdentifiedEntryIdentityInput, + RecordingDeviceInput, } from '../mobile/types.js' -const SEPARATOR = '|' +/** Resource kind only; graph participation roles never partition Device identity. */ +export type DeviceSnapshotRole = 'application' | 'host' | 'recording-device' -/** Joins components behind the scheme version. Nothing is hashed, escaped, or re-encoded. */ -const compose = (components: readonly string[]): Result => { - for (const component of components) { - if (component.includes(SEPARATOR)) { - return err( - 'invalid-identifier', - 'An identity component must not contain a vertical bar; such a value is rejected, never escaped.', - ) - } - if (containsIsolatedSurrogate(component)) { - return err( - 'invalid-identifier', - 'Identity components must contain Unicode scalar values only.', - ) - } +const deviceSnapshotRoles: ReadonlySet = new Set([ + 'application', + 'host', + 'recording-device', +]) + +type JsonObject = Readonly> + +const asJsonObject = (value: JsonValue | undefined): JsonObject | undefined => + typeof value === 'object' && value !== null && !Array.isArray(value) ? + (value as JsonObject) + : undefined + +const snapshotObject = (input: unknown): Result => { + const snapshot = cloneJsonValue(input) + if (!snapshot.ok) return snapshot + if ( + typeof snapshot.value !== 'object' || + snapshot.value === null || + Array.isArray(snapshot.value) + ) { + return err('invalid-type', 'Expected a complete identity input object.') + } + return ok(snapshot.value as JsonObject) +} + +/** Derives an immutable event-scoped Device snapshot identity. */ +export const deriveDeviceSnapshotEntryIdentity = ( + deployment: DeploymentIdentityInput, + event: CompleteIdentifierInput, + sourceDeviceToken: string, + role: DeviceSnapshotRole, + id?: import('../core/index.js').FhirId, +): Result => { + const safeDeployment = validateDeploymentIdentity(deployment) + if (!safeDeployment.ok) return safeDeployment + const safeEvent = snapshotObject(event) + if (!safeEvent.ok) return safeEvent + const eventSystem = safeEvent.value.system + const eventValue = safeEvent.value.value + const eventRole = safeEvent.value.role + if ( + typeof sourceDeviceToken !== 'string' || + sourceDeviceToken.trim() === '' || + !deviceSnapshotRoles.has(role) || + eventSystem !== safeDeployment.value.eventIdentifierSystem || + eventRole !== 'event' || + typeof eventValue !== 'string' || + !isEventIdentityValue(eventValue) || + !eventValue.startsWith(`e2:${safeDeployment.value.producerInstance}:`) + ) { + return err( + 'invalid-identifier', + "Device snapshot identity requires a nonblank token and this producer's complete typed event Identifier.", + ) } - return ok(`v1:${components.join(SEPARATOR)}`) + const identifier = deriveOpaqueIdentifier( + safeDeployment.value, + 'device-snapshot', + [eventSystem, eventValue, role, sourceDeviceToken], + ) + if (!identifier.ok) return identifier + return createEntryIdentity(identifier.value, id) } -const connectedIdentitySystem = ( - role: 'output' | 'sourceRecord', -): AbsoluteUri => providerAdapterCatalog.identity[role].system as AbsoluteUri - -/** - * Whether this provider's native record key is unique across the whole provider, or only within - * one account. An account-scoped provider carries its deployment pseudonym as a component, so the - * identifier still stands alone and a receiver deduplicates by comparing system and value. - */ -const identifierScope = (provider: ConnectedProvider): 'account' | 'none' => { - const entry = providerAdapterCatalog.providers.find( - (candidate) => candidate.id === provider, - ) as { readonly identifierScope?: 'account' | 'none' } | undefined - // Scoped by account unless the vendor documents that its record key is unique across accounts. - return entry?.identifierScope ?? 'account' +/** Derives an immutable event-scoped application Device snapshot identity. */ +export const deriveApplicationEntryIdentity = ( + deployment: DeploymentIdentityInput, + event: CompleteIdentifierInput, + input: ApplicationDeviceInput, +): Result => { + const safeInput = snapshotObject(input) + if (!safeInput.ok) return safeInput + const sourceDeviceToken = safeInput.value.sourceDeviceToken + const id = safeInput.value.id + if ( + typeof sourceDeviceToken !== 'string' || + (id !== undefined && typeof id !== 'string') + ) { + return err( + 'invalid-identifier', + 'Application identity requires a source Device token and optional valid FHIR id.', + ) + } + return deriveDeviceSnapshotEntryIdentity( + deployment, + event, + sourceDeviceToken, + 'application', + id as FhirId | undefined, + ) } -export interface ProviderIdentityInput { - readonly provider: ConnectedProvider - readonly providerAccountIdentifier: CompleteIdentifierInput +export interface RecordingDeviceGraphIdentity { + readonly stableIdentifier: CompleteIdentifierInput + /** Selected Bundle entry key for this immutable event-time Device snapshot. */ + readonly snapshot: IdentifiedEntryIdentityInput +} + +/** Derives a stable per-unit recording Device identity from admitted instance evidence. */ +export const deriveRecordingDeviceEntryIdentity = ( + deployment: DeploymentIdentityInput, + event: CompleteIdentifierInput, + adapterId: string, + input: RecordingDeviceInput, +): Result => { + const safeInput = snapshotObject(input) + if (!safeInput.ok) return safeInput + const stableUnitToken = safeInput.value.stableUnitToken + const subjectIdentifier = asJsonObject(safeInput.value.subjectIdentifier) + const subjectSystem = subjectIdentifier?.system + const subjectValue = subjectIdentifier?.value + const id = safeInput.value.id + const identityScope = safeInput.value.identityScope + const disclosureAuthorization = safeInput.value.disclosureAuthorization + const validIdentityScope = + identityScope === 'deployment-scoped' || + (identityScope === 'authorized-hardware' && + disclosureAuthorization === 'authorized-for-exchange') + if ( + typeof adapterId !== 'string' || + adapterId.trim() === '' || + !recordingDeviceAdapters.has(adapterId) || + typeof stableUnitToken !== 'string' || + stableUnitToken.trim() === '' || + typeof subjectSystem !== 'string' || + typeof subjectValue !== 'string' || + subjectValue.trim() === '' || + !parseAbsoluteUri(subjectSystem).ok || + (id !== undefined && typeof id !== 'string') || + !validIdentityScope + ) { + return err( + 'invalid-identifier', + 'Recording Device identity requires an adapter, a complete subject Identifier, and stable per-unit token.', + ) + } + const stableIdentifier = deriveOpaqueIdentifier( + deployment, + 'recording-device', + [adapterId, subjectSystem, subjectValue, stableUnitToken], + ) + if (!stableIdentifier.ok) return stableIdentifier + const snapshot = deriveDeviceSnapshotEntryIdentity( + deployment, + event, + stableUnitToken, + 'recording-device', + id as FhirId | undefined, + ) + if (!snapshot.ok) return snapshot + return ok({ + stableIdentifier: stableIdentifier.value, + snapshot: snapshot.value, + }) +} + +/** Optional writer-record lineage identity for a logical record assigned by an application. */ +export const deriveWriterRecordIdentifier = ( + deployment: DeploymentIdentityInput, + writerApplication: CompleteIdentifierInput, + writerRecordId: string, +): Result => { + const safeWriter = snapshotObject(writerApplication) + if (!safeWriter.ok) return safeWriter + const writerSystem = safeWriter.value.system + const writerValue = safeWriter.value.value + const parsedWriterSystem = parseAbsoluteUri(writerSystem) + if ( + !parsedWriterSystem.ok || + typeof writerValue !== 'string' || + writerValue.trim() === '' || + typeof writerRecordId !== 'string' || + writerRecordId.trim() === '' + ) { + return err( + 'invalid-identifier', + 'Writer identity requires a complete application Identifier and writer record id.', + ) + } + return deriveOpaqueIdentifier(deployment, 'writer-record', [ + parsedWriterSystem.value, + writerValue, + writerRecordId, + ]) +} + +export type ProviderOutputIdentityInput = + | { + readonly kind: 'provider-artifact' + readonly formatCode: string + readonly partIndex: string + } + | { + readonly kind: 'provider-output' + readonly outputRole: string + readonly outputDiscriminator: string + } + +export interface ProviderIdentityInput< + Provider extends ConnectedProvider = ConnectedProvider, +> { + readonly provider: Provider + readonly providerScopeIdentifier: ProviderScopeIdentifierInput readonly sourceType: string readonly sourceNativeId: string /** Closed catalog selectors; callers do not supply arbitrary values. */ - readonly outputDiscriminators: readonly [string, ...string[]] - readonly eventSequence: PositiveInteger - /** The namespace this deployment owns for graph nodes the export creates. */ - readonly graphIdentifierSystem: AbsoluteUri + readonly outputs: readonly ProviderOutputIdentityInput[] + readonly eventSequence: string + readonly deployment: DeploymentIdentityInput + readonly provenanceNodeRole?: + 'conversion-provenance' | 'retraction-provenance' } export interface ProviderIdentities { readonly sourceRecord: CompleteIdentifierInput - /** Empty for a one-to-one conversion; one entry per output where a record fans out. */ readonly outputs: readonly CompleteIdentifierInput[] - readonly conversion: CompleteIdentifierInput - readonly exchange: CompleteIdentifierInput + /** Sole business identifier for the exchange event and its Bundle. */ + readonly event: CompleteIdentifierInput + /** Typed event-scoped node key for Provenance; not a business identifier. */ + readonly provenanceNode: CompleteIdentifierInput } -/** Internal closed-facade derivation of every Provider business id. */ +const providerCodes: ReadonlySet = new Set( + providerAdapterCatalog.providers.map(({ id }) => id), +) + +const recordingDeviceAdapters: ReadonlySet = new Set([ + ...groveProfileClaims.adapterConversionProvenanceClaims.map( + ({ adapter }) => adapter, + ), + ...providerCodes, +]) + +const providerOutputIdentity = ( + value: JsonValue, +): ProviderOutputIdentityInput | undefined => { + const output = asJsonObject(value) + if (output === undefined) return undefined + const keys = Object.keys(output) + .sort((left, right) => left.localeCompare(right)) + .join(',') + if ( + output.kind === 'provider-output' && + keys === 'kind,outputDiscriminator,outputRole' && + typeof output.outputRole === 'string' && + output.outputRole.trim() !== '' && + typeof output.outputDiscriminator === 'string' && + output.outputDiscriminator.trim() !== '' + ) { + return { + kind: output.kind, + outputRole: output.outputRole, + outputDiscriminator: output.outputDiscriminator, + } + } + if ( + output.kind === 'provider-artifact' && + keys === 'formatCode,kind,partIndex' && + typeof output.formatCode === 'string' && + output.formatCode.trim() !== '' && + typeof output.partIndex === 'string' && + /^(?:0|[1-9]\d*)$/u.test(output.partIndex) + ) { + return { + kind: output.kind, + formatCode: output.formatCode, + partIndex: output.partIndex, + } + } + return undefined +} + +const isProvenanceNodeRole = ( + value: JsonValue | undefined, +): value is 'conversion-provenance' | 'retraction-provenance' => + value === 'conversion-provenance' || value === 'retraction-provenance' + +const admittedProviderOutput = ( + provider: string, + sourceType: string, + output: ProviderOutputIdentityInput, +): boolean => { + const scalarMapping = ( + providerScalarOutputRoles as Readonly< + Record< + string, + Readonly> | undefined>> + > + > + )[provider]?.[sourceType] + const rawRole = ( + providerRawOutputRoles as Readonly< + Record> | undefined> + > + )[provider]?.[sourceType] + const rawDiscriminator = ( + providerRawOutputDiscriminators as Readonly< + Record> | undefined> + > + )[provider]?.[sourceType] + if (output.kind === 'provider-artifact') { + return ( + rawRole !== undefined && + output.formatCode === 'provider-recording' && + output.partIndex === '0' + ) + } + const scalarDiscriminators = ( + providerScalarOutputDiscriminators as Readonly< + Record< + string, + Readonly> | undefined>> + > + > + )[provider]?.[sourceType] + if (rawRole === output.outputRole) { + return rawDiscriminator === output.outputDiscriminator + } + if (scalarMapping === undefined || scalarDiscriminators === undefined) { + return false + } + return Object.keys(scalarMapping).some( + (measurementId) => + scalarMapping[measurementId] === output.outputRole && + scalarDiscriminators[measurementId] === output.outputDiscriminator, + ) +} + +const admittedProviderSource = ( + provider: string, + sourceType: string, +): boolean => + ( + providerScalarOutputRoles as Readonly< + Record> | undefined> + > + )[provider]?.[sourceType] !== undefined || + ( + providerRawOutputRoles as Readonly< + Record> | undefined> + > + )[provider]?.[sourceType] !== undefined + +const providerSourceComponents = ( + input: ProviderIdentityInput, +): readonly [string, string, string, string, string] => [ + input.provider, + input.sourceType, + input.providerScopeIdentifier.system, + input.providerScopeIdentifier.value, + input.sourceNativeId, +] + +interface AdmittedProviderSelector { + readonly provider: ConnectedProvider + readonly sourceType: string +} + +const admittedProviderSelector = ( + provider: JsonValue | undefined, + sourceType: JsonValue | undefined, +): AdmittedProviderSelector | undefined => + ( + typeof provider === 'string' && + providerCodes.has(provider) && + typeof sourceType === 'string' && + admittedProviderSource(provider, sourceType) + ) ? + { provider: provider as ConnectedProvider, sourceType } + : undefined + +const providerOutputsAreAdmitted = ( + provider: ConnectedProvider, + sourceType: string, + outputs: ReadonlyArray, + provenanceNodeRole: JsonValue | undefined, +): outputs is readonly ProviderOutputIdentityInput[] => + (outputs.length > 0 || provenanceNodeRole === 'retraction-provenance') && + outputs.every( + (output) => + output !== undefined && + admittedProviderOutput(provider, sourceType, output), + ) + +/** Internal closed-facade derivation of Provider business and graph-node identifiers. */ export const deriveProviderIdentities = ( input: ProviderIdentityInput, ): Result => { - if (!parseAbsoluteUri(input.providerAccountIdentifier.system).ok) { + const safeInput = snapshotObject(input) + if (!safeInput.ok) return safeInput + const value = safeInput.value + const selector = admittedProviderSelector(value.provider, value.sourceType) + const rawOutputs = value.outputs + if (selector === undefined || !Array.isArray(rawOutputs)) { return err( - 'invalid-uri', - 'Provider account Identifier.system must be an absolute URI.', - ['providerAccountIdentifier', 'system'], + 'unsupported-measurement', + 'Provider identity selectors must match one exact pinned provider catalog row.', + ['outputs'], ) } + const { provider, sourceType } = selector + const parsedOutputs = rawOutputs.map(providerOutputIdentity) + const provenanceNodeRole = value.provenanceNodeRole if ( - input.providerAccountIdentifier.value.trim() === '' || - input.sourceType.trim() === '' || - input.sourceNativeId.trim() === '' || - input.outputDiscriminators.some( - (outputDiscriminator) => outputDiscriminator.trim() === '', + !providerOutputsAreAdmitted( + provider, + sourceType, + parsedOutputs, + provenanceNodeRole, ) ) { return err( - 'invalid-identifier', - 'Provider identity inputs must not be empty.', + 'unsupported-measurement', + 'Provider identity selectors must match one exact pinned provider catalog row.', + ['outputs'], + ) + } + const typedOutputs = parsedOutputs + const providerScope = asJsonObject(value.providerScopeIdentifier) + const providerScopeSystem = parseAbsoluteUri(providerScope?.system) + if (!providerScopeSystem.ok) { + return err( + 'invalid-uri', + 'Provider scope Identifier.system must be an absolute URI.', + ['providerScopeIdentifier', 'system'], ) } + const providerRow = providerAdapterCatalog.providers.find( + ({ id }) => id === provider, + ) + if (providerRow === undefined) { + return err( + 'unsupported-measurement', + 'Provider identity selectors must match one exact pinned provider catalog row.', + ['provider'], + ) + } + const expectedAssurance = providerRow.providerScopeMode if ( - new Set(input.outputDiscriminators).size !== - input.outputDiscriminators.length + typeof providerScope?.value !== 'string' || + providerScope.value.trim() === '' || + providerScope.assurance !== expectedAssurance || + sourceType.trim() === '' || + typeof value.sourceNativeId !== 'string' || + value.sourceNativeId.trim() === '' || + (provenanceNodeRole !== undefined && + !isProvenanceNodeRole(provenanceNodeRole)) ) { + return err( + 'invalid-identifier', + 'Provider identity inputs must be complete canonical values.', + ) + } + const normalizedProvenanceNodeRole = + isProvenanceNodeRole(provenanceNodeRole) ? provenanceNodeRole : undefined + const outputKeys = typedOutputs.map((output) => JSON.stringify(output)) + if (new Set(outputKeys).size !== outputKeys.length) { return err( 'duplicate-identifier', - 'Provider output discriminators must be unique.', - ['outputDiscriminators'], + 'Provider output identity selectors must be unique.', + ['outputs'], ) } - const sequence = parsePositiveInteger(input.eventSequence) - if (!sequence.ok) return sequence - - // A provider whose native key is unique across the whole provider carries that key verbatim: - // the system already says which provider and which key space it belongs to. Only an - // account-scoped provider composes, because its key alone would not identify one record. - const accountScoped = identifierScope(input.provider) === 'account' - const sourceComponents = - accountScoped ? - [ - input.provider, - input.providerAccountIdentifier.value, - input.sourceType, - input.sourceNativeId, - ] - : [input.sourceNativeId] - const sourceValue = - accountScoped ? compose(sourceComponents) : ok(input.sourceNativeId) - if (!sourceValue.ok) return sourceValue - const sourceRecord = { - system: connectedIdentitySystem('sourceRecord'), - value: sourceValue.value, - } - - // A source record yielding one Observation gives it no output identifier: a second namespace - // repeating the source-record value would identify nothing new. Only a fan-out needs to tell - // its outputs apart. - const outputs: CompleteIdentifierInput[] = [] - if (input.outputDiscriminators.length > 1) { - for (const outputDiscriminator of input.outputDiscriminators) { - const outputValue = compose([...sourceComponents, outputDiscriminator]) - if (!outputValue.ok) return outputValue - outputs.push({ - system: connectedIdentitySystem('output'), - value: outputValue.value, - }) - } + const deployment = validateDeploymentIdentity(value.deployment) + if (!deployment.ok) return deployment + + const normalizedInput: ProviderIdentityInput = { + provider: provider, + providerScopeIdentifier: { + system: providerScopeSystem.value, + value: providerScope.value, + assurance: expectedAssurance, + }, + sourceType, + sourceNativeId: value.sourceNativeId, + outputs: typedOutputs, + eventSequence: + typeof value.eventSequence === 'string' ? value.eventSequence : '', + deployment: deployment.value, + ...(normalizedProvenanceNodeRole === undefined ? + {} + : { provenanceNodeRole: normalizedProvenanceNodeRole }), + } + const source = providerSourceComponents(normalizedInput) + const sourceRecord = deriveOpaqueIdentifier( + deployment.value, + 'provider-record', + source, + ) + if (!sourceRecord.ok) return sourceRecord + + const derivedOutputs: CompleteIdentifierInput[] = [] + for (const output of typedOutputs) { + const derived = + output.kind === 'provider-output' ? + deriveOpaqueIdentifier(deployment.value, 'provider-output', [ + ...source, + output.outputRole, + output.outputDiscriminator, + ]) + : deriveOpaqueIdentifier(deployment.value, 'provider-artifact', [ + ...source, + output.formatCode, + output.partIndex, + ]) + if (!derived.ok) return derived + derivedOutputs.push(derived.value) } - // The conversion Provenance and the exchange Bundle record an export event rather than anything - // read from the provider, so they are named in the deployment's own namespace and carry no - // scheme prefix from this guide. - const eventKey = (role: string): string => - [input.provider, String(input.eventSequence), role].join(SEPARATOR) + const event = deriveEventIdentifier( + deployment.value, + normalizedInput.eventSequence, + ) + if (!event.ok) return event + const provenanceNode = deriveEntryNodeIdentifier( + deployment.value, + event.value, + normalizedInput.provenanceNodeRole ?? 'conversion-provenance', + '0', + ) + if (!provenanceNode.ok) return provenanceNode return ok( deepFreeze({ - sourceRecord, - outputs, - conversion: { - system: input.graphIdentifierSystem, - value: eventKey('conversion-provenance'), - }, - exchange: { - system: input.graphIdentifierSystem, - value: eventKey('exchange-bundle'), - }, + sourceRecord: sourceRecord.value, + outputs: derivedOutputs, + event: event.value, + provenanceNode: provenanceNode.value, }) as unknown as ProviderIdentities, ) } - -/** Internal runtime bridge from strict adapter schemas to branded graph identity. */ -export const parseResourceIdentityInput = (input: { - readonly identifier: { readonly system: string; readonly value: string } - readonly id?: string | undefined -}): Result => { - const system = parseAbsoluteUri(input.identifier.system) - if (!system.ok) return system - const id = input.id === undefined ? undefined : parseFhirId(input.id) - if (id !== undefined && !id.ok) return id - return createEntryIdentity( - { system: system.value, value: input.identifier.value }, - id?.value, - ) -} diff --git a/packages/fhir/src/providers/index.ts b/packages/fhir/src/providers/index.ts index 211a9d65..4ba3e917 100644 --- a/packages/fhir/src/providers/index.ts +++ b/packages/fhir/src/providers/index.ts @@ -10,22 +10,33 @@ export { buildProviderMeasurementBundle } from './builder.js' export { adapterMeasurementCatalog, groveRecordingFormatRegistry, + healthKitApplicationDeviceIdentity, + healthKitClinicalRecordAdmission, providerAdapterCatalog, - providerRawMappings, + providerRawOutputDiscriminators, + providerRawOutputRoles, providerRecordEffectiveRules, - providerScalarMappings, + providerScalarOutputDiscriminators, + providerScalarOutputRoles, groveProviderPackageMetadata, groveFhirContractVersion, groveFhirVersion, type AdapterMeasurementCatalog, - type ProviderRawMappings, + type HealthKitApplicationDeviceIdentity, + type HealthKitClinicalRecordAdmission, + type ProviderRawOutputRoles, + type ProviderRawOutputDiscriminators, type ProviderRecordEffectiveRules, - type ProviderScalarMappings, + type ProviderScalarOutputDiscriminators, + type ProviderScalarOutputRoles, } from './contract.generated.js' export { parseProviderMeasurementBundleInput, parseNormalizedProviderRecord, + providerOutputCoordinates, + providerOutputRole, } from './provider.js' +export type { ProviderOutputCoordinates } from './provider.js' export { buildProviderRecordingBundle, encodeRecordingBytes, @@ -35,6 +46,16 @@ export { parseMediaType, parseSha1Base64, } from './recording.js' +export { + buildProviderRetractionBundle, + parseProviderRetractionInput, +} from './retraction.js' +export type { + ProviderRetractionInput, + ProviderRetractionTargetInput, + RetractionTargetRole, +} from './retraction.js' +export type { DeviceSnapshotRole } from './identity.js' export type { CanonicalBase64, ProviderAdapter, @@ -45,6 +66,8 @@ export type { ProviderRecordingSourceRecord, ProviderSourceRecord, ConnectedProvider, + ConnectedProviderMeasurement, + ConnectedProviderMeasurementKind, ConnectedProviderMeasurements, ConnectedProviderRecord, ConnectedRawProvider, @@ -52,21 +75,33 @@ export type { ConnectedSourceType, EmbeddedRecordingAttachmentInput, ExternalRecordingAttachmentInput, + GovernedSourceIdentifierInput, + GovernedSourceIdentifierTypeCodingInput, + GovernedSourceIdentifierTypeInput, ImmutableRecordingUrl, MeasurementRepositoryAssignedResourceIds, MediaType, NormalizedProviderRecord, NormalizedSourceRecord, - ProviderAccountPseudonymInput, + ProviderAccountScopeIdentifierInput, + ProviderGlobalScopeIdentifierInput, + ProviderScopeIdentifierInput, + ProviderExclusiveMeasurement, + ProviderExclusiveMeasurementKind, + ProviderPatientReferenceInput, + ProviderResearchStudyReferenceInput, ProviderRecordingFormat, RawPayloadAdmissionAssertion, RecordingRepositoryAssignedResourceIds, Sha1Base64, SupportedConnectedProviderMeasurementKind, + WriterRecordInput, } from './types.js' export type { ApplicationDeviceInput, + DeploymentIdentityInput, GatewayApplicationInput, + HostDeviceInput, MobileMeasurement, RecordingDeviceInput, RecordingMethod, diff --git a/packages/fhir/src/providers/measurement-definition.ts b/packages/fhir/src/providers/measurement-definition.ts new file mode 100644 index 00000000..e2e9dca2 --- /dev/null +++ b/packages/fhir/src/providers/measurement-definition.ts @@ -0,0 +1,127 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + adapterMeasurementCatalog, + groveProviderProfileCanonicals, + providerAdapterCatalog, + providerScalarOutputRoles, +} from './contract.generated.js' +import type { ConnectedProvider } from './types.js' +import { sharedMobileMeasurementCatalog } from '../mobile/measurement-catalog.generated.js' + +export interface ProviderMeasurementDefinition { + readonly id: string + readonly profile: string + readonly owner?: string + readonly code: Readonly<{ + system: string + code: string + display?: string + }> + readonly requiredCodings?: ReadonlyArray< + Readonly<{ system: string; code: string }> + > + readonly category?: Readonly<{ + system: string + code: string + display?: string + }> + readonly effective: 'Period' | 'dateTime' | 'dateTime-or-Period' + readonly obeys?: readonly string[] + readonly method?: Readonly<{ code: string; display?: string }> + readonly methodChoice?: readonly string[] + readonly valueKind: + 'codeableConcept' | 'components' | 'dateTime' | 'grouping' | 'quantity' + readonly quantity?: Readonly<{ + system: string + code: string + unit?: string + valueDomain?: Readonly<{ + minimum: Readonly<{ value: number; inclusive: boolean }> + maximum?: Readonly<{ value: number; inclusive: boolean }> + integerOnly: boolean + }> + }> | null + readonly resultCodeSystem?: string + readonly allowedValues?: readonly string[] + readonly components?: ReadonlyArray< + Readonly<{ + id: string + system: string + code: string + quantity?: Readonly<{ system: string; code: string; unit?: string }> + }> + > +} + +const SHARED_DEFINITIONS = + sharedMobileMeasurementCatalog as unknown as Readonly< + Record + > +const OWNER_DEFINITIONS = adapterMeasurementCatalog as unknown as Readonly< + Record>> +> +const PROFILE_CANONICALS = groveProviderProfileCanonicals as Readonly< + Record +> + +interface ProviderContractRow { + readonly id: ConnectedProvider + readonly measurementOwner: string + readonly observationProfile: string +} + +const PROVIDER_ROWS = + providerAdapterCatalog.providers as unknown as readonly ProviderContractRow[] + +const providerContractRow = ( + provider: ConnectedProvider, +): ProviderContractRow | undefined => + PROVIDER_ROWS.find((candidate) => candidate.id === provider) + +/** Resolves only a shared definition or one owned by this exact connected provider. */ +export const providerMeasurementDefinition = ( + provider: ConnectedProvider, + kind: string, +): ProviderMeasurementDefinition | undefined => + SHARED_DEFINITIONS[kind] ?? + OWNER_DEFINITIONS[providerContractRow(provider)?.measurementOwner ?? '']?.[ + kind + ] + +/** Returns the exact direct semantic canonical for this provider-owned mapping. */ +export const providerMeasurementProfile = ( + provider: ConnectedProvider, + kind: string, +): string | undefined => { + const profile = providerMeasurementDefinition(provider, kind)?.profile + return profile === undefined ? undefined : PROFILE_CANONICALS[profile] +} + +/** Returns the exact provider-specific Observation envelope required for direct claims. */ +export const providerObservationProfile = ( + provider: ConnectedProvider, +): string | undefined => providerContractRow(provider)?.observationProfile + +/** Closed owner-exclusive definitions that the normalized Provider facade may parse. */ +export const connectedProviderExclusiveDefinitions: Readonly< + Record +> = Object.freeze( + Object.fromEntries( + [...new Set(PROVIDER_ROWS.map(({ measurementOwner }) => measurementOwner))] + .filter((owner) => + PROVIDER_ROWS.some( + ({ id, measurementOwner }) => + measurementOwner === owner && + Object.hasOwn(providerScalarOutputRoles, id), + ), + ) + .flatMap((owner) => Object.entries(OWNER_DEFINITIONS[owner] ?? {})), + ), +) diff --git a/packages/fhir/src/providers/privacy.ts b/packages/fhir/src/providers/privacy.ts deleted file mode 100644 index 1e108e9f..00000000 --- a/packages/fhir/src/providers/privacy.ts +++ /dev/null @@ -1,41 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -/** Raw, form-decoded, and recursively percent-decoded string representations. */ -const decodedStringRepresentations = (value: string): readonly string[] => { - const representations = new Set([value]) - let decoded = value - for (let layer = 0; layer <= value.length; layer += 1) { - const formDecoded = decoded.replaceAll('+', ' ') - representations.add(formDecoded) - let next: string - try { - next = decodeURIComponent(formDecoded) - } catch { - break - } - representations.add(next) - if (next === decoded || next === formDecoded) break - decoded = next - } - return [...representations] -} - -export const containsReversibleIdentityRepresentation = ( - emitted: string, - privateValue: string, -): boolean => { - const privateRepresentations = decodedStringRepresentations(privateValue) - return decodedStringRepresentations(emitted).some((emittedValue) => - privateRepresentations.some( - (privateRepresentation) => - privateRepresentation !== '' && - emittedValue.includes(privateRepresentation), - ), - ) -} diff --git a/packages/fhir/src/providers/profiles.ts b/packages/fhir/src/providers/profiles.ts index 38b2a143..31f5d762 100644 --- a/packages/fhir/src/providers/profiles.ts +++ b/packages/fhir/src/providers/profiles.ts @@ -9,25 +9,31 @@ /* eslint-disable sonarjs/no-clear-text-protocols -- FHIR fixes these canonical URIs to HTTP. */ import { + providerAdapterCatalog, groveProviderPackageCanonicals, groveProviderProfileCanonicals, } from './contract.generated.js' -import { groveFhirExchangeIdentity } from '../mobile/measurement-catalog.generated.js' +import { groveMobileContract } from '../mobile/contract.js' +export const PROVIDER_RECORDING_OUTPUT_ROLE: 'native-recording' = + providerAdapterCatalog.recordingDocument.outputRole +export const PROVIDER_RECORDING_OUTPUT_DISCRIMINATOR: 'single' = + providerAdapterCatalog.recordingDocument.outputDiscriminator type GroveSystems = Readonly<{ groveAggregationMethod: string + groveIdentifierRole: string + groveLifecycleEvent: string groveRecordingMethod: string isoLifecycle: string - observationCategory: string provenanceParticipant: string }> export const SYSTEMS: GroveSystems = { groveAggregationMethod: `${groveProviderPackageCanonicals.mobile}/CodeSystem/grove-aggregation-method`, + groveIdentifierRole: groveMobileContract.systems.identifierRole, + groveLifecycleEvent: groveMobileContract.systems.lifecycleEvent, groveRecordingMethod: `${groveProviderPackageCanonicals.mobile}/CodeSystem/grove-recording-method`, isoLifecycle: 'http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle', - observationCategory: - 'http://terminology.hl7.org/CodeSystem/observation-category', provenanceParticipant: 'http://terminology.hl7.org/CodeSystem/provenance-participant-type', } as const @@ -36,23 +42,29 @@ type GroveProfiles = Readonly<{ mobileBundle: string recordingDevice: string applicationDevice: string + hostDevice: string providerObservation: string sensorRecordingDocument: string providerRecordingDocument: string providerConversionProvenance: string + retractionBundle: string + retractionProvenance: string }> export const PROFILES: GroveProfiles = { mobileBundle: groveProviderProfileCanonicals['grove-mobile-exchange-bundle'], recordingDevice: groveProviderProfileCanonicals['grove-recording-device'], applicationDevice: groveProviderProfileCanonicals['grove-application-device'], - providerObservation: groveProviderProfileCanonicals['provider-observation'], + hostDevice: groveMobileContract.profiles.hostDevice, + providerObservation: groveProviderProfileCanonicals['providers-observation'], sensorRecordingDocument: groveProviderProfileCanonicals['grove-sensor-recording-document'], providerRecordingDocument: - groveProviderProfileCanonicals['provider-recording-document'], + groveProviderProfileCanonicals['providers-recording-document'], providerConversionProvenance: - groveProviderProfileCanonicals['provider-conversion-provenance'], + groveProviderProfileCanonicals['providers-conversion-provenance'], + retractionBundle: groveMobileContract.profiles.retractionBundle, + retractionProvenance: groveMobileContract.profiles.retractionProvenance, } as const type GroveExtensions = Readonly<{ @@ -60,8 +72,10 @@ type GroveExtensions = Readonly<{ recordingMethod: string provider: string providerSourceType: string - exchangeEntryIdentifier: string + entryNodeKey: string + retractionTargetRole: string researchStudy: string + writerRecordVersion: string }> export const EXTENSIONS: GroveExtensions = { @@ -70,7 +84,10 @@ export const EXTENSIONS: GroveExtensions = { recordingMethod: `${groveProviderPackageCanonicals.mobile}/StructureDefinition/grove-recording-method`, provider: `${groveProviderPackageCanonicals.providers}/StructureDefinition/provider`, providerSourceType: `${groveProviderPackageCanonicals.providers}/StructureDefinition/provider-source-type`, - exchangeEntryIdentifier: groveFhirExchangeIdentity.entryIdentifierExtension, + entryNodeKey: groveMobileContract.extensions.entryNodeKey, + retractionTargetRole: groveMobileContract.extensions.retractionTargetRole, researchStudy: 'http://hl7.org/fhir/StructureDefinition/workflow-researchStudy', + writerRecordVersion: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version', } as const diff --git a/packages/fhir/src/providers/provider-input-schemas.ts b/packages/fhir/src/providers/provider-input-schemas.ts new file mode 100644 index 00000000..a16e41b3 --- /dev/null +++ b/packages/fhir/src/providers/provider-input-schemas.ts @@ -0,0 +1,319 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { z } from 'zod' +import { providerAdapterCatalog } from './contract.generated.js' +import { SYSTEMS } from './profiles.js' +import type { + ProviderPatientReferenceInput, + ProviderResearchStudyReferenceInput, +} from './types.js' +import { + parseAbsoluteUri, + parseFhirInstant, + type FhirInstant, + type Issue, +} from '../core/index.js' +import { containsIsolatedSurrogate } from '../mobile/identity.js' +import { groveExchangeProtocol } from '../mobile/measurement-catalog.generated.js' +import { canonicalizeMobileEffectiveInstant } from '../mobile/time.js' +import type { + ApplicationDeviceInput, + DeploymentIdentityInput, + GatewayApplicationInput, + GroveOpaqueIdentityKind, + RecordingDeviceInput, +} from '../mobile/types.js' + +const primitiveInstantSchemaValue = z + .string() + .refine((value) => parseFhirInstant(value).ok, { + message: + 'Expected an RFC 3339 instant with seconds and an explicit UTC offset.', + }) +export const primitiveInstantSchema: z.ZodString = primitiveInstantSchemaValue + +const mobileEffectiveInstantSchemaValue = z + .string() + .transform((value, context) => { + const canonical = canonicalizeMobileEffectiveInstant(value) + if (!canonical.ok) { + for (const issue of canonical.issues) { + context.addIssue({ code: 'custom', message: issue.message }) + } + return z.NEVER + } + return canonical.value + }) +export const mobileEffectiveInstantSchema: z.ZodType = + mobileEffectiveInstantSchemaValue + +const nonBlankStringSchemaValue = z + .string() + .refine( + (value) => value.trim() !== '' && !containsIsolatedSurrogate(value), + 'Expected a non-blank Unicode-scalar string.', + ) +export const nonBlankStringSchema: z.ZodString = nonBlankStringSchemaValue + +const FHIR_CODE = /^[^\s\p{Cc}]+(?: [^\s\p{Cc}]+)*$/u +const fhirCodeSchema = nonBlankStringSchema.refine( + (value) => FHIR_CODE.test(value), + 'Expected a FHIR code with no leading, trailing, consecutive, or control whitespace.', +) + +export const absoluteUriSchema: z.ZodString = z + .string() + .refine((value) => parseAbsoluteUri(value).ok, { + message: 'Expected an absolute ASCII RFC 3986 URI.', + }) + +const identifierInputSchemaValue = z.strictObject({ + system: absoluteUriSchema, + value: nonBlankStringSchema, +}) +export const identifierInputSchema: z.ZodObject< + { system: z.ZodString; value: z.ZodString }, + z.core.$strict +> = identifierInputSchemaValue + +export type ProviderScopeAssurance = + 'deployment-scoped-account-pseudonym' | 'documented-global-key-space' + +export const providerScopeIdentifierSchema: z.ZodType<{ + readonly system: string + readonly value: string + readonly assurance: ProviderScopeAssurance +}> = identifierInputSchema.extend({ + assurance: z.enum([ + 'deployment-scoped-account-pseudonym', + 'documented-global-key-space', + ]), +}) + +const providerScopeAssuranceByProvider: ReadonlyMap< + string, + ProviderScopeAssurance +> = new Map( + providerAdapterCatalog.providers.map((row) => [ + row.id, + row.providerScopeMode, + ]), +) + +/** Validates the provider-dependent scope assertion projected from the pinned catalog. */ +export const providerScopeIdentifierIssues = ( + provider: string, + identifier: { readonly assurance: ProviderScopeAssurance }, + path: ReadonlyArray = [ + 'source', + 'providerScopeIdentifier', + 'assurance', + ], +): readonly Issue[] => { + const expected = providerScopeAssuranceByProvider.get(provider) + if (expected === undefined || identifier.assurance === expected) return [] + return [ + { + severity: 'error', + code: 'value-mismatch', + path, + message: + expected === 'deployment-scoped-account-pseudonym' ? + 'This provider has account-scoped native keys and requires a deployment-scoped account pseudonym.' + : 'This provider has globally unique native keys and requires its documented global key-space pair.', + }, + ] +} + +const governedSourceIdentifierTypeCodingSchema = z.strictObject({ + system: absoluteUriSchema, + code: fhirCodeSchema, + display: nonBlankStringSchema.optional(), +}) + +const governedSourceIdentifierTypeSchema = z + .strictObject({ + coding: z + .array(governedSourceIdentifierTypeCodingSchema) + .nonempty() + .optional(), + text: nonBlankStringSchema.optional(), + }) + .refine((value) => value.coding !== undefined || value.text !== undefined, { + message: 'Identifier.type requires a non-empty coding or text.', + }) + +interface GovernedSourceIdentifierRuntime { + readonly system: string + readonly nativeId: string + readonly type?: + | { + readonly coding?: + | ReadonlyArray<{ + readonly system: string + readonly code: string + readonly display?: string | undefined + }> + | undefined + readonly text?: string | undefined + } + | undefined +} + +const governedSourceIdentifierSchemaValue = z.strictObject({ + system: absoluteUriSchema, + nativeId: nonBlankStringSchema, + type: governedSourceIdentifierTypeSchema.optional(), +}) +export const governedSourceIdentifierSchema: z.ZodType = + governedSourceIdentifierSchemaValue + +/** Deployment-aware checks the structural disclosure schema cannot decide on its own. */ +export const governedSourceIdentifierIssues = ( + disclosure: GovernedSourceIdentifierRuntime | undefined, + sourceNativeId: string, + deployment: DeploymentIdentityInput, +): readonly Issue[] => { + if (disclosure === undefined) return [] + const findings: Issue[] = [] + if (disclosure.nativeId !== sourceNativeId) { + findings.push({ + severity: 'error', + code: 'value-mismatch', + path: ['nativeIdentifierDisclosure', 'nativeId'], + message: + 'The governed source Identifier value must exactly equal the source-native id used for Grove reconciliation.', + }) + } + const groveIdentitySystems = new Set([ + SYSTEMS.groveIdentifierRole, + deployment.eventIdentifierSystem, + deployment.entryNodeIdentifierSystem, + ...Object.values(deployment.opaqueIdentifierSystems), + ]) + if (groveIdentitySystems.has(disclosure.system)) { + findings.push({ + severity: 'error', + code: 'value-mismatch', + path: ['nativeIdentifierDisclosure', 'system'], + message: + 'A source-native Identifier requires its own governed provider/store namespace, never a Grove graph-role or identity system.', + }) + } + for (const [index, coding] of (disclosure.type?.coding ?? []).entries()) { + if (coding.system === SYSTEMS.groveIdentifierRole) { + findings.push({ + severity: 'error', + code: 'value-mismatch', + path: ['nativeIdentifierDisclosure', 'type', 'coding', index, 'system'], + message: + 'A source-native Identifier.type must not claim a Grove graph identity role.', + }) + } + } + return findings +} + +const providerPatientReferenceSchemaValue = z.strictObject({ + type: z.literal('Patient'), + identifier: identifierInputSchema.extend({ + assurance: z.literal('deployment-scoped-pseudonym'), + }), +}) +export const providerPatientReferenceSchema: z.ZodType = + providerPatientReferenceSchemaValue as unknown as z.ZodType + +const providerResearchStudyReferenceSchemaValue = z.strictObject({ + type: z.literal('ResearchStudy'), + identifier: identifierInputSchema, +}) +export const providerResearchStudyReferenceSchema: z.ZodType = + providerResearchStudyReferenceSchemaValue as unknown as z.ZodType + +const opaqueIdentifierSystemShape = Object.fromEntries( + groveExchangeProtocol.opaqueIdentity.identityKinds.map(({ kind }) => [ + kind, + absoluteUriSchema, + ]), +) as Record + +const deploymentIdentitySchemaValue = z.strictObject({ + opaqueIdentifierSystems: z.strictObject(opaqueIdentifierSystemShape), + eventIdentifierSystem: absoluteUriSchema, + entryNodeIdentifierSystem: absoluteUriSchema, + keyId: z.string().regex(/^[A-Za-z0-9._-]+$/u), + keyEpoch: z.string().regex(/^[1-9]\d*$/u), + secretBase64Url: z.string().regex(/^[A-Za-z0-9_-]+$/u), + producerInstance: z + .string() + .regex( + /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u, + ), +}) +export const deploymentIdentitySchema: z.ZodType = + deploymentIdentitySchemaValue as unknown as z.ZodType + +const fhirIdSchemaValue = z.string().regex(/^[A-Za-z0-9\-.]{1,64}$/u) +export const fhirIdSchema: z.ZodString = fhirIdSchemaValue + +const hostDeviceSchema = z.strictObject({ + sourceDeviceToken: nonBlankStringSchema, + id: fhirIdSchema.optional(), + name: nonBlankStringSchema.optional(), + manufacturer: nonBlankStringSchema.optional(), + modelNumber: nonBlankStringSchema.optional(), + operatingSystemVersion: nonBlankStringSchema, +}) + +const applicationDeviceSchemaValue = z.strictObject({ + sourceDeviceToken: nonBlankStringSchema, + id: fhirIdSchema.optional(), + name: nonBlankStringSchema, + version: nonBlankStringSchema.optional(), + build: nonBlankStringSchema.optional(), + manufacturer: nonBlankStringSchema.optional(), + host: hostDeviceSchema.optional(), +}) +export const applicationDeviceSchema: z.ZodType = + applicationDeviceSchemaValue as unknown as z.ZodType + +const recordingDeviceBase = { + stableUnitToken: nonBlankStringSchema, + subjectIdentifier: identifierInputSchema, + id: fhirIdSchema.optional(), + name: nonBlankStringSchema.optional(), + manufacturer: nonBlankStringSchema.optional(), + modelNumber: nonBlankStringSchema.optional(), +} as const + +export const recordingDeviceSchema: z.ZodType = + z.discriminatedUnion('identityScope', [ + z.strictObject({ + ...recordingDeviceBase, + identityScope: z.literal('deployment-scoped'), + }), + z.strictObject({ + ...recordingDeviceBase, + identityScope: z.literal('authorized-hardware'), + disclosureAuthorization: z.literal('authorized-for-exchange'), + }), + ]) as unknown as z.ZodType + +export const gatewayApplicationSchema: z.ZodType = + z.discriminatedUnion('kind', [ + z.strictObject({ + kind: z.literal('converter-application'), + roleAssurance: z.literal('mediated-or-routed-measurement'), + }), + z.strictObject({ + kind: z.literal('distinct-application'), + roleAssurance: z.literal('mediated-or-routed-measurement'), + application: applicationDeviceSchema, + }), + ]) diff --git a/packages/fhir/src/providers/provider.ts b/packages/fhir/src/providers/provider.ts index ba3de108..ca90cbc7 100644 --- a/packages/fhir/src/providers/provider.ts +++ b/packages/fhir/src/providers/provider.ts @@ -9,28 +9,46 @@ import { z } from 'zod' import { providerRecordEffectiveRules, - providerScalarMappings, + providerScalarOutputDiscriminators, + providerScalarOutputRoles, } from './contract.generated.js' +import { deriveProviderIdentities } from './identity.js' import { - deriveProviderIdentities, - parseResourceIdentityInput, -} from './identity.js' -import { containsReversibleIdentityRepresentation } from './privacy.js' + connectedProviderExclusiveDefinitions, + type ProviderMeasurementDefinition, +} from './measurement-definition.js' +import { + absoluteUriSchema, + applicationDeviceSchema, + deploymentIdentitySchema, + fhirIdSchema, + gatewayApplicationSchema, + governedSourceIdentifierIssues, + governedSourceIdentifierSchema, + identifierInputSchema, + mobileEffectiveInstantSchema, + nonBlankStringSchema, + primitiveInstantSchema, + providerPatientReferenceSchema, + providerScopeIdentifierIssues, + providerScopeIdentifierSchema, + providerResearchStudyReferenceSchema, + recordingDeviceSchema, +} from './provider-input-schemas.js' import type { ProviderMeasurementBundleInput, + ConnectedProviderMeasurementKind, ConnectedProvider, NormalizedProviderRecord, } from './types.js' import { + cloneJsonValue, compareFhirInstants, deepFreeze, + err, issues, ok, parseAbsoluteUri, - parseFhirInstant, - parsePatientReference, - parsePositiveInteger, - parseResearchStudyReference, type Issue, type Result, } from '../core/index.js' @@ -38,8 +56,8 @@ import { sharedMobileMeasurementCatalog, type SharedMobileMeasurementKind, } from '../mobile/measurement-catalog.generated.js' -import { canonicalizeMobileEffectiveInstant } from '../mobile/time.js' import type { + ChoiceQuantityMeasurementKind, InstantCodedMeasurementKind, InstantQuantityMeasurementKind, MobileMeasurement, @@ -47,99 +65,49 @@ import type { PeriodQuantityMeasurementKind, } from '../mobile/types.js' -const primitiveInstantSchemaValue = z - .string() - .refine((value) => parseFhirInstant(value).ok, { - message: - 'Expected an RFC 3339 instant with seconds and an explicit UTC offset.', - }) -export const primitiveInstantSchema: z.ZodString = primitiveInstantSchemaValue - -const mobileEffectiveInstantSchema = z.string().transform((value, context) => { - const canonical = canonicalizeMobileEffectiveInstant(value) - if (!canonical.ok) { - for (const issue of canonical.issues) { - context.addIssue({ code: 'custom', message: issue.message }) +type ParsedProviderMeasurement = + | MobileMeasurement + | { + readonly kind: string + readonly value: number | string + readonly effective: + | { readonly kind: 'date-time'; readonly value: string } + | { + readonly kind: 'period' + readonly start: string + readonly end: string + } } - return z.NEVER - } - return canonical.value -}) - -const nonBlankStringSchemaValue = z - .string() - .refine((value) => value.trim() !== '', 'Expected a non-blank string.') -export const nonBlankStringSchema: z.ZodString = nonBlankStringSchemaValue - -const identifierInputSchemaValue = z.strictObject({ - system: z.url(), - value: nonBlankStringSchema, -}) -export const identifierInputSchema: z.ZodObject< - { system: z.ZodURL; value: z.ZodString }, - z.core.$strict -> = identifierInputSchemaValue - -const fhirIdSchemaValue = z.string().regex(/^[A-Za-z0-9\-.]{1,64}$/u) -export const fhirIdSchema: z.ZodString = fhirIdSchemaValue - -const resourceIdentitySchema = z.strictObject({ - identifier: identifierInputSchema, - id: fhirIdSchema.optional(), -}) - -const applicationDeviceSchemaValue = z.strictObject({ - identity: resourceIdentitySchema, - name: nonBlankStringSchema, - version: nonBlankStringSchema.optional(), - manufacturer: nonBlankStringSchema.optional(), -}) -export const applicationDeviceSchema: z.ZodType<{ - identity: { - identifier: { system: string; value: string } - id?: string | undefined - } - name: string - version?: string | undefined - manufacturer?: string | undefined -}> = applicationDeviceSchemaValue - -const recordingDeviceBase = { - identity: resourceIdentitySchema, - name: nonBlankStringSchema.optional(), - manufacturer: nonBlankStringSchema.optional(), - modelNumber: nonBlankStringSchema.optional(), -} as const - -const recordingDeviceSchema = z.discriminatedUnion('identityScope', [ - z.strictObject({ - ...recordingDeviceBase, - identityScope: z.literal('deployment-scoped'), - }), - z.strictObject({ - ...recordingDeviceBase, - identityScope: z.literal('authorized-hardware'), - disclosureAuthorization: z.literal('authorized-for-exchange'), - }), -]) -const gatewayApplicationSchema = z.discriminatedUnion('kind', [ - z.strictObject({ - kind: z.literal('converter-application'), - roleAssurance: z.literal('mediated-or-routed-measurement'), - }), - z.strictObject({ - kind: z.literal('distinct-application'), - roleAssurance: z.literal('mediated-or-routed-measurement'), - application: applicationDeviceSchema, - }), -]) +export { + applicationDeviceSchema, + deploymentIdentitySchema, + fhirIdSchema, + governedSourceIdentifierIssues, + governedSourceIdentifierSchema, + identifierInputSchema, + nonBlankStringSchema, + primitiveInstantSchema, + providerPatientReferenceSchema, + providerScopeIdentifierIssues, + providerScopeIdentifierSchema, +} from './provider-input-schemas.js' const sourceBase = { recordingMethod: z .enum(['actively-recorded', 'automatically-recorded', 'manual-entry']) .optional(), recordingDevice: recordingDeviceSchema.optional(), + writerRecord: z + .strictObject({ + applicationIdentifier: identifierInputSchema, + nativeRecordId: nonBlankStringSchema, + version: z + .string() + .regex(/^(?:0|[1-9]\d*)$/u) + .optional(), + }) + .optional(), } as const const sourceSchema = z.strictObject({ @@ -147,15 +115,13 @@ const sourceSchema = z.strictObject({ adapter: z.strictObject({ kind: z.literal('providers'), provider: z.enum( - Object.keys(providerScalarMappings) as [ + Object.keys(providerScalarOutputRoles) as [ ConnectedProvider, ...ConnectedProvider[], ], ), }), - providerAccountIdentifier: identifierInputSchema.extend({ - assurance: z.literal('deployment-scoped-pseudonym'), - }), + providerScopeIdentifier: providerScopeIdentifierSchema, sourceType: nonBlankStringSchema, sourceNativeId: nonBlankStringSchema, dataOrigin: applicationDeviceSchema, @@ -175,14 +141,14 @@ const periodEffectiveSchema = z .refine( (value) => { const ordering = compareFhirInstants(value.start, value.end) - return ordering.ok && ordering.value === -1 + return ordering.ok && ordering.value !== 1 }, - { message: 'A measurement Period must end after it starts.' }, + { message: 'A measurement Period must not end before it starts.' }, ) const measurementKindsWhere = ( valueKind: 'codeableConcept' | 'quantity', - effective: 'Period' | 'dateTime', + effective: 'Period' | 'dateTime' | 'dateTime-or-Period', excluded?: SharedMobileMeasurementKind, ): [Kind, ...Kind[]] => ( @@ -211,10 +177,28 @@ const periodQuantityMeasurementSchema = z.strictObject({ kind: z.enum( measurementKindsWhere('quantity', 'Period'), ), - value: z.number().nonnegative(), + value: z.number(), effective: periodEffectiveSchema, }) +const choiceQuantityMeasurementKinds = + measurementKindsWhere( + 'quantity', + 'dateTime-or-Period', + ) +const choiceQuantityMeasurementKindSet: ReadonlySet = new Set( + choiceQuantityMeasurementKinds, +) +const choiceQuantityMeasurementSchema = z.strictObject({ + kind: z.custom( + (value) => + typeof value === 'string' && choiceQuantityMeasurementKindSet.has(value), + { message: 'Expected a catalog measurement with choice effective[x].' }, + ), + value: z.number(), + effective: z.union([instantEffectiveSchema, periodEffectiveSchema]), +}) + const instantCodedMeasurementSchema = z.strictObject({ kind: z.enum( measurementKindsWhere( @@ -240,8 +224,8 @@ const periodCodedMeasurementSchema = z.strictObject({ const bloodPressureMeasurementSchema = z.strictObject({ kind: z.literal('blood-pressure'), - systolic: z.number().positive(), - diastolic: z.number().positive(), + systolic: z.number(), + diastolic: z.number(), effective: instantEffectiveSchema, }) @@ -250,7 +234,7 @@ const sleepStageMeasurementSchema = z.strictObject({ stage: z.enum(sharedMobileMeasurementCatalog['sleep-stage'].allowedValues), sourceStageCoding: z .strictObject({ - system: z.url(), + system: absoluteUriSchema, code: nonBlankStringSchema, display: nonBlankStringSchema.optional(), }) @@ -258,66 +242,142 @@ const sleepStageMeasurementSchema = z.strictObject({ effective: periodEffectiveSchema, }) -const measurementSchema = z.discriminatedUnion('kind', [ +const exclusiveDefinitions = connectedProviderExclusiveDefinitions +const exclusiveQuantityKinds = Object.values(exclusiveDefinitions) + .filter(({ valueKind }) => valueKind === 'quantity') + .map(({ id }) => id) as [string, ...string[]] +const exclusiveCodedKinds = Object.values(exclusiveDefinitions) + .filter(({ valueKind }) => valueKind === 'codeableConcept') + .map(({ id }) => id) as [string, ...string[]] + +const exclusiveQuantityMeasurementSchema = z.strictObject({ + kind: z.enum(exclusiveQuantityKinds), + value: z.number(), + effective: z.union([instantEffectiveSchema, periodEffectiveSchema]), +}) + +const exclusiveCodedMeasurementSchema = z.strictObject({ + kind: z.enum(exclusiveCodedKinds), + value: nonBlankStringSchema, + effective: z.union([instantEffectiveSchema, periodEffectiveSchema]), +}) + +const measurementSchema: z.ZodType = z.union([ instantQuantityMeasurementSchema, periodQuantityMeasurementSchema, + choiceQuantityMeasurementSchema, instantCodedMeasurementSchema, periodCodedMeasurementSchema, bloodPressureMeasurementSchema, sleepStageMeasurementSchema, -]) + exclusiveQuantityMeasurementSchema, + exclusiveCodedMeasurementSchema, +]) as unknown as z.ZodType + +interface NumericBoundary { + readonly value: number + readonly inclusive: boolean +} + +const violatesMinimum = (value: number, boundary: NumericBoundary): boolean => + value < boundary.value || (!boundary.inclusive && value === boundary.value) + +const violatesMaximum = (value: number, boundary: NumericBoundary): boolean => + value > boundary.value || (!boundary.inclusive && value === boundary.value) + +const effectiveKindMatches = ( + definition: ProviderMeasurementDefinition, + effectiveKind: 'date-time' | 'period', +): boolean => + definition.effective === 'dateTime-or-Period' || + (definition.effective === 'dateTime' && effectiveKind === 'date-time') || + (definition.effective === 'Period' && effectiveKind === 'period') + +const violatesRequiredPeriodOrdering = ( + measurement: z.infer, + definition: ProviderMeasurementDefinition, +): boolean => { + if ( + measurement.effective.kind !== 'period' || + !(definition.obeys ?? []).includes('grove-step-count-period-1') + ) { + return false + } + const ordering = compareFhirInstants( + measurement.effective.start, + measurement.effective.end, + ) + return !ordering.ok || ordering.value !== -1 +} + +const violatesQuantityDomain = ( + value: number, + definition: ProviderMeasurementDefinition, +): boolean => { + const domain = definition.quantity?.valueDomain + if (domain === undefined) return false + return ( + violatesMinimum(value, domain.minimum) || + (domain.maximum !== undefined && violatesMaximum(value, domain.maximum)) || + (domain.integerOnly && !Number.isInteger(value)) + ) +} const refineMeasurement = ( measurement: z.infer, path: ReadonlyArray, context: z.core.$RefinementCtx, ) => { + const measurementDefinition = + ( + sharedMobileMeasurementCatalog as unknown as Readonly< + Record + > + )[measurement.kind] ?? exclusiveDefinitions[measurement.kind] + if (measurementDefinition === undefined) { + context.addIssue({ + code: 'custom', + path: [...path, 'kind'], + message: `No closed Provider measurement definition exists for ${measurement.kind}.`, + }) + return + } if ( - measurement.effective.kind === 'date-time' && - 'value' in measurement && - typeof measurement.value === 'number' && - measurement.kind !== 'oxygen-saturation' && - measurement.value <= 0 + !effectiveKindMatches(measurementDefinition, measurement.effective.kind) ) { context.addIssue({ code: 'custom', - path: [...path, 'value'], - message: 'An instantaneous measurement value must be greater than zero.', + path: [...path, 'effective'], + message: `${measurement.kind} requires catalog effective[x] ${measurementDefinition.effective}.`, }) } - if ('value' in measurement && typeof measurement.value === 'string') { - const definition = sharedMobileMeasurementCatalog[measurement.kind] - if ( - !('allowedValues' in definition) || - !(definition.allowedValues as readonly string[]).includes( - measurement.value, - ) - ) { - context.addIssue({ - code: 'custom', - path: [...path, 'value'], - message: `Expected a catalog-allowed coded result for ${measurement.kind}.`, - }) - } + if (violatesRequiredPeriodOrdering(measurement, measurementDefinition)) { + context.addIssue({ + code: 'custom', + path: [...path, 'effective'], + message: `The ${measurement.kind} Period must satisfy its catalog-owned nonzero-duration rule.`, + }) } if ( - measurement.kind === 'oxygen-saturation' && - (measurement.value < 0 || measurement.value > 100) + 'value' in measurement && + typeof measurement.value === 'string' && + measurementDefinition.allowedValues?.includes(measurement.value) !== true ) { context.addIssue({ code: 'custom', path: [...path, 'value'], - message: 'Oxygen saturation must be between 0 and 100 percent.', + message: `Expected a catalog-allowed coded result for ${measurement.kind}.`, }) } if ( - measurement.kind === 'step-count' && - !Number.isInteger(measurement.value) + 'value' in measurement && + typeof measurement.value === 'number' && + violatesQuantityDomain(measurement.value, measurementDefinition) ) { context.addIssue({ code: 'custom', path: [...path, 'value'], - message: 'Step count must be an integer.', + message: `The ${measurement.kind} value is outside its catalog-owned value domain.`, }) } } @@ -342,145 +402,48 @@ const normalizedProviderRecordSchema = z .strictObject(normalizedProviderRecordShape) .superRefine(refineMeasurements) +const scalarOutputRoles = providerScalarOutputRoles as Readonly< + Record>>>> +> + +const connectedProviderMeasurementKinds = [ + ...new Set( + Object.values(scalarOutputRoles).flatMap((sources) => + Object.values(sources).flatMap((mapping) => Object.keys(mapping)), + ), + ), +] as [ConnectedProviderMeasurementKind, ...ConnectedProviderMeasurementKind[]] + const providerMeasurementBundleInputSchema = z .strictObject({ ...normalizedProviderRecordShape, - subject: z.string().refine((value) => parsePatientReference(value).ok, { - message: - 'Expected Patient/{id} or an absolute HTTP(S) URL ending in /Patient/{id}.', - }), + subject: providerPatientReferenceSchema, application: applicationDeviceSchema, gatewayApplication: gatewayApplicationSchema.optional(), - eventSequence: z - .number() - .refine((value) => parsePositiveInteger(value).ok, { - message: 'Expected a positive safe integer greater than zero.', - }), - graphIdentifierSystem: z - .string() - .refine((value) => parseAbsoluteUri(value).ok, { - message: 'Expected an absolute URI the deployment owns.', - }), - issued: primitiveInstantSchema, + eventSequence: z.string().regex(/^[1-9]\d*$/u), + deploymentIdentity: deploymentIdentitySchema, + nativeIdentifierDisclosure: governedSourceIdentifierSchema.optional(), + occurred: primitiveInstantSchema, recorded: primitiveInstantSchema, + assembled: primitiveInstantSchema, repositoryIds: z .strictObject({ bundle: fhirIdSchema.optional(), observations: z .partialRecord( - z.enum( - Object.keys(sharedMobileMeasurementCatalog) as [ - MobileMeasurement['kind'], - ...Array, - ], - ), + z.enum(connectedProviderMeasurementKinds), fhirIdSchema, ) .optional(), provenance: fhirIdSchema.optional(), }) .optional(), - researchStudyReferences: z.array(z.string()).optional(), + researchStudyReferences: z + .array(providerResearchStudyReferenceSchema) + .optional(), }) .superRefine(refineMeasurements) -const applicationStrings = ( - input: z.infer | undefined, -): readonly string[] => - input === undefined ? - [] - : [ - input.identity.identifier.system, - input.identity.identifier.value, - input.identity.id ?? '', - input.name, - input.version ?? '', - input.manufacturer ?? '', - ] - -const recordingDeviceStrings = ( - input: z.infer | undefined, -): readonly string[] => - input === undefined ? - [] - : [ - input.identity.identifier.system, - input.identity.identifier.value, - input.identity.id ?? '', - input.name ?? '', - input.manufacturer ?? '', - input.modelNumber ?? '', - ] - -const stringLeaves = (value: unknown): readonly string[] => { - if (typeof value === 'string') return [value] - if (Array.isArray(value)) return value.flatMap(stringLeaves) - if (typeof value !== 'object' || value === null) return [] - return Object.values(value).flatMap(stringLeaves) -} - -const normalizedEmittedCallerStrings = ( - input: z.infer, -): readonly string[] => [ - input.source.adapter.provider, - input.source.sourceType, - input.source.recordingMethod ?? '', - ...stringLeaves(input.measurements), - ...applicationStrings(input.source.dataOrigin), - ...recordingDeviceStrings(input.source.recordingDevice), -] - -const graphEmittedCallerStrings = ( - input: z.infer, -): readonly string[] => [ - ...normalizedEmittedCallerStrings(input), - input.subject, - input.issued, - input.recorded, - ...applicationStrings(input.application), - ...applicationStrings( - input.gatewayApplication?.kind === 'distinct-application' ? - input.gatewayApplication.application - : undefined, - ), - input.repositoryIds?.bundle ?? '', - ...Object.values(input.repositoryIds?.observations ?? {}), - input.repositoryIds?.provenance ?? '', - ...(input.researchStudyReferences ?? []), -] - -const identityLeakageIssues = ( - source: z.infer, - emitted: readonly string[], -): readonly Issue[] => { - const privateInputs = [ - [source.sourceNativeId, ['source', 'sourceNativeId'], 'sourceNativeId'], - [ - source.providerAccountIdentifier.value, - ['source', 'providerAccountIdentifier', 'value'], - 'providerAccountIdentifier.value', - ], - ] as const - return privateInputs.flatMap(([privateValue, path, label]) => - ( - emitted.some( - (value) => - value !== '' && - containsReversibleIdentityRepresentation(value, privateValue), - ) - ) ? - [ - { - severity: 'error' as const, - code: 'invalid-identifier' as const, - path, - message: `${label} is an identity input only and must not appear in emitted FHIR metadata.`, - }, - ] - : [], - ) -} - export const normalizeZodIssue = (entry: z.core.$ZodIssue): Issue => ({ severity: 'error', code: 'schema-invalid', @@ -496,13 +459,28 @@ const providerSourceMapping = ( provider: ConnectedProvider, sourceType: string, ): Readonly> | undefined => { - const providerMappings = providerScalarMappings[provider] as Record< + const providerMappings = providerScalarOutputRoles[provider] as Record< string, Readonly> | undefined > return providerMappings[sourceType] } +const providerSourceDiscriminatorMapping = ( + provider: ConnectedProvider, + sourceType: string, +): Readonly> | undefined => { + const providerMappings = providerScalarOutputDiscriminators[ + provider + ] as Record> | undefined> + return providerMappings[sourceType] +} + +export interface ProviderOutputCoordinates { + readonly outputRole: string + readonly outputDiscriminator: string +} + interface RecordEffectiveRule { readonly kind: 'complete-civil-day-period' readonly measurementIds: readonly string[] @@ -586,21 +564,37 @@ const recordEffectiveIssues = (input: { ) } -export const providerOutputDiscriminator = ( +export const providerOutputRole = ( provider: ConnectedProvider, sourceType: string, - kind: MobileMeasurement['kind'], + kind: string, ): string | undefined => { return providerSourceMapping(provider, sourceType)?.[kind] } +/** Exact catalog-owned HMAC coordinates for one Provider Observation output. */ +export const providerOutputCoordinates = ( + provider: ConnectedProvider, + sourceType: string, + kind: string, +): ProviderOutputCoordinates | undefined => { + const outputRole = providerSourceMapping(provider, sourceType)?.[kind] + const outputDiscriminator = providerSourceDiscriminatorMapping( + provider, + sourceType, + )?.[kind] + return outputRole === undefined || outputDiscriminator === undefined ? + undefined + : { outputRole, outputDiscriminator } +} + const recordMappingIssues = (input: { readonly source: z.infer readonly measurements: ReadonlyArray> readonly repositoryIds?: | { readonly observations?: - | Readonly>> + | Readonly>> | undefined } | undefined @@ -621,6 +615,12 @@ const recordMappingIssues = (input: { } const findings: Issue[] = [] + findings.push( + ...providerScopeIdentifierIssues( + input.source.adapter.provider, + input.source.providerScopeIdentifier, + ), + ) const kinds = input.measurements.map(({ kind }) => kind) for (const [index, kind] of kinds.entries()) { if (!Object.hasOwn(mapping, kind)) { @@ -642,7 +642,7 @@ const recordMappingIssues = (input: { }) } for (const kind of Object.keys(input.repositoryIds?.observations ?? {})) { - if (!kinds.includes(kind as MobileMeasurement['kind'])) { + if (!kinds.includes(kind)) { findings.push({ severity: 'error', code: 'invalid-reference', @@ -689,15 +689,18 @@ const sortMeasurements = < export const parseNormalizedProviderRecord = ( input: unknown, ): Result => { - const result = normalizedProviderRecordSchema.safeParse(input) - if (!result.success) { - return issues(result.error.issues.map(normalizeZodIssue)) + const snapshot = cloneJsonValue(input) + if (!snapshot.ok) return snapshot + let result: ReturnType + try { + result = normalizedProviderRecordSchema.safeParse(snapshot.value) + } catch { + return err( + 'schema-invalid', + 'Provider record validation could not safely inspect the supplied value.', + ) } - const leakageIssues = identityLeakageIssues( - result.data.source, - normalizedEmittedCallerStrings(result.data), - ) - if (leakageIssues.length > 0) return issues(leakageIssues) + if (!result.success) return issues(result.error.issues.map(normalizeZodIssue)) const mappingIssues = recordMappingIssues(result.data) if (mappingIssues.length > 0) return issues(mappingIssues) return ok( @@ -709,64 +712,30 @@ export const parseNormalizedProviderRecord = ( export const parseProviderMeasurementBundleInput = ( input: unknown, ): Result => { - const result = providerMeasurementBundleInputSchema.safeParse(input) - if (!result.success) { - return issues(result.error.issues.map(normalizeZodIssue)) - } - type GraphIdentityInput = readonly [ - Parameters[0], - readonly string[], - ] - const graphIdentityInputs: GraphIdentityInput[] = [ - [result.data.application.identity, ['application', 'identity']], - [ - result.data.source.dataOrigin.identity, - ['source', 'dataOrigin', 'identity'], - ], - ] - if (result.data.source.recordingDevice !== undefined) { - graphIdentityInputs.push([ - result.data.source.recordingDevice.identity, - ['source', 'recordingDevice', 'identity'], - ]) - } - if (result.data.gatewayApplication?.kind === 'distinct-application') { - graphIdentityInputs.push([ - result.data.gatewayApplication.application.identity, - ['gatewayApplication', 'application', 'identity'], - ]) + const snapshot = cloneJsonValue(input) + if (!snapshot.ok) return snapshot + let result: ReturnType + try { + result = providerMeasurementBundleInputSchema.safeParse(snapshot.value) + } catch { + return err( + 'schema-invalid', + 'Provider graph input validation could not safely inspect the supplied value.', + ) } - const graphIdentityIssues = graphIdentityInputs.flatMap( - ([identity, path]) => { - const parsedIdentity = parseResourceIdentityInput(identity) - return parsedIdentity.ok ? - [] - : parsedIdentity.issues.map((entry) => ({ - ...entry, - path: [...path, ...entry.path], - })) - }, + if (!result.success) return issues(result.error.issues.map(normalizeZodIssue)) + const disclosureIssues = governedSourceIdentifierIssues( + result.data.nativeIdentifierDisclosure, + result.data.source.sourceNativeId, + result.data.deploymentIdentity, ) - if (graphIdentityIssues.length > 0) return issues(graphIdentityIssues) + if (disclosureIssues.length > 0) return issues(disclosureIssues) const researchStudyIssues: Issue[] = [] - for (const [index, reference] of ( - result.data.researchStudyReferences ?? [] - ).entries()) { - if (!parseResearchStudyReference(reference).ok) { - researchStudyIssues.push({ - severity: 'error', - code: 'invalid-reference', - path: ['researchStudyReferences', index], - message: - 'Expected ResearchStudy/{id} or an absolute HTTP(S) URL ending in /ResearchStudy/{id} without a query or fragment.', - }) - } - } - if ( - result.data.researchStudyReferences !== undefined && - new Set(result.data.researchStudyReferences).size !== - result.data.researchStudyReferences.length - ) { + const researchStudyKeys = (result.data.researchStudyReferences ?? []).map( + ({ identifier }) => + `${identifier.system.length}:${identifier.system}${identifier.value.length}:${identifier.value}`, + ) + if (new Set(researchStudyKeys).size !== researchStudyKeys.length) { researchStudyIssues.push({ severity: 'error', code: 'duplicate-identifier', @@ -775,50 +744,72 @@ export const parseProviderMeasurementBundleInput = ( }) } if (researchStudyIssues.length > 0) return issues(researchStudyIssues) - const leakageIssues = identityLeakageIssues( - result.data.source, - graphEmittedCallerStrings(result.data), - ) - if (leakageIssues.length > 0) return issues(leakageIssues) const mappingIssues = recordMappingIssues(result.data) if (mappingIssues.length > 0) return issues(mappingIssues) - const outputDiscriminators = result.data.measurements.map(({ kind }) => - providerOutputDiscriminator( + const sourceMapping = providerSourceMapping( + result.data.source.adapter.provider, + result.data.source.sourceType, + ) + if ( + result.data.nativeIdentifierDisclosure !== undefined && + (sourceMapping === undefined || + Object.keys(sourceMapping).length !== 1 || + result.data.measurements.length !== 1) + ) { + return issues([ + { + severity: 'error', + code: 'value-mismatch', + path: ['nativeIdentifierDisclosure'], + message: + 'The Provider catalog must designate one unique one-to-one Observation before a governed source Identifier may be disclosed; ambiguous multi-output records must omit it.', + }, + ]) + } + const outputCoordinates = result.data.measurements.map(({ kind }) => + providerOutputCoordinates( result.data.source.adapter.provider, result.data.source.sourceType, kind, ), ) - if (outputDiscriminators.some((value) => value === undefined)) { + const definedOutputCoordinates = outputCoordinates.filter( + (coordinates): coordinates is ProviderOutputCoordinates => + coordinates !== undefined, + ) + if (definedOutputCoordinates.length !== outputCoordinates.length) { return issues([ { severity: 'error', code: 'unsupported-measurement', path: ['measurements'], message: - 'Every emitted measurement requires a catalog-owned Provider output discriminator.', + 'Every emitted measurement requires a catalog-owned Provider output role.', }, ]) } - const providerAccountSystem = parseAbsoluteUri( - result.data.source.providerAccountIdentifier.system, + const providerScopeSystem = parseAbsoluteUri( + result.data.source.providerScopeIdentifier.system, ) - const eventSequence = parsePositiveInteger(result.data.eventSequence) - const graphSystem = parseAbsoluteUri(result.data.graphIdentifierSystem) - if (!providerAccountSystem.ok) return providerAccountSystem - if (!eventSequence.ok) return eventSequence - if (!graphSystem.ok) return graphSystem + if (!providerScopeSystem.ok) return providerScopeSystem const identity = deriveProviderIdentities({ provider: result.data.source.adapter.provider, - providerAccountIdentifier: { - system: providerAccountSystem.value, - value: result.data.source.providerAccountIdentifier.value, + providerScopeIdentifier: { + system: providerScopeSystem.value, + value: result.data.source.providerScopeIdentifier.value, + assurance: result.data.source.providerScopeIdentifier.assurance, }, sourceType: result.data.source.sourceType, sourceNativeId: result.data.source.sourceNativeId, - outputDiscriminators: outputDiscriminators as [string, ...string[]], - eventSequence: eventSequence.value, - graphIdentifierSystem: graphSystem.value, + outputs: definedOutputCoordinates.map( + ({ outputRole, outputDiscriminator }) => ({ + kind: 'provider-output' as const, + outputRole, + outputDiscriminator, + }), + ), + eventSequence: result.data.eventSequence, + deployment: result.data.deploymentIdentity, }) if (!identity.ok) return identity return ok( diff --git a/packages/fhir/src/providers/recording.ts b/packages/fhir/src/providers/recording.ts index 77b53914..90811a27 100644 --- a/packages/fhir/src/providers/recording.ts +++ b/packages/fhir/src/providers/recording.ts @@ -12,32 +12,47 @@ import { groveFhirContractVersion, groveRecordingFormatRegistry, providerAdapterCatalog, - providerRawMappings, + providerRawOutputRoles, type ProviderRecordingFormat, } from './contract.generated.js' import { assemblerAgent, coding, + deduplicateIdentifiedEntries, + governedSourceIdentifier, identifiedEntry, identifier, makeApplicationDevice, + makeHostDevice, provenanceActivity, resourceId, sourceEntityAgent, } from './graph.js' import { + deriveApplicationEntryIdentity, + deriveDeviceSnapshotEntryIdentity, deriveProviderIdentities, - parseResourceIdentityInput, + deriveWriterRecordIdentifier, } from './identity.js' -import { containsReversibleIdentityRepresentation } from './privacy.js' -import { EXTENSIONS, PROFILES } from './profiles.js' +import { + EXTENSIONS, + PROFILES, + PROVIDER_RECORDING_OUTPUT_DISCRIMINATOR, + PROVIDER_RECORDING_OUTPUT_ROLE, +} from './profiles.js' import { applicationDeviceSchema, + deploymentIdentitySchema, fhirIdSchema, identifierInputSchema, + governedSourceIdentifierIssues, + governedSourceIdentifierSchema, nonBlankStringSchema, normalizeZodIssue, primitiveInstantSchema, + providerPatientReferenceSchema, + providerScopeIdentifierIssues, + providerScopeIdentifierSchema, } from './provider.js' import type { CanonicalBase64, @@ -48,21 +63,22 @@ import type { Sha1Base64, } from './types.js' import { + cloneJsonValue, deepFreeze, err, issues, ok, parseAbsoluteUri, parseFhirInstant, - parsePatientReference, - parsePositiveInteger, type Issue, - type PositiveInteger, type Result, } from '../core/index.js' import { createEntryIdentity } from '../mobile/identity.js' import type { IdentifiedEntryIdentityInput } from '../mobile/types.js' -import { parseCollectionBundle, type CollectionBundle } from '../r4/index.js' +import { + parseGroveMobileExchangeBundle, + type GroveMobileExchangeBundle, +} from '../r4/index.js' const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' @@ -160,16 +176,19 @@ export const parseMediaType = (value: unknown): Result => { return ok(value as MediaType) } -const parseAttachmentSize = (value: unknown): Result => { - const parsed = parsePositiveInteger(value) - if (!parsed.ok) return parsed - if (parsed.value > 2_147_483_647) { +const parseAttachmentSize = (value: unknown): Result => { + if ( + typeof value !== 'number' || + !Number.isInteger(value) || + value < 0 || + value > 2_147_483_647 + ) { return err( 'out-of-range', - 'Attachment.size must fit the FHIR R4 unsignedInt range.', + 'Attachment.size must be an integer in the FHIR R4 unsignedInt range 0...2147483647.', ) } - return parsed + return ok(value) } export const parseImmutableRecordingUrl = ( @@ -200,7 +219,7 @@ export const parseImmutableRecordingUrl = ( return ok(value as ImmutableRecordingUrl) } -const providerValues = Object.keys(providerRawMappings) as [ +const providerValues = Object.keys(providerRawOutputRoles) as [ ConnectedRawProvider, ...ConnectedRawProvider[], ] @@ -210,23 +229,28 @@ const recordingSourceSchema = z.strictObject({ kind: z.literal('providers'), provider: z.enum(providerValues), }), - providerAccountIdentifier: identifierInputSchema.extend({ - assurance: z.literal('deployment-scoped-pseudonym'), - }), + providerScopeIdentifier: providerScopeIdentifierSchema, sourceType: nonBlankStringSchema, sourceNativeId: nonBlankStringSchema, dataOrigin: applicationDeviceSchema, + writerRecord: z + .strictObject({ + applicationIdentifier: identifierInputSchema, + nativeRecordId: nonBlankStringSchema, + version: z + .string() + .regex(/^(?:0|[1-9]\d*)$/u) + .optional(), + }) + .optional(), }) const recordingAttachmentBase = { contentType: z.string().min(1), - title: nonBlankStringSchema, - format: z.enum( - Object.keys(groveRecordingFormatRegistry.formats) as [ - ProviderRecordingFormat, - ...ProviderRecordingFormat[], - ], - ), + title: nonBlankStringSchema.optional(), + format: z.literal( + 'provider-recording', + ) satisfies z.ZodType, payloadAssertion: z.enum( providerAdapterCatalog.rawPayloadAdmission.allowedAssertions, ), @@ -251,12 +275,15 @@ const recordingAttachmentSchema = z.discriminatedUnion('kind', [ const recordingBundleInputSchema = z.strictObject({ source: recordingSourceSchema, attachment: recordingAttachmentSchema, - subject: z.string(), + subject: providerPatientReferenceSchema, application: applicationDeviceSchema, - eventSequence: z.number(), - graphIdentifierSystem: z.string(), + eventSequence: z.string().regex(/^[1-9]\d*$/u), + deploymentIdentity: deploymentIdentitySchema, + nativeIdentifierDisclosure: governedSourceIdentifierSchema.optional(), documentDate: primitiveInstantSchema, + occurred: primitiveInstantSchema, recorded: primitiveInstantSchema, + assembled: primitiveInstantSchema, repositoryIds: z .strictObject({ bundle: fhirIdSchema.optional(), @@ -266,90 +293,42 @@ const recordingBundleInputSchema = z.strictObject({ .optional(), }) -const recordingGraphIdentityIssues = ( - input: z.infer, -): readonly Issue[] => { - const graphIdentityInputs: ReadonlyArray< - readonly [ - Parameters[0], - readonly string[], - ] - > = [ - [input.application.identity, ['application', 'identity']], - [input.source.dataOrigin.identity, ['source', 'dataOrigin', 'identity']], - ] - return graphIdentityInputs.flatMap(([identity, path]) => { - const parsedIdentity = parseResourceIdentityInput(identity) - return parsedIdentity.ok ? - [] - : parsedIdentity.issues.map((entry) => ({ - ...entry, - path: [...path, ...entry.path], - })) - }) -} - const hasRawMapping = ( provider: ConnectedRawProvider, sourceType: string, -): boolean => Object.hasOwn(providerRawMappings[provider], sourceType) - -const attachmentMetadataStrings = ( - attachment: z.infer, -): readonly string[] => { - if (attachment.kind === 'external') { - return [ - attachment.contentType, - attachment.title, - attachment.hash, - attachment.url, - ] - } - const bytes = decodeCanonicalBase64(attachment.dataBase64) - return [ - attachment.contentType, - attachment.title, - ...(bytes === undefined ? [] : [encodeBase64(sha1(bytes))]), - ] -} - -const emittedCallerStrings = ( - input: z.infer, -): readonly string[] => [ - input.source.adapter.provider, - input.source.sourceType, - input.subject, - input.documentDate, - input.recorded, - ...attachmentMetadataStrings(input.attachment), - input.application.identity.identifier.system, - input.application.identity.identifier.value, - input.application.identity.id ?? '', - input.application.name, - input.application.version ?? '', - input.application.manufacturer ?? '', - input.source.dataOrigin.identity.identifier.system, - input.source.dataOrigin.identity.identifier.value, - input.source.dataOrigin.identity.id ?? '', - input.source.dataOrigin.name, - input.source.dataOrigin.version ?? '', - input.source.dataOrigin.manufacturer ?? '', - input.repositoryIds?.bundle ?? '', - input.repositoryIds?.document ?? '', - input.repositoryIds?.provenance ?? '', -] +): boolean => Object.hasOwn(providerRawOutputRoles[provider], sourceType) /** Strict parser for the closed Provider mapped-standard facade. */ export const parseProviderRecordingBundleInput = ( input: unknown, ): Result => { - const parsed = recordingBundleInputSchema.safeParse(input) - if (!parsed.success) { - return issues(parsed.error.issues.map(normalizeZodIssue)) + const snapshot = cloneJsonValue(input) + if (!snapshot.ok) return snapshot + let parsed: ReturnType + try { + parsed = recordingBundleInputSchema.safeParse(snapshot.value) + } catch { + return err( + 'schema-invalid', + 'Provider recording input validation could not safely inspect the supplied value.', + ) } + if (!parsed.success) return issues(parsed.error.issues.map(normalizeZodIssue)) const findings: Issue[] = [] - findings.push(...recordingGraphIdentityIssues(parsed.data)) + findings.push( + ...providerScopeIdentifierIssues( + parsed.data.source.adapter.provider, + parsed.data.source.providerScopeIdentifier, + ), + ) + findings.push( + ...governedSourceIdentifierIssues( + parsed.data.nativeIdentifierDisclosure, + parsed.data.source.sourceNativeId, + parsed.data.deploymentIdentity, + ), + ) if ( !hasRawMapping( parsed.data.source.adapter.provider, @@ -366,10 +345,10 @@ export const parseProviderRecordingBundleInput = ( const checks: ReadonlyArray< readonly [Result, ReadonlyArray] > = [ - [parsePatientReference(parsed.data.subject), ['subject']], - [parsePositiveInteger(parsed.data.eventSequence), ['eventSequence']], [parseFhirInstant(parsed.data.documentDate), ['documentDate']], + [parseFhirInstant(parsed.data.occurred), ['occurred']], [parseFhirInstant(parsed.data.recorded), ['recorded']], + [parseFhirInstant(parsed.data.assembled), ['assembled']], [ parseMediaType(parsed.data.attachment.contentType), ['attachment', 'contentType'], @@ -415,61 +394,36 @@ export const parseProviderRecordingBundleInput = ( }) } - const emittedStrings = emittedCallerStrings(parsed.data) - for (const [privateValue, path, label] of [ - [ - parsed.data.source.sourceNativeId, - ['source', 'sourceNativeId'], - 'sourceNativeId', - ], - [ - parsed.data.source.providerAccountIdentifier.value, - ['source', 'providerAccountIdentifier', 'value'], - 'providerAccountIdentifier.value', - ], - ] as const) { - if ( - emittedStrings.some( - (value) => - value !== '' && - containsReversibleIdentityRepresentation(value, privateValue), - ) - ) { - findings.push({ - severity: 'error', - code: 'invalid-identifier', - path, - message: `${label} is an identity input only and must not appear in emitted FHIR metadata.`, - }) - } - } - if (findings.length === 0) { - const providerAccountSystem = parseAbsoluteUri( - parsed.data.source.providerAccountIdentifier.system, + const providerScopeSystem = parseAbsoluteUri( + parsed.data.source.providerScopeIdentifier.system, ) - const eventSequence = parsePositiveInteger(parsed.data.eventSequence) - const graphSystem = parseAbsoluteUri(parsed.data.graphIdentifierSystem) - if (!graphSystem.ok) { - findings.push(...graphSystem.issues) - } else if (!providerAccountSystem.ok) { - findings.push(...providerAccountSystem.issues) - } else if (!eventSequence.ok) { - findings.push(...eventSequence.issues) + if (!providerScopeSystem.ok) { + findings.push(...providerScopeSystem.issues) } else { const identity = deriveProviderIdentities({ provider: parsed.data.source.adapter.provider, - providerAccountIdentifier: { - system: providerAccountSystem.value, - value: parsed.data.source.providerAccountIdentifier.value, + providerScopeIdentifier: { + system: providerScopeSystem.value, + value: parsed.data.source.providerScopeIdentifier.value, + assurance: parsed.data.source.providerScopeIdentifier.assurance, }, sourceType: parsed.data.source.sourceType, sourceNativeId: parsed.data.source.sourceNativeId, - outputDiscriminators: [ - providerAdapterCatalog.recordingDocument.outputDiscriminator, + outputs: [ + { + kind: 'provider-output', + outputRole: PROVIDER_RECORDING_OUTPUT_ROLE, + outputDiscriminator: PROVIDER_RECORDING_OUTPUT_DISCRIMINATOR, + }, + { + kind: 'provider-artifact', + formatCode: parsed.data.attachment.format, + partIndex: '0', + }, ], - eventSequence: eventSequence.value, - graphIdentifierSystem: graphSystem.value, + eventSequence: parsed.data.eventSequence, + deployment: parsed.data.deploymentIdentity, }) if (!identity.ok) findings.push(...identity.issues) } @@ -484,14 +438,18 @@ interface RecordingGraphIdentities { readonly system: import('../core/index.js').AbsoluteUri readonly value: string } - readonly exchange: { + readonly event: { readonly system: import('../core/index.js').AbsoluteUri readonly value: string } readonly document: IdentifiedEntryIdentityInput + readonly sourceArtifact: import('../mobile/types.js').CompleteIdentifierInput readonly provenance: IdentifiedEntryIdentityInput readonly application: IdentifiedEntryIdentityInput + readonly applicationHost?: IdentifiedEntryIdentityInput readonly dataOrigin: IdentifiedEntryIdentityInput + readonly dataOriginHost?: IdentifiedEntryIdentityInput + readonly writerRecord?: import('../mobile/types.js').CompleteIdentifierInput } const resolveRecordingGraphIdentities = ( @@ -499,63 +457,116 @@ const resolveRecordingGraphIdentities = ( ): Result => { const connected = deriveProviderIdentities({ provider: input.source.adapter.provider, - providerAccountIdentifier: input.source.providerAccountIdentifier, + providerScopeIdentifier: input.source.providerScopeIdentifier, sourceType: input.source.sourceType, sourceNativeId: input.source.sourceNativeId, - outputDiscriminators: [ - providerAdapterCatalog.recordingDocument.outputDiscriminator, + outputs: [ + { + kind: 'provider-output', + outputRole: PROVIDER_RECORDING_OUTPUT_ROLE, + outputDiscriminator: PROVIDER_RECORDING_OUTPUT_DISCRIMINATOR, + }, + { + kind: 'provider-artifact', + formatCode: input.attachment.format, + partIndex: '0', + }, ], eventSequence: input.eventSequence, - graphIdentifierSystem: input.graphIdentifierSystem, + deployment: input.deploymentIdentity, }) if (!connected.ok) return connected - // A recording document is a one-to-one conversion, so its identity is the source record's. + const documentIdentifier = connected.value.outputs[0] + const sourceArtifact = connected.value.outputs[1] + if (documentIdentifier === undefined || sourceArtifact === undefined) { + return err( + 'missing-required', + 'The recording output and source artifact identities are required.', + ) + } const document = createEntryIdentity( - connected.value.outputs[0] ?? connected.value.sourceRecord, + documentIdentifier, input.repositoryIds?.document, ) if (!document.ok) return document const provenance = createEntryIdentity( - connected.value.conversion, + connected.value.provenanceNode, input.repositoryIds?.provenance, ) if (!provenance.ok) return provenance - const application = createEntryIdentity( - input.application.identity.identifier, - input.application.identity.id, + const application = deriveApplicationEntryIdentity( + input.deploymentIdentity, + connected.value.event, + input.application, ) if (!application.ok) return application - const dataOrigin = createEntryIdentity( - input.source.dataOrigin.identity.identifier, - input.source.dataOrigin.identity.id, + const applicationHost = + input.application.host === undefined ? + undefined + : deriveDeviceSnapshotEntryIdentity( + input.deploymentIdentity, + connected.value.event, + input.application.host.sourceDeviceToken, + 'host', + input.application.host.id, + ) + if (applicationHost !== undefined && !applicationHost.ok) { + return applicationHost + } + const dataOrigin = deriveApplicationEntryIdentity( + input.deploymentIdentity, + connected.value.event, + input.source.dataOrigin, ) if (!dataOrigin.ok) return dataOrigin - - const fullUrls = [ - document.value.fullUrl, - provenance.value.fullUrl, - application.value.fullUrl, - dataOrigin.value.fullUrl, - ] - if (new Set(fullUrls).size !== fullUrls.length) { - return err( - 'duplicate-identifier', - 'Every Bundle entry requires a distinct business identifier.', - ) + const dataOriginHost = + input.source.dataOrigin.host === undefined ? + undefined + : deriveDeviceSnapshotEntryIdentity( + input.deploymentIdentity, + connected.value.event, + input.source.dataOrigin.host.sourceDeviceToken, + 'host', + input.source.dataOrigin.host.id, + ) + if (dataOriginHost !== undefined && !dataOriginHost.ok) { + return dataOriginHost } + + const writerRecord = + input.source.writerRecord === undefined ? + undefined + : deriveWriterRecordIdentifier( + input.deploymentIdentity, + input.source.writerRecord.applicationIdentifier, + input.source.writerRecord.nativeRecordId, + ) + if (writerRecord !== undefined && !writerRecord.ok) return writerRecord + return ok({ sourceRecord: connected.value.sourceRecord, - exchange: connected.value.exchange, + event: connected.value.event, document: document.value, + sourceArtifact, provenance: provenance.value, application: application.value, dataOrigin: dataOrigin.value, + ...(applicationHost === undefined ? + {} + : { applicationHost: applicationHost.value }), + ...(dataOriginHost === undefined ? + {} + : { dataOriginHost: dataOriginHost.value }), + ...(writerRecord === undefined ? {} : { writerRecord: writerRecord.value }), }) } +const PROVIDER_TITLES = Object.fromEntries( + providerAdapterCatalog.providers.map(({ id, title }) => [id, title]), +) as Readonly> + const providerTitle = (provider: ConnectedRawProvider): string => - providerAdapterCatalog.providers.find((entry) => entry.id === provider) - ?.title ?? provider + PROVIDER_TITLES[provider] const attachmentFor = (input: ProviderRecordingBundleInput['attachment']) => { if (input.kind === 'external') { @@ -564,7 +575,7 @@ const attachmentFor = (input: ProviderRecordingBundleInput['attachment']) => { url: input.url, size: input.size, hash: input.hash, - title: input.title, + ...(input.title === undefined ? {} : { title: input.title }), } } const validatedBytes = decodeBase64(input.dataBase64) @@ -573,7 +584,7 @@ const attachmentFor = (input: ProviderRecordingBundleInput['attachment']) => { data: input.dataBase64, size: validatedBytes.length, hash: encodeBase64(sha1(validatedBytes)), - title: input.title, + ...(input.title === undefined ? {} : { title: input.title }), } } @@ -584,7 +595,7 @@ const attachmentFor = (input: ProviderRecordingBundleInput['attachment']) => { */ export const buildProviderRecordingBundle = ( input: ProviderRecordingBundleInput, -): Result => { +): Result => { const parsed = parseProviderRecordingBundleInput(input) if (!parsed.ok) return parsed const validated = parsed.value @@ -610,25 +621,37 @@ export const buildProviderRecordingBundle = ( url: EXTENSIONS.providerSourceType, valueCode: sourceCode, }, + ...(validated.source.writerRecord?.version === undefined ? + [] + : [ + { + url: EXTENSIONS.writerRecordVersion, + valueString: validated.source.writerRecord.version, + }, + ]), ], - // A one-to-one conversion identifies the document by its source record, so the entry identity - // is that same pair; listing it twice would repeat one identifier on the resource. identifier: [ identifier(identities.value.sourceRecord), - ...(( - identities.value.document.identifier.system === - identities.value.sourceRecord.system && - identities.value.document.identifier.value === - identities.value.sourceRecord.value - ) ? + identifier(identities.value.document.identifier), + identifier(identities.value.sourceArtifact), + ...(identities.value.writerRecord === undefined ? + [] + : [identifier(identities.value.writerRecord)]), + ...(validated.nativeIdentifierDisclosure === undefined ? [] - : [identifier(identities.value.document.identifier)]), + : [governedSourceIdentifier(validated.nativeIdentifierDisclosure)]), ], status: 'current' as const, type: { text: `${providerTitle(validated.source.adapter.provider)} ${validated.source.sourceType} archive`, }, - subject: { reference: validated.subject }, + subject: { + type: validated.subject.type, + identifier: { + system: validated.subject.identifier.system, + value: validated.subject.identifier.value, + }, + }, date: validated.documentDate, author: [{ reference: identities.value.application.fullUrl }], content: [ @@ -648,13 +671,48 @@ export const buildProviderRecordingBundle = ( }, ], } - const application = makeApplicationDevice(validated.application) - const dataOrigin = makeApplicationDevice(validated.source.dataOrigin) + const application = makeApplicationDevice({ + ...validated.application, + identity: identities.value.application, + ...(identities.value.applicationHost === undefined ? + {} + : { parentReference: identities.value.applicationHost.fullUrl }), + }) + const applicationHost = + ( + validated.application.host === undefined || + identities.value.applicationHost === undefined + ) ? + undefined + : makeHostDevice({ + ...validated.application.host, + identity: identities.value.applicationHost, + }) + const dataOrigin = makeApplicationDevice({ + ...validated.source.dataOrigin, + identity: identities.value.dataOrigin, + ...(identities.value.dataOriginHost === undefined ? + {} + : { parentReference: identities.value.dataOriginHost.fullUrl }), + }) + const dataOriginHost = + ( + validated.source.dataOrigin.host === undefined || + identities.value.dataOriginHost === undefined + ) ? + undefined + : makeHostDevice({ + ...validated.source.dataOrigin.host, + identity: identities.value.dataOriginHost, + }) const provenance = { resourceType: 'Provenance' as const, ...resourceId(identities.value.provenance), - meta: { profile: [PROFILES.providerConversionProvenance] }, + meta: { + profile: [PROFILES.providerConversionProvenance], + }, target: [{ reference: identities.value.document.fullUrl }], + occurredDateTime: validated.occurred, recorded: validated.recorded, activity: provenanceActivity(), agent: [assemblerAgent(identities.value.application.fullUrl)], @@ -667,20 +725,35 @@ export const buildProviderRecordingBundle = ( ], } - return parseCollectionBundle({ + const entries = deduplicateIdentifiedEntries([ + identifiedEntry(identities.value.document, document), + ...(( + dataOriginHost === undefined || + identities.value.dataOriginHost === undefined + ) ? + [] + : [identifiedEntry(identities.value.dataOriginHost, dataOriginHost)]), + identifiedEntry(identities.value.dataOrigin, dataOrigin), + ...(( + applicationHost === undefined || + identities.value.applicationHost === undefined + ) ? + [] + : [identifiedEntry(identities.value.applicationHost, applicationHost)]), + identifiedEntry(identities.value.application, application), + identifiedEntry(identities.value.provenance, provenance), + ]) + if (!entries.ok) return entries + + return parseGroveMobileExchangeBundle({ resourceType: 'Bundle', ...(validated.repositoryIds?.bundle === undefined ? {} : { id: validated.repositoryIds.bundle }), meta: { profile: [PROFILES.mobileBundle] }, - identifier: identifier(identities.value.exchange), + identifier: identifier(identities.value.event), type: 'collection', - timestamp: validated.recorded, - entry: [ - identifiedEntry(identities.value.document, document), - identifiedEntry(identities.value.dataOrigin, dataOrigin), - identifiedEntry(identities.value.application, application), - identifiedEntry(identities.value.provenance, provenance), - ], + timestamp: validated.assembled, + entry: entries.value, }) } diff --git a/packages/fhir/src/providers/retraction.ts b/packages/fhir/src/providers/retraction.ts new file mode 100644 index 00000000..9a9722a3 --- /dev/null +++ b/packages/fhir/src/providers/retraction.ts @@ -0,0 +1,529 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { z } from 'zod' +import { + providerAdapterCatalog, + providerRawOutputRoles, + providerScalarOutputDiscriminators, + providerScalarOutputRoles, +} from './contract.generated.js' +import { + concept, + deduplicateIdentifiedEntries, + identifiedEntry, + identifier, + makeApplicationDevice, + makeHostDevice, + resourceId, +} from './graph.js' +import { + deriveApplicationEntryIdentity, + deriveDeviceSnapshotEntryIdentity, + deriveProviderIdentities, + type DeviceSnapshotRole, +} from './identity.js' +import { + EXTENSIONS, + PROFILES, + PROVIDER_RECORDING_OUTPUT_DISCRIMINATOR, + PROVIDER_RECORDING_OUTPUT_ROLE, + SYSTEMS, +} from './profiles.js' +import { + applicationDeviceSchema, + deploymentIdentitySchema, + fhirIdSchema, + nonBlankStringSchema, + normalizeZodIssue, + primitiveInstantSchema, + providerScopeIdentifierIssues, + providerScopeIdentifierSchema, +} from './provider.js' +import type { + ConnectedProvider, + ProviderScopeIdentifierInput, +} from './types.js' +import { + cloneJsonValue, + deepFreeze, + err, + issues, + type FhirId, + type FhirInstant, + type Issue, + type Result, +} from '../core/index.js' +import { groveMobileContract } from '../mobile/contract.js' +import { + createEntryIdentity, + deriveEventIdentifier, + deriveOpaqueIdentifier, + validateDeploymentIdentity, +} from '../mobile/identity.js' +import { type groveExchangeProtocol } from '../mobile/measurement-catalog.generated.js' +import type { + ApplicationDeviceInput, + CompleteIdentifierInput, + DeploymentIdentityInput, +} from '../mobile/types.js' +import { + parseGroveMobileRetractionBundle, + type GroveMobileRetractionBundle, +} from '../r4/index.js' + +export type RetractionTargetRole = + keyof typeof groveExchangeProtocol.lifecycle.retraction.targetRoles + +export type ProviderRetractionTargetInput = + | { + readonly role: 'primary-output' + readonly resourceType: 'Observation' + /** Exact closed output role used when the active Observation was emitted. */ + readonly outputRole: string + /** Exact closed discriminator paired with the output role in the catalog. */ + readonly outputDiscriminator: string + } + | { + readonly role: 'source-artifact' + readonly resourceType: 'DocumentReference' + readonly formatCode: 'provider-recording' + readonly partIndex: '0' + } + | { + readonly role: 'device-snapshot' + readonly resourceType: 'Device' + readonly priorEventSequence: string + readonly deviceRole: DeviceSnapshotRole + readonly sourceDeviceToken: string + } + +export interface ProviderRetractionInput< + Provider extends ConnectedProvider = ConnectedProvider, +> { + readonly source: { + readonly provider: Provider + readonly providerScopeIdentifier: ProviderScopeIdentifierInput + readonly sourceType: string + readonly sourceNativeId: string + } + readonly targets: readonly [ + ProviderRetractionTargetInput, + ...ProviderRetractionTargetInput[], + ] + readonly application: ApplicationDeviceInput + readonly eventSequence: string + readonly deploymentIdentity: DeploymentIdentityInput + /** Time the producer learned that the source record was no longer exposed. */ + readonly occurred: FhirInstant + /** Time the retraction Provenance assertion was recorded. */ + readonly recorded: FhirInstant + /** Time the immutable retraction Bundle was assembled. */ + readonly assembled: FhirInstant + readonly repositoryIds?: { + readonly bundle?: FhirId + readonly provenance?: FhirId + } +} + +const providerCodes = new Set( + providerAdapterCatalog.providers.map(({ id }) => id as string), +) +const providerSourceTypes = new Map( + providerAdapterCatalog.providers.map(({ id, sourceTypes }) => [ + id as string, + new Set(sourceTypes.map(({ token }) => token as string)), + ]), +) +const POSITIVE_DECIMAL = /^[1-9]\d*$/u + +const comparePositiveDecimals = (left: string, right: string): -1 | 0 | 1 => { + if (left.length !== right.length) return left.length < right.length ? -1 : 1 + if (left === right) return 0 + return left < right ? -1 : 1 +} + +const retractionTargetSchema = z.discriminatedUnion('role', [ + z.strictObject({ + role: z.literal('primary-output'), + resourceType: z.literal('Observation'), + outputRole: nonBlankStringSchema, + outputDiscriminator: nonBlankStringSchema, + }), + z.strictObject({ + role: z.literal('source-artifact'), + resourceType: z.literal('DocumentReference'), + formatCode: z.literal('provider-recording'), + partIndex: z.literal('0'), + }), + z.strictObject({ + role: z.literal('device-snapshot'), + resourceType: z.literal('Device'), + priorEventSequence: z.string().regex(POSITIVE_DECIMAL), + deviceRole: z.enum(['application', 'host', 'recording-device']), + sourceDeviceToken: nonBlankStringSchema, + }), +]) + +const retractionInputSchema = z.strictObject({ + source: z.strictObject({ + provider: nonBlankStringSchema, + providerScopeIdentifier: providerScopeIdentifierSchema, + sourceType: nonBlankStringSchema, + sourceNativeId: nonBlankStringSchema, + }), + targets: z.array(retractionTargetSchema).nonempty(), + application: applicationDeviceSchema, + eventSequence: z.string().regex(POSITIVE_DECIMAL), + deploymentIdentity: deploymentIdentitySchema, + occurred: primitiveInstantSchema, + recorded: primitiveInstantSchema, + assembled: primitiveInstantSchema, + repositoryIds: z + .strictObject({ + bundle: fhirIdSchema.optional(), + provenance: fhirIdSchema.optional(), + }) + .optional(), +}) + +const targetIsAdmitted = ( + input: ProviderRetractionInput, + target: ProviderRetractionTargetInput, +): boolean => { + if (target.role === 'primary-output') { + const mapping = ( + providerScalarOutputRoles as Readonly< + Record< + string, + Readonly> | undefined>> + > + > + )[input.source.provider]?.[input.source.sourceType] + const discriminators = ( + providerScalarOutputDiscriminators as Readonly< + Record< + string, + Readonly> | undefined>> + > + > + )[input.source.provider]?.[input.source.sourceType] + return ( + mapping !== undefined && + discriminators !== undefined && + Object.keys(mapping).some( + (measurementId) => + mapping[measurementId] === target.outputRole && + discriminators[measurementId] === target.outputDiscriminator, + ) + ) + } + if (target.role === 'source-artifact') { + return ( + ( + providerRawOutputRoles as Readonly< + Record> | undefined> + > + )[input.source.provider]?.[input.source.sourceType] !== undefined + ) + } + return ( + comparePositiveDecimals(target.priorEventSequence, input.eventSequence) < 0 + ) +} + +const validateInput = (input: ProviderRetractionInput): readonly Issue[] => { + const findings: Issue[] = [] + findings.push( + ...providerScopeIdentifierIssues( + input.source.provider, + input.source.providerScopeIdentifier, + ), + ) + const deployment = validateDeploymentIdentity(input.deploymentIdentity) + if (!deployment.ok) { + findings.push( + ...deployment.issues.map((issue) => ({ + ...issue, + path: ['deploymentIdentity', ...issue.path], + })), + ) + } + if (!providerCodes.has(input.source.provider)) { + findings.push({ + severity: 'error', + code: 'invalid-code', + path: ['source', 'provider'], + message: 'Retraction source provider must be catalog-owned.', + }) + } else if ( + !providerSourceTypes + .get(input.source.provider) + ?.has(input.source.sourceType) + ) { + findings.push({ + severity: 'error', + code: 'invalid-code', + path: ['source', 'sourceType'], + message: 'Retraction source type must be catalog-owned for its provider.', + }) + } + for (const [index, target] of input.targets.entries()) { + if (!targetIsAdmitted(input, target)) { + findings.push({ + severity: 'error', + code: 'unsupported-measurement', + path: ['targets', index], + message: + 'The Provider facade can retract only a catalog-admitted primary output, source artifact, or device snapshot.', + }) + } + } + if ( + new Set(input.targets.map((target) => JSON.stringify(target))).size !== + input.targets.length + ) { + findings.push({ + severity: 'error', + code: 'duplicate-identifier', + path: ['targets'], + message: 'Retraction targets must be unique.', + }) + } + return findings +} + +/** Strict runtime boundary for one Provider source-record retraction assertion. */ +export const parseProviderRetractionInput = ( + input: unknown, +): Result => { + const snapshot = cloneJsonValue(input) + if (!snapshot.ok) return snapshot + let parsed: ReturnType + try { + parsed = retractionInputSchema.safeParse(snapshot.value) + } catch { + return err( + 'schema-invalid', + 'Provider retraction input validation could not safely inspect the supplied value.', + ) + } + if (!parsed.success) return issues(parsed.error.issues.map(normalizeZodIssue)) + const value = parsed.data as unknown as ProviderRetractionInput + const findings = validateInput(value) + if (findings.length > 0) return issues(findings) + return { ok: true, value: deepFreeze(value) as ProviderRetractionInput } +} + +const sourceComponents = ( + input: ProviderRetractionInput, +): readonly [string, string, string, string, string] => [ + input.source.provider, + input.source.sourceType, + input.source.providerScopeIdentifier.system, + input.source.providerScopeIdentifier.value, + input.source.sourceNativeId, +] + +const deriveTargetIdentifier = ( + input: ProviderRetractionInput, + target: ProviderRetractionTargetInput, +): Result => { + const source = sourceComponents(input) + if (target.role === 'source-artifact') { + return deriveOpaqueIdentifier(input.deploymentIdentity, 'provider-output', [ + ...source, + PROVIDER_RECORDING_OUTPUT_ROLE, + PROVIDER_RECORDING_OUTPUT_DISCRIMINATOR, + ]) + } + if (target.role === 'device-snapshot') { + const priorEvent = deriveEventIdentifier( + input.deploymentIdentity, + target.priorEventSequence, + ) + if (!priorEvent.ok) return priorEvent + return deriveOpaqueIdentifier(input.deploymentIdentity, 'device-snapshot', [ + priorEvent.value.system, + priorEvent.value.value, + target.deviceRole, + target.sourceDeviceToken, + ]) + } + return deriveOpaqueIdentifier(input.deploymentIdentity, 'provider-output', [ + ...source, + target.outputRole, + target.outputDiscriminator, + ]) +} + +const compareCanonicalText = (left: string, right: string): -1 | 0 | 1 => { + if (left === right) return 0 + return left < right ? -1 : 1 +} + +const compareTargetReferences = ( + left: { + readonly extension: readonly [{ readonly valueCode: RetractionTargetRole }] + readonly identifier: ReturnType + readonly type: string + }, + right: { + readonly extension: readonly [{ readonly valueCode: RetractionTargetRole }] + readonly identifier: ReturnType + readonly type: string + }, +): number => + compareCanonicalText( + left.extension[0].valueCode, + right.extension[0].valueCode, + ) || + compareCanonicalText(left.type, right.type) || + compareCanonicalText( + left.identifier.system ?? '', + right.identifier.system ?? '', + ) || + compareCanonicalText( + left.identifier.value ?? '', + right.identifier.value ?? '', + ) + +/** + * Builds one append-only source-retraction assertion. + * + * It emits logical typed targets only. It never copies, mutates, tombstones, or marks prior + * clinical resources entered-in-error; receiver lifecycle policy remains separate. + */ +export const buildProviderRetractionBundle = ( + input: ProviderRetractionInput, +): Result => { + const parsed = parseProviderRetractionInput(input) + if (!parsed.ok) return parsed + const validated = parsed.value + + const identities = deriveProviderIdentities({ + provider: validated.source.provider, + providerScopeIdentifier: validated.source.providerScopeIdentifier, + sourceType: validated.source.sourceType, + sourceNativeId: validated.source.sourceNativeId, + outputs: [], + eventSequence: validated.eventSequence, + deployment: validated.deploymentIdentity, + provenanceNodeRole: 'retraction-provenance', + }) + if (!identities.ok) return identities + const provenanceIdentity = createEntryIdentity( + identities.value.provenanceNode, + validated.repositoryIds?.provenance, + ) + if (!provenanceIdentity.ok) return provenanceIdentity + const applicationIdentity = deriveApplicationEntryIdentity( + validated.deploymentIdentity, + identities.value.event, + validated.application, + ) + if (!applicationIdentity.ok) return applicationIdentity + const hostIdentity = + validated.application.host === undefined ? + undefined + : deriveDeviceSnapshotEntryIdentity( + validated.deploymentIdentity, + identities.value.event, + validated.application.host.sourceDeviceToken, + 'host', + validated.application.host.id, + ) + if (hostIdentity !== undefined && !hostIdentity.ok) return hostIdentity + + const targetReferences: Array<{ + readonly extension: readonly [ + { readonly url: string; readonly valueCode: RetractionTargetRole }, + ] + readonly identifier: ReturnType + readonly type: string + }> = [] + for (const target of validated.targets) { + const targetIdentifier = deriveTargetIdentifier(validated, target) + if (!targetIdentifier.ok) return targetIdentifier + targetReferences.push({ + extension: [ + { + url: EXTENSIONS.retractionTargetRole, + valueCode: target.role, + }, + ], + type: target.resourceType, + identifier: identifier(targetIdentifier.value), + }) + } + targetReferences.sort(compareTargetReferences) + + const provenance = { + resourceType: 'Provenance' as const, + ...resourceId(provenanceIdentity.value), + meta: { profile: [PROFILES.retractionProvenance] }, + target: targetReferences, + occurredDateTime: validated.occurred, + recorded: validated.recorded, + activity: concept( + SYSTEMS.groveLifecycleEvent, + groveMobileContract.lifecycle.sourceRecordRetracted, + 'Source record retracted', + ), + agent: [ + { + type: concept(SYSTEMS.provenanceParticipant, 'assembler', 'Assembler'), + who: { + reference: applicationIdentity.value.fullUrl, + }, + }, + ], + entity: [ + { + role: 'source' as const, + what: { identifier: identifier(identities.value.sourceRecord) }, + }, + ], + } + const application = makeApplicationDevice({ + ...validated.application, + identity: applicationIdentity.value, + ...(hostIdentity === undefined ? + {} + : { parentReference: hostIdentity.value.fullUrl }), + }) + const host = + validated.application.host === undefined || hostIdentity === undefined ? + undefined + : makeHostDevice({ + ...validated.application.host, + identity: hostIdentity.value, + }) + const entry = deduplicateIdentifiedEntries([ + ...(host === undefined || hostIdentity === undefined ? + [] + : [identifiedEntry(hostIdentity.value, host)]), + identifiedEntry(applicationIdentity.value, application), + identifiedEntry(provenanceIdentity.value, provenance), + ]) + if (!entry.ok) return entry + + return parseGroveMobileRetractionBundle( + deepFreeze({ + resourceType: 'Bundle' as const, + ...(validated.repositoryIds?.bundle === undefined ? + {} + : { id: validated.repositoryIds.bundle }), + meta: { profile: [PROFILES.retractionBundle] }, + identifier: identifier(identities.value.event), + type: 'collection' as const, + timestamp: validated.assembled, + entry: entry.value, + }), + ) +} diff --git a/packages/fhir/src/providers/types.ts b/packages/fhir/src/providers/types.ts index 22ce96b8..7b0670ee 100644 --- a/packages/fhir/src/providers/types.ts +++ b/packages/fhir/src/providers/types.ts @@ -7,46 +7,175 @@ // import type { - ProviderRawMappings, - ProviderScalarMappings, + AdapterMeasurementCatalog, + ProviderRawOutputRoles, + ProviderScalarOutputRoles, } from './contract.generated.js' -import type { - AbsoluteUri, - FhirId, - FhirInstant, - PatientReference, - PositiveInteger, - ResearchStudyReference, -} from '../core/index.js' +import type { AbsoluteUri, FhirId, FhirInstant } from '../core/index.js' import type { SharedMobileMeasurementKind } from '../mobile/measurement-catalog.generated.js' import type { ApplicationDeviceInput, CompleteIdentifierInput, + DeploymentIdentityInput, GatewayApplicationInput, + InstantEffectiveTime, MobileMeasurement, + PeriodEffectiveTime, RecordingDeviceInput, RecordingMethod, } from '../mobile/types.js' -type ScalarMappings = ProviderScalarMappings +type ScalarOutputRoles = ProviderScalarOutputRoles + +/** Deployment-owned pseudonym for a provider whose native keys are account-scoped. */ +export interface ProviderAccountScopeIdentifierInput extends CompleteIdentifierInput { + readonly assurance: 'deployment-scoped-account-pseudonym' +} + +/** Authoritative global provider key-space asserted for globally unique native keys. */ +export interface ProviderGlobalScopeIdentifierInput extends CompleteIdentifierInput { + readonly assurance: 'documented-global-key-space' +} + +/** Identifier-only Patient reference; the pseudonym is intentionally carried in emitted FHIR. */ +export interface ProviderPatientReferenceInput { + readonly type: 'Patient' + readonly identifier: CompleteIdentifierInput & { + readonly assurance: 'deployment-scoped-pseudonym' + } +} -/** Explicit assertion that this account key is a deployment-owned pseudonym. */ -export interface ProviderAccountPseudonymInput extends CompleteIdentifierInput { - readonly assurance: 'deployment-scoped-pseudonym' +/** Identifier-only ResearchStudy reference; no unresolved literal is emitted. */ +export interface ProviderResearchStudyReferenceInput { + readonly type: 'ResearchStudy' + readonly identifier: CompleteIdentifierInput } /** Exact closed provider codes defined by the Provider IG. */ -export type ConnectedProvider = keyof ScalarMappings +export type ConnectedProvider = keyof ScalarOutputRoles /** Exact source tokens that contain at least one admitted scalar mapping. */ export type ConnectedSourceType = - keyof ScalarMappings[Provider] & string + keyof ScalarOutputRoles[Provider] & string export type SupportedConnectedProviderMeasurementKind< Provider extends ConnectedProvider, SourceType extends ConnectedSourceType = ConnectedSourceType, -> = keyof ScalarMappings[Provider][SourceType] & SharedMobileMeasurementKind +> = keyof ScalarOutputRoles[Provider][SourceType] & string + +type ProviderContractRow = Extract< + (typeof import('./contract.generated.js').providerAdapterCatalog)['providers'][number], + { readonly id: Provider } +> + +type ProviderIdentifierScope = + ProviderContractRow['identifierScope'] + +type ProviderScopeMode = + ProviderContractRow extends ( + { + readonly providerScopeMode: infer Mode extends + 'deployment-scoped-account-pseudonym' | 'documented-global-key-space' + } + ) ? + Mode + : ProviderIdentifierScope extends 'account' ? + 'deployment-scoped-account-pseudonym' + : 'documented-global-key-space' + +/** + * Complete provider-scope pair required by the Provider identity protocol. + * Account-scoped providers use a deployment pseudonym; globally unique providers use one + * documented global key-space pair, so identity does not fragment across accounts. + */ +export type ProviderScopeIdentifierInput< + Provider extends ConnectedProvider = ConnectedProvider, +> = + Provider extends ConnectedProvider ? + ProviderScopeMode extends 'deployment-scoped-account-pseudonym' ? + ProviderAccountScopeIdentifierInput + : ProviderGlobalScopeIdentifierInput + : never + +type ProviderMeasurementOwner = + ProviderContractRow extends ( + { + readonly measurementOwner: infer Owner extends string + } + ) ? + Owner + : never + +type ExclusiveCatalogFor = + ProviderMeasurementOwner extends keyof AdapterMeasurementCatalog ? + AdapterMeasurementCatalog[ProviderMeasurementOwner] + : never + +/** Provider-owned measurement ids that intentionally have no source-neutral Mobile profile. */ +export type ProviderExclusiveMeasurementKind< + Provider extends ConnectedProvider, +> = keyof ExclusiveCatalogFor & string + +type ExclusiveDefinition< + Provider extends ConnectedProvider, + Kind extends ProviderExclusiveMeasurementKind, +> = ExclusiveCatalogFor[Kind] + +type ExclusiveEffective = + Definition extends { readonly effective: 'Period' } ? PeriodEffectiveTime + : Definition extends { readonly effective: 'dateTime' } ? InstantEffectiveTime + : Definition extends { readonly effective: 'dateTime-or-Period' } ? + InstantEffectiveTime | PeriodEffectiveTime + : never + +type ExclusiveResult = + Definition extends { readonly valueKind: 'quantity' } ? + { readonly value: number } + : Definition extends ( + { + readonly valueKind: 'codeableConcept' + readonly allowedValues: ReadonlyArray + } + ) ? + { readonly value: Value } + : never + +/** Exact normalized input for one provider-owned structured measurement profile. */ +export type ProviderExclusiveMeasurement< + Provider extends ConnectedProvider, + Kind extends ProviderExclusiveMeasurementKind = + ProviderExclusiveMeasurementKind, +> = + Kind extends ProviderExclusiveMeasurementKind ? + { + readonly kind: Kind + readonly effective: ExclusiveEffective< + ExclusiveDefinition + > + } & ExclusiveResult> + : never + +type ProviderMeasurementForKind< + Provider extends ConnectedProvider, + Kind extends string, +> = + | Extract< + MobileMeasurement, + { readonly kind: Kind & SharedMobileMeasurementKind } + > + | (Kind extends ProviderExclusiveMeasurementKind ? + ProviderExclusiveMeasurement + : never) + +export type ConnectedProviderMeasurement< + Provider extends ConnectedProvider, + SourceType extends ConnectedSourceType, +> = { + readonly [ + Kind in SupportedConnectedProviderMeasurementKind + ]: ProviderMeasurementForKind +}[SupportedConnectedProviderMeasurementKind] export interface ProviderAdapter< Provider extends ConnectedProvider = ConnectedProvider, @@ -58,19 +187,56 @@ export interface ProviderAdapter< interface NormalizedSourceRecordBase { readonly recordingMethod?: RecordingMethod readonly recordingDevice?: RecordingDeviceInput + /** Optional logical identity assigned by the application that wrote the source record. */ + readonly writerRecord?: WriterRecordInput +} + +export interface WriterRecordInput { + readonly applicationIdentifier: CompleteIdentifierInput + readonly nativeRecordId: string + /** Canonical non-negative writer revision, when the source exposes one. */ + readonly version?: string +} + +/** Optional FHIR type coding for a deliberately disclosed source-native Identifier. */ +export interface GovernedSourceIdentifierTypeCodingInput { + readonly system: AbsoluteUri + readonly code: string + readonly display?: string +} + +/** Narrow CodeableConcept surface for an optional source-native Identifier.type. */ +export interface GovernedSourceIdentifierTypeInput { + readonly coding?: readonly [ + GovernedSourceIdentifierTypeCodingInput, + ...GovernedSourceIdentifierTypeCodingInput[], + ] + readonly text?: string +} + +/** + * Explicit opt-in to place the exact source-native value under a caller-governed key-space URI. + * This traceability Identifier supplements Grove identities and is never an entry/retraction key. + */ +export interface GovernedSourceIdentifierInput { + readonly system: AbsoluteUri + readonly nativeId: string + readonly type?: GovernedSourceIdentifierTypeInput } /** - * Identity-only source fields. The native id is hashed and never copied into - * FHIR output. The account identifier must be deployment-scoped and - * pseudonymous; an email, vendor account id, or access credential is invalid. + * Source identity fields. The native id is HMAC input and omitted by default; + * an eligible graph may disclose it only through the explicit governed + * top-level configuration. The provider scope must match the catalog: a deployment-owned + * account pseudonym for account-scoped keys, or the documented global key-space pair for + * globally unique keys. An email, access credential, or inferred scope is invalid. */ export interface ProviderSourceRecord< Provider extends ConnectedProvider, SourceType extends ConnectedSourceType, > extends NormalizedSourceRecordBase { readonly adapter: ProviderAdapter - readonly providerAccountIdentifier: ProviderAccountPseudonymInput + readonly providerScopeIdentifier: ProviderScopeIdentifierInput readonly sourceType: SourceType readonly sourceNativeId: string /** Application that entered the record into the connected provider. */ @@ -81,26 +247,8 @@ export type ConnectedProviderMeasurements< Provider extends ConnectedProvider, SourceType extends ConnectedSourceType, > = readonly [ - Extract< - MobileMeasurement, - { - readonly kind: SupportedConnectedProviderMeasurementKind< - Provider, - SourceType - > - } - >, - ...Array< - Extract< - MobileMeasurement, - { - readonly kind: SupportedConnectedProviderMeasurementKind< - Provider, - SourceType - > - } - > - >, + ConnectedProviderMeasurement, + ...Array>, ] export interface ConnectedProviderRecord< @@ -125,14 +273,14 @@ export type NormalizedProviderRecord = { export type NormalizedSourceRecord = NormalizedProviderRecord['source'] -type RawMappings = ProviderRawMappings +type RawOutputRoles = ProviderRawOutputRoles /** Providers with at least one source admitted as a native Sensor recording. */ -export type ConnectedRawProvider = keyof RawMappings +export type ConnectedRawProvider = keyof RawOutputRoles /** Exact provider source tokens admitted by the Provider raw contract. */ export type ConnectedRawSourceType = - keyof RawMappings[Provider] & string + keyof RawOutputRoles[Provider] & string declare const recordingInputBrand: unique symbol @@ -161,17 +309,22 @@ export interface ProviderRecordingSourceRecord< SourceType extends ConnectedRawSourceType, > { readonly adapter: ProviderAdapter - readonly providerAccountIdentifier: ProviderAccountPseudonymInput + readonly providerScopeIdentifier: ProviderScopeIdentifierInput readonly sourceType: SourceType - /** Digest input only. It is never copied into emitted FHIR metadata. */ + /** + * Opaque-identity digest input. Omitted from FHIR by default; governed + * disclosure may place it on the sole source DocumentReference. + */ readonly sourceNativeId: string /** Application that entered the already-obtained record at the provider. */ readonly dataOrigin: ApplicationDeviceInput + readonly writerRecord?: WriterRecordInput } interface RecordingAttachmentBaseInput { readonly contentType: MediaType - readonly title: string + /** Optional human-facing presentation text; it never participates in identity. */ + readonly title?: string /** Registered payload format the recording bytes conform to. */ readonly format: ProviderRecordingFormat /** Required deployment assertion for opaque content Grove cannot inspect. */ @@ -193,7 +346,7 @@ export interface EmbeddedRecordingAttachmentInput extends RecordingAttachmentBas export interface ExternalRecordingAttachmentInput extends RecordingAttachmentBaseInput { readonly kind: 'external' readonly url: ImmutableRecordingUrl - readonly size: PositiveInteger + readonly size: number readonly hash: Sha1Base64 readonly immutabilityAssurance: 'immutable-version-specific' } @@ -219,47 +372,55 @@ export interface RecordingRepositoryAssignedResourceIds { export interface ProviderRecordingBundleInput { readonly source: ProviderRecordingSource readonly attachment: ProviderRecordingAttachmentInput - readonly subject: PatientReference + readonly subject: ProviderPatientReferenceInput readonly application: ApplicationDeviceInput /** Durable caller-owned sequence for a new conversion/exchange event. */ - readonly eventSequence: PositiveInteger - /** - * The namespace this deployment owns for graph nodes the export creates. - * - * The conversion Provenance and the exchange Bundle record an export event rather than anything - * read from the provider, so they are named here rather than in a namespace this guide owns. - */ - readonly graphIdentifierSystem: AbsoluteUri + readonly eventSequence: string + /** Deployment-owned, key-epoch-specific HMAC identity configuration. */ + readonly deploymentIdentity: DeploymentIdentityInput + readonly nativeIdentifierDisclosure?: GovernedSourceIdentifierInput readonly documentDate: FhirInstant + /** Source activity time carried on conversion Provenance.occurred[x]. */ + readonly occurred: FhirInstant readonly recorded: FhirInstant + /** Time the immutable exchange graph was assembled (Bundle.timestamp). */ + readonly assembled: FhirInstant readonly repositoryIds?: RecordingRepositoryAssignedResourceIds } export interface MeasurementRepositoryAssignedResourceIds { readonly bundle?: FhirId readonly observations?: Readonly< - Partial> + Partial> > readonly provenance?: FhirId } +/** Every shared or provider-owned measurement kind admitted by a connected source row. */ +export type ConnectedProviderMeasurementKind = { + readonly [Provider in ConnectedProvider]: { + readonly [ + SourceType in ConnectedSourceType + ]: SupportedConnectedProviderMeasurementKind + }[ConnectedSourceType] +}[ConnectedProvider] + interface ProviderMeasurementGraphInput { - readonly subject: PatientReference + readonly subject: ProviderPatientReferenceInput readonly application: ApplicationDeviceInput readonly gatewayApplication?: GatewayApplicationInput /** Durable caller-owned sequence for a new conversion/exchange event. */ - readonly eventSequence: PositiveInteger - /** - * The namespace this deployment owns for graph nodes the export creates. - * - * The conversion Provenance and the exchange Bundle record an export event rather than anything - * read from the provider, so they are named here rather than in a namespace this guide owns. - */ - readonly graphIdentifierSystem: AbsoluteUri - readonly issued: FhirInstant + readonly eventSequence: string + /** Deployment-owned, key-epoch-specific HMAC identity configuration. */ + readonly deploymentIdentity: DeploymentIdentityInput + readonly nativeIdentifierDisclosure?: GovernedSourceIdentifierInput + /** Source activity time carried on conversion Provenance.occurred[x]. */ + readonly occurred: FhirInstant readonly recorded: FhirInstant + /** Time the immutable exchange graph was assembled (Bundle.timestamp). */ + readonly assembled: FhirInstant readonly repositoryIds?: MeasurementRepositoryAssignedResourceIds - readonly researchStudyReferences?: readonly ResearchStudyReference[] + readonly researchStudyReferences?: readonly ProviderResearchStudyReferenceInput[] } export type ProviderMeasurementBundleInput = NormalizedProviderRecord & diff --git a/packages/fhir/src/questionnaire/builder.ts b/packages/fhir/src/questionnaire/builder.ts index 7e151eba..9deece5e 100644 --- a/packages/fhir/src/questionnaire/builder.ts +++ b/packages/fhir/src/questionnaire/builder.ts @@ -14,6 +14,12 @@ import { } from './contract.js' import { parseQuestionnaire, parseQuestionnaireResponse } from './parse.js' import { isR4ResourceType } from './r4-resource-types.js' +import { + isExactQuestionnaireUrl, + isQuestionnaireResponseBuilderReference, + QUESTIONNAIRE_RESPONSE_AUTHOR_TYPES, + QUESTIONNAIRE_RESPONSE_SOURCE_TYPES, +} from './references.js' import { questionnaireBuilderInputSchema, questionnaireResponseBuilderInputSchema, @@ -25,12 +31,13 @@ import type { QuestionnaireResponseInput, } from './types.js' import { + cloneJsonValue, + err, issues, parseAbsoluteUri, parseCanonical, parseFhirId, parseFhirInstant, - parsePatientReference, parseSemVer, type Issue, type Result, @@ -63,14 +70,23 @@ const schemaIssue = (entry: z.core.$ZodIssue): Issue => ({ }) const parseBuilderInput = (schema: z.ZodType, input: unknown): Result => { - const parsed = schema.safeParse(input) - return parsed.success ? - ({ ok: true, value: parsed.data as T, warnings: [] } as const) - : issues(parsed.error.issues.map(schemaIssue)) + const snapshot = cloneJsonValue(input) + if (!snapshot.ok) return snapshot + try { + const parsed = schema.safeParse(snapshot.value) + return parsed.success ? + ({ ok: true, value: parsed.data as T, warnings: [] } as const) + : issues(parsed.error.issues.map(schemaIssue)) + } catch { + return err( + 'schema-invalid', + 'Questionnaire builder input could not be safely inspected.', + ) + } } const extensionCount = ( - extensions: ReadonlyArray<{ readonly url: string }> | undefined, + extensions: ReadonlyArray<{ readonly url?: string }> | undefined, url: string, ) => extensions?.filter((extension) => extension.url === url).length ?? 0 @@ -78,9 +94,13 @@ const validateQuestionnaireInput = ( input: QuestionnaireInput, ): readonly Issue[] => { const failures: Issue[] = [] - if (!parseAbsoluteUri(input.url).ok) { + if (!isExactQuestionnaireUrl(input.url)) { failures.push( - issue('invalid-uri', ['url'], 'Questionnaire.url is invalid.'), + issue( + 'invalid-uri', + ['url'], + 'Questionnaire.url must be an exact absolute HTTP(S) canonical without a fragment or version delimiter.', + ), ) } if (!parseSemVer(input.version).ok) { @@ -233,18 +253,32 @@ const validateResponseInput = ( issue('invalid-identifier', ['id'], 'Response.id is invalid.'), ) } - if (input.subject !== undefined && !parsePatientReference(input.subject).ok) { + if ( + input.subject !== undefined && + !isQuestionnaireResponseBuilderReference(input.subject) + ) { failures.push( - issue('invalid-reference', ['subject'], 'Response.subject is invalid.'), + issue( + 'invalid-reference', + ['subject'], + 'Response.subject requires one typed literal or identifier-only logical R4 Reference.', + ), ) } - for (const [field, reference] of [ - ['authorReference', input.authorReference], - ['sourceReference', input.sourceReference], + for (const [field, reference, allowedTypes] of [ + ['author', input.author, QUESTIONNAIRE_RESPONSE_AUTHOR_TYPES], + ['source', input.source, QUESTIONNAIRE_RESPONSE_SOURCE_TYPES], ] as const) { - if (reference?.trim() === '') { + if ( + reference !== undefined && + !isQuestionnaireResponseBuilderReference(reference, allowedTypes) + ) { failures.push( - issue('invalid-reference', [field], `${field} must not be empty.`), + issue( + 'invalid-reference', + [field], + `${field} requires one typed literal or identifier-only logical Reference to an admitted target type.`, + ), ) } } @@ -298,14 +332,14 @@ export const buildQuestionnaireResponse = ( status: validatedInput.status, ...(validatedInput.subject === undefined ? {} - : { subject: { reference: validatedInput.subject } }), + : { subject: validatedInput.subject }), authored: validatedInput.authored, - ...(validatedInput.authorReference === undefined ? + ...(validatedInput.author === undefined ? {} - : { author: { reference: validatedInput.authorReference } }), - ...(validatedInput.sourceReference === undefined ? + : { author: validatedInput.author }), + ...(validatedInput.source === undefined ? {} - : { source: { reference: validatedInput.sourceReference } }), + : { source: validatedInput.source }), ...(validatedInput.items === undefined ? {} : { item: validatedInput.items }), diff --git a/packages/fhir/src/questionnaire/contract.generated.ts b/packages/fhir/src/questionnaire/contract.generated.ts index e11ec70d..a60597cc 100644 --- a/packages/fhir/src/questionnaire/contract.generated.ts +++ b/packages/fhir/src/questionnaire/contract.generated.ts @@ -9,13 +9,13 @@ import { deepFreeze } from '../core/index.js' -export const groveFhirContractVersion = '0.5.0' as const +export const groveFhirContractVersion = '0.6.0' as const export const groveFhirVersion = '4.0.1' as const const groveQuestionnairePackageMetadataValue = { fhirVersion: '4.0.1', - version: '0.5.0', + version: '0.6.0', packageId: 'org.grovealliance.fhir.questionnaire', canonical: 'https://grovealliance.org/fhir/questionnaire', dependencies: ['hl7.fhir.uv.sdc#4.0.0', 'hl7.fhir.uv.extensions.r4#5.3.0'], diff --git a/packages/fhir/src/questionnaire/contract.ts b/packages/fhir/src/questionnaire/contract.ts index bcf6c081..16145098 100644 --- a/packages/fhir/src/questionnaire/contract.ts +++ b/packages/fhir/src/questionnaire/contract.ts @@ -7,12 +7,19 @@ // import { - compareR4Temporal, - isR4Date, - isR4DateTime, - isR4Time, - type R4TemporalKind, -} from './temporal.js' + QUESTIONNAIRE_EXTENSIONS, + expectedAnswerOptionKeys, + expectedQuestionnaireAnswerKeys, + extensionValue, + extensionsFor, + firstExtensionValue, + issue, + populatedQuestionnaireAnswerKey, + temporalValidatorForItem, + validateBoundPair, + validateExpressionScope, + type ExtensionElement, +} from './questionnaire-extensions.js' import type { QuestionnaireItemInput, QuestionnaireResponseAnswerInput, @@ -21,478 +28,16 @@ import type { import type { Issue } from '../core/index.js' import type { Extension } from '../r4/index.js' -export const QUESTIONNAIRE_EXTENSIONS = { - calculatedExpression: - 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression', - enableWhenExpression: - 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression', - initialExpression: - 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', - hidden: 'http://hl7.org/fhir/StructureDefinition/questionnaire-hidden', - itemWeight: 'http://hl7.org/fhir/StructureDefinition/itemWeight', - maxDecimalPlaces: 'http://hl7.org/fhir/StructureDefinition/maxDecimalPlaces', - maxOccurs: 'http://hl7.org/fhir/StructureDefinition/questionnaire-maxOccurs', - maxQuantity: - 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-maxQuantity', - maxSize: 'http://hl7.org/fhir/StructureDefinition/maxSize', - maxValue: 'http://hl7.org/fhir/StructureDefinition/maxValue', - mimeType: 'http://hl7.org/fhir/StructureDefinition/mimeType', - minLength: 'http://hl7.org/fhir/StructureDefinition/minLength', - minOccurs: 'http://hl7.org/fhir/StructureDefinition/questionnaire-minOccurs', - minQuantity: - 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-minQuantity', - minValue: 'http://hl7.org/fhir/StructureDefinition/minValue', - optionExclusive: - 'http://hl7.org/fhir/StructureDefinition/questionnaire-optionExclusive', - questionnaireUnit: - 'http://hl7.org/fhir/StructureDefinition/questionnaire-unit', - styleSensitive: - 'http://hl7.org/fhir/StructureDefinition/rendering-styleSensitive', - targetConstraint: 'http://hl7.org/fhir/StructureDefinition/targetConstraint', - unitOption: - 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption', - unitValueSet: - 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitValueSet', - variable: 'http://hl7.org/fhir/StructureDefinition/variable', -} as const - -interface ExtensionElement { - readonly extension?: readonly Extension[] -} - -export interface ExtensionValue { - readonly key: string - readonly value: unknown -} - -export type QuestionnaireAnswerValueKey = - | 'valueAttachment' - | 'valueBoolean' - | 'valueCoding' - | 'valueDate' - | 'valueDateTime' - | 'valueDecimal' - | 'valueInteger' - | 'valueQuantity' - | 'valueString' - | 'valueTime' - | 'valueUri' - -/** Expected Questionnaire/Response value[x] discriminators for an item type. */ -export const expectedQuestionnaireAnswerKeys = ( - type: QuestionnaireItemInput['type'], -): readonly QuestionnaireAnswerValueKey[] => { - switch (type) { - case 'boolean': - return ['valueBoolean'] - case 'decimal': - return ['valueDecimal'] - case 'integer': - return ['valueInteger'] - case 'date': - return ['valueDate'] - case 'dateTime': - return ['valueDateTime'] - case 'time': - return ['valueTime'] - case 'string': - case 'text': - return ['valueString'] - case 'url': - return ['valueUri'] - case 'choice': - return ['valueCoding'] - case 'open-choice': - return ['valueCoding', 'valueString'] - case 'attachment': - return ['valueAttachment'] - case 'quantity': - return ['valueQuantity'] - case 'display': - case 'group': - return [] - } -} - -const expectedAnswerOptionKeys = ( - type: QuestionnaireItemInput['type'], -): readonly QuestionnaireAnswerValueKey[] => { - switch (type) { - case 'integer': - return ['valueInteger'] - case 'date': - return ['valueDate'] - case 'time': - return ['valueTime'] - case 'string': - return ['valueString'] - case 'choice': - return ['valueCoding'] - case 'open-choice': - return ['valueCoding', 'valueString'] - default: - return [] - } -} - -const questionnaireAnswerValueKeys: readonly QuestionnaireAnswerValueKey[] = [ - 'valueBoolean', - 'valueDecimal', - 'valueInteger', - 'valueDate', - 'valueDateTime', - 'valueTime', - 'valueString', - 'valueUri', - 'valueAttachment', - 'valueCoding', - 'valueQuantity', -] - -const populatedQuestionnaireAnswerKey = ( - value: object, -): QuestionnaireAnswerValueKey | undefined => - questionnaireAnswerValueKeys.find( - (key) => Reflect.get(value, key) !== undefined, - ) - -const issue = ( - code: Issue['code'], - path: Issue['path'], - message: string, -): Issue => ({ severity: 'error', code, path, message }) - -export const extensionsFor = ( - element: ExtensionElement, - url: string, -): readonly Extension[] => - (element.extension ?? []).filter((extension) => extension.url === url) - -export const extensionValue = ( - extension: Extension | undefined, -): ExtensionValue | undefined => { - if (extension === undefined) return undefined - const values = Object.entries(extension).filter( - ([key]) => key.startsWith('value') && key !== 'valueSet', - ) - return values.length === 1 ? - { key: values[0]?.[0] ?? '', value: values[0]?.[1] } - : undefined -} - -export const firstExtensionValue = ( - element: ExtensionElement, - url: string, -): ExtensionValue | undefined => { - const first = extensionsFor(element, url)[0] - return first === undefined ? undefined : extensionValue(first) -} - -const expressionPart = (value: unknown, key: string): unknown => - typeof value === 'object' && value !== null ? - Reflect.get(value, key) - : undefined - -const isValidFhirPathExpression = ( - value: unknown, - requireName = false, -): boolean => - expressionPart(value, 'language') === 'text/fhirpath' && - typeof expressionPart(value, 'expression') === 'string' && - (expressionPart(value, 'expression') as string).trim() !== '' && - (!requireName || - (typeof expressionPart(value, 'name') === 'string' && - (expressionPart(value, 'name') as string).trim() !== '')) - -const expressionUrls = new Set([ - QUESTIONNAIRE_EXTENSIONS.variable, - QUESTIONNAIRE_EXTENSIONS.enableWhenExpression, - QUESTIONNAIRE_EXTENSIONS.initialExpression, - QUESTIONNAIRE_EXTENSIONS.calculatedExpression, -]) - -const variableName = /^[A-Za-z]\w*$/u -const reservedVariableNames = new Set([ - 'context', - 'definition', - 'loinc', - 'qitem', - 'questionnaire', - 'resource', - 'rootResource', - 'sct', - 'target', - 'ucum', -]) - -const targetParts = ( - extension: Extension, - name: string, -): readonly Extension[] => - extension.extension?.filter((part) => part.url === name) ?? [] - -const validateVariableName = ( - value: unknown, - names: Set, - path: ReadonlyArray, -): readonly Issue[] => { - const name = expressionPart(value, 'name') - if (typeof name !== 'string' || name === '') return [] - const failures: Issue[] = [] - if (!variableName.test(name) || reservedVariableNames.has(name)) { - failures.push( - issue( - 'invalid-code', - [...path, 'valueExpression', 'name'], - `FHIRPath variable name ${name} is invalid or reserved.`, - ), - ) - } - if (names.has(name)) { - failures.push( - issue( - 'duplicate-identifier', - [...path, 'valueExpression', 'name'], - `FHIRPath variable name ${name} is duplicated in this scope.`, - ), - ) - } - names.add(name) - return failures -} - -const validateExpressionExtension = ( - extension: Extension, - names: Set, - path: ReadonlyArray, -): readonly Issue[] => { - if (!expressionUrls.has(extension.url)) return [] - const value = extensionValue(extension)?.value - const isVariable = extension.url === QUESTIONNAIRE_EXTENSIONS.variable - const failures: Issue[] = [] - if (!isValidFhirPathExpression(value, isVariable)) { - failures.push( - issue( - 'invalid-type', - path, - 'Expression extensions require a non-empty text/fhirpath expression and variables also require a name.', - ), - ) - } - if (isVariable) failures.push(...validateVariableName(value, names, path)) - return failures -} - -const validateTargetConstraint = ( - extension: Extension, - targetKeys: Set, - path: ReadonlyArray, -): readonly Issue[] => { - if (extension.url !== QUESTIONNAIRE_EXTENSIONS.targetConstraint) return [] - const failures: Issue[] = [] - const values = new Map() - for (const name of ['key', 'severity', 'human', 'expression']) { - const matching = targetParts(extension, name) - if (matching.length !== 1) { - failures.push( - issue( - matching.length === 0 ? 'missing-required' : 'duplicate-identifier', - path, - `targetConstraint requires exactly one ${name} part.`, - ), - ) - } - values.set(name, matching.length === 1 ? matching[0] : undefined) - } - if ((extension.extension ?? []).some((part) => !values.has(part.url))) { - failures.push( - issue( - 'invalid-choice', - path, - 'targetConstraint contains an unsupported nested part.', - ), - ) - } - const key = extensionValue(values.get('key')) - const severity = extensionValue(values.get('severity')) - const human = extensionValue(values.get('human')) - const expression = extensionValue(values.get('expression')) - if ( - key?.key !== 'valueId' || - typeof key.value !== 'string' || - key.value.trim() === '' - ) { - failures.push( - issue( - 'missing-required', - path, - 'targetConstraint requires a non-empty valueId key.', - ), - ) - } else if (targetKeys.has(key.value)) { - failures.push( - issue( - 'duplicate-identifier', - path, - `targetConstraint key ${key.value} is not unique.`, - ), - ) - } else { - targetKeys.add(key.value) - } - if ( - severity?.key !== 'valueCode' || - !['error', 'warning'].includes(String(severity.value)) - ) { - failures.push( - issue( - 'invalid-code', - path, - 'targetConstraint severity must be error or warning.', - ), - ) - } - if ( - human?.key !== 'valueString' || - typeof human.value !== 'string' || - human.value.trim() === '' - ) { - failures.push( - issue( - 'missing-required', - path, - 'targetConstraint requires non-empty human guidance.', - ), - ) - } - if ( - expression?.key !== 'valueExpression' || - !isValidFhirPathExpression(expression.value) - ) { - failures.push( - issue( - 'invalid-type', - path, - 'targetConstraint requires a non-empty text/fhirpath expression.', - ), - ) - } - return failures -} - -const validateExpressionScope = ( - element: ExtensionElement, - path: ReadonlyArray, - targetKeys: Set, -): readonly Issue[] => { - const failures: Issue[] = [] - const names = new Set() - for (const [index, extension] of (element.extension ?? []).entries()) { - const extensionPath = [...path, 'extension', index] - failures.push( - ...validateExpressionExtension(extension, names, extensionPath), - ) - failures.push( - ...validateTargetConstraint(extension, targetKeys, extensionPath), - ) - } - return failures -} - -const comparableQuantity = ( - value: unknown, -): readonly [number, string, string] | undefined => { - if (typeof value !== 'object' || value === null) return undefined - const quantity = value as Readonly> - const quantityValue = quantity.value - const system = quantity.system - const code = quantity.code - return ( - typeof quantityValue === 'number' && - Number.isFinite(quantityValue) && - typeof system === 'string' && - typeof code === 'string' - ) ? - [quantityValue, system, code] - : undefined -} - -/** Returns whether left is less than or equal to a comparable right value. */ -export const boundIsOrdered = ( - left: unknown, - right: unknown, - temporalKind?: R4TemporalKind, -): boolean | undefined => { - if (typeof left === 'number' && typeof right === 'number') { - return Number.isFinite(left) && Number.isFinite(right) ? - left <= right - : undefined - } - if (typeof left === 'string' && typeof right === 'string') { - if (temporalKind === undefined) return left <= right - const comparison = compareR4Temporal(left, right, temporalKind) - return comparison === undefined ? undefined : comparison <= 0 - } - const leftQuantity = comparableQuantity(left) - const rightQuantity = comparableQuantity(right) - if (leftQuantity === undefined || rightQuantity === undefined) - return undefined - return ( - leftQuantity[1] === rightQuantity[1] && - leftQuantity[2] === rightQuantity[2] - ) ? - leftQuantity[0] <= rightQuantity[0] - : undefined -} - -const temporalKindForItem = ( - type: QuestionnaireItemInput['type'], -): R4TemporalKind | undefined => { - if (type === 'date' || type === 'dateTime' || type === 'time') return type - return undefined -} - -const temporalValidatorForItem = ( - type: QuestionnaireItemInput['type'], -): ((value: unknown) => boolean) | undefined => { - switch (type) { - case 'date': - return isR4Date - case 'dateTime': - return isR4DateTime - case 'time': - return isR4Time - default: - return undefined - } -} - -const validateBoundPair = ( - item: QuestionnaireItemInput, - path: ReadonlyArray, - minimumUrl: string, - maximumUrl: string, -): readonly Issue[] => { - const minimum = firstExtensionValue(item, minimumUrl) - const maximum = firstExtensionValue(item, maximumUrl) - if (minimum === undefined || maximum === undefined) return [] - return ( - minimum.key === maximum.key && - boundIsOrdered( - minimum.value, - maximum.value, - temporalKindForItem(item.type), - ) === true - ) ? - [] - : [ - issue( - 'out-of-range', - path, - 'Minimum and maximum must use comparable values and minimum must not exceed maximum.', - ), - ] -} +export { + QUESTIONNAIRE_EXTENSIONS, + boundIsOrdered, + expectedQuestionnaireAnswerKeys, + extensionValue, + extensionsFor, + firstExtensionValue, + type ExtensionValue, + type QuestionnaireAnswerValueKey, +} from './questionnaire-extensions.js' const extensionCount = (item: ExtensionElement, url: string): number => extensionsFor(item, url).length @@ -681,17 +226,12 @@ const validateAuthoredItemValues = ( return failures } -// Each linear branch below mirrors one separately named normative rule family. -/* eslint-disable sonarjs/cognitive-complexity */ -const validateItem = ( +const validateItemIdentityAndStructure = ( item: QuestionnaireItemInput, path: ReadonlyArray, seenLinkIds: Set, - targetKeys: Set, ): readonly Issue[] => { const failures: Issue[] = [] - failures.push(...validateKnownItemExtensions(item, path)) - failures.push(...validateAuthoredItemValues(item, path)) if (item.linkId.trim() === '') { failures.push( issue( @@ -789,6 +329,14 @@ const validateItem = ( ), ) } + return failures +} + +const validateItemEnablement = ( + item: QuestionnaireItemInput, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] if ((item.enableWhen?.length ?? 0) > 1 && item.enableBehavior === undefined) { failures.push( issue( @@ -832,6 +380,15 @@ const validateItem = ( issue('invalid-choice', path, 'Use either initial or initialExpression.'), ) } + return failures +} + +const validateItemAnswerSources = ( + item: QuestionnaireItemInput, + path: ReadonlyArray, + targetKeys: Set, +): readonly Issue[] => { + const failures: Issue[] = [] if ( (item.answerOption?.length ?? 0) > 0 && item.answerValueSet !== undefined @@ -914,7 +471,14 @@ const validateItem = ( ) } failures.push(...validateExpressionScope(item, path, targetKeys)) + return failures +} +const validateTextAndDecimalConstraints = ( + item: QuestionnaireItemInput, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] const minimumLength = firstExtensionValue( item, QUESTIONNAIRE_EXTENSIONS.minLength, @@ -948,7 +512,6 @@ const validateItem = ( ), ) } - const decimalPlaces = firstExtensionValue( item, QUESTIONNAIRE_EXTENSIONS.maxDecimalPlaces, @@ -977,7 +540,14 @@ const validateItem = ( ), ) } + return failures +} +const validatePrimitiveValueBounds = ( + item: QuestionnaireItemInput, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] const minimumValue = firstExtensionValue( item, QUESTIONNAIRE_EXTENSIONS.minValue, @@ -1017,32 +587,39 @@ const validateItem = ( failures.push( issue('invalid-type', path, 'Bound datatype must match the item type.'), ) - } else { - const temporalValidator = temporalValidatorForItem(item.type) - if ( - temporalValidator !== undefined && - [minimumValue, maximumValue].some( - (entry) => entry !== undefined && !temporalValidator(entry.value), - ) - ) { - failures.push( - issue( - 'invalid-date-time', - path, - `Temporal bounds must be valid FHIR R4 ${item.type} primitives.`, - ), - ) - } + return failures + } + const temporalValidator = temporalValidatorForItem(item.type) + if ( + temporalValidator !== undefined && + [minimumValue, maximumValue].some( + (entry) => entry !== undefined && !temporalValidator(entry.value), + ) + ) { failures.push( - ...validateBoundPair( - item, + issue( + 'invalid-date-time', path, - QUESTIONNAIRE_EXTENSIONS.minValue, - QUESTIONNAIRE_EXTENSIONS.maxValue, + `Temporal bounds must be valid FHIR R4 ${item.type} primitives.`, ), ) } + failures.push( + ...validateBoundPair( + item, + path, + QUESTIONNAIRE_EXTENSIONS.minValue, + QUESTIONNAIRE_EXTENSIONS.maxValue, + ), + ) + return failures +} +const validateQuantityAndUnitConstraints = ( + item: QuestionnaireItemInput, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] const minimumQuantity = firstExtensionValue( item, QUESTIONNAIRE_EXTENSIONS.minQuantity, @@ -1077,7 +654,6 @@ const validateItem = ( ), ) } - const fixedUnits = extensionsFor( item, QUESTIONNAIRE_EXTENSIONS.questionnaireUnit, @@ -1117,7 +693,14 @@ const validateItem = ( ), ) } + return failures +} +const validateAttachmentAndOccurrenceConstraints = ( + item: QuestionnaireItemInput, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] const mimeTypes = extensionsFor(item, QUESTIONNAIRE_EXTENSIONS.mimeType) const maximumSize = firstExtensionValue( item, @@ -1146,7 +729,6 @@ const validateItem = ( issue('out-of-range', path, 'Maximum attachment size must be positive.'), ) } - const minimumOccurs = firstExtensionValue( item, QUESTIONNAIRE_EXTENSIONS.minOccurs, @@ -1188,7 +770,25 @@ const validateItem = ( issue('out-of-range', path, 'Occurrence bounds are invalid or reversed.'), ) } + return failures +} +const validateItem = ( + item: QuestionnaireItemInput, + path: ReadonlyArray, + seenLinkIds: Set, + targetKeys: Set, +): readonly Issue[] => { + const failures: Issue[] = [] + failures.push(...validateKnownItemExtensions(item, path)) + failures.push(...validateAuthoredItemValues(item, path)) + failures.push(...validateItemIdentityAndStructure(item, path, seenLinkIds)) + failures.push(...validateItemEnablement(item, path)) + failures.push(...validateItemAnswerSources(item, path, targetKeys)) + failures.push(...validateTextAndDecimalConstraints(item, path)) + failures.push(...validatePrimitiveValueBounds(item, path)) + failures.push(...validateQuantityAndUnitConstraints(item, path)) + failures.push(...validateAttachmentAndOccurrenceConstraints(item, path)) for (const [index, child] of (item.item ?? []).entries()) { failures.push( ...validateItem(child, [...path, 'item', index], seenLinkIds, targetKeys), @@ -1196,7 +796,6 @@ const validateItem = ( } return failures } -/* eslint-enable sonarjs/cognitive-complexity */ const validateQuestionnaireResponseAnswerContract = ( answer: QuestionnaireResponseAnswerInput, diff --git a/packages/fhir/src/questionnaire/index.ts b/packages/fhir/src/questionnaire/index.ts index 5c5fad9b..3e43098b 100644 --- a/packages/fhir/src/questionnaire/index.ts +++ b/packages/fhir/src/questionnaire/index.ts @@ -34,9 +34,12 @@ export type { QuestionnairePair, QuestionnairePreflightOptions, QuestionnaireResponseAnswerInput, + QuestionnaireResponseAuthorInput, QuestionnaireResponseIdentifierInput, QuestionnaireResponseInput, QuestionnaireResponseItemInput, + QuestionnaireResponseSourceInput, + QuestionnaireResponseSubjectInput, QuestionnaireValueSetConceptInput, ResolvedQuestionnaireValueSetInput, } from './types.js' diff --git a/packages/fhir/src/questionnaire/parse.ts b/packages/fhir/src/questionnaire/parse.ts index d3981397..b444068c 100644 --- a/packages/fhir/src/questionnaire/parse.ts +++ b/packages/fhir/src/questionnaire/parse.ts @@ -14,6 +14,12 @@ import { validateQuestionnaireContract, validateQuestionnaireResponseItemContract, } from './contract.js' +import { + isExactQuestionnaireUrl, + isQuestionnaireResponseReference, + QUESTIONNAIRE_RESPONSE_AUTHOR_TYPES, + QUESTIONNAIRE_RESPONSE_SOURCE_TYPES, +} from './references.js' import { questionnaireResponseSchema, questionnaireSchema } from './schemas.js' import type { GroveQuestionnaire, @@ -22,9 +28,12 @@ import type { QuestionnaireResponseItemInput, } from './types.js' import { + cloneJsonValue, deepFreeze, + err, issues, ok, + parseAbsoluteUri, parseCanonical, parseSemVer, type Issue, @@ -75,6 +84,20 @@ const objectPart = (value: unknown, property: string): unknown => Reflect.get(value, property) : undefined +export const isExactQuestionnaireCanonical = (value: unknown): boolean => { + if (typeof value !== 'string' || !parseCanonical(value).ok) return false + const separator = value.indexOf('|') + if (separator <= 0 || separator !== value.lastIndexOf('|')) return false + const canonical = value.slice(0, separator) + const version = value.slice(separator + 1) + return ( + /^https?:\/\//u.test(canonical) && + !canonical.includes('#') && + parseAbsoluteUri(canonical).ok && + parseSemVer(version).ok + ) +} + const normalizeIssue = (entry: z.core.$ZodIssue): Issue => ({ severity: 'error', code: 'schema-invalid', @@ -89,7 +112,10 @@ const normalizeIssue = (entry: z.core.$ZodIssue): Issue => ({ const exactProfileIssues = ( resource: { readonly meta?: - { readonly profile?: readonly string[] | undefined } | undefined + | { + readonly profile?: ReadonlyArray | undefined + } + | undefined }, expected: string, ): readonly Issue[] => @@ -174,6 +200,15 @@ const questionnaireContractIssues = ( ), ...validateElementExtensions(questionnaire), ] + if (!isExactQuestionnaireUrl(questionnaire.url)) { + failures.push( + issue( + 'invalid-uri', + ['url'], + 'Questionnaire.url must be an exact absolute HTTP(S) canonical without a fragment or version delimiter.', + ), + ) + } if (!parseSemVer(questionnaire.version).ok) { failures.push( issue( @@ -221,11 +256,7 @@ const responseContractIssues = ( ), ...validateElementExtensions(response), ] - if ( - !parseCanonical(response.questionnaire).ok || - !/^[^|#]+\|[^|#]+$/u.test(response.questionnaire) || - !parseSemVer(response.questionnaire.split('|')[1]).ok - ) { + if (!isExactQuestionnaireCanonical(response.questionnaire)) { failures.push( issue( 'invalid-uri', @@ -234,6 +265,43 @@ const responseContractIssues = ( ), ) } + const identifierSystem = objectPart(response.identifier, 'system') + const identifierValue = objectPart(response.identifier, 'value') + if ( + !parseAbsoluteUri(identifierSystem).ok || + typeof identifierValue !== 'string' || + identifierValue.trim() === '' + ) { + failures.push( + issue( + 'invalid-identifier', + ['identifier'], + 'QuestionnaireResponse requires one complete business Identifier.', + ), + ) + } + for (const [field, reference, allowedTypes] of [ + ['subject', response.subject, undefined], + ['author', response.author, QUESTIONNAIRE_RESPONSE_AUTHOR_TYPES], + ['source', response.source, QUESTIONNAIRE_RESPONSE_SOURCE_TYPES], + ] as const) { + if ( + reference !== undefined && + !isQuestionnaireResponseReference( + reference, + allowedTypes, + response.contained ?? [], + ) + ) { + failures.push( + issue( + 'invalid-reference', + [field], + `QuestionnaireResponse.${field} requires one typed literal or identifier-only logical Reference to an admitted target type.`, + ), + ) + } + } const completionModes = extensionsFor( { extension: response.extension ?? [] }, COMPLETION_MODE, @@ -269,9 +337,18 @@ const responseContractIssues = ( // The schema decides the parsed type; a caller-chosen T would make the cast below a lie. const parseWith = (schema: z.ZodType, input: unknown): Result => { - const result = schema.safeParse(input) - if (!result.success) return issues(result.error.issues.map(normalizeIssue)) - return ok(deepFreeze(result.data) as T) + const snapshot = cloneJsonValue(input) + if (!snapshot.ok) return snapshot + try { + const result = schema.safeParse(snapshot.value) + if (!result.success) return issues(result.error.issues.map(normalizeIssue)) + return ok(deepFreeze(result.data) as T) + } catch { + return err( + 'schema-invalid', + 'Questionnaire JSON validation could not safely inspect the supplied value.', + ) + } } export const parseQuestionnaire = ( diff --git a/packages/fhir/src/questionnaire/preflight-answer-validation.ts b/packages/fhir/src/questionnaire/preflight-answer-validation.ts new file mode 100644 index 00000000..13e5e3a8 --- /dev/null +++ b/packages/fhir/src/questionnaire/preflight-answer-validation.ts @@ -0,0 +1,680 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + boundIsOrdered, + extensionValue, + expectedQuestionnaireAnswerKeys, + extensionsFor, + firstExtensionValue, + QUESTIONNAIRE_EXTENSIONS, +} from './contract.js' +import { issue, warning } from './preflight-diagnostics.js' +import { compareR4Temporal, type R4TemporalKind } from './temporal.js' +import type { + QuestionnaireItemInput, + QuestionnairePreflightOptions, + QuestionnaireResponseAnswerInput, + QuestionnaireResponseItemInput, +} from './types.js' +import type { Issue } from '../core/index.js' +import type { Extension } from '../r4/index.js' + +const answerKeys = [ + 'valueBoolean', + 'valueDecimal', + 'valueInteger', + 'valueDate', + 'valueDateTime', + 'valueTime', + 'valueString', + 'valueUri', + 'valueAttachment', + 'valueCoding', + 'valueQuantity', +] as const + +type AnswerKey = (typeof answerKeys)[number] + +const populatedAnswerKeys = ( + answer: QuestionnaireResponseAnswerInput, +): readonly AnswerKey[] => + answerKeys.filter((key) => Reflect.get(answer, key) !== undefined) + +export const codingPart = (value: unknown, property: string): unknown => + typeof value === 'object' && value !== null ? + Reflect.get(value, property) + : undefined + +const quantityIdentity = (value: unknown): readonly unknown[] | undefined => { + const numericValue = codingPart(value, 'value') + const system = codingPart(value, 'system') + const code = codingPart(value, 'code') + return typeof numericValue === 'number' ? + [numericValue, system, code] + : undefined +} + +const valuesMatch = ( + key: AnswerKey, + optionValue: unknown, + answerValue: unknown, +): boolean => { + if (key !== 'valueCoding') return Object.is(optionValue, answerValue) + const optionCode = codingPart(optionValue, 'code') + const answerCode = codingPart(answerValue, 'code') + return ( + typeof optionCode === 'string' && + typeof answerCode === 'string' && + codingPart(optionValue, 'system') === codingPart(answerValue, 'system') && + optionCode === answerCode + ) +} + +const temporalKindForAnswer = (key: AnswerKey): R4TemporalKind | undefined => { + switch (key) { + case 'valueDate': + return 'date' + case 'valueDateTime': + return 'dateTime' + case 'valueTime': + return 'time' + default: + return undefined + } +} + +const questionnaireItemTemporalKind = ( + type: QuestionnaireItemInput['type'], +): R4TemporalKind | undefined => + type === 'date' || type === 'dateTime' || type === 'time' ? type : undefined + +const semanticValuesMatch = ( + left: unknown, + right: unknown, + key?: AnswerKey, +): boolean => { + const leftCode = codingPart(left, 'code') + const rightCode = codingPart(right, 'code') + if (leftCode !== undefined || rightCode !== undefined) { + return ( + typeof leftCode === 'string' && + typeof rightCode === 'string' && + codingPart(left, 'system') === codingPart(right, 'system') && + leftCode === rightCode + ) + } + const leftQuantity = quantityIdentity(left) + const rightQuantity = quantityIdentity(right) + if (leftQuantity !== undefined || rightQuantity !== undefined) { + return ( + leftQuantity !== undefined && + rightQuantity !== undefined && + leftQuantity.every((value, index) => + Object.is(value, rightQuantity[index]), + ) + ) + } + const temporalKind = + key === undefined ? undefined : temporalKindForAnswer(key) + if (temporalKind !== undefined) { + return compareR4Temporal(left, right, temporalKind) === 0 + } + return Object.is(left, right) +} + +const enableAnswerKeys = { + answerBoolean: 'valueBoolean', + answerCoding: 'valueCoding', + answerDate: 'valueDate', + answerDateTime: 'valueDateTime', + answerDecimal: 'valueDecimal', + answerInteger: 'valueInteger', + answerQuantity: 'valueQuantity', + answerString: 'valueString', + answerTime: 'valueTime', +} as const + +type EnableAnswerKey = keyof typeof enableAnswerKeys + +const enableExpectedValue = ( + condition: NonNullable[number], +): readonly [AnswerKey, unknown] | undefined => { + const key = (Object.keys(enableAnswerKeys) as EnableAnswerKey[]).find( + (candidate) => Reflect.get(condition, candidate) !== undefined, + ) + return key === undefined ? undefined : ( + [enableAnswerKeys[key], Reflect.get(condition, key)] + ) +} + +export const responseAnswers = ( + items: readonly QuestionnaireResponseItemInput[], + values: Map = new Map(), +): ReadonlyMap => { + for (const item of items) { + const bucket = values.get(item.linkId) ?? [] + values.set(item.linkId, bucket) + for (const answer of item.answer ?? []) { + const key = populatedAnswerKeys(answer)[0] + if (key !== undefined) bucket.push(Reflect.get(answer, key)) + responseAnswers(answer.item ?? [], values) + } + responseAnswers(item.item ?? [], values) + } + return values +} + +const compare = ( + left: unknown, + right: unknown, + operator: '<' | '<=' | '>' | '>=', + key: AnswerKey, +): boolean | undefined => { + const temporalKind = temporalKindForAnswer(key) + if (temporalKind !== undefined) { + const comparison = compareR4Temporal(left, right, temporalKind) + if (comparison === undefined) return undefined + switch (operator) { + case '<': + return comparison < 0 + case '<=': + return comparison <= 0 + case '>': + return comparison > 0 + case '>=': + return comparison >= 0 + } + } + if ( + (typeof left !== 'number' || typeof right !== 'number') && + (typeof left !== 'string' || typeof right !== 'string') + ) { + return undefined + } + switch (operator) { + case '<': + return left < right + case '<=': + return left <= right + case '>': + return left > right + case '>=': + return left >= right + } +} + +const isRelationalOperator = ( + value: string, +): value is '<' | '<=' | '>' | '>=' => + value === '<' || value === '<=' || value === '>' || value === '>=' + +export const evaluateEnableWhen = ( + item: QuestionnaireItemInput, + answers: ReadonlyMap, +): boolean | undefined => { + if ((item.enableWhen?.length ?? 0) === 0) return true + const outcomes: boolean[] = [] + for (const condition of item.enableWhen ?? []) { + const expected = enableExpectedValue(condition) + if (expected === undefined) return undefined + const actualValues = answers.get(condition.question) ?? [] + if (condition.operator === 'exists') { + if (typeof expected[1] !== 'boolean') return undefined + outcomes.push(actualValues.length > 0 === expected[1]) + } else if (condition.operator === '=') { + outcomes.push( + actualValues.some((value) => + semanticValuesMatch(value, expected[1], expected[0]), + ), + ) + } else if (condition.operator === '!=') { + outcomes.push( + actualValues.some( + (value) => !semanticValuesMatch(value, expected[1], expected[0]), + ), + ) + } else { + if (!isRelationalOperator(condition.operator)) return undefined + const operator = condition.operator + const comparisons = actualValues.map((value) => + compare(value, expected[1], operator, expected[0]), + ) + if (comparisons.includes(undefined)) return undefined + outcomes.push(comparisons.includes(true)) + } + } + return item.enableBehavior === 'any' ? + outcomes.includes(true) + : outcomes.every(Boolean) +} + +export type ResolvedValueSet = NonNullable< + QuestionnairePreflightOptions['valueSets'] +>[number] + +export interface PreflightContext { + readonly completed: boolean + readonly answers: ReadonlyMap + readonly valueSets: ReadonlyMap +} + +const targetConstraintKey = (extension: Extension): string => { + const keyPart = extension.extension?.find((part) => part.url === 'key') + const key = extensionValue(keyPart) + return key?.key === 'valueId' && typeof key.value === 'string' ? + key.value + : 'targetConstraint' +} + +const targetConstraintSeverity = (extension: Extension) => { + const severityPart = extension.extension?.find( + (part) => part.url === 'severity', + ) + const severity = extensionValue(severityPart) + return severity?.key === 'valueCode' && severity.value === 'warning' ? + 'warning' + : 'error' +} + +export const validateExpressionEngineRequirements = ( + rootExtensions: readonly Extension[] | undefined, + items: readonly QuestionnaireItemInput[], +): readonly Issue[] => { + const failures: Issue[] = [] + const checkExtensions = ( + extensions: readonly Extension[] | undefined, + path: ReadonlyArray, + linkId?: string, + ) => { + for (const extension of extensions ?? []) { + if (extension.url === QUESTIONNAIRE_EXTENSIONS.targetConstraint) { + const createFinding = + targetConstraintSeverity(extension) === 'warning' ? warning : issue + failures.push( + createFinding( + 'external-validation-required', + path, + `FHIRPath must evaluate ${targetConstraintKey(extension)} before a completed or amended response can be accepted.`, + ), + ) + } + if (extension.url === QUESTIONNAIRE_EXTENSIONS.enableWhenExpression) { + failures.push( + issue( + 'external-validation-required', + path, + `FHIRPath enablement must be evaluated for ${linkId ?? 'this item'}.`, + ), + ) + } + if (extension.url === QUESTIONNAIRE_EXTENSIONS.calculatedExpression) { + failures.push( + issue( + 'external-validation-required', + path, + `calculatedExpression output for ${linkId ?? 'this element'} must be recomputed by a conforming SDC FHIRPath engine.`, + ), + ) + } + } + } + checkExtensions(rootExtensions, ['response']) + const visit = ( + candidates: readonly QuestionnaireItemInput[], + path: ReadonlyArray, + ) => { + for (const [index, item] of candidates.entries()) { + const itemPath = [...path, index] + checkExtensions(item.extension, itemPath, item.linkId) + visit(item.item ?? [], [...itemPath, 'item']) + } + } + visit(items, ['questionnaire', 'item']) + return failures +} + +const valueSetMembership = ( + canonical: string, + coding: unknown, + context: PreflightContext, +): boolean | undefined => { + const valueSet = context.valueSets.get(canonical) + const system = codingPart(coding, 'system') + const code = codingPart(coding, 'code') + if ( + valueSet === undefined || + typeof system !== 'string' || + typeof code !== 'string' + ) { + return undefined + } + return valueSet.concepts.some( + (concept) => concept.system === system && concept.code === code, + ) +} + +const optionMatches = ( + item: QuestionnaireItemInput, + answer: QuestionnaireResponseAnswerInput, +): boolean => { + if (item.answerOption === undefined || item.answerOption.length === 0) { + return true + } + const key = populatedAnswerKeys(answer)[0] + if ( + key === undefined || + (item.type === 'open-choice' && key === 'valueString') + ) { + return true + } + + const answerValue = Reflect.get(answer, key) + return item.answerOption.some((option) => + valuesMatch(key, Reflect.get(option, key), answerValue), + ) +} + +type QuestionnaireAnswerOption = NonNullable< + QuestionnaireItemInput['answerOption'] +>[number] + +export const selectedInlineOption = ( + item: QuestionnaireItemInput, + answer: QuestionnaireResponseAnswerInput, +): QuestionnaireAnswerOption | undefined => { + const key = populatedAnswerKeys(answer)[0] + if (key === undefined) return undefined + const answerValue = Reflect.get(answer, key) + return item.answerOption?.find((option) => + valuesMatch(key, Reflect.get(option, key), answerValue), + ) +} + +export const optionIsExclusive = (option: { + readonly extension?: readonly unknown[] +}): boolean => + (option.extension ?? []).some((candidate) => { + if (typeof candidate !== 'object' || candidate === null) return false + if ( + Reflect.get(candidate, 'url') !== QUESTIONNAIRE_EXTENSIONS.optionExclusive + ) { + return false + } + return Reflect.get(candidate, 'valueBoolean') === true + }) + +const codeAndSystemMatch = (left: unknown, right: unknown): boolean => + typeof codingPart(left, 'code') === 'string' && + codingPart(left, 'system') === codingPart(right, 'system') && + codingPart(left, 'code') === codingPart(right, 'code') + +const decimalPlaces = (value: number): number => { + const representation = value.toString().toLowerCase() + const [coefficient = '', exponentText = '0'] = representation.split('e') + const exponent = Number(exponentText) + const fractionLength = coefficient.split('.')[1]?.length ?? 0 + return Math.max(0, fractionLength - exponent) +} + +const textLength = (value: string): number => Array.from(value).length + +const validateTextLength = ( + item: QuestionnaireItemInput, + value: unknown, + path: ReadonlyArray, +): readonly Issue[] => { + if (typeof value !== 'string') return [] + const minimum = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.minLength, + )?.value + const length = textLength(value) + const failures: Issue[] = [] + if (typeof minimum === 'number' && length < minimum) { + failures.push( + issue('out-of-range', path, 'Answer is shorter than minLength.'), + ) + } + if (item.maxLength !== undefined && length > item.maxLength) { + failures.push(issue('out-of-range', path, 'Answer exceeds maxLength.')) + } + return failures +} + +const validateDecimalPlaces = ( + item: QuestionnaireItemInput, + value: unknown, + path: ReadonlyArray, +): readonly Issue[] => { + const maximum = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.maxDecimalPlaces, + )?.value + return ( + typeof value === 'number' && + typeof maximum === 'number' && + decimalPlaces(value) > maximum + ) ? + [issue('out-of-range', path, 'Answer exceeds maxDecimalPlaces.')] + : [] +} + +const validateBounds = ( + item: QuestionnaireItemInput, + value: unknown, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] + for (const [url, direction, label] of [ + [QUESTIONNAIRE_EXTENSIONS.minValue, 'minimum', 'value'], + [QUESTIONNAIRE_EXTENSIONS.maxValue, 'maximum', 'value'], + [QUESTIONNAIRE_EXTENSIONS.minQuantity, 'minimum', 'quantity'], + [QUESTIONNAIRE_EXTENSIONS.maxQuantity, 'maximum', 'quantity'], + ] as const) { + const bound = firstExtensionValue(item, url)?.value + if (bound === undefined) continue + const valid = + direction === 'minimum' ? + boundIsOrdered(bound, value, questionnaireItemTemporalKind(item.type)) + : boundIsOrdered(value, bound, questionnaireItemTemporalKind(item.type)) + if (valid !== true) { + failures.push( + issue( + 'out-of-range', + path, + `Answer violates the ${direction} ${label} bound or uses an incomparable unit.`, + ), + ) + } + } + return failures +} + +const validateQuantityUnits = ( + item: QuestionnaireItemInput, + value: unknown, + context: PreflightContext, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] + const options = extensionsFor( + item, + QUESTIONNAIRE_EXTENSIONS.unitOption, + ).flatMap((extension) => { + const option = extensionValue(extension) + return option?.key === 'valueCoding' ? [option.value] : [] + }) + if ( + options.length > 0 && + !options.some((option) => codeAndSystemMatch(value, option)) + ) { + failures.push( + issue('value-mismatch', path, 'Quantity unit is not one of unitOption.'), + ) + } + const valueSet = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.unitValueSet, + ) + if ( + valueSet?.key === 'valueCanonical' && + typeof valueSet.value === 'string' + ) { + const membership = valueSetMembership( + valueSet.value, + { + system: codingPart(value, 'system'), + code: codingPart(value, 'code'), + }, + context, + ) + if (membership === undefined) { + failures.push( + issue( + 'external-validation-required', + path, + `Resolved concepts are required for ${valueSet.value}.`, + ), + ) + } else if (!membership) { + failures.push( + issue( + 'value-mismatch', + path, + 'Quantity unit is not certified by unitValueSet.', + ), + ) + } + } + return failures +} + +const validateAttachment = ( + item: QuestionnaireItemInput, + value: unknown, + path: ReadonlyArray, +): readonly Issue[] => { + const failures: Issue[] = [] + const allowedTypes = new Set( + extensionsFor(item, QUESTIONNAIRE_EXTENSIONS.mimeType).flatMap( + (extension) => { + const allowed = extensionValue(extension) + return ( + allowed?.key === 'valueCode' && typeof allowed.value === 'string' + ) ? + [allowed.value] + : [] + }, + ), + ) + const contentType = codingPart(value, 'contentType') + if (allowedTypes.size > 0 && !allowedTypes.has(String(contentType))) { + failures.push( + issue('value-mismatch', path, 'Attachment contentType is not allowed.'), + ) + } + const maximumSize = firstExtensionValue( + item, + QUESTIONNAIRE_EXTENSIONS.maxSize, + )?.value + const size = codingPart(value, 'size') + if ( + typeof maximumSize === 'number' && + (typeof size !== 'number' || !Number.isInteger(size) || size > maximumSize) + ) { + failures.push( + issue( + 'out-of-range', + path, + 'Attachment exceeds maxSize or does not declare size.', + ), + ) + } + return failures +} + +export const validateAnswer = ( + questionnaireItem: QuestionnaireItemInput, + answer: QuestionnaireResponseAnswerInput, + context: PreflightContext, + path: ReadonlyArray, +): readonly Issue[] => { + const populated = populatedAnswerKeys(answer) + const expected = expectedQuestionnaireAnswerKeys(questionnaireItem.type) + const failures: Issue[] = [] + const actual = populated[0] + if ( + populated.length !== 1 || + actual === undefined || + !expected.includes(actual) + ) { + failures.push( + issue( + 'invalid-type', + path, + `Answer for ${questionnaireItem.linkId} does not match item type ${questionnaireItem.type}.`, + ), + ) + return failures + } + + const value = Reflect.get(answer, actual) + if (!optionMatches(questionnaireItem, answer)) { + failures.push( + issue( + 'value-mismatch', + path, + `Answer for ${questionnaireItem.linkId} is not an admitted answerOption.`, + ), + ) + } + if ( + actual === 'valueCoding' && + questionnaireItem.answerValueSet !== undefined + ) { + const membership = valueSetMembership( + questionnaireItem.answerValueSet, + value, + context, + ) + if (membership === undefined) { + failures.push( + issue( + 'external-validation-required', + path, + `Resolved concepts are required for ${questionnaireItem.answerValueSet}.`, + ), + ) + } else if (!membership) { + failures.push( + issue( + 'value-mismatch', + path, + `Answer for ${questionnaireItem.linkId} is not in its resolved ValueSet.`, + ), + ) + } + } + if (actual === 'valueString' || actual === 'valueUri') { + failures.push(...validateTextLength(questionnaireItem, value, path)) + } + if (actual === 'valueDecimal') { + failures.push(...validateDecimalPlaces(questionnaireItem, value, path)) + } + failures.push(...validateBounds(questionnaireItem, value, path)) + if (actual === 'valueQuantity') { + failures.push( + ...validateQuantityUnits(questionnaireItem, value, context, path), + ) + } + if (actual === 'valueAttachment') { + failures.push(...validateAttachment(questionnaireItem, value, path)) + } + return failures +} diff --git a/packages/fhir/src/questionnaire/preflight-diagnostics.ts b/packages/fhir/src/questionnaire/preflight-diagnostics.ts new file mode 100644 index 00000000..10defa8a --- /dev/null +++ b/packages/fhir/src/questionnaire/preflight-diagnostics.ts @@ -0,0 +1,27 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import type { Issue } from '../core/index.js' + +export const issue = ( + code: Issue['code'], + path: Issue['path'], + message: string, +): Issue => ({ severity: 'error', code, path, message }) + +export const warning = ( + code: Issue['code'], + path: Issue['path'], + message: string, +): Issue => ({ severity: 'warning', code, path, message }) + +export const prefixed = ( + entries: readonly Issue[], + prefix: string, +): readonly Issue[] => + entries.map((entry) => ({ ...entry, path: [prefix, ...entry.path] })) diff --git a/packages/fhir/src/questionnaire/preflight.ts b/packages/fhir/src/questionnaire/preflight.ts index 0e03d837..5f32d6a5 100644 --- a/packages/fhir/src/questionnaire/preflight.ts +++ b/packages/fhir/src/questionnaire/preflight.ts @@ -7,33 +7,44 @@ // import { - boundIsOrdered, extensionValue, - expectedQuestionnaireAnswerKeys, extensionsFor, firstExtensionValue, QUESTIONNAIRE_EXTENSIONS, validateQuestionnaireContract, } from './contract.js' -import { parseQuestionnaire, parseQuestionnaireResponse } from './parse.js' -import { compareR4Temporal, type R4TemporalKind } from './temporal.js' +import { + isExactQuestionnaireCanonical, + parseQuestionnaire, + parseQuestionnaireResponse, +} from './parse.js' +import { + codingPart, + evaluateEnableWhen, + optionIsExclusive, + responseAnswers, + selectedInlineOption, + validateAnswer, + validateExpressionEngineRequirements, + type PreflightContext, + type ResolvedValueSet, +} from './preflight-answer-validation.js' +import { issue, prefixed } from './preflight-diagnostics.js' +import { questionnaireResponseReferenceType } from './references.js' import type { QuestionnaireItemInput, QuestionnairePair, QuestionnairePreflightOptions, - QuestionnaireResponseAnswerInput, QuestionnaireResponseItemInput, } from './types.js' import { issues, ok, parseAbsoluteUri, - parseCanonical, parseSemVer, type Issue, type Result, } from '../core/index.js' -import type { Extension } from '../r4/index.js' /* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 canonicals are normative HTTP URIs. */ @@ -46,40 +57,6 @@ const COMPLETION_MODE = const PARTICIPATION_MODE = 'http://terminology.hl7.org/CodeSystem/v3-ParticipationMode' -const answerKeys = [ - 'valueBoolean', - 'valueDecimal', - 'valueInteger', - 'valueDate', - 'valueDateTime', - 'valueTime', - 'valueString', - 'valueUri', - 'valueAttachment', - 'valueCoding', - 'valueQuantity', -] as const - -type AnswerKey = (typeof answerKeys)[number] - -const issue = ( - code: Issue['code'], - path: Issue['path'], - message: string, -): Issue => ({ severity: 'error', code, path, message }) - -const warning = ( - code: Issue['code'], - path: Issue['path'], - message: string, -): Issue => ({ severity: 'warning', code, path, message }) - -const prefixed = ( - entries: readonly Issue[], - prefix: string, -): readonly Issue[] => - entries.map((entry) => ({ ...entry, path: [prefix, ...entry.path] })) - const validateQuestionnaireEnvelope = ( questionnaire: QuestionnairePair['questionnaire'], ): readonly Issue[] => { @@ -121,11 +98,7 @@ const validateQuestionnaireResponseEnvelope = ( response: QuestionnairePair['response'], ): readonly Issue[] => { const failures: Issue[] = [] - if ( - !parseCanonical(response.questionnaire).ok || - !/^[^|#]+\|[^|#]+$/u.test(response.questionnaire) || - !parseSemVer(response.questionnaire.split('|')[1]).ok - ) { + if (!isExactQuestionnaireCanonical(response.questionnaire)) { failures.push( issue( 'invalid-uri', @@ -180,640 +153,6 @@ const validateQuestionnaireResponseEnvelope = ( return failures } -const populatedAnswerKeys = ( - answer: QuestionnaireResponseAnswerInput, -): readonly AnswerKey[] => - answerKeys.filter((key) => Reflect.get(answer, key) !== undefined) - -const codingPart = (value: unknown, property: string): unknown => - typeof value === 'object' && value !== null ? - Reflect.get(value, property) - : undefined - -const quantityIdentity = (value: unknown): readonly unknown[] | undefined => { - const numericValue = codingPart(value, 'value') - const system = codingPart(value, 'system') - const code = codingPart(value, 'code') - return typeof numericValue === 'number' ? - [numericValue, system, code] - : undefined -} - -const valuesMatch = ( - key: AnswerKey, - optionValue: unknown, - answerValue: unknown, -): boolean => { - if (key !== 'valueCoding') return Object.is(optionValue, answerValue) - const optionCode = codingPart(optionValue, 'code') - const answerCode = codingPart(answerValue, 'code') - return ( - typeof optionCode === 'string' && - typeof answerCode === 'string' && - codingPart(optionValue, 'system') === codingPart(answerValue, 'system') && - optionCode === answerCode - ) -} - -const temporalKindForAnswer = (key: AnswerKey): R4TemporalKind | undefined => { - switch (key) { - case 'valueDate': - return 'date' - case 'valueDateTime': - return 'dateTime' - case 'valueTime': - return 'time' - default: - return undefined - } -} - -const questionnaireItemTemporalKind = ( - type: QuestionnaireItemInput['type'], -): R4TemporalKind | undefined => - type === 'date' || type === 'dateTime' || type === 'time' ? type : undefined - -const semanticValuesMatch = ( - left: unknown, - right: unknown, - key?: AnswerKey, -): boolean => { - const leftCode = codingPart(left, 'code') - const rightCode = codingPart(right, 'code') - if (leftCode !== undefined || rightCode !== undefined) { - return ( - typeof leftCode === 'string' && - typeof rightCode === 'string' && - codingPart(left, 'system') === codingPart(right, 'system') && - leftCode === rightCode - ) - } - const leftQuantity = quantityIdentity(left) - const rightQuantity = quantityIdentity(right) - if (leftQuantity !== undefined || rightQuantity !== undefined) { - return ( - leftQuantity !== undefined && - rightQuantity !== undefined && - leftQuantity.every((value, index) => - Object.is(value, rightQuantity[index]), - ) - ) - } - const temporalKind = - key === undefined ? undefined : temporalKindForAnswer(key) - if (temporalKind !== undefined) { - return compareR4Temporal(left, right, temporalKind) === 0 - } - return Object.is(left, right) -} - -const enableAnswerKeys = { - answerBoolean: 'valueBoolean', - answerCoding: 'valueCoding', - answerDate: 'valueDate', - answerDateTime: 'valueDateTime', - answerDecimal: 'valueDecimal', - answerInteger: 'valueInteger', - answerQuantity: 'valueQuantity', - answerString: 'valueString', - answerTime: 'valueTime', -} as const - -type EnableAnswerKey = keyof typeof enableAnswerKeys - -const enableExpectedValue = ( - condition: NonNullable[number], -): readonly [AnswerKey, unknown] | undefined => { - const key = (Object.keys(enableAnswerKeys) as EnableAnswerKey[]).find( - (candidate) => Reflect.get(condition, candidate) !== undefined, - ) - return key === undefined ? undefined : ( - [enableAnswerKeys[key], Reflect.get(condition, key)] - ) -} - -const responseAnswers = ( - items: readonly QuestionnaireResponseItemInput[], - values: Map = new Map(), -): ReadonlyMap => { - for (const item of items) { - const bucket = values.get(item.linkId) ?? [] - values.set(item.linkId, bucket) - for (const answer of item.answer ?? []) { - const key = populatedAnswerKeys(answer)[0] - if (key !== undefined) bucket.push(Reflect.get(answer, key)) - responseAnswers(answer.item ?? [], values) - } - responseAnswers(item.item ?? [], values) - } - return values -} - -const compare = ( - left: unknown, - right: unknown, - operator: '<' | '<=' | '>' | '>=', - key: AnswerKey, -): boolean | undefined => { - const temporalKind = temporalKindForAnswer(key) - if (temporalKind !== undefined) { - const comparison = compareR4Temporal(left, right, temporalKind) - if (comparison === undefined) return undefined - switch (operator) { - case '<': - return comparison < 0 - case '<=': - return comparison <= 0 - case '>': - return comparison > 0 - case '>=': - return comparison >= 0 - } - } - if ( - (typeof left !== 'number' || typeof right !== 'number') && - (typeof left !== 'string' || typeof right !== 'string') - ) { - return undefined - } - switch (operator) { - case '<': - return left < right - case '<=': - return left <= right - case '>': - return left > right - case '>=': - return left >= right - } -} - -const isRelationalOperator = ( - value: string, -): value is '<' | '<=' | '>' | '>=' => - value === '<' || value === '<=' || value === '>' || value === '>=' - -const evaluateEnableWhen = ( - item: QuestionnaireItemInput, - answers: ReadonlyMap, -): boolean | undefined => { - if ((item.enableWhen?.length ?? 0) === 0) return true - const outcomes: boolean[] = [] - for (const condition of item.enableWhen ?? []) { - const expected = enableExpectedValue(condition) - if (expected === undefined) return undefined - const actualValues = answers.get(condition.question) ?? [] - if (condition.operator === 'exists') { - if (typeof expected[1] !== 'boolean') return undefined - outcomes.push(actualValues.length > 0 === expected[1]) - } else if (condition.operator === '=') { - outcomes.push( - actualValues.some((value) => - semanticValuesMatch(value, expected[1], expected[0]), - ), - ) - } else if (condition.operator === '!=') { - outcomes.push( - actualValues.some( - (value) => !semanticValuesMatch(value, expected[1], expected[0]), - ), - ) - } else { - if (!isRelationalOperator(condition.operator)) return undefined - const operator = condition.operator - const comparisons = actualValues.map((value) => - compare(value, expected[1], operator, expected[0]), - ) - if (comparisons.includes(undefined)) return undefined - outcomes.push(comparisons.includes(true)) - } - } - return item.enableBehavior === 'any' ? - outcomes.includes(true) - : outcomes.every(Boolean) -} - -type ResolvedValueSet = NonNullable< - QuestionnairePreflightOptions['valueSets'] ->[number] - -interface PreflightContext { - readonly completed: boolean - readonly answers: ReadonlyMap - readonly valueSets: ReadonlyMap -} - -const targetConstraintKey = (extension: Extension): string => { - const keyPart = extension.extension?.find((part) => part.url === 'key') - const key = extensionValue(keyPart) - return key?.key === 'valueId' && typeof key.value === 'string' ? - key.value - : 'targetConstraint' -} - -const targetConstraintSeverity = (extension: Extension) => { - const severityPart = extension.extension?.find( - (part) => part.url === 'severity', - ) - const severity = extensionValue(severityPart) - return severity?.key === 'valueCode' && severity.value === 'warning' ? - 'warning' - : 'error' -} - -const validateExpressionEngineRequirements = ( - rootExtensions: readonly Extension[] | undefined, - items: readonly QuestionnaireItemInput[], -): readonly Issue[] => { - const failures: Issue[] = [] - const checkExtensions = ( - extensions: readonly Extension[] | undefined, - path: ReadonlyArray, - linkId?: string, - ) => { - for (const extension of extensions ?? []) { - if (extension.url === QUESTIONNAIRE_EXTENSIONS.targetConstraint) { - const createFinding = - targetConstraintSeverity(extension) === 'warning' ? warning : issue - failures.push( - createFinding( - 'external-validation-required', - path, - `FHIRPath must evaluate ${targetConstraintKey(extension)} before a completed or amended response can be accepted.`, - ), - ) - } - if (extension.url === QUESTIONNAIRE_EXTENSIONS.enableWhenExpression) { - failures.push( - issue( - 'external-validation-required', - path, - `FHIRPath enablement must be evaluated for ${linkId ?? 'this item'}.`, - ), - ) - } - if (extension.url === QUESTIONNAIRE_EXTENSIONS.calculatedExpression) { - failures.push( - issue( - 'external-validation-required', - path, - `calculatedExpression output for ${linkId ?? 'this element'} must be recomputed by a conforming SDC FHIRPath engine.`, - ), - ) - } - } - } - checkExtensions(rootExtensions, ['response']) - const visit = ( - candidates: readonly QuestionnaireItemInput[], - path: ReadonlyArray, - ) => { - for (const [index, item] of candidates.entries()) { - const itemPath = [...path, index] - checkExtensions(item.extension, itemPath, item.linkId) - visit(item.item ?? [], [...itemPath, 'item']) - } - } - visit(items, ['questionnaire', 'item']) - return failures -} - -const valueSetMembership = ( - canonical: string, - coding: unknown, - context: PreflightContext, -): boolean | undefined => { - const valueSet = context.valueSets.get(canonical) - const system = codingPart(coding, 'system') - const code = codingPart(coding, 'code') - if ( - valueSet === undefined || - typeof system !== 'string' || - typeof code !== 'string' - ) { - return undefined - } - return valueSet.concepts.some( - (concept) => concept.system === system && concept.code === code, - ) -} - -const optionMatches = ( - item: QuestionnaireItemInput, - answer: QuestionnaireResponseAnswerInput, -): boolean => { - if (item.answerOption === undefined || item.answerOption.length === 0) { - return true - } - const key = populatedAnswerKeys(answer)[0] - if ( - key === undefined || - (item.type === 'open-choice' && key === 'valueString') - ) { - return true - } - - const answerValue = Reflect.get(answer, key) - return item.answerOption.some((option) => - valuesMatch(key, Reflect.get(option, key), answerValue), - ) -} - -const selectedInlineOption = ( - item: QuestionnaireItemInput, - answer: QuestionnaireResponseAnswerInput, -) => { - const key = populatedAnswerKeys(answer)[0] - if (key === undefined) return undefined - const answerValue = Reflect.get(answer, key) - return item.answerOption?.find((option) => - valuesMatch(key, Reflect.get(option, key), answerValue), - ) -} - -const optionIsExclusive = (option: { - readonly extension?: readonly unknown[] -}) => - (option.extension ?? []).some((candidate) => { - if (typeof candidate !== 'object' || candidate === null) return false - if ( - Reflect.get(candidate, 'url') !== QUESTIONNAIRE_EXTENSIONS.optionExclusive - ) { - return false - } - return Reflect.get(candidate, 'valueBoolean') === true - }) - -const codeAndSystemMatch = (left: unknown, right: unknown): boolean => - typeof codingPart(left, 'code') === 'string' && - codingPart(left, 'system') === codingPart(right, 'system') && - codingPart(left, 'code') === codingPart(right, 'code') - -const decimalPlaces = (value: number): number => { - const representation = value.toString().toLowerCase() - const [coefficient = '', exponentText = '0'] = representation.split('e') - const exponent = Number(exponentText) - const fractionLength = coefficient.split('.')[1]?.length ?? 0 - return Math.max(0, fractionLength - exponent) -} - -const textLength = (value: string): number => Array.from(value).length - -const validateTextLength = ( - item: QuestionnaireItemInput, - value: unknown, - path: ReadonlyArray, -): readonly Issue[] => { - if (typeof value !== 'string') return [] - const minimum = firstExtensionValue( - item, - QUESTIONNAIRE_EXTENSIONS.minLength, - )?.value - const length = textLength(value) - const failures: Issue[] = [] - if (typeof minimum === 'number' && length < minimum) { - failures.push( - issue('out-of-range', path, 'Answer is shorter than minLength.'), - ) - } - if (item.maxLength !== undefined && length > item.maxLength) { - failures.push(issue('out-of-range', path, 'Answer exceeds maxLength.')) - } - return failures -} - -const validateDecimalPlaces = ( - item: QuestionnaireItemInput, - value: unknown, - path: ReadonlyArray, -): readonly Issue[] => { - const maximum = firstExtensionValue( - item, - QUESTIONNAIRE_EXTENSIONS.maxDecimalPlaces, - )?.value - return ( - typeof value === 'number' && - typeof maximum === 'number' && - decimalPlaces(value) > maximum - ) ? - [issue('out-of-range', path, 'Answer exceeds maxDecimalPlaces.')] - : [] -} - -const validateBounds = ( - item: QuestionnaireItemInput, - value: unknown, - path: ReadonlyArray, -): readonly Issue[] => { - const failures: Issue[] = [] - for (const [url, direction, label] of [ - [QUESTIONNAIRE_EXTENSIONS.minValue, 'minimum', 'value'], - [QUESTIONNAIRE_EXTENSIONS.maxValue, 'maximum', 'value'], - [QUESTIONNAIRE_EXTENSIONS.minQuantity, 'minimum', 'quantity'], - [QUESTIONNAIRE_EXTENSIONS.maxQuantity, 'maximum', 'quantity'], - ] as const) { - const bound = firstExtensionValue(item, url)?.value - if (bound === undefined) continue - const valid = - direction === 'minimum' ? - boundIsOrdered(bound, value, questionnaireItemTemporalKind(item.type)) - : boundIsOrdered(value, bound, questionnaireItemTemporalKind(item.type)) - if (valid !== true) { - failures.push( - issue( - 'out-of-range', - path, - `Answer violates the ${direction} ${label} bound or uses an incomparable unit.`, - ), - ) - } - } - return failures -} - -const validateQuantityUnits = ( - item: QuestionnaireItemInput, - value: unknown, - context: PreflightContext, - path: ReadonlyArray, -): readonly Issue[] => { - const failures: Issue[] = [] - const options = extensionsFor( - item, - QUESTIONNAIRE_EXTENSIONS.unitOption, - ).flatMap((extension) => { - const option = extensionValue(extension) - return option?.key === 'valueCoding' ? [option.value] : [] - }) - if ( - options.length > 0 && - !options.some((option) => codeAndSystemMatch(value, option)) - ) { - failures.push( - issue('value-mismatch', path, 'Quantity unit is not one of unitOption.'), - ) - } - const valueSet = firstExtensionValue( - item, - QUESTIONNAIRE_EXTENSIONS.unitValueSet, - ) - if ( - valueSet?.key === 'valueCanonical' && - typeof valueSet.value === 'string' - ) { - const membership = valueSetMembership( - valueSet.value, - { - system: codingPart(value, 'system'), - code: codingPart(value, 'code'), - }, - context, - ) - if (membership === undefined) { - failures.push( - issue( - 'external-validation-required', - path, - `Resolved concepts are required for ${valueSet.value}.`, - ), - ) - } else if (!membership) { - failures.push( - issue( - 'value-mismatch', - path, - 'Quantity unit is not certified by unitValueSet.', - ), - ) - } - } - return failures -} - -const validateAttachment = ( - item: QuestionnaireItemInput, - value: unknown, - path: ReadonlyArray, -): readonly Issue[] => { - const failures: Issue[] = [] - const allowedTypes = new Set( - extensionsFor(item, QUESTIONNAIRE_EXTENSIONS.mimeType).flatMap( - (extension) => { - const allowed = extensionValue(extension) - return ( - allowed?.key === 'valueCode' && typeof allowed.value === 'string' - ) ? - [allowed.value] - : [] - }, - ), - ) - const contentType = codingPart(value, 'contentType') - if (allowedTypes.size > 0 && !allowedTypes.has(String(contentType))) { - failures.push( - issue('value-mismatch', path, 'Attachment contentType is not allowed.'), - ) - } - const maximumSize = firstExtensionValue( - item, - QUESTIONNAIRE_EXTENSIONS.maxSize, - )?.value - const size = codingPart(value, 'size') - if ( - typeof maximumSize === 'number' && - (typeof size !== 'number' || !Number.isInteger(size) || size > maximumSize) - ) { - failures.push( - issue( - 'out-of-range', - path, - 'Attachment exceeds maxSize or does not declare size.', - ), - ) - } - return failures -} - -const validateAnswer = ( - questionnaireItem: QuestionnaireItemInput, - answer: QuestionnaireResponseAnswerInput, - context: PreflightContext, - path: ReadonlyArray, -): readonly Issue[] => { - const populated = populatedAnswerKeys(answer) - const expected = expectedQuestionnaireAnswerKeys(questionnaireItem.type) - const failures: Issue[] = [] - const actual = populated[0] - if ( - populated.length !== 1 || - actual === undefined || - !expected.includes(actual) - ) { - failures.push( - issue( - 'invalid-type', - path, - `Answer for ${questionnaireItem.linkId} does not match item type ${questionnaireItem.type}.`, - ), - ) - return failures - } - - const value = Reflect.get(answer, actual) - if (!optionMatches(questionnaireItem, answer)) { - failures.push( - issue( - 'value-mismatch', - path, - `Answer for ${questionnaireItem.linkId} is not an admitted answerOption.`, - ), - ) - } - if ( - actual === 'valueCoding' && - questionnaireItem.answerValueSet !== undefined - ) { - const membership = valueSetMembership( - questionnaireItem.answerValueSet, - value, - context, - ) - if (membership === undefined) { - failures.push( - issue( - 'external-validation-required', - path, - `Resolved concepts are required for ${questionnaireItem.answerValueSet}.`, - ), - ) - } else if (!membership) { - failures.push( - issue( - 'value-mismatch', - path, - `Answer for ${questionnaireItem.linkId} is not in its resolved ValueSet.`, - ), - ) - } - } - if (actual === 'valueString' || actual === 'valueUri') { - failures.push(...validateTextLength(questionnaireItem, value, path)) - } - if (actual === 'valueDecimal') { - failures.push(...validateDecimalPlaces(questionnaireItem, value, path)) - } - failures.push(...validateBounds(questionnaireItem, value, path)) - if (actual === 'valueQuantity') { - failures.push( - ...validateQuantityUnits(questionnaireItem, value, context, path), - ) - } - if (actual === 'valueAttachment') { - failures.push(...validateAttachment(questionnaireItem, value, path)) - } - return failures -} - const validateOmittedGroupChildren = ( item: QuestionnaireItemInput, responseItem: QuestionnaireResponseItemInput | undefined, @@ -1107,6 +446,27 @@ export const preflightQuestionnairePair = ( ), ) } + const admittedSubjectTypes = questionnaire.value.subjectType ?? [] + const responseSubjectType = questionnaireResponseReferenceType( + response.value.subject, + response.value.contained ?? [], + ) + if ( + response.value.subject !== undefined && + (responseSubjectType === undefined || + (admittedSubjectTypes.length > 0 && + !admittedSubjectTypes.some( + (admitted) => admitted === responseSubjectType, + ))) + ) { + failures.push( + issue( + 'value-mismatch', + ['response', 'subject', 'type'], + 'QuestionnaireResponse.subject.type must be admitted by Questionnaire.subjectType.', + ), + ) + } if (response.value.status === 'entered-in-error') { failures.push( issue( @@ -1132,7 +492,9 @@ export const preflightQuestionnairePair = ( } } const context: PreflightContext = { - completed: completedStatuses.has(response.value.status), + completed: + response.value.status !== undefined && + completedStatuses.has(response.value.status), answers: responseAnswers( (response.value.item ?? []) as unknown as readonly QuestionnaireResponseItemInput[], diff --git a/packages/fhir/src/questionnaire/questionnaire-extensions.ts b/packages/fhir/src/questionnaire/questionnaire-extensions.ts new file mode 100644 index 00000000..35aff805 --- /dev/null +++ b/packages/fhir/src/questionnaire/questionnaire-extensions.ts @@ -0,0 +1,497 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + compareR4Temporal, + isR4Date, + isR4DateTime, + isR4Time, + type R4TemporalKind, +} from './temporal.js' +import type { QuestionnaireItemInput } from './types.js' +import type { Issue } from '../core/index.js' +import type { Extension } from '../r4/index.js' + +export const QUESTIONNAIRE_EXTENSIONS = { + calculatedExpression: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression', + enableWhenExpression: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression', + initialExpression: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', + hidden: 'http://hl7.org/fhir/StructureDefinition/questionnaire-hidden', + itemWeight: 'http://hl7.org/fhir/StructureDefinition/itemWeight', + maxDecimalPlaces: 'http://hl7.org/fhir/StructureDefinition/maxDecimalPlaces', + maxOccurs: 'http://hl7.org/fhir/StructureDefinition/questionnaire-maxOccurs', + maxQuantity: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-maxQuantity', + maxSize: 'http://hl7.org/fhir/StructureDefinition/maxSize', + maxValue: 'http://hl7.org/fhir/StructureDefinition/maxValue', + mimeType: 'http://hl7.org/fhir/StructureDefinition/mimeType', + minLength: 'http://hl7.org/fhir/StructureDefinition/minLength', + minOccurs: 'http://hl7.org/fhir/StructureDefinition/questionnaire-minOccurs', + minQuantity: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-minQuantity', + minValue: 'http://hl7.org/fhir/StructureDefinition/minValue', + optionExclusive: + 'http://hl7.org/fhir/StructureDefinition/questionnaire-optionExclusive', + questionnaireUnit: + 'http://hl7.org/fhir/StructureDefinition/questionnaire-unit', + styleSensitive: + 'http://hl7.org/fhir/StructureDefinition/rendering-styleSensitive', + targetConstraint: 'http://hl7.org/fhir/StructureDefinition/targetConstraint', + unitOption: + 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption', + unitValueSet: + 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitValueSet', + variable: 'http://hl7.org/fhir/StructureDefinition/variable', +} as const + +export interface ExtensionElement { + readonly extension?: readonly Extension[] +} + +export interface ExtensionValue { + readonly key: string + readonly value: unknown +} + +export type QuestionnaireAnswerValueKey = + | 'valueAttachment' + | 'valueBoolean' + | 'valueCoding' + | 'valueDate' + | 'valueDateTime' + | 'valueDecimal' + | 'valueInteger' + | 'valueQuantity' + | 'valueString' + | 'valueTime' + | 'valueUri' + +/** Expected Questionnaire/Response value[x] discriminators for an item type. */ +export const expectedQuestionnaireAnswerKeys = ( + type: QuestionnaireItemInput['type'], +): readonly QuestionnaireAnswerValueKey[] => { + switch (type) { + case 'boolean': + return ['valueBoolean'] + case 'decimal': + return ['valueDecimal'] + case 'integer': + return ['valueInteger'] + case 'date': + return ['valueDate'] + case 'dateTime': + return ['valueDateTime'] + case 'time': + return ['valueTime'] + case 'string': + case 'text': + return ['valueString'] + case 'url': + return ['valueUri'] + case 'choice': + return ['valueCoding'] + case 'open-choice': + return ['valueCoding', 'valueString'] + case 'attachment': + return ['valueAttachment'] + case 'quantity': + return ['valueQuantity'] + case 'display': + case 'group': + return [] + } +} + +export const expectedAnswerOptionKeys = ( + type: QuestionnaireItemInput['type'], +): readonly QuestionnaireAnswerValueKey[] => { + switch (type) { + case 'integer': + return ['valueInteger'] + case 'date': + return ['valueDate'] + case 'time': + return ['valueTime'] + case 'string': + return ['valueString'] + case 'choice': + return ['valueCoding'] + case 'open-choice': + return ['valueCoding', 'valueString'] + default: + return [] + } +} + +const questionnaireAnswerValueKeys: readonly QuestionnaireAnswerValueKey[] = [ + 'valueBoolean', + 'valueDecimal', + 'valueInteger', + 'valueDate', + 'valueDateTime', + 'valueTime', + 'valueString', + 'valueUri', + 'valueAttachment', + 'valueCoding', + 'valueQuantity', +] + +export const populatedQuestionnaireAnswerKey = ( + value: object, +): QuestionnaireAnswerValueKey | undefined => + questionnaireAnswerValueKeys.find( + (key) => Reflect.get(value, key) !== undefined, + ) + +export const issue = ( + code: Issue['code'], + path: Issue['path'], + message: string, +): Issue => ({ severity: 'error', code, path, message }) + +export const extensionsFor = ( + element: ExtensionElement, + url: string, +): readonly Extension[] => + (element.extension ?? []).filter((extension) => extension.url === url) + +export const extensionValue = ( + extension: Extension | undefined, +): ExtensionValue | undefined => { + if (extension === undefined) return undefined + const values = Object.entries(extension).filter( + ([key]) => key.startsWith('value') && key !== 'valueSet', + ) + return values.length === 1 ? + { key: values[0]?.[0] ?? '', value: values[0]?.[1] } + : undefined +} + +export const firstExtensionValue = ( + element: ExtensionElement, + url: string, +): ExtensionValue | undefined => { + const first = extensionsFor(element, url)[0] + return first === undefined ? undefined : extensionValue(first) +} + +const expressionPart = (value: unknown, key: string): unknown => + typeof value === 'object' && value !== null ? + Reflect.get(value, key) + : undefined + +const isValidFhirPathExpression = ( + value: unknown, + requireName = false, +): boolean => + expressionPart(value, 'language') === 'text/fhirpath' && + typeof expressionPart(value, 'expression') === 'string' && + (expressionPart(value, 'expression') as string).trim() !== '' && + (!requireName || + (typeof expressionPart(value, 'name') === 'string' && + (expressionPart(value, 'name') as string).trim() !== '')) + +const expressionUrls = new Set([ + QUESTIONNAIRE_EXTENSIONS.variable, + QUESTIONNAIRE_EXTENSIONS.enableWhenExpression, + QUESTIONNAIRE_EXTENSIONS.initialExpression, + QUESTIONNAIRE_EXTENSIONS.calculatedExpression, +]) + +const variableName = /^[A-Za-z]\w*$/u +const reservedVariableNames = new Set([ + 'context', + 'definition', + 'loinc', + 'qitem', + 'questionnaire', + 'resource', + 'rootResource', + 'sct', + 'target', + 'ucum', +]) + +const targetParts = ( + extension: Extension, + name: string, +): readonly Extension[] => + extension.extension?.filter((part) => part.url === name) ?? [] + +const validateVariableName = ( + value: unknown, + names: Set, + path: ReadonlyArray, +): readonly Issue[] => { + const name = expressionPart(value, 'name') + if (typeof name !== 'string' || name === '') return [] + const failures: Issue[] = [] + if (!variableName.test(name) || reservedVariableNames.has(name)) { + failures.push( + issue( + 'invalid-code', + [...path, 'valueExpression', 'name'], + `FHIRPath variable name ${name} is invalid or reserved.`, + ), + ) + } + if (names.has(name)) { + failures.push( + issue( + 'duplicate-identifier', + [...path, 'valueExpression', 'name'], + `FHIRPath variable name ${name} is duplicated in this scope.`, + ), + ) + } + names.add(name) + return failures +} + +const validateExpressionExtension = ( + extension: Extension, + names: Set, + path: ReadonlyArray, +): readonly Issue[] => { + if (extension.url === undefined || !expressionUrls.has(extension.url)) { + return [] + } + const value = extensionValue(extension)?.value + const isVariable = extension.url === QUESTIONNAIRE_EXTENSIONS.variable + const failures: Issue[] = [] + if (!isValidFhirPathExpression(value, isVariable)) { + failures.push( + issue( + 'invalid-type', + path, + 'Expression extensions require a non-empty text/fhirpath expression and variables also require a name.', + ), + ) + } + if (isVariable) failures.push(...validateVariableName(value, names, path)) + return failures +} + +const validateTargetConstraint = ( + extension: Extension, + targetKeys: Set, + path: ReadonlyArray, +): readonly Issue[] => { + if (extension.url !== QUESTIONNAIRE_EXTENSIONS.targetConstraint) return [] + const failures: Issue[] = [] + const values = new Map() + for (const name of ['key', 'severity', 'human', 'expression']) { + const matching = targetParts(extension, name) + if (matching.length !== 1) { + failures.push( + issue( + matching.length === 0 ? 'missing-required' : 'duplicate-identifier', + path, + `targetConstraint requires exactly one ${name} part.`, + ), + ) + } + values.set(name, matching.length === 1 ? matching[0] : undefined) + } + if ( + (extension.extension ?? []).some( + (part) => part.url === undefined || !values.has(part.url), + ) + ) { + failures.push( + issue( + 'invalid-choice', + path, + 'targetConstraint contains an unsupported nested part.', + ), + ) + } + const key = extensionValue(values.get('key')) + const severity = extensionValue(values.get('severity')) + const human = extensionValue(values.get('human')) + const expression = extensionValue(values.get('expression')) + if ( + key?.key !== 'valueId' || + typeof key.value !== 'string' || + key.value.trim() === '' + ) { + failures.push( + issue( + 'missing-required', + path, + 'targetConstraint requires a non-empty valueId key.', + ), + ) + } else if (targetKeys.has(key.value)) { + failures.push( + issue( + 'duplicate-identifier', + path, + `targetConstraint key ${key.value} is not unique.`, + ), + ) + } else { + targetKeys.add(key.value) + } + if ( + severity?.key !== 'valueCode' || + !['error', 'warning'].includes(String(severity.value)) + ) { + failures.push( + issue( + 'invalid-code', + path, + 'targetConstraint severity must be error or warning.', + ), + ) + } + if ( + human?.key !== 'valueString' || + typeof human.value !== 'string' || + human.value.trim() === '' + ) { + failures.push( + issue( + 'missing-required', + path, + 'targetConstraint requires non-empty human guidance.', + ), + ) + } + if ( + expression?.key !== 'valueExpression' || + !isValidFhirPathExpression(expression.value) + ) { + failures.push( + issue( + 'invalid-type', + path, + 'targetConstraint requires a non-empty text/fhirpath expression.', + ), + ) + } + return failures +} + +export const validateExpressionScope = ( + element: ExtensionElement, + path: ReadonlyArray, + targetKeys: Set, +): readonly Issue[] => { + const failures: Issue[] = [] + const names = new Set() + for (const [index, extension] of (element.extension ?? []).entries()) { + const extensionPath = [...path, 'extension', index] + failures.push( + ...validateExpressionExtension(extension, names, extensionPath), + ) + failures.push( + ...validateTargetConstraint(extension, targetKeys, extensionPath), + ) + } + return failures +} + +const comparableQuantity = ( + value: unknown, +): readonly [number, string, string] | undefined => { + if (typeof value !== 'object' || value === null) return undefined + const quantity = value as Readonly> + const quantityValue = quantity.value + const system = quantity.system + const code = quantity.code + return ( + typeof quantityValue === 'number' && + Number.isFinite(quantityValue) && + typeof system === 'string' && + typeof code === 'string' + ) ? + [quantityValue, system, code] + : undefined +} + +/** Returns whether left is less than or equal to a comparable right value. */ +export const boundIsOrdered = ( + left: unknown, + right: unknown, + temporalKind?: R4TemporalKind, +): boolean | undefined => { + if (typeof left === 'number' && typeof right === 'number') { + return Number.isFinite(left) && Number.isFinite(right) ? + left <= right + : undefined + } + if (typeof left === 'string' && typeof right === 'string') { + if (temporalKind === undefined) return left <= right + const comparison = compareR4Temporal(left, right, temporalKind) + return comparison === undefined ? undefined : comparison <= 0 + } + const leftQuantity = comparableQuantity(left) + const rightQuantity = comparableQuantity(right) + if (leftQuantity === undefined || rightQuantity === undefined) + return undefined + return ( + leftQuantity[1] === rightQuantity[1] && + leftQuantity[2] === rightQuantity[2] + ) ? + leftQuantity[0] <= rightQuantity[0] + : undefined +} + +const temporalKindForItem = ( + type: QuestionnaireItemInput['type'], +): R4TemporalKind | undefined => { + if (type === 'date' || type === 'dateTime' || type === 'time') return type + return undefined +} + +export const temporalValidatorForItem = ( + type: QuestionnaireItemInput['type'], +): ((value: unknown) => boolean) | undefined => { + switch (type) { + case 'date': + return isR4Date + case 'dateTime': + return isR4DateTime + case 'time': + return isR4Time + default: + return undefined + } +} + +export const validateBoundPair = ( + item: QuestionnaireItemInput, + path: ReadonlyArray, + minimumUrl: string, + maximumUrl: string, +): readonly Issue[] => { + const minimum = firstExtensionValue(item, minimumUrl) + const maximum = firstExtensionValue(item, maximumUrl) + if (minimum === undefined || maximum === undefined) return [] + return ( + minimum.key === maximum.key && + boundIsOrdered( + minimum.value, + maximum.value, + temporalKindForItem(item.type), + ) === true + ) ? + [] + : [ + issue( + 'out-of-range', + path, + 'Minimum and maximum must use comparable values and minimum must not exceed maximum.', + ), + ] +} diff --git a/packages/fhir/src/questionnaire/r4-resource-types.ts b/packages/fhir/src/questionnaire/r4-resource-types.ts index 9ff402ea..67eadac9 100644 --- a/packages/fhir/src/questionnaire/r4-resource-types.ts +++ b/packages/fhir/src/questionnaire/r4-resource-types.ts @@ -7,7 +7,7 @@ // // Exact codes from the required R4 ResourceType value set (FHIR 4.0.1). -const R4_RESOURCE_TYPES = new Set([ +const R4_RESOURCE_TYPE_CODES = [ 'Account', 'ActivityDefinition', 'AdverseEvent', @@ -156,7 +156,12 @@ const R4_RESOURCE_TYPES = new Set([ 'ValueSet', 'VerificationResult', 'VisionPrescription', -]) +] as const + +/** Exact code union from the required FHIR R4 ResourceType value set. */ +export type R4ResourceType = (typeof R4_RESOURCE_TYPE_CODES)[number] + +const R4_RESOURCE_TYPES: ReadonlySet = new Set(R4_RESOURCE_TYPE_CODES) /** Whether a code belongs to the required FHIR R4 ResourceType value set. */ export const isR4ResourceType = (value: string): boolean => diff --git a/packages/fhir/src/questionnaire/references.ts b/packages/fhir/src/questionnaire/references.ts new file mode 100644 index 00000000..71fb7e79 --- /dev/null +++ b/packages/fhir/src/questionnaire/references.ts @@ -0,0 +1,160 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/** Closed QuestionnaireResponse reference targets and literal/logical shapes. */ + +import { isR4ResourceType } from './r4-resource-types.js' +import { parseAbsoluteUri } from '../core/index.js' + +type UnknownRecord = Readonly> + +const asRecord = (value: unknown): UnknownRecord | undefined => + typeof value === 'object' && value !== null ? + (value as UnknownRecord) + : undefined + +const FHIR_ID = /^[A-Za-z\d\-.]{1,64}$/u +const TYPED_LITERAL_REFERENCE = + /(?:^|\/)([A-Z][A-Za-z\d]*)\/([A-Za-z\d\-.]{1,64})(?:\/_history\/[A-Za-z\d\-.]{1,64})?$/u + +export const QUESTIONNAIRE_RESPONSE_AUTHOR_TYPES: ReadonlySet = new Set( + [ + 'Device', + 'Organization', + 'Patient', + 'Practitioner', + 'PractitionerRole', + 'RelatedPerson', + ], +) + +export const QUESTIONNAIRE_RESPONSE_SOURCE_TYPES: ReadonlySet = new Set( + ['Patient', 'Practitioner', 'PractitionerRole', 'RelatedPerson'], +) + +const declaredTargetType = (value: unknown): string | undefined => { + if (typeof value !== 'string' || value === '') return undefined + let end = value.length + while (end > 0 && value[end - 1] === '/') end -= 1 + const separator = value.lastIndexOf('/', end - 1) + const type = value.slice(separator + 1, end) + return isR4ResourceType(type) ? type : undefined +} + +const containedTargetType = ( + reference: string, + contained: readonly unknown[], +): string | undefined => { + const id = reference.slice(1) + if (!FHIR_ID.test(id)) return undefined + const matches = contained.filter( + (candidate) => asRecord(candidate)?.id === id, + ) + if (matches.length !== 1) return undefined + const resourceType = asRecord(matches[0])?.resourceType + return typeof resourceType === 'string' && isR4ResourceType(resourceType) ? + resourceType + : undefined +} + +const literalTargetType = ( + reference: string, + contained: readonly unknown[], +): string | undefined => { + if (reference.startsWith('#')) { + return containedTargetType(reference, contained) + } + if (/[?#]/u.test(reference)) return undefined + const match = TYPED_LITERAL_REFERENCE.exec(reference) + const type = match?.[1] + return type !== undefined && isR4ResourceType(type) ? type : undefined +} + +const completeLogicalIdentifier = (value: unknown): boolean => { + const identifier = asRecord(value) + return ( + parseAbsoluteUri(identifier?.system).ok && + typeof identifier?.value === 'string' && + identifier.value.trim() !== '' + ) +} + +/** Resolves a target type from Reference.type, a typed literal, or a contained target. */ +export const questionnaireResponseReferenceType = ( + value: unknown, + contained: readonly unknown[] = [], +): string | undefined => { + const reference = asRecord(value) + if (reference === undefined) return undefined + const declared = declaredTargetType(reference.type) + if (reference.type !== undefined && declared === undefined) return undefined + if (typeof reference.reference !== 'string') return declared + const literal = literalTargetType(reference.reference, contained) + if (reference.reference.startsWith('#') && literal === undefined) { + return undefined + } + if (declared !== undefined && literal !== undefined && declared !== literal) { + return undefined + } + return declared ?? literal +} + +/** Validates a conformant parser-side literal and/or logical Reference. */ +export const isQuestionnaireResponseReference = ( + value: unknown, + allowedTypes?: ReadonlySet, + contained: readonly unknown[] = [], +): boolean => { + const reference = asRecord(value) + if (reference === undefined) return false + const hasLiteral = + typeof reference.reference === 'string' && reference.reference !== '' + const hasIdentifier = reference.identifier !== undefined + if (!hasLiteral && !hasIdentifier) return false + if (hasIdentifier && !completeLogicalIdentifier(reference.identifier)) { + return false + } + const type = questionnaireResponseReferenceType(reference, contained) + return ( + type !== undefined && + (hasLiteral || reference.type !== undefined) && + (allowedTypes === undefined || allowedTypes.has(type)) + ) +} + +/** Strict builder-side shape: explicit type and exactly one literal or logical target. */ +export const isQuestionnaireResponseBuilderReference = ( + value: unknown, + allowedTypes?: ReadonlySet, +): boolean => { + const reference = asRecord(value) + if (reference === undefined) return false + const type = declaredTargetType(reference.type) + if ( + type === undefined || + reference.type !== type || + (allowedTypes !== undefined && !allowedTypes.has(type)) + ) { + return false + } + const hasLiteral = + typeof reference.reference === 'string' && reference.reference !== '' + const hasIdentifier = reference.identifier !== undefined + if (hasLiteral === hasIdentifier) return false + if (hasIdentifier) return completeLogicalIdentifier(reference.identifier) + const literal = literalTargetType(reference.reference as string, []) + return literal === type +} + +/** Required canonical base form for Grove Questionnaire.url. */ +export const isExactQuestionnaireUrl = (value: unknown): boolean => + typeof value === 'string' && + /^https?:\/\//u.test(value) && + !value.includes('#') && + !value.includes('|') && + parseAbsoluteUri(value).ok diff --git a/packages/fhir/src/questionnaire/schemas.ts b/packages/fhir/src/questionnaire/schemas.ts index fd26f315..200076f2 100644 --- a/packages/fhir/src/questionnaire/schemas.ts +++ b/packages/fhir/src/questionnaire/schemas.ts @@ -10,6 +10,7 @@ import type { QuestionnaireItem as R4QuestionnaireItem, QuestionnaireResponseItem as R4QuestionnaireResponseItem, QuestionnaireResponseItemAnswer as R4QuestionnaireResponseItemAnswer, + Resource as R4Resource, } from 'fhir/r4.js' import { z } from 'zod' import { isR4ResourceType } from './r4-resource-types.js' @@ -21,6 +22,7 @@ import { } from './temporal.js' import type { GroveQuestionnaire, GroveQuestionnaireResponse } from './types.js' import { parseFhirInstant } from '../core/index.js' +import type { FhirJson } from '../r4/json.js' import { attachmentSchema, codingSchema, @@ -101,6 +103,13 @@ const answerQuantitySchema = quantitySchema.refine( 'Quantity answer values cannot be empty.', ) +const containedResourceSchema = z.looseObject({ + resourceType: z + .string() + .refine(isR4ResourceType, 'Unknown contained R4 ResourceType code.'), + id: z.string().regex(/^[A-Za-z\d\-.]{1,64}$/u), +}) as unknown as z.ZodType + const resourceFields = { id: z.string().optional(), _id: primitiveMetadata, @@ -109,6 +118,7 @@ const resourceFields = { _implicitRules: primitiveMetadata, language: z.string().optional(), _language: primitiveMetadata, + contained: z.array(containedResourceSchema).optional(), extension: z.array(extensionSchema).optional(), modifierExtension: z.array(extensionSchema).optional(), } as const @@ -223,8 +233,8 @@ const initialSchema = z // at every nesting depth, must carry a non-blank linkId. The generated base states the release's // own constraint, which admits a blank one, and layering the narrowing on top would take a // recursive walk of item.item that is harder to read than the constraint stated inline. -export const questionnaireItemSchema: z.ZodType = z.lazy( - () => +export const questionnaireItemSchema: z.ZodType> = + z.lazy(() => z .strictObject({ id: z.string().optional(), @@ -310,7 +320,7 @@ export const questionnaireItemSchema: z.ZodType = z.lazy( }) } }), -) + ) const questionnaireSchemaValue = z.strictObject({ resourceType: z.literal('Questionnaire'), @@ -353,47 +363,49 @@ const questionnaireSchemaValue = z.strictObject({ export const questionnaireSchema: z.ZodType = questionnaireSchemaValue as z.ZodType -export const questionnaireResponseAnswerSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: z.string().optional(), - extension: z.array(extensionSchema).optional(), - modifierExtension: z.array(extensionSchema).optional(), - ...answerValueFields, - item: z.array(questionnaireResponseItemSchema).optional(), - }) - .superRefine((value, context) => - exactlyOneAnswerChoice(value, answerValueKeys, context), - ), - ) +export const questionnaireResponseAnswerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z.array(extensionSchema).optional(), + modifierExtension: z.array(extensionSchema).optional(), + ...answerValueFields, + item: z.array(questionnaireResponseItemSchema).optional(), + }) + .superRefine((value, context) => + exactlyOneAnswerChoice(value, answerValueKeys, context), + ), +) -export const questionnaireResponseItemSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: z.string().optional(), - extension: z.array(extensionSchema).optional(), - modifierExtension: z.array(extensionSchema).optional(), - linkId: z.string().min(1), - _linkId: primitiveMetadata, - definition: z.string().optional(), - _definition: primitiveMetadata, - text: z.string().optional(), - _text: primitiveMetadata, - answer: z.array(questionnaireResponseAnswerSchema).optional(), - item: z.array(questionnaireResponseItemSchema).optional(), - }) - .superRefine((value, context) => { - if ((value.answer?.length ?? 0) > 0 && (value.item?.length ?? 0) > 0) { - context.addIssue({ - code: 'custom', - message: - 'A QuestionnaireResponse item cannot contain both answer and item.', - }) - } - }), - ) +export const questionnaireResponseItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z.array(extensionSchema).optional(), + modifierExtension: z.array(extensionSchema).optional(), + linkId: z.string().min(1), + _linkId: primitiveMetadata, + definition: z.string().optional(), + _definition: primitiveMetadata, + text: z.string().optional(), + _text: primitiveMetadata, + answer: z.array(questionnaireResponseAnswerSchema).optional(), + item: z.array(questionnaireResponseItemSchema).optional(), + }) + .superRefine((value, context) => { + if ((value.answer?.length ?? 0) > 0 && (value.item?.length ?? 0) > 0) { + context.addIssue({ + code: 'custom', + message: + 'A QuestionnaireResponse item cannot contain both answer and item.', + }) + } + }), +) const questionnaireResponseSchemaValue = z.strictObject({ resourceType: z.literal('QuestionnaireResponse'), @@ -560,12 +572,37 @@ const questionnaireResponseBuilderItemSchema: z.ZodType = z.lazy(() => }), ) +const questionnaireResponseIdentifierInputSchema = z.strictObject({ + system: z.string(), + value: z.string(), +}) + +const builderReferenceSchema = (type: z.ZodType): z.ZodType => + z.union([ + z.strictObject({ + type, + reference: z.string(), + }), + z.strictObject({ + type, + identifier: questionnaireResponseIdentifierInputSchema, + }), + ]) + +const subjectReferenceInputSchema = builderReferenceSchema( + z.string().refine(isR4ResourceType, 'Expected a FHIR R4 ResourceType code.'), +) +// The shape boundary admits every R4 type so semantic validation can report +// the field-specific author/source target rule alongside other input issues. +const authorReferenceInputSchema = subjectReferenceInputSchema +const sourceReferenceInputSchema = subjectReferenceInputSchema + /** Strict runtime boundary for the public QuestionnaireResponse builder input. */ export const questionnaireResponseBuilderInputSchema: z.ZodType = z.strictObject({ id: z.string().optional(), questionnaire: z.string(), - identifier: z.strictObject({ system: z.string(), value: z.string() }), + identifier: questionnaireResponseIdentifierInputSchema, status: z.enum([ 'in-progress', 'completed', @@ -573,10 +610,10 @@ export const questionnaireResponseBuilderInputSchema: z.ZodType = 'entered-in-error', 'stopped', ]), - subject: z.string().optional(), + subject: subjectReferenceInputSchema.optional(), authored: z.string(), - authorReference: z.string().optional(), - sourceReference: z.string().optional(), + author: authorReferenceInputSchema.optional(), + source: sourceReferenceInputSchema.optional(), extensions: z.array(extensionSchema).optional(), items: z.array(questionnaireResponseBuilderItemSchema).optional(), }) diff --git a/packages/fhir/src/questionnaire/types.ts b/packages/fhir/src/questionnaire/types.ts index b4f8a9e0..0bc135eb 100644 --- a/packages/fhir/src/questionnaire/types.ts +++ b/packages/fhir/src/questionnaire/types.ts @@ -6,12 +6,12 @@ // SPDX-License-Identifier: MIT // +import type { R4ResourceType } from './r4-resource-types.js' import type { AbsoluteUri, Canonical, FhirId, FhirInstant, - PatientReference, SemVer, } from '../core/index.js' import type { @@ -153,7 +153,7 @@ export interface QuestionnaireInput { readonly name?: string readonly title?: string readonly status: Questionnaire['status'] - readonly subjectTypes?: readonly string[] + readonly subjectTypes?: readonly R4ResourceType[] readonly date?: FhirInstant readonly description?: string readonly purpose?: string @@ -166,16 +166,47 @@ export interface QuestionnaireResponseIdentifierInput { readonly value: string } +type TypedQuestionnaireResponseReference = + | Readonly<{ + type: ResourceType + reference: string + identifier?: never + }> + | Readonly<{ + type: ResourceType + reference?: never + identifier: QuestionnaireResponseIdentifierInput + }> + +/** Any typed literal or identifier-only logical R4 subject Reference. */ +export type QuestionnaireResponseSubjectInput = + TypedQuestionnaireResponseReference + +export type QuestionnaireResponseAuthorInput = + TypedQuestionnaireResponseReference< + | 'Device' + | 'Organization' + | 'Patient' + | 'Practitioner' + | 'PractitionerRole' + | 'RelatedPerson' + > + +export type QuestionnaireResponseSourceInput = + TypedQuestionnaireResponseReference< + 'Patient' | 'Practitioner' | 'PractitionerRole' | 'RelatedPerson' + > + export interface QuestionnaireResponseInput { readonly id?: FhirId /** Exact `Questionnaire.url|Questionnaire.version` canonical. */ readonly questionnaire: Canonical readonly identifier: QuestionnaireResponseIdentifierInput readonly status: QuestionnaireResponse['status'] - readonly subject?: PatientReference + readonly subject?: QuestionnaireResponseSubjectInput readonly authored: FhirInstant - readonly authorReference?: string - readonly sourceReference?: string + readonly author?: QuestionnaireResponseAuthorInput + readonly source?: QuestionnaireResponseSourceInput readonly extensions?: readonly Extension[] readonly items?: readonly QuestionnaireResponseItemInput[] } diff --git a/packages/fhir/src/r4/active-graph-semantics.ts b/packages/fhir/src/r4/active-graph-semantics.ts new file mode 100644 index 00000000..6799afba --- /dev/null +++ b/packages/fhir/src/r4/active-graph-semantics.ts @@ -0,0 +1,615 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/** Active exchange-envelope and cross-resource measurement semantics. */ + +import type { z } from 'zod' +import { validateMobileObservationSemantics } from './active-observation-semantics.js' +import { + validateExchangeEnvelope, + type ValidatedEnvelope, +} from './exchange-envelope.js' +import { + APPLICATION_DEVICE_PROFILES, + ISO_LIFECYCLE_SYSTEM, + PROVENANCE_PARTICIPANT_SYSTEM, + addIssue, + asRecord, + codingCount, + codingCountForSystem, + codingExists, + completeIdentifier, + identifierPairEqual, + identifierRole, + identifierWithRole, + locatedReferences, + type UnknownRecord, +} from './graph-schema-utils.js' +import type { R4CollectionBundle } from './types.js' +import { parseAbsoluteUri } from '../core/index.js' +import { groveMobileContract } from '../mobile/contract.js' +import { isOpaqueIdentityValue } from '../mobile/identity.js' +import { + groveProfileClaims, + healthConnectDataOriginApplication, +} from '../mobile/measurement-catalog.generated.js' + +export { hasAdmittedMobileObservationProfile } from './active-observation-semantics.js' +export { validateExchangeEnvelope, type ValidatedEnvelope } + +const ACTIVE_ENTRY_POLICY = + groveMobileContract.lifecycle.activeEntryResourcePolicy +const ACTIVE_OUTPUT_TYPES: ReadonlySet = new Set( + ACTIVE_ENTRY_POLICY.outputResourceTypes, +) +const ACTIVE_SUPPORTING_TYPES: ReadonlySet = new Set( + ACTIVE_ENTRY_POLICY.supportingResourceTypes, +) +const ACTIVE_ENTRY_TYPES: ReadonlySet = new Set([ + ...ACTIVE_OUTPUT_TYPES, + ...ACTIVE_SUPPORTING_TYPES, + ACTIVE_ENTRY_POLICY.lifecycleResourceType, +]) + +/** Whether a top-level active event resource belongs to the protocol's closed entry set. */ +export const isAdmittedActiveEntryResource = (resource: unknown): boolean => { + const resourceType = asRecord(resource)?.resourceType + return ( + typeof resourceType === 'string' && ACTIVE_ENTRY_TYPES.has(resourceType) + ) +} + +/** Mobile event graphs prohibit the contained property, including an empty array. */ +export const hasProhibitedContainedResource = (resource: unknown): boolean => { + const record = asRecord(resource) + return record !== undefined && 'contained' in record +} + +export const validateAssemblerAgent = ( + provenance: UnknownRecord, + envelope: ValidatedEnvelope, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + const agents = Array.isArray(provenance.agent) ? provenance.agent : [] + const assemblers = agents.filter( + (agent) => + codingCount( + asRecord(agent)?.type, + PROVENANCE_PARTICIPANT_SYSTEM, + 'assembler', + ) > 0, + ) + const assembler = asRecord(assemblers[0]) + const who = asRecord(assembler?.who) + const internal = + typeof who?.reference === 'string' ? + envelope.resourcesByFullUrl.get(who.reference) + : undefined + const internalProfiles = asRecord(internal?.meta)?.profile + const internalApplication = + internal?.resourceType === 'Device' && + Array.isArray(internalProfiles) && + internalProfiles.filter( + (profile) => + typeof profile === 'string' && APPLICATION_DEVICE_PROFILES.has(profile), + ).length === 1 + const logicalIdentifier = who?.identifier + const logicalApplication = + who?.type === 'Device' && + completeIdentifier(logicalIdentifier) && + parseAbsoluteUri(logicalIdentifier.system).ok && + identifierRole(logicalIdentifier) === 'device-snapshot' && + isOpaqueIdentityValue(logicalIdentifier.value) + if ( + assemblers.length !== 1 || + codingCount(assembler?.type, PROVENANCE_PARTICIPANT_SYSTEM, 'assembler') !== + 1 || + codingCountForSystem(assembler?.type, PROVENANCE_PARTICIPANT_SYSTEM) !== + 1 || + (!internalApplication && !logicalApplication) + ) { + addIssue( + context, + 'mobile-provenance.assembler', + path, + 'Grove Provenance requires exactly one assembler linked to an application Device snapshot.', + ) + } +} + +const CONVERSION_PROVENANCE_PROFILES: ReadonlySet = new Set([ + groveMobileContract.profiles.conversionProvenance, + ...groveProfileClaims.adapterConversionProvenanceClaims.map( + ({ profile }) => profile, + ), +]) + +/** Whether active lifecycle Provenance declares exactly one admitted direct profile. */ +export const hasAdmittedActiveProvenanceProfile = ( + resource: unknown, +): boolean => { + const record = asRecord(resource) + const profiles = asRecord(record?.meta)?.profile + return ( + record?.resourceType === 'Provenance' && + Array.isArray(profiles) && + profiles.length === 1 && + typeof profiles[0] === 'string' && + CONVERSION_PROVENANCE_PROFILES.has(profiles[0]) + ) +} + +const ADAPTER_CONVERSION_CLAIMS = + groveProfileClaims.adapterConversionProvenanceClaims +const HEALTH_CONNECT_CONVERSION_PROVENANCE_PROFILE = + ADAPTER_CONVERSION_CLAIMS.find( + ({ adapter }) => adapter === 'health-connect', + )?.profile + +const validateHealthConnectDataOriginApplication = ( + provenance: UnknownRecord, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +) => { + const entities = Array.isArray(provenance.entity) ? provenance.entity : [] + const agents = + Array.isArray(asRecord(entities[0])?.agent) ? + (asRecord(entities[0])?.agent as readonly unknown[]) + : [] + const agent = asRecord(agents[0]) + const who = asRecord(agent?.who) + const identifier = who?.identifier + if ( + agents.length !== 1 || + codingCount(agent?.type, PROVENANCE_PARTICIPANT_SYSTEM, 'enterer') !== 1 || + codingCountForSystem(agent?.type, PROVENANCE_PARTICIPANT_SYSTEM) !== 1 || + who?.reference !== undefined || + who?.type !== healthConnectDataOriginApplication.referenceType || + !completeIdentifier(identifier) || + identifier.system !== healthConnectDataOriginApplication.identifierSystem || + identifier.value.trim() === '' + ) { + addIssue( + context, + 'health-connect.data-origin-application', + [...path, 'entity', 0, 'agent'], + 'Health Connect DataOrigin.packageName must be one enterer agent with a typed identifier-only logical Device Reference in the Android package-name namespace.', + ) + } +} + +const adapterClaimsForOutput = ( + resource: unknown, +): ReadonlyArray<(typeof ADAPTER_CONVERSION_CLAIMS)[number]> => { + const profiles = asRecord(asRecord(resource)?.meta)?.profile + if (!Array.isArray(profiles)) return [] + const direct = new Set( + profiles.filter((profile) => typeof profile === 'string'), + ) + return ADAPTER_CONVERSION_CLAIMS.filter(({ targetAdapterProfiles }) => + targetAdapterProfiles.some((profile) => direct.has(profile)), + ) +} + +interface IndexedOutputEntry { + readonly entry: UnknownRecord + readonly index: number +} + +interface ConversionProvenance { + readonly resource: UnknownRecord + readonly profiles: unknown +} + +const selectConversionProvenance = ( + envelope: ValidatedEnvelope, + context: z.core.$RefinementCtx, +): ConversionProvenance | undefined => { + const resources = envelope.entries.map(({ resource }) => resource) + const provenances = resources.filter( + (resource) => asRecord(resource)?.resourceType === 'Provenance', + ) + const conversions = provenances.filter((resource) => + codingExists( + asRecord(resource)?.activity, + ISO_LIFECYCLE_SYSTEM, + 'transform', + ), + ) + const retractions = provenances.filter((resource) => + codingExists( + asRecord(resource)?.activity, + groveMobileContract.systems.lifecycleEvent, + groveMobileContract.lifecycle.sourceRecordRetracted, + ), + ) + if ( + provenances.length !== 1 || + conversions.length !== 1 || + retractions.length !== 0 + ) { + addIssue( + context, + 'mobile-exchange.transform-provenance', + ['entry'], + 'An active event requires exactly one transform Provenance and no retraction Provenance.', + ) + return undefined + } + const resource = asRecord(conversions[0]) + return resource === undefined ? undefined : ( + { resource, profiles: asRecord(resource.meta)?.profile } + ) +} + +const directConversionProfile = (profiles: unknown): string | undefined => + Array.isArray(profiles) && typeof profiles[0] === 'string' ? + profiles[0] + : undefined + +const validateConversionProvenanceHeader = ( + conversion: ConversionProvenance, + envelope: ValidatedEnvelope, + context: z.core.$RefinementCtx, +): string | undefined => { + const { resource: provenance, profiles } = conversion + if ( + !Array.isArray(profiles) || + profiles.length !== 1 || + typeof profiles[0] !== 'string' || + !CONVERSION_PROVENANCE_PROFILES.has(profiles[0]) + ) { + addIssue( + context, + 'mobile-exchange.provenance-profile', + ['entry'], + 'Conversion Provenance must directly declare exactly its admitted Mobile or adapter conversion profile.', + ) + } + if ( + codingCount(provenance.activity, ISO_LIFECYCLE_SYSTEM, 'transform') !== 1 || + codingCountForSystem(provenance.activity, ISO_LIFECYCLE_SYSTEM) !== 1 || + codingCountForSystem( + provenance.activity, + groveMobileContract.systems.lifecycleEvent, + ) !== 0 + ) { + addIssue( + context, + 'mobile-exchange.lifecycle-coding', + ['entry'], + 'Conversion Provenance requires exactly one transform coding from the ISO lifecycle system and no Grove retraction lifecycle coding.', + ) + } + validateAssemblerAgent(provenance, envelope, context, ['entry']) + if ( + provenance.occurredDateTime === undefined && + provenance.occurredPeriod === undefined + ) { + addIssue( + context, + 'mobile-exchange.occurred-time', + ['entry'], + 'Conversion Provenance requires occurred[x].', + ) + } + if (provenance.recorded === undefined) { + addIssue( + context, + 'mobile-exchange.recorded-time', + ['entry'], + 'Conversion Provenance requires recorded.', + ) + } + return directConversionProfile(profiles) +} + +const conversionSourceIdentifier = ( + provenance: UnknownRecord, + context: z.core.$RefinementCtx, +): unknown => { + const entities = Array.isArray(provenance.entity) ? provenance.entity : [] + const sourceEntity = asRecord(entities[0]) + const sourceWhat = asRecord(sourceEntity?.what) + const identifier = sourceWhat?.identifier + if (entities.length !== 1) { + addIssue( + context, + 'mobile-exchange.single-source-entity', + ['entry'], + 'Conversion Provenance must carry exactly one source entity.', + ) + } + if ( + sourceEntity?.role !== 'source' || + sourceWhat?.reference !== undefined || + !completeIdentifier(identifier) || + !parseAbsoluteUri(identifier.system).ok || + identifierRole(identifier) !== 'source-record' || + !isOpaqueIdentityValue(identifier.value) + ) { + addIssue( + context, + 'mobile-exchange.logical-source-entity', + ['entry'], + 'Conversion Provenance source must be a logical typed opaque source-record Identifier with no literal reference.', + ) + } + return identifier +} + +const collectOutputEntries = ( + envelope: ValidatedEnvelope, + context: z.core.$RefinementCtx, +): readonly IndexedOutputEntry[] => { + const outputs = envelope.entries.flatMap((entry, index) => + ACTIVE_OUTPUT_TYPES.has(String(asRecord(entry.resource)?.resourceType)) ? + [{ entry, index }] + : [], + ) + if (outputs.length === 0) { + addIssue( + context, + 'mobile-exchange.output-required', + ['entry'], + 'An active exchange event requires at least one clinical or source-artifact output.', + ) + } + return outputs +} + +const validateOutputAdapterClaim = ( + output: IndexedOutputEntry, + provenanceAdapterClaim: + (typeof ADAPTER_CONVERSION_CLAIMS)[number] | undefined, + context: z.core.$RefinementCtx, +): void => { + const claims = adapterClaimsForOutput(output.entry.resource) + const matches = + provenanceAdapterClaim === undefined ? + claims.length === 0 + : claims.length === 1 && + claims[0]?.profile === provenanceAdapterClaim.profile + if (claims.length > 1 || !matches) { + addIssue( + context, + 'mobile-exchange.adapter-provenance-graph', + ['entry', output.index, 'resource', 'meta', 'profile'], + "Every adapter output must claim exactly the adapter profile governed by this event's conversion Provenance, and source-neutral events must not claim adapter outputs.", + ) + } +} + +const validateOutputIdentities = ( + outputEntry: IndexedOutputEntry, + sourceIdentifier: unknown, + context: z.core.$RefinementCtx, +): void => { + const { entry, index } = outputEntry + const source = identifierWithRole(entry.resource, 'source-record') + const output = identifierWithRole(entry.resource, 'source-output') + if ( + !completeIdentifier(source) || + !parseAbsoluteUri(source.system).ok || + !isOpaqueIdentityValue(source.value) || + !identifierPairEqual(source, sourceIdentifier) || + !completeIdentifier(output) || + !parseAbsoluteUri(output.system).ok || + !isOpaqueIdentityValue(output.value) + ) { + addIssue( + context, + 'mobile-output.source-output-required', + ['entry', index, 'resource', 'identifier'], + 'Every active output requires matching source-record and exact typed output identities.', + ) + } +} + +const validateActiveOutputs = ( + outputs: readonly IndexedOutputEntry[], + sourceIdentifier: unknown, + provenanceProfile: string | undefined, + context: z.core.$RefinementCtx, +): void => { + const provenanceAdapterClaim = ADAPTER_CONVERSION_CLAIMS.find( + ({ profile }) => profile === provenanceProfile, + ) + for (const output of outputs) { + validateMobileObservationSemantics(output.entry.resource, context, [ + 'entry', + output.index, + 'resource', + ]) + validateOutputAdapterClaim(output, provenanceAdapterClaim, context) + validateOutputIdentities(output, sourceIdentifier, context) + } +} + +const isHybridObservation = (entry: UnknownRecord): boolean => { + const claim = groveProfileClaims.sensorKitHybridObservationClaims + const resource = asRecord(entry.resource) + const profiles = asRecord(resource?.meta)?.profile + const admittedProfiles: ReadonlySet = new Set(claim.profiles) + return ( + resource?.resourceType === 'Observation' && + Array.isArray(profiles) && + profiles.length === claim.cardinality && + profiles.every( + (profile) => typeof profile === 'string' && admittedProfiles.has(profile), + ) + ) +} + +const hasHybridDocumentCompanion = ( + source: unknown, + outputs: readonly IndexedOutputEntry[], +): boolean => { + const requiredProfile = + groveProfileClaims.sensorKitHybridObservationClaims.requiredCompanionProfile + return outputs.some(({ entry }) => { + const resource = asRecord(entry.resource) + const profiles = asRecord(resource?.meta)?.profile + return ( + resource?.resourceType === 'DocumentReference' && + Array.isArray(profiles) && + profiles.length === 1 && + profiles[0] === requiredProfile && + identifierPairEqual(identifierWithRole(resource, 'source-record'), source) + ) + }) +} + +const validateHybridCompanions = ( + outputs: readonly IndexedOutputEntry[], + context: z.core.$RefinementCtx, +): void => { + for (const output of outputs.filter(({ entry }) => + isHybridObservation(entry), + )) { + const source = identifierWithRole(output.entry.resource, 'source-record') + if (!hasHybridDocumentCompanion(source, outputs)) { + addIssue( + context, + 'mobile-output.hybrid-companion', + ['entry', output.index, 'resource', 'meta', 'profile'], + 'A SensorKit ECG Observation requires one exact native Recording Document companion in the same event with the same source-record Identifier.', + ) + } + } +} + +const validateProvenanceTargets = ( + provenance: UnknownRecord, + outputs: readonly IndexedOutputEntry[], + context: z.core.$RefinementCtx, +): void => { + const expected = new Set( + outputs.flatMap(({ entry }) => + typeof entry.fullUrl === 'string' ? [entry.fullUrl] : [], + ), + ) + const actual = + Array.isArray(provenance.target) ? + provenance.target.flatMap((target) => { + const reference = asRecord(target)?.reference + return typeof reference === 'string' ? [reference] : [] + }) + : [] + if ( + actual.length !== expected.size || + new Set(actual).size !== actual.length || + actual.some((reference) => !expected.has(reference)) + ) { + addIssue( + context, + 'mobile-exchange.provenance-targets', + ['entry'], + 'Conversion Provenance must target every and only active clinical output.', + ) + } +} + +const referenceAdjacency = ( + resources: ReadonlyMap, +): ReadonlyMap> => { + const adjacency = new Map>( + [...resources.keys()].map((fullUrl) => [fullUrl, new Set()]), + ) + for (const [sourceUrl, resource] of resources) { + for (const { reference } of locatedReferences(resource)) { + if (!resources.has(reference)) continue + adjacency.get(sourceUrl)?.add(reference) + adjacency.get(reference)?.add(sourceUrl) + } + } + return adjacency +} + +const connectedGraphNodes = ( + envelope: ValidatedEnvelope, + adjacency: ReadonlyMap>, +): ReadonlySet => { + const reachable = new Set( + [...envelope.resourcesByFullUrl].flatMap(([fullUrl, resource]) => + ( + ACTIVE_OUTPUT_TYPES.has(String(resource.resourceType)) || + resource.resourceType === ACTIVE_ENTRY_POLICY.lifecycleResourceType + ) ? + [fullUrl] + : [], + ), + ) + const pending = [...reachable] + for (const current of pending) { + for (const connected of adjacency.get(current) ?? []) { + if (reachable.has(connected)) continue + reachable.add(connected) + pending.push(connected) + } + } + return reachable +} + +const validateSupportingResourceConnectivity = ( + envelope: ValidatedEnvelope, + context: z.core.$RefinementCtx, +): void => { + const reachable = connectedGraphNodes( + envelope, + referenceAdjacency(envelope.resourcesByFullUrl), + ) + const disconnected = [...envelope.resourcesByFullUrl].some( + ([fullUrl, resource]) => + ACTIVE_SUPPORTING_TYPES.has(String(resource.resourceType)) && + !reachable.has(fullUrl), + ) + if (disconnected) { + addIssue( + context, + 'mobile-support.connected', + ['entry'], + 'Every supporting resource must be connected by literal Bundle references to an output or the lifecycle Provenance.', + ) + } +} + +export const refineActiveBundle = ( + bundle: R4CollectionBundle, + context: z.core.$RefinementCtx, +): void => { + const envelope = validateExchangeEnvelope( + bundle, + context, + groveMobileContract.profiles.exchangeBundle, + ) + if (envelope === undefined) return + const conversion = selectConversionProvenance(envelope, context) + if (conversion === undefined) return + const provenanceProfile = validateConversionProvenanceHeader( + conversion, + envelope, + context, + ) + const sourceIdentifier = conversionSourceIdentifier( + conversion.resource, + context, + ) + if (provenanceProfile === HEALTH_CONNECT_CONVERSION_PROVENANCE_PROFILE) { + validateHealthConnectDataOriginApplication(conversion.resource, context, [ + 'entry', + 'resource', + ]) + } + const outputs = collectOutputEntries(envelope, context) + validateActiveOutputs(outputs, sourceIdentifier, provenanceProfile, context) + validateHybridCompanions(outputs, context) + validateProvenanceTargets(conversion.resource, outputs, context) + validateSupportingResourceConnectivity(envelope, context) +} diff --git a/packages/fhir/src/r4/active-observation-semantics.ts b/packages/fhir/src/r4/active-observation-semantics.ts new file mode 100644 index 00000000..7d78b9ff --- /dev/null +++ b/packages/fhir/src/r4/active-observation-semantics.ts @@ -0,0 +1,674 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/** Catalog-driven Observation profile and result semantics. */ + +import type { z } from 'zod' +import { + addIssue, + asRecord, + codingCount, + codingCountForSystem, + type UnknownRecord, +} from './graph-schema-utils.js' +import { groveMobileContract } from '../mobile/contract.js' +import { + groveProfileClaims, + sharedMobileMeasurementCatalog, +} from '../mobile/measurement-catalog.generated.js' +import { + adapterMeasurementCatalog, + groveProviderProfileCanonicals, + providerAdapterCatalog, +} from '../providers/contract.generated.js' +import { compareFhirDateTimes } from '../zod/support.js' + +interface MeasurementDefinition { + readonly id: string + readonly profile: string + readonly owner?: string + readonly code: Readonly<{ system: string; code: string }> + readonly requiredCodings?: ReadonlyArray< + Readonly<{ + system: string + code: string + }> + > + readonly category?: Readonly<{ system: string; code: string }> + readonly effective: 'Period' | 'dateTime' | 'dateTime-or-Period' + readonly method?: Readonly<{ code: string }> + readonly methodChoice?: readonly string[] + readonly valueKind: + 'codeableConcept' | 'components' | 'dateTime' | 'grouping' | 'quantity' + readonly quantity?: Readonly<{ + system: string + code: string + unit: string + valueDomain?: Readonly<{ + minimum: Readonly<{ value: number; inclusive: boolean }> + maximum?: Readonly<{ value: number; inclusive: boolean }> + integerOnly: boolean + }> + }> | null + readonly resultCodeSystem?: string + readonly allowedValues?: readonly string[] + readonly components?: ReadonlyArray< + Readonly<{ + id: string + system: string + code: string + cardinality?: string + quantity: Readonly<{ system: string; code: string; unit: string }> + }> + > +} + +const ADAPTER_MEASUREMENT_DEFINITIONS = + adapterMeasurementCatalog as unknown as Readonly< + Record>> + > + +const MEASUREMENT_DEFINITIONS: readonly MeasurementDefinition[] = [ + ...(Object.values( + sharedMobileMeasurementCatalog, + ) as readonly MeasurementDefinition[]), + ...Object.values(ADAPTER_MEASUREMENT_DEFINITIONS).flatMap((owner) => + Object.values(owner), + ), +] + +const PROFILE_CANONICALS = groveProviderProfileCanonicals as Readonly< + Record +> + +const MEASUREMENT_BY_PROFILE: ReadonlyMap = + new Map( + MEASUREMENT_DEFINITIONS.flatMap((definition) => { + const canonical = PROFILE_CANONICALS[definition.profile] + return canonical === undefined ? [] : [[canonical, definition] as const] + }), + ) + +const SHARED_SEMANTIC_PROFILES: ReadonlySet = new Set([ + ...MEASUREMENT_BY_PROFILE.keys(), + ...groveProfileClaims.observationAdapterClaim.sharedSensorProfiles, + ...groveProfileClaims.observationAdapterClaim.standardAdapterClaims.map( + ({ semanticProfile }) => semanticProfile, + ), +]) + +const ADAPTER_OBSERVATION_PROFILES: ReadonlySet = new Set( + groveProfileClaims.observationAdapterClaim.adapterProfiles, +) + +const SENSORKIT_PLATFORM_PROFILES: ReadonlySet = new Set( + groveProfileClaims.sensorKitPlatformExclusiveClaims.profiles, +) + +const SENSORKIT_HYBRID_PROFILES: ReadonlySet = new Set( + groveProfileClaims.sensorKitHybridObservationClaims.profiles, +) + +interface ProviderContractRow { + readonly id: string + readonly measurementOwner: string + readonly observationProfile: string +} + +const PROVIDER_ROWS = + providerAdapterCatalog.providers as unknown as readonly ProviderContractRow[] +const PROVIDER_OBSERVATION_PROFILES: ReadonlySet = new Set( + PROVIDER_ROWS.map(({ observationProfile }) => observationProfile), +) + +const hasExactProviderObservationClaim = ( + observation: UnknownRecord, + match: MeasurementDefinition | null, +): boolean => { + const profiles = asRecord(observation.meta)?.profile + const directProfiles = Array.isArray(profiles) ? profiles : [] + const providerMarkers = + Array.isArray(observation.extension) ? + observation.extension.filter( + (extension) => + asRecord(extension)?.url === + providerAdapterCatalog.providerExtension.url, + ) + : [] + const isProviderClaim = + providerMarkers.length > 0 || + directProfiles.includes(providerAdapterCatalog.adapterProfile) || + directProfiles.some( + (profile) => + typeof profile === 'string' && + PROVIDER_OBSERVATION_PROFILES.has(profile), + ) || + (match?.owner !== undefined && + PROVIDER_ROWS.some( + ({ measurementOwner }) => measurementOwner === match.owner, + )) + if (!isProviderClaim) { + return true + } + if (match === null || providerMarkers.length !== 1) return false + + const providerCode = asRecord(providerMarkers[0])?.valueCode + const provider = PROVIDER_ROWS.find(({ id }) => id === providerCode) + const semanticProfile = PROFILE_CANONICALS[match.profile] + if ( + provider === undefined || + semanticProfile === undefined || + !Array.isArray(profiles) || + profiles.length !== 2 || + new Set(profiles).size !== 2 || + !profiles.includes(semanticProfile) || + !profiles.includes(provider.observationProfile) || + (match.owner !== undefined && provider.measurementOwner !== match.owner) + ) { + return false + } + return true +} + +const setsEqual = ( + left: ReadonlySet, + right: ReadonlySet, +): boolean => + left.size === right.size && [...left].every((value) => right.has(value)) + +const admittedObservationProfile = ( + profiles: unknown, +): MeasurementDefinition | null | undefined => { + if ( + !Array.isArray(profiles) || + profiles.length === 0 || + profiles.some((profile) => typeof profile !== 'string') + ) { + return undefined + } + const direct = new Set(profiles as readonly string[]) + if (direct.size !== profiles.length) return undefined + if ( + direct.size === 1 && + [...direct].every((profile) => SENSORKIT_PLATFORM_PROFILES.has(profile)) + ) { + return null + } + if (setsEqual(direct, SENSORKIT_HYBRID_PROFILES)) return null + + const semantic = [...direct].filter((profile) => + SHARED_SEMANTIC_PROFILES.has(profile), + ) + const adapters = [...direct].filter((profile) => + ADAPTER_OBSERVATION_PROFILES.has(profile), + ) + if ( + semantic.length !== 1 || + adapters.length > 1 || + direct.size !== semantic.length + adapters.length + ) { + return undefined + } + return MEASUREMENT_BY_PROFILE.get(semantic[0] ?? '') ?? null +} + +/** Whether an Observation directly declares one exact catalog-admitted semantic profile shape. */ +export const hasAdmittedMobileObservationProfile = ( + resource: unknown, +): boolean => { + const observation = asRecord(resource) + if (observation?.resourceType !== 'Observation') return false + const match = admittedObservationProfile(asRecord(observation.meta)?.profile) + return ( + match !== undefined && hasExactProviderObservationClaim(observation, match) + ) +} + +const validateObservationCodes = ( + observation: UnknownRecord, + match: MeasurementDefinition, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + const requiredCodings = match.requiredCodings ?? [] + const admittedPrimaryCodes = new Set([ + match.code.code, + ...requiredCodings + .filter(({ system }) => system === match.code.system) + .map(({ code }) => code), + ]) + const primarySystemCodings = + Array.isArray(asRecord(observation.code)?.coding) ? + (asRecord(observation.code)?.coding as readonly unknown[]).filter( + (candidate) => asRecord(candidate)?.system === match.code.system, + ) + : [] + if ( + codingCount(observation.code, match.code.system, match.code.code) !== 1 || + primarySystemCodings.length !== admittedPrimaryCodes.size || + primarySystemCodings.some( + (candidate) => + typeof asRecord(candidate)?.code !== 'string' || + !admittedPrimaryCodes.has(String(asRecord(candidate)?.code)), + ) + ) { + addIssue( + context, + `mobile-${match.id}.code`, + [...path, 'code'], + `A Grove Mobile ${match.id} Observation requires exactly its closed catalog coding set for ${match.code.system}.`, + ) + } + for (const required of requiredCodings) { + if (codingCount(observation.code, required.system, required.code) !== 1) { + addIssue( + context, + `mobile-${match.id}.required-coding`, + [...path, 'code'], + `A Grove Mobile ${match.id} Observation is missing or duplicates required coding ${required.system}|${required.code}.`, + ) + } + } +} + +const validateObservationCategory = ( + observation: UnknownRecord, + match: MeasurementDefinition, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + const categories: readonly unknown[] = + Array.isArray(observation.category) ? observation.category : [] + const categoryDefinition = match.category + if (categoryDefinition !== undefined) { + if ( + categories.reduce( + (count, category) => + count + + codingCount( + category, + categoryDefinition.system, + categoryDefinition.code, + ), + 0, + ) !== 1 || + categories.reduce( + (count, category) => + count + codingCountForSystem(category, categoryDefinition.system), + 0, + ) !== 1 + ) { + addIssue( + context, + `mobile-${match.id}.category`, + [...path, 'category'], + `A Grove Mobile ${match.id} Observation requires exactly one catalog category coding.`, + ) + } + } else if (categories.length > 0) { + addIssue( + context, + `mobile-${match.id}.category`, + [...path, 'category'], + `A Grove Mobile ${match.id} Observation must not invent an uncatalogued clinical category.`, + ) + } +} + +const validateObservationEffective = ( + observation: UnknownRecord, + match: MeasurementDefinition, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + if (match.effective === 'dateTime') { + if (typeof observation.effectiveDateTime !== 'string') { + addIssue( + context, + `mobile-${match.id}.effective`, + [...path, 'effectiveDateTime'], + `A Grove Mobile ${match.id} Observation requires effectiveDateTime.`, + ) + } + } else if (match.effective === 'Period') { + const period = asRecord(observation.effectivePeriod) + if (typeof period?.start !== 'string' || typeof period.end !== 'string') { + addIssue( + context, + `mobile-${match.id}.effective`, + [...path, 'effectivePeriod'], + `A Grove Mobile ${match.id} Observation requires a bounded effectivePeriod.`, + ) + } + } else { + const dateTime = observation.effectiveDateTime + const period = asRecord(observation.effectivePeriod) + const hasDateTime = typeof dateTime === 'string' + const hasPeriod = + typeof period?.start === 'string' && typeof period.end === 'string' + if (hasDateTime === hasPeriod) { + addIssue( + context, + `mobile-${match.id}.effective`, + [...path, 'effectiveDateTime'], + `A Grove Mobile ${match.id} Observation requires exactly one admitted effectiveDateTime or bounded effectivePeriod.`, + ) + } + } +} + +const validateObservationMethod = ( + observation: UnknownRecord, + match: MeasurementDefinition, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + const aggregationSystem = `${groveMobileContract.canonical}/CodeSystem/grove-aggregation-method` + if (match.method !== undefined) { + if ( + codingCount(observation.method, aggregationSystem, match.method.code) !== + 1 || + codingCountForSystem(observation.method, aggregationSystem) !== 1 + ) { + addIssue( + context, + `mobile-${match.id}.method`, + [...path, 'method'], + `A Grove Mobile ${match.id} Observation requires its fixed aggregation method.`, + ) + } + } else if (match.methodChoice !== undefined) { + const admittedMethods = match.methodChoice + const coding = asRecord(observation.method)?.coding + const admitted = + Array.isArray(coding) ? + coding.filter((candidate) => { + const item = asRecord(candidate) + return ( + item?.system === aggregationSystem && + typeof item.code === 'string' && + admittedMethods.includes(item.code) + ) + }) + : [] + if ( + admitted.length !== 1 || + codingCountForSystem(observation.method, aggregationSystem) !== 1 + ) { + addIssue( + context, + `mobile-${match.id}.method`, + [...path, 'method'], + `A Grove Mobile ${match.id} Observation requires exactly one admitted aggregation method.`, + ) + } + } +} + +const validateQuantityValueDomain = ( + value: unknown, + match: MeasurementDefinition, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + const domain = match.quantity?.valueDomain + if (typeof value !== 'number' || domain === undefined) return + const belowMinimum = + value < domain.minimum.value || + (!domain.minimum.inclusive && value === domain.minimum.value) + const aboveMaximum = + domain.maximum !== undefined && + (value > domain.maximum.value || + (!domain.maximum.inclusive && value === domain.maximum.value)) + if ( + belowMinimum || + aboveMaximum || + (domain.integerOnly && !Number.isInteger(value)) + ) { + addIssue( + context, + `mobile-${match.id}.value-domain`, + [...path, 'valueQuantity', 'value'], + `A Grove Mobile ${match.id} result must satisfy its catalog-owned value domain.`, + ) + } +} + +const validateStepCountPeriod = ( + observation: UnknownRecord, + match: MeasurementDefinition, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + if (match.id !== 'step-count') return + const period = asRecord(observation.effectivePeriod) + if ( + typeof period?.start === 'string' && + typeof period.end === 'string' && + compareFhirDateTimes(period.start, period.end) === 0 + ) { + addIssue( + context, + 'mobile-step-count.nonzero-period', + [...path, 'effectivePeriod'], + 'A Grove Mobile step-count period must have nonzero duration.', + ) + } +} + +const validateQuantityResult = ( + observation: UnknownRecord, + match: MeasurementDefinition, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + const quantity = match.quantity + const valueQuantity = asRecord(observation.valueQuantity) + if ( + typeof valueQuantity?.value !== 'number' || + !Number.isFinite(valueQuantity.value) || + valueQuantity.comparator !== undefined || + valueQuantity.system !== quantity?.system || + valueQuantity.code !== quantity?.code || + valueQuantity.unit !== quantity?.unit + ) { + addIssue( + context, + 'mobile-output.fixed-quantity-unit', + [...path, 'valueQuantity'], + `A Grove Mobile ${match.id} result requires one finite exact value in fixed UCUM ${String(quantity?.code)}.`, + ) + } + validateQuantityValueDomain(valueQuantity?.value, match, context, path) + validateStepCountPeriod(observation, match, context, path) +} + +const validateCodeableConceptResult = ( + observation: UnknownRecord, + match: MeasurementDefinition, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + const result = asRecord(observation.valueCodeableConcept) + const codings = Array.isArray(result?.coding) ? result.coding : [] + const admitted = codings.filter((candidate) => { + const coding = asRecord(candidate) + return ( + coding !== undefined && + coding.system === match.resultCodeSystem && + typeof coding.code === 'string' && + (match.allowedValues ?? []).includes(coding.code) + ) + }) + const invalidSameSystem = codings.some((candidate) => { + const coding = asRecord(candidate) + return ( + coding !== undefined && + coding.system === match.resultCodeSystem && + (typeof coding.code !== 'string' || + !(match.allowedValues ?? []).includes(coding.code)) + ) + }) + if (admitted.length !== 1 || invalidSameSystem) { + addIssue( + context, + `mobile-${match.id}.coded-result`, + [...path, 'valueCodeableConcept'], + `A Grove Mobile ${match.id} result requires exactly one closed shared result coding.`, + ) + } +} + +const validateDateTimeResult = ( + observation: UnknownRecord, + match: MeasurementDefinition, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + if (typeof observation.valueDateTime !== 'string') { + addIssue( + context, + `mobile-${match.id}.date-time-result`, + [...path, 'valueDateTime'], + `A Grove Mobile ${match.id} result requires valueDateTime.`, + ) + } +} + +const validateGroupingResult = ( + observation: UnknownRecord, + match: MeasurementDefinition, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + const members = + Array.isArray(observation.hasMember) ? observation.hasMember : [] + if (members.length === 0) { + addIssue( + context, + `mobile-${match.id}.members`, + [...path, 'hasMember'], + `A Grove Mobile ${match.id} grouping requires at least one member Observation.`, + ) + } +} + +const validateComponentResult = ( + observation: UnknownRecord, + match: MeasurementDefinition, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + const components = + Array.isArray(observation.component) ? observation.component : [] + const matchedComponentIndexes = new Set() + for (const definition of match.components ?? []) { + const matchingIndexes = components.flatMap((component, index) => + ( + codingCount( + asRecord(component)?.code, + definition.system, + definition.code, + ) === 1 + ) ? + [index] + : [], + ) + const matchedIndex = matchingIndexes[0] + const matching = + matchedIndex === undefined ? undefined : ( + asRecord(components[matchedIndex]) + ) + const quantity = asRecord(matching?.valueQuantity) + const optional = definition.cardinality === '0..1' + if ( + matchingIndexes.length > 1 || + (!optional && matchingIndexes.length !== 1) || + (matchedIndex !== undefined && + matchedComponentIndexes.has(matchedIndex)) || + (matching !== undefined && + codingCountForSystem(matching.code, definition.system) !== 1) || + (matching !== undefined && + (typeof quantity?.value !== 'number' || + !Number.isFinite(quantity.value) || + quantity.comparator !== undefined || + quantity.system !== definition.quantity.system || + quantity.code !== definition.quantity.code || + quantity.unit !== definition.quantity.unit)) + ) { + addIssue( + context, + `mobile-${match.id}.${definition.id}`, + [...path, 'component'], + `A Grove Mobile ${match.id} panel admits at most one distinct finite ${definition.id} component in fixed UCUM ${definition.quantity.code}${optional ? '' : ' and requires it'}.`, + ) + } + if (matchedIndex !== undefined) matchedComponentIndexes.add(matchedIndex) + } +} + +const validateObservationResult = ( + observation: UnknownRecord, + match: MeasurementDefinition, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + switch (match.valueKind) { + case 'quantity': + validateQuantityResult(observation, match, context, path) + break + case 'codeableConcept': + validateCodeableConceptResult(observation, match, context, path) + break + case 'dateTime': + validateDateTimeResult(observation, match, context, path) + break + case 'grouping': + validateGroupingResult(observation, match, context, path) + break + case 'components': + validateComponentResult(observation, match, context, path) + break + } +} + +export const validateMobileObservationSemantics = ( + resource: unknown, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + const observation = asRecord(resource) + if (observation?.resourceType !== 'Observation') return + const match = admittedObservationProfile(asRecord(observation.meta)?.profile) + if ( + match === undefined || + !hasExactProviderObservationClaim(observation, match) + ) { + addIssue( + context, + 'mobile-output.semantic-profile', + [...path, 'meta', 'profile'], + 'Every active Observation must directly claim its exact admitted semantic/adapter profile set; connected-provider outputs require their exact provider-specific envelope and marker.', + ) + return + } + if (match === null) return + if (observation.status !== 'final') { + addIssue( + context, + `mobile-${match.id}.status`, + [...path, 'status'], + `A Grove Mobile ${match.id} Observation must be final.`, + ) + } + validateObservationCodes(observation, match, context, path) + validateObservationCategory(observation, match, context, path) + validateObservationEffective(observation, match, context, path) + validateObservationMethod(observation, match, context, path) + validateObservationResult(observation, match, context, path) +} diff --git a/packages/fhir/src/r4/base-schemas.ts b/packages/fhir/src/r4/base-schemas.ts new file mode 100644 index 00000000..74d27150 --- /dev/null +++ b/packages/fhir/src/r4/base-schemas.ts @@ -0,0 +1,131 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { z } from 'zod' +import type { + GraphResource, + R4CollectionBundle, + Reference, + SupportedR4Resource, +} from './types.js' +import { + attachmentSchema, + bundleSchema, + codeableConceptSchema, + codingSchema, + deviceSchema, + documentReferenceSchema, + elementSchema, + expressionSchema, + extensionSchema, + identifierSchema, + medicationAdministrationSchema, + medicationStatementSchema, + metaSchema, + observationComponentSchema, + observationSchema, + patientSchema, + periodSchema, + planDefinitionSchema, + provenanceSchema, + quantitySchema, + questionnaireResponseSchema, + referenceSchema as baseReferenceSchema, + researchStudySchema, + researchSubjectSchema, + sampledDataSchema, + specimenSchema, + visionPrescriptionSchema, +} from '../zod/r4/index.js' + +export { + attachmentSchema, + codeableConceptSchema, + codingSchema, + deviceSchema, + documentReferenceSchema, + expressionSchema, + extensionSchema, + identifierSchema, + metaSchema, + observationComponentSchema, + observationSchema, + periodSchema, + provenanceSchema, + quantitySchema, + sampledDataSchema, + specimenSchema, +} + +type UnknownRecord = Readonly> + +const asRecord = (value: unknown): UnknownRecord | undefined => + typeof value === 'object' && value !== null ? + (value as UnknownRecord) + : undefined + +const completeIdentifier = ( + value: unknown, +): value is { readonly system: string; readonly value: string } => { + const identifier = asRecord(value) + return ( + typeof identifier?.system === 'string' && + identifier.system !== '' && + typeof identifier.value === 'string' && + identifier.value !== '' + ) +} + +/** A nonblank literal pointer or a complete `(system, value)` business identifier. */ +export const resolvableReferenceSchema: z.ZodType = + baseReferenceSchema.refine( + (value) => + (typeof value.reference === 'string' && value.reference.trim() !== '') || + completeIdentifier(value.identifier), + { + message: + 'A Reference requires a nonblank reference or complete identifier.', + }, + ) + +export const referenceSchema: z.ZodType = resolvableReferenceSchema + +/** The Element a primitive carries its id and extensions in. */ +export const primitiveElementSchema: typeof elementSchema = elementSchema + +const graphResourceMembers = [ + observationSchema, + deviceSchema, + documentReferenceSchema, + provenanceSchema, + specimenSchema, + patientSchema, + researchStudySchema, + researchSubjectSchema, + planDefinitionSchema, + medicationAdministrationSchema, + medicationStatementSchema, + visionPrescriptionSchema, + questionnaireResponseSchema, +] as const + +/** The bounded resource surface Grove graph helpers expose. */ +export const graphResourceSchema: z.ZodType = + z.union(graphResourceMembers) + +/** Base-R4 collection parser: no Grove profile or graph promises are implied. */ +export const r4CollectionBundleSchema: z.ZodType = + bundleSchema + .refine((value) => value.type === 'collection', { + message: 'Expected a FHIR R4 collection Bundle.', + }) + .transform((value) => value as R4CollectionBundle) + +/** Any standalone bounded graph resource or base-R4 collection Bundle. */ +export const supportedR4ResourceSchema: z.ZodType = + z.union([r4CollectionBundleSchema, ...graphResourceMembers]) diff --git a/packages/fhir/src/r4/diagnostics.ts b/packages/fhir/src/r4/diagnostics.ts new file mode 100644 index 00000000..9c46c532 --- /dev/null +++ b/packages/fhir/src/r4/diagnostics.ts @@ -0,0 +1,167 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import type { Issue } from '../core/index.js' +import { groveExchangeRuleDiagnostics } from '../mobile/measurement-catalog.generated.js' + +const ENCODED_RULE_PREFIX = 'grove-producer-rule:' + +export type GroveExchangeRuleCode = keyof typeof groveExchangeRuleDiagnostics + +export interface GroveExchangeRuleDiagnostic { + readonly code: GroveExchangeRuleCode + readonly reason: string + readonly location: string + readonly severity: 'error' +} + +const entryIndex = ( + path: ReadonlyArray, +): number | undefined => { + const index = path.indexOf('entry') + const candidate = index === -1 ? undefined : path[index + 1] + return typeof candidate === 'number' ? candidate : undefined +} + +/** + * Stable FHIR-facing locations for producer rules. Array ordinals remain dynamic, + * while logical resource paths intentionally avoid leaking parser-internal paths. + */ +const groveRuleLocation = ( + code: GroveExchangeRuleCode, + path: ReadonlyArray, +): string => { + const index = entryIndex(path) + const bundleEntry = `Bundle.entry[${index ?? 0}]` + switch (code) { + case 'mobile-exchange.entry-node-key': + return bundleEntry + case 'mobile-exchange.deterministic-full-url': + return `${bundleEntry}.fullUrl` + case 'mobile-exchange.resolved-reference': + return `${bundleEntry}.resource.subject.reference` + case 'mobile-output.fixed-quantity-unit': + return `${bundleEntry}.resource.valueQuantity.code` + case 'mobile-exchange.event-identity': + return 'Bundle.identifier.value' + case 'mobile-exchange.entry-node-digest': + return `${bundleEntry}.extension.valueIdentifier.value` + case 'mobile-output.source-output-required': + return `${bundleEntry}.resource.identifier` + case 'mobile-exchange.identity-system-role': + return 'Bundle' + case 'mobile-exchange.transform-provenance': + return 'Bundle.entry' + case 'mobile-retraction.logical-target': + return `Provenance.target[${typeof path.at(-1) === 'number' ? String(path.at(-1)) : '0'}]` + case 'mobile-retraction.target-role': + return `Provenance.target[${typeof path.at(-2) === 'number' ? String(path.at(-2)) : '0'}].extension` + case 'mobile-retraction.opaque-target': + return `Provenance.target[${typeof path.at(-2) === 'number' ? String(path.at(-2)) : '0'}].identifier.value` + case 'mobile-retraction.no-clinical-copy': + return `${bundleEntry}.resource` + case 'mobile-exchange.lifecycle-coding': + return 'Provenance.activity.coding' + case 'mobile-output.semantic-profile': + return 'Observation.meta.profile' + case 'mobile-exchange.reference-target-type': + return 'Observation.subject.reference' + case 'mobile-exchange.reference-declared-type': + return 'Observation.subject.type' + case 'mobile-exchange.logical-source-entity': + return 'Provenance.entity[0].what' + case 'mobile-retraction.role-target-type': + return `Provenance.target[${typeof path.at(-2) === 'number' ? String(path.at(-2)) : '0'}].type` + case 'mobile-exchange.single-source-entity': + return 'Provenance.entity' + case 'mobile-exchange.reference-shape': + case 'mobile-exchange.logical-patient-reference': + return 'Observation.subject' + case 'mobile-exchange.entry-resource-type': + return `${bundleEntry}.resource.resourceType` + case 'mobile-output.adapter-only-profile': + return 'Specimen.meta.profile' + case 'mobile-exchange.contained-resource-prohibited': + return `${bundleEntry}.resource.contained` + case 'mobile-output.document-profile': + return 'DocumentReference.meta.profile' + case 'mobile-support.device-profile': + return 'Device.meta.profile' + case 'mobile-exchange.provenance-profile': + return 'Provenance.meta.profile' + case 'mobile-support.connected': + return 'Bundle.entry' + } +} + +const isGroveExchangeRuleCode = (code: string): code is GroveExchangeRuleCode => + Object.hasOwn(groveExchangeRuleDiagnostics, code) + +const groveRuleDiagnostic = ( + code: GroveExchangeRuleCode, + location: string, +): GroveExchangeRuleDiagnostic => ({ + code, + reason: groveExchangeRuleDiagnostics[code].reason, + location, + severity: groveExchangeRuleDiagnostics[code].severity, +}) + +export const encodeGroveRuleDiagnostic = ( + code: string, + path: ReadonlyArray, + fallbackMessage: string, + location?: string, +): string => + isGroveExchangeRuleCode(code) ? + `${ENCODED_RULE_PREFIX}${JSON.stringify(groveRuleDiagnostic(code, location ?? groveRuleLocation(code, path)))}` + : `${code}: ${fallbackMessage}` + +export const decodeGroveRuleDiagnostic = ( + message: string, +): GroveExchangeRuleDiagnostic | undefined => { + if (!message.startsWith(ENCODED_RULE_PREFIX)) return undefined + try { + const candidate = JSON.parse(message.slice(ENCODED_RULE_PREFIX.length)) as { + readonly code?: unknown + readonly reason?: unknown + readonly location?: unknown + readonly severity?: unknown + } + if ( + typeof candidate.code !== 'string' || + !isGroveExchangeRuleCode(candidate.code) || + candidate.reason !== + groveExchangeRuleDiagnostics[candidate.code].reason || + candidate.severity !== + groveExchangeRuleDiagnostics[candidate.code].severity || + typeof candidate.location !== 'string' || + candidate.location.length === 0 + ) { + return undefined + } + return candidate as GroveExchangeRuleDiagnostic + } catch { + return undefined + } +} + +export const groveRuleIssue = ( + code: GroveExchangeRuleCode, + path: ReadonlyArray, +): Issue => { + const diagnostic = groveRuleDiagnostic(code, groveRuleLocation(code, path)) + return { + severity: diagnostic.severity, + code: diagnostic.code, + path, + message: diagnostic.reason, + reason: diagnostic.reason, + location: diagnostic.location, + } +} diff --git a/packages/fhir/src/r4/exchange-envelope.ts b/packages/fhir/src/r4/exchange-envelope.ts new file mode 100644 index 00000000..daece461 --- /dev/null +++ b/packages/fhir/src/r4/exchange-envelope.ts @@ -0,0 +1,608 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/** Deterministic Bundle envelope, entry identity, and reference validation. */ + +import type { z } from 'zod' +import { + OPAQUE_IDENTIFIER_ROLES, + addIssue, + asRecord, + completeIdentifier, + entryKey, + fhirLocation, + groveIdentifierRoles, + identifierPairEqual, + identifierRole, + identifiersOf, + isLowercaseUuidV5, + locatedGroveIdentifiers, + locatedReferences, + parseEntryNodeParts, + referenceTypeMatches, + selectedBusinessKey, + type UnknownRecord, + validateProfileReferenceTargets, +} from './graph-schema-utils.js' +import { validateProfiledResource } from './profile-semantics.js' +import type { R4CollectionBundle } from './types.js' +import { parseAbsoluteUri, type AbsoluteUri } from '../core/index.js' +import { groveMobileContract } from '../mobile/contract.js' +import { + deriveEntryFullUrl, + deriveEntryNodeValue, + isEntryNodeIdentityValue, + isEventIdentityValue, + isOpaqueIdentityValue, +} from '../mobile/identity.js' + +type CompleteIdentifier = Readonly<{ system: string; value: string }> + +export interface ValidatedEnvelope { + readonly entries: readonly UnknownRecord[] + readonly event: CompleteIdentifier + readonly fullUrls: ReadonlySet + readonly resourcesByFullUrl: ReadonlyMap +} + +interface EnvelopeState { + readonly fullUrls: Set + readonly keyPairs: Set + readonly internalLogicalReferences: Set + readonly resourcesByFullUrl: Map +} + +interface ValidatedEntryKey { + readonly identifier: CompleteIdentifier + readonly role: string | undefined +} + +const ACTIVE_ENTRY_POLICY = + groveMobileContract.lifecycle.activeEntryResourcePolicy +const ACTIVE_ENTRY_TYPES: ReadonlySet = new Set([ + ...ACTIVE_ENTRY_POLICY.outputResourceTypes, + ...ACTIVE_ENTRY_POLICY.supportingResourceTypes, + ACTIVE_ENTRY_POLICY.lifecycleResourceType, +]) + +const identifierPairKey = (identifier: CompleteIdentifier): string => + `${identifier.system.length}:${identifier.system}${identifier.value.length}:${identifier.value}` + +const validateEnvelopeProfile = ( + bundle: R4CollectionBundle, + requiredProfile: string, + context: z.core.$RefinementCtx, +): void => { + const exchangeProfiles = new Set([ + groveMobileContract.profiles.exchangeBundle, + groveMobileContract.profiles.retractionBundle, + ]) + const claimed = (bundle.meta?.profile ?? []).filter( + (profile): profile is string => + typeof profile === 'string' && exchangeProfiles.has(profile), + ) + if (claimed.length !== 1 || claimed[0] !== requiredProfile) { + addIssue( + context, + 'mobile-exchange.profile', + ['meta', 'profile'], + `Bundle must declare ${requiredProfile}.`, + ) + } +} + +const validateIdentitySystemRoles = ( + bundle: R4CollectionBundle, + context: z.core.$RefinementCtx, +): void => { + const systemsByRole = new Map() + const rolesBySystem = new Map() + for (const located of locatedGroveIdentifiers(bundle)) { + const priorSystem = systemsByRole.get(located.role) + const priorRole = rolesBySystem.get(located.system) + if ( + (priorSystem !== undefined && priorSystem !== located.system) || + (priorRole !== undefined && priorRole !== located.role) + ) { + addIssue( + context, + 'mobile-exchange.identity-system-role', + located.path, + 'Within one event, each Grove Identifier role must use one dedicated Identifier.system and a system must not serve multiple Grove roles.', + ) + continue + } + systemsByRole.set(located.role, located.system) + rolesBySystem.set(located.system, located.role) + } +} + +const validateEntryTransport = ( + entry: UnknownRecord, + index: number, + state: EnvelopeState, + context: z.core.$RefinementCtx, +): string | undefined => { + if ( + entry.search !== undefined || + entry.request !== undefined || + entry.response !== undefined + ) { + addIssue( + context, + 'mobile-exchange.collection-entry', + ['entry', index], + 'Exchange collection entries prohibit search, request, and response metadata.', + ) + } + if (typeof entry.fullUrl !== 'string' || !isLowercaseUuidV5(entry.fullUrl)) { + addIssue( + context, + 'mobile-exchange.deterministic-full-url', + ['entry', index, 'fullUrl'], + 'entry.fullUrl must be a lowercase RFC 4122 UUID-v5 URN.', + ) + return undefined + } + if (state.fullUrls.has(entry.fullUrl)) { + addIssue( + context, + 'mobile-exchange.distinct-full-url', + ['entry', index, 'fullUrl'], + 'Bundle entry fullUrls must be unique.', + ) + } + state.fullUrls.add(entry.fullUrl) + return entry.fullUrl +} + +const validateEntryNodeDigest = ( + key: CompleteIdentifier, + event: CompleteIdentifier, + index: number, + context: z.core.$RefinementCtx, +): void => { + const parts = parseEntryNodeParts(key.value) + const expected = + parts === null ? undefined : ( + deriveEntryNodeValue( + event.system, + event.value, + parts[1] ?? '', + parts[2] ?? '', + ) + ) + if ( + !isEntryNodeIdentityValue(key.value) || + !expected?.ok || + expected.value !== key.value + ) { + addIssue( + context, + 'mobile-exchange.entry-node-digest', + ['entry', index, 'extension'], + 'An entry-node digest must be derived from this event, role, and ordinal.', + ) + } +} + +const validateEntryKeyValue = ( + key: CompleteIdentifier, + role: string | undefined, + event: CompleteIdentifier, + index: number, + context: z.core.$RefinementCtx, +): void => { + if (role === undefined) { + addIssue( + context, + 'mobile-exchange.entry-key-role', + ['entry', index, 'extension'], + 'The entry key requires one Grove Identifier role.', + ) + } else if (role === 'entry-node') { + validateEntryNodeDigest(key, event, index, context) + } else if ( + !OPAQUE_IDENTIFIER_ROLES.has(role) || + !isOpaqueIdentityValue(key.value) + ) { + addIssue( + context, + 'mobile-exchange.opaque-entry-key', + ['entry', index, 'extension'], + 'Business entry keys require a closed role and canonical v2 HMAC value.', + ) + } +} + +const validateEntryKey = ( + entry: UnknownRecord, + event: CompleteIdentifier, + index: number, + state: EnvelopeState, + context: z.core.$RefinementCtx, +): ValidatedEntryKey | undefined => { + const key = entryKey(entry) + if (!completeIdentifier(key)) { + addIssue( + context, + 'mobile-exchange.entry-node-key', + ['entry', index, 'extension'], + 'Every entry requires exactly one complete Grove entry-node-key Identifier.', + ) + return undefined + } + const pair = identifierPairKey(key) + if (state.keyPairs.has(pair)) { + addIssue( + context, + 'mobile-exchange.distinct-entry-key', + ['entry', index, 'extension'], + 'Entry node-key Identifier pairs must be distinct.', + ) + } + state.keyPairs.add(pair) + const role = identifierRole(key) + validateEntryKeyValue(key, role, event, index, context) + return { identifier: key, role } +} + +const validateResourceShape = ( + resource: UnknownRecord, + active: boolean, + index: number, + state: EnvelopeState, + context: z.core.$RefinementCtx, +): void => { + const resourceType = resource.resourceType + if ( + active && + (typeof resourceType !== 'string' || !ACTIVE_ENTRY_TYPES.has(resourceType)) + ) { + addIssue( + context, + 'mobile-exchange.entry-resource-type', + ['entry', index, 'resource', 'resourceType'], + 'An active event admits only its catalog-closed output, supporting, and lifecycle resource types.', + ) + } + if ('contained' in resource) { + addIssue( + context, + 'mobile-exchange.contained-resource-prohibited', + ['entry', index, 'resource', 'contained'], + 'Mobile exchange event graphs prohibit contained resources.', + ) + } + if (typeof resourceType === 'string' && typeof resource.id === 'string') { + state.internalLogicalReferences.add(`${resourceType}/${resource.id}`) + } +} + +const resourceIdentityRoleCounts = ( + resource: unknown, + index: number, + context: z.core.$RefinementCtx, +): ReadonlyMap => { + const roleCounts = new Map() + for (const candidate of identifiersOf(resource)) { + const groveRoles = groveIdentifierRoles(candidate) + const role = identifierRole(candidate) + if (groveRoles.length === 0) continue + if (role === undefined || !OPAQUE_IDENTIFIER_ROLES.has(role)) { + addIssue( + context, + 'mobile-exchange.identifier-role', + ['entry', index, 'resource', 'identifier'], + 'A Grove-typed resource Identifier requires exactly one closed opaque identity role.', + ) + continue + } + roleCounts.set(role, (roleCounts.get(role) ?? 0) + 1) + if ( + !completeIdentifier(candidate) || + !parseAbsoluteUri(candidate.system).ok || + !isOpaqueIdentityValue(candidate.value) + ) { + addIssue( + context, + 'mobile-exchange.opaque-resource-identity', + ['entry', index, 'resource', 'identifier'], + 'A typed resource identity must be one complete canonical v2 HMAC Identifier.', + ) + } + } + if ([...roleCounts.values()].some((count) => count > 1)) { + addIssue( + context, + 'mobile-exchange.distinct-resource-identity-role', + ['entry', index, 'resource', 'identifier'], + 'A resource may carry at most one Identifier for each Grove identity role.', + ) + } + return roleCounts +} + +const validateSelectedEntryKey = ( + resource: unknown, + key: CompleteIdentifier, + role: string | undefined, + index: number, + context: z.core.$RefinementCtx, +): void => { + const selected = selectedBusinessKey(resource) + if ( + (selected === undefined && role !== 'entry-node') || + (selected !== undefined && !identifierPairEqual(selected, key)) + ) { + addIssue( + context, + 'mobile-exchange.entry-key-selection', + ['entry', index, 'extension'], + 'The entry key must be the highest-priority typed business Identifier, or entry-node when none exists.', + ) + } +} + +const validateProvenanceEntryNode = ( + resource: UnknownRecord, + key: CompleteIdentifier, + role: string | undefined, + requiredProfile: string, + index: number, + context: z.core.$RefinementCtx, +): void => { + if (resource.resourceType !== 'Provenance' || role !== 'entry-node') return + const parts = parseEntryNodeParts(key.value) + const expectedRole = + requiredProfile === groveMobileContract.profiles.retractionBundle ? + 'retraction-provenance' + : 'conversion-provenance' + if (parts?.[1] !== expectedRole || parts[2] !== '0') { + addIssue( + context, + 'mobile-exchange.provenance-node-key', + ['entry', index, 'extension'], + `Provenance requires the ${expectedRole} entry-node at ordinal zero.`, + ) + } +} + +const validateEntryResource = ( + entry: UnknownRecord, + fullUrl: string, + key: ValidatedEntryKey, + requiredProfile: string, + active: boolean, + index: number, + state: EnvelopeState, + context: z.core.$RefinementCtx, +): void => { + if (entry.resource === undefined) { + addIssue( + context, + 'mobile-exchange.resource-required', + ['entry', index, 'resource'], + 'Every exchange entry requires a resource.', + ) + return + } + const resource = asRecord(entry.resource) ?? {} + validateResourceShape(resource, active, index, state, context) + state.resourcesByFullUrl.set(fullUrl, resource) + const roleCounts = resourceIdentityRoleCounts(entry.resource, index, context) + validateProfiledResource(entry.resource, roleCounts, context, [ + 'entry', + index, + 'resource', + ]) + validateSelectedEntryKey( + entry.resource, + key.identifier, + key.role, + index, + context, + ) + validateProvenanceEntryNode( + resource, + key.identifier, + key.role, + requiredProfile, + index, + context, + ) +} + +const validateDerivedFullUrl = ( + fullUrl: string, + key: CompleteIdentifier, + index: number, + context: z.core.$RefinementCtx, +): void => { + const derived = deriveEntryFullUrl({ + system: key.system as AbsoluteUri, + value: key.value, + }) + if (!derived.ok || derived.value !== fullUrl) { + addIssue( + context, + 'mobile-exchange.deterministic-full-url', + ['entry', index, 'fullUrl'], + 'entry.fullUrl must be UUID-v5 over the length-framed entry key pair.', + ) + } +} + +const validateEntry = ( + entry: UnknownRecord, + event: CompleteIdentifier, + requiredProfile: string, + active: boolean, + index: number, + state: EnvelopeState, + context: z.core.$RefinementCtx, +): void => { + const fullUrl = validateEntryTransport(entry, index, state, context) + if (fullUrl === undefined) return + const key = validateEntryKey(entry, event, index, state, context) + if (key === undefined) return + validateEntryResource( + entry, + fullUrl, + key, + requiredProfile, + active, + index, + state, + context, + ) + validateDerivedFullUrl(fullUrl, key.identifier, index, context) +} + +const validateLocatedReference = ( + located: ReturnType[number], + resource: UnknownRecord | undefined, + entryIndex: number, + state: EnvelopeState, + context: z.core.$RefinementCtx, +): void => { + const bundleLocation = fhirLocation('Bundle', [ + 'entry', + entryIndex, + 'resource', + ...located.path, + ]) + const resourceLocation = fhirLocation( + typeof resource?.resourceType === 'string' ? + resource.resourceType + : 'Resource', + located.path, + ) + if (!state.fullUrls.has(located.reference)) { + addIssue( + context, + 'mobile-exchange.resolved-reference', + ['entry', entryIndex, 'resource', ...located.path], + `Literal reference ${located.reference} does not resolve to one Bundle entry fullUrl.`, + `${bundleLocation}.reference`, + ) + } else { + const targetType = state.resourcesByFullUrl.get( + located.reference, + )?.resourceType + if ( + located.type !== undefined && + (typeof targetType !== 'string' || + !referenceTypeMatches(located.type, targetType)) + ) { + addIssue( + context, + 'mobile-exchange.reference-declared-type', + ['entry', entryIndex, 'resource', ...located.path, 'type'], + 'Reference.type must agree with the resolved internal resource type.', + `${resourceLocation}.type`, + ) + } + } + if (state.internalLogicalReferences.has(located.reference)) { + addIssue( + context, + 'mobile-exchange.internal-reference-full-url', + ['entry', entryIndex, 'resource', ...located.path], + `Reference ${located.reference} targets a Bundle entry and must use that entry's UUID fullUrl.`, + ) + } +} + +const validateEnvelopeReferences = ( + entries: readonly UnknownRecord[], + state: EnvelopeState, + context: z.core.$RefinementCtx, +): void => { + for (const [index, entry] of entries.entries()) { + const resource = asRecord(entry.resource) + for (const located of locatedReferences(entry.resource)) { + validateLocatedReference(located, resource, index, state, context) + } + if (resource !== undefined) { + validateProfileReferenceTargets( + resource, + state.resourcesByFullUrl, + context, + ['entry', index, 'resource'], + ) + } + } +} + +export const validateExchangeEnvelope = ( + bundle: R4CollectionBundle, + context: z.core.$RefinementCtx, + requiredProfile: string, +): ValidatedEnvelope | undefined => { + validateEnvelopeProfile(bundle, requiredProfile, context) + if ( + !completeIdentifier(bundle.identifier) || + !parseAbsoluteUri(bundle.identifier.system).ok || + identifierRole(bundle.identifier) !== 'event' || + !isEventIdentityValue(bundle.identifier.value) + ) { + addIssue( + context, + 'mobile-exchange.event-identity', + ['identifier'], + 'Bundle.identifier must be one complete typed canonical e2 event Identifier.', + ) + return undefined + } + if (bundle.timestamp === undefined) { + addIssue( + context, + 'mobile-exchange.assembled-time', + ['timestamp'], + 'Bundle.timestamp is the mandatory graph assembly time.', + ) + } + if (bundle.entry === undefined || bundle.entry.length === 0) { + addIssue( + context, + 'mobile-exchange.entry-required', + ['entry'], + 'An exchange event requires at least one entry.', + ) + return undefined + } + + validateIdentitySystemRoles(bundle, context) + const entries = bundle.entry.map((entry) => entry as UnknownRecord) + const state: EnvelopeState = { + fullUrls: new Set(), + keyPairs: new Set(), + internalLogicalReferences: new Set(), + resourcesByFullUrl: new Map(), + } + const active = requiredProfile === groveMobileContract.profiles.exchangeBundle + for (const [index, entry] of entries.entries()) { + validateEntry( + entry, + bundle.identifier, + requiredProfile, + active, + index, + state, + context, + ) + } + validateEnvelopeReferences(entries, state, context) + return { + entries, + event: bundle.identifier, + fullUrls: state.fullUrls, + resourcesByFullUrl: state.resourcesByFullUrl, + } +} diff --git a/packages/fhir/src/r4/graph-schema-utils.ts b/packages/fhir/src/r4/graph-schema-utils.ts new file mode 100644 index 00000000..c975f2b0 --- /dev/null +++ b/packages/fhir/src/r4/graph-schema-utils.ts @@ -0,0 +1,436 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/** FHIR graph identity, reference, coding, and diagnostic utilities. */ + +/* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 fixes lifecycle and participant canonicals to HTTP. */ + +import type { z } from 'zod' +import { encodeGroveRuleDiagnostic } from './diagnostics.js' +import { parseAbsoluteUri } from '../core/index.js' +import { groveMobileContract } from '../mobile/contract.js' +import { healthKitApplicationDeviceIdentity } from '../providers/contract.generated.js' + +export type UnknownRecord = Readonly> + +export const asRecord = (value: unknown): UnknownRecord | undefined => + typeof value === 'object' && value !== null ? + (value as UnknownRecord) + : undefined + +export const completeIdentifier = ( + value: unknown, +): value is { readonly system: string; readonly value: string } => { + const identifier = asRecord(value) + return ( + typeof identifier?.system === 'string' && + identifier.system !== '' && + typeof identifier.value === 'string' && + identifier.value !== '' + ) +} + +const LOWERCASE_UUID_V5 = + /^urn:uuid:[\da-f]{8}-[\da-f]{4}-5[\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}$/u +const ENTRY_NODE_PARTS = /^n2:([a-z][a-z\d-]*):(0|[1-9]\d*):[A-Za-z\d_-]{43}$/u + +export const isLowercaseUuidV5 = (value: string): boolean => + LOWERCASE_UUID_V5.test(value) + +export const parseEntryNodeParts = (value: string): RegExpExecArray | null => + ENTRY_NODE_PARTS.exec(value) +export const ISO_LIFECYCLE_SYSTEM = + 'http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle' +export const PROVENANCE_PARTICIPANT_SYSTEM = + 'http://terminology.hl7.org/CodeSystem/provenance-participant-type' + +export const APPLICATION_DEVICE_PROFILES: ReadonlySet = new Set([ + groveMobileContract.profiles.applicationDevice, + healthKitApplicationDeviceIdentity.profile, +]) + +const ENTRY_KEY_PRIORITY: readonly string[] = + groveMobileContract.identity.resourceIdentifierPriority + +export const OPAQUE_IDENTIFIER_ROLES: ReadonlySet = new Set( + groveMobileContract.identity.opaqueIdentifierRoles, +) + +export const groveIdentifierRoles = (value: unknown): readonly string[] => { + const identifier = asRecord(value) + const type = asRecord(identifier?.type) + const coding = Array.isArray(type?.coding) ? type.coding : [] + return coding.flatMap((candidate) => { + const item = asRecord(candidate) + return ( + item?.system === groveMobileContract.systems.identifierRole && + typeof item.code === 'string' + ) ? + [item.code] + : [] + }) +} + +export const identifierRole = (value: unknown): string | undefined => { + const groveRoles = groveIdentifierRoles(value) + return groveRoles.length === 1 ? groveRoles[0] : undefined +} + +export const identifiersOf = (resource: unknown): readonly unknown[] => { + const value = asRecord(resource)?.identifier + if (Array.isArray(value)) return value + return value === undefined ? [] : [value] +} + +export const identifierWithRole = (resource: unknown, role: string): unknown => + identifiersOf(resource).find( + (candidate) => identifierRole(candidate) === role, + ) + +export const identifierPairEqual = (left: unknown, right: unknown): boolean => + completeIdentifier(left) && + completeIdentifier(right) && + left.system === right.system && + left.value === right.value + +export const selectedBusinessKey = (resource: unknown): unknown => { + for (const role of ENTRY_KEY_PRIORITY) { + const selected = identifierWithRole(resource, role) + if (selected !== undefined) return selected + } + return undefined +} + +export const entryKey = (entry: unknown): unknown => { + const extension = asRecord(entry)?.extension + if (!Array.isArray(extension)) return undefined + const matches = extension.filter( + (candidate) => + asRecord(candidate)?.url === groveMobileContract.extensions.entryNodeKey, + ) + return matches.length === 1 ? + asRecord(matches[0])?.valueIdentifier + : undefined +} + +export interface LocatedReference { + readonly reference: string + readonly type?: unknown + readonly path: ReadonlyArray +} + +export const fhirLocation = ( + root: string, + path: ReadonlyArray, +): string => + path.reduce( + (location, component) => + typeof component === 'number' ? + `${location}[${String(component)}]` + : `${location}.${component}`, + root, + ) + +export interface LocatedGroveIdentifier { + readonly role: string + readonly system: string + readonly path: ReadonlyArray +} + +export const locatedGroveIdentifiers = ( + value: unknown, + path: ReadonlyArray = [], + visited: WeakSet = new WeakSet(), +): readonly LocatedGroveIdentifier[] => { + const record = asRecord(value) + if (record === undefined || visited.has(record)) return [] + visited.add(record) + + const roles = groveIdentifierRoles(record) + const located = + roles.length === 1 && typeof record.system === 'string' ? + [{ role: roles[0] ?? '', system: record.system, path }] + : [] + return [ + ...located, + ...Object.entries(record).flatMap(([key, nested]) => + Array.isArray(nested) ? + nested.flatMap((candidate, index) => + locatedGroveIdentifiers(candidate, [...path, key, index], visited), + ) + : locatedGroveIdentifiers(nested, [...path, key], visited), + ), + ] +} + +const REFERENCE_PROPERTY_NAMES: ReadonlySet = new Set([ + 'id', + 'extension', + 'reference', + '_reference', + 'type', + '_type', + 'identifier', + 'display', + '_display', +]) + +const isReferenceObject = (value: UnknownRecord): boolean => + Object.keys(value).every((key) => REFERENCE_PROPERTY_NAMES.has(key)) + +export const locatedReferences = ( + value: unknown, + path: ReadonlyArray = [], + visited: WeakSet = new WeakSet(), +): readonly LocatedReference[] => { + if (typeof value !== 'object' || value === null || visited.has(value)) { + return [] + } + visited.add(value) + if (Array.isArray(value)) { + return value.flatMap((item, index) => + locatedReferences(item, [...path, index], visited), + ) + } + const record = asRecord(value) + if (record === undefined) return [] + return [ + ...(typeof record.reference === 'string' && isReferenceObject(record) ? + [{ reference: record.reference, type: record.type, path }] + : []), + ...Object.entries(record) + .filter(([key]) => key !== 'reference') + .flatMap(([key, nested]) => + locatedReferences(nested, [...path, key], visited), + ), + ] +} + +export const referenceTypeMatches = ( + declared: unknown, + actual: string, +): boolean => declared === actual + +const internalReferenceTarget = ( + reference: unknown, + resourcesByFullUrl: ReadonlyMap, +): UnknownRecord | undefined => { + const literal = asRecord(reference)?.reference + if (typeof literal !== 'string') return undefined + return resourcesByFullUrl.get(literal) +} + +const validateInternalTarget = ( + reference: unknown, + allowedTypes: ReadonlySet, + resourcesByFullUrl: ReadonlyMap, + context: z.core.$RefinementCtx, + path: ReadonlyArray, + location: string, +): void => { + const target = internalReferenceTarget(reference, resourcesByFullUrl) + if (target === undefined) return + if ( + typeof target.resourceType !== 'string' || + !allowedTypes.has(target.resourceType) + ) { + addIssue( + context, + 'mobile-exchange.reference-target-type', + path, + `Internal reference must resolve to ${[...allowedTypes].join(' or ')}.`, + `${location}.reference`, + ) + } +} + +const validateGovernedReferenceShape = ( + reference: unknown, + allowedTypes: ReadonlySet, + context: z.core.$RefinementCtx, + path: ReadonlyArray, + location: string, +): void => { + const value = asRecord(reference) + const hasLiteral = + typeof value?.reference === 'string' && value.reference.trim() !== '' + const hasIdentifier = value?.identifier !== undefined + const validLogical = + !hasLiteral && + completeIdentifier(value?.identifier) && + parseAbsoluteUri(value.identifier.system).ok && + typeof value.type === 'string' && + allowedTypes.has(value.type) + if (hasLiteral && hasIdentifier) { + addIssue( + context, + 'mobile-exchange.reference-shape', + path, + 'A governed Reference must be exclusively a resolving literal or a typed identifier-only logical reference with one complete absolute-system Identifier.', + location, + ) + } else if (!hasLiteral && hasIdentifier && !validLogical) { + addIssue( + context, + allowedTypes.size === 1 && allowedTypes.has('Patient') ? + 'mobile-exchange.logical-patient-reference' + : 'mobile-exchange.reference-shape', + path, + 'An identifier-only logical Reference requires its exact admitted type and one complete absolute-system Identifier.', + location, + ) + } else if (!hasLiteral && !validLogical) { + addIssue( + context, + 'mobile-exchange.reference-shape', + path, + 'A governed Reference requires exactly one resolving literal or identifier-only logical shape.', + location, + ) + } +} + +const validateProfilePathReferenceTargets = ( + resource: UnknownRecord, + resourcesByFullUrl: ReadonlyMap, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + for (const rule of groveMobileContract.referencePolicy.paths) { + if (rule.resourceType !== resource.resourceType) continue + const references = resource[rule.path] + const values = Array.isArray(references) ? references : [references] + for (const [index, reference] of values.entries()) { + if (reference === undefined) continue + const targetTypes = new Set(rule.targetTypes) + const referencePath = [ + ...path, + rule.path, + ...(Array.isArray(references) ? [index] : []), + ] + const referenceIndex = + Array.isArray(references) ? `[${String(index)}]` : '' + const referenceLocation = `${String(resource.resourceType)}.${rule.path}${referenceIndex}` + validateGovernedReferenceShape( + reference, + targetTypes, + context, + referencePath, + referenceLocation, + ) + validateInternalTarget( + reference, + targetTypes, + resourcesByFullUrl, + context, + referencePath, + referenceLocation, + ) + } + } +} + +const validateProfileExtensionReferenceTargets = ( + resource: UnknownRecord, + resourcesByFullUrl: ReadonlyMap, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + const extensions = Array.isArray(resource.extension) ? resource.extension : [] + for (const [index, extension] of extensions.entries()) { + const item = asRecord(extension) + const rule = groveMobileContract.referencePolicy.extensionTargets.find( + ({ url }) => url === item?.url, + ) + if (rule !== undefined && item !== undefined) { + const targetTypes = new Set(rule.targetTypes) + const referencePath = [...path, 'extension', index, 'valueReference'] + const referenceLocation = `${String(resource.resourceType)}.extension[${String(index)}].valueReference` + validateGovernedReferenceShape( + item.valueReference, + targetTypes, + context, + referencePath, + referenceLocation, + ) + validateInternalTarget( + item.valueReference, + targetTypes, + resourcesByFullUrl, + context, + referencePath, + referenceLocation, + ) + } + } +} + +export const validateProfileReferenceTargets = ( + resource: UnknownRecord, + resourcesByFullUrl: ReadonlyMap, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + validateProfilePathReferenceTargets( + resource, + resourcesByFullUrl, + context, + path, + ) + validateProfileExtensionReferenceTargets( + resource, + resourcesByFullUrl, + context, + path, + ) +} + +export const codingExists = ( + concept: unknown, + system: string, + code: string, +): boolean => codingCount(concept, system, code) > 0 + +export const codingCount = ( + concept: unknown, + system: string, + code: string, +): number => { + const coding = asRecord(concept)?.coding + return Array.isArray(coding) ? + coding.filter((candidate) => { + const item = asRecord(candidate) + return item?.system === system && item.code === code + }).length + : 0 +} + +export const codingCountForSystem = ( + concept: unknown, + system: string, +): number => { + const coding = asRecord(concept)?.coding + return Array.isArray(coding) ? + coding.filter((candidate) => asRecord(candidate)?.system === system) + .length + : 0 +} + +export const addIssue = ( + context: z.core.$RefinementCtx, + code: string, + path: ReadonlyArray, + message: string, + location?: string, +): void => { + context.addIssue({ + code: 'custom', + path: [...path], + message: encodeGroveRuleDiagnostic(code, path, message, location), + }) +} diff --git a/packages/fhir/src/r4/index.ts b/packages/fhir/src/r4/index.ts index 553f5668..d759bcfb 100644 --- a/packages/fhir/src/r4/index.ts +++ b/packages/fhir/src/r4/index.ts @@ -7,7 +7,9 @@ // export { - parseCollectionBundle, + parseGroveMobileExchangeBundle, + parseGroveMobileRetractionBundle, + parseR4CollectionBundle, parseDevice, parseDocumentReference, parseObservation, @@ -19,12 +21,13 @@ export { attachmentSchema, codeableConceptSchema, codingSchema, - collectionBundleSchema, deviceSchema, documentReferenceSchema, expressionSchema, extensionSchema, graphResourceSchema, + groveMobileExchangeBundleSchema, + groveMobileRetractionBundleSchema, identifierSchema, metaSchema, observationComponentSchema, @@ -35,6 +38,7 @@ export { quantitySchema, referenceSchema, resolvableReferenceSchema, + r4CollectionBundleSchema, sampledDataSchema, specimenSchema, supportedR4ResourceSchema, @@ -43,15 +47,17 @@ export type { Attachment, CodeableConcept, Coding, - CollectionBundle, Device, DocumentReference, Extension, GraphResource, + GroveMobileExchangeBundle, + GroveMobileRetractionBundle, Identifier, Observation, Period, Provenance, + R4CollectionBundle, Questionnaire, QuestionnaireResponse, Quantity, @@ -60,3 +66,4 @@ export type { Specimen, SupportedR4Resource, } from './types.js' +export type { FhirJson } from './json.js' diff --git a/packages/fhir/src/r4/json.ts b/packages/fhir/src/r4/json.ts new file mode 100644 index 00000000..f7a16e25 --- /dev/null +++ b/packages/fhir/src/r4/json.ts @@ -0,0 +1,51 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +type JsonPrimitive = boolean | number | string + +type Nullish = + | (undefined extends Value ? undefined : never) + | (null extends Value ? null : never) + +type FhirJsonProperty = + [NonNullable] extends [ReadonlyArray] ? + | (Key extends `_${string}` ? Array | null> + : [Element] extends [JsonPrimitive] ? Array + : Array>) + | Nullish + : FhirJson + +type PrimitiveShadowedKey = { + [Key in keyof Value]-?: Key extends string ? + `_${Key}` extends keyof Value ? + Key + : never + : never +}[keyof Value] + +type FhirJsonMapped = { + [Key in keyof Value]: FhirJsonProperty +} + +type FhirJsonObject = Omit< + FhirJsonMapped, + PrimitiveShadowedKey +> & + // A required FHIR primitive element can exist through `_field` metadata alone, so the JSON + // value property is not independently required even when a declaration package models it so. + Partial, PrimitiveShadowedKey>> + +/** + * Corrects declaration packages that omit legal null placeholders from repeating FHIR JSON + * primitive value and `_shadow` arrays, and permits a primitive value to be omitted when its + * `_shadow` Element carries the element, while preserving every other published property shape. + */ +export type FhirJson = + Value extends JsonPrimitive | null | undefined ? Value + : Value extends object ? FhirJsonObject + : Value diff --git a/packages/fhir/src/r4/parse.ts b/packages/fhir/src/r4/parse.ts index d36f4dc9..4e57f167 100644 --- a/packages/fhir/src/r4/parse.ts +++ b/packages/fhir/src/r4/parse.ts @@ -7,8 +7,18 @@ // import { type z } from 'zod' +import { decodeGroveRuleDiagnostic, groveRuleIssue } from './diagnostics.js' import { - collectionBundleSchema, + groveMobileExchangeBundleSchema, + groveMobileRetractionBundleSchema, + hasAdmittedActiveDeviceProfile, + hasAdmittedActiveDocumentReferenceProfile, + hasAdmittedActiveProvenanceProfile, + hasAdmittedAdapterOnlyOutputProfile, + hasAdmittedMobileObservationProfile, + hasProhibitedContainedResource, + isAdmittedActiveEntryResource, + r4CollectionBundleSchema, deviceSchema, documentReferenceSchema, observationSchema, @@ -17,42 +27,95 @@ import { supportedR4ResourceSchema, } from './schemas.js' import type { - CollectionBundle, Device, DocumentReference, Observation, Provenance, Specimen, SupportedR4Resource, + GroveMobileExchangeBundle, + GroveMobileRetractionBundle, + R4CollectionBundle, } from './types.js' import { + cloneJsonValue, deepFreeze, + err, issues, + type IssueCode, ok, type Issue, type Result, } from '../core/index.js' -const normalizeIssue = (entry: z.core.$ZodIssue): Issue => ({ - severity: 'error', - code: 'schema-invalid', - path: entry.path.map((component) => +const LOCAL_MOBILE_RULE = /^(mobile-[a-z\d.-]+): (.+)$/su + +const issuePath = (entry: z.core.$ZodIssue): ReadonlyArray => + entry.path.map((component) => typeof component === 'symbol' ? (component.description ?? component.toString()) : component, - ), - message: entry.message, -}) + ) + +const normalizeIssue = (entry: z.core.$ZodIssue): Issue => { + const path = issuePath(entry) + const diagnostic = decodeGroveRuleDiagnostic(entry.message) + if (diagnostic !== undefined) { + return { + severity: diagnostic.severity, + code: diagnostic.code, + path, + message: diagnostic.reason, + reason: diagnostic.reason, + location: diagnostic.location, + } + } + const localRule = LOCAL_MOBILE_RULE.exec(entry.message) + if (localRule !== null) { + return { + severity: 'error', + code: localRule[1] as IssueCode, + path, + message: entry.message, + } + } + return { + severity: 'error', + code: 'schema-invalid', + path, + message: entry.message, + } +} // The schema decides the parsed type; a caller-chosen T would make the cast below a lie. -const parseWith = (schema: z.ZodType, input: unknown): Result => { - const result = schema.safeParse(input) - if (!result.success) { - return issues(result.error.issues.map(normalizeIssue)) +const parseSnapshotWith = ( + schema: z.ZodType, + snapshot: unknown, +): Result => { + try { + const result = schema.safeParse(snapshot) + if (!result.success) { + return issues(result.error.issues.map(normalizeIssue)) + } + return ok(deepFreeze(result.data) as T) + } catch { + return err( + 'schema-invalid', + 'FHIR JSON validation could not safely inspect the supplied value.', + ) } - return ok(deepFreeze(result.data) as T) } +const parseWith = (schema: z.ZodType, input: unknown): Result => { + const snapshot = cloneJsonValue(input) + return snapshot.ok ? parseSnapshotWith(schema, snapshot.value) : snapshot +} + +const entryResource = (entry: unknown): unknown => + typeof entry === 'object' && entry !== null && 'resource' in entry ? + (entry as { readonly resource?: unknown }).resource + : undefined + export const parseObservation = (input: unknown): Result => parseWith(observationSchema, input) @@ -69,9 +132,218 @@ export const parseProvenance = (input: unknown): Result => export const parseSpecimen = (input: unknown): Result => parseWith(specimenSchema, input) -export const parseCollectionBundle = ( +export const parseR4CollectionBundle = ( + input: unknown, +): Result => parseWith(r4CollectionBundleSchema, input) + +export const parseGroveMobileExchangeBundle = ( input: unknown, -): Result => parseWith(collectionBundleSchema, input) +): Result => { + const snapshot = cloneJsonValue(input) + if (!snapshot.ok) return snapshot + input = snapshot.value + const entries = + typeof input === 'object' && input !== null && 'entry' in input ? + (input as { readonly entry?: unknown }).entry + : undefined + if (Array.isArray(entries)) { + const invalidResourceTypeIndex = entries.findIndex( + (entry) => !isAdmittedActiveEntryResource(entryResource(entry)), + ) + if (invalidResourceTypeIndex !== -1) { + return issues([ + groveRuleIssue('mobile-exchange.entry-resource-type', [ + 'entry', + invalidResourceTypeIndex, + 'resource', + 'resourceType', + ]), + ]) + } + const containedIndex = entries.findIndex((entry) => + hasProhibitedContainedResource(entryResource(entry)), + ) + if (containedIndex !== -1) { + return issues([ + groveRuleIssue('mobile-exchange.contained-resource-prohibited', [ + 'entry', + containedIndex, + 'resource', + 'contained', + ]), + ]) + } + const invalidIndex = entries.findIndex((entry) => { + if ( + typeof entry !== 'object' || + entry === null || + !('resource' in entry) + ) { + return false + } + const resource = (entry as { readonly resource?: unknown }).resource + return ( + typeof resource === 'object' && + resource !== null && + 'resourceType' in resource && + (resource as { readonly resourceType?: unknown }).resourceType === + 'Observation' && + !hasAdmittedMobileObservationProfile(resource) + ) + }) + if (invalidIndex !== -1) { + return issues([ + groveRuleIssue('mobile-output.semantic-profile', [ + 'entry', + invalidIndex, + 'resource', + 'meta', + 'profile', + ]), + ]) + } + const invalidAdapterOnlyIndex = entries.findIndex((entry) => { + if ( + typeof entry !== 'object' || + entry === null || + !('resource' in entry) + ) { + return false + } + return !hasAdmittedAdapterOnlyOutputProfile( + (entry as { readonly resource?: unknown }).resource, + ) + }) + if (invalidAdapterOnlyIndex !== -1) { + return issues([ + groveRuleIssue('mobile-output.adapter-only-profile', [ + 'entry', + invalidAdapterOnlyIndex, + 'resource', + 'meta', + 'profile', + ]), + ]) + } + const invalidDocumentIndex = entries.findIndex((entry) => { + const resource = entryResource(entry) + return ( + typeof resource === 'object' && + resource !== null && + 'resourceType' in resource && + (resource as { readonly resourceType?: unknown }).resourceType === + 'DocumentReference' && + !hasAdmittedActiveDocumentReferenceProfile(resource) + ) + }) + if (invalidDocumentIndex !== -1) { + return issues([ + groveRuleIssue('mobile-output.document-profile', [ + 'entry', + invalidDocumentIndex, + 'resource', + 'meta', + 'profile', + ]), + ]) + } + const invalidDeviceIndex = entries.findIndex((entry) => { + const resource = entryResource(entry) + return ( + typeof resource === 'object' && + resource !== null && + 'resourceType' in resource && + (resource as { readonly resourceType?: unknown }).resourceType === + 'Device' && + !hasAdmittedActiveDeviceProfile(resource) + ) + }) + if (invalidDeviceIndex !== -1) { + return issues([ + groveRuleIssue('mobile-support.device-profile', [ + 'entry', + invalidDeviceIndex, + 'resource', + 'meta', + 'profile', + ]), + ]) + } + const invalidProvenanceIndex = entries.findIndex((entry) => { + const resource = entryResource(entry) + return ( + typeof resource === 'object' && + resource !== null && + 'resourceType' in resource && + (resource as { readonly resourceType?: unknown }).resourceType === + 'Provenance' && + !hasAdmittedActiveProvenanceProfile(resource) + ) + }) + if (invalidProvenanceIndex !== -1) { + return issues([ + groveRuleIssue('mobile-exchange.provenance-profile', [ + 'entry', + invalidProvenanceIndex, + 'resource', + 'meta', + 'profile', + ]), + ]) + } + } + return parseSnapshotWith(groveMobileExchangeBundleSchema, input) +} + +export const parseGroveMobileRetractionBundle = ( + input: unknown, +): Result => { + const snapshot = cloneJsonValue(input) + if (!snapshot.ok) return snapshot + input = snapshot.value + const entries = + typeof input === 'object' && input !== null && 'entry' in input ? + (input as { readonly entry?: unknown }).entry + : undefined + if (Array.isArray(entries)) { + const invalidIndex = entries.findIndex((entry) => { + const resource = entryResource(entry) + return ( + typeof resource === 'object' && + resource !== null && + 'resourceType' in resource && + !['Device', 'Provenance'].includes( + String( + (resource as { readonly resourceType?: unknown }).resourceType, + ), + ) + ) + }) + if (invalidIndex !== -1) { + return issues([ + groveRuleIssue('mobile-retraction.no-clinical-copy', [ + 'entry', + invalidIndex, + 'resource', + ]), + ]) + } + const containedIndex = entries.findIndex((entry) => + hasProhibitedContainedResource(entryResource(entry)), + ) + if (containedIndex !== -1) { + return issues([ + groveRuleIssue('mobile-exchange.contained-resource-prohibited', [ + 'entry', + containedIndex, + 'resource', + 'contained', + ]), + ]) + } + } + return parseSnapshotWith(groveMobileRetractionBundleSchema, input) +} export const parseSupportedR4Resource = ( input: unknown, diff --git a/packages/fhir/src/r4/profile-semantics.ts b/packages/fhir/src/r4/profile-semantics.ts new file mode 100644 index 00000000..03800a3b --- /dev/null +++ b/packages/fhir/src/r4/profile-semantics.ts @@ -0,0 +1,517 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/** Exact profile claims and adapter-specific resource semantics. */ + +import { sha1 } from '@noble/hashes/legacy.js' +import type { z } from 'zod' +import { + addIssue, + asRecord, + codingCount, + codingCountForSystem, + identifiersOf, + type UnknownRecord, +} from './graph-schema-utils.js' +import { groveMobileContract } from '../mobile/contract.js' +import { + adapterSourceMarkerClaims, + groveProfileClaims, +} from '../mobile/measurement-catalog.generated.js' +import { + healthKitApplicationDeviceIdentity, + healthKitClinicalRecordAdmission, +} from '../providers/contract.generated.js' + +const SHA1_BASE64 = /^[A-Za-z\d+/]{26}[AEIMQUYcgkosw048]=$/u + +const decodeFhirBase64 = (value: unknown): Uint8Array | undefined => { + if (typeof value !== 'string') return undefined + const packed = value.replaceAll(/[ \t\n\r]/gu, '') + if (packed.length === 0 || packed.length % 4 !== 0) return undefined + try { + const decoded = atob(packed) + const bytes = new Uint8Array(decoded.length) + for (let index = 0; index < decoded.length; index += 1) { + bytes[index] = decoded.charCodeAt(index) + } + return bytes + } catch { + return undefined + } +} + +const bytesEqual = (left: Uint8Array, right: Uint8Array): boolean => + left.length === right.length && + left.every((value, index) => value === right[index]) + +interface ExactProfileClaim { + readonly cardinality: number + readonly profiles: readonly string[] + readonly requiredIdentifierRoles?: readonly string[] +} + +const DOCUMENT_REFERENCE_PROFILE_CLAIMS: readonly ExactProfileClaim[] = [ + groveProfileClaims.sensorRecordingDocumentClaim, + groveProfileClaims.healthKitRecordingDocumentClaim, + groveProfileClaims.healthKitClinicalRecordDocumentClaim, + groveProfileClaims.providerRecordingDocumentClaim, + groveProfileClaims.sensorKitRecordingDocumentClaim, +] + +const ADAPTER_ONLY_OUTPUT_PROFILE_CLAIMS = [ + groveProfileClaims.healthConnectSpecimenClaim, + ...groveProfileClaims.healthKitPlatformExclusiveResourceClaims, +] as const +const ADAPTER_ONLY_OUTPUT_TYPES: ReadonlySet = new Set( + groveMobileContract.lifecycle.adapterOnlyOutputProfileClaims.resourceTypes, +) + +/** Whether an adapter-only output declares its exact catalog-owned single profile. */ +export const hasAdmittedAdapterOnlyOutputProfile = ( + resource: unknown, +): boolean => { + const record = asRecord(resource) + if ( + typeof record?.resourceType !== 'string' || + !ADAPTER_ONLY_OUTPUT_TYPES.has(record.resourceType) + ) { + return true + } + const claim = ADAPTER_ONLY_OUTPUT_PROFILE_CLAIMS.find( + ({ resourceType }) => resourceType === record.resourceType, + ) + const expectedProfile = claim?.profile + const profiles = asRecord(record.meta)?.profile + return ( + typeof expectedProfile === 'string' && + Array.isArray(profiles) && + profiles.length === 1 && + profiles[0] === expectedProfile + ) +} + +const exactProfileClaim = ( + resource: UnknownRecord, + claims: readonly ExactProfileClaim[], +): ExactProfileClaim | undefined => { + const profiles = asRecord(resource.meta)?.profile + if (!Array.isArray(profiles)) { + return undefined + } + const directProfiles = profiles.filter( + (profile): profile is string => typeof profile === 'string', + ) + if ( + directProfiles.length !== profiles.length || + new Set(directProfiles).size !== directProfiles.length + ) { + return undefined + } + const matching = claims.filter((claim) => { + const expected = new Set(claim.profiles) + return ( + directProfiles.length === claim.cardinality && + directProfiles.length === expected.size && + directProfiles.every((profile) => expected.has(profile)) + ) + }) + if (matching.length !== 1) return undefined + const claim = matching[0] + if (claim === undefined) return undefined + return claim +} + +/** Whether an active DocumentReference declares one exact catalog-admitted direct profile mode. */ +export const hasAdmittedActiveDocumentReferenceProfile = ( + resource: unknown, +): boolean => { + const record = asRecord(resource) + return ( + record?.resourceType === 'DocumentReference' && + exactProfileClaim(record, DOCUMENT_REFERENCE_PROFILE_CLAIMS) !== undefined + ) +} + +/** Whether an active Device declares one exact catalog-admitted direct profile mode. */ +export const hasAdmittedActiveDeviceProfile = (resource: unknown): boolean => { + const record = asRecord(resource) + return ( + record?.resourceType === 'Device' && + exactProfileClaim(record, groveProfileClaims.activeDeviceClaims) !== + undefined + ) +} + +const validateProfiledDevice = ( + resource: UnknownRecord, + roleCounts: ReadonlyMap, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +) => { + const claim = exactProfileClaim( + resource, + groveProfileClaims.activeDeviceClaims, + ) + if (claim === undefined) { + addIssue( + context, + 'mobile-support.device-profile', + [...path, 'meta', 'profile'], + 'An active Device must declare exactly one catalog-admitted direct profile mode.', + ) + return + } + const requiredRoles = new Set(claim.requiredIdentifierRoles ?? []) + if ( + [...requiredRoles].some((role) => roleCounts.get(role) !== 1) || + [...roleCounts].some(([role]) => !requiredRoles.has(role)) + ) { + addIssue( + context, + 'mobile-support.device-profile', + [...path, 'identifier'], + 'An active Device must carry exactly the typed identities required by its admitted profile mode.', + ) + return + } + const profile = claim.profiles[0] + if ( + profile === groveMobileContract.profiles.recordingDevice && + (roleCounts.get('recording-device') !== 1 || + identifiersOf(resource).length !== 2) + ) { + addIssue( + context, + 'mobile-recording-device.identities', + [...path, 'identifier'], + 'A recording Device requires exactly one stable recording-device identity and one event snapshot identity.', + ) + } + if ( + profile === groveMobileContract.profiles.hostDevice && + identifiersOf(resource).length !== 1 + ) { + addIssue( + context, + 'mobile-host-device.identity', + [...path, 'identifier'], + 'A host Device carries exactly one event snapshot identity.', + ) + } + if (profile === healthKitApplicationDeviceIdentity.profile) { + const definition = healthKitApplicationDeviceIdentity.bundleIdentifier + const bundleIdentifiers = identifiersOf(resource).filter( + (candidate) => + codingCount( + asRecord(candidate)?.type, + definition.typeSystem, + definition.typeCode, + ) === 1, + ) + const bundleIdentifier = asRecord(bundleIdentifiers[0]) + const bundleIdentifierValue = bundleIdentifier?.value + if ( + bundleIdentifiers.length !== 1 || + codingCountForSystem(bundleIdentifier?.type, definition.typeSystem) !== + 1 || + bundleIdentifier?.system !== definition.system || + typeof bundleIdentifierValue !== 'string' || + bundleIdentifierValue.trim() === '' + ) { + addIssue( + context, + 'healthkit-application-device.bundle-identifier', + [...path, 'identifier'], + 'A HealthKit application Device requires exactly one typed Apple bundle product identifier.', + ) + } + } +} + +const validateRecordingDocument = ( + resource: UnknownRecord, + roleCounts: ReadonlyMap, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +) => { + const claim = exactProfileClaim(resource, DOCUMENT_REFERENCE_PROFILE_CLAIMS) + if (claim === undefined) { + addIssue( + context, + 'mobile-output.document-profile', + [...path, 'meta', 'profile'], + 'A source-preservation DocumentReference must declare exactly one catalog-admitted direct profile set.', + ) + } + if (claim === groveProfileClaims.healthKitClinicalRecordDocumentClaim) { + const definition = healthKitClinicalRecordAdmission.fhirRepresentation + const extensions = + Array.isArray(resource.extension) ? resource.extension : [] + const matching = extensions.filter( + (extension) => asRecord(extension)?.url === definition.extensionUrl, + ) + const representedRelease = asRecord(matching[0]) + const valueElements = Object.keys(representedRelease ?? {}).filter((key) => + /^value[A-Z]/u.test(key), + ) + if ( + resource.resourceType !== definition.resourceType || + matching.length < definition.cardinality.min || + matching.length > definition.cardinality.max || + representedRelease?.[definition.valueElement] !== definition.fixedValue || + valueElements.length !== 1 || + valueElements[0] !== definition.valueElement + ) { + addIssue( + context, + 'healthkit-clinical-record.fhir-release', + [...path, 'extension'], + 'A HealthKit clinical-record document must carry exactly the catalog-admitted FHIR release representation.', + ) + } + } + const requiredRoles: ReadonlySet = new Set( + claim?.requiredIdentifierRoles ?? [], + ) + const missingRoles = [...requiredRoles].filter( + (role) => roleCounts.get(role) !== 1, + ) + if (claim !== undefined && missingRoles.length > 0) { + addIssue( + context, + 'mobile-recording-document.identities', + [...path, 'identifier'], + 'A source-preservation DocumentReference requires every identity role named by its exact profile claim.', + ) + } + const admittedRoles = new Set([...requiredRoles, 'writer-record']) + if ( + (roleCounts.get('writer-record') ?? 0) > 1 || + (claim !== undefined && + [...roleCounts].some(([role]) => !admittedRoles.has(role))) + ) { + addIssue( + context, + 'mobile-recording-document.identity-roles', + [...path, 'identifier'], + 'A recording DocumentReference admits only its three required identities and at most one writer-record identity.', + ) + } + if ( + resource.type === undefined || + resource.subject === undefined || + typeof resource.date !== 'string' + ) { + addIssue( + context, + 'mobile-recording-document.required-metadata', + path, + 'A recording DocumentReference requires type, Patient subject, and document creation date.', + ) + } + const content = Array.isArray(resource.content) ? resource.content : [] + const item = asRecord(content[0]) + const attachment = asRecord(item?.attachment) + const format = asRecord(item?.format) + const hasData = attachment?.data !== undefined + const hasUrl = attachment?.url !== undefined + if ( + content.length !== 1 || + attachment === undefined || + hasData === hasUrl || + typeof attachment.contentType !== 'string' || + attachment.contentType === '' || + !Number.isInteger(attachment.size) || + Number(attachment.size) < 0 || + Number(attachment.size) > 2_147_483_647 || + typeof attachment.hash !== 'string' || + !SHA1_BASE64.test(attachment.hash) + ) { + addIssue( + context, + 'mobile-recording-document.attachment', + [...path, 'content'], + 'A recording has one attachment with exactly one payload location, media type, R4-sized byte count, and canonical SHA-1 hash.', + ) + } + if (typeof attachment?.data === 'string') { + const payload = decodeFhirBase64(attachment.data) + const digest = decodeFhirBase64(attachment.hash) + if ( + payload === undefined || + digest === undefined || + payload.length !== attachment.size || + !bytesEqual(sha1(payload), digest) + ) { + addIssue( + context, + 'mobile-recording-document.embedded-integrity', + [...path, 'content', 0, 'attachment'], + 'Embedded recording size and SHA-1 hash must match the decoded attachment bytes.', + ) + } + } + const formats = groveMobileContract.recordingFormats.formats as Readonly< + Record + > + const definition = + typeof format?.code === 'string' ? formats[format.code] : undefined + if ( + format?.system !== groveMobileContract.recordingFormats.codeSystem || + format.version !== groveMobileContract.recordingFormats.version || + definition?.status !== 'active' || + definition.contentType !== attachment?.contentType + ) { + addIssue( + context, + 'mobile-recording-document.format', + [...path, 'content', 0, 'format'], + 'Recording format, release, and content type must match the active Grove registry entry.', + ) + } +} + +const validateProfiledQuestionnaireResponse = ( + resource: UnknownRecord, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +) => { + if ( + exactProfileClaim(resource, [ + groveProfileClaims.activeQuestionnaireResponseClaim, + ]) === undefined + ) { + addIssue( + context, + 'mobile-support.questionnaire-response-profile', + [...path, 'meta', 'profile'], + 'An active QuestionnaireResponse must directly declare exactly the admitted Grove QuestionnaireResponse profile.', + ) + } +} + +type AdapterSourceMarker = + (typeof adapterSourceMarkerClaims)[number]['markers'][number] + +const adapterSourceMarkerCandidates = ( + resource: UnknownRecord, + marker: AdapterSourceMarker, +): readonly unknown[] => + (Array.isArray(resource.extension) ? resource.extension : []).filter( + (extension) => asRecord(extension)?.url === marker.url, + ) + +const hasCompleteAdapterSourceMarker = ( + candidates: readonly unknown[], +): boolean => + candidates.every((candidate) => { + const item = asRecord(candidate) + return typeof item?.valueCode === 'string' && item.valueCode.trim() !== '' + }) + +const validateAdapterSourceMarkers = ( + resource: UnknownRecord, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +) => { + const profiles = asRecord(resource.meta)?.profile + const directProfiles = new Set( + Array.isArray(profiles) ? + profiles.filter( + (profile): profile is string => typeof profile === 'string', + ) + : [], + ) + for (const claim of adapterSourceMarkerClaims) { + const claimsAdapterProfile = claim.profiles.some((profile) => + directProfiles.has(profile), + ) + for (const marker of claim.markers) { + if ( + typeof resource.resourceType !== 'string' || + !(marker.resourceTypes as readonly string[]).includes( + resource.resourceType, + ) + ) { + continue + } + const candidates = adapterSourceMarkerCandidates(resource, marker) + const complete = hasCompleteAdapterSourceMarker(candidates) + if ( + (claimsAdapterProfile && (candidates.length !== 1 || !complete)) || + (!claimsAdapterProfile && candidates.length !== 0) + ) { + addIssue( + context, + 'mobile-exchange.adapter-source-marker', + path, + `The ${claim.adapter} source marker must appear exactly once on its catalog-owned adapter output and never on a source-neutral or different-adapter output.`, + ) + } + } + } +} + +const validateAdapterOnlyOutputProfile = ( + resource: UnknownRecord, + roleCounts: ReadonlyMap, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +) => { + if ( + typeof resource.resourceType !== 'string' || + !ADAPTER_ONLY_OUTPUT_TYPES.has(resource.resourceType) + ) { + return + } + const matchingClaims = ADAPTER_ONLY_OUTPUT_PROFILE_CLAIMS.filter( + ({ resourceType }) => resourceType === resource.resourceType, + ) + const claim = matchingClaims[0] + const exactProfile = hasAdmittedAdapterOnlyOutputProfile(resource) + const requiredRoles: ReadonlySet = new Set( + claim?.requiredIdentifierRoles ?? [], + ) + const invalidRoles = + [...requiredRoles].some((role) => roleCounts.get(role) !== 1) || + [...roleCounts].some( + ([role]) => !requiredRoles.has(role) && role !== 'writer-record', + ) + const closedSpecimenIdentifiers = + resource.resourceType === 'Specimen' && + identifiersOf(resource).length !== requiredRoles.size + if (!exactProfile || invalidRoles || closedSpecimenIdentifiers) { + addIssue( + context, + 'mobile-output.adapter-only-profile', + [...path, 'meta', 'profile'], + 'This active output resource type is admitted only under its exact one-profile adapter claim and required typed identities.', + ) + } +} + +export const validateProfiledResource = ( + resource: unknown, + roleCounts: ReadonlyMap, + context: z.core.$RefinementCtx, + path: ReadonlyArray, +): void => { + const record = asRecord(resource) + if (record !== undefined) { + validateAdapterSourceMarkers(record, context, path) + validateAdapterOnlyOutputProfile(record, roleCounts, context, path) + } + if (record?.resourceType === 'Device') { + validateProfiledDevice(record, roleCounts, context, path) + } else if (record?.resourceType === 'DocumentReference') { + validateRecordingDocument(record, roleCounts, context, path) + } else if (record?.resourceType === 'QuestionnaireResponse') { + validateProfiledQuestionnaireResponse(record, context, path) + } +} diff --git a/packages/fhir/src/r4/retraction-graph-semantics.ts b/packages/fhir/src/r4/retraction-graph-semantics.ts new file mode 100644 index 00000000..7930889a --- /dev/null +++ b/packages/fhir/src/r4/retraction-graph-semantics.ts @@ -0,0 +1,339 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/** Append-only retraction graph and logical-target semantics. */ + +import type { z } from 'zod' +import { + validateAssemblerAgent, + validateExchangeEnvelope, +} from './active-graph-semantics.js' +import { + ISO_LIFECYCLE_SYSTEM, + addIssue, + asRecord, + codingCount, + codingCountForSystem, + codingExists, + completeIdentifier, + identifierRole, + type UnknownRecord, +} from './graph-schema-utils.js' +import type { R4CollectionBundle } from './types.js' +import { parseAbsoluteUri } from '../core/index.js' +import { groveMobileContract } from '../mobile/contract.js' +import { isOpaqueIdentityValue } from '../mobile/identity.js' + +const retractionRoleFor = (reference: unknown): string | undefined => { + const extensions = asRecord(reference)?.extension + if (!Array.isArray(extensions)) return undefined + const matches = extensions.filter( + (extension) => + asRecord(extension)?.url === + groveMobileContract.extensions.retractionTargetRole, + ) + const code = asRecord(matches[0])?.valueCode + return matches.length === 1 && typeof code === 'string' ? code : undefined +} + +const RETRACTION_TARGET_RULES = groveMobileContract.lifecycle.retractionTargets +const RETRACTION_ROLES: ReadonlySet = new Set( + Object.keys(RETRACTION_TARGET_RULES), +) + +const validateRetractionResourceSet = ( + resources: readonly unknown[], + context: z.core.$RefinementCtx, +): void => { + const containsClinicalCopy = resources.some( + (resource) => + !['Device', 'Provenance'].includes( + String(asRecord(resource)?.resourceType), + ), + ) + if (containsClinicalCopy) { + addIssue( + context, + 'mobile-retraction.no-clinical-copy', + ['entry'], + 'Retraction Bundles contain Provenance and optional Device agents only.', + ) + } +} + +const selectRetractionProvenance = ( + resources: readonly unknown[], + context: z.core.$RefinementCtx, +): UnknownRecord | undefined => { + const provenances = resources.filter( + (resource) => asRecord(resource)?.resourceType === 'Provenance', + ) + const retractions = provenances.filter((resource) => + codingExists( + asRecord(resource)?.activity, + groveMobileContract.systems.lifecycleEvent, + groveMobileContract.lifecycle.sourceRecordRetracted, + ), + ) + if (provenances.length !== 1 || retractions.length !== 1) { + addIssue( + context, + 'mobile-retraction.provenance', + ['entry'], + 'A retraction event requires exactly one retraction Provenance and no transform Provenance.', + ) + return undefined + } + return asRecord(retractions[0]) +} + +const validateRetractionProvenanceHeader = ( + provenance: UnknownRecord, + envelope: import('./exchange-envelope.js').ValidatedEnvelope, + context: z.core.$RefinementCtx, +): void => { + const profiles = asRecord(provenance.meta)?.profile + if ( + !Array.isArray(profiles) || + profiles.length !== 1 || + profiles[0] !== groveMobileContract.profiles.retractionProvenance + ) { + addIssue( + context, + 'mobile-retraction.provenance-profile', + ['entry'], + 'Retraction Provenance must declare the Grove Mobile retraction profile.', + ) + } + if ( + codingCount( + provenance.activity, + groveMobileContract.systems.lifecycleEvent, + groveMobileContract.lifecycle.sourceRecordRetracted, + ) !== 1 || + codingCountForSystem( + provenance.activity, + groveMobileContract.systems.lifecycleEvent, + ) !== 1 || + codingCountForSystem(provenance.activity, ISO_LIFECYCLE_SYSTEM) !== 0 + ) { + addIssue( + context, + 'mobile-exchange.lifecycle-coding', + ['entry'], + 'Retraction Provenance requires exactly one source-record-retracted coding from the Grove lifecycle system and no ISO transform lifecycle coding.', + ) + } + validateAssemblerAgent(provenance, envelope, context, ['entry']) +} + +const validateRetractionTargetShape = ( + reference: UnknownRecord | undefined, + targetIdentifier: unknown, + index: number, + context: z.core.$RefinementCtx, +): void => { + if ( + reference?.reference !== undefined || + typeof reference?.type !== 'string' || + reference.type === '' || + !completeIdentifier(targetIdentifier) + ) { + addIssue( + context, + 'mobile-retraction.logical-target', + ['entry', 'resource', 'target', index], + 'A retraction target must be a typed logical Reference with no literal reference.', + ) + } +} + +const validateRetractionTargetIdentity = ( + targetIdentifier: unknown, + expectedRole: string | undefined, + index: number, + context: z.core.$RefinementCtx, +): void => { + if ( + !completeIdentifier(targetIdentifier) || + !parseAbsoluteUri(targetIdentifier.system).ok || + identifierRole(targetIdentifier) !== expectedRole || + !isOpaqueIdentityValue(targetIdentifier.value) + ) { + addIssue( + context, + 'mobile-retraction.opaque-target', + ['entry', 'resource', 'target', index, 'identifier'], + 'Each target requires the exact typed canonical v2 HMAC Identifier previously emitted.', + ) + } +} + +const recordDistinctTarget = ( + targetIdentifier: unknown, + index: number, + keys: Set, + context: z.core.$RefinementCtx, +): void => { + if (!completeIdentifier(targetIdentifier)) return + const pair = `${targetIdentifier.system.length}:${targetIdentifier.system}${targetIdentifier.value.length}:${targetIdentifier.value}` + if (keys.has(pair)) { + addIssue( + context, + 'mobile-retraction.distinct-target', + ['entry', 'resource', 'target', index, 'identifier'], + 'Retraction target Identifier pairs must be unique.', + ) + } + keys.add(pair) +} + +const validateRetractionTargetType = ( + reference: UnknownRecord | undefined, + role: string | undefined, + targetRule: + | (typeof RETRACTION_TARGET_RULES)[keyof typeof RETRACTION_TARGET_RULES] + | undefined, + index: number, + context: z.core.$RefinementCtx, +): void => { + if ( + targetRule !== undefined && + (typeof reference?.type !== 'string' || + !(targetRule.resourceTypes as readonly string[]).includes(reference.type)) + ) { + addIssue( + context, + 'mobile-retraction.role-target-type', + ['entry', 'resource', 'target', index, 'type'], + `${String(role)} targets must declare one of the admitted Reference.type values: ${(targetRule.resourceTypes as readonly string[]).join(', ')}.`, + ) + } +} + +const validateRetractionTarget = ( + target: unknown, + index: number, + keys: Set, + context: z.core.$RefinementCtx, +): void => { + const reference = asRecord(target) + const role = retractionRoleFor(reference) + const targetRule = + role !== undefined && Object.hasOwn(RETRACTION_TARGET_RULES, role) ? + RETRACTION_TARGET_RULES[role as keyof typeof RETRACTION_TARGET_RULES] + : undefined + const identifier = reference?.identifier + validateRetractionTargetShape(reference, identifier, index, context) + if (role === undefined || !RETRACTION_ROLES.has(role)) { + addIssue( + context, + 'mobile-retraction.target-role', + ['entry', 'resource', 'target', index, 'extension'], + 'Each target requires exactly one closed Grove target role.', + ) + } + validateRetractionTargetIdentity( + identifier, + targetRule?.identifierRole, + index, + context, + ) + recordDistinctTarget(identifier, index, keys, context) + validateRetractionTargetType(reference, role, targetRule, index, context) +} + +const validateRetractionTargets = ( + provenance: UnknownRecord, + context: z.core.$RefinementCtx, +): void => { + const targets = Array.isArray(provenance.target) ? provenance.target : [] + if (targets.length === 0) { + addIssue( + context, + 'mobile-retraction.target-required', + ['entry'], + 'Retraction Provenance requires at least one exact logical target.', + ) + } + const keys = new Set() + for (const [index, target] of targets.entries()) { + validateRetractionTarget(target, index, keys, context) + } +} + +const validateRetractionSource = ( + provenance: UnknownRecord, + context: z.core.$RefinementCtx, +): void => { + const entities = Array.isArray(provenance.entity) ? provenance.entity : [] + const sourceEntity = asRecord(entities[0]) + const sourceWhat = asRecord(sourceEntity?.what) + const source = sourceWhat?.identifier + if (entities.length !== 1) { + addIssue( + context, + 'mobile-exchange.single-source-entity', + ['entry'], + 'Retraction Provenance must carry exactly one source entity.', + ) + } + if ( + sourceEntity?.role !== 'source' || + sourceWhat?.reference !== undefined || + !completeIdentifier(source) || + !parseAbsoluteUri(source.system).ok || + identifierRole(source) !== 'source-record' || + !isOpaqueIdentityValue(source.value) + ) { + addIssue( + context, + 'mobile-exchange.logical-source-entity', + ['entry'], + 'Retraction Provenance source must be a logical typed opaque source-record Identifier with no literal reference.', + ) + } +} + +const validateRetractionTimes = ( + provenance: UnknownRecord, + context: z.core.$RefinementCtx, +): void => { + if ( + (provenance.occurredDateTime === undefined && + provenance.occurredPeriod === undefined) || + provenance.recorded === undefined + ) { + addIssue( + context, + 'mobile-retraction.times', + ['entry'], + 'Retraction Provenance requires distinct occurred[x] and recorded fields.', + ) + } +} + +export const refineRetractionBundle = ( + bundle: R4CollectionBundle, + context: z.core.$RefinementCtx, +): void => { + const envelope = validateExchangeEnvelope( + bundle, + context, + groveMobileContract.profiles.retractionBundle, + ) + if (envelope === undefined) return + const resources = envelope.entries.map(({ resource }) => resource) + validateRetractionResourceSet(resources, context) + const provenance = selectRetractionProvenance(resources, context) + if (provenance === undefined) return + validateRetractionProvenanceHeader(provenance, envelope, context) + validateRetractionTargets(provenance, context) + validateRetractionSource(provenance, context) + validateRetractionTimes(provenance, context) +} diff --git a/packages/fhir/src/r4/schemas.ts b/packages/fhir/src/r4/schemas.ts index daf7912f..79c27bb3 100644 --- a/packages/fhir/src/r4/schemas.ts +++ b/packages/fhir/src/r4/schemas.ts @@ -6,151 +6,38 @@ // SPDX-License-Identifier: MIT // -import type { Reference as R4Reference } from 'fhir/r4.js' -import { z } from 'zod' +/** Stable public schema composition; domain refinements live in focused modules. */ + +import type { z } from 'zod' +import { refineActiveBundle } from './active-graph-semantics.js' +import { r4CollectionBundleSchema } from './base-schemas.js' +import { refineRetractionBundle } from './retraction-graph-semantics.js' import type { - CollectionBundle, - GraphResource, - SupportedR4Resource, + GroveMobileExchangeBundle, + GroveMobileRetractionBundle, } from './types.js' -import { - attachmentSchema, - codeableConceptSchema, - codingSchema, - deviceSchema, - documentReferenceSchema, - elementSchema, - expressionSchema, - extensionSchema, - identifierSchema, - instantSchema, - medicationAdministrationSchema, - medicationStatementSchema, - metaSchema, - observationComponentSchema, - observationSchema, - patientSchema, - periodSchema, - planDefinitionSchema, - provenanceSchema, - quantitySchema, - questionnaireResponseSchema, - questionnaireSchema, - referenceSchema as baseReferenceSchema, - researchStudySchema, - researchSubjectSchema, - sampledDataSchema, - specimenSchema, - visionPrescriptionSchema, -} from '../zod/r4/index.js' -// The FHIR R4 model is generated from the release's own StructureDefinitions; only Grove's -// narrowings are written here. Anything hand-written that merely restates the specification -// would be a second source of truth for it. +export * from './base-schemas.js' export { - attachmentSchema, - codeableConceptSchema, - codingSchema, - deviceSchema, - documentReferenceSchema, - expressionSchema, - extensionSchema, - identifierSchema, - metaSchema, - observationComponentSchema, - observationSchema, - periodSchema, - provenanceSchema, - quantitySchema, - sampledDataSchema, - specimenSchema, -} - -/** - * A reference Grove can resolve. - * - * FHIR admits a Reference carrying only a display string. An exchange graph cannot follow one, so - * Grove requires an actual pointer: a literal reference or a business identifier. - */ -export const resolvableReferenceSchema: z.ZodType = - baseReferenceSchema.refine( - (value) => { - const reference = value as { reference?: unknown; identifier?: unknown } - return ( - reference.reference !== undefined || reference.identifier !== undefined - ) - }, - { message: 'A Reference requires reference or identifier.' }, - ) - -/** - * The reference every Grove resource uses. - * - * Named separately from the generated FHIR one, which the `/zod/r4` entry point also exports as - * `referenceSchema`: they are different objects with different semantics, and a consumer - * reaching for the wrong one would get no error. - */ -export const referenceSchema: z.ZodType = resolvableReferenceSchema - -/** The Element a primitive carries its id and extensions in. */ -export const primitiveElementSchema: typeof elementSchema = elementSchema - -/** The resources a Grove exchange graph may carry. */ -const graphResourceMembers = [ - observationSchema, - deviceSchema, - documentReferenceSchema, - provenanceSchema, - specimenSchema, - patientSchema, - researchStudySchema, - researchSubjectSchema, - planDefinitionSchema, - medicationAdministrationSchema, - medicationStatementSchema, - visionPrescriptionSchema, - questionnaireSchema, - questionnaireResponseSchema, -] as const - -export const graphResourceSchema: z.ZodType = - z.union(graphResourceMembers) - -/** - * A Grove exchange bundle. - * - * Narrower than a FHIR Bundle in three ways the exchange contract requires: the type is always - * `collection`, every entry carries a `urn:uuid:` full URL, and an entry's resource is one of the - * graph resources rather than any resource at all. - */ -export const collectionBundleSchema: z.ZodType = - z.strictObject({ - resourceType: z.literal('Bundle'), - id: z.string().optional(), - meta: metaSchema.optional(), - implicitRules: z.string().optional(), - language: z.string().optional(), - identifier: identifierSchema.optional(), - type: z.literal('collection'), - timestamp: instantSchema.optional(), - entry: z - .array( - z.strictObject({ - id: z.string().optional(), - extension: z.array(extensionSchema).optional(), - modifierExtension: z.array(extensionSchema).optional(), - fullUrl: z.string().regex( - // Any UUID version, either case: RFC 4122 is case-insensitive on input and the - // version nibble keeps growing. Grove mints lowercase v4 itself, but this also - // validates bundles it receives, and rejecting a conformant v7 would be wrong. - /^urn:uuid:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/u, - ), - resource: graphResourceSchema, - }), - ) - .optional(), - }) as unknown as z.ZodType - -/** Any resource the package parses on its own. */ -export const supportedR4ResourceSchema: z.ZodType = - z.union(graphResourceMembers) + hasAdmittedAdapterOnlyOutputProfile, + hasAdmittedActiveDeviceProfile, + hasAdmittedActiveDocumentReferenceProfile, +} from './profile-semantics.js' +export { + hasAdmittedActiveProvenanceProfile, + hasAdmittedMobileObservationProfile, + hasProhibitedContainedResource, + isAdmittedActiveEntryResource, +} from './active-graph-semantics.js' + +/** Profile-aware parser for one immutable active Grove exchange event. */ +export const groveMobileExchangeBundleSchema: z.ZodType = + r4CollectionBundleSchema.superRefine( + refineActiveBundle, + ) as unknown as z.ZodType + +/** Profile-aware parser for one append-only Grove source retraction assertion. */ +export const groveMobileRetractionBundleSchema: z.ZodType = + r4CollectionBundleSchema.superRefine( + refineRetractionBundle, + ) as unknown as z.ZodType diff --git a/packages/fhir/src/r4/types.ts b/packages/fhir/src/r4/types.ts index 527163dc..e36fd893 100644 --- a/packages/fhir/src/r4/types.ts +++ b/packages/fhir/src/r4/types.ts @@ -32,31 +32,36 @@ import type { Specimen as R4Specimen, VisionPrescription as R4VisionPrescription, } from 'fhir/r4.js' +import type { FhirJson } from './json.js' import type { ReadonlyDeep } from '../core/index.js' -export type Attachment = ReadonlyDeep -export type Coding = ReadonlyDeep -export type CodeableConcept = ReadonlyDeep -export type Device = ReadonlyDeep -export type DocumentReference = ReadonlyDeep -export type Extension = ReadonlyDeep -export type Identifier = ReadonlyDeep -export type MedicationAdministration = ReadonlyDeep -export type MedicationStatement = ReadonlyDeep -export type Observation = ReadonlyDeep -export type Patient = ReadonlyDeep -export type Period = ReadonlyDeep -export type PlanDefinition = ReadonlyDeep -export type Provenance = ReadonlyDeep -export type Questionnaire = ReadonlyDeep -export type QuestionnaireResponse = ReadonlyDeep -export type Quantity = ReadonlyDeep -export type Reference = ReadonlyDeep -export type SampledData = ReadonlyDeep -export type ResearchStudy = ReadonlyDeep -export type ResearchSubject = ReadonlyDeep -export type Specimen = ReadonlyDeep -export type VisionPrescription = ReadonlyDeep +export type Attachment = ReadonlyDeep> +export type Coding = ReadonlyDeep> +export type CodeableConcept = ReadonlyDeep> +export type Device = ReadonlyDeep> +export type DocumentReference = ReadonlyDeep> +export type Extension = ReadonlyDeep> +export type Identifier = ReadonlyDeep> +export type MedicationAdministration = ReadonlyDeep< + FhirJson +> +export type MedicationStatement = ReadonlyDeep> +export type Observation = ReadonlyDeep> +export type Patient = ReadonlyDeep> +export type Period = ReadonlyDeep> +export type PlanDefinition = ReadonlyDeep> +export type Provenance = ReadonlyDeep> +export type Questionnaire = ReadonlyDeep> +export type QuestionnaireResponse = ReadonlyDeep< + FhirJson +> +export type Quantity = ReadonlyDeep> +export type Reference = ReadonlyDeep> +export type SampledData = ReadonlyDeep> +export type ResearchStudy = ReadonlyDeep> +export type ResearchSubject = ReadonlyDeep> +export type Specimen = ReadonlyDeep> +export type VisionPrescription = ReadonlyDeep> /** Every resource a Grove exchange graph may carry, as the guides publish them. */ export type GraphResource = @@ -68,25 +73,61 @@ export type GraphResource = | Patient | PlanDefinition | Provenance - | Questionnaire | QuestionnaireResponse | ResearchStudy | ResearchSubject | Specimen | VisionPrescription -type BundleEntry = ReadonlyDeep[number]> +type BundleEntry = ReadonlyDeep< + FhirJson[number]> +> -export type CollectionBundle = ReadonlyDeep< - Omit & { +/** Bounded base-R4 collection Bundle; base fields and entries remain optional. */ +export type R4CollectionBundle = ReadonlyDeep< + FhirJson> & { readonly type: 'collection' - readonly entry: ReadonlyArray< + } +> + +/** Grove-profiled event graph with complete event and entry identity. */ +export type GroveMobileExchangeBundle = ReadonlyDeep< + Omit & { + readonly identifier: Identifier + readonly timestamp: string + readonly entry: readonly [ Omit & { readonly fullUrl: string readonly resource: GraphResource - } - > + }, + ...Array< + Omit & { + readonly fullUrl: string + readonly resource: GraphResource + } + >, + ] + } +> + +/** Grove-profiled retraction assertion containing Provenance and optional Device agents only. */ +export type GroveMobileRetractionBundle = ReadonlyDeep< + Omit & { + readonly identifier: Identifier + readonly timestamp: string + readonly entry: readonly [ + Omit & { + readonly fullUrl: string + readonly resource: Device | Provenance + }, + ...Array< + Omit & { + readonly fullUrl: string + readonly resource: Device | Provenance + } + >, + ] } > -export type SupportedR4Resource = CollectionBundle | GraphResource +export type SupportedR4Resource = R4CollectionBundle | GraphResource diff --git a/packages/fhir/src/zod/r4/schemas.ts b/packages/fhir/src/zod/r4/schemas.ts index cfb1f553..246370ac 100644 --- a/packages/fhir/src/zod/r4/schemas.ts +++ b/packages/fhir/src/zod/r4/schemas.ts @@ -8,7 +8,7 @@ // GENERATED FILE. Run `npm run generate:zod` after changing the generator or the release pin. // -/* eslint-disable sonarjs/regex-complexity, sonarjs/concise-regex, sonarjs/single-char-in-character-classes, sonarjs/single-character-alternation */ +/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion, sonarjs/no-nested-functions, sonarjs/regex-complexity, sonarjs/concise-regex, sonarjs/single-char-in-character-classes, sonarjs/single-character-alternation */ import type { Account as FhirAccount, @@ -673,544 +673,682 @@ import type { VisionPrescriptionLensSpecificationPrism as FhirVisionPrescriptionLensSpecificationPrism, } from 'fhir/r4.js' import { z } from 'zod' -import { fhirDateTimeToEpoch } from '../support.js' +import type { FhirJson } from '../../r4/json.js' +import { compareFhirDateTimes, hasValidFhirCalendarDate } from '../support.js' // Generated from hl7.fhir.r4.core#4.0.1 (FHIR 4.0.1). // 206 structures: every type the exposed roots reach. -export const accountCoverageSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const accountCoverageSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + coverage: z.lazy(() => referenceSchema), + priority: positiveIntSchema.optional(), + _priority: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const accountGuarantorSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + party: z.lazy(() => referenceSchema), + onHold: booleanSchema.optional(), + _onHold: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const accountSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Account'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - coverage: z.lazy(() => referenceSchema), - priority: positiveIntSchema.optional(), - _priority: z.lazy(() => elementSchema).optional(), - }), -) + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['active', 'entered-in-error', 'inactive', 'on-hold', 'unknown']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + subject: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + servicePeriod: z.lazy(() => periodSchema).optional(), + coverage: accountCoverageSchema.array().min(1).optional(), + owner: z.lazy(() => referenceSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + guarantor: accountGuarantorSchema.array().min(1).optional(), + partOf: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const accountGuarantorSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const activityDefinitionParticipantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - party: z.lazy(() => referenceSchema), - onHold: booleanSchema.optional(), - _onHold: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), -) - -export const accountSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Account'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'entered-in-error', - 'inactive', - 'on-hold', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - subject: z - .lazy(() => referenceSchema) - .array() - .optional(), - servicePeriod: z.lazy(() => periodSchema).optional(), - coverage: accountCoverageSchema.array().optional(), - owner: z.lazy(() => referenceSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - guarantor: accountGuarantorSchema.array().optional(), - partOf: z.lazy(() => referenceSchema).optional(), - }), -) - -export const activityDefinitionParticipantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ + type: z + .enum(['device', 'patient', 'practitioner', 'related-person']) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const activityDefinitionDynamicValueSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum(['device', 'patient', 'practitioner', 'related-person']), - _type: z.lazy(() => elementSchema).optional(), - role: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'path', shadow: '_path' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const activityDefinitionDynamicValueSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const activityDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ActivityDefinition'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - path: stringSchema, - _path: z.lazy(() => elementSchema).optional(), - expression: z.lazy(() => expressionSchema), - }), - ) - -export const activityDefinitionSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - resourceType: z.literal('ActivityDefinition'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - author: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - editor: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - reviewer: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - endorser: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) - .array() - .optional(), - library: canonicalSchema.array().optional(), - _library: z - .lazy(() => elementSchema) - .array() - .optional(), - kind: z - .enum([ - 'Appointment', - 'AppointmentResponse', - 'CarePlan', - 'Claim', - 'CommunicationRequest', - 'Contract', - 'DeviceRequest', - 'EnrollmentRequest', - 'ImmunizationRecommendation', - 'MedicationRequest', - 'NutritionOrder', - 'ServiceRequest', - 'SupplyRequest', - 'Task', - 'VisionPrescription', - ]) - .optional(), - _kind: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.optional(), - _profile: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - intent: z - .enum([ - 'directive', - 'filler-order', - 'instance-order', - 'option', - 'order', - 'original-order', - 'plan', - 'proposal', - 'reflex-order', - ]) - .optional(), - _intent: z.lazy(() => elementSchema).optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: z.lazy(() => elementSchema).optional(), - timingTiming: z.lazy(() => timingSchema).optional(), - timingDateTime: dateTimeSchema.optional(), - _timingDateTime: z.lazy(() => elementSchema).optional(), - timingAge: z.lazy(() => ageSchema).optional(), - timingPeriod: z.lazy(() => periodSchema).optional(), - timingRange: z.lazy(() => rangeSchema).optional(), - timingDuration: z.lazy(() => durationSchema).optional(), - location: z.lazy(() => referenceSchema).optional(), - participant: activityDefinitionParticipantSchema.array().optional(), - productReference: z.lazy(() => referenceSchema).optional(), - productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - dosage: z - .lazy(() => dosageSchema) - .array() - .optional(), - bodySite: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - specimenRequirement: z - .lazy(() => referenceSchema) - .array() - .optional(), - observationRequirement: z - .lazy(() => referenceSchema) - .array() - .optional(), - observationResultRequirement: z - .lazy(() => referenceSchema) - .array() - .optional(), - transform: canonicalSchema.optional(), - _transform: z.lazy(() => elementSchema).optional(), - dynamicValue: activityDefinitionDynamicValueSchema.array().optional(), - }) - .refine( - (value) => - [ - { - names: ['subjectCodeableConcept', 'subjectReference'], - required: false, - }, - { - names: [ - 'timingTiming', - 'timingDateTime', - 'timingAge', - 'timingPeriod', - 'timingRange', - 'timingDuration', - ], - required: false, - }, - { - names: ['productReference', 'productCodeableConcept'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + library: canonicalSchema.nullable().array().min(1).optional(), + _library: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + kind: z + .enum([ + 'Appointment', + 'AppointmentResponse', + 'CarePlan', + 'Claim', + 'CommunicationRequest', + 'Contract', + 'DeviceRequest', + 'EnrollmentRequest', + 'ImmunizationRecommendation', + 'MedicationRequest', + 'NutritionOrder', + 'ServiceRequest', + 'SupplyRequest', + 'Task', + 'VisionPrescription', + ]) + .optional(), + _kind: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + intent: z + .enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]) + .optional(), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + timingAge: z.lazy(() => ageSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingRange: z.lazy(() => rangeSchema).optional(), + timingDuration: z.lazy(() => durationSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + participant: activityDefinitionParticipantSchema + .array() + .min(1) + .optional(), + productReference: z.lazy(() => referenceSchema).optional(), + productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + dosage: z + .lazy(() => dosageSchema) + .array() + .min(1) + .optional(), + bodySite: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + specimenRequirement: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + observationRequirement: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + observationResultRequirement: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + transform: canonicalSchema.optional(), + _transform: z.lazy(() => elementSchema).optional(), + dynamicValue: activityDefinitionDynamicValueSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['subjectCodeableConcept'], ['subjectReference']], + required: false, + }, + { + alternatives: [ + ['timingTiming'], + ['timingDateTime', '_timingDateTime'], + ['timingAge'], + ['timingPeriod'], + ['timingRange'], + ['timingDuration'], + ], + required: false, + }, + { + alternatives: [['productReference'], ['productCodeableConcept']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'library', shadow: '_library', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const addressSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - use: z.enum(['billing', 'home', 'old', 'temp', 'work']).optional(), - _use: z.lazy(() => elementSchema).optional(), - type: z.enum(['both', 'physical', 'postal']).optional(), - _type: z.lazy(() => elementSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - line: stringSchema.array().optional(), - _line: z - .lazy(() => elementSchema) - .array() - .optional(), - city: stringSchema.optional(), - _city: z.lazy(() => elementSchema).optional(), - district: stringSchema.optional(), - _district: z.lazy(() => elementSchema).optional(), - state: stringSchema.optional(), - _state: z.lazy(() => elementSchema).optional(), - postalCode: stringSchema.optional(), - _postalCode: z.lazy(() => elementSchema).optional(), - country: stringSchema.optional(), - _country: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), -) - -export const adverseEventSuspectEntitySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const addressSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + use: z.enum(['billing', 'home', 'old', 'temp', 'work']).optional(), + _use: z.lazy(() => elementSchema).optional(), + type: z.enum(['both', 'physical', 'postal']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + line: stringSchema.nullable().array().min(1).optional(), + _line: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - instance: z.lazy(() => referenceSchema), - causality: adverseEventSuspectEntityCausalitySchema.array().optional(), - }), - ) + city: stringSchema.optional(), + _city: z.lazy(() => elementSchema).optional(), + district: stringSchema.optional(), + _district: z.lazy(() => elementSchema).optional(), + state: stringSchema.optional(), + _state: z.lazy(() => elementSchema).optional(), + postalCode: stringSchema.optional(), + _postalCode: z.lazy(() => elementSchema).optional(), + country: stringSchema.optional(), + _country: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'line', shadow: '_line', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const adverseEventSuspectEntityCausalitySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const adverseEventSuspectEntitySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - assessment: z.lazy(() => codeableConceptSchema).optional(), - productRelatedness: stringSchema.optional(), - _productRelatedness: z.lazy(() => elementSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + instance: z.lazy(() => referenceSchema), + causality: adverseEventSuspectEntityCausalitySchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const adverseEventSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('AdverseEvent'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - actuality: z.enum(['actual', 'potential']), - _actuality: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - event: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - detected: dateTimeSchema.optional(), - _detected: z.lazy(() => elementSchema).optional(), - recordedDate: dateTimeSchema.optional(), - _recordedDate: z.lazy(() => elementSchema).optional(), - resultingCondition: z - .lazy(() => referenceSchema) - .array() - .optional(), - location: z.lazy(() => referenceSchema).optional(), - seriousness: z.lazy(() => codeableConceptSchema).optional(), - severity: z.lazy(() => codeableConceptSchema).optional(), - outcome: z.lazy(() => codeableConceptSchema).optional(), - recorder: z.lazy(() => referenceSchema).optional(), - contributor: z - .lazy(() => referenceSchema) - .array() - .optional(), - suspectEntity: adverseEventSuspectEntitySchema.array().optional(), - subjectMedicalHistory: z - .lazy(() => referenceSchema) - .array() - .optional(), - referenceDocument: z - .lazy(() => referenceSchema) - .array() - .optional(), - study: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) - -export const ageSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - comparator: z.enum(['<', '<=', '>', '>=']).optional(), - _comparator: z.lazy(() => elementSchema).optional(), - unit: stringSchema.optional(), - _unit: z.lazy(() => elementSchema).optional(), - system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - }), -) - -export const allergyIntoleranceReactionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const adverseEventSuspectEntityCausalitySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - substance: z.lazy(() => codeableConceptSchema).optional(), - manifestation: z - .lazy(() => codeableConceptSchema) - .array() - .min(1), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - onset: dateTimeSchema.optional(), - _onset: z.lazy(() => elementSchema).optional(), - severity: z.enum(['mild', 'moderate', 'severe']).optional(), - _severity: z.lazy(() => elementSchema).optional(), - exposureRoute: z.lazy(() => codeableConceptSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }), - ) + assessment: z.lazy(() => codeableConceptSchema).optional(), + productRelatedness: stringSchema.optional(), + _productRelatedness: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const allergyIntoleranceSchema: z.ZodType = - z.lazy(() => +export const adverseEventSchema: z.ZodType> = z.lazy( + () => z .strictObject({ - resourceType: z.literal('AllergyIntolerance'), + resourceType: z.literal('AdverseEvent'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -1222,241 +1360,179 @@ export const allergyIntoleranceSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - clinicalStatus: z.lazy(() => codeableConceptSchema).optional(), - verificationStatus: z.lazy(() => codeableConceptSchema).optional(), - type: z.enum(['allergy', 'intolerance']).optional(), - _type: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + actuality: z.enum(['actual', 'potential']).optional(), + _actuality: z.lazy(() => elementSchema).optional(), category: z - .enum(['biologic', 'environment', 'food', 'medication']) - .array() - .optional(), - _category: z - .lazy(() => elementSchema) + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - criticality: z.enum(['high', 'low', 'unable-to-assess']).optional(), - _criticality: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - patient: z.lazy(() => referenceSchema), + event: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema), encounter: z.lazy(() => referenceSchema).optional(), - onsetDateTime: dateTimeSchema.optional(), - _onsetDateTime: z.lazy(() => elementSchema).optional(), - onsetAge: z.lazy(() => ageSchema).optional(), - onsetPeriod: z.lazy(() => periodSchema).optional(), - onsetRange: z.lazy(() => rangeSchema).optional(), - onsetString: stringSchema.optional(), - _onsetString: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + detected: dateTimeSchema.optional(), + _detected: z.lazy(() => elementSchema).optional(), recordedDate: dateTimeSchema.optional(), _recordedDate: z.lazy(() => elementSchema).optional(), + resultingCondition: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + location: z.lazy(() => referenceSchema).optional(), + seriousness: z.lazy(() => codeableConceptSchema).optional(), + severity: z.lazy(() => codeableConceptSchema).optional(), + outcome: z.lazy(() => codeableConceptSchema).optional(), recorder: z.lazy(() => referenceSchema).optional(), - asserter: z.lazy(() => referenceSchema).optional(), - lastOccurrence: dateTimeSchema.optional(), - _lastOccurrence: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) + contributor: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + suspectEntity: adverseEventSuspectEntitySchema + .array() + .min(1) + .optional(), + subjectMedicalHistory: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - reaction: allergyIntoleranceReactionSchema.array().optional(), + referenceDocument: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + study: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'actuality', shadow: '_actuality' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: [ - 'onsetDateTime', - 'onsetAge', - 'onsetPeriod', - 'onsetRange', - 'onsetString', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> -export const annotationSchema: z.ZodType = z.lazy(() => +export const ageSchema: z.ZodType> = z.lazy(() => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - authorReference: z.lazy(() => referenceSchema).optional(), - authorString: stringSchema.optional(), - _authorString: z.lazy(() => elementSchema).optional(), - time: dateTimeSchema.optional(), - _time: z.lazy(() => elementSchema).optional(), - text: markdownSchema, - _text: z.lazy(() => elementSchema).optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), }) .refine( - (value) => - [{ names: ['authorReference', 'authorString'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const quantity = value as Record + const hasCode = + quantity.code !== undefined || quantity._code !== undefined + return ( + !hasCode || + quantity.system !== undefined || + quantity._system !== undefined + ) }, + { message: 'qty-3: a coded Quantity requires its unit system.' }, ), -) +) as unknown as z.ZodType> -export const appointmentParticipantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const allergyIntoleranceReactionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z + substance: z.lazy(() => codeableConceptSchema).optional(), + manifestation: z .lazy(() => codeableConceptSchema) .array() + .min(1), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + onset: dateTimeSchema.optional(), + _onset: z.lazy(() => elementSchema).optional(), + severity: z.enum(['mild', 'moderate', 'severe']).optional(), + _severity: z.lazy(() => elementSchema).optional(), + exposureRoute: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) .optional(), - actor: z.lazy(() => referenceSchema).optional(), - required: z.enum(['information-only', 'optional', 'required']).optional(), - _required: z.lazy(() => elementSchema).optional(), - status: z.enum(['accepted', 'declined', 'needs-action', 'tentative']), - _status: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const appointmentSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Appointment'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'arrived', - 'booked', - 'cancelled', - 'checked-in', - 'entered-in-error', - 'fulfilled', - 'noshow', - 'pending', - 'proposed', - 'waitlist', - ]), - _status: z.lazy(() => elementSchema).optional(), - cancelationReason: z.lazy(() => codeableConceptSchema).optional(), - serviceCategory: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - serviceType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - specialty: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - appointmentType: z.lazy(() => codeableConceptSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - priority: unsignedIntSchema.optional(), - _priority: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - supportingInformation: z - .lazy(() => referenceSchema) - .array() - .optional(), - start: instantSchema.optional(), - _start: z.lazy(() => elementSchema).optional(), - end: instantSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), - minutesDuration: positiveIntSchema.optional(), - _minutesDuration: z.lazy(() => elementSchema).optional(), - slot: z - .lazy(() => referenceSchema) - .array() - .optional(), - created: dateTimeSchema.optional(), - _created: z.lazy(() => elementSchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - patientInstruction: stringSchema.optional(), - _patientInstruction: z.lazy(() => elementSchema).optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - participant: appointmentParticipantSchema.array().min(1), - requestedPeriod: z - .lazy(() => periodSchema) - .array() - .optional(), - }), -) - -export const appointmentResponseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('AppointmentResponse'), +export const allergyIntoleranceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('AllergyIntolerance'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -1468,315 +1544,508 @@ export const appointmentResponseSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - appointment: z.lazy(() => referenceSchema), - start: instantSchema.optional(), - _start: z.lazy(() => elementSchema).optional(), - end: instantSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), - participantType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - actor: z.lazy(() => referenceSchema).optional(), - participantStatus: z.enum([ - 'accepted', - 'declined', - 'needs-action', - 'tentative', - ]), - _participantStatus: z.lazy(() => elementSchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - }), - ) - -export const attachmentSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - contentType: codeSchema.optional(), - _contentType: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - data: base64BinarySchema.optional(), - _data: z.lazy(() => elementSchema).optional(), - url: urlSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - size: unsignedIntSchema.optional(), - _size: z.lazy(() => elementSchema).optional(), - hash: base64BinarySchema.optional(), - _hash: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - creation: dateTimeSchema.optional(), - _creation: z.lazy(() => elementSchema).optional(), - }), -) - -export const auditEventAgentSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - role: z - .lazy(() => codeableConceptSchema) + clinicalStatus: z.lazy(() => codeableConceptSchema).optional(), + verificationStatus: z.lazy(() => codeableConceptSchema).optional(), + type: z.enum(['allergy', 'intolerance']).optional(), + _type: z.lazy(() => elementSchema).optional(), + category: z + .enum(['biologic', 'environment', 'food', 'medication']) + .nullable() .array() + .min(1) .optional(), - who: z.lazy(() => referenceSchema).optional(), - altId: stringSchema.optional(), - _altId: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - requestor: booleanSchema, - _requestor: z.lazy(() => elementSchema).optional(), - location: z.lazy(() => referenceSchema).optional(), - policy: uriSchema.array().optional(), - _policy: z + _category: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - media: z.lazy(() => codingSchema).optional(), - network: auditEventAgentNetworkSchema.optional(), - purposeOfUse: z - .lazy(() => codeableConceptSchema) + criticality: z.enum(['high', 'low', 'unable-to-assess']).optional(), + _criticality: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + patient: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + onsetDateTime: dateTimeSchema.optional(), + _onsetDateTime: z.lazy(() => elementSchema).optional(), + onsetAge: z.lazy(() => ageSchema).optional(), + onsetPeriod: z.lazy(() => periodSchema).optional(), + onsetRange: z.lazy(() => rangeSchema).optional(), + onsetString: stringSchema.optional(), + _onsetString: z.lazy(() => elementSchema).optional(), + recordedDate: dateTimeSchema.optional(), + _recordedDate: z.lazy(() => elementSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + asserter: z.lazy(() => referenceSchema).optional(), + lastOccurrence: dateTimeSchema.optional(), + _lastOccurrence: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - }), -) + reaction: allergyIntoleranceReactionSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['onsetDateTime', '_onsetDateTime'], + ['onsetAge'], + ['onsetPeriod'], + ['onsetRange'], + ['onsetString', '_onsetString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'category', shadow: '_category', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const auditEventSourceSchema: z.ZodType = z.lazy( +export const annotationSchema: z.ZodType> = z.lazy( () => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + authorReference: z.lazy(() => referenceSchema).optional(), + authorString: stringSchema.optional(), + _authorString: z.lazy(() => elementSchema).optional(), + time: dateTimeSchema.optional(), + _time: z.lazy(() => elementSchema).optional(), + text: markdownSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['authorReference'], + ['authorString', '_authorString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'text', shadow: '_text' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const appointmentParticipantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - site: stringSchema.optional(), - _site: z.lazy(() => elementSchema).optional(), - observer: z.lazy(() => referenceSchema), type: z - .lazy(() => codingSchema) + .lazy(() => codeableConceptSchema) .array() + .min(1) + .optional(), + actor: z.lazy(() => referenceSchema).optional(), + required: z.enum(['information-only', 'optional', 'required']).optional(), + _required: z.lazy(() => elementSchema).optional(), + status: z + .enum(['accepted', 'declined', 'needs-action', 'tentative']) .optional(), - }), -) + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const auditEventEntitySchema: z.ZodType = z.lazy( +export const appointmentSchema: z.ZodType> = z.lazy( () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - what: z.lazy(() => referenceSchema).optional(), - type: z.lazy(() => codingSchema).optional(), - role: z.lazy(() => codingSchema).optional(), - lifecycle: z.lazy(() => codingSchema).optional(), - securityLabel: z - .lazy(() => codingSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - query: base64BinarySchema.optional(), - _query: z.lazy(() => elementSchema).optional(), - detail: auditEventEntityDetailSchema.array().optional(), - }), -) + z + .strictObject({ + resourceType: z.literal('Appointment'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'arrived', + 'booked', + 'cancelled', + 'checked-in', + 'entered-in-error', + 'fulfilled', + 'noshow', + 'pending', + 'proposed', + 'waitlist', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + cancelationReason: z.lazy(() => codeableConceptSchema).optional(), + serviceCategory: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + serviceType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + appointmentType: z.lazy(() => codeableConceptSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + priority: unsignedIntSchema.optional(), + _priority: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + start: instantSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: instantSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + minutesDuration: positiveIntSchema.optional(), + _minutesDuration: z.lazy(() => elementSchema).optional(), + slot: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + patientInstruction: stringSchema.optional(), + _patientInstruction: z.lazy(() => elementSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + participant: appointmentParticipantSchema.array().min(1), + requestedPeriod: z + .lazy(() => periodSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const auditEventAgentNetworkSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const appointmentResponseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('AppointmentResponse'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - address: stringSchema.optional(), - _address: z.lazy(() => elementSchema).optional(), - type: z.enum(['1', '2', '3', '4', '5']).optional(), - _type: z.lazy(() => elementSchema).optional(), - }), - ) + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + appointment: z.lazy(() => referenceSchema), + start: instantSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: instantSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + participantType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + actor: z.lazy(() => referenceSchema).optional(), + participantStatus: z + .enum(['accepted', 'declined', 'needs-action', 'tentative']) + .optional(), + _participantStatus: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'participantStatus', shadow: '_participantStatus' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const auditEventEntityDetailSchema: z.ZodType = - z.lazy(() => +export const attachmentSchema: z.ZodType> = z.lazy( + () => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: stringSchema, - _type: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: z.lazy(() => elementSchema).optional(), + contentType: codeSchema.optional(), + _contentType: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + data: base64BinarySchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + url: urlSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + size: unsignedIntSchema.optional(), + _size: z.lazy(() => elementSchema).optional(), + hash: base64BinarySchema.optional(), + _hash: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + creation: dateTimeSchema.optional(), + _creation: z.lazy(() => elementSchema).optional(), }) .refine( - (value) => - [ - { names: ['valueString', 'valueBase64Binary'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> -export const auditEventSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('AuditEvent'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codingSchema), - subtype: z - .lazy(() => codingSchema) - .array() - .optional(), - action: z.enum(['C', 'D', 'E', 'R', 'U']).optional(), - _action: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - recorded: instantSchema, - _recorded: z.lazy(() => elementSchema).optional(), - outcome: z.enum(['0', '12', '4', '8']).optional(), - _outcome: z.lazy(() => elementSchema).optional(), - outcomeDesc: stringSchema.optional(), - _outcomeDesc: z.lazy(() => elementSchema).optional(), - purposeOfEvent: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - agent: auditEventAgentSchema.array().min(1), - source: auditEventSourceSchema, - entity: auditEventEntitySchema.array().optional(), - }), -) - -export const basicSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Basic'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - subject: z.lazy(() => referenceSchema).optional(), - created: dateSchema.optional(), - _created: z.lazy(() => elementSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - }), -) - -export const binarySchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Binary'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - contentType: codeSchema, - _contentType: z.lazy(() => elementSchema).optional(), - securityContext: z.lazy(() => referenceSchema).optional(), - data: base64BinarySchema.optional(), - _data: z.lazy(() => elementSchema).optional(), - }), -) - -export const biologicallyDerivedProductCollectionSchema: z.ZodType = +export const auditEventAgentSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -1784,38 +2053,112 @@ export const biologicallyDerivedProductCollectionSchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - collector: z.lazy(() => referenceSchema).optional(), - source: z.lazy(() => referenceSchema).optional(), - collectedDateTime: dateTimeSchema.optional(), - _collectedDateTime: z.lazy(() => elementSchema).optional(), - collectedPeriod: z.lazy(() => periodSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + role: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + who: z.lazy(() => referenceSchema).optional(), + altId: stringSchema.optional(), + _altId: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + requestor: booleanSchema.optional(), + _requestor: z.lazy(() => elementSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + policy: uriSchema.nullable().array().min(1).optional(), + _policy: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + media: z.lazy(() => codingSchema).optional(), + network: auditEventAgentNetworkSchema.optional(), + purposeOfUse: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'requestor', shadow: '_requestor' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'policy', shadow: '_policy', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } }) .refine( - (value) => - [ - { - names: ['collectedDateTime', 'collectedPeriod'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const biologicallyDerivedProductProcessingSchema: z.ZodType = +export const auditEventSourceSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -1823,38 +2166,31 @@ export const biologicallyDerivedProductProcessingSchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + site: stringSchema.optional(), + _site: z.lazy(() => elementSchema).optional(), + observer: z.lazy(() => referenceSchema), + type: z + .lazy(() => codingSchema) + .array() + .min(1) .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - procedure: z.lazy(() => codeableConceptSchema).optional(), - additive: z.lazy(() => referenceSchema).optional(), - timeDateTime: dateTimeSchema.optional(), - _timeDateTime: z.lazy(() => elementSchema).optional(), - timePeriod: z.lazy(() => periodSchema).optional(), }) .refine( - (value) => - [{ names: ['timeDateTime', 'timePeriod'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const biologicallyDerivedProductManipulationSchema: z.ZodType = +export const auditEventEntitySchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -1862,1538 +2198,1817 @@ export const biologicallyDerivedProductManipulationSchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + what: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codingSchema).optional(), + role: z.lazy(() => codingSchema).optional(), + lifecycle: z.lazy(() => codingSchema).optional(), + securityLabel: z + .lazy(() => codingSchema) + .array() + .min(1) .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - timeDateTime: dateTimeSchema.optional(), - _timeDateTime: z.lazy(() => elementSchema).optional(), - timePeriod: z.lazy(() => periodSchema).optional(), + query: base64BinarySchema.optional(), + _query: z.lazy(() => elementSchema).optional(), + detail: auditEventEntityDetailSchema.array().min(1).optional(), }) .refine( - (value) => - [{ names: ['timeDateTime', 'timePeriod'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const biologicallyDerivedProductStorageSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const auditEventAgentNetworkSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - temperature: decimalSchema.optional(), - _temperature: z.lazy(() => elementSchema).optional(), - scale: z.enum(['celsius', 'farenheit', 'kelvin']).optional(), - _scale: z.lazy(() => elementSchema).optional(), - duration: z.lazy(() => periodSchema).optional(), - }), - ) + address: stringSchema.optional(), + _address: z.lazy(() => elementSchema).optional(), + type: z.enum(['1', '2', '3', '4', '5']).optional(), + _type: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const biologicallyDerivedProductSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('BiologicallyDerivedProduct'), +export const auditEventEntityDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - productCategory: z - .enum(['biologicalAgent', 'cells', 'fluid', 'organ', 'tissue']) - .optional(), - _productCategory: z.lazy(() => elementSchema).optional(), - productCode: z.lazy(() => codeableConceptSchema).optional(), - status: z.enum(['available', 'unavailable']).optional(), - _status: z.lazy(() => elementSchema).optional(), - request: z - .lazy(() => referenceSchema) - .array() - .optional(), - quantity: integerSchema.optional(), - _quantity: z.lazy(() => elementSchema).optional(), - parent: z - .lazy(() => referenceSchema) - .array() - .optional(), - collection: biologicallyDerivedProductCollectionSchema.optional(), - processing: biologicallyDerivedProductProcessingSchema.array().optional(), - manipulation: biologicallyDerivedProductManipulationSchema.optional(), - storage: biologicallyDerivedProductStorageSchema.array().optional(), - }), - ) + type: stringSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueString', '_valueString'], + ['valueBase64Binary', '_valueBase64Binary'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const bodyStructureSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('BodyStructure'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - morphology: z.lazy(() => codeableConceptSchema).optional(), - location: z.lazy(() => codeableConceptSchema).optional(), - locationQualifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - image: z - .lazy(() => attachmentSchema) - .array() - .optional(), - patient: z.lazy(() => referenceSchema), - }), -) - -export const bundleLinkSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - relation: stringSchema, - _relation: z.lazy(() => elementSchema).optional(), - url: uriSchema, - _url: z.lazy(() => elementSchema).optional(), - }), -) - -export const bundleEntrySchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - link: z - .lazy(() => bundleLinkSchema) - .array() - .optional(), - fullUrl: uriSchema.optional(), - _fullUrl: z.lazy(() => elementSchema).optional(), - resource: z.lazy(() => resourceSchema).optional(), - search: bundleEntrySearchSchema.optional(), - request: bundleEntryRequestSchema.optional(), - response: bundleEntryResponseSchema.optional(), - }), -) - -export const bundleEntrySearchSchema: z.ZodType = z.lazy( +export const auditEventSchema: z.ZodType> = z.lazy( () => - z.strictObject({ + z + .strictObject({ + resourceType: z.literal('AuditEvent'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codingSchema), + subtype: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + action: z.enum(['C', 'D', 'E', 'R', 'U']).optional(), + _action: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + recorded: instantSchema.optional(), + _recorded: z.lazy(() => elementSchema).optional(), + outcome: z.enum(['0', '12', '4', '8']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + outcomeDesc: stringSchema.optional(), + _outcomeDesc: z.lazy(() => elementSchema).optional(), + purposeOfEvent: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + agent: auditEventAgentSchema.array().min(1), + source: auditEventSourceSchema, + entity: auditEventEntitySchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'recorded', shadow: '_recorded' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const basicSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Basic'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - mode: z.enum(['include', 'match', 'outcome']).optional(), - _mode: z.lazy(() => elementSchema).optional(), - score: decimalSchema.optional(), - _score: z.lazy(() => elementSchema).optional(), - }), -) + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema).optional(), + created: dateSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const bundleEntryRequestSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const binarySchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Binary'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + contentType: codeSchema.optional(), + _contentType: z.lazy(() => elementSchema).optional(), + securityContext: z.lazy(() => referenceSchema).optional(), + data: base64BinarySchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'contentType', shadow: '_contentType' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const biologicallyDerivedProductCollectionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - method: z.enum(['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT']), - _method: z.lazy(() => elementSchema).optional(), - url: uriSchema, - _url: z.lazy(() => elementSchema).optional(), - ifNoneMatch: stringSchema.optional(), - _ifNoneMatch: z.lazy(() => elementSchema).optional(), - ifModifiedSince: instantSchema.optional(), - _ifModifiedSince: z.lazy(() => elementSchema).optional(), - ifMatch: stringSchema.optional(), - _ifMatch: z.lazy(() => elementSchema).optional(), - ifNoneExist: stringSchema.optional(), - _ifNoneExist: z.lazy(() => elementSchema).optional(), - }), - ) + collector: z.lazy(() => referenceSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + collectedDateTime: dateTimeSchema.optional(), + _collectedDateTime: z.lazy(() => elementSchema).optional(), + collectedPeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['collectedDateTime', '_collectedDateTime'], + ['collectedPeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const bundleEntryResponseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const biologicallyDerivedProductProcessingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - status: stringSchema, - _status: z.lazy(() => elementSchema).optional(), - location: uriSchema.optional(), - _location: z.lazy(() => elementSchema).optional(), - etag: stringSchema.optional(), - _etag: z.lazy(() => elementSchema).optional(), - lastModified: instantSchema.optional(), - _lastModified: z.lazy(() => elementSchema).optional(), - outcome: z.lazy(() => resourceSchema).optional(), - }), - ) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + procedure: z.lazy(() => codeableConceptSchema).optional(), + additive: z.lazy(() => referenceSchema).optional(), + timeDateTime: dateTimeSchema.optional(), + _timeDateTime: z.lazy(() => elementSchema).optional(), + timePeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['timeDateTime', '_timeDateTime'], ['timePeriod']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const bundleSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Bundle'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), - type: z.enum([ - 'batch', - 'batch-response', - 'collection', - 'document', - 'history', - 'message', - 'searchset', - 'transaction', - 'transaction-response', - ]), - _type: z.lazy(() => elementSchema).optional(), - timestamp: instantSchema.optional(), - _timestamp: z.lazy(() => elementSchema).optional(), - total: unsignedIntSchema.optional(), - _total: z.lazy(() => elementSchema).optional(), - link: bundleLinkSchema.array().optional(), - entry: bundleEntrySchema.array().optional(), - signature: z.lazy(() => signatureSchema).optional(), - }), -) - -export const capabilityStatementSoftwareSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const biologicallyDerivedProductManipulationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - releaseDate: dateTimeSchema.optional(), - _releaseDate: z.lazy(() => elementSchema).optional(), - }), - ) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + timeDateTime: dateTimeSchema.optional(), + _timeDateTime: z.lazy(() => elementSchema).optional(), + timePeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['timeDateTime', '_timeDateTime'], ['timePeriod']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const capabilityStatementImplementationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const biologicallyDerivedProductStorageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: stringSchema, + description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - url: urlSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - custodian: z.lazy(() => referenceSchema).optional(), - }), - ) + temperature: decimalSchema.optional(), + _temperature: z.lazy(() => elementSchema).optional(), + scale: z.enum(['celsius', 'farenheit', 'kelvin']).optional(), + _scale: z.lazy(() => elementSchema).optional(), + duration: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const capabilityStatementRestSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const biologicallyDerivedProductSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('BiologicallyDerivedProduct'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - mode: z.enum(['client', 'server']), - _mode: z.lazy(() => elementSchema).optional(), - documentation: markdownSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - security: capabilityStatementRestSecuritySchema.optional(), - resource: capabilityStatementRestResourceSchema.array().optional(), - interaction: capabilityStatementRestInteractionSchema.array().optional(), - searchParam: z - .lazy(() => capabilityStatementRestResourceSearchParamSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - operation: z - .lazy(() => capabilityStatementRestResourceOperationSchema) - .array() + productCategory: z + .enum(['biologicalAgent', 'cells', 'fluid', 'organ', 'tissue']) .optional(), - compartment: canonicalSchema.array().optional(), - _compartment: z - .lazy(() => elementSchema) + _productCategory: z.lazy(() => elementSchema).optional(), + productCode: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum(['available', 'unavailable']).optional(), + _status: z.lazy(() => elementSchema).optional(), + request: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - }), - ) - -export const capabilityStatementMessagingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + quantity: integerSchema.optional(), + _quantity: z.lazy(() => elementSchema).optional(), + parent: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + collection: biologicallyDerivedProductCollectionSchema.optional(), + processing: biologicallyDerivedProductProcessingSchema .array() + .min(1) .optional(), - endpoint: capabilityStatementMessagingEndpointSchema.array().optional(), - reliableCache: unsignedIntSchema.optional(), - _reliableCache: z.lazy(() => elementSchema).optional(), - documentation: markdownSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - supportedMessage: capabilityStatementMessagingSupportedMessageSchema + manipulation: biologicallyDerivedProductManipulationSchema.optional(), + storage: biologicallyDerivedProductStorageSchema .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const capabilityStatementDocumentSchema: z.ZodType = +export const bodyStructureSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + resourceType: z.literal('BodyStructure'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + morphology: z.lazy(() => codeableConceptSchema).optional(), + location: z.lazy(() => codeableConceptSchema).optional(), + locationQualifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + image: z + .lazy(() => attachmentSchema) + .array() + .min(1) + .optional(), + patient: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const bundleLinkSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + relation: stringSchema.optional(), + _relation: z.lazy(() => elementSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'relation', shadow: '_relation' }, + { name: 'url', shadow: '_url' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const bundleEntrySchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + link: z + .lazy(() => bundleLinkSchema) + .array() + .min(1) + .optional(), + fullUrl: uriSchema.optional(), + _fullUrl: z.lazy(() => elementSchema).optional(), + resource: z.lazy(() => resourceSchema).optional(), + search: bundleEntrySearchSchema.optional(), + request: bundleEntryRequestSchema.optional(), + response: bundleEntryResponseSchema.optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const bundleEntrySearchSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - mode: z.enum(['consumer', 'producer']), + mode: z.enum(['include', 'match', 'outcome']).optional(), _mode: z.lazy(() => elementSchema).optional(), - documentation: markdownSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema, - _profile: z.lazy(() => elementSchema).optional(), - }), - ) + score: decimalSchema.optional(), + _score: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const capabilityStatementRestSecuritySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const bundleEntryRequestSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - cors: booleanSchema.optional(), - _cors: z.lazy(() => elementSchema).optional(), - service: z - .lazy(() => codeableConceptSchema) - .array() + method: z + .enum(['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT']) .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - }), - ) + _method: z.lazy(() => elementSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + ifNoneMatch: stringSchema.optional(), + _ifNoneMatch: z.lazy(() => elementSchema).optional(), + ifModifiedSince: instantSchema.optional(), + _ifModifiedSince: z.lazy(() => elementSchema).optional(), + ifMatch: stringSchema.optional(), + _ifMatch: z.lazy(() => elementSchema).optional(), + ifNoneExist: stringSchema.optional(), + _ifNoneExist: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'method', shadow: '_method' }, + { name: 'url', shadow: '_url' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const capabilityStatementRestResourceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum([ - 'Account', - 'ActivityDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'EffectEvidenceSynthesis', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProduct', - 'MedicinalProductAuthorization', - 'MedicinalProductContraindication', - 'MedicinalProductIndication', - 'MedicinalProductIngredient', - 'MedicinalProductInteraction', - 'MedicinalProductManufactured', - 'MedicinalProductPackaged', - 'MedicinalProductPharmaceutical', - 'MedicinalProductUndesirableEffect', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'RiskEvidenceSynthesis', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'Substance', - 'SubstanceNucleicAcid', - 'SubstancePolymer', - 'SubstanceProtein', - 'SubstanceReferenceInformation', - 'SubstanceSourceMaterial', - 'SubstanceSpecification', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]), - _type: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.optional(), - _profile: z.lazy(() => elementSchema).optional(), - supportedProfile: canonicalSchema.array().optional(), - _supportedProfile: z - .lazy(() => elementSchema) - .array() - .optional(), - documentation: markdownSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - interaction: capabilityStatementRestResourceInteractionSchema - .array() - .optional(), - versioning: z - .enum(['no-version', 'versioned', 'versioned-update']) - .optional(), - _versioning: z.lazy(() => elementSchema).optional(), - readHistory: booleanSchema.optional(), - _readHistory: z.lazy(() => elementSchema).optional(), - updateCreate: booleanSchema.optional(), - _updateCreate: z.lazy(() => elementSchema).optional(), - conditionalCreate: booleanSchema.optional(), - _conditionalCreate: z.lazy(() => elementSchema).optional(), - conditionalRead: z - .enum(['full-support', 'modified-since', 'not-match', 'not-supported']) - .optional(), - _conditionalRead: z.lazy(() => elementSchema).optional(), - conditionalUpdate: booleanSchema.optional(), - _conditionalUpdate: z.lazy(() => elementSchema).optional(), - conditionalDelete: z - .enum(['multiple', 'not-supported', 'single']) - .optional(), - _conditionalDelete: z.lazy(() => elementSchema).optional(), - referencePolicy: z - .enum(['enforced', 'literal', 'local', 'logical', 'resolves']) - .array() - .optional(), - _referencePolicy: z - .lazy(() => elementSchema) - .array() - .optional(), - searchInclude: stringSchema.array().optional(), - _searchInclude: z - .lazy(() => elementSchema) - .array() - .optional(), - searchRevInclude: stringSchema.array().optional(), - _searchRevInclude: z - .lazy(() => elementSchema) +export const bundleEntryResponseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - searchParam: capabilityStatementRestResourceSearchParamSchema + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - operation: capabilityStatementRestResourceOperationSchema - .array() + status: stringSchema.optional(), + _status: z.lazy(() => elementSchema).optional(), + location: uriSchema.optional(), + _location: z.lazy(() => elementSchema).optional(), + etag: stringSchema.optional(), + _etag: z.lazy(() => elementSchema).optional(), + lastModified: instantSchema.optional(), + _lastModified: z.lazy(() => elementSchema).optional(), + outcome: z.lazy(() => resourceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const bundleSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Bundle'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + type: z + .enum([ + 'batch', + 'batch-response', + 'collection', + 'document', + 'history', + 'message', + 'searchset', + 'transaction', + 'transaction-response', + ]) .optional(), - }), - ) + _type: z.lazy(() => elementSchema).optional(), + timestamp: instantSchema.optional(), + _timestamp: z.lazy(() => elementSchema).optional(), + total: unsignedIntSchema.optional(), + _total: z.lazy(() => elementSchema).optional(), + link: bundleLinkSchema.array().min(1).optional(), + entry: bundleEntrySchema.array().min(1).optional(), + signature: z.lazy(() => signatureSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const capabilityStatementRestInteractionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementSoftwareSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.enum(['batch', 'history-system', 'search-system', 'transaction']), - _code: z.lazy(() => elementSchema).optional(), - documentation: markdownSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - }), - ) - -export const capabilityStatementRestResourceSearchParamSchema: z.ZodType = - z.lazy(() => - z.strictObject({ + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + releaseDate: dateTimeSchema.optional(), + _releaseDate: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const capabilityStatementImplementationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - definition: canonicalSchema.optional(), - _definition: z.lazy(() => elementSchema).optional(), - type: z.enum([ - 'composite', - 'date', - 'number', - 'quantity', - 'reference', - 'special', - 'string', - 'token', - 'uri', - ]), - _type: z.lazy(() => elementSchema).optional(), - documentation: markdownSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - }), - ) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + url: urlSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + custodian: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'description', shadow: '_description' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const capabilityStatementRestResourceOperationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementRestSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - definition: canonicalSchema, - _definition: z.lazy(() => elementSchema).optional(), + mode: z.enum(['client', 'server']).optional(), + _mode: z.lazy(() => elementSchema).optional(), documentation: markdownSchema.optional(), _documentation: z.lazy(() => elementSchema).optional(), - }), - ) - -export const capabilityStatementMessagingEndpointSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + security: capabilityStatementRestSecuritySchema.optional(), + resource: capabilityStatementRestResourceSchema.array().min(1).optional(), + interaction: capabilityStatementRestInteractionSchema .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + searchParam: z + .lazy(() => capabilityStatementRestResourceSearchParamSchema) .array() + .min(1) .optional(), - protocol: z.lazy(() => codingSchema), - address: urlSchema, - _address: z.lazy(() => elementSchema).optional(), - }), - ) + operation: z + .lazy(() => capabilityStatementRestResourceOperationSchema) + .array() + .min(1) + .optional(), + compartment: canonicalSchema.nullable().array().min(1).optional(), + _compartment: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'mode', shadow: '_mode' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'compartment', shadow: '_compartment', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const capabilityStatementMessagingSupportedMessageSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementMessagingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - mode: z.enum(['receiver', 'sender']), - _mode: z.lazy(() => elementSchema).optional(), - definition: canonicalSchema, - _definition: z.lazy(() => elementSchema).optional(), - }), - ) + endpoint: capabilityStatementMessagingEndpointSchema + .array() + .min(1) + .optional(), + reliableCache: unsignedIntSchema.optional(), + _reliableCache: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + supportedMessage: capabilityStatementMessagingSupportedMessageSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const capabilityStatementRestResourceInteractionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementDocumentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.enum([ - 'create', - 'delete', - 'history-instance', - 'history-type', - 'patch', - 'read', - 'search-type', - 'update', - 'vread', - ]), - _code: z.lazy(() => elementSchema).optional(), + mode: z.enum(['consumer', 'producer']).optional(), + _mode: z.lazy(() => elementSchema).optional(), documentation: markdownSchema.optional(), _documentation: z.lazy(() => elementSchema).optional(), - }), - ) + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'mode', shadow: '_mode' }, + { name: 'profile', shadow: '_profile' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const capabilityStatementSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('CapabilityStatement'), +export const capabilityStatementRestSecuritySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema, - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) + cors: booleanSchema.optional(), + _cors: z.lazy(() => elementSchema).optional(), + service: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - kind: z.enum(['capability', 'instance', 'requirements']), - _kind: z.lazy(() => elementSchema).optional(), - instantiates: canonicalSchema.array().optional(), - _instantiates: z - .lazy(() => elementSchema) - .array() - .optional(), - imports: canonicalSchema.array().optional(), - _imports: z - .lazy(() => elementSchema) - .array() - .optional(), - software: capabilityStatementSoftwareSchema.optional(), - implementation: capabilityStatementImplementationSchema.optional(), - fhirVersion: z.enum([ - '0.0.80', - '0.0.81', - '0.0.82', - '0.01', - '0.05', - '0.06', - '0.11', - '0.4.0', - '0.5.0', - '1.0.0', - '1.0.1', - '1.0.2', - '1.1.0', - '1.4.0', - '1.6.0', - '1.8.0', - '3.0.0', - '3.0.1', - '3.3.0', - '3.5.0', - '4.0.0', - '4.0.1', - ]), - _fhirVersion: z.lazy(() => elementSchema).optional(), - format: codeSchema.array().min(1), - _format: z - .lazy(() => elementSchema) - .array() - .optional(), - patchFormat: codeSchema.array().optional(), - _patchFormat: z - .lazy(() => elementSchema) - .array() - .optional(), - implementationGuide: canonicalSchema.array().optional(), - _implementationGuide: z - .lazy(() => elementSchema) - .array() - .optional(), - rest: capabilityStatementRestSchema.array().optional(), - messaging: capabilityStatementMessagingSchema.array().optional(), - document: capabilityStatementDocumentSchema.array().optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const carePlanActivitySchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const capabilityStatementRestResourceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - outcomeCodeableConcept: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - outcomeReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - progress: z - .lazy(() => annotationSchema) - .array() - .optional(), - reference: z.lazy(() => referenceSchema).optional(), - detail: carePlanActivityDetailSchema.optional(), - }), -) - -export const carePlanActivityDetailSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - kind: z - .enum([ - 'Appointment', - 'CommunicationRequest', - 'DeviceRequest', - 'MedicationRequest', - 'NutritionOrder', - 'ServiceRequest', - 'Task', - 'VisionPrescription', - ]) - .optional(), - _kind: z.lazy(() => elementSchema).optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - goal: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'cancelled', - 'completed', - 'entered-in-error', - 'in-progress', - 'not-started', - 'on-hold', - 'scheduled', - 'stopped', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: z.lazy(() => elementSchema).optional(), - scheduledTiming: z.lazy(() => timingSchema).optional(), - scheduledPeriod: z.lazy(() => periodSchema).optional(), - scheduledString: stringSchema.optional(), - _scheduledString: z.lazy(() => elementSchema).optional(), - location: z.lazy(() => referenceSchema).optional(), - performer: z - .lazy(() => referenceSchema) - .array() - .optional(), - productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - productReference: z.lazy(() => referenceSchema).optional(), - dailyAmount: z.lazy(() => quantitySchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['scheduledTiming', 'scheduledPeriod', 'scheduledString'], - required: false, - }, - { - names: ['productCodeableConcept', 'productReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + type: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + supportedProfile: canonicalSchema.nullable().array().min(1).optional(), + _supportedProfile: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + interaction: capabilityStatementRestResourceInteractionSchema + .array() + .min(1) + .optional(), + versioning: z + .enum(['no-version', 'versioned', 'versioned-update']) + .optional(), + _versioning: z.lazy(() => elementSchema).optional(), + readHistory: booleanSchema.optional(), + _readHistory: z.lazy(() => elementSchema).optional(), + updateCreate: booleanSchema.optional(), + _updateCreate: z.lazy(() => elementSchema).optional(), + conditionalCreate: booleanSchema.optional(), + _conditionalCreate: z.lazy(() => elementSchema).optional(), + conditionalRead: z + .enum(['full-support', 'modified-since', 'not-match', 'not-supported']) + .optional(), + _conditionalRead: z.lazy(() => elementSchema).optional(), + conditionalUpdate: booleanSchema.optional(), + _conditionalUpdate: z.lazy(() => elementSchema).optional(), + conditionalDelete: z + .enum(['multiple', 'not-supported', 'single']) + .optional(), + _conditionalDelete: z.lazy(() => elementSchema).optional(), + referencePolicy: z + .enum(['enforced', 'literal', 'local', 'logical', 'resolves']) + .nullable() + .array() + .min(1) + .optional(), + _referencePolicy: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + searchInclude: stringSchema.nullable().array().min(1).optional(), + _searchInclude: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + searchRevInclude: stringSchema.nullable().array().min(1).optional(), + _searchRevInclude: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + searchParam: capabilityStatementRestResourceSearchParamSchema + .array() + .min(1) + .optional(), + operation: capabilityStatementRestResourceOperationSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + name: 'supportedProfile', + shadow: '_supportedProfile', + required: false, }, - ), - ) + { + name: 'referencePolicy', + shadow: '_referencePolicy', + required: false, + }, + { name: 'searchInclude', shadow: '_searchInclude', required: false }, + { + name: 'searchRevInclude', + shadow: '_searchRevInclude', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const carePlanSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('CarePlan'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - replaces: z - .lazy(() => referenceSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'completed', - 'draft', - 'entered-in-error', - 'on-hold', - 'revoked', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - intent: z.enum(['option', 'order', 'plan', 'proposal']), - _intent: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - created: dateTimeSchema.optional(), - _created: z.lazy(() => elementSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - contributor: z - .lazy(() => referenceSchema) - .array() - .optional(), - careTeam: z - .lazy(() => referenceSchema) - .array() - .optional(), - addresses: z - .lazy(() => referenceSchema) - .array() - .optional(), - supportingInfo: z - .lazy(() => referenceSchema) - .array() - .optional(), - goal: z - .lazy(() => referenceSchema) - .array() - .optional(), - activity: carePlanActivitySchema.array().optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }), -) - -export const careTeamParticipantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementRestInteractionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - role: z - .lazy(() => codeableConceptSchema) - .array() + code: z + .enum(['batch', 'history-system', 'search-system', 'transaction']) .optional(), - member: z.lazy(() => referenceSchema).optional(), - onBehalfOf: z.lazy(() => referenceSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), - ) + _code: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const careTeamSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('CareTeam'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z - .enum(['active', 'entered-in-error', 'inactive', 'proposed', 'suspended']) - .optional(), - _status: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - participant: careTeamParticipantSchema.array().optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - managingOrganization: z - .lazy(() => referenceSchema) - .array() - .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }), -) - -export const catalogEntryRelatedEntrySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementRestResourceSearchParamSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - relationtype: z.enum(['is-replaced-by', 'triggers']), - _relationtype: z.lazy(() => elementSchema).optional(), - item: z.lazy(() => referenceSchema), - }), - ) - -export const catalogEntrySchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('CatalogEntry'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - orderable: booleanSchema, - _orderable: z.lazy(() => elementSchema).optional(), - referencedItem: z.lazy(() => referenceSchema), - additionalIdentifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - classification: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), - _status: z.lazy(() => elementSchema).optional(), - validityPeriod: z.lazy(() => periodSchema).optional(), - validTo: dateTimeSchema.optional(), - _validTo: z.lazy(() => elementSchema).optional(), - lastUpdated: dateTimeSchema.optional(), - _lastUpdated: z.lazy(() => elementSchema).optional(), - additionalCharacteristic: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - additionalClassification: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - relatedEntry: catalogEntryRelatedEntrySchema.array().optional(), - }), -) - -export const chargeItemPerformerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + definition: canonicalSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'composite', + 'date', + 'number', + 'quantity', + 'reference', + 'special', + 'string', + 'token', + 'uri', + ]) .optional(), - function: z.lazy(() => codeableConceptSchema).optional(), - actor: z.lazy(() => referenceSchema), - }), - ) + _type: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const chargeItemSchema: z.ZodType = z.lazy(() => +export const capabilityStatementRestResourceOperationSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('ChargeItem'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - definitionUri: uriSchema.array().optional(), - _definitionUri: z - .lazy(() => elementSchema) - .array() - .optional(), - definitionCanonical: canonicalSchema.array().optional(), - _definitionCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - status: z.enum([ - 'aborted', - 'billable', - 'billed', - 'entered-in-error', - 'not-billable', - 'planned', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - subject: z.lazy(() => referenceSchema), - context: z.lazy(() => referenceSchema).optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - occurrenceTiming: z.lazy(() => timingSchema).optional(), - performer: chargeItemPerformerSchema.array().optional(), - performingOrganization: z.lazy(() => referenceSchema).optional(), - requestingOrganization: z.lazy(() => referenceSchema).optional(), - costCenter: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - bodysite: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - factorOverride: decimalSchema.optional(), - _factorOverride: z.lazy(() => elementSchema).optional(), - priceOverride: z.lazy(() => moneySchema).optional(), - overrideReason: stringSchema.optional(), - _overrideReason: z.lazy(() => elementSchema).optional(), - enterer: z.lazy(() => referenceSchema).optional(), - enteredDate: dateTimeSchema.optional(), - _enteredDate: z.lazy(() => elementSchema).optional(), - reason: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - service: z - .lazy(() => referenceSchema) - .array() - .optional(), - productReference: z.lazy(() => referenceSchema).optional(), - productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - account: z - .lazy(() => referenceSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - supportingInformation: z - .lazy(() => referenceSchema) - .array() - .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + definition: canonicalSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'definition', shadow: '_definition' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: [ - 'occurrenceDateTime', - 'occurrencePeriod', - 'occurrenceTiming', - ], - required: false, - }, - { - names: ['productReference', 'productCodeableConcept'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType< + FhirJson +> -export const chargeItemDefinitionApplicabilitySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementMessagingEndpointSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - language: stringSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - expression: stringSchema.optional(), - _expression: z.lazy(() => elementSchema).optional(), - }), - ) + protocol: z.lazy(() => codingSchema), + address: urlSchema.optional(), + _address: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'address', shadow: '_address' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const chargeItemDefinitionPropertyGroupSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementMessagingSupportedMessageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - applicability: z - .lazy(() => chargeItemDefinitionApplicabilitySchema) - .array() - .optional(), - priceComponent: chargeItemDefinitionPropertyGroupPriceComponentSchema - .array() - .optional(), - }), - ) + mode: z.enum(['receiver', 'sender']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + definition: canonicalSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'mode', shadow: '_mode' }, + { name: 'definition', shadow: '_definition' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const chargeItemDefinitionPropertyGroupPriceComponentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementRestResourceInteractionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum([ - 'base', - 'deduction', - 'discount', - 'informational', - 'surcharge', - 'tax', - ]), - _type: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - amount: z.lazy(() => moneySchema).optional(), - }), - ) + code: z + .enum([ + 'create', + 'delete', + 'history-instance', + 'history-type', + 'patch', + 'read', + 'search-type', + 'update', + 'vread', + ]) + .optional(), + _code: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const chargeItemDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('ChargeItemDefinition'), +export const capabilityStatementSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CapabilityStatement'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -3405,41 +4020,27 @@ export const chargeItemDefinitionSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema, + url: uriSchema.optional(), _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), version: stringSchema.optional(), _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), title: stringSchema.optional(), _title: z.lazy(() => elementSchema).optional(), - derivedFromUri: uriSchema.array().optional(), - _derivedFromUri: z - .lazy(() => elementSchema) - .array() - .optional(), - partOf: canonicalSchema.array().optional(), - _partOf: z - .lazy(() => elementSchema) - .array() - .optional(), - replaces: canonicalSchema.array().optional(), - _replaces: z - .lazy(() => elementSchema) - .array() - .optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), _status: z.lazy(() => elementSchema).optional(), experimental: booleanSchema.optional(), _experimental: z.lazy(() => elementSchema).optional(), @@ -3450,287 +4051,332 @@ export const chargeItemDefinitionSchema: z.ZodType = contact: z .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), useContext: z .lazy(() => usageContextSchema) .array() + .min(1) .optional(), jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), copyright: markdownSchema.optional(), _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - instance: z - .lazy(() => referenceSchema) + kind: z.enum(['capability', 'instance', 'requirements']).optional(), + _kind: z.lazy(() => elementSchema).optional(), + instantiates: canonicalSchema.nullable().array().min(1).optional(), + _instantiates: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - applicability: chargeItemDefinitionApplicabilitySchema.array().optional(), - propertyGroup: chargeItemDefinitionPropertyGroupSchema.array().optional(), - }), - ) - -export const claimRelatedSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - claim: z.lazy(() => referenceSchema).optional(), - relationship: z.lazy(() => codeableConceptSchema).optional(), - reference: z.lazy(() => identifierSchema).optional(), - }), -) - -export const claimPayeeSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - party: z.lazy(() => referenceSchema).optional(), - }), -) - -export const claimCareTeamSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - provider: z.lazy(() => referenceSchema), - responsible: booleanSchema.optional(), - _responsible: z.lazy(() => elementSchema).optional(), - role: z.lazy(() => codeableConceptSchema).optional(), - qualification: z.lazy(() => codeableConceptSchema).optional(), - }), -) - -export const claimSupportingInfoSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - category: z.lazy(() => codeableConceptSchema), - code: z.lazy(() => codeableConceptSchema).optional(), - timingDate: dateSchema.optional(), - _timingDate: z.lazy(() => elementSchema).optional(), - timingPeriod: z.lazy(() => periodSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - reason: z.lazy(() => codeableConceptSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['timingDate', 'timingPeriod'], required: false }, - { - names: [ - 'valueBoolean', - 'valueString', - 'valueQuantity', - 'valueAttachment', - 'valueReference', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const claimDiagnosisSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + imports: canonicalSchema.nullable().array().min(1).optional(), + _imports: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + software: capabilityStatementSoftwareSchema.optional(), + implementation: capabilityStatementImplementationSchema.optional(), + fhirVersion: z + .enum([ + '0.0.80', + '0.0.81', + '0.0.82', + '0.01', + '0.05', + '0.06', + '0.11', + '0.4.0', + '0.5.0', + '1.0.0', + '1.0.1', + '1.0.2', + '1.1.0', + '1.4.0', + '1.6.0', + '1.8.0', + '3.0.0', + '3.0.1', + '3.3.0', + '3.5.0', + '4.0.0', + '4.0.1', + ]) + .optional(), + _fhirVersion: z.lazy(() => elementSchema).optional(), + format: codeSchema.nullable().array().min(1).optional(), + _format: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - diagnosisCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - diagnosisReference: z.lazy(() => referenceSchema).optional(), - type: z - .lazy(() => codeableConceptSchema) + patchFormat: codeSchema.nullable().array().min(1).optional(), + _patchFormat: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - onAdmission: z.lazy(() => codeableConceptSchema).optional(), - packageCode: z.lazy(() => codeableConceptSchema).optional(), + implementationGuide: canonicalSchema.nullable().array().min(1).optional(), + _implementationGuide: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + rest: capabilityStatementRestSchema.array().min(1).optional(), + messaging: capabilityStatementMessagingSchema.array().min(1).optional(), + document: capabilityStatementDocumentSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'date', shadow: '_date' }, + { name: 'kind', shadow: '_kind' }, + { name: 'fhirVersion', shadow: '_fhirVersion' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'instantiates', shadow: '_instantiates', required: false }, + { name: 'imports', shadow: '_imports', required: false }, + { name: 'format', shadow: '_format', required: true }, + { name: 'patchFormat', shadow: '_patchFormat', required: false }, + { + name: 'implementationGuide', + shadow: '_implementationGuide', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } }) .refine( - (value) => - [ - { - names: ['diagnosisCodeableConcept', 'diagnosisReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> + +export const carePlanActivitySchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + outcomeCodeableConcept: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + outcomeReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + progress: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + reference: z.lazy(() => referenceSchema).optional(), + detail: carePlanActivityDetailSchema.optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const claimProcedureSchema: z.ZodType = z.lazy(() => +export const carePlanActivityDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - type: z + kind: z + .enum([ + 'Appointment', + 'CommunicationRequest', + 'DeviceRequest', + 'MedicationRequest', + 'NutritionOrder', + 'ServiceRequest', + 'Task', + 'VisionPrescription', + ]) + .optional(), + _kind: z.lazy(() => elementSchema).optional(), + instantiatesCanonical: canonicalSchema + .nullable() + .array() + .min(1) + .optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + reasonCode: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - procedureCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - procedureReference: z.lazy(() => referenceSchema).optional(), - udi: z + reasonReference: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - }) - .refine( - (value) => - [ - { - names: ['procedureCodeableConcept', 'procedureReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const claimInsuranceSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - focal: booleanSchema, - _focal: z.lazy(() => elementSchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), - coverage: z.lazy(() => referenceSchema), - businessArrangement: stringSchema.optional(), - _businessArrangement: z.lazy(() => elementSchema).optional(), - preAuthRef: stringSchema.array().optional(), - _preAuthRef: z - .lazy(() => elementSchema) - .array() - .optional(), - claimResponse: z.lazy(() => referenceSchema).optional(), - }), -) - -export const claimAccidentSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + goal: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + status: z + .enum([ + 'cancelled', + 'completed', + 'entered-in-error', + 'in-progress', + 'not-started', + 'on-hold', + 'scheduled', + 'stopped', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + scheduledTiming: z.lazy(() => timingSchema).optional(), + scheduledPeriod: z.lazy(() => periodSchema).optional(), + scheduledString: stringSchema.optional(), + _scheduledString: z.lazy(() => elementSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + performer: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - date: dateSchema, - _date: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - locationAddress: z.lazy(() => addressSchema).optional(), - locationReference: z.lazy(() => referenceSchema).optional(), + productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + productReference: z.lazy(() => referenceSchema).optional(), + dailyAmount: z.lazy(() => quantitySchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), }) .refine( (value) => [ - { names: ['locationAddress', 'locationReference'], required: false }, + { + alternatives: [ + ['scheduledTiming'], + ['scheduledPeriod'], + ['scheduledString', '_scheduledString'], + ], + required: false, + }, + { + alternatives: [['productCodeableConcept'], ['productReference']], + required: false, + }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -3738,795 +4384,1311 @@ export const claimAccidentSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const claimItemSchema: z.ZodType = z.lazy(() => +export const carePlanSchema: z.ZodType> = z.lazy(() => z .strictObject({ + resourceType: z.literal('CarePlan'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - careTeamSequence: positiveIntSchema.array().optional(), - _careTeamSequence: z - .lazy(() => elementSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - diagnosisSequence: positiveIntSchema.array().optional(), - _diagnosisSequence: z - .lazy(() => elementSchema) + instantiatesCanonical: canonicalSchema + .nullable() .array() + .min(1) .optional(), - procedureSequence: positiveIntSchema.array().optional(), - _procedureSequence: z + _instantiatesCanonical: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - informationSequence: positiveIntSchema.array().optional(), - _informationSequence: z + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - revenue: z.lazy(() => codeableConceptSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - programCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - servicedDate: dateSchema.optional(), - _servicedDate: z.lazy(() => elementSchema).optional(), - servicedPeriod: z.lazy(() => periodSchema).optional(), - locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - locationAddress: z.lazy(() => addressSchema).optional(), - locationReference: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - udi: z + basedOn: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), - subSite: z - .lazy(() => codeableConceptSchema) + replaces: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - encounter: z + partOf: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - detail: claimItemDetailSchema.array().optional(), - }) - .refine( - (value) => - [ - { names: ['servicedDate', 'servicedPeriod'], required: false }, - { - names: [ - 'locationCodeableConcept', - 'locationAddress', - 'locationReference', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + status: z + .enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + intent: z.enum(['option', 'order', 'plan', 'proposal']).optional(), + _intent: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + contributor: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + careTeam: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + addresses: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + goal: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + activity: carePlanActivitySchema.array().min(1).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'intent', shadow: '_intent' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const claimItemDetailSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const careTeamParticipantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - revenue: z.lazy(() => codeableConceptSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - programCode: z + role: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - udi: z - .lazy(() => referenceSchema) - .array() - .optional(), - subDetail: claimItemDetailSubDetailSchema.array().optional(), - }), -) + member: z.lazy(() => referenceSchema).optional(), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const claimItemDetailSubDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const careTeamSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CareTeam'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - revenue: z.lazy(() => codeableConceptSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'active', + 'entered-in-error', + 'inactive', + 'proposed', + 'suspended', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - programCode: z + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + participant: careTeamParticipantSchema.array().min(1).optional(), + reasonCode: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - udi: z + reasonReference: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - }), - ) + managingOrganization: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const claimSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Claim'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - subType: z.lazy(() => codeableConceptSchema).optional(), - use: z.enum(['claim', 'preauthorization', 'predetermination']), - _use: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema), - billablePeriod: z.lazy(() => periodSchema).optional(), - created: dateTimeSchema, - _created: z.lazy(() => elementSchema).optional(), - enterer: z.lazy(() => referenceSchema).optional(), - insurer: z.lazy(() => referenceSchema).optional(), - provider: z.lazy(() => referenceSchema), - priority: z.lazy(() => codeableConceptSchema), - fundsReserve: z.lazy(() => codeableConceptSchema).optional(), - related: claimRelatedSchema.array().optional(), - prescription: z.lazy(() => referenceSchema).optional(), - originalPrescription: z.lazy(() => referenceSchema).optional(), - payee: claimPayeeSchema.optional(), - referral: z.lazy(() => referenceSchema).optional(), - facility: z.lazy(() => referenceSchema).optional(), - careTeam: claimCareTeamSchema.array().optional(), - supportingInfo: claimSupportingInfoSchema.array().optional(), - diagnosis: claimDiagnosisSchema.array().optional(), - procedure: claimProcedureSchema.array().optional(), - insurance: claimInsuranceSchema.array().min(1), - accident: claimAccidentSchema.optional(), - item: claimItemSchema.array().optional(), - total: z.lazy(() => moneySchema).optional(), - }), -) - -export const claimResponseItemSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const catalogEntryRelatedEntrySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - itemSequence: positiveIntSchema, - _itemSequence: z.lazy(() => elementSchema).optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) - .array() - .optional(), - adjudication: claimResponseItemAdjudicationSchema.array().min(1), - detail: claimResponseItemDetailSchema.array().optional(), - }), -) + relationtype: z.enum(['is-replaced-by', 'triggers']).optional(), + _relationtype: z.lazy(() => elementSchema).optional(), + item: z.lazy(() => referenceSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'relationtype', shadow: '_relationtype' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const claimResponseAddItemSchema: z.ZodType = - z.lazy(() => +export const catalogEntrySchema: z.ZodType> = z.lazy( + () => z .strictObject({ + resourceType: z.literal('CatalogEntry'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - itemSequence: positiveIntSchema.array().optional(), - _itemSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - detailSequence: positiveIntSchema.array().optional(), - _detailSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - subdetailSequence: positiveIntSchema.array().optional(), - _subdetailSequence: z - .lazy(() => elementSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - provider: z - .lazy(() => referenceSchema) + type: z.lazy(() => codeableConceptSchema).optional(), + orderable: booleanSchema.optional(), + _orderable: z.lazy(() => elementSchema).optional(), + referencedItem: z.lazy(() => referenceSchema), + additionalIdentifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z + classification: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - programCode: z + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + validityPeriod: z.lazy(() => periodSchema).optional(), + validTo: dateTimeSchema.optional(), + _validTo: z.lazy(() => elementSchema).optional(), + lastUpdated: dateTimeSchema.optional(), + _lastUpdated: z.lazy(() => elementSchema).optional(), + additionalCharacteristic: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - servicedDate: dateSchema.optional(), - _servicedDate: z.lazy(() => elementSchema).optional(), - servicedPeriod: z.lazy(() => periodSchema).optional(), - locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - locationAddress: z.lazy(() => addressSchema).optional(), - locationReference: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), - subSite: z + additionalClassification: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) - .array() - .optional(), - adjudication: z - .lazy(() => claimResponseItemAdjudicationSchema) - .array() - .min(1), - detail: claimResponseAddItemDetailSchema.array().optional(), + relatedEntry: catalogEntryRelatedEntrySchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'orderable', shadow: '_orderable' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { names: ['servicedDate', 'servicedPeriod'], required: false }, - { - names: [ - 'locationCodeableConcept', - 'locationAddress', - 'locationReference', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> -export const claimResponseItemAdjudicationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const chargeItemPerformerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - category: z.lazy(() => codeableConceptSchema), - reason: z.lazy(() => codeableConceptSchema).optional(), - amount: z.lazy(() => moneySchema).optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - }), - ) + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const claimResponseTotalSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - category: z.lazy(() => codeableConceptSchema), - amount: z.lazy(() => moneySchema), - }), - ) +export const chargeItemSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('ChargeItem'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + definitionUri: uriSchema.nullable().array().min(1).optional(), + _definitionUri: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + definitionCanonical: canonicalSchema + .nullable() + .array() + .min(1) + .optional(), + _definitionCanonical: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + status: z + .enum([ + 'aborted', + 'billable', + 'billed', + 'entered-in-error', + 'not-billable', + 'planned', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema), + context: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + performer: chargeItemPerformerSchema.array().min(1).optional(), + performingOrganization: z.lazy(() => referenceSchema).optional(), + requestingOrganization: z.lazy(() => referenceSchema).optional(), + costCenter: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + bodysite: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + factorOverride: decimalSchema.optional(), + _factorOverride: z.lazy(() => elementSchema).optional(), + priceOverride: z.lazy(() => moneySchema).optional(), + overrideReason: stringSchema.optional(), + _overrideReason: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + enteredDate: dateTimeSchema.optional(), + _enteredDate: z.lazy(() => elementSchema).optional(), + reason: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + service: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + productReference: z.lazy(() => referenceSchema).optional(), + productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + account: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], + ['occurrenceTiming'], + ], + required: false, + }, + { + alternatives: [['productReference'], ['productCodeableConcept']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'definitionUri', shadow: '_definitionUri', required: false }, + { + name: 'definitionCanonical', + shadow: '_definitionCanonical', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const claimResponsePaymentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const chargeItemDefinitionApplicabilitySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - adjustment: z.lazy(() => moneySchema).optional(), - adjustmentReason: z.lazy(() => codeableConceptSchema).optional(), - date: dateSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - amount: z.lazy(() => moneySchema), - identifier: z.lazy(() => identifierSchema).optional(), - }), - ) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + language: stringSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const claimResponseProcessNoteSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const chargeItemDefinitionPropertyGroupSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - number: positiveIntSchema.optional(), - _number: z.lazy(() => elementSchema).optional(), - type: z.enum(['display', 'print', 'printoper']).optional(), - _type: z.lazy(() => elementSchema).optional(), - text: stringSchema, - _text: z.lazy(() => elementSchema).optional(), - language: z.lazy(() => codeableConceptSchema).optional(), - }), - ) - -export const claimResponseInsuranceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + applicability: z + .lazy(() => chargeItemDefinitionApplicabilitySchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + priceComponent: chargeItemDefinitionPropertyGroupPriceComponentSchema .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - focal: booleanSchema, - _focal: z.lazy(() => elementSchema).optional(), - coverage: z.lazy(() => referenceSchema), - businessArrangement: stringSchema.optional(), - _businessArrangement: z.lazy(() => elementSchema).optional(), - claimResponse: z.lazy(() => referenceSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const claimResponseErrorSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const chargeItemDefinitionPropertyGroupPriceComponentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - itemSequence: positiveIntSchema.optional(), - _itemSequence: z.lazy(() => elementSchema).optional(), - detailSequence: positiveIntSchema.optional(), - _detailSequence: z.lazy(() => elementSchema).optional(), - subDetailSequence: positiveIntSchema.optional(), - _subDetailSequence: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema), - }), - ) + type: z + .enum([ + 'base', + 'deduction', + 'discount', + 'informational', + 'surcharge', + 'tax', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const claimResponseItemDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const chargeItemDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ChargeItemDefinition'), id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - detailSequence: positiveIntSchema, - _detailSequence: z.lazy(() => elementSchema).optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) .array() + .min(1) .optional(), - adjudication: z - .lazy(() => claimResponseItemAdjudicationSchema) - .array() - .min(1), - subDetail: claimResponseItemDetailSubDetailSchema.array().optional(), - }), - ) - -export const claimResponseAddItemDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + derivedFromUri: uriSchema.nullable().array().min(1).optional(), + _derivedFromUri: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - adjudication: z - .lazy(() => claimResponseItemAdjudicationSchema) - .array() - .min(1), - subDetail: claimResponseAddItemDetailSubDetailSchema.array().optional(), - }), - ) - -export const claimResponseItemDetailSubDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + partOf: canonicalSchema.nullable().array().min(1).optional(), + _partOf: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + replaces: canonicalSchema.nullable().array().min(1).optional(), + _replaces: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - subDetailSequence: positiveIntSchema, - _subDetailSequence: z.lazy(() => elementSchema).optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) - .array() - .optional(), - adjudication: z - .lazy(() => claimResponseItemAdjudicationSchema) - .array() - .optional(), - }), - ) - -export const claimResponseAddItemDetailSubDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z + jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) - .array() - .optional(), - adjudication: z - .lazy(() => claimResponseItemAdjudicationSchema) - .array() - .min(1), - }), - ) - -export const claimResponseSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('ClaimResponse'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - subType: z.lazy(() => codeableConceptSchema).optional(), - use: z.enum(['claim', 'preauthorization', 'predetermination']), - _use: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema), - created: dateTimeSchema, - _created: z.lazy(() => elementSchema).optional(), - insurer: z.lazy(() => referenceSchema), - requestor: z.lazy(() => referenceSchema).optional(), - request: z.lazy(() => referenceSchema).optional(), - outcome: z.enum(['complete', 'error', 'partial', 'queued']), - _outcome: z.lazy(() => elementSchema).optional(), - disposition: stringSchema.optional(), - _disposition: z.lazy(() => elementSchema).optional(), - preAuthRef: stringSchema.optional(), - _preAuthRef: z.lazy(() => elementSchema).optional(), - preAuthPeriod: z.lazy(() => periodSchema).optional(), - payeeType: z.lazy(() => codeableConceptSchema).optional(), - item: claimResponseItemSchema.array().optional(), - addItem: claimResponseAddItemSchema.array().optional(), - adjudication: z - .lazy(() => claimResponseItemAdjudicationSchema) - .array() - .optional(), - total: claimResponseTotalSchema.array().optional(), - payment: claimResponsePaymentSchema.optional(), - fundsReserve: z.lazy(() => codeableConceptSchema).optional(), - formCode: z.lazy(() => codeableConceptSchema).optional(), - form: z.lazy(() => attachmentSchema).optional(), - processNote: claimResponseProcessNoteSchema.array().optional(), - communicationRequest: z - .lazy(() => referenceSchema) - .array() - .optional(), - insurance: claimResponseInsuranceSchema.array().optional(), - error: claimResponseErrorSchema.array().optional(), - }), -) - -export const clinicalImpressionInvestigationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + instance: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + applicability: chargeItemDefinitionApplicabilitySchema .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema), - item: z - .lazy(() => referenceSchema) + propertyGroup: chargeItemDefinitionPropertyGroupSchema .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'status', shadow: '_status' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'derivedFromUri', shadow: '_derivedFromUri', required: false }, + { name: 'partOf', shadow: '_partOf', required: false }, + { name: 'replaces', shadow: '_replaces', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const claimRelatedSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + claim: z.lazy(() => referenceSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema).optional(), + reference: z.lazy(() => identifierSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const claimPayeeSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + party: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const clinicalImpressionFindingSchema: z.ZodType = +export const claimCareTeamSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + provider: z.lazy(() => referenceSchema), + responsible: booleanSchema.optional(), + _responsible: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + qualification: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const claimSupportingInfoSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - itemReference: z.lazy(() => referenceSchema).optional(), - basis: stringSchema.optional(), - _basis: z.lazy(() => elementSchema).optional(), - }), - ) + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + category: z.lazy(() => codeableConceptSchema), + code: z.lazy(() => codeableConceptSchema).optional(), + timingDate: dateSchema.optional(), + _timingDate: z.lazy(() => elementSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + reason: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['timingDate', '_timingDate'], ['timingPeriod']], + required: false, + }, + { + alternatives: [ + ['valueBoolean', '_valueBoolean'], + ['valueString', '_valueString'], + ['valueQuantity'], + ['valueAttachment'], + ['valueReference'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const clinicalImpressionSchema: z.ZodType = +export const claimDiagnosisSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('ClinicalImpression'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['completed', 'entered-in-error', 'in-progress']), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - effectiveDateTime: dateTimeSchema.optional(), - _effectiveDateTime: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - assessor: z.lazy(() => referenceSchema).optional(), - previous: z.lazy(() => referenceSchema).optional(), - problem: z - .lazy(() => referenceSchema) - .array() - .optional(), - investigation: clinicalImpressionInvestigationSchema.array().optional(), - protocol: uriSchema.array().optional(), - _protocol: z - .lazy(() => elementSchema) - .array() - .optional(), - summary: stringSchema.optional(), - _summary: z.lazy(() => elementSchema).optional(), - finding: clinicalImpressionFindingSchema.array().optional(), - prognosisCodeableConcept: z + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + diagnosisCodeableConcept: z .lazy(() => codeableConceptSchema) - .array() - .optional(), - prognosisReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - supportingInfo: z - .lazy(() => referenceSchema) - .array() .optional(), - note: z - .lazy(() => annotationSchema) + diagnosisReference: z.lazy(() => referenceSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), + onAdmission: z.lazy(() => codeableConceptSchema).optional(), + packageCode: z.lazy(() => codeableConceptSchema).optional(), }) .refine( (value) => [ { - names: ['effectiveDateTime', 'effectivePeriod'], - required: false, + alternatives: [ + ['diagnosisCodeableConcept'], + ['diagnosisReference'], + ], + required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -4534,127 +5696,32 @@ export const clinicalImpressionSchema: z.ZodType = message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) + ) as unknown as z.ZodType> -export const codeSystemFilterSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: codeSchema, - _code: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - operator: z - .enum([ - '=', - 'descendent-of', - 'exists', - 'generalizes', - 'in', - 'is-a', - 'is-not-a', - 'not-in', - 'regex', - ]) - .array() - .min(1), - _operator: z - .lazy(() => elementSchema) - .array() - .optional(), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - }), -) - -export const codeSystemPropertySchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: codeSchema, - _code: z.lazy(() => elementSchema).optional(), - uri: uriSchema.optional(), - _uri: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - type: z.enum([ - 'Coding', - 'boolean', - 'code', - 'dateTime', - 'decimal', - 'integer', - 'string', - ]), - _type: z.lazy(() => elementSchema).optional(), - }), - ) - -export const codeSystemConceptSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: codeSchema, - _code: z.lazy(() => elementSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - definition: stringSchema.optional(), - _definition: z.lazy(() => elementSchema).optional(), - designation: codeSystemConceptDesignationSchema.array().optional(), - property: codeSystemConceptPropertySchema.array().optional(), - concept: z - .lazy(() => codeSystemConceptSchema) - .array() - .optional(), - }), -) - -export const codeSystemConceptDesignationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - use: z.lazy(() => codingSchema).optional(), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - }), - ) - -export const codeSystemConceptPropertySchema: z.ZodType = +export const claimProcedureSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -4662,45 +5729,48 @@ export const codeSystemConceptPropertySchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + procedureCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + procedureReference: z.lazy(() => referenceSchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), - code: codeSchema, - _code: z.lazy(() => elementSchema).optional(), - valueCode: codeSchema.optional(), - _valueCode: z.lazy(() => elementSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), }) .refine( (value) => [ { - names: [ - 'valueCode', - 'valueCoding', - 'valueString', - 'valueInteger', - 'valueBoolean', - 'valueDateTime', - 'valueDecimal', + alternatives: [ + ['procedureCodeableConcept'], + ['procedureReference'], ], required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -4708,279 +5778,32 @@ export const codeSystemConceptPropertySchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('CodeSystem'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - caseSensitive: booleanSchema.optional(), - _caseSensitive: z.lazy(() => elementSchema).optional(), - valueSet: canonicalSchema.optional(), - _valueSet: z.lazy(() => elementSchema).optional(), - hierarchyMeaning: z - .enum(['classified-with', 'grouped-by', 'is-a', 'part-of']) - .optional(), - _hierarchyMeaning: z.lazy(() => elementSchema).optional(), - compositional: booleanSchema.optional(), - _compositional: z.lazy(() => elementSchema).optional(), - versionNeeded: booleanSchema.optional(), - _versionNeeded: z.lazy(() => elementSchema).optional(), - content: z.enum([ - 'complete', - 'example', - 'fragment', - 'not-present', - 'supplement', - ]), - _content: z.lazy(() => elementSchema).optional(), - supplements: canonicalSchema.optional(), - _supplements: z.lazy(() => elementSchema).optional(), - count: unsignedIntSchema.optional(), - _count: z.lazy(() => elementSchema).optional(), - filter: codeSystemFilterSchema.array().optional(), - property: codeSystemPropertySchema.array().optional(), - concept: codeSystemConceptSchema.array().optional(), - }), -) - -export const codeableConceptSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - coding: z - .lazy(() => codingSchema) - .array() - .optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - }), -) - -export const codingSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - userSelected: booleanSchema.optional(), - _userSelected: z.lazy(() => elementSchema).optional(), - }), -) - -export const communicationPayloadSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - contentString: stringSchema.optional(), - _contentString: z.lazy(() => elementSchema).optional(), - contentAttachment: z.lazy(() => attachmentSchema).optional(), - contentReference: z.lazy(() => referenceSchema).optional(), + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['contentString', 'contentAttachment', 'contentReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const communicationSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Communication'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - inResponseTo: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'completed', - 'entered-in-error', - 'in-progress', - 'not-done', - 'on-hold', - 'preparation', - 'stopped', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - medium: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - subject: z.lazy(() => referenceSchema).optional(), - topic: z.lazy(() => codeableConceptSchema).optional(), - about: z - .lazy(() => referenceSchema) - .array() - .optional(), - encounter: z.lazy(() => referenceSchema).optional(), - sent: dateTimeSchema.optional(), - _sent: z.lazy(() => elementSchema).optional(), - received: dateTimeSchema.optional(), - _received: z.lazy(() => elementSchema).optional(), - recipient: z - .lazy(() => referenceSchema) - .array() - .optional(), - sender: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - payload: communicationPayloadSchema.array().optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }), -) - -export const communicationRequestPayloadSchema: z.ZodType = +export const claimInsuranceSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -4988,138 +5811,136 @@ export const communicationRequestPayloadSchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - contentString: stringSchema.optional(), - _contentString: z.lazy(() => elementSchema).optional(), - contentAttachment: z.lazy(() => attachmentSchema).optional(), - contentReference: z.lazy(() => referenceSchema).optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + focal: booleanSchema.optional(), + _focal: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + coverage: z.lazy(() => referenceSchema), + businessArrangement: stringSchema.optional(), + _businessArrangement: z.lazy(() => elementSchema).optional(), + preAuthRef: stringSchema.nullable().array().min(1).optional(), + _preAuthRef: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + claimResponse: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'sequence', shadow: '_sequence' }, + { name: 'focal', shadow: '_focal' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'preAuthRef', shadow: '_preAuthRef', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } }) .refine( - (value) => - [ - { - names: ['contentString', 'contentAttachment', 'contentReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const communicationRequestSchema: z.ZodType = +export const claimAccidentSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('CommunicationRequest'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - replaces: z - .lazy(() => referenceSchema) - .array() - .optional(), - groupIdentifier: z.lazy(() => identifierSchema).optional(), - status: z.enum([ - 'active', - 'completed', - 'draft', - 'entered-in-error', - 'on-hold', - 'revoked', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: z.lazy(() => elementSchema).optional(), - medium: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - subject: z.lazy(() => referenceSchema).optional(), - about: z - .lazy(() => referenceSchema) - .array() - .optional(), - encounter: z.lazy(() => referenceSchema).optional(), - payload: communicationRequestPayloadSchema.array().optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: z.lazy(() => elementSchema).optional(), - requester: z.lazy(() => referenceSchema).optional(), - recipient: z - .lazy(() => referenceSchema) - .array() - .optional(), - sender: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ { - names: ['occurrenceDateTime', 'occurrencePeriod'], + alternatives: [['locationAddress'], ['locationReference']], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -5127,267 +5948,239 @@ export const communicationRequestSchema: z.ZodType = message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, - ), - ) - -export const compartmentDefinitionResourceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.enum([ - 'Account', - 'ActivityDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'EffectEvidenceSynthesis', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProduct', - 'MedicinalProductAuthorization', - 'MedicinalProductContraindication', - 'MedicinalProductIndication', - 'MedicinalProductIngredient', - 'MedicinalProductInteraction', - 'MedicinalProductManufactured', - 'MedicinalProductPackaged', - 'MedicinalProductPharmaceutical', - 'MedicinalProductUndesirableEffect', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'RiskEvidenceSynthesis', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'Substance', - 'SubstanceNucleicAcid', - 'SubstancePolymer', - 'SubstanceProtein', - 'SubstanceReferenceInformation', - 'SubstanceSourceMaterial', - 'SubstanceSpecification', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]), - _code: z.lazy(() => elementSchema).optional(), - param: stringSchema.array().optional(), - _param: z - .lazy(() => elementSchema) - .array() - .optional(), - documentation: stringSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - }), - ) + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'date', shadow: '_date' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const compartmentDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('CompartmentDefinition'), +export const claimItemSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema, - _url: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + careTeamSequence: positiveIntSchema.nullable().array().min(1).optional(), + _careTeamSequence: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) + diagnosisSequence: positiveIntSchema.nullable().array().min(1).optional(), + _diagnosisSequence: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - code: z.enum([ - 'Device', - 'Encounter', - 'Patient', - 'Practitioner', - 'RelatedPerson', - ]), - _code: z.lazy(() => elementSchema).optional(), - search: booleanSchema, - _search: z.lazy(() => elementSchema).optional(), - resource: compartmentDefinitionResourceSchema.array().optional(), - }), - ) - -export const compositionAttesterSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + procedureSequence: positiveIntSchema.nullable().array().min(1).optional(), + _procedureSequence: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + informationSequence: positiveIntSchema + .nullable() .array() + .min(1) .optional(), - mode: z.enum(['legal', 'official', 'personal', 'professional']), - _mode: z.lazy(() => elementSchema).optional(), - time: dateTimeSchema.optional(), - _time: z.lazy(() => elementSchema).optional(), - party: z.lazy(() => referenceSchema).optional(), - }), - ) + _informationSequence: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + encounter: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + detail: claimItemDetailSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['servicedDate', '_servicedDate'], + ['servicedPeriod'], + ], + required: false, + }, + { + alternatives: [ + ['locationCodeableConcept'], + ['locationAddress'], + ['locationReference'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'careTeamSequence', + shadow: '_careTeamSequence', + required: false, + }, + { + name: 'diagnosisSequence', + shadow: '_diagnosisSequence', + required: false, + }, + { + name: 'procedureSequence', + shadow: '_procedureSequence', + required: false, + }, + { + name: 'informationSequence', + shadow: '_informationSequence', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const compositionRelatesToSchema: z.ZodType = +export const claimItemDetailSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -5395,351 +6188,419 @@ export const compositionRelatesToSchema: z.ZodType = extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.enum(['appends', 'replaces', 'signs', 'transforms']), - _code: z.lazy(() => elementSchema).optional(), - targetIdentifier: z.lazy(() => identifierSchema).optional(), - targetReference: z.lazy(() => referenceSchema).optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + subDetail: claimItemDetailSubDetailSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { names: ['targetIdentifier', 'targetReference'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const compositionEventSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const claimItemDetailSubDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - period: z.lazy(() => periodSchema).optional(), - detail: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) - -export const compositionSectionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + programCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - author: z + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - focus: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const claimSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Claim'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), text: z.lazy(() => narrativeSchema).optional(), - mode: z.enum(['changes', 'snapshot', 'working']).optional(), - _mode: z.lazy(() => elementSchema).optional(), - orderedBy: z.lazy(() => codeableConceptSchema).optional(), - entry: z - .lazy(() => referenceSchema) - .array() - .optional(), - emptyReason: z.lazy(() => codeableConceptSchema).optional(), - section: z - .lazy(() => compositionSectionSchema) + contained: z + .lazy(() => resourceSchema) .array() + .min(1) .optional(), - }), - ) - -export const compositionSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Composition'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - status: z.enum(['amended', 'entered-in-error', 'final', 'preliminary']), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema, - _date: z.lazy(() => elementSchema).optional(), - author: z - .lazy(() => referenceSchema) - .array() - .min(1), - title: stringSchema, - _title: z.lazy(() => elementSchema).optional(), - confidentiality: z.enum(['L', 'M', 'N', 'R', 'U', 'V']).optional(), - _confidentiality: z.lazy(() => elementSchema).optional(), - attester: compositionAttesterSchema.array().optional(), - custodian: z.lazy(() => referenceSchema).optional(), - relatesTo: compositionRelatesToSchema.array().optional(), - event: compositionEventSchema.array().optional(), - section: compositionSectionSchema.array().optional(), - }), -) - -export const conceptMapGroupSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - source: uriSchema.optional(), - _source: z.lazy(() => elementSchema).optional(), - sourceVersion: stringSchema.optional(), - _sourceVersion: z.lazy(() => elementSchema).optional(), - target: uriSchema.optional(), - _target: z.lazy(() => elementSchema).optional(), - targetVersion: stringSchema.optional(), - _targetVersion: z.lazy(() => elementSchema).optional(), - element: conceptMapGroupElementSchema.array().min(1), - unmapped: conceptMapGroupUnmappedSchema.optional(), - }), -) - -export const conceptMapGroupElementSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) .optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - target: conceptMapGroupElementTargetSchema.array().optional(), - }), - ) + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + use: z.enum(['claim', 'preauthorization', 'predetermination']).optional(), + _use: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + billablePeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + insurer: z.lazy(() => referenceSchema).optional(), + provider: z.lazy(() => referenceSchema), + priority: z.lazy(() => codeableConceptSchema), + fundsReserve: z.lazy(() => codeableConceptSchema).optional(), + related: claimRelatedSchema.array().min(1).optional(), + prescription: z.lazy(() => referenceSchema).optional(), + originalPrescription: z.lazy(() => referenceSchema).optional(), + payee: claimPayeeSchema.optional(), + referral: z.lazy(() => referenceSchema).optional(), + facility: z.lazy(() => referenceSchema).optional(), + careTeam: claimCareTeamSchema.array().min(1).optional(), + supportingInfo: claimSupportingInfoSchema.array().min(1).optional(), + diagnosis: claimDiagnosisSchema.array().min(1).optional(), + procedure: claimProcedureSchema.array().min(1).optional(), + insurance: claimInsuranceSchema.array().min(1), + accident: claimAccidentSchema.optional(), + item: claimItemSchema.array().min(1).optional(), + total: z.lazy(() => moneySchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'use', shadow: '_use' }, + { name: 'created', shadow: '_created' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const conceptMapGroupUnmappedSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const claimResponseItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - mode: z.enum(['fixed', 'other-map', 'provided']), - _mode: z.lazy(() => elementSchema).optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - url: canonicalSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - }), - ) + itemSequence: positiveIntSchema.optional(), + _itemSequence: z.lazy(() => elementSchema).optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: claimResponseItemAdjudicationSchema.array().min(1), + detail: claimResponseItemDetailSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'itemSequence', shadow: '_itemSequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const conceptMapGroupElementTargetSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const claimResponseAddItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - equivalence: z.enum([ - 'disjoint', - 'equal', - 'equivalent', - 'inexact', - 'narrower', - 'relatedto', - 'specializes', - 'subsumes', - 'unmatched', - 'wider', - ]), - _equivalence: z.lazy(() => elementSchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - dependsOn: conceptMapGroupElementTargetDependsOnSchema.array().optional(), - product: z - .lazy(() => conceptMapGroupElementTargetDependsOnSchema) + itemSequence: positiveIntSchema.nullable().array().min(1).optional(), + _itemSequence: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - }), - ) - -export const conceptMapGroupElementTargetDependsOnSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + detailSequence: positiveIntSchema.nullable().array().min(1).optional(), + _detailSequence: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + subdetailSequence: positiveIntSchema.nullable().array().min(1).optional(), + _subdetailSequence: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - property: uriSchema, - _property: z.lazy(() => elementSchema).optional(), - system: canonicalSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - }), - ) - -export const conceptMapSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - resourceType: z.literal('ConceptMap'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + provider: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - extension: z - .lazy(() => extensionSchema) + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + programCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - sourceUri: uriSchema.optional(), - _sourceUri: z.lazy(() => elementSchema).optional(), - sourceCanonical: canonicalSchema.optional(), - _sourceCanonical: z.lazy(() => elementSchema).optional(), - targetUri: uriSchema.optional(), - _targetUri: z.lazy(() => elementSchema).optional(), - targetCanonical: canonicalSchema.optional(), - _targetCanonical: z.lazy(() => elementSchema).optional(), - group: conceptMapGroupSchema.array().optional(), + .min(1), + detail: claimResponseAddItemDetailSchema.array().min(1).optional(), }) .refine( (value) => [ - { names: ['sourceUri', 'sourceCanonical'], required: false }, - { names: ['targetUri', 'targetCanonical'], required: false }, + { + alternatives: [ + ['servicedDate', '_servicedDate'], + ['servicedPeriod'], + ], + required: false, + }, + { + alternatives: [ + ['locationCodeableConcept'], + ['locationAddress'], + ['locationReference'], + ], + required: false, + }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -5747,729 +6608,1006 @@ export const conceptMapSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'itemSequence', shadow: '_itemSequence', required: false }, + { name: 'detailSequence', shadow: '_detailSequence', required: false }, + { + name: 'subdetailSequence', + shadow: '_subdetailSequence', + required: false, + }, + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const conditionStageSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - summary: z.lazy(() => codeableConceptSchema).optional(), - assessment: z - .lazy(() => referenceSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - }), -) - -export const conditionEvidenceSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +) as unknown as z.ZodType> + +export const claimResponseItemAdjudicationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z - .lazy(() => codeableConceptSchema) + category: z.lazy(() => codeableConceptSchema), + reason: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const claimResponseTotalSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - detail: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), -) + category: z.lazy(() => codeableConceptSchema), + amount: z.lazy(() => moneySchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const conditionSchema: z.ZodType = z.lazy(() => +export const claimResponsePaymentSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Condition'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) + type: z.lazy(() => codeableConceptSchema), + adjustment: z.lazy(() => moneySchema).optional(), + adjustmentReason: z.lazy(() => codeableConceptSchema).optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema), + identifier: z.lazy(() => identifierSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const claimResponseProcessNoteSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - clinicalStatus: z.lazy(() => codeableConceptSchema).optional(), - verificationStatus: z.lazy(() => codeableConceptSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - severity: z.lazy(() => codeableConceptSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - bodySite: z - .lazy(() => codeableConceptSchema) + number: positiveIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + type: z.enum(['display', 'print', 'printoper']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + language: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'text', shadow: '_text' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const claimResponseInsuranceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - onsetDateTime: dateTimeSchema.optional(), - _onsetDateTime: z.lazy(() => elementSchema).optional(), - onsetAge: z.lazy(() => ageSchema).optional(), - onsetPeriod: z.lazy(() => periodSchema).optional(), - onsetRange: z.lazy(() => rangeSchema).optional(), - onsetString: stringSchema.optional(), - _onsetString: z.lazy(() => elementSchema).optional(), - abatementDateTime: dateTimeSchema.optional(), - _abatementDateTime: z.lazy(() => elementSchema).optional(), - abatementAge: z.lazy(() => ageSchema).optional(), - abatementPeriod: z.lazy(() => periodSchema).optional(), - abatementRange: z.lazy(() => rangeSchema).optional(), - abatementString: stringSchema.optional(), - _abatementString: z.lazy(() => elementSchema).optional(), - recordedDate: dateTimeSchema.optional(), - _recordedDate: z.lazy(() => elementSchema).optional(), - recorder: z.lazy(() => referenceSchema).optional(), - asserter: z.lazy(() => referenceSchema).optional(), - stage: conditionStageSchema.array().optional(), - evidence: conditionEvidenceSchema.array().optional(), - note: z - .lazy(() => annotationSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + focal: booleanSchema.optional(), + _focal: z.lazy(() => elementSchema).optional(), + coverage: z.lazy(() => referenceSchema), + businessArrangement: stringSchema.optional(), + _businessArrangement: z.lazy(() => elementSchema).optional(), + claimResponse: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'sequence', shadow: '_sequence' }, + { name: 'focal', shadow: '_focal' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: [ - 'onsetDateTime', - 'onsetAge', - 'onsetPeriod', - 'onsetRange', - 'onsetString', - ], - required: false, - }, - { - names: [ - 'abatementDateTime', - 'abatementAge', - 'abatementPeriod', - 'abatementRange', - 'abatementString', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const consentPolicySchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - authority: uriSchema.optional(), - _authority: z.lazy(() => elementSchema).optional(), - uri: uriSchema.optional(), - _uri: z.lazy(() => elementSchema).optional(), - }), -) - -export const consentVerificationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +) as unknown as z.ZodType> + +export const claimResponseErrorSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - verified: booleanSchema, - _verified: z.lazy(() => elementSchema).optional(), - verifiedWith: z.lazy(() => referenceSchema).optional(), - verificationDate: dateTimeSchema.optional(), - _verificationDate: z.lazy(() => elementSchema).optional(), - }), - ) + itemSequence: positiveIntSchema.optional(), + _itemSequence: z.lazy(() => elementSchema).optional(), + detailSequence: positiveIntSchema.optional(), + _detailSequence: z.lazy(() => elementSchema).optional(), + subDetailSequence: positiveIntSchema.optional(), + _subDetailSequence: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const consentProvisionSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const claimResponseItemDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum(['deny', 'permit']).optional(), - _type: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - actor: consentProvisionActorSchema.array().optional(), - action: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - securityLabel: z - .lazy(() => codingSchema) - .array() - .optional(), - purpose: z - .lazy(() => codingSchema) - .array() - .optional(), - class: z - .lazy(() => codingSchema) + detailSequence: positiveIntSchema.optional(), + _detailSequence: z.lazy(() => elementSchema).optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - code: z - .lazy(() => codeableConceptSchema) + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) .array() - .optional(), - dataPeriod: z.lazy(() => periodSchema).optional(), - data: consentProvisionDataSchema.array().optional(), - provision: z - .lazy(() => consentProvisionSchema) + .min(1), + subDetail: claimResponseItemDetailSubDetailSchema .array() + .min(1) .optional(), - }), -) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'detailSequence', shadow: '_detailSequence' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const consentProvisionActorSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const claimResponseAddItemDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - role: z.lazy(() => codeableConceptSchema), - reference: z.lazy(() => referenceSchema), - }), - ) - -export const consentProvisionDataSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - meaning: z.enum(['authoredby', 'dependents', 'instance', 'related']), - _meaning: z.lazy(() => elementSchema).optional(), - reference: z.lazy(() => referenceSchema), - }), - ) + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1), + subDetail: claimResponseAddItemDetailSubDetailSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const consentSchema: z.ZodType = z.lazy(() => +export const claimResponseItemDetailSubDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Consent'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'draft', - 'entered-in-error', - 'inactive', - 'proposed', - 'rejected', - ]), - _status: z.lazy(() => elementSchema).optional(), - scope: z.lazy(() => codeableConceptSchema), - category: z - .lazy(() => codeableConceptSchema) - .array() - .min(1), - patient: z.lazy(() => referenceSchema).optional(), - dateTime: dateTimeSchema.optional(), - _dateTime: z.lazy(() => elementSchema).optional(), - performer: z - .lazy(() => referenceSchema) + subDetailSequence: positiveIntSchema.optional(), + _subDetailSequence: z.lazy(() => elementSchema).optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - organization: z - .lazy(() => referenceSchema) + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) .array() + .min(1) .optional(), - sourceAttachment: z.lazy(() => attachmentSchema).optional(), - sourceReference: z.lazy(() => referenceSchema).optional(), - policy: consentPolicySchema.array().optional(), - policyRule: z.lazy(() => codeableConceptSchema).optional(), - verification: consentVerificationSchema.array().optional(), - provision: consentProvisionSchema.optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'subDetailSequence', shadow: '_subDetailSequence' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } }) .refine( - (value) => - [ - { names: ['sourceAttachment', 'sourceReference'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const contactDetailSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - }), -) - -export const contactPointSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - system: z - .enum(['email', 'fax', 'other', 'pager', 'phone', 'sms', 'url']) - .optional(), - _system: z.lazy(() => elementSchema).optional(), - value: stringSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - use: z.enum(['home', 'mobile', 'old', 'temp', 'work']).optional(), - _use: z.lazy(() => elementSchema).optional(), - rank: positiveIntSchema.optional(), - _rank: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), -) - -export const contractContentDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - subType: z.lazy(() => codeableConceptSchema).optional(), - publisher: z.lazy(() => referenceSchema).optional(), - publicationDate: dateTimeSchema.optional(), - _publicationDate: z.lazy(() => elementSchema).optional(), - publicationStatus: z.enum([ - 'amended', - 'appended', - 'cancelled', - 'disputed', - 'entered-in-error', - 'executable', - 'executed', - 'negotiable', - 'offered', - 'policy', - 'rejected', - 'renewed', - 'resolved', - 'revoked', - 'terminated', - ]), - _publicationStatus: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - }), - ) +) as unknown as z.ZodType> -export const contractTermSchema: z.ZodType = z.lazy(() => +export const claimResponseAddItemDetailSubDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - issued: dateTimeSchema.optional(), - _issued: z.lazy(() => elementSchema).optional(), - applies: z.lazy(() => periodSchema).optional(), - topicCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - topicReference: z.lazy(() => referenceSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - subType: z.lazy(() => codeableConceptSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - securityLabel: contractTermSecurityLabelSchema.array().optional(), - offer: contractTermOfferSchema, - asset: contractTermAssetSchema.array().optional(), - action: contractTermActionSchema.array().optional(), - group: z - .lazy(() => contractTermSchema) + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } }) .refine( - (value) => - [ - { - names: ['topicCodeableConcept', 'topicReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const contractSignerSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codingSchema), - party: z.lazy(() => referenceSchema), - signature: z - .lazy(() => signatureSchema) - .array() - .min(1), - }), -) - -export const contractFriendlySchema: z.ZodType = z.lazy( - () => +) as unknown as z.ZodType> + +export const claimResponseSchema: z.ZodType> = + z.lazy(() => z .strictObject({ + resourceType: z.literal('ClaimResponse'), id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - contentAttachment: z.lazy(() => attachmentSchema).optional(), - contentReference: z.lazy(() => referenceSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + use: z + .enum(['claim', 'preauthorization', 'predetermination']) + .optional(), + _use: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + insurer: z.lazy(() => referenceSchema), + requestor: z.lazy(() => referenceSchema).optional(), + request: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + preAuthRef: stringSchema.optional(), + _preAuthRef: z.lazy(() => elementSchema).optional(), + preAuthPeriod: z.lazy(() => periodSchema).optional(), + payeeType: z.lazy(() => codeableConceptSchema).optional(), + item: claimResponseItemSchema.array().min(1).optional(), + addItem: claimResponseAddItemSchema.array().min(1).optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1) + .optional(), + total: claimResponseTotalSchema.array().min(1).optional(), + payment: claimResponsePaymentSchema.optional(), + fundsReserve: z.lazy(() => codeableConceptSchema).optional(), + formCode: z.lazy(() => codeableConceptSchema).optional(), + form: z.lazy(() => attachmentSchema).optional(), + processNote: claimResponseProcessNoteSchema.array().min(1).optional(), + communicationRequest: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + insurance: claimResponseInsuranceSchema.array().min(1).optional(), + error: claimResponseErrorSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'use', shadow: '_use' }, + { name: 'created', shadow: '_created' }, + { name: 'outcome', shadow: '_outcome' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['contentAttachment', 'contentReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), -) + ) as unknown as z.ZodType> -export const contractLegalSchema: z.ZodType = z.lazy(() => +export const clinicalImpressionInvestigationSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + item: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), - contentAttachment: z.lazy(() => attachmentSchema).optional(), - contentReference: z.lazy(() => referenceSchema).optional(), }) .refine( - (value) => - [ - { names: ['contentAttachment', 'contentReference'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const contractRuleSchema: z.ZodType = z.lazy(() => +export const clinicalImpressionFindingSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - contentAttachment: z.lazy(() => attachmentSchema).optional(), - contentReference: z.lazy(() => referenceSchema).optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + basis: stringSchema.optional(), + _basis: z.lazy(() => elementSchema).optional(), }) .refine( - (value) => - [ - { names: ['contentAttachment', 'contentReference'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const contractTermSecurityLabelSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const clinicalImpressionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ClinicalImpression'), id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - number: unsignedIntSchema.array().optional(), - _number: z - .lazy(() => elementSchema) - .array() - .optional(), - classification: z.lazy(() => codingSchema), - category: z - .lazy(() => codingSchema) - .array() - .optional(), - control: z - .lazy(() => codingSchema) + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) .array() + .min(1) .optional(), - }), - ) - -export const contractTermOfferSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - party: contractTermOfferPartySchema.array().optional(), - topic: z.lazy(() => referenceSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - decision: z.lazy(() => codeableConceptSchema).optional(), - decisionMode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - answer: contractTermOfferAnswerSchema.array().optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - linkId: stringSchema.array().optional(), - _linkId: z - .lazy(() => elementSchema) - .array() + status: z + .enum(['completed', 'entered-in-error', 'in-progress']) .optional(), - securityLabelNumber: unsignedIntSchema.array().optional(), - _securityLabelNumber: z - .lazy(() => elementSchema) + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + assessor: z.lazy(() => referenceSchema).optional(), + previous: z.lazy(() => referenceSchema).optional(), + problem: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - }), -) - -export const contractTermAssetSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + investigation: clinicalImpressionInvestigationSchema .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + protocol: uriSchema.nullable().array().min(1).optional(), + _protocol: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - scope: z.lazy(() => codeableConceptSchema).optional(), - type: z + summary: stringSchema.optional(), + _summary: z.lazy(() => elementSchema).optional(), + finding: clinicalImpressionFindingSchema.array().min(1).optional(), + prognosisCodeableConcept: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - typeReference: z + prognosisReference: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - subtype: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - relationship: z.lazy(() => codingSchema).optional(), - context: contractTermAssetContextSchema.array().optional(), - condition: stringSchema.optional(), - _condition: z.lazy(() => elementSchema).optional(), - periodType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - period: z - .lazy(() => periodSchema) - .array() - .optional(), - usePeriod: z - .lazy(() => periodSchema) - .array() - .optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - linkId: stringSchema.array().optional(), - _linkId: z - .lazy(() => elementSchema) - .array() - .optional(), - answer: z - .lazy(() => contractTermOfferAnswerSchema) + supportingInfo: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - securityLabelNumber: unsignedIntSchema.array().optional(), - _securityLabelNumber: z - .lazy(() => elementSchema) + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - valuedItem: contractTermAssetValuedItemSchema.array().optional(), - }), -) + }) + .refine( + (value) => + [ + { + alternatives: [ + ['effectiveDateTime', '_effectiveDateTime'], + ['effectivePeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'protocol', shadow: '_protocol', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const contractTermActionSchema: z.ZodType = +export const codeSystemFilterSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -6477,423 +7615,322 @@ export const contractTermActionSchema: z.ZodType = extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - subject: contractTermActionSubjectSchema.array().optional(), - intent: z.lazy(() => codeableConceptSchema), - linkId: stringSchema.array().optional(), - _linkId: z - .lazy(() => elementSchema) - .array() - .optional(), - status: z.lazy(() => codeableConceptSchema), - context: z.lazy(() => referenceSchema).optional(), - contextLinkId: stringSchema.array().optional(), - _contextLinkId: z - .lazy(() => elementSchema) - .array() - .optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - occurrenceTiming: z.lazy(() => timingSchema).optional(), - requester: z - .lazy(() => referenceSchema) - .array() - .optional(), - requesterLinkId: stringSchema.array().optional(), - _requesterLinkId: z - .lazy(() => elementSchema) - .array() - .optional(), - performerType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - performerRole: z.lazy(() => codeableConceptSchema).optional(), - performer: z.lazy(() => referenceSchema).optional(), - performerLinkId: stringSchema.array().optional(), - _performerLinkId: z - .lazy(() => elementSchema) - .array() - .optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + operator: z + .enum([ + '=', + 'descendent-of', + 'exists', + 'generalizes', + 'in', + 'is-a', + 'is-not-a', + 'not-in', + 'regex', + ]) + .nullable() .array() + .min(1) .optional(), - reason: stringSchema.array().optional(), - _reason: z - .lazy(() => elementSchema) - .array() - .optional(), - reasonLinkId: stringSchema.array().optional(), - _reasonLinkId: z - .lazy(() => elementSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - securityLabelNumber: unsignedIntSchema.array().optional(), - _securityLabelNumber: z + _operator: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'code', shadow: '_code' }, + { name: 'value', shadow: '_value' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'operator', shadow: '_operator', required: true }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } }) .refine( - (value) => - [ - { - names: [ - 'occurrenceDateTime', - 'occurrencePeriod', - 'occurrenceTiming', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const contractTermOfferPartySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const codeSystemPropertySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - reference: z - .lazy(() => referenceSchema) - .array() - .min(1), - role: z.lazy(() => codeableConceptSchema), - }), - ) - -export const contractTermOfferAnswerSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueBoolean', - 'valueDecimal', - 'valueInteger', - 'valueDate', - 'valueDateTime', - 'valueTime', - 'valueString', - 'valueUri', - 'valueAttachment', - 'valueCoding', - 'valueQuantity', - 'valueReference', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + uri: uriSchema.optional(), + _uri: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'Coding', + 'boolean', + 'code', + 'dateTime', + 'decimal', + 'integer', + 'string', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'code', shadow: '_code' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const contractTermAssetContextSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const codeSystemConceptSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - reference: z.lazy(() => referenceSchema).optional(), - code: z - .lazy(() => codeableConceptSchema) + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + definition: stringSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + designation: codeSystemConceptDesignationSchema.array().min(1).optional(), + property: codeSystemConceptPropertySchema.array().min(1).optional(), + concept: z + .lazy(() => codeSystemConceptSchema) .array() + .min(1) .optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - }), - ) - -export const contractTermAssetValuedItemSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - entityCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - entityReference: z.lazy(() => referenceSchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), - effectiveTime: dateTimeSchema.optional(), - _effectiveTime: z.lazy(() => elementSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - points: decimalSchema.optional(), - _points: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - payment: stringSchema.optional(), - _payment: z.lazy(() => elementSchema).optional(), - paymentDate: dateTimeSchema.optional(), - _paymentDate: z.lazy(() => elementSchema).optional(), - responsible: z.lazy(() => referenceSchema).optional(), - recipient: z.lazy(() => referenceSchema).optional(), - linkId: stringSchema.array().optional(), - _linkId: z - .lazy(() => elementSchema) - .array() - .optional(), - securityLabelNumber: unsignedIntSchema.array().optional(), - _securityLabelNumber: z - .lazy(() => elementSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['entityCodeableConcept', 'entityReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const contractTermActionSubjectSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const codeSystemConceptDesignationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - reference: z - .lazy(() => referenceSchema) - .array() - .min(1), - role: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + use: z.lazy(() => codingSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'value', shadow: '_value' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const contractSchema: z.ZodType = z.lazy(() => +export const codeSystemConceptPropertySchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Contract'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - status: z - .enum([ - 'amended', - 'appended', - 'cancelled', - 'disputed', - 'entered-in-error', - 'executable', - 'executed', - 'negotiable', - 'offered', - 'policy', - 'rejected', - 'renewed', - 'resolved', - 'revoked', - 'terminated', - ]) - .optional(), - _status: z.lazy(() => elementSchema).optional(), - legalState: z.lazy(() => codeableConceptSchema).optional(), - instantiatesCanonical: z.lazy(() => referenceSchema).optional(), - instantiatesUri: uriSchema.optional(), - _instantiatesUri: z.lazy(() => elementSchema).optional(), - contentDerivative: z.lazy(() => codeableConceptSchema).optional(), - issued: dateTimeSchema.optional(), - _issued: z.lazy(() => elementSchema).optional(), - applies: z.lazy(() => periodSchema).optional(), - expirationType: z.lazy(() => codeableConceptSchema).optional(), - subject: z - .lazy(() => referenceSchema) - .array() - .optional(), - authority: z - .lazy(() => referenceSchema) - .array() - .optional(), - domain: z - .lazy(() => referenceSchema) - .array() - .optional(), - site: z - .lazy(() => referenceSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - alias: stringSchema.array().optional(), - _alias: z - .lazy(() => elementSchema) - .array() - .optional(), - author: z.lazy(() => referenceSchema).optional(), - scope: z.lazy(() => codeableConceptSchema).optional(), - topicCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - topicReference: z.lazy(() => referenceSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - subType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - contentDefinition: contractContentDefinitionSchema.optional(), - term: contractTermSchema.array().optional(), - supportingInfo: z - .lazy(() => referenceSchema) - .array() - .optional(), - relevantHistory: z - .lazy(() => referenceSchema) - .array() - .optional(), - signer: contractSignerSchema.array().optional(), - friendly: contractFriendlySchema.array().optional(), - legal: contractLegalSchema.array().optional(), - rule: contractRuleSchema.array().optional(), - legallyBindingAttachment: z.lazy(() => attachmentSchema).optional(), - legallyBindingReference: z.lazy(() => referenceSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), }) .refine( (value) => [ { - names: ['topicCodeableConcept', 'topicReference'], - required: false, - }, - { - names: ['legallyBindingAttachment', 'legallyBindingReference'], - required: false, + alternatives: [ + ['valueCode', '_valueCode'], + ['valueCoding'], + ['valueString', '_valueString'], + ['valueInteger', '_valueInteger'], + ['valueBoolean', '_valueBoolean'], + ['valueDateTime', '_valueDateTime'], + ['valueDecimal', '_valueDecimal'], + ], + required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -6901,666 +7938,693 @@ export const contractSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const contributorSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum(['author', 'editor', 'endorser', 'reviewer']), - _type: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - }), -) - -export const countSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - comparator: z.enum(['<', '<=', '>', '>=']).optional(), - _comparator: z.lazy(() => elementSchema).optional(), - unit: stringSchema.optional(), - _unit: z.lazy(() => elementSchema).optional(), - system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - }), -) - -export const coverageClassSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - }), -) - -export const coverageCostToBeneficiarySchema: z.ZodType = - z.lazy(() => +) as unknown as z.ZodType> + +export const codeSystemSchema: z.ZodType> = z.lazy( + () => z .strictObject({ + resourceType: z.literal('CodeSystem'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueMoney: z.lazy(() => moneySchema).optional(), - exception: coverageCostToBeneficiaryExceptionSchema.array().optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + caseSensitive: booleanSchema.optional(), + _caseSensitive: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema.optional(), + _valueSet: z.lazy(() => elementSchema).optional(), + hierarchyMeaning: z + .enum(['classified-with', 'grouped-by', 'is-a', 'part-of']) + .optional(), + _hierarchyMeaning: z.lazy(() => elementSchema).optional(), + compositional: booleanSchema.optional(), + _compositional: z.lazy(() => elementSchema).optional(), + versionNeeded: booleanSchema.optional(), + _versionNeeded: z.lazy(() => elementSchema).optional(), + content: z + .enum([ + 'complete', + 'example', + 'fragment', + 'not-present', + 'supplement', + ]) + .optional(), + _content: z.lazy(() => elementSchema).optional(), + supplements: canonicalSchema.optional(), + _supplements: z.lazy(() => elementSchema).optional(), + count: unsignedIntSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + filter: codeSystemFilterSchema.array().min(1).optional(), + property: codeSystemPropertySchema.array().min(1).optional(), + concept: codeSystemConceptSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'content', shadow: '_content' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [{ names: ['valueQuantity', 'valueMoney'], required: true }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) - -export const coverageCostToBeneficiaryExceptionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - period: z.lazy(() => periodSchema).optional(), - }), - ) +) as unknown as z.ZodType> -export const coverageSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Coverage'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - policyHolder: z.lazy(() => referenceSchema).optional(), - subscriber: z.lazy(() => referenceSchema).optional(), - subscriberId: stringSchema.optional(), - _subscriberId: z.lazy(() => elementSchema).optional(), - beneficiary: z.lazy(() => referenceSchema), - dependent: stringSchema.optional(), - _dependent: z.lazy(() => elementSchema).optional(), - relationship: z.lazy(() => codeableConceptSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - payor: z - .lazy(() => referenceSchema) - .array() - .min(1), - class: coverageClassSchema.array().optional(), - order: positiveIntSchema.optional(), - _order: z.lazy(() => elementSchema).optional(), - network: stringSchema.optional(), - _network: z.lazy(() => elementSchema).optional(), - costToBeneficiary: coverageCostToBeneficiarySchema.array().optional(), - subrogation: booleanSchema.optional(), - _subrogation: z.lazy(() => elementSchema).optional(), - contract: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) - -export const coverageEligibilityRequestSupportingInfoSchema: z.ZodType = +export const codeableConceptSchema: z.ZodType> = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - information: z.lazy(() => referenceSchema), - appliesToAll: booleanSchema.optional(), - _appliesToAll: z.lazy(() => elementSchema).optional(), - }), - ) + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + coding: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const coverageEligibilityRequestInsuranceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const codingSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - focal: booleanSchema.optional(), - _focal: z.lazy(() => elementSchema).optional(), - coverage: z.lazy(() => referenceSchema), - businessArrangement: stringSchema.optional(), - _businessArrangement: z.lazy(() => elementSchema).optional(), - }), - ) + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + userSelected: booleanSchema.optional(), + _userSelected: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const coverageEligibilityRequestItemSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const communicationPayloadSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - supportingInfoSequence: positiveIntSchema.array().optional(), - _supportingInfoSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema).optional(), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - provider: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - facility: z.lazy(() => referenceSchema).optional(), - diagnosis: coverageEligibilityRequestItemDiagnosisSchema - .array() - .optional(), - detail: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), - ) + contentString: stringSchema.optional(), + _contentString: z.lazy(() => elementSchema).optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['contentString', '_contentString'], + ['contentAttachment'], + ['contentReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const coverageEligibilityRequestItemDiagnosisSchema: z.ZodType = +export const communicationSchema: z.ZodType> = z.lazy(() => z .strictObject({ + resourceType: z.literal('Communication'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - diagnosisCodeableConcept: z - .lazy(() => codeableConceptSchema) + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) .optional(), - diagnosisReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['diagnosisCodeableConcept', 'diagnosisReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const coverageEligibilityRequestSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - resourceType: z.literal('CoverageEligibilityRequest'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + instantiatesCanonical: canonicalSchema + .nullable() .array() + .min(1) .optional(), - extension: z - .lazy(() => extensionSchema) + _instantiatesCanonical: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) + basedOn: z + .lazy(() => referenceSchema) .array() + .min(1) + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + inResponseTo: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', + ]) .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), _status: z.lazy(() => elementSchema).optional(), - priority: z.lazy(() => codeableConceptSchema).optional(), - purpose: z - .enum(['auth-requirements', 'benefits', 'discovery', 'validation']) + statusReason: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) .array() - .min(1), - _purpose: z - .lazy(() => elementSchema) + .min(1) + .optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + medium: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - patient: z.lazy(() => referenceSchema), - servicedDate: dateSchema.optional(), - _servicedDate: z.lazy(() => elementSchema).optional(), - servicedPeriod: z.lazy(() => periodSchema).optional(), - created: dateTimeSchema, - _created: z.lazy(() => elementSchema).optional(), - enterer: z.lazy(() => referenceSchema).optional(), - provider: z.lazy(() => referenceSchema).optional(), - insurer: z.lazy(() => referenceSchema), - facility: z.lazy(() => referenceSchema).optional(), - supportingInfo: coverageEligibilityRequestSupportingInfoSchema + subject: z.lazy(() => referenceSchema).optional(), + topic: z.lazy(() => codeableConceptSchema).optional(), + about: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + encounter: z.lazy(() => referenceSchema).optional(), + sent: dateTimeSchema.optional(), + _sent: z.lazy(() => elementSchema).optional(), + received: dateTimeSchema.optional(), + _received: z.lazy(() => elementSchema).optional(), + recipient: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + sender: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + payload: communicationPayloadSchema.array().min(1).optional(), + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - insurance: coverageEligibilityRequestInsuranceSchema.array().optional(), - item: coverageEligibilityRequestItemSchema.array().optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } }) .refine( - (value) => - [ - { names: ['servicedDate', 'servicedPeriod'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const coverageEligibilityResponseInsuranceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const communicationRequestPayloadSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - coverage: z.lazy(() => referenceSchema), - inforce: booleanSchema.optional(), - _inforce: z.lazy(() => elementSchema).optional(), - benefitPeriod: z.lazy(() => periodSchema).optional(), - item: coverageEligibilityResponseInsuranceItemSchema.array().optional(), - }), - ) + contentString: stringSchema.optional(), + _contentString: z.lazy(() => elementSchema).optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['contentString', '_contentString'], + ['contentAttachment'], + ['contentReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const coverageEligibilityResponseErrorSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const communicationRequestSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CommunicationRequest'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema), - }), - ) - -export const coverageEligibilityResponseInsuranceItemSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + basedOn: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema).optional(), - modifier: z + replaces: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + status: z + .enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + category: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - provider: z.lazy(() => referenceSchema).optional(), - excluded: booleanSchema.optional(), - _excluded: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - network: z.lazy(() => codeableConceptSchema).optional(), - unit: z.lazy(() => codeableConceptSchema).optional(), - term: z.lazy(() => codeableConceptSchema).optional(), - benefit: coverageEligibilityResponseInsuranceItemBenefitSchema + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + medium: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - authorizationRequired: booleanSchema.optional(), - _authorizationRequired: z.lazy(() => elementSchema).optional(), - authorizationSupporting: z + subject: z.lazy(() => referenceSchema).optional(), + about: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + encounter: z.lazy(() => referenceSchema).optional(), + payload: communicationRequestPayloadSchema.array().min(1).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + recipient: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + sender: z.lazy(() => referenceSchema).optional(), + reasonCode: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - authorizationUrl: uriSchema.optional(), - _authorizationUrl: z.lazy(() => elementSchema).optional(), - }), - ) - -export const coverageEligibilityResponseInsuranceItemBenefitSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - allowedUnsignedInt: unsignedIntSchema.optional(), - _allowedUnsignedInt: z.lazy(() => elementSchema).optional(), - allowedString: stringSchema.optional(), - _allowedString: z.lazy(() => elementSchema).optional(), - allowedMoney: z.lazy(() => moneySchema).optional(), - usedUnsignedInt: unsignedIntSchema.optional(), - _usedUnsignedInt: z.lazy(() => elementSchema).optional(), - usedString: stringSchema.optional(), - _usedString: z.lazy(() => elementSchema).optional(), - usedMoney: z.lazy(() => moneySchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['allowedUnsignedInt', 'allowedString', 'allowedMoney'], - required: false, - }, - { - names: ['usedUnsignedInt', 'usedString', 'usedMoney'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const coverageEligibilityResponseSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - resourceType: z.literal('CoverageEligibilityResponse'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), - _status: z.lazy(() => elementSchema).optional(), - purpose: z - .enum(['auth-requirements', 'benefits', 'discovery', 'validation']) - .array() - .min(1), - _purpose: z - .lazy(() => elementSchema) - .array() - .optional(), - patient: z.lazy(() => referenceSchema), - servicedDate: dateSchema.optional(), - _servicedDate: z.lazy(() => elementSchema).optional(), - servicedPeriod: z.lazy(() => periodSchema).optional(), - created: dateTimeSchema, - _created: z.lazy(() => elementSchema).optional(), - requestor: z.lazy(() => referenceSchema).optional(), - request: z.lazy(() => referenceSchema), - outcome: z.enum(['complete', 'error', 'partial', 'queued']), - _outcome: z.lazy(() => elementSchema).optional(), - disposition: stringSchema.optional(), - _disposition: z.lazy(() => elementSchema).optional(), - insurer: z.lazy(() => referenceSchema), - insurance: coverageEligibilityResponseInsuranceSchema - .array() - .optional(), - preAuthRef: stringSchema.optional(), - _preAuthRef: z.lazy(() => elementSchema).optional(), - form: z.lazy(() => codeableConceptSchema).optional(), - error: coverageEligibilityResponseErrorSchema.array().optional(), - }) - .refine( - (value) => - [ - { names: ['servicedDate', 'servicedPeriod'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const dataRequirementCodeFilterSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + reasonReference: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - path: stringSchema.optional(), - _path: z.lazy(() => elementSchema).optional(), - searchParam: stringSchema.optional(), - _searchParam: z.lazy(() => elementSchema).optional(), - valueSet: canonicalSchema.optional(), - _valueSet: z.lazy(() => elementSchema).optional(), - code: z - .lazy(() => codingSchema) + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - }), - ) - -export const dataRequirementDateFilterSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - path: stringSchema.optional(), - _path: z.lazy(() => elementSchema).optional(), - searchParam: stringSchema.optional(), - _searchParam: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valuePeriod: z.lazy(() => periodSchema).optional(), - valueDuration: z.lazy(() => durationSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['valueDateTime', 'valuePeriod', 'valueDuration'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( + }) + .refine( + (value) => + [ + { + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const dataRequirementSortSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const compartmentDefinitionResourceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - path: stringSchema, - _path: z.lazy(() => elementSchema).optional(), - direction: z.enum(['ascending', 'descending']), - _direction: z.lazy(() => elementSchema).optional(), - }), - ) - -export const dataRequirementSchema: z.ZodType = z.lazy( - () => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum([ + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z + .enum([ 'Account', 'ActivityDefinition', - 'Address', 'AdverseEvent', - 'Age', 'AllergyIntolerance', - 'Annotation', - 'Any', 'Appointment', 'AppointmentResponse', - 'Attachment', 'AuditEvent', - 'BackboneElement', 'Basic', 'Binary', 'BiologicallyDerivedProduct', @@ -7576,8 +8640,6 @@ export const dataRequirementSchema: z.ZodType = z.lazy( 'ClaimResponse', 'ClinicalImpression', 'CodeSystem', - 'CodeableConcept', - 'Coding', 'Communication', 'CommunicationRequest', 'CompartmentDefinition', @@ -7585,15 +8647,10 @@ export const dataRequirementSchema: z.ZodType = z.lazy( 'ConceptMap', 'Condition', 'Consent', - 'ContactDetail', - 'ContactPoint', 'Contract', - 'Contributor', - 'Count', 'Coverage', 'CoverageEligibilityRequest', 'CoverageEligibilityResponse', - 'DataRequirement', 'DetectedIssue', 'Device', 'DeviceDefinition', @@ -7601,15 +8658,10 @@ export const dataRequirementSchema: z.ZodType = z.lazy( 'DeviceRequest', 'DeviceUseStatement', 'DiagnosticReport', - 'Distance', 'DocumentManifest', 'DocumentReference', 'DomainResource', - 'Dosage', - 'Duration', 'EffectEvidenceSynthesis', - 'Element', - 'ElementDefinition', 'Encounter', 'Endpoint', 'EnrollmentRequest', @@ -7620,8 +8672,6 @@ export const dataRequirementSchema: z.ZodType = z.lazy( 'EvidenceVariable', 'ExampleScenario', 'ExplanationOfBenefit', - 'Expression', - 'Extension', 'FamilyMemberHistory', 'Flag', 'Goal', @@ -7629,8 +8679,6 @@ export const dataRequirementSchema: z.ZodType = z.lazy( 'Group', 'GuidanceResponse', 'HealthcareService', - 'HumanName', - 'Identifier', 'ImagingStudy', 'Immunization', 'ImmunizationEvaluation', @@ -7642,7 +8690,6 @@ export const dataRequirementSchema: z.ZodType = z.lazy( 'Linkage', 'List', 'Location', - 'MarketingStatus', 'Measure', 'MeasureReport', 'Media', @@ -7664,12 +8711,8 @@ export const dataRequirementSchema: z.ZodType = z.lazy( 'MedicinalProductUndesirableEffect', 'MessageDefinition', 'MessageHeader', - 'Meta', 'MolecularSequence', - 'Money', - 'MoneyQuantity', 'NamingSystem', - 'Narrative', 'NutritionOrder', 'Observation', 'ObservationDefinition', @@ -7677,28 +8720,18 @@ export const dataRequirementSchema: z.ZodType = z.lazy( 'OperationOutcome', 'Organization', 'OrganizationAffiliation', - 'ParameterDefinition', 'Parameters', 'Patient', 'PaymentNotice', 'PaymentReconciliation', - 'Period', 'Person', 'PlanDefinition', - 'Population', 'Practitioner', 'PractitionerRole', 'Procedure', - 'ProdCharacteristic', - 'ProductShelfLife', 'Provenance', - 'Quantity', 'Questionnaire', 'QuestionnaireResponse', - 'Range', - 'Ratio', - 'Reference', - 'RelatedArtifact', 'RelatedPerson', 'RequestGroup', 'ResearchDefinition', @@ -7708,12 +8741,9 @@ export const dataRequirementSchema: z.ZodType = z.lazy( 'Resource', 'RiskAssessment', 'RiskEvidenceSynthesis', - 'SampledData', 'Schedule', 'SearchParameter', 'ServiceRequest', - 'Signature', - 'SimpleQuantity', 'Slot', 'Specimen', 'SpecimenDefinition', @@ -7721,7 +8751,6 @@ export const dataRequirementSchema: z.ZodType = z.lazy( 'StructureMap', 'Subscription', 'Substance', - 'SubstanceAmount', 'SubstanceNucleicAcid', 'SubstancePolymer', 'SubstanceProtein', @@ -7734,183 +8763,267 @@ export const dataRequirementSchema: z.ZodType = z.lazy( 'TerminologyCapabilities', 'TestReport', 'TestScript', - 'Timing', - 'TriggerDefinition', - 'Type', - 'UsageContext', 'ValueSet', 'VerificationResult', 'VisionPrescription', - 'base64Binary', - 'boolean', - 'canonical', - 'code', - 'date', - 'dateTime', - 'decimal', - 'id', - 'instant', - 'integer', - 'markdown', - 'oid', - 'positiveInt', - 'string', - 'time', - 'unsignedInt', - 'uri', - 'url', - 'uuid', - 'xhtml', - ]), - _type: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.array().optional(), - _profile: z - .lazy(() => elementSchema) - .array() - .optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - mustSupport: stringSchema.array().optional(), - _mustSupport: z - .lazy(() => elementSchema) - .array() - .optional(), - codeFilter: dataRequirementCodeFilterSchema.array().optional(), - dateFilter: dataRequirementDateFilterSchema.array().optional(), - limit: positiveIntSchema.optional(), - _limit: z.lazy(() => elementSchema).optional(), - sort: dataRequirementSortSchema.array().optional(), - }) - .refine( - (value) => - [ - { - names: ['subjectCodeableConcept', 'subjectReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) + ]) + .optional(), + _code: z.lazy(() => elementSchema).optional(), + param: stringSchema.nullable().array().min(1).optional(), + _param: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'param', shadow: '_param', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const detectedIssueEvidenceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const compartmentDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CompartmentDefinition'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z - .lazy(() => codeableConceptSchema) + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - detail: z - .lazy(() => referenceSchema) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - }), - ) + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + code: z + .enum([ + 'Device', + 'Encounter', + 'Patient', + 'Practitioner', + 'RelatedPerson', + ]) + .optional(), + _code: z.lazy(() => elementSchema).optional(), + search: booleanSchema.optional(), + _search: z.lazy(() => elementSchema).optional(), + resource: compartmentDefinitionResourceSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + { name: 'code', shadow: '_code' }, + { name: 'search', shadow: '_search' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const detectedIssueMitigationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const compositionAttesterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - action: z.lazy(() => codeableConceptSchema), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - }), - ) + mode: z + .enum(['legal', 'official', 'personal', 'professional']) + .optional(), + _mode: z.lazy(() => elementSchema).optional(), + time: dateTimeSchema.optional(), + _time: z.lazy(() => elementSchema).optional(), + party: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'mode', shadow: '_mode' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const detectedIssueSchema: z.ZodType = z.lazy(() => +export const compositionRelatesToSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('DetectedIssue'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'amended', - 'cancelled', - 'corrected', - 'entered-in-error', - 'final', - 'preliminary', - 'registered', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - severity: z.enum(['high', 'low', 'moderate']).optional(), - _severity: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema).optional(), - identifiedDateTime: dateTimeSchema.optional(), - _identifiedDateTime: z.lazy(() => elementSchema).optional(), - identifiedPeriod: z.lazy(() => periodSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - implicated: z - .lazy(() => referenceSchema) - .array() - .optional(), - evidence: detectedIssueEvidenceSchema.array().optional(), - detail: stringSchema.optional(), - _detail: z.lazy(() => elementSchema).optional(), - reference: uriSchema.optional(), - _reference: z.lazy(() => elementSchema).optional(), - mitigation: detectedIssueMitigationSchema.array().optional(), + code: z.enum(['appends', 'replaces', 'signs', 'transforms']).optional(), + _code: z.lazy(() => elementSchema).optional(), + targetIdentifier: z.lazy(() => identifierSchema).optional(), + targetReference: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ { - names: ['identifiedDateTime', 'identifiedPeriod'], - required: false, + alternatives: [['targetIdentifier'], ['targetReference']], + required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -7918,337 +9031,429 @@ export const detectedIssueSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const deviceUdiCarrierSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const compositionEventSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + detail: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const compositionSectionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - deviceIdentifier: stringSchema.optional(), - _deviceIdentifier: z.lazy(() => elementSchema).optional(), - issuer: uriSchema.optional(), - _issuer: z.lazy(() => elementSchema).optional(), - jurisdiction: uriSchema.optional(), - _jurisdiction: z.lazy(() => elementSchema).optional(), - carrierAIDC: base64BinarySchema.optional(), - _carrierAIDC: z.lazy(() => elementSchema).optional(), - carrierHRF: stringSchema.optional(), - _carrierHRF: z.lazy(() => elementSchema).optional(), - entryType: z - .enum(['barcode', 'card', 'manual', 'rfid', 'self-reported', 'unknown']) - .optional(), - _entryType: z.lazy(() => elementSchema).optional(), - }), -) - -export const deviceDeviceNameSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + author: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + focus: z.lazy(() => referenceSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + mode: z.enum(['changes', 'snapshot', 'working']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + orderedBy: z.lazy(() => codeableConceptSchema).optional(), + entry: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - type: z.enum([ - 'manufacturer-name', - 'model-name', - 'other', - 'patient-reported-name', - 'udi-label-name', - 'user-friendly-name', - ]), - _type: z.lazy(() => elementSchema).optional(), - }), -) + emptyReason: z.lazy(() => codeableConceptSchema).optional(), + section: z + .lazy(() => compositionSectionSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const compositionSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('Composition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + status: z + .enum(['amended', 'entered-in-error', 'final', 'preliminary']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + author: z + .lazy(() => referenceSchema) + .array() + .min(1), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + confidentiality: z.enum(['L', 'M', 'N', 'R', 'U', 'V']).optional(), + _confidentiality: z.lazy(() => elementSchema).optional(), + attester: compositionAttesterSchema.array().min(1).optional(), + custodian: z.lazy(() => referenceSchema).optional(), + relatesTo: compositionRelatesToSchema.array().min(1).optional(), + event: compositionEventSchema.array().min(1).optional(), + section: compositionSectionSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'date', shadow: '_date' }, + { name: 'title', shadow: '_title' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const deviceSpecializationSchema: z.ZodType = +export const conceptMapGroupSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + source: uriSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + sourceVersion: stringSchema.optional(), + _sourceVersion: z.lazy(() => elementSchema).optional(), + target: uriSchema.optional(), + _target: z.lazy(() => elementSchema).optional(), + targetVersion: stringSchema.optional(), + _targetVersion: z.lazy(() => elementSchema).optional(), + element: conceptMapGroupElementSchema.array().min(1), + unmapped: conceptMapGroupUnmappedSchema.optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const conceptMapGroupElementSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - systemType: z.lazy(() => codeableConceptSchema), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - }), - ) - -export const deviceVersionSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - component: z.lazy(() => identifierSchema).optional(), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - }), -) - -export const devicePropertySchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - valueQuantity: z - .lazy(() => quantitySchema) - .array() - .optional(), - valueCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - }), -) - -export const deviceSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Device'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - definition: z.lazy(() => referenceSchema).optional(), - udiCarrier: deviceUdiCarrierSchema.array().optional(), - status: z - .enum(['active', 'entered-in-error', 'inactive', 'unknown']) - .optional(), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - distinctIdentifier: stringSchema.optional(), - _distinctIdentifier: z.lazy(() => elementSchema).optional(), - manufacturer: stringSchema.optional(), - _manufacturer: z.lazy(() => elementSchema).optional(), - manufactureDate: dateTimeSchema.optional(), - _manufactureDate: z.lazy(() => elementSchema).optional(), - expirationDate: dateTimeSchema.optional(), - _expirationDate: z.lazy(() => elementSchema).optional(), - lotNumber: stringSchema.optional(), - _lotNumber: z.lazy(() => elementSchema).optional(), - serialNumber: stringSchema.optional(), - _serialNumber: z.lazy(() => elementSchema).optional(), - deviceName: deviceDeviceNameSchema.array().optional(), - modelNumber: stringSchema.optional(), - _modelNumber: z.lazy(() => elementSchema).optional(), - partNumber: stringSchema.optional(), - _partNumber: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - specialization: deviceSpecializationSchema.array().optional(), - version: deviceVersionSchema.array().optional(), - property: devicePropertySchema.array().optional(), - patient: z.lazy(() => referenceSchema).optional(), - owner: z.lazy(() => referenceSchema).optional(), - contact: z - .lazy(() => contactPointSchema) - .array() - .optional(), - location: z.lazy(() => referenceSchema).optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - safety: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - parent: z.lazy(() => referenceSchema).optional(), - }), -) - -export const deviceDefinitionUdiDeviceIdentifierSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - deviceIdentifier: stringSchema, - _deviceIdentifier: z.lazy(() => elementSchema).optional(), - issuer: uriSchema, - _issuer: z.lazy(() => elementSchema).optional(), - jurisdiction: uriSchema, - _jurisdiction: z.lazy(() => elementSchema).optional(), - }), - ) - -export const deviceDefinitionDeviceNameSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - type: z.enum([ - 'manufacturer-name', - 'model-name', - 'other', - 'patient-reported-name', - 'udi-label-name', - 'user-friendly-name', - ]), - _type: z.lazy(() => elementSchema).optional(), - }), - ) + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + target: conceptMapGroupElementTargetSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const deviceDefinitionSpecializationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const conceptMapGroupUnmappedSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - systemType: stringSchema, - _systemType: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - }), - ) + mode: z.enum(['fixed', 'other-map', 'provided']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + url: canonicalSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'mode', shadow: '_mode' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const deviceDefinitionCapabilitySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const conceptMapGroupElementTargetSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - description: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - }), - ) - -export const deviceDefinitionPropertySchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + equivalence: z + .enum([ + 'disjoint', + 'equal', + 'equivalent', + 'inexact', + 'narrower', + 'relatedto', + 'specializes', + 'subsumes', + 'unmatched', + 'wider', + ]) .optional(), - type: z.lazy(() => codeableConceptSchema), - valueQuantity: z - .lazy(() => quantitySchema) + _equivalence: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + dependsOn: conceptMapGroupElementTargetDependsOnSchema .array() + .min(1) .optional(), - valueCode: z - .lazy(() => codeableConceptSchema) + product: z + .lazy(() => conceptMapGroupElementTargetDependsOnSchema) .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'equivalence', shadow: '_equivalence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const deviceDefinitionMaterialSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const conceptMapGroupElementTargetDependsOnSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - substance: z.lazy(() => codeableConceptSchema), - alternate: booleanSchema.optional(), - _alternate: z.lazy(() => elementSchema).optional(), - allergenicIndicator: booleanSchema.optional(), - _allergenicIndicator: z.lazy(() => elementSchema).optional(), - }), - ) + property: uriSchema.optional(), + _property: z.lazy(() => elementSchema).optional(), + system: canonicalSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'property', shadow: '_property' }, + { name: 'value', shadow: '_value' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const deviceDefinitionSchema: z.ZodType = z.lazy( +export const conceptMapSchema: z.ZodType> = z.lazy( () => z .strictObject({ - resourceType: z.literal('DeviceDefinition'), + resourceType: z.literal('ConceptMap'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -8260,79 +9465,89 @@ export const deviceDefinitionSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - udiDeviceIdentifier: deviceDefinitionUdiDeviceIdentifierSchema - .array() - .optional(), - manufacturerString: stringSchema.optional(), - _manufacturerString: z.lazy(() => elementSchema).optional(), - manufacturerReference: z.lazy(() => referenceSchema).optional(), - deviceName: deviceDefinitionDeviceNameSchema.array().optional(), - modelNumber: stringSchema.optional(), - _modelNumber: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - specialization: deviceDefinitionSpecializationSchema.array().optional(), - version: stringSchema.array().optional(), - _version: z - .lazy(() => elementSchema) - .array() - .optional(), - safety: z - .lazy(() => codeableConceptSchema) + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - shelfLifeStorage: z - .lazy(() => productShelfLifeSchema) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - physicalCharacteristics: z - .lazy(() => prodCharacteristicSchema) - .optional(), - languageCode: z + jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - capability: deviceDefinitionCapabilitySchema.array().optional(), - property: deviceDefinitionPropertySchema.array().optional(), - owner: z.lazy(() => referenceSchema).optional(), - contact: z - .lazy(() => contactPointSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - onlineInformation: uriSchema.optional(), - _onlineInformation: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - parentDevice: z.lazy(() => referenceSchema).optional(), - material: deviceDefinitionMaterialSchema.array().optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + sourceUri: uriSchema.optional(), + _sourceUri: z.lazy(() => elementSchema).optional(), + sourceCanonical: canonicalSchema.optional(), + _sourceCanonical: z.lazy(() => elementSchema).optional(), + targetUri: uriSchema.optional(), + _targetUri: z.lazy(() => elementSchema).optional(), + targetCanonical: canonicalSchema.optional(), + _targetCanonical: z.lazy(() => elementSchema).optional(), + group: conceptMapGroupSchema.array().min(1).optional(), }) .refine( (value) => [ { - names: ['manufacturerString', 'manufacturerReference'], + alternatives: [ + ['sourceUri', '_sourceUri'], + ['sourceCanonical', '_sourceCanonical'], + ], + required: false, + }, + { + alternatives: [ + ['targetUri', '_targetUri'], + ['targetCanonical', '_targetCanonical'], + ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -8340,93 +9555,32 @@ export const deviceDefinitionSchema: z.ZodType = z.lazy( message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), -) - -export const deviceMetricCalibrationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum(['gain', 'offset', 'two-point', 'unspecified']).optional(), - _type: z.lazy(() => elementSchema).optional(), - state: z - .enum([ - 'calibrated', - 'calibration-required', - 'not-calibrated', - 'unspecified', - ]) - .optional(), - _state: z.lazy(() => elementSchema).optional(), - time: instantSchema.optional(), - _time: z.lazy(() => elementSchema).optional(), - }), - ) +) as unknown as z.ZodType> -export const deviceMetricSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('DeviceMetric'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - unit: z.lazy(() => codeableConceptSchema).optional(), - source: z.lazy(() => referenceSchema).optional(), - parent: z.lazy(() => referenceSchema).optional(), - operationalStatus: z - .enum(['entered-in-error', 'off', 'on', 'standby']) - .optional(), - _operationalStatus: z.lazy(() => elementSchema).optional(), - color: z - .enum([ - 'black', - 'blue', - 'cyan', - 'green', - 'magenta', - 'red', - 'white', - 'yellow', - ]) - .optional(), - _color: z.lazy(() => elementSchema).optional(), - category: z.enum(['calculation', 'measurement', 'setting', 'unspecified']), - _category: z.lazy(() => elementSchema).optional(), - measurementPeriod: z.lazy(() => timingSchema).optional(), - calibration: deviceMetricCalibrationSchema.array().optional(), - }), -) - -export const deviceRequestParameterSchema: z.ZodType = +export const conditionStageSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -8434,170 +9588,164 @@ export const deviceRequestParameterSchema: z.ZodType extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), + summary: z.lazy(() => codeableConceptSchema).optional(), + assessment: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), }) .refine( - (value) => - [ - { - names: [ - 'valueCodeableConcept', - 'valueQuantity', - 'valueRange', - 'valueBoolean', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const deviceRequestSchema: z.ZodType = z.lazy(() => +export const conditionEvidenceSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('DeviceRequest'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) + code: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - basedOn: z + detail: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - priorRequest: z - .lazy(() => referenceSchema) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const conditionSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Condition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) .array() + .min(1) .optional(), - groupIdentifier: z.lazy(() => identifierSchema).optional(), - status: z - .enum([ - 'active', - 'completed', - 'draft', - 'entered-in-error', - 'on-hold', - 'revoked', - 'unknown', - ]) + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) .optional(), - _status: z.lazy(() => elementSchema).optional(), - intent: z.enum([ - 'directive', - 'filler-order', - 'instance-order', - 'option', - 'order', - 'original-order', - 'plan', - 'proposal', - 'reflex-order', - ]), - _intent: z.lazy(() => elementSchema).optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - codeReference: z.lazy(() => referenceSchema).optional(), - codeCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - parameter: deviceRequestParameterSchema.array().optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - occurrenceTiming: z.lazy(() => timingSchema).optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: z.lazy(() => elementSchema).optional(), - requester: z.lazy(() => referenceSchema).optional(), - performerType: z.lazy(() => codeableConceptSchema).optional(), - performer: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - reasonReference: z - .lazy(() => referenceSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - insurance: z - .lazy(() => referenceSchema) + clinicalStatus: z.lazy(() => codeableConceptSchema).optional(), + verificationStatus: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - supportingInfo: z - .lazy(() => referenceSchema) + severity: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + bodySite: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + onsetDateTime: dateTimeSchema.optional(), + _onsetDateTime: z.lazy(() => elementSchema).optional(), + onsetAge: z.lazy(() => ageSchema).optional(), + onsetPeriod: z.lazy(() => periodSchema).optional(), + onsetRange: z.lazy(() => rangeSchema).optional(), + onsetString: stringSchema.optional(), + _onsetString: z.lazy(() => elementSchema).optional(), + abatementDateTime: dateTimeSchema.optional(), + _abatementDateTime: z.lazy(() => elementSchema).optional(), + abatementAge: z.lazy(() => ageSchema).optional(), + abatementPeriod: z.lazy(() => periodSchema).optional(), + abatementRange: z.lazy(() => rangeSchema).optional(), + abatementString: stringSchema.optional(), + _abatementString: z.lazy(() => elementSchema).optional(), + recordedDate: dateTimeSchema.optional(), + _recordedDate: z.lazy(() => elementSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + asserter: z.lazy(() => referenceSchema).optional(), + stage: conditionStageSchema.array().min(1).optional(), + evidence: conditionEvidenceSchema.array().min(1).optional(), note: z .lazy(() => annotationSchema) .array() - .optional(), - relevantHistory: z - .lazy(() => referenceSchema) - .array() + .min(1) .optional(), }) .refine( (value) => [ - { names: ['codeReference', 'codeCodeableConcept'], required: true }, { - names: [ - 'occurrenceDateTime', - 'occurrencePeriod', - 'occurrenceTiming', + alternatives: [ + ['onsetDateTime', '_onsetDateTime'], + ['onsetAge'], + ['onsetPeriod'], + ['onsetRange'], + ['onsetString', '_onsetString'], + ], + required: false, + }, + { + alternatives: [ + ['abatementDateTime', '_abatementDateTime'], + ['abatementAge'], + ['abatementPeriod'], + ['abatementRange'], + ['abatementString', '_abatementString'], ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -8605,388 +9753,218 @@ export const deviceRequestSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const deviceUseStatementSchema: z.ZodType = +export const consentPolicySchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('DeviceUseStatement'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'completed', - 'entered-in-error', - 'intended', - 'on-hold', - 'stopped', - ]), - _status: z.lazy(() => elementSchema).optional(), - subject: z.lazy(() => referenceSchema), - derivedFrom: z - .lazy(() => referenceSchema) - .array() - .optional(), - timingTiming: z.lazy(() => timingSchema).optional(), - timingPeriod: z.lazy(() => periodSchema).optional(), - timingDateTime: dateTimeSchema.optional(), - _timingDateTime: z.lazy(() => elementSchema).optional(), - recordedOn: dateTimeSchema.optional(), - _recordedOn: z.lazy(() => elementSchema).optional(), - source: z.lazy(() => referenceSchema).optional(), - device: z.lazy(() => referenceSchema), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), + authority: uriSchema.optional(), + _authority: z.lazy(() => elementSchema).optional(), + uri: uriSchema.optional(), + _uri: z.lazy(() => elementSchema).optional(), }) .refine( - (value) => - [ - { - names: ['timingTiming', 'timingPeriod', 'timingDateTime'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const diagnosticReportMediaSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const consentVerificationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - link: z.lazy(() => referenceSchema), - }), - ) + verified: booleanSchema.optional(), + _verified: z.lazy(() => elementSchema).optional(), + verifiedWith: z.lazy(() => referenceSchema).optional(), + verificationDate: dateTimeSchema.optional(), + _verificationDate: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'verified', shadow: '_verified' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const diagnosticReportSchema: z.ZodType = z.lazy( - () => +export const consentProvisionSchema: z.ZodType> = + z.lazy(() => z .strictObject({ - resourceType: z.literal('DiagnosticReport'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'amended', - 'appended', - 'cancelled', - 'corrected', - 'entered-in-error', - 'final', - 'partial', - 'preliminary', - 'registered', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - category: z + type: z.enum(['deny', 'permit']).optional(), + _type: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + actor: consentProvisionActorSchema.array().min(1).optional(), + action: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema), - subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - effectiveDateTime: dateTimeSchema.optional(), - _effectiveDateTime: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - issued: instantSchema.optional(), - _issued: z.lazy(() => elementSchema).optional(), - performer: z - .lazy(() => referenceSchema) - .array() - .optional(), - resultsInterpreter: z - .lazy(() => referenceSchema) - .array() - .optional(), - specimen: z - .lazy(() => referenceSchema) + securityLabel: z + .lazy(() => codingSchema) .array() + .min(1) .optional(), - result: z - .lazy(() => referenceSchema) + purpose: z + .lazy(() => codingSchema) .array() + .min(1) .optional(), - imagingStudy: z - .lazy(() => referenceSchema) + class: z + .lazy(() => codingSchema) .array() + .min(1) .optional(), - media: diagnosticReportMediaSchema.array().optional(), - conclusion: stringSchema.optional(), - _conclusion: z.lazy(() => elementSchema).optional(), - conclusionCode: z + code: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - presentedForm: z - .lazy(() => attachmentSchema) + dataPeriod: z.lazy(() => periodSchema).optional(), + data: consentProvisionDataSchema.array().min(1).optional(), + provision: z + .lazy(() => consentProvisionSchema) .array() + .min(1) .optional(), }) .refine( - (value) => - [ - { - names: ['effectiveDateTime', 'effectivePeriod'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), -) - -export const distanceSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - comparator: z.enum(['<', '<=', '>', '>=']).optional(), - _comparator: z.lazy(() => elementSchema).optional(), - unit: stringSchema.optional(), - _unit: z.lazy(() => elementSchema).optional(), - system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - }), -) - -export const documentManifestRelatedSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - ref: z.lazy(() => referenceSchema).optional(), - }), - ) - -export const documentManifestSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('DocumentManifest'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - masterIdentifier: z.lazy(() => identifierSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['current', 'entered-in-error', 'superseded']), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - created: dateTimeSchema.optional(), - _created: z.lazy(() => elementSchema).optional(), - author: z - .lazy(() => referenceSchema) - .array() - .optional(), - recipient: z - .lazy(() => referenceSchema) - .array() - .optional(), - source: uriSchema.optional(), - _source: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - content: z - .lazy(() => referenceSchema) - .array() - .min(1), - related: documentManifestRelatedSchema.array().optional(), - }), -) - -export const documentReferenceRelatesToSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.enum(['appends', 'replaces', 'signs', 'transforms']), - _code: z.lazy(() => elementSchema).optional(), - target: z.lazy(() => referenceSchema), - }), - ) + ) as unknown as z.ZodType> -export const documentReferenceContentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const consentProvisionActorSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - attachment: z.lazy(() => attachmentSchema), - format: z.lazy(() => codingSchema).optional(), - }), - ) + role: z.lazy(() => codeableConceptSchema), + reference: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const documentReferenceContextSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const consentProvisionDataSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - encounter: z - .lazy(() => referenceSchema) - .array() - .optional(), - event: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - period: z.lazy(() => periodSchema).optional(), - facilityType: z.lazy(() => codeableConceptSchema).optional(), - practiceSetting: z.lazy(() => codeableConceptSchema).optional(), - sourcePatientInfo: z.lazy(() => referenceSchema).optional(), - related: z - .lazy(() => referenceSchema) - .array() + meaning: z + .enum(['authoredby', 'dependents', 'instance', 'related']) .optional(), - }), - ) + _meaning: z.lazy(() => elementSchema).optional(), + reference: z.lazy(() => referenceSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'meaning', shadow: '_meaning' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const documentReferenceSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('DocumentReference'), +export const consentSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Consent'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -8998,53 +9976,128 @@ export const documentReferenceSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - masterIdentifier: z.lazy(() => identifierSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - status: z.enum(['current', 'entered-in-error', 'superseded']), - _status: z.lazy(() => elementSchema).optional(), - docStatus: z - .enum(['amended', 'entered-in-error', 'final', 'preliminary']) + status: z + .enum([ + 'active', + 'draft', + 'entered-in-error', + 'inactive', + 'proposed', + 'rejected', + ]) .optional(), - _docStatus: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), + _status: z.lazy(() => elementSchema).optional(), + scope: z.lazy(() => codeableConceptSchema), category: z .lazy(() => codeableConceptSchema) .array() - .optional(), - subject: z.lazy(() => referenceSchema).optional(), - date: instantSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - author: z + .min(1), + patient: z.lazy(() => referenceSchema).optional(), + dateTime: dateTimeSchema.optional(), + _dateTime: z.lazy(() => elementSchema).optional(), + performer: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - authenticator: z.lazy(() => referenceSchema).optional(), - custodian: z.lazy(() => referenceSchema).optional(), - relatesTo: documentReferenceRelatesToSchema.array().optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - securityLabel: z - .lazy(() => codeableConceptSchema) + organization: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - content: documentReferenceContentSchema.array().min(1), - context: documentReferenceContextSchema.optional(), - }), -) + sourceAttachment: z.lazy(() => attachmentSchema).optional(), + sourceReference: z.lazy(() => referenceSchema).optional(), + policy: consentPolicySchema.array().min(1).optional(), + policyRule: z.lazy(() => codeableConceptSchema).optional(), + verification: consentVerificationSchema.array().min(1).optional(), + provision: consentProvisionSchema.optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['sourceAttachment'], ['sourceReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const contactDetailSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const dosageDoseAndRateSchema: z.ZodType = z.lazy( +export const contactPointSchema: z.ZodType> = z.lazy( () => z .strictObject({ @@ -9052,1548 +10105,1567 @@ export const dosageDoseAndRateSchema: z.ZodType = z.lazy( extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - doseRange: z.lazy(() => rangeSchema).optional(), - doseQuantity: z.lazy(() => quantitySchema).optional(), - rateRatio: z.lazy(() => ratioSchema).optional(), - rateRange: z.lazy(() => rangeSchema).optional(), - rateQuantity: z.lazy(() => quantitySchema).optional(), + system: z + .enum(['email', 'fax', 'other', 'pager', 'phone', 'sms', 'url']) + .optional(), + _system: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + use: z.enum(['home', 'mobile', 'old', 'temp', 'work']).optional(), + _use: z.lazy(() => elementSchema).optional(), + rank: positiveIntSchema.optional(), + _rank: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), }) .refine( - (value) => - [ - { names: ['doseRange', 'doseQuantity'], required: false }, - { - names: ['rateRatio', 'rateRange', 'rateQuantity'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), -) +) as unknown as z.ZodType> -export const dosageSchema: z.ZodType = z.lazy(() => +export const contractContentDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: integerSchema.optional(), - _sequence: z.lazy(() => elementSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - additionalInstruction: z - .lazy(() => codeableConceptSchema) - .array() + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + publisher: z.lazy(() => referenceSchema).optional(), + publicationDate: dateTimeSchema.optional(), + _publicationDate: z.lazy(() => elementSchema).optional(), + publicationStatus: z + .enum([ + 'amended', + 'appended', + 'cancelled', + 'disputed', + 'entered-in-error', + 'executable', + 'executed', + 'negotiable', + 'offered', + 'policy', + 'rejected', + 'renewed', + 'resolved', + 'revoked', + 'terminated', + ]) .optional(), - patientInstruction: stringSchema.optional(), - _patientInstruction: z.lazy(() => elementSchema).optional(), - timing: z.lazy(() => timingSchema).optional(), - asNeededBoolean: booleanSchema.optional(), - _asNeededBoolean: z.lazy(() => elementSchema).optional(), - asNeededCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - site: z.lazy(() => codeableConceptSchema).optional(), - route: z.lazy(() => codeableConceptSchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - doseAndRate: dosageDoseAndRateSchema.array().optional(), - maxDosePerPeriod: z.lazy(() => ratioSchema).optional(), - maxDosePerAdministration: z.lazy(() => quantitySchema).optional(), - maxDosePerLifetime: z.lazy(() => quantitySchema).optional(), + _publicationStatus: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'publicationStatus', shadow: '_publicationStatus' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['asNeededBoolean', 'asNeededCodeableConcept'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const durationSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - comparator: z.enum(['<', '<=', '>', '>=']).optional(), - _comparator: z.lazy(() => elementSchema).optional(), - unit: stringSchema.optional(), - _unit: z.lazy(() => elementSchema).optional(), - system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - }), -) - -export const effectEvidenceSynthesisSampleSizeSchema: z.ZodType = +) as unknown as z.ZodType> + +export const contractTermSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + issued: dateTimeSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + applies: z.lazy(() => periodSchema).optional(), + topicCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + topicReference: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subType: z.lazy(() => codeableConceptSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + securityLabel: contractTermSecurityLabelSchema + .array() + .min(1) + .optional(), + offer: contractTermOfferSchema, + asset: contractTermAssetSchema.array().min(1).optional(), + action: contractTermActionSchema.array().min(1).optional(), + group: z + .lazy(() => contractTermSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['topicCodeableConcept'], ['topicReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const contractSignerSchema: z.ZodType> = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - numberOfStudies: integerSchema.optional(), - _numberOfStudies: z.lazy(() => elementSchema).optional(), - numberOfParticipants: integerSchema.optional(), - _numberOfParticipants: z.lazy(() => elementSchema).optional(), - }), - ) + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codingSchema), + party: z.lazy(() => referenceSchema), + signature: z + .lazy(() => signatureSchema) + .array() + .min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const contractFriendlySchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['contentAttachment'], ['contentReference']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const effectEvidenceSynthesisResultsByExposureSchema: z.ZodType = +export const contractLegalSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['contentAttachment'], ['contentReference']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const contractRuleSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['contentAttachment'], ['contentReference']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const contractTermSecurityLabelSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - exposureState: z.enum(['exposure', 'exposure-alternative']).optional(), - _exposureState: z.lazy(() => elementSchema).optional(), - variantState: z.lazy(() => codeableConceptSchema).optional(), - riskEvidenceSynthesis: z.lazy(() => referenceSchema), - }), - ) - -export const effectEvidenceSynthesisEffectEstimateSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + number: unsignedIntSchema.nullable().array().min(1).optional(), + _number: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + classification: z.lazy(() => codingSchema), + category: z + .lazy(() => codingSchema) .array() + .min(1) .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - variantState: z.lazy(() => codeableConceptSchema).optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - unitOfMeasure: z.lazy(() => codeableConceptSchema).optional(), - precisionEstimate: - effectEvidenceSynthesisEffectEstimatePrecisionEstimateSchema - .array() - .optional(), - }), - ) + control: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'number', shadow: '_number', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const effectEvidenceSynthesisCertaintySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const contractTermOfferSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - rating: z + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + party: contractTermOfferPartySchema.array().min(1).optional(), + topic: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + decision: z.lazy(() => codeableConceptSchema).optional(), + decisionMode: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - note: z - .lazy(() => annotationSchema) + answer: contractTermOfferAnswerSchema.array().min(1).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + linkId: stringSchema.nullable().array().min(1).optional(), + _linkId: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - certaintySubcomponent: - effectEvidenceSynthesisCertaintyCertaintySubcomponentSchema - .array() - .optional(), - }), - ) - -export const effectEvidenceSynthesisEffectEstimatePrecisionEstimateSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + securityLabelNumber: unsignedIntSchema + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + _securityLabelNumber: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - level: decimalSchema.optional(), - _level: z.lazy(() => elementSchema).optional(), - from: decimalSchema.optional(), - _from: z.lazy(() => elementSchema).optional(), - to: decimalSchema.optional(), - _to: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'linkId', shadow: '_linkId', required: false }, + { + name: 'securityLabelNumber', + shadow: '_securityLabelNumber', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const effectEvidenceSynthesisCertaintyCertaintySubcomponentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const contractTermAssetSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - rating: z + scope: z.lazy(() => codeableConceptSchema).optional(), + type: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - note: z - .lazy(() => annotationSchema) + typeReference: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - }), - ) - -export const effectEvidenceSynthesisSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('EffectEvidenceSynthesis'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + subtype: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + relationship: z.lazy(() => codingSchema).optional(), + context: contractTermAssetContextSchema.array().min(1).optional(), + condition: stringSchema.optional(), + _condition: z.lazy(() => elementSchema).optional(), + periodType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + period: z + .lazy(() => periodSchema) + .array() + .min(1) + .optional(), + usePeriod: z + .lazy(() => periodSchema) + .array() + .min(1) + .optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + linkId: stringSchema.nullable().array().min(1).optional(), + _linkId: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + answer: z + .lazy(() => contractTermOfferAnswerSchema) + .array() + .min(1) + .optional(), + securityLabelNumber: unsignedIntSchema + .nullable() + .array() + .min(1) + .optional(), + _securityLabelNumber: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), + valuedItem: contractTermAssetValuedItemSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'linkId', shadow: '_linkId', required: false }, + { + name: 'securityLabelNumber', + shadow: '_securityLabelNumber', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const contractTermActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subject: contractTermActionSubjectSchema.array().min(1).optional(), + intent: z.lazy(() => codeableConceptSchema), + linkId: stringSchema.nullable().array().min(1).optional(), + _linkId: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) + status: z.lazy(() => codeableConceptSchema), + context: z.lazy(() => referenceSchema).optional(), + contextLinkId: stringSchema.nullable().array().min(1).optional(), + _contextLinkId: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + requester: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - useContext: z - .lazy(() => usageContextSchema) + requesterLinkId: stringSchema.nullable().array().min(1).optional(), + _requesterLinkId: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - jurisdiction: z + performerType: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z - .lazy(() => codeableConceptSchema) + performerRole: z.lazy(() => codeableConceptSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + performerLinkId: stringSchema.nullable().array().min(1).optional(), + _performerLinkId: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - author: z - .lazy(() => contactDetailSchema) + reasonCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - editor: z - .lazy(() => contactDetailSchema) + reasonReference: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - reviewer: z - .lazy(() => contactDetailSchema) + reason: stringSchema.nullable().array().min(1).optional(), + _reason: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - endorser: z - .lazy(() => contactDetailSchema) + reasonLinkId: stringSchema.nullable().array().min(1).optional(), + _reasonLinkId: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - synthesisType: z.lazy(() => codeableConceptSchema).optional(), - studyType: z.lazy(() => codeableConceptSchema).optional(), - population: z.lazy(() => referenceSchema), - exposure: z.lazy(() => referenceSchema), - exposureAlternative: z.lazy(() => referenceSchema), - outcome: z.lazy(() => referenceSchema), - sampleSize: effectEvidenceSynthesisSampleSizeSchema.optional(), - resultsByExposure: effectEvidenceSynthesisResultsByExposureSchema + securityLabelNumber: unsignedIntSchema + .nullable() .array() + .min(1) .optional(), - effectEstimate: effectEvidenceSynthesisEffectEstimateSchema + _securityLabelNumber: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - certainty: effectEvidenceSynthesisCertaintySchema.array().optional(), - }), - ) + }) + .refine( + (value) => + [ + { + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], + ['occurrenceTiming'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'linkId', shadow: '_linkId', required: false }, + { name: 'contextLinkId', shadow: '_contextLinkId', required: false }, + { + name: 'requesterLinkId', + shadow: '_requesterLinkId', + required: false, + }, + { + name: 'performerLinkId', + shadow: '_performerLinkId', + required: false, + }, + { name: 'reason', shadow: '_reason', required: false }, + { name: 'reasonLinkId', shadow: '_reasonLinkId', required: false }, + { + name: 'securityLabelNumber', + shadow: '_securityLabelNumber', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const elementSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - }), -) - -export const elementDefinitionSlicingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const contractTermOfferPartySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - discriminator: elementDefinitionSlicingDiscriminatorSchema - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - ordered: booleanSchema.optional(), - _ordered: z.lazy(() => elementSchema).optional(), - rules: z.enum(['closed', 'open', 'openAtEnd']), - _rules: z.lazy(() => elementSchema).optional(), - }), - ) - -export const elementDefinitionBaseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z + modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - path: stringSchema, - _path: z.lazy(() => elementSchema).optional(), - min: unsignedIntSchema, - _min: z.lazy(() => elementSchema).optional(), - max: stringSchema, - _max: z.lazy(() => elementSchema).optional(), - }), - ) + reference: z + .lazy(() => referenceSchema) + .array() + .min(1), + role: z.lazy(() => codeableConceptSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const elementDefinitionTypeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const contractTermOfferAnswerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: uriSchema, - _code: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.array().optional(), - _profile: z - .lazy(() => elementSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - targetProfile: canonicalSchema.array().optional(), - _targetProfile: z - .lazy(() => elementSchema) + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueBoolean', '_valueBoolean'], + ['valueDecimal', '_valueDecimal'], + ['valueInteger', '_valueInteger'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueTime', '_valueTime'], + ['valueString', '_valueString'], + ['valueUri', '_valueUri'], + ['valueAttachment'], + ['valueCoding'], + ['valueQuantity'], + ['valueReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const contractTermAssetContextSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - aggregation: z - .enum(['bundled', 'contained', 'referenced']) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - _aggregation: z - .lazy(() => elementSchema) + reference: z.lazy(() => referenceSchema).optional(), + code: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - versioning: z.enum(['either', 'independent', 'specific']).optional(), - _versioning: z.lazy(() => elementSchema).optional(), - }), - ) - -export const elementDefinitionExampleSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - label: stringSchema, - _label: z.lazy(() => elementSchema).optional(), - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueCanonical: canonicalSchema.optional(), - _valueCanonical: z.lazy(() => elementSchema).optional(), - valueCode: codeSchema.optional(), - _valueCode: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueId: idSchema.optional(), - _valueId: z.lazy(() => elementSchema).optional(), - valueInstant: instantSchema.optional(), - _valueInstant: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueMarkdown: markdownSchema.optional(), - _valueMarkdown: z.lazy(() => elementSchema).optional(), - valueOid: oidSchema.optional(), - _valueOid: z.lazy(() => elementSchema).optional(), - valuePositiveInt: positiveIntSchema.optional(), - _valuePositiveInt: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueUnsignedInt: unsignedIntSchema.optional(), - _valueUnsignedInt: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueUrl: urlSchema.optional(), - _valueUrl: z.lazy(() => elementSchema).optional(), - valueUuid: uuidSchema.optional(), - _valueUuid: z.lazy(() => elementSchema).optional(), - valueAddress: z.lazy(() => addressSchema).optional(), - valueAge: z.lazy(() => ageSchema).optional(), - valueAnnotation: z.lazy(() => annotationSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueContactPoint: z.lazy(() => contactPointSchema).optional(), - valueCount: z.lazy(() => countSchema).optional(), - valueDistance: z.lazy(() => distanceSchema).optional(), - valueDuration: z.lazy(() => durationSchema).optional(), - valueHumanName: z.lazy(() => humanNameSchema).optional(), - valueIdentifier: z.lazy(() => identifierSchema).optional(), - valueMoney: z.lazy(() => moneySchema).optional(), - valuePeriod: z.lazy(() => periodSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueRatio: z.lazy(() => ratioSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - valueSampledData: z.lazy(() => sampledDataSchema).optional(), - valueSignature: z.lazy(() => signatureSchema).optional(), - valueTiming: z.lazy(() => timingSchema).optional(), - valueContactDetail: z.lazy(() => contactDetailSchema).optional(), - valueContributor: z.lazy(() => contributorSchema).optional(), - valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), - valueExpression: z.lazy(() => expressionSchema).optional(), - valueParameterDefinition: z - .lazy(() => parameterDefinitionSchema) - .optional(), - valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), - valueTriggerDefinition: z - .lazy(() => triggerDefinitionSchema) - .optional(), - valueUsageContext: z.lazy(() => usageContextSchema).optional(), - valueDosage: z.lazy(() => dosageSchema).optional(), - valueMeta: z.lazy(() => metaSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueBase64Binary', - 'valueBoolean', - 'valueCanonical', - 'valueCode', - 'valueDate', - 'valueDateTime', - 'valueDecimal', - 'valueId', - 'valueInstant', - 'valueInteger', - 'valueMarkdown', - 'valueOid', - 'valuePositiveInt', - 'valueString', - 'valueTime', - 'valueUnsignedInt', - 'valueUri', - 'valueUrl', - 'valueUuid', - 'valueAddress', - 'valueAge', - 'valueAnnotation', - 'valueAttachment', - 'valueCodeableConcept', - 'valueCoding', - 'valueContactPoint', - 'valueCount', - 'valueDistance', - 'valueDuration', - 'valueHumanName', - 'valueIdentifier', - 'valueMoney', - 'valuePeriod', - 'valueQuantity', - 'valueRange', - 'valueRatio', - 'valueReference', - 'valueSampledData', - 'valueSignature', - 'valueTiming', - 'valueContactDetail', - 'valueContributor', - 'valueDataRequirement', - 'valueExpression', - 'valueParameterDefinition', - 'valueRelatedArtifact', - 'valueTriggerDefinition', - 'valueUsageContext', - 'valueDosage', - 'valueMeta', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const elementDefinitionConstraintSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const contractTermAssetValuedItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - key: idSchema, - _key: z.lazy(() => elementSchema).optional(), - requirements: stringSchema.optional(), - _requirements: z.lazy(() => elementSchema).optional(), - severity: z.enum(['error', 'warning']), - _severity: z.lazy(() => elementSchema).optional(), - human: stringSchema, - _human: z.lazy(() => elementSchema).optional(), - expression: stringSchema.optional(), - _expression: z.lazy(() => elementSchema).optional(), - xpath: stringSchema.optional(), - _xpath: z.lazy(() => elementSchema).optional(), - source: canonicalSchema.optional(), - _source: z.lazy(() => elementSchema).optional(), - }), - ) - -export const elementDefinitionBindingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z + modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - strength: z.enum(['example', 'extensible', 'preferred', 'required']), - _strength: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - valueSet: canonicalSchema.optional(), - _valueSet: z.lazy(() => elementSchema).optional(), - }), - ) + entityCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + entityReference: z.lazy(() => referenceSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + effectiveTime: dateTimeSchema.optional(), + _effectiveTime: z.lazy(() => elementSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + points: decimalSchema.optional(), + _points: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + payment: stringSchema.optional(), + _payment: z.lazy(() => elementSchema).optional(), + paymentDate: dateTimeSchema.optional(), + _paymentDate: z.lazy(() => elementSchema).optional(), + responsible: z.lazy(() => referenceSchema).optional(), + recipient: z.lazy(() => referenceSchema).optional(), + linkId: stringSchema.nullable().array().min(1).optional(), + _linkId: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + securityLabelNumber: unsignedIntSchema + .nullable() + .array() + .min(1) + .optional(), + _securityLabelNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['entityCodeableConcept'], ['entityReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'linkId', shadow: '_linkId', required: false }, + { + name: 'securityLabelNumber', + shadow: '_securityLabelNumber', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const elementDefinitionMappingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const contractTermActionSubjectSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identity: idSchema, - _identity: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - map: stringSchema, - _map: z.lazy(() => elementSchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - }), - ) - -export const elementDefinitionSlicingDiscriminatorSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z + modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum(['exists', 'pattern', 'profile', 'type', 'value']), - _type: z.lazy(() => elementSchema).optional(), - path: stringSchema, - _path: z.lazy(() => elementSchema).optional(), - }), - ) - -export const elementDefinitionSchema: z.ZodType = z.lazy( - () => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - path: stringSchema, - _path: z.lazy(() => elementSchema).optional(), - representation: z - .enum(['cdaText', 'typeAttr', 'xhtml', 'xmlAttr', 'xmlText']) - .array() - .optional(), - _representation: z - .lazy(() => elementSchema) - .array() - .optional(), - sliceName: stringSchema.optional(), - _sliceName: z.lazy(() => elementSchema).optional(), - sliceIsConstraining: booleanSchema.optional(), - _sliceIsConstraining: z.lazy(() => elementSchema).optional(), - label: stringSchema.optional(), - _label: z.lazy(() => elementSchema).optional(), - code: z - .lazy(() => codingSchema) - .array() - .optional(), - slicing: elementDefinitionSlicingSchema.optional(), - short: stringSchema.optional(), - _short: z.lazy(() => elementSchema).optional(), - definition: markdownSchema.optional(), - _definition: z.lazy(() => elementSchema).optional(), - comment: markdownSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - requirements: markdownSchema.optional(), - _requirements: z.lazy(() => elementSchema).optional(), - alias: stringSchema.array().optional(), - _alias: z - .lazy(() => elementSchema) - .array() - .optional(), - min: unsignedIntSchema.optional(), - _min: z.lazy(() => elementSchema).optional(), - max: stringSchema.optional(), - _max: z.lazy(() => elementSchema).optional(), - base: elementDefinitionBaseSchema.optional(), - contentReference: uriSchema.optional(), - _contentReference: z.lazy(() => elementSchema).optional(), - type: elementDefinitionTypeSchema.array().optional(), - defaultValueBase64Binary: base64BinarySchema.optional(), - _defaultValueBase64Binary: z.lazy(() => elementSchema).optional(), - defaultValueBoolean: booleanSchema.optional(), - _defaultValueBoolean: z.lazy(() => elementSchema).optional(), - defaultValueCanonical: canonicalSchema.optional(), - _defaultValueCanonical: z.lazy(() => elementSchema).optional(), - defaultValueCode: codeSchema.optional(), - _defaultValueCode: z.lazy(() => elementSchema).optional(), - defaultValueDate: dateSchema.optional(), - _defaultValueDate: z.lazy(() => elementSchema).optional(), - defaultValueDateTime: dateTimeSchema.optional(), - _defaultValueDateTime: z.lazy(() => elementSchema).optional(), - defaultValueDecimal: decimalSchema.optional(), - _defaultValueDecimal: z.lazy(() => elementSchema).optional(), - defaultValueId: idSchema.optional(), - _defaultValueId: z.lazy(() => elementSchema).optional(), - defaultValueInstant: instantSchema.optional(), - _defaultValueInstant: z.lazy(() => elementSchema).optional(), - defaultValueInteger: integerSchema.optional(), - _defaultValueInteger: z.lazy(() => elementSchema).optional(), - defaultValueMarkdown: markdownSchema.optional(), - _defaultValueMarkdown: z.lazy(() => elementSchema).optional(), - defaultValueOid: oidSchema.optional(), - _defaultValueOid: z.lazy(() => elementSchema).optional(), - defaultValuePositiveInt: positiveIntSchema.optional(), - _defaultValuePositiveInt: z.lazy(() => elementSchema).optional(), - defaultValueString: stringSchema.optional(), - _defaultValueString: z.lazy(() => elementSchema).optional(), - defaultValueTime: timeSchema.optional(), - _defaultValueTime: z.lazy(() => elementSchema).optional(), - defaultValueUnsignedInt: unsignedIntSchema.optional(), - _defaultValueUnsignedInt: z.lazy(() => elementSchema).optional(), - defaultValueUri: uriSchema.optional(), - _defaultValueUri: z.lazy(() => elementSchema).optional(), - defaultValueUrl: urlSchema.optional(), - _defaultValueUrl: z.lazy(() => elementSchema).optional(), - defaultValueUuid: uuidSchema.optional(), - _defaultValueUuid: z.lazy(() => elementSchema).optional(), - defaultValueAddress: z.lazy(() => addressSchema).optional(), - defaultValueAge: z.lazy(() => ageSchema).optional(), - defaultValueAnnotation: z.lazy(() => annotationSchema).optional(), - defaultValueAttachment: z.lazy(() => attachmentSchema).optional(), - defaultValueCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - defaultValueCoding: z.lazy(() => codingSchema).optional(), - defaultValueContactPoint: z.lazy(() => contactPointSchema).optional(), - defaultValueCount: z.lazy(() => countSchema).optional(), - defaultValueDistance: z.lazy(() => distanceSchema).optional(), - defaultValueDuration: z.lazy(() => durationSchema).optional(), - defaultValueHumanName: z.lazy(() => humanNameSchema).optional(), - defaultValueIdentifier: z.lazy(() => identifierSchema).optional(), - defaultValueMoney: z.lazy(() => moneySchema).optional(), - defaultValuePeriod: z.lazy(() => periodSchema).optional(), - defaultValueQuantity: z.lazy(() => quantitySchema).optional(), - defaultValueRange: z.lazy(() => rangeSchema).optional(), - defaultValueRatio: z.lazy(() => ratioSchema).optional(), - defaultValueReference: z.lazy(() => referenceSchema).optional(), - defaultValueSampledData: z.lazy(() => sampledDataSchema).optional(), - defaultValueSignature: z.lazy(() => signatureSchema).optional(), - defaultValueTiming: z.lazy(() => timingSchema).optional(), - defaultValueContactDetail: z.lazy(() => contactDetailSchema).optional(), - defaultValueContributor: z.lazy(() => contributorSchema).optional(), - defaultValueDataRequirement: z - .lazy(() => dataRequirementSchema) - .optional(), - defaultValueExpression: z.lazy(() => expressionSchema).optional(), - defaultValueParameterDefinition: z - .lazy(() => parameterDefinitionSchema) - .optional(), - defaultValueRelatedArtifact: z - .lazy(() => relatedArtifactSchema) - .optional(), - defaultValueTriggerDefinition: z - .lazy(() => triggerDefinitionSchema) - .optional(), - defaultValueUsageContext: z.lazy(() => usageContextSchema).optional(), - defaultValueDosage: z.lazy(() => dosageSchema).optional(), - defaultValueMeta: z.lazy(() => metaSchema).optional(), - meaningWhenMissing: markdownSchema.optional(), - _meaningWhenMissing: z.lazy(() => elementSchema).optional(), - orderMeaning: stringSchema.optional(), - _orderMeaning: z.lazy(() => elementSchema).optional(), - fixedBase64Binary: base64BinarySchema.optional(), - _fixedBase64Binary: z.lazy(() => elementSchema).optional(), - fixedBoolean: booleanSchema.optional(), - _fixedBoolean: z.lazy(() => elementSchema).optional(), - fixedCanonical: canonicalSchema.optional(), - _fixedCanonical: z.lazy(() => elementSchema).optional(), - fixedCode: codeSchema.optional(), - _fixedCode: z.lazy(() => elementSchema).optional(), - fixedDate: dateSchema.optional(), - _fixedDate: z.lazy(() => elementSchema).optional(), - fixedDateTime: dateTimeSchema.optional(), - _fixedDateTime: z.lazy(() => elementSchema).optional(), - fixedDecimal: decimalSchema.optional(), - _fixedDecimal: z.lazy(() => elementSchema).optional(), - fixedId: idSchema.optional(), - _fixedId: z.lazy(() => elementSchema).optional(), - fixedInstant: instantSchema.optional(), - _fixedInstant: z.lazy(() => elementSchema).optional(), - fixedInteger: integerSchema.optional(), - _fixedInteger: z.lazy(() => elementSchema).optional(), - fixedMarkdown: markdownSchema.optional(), - _fixedMarkdown: z.lazy(() => elementSchema).optional(), - fixedOid: oidSchema.optional(), - _fixedOid: z.lazy(() => elementSchema).optional(), - fixedPositiveInt: positiveIntSchema.optional(), - _fixedPositiveInt: z.lazy(() => elementSchema).optional(), - fixedString: stringSchema.optional(), - _fixedString: z.lazy(() => elementSchema).optional(), - fixedTime: timeSchema.optional(), - _fixedTime: z.lazy(() => elementSchema).optional(), - fixedUnsignedInt: unsignedIntSchema.optional(), - _fixedUnsignedInt: z.lazy(() => elementSchema).optional(), - fixedUri: uriSchema.optional(), - _fixedUri: z.lazy(() => elementSchema).optional(), - fixedUrl: urlSchema.optional(), - _fixedUrl: z.lazy(() => elementSchema).optional(), - fixedUuid: uuidSchema.optional(), - _fixedUuid: z.lazy(() => elementSchema).optional(), - fixedAddress: z.lazy(() => addressSchema).optional(), - fixedAge: z.lazy(() => ageSchema).optional(), - fixedAnnotation: z.lazy(() => annotationSchema).optional(), - fixedAttachment: z.lazy(() => attachmentSchema).optional(), - fixedCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - fixedCoding: z.lazy(() => codingSchema).optional(), - fixedContactPoint: z.lazy(() => contactPointSchema).optional(), - fixedCount: z.lazy(() => countSchema).optional(), - fixedDistance: z.lazy(() => distanceSchema).optional(), - fixedDuration: z.lazy(() => durationSchema).optional(), - fixedHumanName: z.lazy(() => humanNameSchema).optional(), - fixedIdentifier: z.lazy(() => identifierSchema).optional(), - fixedMoney: z.lazy(() => moneySchema).optional(), - fixedPeriod: z.lazy(() => periodSchema).optional(), - fixedQuantity: z.lazy(() => quantitySchema).optional(), - fixedRange: z.lazy(() => rangeSchema).optional(), - fixedRatio: z.lazy(() => ratioSchema).optional(), - fixedReference: z.lazy(() => referenceSchema).optional(), - fixedSampledData: z.lazy(() => sampledDataSchema).optional(), - fixedSignature: z.lazy(() => signatureSchema).optional(), - fixedTiming: z.lazy(() => timingSchema).optional(), - fixedContactDetail: z.lazy(() => contactDetailSchema).optional(), - fixedContributor: z.lazy(() => contributorSchema).optional(), - fixedDataRequirement: z.lazy(() => dataRequirementSchema).optional(), - fixedExpression: z.lazy(() => expressionSchema).optional(), - fixedParameterDefinition: z - .lazy(() => parameterDefinitionSchema) - .optional(), - fixedRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), - fixedTriggerDefinition: z - .lazy(() => triggerDefinitionSchema) - .optional(), - fixedUsageContext: z.lazy(() => usageContextSchema).optional(), - fixedDosage: z.lazy(() => dosageSchema).optional(), - fixedMeta: z.lazy(() => metaSchema).optional(), - patternBase64Binary: base64BinarySchema.optional(), - _patternBase64Binary: z.lazy(() => elementSchema).optional(), - patternBoolean: booleanSchema.optional(), - _patternBoolean: z.lazy(() => elementSchema).optional(), - patternCanonical: canonicalSchema.optional(), - _patternCanonical: z.lazy(() => elementSchema).optional(), - patternCode: codeSchema.optional(), - _patternCode: z.lazy(() => elementSchema).optional(), - patternDate: dateSchema.optional(), - _patternDate: z.lazy(() => elementSchema).optional(), - patternDateTime: dateTimeSchema.optional(), - _patternDateTime: z.lazy(() => elementSchema).optional(), - patternDecimal: decimalSchema.optional(), - _patternDecimal: z.lazy(() => elementSchema).optional(), - patternId: idSchema.optional(), - _patternId: z.lazy(() => elementSchema).optional(), - patternInstant: instantSchema.optional(), - _patternInstant: z.lazy(() => elementSchema).optional(), - patternInteger: integerSchema.optional(), - _patternInteger: z.lazy(() => elementSchema).optional(), - patternMarkdown: markdownSchema.optional(), - _patternMarkdown: z.lazy(() => elementSchema).optional(), - patternOid: oidSchema.optional(), - _patternOid: z.lazy(() => elementSchema).optional(), - patternPositiveInt: positiveIntSchema.optional(), - _patternPositiveInt: z.lazy(() => elementSchema).optional(), - patternString: stringSchema.optional(), - _patternString: z.lazy(() => elementSchema).optional(), - patternTime: timeSchema.optional(), - _patternTime: z.lazy(() => elementSchema).optional(), - patternUnsignedInt: unsignedIntSchema.optional(), - _patternUnsignedInt: z.lazy(() => elementSchema).optional(), - patternUri: uriSchema.optional(), - _patternUri: z.lazy(() => elementSchema).optional(), - patternUrl: urlSchema.optional(), - _patternUrl: z.lazy(() => elementSchema).optional(), - patternUuid: uuidSchema.optional(), - _patternUuid: z.lazy(() => elementSchema).optional(), - patternAddress: z.lazy(() => addressSchema).optional(), - patternAge: z.lazy(() => ageSchema).optional(), - patternAnnotation: z.lazy(() => annotationSchema).optional(), - patternAttachment: z.lazy(() => attachmentSchema).optional(), - patternCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - patternCoding: z.lazy(() => codingSchema).optional(), - patternContactPoint: z.lazy(() => contactPointSchema).optional(), - patternCount: z.lazy(() => countSchema).optional(), - patternDistance: z.lazy(() => distanceSchema).optional(), - patternDuration: z.lazy(() => durationSchema).optional(), - patternHumanName: z.lazy(() => humanNameSchema).optional(), - patternIdentifier: z.lazy(() => identifierSchema).optional(), - patternMoney: z.lazy(() => moneySchema).optional(), - patternPeriod: z.lazy(() => periodSchema).optional(), - patternQuantity: z.lazy(() => quantitySchema).optional(), - patternRange: z.lazy(() => rangeSchema).optional(), - patternRatio: z.lazy(() => ratioSchema).optional(), - patternReference: z.lazy(() => referenceSchema).optional(), - patternSampledData: z.lazy(() => sampledDataSchema).optional(), - patternSignature: z.lazy(() => signatureSchema).optional(), - patternTiming: z.lazy(() => timingSchema).optional(), - patternContactDetail: z.lazy(() => contactDetailSchema).optional(), - patternContributor: z.lazy(() => contributorSchema).optional(), - patternDataRequirement: z.lazy(() => dataRequirementSchema).optional(), - patternExpression: z.lazy(() => expressionSchema).optional(), - patternParameterDefinition: z - .lazy(() => parameterDefinitionSchema) - .optional(), - patternRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), - patternTriggerDefinition: z - .lazy(() => triggerDefinitionSchema) - .optional(), - patternUsageContext: z.lazy(() => usageContextSchema).optional(), - patternDosage: z.lazy(() => dosageSchema).optional(), - patternMeta: z.lazy(() => metaSchema).optional(), - example: elementDefinitionExampleSchema.array().optional(), - minValueDate: dateSchema.optional(), - _minValueDate: z.lazy(() => elementSchema).optional(), - minValueDateTime: dateTimeSchema.optional(), - _minValueDateTime: z.lazy(() => elementSchema).optional(), - minValueInstant: instantSchema.optional(), - _minValueInstant: z.lazy(() => elementSchema).optional(), - minValueTime: timeSchema.optional(), - _minValueTime: z.lazy(() => elementSchema).optional(), - minValueDecimal: decimalSchema.optional(), - _minValueDecimal: z.lazy(() => elementSchema).optional(), - minValueInteger: integerSchema.optional(), - _minValueInteger: z.lazy(() => elementSchema).optional(), - minValuePositiveInt: positiveIntSchema.optional(), - _minValuePositiveInt: z.lazy(() => elementSchema).optional(), - minValueUnsignedInt: unsignedIntSchema.optional(), - _minValueUnsignedInt: z.lazy(() => elementSchema).optional(), - minValueQuantity: z.lazy(() => quantitySchema).optional(), - maxValueDate: dateSchema.optional(), - _maxValueDate: z.lazy(() => elementSchema).optional(), - maxValueDateTime: dateTimeSchema.optional(), - _maxValueDateTime: z.lazy(() => elementSchema).optional(), - maxValueInstant: instantSchema.optional(), - _maxValueInstant: z.lazy(() => elementSchema).optional(), - maxValueTime: timeSchema.optional(), - _maxValueTime: z.lazy(() => elementSchema).optional(), - maxValueDecimal: decimalSchema.optional(), - _maxValueDecimal: z.lazy(() => elementSchema).optional(), - maxValueInteger: integerSchema.optional(), - _maxValueInteger: z.lazy(() => elementSchema).optional(), - maxValuePositiveInt: positiveIntSchema.optional(), - _maxValuePositiveInt: z.lazy(() => elementSchema).optional(), - maxValueUnsignedInt: unsignedIntSchema.optional(), - _maxValueUnsignedInt: z.lazy(() => elementSchema).optional(), - maxValueQuantity: z.lazy(() => quantitySchema).optional(), - maxLength: integerSchema.optional(), - _maxLength: z.lazy(() => elementSchema).optional(), - condition: idSchema.array().optional(), - _condition: z - .lazy(() => elementSchema) - .array() - .optional(), - constraint: elementDefinitionConstraintSchema.array().optional(), - mustSupport: booleanSchema.optional(), - _mustSupport: z.lazy(() => elementSchema).optional(), - isModifier: booleanSchema.optional(), - _isModifier: z.lazy(() => elementSchema).optional(), - isModifierReason: stringSchema.optional(), - _isModifierReason: z.lazy(() => elementSchema).optional(), - isSummary: booleanSchema.optional(), - _isSummary: z.lazy(() => elementSchema).optional(), - binding: elementDefinitionBindingSchema.optional(), - mapping: elementDefinitionMappingSchema.array().optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'defaultValueBase64Binary', - 'defaultValueBoolean', - 'defaultValueCanonical', - 'defaultValueCode', - 'defaultValueDate', - 'defaultValueDateTime', - 'defaultValueDecimal', - 'defaultValueId', - 'defaultValueInstant', - 'defaultValueInteger', - 'defaultValueMarkdown', - 'defaultValueOid', - 'defaultValuePositiveInt', - 'defaultValueString', - 'defaultValueTime', - 'defaultValueUnsignedInt', - 'defaultValueUri', - 'defaultValueUrl', - 'defaultValueUuid', - 'defaultValueAddress', - 'defaultValueAge', - 'defaultValueAnnotation', - 'defaultValueAttachment', - 'defaultValueCodeableConcept', - 'defaultValueCoding', - 'defaultValueContactPoint', - 'defaultValueCount', - 'defaultValueDistance', - 'defaultValueDuration', - 'defaultValueHumanName', - 'defaultValueIdentifier', - 'defaultValueMoney', - 'defaultValuePeriod', - 'defaultValueQuantity', - 'defaultValueRange', - 'defaultValueRatio', - 'defaultValueReference', - 'defaultValueSampledData', - 'defaultValueSignature', - 'defaultValueTiming', - 'defaultValueContactDetail', - 'defaultValueContributor', - 'defaultValueDataRequirement', - 'defaultValueExpression', - 'defaultValueParameterDefinition', - 'defaultValueRelatedArtifact', - 'defaultValueTriggerDefinition', - 'defaultValueUsageContext', - 'defaultValueDosage', - 'defaultValueMeta', - ], - required: false, - }, - { - names: [ - 'fixedBase64Binary', - 'fixedBoolean', - 'fixedCanonical', - 'fixedCode', - 'fixedDate', - 'fixedDateTime', - 'fixedDecimal', - 'fixedId', - 'fixedInstant', - 'fixedInteger', - 'fixedMarkdown', - 'fixedOid', - 'fixedPositiveInt', - 'fixedString', - 'fixedTime', - 'fixedUnsignedInt', - 'fixedUri', - 'fixedUrl', - 'fixedUuid', - 'fixedAddress', - 'fixedAge', - 'fixedAnnotation', - 'fixedAttachment', - 'fixedCodeableConcept', - 'fixedCoding', - 'fixedContactPoint', - 'fixedCount', - 'fixedDistance', - 'fixedDuration', - 'fixedHumanName', - 'fixedIdentifier', - 'fixedMoney', - 'fixedPeriod', - 'fixedQuantity', - 'fixedRange', - 'fixedRatio', - 'fixedReference', - 'fixedSampledData', - 'fixedSignature', - 'fixedTiming', - 'fixedContactDetail', - 'fixedContributor', - 'fixedDataRequirement', - 'fixedExpression', - 'fixedParameterDefinition', - 'fixedRelatedArtifact', - 'fixedTriggerDefinition', - 'fixedUsageContext', - 'fixedDosage', - 'fixedMeta', - ], - required: false, - }, - { - names: [ - 'patternBase64Binary', - 'patternBoolean', - 'patternCanonical', - 'patternCode', - 'patternDate', - 'patternDateTime', - 'patternDecimal', - 'patternId', - 'patternInstant', - 'patternInteger', - 'patternMarkdown', - 'patternOid', - 'patternPositiveInt', - 'patternString', - 'patternTime', - 'patternUnsignedInt', - 'patternUri', - 'patternUrl', - 'patternUuid', - 'patternAddress', - 'patternAge', - 'patternAnnotation', - 'patternAttachment', - 'patternCodeableConcept', - 'patternCoding', - 'patternContactPoint', - 'patternCount', - 'patternDistance', - 'patternDuration', - 'patternHumanName', - 'patternIdentifier', - 'patternMoney', - 'patternPeriod', - 'patternQuantity', - 'patternRange', - 'patternRatio', - 'patternReference', - 'patternSampledData', - 'patternSignature', - 'patternTiming', - 'patternContactDetail', - 'patternContributor', - 'patternDataRequirement', - 'patternExpression', - 'patternParameterDefinition', - 'patternRelatedArtifact', - 'patternTriggerDefinition', - 'patternUsageContext', - 'patternDosage', - 'patternMeta', - ], - required: false, - }, - { - names: [ - 'minValueDate', - 'minValueDateTime', - 'minValueInstant', - 'minValueTime', - 'minValueDecimal', - 'minValueInteger', - 'minValuePositiveInt', - 'minValueUnsignedInt', - 'minValueQuantity', - ], - required: false, - }, - { - names: [ - 'maxValueDate', - 'maxValueDateTime', - 'maxValueInstant', - 'maxValueTime', - 'maxValueDecimal', - 'maxValueInteger', - 'maxValuePositiveInt', - 'maxValueUnsignedInt', - 'maxValueQuantity', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) + reference: z + .lazy(() => referenceSchema) + .array() + .min(1), + role: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const encounterStatusHistorySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const contractSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Contract'), id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) .array() + .min(1) .optional(), - status: z.enum([ - 'arrived', - 'cancelled', - 'entered-in-error', - 'finished', - 'in-progress', - 'onleave', - 'planned', - 'triaged', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema), - }), - ) - -export const encounterClassHistorySchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - class: z.lazy(() => codingSchema), - period: z.lazy(() => periodSchema), - }), - ) - -export const encounterParticipantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + status: z + .enum([ + 'amended', + 'appended', + 'cancelled', + 'disputed', + 'entered-in-error', + 'executable', + 'executed', + 'negotiable', + 'offered', + 'policy', + 'rejected', + 'renewed', + 'resolved', + 'revoked', + 'terminated', + ]) .optional(), - type: z - .lazy(() => codeableConceptSchema) + _status: z.lazy(() => elementSchema).optional(), + legalState: z.lazy(() => codeableConceptSchema).optional(), + instantiatesCanonical: z.lazy(() => referenceSchema).optional(), + instantiatesUri: uriSchema.optional(), + _instantiatesUri: z.lazy(() => elementSchema).optional(), + contentDerivative: z.lazy(() => codeableConceptSchema).optional(), + issued: dateTimeSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + applies: z.lazy(() => periodSchema).optional(), + expirationType: z.lazy(() => codeableConceptSchema).optional(), + subject: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - period: z.lazy(() => periodSchema).optional(), - individual: z.lazy(() => referenceSchema).optional(), - }), - ) - -export const encounterDiagnosisSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + authority: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + domain: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - condition: z.lazy(() => referenceSchema), - use: z.lazy(() => codeableConceptSchema).optional(), - rank: positiveIntSchema.optional(), - _rank: z.lazy(() => elementSchema).optional(), - }), - ) - -export const encounterHospitalizationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + site: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + alias: stringSchema.nullable().array().min(1).optional(), + _alias: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - preAdmissionIdentifier: z.lazy(() => identifierSchema).optional(), - origin: z.lazy(() => referenceSchema).optional(), - admitSource: z.lazy(() => codeableConceptSchema).optional(), - reAdmission: z.lazy(() => codeableConceptSchema).optional(), - dietPreference: z + author: z.lazy(() => referenceSchema).optional(), + scope: z.lazy(() => codeableConceptSchema).optional(), + topicCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + topicReference: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subType: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - specialCourtesy: z - .lazy(() => codeableConceptSchema) + contentDefinition: contractContentDefinitionSchema.optional(), + term: contractTermSchema.array().min(1).optional(), + supportingInfo: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - specialArrangement: z - .lazy(() => codeableConceptSchema) + relevantHistory: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - destination: z.lazy(() => referenceSchema).optional(), - dischargeDisposition: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + signer: contractSignerSchema.array().min(1).optional(), + friendly: contractFriendlySchema.array().min(1).optional(), + legal: contractLegalSchema.array().min(1).optional(), + rule: contractRuleSchema.array().min(1).optional(), + legallyBindingAttachment: z.lazy(() => attachmentSchema).optional(), + legallyBindingReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['topicCodeableConcept'], ['topicReference']], + required: false, + }, + { + alternatives: [ + ['legallyBindingAttachment'], + ['legallyBindingReference'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'alias', shadow: '_alias', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const encounterLocationSchema: z.ZodType = z.lazy( +export const contributorSchema: z.ZodType> = z.lazy( () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - location: z.lazy(() => referenceSchema), - status: z.enum(['active', 'completed', 'planned', 'reserved']).optional(), - _status: z.lazy(() => elementSchema).optional(), - physicalType: z.lazy(() => codeableConceptSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), -) - -export const encounterSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Encounter'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'arrived', - 'cancelled', - 'entered-in-error', - 'finished', - 'in-progress', - 'onleave', - 'planned', - 'triaged', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusHistory: encounterStatusHistorySchema.array().optional(), - class: z.lazy(() => codingSchema), - classHistory: encounterClassHistorySchema.array().optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - serviceType: z.lazy(() => codeableConceptSchema).optional(), - priority: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - episodeOfCare: z - .lazy(() => referenceSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - participant: encounterParticipantSchema.array().optional(), - appointment: z - .lazy(() => referenceSchema) - .array() - .optional(), - period: z.lazy(() => periodSchema).optional(), - length: z.lazy(() => durationSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - diagnosis: encounterDiagnosisSchema.array().optional(), - account: z - .lazy(() => referenceSchema) - .array() - .optional(), - hospitalization: encounterHospitalizationSchema.optional(), - location: encounterLocationSchema.array().optional(), - serviceProvider: z.lazy(() => referenceSchema).optional(), - partOf: z.lazy(() => referenceSchema).optional(), - }), -) - -export const endpointSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Endpoint'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'entered-in-error', - 'error', - 'off', - 'suspended', - 'test', - ]), - _status: z.lazy(() => elementSchema).optional(), - connectionType: z.lazy(() => codingSchema), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - managingOrganization: z.lazy(() => referenceSchema).optional(), - contact: z - .lazy(() => contactPointSchema) - .array() - .optional(), - period: z.lazy(() => periodSchema).optional(), - payloadType: z - .lazy(() => codeableConceptSchema) - .array() - .min(1), - payloadMimeType: codeSchema.array().optional(), - _payloadMimeType: z - .lazy(() => elementSchema) - .array() - .optional(), - address: urlSchema, - _address: z.lazy(() => elementSchema).optional(), - header: stringSchema.array().optional(), - _header: z - .lazy(() => elementSchema) - .array() - .optional(), - }), -) - -export const enrollmentRequestSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('EnrollmentRequest'), + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.enum(['author', 'editor', 'endorser', 'reviewer']).optional(), + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'type', shadow: '_type' }, + { name: 'name', shadow: '_name' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const countSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const quantity = value as Record + const hasCode = + quantity.code !== undefined || quantity._code !== undefined + return ( + !hasCode || + quantity.system !== undefined || + quantity._system !== undefined + ) + }, + { message: 'qty-3: a coded Quantity requires its unit system.' }, + ), +) as unknown as z.ZodType> + +export const coverageClassSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'value', shadow: '_value' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const coverageCostToBeneficiarySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) + type: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + exception: coverageCostToBeneficiaryExceptionSchema .array() + .min(1) .optional(), - status: z - .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + }) + .refine( + (value) => + [ + { alternatives: [['valueQuantity'], ['valueMoney']], required: true }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const coverageCostToBeneficiaryExceptionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) .optional(), - _status: z.lazy(() => elementSchema).optional(), - created: dateTimeSchema.optional(), - _created: z.lazy(() => elementSchema).optional(), - insurer: z.lazy(() => referenceSchema).optional(), - provider: z.lazy(() => referenceSchema).optional(), - candidate: z.lazy(() => referenceSchema).optional(), - coverage: z.lazy(() => referenceSchema).optional(), - }), -) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + period: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const enrollmentResponseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('EnrollmentResponse'), +export const coverageSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Coverage'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -10605,910 +11677,653 @@ export const enrollmentResponseSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), status: z .enum(['active', 'cancelled', 'draft', 'entered-in-error']) .optional(), _status: z.lazy(() => elementSchema).optional(), - request: z.lazy(() => referenceSchema).optional(), - outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), - _outcome: z.lazy(() => elementSchema).optional(), - disposition: stringSchema.optional(), - _disposition: z.lazy(() => elementSchema).optional(), - created: dateTimeSchema.optional(), - _created: z.lazy(() => elementSchema).optional(), - organization: z.lazy(() => referenceSchema).optional(), - requestProvider: z.lazy(() => referenceSchema).optional(), - }), - ) + type: z.lazy(() => codeableConceptSchema).optional(), + policyHolder: z.lazy(() => referenceSchema).optional(), + subscriber: z.lazy(() => referenceSchema).optional(), + subscriberId: stringSchema.optional(), + _subscriberId: z.lazy(() => elementSchema).optional(), + beneficiary: z.lazy(() => referenceSchema), + dependent: stringSchema.optional(), + _dependent: z.lazy(() => elementSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + payor: z + .lazy(() => referenceSchema) + .array() + .min(1), + class: coverageClassSchema.array().min(1).optional(), + order: positiveIntSchema.optional(), + _order: z.lazy(() => elementSchema).optional(), + network: stringSchema.optional(), + _network: z.lazy(() => elementSchema).optional(), + costToBeneficiary: coverageCostToBeneficiarySchema + .array() + .min(1) + .optional(), + subrogation: booleanSchema.optional(), + _subrogation: z.lazy(() => elementSchema).optional(), + contract: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const episodeOfCareStatusHistorySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const coverageEligibilityRequestSupportingInfoSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - status: z.enum([ - 'active', - 'cancelled', - 'entered-in-error', - 'finished', - 'onhold', - 'planned', - 'waitlist', - ]), - _status: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema), - }), - ) + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + information: z.lazy(() => referenceSchema), + appliesToAll: booleanSchema.optional(), + _appliesToAll: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const episodeOfCareDiagnosisSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const coverageEligibilityRequestInsuranceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - condition: z.lazy(() => referenceSchema), - role: z.lazy(() => codeableConceptSchema).optional(), - rank: positiveIntSchema.optional(), - _rank: z.lazy(() => elementSchema).optional(), - }), - ) + focal: booleanSchema.optional(), + _focal: z.lazy(() => elementSchema).optional(), + coverage: z.lazy(() => referenceSchema), + businessArrangement: stringSchema.optional(), + _businessArrangement: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const episodeOfCareSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('EpisodeOfCare'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'cancelled', - 'entered-in-error', - 'finished', - 'onhold', - 'planned', - 'waitlist', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusHistory: episodeOfCareStatusHistorySchema.array().optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - diagnosis: episodeOfCareDiagnosisSchema.array().optional(), - patient: z.lazy(() => referenceSchema), - managingOrganization: z.lazy(() => referenceSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - referralRequest: z - .lazy(() => referenceSchema) - .array() - .optional(), - careManager: z.lazy(() => referenceSchema).optional(), - team: z - .lazy(() => referenceSchema) - .array() - .optional(), - account: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) - -export const eventDefinitionSchema: z.ZodType = z.lazy( - () => - z - .strictObject({ - resourceType: z.literal('EventDefinition'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - author: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - editor: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - reviewer: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - endorser: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) - .array() - .optional(), - trigger: z - .lazy(() => triggerDefinitionSchema) - .array() - .min(1), - }) - .refine( - (value) => - [ - { - names: ['subjectCodeableConcept', 'subjectReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const evidenceSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Evidence'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - shortTitle: stringSchema.optional(), - _shortTitle: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - author: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - editor: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - reviewer: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - endorser: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) - .array() - .optional(), - exposureBackground: z.lazy(() => referenceSchema), - exposureVariant: z - .lazy(() => referenceSchema) - .array() - .optional(), - outcome: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) - -export const evidenceVariableCharacteristicSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - definitionReference: z.lazy(() => referenceSchema).optional(), - definitionCanonical: canonicalSchema.optional(), - _definitionCanonical: z.lazy(() => elementSchema).optional(), - definitionCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - definitionExpression: z.lazy(() => expressionSchema).optional(), - definitionDataRequirement: z - .lazy(() => dataRequirementSchema) - .optional(), - definitionTriggerDefinition: z - .lazy(() => triggerDefinitionSchema) - .optional(), - usageContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - exclude: booleanSchema.optional(), - _exclude: z.lazy(() => elementSchema).optional(), - participantEffectiveDateTime: dateTimeSchema.optional(), - _participantEffectiveDateTime: z.lazy(() => elementSchema).optional(), - participantEffectivePeriod: z.lazy(() => periodSchema).optional(), - participantEffectiveDuration: z.lazy(() => durationSchema).optional(), - participantEffectiveTiming: z.lazy(() => timingSchema).optional(), - timeFromStart: z.lazy(() => durationSchema).optional(), - groupMeasure: z - .enum([ - 'mean', - 'mean-of-mean', - 'mean-of-median', - 'median', - 'median-of-mean', - 'median-of-median', - ]) - .optional(), - _groupMeasure: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'definitionReference', - 'definitionCanonical', - 'definitionCodeableConcept', - 'definitionExpression', - 'definitionDataRequirement', - 'definitionTriggerDefinition', - ], - required: true, - }, - { - names: [ - 'participantEffectiveDateTime', - 'participantEffectivePeriod', - 'participantEffectiveDuration', - 'participantEffectiveTiming', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const evidenceVariableSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('EvidenceVariable'), +export const coverageEligibilityRequestItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - shortTitle: stringSchema.optional(), - _shortTitle: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - useContext: z - .lazy(() => usageContextSchema) + supportingInfoSequence: positiveIntSchema + .nullable() .array() + .min(1) .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) + _supportingInfoSequence: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema).optional(), + modifier: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - author: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - editor: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - reviewer: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - endorser: z - .lazy(() => contactDetailSchema) + provider: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + facility: z.lazy(() => referenceSchema).optional(), + diagnosis: coverageEligibilityRequestItemDiagnosisSchema .array() + .min(1) .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) + detail: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - type: z.enum(['continuous', 'descriptive', 'dichotomous']).optional(), - _type: z.lazy(() => elementSchema).optional(), - characteristic: evidenceVariableCharacteristicSchema.array().min(1), - }), -) + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'supportingInfoSequence', + shadow: '_supportingInfoSequence', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const exampleScenarioActorSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const coverageEligibilityRequestItemDiagnosisSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - actorId: stringSchema, - _actorId: z.lazy(() => elementSchema).optional(), - type: z.enum(['entity', 'person']), - _type: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - }), - ) + diagnosisCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + diagnosisReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['diagnosisCodeableConcept'], + ['diagnosisReference'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const exampleScenarioInstanceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const coverageEligibilityRequestSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CoverageEligibilityRequest'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - resourceId: stringSchema, - _resourceId: z.lazy(() => elementSchema).optional(), - resourceType: z.enum([ - 'Account', - 'ActivityDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'EffectEvidenceSynthesis', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProduct', - 'MedicinalProductAuthorization', - 'MedicinalProductContraindication', - 'MedicinalProductIndication', - 'MedicinalProductIngredient', - 'MedicinalProductInteraction', - 'MedicinalProductManufactured', - 'MedicinalProductPackaged', - 'MedicinalProductPharmaceutical', - 'MedicinalProductUndesirableEffect', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'RiskEvidenceSynthesis', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'Substance', - 'SubstanceNucleicAcid', - 'SubstancePolymer', - 'SubstanceProtein', - 'SubstanceReferenceInformation', - 'SubstanceSourceMaterial', - 'SubstanceSpecification', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]), - _resourceType: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - version: exampleScenarioInstanceVersionSchema.array().optional(), - containedInstance: exampleScenarioInstanceContainedInstanceSchema + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - }), - ) - -export const exampleScenarioProcessSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + _status: z.lazy(() => elementSchema).optional(), + priority: z.lazy(() => codeableConceptSchema).optional(), + purpose: z + .enum(['auth-requirements', 'benefits', 'discovery', 'validation']) + .nullable() .array() + .min(1) .optional(), - title: stringSchema, - _title: z.lazy(() => elementSchema).optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - preConditions: markdownSchema.optional(), - _preConditions: z.lazy(() => elementSchema).optional(), - postConditions: markdownSchema.optional(), - _postConditions: z.lazy(() => elementSchema).optional(), - step: exampleScenarioProcessStepSchema.array().optional(), - }), - ) - -export const exampleScenarioInstanceVersionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + _purpose: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + patient: z.lazy(() => referenceSchema), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + provider: z.lazy(() => referenceSchema).optional(), + insurer: z.lazy(() => referenceSchema), + facility: z.lazy(() => referenceSchema).optional(), + supportingInfo: coverageEligibilityRequestSupportingInfoSchema .array() + .min(1) .optional(), - versionId: stringSchema, - _versionId: z.lazy(() => elementSchema).optional(), - description: markdownSchema, - _description: z.lazy(() => elementSchema).optional(), - }), - ) + insurance: coverageEligibilityRequestInsuranceSchema + .array() + .min(1) + .optional(), + item: coverageEligibilityRequestItemSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['servicedDate', '_servicedDate'], + ['servicedPeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'created', shadow: '_created' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'purpose', shadow: '_purpose', required: true }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const exampleScenarioInstanceContainedInstanceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const coverageEligibilityResponseInsuranceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - resourceId: stringSchema, - _resourceId: z.lazy(() => elementSchema).optional(), - versionId: stringSchema.optional(), - _versionId: z.lazy(() => elementSchema).optional(), - }), - ) + coverage: z.lazy(() => referenceSchema), + inforce: booleanSchema.optional(), + _inforce: z.lazy(() => elementSchema).optional(), + benefitPeriod: z.lazy(() => periodSchema).optional(), + item: coverageEligibilityResponseInsuranceItemSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const exampleScenarioProcessStepSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const coverageEligibilityResponseErrorSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - process: z - .lazy(() => exampleScenarioProcessSchema) - .array() - .optional(), - pause: booleanSchema.optional(), - _pause: z.lazy(() => elementSchema).optional(), - operation: exampleScenarioProcessStepOperationSchema.optional(), - alternative: exampleScenarioProcessStepAlternativeSchema - .array() - .optional(), - }), - ) + code: z.lazy(() => codeableConceptSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const exampleScenarioProcessStepOperationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const coverageEligibilityResponseInsuranceItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - number: stringSchema, - _number: z.lazy(() => elementSchema).optional(), - type: stringSchema.optional(), - _type: z.lazy(() => elementSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema).optional(), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + provider: z.lazy(() => referenceSchema).optional(), + excluded: booleanSchema.optional(), + _excluded: z.lazy(() => elementSchema).optional(), name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), - initiator: stringSchema.optional(), - _initiator: z.lazy(() => elementSchema).optional(), - receiver: stringSchema.optional(), - _receiver: z.lazy(() => elementSchema).optional(), - description: markdownSchema.optional(), + description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - initiatorActive: booleanSchema.optional(), - _initiatorActive: z.lazy(() => elementSchema).optional(), - receiverActive: booleanSchema.optional(), - _receiverActive: z.lazy(() => elementSchema).optional(), - request: z - .lazy(() => exampleScenarioInstanceContainedInstanceSchema) + network: z.lazy(() => codeableConceptSchema).optional(), + unit: z.lazy(() => codeableConceptSchema).optional(), + term: z.lazy(() => codeableConceptSchema).optional(), + benefit: coverageEligibilityResponseInsuranceItemBenefitSchema + .array() + .min(1) .optional(), - response: z - .lazy(() => exampleScenarioInstanceContainedInstanceSchema) + authorizationRequired: booleanSchema.optional(), + _authorizationRequired: z.lazy(() => elementSchema).optional(), + authorizationSupporting: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) .optional(), - }), - ) + authorizationUrl: uriSchema.optional(), + _authorizationUrl: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const exampleScenarioProcessStepAlternativeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const coverageEligibilityResponseInsuranceItemBenefitSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - title: stringSchema, - _title: z.lazy(() => elementSchema).optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - step: z - .lazy(() => exampleScenarioProcessStepSchema) - .array() - .optional(), - }), - ) + type: z.lazy(() => codeableConceptSchema), + allowedUnsignedInt: unsignedIntSchema.optional(), + _allowedUnsignedInt: z.lazy(() => elementSchema).optional(), + allowedString: stringSchema.optional(), + _allowedString: z.lazy(() => elementSchema).optional(), + allowedMoney: z.lazy(() => moneySchema).optional(), + usedUnsignedInt: unsignedIntSchema.optional(), + _usedUnsignedInt: z.lazy(() => elementSchema).optional(), + usedString: stringSchema.optional(), + _usedString: z.lazy(() => elementSchema).optional(), + usedMoney: z.lazy(() => moneySchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['allowedUnsignedInt', '_allowedUnsignedInt'], + ['allowedString', '_allowedString'], + ['allowedMoney'], + ], + required: false, + }, + { + alternatives: [ + ['usedUnsignedInt', '_usedUnsignedInt'], + ['usedString', '_usedString'], + ['usedMoney'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const exampleScenarioSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('ExampleScenario'), +export const coverageEligibilityResponseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CoverageEligibilityResponse'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -11520,118 +12335,276 @@ export const exampleScenarioSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) .optional(), - useContext: z - .lazy(() => usageContextSchema) + _status: z.lazy(() => elementSchema).optional(), + purpose: z + .enum(['auth-requirements', 'benefits', 'discovery', 'validation']) + .nullable() .array() + .min(1) .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) + _purpose: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - actor: exampleScenarioActorSchema.array().optional(), - instance: exampleScenarioInstanceSchema.array().optional(), - process: exampleScenarioProcessSchema.array().optional(), - workflow: canonicalSchema.array().optional(), - _workflow: z - .lazy(() => elementSchema) + patient: z.lazy(() => referenceSchema), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + requestor: z.lazy(() => referenceSchema).optional(), + request: z.lazy(() => referenceSchema), + outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + insurer: z.lazy(() => referenceSchema), + insurance: coverageEligibilityResponseInsuranceSchema .array() + .min(1) .optional(), - }), -) + preAuthRef: stringSchema.optional(), + _preAuthRef: z.lazy(() => elementSchema).optional(), + form: z.lazy(() => codeableConceptSchema).optional(), + error: coverageEligibilityResponseErrorSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['servicedDate', '_servicedDate'], + ['servicedPeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'created', shadow: '_created' }, + { name: 'outcome', shadow: '_outcome' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'purpose', shadow: '_purpose', required: true }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitRelatedSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const dataRequirementCodeFilterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + searchParam: stringSchema.optional(), + _searchParam: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema.optional(), + _valueSet: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codingSchema) .array() + .min(1) .optional(), - claim: z.lazy(() => referenceSchema).optional(), - relationship: z.lazy(() => codeableConceptSchema).optional(), - reference: z.lazy(() => identifierSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitPayeeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const dataRequirementDateFilterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - party: z.lazy(() => referenceSchema).optional(), - }), - ) + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + searchParam: stringSchema.optional(), + _searchParam: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueDateTime', '_valueDateTime'], + ['valuePeriod'], + ['valueDuration'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitCareTeamSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const dataRequirementSortSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - provider: z.lazy(() => referenceSchema), - responsible: booleanSchema.optional(), - _responsible: z.lazy(() => elementSchema).optional(), - role: z.lazy(() => codeableConceptSchema).optional(), - qualification: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + direction: z.enum(['ascending', 'descending']).optional(), + _direction: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'path', shadow: '_path' }, + { name: 'direction', shadow: '_direction' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitSupportingInfoSchema: z.ZodType = +export const dataRequirementSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -11639,44 +12612,261 @@ export const explanationOfBenefitSupportingInfoSchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + type: z + .enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Any', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'CodeableConcept', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'EffectEvidenceSynthesis', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'Reference', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceAmount', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'Type', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.nullable().array().min(1).optional(), + _profile: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - category: z.lazy(() => codeableConceptSchema), - code: z.lazy(() => codeableConceptSchema).optional(), - timingDate: dateSchema.optional(), - _timingDate: z.lazy(() => elementSchema).optional(), - timingPeriod: z.lazy(() => periodSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - reason: z.lazy(() => codingSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + mustSupport: stringSchema.nullable().array().min(1).optional(), + _mustSupport: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + codeFilter: dataRequirementCodeFilterSchema.array().min(1).optional(), + dateFilter: dataRequirementDateFilterSchema.array().min(1).optional(), + limit: positiveIntSchema.optional(), + _limit: z.lazy(() => elementSchema).optional(), + sort: dataRequirementSortSchema.array().min(1).optional(), }) .refine( (value) => [ - { names: ['timingDate', 'timingPeriod'], required: false }, { - names: [ - 'valueBoolean', - 'valueString', - 'valueQuantity', - 'valueAttachment', - 'valueReference', - ], + alternatives: [['subjectCodeableConcept'], ['subjectReference']], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -11684,45 +12874,219 @@ export const explanationOfBenefitSupportingInfoSchema: z.ZodType { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'profile', shadow: '_profile', required: false }, + { name: 'mustSupport', shadow: '_mustSupport', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) + ) as unknown as z.ZodType> + +export const detectedIssueEvidenceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + detail: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const detectedIssueMitigationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + action: z.lazy(() => codeableConceptSchema), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitDiagnosisSchema: z.ZodType = +export const detectedIssueSchema: z.ZodType> = z.lazy(() => z .strictObject({ + resourceType: z.literal('DetectedIssue'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - diagnosisCodeableConcept: z - .lazy(() => codeableConceptSchema) + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) .optional(), - diagnosisReference: z.lazy(() => referenceSchema).optional(), - type: z - .lazy(() => codeableConceptSchema) + status: z + .enum([ + 'amended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'preliminary', + 'registered', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + severity: z.enum(['high', 'low', 'moderate']).optional(), + _severity: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema).optional(), + identifiedDateTime: dateTimeSchema.optional(), + _identifiedDateTime: z.lazy(() => elementSchema).optional(), + identifiedPeriod: z.lazy(() => periodSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + implicated: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - onAdmission: z.lazy(() => codeableConceptSchema).optional(), - packageCode: z.lazy(() => codeableConceptSchema).optional(), + evidence: detectedIssueEvidenceSchema.array().min(1).optional(), + detail: stringSchema.optional(), + _detail: z.lazy(() => elementSchema).optional(), + reference: uriSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + mitigation: detectedIssueMitigationSchema.array().min(1).optional(), }) .refine( (value) => [ { - names: ['diagnosisCodeableConcept', 'diagnosisReference'], - required: true, + alternatives: [ + ['identifiedDateTime', '_identifiedDateTime'], + ['identifiedPeriod'], + ], + required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -11730,10 +13094,32 @@ export const explanationOfBenefitDiagnosisSchema: z.ZodType { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) + ) as unknown as z.ZodType> -export const explanationOfBenefitProcedureSchema: z.ZodType = +export const deviceUdiCarrierSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -11741,72 +13127,126 @@ export const explanationOfBenefitProcedureSchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + deviceIdentifier: stringSchema.optional(), + _deviceIdentifier: z.lazy(() => elementSchema).optional(), + issuer: uriSchema.optional(), + _issuer: z.lazy(() => elementSchema).optional(), + jurisdiction: uriSchema.optional(), + _jurisdiction: z.lazy(() => elementSchema).optional(), + carrierAIDC: base64BinarySchema.optional(), + _carrierAIDC: z.lazy(() => elementSchema).optional(), + carrierHRF: stringSchema.optional(), + _carrierHRF: z.lazy(() => elementSchema).optional(), + entryType: z + .enum([ + 'barcode', + 'card', + 'manual', + 'rfid', + 'self-reported', + 'unknown', + ]) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - type: z - .lazy(() => codeableConceptSchema) + _entryType: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const deviceDeviceNameSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - procedureCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - procedureReference: z.lazy(() => referenceSchema).optional(), - udi: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'manufacturer-name', + 'model-name', + 'other', + 'patient-reported-name', + 'udi-label-name', + 'user-friendly-name', + ]) .optional(), + _type: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['procedureCodeableConcept', 'procedureReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const explanationOfBenefitInsuranceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const deviceSpecializationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - focal: booleanSchema, - _focal: z.lazy(() => elementSchema).optional(), - coverage: z.lazy(() => referenceSchema), - preAuthRef: stringSchema.array().optional(), - _preAuthRef: z - .lazy(() => elementSchema) - .array() - .optional(), - }), - ) + systemType: z.lazy(() => codeableConceptSchema), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitAccidentSchema: z.ZodType = +export const deviceVersionSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -11814,143 +13254,43 @@ export const explanationOfBenefitAccidentSchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - date: dateSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), type: z.lazy(() => codeableConceptSchema).optional(), - locationAddress: z.lazy(() => addressSchema).optional(), - locationReference: z.lazy(() => referenceSchema).optional(), + component: z.lazy(() => identifierSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), }) - .refine( - (value) => - [ - { - names: ['locationAddress', 'locationReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const explanationOfBenefitItemSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - careTeamSequence: positiveIntSchema.array().optional(), - _careTeamSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - diagnosisSequence: positiveIntSchema.array().optional(), - _diagnosisSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - procedureSequence: positiveIntSchema.array().optional(), - _procedureSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - informationSequence: positiveIntSchema.array().optional(), - _informationSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - revenue: z.lazy(() => codeableConceptSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - programCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - servicedDate: dateSchema.optional(), - _servicedDate: z.lazy(() => elementSchema).optional(), - servicedPeriod: z.lazy(() => periodSchema).optional(), - locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - locationAddress: z.lazy(() => addressSchema).optional(), - locationReference: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - udi: z - .lazy(() => referenceSchema) - .array() - .optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), - subSite: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - encounter: z - .lazy(() => referenceSchema) - .array() - .optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) - .array() - .optional(), - adjudication: explanationOfBenefitItemAdjudicationSchema - .array() - .optional(), - detail: explanationOfBenefitItemDetailSchema.array().optional(), + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'value', shadow: '_value' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { names: ['servicedDate', 'servicedPeriod'], required: false }, - { - names: [ - 'locationCodeableConcept', - 'locationAddress', - 'locationReference', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const explanationOfBenefitAddItemSchema: z.ZodType = +export const devicePropertySchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -11958,320 +13298,473 @@ export const explanationOfBenefitAddItemSchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - itemSequence: positiveIntSchema.array().optional(), - _itemSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - detailSequence: positiveIntSchema.array().optional(), - _detailSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - subDetailSequence: positiveIntSchema.array().optional(), - _subDetailSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - provider: z - .lazy(() => referenceSchema) - .array() - .optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - programCode: z - .lazy(() => codeableConceptSchema) + type: z.lazy(() => codeableConceptSchema), + valueQuantity: z + .lazy(() => quantitySchema) .array() + .min(1) .optional(), - servicedDate: dateSchema.optional(), - _servicedDate: z.lazy(() => elementSchema).optional(), - servicedPeriod: z.lazy(() => periodSchema).optional(), - locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - locationAddress: z.lazy(() => addressSchema).optional(), - locationReference: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), - subSite: z + valueCode: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) - .array() - .optional(), - adjudication: z - .lazy(() => explanationOfBenefitItemAdjudicationSchema) - .array() - .optional(), - detail: explanationOfBenefitAddItemDetailSchema.array().optional(), }) .refine( - (value) => - [ - { names: ['servicedDate', 'servicedPeriod'], required: false }, - { - names: [ - 'locationCodeableConcept', - 'locationAddress', - 'locationReference', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const explanationOfBenefitItemAdjudicationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const deviceSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Device'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - category: z.lazy(() => codeableConceptSchema), - reason: z.lazy(() => codeableConceptSchema).optional(), - amount: z.lazy(() => moneySchema).optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - }), - ) - -export const explanationOfBenefitTotalSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + definition: z.lazy(() => referenceSchema).optional(), + udiCarrier: deviceUdiCarrierSchema.array().min(1).optional(), + status: z + .enum(['active', 'entered-in-error', 'inactive', 'unknown']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - category: z.lazy(() => codeableConceptSchema), - amount: z.lazy(() => moneySchema), - }), - ) - -export const explanationOfBenefitPaymentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + distinctIdentifier: stringSchema.optional(), + _distinctIdentifier: z.lazy(() => elementSchema).optional(), + manufacturer: stringSchema.optional(), + _manufacturer: z.lazy(() => elementSchema).optional(), + manufactureDate: dateTimeSchema.optional(), + _manufactureDate: z.lazy(() => elementSchema).optional(), + expirationDate: dateTimeSchema.optional(), + _expirationDate: z.lazy(() => elementSchema).optional(), + lotNumber: stringSchema.optional(), + _lotNumber: z.lazy(() => elementSchema).optional(), + serialNumber: stringSchema.optional(), + _serialNumber: z.lazy(() => elementSchema).optional(), + deviceName: deviceDeviceNameSchema.array().min(1).optional(), + modelNumber: stringSchema.optional(), + _modelNumber: z.lazy(() => elementSchema).optional(), + partNumber: stringSchema.optional(), + _partNumber: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + specialization: deviceSpecializationSchema.array().min(1).optional(), + version: deviceVersionSchema.array().min(1).optional(), + property: devicePropertySchema.array().min(1).optional(), + patient: z.lazy(() => referenceSchema).optional(), + owner: z.lazy(() => referenceSchema).optional(), + contact: z + .lazy(() => contactPointSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + location: z.lazy(() => referenceSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - adjustment: z.lazy(() => moneySchema).optional(), - adjustmentReason: z.lazy(() => codeableConceptSchema).optional(), - date: dateSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - amount: z.lazy(() => moneySchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), - }), - ) + safety: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + parent: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitProcessNoteSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const deviceDefinitionUdiDeviceIdentifierSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - number: positiveIntSchema.optional(), - _number: z.lazy(() => elementSchema).optional(), - type: z.enum(['display', 'print', 'printoper']).optional(), - _type: z.lazy(() => elementSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - language: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + deviceIdentifier: stringSchema.optional(), + _deviceIdentifier: z.lazy(() => elementSchema).optional(), + issuer: uriSchema.optional(), + _issuer: z.lazy(() => elementSchema).optional(), + jurisdiction: uriSchema.optional(), + _jurisdiction: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'deviceIdentifier', shadow: '_deviceIdentifier' }, + { name: 'issuer', shadow: '_issuer' }, + { name: 'jurisdiction', shadow: '_jurisdiction' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitBenefitBalanceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const deviceDefinitionDeviceNameSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - category: z.lazy(() => codeableConceptSchema), - excluded: booleanSchema.optional(), - _excluded: z.lazy(() => elementSchema).optional(), name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - network: z.lazy(() => codeableConceptSchema).optional(), - unit: z.lazy(() => codeableConceptSchema).optional(), - term: z.lazy(() => codeableConceptSchema).optional(), - financial: explanationOfBenefitBenefitBalanceFinancialSchema - .array() + type: z + .enum([ + 'manufacturer-name', + 'model-name', + 'other', + 'patient-reported-name', + 'udi-label-name', + 'user-friendly-name', + ]) .optional(), - }), - ) + _type: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitItemDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const deviceDefinitionSpecializationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - revenue: z.lazy(() => codeableConceptSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - programCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - udi: z - .lazy(() => referenceSchema) - .array() - .optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) + systemType: stringSchema.optional(), + _systemType: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'systemType', shadow: '_systemType' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const deviceDefinitionCapabilitySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - adjudication: z - .lazy(() => explanationOfBenefitItemAdjudicationSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - subDetail: explanationOfBenefitItemDetailSubDetailSchema + type: z.lazy(() => codeableConceptSchema), + description: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitAddItemDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const deviceDefinitionPropertySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) + type: z.lazy(() => codeableConceptSchema), + valueQuantity: z + .lazy(() => quantitySchema) .array() + .min(1) .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) + valueCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - adjudication: z - .lazy(() => explanationOfBenefitItemAdjudicationSchema) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const deviceDefinitionMaterialSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - subDetail: explanationOfBenefitAddItemDetailSubDetailSchema + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), - ) + substance: z.lazy(() => codeableConceptSchema), + alternate: booleanSchema.optional(), + _alternate: z.lazy(() => elementSchema).optional(), + allergenicIndicator: booleanSchema.optional(), + _allergenicIndicator: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitBenefitBalanceFinancialSchema: z.ZodType = +export const deviceDefinitionSchema: z.ZodType> = z.lazy(() => z .strictObject({ + resourceType: z.literal('DeviceDefinition'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - allowedUnsignedInt: unsignedIntSchema.optional(), - _allowedUnsignedInt: z.lazy(() => elementSchema).optional(), - allowedString: stringSchema.optional(), - _allowedString: z.lazy(() => elementSchema).optional(), - allowedMoney: z.lazy(() => moneySchema).optional(), - usedUnsignedInt: unsignedIntSchema.optional(), - _usedUnsignedInt: z.lazy(() => elementSchema).optional(), - usedMoney: z.lazy(() => moneySchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + udiDeviceIdentifier: deviceDefinitionUdiDeviceIdentifierSchema + .array() + .min(1) + .optional(), + manufacturerString: stringSchema.optional(), + _manufacturerString: z.lazy(() => elementSchema).optional(), + manufacturerReference: z.lazy(() => referenceSchema).optional(), + deviceName: deviceDefinitionDeviceNameSchema.array().min(1).optional(), + modelNumber: stringSchema.optional(), + _modelNumber: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + specialization: deviceDefinitionSpecializationSchema + .array() + .min(1) + .optional(), + version: stringSchema.nullable().array().min(1).optional(), + _version: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + safety: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + shelfLifeStorage: z + .lazy(() => productShelfLifeSchema) + .array() + .min(1) + .optional(), + physicalCharacteristics: z + .lazy(() => prodCharacteristicSchema) + .optional(), + languageCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + capability: deviceDefinitionCapabilitySchema.array().min(1).optional(), + property: deviceDefinitionPropertySchema.array().min(1).optional(), + owner: z.lazy(() => referenceSchema).optional(), + contact: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + onlineInformation: uriSchema.optional(), + _onlineInformation: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + parentDevice: z.lazy(() => referenceSchema).optional(), + material: deviceDefinitionMaterialSchema.array().min(1).optional(), }) .refine( (value) => [ { - names: ['allowedUnsignedInt', 'allowedString', 'allowedMoney'], + alternatives: [ + ['manufacturerString', '_manufacturerString'], + ['manufacturerReference'], + ], required: false, }, - { names: ['usedUnsignedInt', 'usedMoney'], required: false }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -12279,344 +13772,221 @@ export const explanationOfBenefitBenefitBalanceFinancialSchema: z.ZodType { + for (const group of [ + { name: 'version', shadow: '_version', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) + ) as unknown as z.ZodType> -export const explanationOfBenefitItemDetailSubDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const deviceMetricCalibrationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - revenue: z.lazy(() => codeableConceptSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - programCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - udi: z - .lazy(() => referenceSchema) - .array() - .optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) - .array() - .optional(), - adjudication: z - .lazy(() => explanationOfBenefitItemAdjudicationSchema) - .array() + type: z.enum(['gain', 'offset', 'two-point', 'unspecified']).optional(), + _type: z.lazy(() => elementSchema).optional(), + state: z + .enum([ + 'calibrated', + 'calibration-required', + 'not-calibrated', + 'unspecified', + ]) .optional(), - }), - ) + _state: z.lazy(() => elementSchema).optional(), + time: instantSchema.optional(), + _time: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitAddItemDetailSubDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) - .array() - .optional(), - adjudication: z - .lazy(() => explanationOfBenefitItemAdjudicationSchema) - .array() - .optional(), - }), - ) +export const deviceMetricSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('DeviceMetric'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + unit: z.lazy(() => codeableConceptSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + parent: z.lazy(() => referenceSchema).optional(), + operationalStatus: z + .enum(['entered-in-error', 'off', 'on', 'standby']) + .optional(), + _operationalStatus: z.lazy(() => elementSchema).optional(), + color: z + .enum([ + 'black', + 'blue', + 'cyan', + 'green', + 'magenta', + 'red', + 'white', + 'yellow', + ]) + .optional(), + _color: z.lazy(() => elementSchema).optional(), + category: z + .enum(['calculation', 'measurement', 'setting', 'unspecified']) + .optional(), + _category: z.lazy(() => elementSchema).optional(), + measurementPeriod: z.lazy(() => timingSchema).optional(), + calibration: deviceMetricCalibrationSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'category', shadow: '_category' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('ExplanationOfBenefit'), +export const deviceRequestParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - subType: z.lazy(() => codeableConceptSchema).optional(), - use: z.enum(['claim', 'preauthorization', 'predetermination']), - _use: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema), - billablePeriod: z.lazy(() => periodSchema).optional(), - created: dateTimeSchema, - _created: z.lazy(() => elementSchema).optional(), - enterer: z.lazy(() => referenceSchema).optional(), - insurer: z.lazy(() => referenceSchema), - provider: z.lazy(() => referenceSchema), - priority: z.lazy(() => codeableConceptSchema).optional(), - fundsReserveRequested: z.lazy(() => codeableConceptSchema).optional(), - fundsReserve: z.lazy(() => codeableConceptSchema).optional(), - related: explanationOfBenefitRelatedSchema.array().optional(), - prescription: z.lazy(() => referenceSchema).optional(), - originalPrescription: z.lazy(() => referenceSchema).optional(), - payee: explanationOfBenefitPayeeSchema.optional(), - referral: z.lazy(() => referenceSchema).optional(), - facility: z.lazy(() => referenceSchema).optional(), - claim: z.lazy(() => referenceSchema).optional(), - claimResponse: z.lazy(() => referenceSchema).optional(), - outcome: z.enum(['complete', 'error', 'partial', 'queued']), - _outcome: z.lazy(() => elementSchema).optional(), - disposition: stringSchema.optional(), - _disposition: z.lazy(() => elementSchema).optional(), - preAuthRef: stringSchema.array().optional(), - _preAuthRef: z - .lazy(() => elementSchema) - .array() - .optional(), - preAuthRefPeriod: z - .lazy(() => periodSchema) - .array() - .optional(), - careTeam: explanationOfBenefitCareTeamSchema.array().optional(), - supportingInfo: explanationOfBenefitSupportingInfoSchema - .array() - .optional(), - diagnosis: explanationOfBenefitDiagnosisSchema.array().optional(), - procedure: explanationOfBenefitProcedureSchema.array().optional(), - precedence: positiveIntSchema.optional(), - _precedence: z.lazy(() => elementSchema).optional(), - insurance: explanationOfBenefitInsuranceSchema.array().min(1), - accident: explanationOfBenefitAccidentSchema.optional(), - item: explanationOfBenefitItemSchema.array().optional(), - addItem: explanationOfBenefitAddItemSchema.array().optional(), - adjudication: z - .lazy(() => explanationOfBenefitItemAdjudicationSchema) - .array() - .optional(), - total: explanationOfBenefitTotalSchema.array().optional(), - payment: explanationOfBenefitPaymentSchema.optional(), - formCode: z.lazy(() => codeableConceptSchema).optional(), - form: z.lazy(() => attachmentSchema).optional(), - processNote: explanationOfBenefitProcessNoteSchema.array().optional(), - benefitPeriod: z.lazy(() => periodSchema).optional(), - benefitBalance: explanationOfBenefitBenefitBalanceSchema - .array() - .optional(), - }), - ) - -export const expressionSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - name: idSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - language: codeSchema, - _language: z.lazy(() => elementSchema).optional(), - expression: stringSchema.optional(), - _expression: z.lazy(() => elementSchema).optional(), - reference: uriSchema.optional(), - _reference: z.lazy(() => elementSchema).optional(), - }), -) - -export const extensionSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema, - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueCanonical: canonicalSchema.optional(), - _valueCanonical: z.lazy(() => elementSchema).optional(), - valueCode: codeSchema.optional(), - _valueCode: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueId: idSchema.optional(), - _valueId: z.lazy(() => elementSchema).optional(), - valueInstant: instantSchema.optional(), - _valueInstant: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueMarkdown: markdownSchema.optional(), - _valueMarkdown: z.lazy(() => elementSchema).optional(), - valueOid: oidSchema.optional(), - _valueOid: z.lazy(() => elementSchema).optional(), - valuePositiveInt: positiveIntSchema.optional(), - _valuePositiveInt: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueUnsignedInt: unsignedIntSchema.optional(), - _valueUnsignedInt: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueUrl: urlSchema.optional(), - _valueUrl: z.lazy(() => elementSchema).optional(), - valueUuid: uuidSchema.optional(), - _valueUuid: z.lazy(() => elementSchema).optional(), - valueAddress: z.lazy(() => addressSchema).optional(), - valueAge: z.lazy(() => ageSchema).optional(), - valueAnnotation: z.lazy(() => annotationSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueContactPoint: z.lazy(() => contactPointSchema).optional(), - valueCount: z.lazy(() => countSchema).optional(), - valueDistance: z.lazy(() => distanceSchema).optional(), - valueDuration: z.lazy(() => durationSchema).optional(), - valueHumanName: z.lazy(() => humanNameSchema).optional(), - valueIdentifier: z.lazy(() => identifierSchema).optional(), - valueMoney: z.lazy(() => moneySchema).optional(), - valuePeriod: z.lazy(() => periodSchema).optional(), valueQuantity: z.lazy(() => quantitySchema).optional(), valueRange: z.lazy(() => rangeSchema).optional(), - valueRatio: z.lazy(() => ratioSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - valueSampledData: z.lazy(() => sampledDataSchema).optional(), - valueSignature: z.lazy(() => signatureSchema).optional(), - valueTiming: z.lazy(() => timingSchema).optional(), - valueContactDetail: z.lazy(() => contactDetailSchema).optional(), - valueContributor: z.lazy(() => contributorSchema).optional(), - valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), - valueExpression: z.lazy(() => expressionSchema).optional(), - valueParameterDefinition: z - .lazy(() => parameterDefinitionSchema) - .optional(), - valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), - valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), - valueUsageContext: z.lazy(() => usageContextSchema).optional(), - valueDosage: z.lazy(() => dosageSchema).optional(), - valueMeta: z.lazy(() => metaSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), }) .refine( (value) => [ { - names: [ - 'valueBase64Binary', - 'valueBoolean', - 'valueCanonical', - 'valueCode', - 'valueDate', - 'valueDateTime', - 'valueDecimal', - 'valueId', - 'valueInstant', - 'valueInteger', - 'valueMarkdown', - 'valueOid', - 'valuePositiveInt', - 'valueString', - 'valueTime', - 'valueUnsignedInt', - 'valueUri', - 'valueUrl', - 'valueUuid', - 'valueAddress', - 'valueAge', - 'valueAnnotation', - 'valueAttachment', - 'valueCodeableConcept', - 'valueCoding', - 'valueContactPoint', - 'valueCount', - 'valueDistance', - 'valueDuration', - 'valueHumanName', - 'valueIdentifier', - 'valueMoney', - 'valuePeriod', - 'valueQuantity', - 'valueRange', - 'valueRatio', - 'valueReference', - 'valueSampledData', - 'valueSignature', - 'valueTiming', - 'valueContactDetail', - 'valueContributor', - 'valueDataRequirement', - 'valueExpression', - 'valueParameterDefinition', - 'valueRelatedArtifact', - 'valueTriggerDefinition', - 'valueUsageContext', - 'valueDosage', - 'valueMeta', + alternatives: [ + ['valueCodeableConcept'], + ['valueQuantity'], + ['valueRange'], + ['valueBoolean', '_valueBoolean'], ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -12626,72 +13996,16 @@ export const extensionSchema: z.ZodType = z.lazy(() => }, ) .refine( - (value) => - ((value as { extension?: unknown[] }).extension?.length ?? 0) > 0 !== - Object.keys(value as object).some( - // A primitive sibling means the value element exists carrying only extensions, - // which ext-1 counts as the value being present. - (key) => key.startsWith('value') || key.startsWith('_value'), - ), - { - message: - 'ext-1: an extension has either extensions or a value, never both.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const familyMemberHistoryConditionSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - outcome: z.lazy(() => codeableConceptSchema).optional(), - contributedToDeath: booleanSchema.optional(), - _contributedToDeath: z.lazy(() => elementSchema).optional(), - onsetAge: z.lazy(() => ageSchema).optional(), - onsetRange: z.lazy(() => rangeSchema).optional(), - onsetPeriod: z.lazy(() => periodSchema).optional(), - onsetString: stringSchema.optional(), - _onsetString: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['onsetAge', 'onsetRange', 'onsetPeriod', 'onsetString'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) +) as unknown as z.ZodType> -export const familyMemberHistorySchema: z.ZodType = +export const deviceRequestSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('FamilyMemberHistory'), + resourceType: z.literal('DeviceRequest'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -12703,98 +14017,146 @@ export const familyMemberHistorySchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) + .optional(), + instantiatesCanonical: canonicalSchema + .nullable() + .array() + .min(1) .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), _instantiatesCanonical: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - instantiatesUri: uriSchema.array().optional(), + instantiatesUri: uriSchema.nullable().array().min(1).optional(), _instantiatesUri: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - status: z.enum([ - 'completed', - 'entered-in-error', - 'health-unknown', - 'partial', - ]), - _status: z.lazy(() => elementSchema).optional(), - dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), - patient: z.lazy(() => referenceSchema), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - relationship: z.lazy(() => codeableConceptSchema), - sex: z.lazy(() => codeableConceptSchema).optional(), - bornPeriod: z.lazy(() => periodSchema).optional(), - bornDate: dateSchema.optional(), - _bornDate: z.lazy(() => elementSchema).optional(), - bornString: stringSchema.optional(), - _bornString: z.lazy(() => elementSchema).optional(), - ageAge: z.lazy(() => ageSchema).optional(), - ageRange: z.lazy(() => rangeSchema).optional(), - ageString: stringSchema.optional(), - _ageString: z.lazy(() => elementSchema).optional(), - estimatedAge: booleanSchema.optional(), - _estimatedAge: z.lazy(() => elementSchema).optional(), - deceasedBoolean: booleanSchema.optional(), - _deceasedBoolean: z.lazy(() => elementSchema).optional(), - deceasedAge: z.lazy(() => ageSchema).optional(), - deceasedRange: z.lazy(() => rangeSchema).optional(), - deceasedDate: dateSchema.optional(), - _deceasedDate: z.lazy(() => elementSchema).optional(), - deceasedString: stringSchema.optional(), - _deceasedString: z.lazy(() => elementSchema).optional(), - reasonCode: z + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + priorRequest: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + status: z + .enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + intent: z + .enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]) + .optional(), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + codeReference: z.lazy(() => referenceSchema).optional(), + codeCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + parameter: deviceRequestParameterSchema.array().min(1).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performerType: z.lazy(() => codeableConceptSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + reasonCode: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), reasonReference: z .lazy(() => referenceSchema) .array() + .min(1) + .optional(), + insurance: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), note: z .lazy(() => annotationSchema) .array() + .min(1) + .optional(), + relevantHistory: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), - condition: familyMemberHistoryConditionSchema.array().optional(), }) .refine( (value) => [ { - names: ['bornPeriod', 'bornDate', 'bornString'], - required: false, + alternatives: [['codeReference'], ['codeCodeableConcept']], + required: true, }, - { names: ['ageAge', 'ageRange', 'ageString'], required: false }, { - names: [ - 'deceasedBoolean', - 'deceasedAge', - 'deceasedRange', - 'deceasedDate', - 'deceasedString', + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], + ['occurrenceTiming'], ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -12802,110 +14164,91 @@ export const familyMemberHistorySchema: z.ZodType = message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, - ), - ) - -export const flagSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Flag'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'entered-in-error', 'inactive']), - _status: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - subject: z.lazy(() => referenceSchema), - period: z.lazy(() => periodSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - }), -) - -export const goalTargetSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - measure: z.lazy(() => codeableConceptSchema).optional(), - detailQuantity: z.lazy(() => quantitySchema).optional(), - detailRange: z.lazy(() => rangeSchema).optional(), - detailCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - detailString: stringSchema.optional(), - _detailString: z.lazy(() => elementSchema).optional(), - detailBoolean: booleanSchema.optional(), - _detailBoolean: z.lazy(() => elementSchema).optional(), - detailInteger: integerSchema.optional(), - _detailInteger: z.lazy(() => elementSchema).optional(), - detailRatio: z.lazy(() => ratioSchema).optional(), - dueDate: dateSchema.optional(), - _dueDate: z.lazy(() => elementSchema).optional(), - dueDuration: z.lazy(() => durationSchema).optional(), - }) - .refine( - (value) => - [ + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'intent', shadow: '_intent' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ { - names: [ - 'detailQuantity', - 'detailRange', - 'detailCodeableConcept', - 'detailString', - 'detailBoolean', - 'detailInteger', - 'detailRatio', - ], + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', required: false, }, - { names: ['dueDate', 'dueDuration'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const goalSchema: z.ZodType = z.lazy(() => +export const deviceUseStatementSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Goal'), + resourceType: z.literal('DeviceUseStatement'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -12917,72 +14260,86 @@ export const goalSchema: z.ZodType = z.lazy(() => contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - lifecycleStatus: z.enum([ - 'accepted', - 'active', - 'cancelled', - 'completed', - 'entered-in-error', - 'on-hold', - 'planned', - 'proposed', - 'rejected', - ]), - _lifecycleStatus: z.lazy(() => elementSchema).optional(), - achievementStatus: z.lazy(() => codeableConceptSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) + basedOn: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - priority: z.lazy(() => codeableConceptSchema).optional(), - description: z.lazy(() => codeableConceptSchema), + status: z + .enum([ + 'active', + 'completed', + 'entered-in-error', + 'intended', + 'on-hold', + 'stopped', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), subject: z.lazy(() => referenceSchema), - startDate: dateSchema.optional(), - _startDate: z.lazy(() => elementSchema).optional(), - startCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - target: goalTargetSchema.array().optional(), - statusDate: dateSchema.optional(), - _statusDate: z.lazy(() => elementSchema).optional(), - statusReason: stringSchema.optional(), - _statusReason: z.lazy(() => elementSchema).optional(), - expressedBy: z.lazy(() => referenceSchema).optional(), - addresses: z + derivedFrom: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - outcomeCode: z + timingTiming: z.lazy(() => timingSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + recordedOn: dateTimeSchema.optional(), + _recordedOn: z.lazy(() => elementSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + device: z.lazy(() => referenceSchema), + reasonCode: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - outcomeReference: z + reasonReference: z .lazy(() => referenceSchema) .array() + .min(1) + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) .optional(), }) .refine( (value) => [ - { names: ['startDate', 'startCodeableConcept'], required: false }, + { + alternatives: [ + ['timingTiming'], + ['timingPeriod'], + ['timingDateTime', '_timingDateTime'], + ], + required: false, + }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -12990,494 +14347,176 @@ export const goalSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const graphDefinitionLinkSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const diagnosticReportMediaSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - path: stringSchema.optional(), - _path: z.lazy(() => elementSchema).optional(), - sliceName: stringSchema.optional(), - _sliceName: z.lazy(() => elementSchema).optional(), - min: integerSchema.optional(), - _min: z.lazy(() => elementSchema).optional(), - max: stringSchema.optional(), - _max: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - target: graphDefinitionLinkTargetSchema.array().optional(), - }), - ) - -export const graphDefinitionLinkTargetSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum([ - 'Account', - 'ActivityDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'EffectEvidenceSynthesis', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProduct', - 'MedicinalProductAuthorization', - 'MedicinalProductContraindication', - 'MedicinalProductIndication', - 'MedicinalProductIngredient', - 'MedicinalProductInteraction', - 'MedicinalProductManufactured', - 'MedicinalProductPackaged', - 'MedicinalProductPharmaceutical', - 'MedicinalProductUndesirableEffect', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'RiskEvidenceSynthesis', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'Substance', - 'SubstanceNucleicAcid', - 'SubstancePolymer', - 'SubstanceProtein', - 'SubstanceReferenceInformation', - 'SubstanceSourceMaterial', - 'SubstanceSpecification', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]), - _type: z.lazy(() => elementSchema).optional(), - params: stringSchema.optional(), - _params: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.optional(), - _profile: z.lazy(() => elementSchema).optional(), - compartment: graphDefinitionLinkTargetCompartmentSchema - .array() - .optional(), - link: z - .lazy(() => graphDefinitionLinkSchema) - .array() - .optional(), - }), - ) + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + link: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const graphDefinitionLinkTargetCompartmentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - use: z.enum(['condition', 'requirement']), - _use: z.lazy(() => elementSchema).optional(), - code: z.enum([ - 'Device', - 'Encounter', - 'Patient', - 'Practitioner', - 'RelatedPerson', - ]), - _code: z.lazy(() => elementSchema).optional(), - rule: z.enum(['custom', 'different', 'identical', 'matching']), - _rule: z.lazy(() => elementSchema).optional(), - expression: stringSchema.optional(), - _expression: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - }), - ) - -export const graphDefinitionSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('GraphDefinition'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - start: z.enum([ - 'Account', - 'ActivityDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'EffectEvidenceSynthesis', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProduct', - 'MedicinalProductAuthorization', - 'MedicinalProductContraindication', - 'MedicinalProductIndication', - 'MedicinalProductIngredient', - 'MedicinalProductInteraction', - 'MedicinalProductManufactured', - 'MedicinalProductPackaged', - 'MedicinalProductPharmaceutical', - 'MedicinalProductUndesirableEffect', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'RiskEvidenceSynthesis', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'Substance', - 'SubstanceNucleicAcid', - 'SubstancePolymer', - 'SubstanceProtein', - 'SubstanceReferenceInformation', - 'SubstanceSourceMaterial', - 'SubstanceSpecification', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]), - _start: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.optional(), - _profile: z.lazy(() => elementSchema).optional(), - link: graphDefinitionLinkSchema.array().optional(), - }), -) - -export const groupCharacteristicSchema: z.ZodType = +export const diagnosticReportSchema: z.ZodType> = z.lazy(() => z .strictObject({ + resourceType: z.literal('DiagnosticReport'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'amended', + 'appended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'partial', + 'preliminary', + 'registered', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) .optional(), code: z.lazy(() => codeableConceptSchema), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - exclude: booleanSchema, - _exclude: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + issued: instantSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + performer: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + resultsInterpreter: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + specimen: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + result: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + imagingStudy: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + media: diagnosticReportMediaSchema.array().min(1).optional(), + conclusion: stringSchema.optional(), + _conclusion: z.lazy(() => elementSchema).optional(), + conclusionCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + presentedForm: z + .lazy(() => attachmentSchema) + .array() + .min(1) + .optional(), }) .refine( (value) => [ { - names: [ - 'valueCodeableConcept', - 'valueBoolean', - 'valueQuantity', - 'valueRange', - 'valueReference', + alternatives: [ + ['effectiveDateTime', '_effectiveDateTime'], + ['effectivePeriod'], ], - required: true, + required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -13485,83 +14524,100 @@ export const groupCharacteristicSchema: z.ZodType = message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) + ) as unknown as z.ZodType> -export const groupMemberSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - entity: z.lazy(() => referenceSchema), - period: z.lazy(() => periodSchema).optional(), - inactive: booleanSchema.optional(), - _inactive: z.lazy(() => elementSchema).optional(), - }), -) - -export const groupSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Group'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - type: z.enum([ - 'animal', - 'device', - 'medication', - 'person', - 'practitioner', - 'substance', - ]), - _type: z.lazy(() => elementSchema).optional(), - actual: booleanSchema, - _actual: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - quantity: unsignedIntSchema.optional(), - _quantity: z.lazy(() => elementSchema).optional(), - managingEntity: z.lazy(() => referenceSchema).optional(), - characteristic: groupCharacteristicSchema.array().optional(), - member: groupMemberSchema.array().optional(), - }), -) - -export const guidanceResponseSchema: z.ZodType = z.lazy( - () => +export const distanceSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const quantity = value as Record + const hasCode = + quantity.code !== undefined || quantity._code !== undefined + return ( + !hasCode || + quantity.system !== undefined || + quantity._system !== undefined + ) + }, + { message: 'qty-3: a coded Quantity requires its unit system.' }, + ), +) as unknown as z.ZodType> + +export const documentManifestRelatedSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + ref: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const documentManifestSchema: z.ZodType> = + z.lazy(() => z .strictObject({ - resourceType: z.literal('GuidanceResponse'), + resourceType: z.literal('DocumentManifest'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -13573,151 +14629,191 @@ export const guidanceResponseSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - requestIdentifier: z.lazy(() => identifierSchema).optional(), + masterIdentifier: z.lazy(() => identifierSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) + .optional(), + status: z + .enum(['current', 'entered-in-error', 'superseded']) .optional(), - moduleUri: uriSchema.optional(), - _moduleUri: z.lazy(() => elementSchema).optional(), - moduleCanonical: canonicalSchema.optional(), - _moduleCanonical: z.lazy(() => elementSchema).optional(), - moduleCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - status: z.enum([ - 'data-requested', - 'data-required', - 'entered-in-error', - 'failure', - 'in-progress', - 'success', - ]), _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - performer: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + author: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - evaluationMessage: z + recipient: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - outputParameters: z.lazy(() => referenceSchema).optional(), - result: z.lazy(() => referenceSchema).optional(), - dataRequirement: z - .lazy(() => dataRequirementSchema) + source: uriSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + content: z + .lazy(() => referenceSchema) .array() - .optional(), + .min(1), + related: documentManifestRelatedSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['moduleUri', 'moduleCanonical', 'moduleCodeableConcept'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), -) + ) as unknown as z.ZodType> -export const healthcareServiceEligibilitySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const documentReferenceRelatesToSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - comment: markdownSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - }), - ) + code: z.enum(['appends', 'replaces', 'signs', 'transforms']).optional(), + _code: z.lazy(() => elementSchema).optional(), + target: z.lazy(() => referenceSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const healthcareServiceAvailableTimeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const documentReferenceContentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - daysOfWeek: z - .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) - .array() - .optional(), - _daysOfWeek: z - .lazy(() => elementSchema) - .array() - .optional(), - allDay: booleanSchema.optional(), - _allDay: z.lazy(() => elementSchema).optional(), - availableStartTime: timeSchema.optional(), - _availableStartTime: z.lazy(() => elementSchema).optional(), - availableEndTime: timeSchema.optional(), - _availableEndTime: z.lazy(() => elementSchema).optional(), - }), - ) + attachment: z.lazy(() => attachmentSchema), + format: z.lazy(() => codingSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const healthcareServiceNotAvailableSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const documentReferenceContextSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: stringSchema, - _description: z.lazy(() => elementSchema).optional(), - during: z.lazy(() => periodSchema).optional(), - }), - ) + encounter: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + event: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + facilityType: z.lazy(() => codeableConceptSchema).optional(), + practiceSetting: z.lazy(() => codeableConceptSchema).optional(), + sourcePatientInfo: z.lazy(() => referenceSchema).optional(), + related: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const healthcareServiceSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('HealthcareService'), +export const documentReferenceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('DocumentReference'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -13729,696 +14825,436 @@ export const healthcareServiceSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), + masterIdentifier: z.lazy(() => identifierSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - providedBy: z.lazy(() => referenceSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() + status: z.enum(['current', 'entered-in-error', 'superseded']).optional(), + _status: z.lazy(() => elementSchema).optional(), + docStatus: z + .enum(['amended', 'entered-in-error', 'final', 'preliminary']) .optional(), - specialty: z + _docStatus: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + category: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - location: z - .lazy(() => referenceSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - extraDetails: markdownSchema.optional(), - _extraDetails: z.lazy(() => elementSchema).optional(), - photo: z.lazy(() => attachmentSchema).optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - coverageArea: z + subject: z.lazy(() => referenceSchema).optional(), + date: instantSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + author: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - serviceProvisionCode: z + authenticator: z.lazy(() => referenceSchema).optional(), + custodian: z.lazy(() => referenceSchema).optional(), + relatesTo: documentReferenceRelatesToSchema.array().min(1).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + securityLabel: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - eligibility: healthcareServiceEligibilitySchema.array().optional(), - program: z - .lazy(() => codeableConceptSchema) + content: documentReferenceContentSchema.array().min(1), + context: documentReferenceContextSchema.optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const dosageDoseAndRateSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - characteristic: z - .lazy(() => codeableConceptSchema) + type: z.lazy(() => codeableConceptSchema).optional(), + doseRange: z.lazy(() => rangeSchema).optional(), + doseQuantity: z.lazy(() => quantitySchema).optional(), + rateRatio: z.lazy(() => ratioSchema).optional(), + rateRange: z.lazy(() => rangeSchema).optional(), + rateQuantity: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => + [ + { alternatives: [['doseRange'], ['doseQuantity']], required: false }, + { + alternatives: [['rateRatio'], ['rateRange'], ['rateQuantity']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const dosageSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - communication: z - .lazy(() => codeableConceptSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - referralMethod: z + sequence: integerSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + additionalInstruction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - appointmentRequired: booleanSchema.optional(), - _appointmentRequired: z.lazy(() => elementSchema).optional(), - availableTime: healthcareServiceAvailableTimeSchema.array().optional(), - notAvailable: healthcareServiceNotAvailableSchema.array().optional(), - availabilityExceptions: stringSchema.optional(), - _availabilityExceptions: z.lazy(() => elementSchema).optional(), - endpoint: z - .lazy(() => referenceSchema) + patientInstruction: stringSchema.optional(), + _patientInstruction: z.lazy(() => elementSchema).optional(), + timing: z.lazy(() => timingSchema).optional(), + asNeededBoolean: booleanSchema.optional(), + _asNeededBoolean: z.lazy(() => elementSchema).optional(), + asNeededCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + site: z.lazy(() => codeableConceptSchema).optional(), + route: z.lazy(() => codeableConceptSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + doseAndRate: dosageDoseAndRateSchema.array().min(1).optional(), + maxDosePerPeriod: z.lazy(() => ratioSchema).optional(), + maxDosePerAdministration: z.lazy(() => quantitySchema).optional(), + maxDosePerLifetime: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['asNeededBoolean', '_asNeededBoolean'], + ['asNeededCodeableConcept'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const durationSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), -) - -export const humanNameSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - use: z - .enum([ - 'anonymous', - 'maiden', - 'nickname', - 'official', - 'old', - 'temp', - 'usual', - ]) - .optional(), - _use: z.lazy(() => elementSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - family: stringSchema.optional(), - _family: z.lazy(() => elementSchema).optional(), - given: stringSchema.array().optional(), - _given: z - .lazy(() => elementSchema) - .array() - .optional(), - prefix: stringSchema.array().optional(), - _prefix: z - .lazy(() => elementSchema) - .array() - .optional(), - suffix: stringSchema.array().optional(), - _suffix: z - .lazy(() => elementSchema) - .array() - .optional(), - period: z.lazy(() => periodSchema).optional(), - }), -) - -export const identifierSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - use: z.enum(['official', 'old', 'secondary', 'temp', 'usual']).optional(), - _use: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - value: stringSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - assigner: z.lazy(() => referenceSchema).optional(), - }), -) - -export const imagingStudySeriesSchema: z.ZodType = - z.lazy(() => - z.strictObject({ + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const quantity = value as Record + const hasCode = + quantity.code !== undefined || quantity._code !== undefined + return ( + !hasCode || + quantity.system !== undefined || + quantity._system !== undefined + ) + }, + { message: 'qty-3: a coded Quantity requires its unit system.' }, + ), +) as unknown as z.ZodType> + +export const effectEvidenceSynthesisSampleSizeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - uid: idSchema, - _uid: z.lazy(() => elementSchema).optional(), - number: unsignedIntSchema.optional(), - _number: z.lazy(() => elementSchema).optional(), - modality: z.lazy(() => codingSchema), description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - numberOfInstances: unsignedIntSchema.optional(), - _numberOfInstances: z.lazy(() => elementSchema).optional(), - endpoint: z - .lazy(() => referenceSchema) - .array() - .optional(), - bodySite: z.lazy(() => codingSchema).optional(), - laterality: z.lazy(() => codingSchema).optional(), - specimen: z - .lazy(() => referenceSchema) - .array() - .optional(), - started: dateTimeSchema.optional(), - _started: z.lazy(() => elementSchema).optional(), - performer: imagingStudySeriesPerformerSchema.array().optional(), - instance: imagingStudySeriesInstanceSchema.array().optional(), - }), - ) + numberOfStudies: integerSchema.optional(), + _numberOfStudies: z.lazy(() => elementSchema).optional(), + numberOfParticipants: integerSchema.optional(), + _numberOfParticipants: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const imagingStudySeriesPerformerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const effectEvidenceSynthesisResultsByExposureSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - function: z.lazy(() => codeableConceptSchema).optional(), - actor: z.lazy(() => referenceSchema), - }), - ) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + exposureState: z.enum(['exposure', 'exposure-alternative']).optional(), + _exposureState: z.lazy(() => elementSchema).optional(), + variantState: z.lazy(() => codeableConceptSchema).optional(), + riskEvidenceSynthesis: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const imagingStudySeriesInstanceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const effectEvidenceSynthesisEffectEstimateSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - uid: idSchema, - _uid: z.lazy(() => elementSchema).optional(), - sopClass: z.lazy(() => codingSchema), - number: unsignedIntSchema.optional(), - _number: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - }), - ) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + variantState: z.lazy(() => codeableConceptSchema).optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + unitOfMeasure: z.lazy(() => codeableConceptSchema).optional(), + precisionEstimate: + effectEvidenceSynthesisEffectEstimatePrecisionEstimateSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const imagingStudySchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('ImagingStudy'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'available', - 'cancelled', - 'entered-in-error', - 'registered', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - modality: z - .lazy(() => codingSchema) - .array() - .optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - started: dateTimeSchema.optional(), - _started: z.lazy(() => elementSchema).optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - referrer: z.lazy(() => referenceSchema).optional(), - interpreter: z - .lazy(() => referenceSchema) - .array() - .optional(), - endpoint: z - .lazy(() => referenceSchema) - .array() - .optional(), - numberOfSeries: unsignedIntSchema.optional(), - _numberOfSeries: z.lazy(() => elementSchema).optional(), - numberOfInstances: unsignedIntSchema.optional(), - _numberOfInstances: z.lazy(() => elementSchema).optional(), - procedureReference: z.lazy(() => referenceSchema).optional(), - procedureCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - location: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - series: imagingStudySeriesSchema.array().optional(), - }), -) - -export const immunizationPerformerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const effectEvidenceSynthesisCertaintySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - function: z.lazy(() => codeableConceptSchema).optional(), - actor: z.lazy(() => referenceSchema), - }), - ) - -export const immunizationEducationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + rating: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - documentType: stringSchema.optional(), - _documentType: z.lazy(() => elementSchema).optional(), - reference: uriSchema.optional(), - _reference: z.lazy(() => elementSchema).optional(), - publicationDate: dateTimeSchema.optional(), - _publicationDate: z.lazy(() => elementSchema).optional(), - presentationDate: dateTimeSchema.optional(), - _presentationDate: z.lazy(() => elementSchema).optional(), - }), - ) + certaintySubcomponent: + effectEvidenceSynthesisCertaintyCertaintySubcomponentSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const immunizationReactionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const effectEvidenceSynthesisEffectEstimatePrecisionEstimateSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - detail: z.lazy(() => referenceSchema).optional(), - reported: booleanSchema.optional(), - _reported: z.lazy(() => elementSchema).optional(), - }), - ) - -export const immunizationProtocolAppliedSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - series: stringSchema.optional(), - _series: z.lazy(() => elementSchema).optional(), - authority: z.lazy(() => referenceSchema).optional(), - targetDisease: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - doseNumberPositiveInt: positiveIntSchema.optional(), - _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), - doseNumberString: stringSchema.optional(), - _doseNumberString: z.lazy(() => elementSchema).optional(), - seriesDosesPositiveInt: positiveIntSchema.optional(), - _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), - seriesDosesString: stringSchema.optional(), - _seriesDosesString: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['doseNumberPositiveInt', 'doseNumberString'], - required: true, - }, - { - names: ['seriesDosesPositiveInt', 'seriesDosesString'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + type: z.lazy(() => codeableConceptSchema).optional(), + level: decimalSchema.optional(), + _level: z.lazy(() => elementSchema).optional(), + from: decimalSchema.optional(), + _from: z.lazy(() => elementSchema).optional(), + to: decimalSchema.optional(), + _to: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const immunizationSchema: z.ZodType = z.lazy(() => +export const effectEvidenceSynthesisCertaintyCertaintySubcomponentSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Immunization'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) + type: z.lazy(() => codeableConceptSchema).optional(), + rating: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - status: z.enum(['completed', 'entered-in-error', 'not-done']), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - vaccineCode: z.lazy(() => codeableConceptSchema), - patient: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrenceString: stringSchema.optional(), - _occurrenceString: z.lazy(() => elementSchema).optional(), - recorded: dateTimeSchema.optional(), - _recorded: z.lazy(() => elementSchema).optional(), - primarySource: booleanSchema.optional(), - _primarySource: z.lazy(() => elementSchema).optional(), - reportOrigin: z.lazy(() => codeableConceptSchema).optional(), - location: z.lazy(() => referenceSchema).optional(), - manufacturer: z.lazy(() => referenceSchema).optional(), - lotNumber: stringSchema.optional(), - _lotNumber: z.lazy(() => elementSchema).optional(), - expirationDate: dateSchema.optional(), - _expirationDate: z.lazy(() => elementSchema).optional(), - site: z.lazy(() => codeableConceptSchema).optional(), - route: z.lazy(() => codeableConceptSchema).optional(), - doseQuantity: z.lazy(() => quantitySchema).optional(), - performer: immunizationPerformerSchema.array().optional(), note: z .lazy(() => annotationSchema) .array() + .min(1) .optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - isSubpotent: booleanSchema.optional(), - _isSubpotent: z.lazy(() => elementSchema).optional(), - subpotentReason: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - education: immunizationEducationSchema.array().optional(), - programEligibility: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - fundingSource: z.lazy(() => codeableConceptSchema).optional(), - reaction: immunizationReactionSchema.array().optional(), - protocolApplied: immunizationProtocolAppliedSchema.array().optional(), }) .refine( - (value) => - [ - { names: ['occurrenceDateTime', 'occurrenceString'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const immunizationEvaluationSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - resourceType: z.literal('ImmunizationEvaluation'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['completed', 'entered-in-error']), - _status: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - authority: z.lazy(() => referenceSchema).optional(), - targetDisease: z.lazy(() => codeableConceptSchema), - immunizationEvent: z.lazy(() => referenceSchema), - doseStatus: z.lazy(() => codeableConceptSchema), - doseStatusReason: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - series: stringSchema.optional(), - _series: z.lazy(() => elementSchema).optional(), - doseNumberPositiveInt: positiveIntSchema.optional(), - _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), - doseNumberString: stringSchema.optional(), - _doseNumberString: z.lazy(() => elementSchema).optional(), - seriesDosesPositiveInt: positiveIntSchema.optional(), - _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), - seriesDosesString: stringSchema.optional(), - _seriesDosesString: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['doseNumberPositiveInt', 'doseNumberString'], - required: false, - }, - { - names: ['seriesDosesPositiveInt', 'seriesDosesString'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const immunizationRecommendationRecommendationSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - vaccineCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - targetDisease: z.lazy(() => codeableConceptSchema).optional(), - contraindicatedVaccineCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - forecastStatus: z.lazy(() => codeableConceptSchema), - forecastReason: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - dateCriterion: - immunizationRecommendationRecommendationDateCriterionSchema - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - series: stringSchema.optional(), - _series: z.lazy(() => elementSchema).optional(), - doseNumberPositiveInt: positiveIntSchema.optional(), - _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), - doseNumberString: stringSchema.optional(), - _doseNumberString: z.lazy(() => elementSchema).optional(), - seriesDosesPositiveInt: positiveIntSchema.optional(), - _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), - seriesDosesString: stringSchema.optional(), - _seriesDosesString: z.lazy(() => elementSchema).optional(), - supportingImmunization: z - .lazy(() => referenceSchema) - .array() - .optional(), - supportingPatientInformation: z - .lazy(() => referenceSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['doseNumberPositiveInt', 'doseNumberString'], - required: false, - }, - { - names: ['seriesDosesPositiveInt', 'seriesDosesString'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const immunizationRecommendationRecommendationDateCriterionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - value: dateTimeSchema, - _value: z.lazy(() => elementSchema).optional(), - }), - ) +) as unknown as z.ZodType< + FhirJson +> -export const immunizationRecommendationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('ImmunizationRecommendation'), +export const effectEvidenceSynthesisSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('EffectEvidenceSynthesis'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -14430,595 +15266,8886 @@ export const immunizationRecommendationSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - patient: z.lazy(() => referenceSchema), - date: dateTimeSchema, - _date: z.lazy(() => elementSchema).optional(), - authority: z.lazy(() => referenceSchema).optional(), - recommendation: immunizationRecommendationRecommendationSchema - .array() - .min(1), - }), - ) - -export const implementationGuideDependsOnSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - uri: canonicalSchema, - _uri: z.lazy(() => elementSchema).optional(), - packageId: idSchema.optional(), - _packageId: z.lazy(() => elementSchema).optional(), version: stringSchema.optional(), _version: z.lazy(() => elementSchema).optional(), - }), - ) - -export const implementationGuideGlobalSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum([ - 'Account', - 'ActivityDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'EffectEvidenceSynthesis', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProduct', - 'MedicinalProductAuthorization', - 'MedicinalProductContraindication', - 'MedicinalProductIndication', - 'MedicinalProductIngredient', - 'MedicinalProductInteraction', - 'MedicinalProductManufactured', - 'MedicinalProductPackaged', - 'MedicinalProductPharmaceutical', - 'MedicinalProductUndesirableEffect', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'RiskEvidenceSynthesis', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'Substance', - 'SubstanceNucleicAcid', - 'SubstancePolymer', - 'SubstanceProtein', - 'SubstanceReferenceInformation', - 'SubstanceSourceMaterial', - 'SubstanceSpecification', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]), - _type: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema, - _profile: z.lazy(() => elementSchema).optional(), - }), - ) - -export const implementationGuideDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - grouping: implementationGuideDefinitionGroupingSchema.array().optional(), - resource: implementationGuideDefinitionResourceSchema.array().min(1), - page: implementationGuideDefinitionPageSchema.optional(), - parameter: implementationGuideDefinitionParameterSchema + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - template: implementationGuideDefinitionTemplateSchema.array().optional(), - }), - ) - -export const implementationGuideManifestSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + jurisdiction: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - rendering: urlSchema.optional(), - _rendering: z.lazy(() => elementSchema).optional(), - resource: implementationGuideManifestResourceSchema.array().min(1), - page: implementationGuideManifestPageSchema.array().optional(), - image: stringSchema.array().optional(), - _image: z - .lazy(() => elementSchema) + author: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - other: stringSchema.array().optional(), - _other: z - .lazy(() => elementSchema) + editor: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - }), - ) + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + synthesisType: z.lazy(() => codeableConceptSchema).optional(), + studyType: z.lazy(() => codeableConceptSchema).optional(), + population: z.lazy(() => referenceSchema), + exposure: z.lazy(() => referenceSchema), + exposureAlternative: z.lazy(() => referenceSchema), + outcome: z.lazy(() => referenceSchema), + sampleSize: effectEvidenceSynthesisSampleSizeSchema.optional(), + resultsByExposure: effectEvidenceSynthesisResultsByExposureSchema + .array() + .min(1) + .optional(), + effectEstimate: effectEvidenceSynthesisEffectEstimateSchema + .array() + .min(1) + .optional(), + certainty: effectEvidenceSynthesisCertaintySchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const implementationGuideDefinitionGroupingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const elementSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - }), - ) - -export const implementationGuideDefinitionResourceSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - reference: z.lazy(() => referenceSchema), - fhirVersion: z - .enum([ - '0.0.80', - '0.0.81', - '0.0.82', - '0.01', - '0.05', - '0.06', - '0.11', - '0.4.0', - '0.5.0', - '1.0.0', - '1.0.1', - '1.0.2', - '1.1.0', - '1.4.0', - '1.6.0', - '1.8.0', - '3.0.0', - '3.0.1', - '3.3.0', - '3.5.0', - '4.0.0', - '4.0.1', - ]) - .array() - .optional(), - _fhirVersion: z - .lazy(() => elementSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - exampleBoolean: booleanSchema.optional(), - _exampleBoolean: z.lazy(() => elementSchema).optional(), - exampleCanonical: canonicalSchema.optional(), - _exampleCanonical: z.lazy(() => elementSchema).optional(), - groupingId: idSchema.optional(), - _groupingId: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['exampleBoolean', 'exampleCanonical'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const implementationGuideDefinitionPageSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - nameUrl: urlSchema.optional(), - _nameUrl: z.lazy(() => elementSchema).optional(), - nameReference: z.lazy(() => referenceSchema).optional(), - title: stringSchema, - _title: z.lazy(() => elementSchema).optional(), - generation: z.enum(['generated', 'html', 'markdown', 'xml']), - _generation: z.lazy(() => elementSchema).optional(), - page: z - .lazy(() => implementationGuideDefinitionPageSchema) - .array() - .optional(), - }) - .refine( - (value) => - [{ names: ['nameUrl', 'nameReference'], required: true }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const implementationGuideDefinitionParameterSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const elementDefinitionSlicingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + discriminator: elementDefinitionSlicingDiscriminatorSchema .array() + .min(1) .optional(), - code: z.enum([ - 'apply', - 'expansion-parameter', - 'generate-json', - 'generate-turtle', - 'generate-xml', - 'html-template', - 'path-pages', - 'path-resource', - 'path-tx-cache', - 'rule-broken-links', - ]), - _code: z.lazy(() => elementSchema).optional(), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - }), - ) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + ordered: booleanSchema.optional(), + _ordered: z.lazy(() => elementSchema).optional(), + rules: z.enum(['closed', 'open', 'openAtEnd']).optional(), + _rules: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'rules', shadow: '_rules' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const implementationGuideDefinitionTemplateSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const elementDefinitionBaseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: codeSchema, - _code: z.lazy(() => elementSchema).optional(), - source: stringSchema, - _source: z.lazy(() => elementSchema).optional(), - scope: stringSchema.optional(), - _scope: z.lazy(() => elementSchema).optional(), - }), - ) - -export const implementationGuideManifestResourceSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - reference: z.lazy(() => referenceSchema), - exampleBoolean: booleanSchema.optional(), - _exampleBoolean: z.lazy(() => elementSchema).optional(), - exampleCanonical: canonicalSchema.optional(), - _exampleCanonical: z.lazy(() => elementSchema).optional(), - relativePath: urlSchema.optional(), - _relativePath: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['exampleBoolean', 'exampleCanonical'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + min: unsignedIntSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'path', shadow: '_path' }, + { name: 'min', shadow: '_min' }, + { name: 'max', shadow: '_max' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const implementationGuideManifestPageSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const elementDefinitionTypeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + code: uriSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.nullable().array().min(1).optional(), + _profile: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - anchor: stringSchema.array().optional(), - _anchor: z + targetProfile: canonicalSchema.nullable().array().min(1).optional(), + _targetProfile: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - }), - ) + aggregation: z + .enum(['bundled', 'contained', 'referenced']) + .nullable() + .array() + .min(1) + .optional(), + _aggregation: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + versioning: z.enum(['either', 'independent', 'specific']).optional(), + _versioning: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'profile', shadow: '_profile', required: false }, + { name: 'targetProfile', shadow: '_targetProfile', required: false }, + { name: 'aggregation', shadow: '_aggregation', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const implementationGuideSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('ImplementationGuide'), +export const elementDefinitionExampleSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + label: stringSchema.optional(), + _label: z.lazy(() => elementSchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + valueMeta: z.lazy(() => metaSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueBase64Binary', '_valueBase64Binary'], + ['valueBoolean', '_valueBoolean'], + ['valueCanonical', '_valueCanonical'], + ['valueCode', '_valueCode'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueDecimal', '_valueDecimal'], + ['valueId', '_valueId'], + ['valueInstant', '_valueInstant'], + ['valueInteger', '_valueInteger'], + ['valueMarkdown', '_valueMarkdown'], + ['valueOid', '_valueOid'], + ['valuePositiveInt', '_valuePositiveInt'], + ['valueString', '_valueString'], + ['valueTime', '_valueTime'], + ['valueUnsignedInt', '_valueUnsignedInt'], + ['valueUri', '_valueUri'], + ['valueUrl', '_valueUrl'], + ['valueUuid', '_valueUuid'], + ['valueAddress'], + ['valueAge'], + ['valueAnnotation'], + ['valueAttachment'], + ['valueCodeableConcept'], + ['valueCoding'], + ['valueContactPoint'], + ['valueCount'], + ['valueDistance'], + ['valueDuration'], + ['valueHumanName'], + ['valueIdentifier'], + ['valueMoney'], + ['valuePeriod'], + ['valueQuantity'], + ['valueRange'], + ['valueRatio'], + ['valueReference'], + ['valueSampledData'], + ['valueSignature'], + ['valueTiming'], + ['valueContactDetail'], + ['valueContributor'], + ['valueDataRequirement'], + ['valueExpression'], + ['valueParameterDefinition'], + ['valueRelatedArtifact'], + ['valueTriggerDefinition'], + ['valueUsageContext'], + ['valueDosage'], + ['valueMeta'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'label', shadow: '_label' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const elementDefinitionConstraintSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + key: idSchema.optional(), + _key: z.lazy(() => elementSchema).optional(), + requirements: stringSchema.optional(), + _requirements: z.lazy(() => elementSchema).optional(), + severity: z.enum(['error', 'warning']).optional(), + _severity: z.lazy(() => elementSchema).optional(), + human: stringSchema.optional(), + _human: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + xpath: stringSchema.optional(), + _xpath: z.lazy(() => elementSchema).optional(), + source: canonicalSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'key', shadow: '_key' }, + { name: 'severity', shadow: '_severity' }, + { name: 'human', shadow: '_human' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const elementDefinitionBindingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + strength: z + .enum(['example', 'extensible', 'preferred', 'required']) + .optional(), + _strength: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema.optional(), + _valueSet: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'strength', shadow: '_strength' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const elementDefinitionMappingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identity: idSchema.optional(), + _identity: z.lazy(() => elementSchema).optional(), language: codeSchema.optional(), _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + map: stringSchema.optional(), + _map: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'identity', shadow: '_identity' }, + { name: 'map', shadow: '_map' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const elementDefinitionSlicingDiscriminatorSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + type: z + .enum(['exists', 'pattern', 'profile', 'type', 'value']) .optional(), + _type: z.lazy(() => elementSchema).optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'type', shadow: '_type' }, + { name: 'path', shadow: '_path' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const elementDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema, - _url: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + representation: z + .enum(['cdaText', 'typeAttr', 'xhtml', 'xmlAttr', 'xmlText']) + .nullable() .array() + .min(1) .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) + _representation: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) + sliceName: stringSchema.optional(), + _sliceName: z.lazy(() => elementSchema).optional(), + sliceIsConstraining: booleanSchema.optional(), + _sliceIsConstraining: z.lazy(() => elementSchema).optional(), + label: stringSchema.optional(), + _label: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codingSchema) .array() + .min(1) .optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - packageId: idSchema, - _packageId: z.lazy(() => elementSchema).optional(), - license: z - .enum([ - '0BSD', - 'AAL', - 'ADSL', - 'AFL-1.1', - 'AFL-1.2', - 'AFL-2.0', - 'AFL-2.1', - 'AFL-3.0', - 'AGPL-1.0-only', - 'AGPL-1.0-or-later', - 'AGPL-3.0-only', - 'AGPL-3.0-or-later', - 'AMDPLPA', - 'AML', - 'AMPAS', - 'ANTLR-PD', - 'APAFML', - 'APL-1.0', + slicing: elementDefinitionSlicingSchema.optional(), + short: stringSchema.optional(), + _short: z.lazy(() => elementSchema).optional(), + definition: markdownSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + comment: markdownSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + requirements: markdownSchema.optional(), + _requirements: z.lazy(() => elementSchema).optional(), + alias: stringSchema.nullable().array().min(1).optional(), + _alias: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + min: unsignedIntSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + base: elementDefinitionBaseSchema.optional(), + contentReference: uriSchema.optional(), + _contentReference: z.lazy(() => elementSchema).optional(), + type: elementDefinitionTypeSchema.array().min(1).optional(), + defaultValueBase64Binary: base64BinarySchema.optional(), + _defaultValueBase64Binary: z.lazy(() => elementSchema).optional(), + defaultValueBoolean: booleanSchema.optional(), + _defaultValueBoolean: z.lazy(() => elementSchema).optional(), + defaultValueCanonical: canonicalSchema.optional(), + _defaultValueCanonical: z.lazy(() => elementSchema).optional(), + defaultValueCode: codeSchema.optional(), + _defaultValueCode: z.lazy(() => elementSchema).optional(), + defaultValueDate: dateSchema.optional(), + _defaultValueDate: z.lazy(() => elementSchema).optional(), + defaultValueDateTime: dateTimeSchema.optional(), + _defaultValueDateTime: z.lazy(() => elementSchema).optional(), + defaultValueDecimal: decimalSchema.optional(), + _defaultValueDecimal: z.lazy(() => elementSchema).optional(), + defaultValueId: idSchema.optional(), + _defaultValueId: z.lazy(() => elementSchema).optional(), + defaultValueInstant: instantSchema.optional(), + _defaultValueInstant: z.lazy(() => elementSchema).optional(), + defaultValueInteger: integerSchema.optional(), + _defaultValueInteger: z.lazy(() => elementSchema).optional(), + defaultValueMarkdown: markdownSchema.optional(), + _defaultValueMarkdown: z.lazy(() => elementSchema).optional(), + defaultValueOid: oidSchema.optional(), + _defaultValueOid: z.lazy(() => elementSchema).optional(), + defaultValuePositiveInt: positiveIntSchema.optional(), + _defaultValuePositiveInt: z.lazy(() => elementSchema).optional(), + defaultValueString: stringSchema.optional(), + _defaultValueString: z.lazy(() => elementSchema).optional(), + defaultValueTime: timeSchema.optional(), + _defaultValueTime: z.lazy(() => elementSchema).optional(), + defaultValueUnsignedInt: unsignedIntSchema.optional(), + _defaultValueUnsignedInt: z.lazy(() => elementSchema).optional(), + defaultValueUri: uriSchema.optional(), + _defaultValueUri: z.lazy(() => elementSchema).optional(), + defaultValueUrl: urlSchema.optional(), + _defaultValueUrl: z.lazy(() => elementSchema).optional(), + defaultValueUuid: uuidSchema.optional(), + _defaultValueUuid: z.lazy(() => elementSchema).optional(), + defaultValueAddress: z.lazy(() => addressSchema).optional(), + defaultValueAge: z.lazy(() => ageSchema).optional(), + defaultValueAnnotation: z.lazy(() => annotationSchema).optional(), + defaultValueAttachment: z.lazy(() => attachmentSchema).optional(), + defaultValueCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + defaultValueCoding: z.lazy(() => codingSchema).optional(), + defaultValueContactPoint: z.lazy(() => contactPointSchema).optional(), + defaultValueCount: z.lazy(() => countSchema).optional(), + defaultValueDistance: z.lazy(() => distanceSchema).optional(), + defaultValueDuration: z.lazy(() => durationSchema).optional(), + defaultValueHumanName: z.lazy(() => humanNameSchema).optional(), + defaultValueIdentifier: z.lazy(() => identifierSchema).optional(), + defaultValueMoney: z.lazy(() => moneySchema).optional(), + defaultValuePeriod: z.lazy(() => periodSchema).optional(), + defaultValueQuantity: z.lazy(() => quantitySchema).optional(), + defaultValueRange: z.lazy(() => rangeSchema).optional(), + defaultValueRatio: z.lazy(() => ratioSchema).optional(), + defaultValueReference: z.lazy(() => referenceSchema).optional(), + defaultValueSampledData: z.lazy(() => sampledDataSchema).optional(), + defaultValueSignature: z.lazy(() => signatureSchema).optional(), + defaultValueTiming: z.lazy(() => timingSchema).optional(), + defaultValueContactDetail: z.lazy(() => contactDetailSchema).optional(), + defaultValueContributor: z.lazy(() => contributorSchema).optional(), + defaultValueDataRequirement: z + .lazy(() => dataRequirementSchema) + .optional(), + defaultValueExpression: z.lazy(() => expressionSchema).optional(), + defaultValueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + defaultValueRelatedArtifact: z + .lazy(() => relatedArtifactSchema) + .optional(), + defaultValueTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + defaultValueUsageContext: z.lazy(() => usageContextSchema).optional(), + defaultValueDosage: z.lazy(() => dosageSchema).optional(), + defaultValueMeta: z.lazy(() => metaSchema).optional(), + meaningWhenMissing: markdownSchema.optional(), + _meaningWhenMissing: z.lazy(() => elementSchema).optional(), + orderMeaning: stringSchema.optional(), + _orderMeaning: z.lazy(() => elementSchema).optional(), + fixedBase64Binary: base64BinarySchema.optional(), + _fixedBase64Binary: z.lazy(() => elementSchema).optional(), + fixedBoolean: booleanSchema.optional(), + _fixedBoolean: z.lazy(() => elementSchema).optional(), + fixedCanonical: canonicalSchema.optional(), + _fixedCanonical: z.lazy(() => elementSchema).optional(), + fixedCode: codeSchema.optional(), + _fixedCode: z.lazy(() => elementSchema).optional(), + fixedDate: dateSchema.optional(), + _fixedDate: z.lazy(() => elementSchema).optional(), + fixedDateTime: dateTimeSchema.optional(), + _fixedDateTime: z.lazy(() => elementSchema).optional(), + fixedDecimal: decimalSchema.optional(), + _fixedDecimal: z.lazy(() => elementSchema).optional(), + fixedId: idSchema.optional(), + _fixedId: z.lazy(() => elementSchema).optional(), + fixedInstant: instantSchema.optional(), + _fixedInstant: z.lazy(() => elementSchema).optional(), + fixedInteger: integerSchema.optional(), + _fixedInteger: z.lazy(() => elementSchema).optional(), + fixedMarkdown: markdownSchema.optional(), + _fixedMarkdown: z.lazy(() => elementSchema).optional(), + fixedOid: oidSchema.optional(), + _fixedOid: z.lazy(() => elementSchema).optional(), + fixedPositiveInt: positiveIntSchema.optional(), + _fixedPositiveInt: z.lazy(() => elementSchema).optional(), + fixedString: stringSchema.optional(), + _fixedString: z.lazy(() => elementSchema).optional(), + fixedTime: timeSchema.optional(), + _fixedTime: z.lazy(() => elementSchema).optional(), + fixedUnsignedInt: unsignedIntSchema.optional(), + _fixedUnsignedInt: z.lazy(() => elementSchema).optional(), + fixedUri: uriSchema.optional(), + _fixedUri: z.lazy(() => elementSchema).optional(), + fixedUrl: urlSchema.optional(), + _fixedUrl: z.lazy(() => elementSchema).optional(), + fixedUuid: uuidSchema.optional(), + _fixedUuid: z.lazy(() => elementSchema).optional(), + fixedAddress: z.lazy(() => addressSchema).optional(), + fixedAge: z.lazy(() => ageSchema).optional(), + fixedAnnotation: z.lazy(() => annotationSchema).optional(), + fixedAttachment: z.lazy(() => attachmentSchema).optional(), + fixedCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + fixedCoding: z.lazy(() => codingSchema).optional(), + fixedContactPoint: z.lazy(() => contactPointSchema).optional(), + fixedCount: z.lazy(() => countSchema).optional(), + fixedDistance: z.lazy(() => distanceSchema).optional(), + fixedDuration: z.lazy(() => durationSchema).optional(), + fixedHumanName: z.lazy(() => humanNameSchema).optional(), + fixedIdentifier: z.lazy(() => identifierSchema).optional(), + fixedMoney: z.lazy(() => moneySchema).optional(), + fixedPeriod: z.lazy(() => periodSchema).optional(), + fixedQuantity: z.lazy(() => quantitySchema).optional(), + fixedRange: z.lazy(() => rangeSchema).optional(), + fixedRatio: z.lazy(() => ratioSchema).optional(), + fixedReference: z.lazy(() => referenceSchema).optional(), + fixedSampledData: z.lazy(() => sampledDataSchema).optional(), + fixedSignature: z.lazy(() => signatureSchema).optional(), + fixedTiming: z.lazy(() => timingSchema).optional(), + fixedContactDetail: z.lazy(() => contactDetailSchema).optional(), + fixedContributor: z.lazy(() => contributorSchema).optional(), + fixedDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + fixedExpression: z.lazy(() => expressionSchema).optional(), + fixedParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + fixedRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + fixedTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), + fixedUsageContext: z.lazy(() => usageContextSchema).optional(), + fixedDosage: z.lazy(() => dosageSchema).optional(), + fixedMeta: z.lazy(() => metaSchema).optional(), + patternBase64Binary: base64BinarySchema.optional(), + _patternBase64Binary: z.lazy(() => elementSchema).optional(), + patternBoolean: booleanSchema.optional(), + _patternBoolean: z.lazy(() => elementSchema).optional(), + patternCanonical: canonicalSchema.optional(), + _patternCanonical: z.lazy(() => elementSchema).optional(), + patternCode: codeSchema.optional(), + _patternCode: z.lazy(() => elementSchema).optional(), + patternDate: dateSchema.optional(), + _patternDate: z.lazy(() => elementSchema).optional(), + patternDateTime: dateTimeSchema.optional(), + _patternDateTime: z.lazy(() => elementSchema).optional(), + patternDecimal: decimalSchema.optional(), + _patternDecimal: z.lazy(() => elementSchema).optional(), + patternId: idSchema.optional(), + _patternId: z.lazy(() => elementSchema).optional(), + patternInstant: instantSchema.optional(), + _patternInstant: z.lazy(() => elementSchema).optional(), + patternInteger: integerSchema.optional(), + _patternInteger: z.lazy(() => elementSchema).optional(), + patternMarkdown: markdownSchema.optional(), + _patternMarkdown: z.lazy(() => elementSchema).optional(), + patternOid: oidSchema.optional(), + _patternOid: z.lazy(() => elementSchema).optional(), + patternPositiveInt: positiveIntSchema.optional(), + _patternPositiveInt: z.lazy(() => elementSchema).optional(), + patternString: stringSchema.optional(), + _patternString: z.lazy(() => elementSchema).optional(), + patternTime: timeSchema.optional(), + _patternTime: z.lazy(() => elementSchema).optional(), + patternUnsignedInt: unsignedIntSchema.optional(), + _patternUnsignedInt: z.lazy(() => elementSchema).optional(), + patternUri: uriSchema.optional(), + _patternUri: z.lazy(() => elementSchema).optional(), + patternUrl: urlSchema.optional(), + _patternUrl: z.lazy(() => elementSchema).optional(), + patternUuid: uuidSchema.optional(), + _patternUuid: z.lazy(() => elementSchema).optional(), + patternAddress: z.lazy(() => addressSchema).optional(), + patternAge: z.lazy(() => ageSchema).optional(), + patternAnnotation: z.lazy(() => annotationSchema).optional(), + patternAttachment: z.lazy(() => attachmentSchema).optional(), + patternCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + patternCoding: z.lazy(() => codingSchema).optional(), + patternContactPoint: z.lazy(() => contactPointSchema).optional(), + patternCount: z.lazy(() => countSchema).optional(), + patternDistance: z.lazy(() => distanceSchema).optional(), + patternDuration: z.lazy(() => durationSchema).optional(), + patternHumanName: z.lazy(() => humanNameSchema).optional(), + patternIdentifier: z.lazy(() => identifierSchema).optional(), + patternMoney: z.lazy(() => moneySchema).optional(), + patternPeriod: z.lazy(() => periodSchema).optional(), + patternQuantity: z.lazy(() => quantitySchema).optional(), + patternRange: z.lazy(() => rangeSchema).optional(), + patternRatio: z.lazy(() => ratioSchema).optional(), + patternReference: z.lazy(() => referenceSchema).optional(), + patternSampledData: z.lazy(() => sampledDataSchema).optional(), + patternSignature: z.lazy(() => signatureSchema).optional(), + patternTiming: z.lazy(() => timingSchema).optional(), + patternContactDetail: z.lazy(() => contactDetailSchema).optional(), + patternContributor: z.lazy(() => contributorSchema).optional(), + patternDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + patternExpression: z.lazy(() => expressionSchema).optional(), + patternParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + patternRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + patternTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + patternUsageContext: z.lazy(() => usageContextSchema).optional(), + patternDosage: z.lazy(() => dosageSchema).optional(), + patternMeta: z.lazy(() => metaSchema).optional(), + example: elementDefinitionExampleSchema.array().min(1).optional(), + minValueDate: dateSchema.optional(), + _minValueDate: z.lazy(() => elementSchema).optional(), + minValueDateTime: dateTimeSchema.optional(), + _minValueDateTime: z.lazy(() => elementSchema).optional(), + minValueInstant: instantSchema.optional(), + _minValueInstant: z.lazy(() => elementSchema).optional(), + minValueTime: timeSchema.optional(), + _minValueTime: z.lazy(() => elementSchema).optional(), + minValueDecimal: decimalSchema.optional(), + _minValueDecimal: z.lazy(() => elementSchema).optional(), + minValueInteger: integerSchema.optional(), + _minValueInteger: z.lazy(() => elementSchema).optional(), + minValuePositiveInt: positiveIntSchema.optional(), + _minValuePositiveInt: z.lazy(() => elementSchema).optional(), + minValueUnsignedInt: unsignedIntSchema.optional(), + _minValueUnsignedInt: z.lazy(() => elementSchema).optional(), + minValueQuantity: z.lazy(() => quantitySchema).optional(), + maxValueDate: dateSchema.optional(), + _maxValueDate: z.lazy(() => elementSchema).optional(), + maxValueDateTime: dateTimeSchema.optional(), + _maxValueDateTime: z.lazy(() => elementSchema).optional(), + maxValueInstant: instantSchema.optional(), + _maxValueInstant: z.lazy(() => elementSchema).optional(), + maxValueTime: timeSchema.optional(), + _maxValueTime: z.lazy(() => elementSchema).optional(), + maxValueDecimal: decimalSchema.optional(), + _maxValueDecimal: z.lazy(() => elementSchema).optional(), + maxValueInteger: integerSchema.optional(), + _maxValueInteger: z.lazy(() => elementSchema).optional(), + maxValuePositiveInt: positiveIntSchema.optional(), + _maxValuePositiveInt: z.lazy(() => elementSchema).optional(), + maxValueUnsignedInt: unsignedIntSchema.optional(), + _maxValueUnsignedInt: z.lazy(() => elementSchema).optional(), + maxValueQuantity: z.lazy(() => quantitySchema).optional(), + maxLength: integerSchema.optional(), + _maxLength: z.lazy(() => elementSchema).optional(), + condition: idSchema.nullable().array().min(1).optional(), + _condition: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + constraint: elementDefinitionConstraintSchema.array().min(1).optional(), + mustSupport: booleanSchema.optional(), + _mustSupport: z.lazy(() => elementSchema).optional(), + isModifier: booleanSchema.optional(), + _isModifier: z.lazy(() => elementSchema).optional(), + isModifierReason: stringSchema.optional(), + _isModifierReason: z.lazy(() => elementSchema).optional(), + isSummary: booleanSchema.optional(), + _isSummary: z.lazy(() => elementSchema).optional(), + binding: elementDefinitionBindingSchema.optional(), + mapping: elementDefinitionMappingSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['defaultValueBase64Binary', '_defaultValueBase64Binary'], + ['defaultValueBoolean', '_defaultValueBoolean'], + ['defaultValueCanonical', '_defaultValueCanonical'], + ['defaultValueCode', '_defaultValueCode'], + ['defaultValueDate', '_defaultValueDate'], + ['defaultValueDateTime', '_defaultValueDateTime'], + ['defaultValueDecimal', '_defaultValueDecimal'], + ['defaultValueId', '_defaultValueId'], + ['defaultValueInstant', '_defaultValueInstant'], + ['defaultValueInteger', '_defaultValueInteger'], + ['defaultValueMarkdown', '_defaultValueMarkdown'], + ['defaultValueOid', '_defaultValueOid'], + ['defaultValuePositiveInt', '_defaultValuePositiveInt'], + ['defaultValueString', '_defaultValueString'], + ['defaultValueTime', '_defaultValueTime'], + ['defaultValueUnsignedInt', '_defaultValueUnsignedInt'], + ['defaultValueUri', '_defaultValueUri'], + ['defaultValueUrl', '_defaultValueUrl'], + ['defaultValueUuid', '_defaultValueUuid'], + ['defaultValueAddress'], + ['defaultValueAge'], + ['defaultValueAnnotation'], + ['defaultValueAttachment'], + ['defaultValueCodeableConcept'], + ['defaultValueCoding'], + ['defaultValueContactPoint'], + ['defaultValueCount'], + ['defaultValueDistance'], + ['defaultValueDuration'], + ['defaultValueHumanName'], + ['defaultValueIdentifier'], + ['defaultValueMoney'], + ['defaultValuePeriod'], + ['defaultValueQuantity'], + ['defaultValueRange'], + ['defaultValueRatio'], + ['defaultValueReference'], + ['defaultValueSampledData'], + ['defaultValueSignature'], + ['defaultValueTiming'], + ['defaultValueContactDetail'], + ['defaultValueContributor'], + ['defaultValueDataRequirement'], + ['defaultValueExpression'], + ['defaultValueParameterDefinition'], + ['defaultValueRelatedArtifact'], + ['defaultValueTriggerDefinition'], + ['defaultValueUsageContext'], + ['defaultValueDosage'], + ['defaultValueMeta'], + ], + required: false, + }, + { + alternatives: [ + ['fixedBase64Binary', '_fixedBase64Binary'], + ['fixedBoolean', '_fixedBoolean'], + ['fixedCanonical', '_fixedCanonical'], + ['fixedCode', '_fixedCode'], + ['fixedDate', '_fixedDate'], + ['fixedDateTime', '_fixedDateTime'], + ['fixedDecimal', '_fixedDecimal'], + ['fixedId', '_fixedId'], + ['fixedInstant', '_fixedInstant'], + ['fixedInteger', '_fixedInteger'], + ['fixedMarkdown', '_fixedMarkdown'], + ['fixedOid', '_fixedOid'], + ['fixedPositiveInt', '_fixedPositiveInt'], + ['fixedString', '_fixedString'], + ['fixedTime', '_fixedTime'], + ['fixedUnsignedInt', '_fixedUnsignedInt'], + ['fixedUri', '_fixedUri'], + ['fixedUrl', '_fixedUrl'], + ['fixedUuid', '_fixedUuid'], + ['fixedAddress'], + ['fixedAge'], + ['fixedAnnotation'], + ['fixedAttachment'], + ['fixedCodeableConcept'], + ['fixedCoding'], + ['fixedContactPoint'], + ['fixedCount'], + ['fixedDistance'], + ['fixedDuration'], + ['fixedHumanName'], + ['fixedIdentifier'], + ['fixedMoney'], + ['fixedPeriod'], + ['fixedQuantity'], + ['fixedRange'], + ['fixedRatio'], + ['fixedReference'], + ['fixedSampledData'], + ['fixedSignature'], + ['fixedTiming'], + ['fixedContactDetail'], + ['fixedContributor'], + ['fixedDataRequirement'], + ['fixedExpression'], + ['fixedParameterDefinition'], + ['fixedRelatedArtifact'], + ['fixedTriggerDefinition'], + ['fixedUsageContext'], + ['fixedDosage'], + ['fixedMeta'], + ], + required: false, + }, + { + alternatives: [ + ['patternBase64Binary', '_patternBase64Binary'], + ['patternBoolean', '_patternBoolean'], + ['patternCanonical', '_patternCanonical'], + ['patternCode', '_patternCode'], + ['patternDate', '_patternDate'], + ['patternDateTime', '_patternDateTime'], + ['patternDecimal', '_patternDecimal'], + ['patternId', '_patternId'], + ['patternInstant', '_patternInstant'], + ['patternInteger', '_patternInteger'], + ['patternMarkdown', '_patternMarkdown'], + ['patternOid', '_patternOid'], + ['patternPositiveInt', '_patternPositiveInt'], + ['patternString', '_patternString'], + ['patternTime', '_patternTime'], + ['patternUnsignedInt', '_patternUnsignedInt'], + ['patternUri', '_patternUri'], + ['patternUrl', '_patternUrl'], + ['patternUuid', '_patternUuid'], + ['patternAddress'], + ['patternAge'], + ['patternAnnotation'], + ['patternAttachment'], + ['patternCodeableConcept'], + ['patternCoding'], + ['patternContactPoint'], + ['patternCount'], + ['patternDistance'], + ['patternDuration'], + ['patternHumanName'], + ['patternIdentifier'], + ['patternMoney'], + ['patternPeriod'], + ['patternQuantity'], + ['patternRange'], + ['patternRatio'], + ['patternReference'], + ['patternSampledData'], + ['patternSignature'], + ['patternTiming'], + ['patternContactDetail'], + ['patternContributor'], + ['patternDataRequirement'], + ['patternExpression'], + ['patternParameterDefinition'], + ['patternRelatedArtifact'], + ['patternTriggerDefinition'], + ['patternUsageContext'], + ['patternDosage'], + ['patternMeta'], + ], + required: false, + }, + { + alternatives: [ + ['minValueDate', '_minValueDate'], + ['minValueDateTime', '_minValueDateTime'], + ['minValueInstant', '_minValueInstant'], + ['minValueTime', '_minValueTime'], + ['minValueDecimal', '_minValueDecimal'], + ['minValueInteger', '_minValueInteger'], + ['minValuePositiveInt', '_minValuePositiveInt'], + ['minValueUnsignedInt', '_minValueUnsignedInt'], + ['minValueQuantity'], + ], + required: false, + }, + { + alternatives: [ + ['maxValueDate', '_maxValueDate'], + ['maxValueDateTime', '_maxValueDateTime'], + ['maxValueInstant', '_maxValueInstant'], + ['maxValueTime', '_maxValueTime'], + ['maxValueDecimal', '_maxValueDecimal'], + ['maxValueInteger', '_maxValueInteger'], + ['maxValuePositiveInt', '_maxValuePositiveInt'], + ['maxValueUnsignedInt', '_maxValueUnsignedInt'], + ['maxValueQuantity'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'path', shadow: '_path' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'representation', shadow: '_representation', required: false }, + { name: 'alias', shadow: '_alias', required: false }, + { name: 'condition', shadow: '_condition', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const encounterStatusHistorySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'arrived', + 'cancelled', + 'entered-in-error', + 'finished', + 'in-progress', + 'onleave', + 'planned', + 'triaged', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const encounterClassHistorySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + class: z.lazy(() => codingSchema), + period: z.lazy(() => periodSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const encounterParticipantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + individual: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const encounterDiagnosisSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + condition: z.lazy(() => referenceSchema), + use: z.lazy(() => codeableConceptSchema).optional(), + rank: positiveIntSchema.optional(), + _rank: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const encounterHospitalizationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + preAdmissionIdentifier: z.lazy(() => identifierSchema).optional(), + origin: z.lazy(() => referenceSchema).optional(), + admitSource: z.lazy(() => codeableConceptSchema).optional(), + reAdmission: z.lazy(() => codeableConceptSchema).optional(), + dietPreference: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + specialCourtesy: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + specialArrangement: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + destination: z.lazy(() => referenceSchema).optional(), + dischargeDisposition: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const encounterLocationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + location: z.lazy(() => referenceSchema), + status: z.enum(['active', 'completed', 'planned', 'reserved']).optional(), + _status: z.lazy(() => elementSchema).optional(), + physicalType: z.lazy(() => codeableConceptSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const encounterSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Encounter'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'arrived', + 'cancelled', + 'entered-in-error', + 'finished', + 'in-progress', + 'onleave', + 'planned', + 'triaged', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusHistory: encounterStatusHistorySchema.array().min(1).optional(), + class: z.lazy(() => codingSchema), + classHistory: encounterClassHistorySchema.array().min(1).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + serviceType: z.lazy(() => codeableConceptSchema).optional(), + priority: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + episodeOfCare: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + participant: encounterParticipantSchema.array().min(1).optional(), + appointment: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + length: z.lazy(() => durationSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + diagnosis: encounterDiagnosisSchema.array().min(1).optional(), + account: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + hospitalization: encounterHospitalizationSchema.optional(), + location: encounterLocationSchema.array().min(1).optional(), + serviceProvider: z.lazy(() => referenceSchema).optional(), + partOf: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const endpointSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Endpoint'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'active', + 'entered-in-error', + 'error', + 'off', + 'suspended', + 'test', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + connectionType: z.lazy(() => codingSchema), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + contact: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + payloadType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1), + payloadMimeType: codeSchema.nullable().array().min(1).optional(), + _payloadMimeType: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + address: urlSchema.optional(), + _address: z.lazy(() => elementSchema).optional(), + header: stringSchema.nullable().array().min(1).optional(), + _header: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'address', shadow: '_address' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'payloadMimeType', + shadow: '_payloadMimeType', + required: false, + }, + { name: 'header', shadow: '_header', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const enrollmentRequestSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('EnrollmentRequest'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + insurer: z.lazy(() => referenceSchema).optional(), + provider: z.lazy(() => referenceSchema).optional(), + candidate: z.lazy(() => referenceSchema).optional(), + coverage: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const enrollmentResponseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('EnrollmentResponse'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + request: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + requestProvider: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const episodeOfCareStatusHistorySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'active', + 'cancelled', + 'entered-in-error', + 'finished', + 'onhold', + 'planned', + 'waitlist', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const episodeOfCareDiagnosisSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + condition: z.lazy(() => referenceSchema), + role: z.lazy(() => codeableConceptSchema).optional(), + rank: positiveIntSchema.optional(), + _rank: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const episodeOfCareSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('EpisodeOfCare'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'active', + 'cancelled', + 'entered-in-error', + 'finished', + 'onhold', + 'planned', + 'waitlist', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusHistory: episodeOfCareStatusHistorySchema + .array() + .min(1) + .optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + diagnosis: episodeOfCareDiagnosisSchema.array().min(1).optional(), + patient: z.lazy(() => referenceSchema), + managingOrganization: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + referralRequest: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + careManager: z.lazy(() => referenceSchema).optional(), + team: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + account: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const eventDefinitionSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('EventDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + trigger: z + .lazy(() => triggerDefinitionSchema) + .array() + .min(1), + }) + .refine( + (value) => + [ + { + alternatives: [['subjectCodeableConcept'], ['subjectReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const evidenceSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Evidence'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + shortTitle: stringSchema.optional(), + _shortTitle: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + exposureBackground: z.lazy(() => referenceSchema), + exposureVariant: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + outcome: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const evidenceVariableCharacteristicSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + definitionReference: z.lazy(() => referenceSchema).optional(), + definitionCanonical: canonicalSchema.optional(), + _definitionCanonical: z.lazy(() => elementSchema).optional(), + definitionCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + definitionExpression: z.lazy(() => expressionSchema).optional(), + definitionDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + definitionTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + usageContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + exclude: booleanSchema.optional(), + _exclude: z.lazy(() => elementSchema).optional(), + participantEffectiveDateTime: dateTimeSchema.optional(), + _participantEffectiveDateTime: z.lazy(() => elementSchema).optional(), + participantEffectivePeriod: z.lazy(() => periodSchema).optional(), + participantEffectiveDuration: z.lazy(() => durationSchema).optional(), + participantEffectiveTiming: z.lazy(() => timingSchema).optional(), + timeFromStart: z.lazy(() => durationSchema).optional(), + groupMeasure: z + .enum([ + 'mean', + 'mean-of-mean', + 'mean-of-median', + 'median', + 'median-of-mean', + 'median-of-median', + ]) + .optional(), + _groupMeasure: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['definitionReference'], + ['definitionCanonical', '_definitionCanonical'], + ['definitionCodeableConcept'], + ['definitionExpression'], + ['definitionDataRequirement'], + ['definitionTriggerDefinition'], + ], + required: true, + }, + { + alternatives: [ + ['participantEffectiveDateTime', '_participantEffectiveDateTime'], + ['participantEffectivePeriod'], + ['participantEffectiveDuration'], + ['participantEffectiveTiming'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const evidenceVariableSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('EvidenceVariable'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + shortTitle: stringSchema.optional(), + _shortTitle: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + type: z.enum(['continuous', 'descriptive', 'dichotomous']).optional(), + _type: z.lazy(() => elementSchema).optional(), + characteristic: evidenceVariableCharacteristicSchema.array().min(1), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const exampleScenarioActorSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + actorId: stringSchema.optional(), + _actorId: z.lazy(() => elementSchema).optional(), + type: z.enum(['entity', 'person']).optional(), + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'actorId', shadow: '_actorId' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const exampleScenarioInstanceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + resourceId: stringSchema.optional(), + _resourceId: z.lazy(() => elementSchema).optional(), + resourceType: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .optional(), + _resourceType: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + version: exampleScenarioInstanceVersionSchema.array().min(1).optional(), + containedInstance: exampleScenarioInstanceContainedInstanceSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'resourceId', shadow: '_resourceId' }, + { name: 'resourceType', shadow: '_resourceType' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const exampleScenarioProcessSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + preConditions: markdownSchema.optional(), + _preConditions: z.lazy(() => elementSchema).optional(), + postConditions: markdownSchema.optional(), + _postConditions: z.lazy(() => elementSchema).optional(), + step: exampleScenarioProcessStepSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'title', shadow: '_title' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const exampleScenarioInstanceVersionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + versionId: stringSchema.optional(), + _versionId: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'versionId', shadow: '_versionId' }, + { name: 'description', shadow: '_description' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const exampleScenarioInstanceContainedInstanceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + resourceId: stringSchema.optional(), + _resourceId: z.lazy(() => elementSchema).optional(), + versionId: stringSchema.optional(), + _versionId: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'resourceId', shadow: '_resourceId' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const exampleScenarioProcessStepSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + process: z + .lazy(() => exampleScenarioProcessSchema) + .array() + .min(1) + .optional(), + pause: booleanSchema.optional(), + _pause: z.lazy(() => elementSchema).optional(), + operation: exampleScenarioProcessStepOperationSchema.optional(), + alternative: exampleScenarioProcessStepAlternativeSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const exampleScenarioProcessStepOperationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + number: stringSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + type: stringSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + initiator: stringSchema.optional(), + _initiator: z.lazy(() => elementSchema).optional(), + receiver: stringSchema.optional(), + _receiver: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + initiatorActive: booleanSchema.optional(), + _initiatorActive: z.lazy(() => elementSchema).optional(), + receiverActive: booleanSchema.optional(), + _receiverActive: z.lazy(() => elementSchema).optional(), + request: z + .lazy(() => exampleScenarioInstanceContainedInstanceSchema) + .optional(), + response: z + .lazy(() => exampleScenarioInstanceContainedInstanceSchema) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'number', shadow: '_number' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const exampleScenarioProcessStepAlternativeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + step: z + .lazy(() => exampleScenarioProcessStepSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'title', shadow: '_title' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const exampleScenarioSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ExampleScenario'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + actor: exampleScenarioActorSchema.array().min(1).optional(), + instance: exampleScenarioInstanceSchema.array().min(1).optional(), + process: exampleScenarioProcessSchema.array().min(1).optional(), + workflow: canonicalSchema.nullable().array().min(1).optional(), + _workflow: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'workflow', shadow: '_workflow', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const explanationOfBenefitRelatedSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + claim: z.lazy(() => referenceSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema).optional(), + reference: z.lazy(() => identifierSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitPayeeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + party: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitCareTeamSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + provider: z.lazy(() => referenceSchema), + responsible: booleanSchema.optional(), + _responsible: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + qualification: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitSupportingInfoSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + category: z.lazy(() => codeableConceptSchema), + code: z.lazy(() => codeableConceptSchema).optional(), + timingDate: dateSchema.optional(), + _timingDate: z.lazy(() => elementSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + reason: z.lazy(() => codingSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['timingDate', '_timingDate'], ['timingPeriod']], + required: false, + }, + { + alternatives: [ + ['valueBoolean', '_valueBoolean'], + ['valueString', '_valueString'], + ['valueQuantity'], + ['valueAttachment'], + ['valueReference'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitDiagnosisSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + diagnosisCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + diagnosisReference: z.lazy(() => referenceSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + onAdmission: z.lazy(() => codeableConceptSchema).optional(), + packageCode: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['diagnosisCodeableConcept'], + ['diagnosisReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitProcedureSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + procedureCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + procedureReference: z.lazy(() => referenceSchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['procedureCodeableConcept'], + ['procedureReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitInsuranceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + focal: booleanSchema.optional(), + _focal: z.lazy(() => elementSchema).optional(), + coverage: z.lazy(() => referenceSchema), + preAuthRef: stringSchema.nullable().array().min(1).optional(), + _preAuthRef: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'focal', shadow: '_focal' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'preAuthRef', shadow: '_preAuthRef', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitAccidentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['locationAddress'], ['locationReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + careTeamSequence: positiveIntSchema.nullable().array().min(1).optional(), + _careTeamSequence: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + diagnosisSequence: positiveIntSchema.nullable().array().min(1).optional(), + _diagnosisSequence: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + procedureSequence: positiveIntSchema.nullable().array().min(1).optional(), + _procedureSequence: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + informationSequence: positiveIntSchema + .nullable() + .array() + .min(1) + .optional(), + _informationSequence: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + encounter: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: explanationOfBenefitItemAdjudicationSchema + .array() + .min(1) + .optional(), + detail: explanationOfBenefitItemDetailSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['servicedDate', '_servicedDate'], + ['servicedPeriod'], + ], + required: false, + }, + { + alternatives: [ + ['locationCodeableConcept'], + ['locationAddress'], + ['locationReference'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'careTeamSequence', + shadow: '_careTeamSequence', + required: false, + }, + { + name: 'diagnosisSequence', + shadow: '_diagnosisSequence', + required: false, + }, + { + name: 'procedureSequence', + shadow: '_procedureSequence', + required: false, + }, + { + name: 'informationSequence', + shadow: '_informationSequence', + required: false, + }, + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitAddItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + itemSequence: positiveIntSchema.nullable().array().min(1).optional(), + _itemSequence: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + detailSequence: positiveIntSchema.nullable().array().min(1).optional(), + _detailSequence: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + subDetailSequence: positiveIntSchema.nullable().array().min(1).optional(), + _subDetailSequence: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + provider: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .min(1) + .optional(), + detail: explanationOfBenefitAddItemDetailSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['servicedDate', '_servicedDate'], + ['servicedPeriod'], + ], + required: false, + }, + { + alternatives: [ + ['locationCodeableConcept'], + ['locationAddress'], + ['locationReference'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'itemSequence', shadow: '_itemSequence', required: false }, + { name: 'detailSequence', shadow: '_detailSequence', required: false }, + { + name: 'subDetailSequence', + shadow: '_subDetailSequence', + required: false, + }, + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitItemAdjudicationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + category: z.lazy(() => codeableConceptSchema), + reason: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitTotalSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + category: z.lazy(() => codeableConceptSchema), + amount: z.lazy(() => moneySchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitPaymentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + adjustment: z.lazy(() => moneySchema).optional(), + adjustmentReason: z.lazy(() => codeableConceptSchema).optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitProcessNoteSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + number: positiveIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + type: z.enum(['display', 'print', 'printoper']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + language: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitBenefitBalanceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + category: z.lazy(() => codeableConceptSchema), + excluded: booleanSchema.optional(), + _excluded: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + network: z.lazy(() => codeableConceptSchema).optional(), + unit: z.lazy(() => codeableConceptSchema).optional(), + term: z.lazy(() => codeableConceptSchema).optional(), + financial: explanationOfBenefitBenefitBalanceFinancialSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitItemDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .min(1) + .optional(), + subDetail: explanationOfBenefitItemDetailSubDetailSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitAddItemDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .min(1) + .optional(), + subDetail: explanationOfBenefitAddItemDetailSubDetailSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitBenefitBalanceFinancialSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + allowedUnsignedInt: unsignedIntSchema.optional(), + _allowedUnsignedInt: z.lazy(() => elementSchema).optional(), + allowedString: stringSchema.optional(), + _allowedString: z.lazy(() => elementSchema).optional(), + allowedMoney: z.lazy(() => moneySchema).optional(), + usedUnsignedInt: unsignedIntSchema.optional(), + _usedUnsignedInt: z.lazy(() => elementSchema).optional(), + usedMoney: z.lazy(() => moneySchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['allowedUnsignedInt', '_allowedUnsignedInt'], + ['allowedString', '_allowedString'], + ['allowedMoney'], + ], + required: false, + }, + { + alternatives: [ + ['usedUnsignedInt', '_usedUnsignedInt'], + ['usedMoney'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const explanationOfBenefitItemDetailSubDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitAddItemDetailSubDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const explanationOfBenefitSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ExplanationOfBenefit'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + use: z.enum(['claim', 'preauthorization', 'predetermination']).optional(), + _use: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + billablePeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + insurer: z.lazy(() => referenceSchema), + provider: z.lazy(() => referenceSchema), + priority: z.lazy(() => codeableConceptSchema).optional(), + fundsReserveRequested: z.lazy(() => codeableConceptSchema).optional(), + fundsReserve: z.lazy(() => codeableConceptSchema).optional(), + related: explanationOfBenefitRelatedSchema.array().min(1).optional(), + prescription: z.lazy(() => referenceSchema).optional(), + originalPrescription: z.lazy(() => referenceSchema).optional(), + payee: explanationOfBenefitPayeeSchema.optional(), + referral: z.lazy(() => referenceSchema).optional(), + facility: z.lazy(() => referenceSchema).optional(), + claim: z.lazy(() => referenceSchema).optional(), + claimResponse: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + preAuthRef: stringSchema.nullable().array().min(1).optional(), + _preAuthRef: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + preAuthRefPeriod: z + .lazy(() => periodSchema) + .array() + .min(1) + .optional(), + careTeam: explanationOfBenefitCareTeamSchema.array().min(1).optional(), + supportingInfo: explanationOfBenefitSupportingInfoSchema + .array() + .min(1) + .optional(), + diagnosis: explanationOfBenefitDiagnosisSchema.array().min(1).optional(), + procedure: explanationOfBenefitProcedureSchema.array().min(1).optional(), + precedence: positiveIntSchema.optional(), + _precedence: z.lazy(() => elementSchema).optional(), + insurance: explanationOfBenefitInsuranceSchema.array().min(1), + accident: explanationOfBenefitAccidentSchema.optional(), + item: explanationOfBenefitItemSchema.array().min(1).optional(), + addItem: explanationOfBenefitAddItemSchema.array().min(1).optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .min(1) + .optional(), + total: explanationOfBenefitTotalSchema.array().min(1).optional(), + payment: explanationOfBenefitPaymentSchema.optional(), + formCode: z.lazy(() => codeableConceptSchema).optional(), + form: z.lazy(() => attachmentSchema).optional(), + processNote: explanationOfBenefitProcessNoteSchema + .array() + .min(1) + .optional(), + benefitPeriod: z.lazy(() => periodSchema).optional(), + benefitBalance: explanationOfBenefitBenefitBalanceSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'use', shadow: '_use' }, + { name: 'created', shadow: '_created' }, + { name: 'outcome', shadow: '_outcome' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'preAuthRef', shadow: '_preAuthRef', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const expressionSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + name: idSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + reference: uriSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'language', shadow: '_language' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const extensionSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + valueMeta: z.lazy(() => metaSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueBase64Binary', '_valueBase64Binary'], + ['valueBoolean', '_valueBoolean'], + ['valueCanonical', '_valueCanonical'], + ['valueCode', '_valueCode'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueDecimal', '_valueDecimal'], + ['valueId', '_valueId'], + ['valueInstant', '_valueInstant'], + ['valueInteger', '_valueInteger'], + ['valueMarkdown', '_valueMarkdown'], + ['valueOid', '_valueOid'], + ['valuePositiveInt', '_valuePositiveInt'], + ['valueString', '_valueString'], + ['valueTime', '_valueTime'], + ['valueUnsignedInt', '_valueUnsignedInt'], + ['valueUri', '_valueUri'], + ['valueUrl', '_valueUrl'], + ['valueUuid', '_valueUuid'], + ['valueAddress'], + ['valueAge'], + ['valueAnnotation'], + ['valueAttachment'], + ['valueCodeableConcept'], + ['valueCoding'], + ['valueContactPoint'], + ['valueCount'], + ['valueDistance'], + ['valueDuration'], + ['valueHumanName'], + ['valueIdentifier'], + ['valueMoney'], + ['valuePeriod'], + ['valueQuantity'], + ['valueRange'], + ['valueRatio'], + ['valueReference'], + ['valueSampledData'], + ['valueSignature'], + ['valueTiming'], + ['valueContactDetail'], + ['valueContributor'], + ['valueDataRequirement'], + ['valueExpression'], + ['valueParameterDefinition'], + ['valueRelatedArtifact'], + ['valueTriggerDefinition'], + ['valueUsageContext'], + ['valueDosage'], + ['valueMeta'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => + ((value as { extension?: unknown[] }).extension?.length ?? 0) > 0 !== + Object.keys(value as object).some( + // A primitive sibling means the value element exists carrying only extensions, + // which ext-1 counts as the value being present. + (key) => key.startsWith('value') || key.startsWith('_value'), + ), + { + message: + 'ext-1: an extension has either extensions or a value, never both.', + }, + ), +) as unknown as z.ZodType> + +export const familyMemberHistoryConditionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + outcome: z.lazy(() => codeableConceptSchema).optional(), + contributedToDeath: booleanSchema.optional(), + _contributedToDeath: z.lazy(() => elementSchema).optional(), + onsetAge: z.lazy(() => ageSchema).optional(), + onsetRange: z.lazy(() => rangeSchema).optional(), + onsetPeriod: z.lazy(() => periodSchema).optional(), + onsetString: stringSchema.optional(), + _onsetString: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['onsetAge'], + ['onsetRange'], + ['onsetPeriod'], + ['onsetString', '_onsetString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const familyMemberHistorySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('FamilyMemberHistory'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + instantiatesCanonical: canonicalSchema + .nullable() + .array() + .min(1) + .optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + status: z + .enum(['completed', 'entered-in-error', 'health-unknown', 'partial']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), + patient: z.lazy(() => referenceSchema), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema), + sex: z.lazy(() => codeableConceptSchema).optional(), + bornPeriod: z.lazy(() => periodSchema).optional(), + bornDate: dateSchema.optional(), + _bornDate: z.lazy(() => elementSchema).optional(), + bornString: stringSchema.optional(), + _bornString: z.lazy(() => elementSchema).optional(), + ageAge: z.lazy(() => ageSchema).optional(), + ageRange: z.lazy(() => rangeSchema).optional(), + ageString: stringSchema.optional(), + _ageString: z.lazy(() => elementSchema).optional(), + estimatedAge: booleanSchema.optional(), + _estimatedAge: z.lazy(() => elementSchema).optional(), + deceasedBoolean: booleanSchema.optional(), + _deceasedBoolean: z.lazy(() => elementSchema).optional(), + deceasedAge: z.lazy(() => ageSchema).optional(), + deceasedRange: z.lazy(() => rangeSchema).optional(), + deceasedDate: dateSchema.optional(), + _deceasedDate: z.lazy(() => elementSchema).optional(), + deceasedString: stringSchema.optional(), + _deceasedString: z.lazy(() => elementSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + condition: familyMemberHistoryConditionSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['bornPeriod'], + ['bornDate', '_bornDate'], + ['bornString', '_bornString'], + ], + required: false, + }, + { + alternatives: [ + ['ageAge'], + ['ageRange'], + ['ageString', '_ageString'], + ], + required: false, + }, + { + alternatives: [ + ['deceasedBoolean', '_deceasedBoolean'], + ['deceasedAge'], + ['deceasedRange'], + ['deceasedDate', '_deceasedDate'], + ['deceasedString', '_deceasedString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const flagSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Flag'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema), + period: z.lazy(() => periodSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const goalTargetSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + measure: z.lazy(() => codeableConceptSchema).optional(), + detailQuantity: z.lazy(() => quantitySchema).optional(), + detailRange: z.lazy(() => rangeSchema).optional(), + detailCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + detailString: stringSchema.optional(), + _detailString: z.lazy(() => elementSchema).optional(), + detailBoolean: booleanSchema.optional(), + _detailBoolean: z.lazy(() => elementSchema).optional(), + detailInteger: integerSchema.optional(), + _detailInteger: z.lazy(() => elementSchema).optional(), + detailRatio: z.lazy(() => ratioSchema).optional(), + dueDate: dateSchema.optional(), + _dueDate: z.lazy(() => elementSchema).optional(), + dueDuration: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['detailQuantity'], + ['detailRange'], + ['detailCodeableConcept'], + ['detailString', '_detailString'], + ['detailBoolean', '_detailBoolean'], + ['detailInteger', '_detailInteger'], + ['detailRatio'], + ], + required: false, + }, + { + alternatives: [['dueDate', '_dueDate'], ['dueDuration']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const goalSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Goal'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + lifecycleStatus: z + .enum([ + 'accepted', + 'active', + 'cancelled', + 'completed', + 'entered-in-error', + 'on-hold', + 'planned', + 'proposed', + 'rejected', + ]) + .optional(), + _lifecycleStatus: z.lazy(() => elementSchema).optional(), + achievementStatus: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + priority: z.lazy(() => codeableConceptSchema).optional(), + description: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema), + startDate: dateSchema.optional(), + _startDate: z.lazy(() => elementSchema).optional(), + startCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + target: goalTargetSchema.array().min(1).optional(), + statusDate: dateSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), + statusReason: stringSchema.optional(), + _statusReason: z.lazy(() => elementSchema).optional(), + expressedBy: z.lazy(() => referenceSchema).optional(), + addresses: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + outcomeCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + outcomeReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['startDate', '_startDate'], + ['startCodeableConcept'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'lifecycleStatus', shadow: '_lifecycleStatus' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const graphDefinitionLinkSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + sliceName: stringSchema.optional(), + _sliceName: z.lazy(() => elementSchema).optional(), + min: integerSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + target: graphDefinitionLinkTargetSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const graphDefinitionLinkTargetSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + params: stringSchema.optional(), + _params: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + compartment: graphDefinitionLinkTargetCompartmentSchema + .array() + .min(1) + .optional(), + link: z + .lazy(() => graphDefinitionLinkSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const graphDefinitionLinkTargetCompartmentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + use: z.enum(['condition', 'requirement']).optional(), + _use: z.lazy(() => elementSchema).optional(), + code: z + .enum([ + 'Device', + 'Encounter', + 'Patient', + 'Practitioner', + 'RelatedPerson', + ]) + .optional(), + _code: z.lazy(() => elementSchema).optional(), + rule: z.enum(['custom', 'different', 'identical', 'matching']).optional(), + _rule: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'use', shadow: '_use' }, + { name: 'code', shadow: '_code' }, + { name: 'rule', shadow: '_rule' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const graphDefinitionSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('GraphDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + start: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .optional(), + _start: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + link: graphDefinitionLinkSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + { name: 'start', shadow: '_start' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const groupCharacteristicSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + exclude: booleanSchema.optional(), + _exclude: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueCodeableConcept'], + ['valueBoolean', '_valueBoolean'], + ['valueQuantity'], + ['valueRange'], + ['valueReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'exclude', shadow: '_exclude' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const groupMemberSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + entity: z.lazy(() => referenceSchema), + period: z.lazy(() => periodSchema).optional(), + inactive: booleanSchema.optional(), + _inactive: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const groupSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Group'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'animal', + 'device', + 'medication', + 'person', + 'practitioner', + 'substance', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + actual: booleanSchema.optional(), + _actual: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + quantity: unsignedIntSchema.optional(), + _quantity: z.lazy(() => elementSchema).optional(), + managingEntity: z.lazy(() => referenceSchema).optional(), + characteristic: groupCharacteristicSchema.array().min(1).optional(), + member: groupMemberSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'type', shadow: '_type' }, + { name: 'actual', shadow: '_actual' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const guidanceResponseSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('GuidanceResponse'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + requestIdentifier: z.lazy(() => identifierSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + moduleUri: uriSchema.optional(), + _moduleUri: z.lazy(() => elementSchema).optional(), + moduleCanonical: canonicalSchema.optional(), + _moduleCanonical: z.lazy(() => elementSchema).optional(), + moduleCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + status: z + .enum([ + 'data-requested', + 'data-required', + 'entered-in-error', + 'failure', + 'in-progress', + 'success', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + evaluationMessage: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + outputParameters: z.lazy(() => referenceSchema).optional(), + result: z.lazy(() => referenceSchema).optional(), + dataRequirement: z + .lazy(() => dataRequirementSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['moduleUri', '_moduleUri'], + ['moduleCanonical', '_moduleCanonical'], + ['moduleCodeableConcept'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const healthcareServiceEligibilitySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + comment: markdownSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const healthcareServiceAvailableTimeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + daysOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .nullable() + .array() + .min(1) + .optional(), + _daysOfWeek: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + allDay: booleanSchema.optional(), + _allDay: z.lazy(() => elementSchema).optional(), + availableStartTime: timeSchema.optional(), + _availableStartTime: z.lazy(() => elementSchema).optional(), + availableEndTime: timeSchema.optional(), + _availableEndTime: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'daysOfWeek', shadow: '_daysOfWeek', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const healthcareServiceNotAvailableSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + during: z.lazy(() => periodSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'description', shadow: '_description' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const healthcareServiceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('HealthcareService'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + providedBy: z.lazy(() => referenceSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + location: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + extraDetails: markdownSchema.optional(), + _extraDetails: z.lazy(() => elementSchema).optional(), + photo: z.lazy(() => attachmentSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + coverageArea: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + serviceProvisionCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + eligibility: healthcareServiceEligibilitySchema.array().min(1).optional(), + program: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + characteristic: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + communication: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + referralMethod: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + appointmentRequired: booleanSchema.optional(), + _appointmentRequired: z.lazy(() => elementSchema).optional(), + availableTime: healthcareServiceAvailableTimeSchema + .array() + .min(1) + .optional(), + notAvailable: healthcareServiceNotAvailableSchema + .array() + .min(1) + .optional(), + availabilityExceptions: stringSchema.optional(), + _availabilityExceptions: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const humanNameSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + use: z + .enum([ + 'anonymous', + 'maiden', + 'nickname', + 'official', + 'old', + 'temp', + 'usual', + ]) + .optional(), + _use: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + family: stringSchema.optional(), + _family: z.lazy(() => elementSchema).optional(), + given: stringSchema.nullable().array().min(1).optional(), + _given: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + prefix: stringSchema.nullable().array().min(1).optional(), + _prefix: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + suffix: stringSchema.nullable().array().min(1).optional(), + _suffix: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'given', shadow: '_given', required: false }, + { name: 'prefix', shadow: '_prefix', required: false }, + { name: 'suffix', shadow: '_suffix', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const identifierSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + use: z + .enum(['official', 'old', 'secondary', 'temp', 'usual']) + .optional(), + _use: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + assigner: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const imagingStudySeriesSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + uid: idSchema.optional(), + _uid: z.lazy(() => elementSchema).optional(), + number: unsignedIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + modality: z.lazy(() => codingSchema), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + numberOfInstances: unsignedIntSchema.optional(), + _numberOfInstances: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + bodySite: z.lazy(() => codingSchema).optional(), + laterality: z.lazy(() => codingSchema).optional(), + specimen: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + started: dateTimeSchema.optional(), + _started: z.lazy(() => elementSchema).optional(), + performer: imagingStudySeriesPerformerSchema.array().min(1).optional(), + instance: imagingStudySeriesInstanceSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'uid', shadow: '_uid' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const imagingStudySeriesPerformerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const imagingStudySeriesInstanceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + uid: idSchema.optional(), + _uid: z.lazy(() => elementSchema).optional(), + sopClass: z.lazy(() => codingSchema), + number: unsignedIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'uid', shadow: '_uid' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const imagingStudySchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('ImagingStudy'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'available', + 'cancelled', + 'entered-in-error', + 'registered', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + modality: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + started: dateTimeSchema.optional(), + _started: z.lazy(() => elementSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + referrer: z.lazy(() => referenceSchema).optional(), + interpreter: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + numberOfSeries: unsignedIntSchema.optional(), + _numberOfSeries: z.lazy(() => elementSchema).optional(), + numberOfInstances: unsignedIntSchema.optional(), + _numberOfInstances: z.lazy(() => elementSchema).optional(), + procedureReference: z.lazy(() => referenceSchema).optional(), + procedureCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + location: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + series: imagingStudySeriesSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const immunizationPerformerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const immunizationEducationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + documentType: stringSchema.optional(), + _documentType: z.lazy(() => elementSchema).optional(), + reference: uriSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + publicationDate: dateTimeSchema.optional(), + _publicationDate: z.lazy(() => elementSchema).optional(), + presentationDate: dateTimeSchema.optional(), + _presentationDate: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const immunizationReactionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + detail: z.lazy(() => referenceSchema).optional(), + reported: booleanSchema.optional(), + _reported: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const immunizationProtocolAppliedSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + series: stringSchema.optional(), + _series: z.lazy(() => elementSchema).optional(), + authority: z.lazy(() => referenceSchema).optional(), + targetDisease: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + doseNumberPositiveInt: positiveIntSchema.optional(), + _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), + doseNumberString: stringSchema.optional(), + _doseNumberString: z.lazy(() => elementSchema).optional(), + seriesDosesPositiveInt: positiveIntSchema.optional(), + _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), + seriesDosesString: stringSchema.optional(), + _seriesDosesString: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['doseNumberPositiveInt', '_doseNumberPositiveInt'], + ['doseNumberString', '_doseNumberString'], + ], + required: true, + }, + { + alternatives: [ + ['seriesDosesPositiveInt', '_seriesDosesPositiveInt'], + ['seriesDosesString', '_seriesDosesString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const immunizationSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('Immunization'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['completed', 'entered-in-error', 'not-done']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + vaccineCode: z.lazy(() => codeableConceptSchema), + patient: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrenceString: stringSchema.optional(), + _occurrenceString: z.lazy(() => elementSchema).optional(), + recorded: dateTimeSchema.optional(), + _recorded: z.lazy(() => elementSchema).optional(), + primarySource: booleanSchema.optional(), + _primarySource: z.lazy(() => elementSchema).optional(), + reportOrigin: z.lazy(() => codeableConceptSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + manufacturer: z.lazy(() => referenceSchema).optional(), + lotNumber: stringSchema.optional(), + _lotNumber: z.lazy(() => elementSchema).optional(), + expirationDate: dateSchema.optional(), + _expirationDate: z.lazy(() => elementSchema).optional(), + site: z.lazy(() => codeableConceptSchema).optional(), + route: z.lazy(() => codeableConceptSchema).optional(), + doseQuantity: z.lazy(() => quantitySchema).optional(), + performer: immunizationPerformerSchema.array().min(1).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + isSubpotent: booleanSchema.optional(), + _isSubpotent: z.lazy(() => elementSchema).optional(), + subpotentReason: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + education: immunizationEducationSchema.array().min(1).optional(), + programEligibility: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + fundingSource: z.lazy(() => codeableConceptSchema).optional(), + reaction: immunizationReactionSchema.array().min(1).optional(), + protocolApplied: immunizationProtocolAppliedSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrenceString', '_occurrenceString'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const immunizationEvaluationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ImmunizationEvaluation'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z.enum(['completed', 'entered-in-error']).optional(), + _status: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + authority: z.lazy(() => referenceSchema).optional(), + targetDisease: z.lazy(() => codeableConceptSchema), + immunizationEvent: z.lazy(() => referenceSchema), + doseStatus: z.lazy(() => codeableConceptSchema), + doseStatusReason: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + series: stringSchema.optional(), + _series: z.lazy(() => elementSchema).optional(), + doseNumberPositiveInt: positiveIntSchema.optional(), + _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), + doseNumberString: stringSchema.optional(), + _doseNumberString: z.lazy(() => elementSchema).optional(), + seriesDosesPositiveInt: positiveIntSchema.optional(), + _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), + seriesDosesString: stringSchema.optional(), + _seriesDosesString: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['doseNumberPositiveInt', '_doseNumberPositiveInt'], + ['doseNumberString', '_doseNumberString'], + ], + required: false, + }, + { + alternatives: [ + ['seriesDosesPositiveInt', '_seriesDosesPositiveInt'], + ['seriesDosesString', '_seriesDosesString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const immunizationRecommendationRecommendationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + vaccineCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + targetDisease: z.lazy(() => codeableConceptSchema).optional(), + contraindicatedVaccineCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + forecastStatus: z.lazy(() => codeableConceptSchema), + forecastReason: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + dateCriterion: immunizationRecommendationRecommendationDateCriterionSchema + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + series: stringSchema.optional(), + _series: z.lazy(() => elementSchema).optional(), + doseNumberPositiveInt: positiveIntSchema.optional(), + _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), + doseNumberString: stringSchema.optional(), + _doseNumberString: z.lazy(() => elementSchema).optional(), + seriesDosesPositiveInt: positiveIntSchema.optional(), + _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), + seriesDosesString: stringSchema.optional(), + _seriesDosesString: z.lazy(() => elementSchema).optional(), + supportingImmunization: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + supportingPatientInformation: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['doseNumberPositiveInt', '_doseNumberPositiveInt'], + ['doseNumberString', '_doseNumberString'], + ], + required: false, + }, + { + alternatives: [ + ['seriesDosesPositiveInt', '_seriesDosesPositiveInt'], + ['seriesDosesString', '_seriesDosesString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const immunizationRecommendationRecommendationDateCriterionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + value: dateTimeSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'value', shadow: '_value' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const immunizationRecommendationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ImmunizationRecommendation'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + patient: z.lazy(() => referenceSchema), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + authority: z.lazy(() => referenceSchema).optional(), + recommendation: immunizationRecommendationRecommendationSchema + .array() + .min(1), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'date', shadow: '_date' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideDependsOnSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + uri: canonicalSchema.optional(), + _uri: z.lazy(() => elementSchema).optional(), + packageId: idSchema.optional(), + _packageId: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'uri', shadow: '_uri' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideGlobalSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'type', shadow: '_type' }, + { name: 'profile', shadow: '_profile' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + grouping: implementationGuideDefinitionGroupingSchema + .array() + .min(1) + .optional(), + resource: implementationGuideDefinitionResourceSchema.array().min(1), + page: implementationGuideDefinitionPageSchema.optional(), + parameter: implementationGuideDefinitionParameterSchema + .array() + .min(1) + .optional(), + template: implementationGuideDefinitionTemplateSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideManifestSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + rendering: urlSchema.optional(), + _rendering: z.lazy(() => elementSchema).optional(), + resource: implementationGuideManifestResourceSchema.array().min(1), + page: implementationGuideManifestPageSchema.array().min(1).optional(), + image: stringSchema.nullable().array().min(1).optional(), + _image: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + other: stringSchema.nullable().array().min(1).optional(), + _other: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'image', shadow: '_image', required: false }, + { name: 'other', shadow: '_other', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideDefinitionGroupingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideDefinitionResourceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + reference: z.lazy(() => referenceSchema), + fhirVersion: z + .enum([ + '0.0.80', + '0.0.81', + '0.0.82', + '0.01', + '0.05', + '0.06', + '0.11', + '0.4.0', + '0.5.0', + '1.0.0', + '1.0.1', + '1.0.2', + '1.1.0', + '1.4.0', + '1.6.0', + '1.8.0', + '3.0.0', + '3.0.1', + '3.3.0', + '3.5.0', + '4.0.0', + '4.0.1', + ]) + .nullable() + .array() + .min(1) + .optional(), + _fhirVersion: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + exampleBoolean: booleanSchema.optional(), + _exampleBoolean: z.lazy(() => elementSchema).optional(), + exampleCanonical: canonicalSchema.optional(), + _exampleCanonical: z.lazy(() => elementSchema).optional(), + groupingId: idSchema.optional(), + _groupingId: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['exampleBoolean', '_exampleBoolean'], + ['exampleCanonical', '_exampleCanonical'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'fhirVersion', shadow: '_fhirVersion', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideDefinitionPageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + nameUrl: urlSchema.optional(), + _nameUrl: z.lazy(() => elementSchema).optional(), + nameReference: z.lazy(() => referenceSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + generation: z.enum(['generated', 'html', 'markdown', 'xml']).optional(), + _generation: z.lazy(() => elementSchema).optional(), + page: z + .lazy(() => implementationGuideDefinitionPageSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['nameUrl', '_nameUrl'], ['nameReference']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'title', shadow: '_title' }, + { name: 'generation', shadow: '_generation' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideDefinitionParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z + .enum([ + 'apply', + 'expansion-parameter', + 'generate-json', + 'generate-turtle', + 'generate-xml', + 'html-template', + 'path-pages', + 'path-resource', + 'path-tx-cache', + 'rule-broken-links', + ]) + .optional(), + _code: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'code', shadow: '_code' }, + { name: 'value', shadow: '_value' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideDefinitionTemplateSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + source: stringSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + scope: stringSchema.optional(), + _scope: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'code', shadow: '_code' }, + { name: 'source', shadow: '_source' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideManifestResourceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + reference: z.lazy(() => referenceSchema), + exampleBoolean: booleanSchema.optional(), + _exampleBoolean: z.lazy(() => elementSchema).optional(), + exampleCanonical: canonicalSchema.optional(), + _exampleCanonical: z.lazy(() => elementSchema).optional(), + relativePath: urlSchema.optional(), + _relativePath: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['exampleBoolean', '_exampleBoolean'], + ['exampleCanonical', '_exampleCanonical'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideManifestPageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + anchor: stringSchema.nullable().array().min(1).optional(), + _anchor: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'anchor', shadow: '_anchor', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ImplementationGuide'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + packageId: idSchema.optional(), + _packageId: z.lazy(() => elementSchema).optional(), + license: z + .enum([ + '0BSD', + 'AAL', + 'ADSL', + 'AFL-1.1', + 'AFL-1.2', + 'AFL-2.0', + 'AFL-2.1', + 'AFL-3.0', + 'AGPL-1.0-only', + 'AGPL-1.0-or-later', + 'AGPL-3.0-only', + 'AGPL-3.0-or-later', + 'AMDPLPA', + 'AML', + 'AMPAS', + 'ANTLR-PD', + 'APAFML', + 'APL-1.0', 'APSL-1.0', 'APSL-1.1', 'APSL-1.2', @@ -15349,560 +24476,3268 @@ export const implementationGuideSchema: z.ZodType = 'zlib-acknowledgement', ]) .optional(), - _license: z.lazy(() => elementSchema).optional(), - fhirVersion: z + _license: z.lazy(() => elementSchema).optional(), + fhirVersion: z + .enum([ + '0.0.80', + '0.0.81', + '0.0.82', + '0.01', + '0.05', + '0.06', + '0.11', + '0.4.0', + '0.5.0', + '1.0.0', + '1.0.1', + '1.0.2', + '1.1.0', + '1.4.0', + '1.6.0', + '1.8.0', + '3.0.0', + '3.0.1', + '3.3.0', + '3.5.0', + '4.0.0', + '4.0.1', + ]) + .nullable() + .array() + .min(1) + .optional(), + _fhirVersion: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + dependsOn: implementationGuideDependsOnSchema.array().min(1).optional(), + global: implementationGuideGlobalSchema.array().min(1).optional(), + definition: implementationGuideDefinitionSchema.optional(), + manifest: implementationGuideManifestSchema.optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + { name: 'packageId', shadow: '_packageId' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'fhirVersion', shadow: '_fhirVersion', required: true }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanContactSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + purpose: z.lazy(() => codeableConceptSchema).optional(), + name: z.lazy(() => humanNameSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + address: z.lazy(() => addressSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanCoverageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + network: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + benefit: insurancePlanCoverageBenefitSchema.array().min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanPlanSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + coverageArea: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + network: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + generalCost: insurancePlanPlanGeneralCostSchema.array().min(1).optional(), + specificCost: insurancePlanPlanSpecificCostSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanCoverageBenefitSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + requirement: stringSchema.optional(), + _requirement: z.lazy(() => elementSchema).optional(), + limit: insurancePlanCoverageBenefitLimitSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanPlanGeneralCostSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + groupSize: positiveIntSchema.optional(), + _groupSize: z.lazy(() => elementSchema).optional(), + cost: z.lazy(() => moneySchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanPlanSpecificCostSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + category: z.lazy(() => codeableConceptSchema), + benefit: insurancePlanPlanSpecificCostBenefitSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanCoverageBenefitLimitSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + value: z.lazy(() => quantitySchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanPlanSpecificCostBenefitSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + cost: insurancePlanPlanSpecificCostBenefitCostSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanPlanSpecificCostBenefitCostSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + applicability: z.lazy(() => codeableConceptSchema).optional(), + qualifiers: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + value: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const insurancePlanSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('InsurancePlan'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + alias: stringSchema.nullable().array().min(1).optional(), + _alias: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + ownedBy: z.lazy(() => referenceSchema).optional(), + administeredBy: z.lazy(() => referenceSchema).optional(), + coverageArea: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + contact: insurancePlanContactSchema.array().min(1).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + network: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + coverage: insurancePlanCoverageSchema.array().min(1).optional(), + plan: insurancePlanPlanSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'alias', shadow: '_alias', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const invoiceParticipantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const invoiceLineItemSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + chargeItemReference: z.lazy(() => referenceSchema).optional(), + chargeItemCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + priceComponent: invoiceLineItemPriceComponentSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['chargeItemReference'], + ['chargeItemCodeableConcept'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const invoiceLineItemPriceComponentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z + .enum([ + 'base', + 'deduction', + 'discount', + 'informational', + 'surcharge', + 'tax', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const invoiceSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Invoice'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['balanced', 'cancelled', 'draft', 'entered-in-error', 'issued']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + cancelledReason: stringSchema.optional(), + _cancelledReason: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + recipient: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + participant: invoiceParticipantSchema.array().min(1).optional(), + issuer: z.lazy(() => referenceSchema).optional(), + account: z.lazy(() => referenceSchema).optional(), + lineItem: invoiceLineItemSchema.array().min(1).optional(), + totalPriceComponent: z + .lazy(() => invoiceLineItemPriceComponentSchema) + .array() + .min(1) + .optional(), + totalNet: z.lazy(() => moneySchema).optional(), + totalGross: z.lazy(() => moneySchema).optional(), + paymentTerms: markdownSchema.optional(), + _paymentTerms: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const librarySchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Library'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + parameter: z + .lazy(() => parameterDefinitionSchema) + .array() + .min(1) + .optional(), + dataRequirement: z + .lazy(() => dataRequirementSchema) + .array() + .min(1) + .optional(), + content: z + .lazy(() => attachmentSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['subjectCodeableConcept'], ['subjectReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const linkageItemSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.enum(['alternate', 'historical', 'source']).optional(), + _type: z.lazy(() => elementSchema).optional(), + resource: z.lazy(() => referenceSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const linkageSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Linkage'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + item: linkageItemSchema.array().min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const listEntrySchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + flag: z.lazy(() => codeableConceptSchema).optional(), + deleted: booleanSchema.optional(), + _deleted: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + item: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const listSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('List'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z.enum(['current', 'entered-in-error', 'retired']).optional(), + _status: z.lazy(() => elementSchema).optional(), + mode: z.enum(['changes', 'snapshot', 'working']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + orderedBy: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + entry: listEntrySchema.array().min(1).optional(), + emptyReason: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'mode', shadow: '_mode' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const locationPositionSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + longitude: decimalSchema.optional(), + _longitude: z.lazy(() => elementSchema).optional(), + latitude: decimalSchema.optional(), + _latitude: z.lazy(() => elementSchema).optional(), + altitude: decimalSchema.optional(), + _altitude: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'longitude', shadow: '_longitude' }, + { name: 'latitude', shadow: '_latitude' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const locationHoursOfOperationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + daysOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .nullable() + .array() + .min(1) + .optional(), + _daysOfWeek: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + allDay: booleanSchema.optional(), + _allDay: z.lazy(() => elementSchema).optional(), + openingTime: timeSchema.optional(), + _openingTime: z.lazy(() => elementSchema).optional(), + closingTime: timeSchema.optional(), + _closingTime: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'daysOfWeek', shadow: '_daysOfWeek', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const locationSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Location'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z.enum(['active', 'inactive', 'suspended']).optional(), + _status: z.lazy(() => elementSchema).optional(), + operationalStatus: z.lazy(() => codingSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + alias: stringSchema.nullable().array().min(1).optional(), + _alias: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + mode: z.enum(['instance', 'kind']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + address: z.lazy(() => addressSchema).optional(), + physicalType: z.lazy(() => codeableConceptSchema).optional(), + position: locationPositionSchema.optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + partOf: z.lazy(() => referenceSchema).optional(), + hoursOfOperation: locationHoursOfOperationSchema + .array() + .min(1) + .optional(), + availabilityExceptions: stringSchema.optional(), + _availabilityExceptions: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'alias', shadow: '_alias', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const marketingStatusSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + country: z.lazy(() => codeableConceptSchema), + jurisdiction: z.lazy(() => codeableConceptSchema).optional(), + status: z.lazy(() => codeableConceptSchema), + dateRange: z.lazy(() => periodSchema), + restoreDate: dateTimeSchema.optional(), + _restoreDate: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const measureGroupSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + population: measureGroupPopulationSchema.array().min(1).optional(), + stratifier: measureGroupStratifierSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const measureSupplementalDataSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + usage: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureGroupPopulationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureGroupStratifierSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema).optional(), + component: measureGroupStratifierComponentSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureGroupStratifierComponentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Measure'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + library: canonicalSchema.nullable().array().min(1).optional(), + _library: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + disclaimer: markdownSchema.optional(), + _disclaimer: z.lazy(() => elementSchema).optional(), + scoring: z.lazy(() => codeableConceptSchema).optional(), + compositeScoring: z.lazy(() => codeableConceptSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + riskAdjustment: stringSchema.optional(), + _riskAdjustment: z.lazy(() => elementSchema).optional(), + rateAggregation: stringSchema.optional(), + _rateAggregation: z.lazy(() => elementSchema).optional(), + rationale: markdownSchema.optional(), + _rationale: z.lazy(() => elementSchema).optional(), + clinicalRecommendationStatement: markdownSchema.optional(), + _clinicalRecommendationStatement: z.lazy(() => elementSchema).optional(), + improvementNotation: z.lazy(() => codeableConceptSchema).optional(), + definition: markdownSchema.nullable().array().min(1).optional(), + _definition: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + guidance: markdownSchema.optional(), + _guidance: z.lazy(() => elementSchema).optional(), + group: measureGroupSchema.array().min(1).optional(), + supplementalData: measureSupplementalDataSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['subjectCodeableConcept'], ['subjectReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'library', shadow: '_library', required: false }, + { name: 'definition', shadow: '_definition', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureReportGroupSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + population: measureReportGroupPopulationSchema.array().min(1).optional(), + measureScore: z.lazy(() => quantitySchema).optional(), + stratifier: measureReportGroupStratifierSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureReportGroupPopulationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + count: integerSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + subjectResults: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureReportGroupStratifierSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + stratum: measureReportGroupStratifierStratumSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureReportGroupStratifierStratumSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + value: z.lazy(() => codeableConceptSchema).optional(), + component: measureReportGroupStratifierStratumComponentSchema + .array() + .min(1) + .optional(), + population: measureReportGroupStratifierStratumPopulationSchema + .array() + .min(1) + .optional(), + measureScore: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureReportGroupStratifierStratumComponentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + value: z.lazy(() => codeableConceptSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const measureReportGroupStratifierStratumPopulationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + count: integerSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + subjectResults: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const measureReportSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MeasureReport'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z.enum(['complete', 'error', 'pending']).optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z + .enum(['data-collection', 'individual', 'subject-list', 'summary']) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + measure: canonicalSchema.optional(), + _measure: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + reporter: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema), + improvementNotation: z.lazy(() => codeableConceptSchema).optional(), + group: measureReportGroupSchema.array().min(1).optional(), + evaluatedResource: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'type', shadow: '_type' }, + { name: 'measure', shadow: '_measure' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const mediaSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Media'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z .enum([ - '0.0.80', - '0.0.81', - '0.0.82', - '0.01', - '0.05', - '0.06', - '0.11', - '0.4.0', - '0.5.0', - '1.0.0', - '1.0.1', - '1.0.2', - '1.1.0', - '1.4.0', - '1.6.0', - '1.8.0', - '3.0.0', - '3.0.1', - '3.3.0', - '3.5.0', - '4.0.0', - '4.0.1', + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + modality: z.lazy(() => codeableConceptSchema).optional(), + view: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + createdDateTime: dateTimeSchema.optional(), + _createdDateTime: z.lazy(() => elementSchema).optional(), + createdPeriod: z.lazy(() => periodSchema).optional(), + issued: instantSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + operator: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + deviceName: stringSchema.optional(), + _deviceName: z.lazy(() => elementSchema).optional(), + device: z.lazy(() => referenceSchema).optional(), + height: positiveIntSchema.optional(), + _height: z.lazy(() => elementSchema).optional(), + width: positiveIntSchema.optional(), + _width: z.lazy(() => elementSchema).optional(), + frames: positiveIntSchema.optional(), + _frames: z.lazy(() => elementSchema).optional(), + duration: decimalSchema.optional(), + _duration: z.lazy(() => elementSchema).optional(), + content: z.lazy(() => attachmentSchema), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['createdDateTime', '_createdDateTime'], + ['createdPeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationIngredientSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + isActive: booleanSchema.optional(), + _isActive: z.lazy(() => elementSchema).optional(), + strength: z.lazy(() => ratioSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['itemCodeableConcept'], ['itemReference']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationBatchSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + lotNumber: stringSchema.optional(), + _lotNumber: z.lazy(() => elementSchema).optional(), + expirationDate: dateTimeSchema.optional(), + _expirationDate: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const medicationSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('Medication'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), + _status: z.lazy(() => elementSchema).optional(), + manufacturer: z.lazy(() => referenceSchema).optional(), + form: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => ratioSchema).optional(), + ingredient: medicationIngredientSchema.array().min(1).optional(), + batch: medicationBatchSchema.optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const medicationAdministrationPerformerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationAdministrationDosageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + site: z.lazy(() => codeableConceptSchema).optional(), + route: z.lazy(() => codeableConceptSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + dose: z.lazy(() => quantitySchema).optional(), + rateRatio: z.lazy(() => ratioSchema).optional(), + rateQuantity: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => + [ + { alternatives: [['rateRatio'], ['rateQuantity']], required: false }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationAdministrationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicationAdministration'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) .array() - .min(1), - _fhirVersion: z + .min(1) + .optional(), + instantiates: uriSchema.nullable().array().min(1).optional(), + _instantiates: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - dependsOn: implementationGuideDependsOnSchema.array().optional(), - global: implementationGuideGlobalSchema.array().optional(), - definition: implementationGuideDefinitionSchema.optional(), - manifest: implementationGuideManifestSchema.optional(), - }), - ) + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'stopped', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + medicationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema), + context: z.lazy(() => referenceSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + performer: medicationAdministrationPerformerSchema + .array() + .min(1) + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + request: z.lazy(() => referenceSchema).optional(), + device: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + dosage: medicationAdministrationDosageSchema.optional(), + eventHistory: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['medicationCodeableConcept'], + ['medicationReference'], + ], + required: true, + }, + { + alternatives: [ + ['effectiveDateTime', '_effectiveDateTime'], + ['effectivePeriod'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'instantiates', shadow: '_instantiates', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const insurancePlanContactSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationDispensePerformerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - purpose: z.lazy(() => codeableConceptSchema).optional(), - name: z.lazy(() => humanNameSchema).optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - address: z.lazy(() => addressSchema).optional(), - }), - ) + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const insurancePlanCoverageSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationDispenseSubstitutionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - network: z + wasSubstituted: booleanSchema.optional(), + _wasSubstituted: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + reason: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + responsibleParty: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - benefit: insurancePlanCoverageBenefitSchema.array().min(1), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'wasSubstituted', shadow: '_wasSubstituted' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const insurancePlanPlanSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const medicationDispenseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicationDispense'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'cancelled', + 'completed', + 'declined', + 'entered-in-error', + 'in-progress', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReasonCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + statusReasonReference: z.lazy(() => referenceSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + medicationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + context: z.lazy(() => referenceSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + performer: medicationDispensePerformerSchema.array().min(1).optional(), + location: z.lazy(() => referenceSchema).optional(), + authorizingPrescription: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), type: z.lazy(() => codeableConceptSchema).optional(), - coverageArea: z + quantity: z.lazy(() => quantitySchema).optional(), + daysSupply: z.lazy(() => quantitySchema).optional(), + whenPrepared: dateTimeSchema.optional(), + _whenPrepared: z.lazy(() => elementSchema).optional(), + whenHandedOver: dateTimeSchema.optional(), + _whenHandedOver: z.lazy(() => elementSchema).optional(), + destination: z.lazy(() => referenceSchema).optional(), + receiver: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - network: z + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + dosageInstruction: z + .lazy(() => dosageSchema) + .array() + .min(1) + .optional(), + substitution: medicationDispenseSubstitutionSchema.optional(), + detectedIssue: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + eventHistory: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - generalCost: insurancePlanPlanGeneralCostSchema.array().optional(), - specificCost: insurancePlanPlanSpecificCostSchema.array().optional(), - }), -) + }) + .refine( + (value) => + [ + { + alternatives: [ + ['statusReasonCodeableConcept'], + ['statusReasonReference'], + ], + required: false, + }, + { + alternatives: [ + ['medicationCodeableConcept'], + ['medicationReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const insurancePlanCoverageBenefitSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationKnowledgeRelatedMedicationKnowledgeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), type: z.lazy(() => codeableConceptSchema), - requirement: stringSchema.optional(), - _requirement: z.lazy(() => elementSchema).optional(), - limit: insurancePlanCoverageBenefitLimitSchema.array().optional(), - }), - ) + reference: z + .lazy(() => referenceSchema) + .array() + .min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const insurancePlanPlanGeneralCostSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationKnowledgeMonographSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), type: z.lazy(() => codeableConceptSchema).optional(), - groupSize: positiveIntSchema.optional(), - _groupSize: z.lazy(() => elementSchema).optional(), - cost: z.lazy(() => moneySchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - }), - ) + source: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationKnowledgeIngredientSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + isActive: booleanSchema.optional(), + _isActive: z.lazy(() => elementSchema).optional(), + strength: z.lazy(() => ratioSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['itemCodeableConcept'], ['itemReference']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const insurancePlanPlanSpecificCostSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationKnowledgeCostSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - category: z.lazy(() => codeableConceptSchema), - benefit: insurancePlanPlanSpecificCostBenefitSchema.array().optional(), - }), - ) + type: z.lazy(() => codeableConceptSchema), + source: stringSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + cost: z.lazy(() => moneySchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const insurancePlanCoverageBenefitLimitSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationKnowledgeMonitoringProgramSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - value: z.lazy(() => quantitySchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + type: z.lazy(() => codeableConceptSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const insurancePlanPlanSpecificCostBenefitSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationKnowledgeAdministrationGuidelinesSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - cost: insurancePlanPlanSpecificCostBenefitCostSchema.array().optional(), - }), - ) + dosage: medicationKnowledgeAdministrationGuidelinesDosageSchema + .array() + .min(1) + .optional(), + indicationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + indicationReference: z.lazy(() => referenceSchema).optional(), + patientCharacteristics: + medicationKnowledgeAdministrationGuidelinesPatientCharacteristicsSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['indicationCodeableConcept'], + ['indicationReference'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const insurancePlanPlanSpecificCostBenefitCostSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationKnowledgeMedicineClassificationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), type: z.lazy(() => codeableConceptSchema), - applicability: z.lazy(() => codeableConceptSchema).optional(), - qualifiers: z + classification: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - value: z.lazy(() => quantitySchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const insurancePlanSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('InsurancePlan'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), - _status: z.lazy(() => elementSchema).optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - alias: stringSchema.array().optional(), - _alias: z - .lazy(() => elementSchema) - .array() - .optional(), - period: z.lazy(() => periodSchema).optional(), - ownedBy: z.lazy(() => referenceSchema).optional(), - administeredBy: z.lazy(() => referenceSchema).optional(), - coverageArea: z - .lazy(() => referenceSchema) - .array() - .optional(), - contact: insurancePlanContactSchema.array().optional(), - endpoint: z - .lazy(() => referenceSchema) - .array() - .optional(), - network: z - .lazy(() => referenceSchema) - .array() - .optional(), - coverage: insurancePlanCoverageSchema.array().optional(), - plan: insurancePlanPlanSchema.array().optional(), - }), -) - -export const invoiceParticipantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationKnowledgePackagingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - role: z.lazy(() => codeableConceptSchema).optional(), - actor: z.lazy(() => referenceSchema), - }), - ) - -export const invoiceLineItemSchema: z.ZodType = z.lazy( - () => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequence: positiveIntSchema.optional(), - _sequence: z.lazy(() => elementSchema).optional(), - chargeItemReference: z.lazy(() => referenceSchema).optional(), - chargeItemCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - priceComponent: invoiceLineItemPriceComponentSchema.array().optional(), - }) - .refine( - (value) => - [ - { - names: ['chargeItemReference', 'chargeItemCodeableConcept'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) + type: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const invoiceLineItemPriceComponentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationKnowledgeDrugCharacteristicSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum([ - 'base', - 'deduction', - 'discount', - 'informational', - 'surcharge', - 'tax', - ]), - _type: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - amount: z.lazy(() => moneySchema).optional(), - }), - ) + type: z.lazy(() => codeableConceptSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueCodeableConcept'], + ['valueString', '_valueString'], + ['valueQuantity'], + ['valueBase64Binary', '_valueBase64Binary'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const invoiceSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Invoice'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'balanced', - 'cancelled', - 'draft', - 'entered-in-error', - 'issued', - ]), - _status: z.lazy(() => elementSchema).optional(), - cancelledReason: stringSchema.optional(), - _cancelledReason: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - recipient: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - participant: invoiceParticipantSchema.array().optional(), - issuer: z.lazy(() => referenceSchema).optional(), - account: z.lazy(() => referenceSchema).optional(), - lineItem: invoiceLineItemSchema.array().optional(), - totalPriceComponent: z - .lazy(() => invoiceLineItemPriceComponentSchema) - .array() - .optional(), - totalNet: z.lazy(() => moneySchema).optional(), - totalGross: z.lazy(() => moneySchema).optional(), - paymentTerms: markdownSchema.optional(), - _paymentTerms: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }), -) - -export const librarySchema: z.ZodType = z.lazy(() => +export const medicationKnowledgeRegulatorySchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Library'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) + regulatoryAuthority: z.lazy(() => referenceSchema), + substitution: medicationKnowledgeRegulatorySubstitutionSchema .array() + .min(1) .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) + schedule: medicationKnowledgeRegulatoryScheduleSchema .array() + .min(1) .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z - .lazy(() => codeableConceptSchema) + maxDispense: medicationKnowledgeRegulatoryMaxDispenseSchema.optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationKnowledgeKineticsSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - author: z - .lazy(() => contactDetailSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - editor: z - .lazy(() => contactDetailSchema) + areaUnderCurve: z + .lazy(() => quantitySchema) .array() + .min(1) .optional(), - reviewer: z - .lazy(() => contactDetailSchema) + lethalDose50: z + .lazy(() => quantitySchema) .array() + .min(1) .optional(), - endorser: z - .lazy(() => contactDetailSchema) + halfLifePeriod: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationKnowledgeAdministrationGuidelinesDosageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - parameter: z - .lazy(() => parameterDefinitionSchema) + type: z.lazy(() => codeableConceptSchema), + dosage: z + .lazy(() => dosageSchema) + .array() + .min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const medicationKnowledgeAdministrationGuidelinesPatientCharacteristicsSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - dataRequirement: z - .lazy(() => dataRequirementSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - content: z - .lazy(() => attachmentSchema) + characteristicCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + characteristicQuantity: z.lazy(() => quantitySchema).optional(), + value: stringSchema.nullable().array().min(1).optional(), + _value: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), }) .refine( (value) => [ { - names: ['subjectCodeableConcept', 'subjectReference'], - required: false, + alternatives: [ + ['characteristicCodeableConcept'], + ['characteristicQuantity'], + ], + required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -15910,364 +27745,413 @@ export const librarySchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'value', shadow: '_value', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const linkageItemSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum(['alternate', 'historical', 'source']), - _type: z.lazy(() => elementSchema).optional(), - resource: z.lazy(() => referenceSchema), - }), -) - -export const linkageSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Linkage'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - item: linkageItemSchema.array().min(1), - }), -) - -export const listEntrySchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - flag: z.lazy(() => codeableConceptSchema).optional(), - deleted: booleanSchema.optional(), - _deleted: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - item: z.lazy(() => referenceSchema), - }), -) - -export const listSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('List'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['current', 'entered-in-error', 'retired']), - _status: z.lazy(() => elementSchema).optional(), - mode: z.enum(['changes', 'snapshot', 'working']), - _mode: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - source: z.lazy(() => referenceSchema).optional(), - orderedBy: z.lazy(() => codeableConceptSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - entry: listEntrySchema.array().optional(), - emptyReason: z.lazy(() => codeableConceptSchema).optional(), - }), -) - -export const locationPositionSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +) as unknown as z.ZodType< + FhirJson +> + +export const medicationKnowledgeRegulatorySubstitutionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - longitude: decimalSchema, - _longitude: z.lazy(() => elementSchema).optional(), - latitude: decimalSchema, - _latitude: z.lazy(() => elementSchema).optional(), - altitude: decimalSchema.optional(), - _altitude: z.lazy(() => elementSchema).optional(), - }), -) + type: z.lazy(() => codeableConceptSchema), + allowed: booleanSchema.optional(), + _allowed: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'allowed', shadow: '_allowed' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const locationHoursOfOperationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationKnowledgeRegulatoryScheduleSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - daysOfWeek: z - .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) - .array() - .optional(), - _daysOfWeek: z - .lazy(() => elementSchema) - .array() - .optional(), - allDay: booleanSchema.optional(), - _allDay: z.lazy(() => elementSchema).optional(), - openingTime: timeSchema.optional(), - _openingTime: z.lazy(() => elementSchema).optional(), - closingTime: timeSchema.optional(), - _closingTime: z.lazy(() => elementSchema).optional(), - }), - ) + schedule: z.lazy(() => codeableConceptSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const locationSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Location'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'inactive', 'suspended']).optional(), - _status: z.lazy(() => elementSchema).optional(), - operationalStatus: z.lazy(() => codingSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - alias: stringSchema.array().optional(), - _alias: z - .lazy(() => elementSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - mode: z.enum(['instance', 'kind']).optional(), - _mode: z.lazy(() => elementSchema).optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - address: z.lazy(() => addressSchema).optional(), - physicalType: z.lazy(() => codeableConceptSchema).optional(), - position: locationPositionSchema.optional(), - managingOrganization: z.lazy(() => referenceSchema).optional(), - partOf: z.lazy(() => referenceSchema).optional(), - hoursOfOperation: locationHoursOfOperationSchema.array().optional(), - availabilityExceptions: stringSchema.optional(), - _availabilityExceptions: z.lazy(() => elementSchema).optional(), - endpoint: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) - -export const marketingStatusSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const medicationKnowledgeRegulatoryMaxDispenseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - country: z.lazy(() => codeableConceptSchema), - jurisdiction: z.lazy(() => codeableConceptSchema).optional(), - status: z.lazy(() => codeableConceptSchema), - dateRange: z.lazy(() => periodSchema), - restoreDate: dateTimeSchema.optional(), - _restoreDate: z.lazy(() => elementSchema).optional(), - }), -) - -export const measureGroupSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - population: measureGroupPopulationSchema.array().optional(), - stratifier: measureGroupStratifierSchema.array().optional(), - }), -) - -export const measureSupplementalDataSchema: z.ZodType = - z.lazy(() => - z.strictObject({ + quantity: z.lazy(() => quantitySchema), + period: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const medicationKnowledgeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicationKnowledge'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), code: z.lazy(() => codeableConceptSchema).optional(), - usage: z + status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), + _status: z.lazy(() => elementSchema).optional(), + manufacturer: z.lazy(() => referenceSchema).optional(), + doseForm: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => quantitySchema).optional(), + synonym: stringSchema.nullable().array().min(1).optional(), + _synonym: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + relatedMedicationKnowledge: + medicationKnowledgeRelatedMedicationKnowledgeSchema + .array() + .min(1) + .optional(), + associatedMedication: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + productType: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - criteria: z.lazy(() => expressionSchema), - }), - ) + monograph: medicationKnowledgeMonographSchema.array().min(1).optional(), + ingredient: medicationKnowledgeIngredientSchema.array().min(1).optional(), + preparationInstruction: markdownSchema.optional(), + _preparationInstruction: z.lazy(() => elementSchema).optional(), + intendedRoute: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + cost: medicationKnowledgeCostSchema.array().min(1).optional(), + monitoringProgram: medicationKnowledgeMonitoringProgramSchema + .array() + .min(1) + .optional(), + administrationGuidelines: + medicationKnowledgeAdministrationGuidelinesSchema + .array() + .min(1) + .optional(), + medicineClassification: medicationKnowledgeMedicineClassificationSchema + .array() + .min(1) + .optional(), + packaging: medicationKnowledgePackagingSchema.optional(), + drugCharacteristic: medicationKnowledgeDrugCharacteristicSchema + .array() + .min(1) + .optional(), + contraindication: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + regulatory: medicationKnowledgeRegulatorySchema.array().min(1).optional(), + kinetics: medicationKnowledgeKineticsSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'synonym', shadow: '_synonym', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const measureGroupPopulationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationRequestDispenseRequestSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - criteria: z.lazy(() => expressionSchema), - }), - ) + initialFill: medicationRequestDispenseRequestInitialFillSchema.optional(), + dispenseInterval: z.lazy(() => durationSchema).optional(), + validityPeriod: z.lazy(() => periodSchema).optional(), + numberOfRepeatsAllowed: unsignedIntSchema.optional(), + _numberOfRepeatsAllowed: z.lazy(() => elementSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + expectedSupplyDuration: z.lazy(() => durationSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const measureGroupStratifierSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationRequestSubstitutionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - criteria: z.lazy(() => expressionSchema).optional(), - component: measureGroupStratifierComponentSchema.array().optional(), - }), - ) + allowedBoolean: booleanSchema.optional(), + _allowedBoolean: z.lazy(() => elementSchema).optional(), + allowedCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + reason: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['allowedBoolean', '_allowedBoolean'], + ['allowedCodeableConcept'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const measureGroupStratifierComponentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationRequestDispenseRequestInitialFillSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - criteria: z.lazy(() => expressionSchema), - }), - ) + quantity: z.lazy(() => quantitySchema).optional(), + duration: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const measureSchema: z.ZodType = z.lazy(() => +export const medicationRequestSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Measure'), + resourceType: z.literal('MedicationRequest'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -16279,130 +28163,163 @@ export const measureSchema: z.ZodType = z.lazy(() => contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) + .optional(), + status: z + .enum([ + 'active', + 'cancelled', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'stopped', + 'unknown', + ]) .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) + statusReason: z.lazy(() => codeableConceptSchema).optional(), + intent: z + .enum([ + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]) + .optional(), + _intent: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + reportedBoolean: booleanSchema.optional(), + _reportedBoolean: z.lazy(() => elementSchema).optional(), + reportedReference: z.lazy(() => referenceSchema).optional(), + medicationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + performerType: z.lazy(() => codeableConceptSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) + reasonReference: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z - .lazy(() => codeableConceptSchema) + instantiatesCanonical: canonicalSchema + .nullable() .array() + .min(1) .optional(), - author: z - .lazy(() => contactDetailSchema) + _instantiatesCanonical: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - editor: z - .lazy(() => contactDetailSchema) + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - reviewer: z - .lazy(() => contactDetailSchema) + basedOn: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - endorser: z - .lazy(() => contactDetailSchema) + groupIdentifier: z.lazy(() => identifierSchema).optional(), + courseOfTherapyType: z.lazy(() => codeableConceptSchema).optional(), + insurance: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - library: canonicalSchema.array().optional(), - _library: z - .lazy(() => elementSchema) + dosageInstruction: z + .lazy(() => dosageSchema) .array() + .min(1) .optional(), - disclaimer: markdownSchema.optional(), - _disclaimer: z.lazy(() => elementSchema).optional(), - scoring: z.lazy(() => codeableConceptSchema).optional(), - compositeScoring: z.lazy(() => codeableConceptSchema).optional(), - type: z - .lazy(() => codeableConceptSchema) + dispenseRequest: medicationRequestDispenseRequestSchema.optional(), + substitution: medicationRequestSubstitutionSchema.optional(), + priorPrescription: z.lazy(() => referenceSchema).optional(), + detectedIssue: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - riskAdjustment: stringSchema.optional(), - _riskAdjustment: z.lazy(() => elementSchema).optional(), - rateAggregation: stringSchema.optional(), - _rateAggregation: z.lazy(() => elementSchema).optional(), - rationale: markdownSchema.optional(), - _rationale: z.lazy(() => elementSchema).optional(), - clinicalRecommendationStatement: markdownSchema.optional(), - _clinicalRecommendationStatement: z.lazy(() => elementSchema).optional(), - improvementNotation: z.lazy(() => codeableConceptSchema).optional(), - definition: markdownSchema.array().optional(), - _definition: z - .lazy(() => elementSchema) + eventHistory: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - guidance: markdownSchema.optional(), - _guidance: z.lazy(() => elementSchema).optional(), - group: measureGroupSchema.array().optional(), - supplementalData: measureSupplementalDataSchema.array().optional(), }) .refine( (value) => [ { - names: ['subjectCodeableConcept', 'subjectReference'], + alternatives: [ + ['reportedBoolean', '_reportedBoolean'], + ['reportedReference'], + ], required: false, }, + { + alternatives: [ + ['medicationCodeableConcept'], + ['medicationReference'], + ], + required: true, + }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -16410,260 +28327,364 @@ export const measureSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'intent', shadow: '_intent' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const measureReportGroupSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationStatementSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicationStatement'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - population: measureReportGroupPopulationSchema.array().optional(), - measureScore: z.lazy(() => quantitySchema).optional(), - stratifier: measureReportGroupStratifierSchema.array().optional(), - }), - ) - -export const measureReportGroupPopulationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + basedOn: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - count: integerSchema.optional(), - _count: z.lazy(() => elementSchema).optional(), - subjectResults: z.lazy(() => referenceSchema).optional(), - }), - ) - -export const measureReportGroupStratifierSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + partOf: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() + status: z + .enum([ + 'active', + 'completed', + 'entered-in-error', + 'intended', + 'not-taken', + 'on-hold', + 'stopped', + 'unknown', + ]) .optional(), - code: z + _status: z.lazy(() => elementSchema).optional(), + statusReason: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - stratum: measureReportGroupStratifierStratumSchema.array().optional(), - }), - ) - -export const measureReportGroupStratifierStratumSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + category: z.lazy(() => codeableConceptSchema).optional(), + medicationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema), + context: z.lazy(() => referenceSchema).optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + dateAsserted: dateTimeSchema.optional(), + _dateAsserted: z.lazy(() => elementSchema).optional(), + informationSource: z.lazy(() => referenceSchema).optional(), + derivedFrom: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + reasonCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - value: z.lazy(() => codeableConceptSchema).optional(), - component: measureReportGroupStratifierStratumComponentSchema + reasonReference: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - population: measureReportGroupStratifierStratumPopulationSchema + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - measureScore: z.lazy(() => quantitySchema).optional(), - }), - ) + dosage: z + .lazy(() => dosageSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['medicationCodeableConcept'], + ['medicationReference'], + ], + required: true, + }, + { + alternatives: [ + ['effectiveDateTime', '_effectiveDateTime'], + ['effectivePeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const measureReportGroupStratifierStratumComponentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductNameSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema), - value: z.lazy(() => codeableConceptSchema), - }), - ) + productName: stringSchema.optional(), + _productName: z.lazy(() => elementSchema).optional(), + namePart: medicinalProductNameNamePartSchema.array().min(1).optional(), + countryLanguage: medicinalProductNameCountryLanguageSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'productName', shadow: '_productName' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const measureReportGroupStratifierStratumPopulationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductManufacturingBusinessOperationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - count: integerSchema.optional(), - _count: z.lazy(() => elementSchema).optional(), - subjectResults: z.lazy(() => referenceSchema).optional(), - }), - ) + operationType: z.lazy(() => codeableConceptSchema).optional(), + authorisationReferenceNumber: z.lazy(() => identifierSchema).optional(), + effectiveDate: dateTimeSchema.optional(), + _effectiveDate: z.lazy(() => elementSchema).optional(), + confidentialityIndicator: z.lazy(() => codeableConceptSchema).optional(), + manufacturer: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + regulator: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const measureReportSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('MeasureReport'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['complete', 'error', 'pending']), - _status: z.lazy(() => elementSchema).optional(), - type: z.enum(['data-collection', 'individual', 'subject-list', 'summary']), - _type: z.lazy(() => elementSchema).optional(), - measure: canonicalSchema, - _measure: z.lazy(() => elementSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - reporter: z.lazy(() => referenceSchema).optional(), - period: z.lazy(() => periodSchema), - improvementNotation: z.lazy(() => codeableConceptSchema).optional(), - group: measureReportGroupSchema.array().optional(), - evaluatedResource: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) - -export const mediaSchema: z.ZodType = z.lazy(() => +export const medicinalProductSpecialDesignationSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Media'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'completed', - 'entered-in-error', - 'in-progress', - 'not-done', - 'on-hold', - 'preparation', - 'stopped', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), type: z.lazy(() => codeableConceptSchema).optional(), - modality: z.lazy(() => codeableConceptSchema).optional(), - view: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - createdDateTime: dateTimeSchema.optional(), - _createdDateTime: z.lazy(() => elementSchema).optional(), - createdPeriod: z.lazy(() => periodSchema).optional(), - issued: instantSchema.optional(), - _issued: z.lazy(() => elementSchema).optional(), - operator: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), - deviceName: stringSchema.optional(), - _deviceName: z.lazy(() => elementSchema).optional(), - device: z.lazy(() => referenceSchema).optional(), - height: positiveIntSchema.optional(), - _height: z.lazy(() => elementSchema).optional(), - width: positiveIntSchema.optional(), - _width: z.lazy(() => elementSchema).optional(), - frames: positiveIntSchema.optional(), - _frames: z.lazy(() => elementSchema).optional(), - duration: decimalSchema.optional(), - _duration: z.lazy(() => elementSchema).optional(), - content: z.lazy(() => attachmentSchema), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), + intendedUse: z.lazy(() => codeableConceptSchema).optional(), + indicationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + indicationReference: z.lazy(() => referenceSchema).optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + species: z.lazy(() => codeableConceptSchema).optional(), }) .refine( (value) => [ - { names: ['createdDateTime', 'createdPeriod'], required: false }, + { + alternatives: [ + ['indicationCodeableConcept'], + ['indicationReference'], + ], + required: false, + }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -16671,164 +28692,86 @@ export const mediaSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const medicationIngredientSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - itemReference: z.lazy(() => referenceSchema).optional(), - isActive: booleanSchema.optional(), - _isActive: z.lazy(() => elementSchema).optional(), - strength: z.lazy(() => ratioSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['itemCodeableConcept', 'itemReference'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) +) as unknown as z.ZodType> -export const medicationBatchSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const medicinalProductNameNamePartSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - lotNumber: stringSchema.optional(), - _lotNumber: z.lazy(() => elementSchema).optional(), - expirationDate: dateTimeSchema.optional(), - _expirationDate: z.lazy(() => elementSchema).optional(), - }), -) - -export const medicationSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Medication'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), - _status: z.lazy(() => elementSchema).optional(), - manufacturer: z.lazy(() => referenceSchema).optional(), - form: z.lazy(() => codeableConceptSchema).optional(), - amount: z.lazy(() => ratioSchema).optional(), - ingredient: medicationIngredientSchema.array().optional(), - batch: medicationBatchSchema.optional(), - }), -) - -export const medicationAdministrationPerformerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ + part: stringSchema.optional(), + _part: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codingSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'part', shadow: '_part' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicinalProductNameCountryLanguageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - function: z.lazy(() => codeableConceptSchema).optional(), - actor: z.lazy(() => referenceSchema), - }), - ) - -export const medicationAdministrationDosageSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - site: z.lazy(() => codeableConceptSchema).optional(), - route: z.lazy(() => codeableConceptSchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - dose: z.lazy(() => quantitySchema).optional(), - rateRatio: z.lazy(() => ratioSchema).optional(), - rateQuantity: z.lazy(() => quantitySchema).optional(), - }) - .refine( - (value) => - [{ names: ['rateRatio', 'rateQuantity'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + country: z.lazy(() => codeableConceptSchema), + jurisdiction: z.lazy(() => codeableConceptSchema).optional(), + language: z.lazy(() => codeableConceptSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationAdministrationSchema: z.ZodType = +export const medicinalProductSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('MedicationAdministration'), + resourceType: z.literal('MedicinalProduct'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -16840,668 +28783,986 @@ export const medicationAdministrationSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - instantiates: uriSchema.array().optional(), - _instantiates: z - .lazy(() => elementSchema) - .array() + type: z.lazy(() => codeableConceptSchema).optional(), + domain: z.lazy(() => codingSchema).optional(), + combinedPharmaceuticalDoseForm: z + .lazy(() => codeableConceptSchema) .optional(), - partOf: z - .lazy(() => referenceSchema) + legalStatusOfSupply: z.lazy(() => codeableConceptSchema).optional(), + additionalMonitoringIndicator: z + .lazy(() => codeableConceptSchema) + .optional(), + specialMeasures: stringSchema.nullable().array().min(1).optional(), + _specialMeasures: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - status: z.enum([ - 'completed', - 'entered-in-error', - 'in-progress', - 'not-done', - 'on-hold', - 'stopped', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z + paediatricUseIndicator: z.lazy(() => codeableConceptSchema).optional(), + productClassification: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - medicationCodeableConcept: z - .lazy(() => codeableConceptSchema) + marketingStatus: z + .lazy(() => marketingStatusSchema) + .array() + .min(1) .optional(), - medicationReference: z.lazy(() => referenceSchema).optional(), - subject: z.lazy(() => referenceSchema), - context: z.lazy(() => referenceSchema).optional(), - supportingInformation: z + pharmaceuticalProduct: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - effectiveDateTime: dateTimeSchema.optional(), - _effectiveDateTime: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - performer: medicationAdministrationPerformerSchema.array().optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) + packagedMedicinalProduct: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - reasonReference: z + attachedDocument: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - request: z.lazy(() => referenceSchema).optional(), - device: z + masterFile: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - note: z - .lazy(() => annotationSchema) + contact: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - dosage: medicationAdministrationDosageSchema.optional(), - eventHistory: z + clinicalTrial: z .lazy(() => referenceSchema) .array() + .min(1) + .optional(), + name: medicinalProductNameSchema.array().min(1), + crossReference: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + manufacturingBusinessOperation: + medicinalProductManufacturingBusinessOperationSchema + .array() + .min(1) + .optional(), + specialDesignation: medicinalProductSpecialDesignationSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'specialMeasures', + shadow: '_specialMeasures', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const medicinalProductAuthorizationJurisdictionalAuthorizationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + country: z.lazy(() => codeableConceptSchema).optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + legalStatusOfSupply: z.lazy(() => codeableConceptSchema).optional(), + validityPeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const medicinalProductAuthorizationProcedureSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + datePeriod: z.lazy(() => periodSchema).optional(), + dateDateTime: dateTimeSchema.optional(), + _dateDateTime: z.lazy(() => elementSchema).optional(), + application: z + .lazy(() => medicinalProductAuthorizationProcedureSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['datePeriod'], ['dateDateTime', '_dateDateTime']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicinalProductAuthorizationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicinalProductAuthorization'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + subject: z.lazy(() => referenceSchema).optional(), + country: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + statusDate: dateTimeSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), + restoreDate: dateTimeSchema.optional(), + _restoreDate: z.lazy(() => elementSchema).optional(), + validityPeriod: z.lazy(() => periodSchema).optional(), + dataExclusivityPeriod: z.lazy(() => periodSchema).optional(), + dateOfFirstAuthorization: dateTimeSchema.optional(), + _dateOfFirstAuthorization: z.lazy(() => elementSchema).optional(), + internationalBirthDate: dateTimeSchema.optional(), + _internationalBirthDate: z.lazy(() => elementSchema).optional(), + legalBasis: z.lazy(() => codeableConceptSchema).optional(), + jurisdictionalAuthorization: + medicinalProductAuthorizationJurisdictionalAuthorizationSchema + .array() + .min(1) .optional(), - }) - .refine( - (value) => - [ - { - names: ['medicationCodeableConcept', 'medicationReference'], - required: true, - }, - { names: ['effectiveDateTime', 'effectivePeriod'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + holder: z.lazy(() => referenceSchema).optional(), + regulator: z.lazy(() => referenceSchema).optional(), + procedure: medicinalProductAuthorizationProcedureSchema.optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationDispensePerformerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductContraindicationOtherTherapySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - function: z.lazy(() => codeableConceptSchema).optional(), - actor: z.lazy(() => referenceSchema), - }), - ) + therapyRelationshipType: z.lazy(() => codeableConceptSchema), + medicationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['medicationCodeableConcept'], + ['medicationReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const medicationDispenseSubstitutionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductContraindicationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicinalProductContraindication'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - wasSubstituted: booleanSchema, - _wasSubstituted: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - reason: z + subject: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + disease: z.lazy(() => codeableConceptSchema).optional(), + diseaseStatus: z.lazy(() => codeableConceptSchema).optional(), + comorbidity: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - responsibleParty: z + therapeuticIndication: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - }), - ) + otherTherapy: medicinalProductContraindicationOtherTherapySchema + .array() + .min(1) + .optional(), + population: z + .lazy(() => populationSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationDispenseSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - resourceType: z.literal('MedicationDispense'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'cancelled', - 'completed', - 'declined', - 'entered-in-error', - 'in-progress', - 'on-hold', - 'preparation', - 'stopped', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReasonCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - statusReasonReference: z.lazy(() => referenceSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - medicationCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - medicationReference: z.lazy(() => referenceSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - context: z.lazy(() => referenceSchema).optional(), - supportingInformation: z - .lazy(() => referenceSchema) - .array() - .optional(), - performer: medicationDispensePerformerSchema.array().optional(), - location: z.lazy(() => referenceSchema).optional(), - authorizingPrescription: z - .lazy(() => referenceSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - daysSupply: z.lazy(() => quantitySchema).optional(), - whenPrepared: dateTimeSchema.optional(), - _whenPrepared: z.lazy(() => elementSchema).optional(), - whenHandedOver: dateTimeSchema.optional(), - _whenHandedOver: z.lazy(() => elementSchema).optional(), - destination: z.lazy(() => referenceSchema).optional(), - receiver: z - .lazy(() => referenceSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - dosageInstruction: z - .lazy(() => dosageSchema) - .array() - .optional(), - substitution: medicationDispenseSubstitutionSchema.optional(), - detectedIssue: z - .lazy(() => referenceSchema) - .array() - .optional(), - eventHistory: z - .lazy(() => referenceSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['statusReasonCodeableConcept', 'statusReasonReference'], - required: false, - }, - { - names: ['medicationCodeableConcept', 'medicationReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( +export const medicinalProductIndicationOtherTherapySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + therapyRelationshipType: z.lazy(() => codeableConceptSchema), + medicationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['medicationCodeableConcept'], + ['medicationReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeRelatedMedicationKnowledgeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductIndicationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicinalProductIndication'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - reference: z + subject: z .lazy(() => referenceSchema) .array() - .min(1), - }), - ) + .min(1) + .optional(), + diseaseSymptomProcedure: z.lazy(() => codeableConceptSchema).optional(), + diseaseStatus: z.lazy(() => codeableConceptSchema).optional(), + comorbidity: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + intendedEffect: z.lazy(() => codeableConceptSchema).optional(), + duration: z.lazy(() => quantitySchema).optional(), + otherTherapy: medicinalProductIndicationOtherTherapySchema + .array() + .min(1) + .optional(), + undesirableEffect: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + population: z + .lazy(() => populationSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeMonographSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductIngredientSpecifiedSubstanceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - source: z.lazy(() => referenceSchema).optional(), - }), - ) - -export const medicationKnowledgeIngredientSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - itemReference: z.lazy(() => referenceSchema).optional(), - isActive: booleanSchema.optional(), - _isActive: z.lazy(() => elementSchema).optional(), - strength: z.lazy(() => ratioSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['itemCodeableConcept', 'itemReference'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + code: z.lazy(() => codeableConceptSchema), + group: z.lazy(() => codeableConceptSchema), + confidentiality: z.lazy(() => codeableConceptSchema).optional(), + strength: medicinalProductIngredientSpecifiedSubstanceStrengthSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const medicationKnowledgeCostSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductIngredientSubstanceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - source: stringSchema.optional(), - _source: z.lazy(() => elementSchema).optional(), - cost: z.lazy(() => moneySchema), - }), - ) + code: z.lazy(() => codeableConceptSchema), + strength: z + .lazy(() => medicinalProductIngredientSpecifiedSubstanceStrengthSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeMonitoringProgramSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductIngredientSpecifiedSubstanceStrengthSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - }), - ) - -export const medicationKnowledgeAdministrationGuidelinesSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - dosage: medicationKnowledgeAdministrationGuidelinesDosageSchema + presentation: z.lazy(() => ratioSchema), + presentationLowLimit: z.lazy(() => ratioSchema).optional(), + concentration: z.lazy(() => ratioSchema).optional(), + concentrationLowLimit: z.lazy(() => ratioSchema).optional(), + measurementPoint: stringSchema.optional(), + _measurementPoint: z.lazy(() => elementSchema).optional(), + country: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + referenceStrength: + medicinalProductIngredientSpecifiedSubstanceStrengthReferenceStrengthSchema .array() + .min(1) .optional(), - indicationCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - indicationReference: z.lazy(() => referenceSchema).optional(), - patientCharacteristics: - medicationKnowledgeAdministrationGuidelinesPatientCharacteristicsSchema - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['indicationCodeableConcept', 'indicationReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const medicationKnowledgeMedicineClassificationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductIngredientSpecifiedSubstanceStrengthReferenceStrengthSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - classification: z + substance: z.lazy(() => codeableConceptSchema).optional(), + strength: z.lazy(() => ratioSchema), + strengthLowLimit: z.lazy(() => ratioSchema).optional(), + measurementPoint: stringSchema.optional(), + _measurementPoint: z.lazy(() => elementSchema).optional(), + country: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const medicationKnowledgePackagingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductIngredientSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicinalProductIngredient'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - }), - ) - -export const medicationKnowledgeDrugCharacteristicSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueCodeableConcept', - 'valueString', - 'valueQuantity', - 'valueBase64Binary', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + identifier: z.lazy(() => identifierSchema).optional(), + role: z.lazy(() => codeableConceptSchema), + allergenicIndicator: booleanSchema.optional(), + _allergenicIndicator: z.lazy(() => elementSchema).optional(), + manufacturer: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + specifiedSubstance: medicinalProductIngredientSpecifiedSubstanceSchema + .array() + .min(1) + .optional(), + substance: medicinalProductIngredientSubstanceSchema.optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeRegulatorySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductInteractionInteractantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - regulatoryAuthority: z.lazy(() => referenceSchema), - substitution: medicationKnowledgeRegulatorySubstitutionSchema - .array() - .optional(), - schedule: medicationKnowledgeRegulatoryScheduleSchema.array().optional(), - maxDispense: medicationKnowledgeRegulatoryMaxDispenseSchema.optional(), - }), - ) + itemReference: z.lazy(() => referenceSchema).optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['itemReference'], ['itemCodeableConcept']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeKineticsSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductInteractionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicinalProductInteraction'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - areaUnderCurve: z - .lazy(() => quantitySchema) + subject: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - lethalDose50: z - .lazy(() => quantitySchema) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + interactant: medicinalProductInteractionInteractantSchema .array() + .min(1) .optional(), - halfLifePeriod: z.lazy(() => durationSchema).optional(), - }), - ) + type: z.lazy(() => codeableConceptSchema).optional(), + effect: z.lazy(() => codeableConceptSchema).optional(), + incidence: z.lazy(() => codeableConceptSchema).optional(), + management: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeAdministrationGuidelinesDosageSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductManufacturedSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicinalProductManufactured'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - dosage: z - .lazy(() => dosageSchema) + manufacturedDoseForm: z.lazy(() => codeableConceptSchema), + unitOfPresentation: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema), + manufacturer: z + .lazy(() => referenceSchema) .array() - .min(1), - }), - ) - -export const medicationKnowledgeAdministrationGuidelinesPatientCharacteristicsSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - characteristicCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - characteristicQuantity: z.lazy(() => quantitySchema).optional(), - value: stringSchema.array().optional(), - _value: z - .lazy(() => elementSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'characteristicCodeableConcept', - 'characteristicQuantity', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const medicationKnowledgeRegulatorySubstitutionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + .min(1) + .optional(), + ingredient: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + physicalCharacteristics: z + .lazy(() => prodCharacteristicSchema) + .optional(), + otherCharacteristics: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - allowed: booleanSchema, - _allowed: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeRegulatoryScheduleSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductPackagedBatchIdentifierSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - schedule: z.lazy(() => codeableConceptSchema), - }), - ) + outerPackaging: z.lazy(() => identifierSchema), + immediatePackaging: z.lazy(() => identifierSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeRegulatoryMaxDispenseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductPackagedPackageItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) .optional(), + type: z.lazy(() => codeableConceptSchema), quantity: z.lazy(() => quantitySchema), - period: z.lazy(() => durationSchema).optional(), - }), - ) + material: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + alternateMaterial: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + device: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + manufacturedItem: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + packageItem: z + .lazy(() => medicinalProductPackagedPackageItemSchema) + .array() + .min(1) + .optional(), + physicalCharacteristics: z + .lazy(() => prodCharacteristicSchema) + .optional(), + otherCharacteristics: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + shelfLifeStorage: z + .lazy(() => productShelfLifeSchema) + .array() + .min(1) + .optional(), + manufacturer: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('MedicationKnowledge'), +export const medicinalProductPackagedSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicinalProductPackaged'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -17513,489 +29774,932 @@ export const medicationKnowledgeSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), - _status: z.lazy(() => elementSchema).optional(), - manufacturer: z.lazy(() => referenceSchema).optional(), - doseForm: z.lazy(() => codeableConceptSchema).optional(), - amount: z.lazy(() => quantitySchema).optional(), - synonym: stringSchema.array().optional(), - _synonym: z - .lazy(() => elementSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - relatedMedicationKnowledge: - medicationKnowledgeRelatedMedicationKnowledgeSchema.array().optional(), - associatedMedication: z + subject: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - productType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - monograph: medicationKnowledgeMonographSchema.array().optional(), - ingredient: medicationKnowledgeIngredientSchema.array().optional(), - preparationInstruction: markdownSchema.optional(), - _preparationInstruction: z.lazy(() => elementSchema).optional(), - intendedRoute: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - cost: medicationKnowledgeCostSchema.array().optional(), - monitoringProgram: medicationKnowledgeMonitoringProgramSchema - .array() - .optional(), - administrationGuidelines: - medicationKnowledgeAdministrationGuidelinesSchema.array().optional(), - medicineClassification: medicationKnowledgeMedicineClassificationSchema - .array() - .optional(), - packaging: medicationKnowledgePackagingSchema.optional(), - drugCharacteristic: medicationKnowledgeDrugCharacteristicSchema + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + legalStatusOfSupply: z.lazy(() => codeableConceptSchema).optional(), + marketingStatus: z + .lazy(() => marketingStatusSchema) .array() + .min(1) .optional(), - contraindication: z + marketingAuthorization: z.lazy(() => referenceSchema).optional(), + manufacturer: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - regulatory: medicationKnowledgeRegulatorySchema.array().optional(), - kinetics: medicationKnowledgeKineticsSchema.array().optional(), - }), - ) - -export const medicationRequestDispenseRequestSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + batchIdentifier: medicinalProductPackagedBatchIdentifierSchema .array() + .min(1) .optional(), - initialFill: medicationRequestDispenseRequestInitialFillSchema.optional(), - dispenseInterval: z.lazy(() => durationSchema).optional(), - validityPeriod: z.lazy(() => periodSchema).optional(), - numberOfRepeatsAllowed: unsignedIntSchema.optional(), - _numberOfRepeatsAllowed: z.lazy(() => elementSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - expectedSupplyDuration: z.lazy(() => durationSchema).optional(), - performer: z.lazy(() => referenceSchema).optional(), - }), - ) - -export const medicationRequestSubstitutionSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - allowedBoolean: booleanSchema.optional(), - _allowedBoolean: z.lazy(() => elementSchema).optional(), - allowedCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - reason: z.lazy(() => codeableConceptSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['allowedBoolean', 'allowedCodeableConcept'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + packageItem: medicinalProductPackagedPackageItemSchema.array().min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationRequestDispenseRequestInitialFillSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductPharmaceuticalCharacteristicsSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - duration: z.lazy(() => durationSchema).optional(), - }), - ) - -export const medicationRequestSchema: z.ZodType = z.lazy( - () => - z - .strictObject({ - resourceType: z.literal('MedicationRequest'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'cancelled', - 'completed', - 'draft', - 'entered-in-error', - 'on-hold', - 'stopped', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - intent: z.enum([ - 'filler-order', - 'instance-order', - 'option', - 'order', - 'original-order', - 'plan', - 'proposal', - 'reflex-order', - ]), - _intent: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: z.lazy(() => elementSchema).optional(), - reportedBoolean: booleanSchema.optional(), - _reportedBoolean: z.lazy(() => elementSchema).optional(), - reportedReference: z.lazy(() => referenceSchema).optional(), - medicationCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - medicationReference: z.lazy(() => referenceSchema).optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - supportingInformation: z - .lazy(() => referenceSchema) - .array() - .optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: z.lazy(() => elementSchema).optional(), - requester: z.lazy(() => referenceSchema).optional(), - performer: z.lazy(() => referenceSchema).optional(), - performerType: z.lazy(() => codeableConceptSchema).optional(), - recorder: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - groupIdentifier: z.lazy(() => identifierSchema).optional(), - courseOfTherapyType: z.lazy(() => codeableConceptSchema).optional(), - insurance: z - .lazy(() => referenceSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - dosageInstruction: z - .lazy(() => dosageSchema) - .array() - .optional(), - dispenseRequest: medicationRequestDispenseRequestSchema.optional(), - substitution: medicationRequestSubstitutionSchema.optional(), - priorPrescription: z.lazy(() => referenceSchema).optional(), - detectedIssue: z - .lazy(() => referenceSchema) - .array() - .optional(), - eventHistory: z - .lazy(() => referenceSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['reportedBoolean', 'reportedReference'], - required: false, - }, - { - names: ['medicationCodeableConcept', 'medicationReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) + code: z.lazy(() => codeableConceptSchema), + status: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const medicationStatementSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - resourceType: z.literal('MedicationStatement'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'completed', - 'entered-in-error', - 'intended', - 'not-taken', - 'on-hold', - 'stopped', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - medicationCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - medicationReference: z.lazy(() => referenceSchema).optional(), - subject: z.lazy(() => referenceSchema), - context: z.lazy(() => referenceSchema).optional(), - effectiveDateTime: dateTimeSchema.optional(), - _effectiveDateTime: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - dateAsserted: dateTimeSchema.optional(), - _dateAsserted: z.lazy(() => elementSchema).optional(), - informationSource: z.lazy(() => referenceSchema).optional(), - derivedFrom: z - .lazy(() => referenceSchema) - .array() - .optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) +export const medicinalProductPharmaceuticalRouteOfAdministrationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + firstDose: z.lazy(() => quantitySchema).optional(), + maxSingleDose: z.lazy(() => quantitySchema).optional(), + maxDosePerDay: z.lazy(() => quantitySchema).optional(), + maxDosePerTreatmentPeriod: z.lazy(() => ratioSchema).optional(), + maxTreatmentPeriod: z.lazy(() => durationSchema).optional(), + targetSpecies: + medicinalProductPharmaceuticalRouteOfAdministrationTargetSpeciesSchema .array() + .min(1) .optional(), - note: z - .lazy(() => annotationSchema) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const medicinalProductPharmaceuticalRouteOfAdministrationTargetSpeciesSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + withdrawalPeriod: + medicinalProductPharmaceuticalRouteOfAdministrationTargetSpeciesWithdrawalPeriodSchema .array() + .min(1) .optional(), - dosage: z - .lazy(() => dosageSchema) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const medicinalProductPharmaceuticalRouteOfAdministrationTargetSpeciesWithdrawalPeriodSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + tissue: z.lazy(() => codeableConceptSchema), + value: z.lazy(() => quantitySchema), + supportingInformation: stringSchema.optional(), + _supportingInformation: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const medicinalProductPharmaceuticalSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicinalProductPharmaceutical'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + administrableDoseForm: z.lazy(() => codeableConceptSchema), + unitOfPresentation: z.lazy(() => codeableConceptSchema).optional(), + ingredient: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + device: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + characteristics: medicinalProductPharmaceuticalCharacteristicsSchema + .array() + .min(1) + .optional(), + routeOfAdministration: + medicinalProductPharmaceuticalRouteOfAdministrationSchema .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['medicationCodeableConcept', 'medicationReference'], - required: true, - }, - { - names: ['effectiveDateTime', 'effectivePeriod'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + .min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicinalProductUndesirableEffectSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicinalProductUndesirableEffect'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + subject: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + symptomConditionEffect: z.lazy(() => codeableConceptSchema).optional(), + classification: z.lazy(() => codeableConceptSchema).optional(), + frequencyOfOccurrence: z.lazy(() => codeableConceptSchema).optional(), + population: z + .lazy(() => populationSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const messageDefinitionFocusSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .optional(), + _code: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + min: unsignedIntSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'code', shadow: '_code' }, + { name: 'min', shadow: '_min' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductNameSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const messageDefinitionAllowedResponseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - productName: stringSchema, - _productName: z.lazy(() => elementSchema).optional(), - namePart: medicinalProductNameNamePartSchema.array().optional(), - countryLanguage: medicinalProductNameCountryLanguageSchema + message: canonicalSchema.optional(), + _message: z.lazy(() => elementSchema).optional(), + situation: markdownSchema.optional(), + _situation: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'message', shadow: '_message' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const messageDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MessageDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) .array() + .min(1) .optional(), - }), - ) + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + replaces: canonicalSchema.nullable().array().min(1).optional(), + _replaces: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + base: canonicalSchema.optional(), + _base: z.lazy(() => elementSchema).optional(), + parent: canonicalSchema.nullable().array().min(1).optional(), + _parent: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + eventCoding: z.lazy(() => codingSchema).optional(), + eventUri: uriSchema.optional(), + _eventUri: z.lazy(() => elementSchema).optional(), + category: z.enum(['consequence', 'currency', 'notification']).optional(), + _category: z.lazy(() => elementSchema).optional(), + focus: messageDefinitionFocusSchema.array().min(1).optional(), + responseRequired: z + .enum(['always', 'never', 'on-error', 'on-success']) + .optional(), + _responseRequired: z.lazy(() => elementSchema).optional(), + allowedResponse: messageDefinitionAllowedResponseSchema + .array() + .min(1) + .optional(), + graph: canonicalSchema.nullable().array().min(1).optional(), + _graph: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['eventCoding'], ['eventUri', '_eventUri']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'date', shadow: '_date' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'replaces', shadow: '_replaces', required: false }, + { name: 'parent', shadow: '_parent', required: false }, + { name: 'graph', shadow: '_graph', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductManufacturingBusinessOperationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const messageHeaderDestinationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - operationType: z.lazy(() => codeableConceptSchema).optional(), - authorisationReferenceNumber: z.lazy(() => identifierSchema).optional(), - effectiveDate: dateTimeSchema.optional(), - _effectiveDate: z.lazy(() => elementSchema).optional(), - confidentialityIndicator: z.lazy(() => codeableConceptSchema).optional(), - manufacturer: z - .lazy(() => referenceSchema) + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + target: z.lazy(() => referenceSchema).optional(), + endpoint: urlSchema.optional(), + _endpoint: z.lazy(() => elementSchema).optional(), + receiver: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'endpoint', shadow: '_endpoint' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const messageHeaderSourceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - regulator: z.lazy(() => referenceSchema).optional(), - }), - ) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + software: stringSchema.optional(), + _software: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + contact: z.lazy(() => contactPointSchema).optional(), + endpoint: urlSchema.optional(), + _endpoint: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'endpoint', shadow: '_endpoint' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const messageHeaderResponseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: idSchema.optional(), + _identifier: z.lazy(() => elementSchema).optional(), + code: z.enum(['fatal-error', 'ok', 'transient-error']).optional(), + _code: z.lazy(() => elementSchema).optional(), + details: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'identifier', shadow: '_identifier' }, + { name: 'code', shadow: '_code' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductSpecialDesignationSchema: z.ZodType = +export const messageHeaderSchema: z.ZodType> = z.lazy(() => z .strictObject({ + resourceType: z.literal('MessageHeader'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) + eventCoding: z.lazy(() => codingSchema).optional(), + eventUri: uriSchema.optional(), + _eventUri: z.lazy(() => elementSchema).optional(), + destination: messageHeaderDestinationSchema.array().min(1).optional(), + sender: z.lazy(() => referenceSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + source: messageHeaderSourceSchema, + responsible: z.lazy(() => referenceSchema).optional(), + reason: z.lazy(() => codeableConceptSchema).optional(), + response: messageHeaderResponseSchema.optional(), + focus: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - intendedUse: z.lazy(() => codeableConceptSchema).optional(), - indicationCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - indicationReference: z.lazy(() => referenceSchema).optional(), - status: z.lazy(() => codeableConceptSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - species: z.lazy(() => codeableConceptSchema).optional(), + definition: canonicalSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), }) .refine( (value) => [ { - names: ['indicationCodeableConcept', 'indicationReference'], - required: false, + alternatives: [['eventCoding'], ['eventUri', '_eventUri']], + required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -18003,204 +30707,502 @@ export const medicinalProductSpecialDesignationSchema: z.ZodType Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) + ) as unknown as z.ZodType> -export const medicinalProductNameNamePartSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const metaSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + versionId: idSchema.optional(), + _versionId: z.lazy(() => elementSchema).optional(), + lastUpdated: instantSchema.optional(), + _lastUpdated: z.lazy(() => elementSchema).optional(), + source: uriSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.nullable().array().min(1).optional(), + _profile: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + security: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + tag: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'profile', shadow: '_profile', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const molecularSequenceReferenceSeqSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - part: stringSchema, - _part: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codingSchema), - }), - ) + chromosome: z.lazy(() => codeableConceptSchema).optional(), + genomeBuild: stringSchema.optional(), + _genomeBuild: z.lazy(() => elementSchema).optional(), + orientation: z.enum(['antisense', 'sense']).optional(), + _orientation: z.lazy(() => elementSchema).optional(), + referenceSeqId: z.lazy(() => codeableConceptSchema).optional(), + referenceSeqPointer: z.lazy(() => referenceSchema).optional(), + referenceSeqString: stringSchema.optional(), + _referenceSeqString: z.lazy(() => elementSchema).optional(), + strand: z.enum(['crick', 'watson']).optional(), + _strand: z.lazy(() => elementSchema).optional(), + windowStart: integerSchema.optional(), + _windowStart: z.lazy(() => elementSchema).optional(), + windowEnd: integerSchema.optional(), + _windowEnd: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductNameCountryLanguageSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const molecularSequenceVariantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - country: z.lazy(() => codeableConceptSchema), - jurisdiction: z.lazy(() => codeableConceptSchema).optional(), - language: z.lazy(() => codeableConceptSchema), - }), - ) + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + observedAllele: stringSchema.optional(), + _observedAllele: z.lazy(() => elementSchema).optional(), + referenceAllele: stringSchema.optional(), + _referenceAllele: z.lazy(() => elementSchema).optional(), + cigar: stringSchema.optional(), + _cigar: z.lazy(() => elementSchema).optional(), + variantPointer: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('MedicinalProduct'), +export const molecularSequenceQualitySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), + type: z.enum(['indel', 'snp', 'unknown']).optional(), + _type: z.lazy(() => elementSchema).optional(), + standardSequence: z.lazy(() => codeableConceptSchema).optional(), + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + score: z.lazy(() => quantitySchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + truthTP: decimalSchema.optional(), + _truthTP: z.lazy(() => elementSchema).optional(), + queryTP: decimalSchema.optional(), + _queryTP: z.lazy(() => elementSchema).optional(), + truthFN: decimalSchema.optional(), + _truthFN: z.lazy(() => elementSchema).optional(), + queryFP: decimalSchema.optional(), + _queryFP: z.lazy(() => elementSchema).optional(), + gtFP: decimalSchema.optional(), + _gtFP: z.lazy(() => elementSchema).optional(), + precision: decimalSchema.optional(), + _precision: z.lazy(() => elementSchema).optional(), + recall: decimalSchema.optional(), + _recall: z.lazy(() => elementSchema).optional(), + fScore: decimalSchema.optional(), + _fScore: z.lazy(() => elementSchema).optional(), + roc: molecularSequenceQualityRocSchema.optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const molecularSequenceRepositorySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() + type: z + .enum(['directlink', 'login', 'oauth', 'openapi', 'other']) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - domain: z.lazy(() => codingSchema).optional(), - combinedPharmaceuticalDoseForm: z - .lazy(() => codeableConceptSchema) + _type: z.lazy(() => elementSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + datasetId: stringSchema.optional(), + _datasetId: z.lazy(() => elementSchema).optional(), + variantsetId: stringSchema.optional(), + _variantsetId: z.lazy(() => elementSchema).optional(), + readsetId: stringSchema.optional(), + _readsetId: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const molecularSequenceStructureVariantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) .optional(), - legalStatusOfSupply: z.lazy(() => codeableConceptSchema).optional(), - additionalMonitoringIndicator: z - .lazy(() => codeableConceptSchema) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) .optional(), - specialMeasures: stringSchema.array().optional(), - _specialMeasures: z - .lazy(() => elementSchema) + variantType: z.lazy(() => codeableConceptSchema).optional(), + exact: booleanSchema.optional(), + _exact: z.lazy(() => elementSchema).optional(), + length: integerSchema.optional(), + _length: z.lazy(() => elementSchema).optional(), + outer: molecularSequenceStructureVariantOuterSchema.optional(), + inner: molecularSequenceStructureVariantInnerSchema.optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const molecularSequenceQualityRocSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - paediatricUseIndicator: z.lazy(() => codeableConceptSchema).optional(), - productClassification: z - .lazy(() => codeableConceptSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - marketingStatus: z - .lazy(() => marketingStatusSchema) + score: integerSchema.nullable().array().min(1).optional(), + _score: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - pharmaceuticalProduct: z - .lazy(() => referenceSchema) + numTP: integerSchema.nullable().array().min(1).optional(), + _numTP: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - packagedMedicinalProduct: z - .lazy(() => referenceSchema) + numFP: integerSchema.nullable().array().min(1).optional(), + _numFP: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - attachedDocument: z - .lazy(() => referenceSchema) + numFN: integerSchema.nullable().array().min(1).optional(), + _numFN: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - masterFile: z - .lazy(() => referenceSchema) + precision: decimalSchema.nullable().array().min(1).optional(), + _precision: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - contact: z - .lazy(() => referenceSchema) + sensitivity: decimalSchema.nullable().array().min(1).optional(), + _sensitivity: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - clinicalTrial: z - .lazy(() => referenceSchema) + fMeasure: decimalSchema.nullable().array().min(1).optional(), + _fMeasure: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - name: medicinalProductNameSchema.array().min(1), - crossReference: z - .lazy(() => identifierSchema) + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'score', shadow: '_score', required: false }, + { name: 'numTP', shadow: '_numTP', required: false }, + { name: 'numFP', shadow: '_numFP', required: false }, + { name: 'numFN', shadow: '_numFN', required: false }, + { name: 'precision', shadow: '_precision', required: false }, + { name: 'sensitivity', shadow: '_sensitivity', required: false }, + { name: 'fMeasure', shadow: '_fMeasure', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const molecularSequenceStructureVariantOuterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - manufacturingBusinessOperation: - medicinalProductManufacturingBusinessOperationSchema.array().optional(), - specialDesignation: medicinalProductSpecialDesignationSchema + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), -) + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductAuthorizationJurisdictionalAuthorizationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const molecularSequenceStructureVariantInnerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - country: z.lazy(() => codeableConceptSchema).optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - legalStatusOfSupply: z.lazy(() => codeableConceptSchema).optional(), - validityPeriod: z.lazy(() => periodSchema).optional(), - }), - ) - -export const medicinalProductAuthorizationProcedureSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - datePeriod: z.lazy(() => periodSchema).optional(), - dateDateTime: dateTimeSchema.optional(), - _dateDateTime: z.lazy(() => elementSchema).optional(), - application: z - .lazy(() => medicinalProductAuthorizationProcedureSchema) - .array() - .optional(), - }) - .refine( - (value) => - [{ names: ['datePeriod', 'dateDateTime'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductAuthorizationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('MedicinalProductAuthorization'), +export const molecularSequenceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MolecularSequence'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -18212,554 +31214,1113 @@ export const medicinalProductAuthorizationSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - subject: z.lazy(() => referenceSchema).optional(), - country: z - .lazy(() => codeableConceptSchema) + type: z.enum(['aa', 'dna', 'rna']).optional(), + _type: z.lazy(() => elementSchema).optional(), + coordinateSystem: integerSchema.optional(), + _coordinateSystem: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema).optional(), + specimen: z.lazy(() => referenceSchema).optional(), + device: z.lazy(() => referenceSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + referenceSeq: molecularSequenceReferenceSeqSchema.optional(), + variant: molecularSequenceVariantSchema.array().min(1).optional(), + observedSeq: stringSchema.optional(), + _observedSeq: z.lazy(() => elementSchema).optional(), + quality: molecularSequenceQualitySchema.array().min(1).optional(), + readCoverage: integerSchema.optional(), + _readCoverage: z.lazy(() => elementSchema).optional(), + repository: molecularSequenceRepositorySchema.array().min(1).optional(), + pointer: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) + structureVariant: molecularSequenceStructureVariantSchema .array() + .min(1) .optional(), - status: z.lazy(() => codeableConceptSchema).optional(), - statusDate: dateTimeSchema.optional(), - _statusDate: z.lazy(() => elementSchema).optional(), - restoreDate: dateTimeSchema.optional(), - _restoreDate: z.lazy(() => elementSchema).optional(), - validityPeriod: z.lazy(() => periodSchema).optional(), - dataExclusivityPeriod: z.lazy(() => periodSchema).optional(), - dateOfFirstAuthorization: dateTimeSchema.optional(), - _dateOfFirstAuthorization: z.lazy(() => elementSchema).optional(), - internationalBirthDate: dateTimeSchema.optional(), - _internationalBirthDate: z.lazy(() => elementSchema).optional(), - legalBasis: z.lazy(() => codeableConceptSchema).optional(), - jurisdictionalAuthorization: - medicinalProductAuthorizationJurisdictionalAuthorizationSchema - .array() - .optional(), - holder: z.lazy(() => referenceSchema).optional(), - regulator: z.lazy(() => referenceSchema).optional(), - procedure: medicinalProductAuthorizationProcedureSchema.optional(), - }), - ) - -export const medicinalProductContraindicationOtherTherapySchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - therapyRelationshipType: z.lazy(() => codeableConceptSchema), - medicationCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - medicationReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['medicationCodeableConcept', 'medicationReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'coordinateSystem', shadow: '_coordinateSystem' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductContraindicationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('MedicinalProductContraindication'), +export const moneySchema: z.ZodType> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + currency: codeSchema.optional(), + _currency: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const namingSystemUniqueIdSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - subject: z - .lazy(() => referenceSchema) - .array() - .optional(), - disease: z.lazy(() => codeableConceptSchema).optional(), - diseaseStatus: z.lazy(() => codeableConceptSchema).optional(), - comorbidity: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - therapeuticIndication: z - .lazy(() => referenceSchema) - .array() - .optional(), - otherTherapy: medicinalProductContraindicationOtherTherapySchema - .array() - .optional(), - population: z - .lazy(() => populationSchema) - .array() - .optional(), - }), - ) + type: z.enum(['oid', 'other', 'uri', 'uuid']).optional(), + _type: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + preferred: booleanSchema.optional(), + _preferred: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'type', shadow: '_type' }, + { name: 'value', shadow: '_value' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductIndicationOtherTherapySchema: z.ZodType = - z.lazy(() => +export const namingSystemSchema: z.ZodType> = z.lazy( + () => z .strictObject({ + resourceType: z.literal('NamingSystem'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + kind: z.enum(['codesystem', 'identifier', 'root']).optional(), + _kind: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + responsible: stringSchema.optional(), + _responsible: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) .optional(), - therapyRelationshipType: z.lazy(() => codeableConceptSchema), - medicationCodeableConcept: z + jurisdiction: z .lazy(() => codeableConceptSchema) + .array() + .min(1) .optional(), - medicationReference: z.lazy(() => referenceSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + uniqueId: namingSystemUniqueIdSchema.array().min(1), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + { name: 'kind', shadow: '_kind' }, + { name: 'date', shadow: '_date' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['medicationCodeableConcept', 'medicationReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> -export const medicinalProductIndicationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('MedicinalProductIndication'), +export const narrativeSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + extension: z + .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + status: z + .enum(['additional', 'empty', 'extensions', 'generated']) .optional(), + _status: z.lazy(() => elementSchema).optional(), + div: xhtmlSchema.optional(), + _div: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'div', shadow: '_div' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const nutritionOrderOralDietSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - subject: z - .lazy(() => referenceSchema) + type: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - diseaseSymptomProcedure: z.lazy(() => codeableConceptSchema).optional(), - diseaseStatus: z.lazy(() => codeableConceptSchema).optional(), - comorbidity: z + schedule: z + .lazy(() => timingSchema) + .array() + .min(1) + .optional(), + nutrient: nutritionOrderOralDietNutrientSchema.array().min(1).optional(), + texture: nutritionOrderOralDietTextureSchema.array().min(1).optional(), + fluidConsistencyType: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - intendedEffect: z.lazy(() => codeableConceptSchema).optional(), - duration: z.lazy(() => quantitySchema).optional(), - otherTherapy: medicinalProductIndicationOtherTherapySchema + instruction: stringSchema.optional(), + _instruction: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const nutritionOrderSupplementSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - undesirableEffect: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - population: z - .lazy(() => populationSchema) + type: z.lazy(() => codeableConceptSchema).optional(), + productName: stringSchema.optional(), + _productName: z.lazy(() => elementSchema).optional(), + schedule: z + .lazy(() => timingSchema) .array() + .min(1) .optional(), - }), - ) + quantity: z.lazy(() => quantitySchema).optional(), + instruction: stringSchema.optional(), + _instruction: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductIngredientSpecifiedSubstanceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const nutritionOrderEnteralFormulaSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema), - group: z.lazy(() => codeableConceptSchema), - confidentiality: z.lazy(() => codeableConceptSchema).optional(), - strength: medicinalProductIngredientSpecifiedSubstanceStrengthSchema + baseFormulaType: z.lazy(() => codeableConceptSchema).optional(), + baseFormulaProductName: stringSchema.optional(), + _baseFormulaProductName: z.lazy(() => elementSchema).optional(), + additiveType: z.lazy(() => codeableConceptSchema).optional(), + additiveProductName: stringSchema.optional(), + _additiveProductName: z.lazy(() => elementSchema).optional(), + caloricDensity: z.lazy(() => quantitySchema).optional(), + routeofAdministration: z.lazy(() => codeableConceptSchema).optional(), + administration: nutritionOrderEnteralFormulaAdministrationSchema .array() + .min(1) .optional(), - }), - ) + maxVolumeToDeliver: z.lazy(() => quantitySchema).optional(), + administrationInstruction: stringSchema.optional(), + _administrationInstruction: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductIngredientSubstanceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const nutritionOrderOralDietNutrientSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema), - strength: z - .lazy(() => medicinalProductIngredientSpecifiedSubstanceStrengthSchema) - .array() - .optional(), - }), - ) + modifier: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductIngredientSpecifiedSubstanceStrengthSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const nutritionOrderOralDietTextureSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - presentation: z.lazy(() => ratioSchema), - presentationLowLimit: z.lazy(() => ratioSchema).optional(), - concentration: z.lazy(() => ratioSchema).optional(), - concentrationLowLimit: z.lazy(() => ratioSchema).optional(), - measurementPoint: stringSchema.optional(), - _measurementPoint: z.lazy(() => elementSchema).optional(), - country: z - .lazy(() => codeableConceptSchema) + modifier: z.lazy(() => codeableConceptSchema).optional(), + foodType: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const nutritionOrderEnteralFormulaAdministrationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - referenceStrength: - medicinalProductIngredientSpecifiedSubstanceStrengthReferenceStrengthSchema + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + schedule: z.lazy(() => timingSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + rateQuantity: z.lazy(() => quantitySchema).optional(), + rateRatio: z.lazy(() => ratioSchema).optional(), + }) + .refine( + (value) => + [ + { alternatives: [['rateQuantity'], ['rateRatio']], required: false }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const nutritionOrderSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('NutritionOrder'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) .array() + .min(1) .optional(), - }), - ) + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + instantiatesCanonical: canonicalSchema + .nullable() + .array() + .min(1) + .optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + instantiates: uriSchema.nullable().array().min(1).optional(), + _instantiates: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + status: z + .enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + intent: z + .enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]) + .optional(), + _intent: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + dateTime: dateTimeSchema.optional(), + _dateTime: z.lazy(() => elementSchema).optional(), + orderer: z.lazy(() => referenceSchema).optional(), + allergyIntolerance: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + foodPreferenceModifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + excludeFoodModifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + oralDiet: nutritionOrderOralDietSchema.optional(), + supplement: nutritionOrderSupplementSchema.array().min(1).optional(), + enteralFormula: nutritionOrderEnteralFormulaSchema.optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'intent', shadow: '_intent' }, + { name: 'dateTime', shadow: '_dateTime' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + { name: 'instantiates', shadow: '_instantiates', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const medicinalProductIngredientSpecifiedSubstanceStrengthReferenceStrengthSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const observationReferenceRangeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - substance: z.lazy(() => codeableConceptSchema).optional(), - strength: z.lazy(() => ratioSchema), - strengthLowLimit: z.lazy(() => ratioSchema).optional(), - measurementPoint: stringSchema.optional(), - _measurementPoint: z.lazy(() => elementSchema).optional(), - country: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - }), - ) - -export const medicinalProductIngredientSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('MedicinalProductIngredient'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + low: z.lazy(() => quantitySchema).optional(), + high: z.lazy(() => quantitySchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + appliesTo: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), + age: z.lazy(() => rangeSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const range = value as { + low?: unknown + high?: unknown + text?: unknown + _text?: unknown + } + return ( + range.low !== undefined || + range.high !== undefined || + range.text !== undefined || + range._text !== undefined + ) + }, + { message: 'obs-3: a reference range states a low, a high, or text.' }, + ), +) as unknown as z.ZodType> + +export const observationComponentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - role: z.lazy(() => codeableConceptSchema), - allergenicIndicator: booleanSchema.optional(), - _allergenicIndicator: z.lazy(() => elementSchema).optional(), - manufacturer: z - .lazy(() => referenceSchema) + code: z.lazy(() => codeableConceptSchema), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), + interpretation: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - specifiedSubstance: medicinalProductIngredientSpecifiedSubstanceSchema + referenceRange: z + .lazy(() => observationReferenceRangeSchema) .array() + .min(1) .optional(), - substance: medicinalProductIngredientSubstanceSchema.optional(), - }), - ) + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueQuantity'], + ['valueCodeableConcept'], + ['valueString', '_valueString'], + ['valueBoolean', '_valueBoolean'], + ['valueInteger', '_valueInteger'], + ['valueRange'], + ['valueRatio'], + ['valueSampledData'], + ['valueTime', '_valueTime'], + ['valueDateTime', '_valueDateTime'], + ['valuePeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const observation = value as Record + const hasValue = Object.keys(observation).some( + (key) => + (key.startsWith('value') || key.startsWith('_value')) && + observation[key] !== undefined, + ) + return observation.dataAbsentReason === undefined || !hasValue + }, + { message: 'obs-6: dataAbsentReason is present only when no value is.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductInteractionInteractantSchema: z.ZodType = - z.lazy(() => +export const observationSchema: z.ZodType> = z.lazy( + () => z .strictObject({ + resourceType: z.literal('Observation'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - itemReference: z.lazy(() => referenceSchema).optional(), - itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'amended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'preliminary', + 'registered', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema).optional(), + focus: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + encounter: z.lazy(() => referenceSchema).optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + effectiveTiming: z.lazy(() => timingSchema).optional(), + effectiveInstant: instantSchema.optional(), + _effectiveInstant: z.lazy(() => elementSchema).optional(), + issued: instantSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + performer: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), + interpretation: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + specimen: z.lazy(() => referenceSchema).optional(), + device: z.lazy(() => referenceSchema).optional(), + referenceRange: observationReferenceRangeSchema + .array() + .min(1) + .optional(), + hasMember: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + derivedFrom: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + component: observationComponentSchema.array().min(1).optional(), }) .refine( (value) => [ - { names: ['itemReference', 'itemCodeableConcept'], required: true }, + { + alternatives: [ + ['effectiveDateTime', '_effectiveDateTime'], + ['effectivePeriod'], + ['effectiveTiming'], + ['effectiveInstant', '_effectiveInstant'], + ], + required: false, + }, + { + alternatives: [ + ['valueQuantity'], + ['valueCodeableConcept'], + ['valueString', '_valueString'], + ['valueBoolean', '_valueBoolean'], + ['valueInteger', '_valueInteger'], + ['valueRange'], + ['valueRatio'], + ['valueSampledData'], + ['valueTime', '_valueTime'], + ['valueDateTime', '_valueDateTime'], + ['valuePeriod'], + ], + required: false, + }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), { message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const medicinalProductInteractionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('MedicinalProductInteraction'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - subject: z - .lazy(() => referenceSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - interactant: medicinalProductInteractionInteractantSchema - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - effect: z.lazy(() => codeableConceptSchema).optional(), - incidence: z.lazy(() => codeableConceptSchema).optional(), - management: z.lazy(() => codeableConceptSchema).optional(), - }), - ) - -export const medicinalProductManufacturedSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('MedicinalProductManufactured'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - manufacturedDoseForm: z.lazy(() => codeableConceptSchema), - unitOfPresentation: z.lazy(() => codeableConceptSchema).optional(), - quantity: z.lazy(() => quantitySchema), - manufacturer: z - .lazy(() => referenceSchema) - .array() - .optional(), - ingredient: z - .lazy(() => referenceSchema) - .array() - .optional(), - physicalCharacteristics: z - .lazy(() => prodCharacteristicSchema) - .optional(), - otherCharacteristics: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - }), - ) + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ) + .refine( + (value) => { + const observation = value as Record + const hasValue = Object.keys(observation).some( + (key) => + (key.startsWith('value') || key.startsWith('_value')) && + observation[key] !== undefined, + ) + return observation.dataAbsentReason === undefined || !hasValue + }, + { + message: 'obs-6: dataAbsentReason is present only when no value is.', + }, + ), +) as unknown as z.ZodType> -export const medicinalProductPackagedBatchIdentifierSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const observationDefinitionQuantitativeDetailsSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - outerPackaging: z.lazy(() => identifierSchema), - immediatePackaging: z.lazy(() => identifierSchema).optional(), - }), - ) + customaryUnit: z.lazy(() => codeableConceptSchema).optional(), + unit: z.lazy(() => codeableConceptSchema).optional(), + conversionFactor: decimalSchema.optional(), + _conversionFactor: z.lazy(() => elementSchema).optional(), + decimalPrecision: integerSchema.optional(), + _decimalPrecision: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const medicinalProductPackagedPackageItemSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const observationDefinitionQualifiedIntervalSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - quantity: z.lazy(() => quantitySchema), - material: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - alternateMaterial: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - device: z - .lazy(() => referenceSchema) - .array() - .optional(), - manufacturedItem: z - .lazy(() => referenceSchema) - .array() - .optional(), - packageItem: z - .lazy(() => medicinalProductPackagedPackageItemSchema) - .array() - .optional(), - physicalCharacteristics: z - .lazy(() => prodCharacteristicSchema) - .optional(), - otherCharacteristics: z + category: z.enum(['absolute', 'critical', 'reference']).optional(), + _category: z.lazy(() => elementSchema).optional(), + range: z.lazy(() => rangeSchema).optional(), + context: z.lazy(() => codeableConceptSchema).optional(), + appliesTo: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - shelfLifeStorage: z - .lazy(() => productShelfLifeSchema) - .array() - .optional(), - manufacturer: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), - ) + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + age: z.lazy(() => rangeSchema).optional(), + gestationalAge: z.lazy(() => rangeSchema).optional(), + condition: stringSchema.optional(), + _condition: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductPackagedSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('MedicinalProductPackaged'), +export const observationDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ObservationDefinition'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -18771,905 +32332,1303 @@ export const medicinalProductPackagedSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - subject: z - .lazy(() => referenceSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - legalStatusOfSupply: z.lazy(() => codeableConceptSchema).optional(), - marketingStatus: z - .lazy(() => marketingStatusSchema) - .array() - .optional(), - marketingAuthorization: z.lazy(() => referenceSchema).optional(), - manufacturer: z - .lazy(() => referenceSchema) - .array() - .optional(), - batchIdentifier: medicinalProductPackagedBatchIdentifierSchema - .array() - .optional(), - packageItem: medicinalProductPackagedPackageItemSchema.array().min(1), - }), - ) - -export const medicinalProductPharmaceuticalCharacteristicsSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - status: z.lazy(() => codeableConceptSchema).optional(), - }), - ) - -export const medicinalProductPharmaceuticalRouteOfAdministrationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - firstDose: z.lazy(() => quantitySchema).optional(), - maxSingleDose: z.lazy(() => quantitySchema).optional(), - maxDosePerDay: z.lazy(() => quantitySchema).optional(), - maxDosePerTreatmentPeriod: z.lazy(() => ratioSchema).optional(), - maxTreatmentPeriod: z.lazy(() => durationSchema).optional(), - targetSpecies: - medicinalProductPharmaceuticalRouteOfAdministrationTargetSpeciesSchema - .array() - .optional(), - }), - ) - -export const medicinalProductPharmaceuticalRouteOfAdministrationTargetSpeciesSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + category: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), code: z.lazy(() => codeableConceptSchema), - withdrawalPeriod: - medicinalProductPharmaceuticalRouteOfAdministrationTargetSpeciesWithdrawalPeriodSchema - .array() - .optional(), - }), - ) - -export const medicinalProductPharmaceuticalRouteOfAdministrationTargetSpeciesWithdrawalPeriodSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - tissue: z.lazy(() => codeableConceptSchema), - value: z.lazy(() => quantitySchema), - supportingInformation: stringSchema.optional(), - _supportingInformation: z.lazy(() => elementSchema).optional(), - }), - ) - -export const medicinalProductPharmaceuticalSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('MedicinalProductPharmaceutical'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - administrableDoseForm: z.lazy(() => codeableConceptSchema), - unitOfPresentation: z.lazy(() => codeableConceptSchema).optional(), - ingredient: z - .lazy(() => referenceSchema) - .array() - .optional(), - device: z - .lazy(() => referenceSchema) - .array() - .optional(), - characteristics: medicinalProductPharmaceuticalCharacteristicsSchema + permittedDataType: z + .enum([ + 'CodeableConcept', + 'Period', + 'Quantity', + 'Range', + 'Ratio', + 'SampledData', + 'boolean', + 'dateTime', + 'integer', + 'string', + 'time', + ]) + .nullable() .array() - .optional(), - routeOfAdministration: - medicinalProductPharmaceuticalRouteOfAdministrationSchema - .array() - .min(1), - }), - ) - -export const medicinalProductUndesirableEffectSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('MedicinalProductUndesirableEffect'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + .min(1) + .optional(), + _permittedDataType: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + multipleResultsAllowed: booleanSchema.optional(), + _multipleResultsAllowed: z.lazy(() => elementSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + preferredReportName: stringSchema.optional(), + _preferredReportName: z.lazy(() => elementSchema).optional(), + quantitativeDetails: + observationDefinitionQuantitativeDetailsSchema.optional(), + qualifiedInterval: observationDefinitionQualifiedIntervalSchema .array() + .min(1) .optional(), + validCodedValueSet: z.lazy(() => referenceSchema).optional(), + normalCodedValueSet: z.lazy(() => referenceSchema).optional(), + abnormalCodedValueSet: z.lazy(() => referenceSchema).optional(), + criticalCodedValueSet: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'permittedDataType', + shadow: '_permittedDataType', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const operationDefinitionParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - subject: z - .lazy(() => referenceSchema) - .array() - .optional(), - symptomConditionEffect: z.lazy(() => codeableConceptSchema).optional(), - classification: z.lazy(() => codeableConceptSchema).optional(), - frequencyOfOccurrence: z.lazy(() => codeableConceptSchema).optional(), - population: z - .lazy(() => populationSchema) - .array() + name: codeSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + use: z.enum(['in', 'out']).optional(), + _use: z.lazy(() => elementSchema).optional(), + min: integerSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Any', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'CodeableConcept', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'EffectEvidenceSynthesis', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'Reference', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceAmount', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'Type', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]) .optional(), - }), - ) - -export const messageDefinitionFocusSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.enum([ - 'Account', - 'ActivityDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'EffectEvidenceSynthesis', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProduct', - 'MedicinalProductAuthorization', - 'MedicinalProductContraindication', - 'MedicinalProductIndication', - 'MedicinalProductIngredient', - 'MedicinalProductInteraction', - 'MedicinalProductManufactured', - 'MedicinalProductPackaged', - 'MedicinalProductPharmaceutical', - 'MedicinalProductUndesirableEffect', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'RiskEvidenceSynthesis', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'Substance', - 'SubstanceNucleicAcid', - 'SubstancePolymer', - 'SubstanceProtein', - 'SubstanceReferenceInformation', - 'SubstanceSourceMaterial', - 'SubstanceSpecification', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]), - _code: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.optional(), - _profile: z.lazy(() => elementSchema).optional(), - min: unsignedIntSchema, - _min: z.lazy(() => elementSchema).optional(), - max: stringSchema.optional(), - _max: z.lazy(() => elementSchema).optional(), - }), - ) - -export const messageDefinitionAllowedResponseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + _type: z.lazy(() => elementSchema).optional(), + targetProfile: canonicalSchema.nullable().array().min(1).optional(), + _targetProfile: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() + searchType: z + .enum([ + 'composite', + 'date', + 'number', + 'quantity', + 'reference', + 'special', + 'string', + 'token', + 'uri', + ]) .optional(), - message: canonicalSchema, - _message: z.lazy(() => elementSchema).optional(), - situation: markdownSchema.optional(), - _situation: z.lazy(() => elementSchema).optional(), - }), - ) - -export const messageDefinitionSchema: z.ZodType = z.lazy( - () => - z - .strictObject({ - resourceType: z.literal('MessageDefinition'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - replaces: canonicalSchema.array().optional(), - _replaces: z - .lazy(() => elementSchema) - .array() - .optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema, - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - base: canonicalSchema.optional(), - _base: z.lazy(() => elementSchema).optional(), - parent: canonicalSchema.array().optional(), - _parent: z - .lazy(() => elementSchema) - .array() - .optional(), - eventCoding: z.lazy(() => codingSchema).optional(), - eventUri: uriSchema.optional(), - _eventUri: z.lazy(() => elementSchema).optional(), - category: z - .enum(['consequence', 'currency', 'notification']) - .optional(), - _category: z.lazy(() => elementSchema).optional(), - focus: messageDefinitionFocusSchema.array().optional(), - responseRequired: z - .enum(['always', 'never', 'on-error', 'on-success']) - .optional(), - _responseRequired: z.lazy(() => elementSchema).optional(), - allowedResponse: messageDefinitionAllowedResponseSchema - .array() - .optional(), - graph: canonicalSchema.array().optional(), - _graph: z - .lazy(() => elementSchema) - .array() - .optional(), - }) - .refine( - (value) => - [{ names: ['eventCoding', 'eventUri'], required: true }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const messageHeaderDestinationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + _searchType: z.lazy(() => elementSchema).optional(), + binding: operationDefinitionParameterBindingSchema.optional(), + referencedFrom: operationDefinitionParameterReferencedFromSchema .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + part: z + .lazy(() => operationDefinitionParameterSchema) .array() + .min(1) .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - target: z.lazy(() => referenceSchema).optional(), - endpoint: urlSchema, - _endpoint: z.lazy(() => elementSchema).optional(), - receiver: z.lazy(() => referenceSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'use', shadow: '_use' }, + { name: 'min', shadow: '_min' }, + { name: 'max', shadow: '_max' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'targetProfile', shadow: '_targetProfile', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const messageHeaderSourceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const operationDefinitionOverloadSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - software: stringSchema.optional(), - _software: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - contact: z.lazy(() => contactPointSchema).optional(), - endpoint: urlSchema, - _endpoint: z.lazy(() => elementSchema).optional(), - }), - ) - -export const messageHeaderResponseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + parameterName: stringSchema.nullable().array().min(1).optional(), + _parameterName: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - identifier: idSchema, - _identifier: z.lazy(() => elementSchema).optional(), - code: z.enum(['fatal-error', 'ok', 'transient-error']), - _code: z.lazy(() => elementSchema).optional(), - details: z.lazy(() => referenceSchema).optional(), - }), - ) + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'parameterName', shadow: '_parameterName', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const messageHeaderSchema: z.ZodType = z.lazy(() => +export const operationDefinitionParameterBindingSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('MessageHeader'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - eventCoding: z.lazy(() => codingSchema).optional(), - eventUri: uriSchema.optional(), - _eventUri: z.lazy(() => elementSchema).optional(), - destination: messageHeaderDestinationSchema.array().optional(), - sender: z.lazy(() => referenceSchema).optional(), - enterer: z.lazy(() => referenceSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - source: messageHeaderSourceSchema, - responsible: z.lazy(() => referenceSchema).optional(), - reason: z.lazy(() => codeableConceptSchema).optional(), - response: messageHeaderResponseSchema.optional(), - focus: z - .lazy(() => referenceSchema) - .array() + strength: z + .enum(['example', 'extensible', 'preferred', 'required']) .optional(), - definition: canonicalSchema.optional(), - _definition: z.lazy(() => elementSchema).optional(), + _strength: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema.optional(), + _valueSet: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'strength', shadow: '_strength' }, + { name: 'valueSet', shadow: '_valueSet' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [{ names: ['eventCoding', 'eventUri'], required: true }].every( - (group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const metaSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - versionId: idSchema.optional(), - _versionId: z.lazy(() => elementSchema).optional(), - lastUpdated: instantSchema.optional(), - _lastUpdated: z.lazy(() => elementSchema).optional(), - source: uriSchema.optional(), - _source: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.array().optional(), - _profile: z - .lazy(() => elementSchema) - .array() - .optional(), - security: z - .lazy(() => codingSchema) - .array() - .optional(), - tag: z - .lazy(() => codingSchema) - .array() - .optional(), - }), -) - -export const molecularSequenceReferenceSeqSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - chromosome: z.lazy(() => codeableConceptSchema).optional(), - genomeBuild: stringSchema.optional(), - _genomeBuild: z.lazy(() => elementSchema).optional(), - orientation: z.enum(['antisense', 'sense']).optional(), - _orientation: z.lazy(() => elementSchema).optional(), - referenceSeqId: z.lazy(() => codeableConceptSchema).optional(), - referenceSeqPointer: z.lazy(() => referenceSchema).optional(), - referenceSeqString: stringSchema.optional(), - _referenceSeqString: z.lazy(() => elementSchema).optional(), - strand: z.enum(['crick', 'watson']).optional(), - _strand: z.lazy(() => elementSchema).optional(), - windowStart: integerSchema.optional(), - _windowStart: z.lazy(() => elementSchema).optional(), - windowEnd: integerSchema.optional(), - _windowEnd: z.lazy(() => elementSchema).optional(), - }), - ) + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const molecularSequenceVariantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const operationDefinitionParameterReferencedFromSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - start: integerSchema.optional(), - _start: z.lazy(() => elementSchema).optional(), - end: integerSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), - observedAllele: stringSchema.optional(), - _observedAllele: z.lazy(() => elementSchema).optional(), - referenceAllele: stringSchema.optional(), - _referenceAllele: z.lazy(() => elementSchema).optional(), - cigar: stringSchema.optional(), - _cigar: z.lazy(() => elementSchema).optional(), - variantPointer: z.lazy(() => referenceSchema).optional(), - }), - ) + source: stringSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + sourceId: stringSchema.optional(), + _sourceId: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'source', shadow: '_source' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const molecularSequenceQualitySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const operationDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('OperationDefinition'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum(['indel', 'snp', 'unknown']), - _type: z.lazy(() => elementSchema).optional(), - standardSequence: z.lazy(() => codeableConceptSchema).optional(), - start: integerSchema.optional(), - _start: z.lazy(() => elementSchema).optional(), - end: integerSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), - score: z.lazy(() => quantitySchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - truthTP: decimalSchema.optional(), - _truthTP: z.lazy(() => elementSchema).optional(), - queryTP: decimalSchema.optional(), - _queryTP: z.lazy(() => elementSchema).optional(), - truthFN: decimalSchema.optional(), - _truthFN: z.lazy(() => elementSchema).optional(), - queryFP: decimalSchema.optional(), - _queryFP: z.lazy(() => elementSchema).optional(), - gtFP: decimalSchema.optional(), - _gtFP: z.lazy(() => elementSchema).optional(), - precision: decimalSchema.optional(), - _precision: z.lazy(() => elementSchema).optional(), - recall: decimalSchema.optional(), - _recall: z.lazy(() => elementSchema).optional(), - fScore: decimalSchema.optional(), - _fScore: z.lazy(() => elementSchema).optional(), - roc: molecularSequenceQualityRocSchema.optional(), - }), - ) - -export const molecularSequenceRepositorySchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + kind: z.enum(['operation', 'query']).optional(), + _kind: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + affectsState: booleanSchema.optional(), + _affectsState: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + comment: markdownSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + base: canonicalSchema.optional(), + _base: z.lazy(() => elementSchema).optional(), + resource: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + _resource: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - type: z.enum(['directlink', 'login', 'oauth', 'openapi', 'other']), + system: booleanSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + type: booleanSchema.optional(), _type: z.lazy(() => elementSchema).optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - datasetId: stringSchema.optional(), - _datasetId: z.lazy(() => elementSchema).optional(), - variantsetId: stringSchema.optional(), - _variantsetId: z.lazy(() => elementSchema).optional(), - readsetId: stringSchema.optional(), - _readsetId: z.lazy(() => elementSchema).optional(), - }), - ) - -export const molecularSequenceStructureVariantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - variantType: z.lazy(() => codeableConceptSchema).optional(), - exact: booleanSchema.optional(), - _exact: z.lazy(() => elementSchema).optional(), - length: integerSchema.optional(), - _length: z.lazy(() => elementSchema).optional(), - outer: molecularSequenceStructureVariantOuterSchema.optional(), - inner: molecularSequenceStructureVariantInnerSchema.optional(), - }), - ) + instance: booleanSchema.optional(), + _instance: z.lazy(() => elementSchema).optional(), + inputProfile: canonicalSchema.optional(), + _inputProfile: z.lazy(() => elementSchema).optional(), + outputProfile: canonicalSchema.optional(), + _outputProfile: z.lazy(() => elementSchema).optional(), + parameter: operationDefinitionParameterSchema.array().min(1).optional(), + overload: operationDefinitionOverloadSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + { name: 'kind', shadow: '_kind' }, + { name: 'code', shadow: '_code' }, + { name: 'system', shadow: '_system' }, + { name: 'type', shadow: '_type' }, + { name: 'instance', shadow: '_instance' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'resource', shadow: '_resource', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const molecularSequenceQualityRocSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const operationOutcomeIssueSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - score: integerSchema.array().optional(), - _score: z - .lazy(() => elementSchema) - .array() - .optional(), - numTP: integerSchema.array().optional(), - _numTP: z - .lazy(() => elementSchema) - .array() - .optional(), - numFP: integerSchema.array().optional(), - _numFP: z - .lazy(() => elementSchema) - .array() - .optional(), - numFN: integerSchema.array().optional(), - _numFN: z - .lazy(() => elementSchema) - .array() - .optional(), - precision: decimalSchema.array().optional(), - _precision: z - .lazy(() => elementSchema) - .array() + severity: z.enum(['error', 'fatal', 'information', 'warning']).optional(), + _severity: z.lazy(() => elementSchema).optional(), + code: z + .enum([ + 'business-rule', + 'code-invalid', + 'conflict', + 'deleted', + 'duplicate', + 'exception', + 'expired', + 'extension', + 'forbidden', + 'incomplete', + 'informational', + 'invalid', + 'invariant', + 'lock-error', + 'login', + 'multiple-matches', + 'no-store', + 'not-found', + 'not-supported', + 'processing', + 'required', + 'security', + 'structure', + 'suppressed', + 'throttled', + 'timeout', + 'too-costly', + 'too-long', + 'transient', + 'unknown', + 'value', + ]) .optional(), - sensitivity: decimalSchema.array().optional(), - _sensitivity: z + _code: z.lazy(() => elementSchema).optional(), + details: z.lazy(() => codeableConceptSchema).optional(), + diagnostics: stringSchema.optional(), + _diagnostics: z.lazy(() => elementSchema).optional(), + location: stringSchema.nullable().array().min(1).optional(), + _location: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - fMeasure: decimalSchema.array().optional(), - _fMeasure: z + expression: stringSchema.nullable().array().min(1).optional(), + _expression: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'severity', shadow: '_severity' }, + { name: 'code', shadow: '_code' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'location', shadow: '_location', required: false }, + { name: 'expression', shadow: '_expression', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const molecularSequenceStructureVariantOuterSchema: z.ZodType = +export const operationOutcomeSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + resourceType: z.literal('OperationOutcome'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + issue: operationOutcomeIssueSchema.array().min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const organizationContactSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - start: integerSchema.optional(), - _start: z.lazy(() => elementSchema).optional(), - end: integerSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), - }), - ) - -export const molecularSequenceStructureVariantInnerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + purpose: z.lazy(() => codeableConceptSchema).optional(), + name: z.lazy(() => humanNameSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) .array() + .min(1) .optional(), - start: integerSchema.optional(), - _start: z.lazy(() => elementSchema).optional(), - end: integerSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), - }), - ) + address: z.lazy(() => addressSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const molecularSequenceSchema: z.ZodType = z.lazy( +export const organizationSchema: z.ZodType> = z.lazy( () => - z.strictObject({ - resourceType: z.literal('MolecularSequence'), + z + .strictObject({ + resourceType: z.literal('Organization'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + alias: stringSchema.nullable().array().min(1).optional(), + _alias: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + address: z + .lazy(() => addressSchema) + .array() + .min(1) + .optional(), + partOf: z.lazy(() => referenceSchema).optional(), + contact: organizationContactSchema.array().min(1).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'alias', shadow: '_alias', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const organizationAffiliationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('OrganizationAffiliation'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -19681,401 +33640,549 @@ export const molecularSequenceSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - type: z.enum(['aa', 'dna', 'rna']).optional(), - _type: z.lazy(() => elementSchema).optional(), - coordinateSystem: integerSchema, - _coordinateSystem: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema).optional(), - specimen: z.lazy(() => referenceSchema).optional(), - device: z.lazy(() => referenceSchema).optional(), - performer: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - referenceSeq: molecularSequenceReferenceSeqSchema.optional(), - variant: molecularSequenceVariantSchema.array().optional(), - observedSeq: stringSchema.optional(), - _observedSeq: z.lazy(() => elementSchema).optional(), - quality: molecularSequenceQualitySchema.array().optional(), - readCoverage: integerSchema.optional(), - _readCoverage: z.lazy(() => elementSchema).optional(), - repository: molecularSequenceRepositorySchema.array().optional(), - pointer: z - .lazy(() => referenceSchema) - .array() - .optional(), - structureVariant: molecularSequenceStructureVariantSchema - .array() - .optional(), - }), -) - -export const moneySchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - currency: codeSchema.optional(), - _currency: z.lazy(() => elementSchema).optional(), - }), -) - -export const namingSystemUniqueIdSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum(['oid', 'other', 'uri', 'uuid']), - _type: z.lazy(() => elementSchema).optional(), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - preferred: booleanSchema.optional(), - _preferred: z.lazy(() => elementSchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), - ) - -export const namingSystemSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('NamingSystem'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - kind: z.enum(['codesystem', 'identifier', 'root']), - _kind: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema, - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - responsible: stringSchema.optional(), - _responsible: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - uniqueId: namingSystemUniqueIdSchema.array().min(1), - }), -) - -export const narrativeSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - status: z.enum(['additional', 'empty', 'extensions', 'generated']), - _status: z.lazy(() => elementSchema).optional(), - div: xhtmlSchema, - _div: z.lazy(() => elementSchema).optional(), - }), -) - -export const nutritionOrderOralDietSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + participatingOrganization: z.lazy(() => referenceSchema).optional(), + network: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - type: z + code: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - schedule: z - .lazy(() => timingSchema) + specialty: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - nutrient: nutritionOrderOralDietNutrientSchema.array().optional(), - texture: nutritionOrderOralDietTextureSchema.array().optional(), - fluidConsistencyType: z - .lazy(() => codeableConceptSchema) + location: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - instruction: stringSchema.optional(), - _instruction: z.lazy(() => elementSchema).optional(), - }), - ) - -export const nutritionOrderSupplementSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + healthcareService: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + telecom: z + .lazy(() => contactPointSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - productName: stringSchema.optional(), - _productName: z.lazy(() => elementSchema).optional(), - schedule: z - .lazy(() => timingSchema) + endpoint: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - instruction: stringSchema.optional(), - _instruction: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const nutritionOrderEnteralFormulaSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const parameterDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - baseFormulaType: z.lazy(() => codeableConceptSchema).optional(), - baseFormulaProductName: stringSchema.optional(), - _baseFormulaProductName: z.lazy(() => elementSchema).optional(), - additiveType: z.lazy(() => codeableConceptSchema).optional(), - additiveProductName: stringSchema.optional(), - _additiveProductName: z.lazy(() => elementSchema).optional(), - caloricDensity: z.lazy(() => quantitySchema).optional(), - routeofAdministration: z.lazy(() => codeableConceptSchema).optional(), - administration: nutritionOrderEnteralFormulaAdministrationSchema - .array() + name: codeSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + use: z.enum(['in', 'out']).optional(), + _use: z.lazy(() => elementSchema).optional(), + min: integerSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Any', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'CodeableConcept', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'EffectEvidenceSynthesis', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'Reference', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceAmount', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'Type', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]) .optional(), - maxVolumeToDeliver: z.lazy(() => quantitySchema).optional(), - administrationInstruction: stringSchema.optional(), - _administrationInstruction: z.lazy(() => elementSchema).optional(), - }), - ) + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'use', shadow: '_use' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const nutritionOrderOralDietNutrientSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const parametersParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifier: z.lazy(() => codeableConceptSchema).optional(), - amount: z.lazy(() => quantitySchema).optional(), - }), - ) - -export const nutritionOrderOralDietTextureSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + valueMeta: z.lazy(() => metaSchema).optional(), + resource: z.lazy(() => resourceSchema).optional(), + part: z + .lazy(() => parametersParameterSchema) .array() + .min(1) .optional(), - modifier: z.lazy(() => codeableConceptSchema).optional(), - foodType: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueBase64Binary', '_valueBase64Binary'], + ['valueBoolean', '_valueBoolean'], + ['valueCanonical', '_valueCanonical'], + ['valueCode', '_valueCode'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueDecimal', '_valueDecimal'], + ['valueId', '_valueId'], + ['valueInstant', '_valueInstant'], + ['valueInteger', '_valueInteger'], + ['valueMarkdown', '_valueMarkdown'], + ['valueOid', '_valueOid'], + ['valuePositiveInt', '_valuePositiveInt'], + ['valueString', '_valueString'], + ['valueTime', '_valueTime'], + ['valueUnsignedInt', '_valueUnsignedInt'], + ['valueUri', '_valueUri'], + ['valueUrl', '_valueUrl'], + ['valueUuid', '_valueUuid'], + ['valueAddress'], + ['valueAge'], + ['valueAnnotation'], + ['valueAttachment'], + ['valueCodeableConcept'], + ['valueCoding'], + ['valueContactPoint'], + ['valueCount'], + ['valueDistance'], + ['valueDuration'], + ['valueHumanName'], + ['valueIdentifier'], + ['valueMoney'], + ['valuePeriod'], + ['valueQuantity'], + ['valueRange'], + ['valueRatio'], + ['valueReference'], + ['valueSampledData'], + ['valueSignature'], + ['valueTiming'], + ['valueContactDetail'], + ['valueContributor'], + ['valueDataRequirement'], + ['valueExpression'], + ['valueParameterDefinition'], + ['valueRelatedArtifact'], + ['valueTriggerDefinition'], + ['valueUsageContext'], + ['valueDosage'], + ['valueMeta'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const nutritionOrderEnteralFormulaAdministrationSchema: z.ZodType = - z.lazy(() => +export const parametersSchema: z.ZodType> = z.lazy( + () => z .strictObject({ + resourceType: z.literal('Parameters'), id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - schedule: z.lazy(() => timingSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - rateQuantity: z.lazy(() => quantitySchema).optional(), - rateRatio: z.lazy(() => ratioSchema).optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + parameter: parametersParameterSchema.array().min(1).optional(), }) .refine( - (value) => - [{ names: ['rateQuantity', 'rateRatio'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> -export const nutritionOrderSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('NutritionOrder'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiates: uriSchema.array().optional(), - _instantiates: z - .lazy(() => elementSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'completed', - 'draft', - 'entered-in-error', - 'on-hold', - 'revoked', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - intent: z.enum([ - 'directive', - 'filler-order', - 'instance-order', - 'option', - 'order', - 'original-order', - 'plan', - 'proposal', - 'reflex-order', - ]), - _intent: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - dateTime: dateTimeSchema, - _dateTime: z.lazy(() => elementSchema).optional(), - orderer: z.lazy(() => referenceSchema).optional(), - allergyIntolerance: z - .lazy(() => referenceSchema) - .array() - .optional(), - foodPreferenceModifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - excludeFoodModifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - oralDiet: nutritionOrderOralDietSchema.optional(), - supplement: nutritionOrderSupplementSchema.array().optional(), - enteralFormula: nutritionOrderEnteralFormulaSchema.optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }), -) - -export const observationReferenceRangeSchema: z.ZodType = +export const patientContactSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -20083,127 +34190,113 @@ export const observationReferenceRangeSchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - low: z.lazy(() => quantitySchema).optional(), - high: z.lazy(() => quantitySchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - appliesTo: z + relationship: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - age: z.lazy(() => rangeSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), + name: z.lazy(() => humanNameSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + address: z.lazy(() => addressSchema).optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), }) .refine( - (value) => { - const range = value as { - low?: unknown - high?: unknown - text?: unknown - } - return ( - range.low !== undefined || - range.high !== undefined || - range.text !== undefined - ) + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', }, - { message: 'obs-3: a reference range states a low, a high, or text.' }, ), - ) + ) as unknown as z.ZodType> -export const observationComponentSchema: z.ZodType = - z.lazy(() => +export const patientCommunicationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + language: z.lazy(() => codeableConceptSchema), + preferred: booleanSchema.optional(), + _preferred: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const patientLinkSchema: z.ZodType> = z.lazy( + () => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueRatio: z.lazy(() => ratioSchema).optional(), - valueSampledData: z.lazy(() => sampledDataSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valuePeriod: z.lazy(() => periodSchema).optional(), - dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), - interpretation: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - referenceRange: z - .lazy(() => observationReferenceRangeSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueQuantity', - 'valueCodeableConcept', - 'valueString', - 'valueBoolean', - 'valueInteger', - 'valueRange', - 'valueRatio', - 'valueSampledData', - 'valueTime', - 'valueDateTime', - 'valuePeriod', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ) + .array() + .min(1) + .optional(), + other: z.lazy(() => referenceSchema), + type: z + .enum(['refer', 'replaced-by', 'replaces', 'seealso']) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) .refine( - (value) => { - const observation = value as Record - const hasValue = Object.keys(observation).some( - (key) => key.startsWith('value') && observation[key] !== undefined, - ) - return observation.dataAbsentReason === undefined || !hasValue - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: 'obs-6: dataAbsentReason is present only when no value is.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> -export const observationSchema: z.ZodType = z.lazy(() => +export const patientSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Observation'), + resourceType: z.literal('Patient'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -20215,132 +34308,90 @@ export const observationSchema: z.ZodType = z.lazy(() => contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'amended', - 'cancelled', - 'corrected', - 'entered-in-error', - 'final', - 'preliminary', - 'registered', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - subject: z.lazy(() => referenceSchema).optional(), - focus: z - .lazy(() => referenceSchema) - .array() - .optional(), - encounter: z.lazy(() => referenceSchema).optional(), - effectiveDateTime: dateTimeSchema.optional(), - _effectiveDateTime: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - effectiveTiming: z.lazy(() => timingSchema).optional(), - effectiveInstant: instantSchema.optional(), - _effectiveInstant: z.lazy(() => elementSchema).optional(), - issued: instantSchema.optional(), - _issued: z.lazy(() => elementSchema).optional(), - performer: z - .lazy(() => referenceSchema) + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + name: z + .lazy(() => humanNameSchema) .array() + .min(1) .optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueRatio: z.lazy(() => ratioSchema).optional(), - valueSampledData: z.lazy(() => sampledDataSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valuePeriod: z.lazy(() => periodSchema).optional(), - dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), - interpretation: z - .lazy(() => codeableConceptSchema) + telecom: z + .lazy(() => contactPointSchema) .array() + .min(1) .optional(), - note: z - .lazy(() => annotationSchema) + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + deceasedBoolean: booleanSchema.optional(), + _deceasedBoolean: z.lazy(() => elementSchema).optional(), + deceasedDateTime: dateTimeSchema.optional(), + _deceasedDateTime: z.lazy(() => elementSchema).optional(), + address: z + .lazy(() => addressSchema) .array() + .min(1) .optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - specimen: z.lazy(() => referenceSchema).optional(), - device: z.lazy(() => referenceSchema).optional(), - referenceRange: observationReferenceRangeSchema.array().optional(), - hasMember: z - .lazy(() => referenceSchema) + maritalStatus: z.lazy(() => codeableConceptSchema).optional(), + multipleBirthBoolean: booleanSchema.optional(), + _multipleBirthBoolean: z.lazy(() => elementSchema).optional(), + multipleBirthInteger: integerSchema.optional(), + _multipleBirthInteger: z.lazy(() => elementSchema).optional(), + photo: z + .lazy(() => attachmentSchema) .array() + .min(1) .optional(), - derivedFrom: z + contact: patientContactSchema.array().min(1).optional(), + communication: patientCommunicationSchema.array().min(1).optional(), + generalPractitioner: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - component: observationComponentSchema.array().optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + link: patientLinkSchema.array().min(1).optional(), }) .refine( (value) => [ { - names: [ - 'effectiveDateTime', - 'effectivePeriod', - 'effectiveTiming', - 'effectiveInstant', + alternatives: [ + ['deceasedBoolean', '_deceasedBoolean'], + ['deceasedDateTime', '_deceasedDateTime'], ], required: false, }, { - names: [ - 'valueQuantity', - 'valueCodeableConcept', - 'valueString', - 'valueBoolean', - 'valueInteger', - 'valueRange', - 'valueRatio', - 'valueSampledData', - 'valueTime', - 'valueDateTime', - 'valuePeriod', + alternatives: [ + ['multipleBirthBoolean', '_multipleBirthBoolean'], + ['multipleBirthInteger', '_multipleBirthInteger'], ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -20350,71 +34401,155 @@ export const observationSchema: z.ZodType = z.lazy(() => }, ) .refine( - (value) => { - const observation = value as Record - const hasValue = Object.keys(observation).some( - (key) => key.startsWith('value') && observation[key] !== undefined, - ) - return observation.dataAbsentReason === undefined || !hasValue - }, - { message: 'obs-6: dataAbsentReason is present only when no value is.' }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const observationDefinitionQuantitativeDetailsSchema: z.ZodType = +export const paymentNoticeSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + resourceType: z.literal('PaymentNotice'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + request: z.lazy(() => referenceSchema).optional(), + response: z.lazy(() => referenceSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + provider: z.lazy(() => referenceSchema).optional(), + payment: z.lazy(() => referenceSchema), + paymentDate: dateSchema.optional(), + _paymentDate: z.lazy(() => elementSchema).optional(), + payee: z.lazy(() => referenceSchema).optional(), + recipient: z.lazy(() => referenceSchema), + amount: z.lazy(() => moneySchema), + paymentStatus: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'created', shadow: '_created' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const paymentReconciliationDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - customaryUnit: z.lazy(() => codeableConceptSchema).optional(), - unit: z.lazy(() => codeableConceptSchema).optional(), - conversionFactor: decimalSchema.optional(), - _conversionFactor: z.lazy(() => elementSchema).optional(), - decimalPrecision: integerSchema.optional(), - _decimalPrecision: z.lazy(() => elementSchema).optional(), - }), - ) + identifier: z.lazy(() => identifierSchema).optional(), + predecessor: z.lazy(() => identifierSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + request: z.lazy(() => referenceSchema).optional(), + submitter: z.lazy(() => referenceSchema).optional(), + response: z.lazy(() => referenceSchema).optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + responsible: z.lazy(() => referenceSchema).optional(), + payee: z.lazy(() => referenceSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const observationDefinitionQualifiedIntervalSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const paymentReconciliationProcessNoteSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - category: z.enum(['absolute', 'critical', 'reference']).optional(), - _category: z.lazy(() => elementSchema).optional(), - range: z.lazy(() => rangeSchema).optional(), - context: z.lazy(() => codeableConceptSchema).optional(), - appliesTo: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), - _gender: z.lazy(() => elementSchema).optional(), - age: z.lazy(() => rangeSchema).optional(), - gestationalAge: z.lazy(() => rangeSchema).optional(), - condition: stringSchema.optional(), - _condition: z.lazy(() => elementSchema).optional(), - }), - ) + type: z.enum(['display', 'print', 'printoper']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const observationDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('ObservationDefinition'), +export const paymentReconciliationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('PaymentReconciliation'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -20422,701 +34557,1332 @@ export const observationDefinitionSchema: z.ZodType = _implicitRules: z.lazy(() => elementSchema).optional(), language: codeSchema.optional(), _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - permittedDataType: z - .enum([ - 'CodeableConcept', - 'Period', - 'Quantity', - 'Range', - 'Ratio', - 'SampledData', - 'boolean', - 'dateTime', - 'integer', - 'string', - 'time', - ]) - .array() - .optional(), - _permittedDataType: z - .lazy(() => elementSchema) - .array() - .optional(), - multipleResultsAllowed: booleanSchema.optional(), - _multipleResultsAllowed: z.lazy(() => elementSchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - preferredReportName: stringSchema.optional(), - _preferredReportName: z.lazy(() => elementSchema).optional(), - quantitativeDetails: - observationDefinitionQuantitativeDetailsSchema.optional(), - qualifiedInterval: observationDefinitionQualifiedIntervalSchema - .array() - .optional(), - validCodedValueSet: z.lazy(() => referenceSchema).optional(), - normalCodedValueSet: z.lazy(() => referenceSchema).optional(), - abnormalCodedValueSet: z.lazy(() => referenceSchema).optional(), - criticalCodedValueSet: z.lazy(() => referenceSchema).optional(), - }), - ) - -export const operationDefinitionParameterSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: codeSchema, - _name: z.lazy(() => elementSchema).optional(), - use: z.enum(['in', 'out']), - _use: z.lazy(() => elementSchema).optional(), - min: integerSchema, - _min: z.lazy(() => elementSchema).optional(), - max: stringSchema, - _max: z.lazy(() => elementSchema).optional(), - documentation: stringSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - type: z - .enum([ - 'Account', - 'ActivityDefinition', - 'Address', - 'AdverseEvent', - 'Age', - 'AllergyIntolerance', - 'Annotation', - 'Any', - 'Appointment', - 'AppointmentResponse', - 'Attachment', - 'AuditEvent', - 'BackboneElement', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'CodeSystem', - 'CodeableConcept', - 'Coding', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'ContactDetail', - 'ContactPoint', - 'Contract', - 'Contributor', - 'Count', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DataRequirement', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'Distance', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Dosage', - 'Duration', - 'EffectEvidenceSynthesis', - 'Element', - 'ElementDefinition', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'Expression', - 'Extension', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'HumanName', - 'Identifier', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'MarketingStatus', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProduct', - 'MedicinalProductAuthorization', - 'MedicinalProductContraindication', - 'MedicinalProductIndication', - 'MedicinalProductIngredient', - 'MedicinalProductInteraction', - 'MedicinalProductManufactured', - 'MedicinalProductPackaged', - 'MedicinalProductPharmaceutical', - 'MedicinalProductUndesirableEffect', - 'MessageDefinition', - 'MessageHeader', - 'Meta', - 'MolecularSequence', - 'Money', - 'MoneyQuantity', - 'NamingSystem', - 'Narrative', - 'NutritionOrder', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'ParameterDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Period', - 'Person', - 'PlanDefinition', - 'Population', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'ProdCharacteristic', - 'ProductShelfLife', - 'Provenance', - 'Quantity', - 'Questionnaire', - 'QuestionnaireResponse', - 'Range', - 'Ratio', - 'Reference', - 'RelatedArtifact', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'RiskEvidenceSynthesis', - 'SampledData', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Signature', - 'SimpleQuantity', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'Substance', - 'SubstanceAmount', - 'SubstanceNucleicAcid', - 'SubstancePolymer', - 'SubstanceProtein', - 'SubstanceReferenceInformation', - 'SubstanceSourceMaterial', - 'SubstanceSpecification', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'Timing', - 'TriggerDefinition', - 'Type', - 'UsageContext', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - 'base64Binary', - 'boolean', - 'canonical', - 'code', - 'date', - 'dateTime', - 'decimal', - 'id', - 'instant', - 'integer', - 'markdown', - 'oid', - 'positiveInt', - 'string', - 'time', - 'unsignedInt', - 'uri', - 'url', - 'uuid', - 'xhtml', - ]) + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + paymentIssuer: z.lazy(() => referenceSchema).optional(), + request: z.lazy(() => referenceSchema).optional(), + requestor: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + paymentDate: dateSchema.optional(), + _paymentDate: z.lazy(() => elementSchema).optional(), + paymentAmount: z.lazy(() => moneySchema), + paymentIdentifier: z.lazy(() => identifierSchema).optional(), + detail: paymentReconciliationDetailSchema.array().min(1).optional(), + formCode: z.lazy(() => codeableConceptSchema).optional(), + processNote: paymentReconciliationProcessNoteSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'created', shadow: '_created' }, + { name: 'paymentDate', shadow: '_paymentDate' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const periodSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) .optional(), - _type: z.lazy(() => elementSchema).optional(), - targetProfile: canonicalSchema.array().optional(), - _targetProfile: z + start: dateTimeSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: dateTimeSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const period = value as { start?: string; end?: string } + if (period.start === undefined || period.end === undefined) return true + const ordering = compareFhirDateTimes(period.start, period.end) + // A malformed endpoint is a malformed date, not an ordering fault; the release says + // as much with start.hasValue(). Reporting both would name the wrong problem. + if (ordering === undefined) return true + return ordering <= 0 + }, + { message: 'Period.start must not be later than Period.end.' }, + ), +) as unknown as z.ZodType> + +export const personLinkSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + target: z.lazy(() => referenceSchema), + assurance: z.enum(['level1', 'level2', 'level3', 'level4']).optional(), + _assurance: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const personSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Person'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + name: z + .lazy(() => humanNameSchema) + .array() + .min(1) + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + address: z + .lazy(() => addressSchema) + .array() + .min(1) + .optional(), + photo: z.lazy(() => attachmentSchema).optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + link: personLinkSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const planDefinitionGoalSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + description: z.lazy(() => codeableConceptSchema), + priority: z.lazy(() => codeableConceptSchema).optional(), + start: z.lazy(() => codeableConceptSchema).optional(), + addresses: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + documentation: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + target: planDefinitionGoalTargetSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const planDefinitionActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + prefix: stringSchema.optional(), + _prefix: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + textEquivalent: stringSchema.optional(), + _textEquivalent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reason: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + documentation: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + goalId: idSchema.nullable().array().min(1).optional(), + _goalId: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - searchType: z + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + trigger: z + .lazy(() => triggerDefinitionSchema) + .array() + .min(1) + .optional(), + condition: planDefinitionActionConditionSchema.array().min(1).optional(), + input: z + .lazy(() => dataRequirementSchema) + .array() + .min(1) + .optional(), + output: z + .lazy(() => dataRequirementSchema) + .array() + .min(1) + .optional(), + relatedAction: planDefinitionActionRelatedActionSchema + .array() + .min(1) + .optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + timingAge: z.lazy(() => ageSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingDuration: z.lazy(() => durationSchema).optional(), + timingRange: z.lazy(() => rangeSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + participant: planDefinitionActionParticipantSchema + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + groupingBehavior: z + .enum(['logical-group', 'sentence-group', 'visual-group']) + .optional(), + _groupingBehavior: z.lazy(() => elementSchema).optional(), + selectionBehavior: z .enum([ - 'composite', - 'date', - 'number', - 'quantity', - 'reference', - 'special', - 'string', - 'token', - 'uri', + 'all', + 'all-or-none', + 'any', + 'at-most-one', + 'exactly-one', + 'one-or-more', ]) .optional(), - _searchType: z.lazy(() => elementSchema).optional(), - binding: operationDefinitionParameterBindingSchema.optional(), - referencedFrom: operationDefinitionParameterReferencedFromSchema + _selectionBehavior: z.lazy(() => elementSchema).optional(), + requiredBehavior: z + .enum(['could', 'must', 'must-unless-documented']) + .optional(), + _requiredBehavior: z.lazy(() => elementSchema).optional(), + precheckBehavior: z.enum(['no', 'yes']).optional(), + _precheckBehavior: z.lazy(() => elementSchema).optional(), + cardinalityBehavior: z.enum(['multiple', 'single']).optional(), + _cardinalityBehavior: z.lazy(() => elementSchema).optional(), + definitionCanonical: canonicalSchema.optional(), + _definitionCanonical: z.lazy(() => elementSchema).optional(), + definitionUri: uriSchema.optional(), + _definitionUri: z.lazy(() => elementSchema).optional(), + transform: canonicalSchema.optional(), + _transform: z.lazy(() => elementSchema).optional(), + dynamicValue: planDefinitionActionDynamicValueSchema + .array() + .min(1) + .optional(), + action: z + .lazy(() => planDefinitionActionSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['subjectCodeableConcept'], ['subjectReference']], + required: false, + }, + { + alternatives: [ + ['timingDateTime', '_timingDateTime'], + ['timingAge'], + ['timingPeriod'], + ['timingDuration'], + ['timingRange'], + ['timingTiming'], + ], + required: false, + }, + { + alternatives: [ + ['definitionCanonical', '_definitionCanonical'], + ['definitionUri', '_definitionUri'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'goalId', shadow: '_goalId', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const planDefinitionGoalTargetSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + measure: z.lazy(() => codeableConceptSchema).optional(), + detailQuantity: z.lazy(() => quantitySchema).optional(), + detailRange: z.lazy(() => rangeSchema).optional(), + detailCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + due: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['detailQuantity'], + ['detailRange'], + ['detailCodeableConcept'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const planDefinitionActionConditionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - part: z - .lazy(() => operationDefinitionParameterSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), - ) + kind: z.enum(['applicability', 'start', 'stop']).optional(), + _kind: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'kind', shadow: '_kind' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const operationDefinitionOverloadSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const planDefinitionActionRelatedActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - parameterName: stringSchema.array().optional(), - _parameterName: z - .lazy(() => elementSchema) - .array() + actionId: idSchema.optional(), + _actionId: z.lazy(() => elementSchema).optional(), + relationship: z + .enum([ + 'after', + 'after-end', + 'after-start', + 'before', + 'before-end', + 'before-start', + 'concurrent', + 'concurrent-with-end', + 'concurrent-with-start', + ]) .optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - }), - ) + _relationship: z.lazy(() => elementSchema).optional(), + offsetDuration: z.lazy(() => durationSchema).optional(), + offsetRange: z.lazy(() => rangeSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['offsetDuration'], ['offsetRange']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'actionId', shadow: '_actionId' }, + { name: 'relationship', shadow: '_relationship' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const operationDefinitionParameterBindingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const planDefinitionActionParticipantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - strength: z.enum(['example', 'extensible', 'preferred', 'required']), - _strength: z.lazy(() => elementSchema).optional(), - valueSet: canonicalSchema, - _valueSet: z.lazy(() => elementSchema).optional(), - }), - ) + type: z + .enum(['device', 'patient', 'practitioner', 'related-person']) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const operationDefinitionParameterReferencedFromSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const planDefinitionActionDynamicValueSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - source: stringSchema, - _source: z.lazy(() => elementSchema).optional(), - sourceId: stringSchema.optional(), - _sourceId: z.lazy(() => elementSchema).optional(), - }), - ) + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const operationDefinitionSchema: z.ZodType = +export const planDefinitionSchema: z.ZodType> = z.lazy(() => - z.strictObject({ - resourceType: z.literal('OperationDefinition'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), + z + .strictObject({ + resourceType: z.literal('PlanDefinition'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + library: canonicalSchema.nullable().array().min(1).optional(), + _library: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + goal: planDefinitionGoalSchema.array().min(1).optional(), + action: planDefinitionActionSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['subjectCodeableConcept'], ['subjectReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'library', shadow: '_library', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const populationSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + ageRange: z.lazy(() => rangeSchema).optional(), + ageCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + gender: z.lazy(() => codeableConceptSchema).optional(), + race: z.lazy(() => codeableConceptSchema).optional(), + physiologicalCondition: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['ageRange'], ['ageCodeableConcept']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const practitionerQualificationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - kind: z.enum(['operation', 'query']), - _kind: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) + code: z.lazy(() => codeableConceptSchema), + period: z.lazy(() => periodSchema).optional(), + issuer: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const practitionerSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('Practitioner'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + name: z + .lazy(() => humanNameSchema) + .array() + .min(1) + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + address: z + .lazy(() => addressSchema) + .array() + .min(1) + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + photo: z + .lazy(() => attachmentSchema) + .array() + .min(1) + .optional(), + qualification: practitionerQualificationSchema + .array() + .min(1) + .optional(), + communication: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const practitionerRoleAvailableTimeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - affectsState: booleanSchema.optional(), - _affectsState: z.lazy(() => elementSchema).optional(), - code: codeSchema, - _code: z.lazy(() => elementSchema).optional(), - comment: markdownSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - base: canonicalSchema.optional(), - _base: z.lazy(() => elementSchema).optional(), - resource: z - .enum([ - 'Account', - 'ActivityDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'EffectEvidenceSynthesis', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProduct', - 'MedicinalProductAuthorization', - 'MedicinalProductContraindication', - 'MedicinalProductIndication', - 'MedicinalProductIngredient', - 'MedicinalProductInteraction', - 'MedicinalProductManufactured', - 'MedicinalProductPackaged', - 'MedicinalProductPharmaceutical', - 'MedicinalProductUndesirableEffect', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'RiskEvidenceSynthesis', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'Substance', - 'SubstanceNucleicAcid', - 'SubstancePolymer', - 'SubstanceProtein', - 'SubstanceReferenceInformation', - 'SubstanceSourceMaterial', - 'SubstanceSpecification', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]) + daysOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .nullable() + .array() + .min(1) + .optional(), + _daysOfWeek: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - _resource: z - .lazy(() => elementSchema) + allDay: booleanSchema.optional(), + _allDay: z.lazy(() => elementSchema).optional(), + availableStartTime: timeSchema.optional(), + _availableStartTime: z.lazy(() => elementSchema).optional(), + availableEndTime: timeSchema.optional(), + _availableEndTime: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'daysOfWeek', shadow: '_daysOfWeek', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const practitionerRoleNotAvailableSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - system: booleanSchema, - _system: z.lazy(() => elementSchema).optional(), - type: booleanSchema, - _type: z.lazy(() => elementSchema).optional(), - instance: booleanSchema, - _instance: z.lazy(() => elementSchema).optional(), - inputProfile: canonicalSchema.optional(), - _inputProfile: z.lazy(() => elementSchema).optional(), - outputProfile: canonicalSchema.optional(), - _outputProfile: z.lazy(() => elementSchema).optional(), - parameter: operationDefinitionParameterSchema.array().optional(), - overload: operationDefinitionOverloadSchema.array().optional(), - }), - ) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + during: z.lazy(() => periodSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'description', shadow: '_description' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const operationOutcomeIssueSchema: z.ZodType = +export const practitionerRoleSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + resourceType: z.literal('PractitionerRole'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + practitioner: z.lazy(() => referenceSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + location: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + healthcareService: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + availableTime: practitionerRoleAvailableTimeSchema + .array() + .min(1) + .optional(), + notAvailable: practitionerRoleNotAvailableSchema + .array() + .min(1) + .optional(), + availabilityExceptions: stringSchema.optional(), + _availabilityExceptions: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const procedurePerformerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - severity: z.enum(['error', 'fatal', 'information', 'warning']), - _severity: z.lazy(() => elementSchema).optional(), - code: z.enum([ - 'business-rule', - 'code-invalid', - 'conflict', - 'deleted', - 'duplicate', - 'exception', - 'expired', - 'extension', - 'forbidden', - 'incomplete', - 'informational', - 'invalid', - 'invariant', - 'lock-error', - 'login', - 'multiple-matches', - 'no-store', - 'not-found', - 'not-supported', - 'processing', - 'required', - 'security', - 'structure', - 'suppressed', - 'throttled', - 'timeout', - 'too-costly', - 'too-long', - 'transient', - 'unknown', - 'value', - ]), - _code: z.lazy(() => elementSchema).optional(), - details: z.lazy(() => codeableConceptSchema).optional(), - diagnostics: stringSchema.optional(), - _diagnostics: z.lazy(() => elementSchema).optional(), - location: stringSchema.array().optional(), - _location: z - .lazy(() => elementSchema) + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const procedureFocalDeviceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - expression: stringSchema.array().optional(), - _expression: z - .lazy(() => elementSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), - ) + action: z.lazy(() => codeableConceptSchema).optional(), + manipulated: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const operationOutcomeSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('OperationOutcome'), +export const procedureSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Procedure'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -21128,402 +35894,365 @@ export const operationOutcomeSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - issue: operationOutcomeIssueSchema.array().min(1), - }), -) - -export const organizationContactSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + instantiatesCanonical: canonicalSchema + .nullable() .array() + .min(1) .optional(), - purpose: z.lazy(() => codeableConceptSchema).optional(), - name: z.lazy(() => humanNameSchema).optional(), - telecom: z - .lazy(() => contactPointSchema) + _instantiatesCanonical: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - address: z.lazy(() => addressSchema).optional(), - }), - ) + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + performedDateTime: dateTimeSchema.optional(), + _performedDateTime: z.lazy(() => elementSchema).optional(), + performedPeriod: z.lazy(() => periodSchema).optional(), + performedString: stringSchema.optional(), + _performedString: z.lazy(() => elementSchema).optional(), + performedAge: z.lazy(() => ageSchema).optional(), + performedRange: z.lazy(() => rangeSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + asserter: z.lazy(() => referenceSchema).optional(), + performer: procedurePerformerSchema.array().min(1).optional(), + location: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + bodySite: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + outcome: z.lazy(() => codeableConceptSchema).optional(), + report: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + complication: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + complicationDetail: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + followUp: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + focalDevice: procedureFocalDeviceSchema.array().min(1).optional(), + usedReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + usedCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['performedDateTime', '_performedDateTime'], + ['performedPeriod'], + ['performedString', '_performedString'], + ['performedAge'], + ['performedRange'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const organizationSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Organization'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - alias: stringSchema.array().optional(), - _alias: z - .lazy(() => elementSchema) - .array() - .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - address: z - .lazy(() => addressSchema) - .array() - .optional(), - partOf: z.lazy(() => referenceSchema).optional(), - contact: organizationContactSchema.array().optional(), - endpoint: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) - -export const organizationAffiliationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('OrganizationAffiliation'), +export const prodCharacteristicSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - organization: z.lazy(() => referenceSchema).optional(), - participatingOrganization: z.lazy(() => referenceSchema).optional(), - network: z - .lazy(() => referenceSchema) - .array() - .optional(), - code: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - specialty: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - location: z - .lazy(() => referenceSchema) - .array() - .optional(), - healthcareService: z - .lazy(() => referenceSchema) + height: z.lazy(() => quantitySchema).optional(), + width: z.lazy(() => quantitySchema).optional(), + depth: z.lazy(() => quantitySchema).optional(), + weight: z.lazy(() => quantitySchema).optional(), + nominalVolume: z.lazy(() => quantitySchema).optional(), + externalDiameter: z.lazy(() => quantitySchema).optional(), + shape: stringSchema.optional(), + _shape: z.lazy(() => elementSchema).optional(), + color: stringSchema.nullable().array().min(1).optional(), + _color: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - telecom: z - .lazy(() => contactPointSchema) + imprint: stringSchema.nullable().array().min(1).optional(), + _imprint: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - endpoint: z - .lazy(() => referenceSchema) + image: z + .lazy(() => attachmentSchema) .array() + .min(1) .optional(), - }), - ) + scoring: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'color', shadow: '_color', required: false }, + { name: 'imprint', shadow: '_imprint', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const parameterDefinitionSchema: z.ZodType = +export const productShelfLifeSchema: z.ZodType> = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: codeSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - use: z.enum(['in', 'out']), - _use: z.lazy(() => elementSchema).optional(), - min: integerSchema.optional(), - _min: z.lazy(() => elementSchema).optional(), - max: stringSchema.optional(), - _max: z.lazy(() => elementSchema).optional(), - documentation: stringSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - type: z.enum([ - 'Account', - 'ActivityDefinition', - 'Address', - 'AdverseEvent', - 'Age', - 'AllergyIntolerance', - 'Annotation', - 'Any', - 'Appointment', - 'AppointmentResponse', - 'Attachment', - 'AuditEvent', - 'BackboneElement', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'CodeSystem', - 'CodeableConcept', - 'Coding', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'ContactDetail', - 'ContactPoint', - 'Contract', - 'Contributor', - 'Count', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DataRequirement', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'Distance', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Dosage', - 'Duration', - 'EffectEvidenceSynthesis', - 'Element', - 'ElementDefinition', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'Expression', - 'Extension', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'HumanName', - 'Identifier', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'MarketingStatus', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProduct', - 'MedicinalProductAuthorization', - 'MedicinalProductContraindication', - 'MedicinalProductIndication', - 'MedicinalProductIngredient', - 'MedicinalProductInteraction', - 'MedicinalProductManufactured', - 'MedicinalProductPackaged', - 'MedicinalProductPharmaceutical', - 'MedicinalProductUndesirableEffect', - 'MessageDefinition', - 'MessageHeader', - 'Meta', - 'MolecularSequence', - 'Money', - 'MoneyQuantity', - 'NamingSystem', - 'Narrative', - 'NutritionOrder', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'ParameterDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Period', - 'Person', - 'PlanDefinition', - 'Population', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'ProdCharacteristic', - 'ProductShelfLife', - 'Provenance', - 'Quantity', - 'Questionnaire', - 'QuestionnaireResponse', - 'Range', - 'Ratio', - 'Reference', - 'RelatedArtifact', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'RiskEvidenceSynthesis', - 'SampledData', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Signature', - 'SimpleQuantity', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'Substance', - 'SubstanceAmount', - 'SubstanceNucleicAcid', - 'SubstancePolymer', - 'SubstanceProtein', - 'SubstanceReferenceInformation', - 'SubstanceSourceMaterial', - 'SubstanceSpecification', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'Timing', - 'TriggerDefinition', - 'Type', - 'UsageContext', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - 'base64Binary', - 'boolean', - 'canonical', - 'code', - 'date', - 'dateTime', - 'decimal', - 'id', - 'instant', - 'integer', - 'markdown', - 'oid', - 'positiveInt', - 'string', - 'time', - 'unsignedInt', - 'uri', - 'url', - 'uuid', - 'xhtml', - ]), - _type: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.optional(), - _profile: z.lazy(() => elementSchema).optional(), - }), - ) + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + period: z.lazy(() => quantitySchema), + specialPrecautionsForStorage: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const parametersParameterSchema: z.ZodType = +export const provenanceAgentSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -21531,153 +36260,155 @@ export const parametersParameterSchema: z.ZodType = extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueCanonical: canonicalSchema.optional(), - _valueCanonical: z.lazy(() => elementSchema).optional(), - valueCode: codeSchema.optional(), - _valueCode: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueId: idSchema.optional(), - _valueId: z.lazy(() => elementSchema).optional(), - valueInstant: instantSchema.optional(), - _valueInstant: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueMarkdown: markdownSchema.optional(), - _valueMarkdown: z.lazy(() => elementSchema).optional(), - valueOid: oidSchema.optional(), - _valueOid: z.lazy(() => elementSchema).optional(), - valuePositiveInt: positiveIntSchema.optional(), - _valuePositiveInt: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueUnsignedInt: unsignedIntSchema.optional(), - _valueUnsignedInt: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueUrl: urlSchema.optional(), - _valueUrl: z.lazy(() => elementSchema).optional(), - valueUuid: uuidSchema.optional(), - _valueUuid: z.lazy(() => elementSchema).optional(), - valueAddress: z.lazy(() => addressSchema).optional(), - valueAge: z.lazy(() => ageSchema).optional(), - valueAnnotation: z.lazy(() => annotationSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueContactPoint: z.lazy(() => contactPointSchema).optional(), - valueCount: z.lazy(() => countSchema).optional(), - valueDistance: z.lazy(() => distanceSchema).optional(), - valueDuration: z.lazy(() => durationSchema).optional(), - valueHumanName: z.lazy(() => humanNameSchema).optional(), - valueIdentifier: z.lazy(() => identifierSchema).optional(), - valueMoney: z.lazy(() => moneySchema).optional(), - valuePeriod: z.lazy(() => periodSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueRatio: z.lazy(() => ratioSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - valueSampledData: z.lazy(() => sampledDataSchema).optional(), - valueSignature: z.lazy(() => signatureSchema).optional(), - valueTiming: z.lazy(() => timingSchema).optional(), - valueContactDetail: z.lazy(() => contactDetailSchema).optional(), - valueContributor: z.lazy(() => contributorSchema).optional(), - valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), - valueExpression: z.lazy(() => expressionSchema).optional(), - valueParameterDefinition: z - .lazy(() => parameterDefinitionSchema) + type: z.lazy(() => codeableConceptSchema).optional(), + role: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) .optional(), - valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), - valueTriggerDefinition: z - .lazy(() => triggerDefinitionSchema) + who: z.lazy(() => referenceSchema), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const provenanceEntitySchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) .optional(), - valueUsageContext: z.lazy(() => usageContextSchema).optional(), - valueDosage: z.lazy(() => dosageSchema).optional(), - valueMeta: z.lazy(() => metaSchema).optional(), - resource: z.lazy(() => resourceSchema).optional(), - part: z - .lazy(() => parametersParameterSchema) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + role: z + .enum(['derivation', 'quotation', 'removal', 'revision', 'source']) + .optional(), + _role: z.lazy(() => elementSchema).optional(), + what: z.lazy(() => referenceSchema), + agent: z + .lazy(() => provenanceAgentSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'role', shadow: '_role' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const provenanceSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('Provenance'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + target: z + .lazy(() => referenceSchema) + .array() + .min(1), + occurredPeriod: z.lazy(() => periodSchema).optional(), + occurredDateTime: dateTimeSchema.optional(), + _occurredDateTime: z.lazy(() => elementSchema).optional(), + recorded: instantSchema.optional(), + _recorded: z.lazy(() => elementSchema).optional(), + policy: uriSchema.nullable().array().min(1).optional(), + _policy: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + location: z.lazy(() => referenceSchema).optional(), + reason: z + .lazy(() => codeableConceptSchema) .array() + .min(1) + .optional(), + activity: z.lazy(() => codeableConceptSchema).optional(), + agent: provenanceAgentSchema.array().min(1), + entity: provenanceEntitySchema.array().min(1).optional(), + signature: z + .lazy(() => signatureSchema) + .array() + .min(1) .optional(), }) .refine( (value) => [ { - names: [ - 'valueBase64Binary', - 'valueBoolean', - 'valueCanonical', - 'valueCode', - 'valueDate', - 'valueDateTime', - 'valueDecimal', - 'valueId', - 'valueInstant', - 'valueInteger', - 'valueMarkdown', - 'valueOid', - 'valuePositiveInt', - 'valueString', - 'valueTime', - 'valueUnsignedInt', - 'valueUri', - 'valueUrl', - 'valueUuid', - 'valueAddress', - 'valueAge', - 'valueAnnotation', - 'valueAttachment', - 'valueCodeableConcept', - 'valueCoding', - 'valueContactPoint', - 'valueCount', - 'valueDistance', - 'valueDuration', - 'valueHumanName', - 'valueIdentifier', - 'valueMoney', - 'valuePeriod', - 'valueQuantity', - 'valueRange', - 'valueRatio', - 'valueReference', - 'valueSampledData', - 'valueSignature', - 'valueTiming', - 'valueContactDetail', - 'valueContributor', - 'valueDataRequirement', - 'valueExpression', - 'valueParameterDefinition', - 'valueRelatedArtifact', - 'valueTriggerDefinition', - 'valueUsageContext', - 'valueDosage', - 'valueMeta', + alternatives: [ + ['occurredPeriod'], + ['occurredDateTime', '_occurredDateTime'], ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -21685,165 +36416,276 @@ export const parametersParameterSchema: z.ZodType = message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'recorded', shadow: '_recorded' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'policy', shadow: '_policy', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) +) as unknown as z.ZodType> -export const parametersSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Parameters'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - parameter: parametersParameterSchema.array().optional(), - }), -) - -export const patientContactSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - relationship: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - name: z.lazy(() => humanNameSchema).optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - address: z.lazy(() => addressSchema).optional(), - gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), - _gender: z.lazy(() => elementSchema).optional(), - organization: z.lazy(() => referenceSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), -) - -export const patientCommunicationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const quantitySchema: z.ZodType> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - language: z.lazy(() => codeableConceptSchema), - preferred: booleanSchema.optional(), - _preferred: z.lazy(() => elementSchema).optional(), - }), - ) + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const quantity = value as Record + const hasCode = + quantity.code !== undefined || quantity._code !== undefined + return ( + !hasCode || + quantity.system !== undefined || + quantity._system !== undefined + ) + }, + { message: 'qty-3: a coded Quantity requires its unit system.' }, + ), +) as unknown as z.ZodType> -export const patientLinkSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - other: z.lazy(() => referenceSchema), - type: z.enum(['refer', 'replaced-by', 'replaces', 'seealso']), - _type: z.lazy(() => elementSchema).optional(), - }), -) - -export const patientSchema: z.ZodType = z.lazy(() => +export const questionnaireItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Patient'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) + linkId: stringSchema.optional(), + _linkId: z.lazy(() => elementSchema).optional(), + definition: uriSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codingSchema) .array() + .min(1) .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - name: z - .lazy(() => humanNameSchema) - .array() + prefix: stringSchema.optional(), + _prefix: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'attachment', + 'boolean', + 'choice', + 'date', + 'dateTime', + 'decimal', + 'display', + 'group', + 'integer', + 'open-choice', + 'quantity', + 'question', + 'reference', + 'string', + 'text', + 'time', + 'url', + ]) .optional(), - telecom: z - .lazy(() => contactPointSchema) + _type: z.lazy(() => elementSchema).optional(), + enableWhen: questionnaireItemEnableWhenSchema.array().min(1).optional(), + enableBehavior: z.enum(['all', 'any']).optional(), + _enableBehavior: z.lazy(() => elementSchema).optional(), + required: booleanSchema.optional(), + _required: z.lazy(() => elementSchema).optional(), + repeats: booleanSchema.optional(), + _repeats: z.lazy(() => elementSchema).optional(), + readOnly: booleanSchema.optional(), + _readOnly: z.lazy(() => elementSchema).optional(), + maxLength: integerSchema.optional(), + _maxLength: z.lazy(() => elementSchema).optional(), + answerValueSet: canonicalSchema.optional(), + _answerValueSet: z.lazy(() => elementSchema).optional(), + answerOption: questionnaireItemAnswerOptionSchema .array() + .min(1) .optional(), - gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), - _gender: z.lazy(() => elementSchema).optional(), - birthDate: dateSchema.optional(), - _birthDate: z.lazy(() => elementSchema).optional(), - deceasedBoolean: booleanSchema.optional(), - _deceasedBoolean: z.lazy(() => elementSchema).optional(), - deceasedDateTime: dateTimeSchema.optional(), - _deceasedDateTime: z.lazy(() => elementSchema).optional(), - address: z - .lazy(() => addressSchema) + initial: questionnaireItemInitialSchema.array().min(1).optional(), + item: z + .lazy(() => questionnaireItemSchema) .array() + .min(1) .optional(), - maritalStatus: z.lazy(() => codeableConceptSchema).optional(), - multipleBirthBoolean: booleanSchema.optional(), - _multipleBirthBoolean: z.lazy(() => elementSchema).optional(), - multipleBirthInteger: integerSchema.optional(), - _multipleBirthInteger: z.lazy(() => elementSchema).optional(), - photo: z - .lazy(() => attachmentSchema) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'linkId', shadow: '_linkId' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const questionnaireItemEnableWhenSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - contact: patientContactSchema.array().optional(), - communication: patientCommunicationSchema.array().optional(), - generalPractitioner: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - managingOrganization: z.lazy(() => referenceSchema).optional(), - link: patientLinkSchema.array().optional(), + question: stringSchema.optional(), + _question: z.lazy(() => elementSchema).optional(), + operator: z.enum(['!=', '<', '<=', '=', '>', '>=', 'exists']).optional(), + _operator: z.lazy(() => elementSchema).optional(), + answerBoolean: booleanSchema.optional(), + _answerBoolean: z.lazy(() => elementSchema).optional(), + answerDecimal: decimalSchema.optional(), + _answerDecimal: z.lazy(() => elementSchema).optional(), + answerInteger: integerSchema.optional(), + _answerInteger: z.lazy(() => elementSchema).optional(), + answerDate: dateSchema.optional(), + _answerDate: z.lazy(() => elementSchema).optional(), + answerDateTime: dateTimeSchema.optional(), + _answerDateTime: z.lazy(() => elementSchema).optional(), + answerTime: timeSchema.optional(), + _answerTime: z.lazy(() => elementSchema).optional(), + answerString: stringSchema.optional(), + _answerString: z.lazy(() => elementSchema).optional(), + answerCoding: z.lazy(() => codingSchema).optional(), + answerQuantity: z.lazy(() => quantitySchema).optional(), + answerReference: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ - { names: ['deceasedBoolean', 'deceasedDateTime'], required: false }, { - names: ['multipleBirthBoolean', 'multipleBirthInteger'], - required: false, + alternatives: [ + ['answerBoolean', '_answerBoolean'], + ['answerDecimal', '_answerDecimal'], + ['answerInteger', '_answerInteger'], + ['answerDate', '_answerDate'], + ['answerDateTime', '_answerDateTime'], + ['answerTime', '_answerTime'], + ['answerString', '_answerString'], + ['answerCoding'], + ['answerQuantity'], + ['answerReference'], + ], + required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -21851,666 +36693,608 @@ export const patientSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'question', shadow: '_question' }, + { name: 'operator', shadow: '_operator' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const paymentNoticeSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('PaymentNotice'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), - _status: z.lazy(() => elementSchema).optional(), - request: z.lazy(() => referenceSchema).optional(), - response: z.lazy(() => referenceSchema).optional(), - created: dateTimeSchema, - _created: z.lazy(() => elementSchema).optional(), - provider: z.lazy(() => referenceSchema).optional(), - payment: z.lazy(() => referenceSchema), - paymentDate: dateSchema.optional(), - _paymentDate: z.lazy(() => elementSchema).optional(), - payee: z.lazy(() => referenceSchema).optional(), - recipient: z.lazy(() => referenceSchema), - amount: z.lazy(() => moneySchema), - paymentStatus: z.lazy(() => codeableConceptSchema).optional(), - }), -) - -export const paymentReconciliationDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - predecessor: z.lazy(() => identifierSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - request: z.lazy(() => referenceSchema).optional(), - submitter: z.lazy(() => referenceSchema).optional(), - response: z.lazy(() => referenceSchema).optional(), - date: dateSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - responsible: z.lazy(() => referenceSchema).optional(), - payee: z.lazy(() => referenceSchema).optional(), - amount: z.lazy(() => moneySchema).optional(), - }), - ) - -export const paymentReconciliationProcessNoteSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum(['display', 'print', 'printoper']).optional(), - _type: z.lazy(() => elementSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - }), - ) +) as unknown as z.ZodType> -export const paymentReconciliationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('PaymentReconciliation'), +export const questionnaireItemAnswerOptionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), - _status: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - created: dateTimeSchema, - _created: z.lazy(() => elementSchema).optional(), - paymentIssuer: z.lazy(() => referenceSchema).optional(), - request: z.lazy(() => referenceSchema).optional(), - requestor: z.lazy(() => referenceSchema).optional(), - outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), - _outcome: z.lazy(() => elementSchema).optional(), - disposition: stringSchema.optional(), - _disposition: z.lazy(() => elementSchema).optional(), - paymentDate: dateSchema, - _paymentDate: z.lazy(() => elementSchema).optional(), - paymentAmount: z.lazy(() => moneySchema), - paymentIdentifier: z.lazy(() => identifierSchema).optional(), - detail: paymentReconciliationDetailSchema.array().optional(), - formCode: z.lazy(() => codeableConceptSchema).optional(), - processNote: paymentReconciliationProcessNoteSchema.array().optional(), - }), - ) - -export const periodSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - start: dateTimeSchema.optional(), - _start: z.lazy(() => elementSchema).optional(), - end: dateTimeSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + initialSelected: booleanSchema.optional(), + _initialSelected: z.lazy(() => elementSchema).optional(), }) .refine( - (value) => { - const period = value as { start?: string; end?: string } - if (period.start === undefined || period.end === undefined) return true - const start = fhirDateTimeToEpoch(period.start) - const end = fhirDateTimeToEpoch(period.end) - // A malformed endpoint is a malformed date, not an ordering fault; the release says - // as much with start.hasValue(). Reporting both would name the wrong problem. - if (Number.isNaN(start) || Number.isNaN(end)) return true - return start <= end + (value) => + [ + { + alternatives: [ + ['valueInteger', '_valueInteger'], + ['valueDate', '_valueDate'], + ['valueTime', '_valueTime'], + ['valueString', '_valueString'], + ['valueCoding'], + ['valueReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, - { message: 'Period.start must not be later than Period.end.' }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const personLinkSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - target: z.lazy(() => referenceSchema), - assurance: z.enum(['level1', 'level2', 'level3', 'level4']).optional(), - _assurance: z.lazy(() => elementSchema).optional(), - }), -) - -export const personSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Person'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - name: z - .lazy(() => humanNameSchema) - .array() - .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), - _gender: z.lazy(() => elementSchema).optional(), - birthDate: dateSchema.optional(), - _birthDate: z.lazy(() => elementSchema).optional(), - address: z - .lazy(() => addressSchema) - .array() - .optional(), - photo: z.lazy(() => attachmentSchema).optional(), - managingOrganization: z.lazy(() => referenceSchema).optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - link: personLinkSchema.array().optional(), - }), -) - -export const planDefinitionGoalSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +) as unknown as z.ZodType> + +export const questionnaireItemInitialSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - description: z.lazy(() => codeableConceptSchema), - priority: z.lazy(() => codeableConceptSchema).optional(), - start: z.lazy(() => codeableConceptSchema).optional(), - addresses: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - documentation: z - .lazy(() => relatedArtifactSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - target: planDefinitionGoalTargetSchema.array().optional(), - }), - ) + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueBoolean', '_valueBoolean'], + ['valueDecimal', '_valueDecimal'], + ['valueInteger', '_valueInteger'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueTime', '_valueTime'], + ['valueString', '_valueString'], + ['valueUri', '_valueUri'], + ['valueAttachment'], + ['valueCoding'], + ['valueQuantity'], + ['valueReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const planDefinitionActionSchema: z.ZodType = +export const questionnaireSchema: z.ZodType> = z.lazy(() => z .strictObject({ + resourceType: z.literal('Questionnaire'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - prefix: stringSchema.optional(), - _prefix: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - textEquivalent: stringSchema.optional(), - _textEquivalent: z.lazy(() => elementSchema).optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - code: z - .lazy(() => codeableConceptSchema) + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - reason: z - .lazy(() => codeableConceptSchema) + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + derivedFrom: canonicalSchema.nullable().array().min(1).optional(), + _derivedFrom: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - documentation: z - .lazy(() => relatedArtifactSchema) + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectType: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .nullable() .array() + .min(1) .optional(), - goalId: idSchema.array().optional(), - _goalId: z + _subjectType: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - trigger: z - .lazy(() => triggerDefinitionSchema) - .array() - .optional(), - condition: planDefinitionActionConditionSchema.array().optional(), - input: z - .lazy(() => dataRequirementSchema) + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - output: z - .lazy(() => dataRequirementSchema) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - relatedAction: planDefinitionActionRelatedActionSchema + jurisdiction: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - timingDateTime: dateTimeSchema.optional(), - _timingDateTime: z.lazy(() => elementSchema).optional(), - timingAge: z.lazy(() => ageSchema).optional(), - timingPeriod: z.lazy(() => periodSchema).optional(), - timingDuration: z.lazy(() => durationSchema).optional(), - timingRange: z.lazy(() => rangeSchema).optional(), - timingTiming: z.lazy(() => timingSchema).optional(), - participant: planDefinitionActionParticipantSchema.array().optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - groupingBehavior: z - .enum(['logical-group', 'sentence-group', 'visual-group']) - .optional(), - _groupingBehavior: z.lazy(() => elementSchema).optional(), - selectionBehavior: z - .enum([ - 'all', - 'all-or-none', - 'any', - 'at-most-one', - 'exactly-one', - 'one-or-more', - ]) - .optional(), - _selectionBehavior: z.lazy(() => elementSchema).optional(), - requiredBehavior: z - .enum(['could', 'must', 'must-unless-documented']) - .optional(), - _requiredBehavior: z.lazy(() => elementSchema).optional(), - precheckBehavior: z.enum(['no', 'yes']).optional(), - _precheckBehavior: z.lazy(() => elementSchema).optional(), - cardinalityBehavior: z.enum(['multiple', 'single']).optional(), - _cardinalityBehavior: z.lazy(() => elementSchema).optional(), - definitionCanonical: canonicalSchema.optional(), - _definitionCanonical: z.lazy(() => elementSchema).optional(), - definitionUri: uriSchema.optional(), - _definitionUri: z.lazy(() => elementSchema).optional(), - transform: canonicalSchema.optional(), - _transform: z.lazy(() => elementSchema).optional(), - dynamicValue: planDefinitionActionDynamicValueSchema.array().optional(), - action: z - .lazy(() => planDefinitionActionSchema) + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + code: z + .lazy(() => codingSchema) .array() + .min(1) .optional(), + item: questionnaireItemSchema.array().min(1).optional(), }) - .refine( - (value) => - [ - { - names: ['subjectCodeableConcept', 'subjectReference'], - required: false, - }, - { - names: [ - 'timingDateTime', - 'timingAge', - 'timingPeriod', - 'timingDuration', - 'timingRange', - 'timingTiming', - ], - required: false, - }, - { - names: ['definitionCanonical', 'definitionUri'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const planDefinitionGoalTargetSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - measure: z.lazy(() => codeableConceptSchema).optional(), - detailQuantity: z.lazy(() => quantitySchema).optional(), - detailRange: z.lazy(() => rangeSchema).optional(), - detailCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - due: z.lazy(() => durationSchema).optional(), + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) - .refine( - (value) => - [ - { - names: ['detailQuantity', 'detailRange', 'detailCodeableConcept'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const planDefinitionActionConditionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - kind: z.enum(['applicability', 'start', 'stop']), - _kind: z.lazy(() => elementSchema).optional(), - expression: z.lazy(() => expressionSchema).optional(), - }), - ) - -export const planDefinitionActionRelatedActionSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - actionId: idSchema, - _actionId: z.lazy(() => elementSchema).optional(), - relationship: z.enum([ - 'after', - 'after-end', - 'after-start', - 'before', - 'before-end', - 'before-start', - 'concurrent', - 'concurrent-with-end', - 'concurrent-with-start', - ]), - _relationship: z.lazy(() => elementSchema).optional(), - offsetDuration: z.lazy(() => durationSchema).optional(), - offsetRange: z.lazy(() => rangeSchema).optional(), + .superRefine((value, context) => { + for (const group of [ + { name: 'derivedFrom', shadow: '_derivedFrom', required: false }, + { name: 'subjectType', shadow: '_subjectType', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } }) .refine( - (value) => - [{ names: ['offsetDuration', 'offsetRange'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const planDefinitionActionParticipantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum(['device', 'patient', 'practitioner', 'related-person']), - _type: z.lazy(() => elementSchema).optional(), - role: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const planDefinitionActionDynamicValueSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const questionnaireResponseItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - path: stringSchema.optional(), - _path: z.lazy(() => elementSchema).optional(), - expression: z.lazy(() => expressionSchema).optional(), - }), - ) + linkId: stringSchema.optional(), + _linkId: z.lazy(() => elementSchema).optional(), + definition: uriSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + answer: questionnaireResponseItemAnswerSchema.array().min(1).optional(), + item: z + .lazy(() => questionnaireResponseItemSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'linkId', shadow: '_linkId' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const planDefinitionSchema: z.ZodType = z.lazy(() => +export const questionnaireResponseItemAnswerSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('PlanDefinition'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - author: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - editor: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - reviewer: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - endorser: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) - .array() - .optional(), - library: canonicalSchema.array().optional(), - _library: z - .lazy(() => elementSchema) + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + item: z + .lazy(() => questionnaireResponseItemSchema) .array() + .min(1) .optional(), - goal: planDefinitionGoalSchema.array().optional(), - action: planDefinitionActionSchema.array().optional(), }) .refine( (value) => [ { - names: ['subjectCodeableConcept', 'subjectReference'], + alternatives: [ + ['valueBoolean', '_valueBoolean'], + ['valueDecimal', '_valueDecimal'], + ['valueInteger', '_valueInteger'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueTime', '_valueTime'], + ['valueString', '_valueString'], + ['valueUri', '_valueUri'], + ['valueAttachment'], + ['valueCoding'], + ['valueQuantity'], + ['valueReference'], + ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -22518,174 +37302,19 @@ export const planDefinitionSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const populationSchema: z.ZodType = z.lazy(() => +export const questionnaireResponseSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - ageRange: z.lazy(() => rangeSchema).optional(), - ageCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - gender: z.lazy(() => codeableConceptSchema).optional(), - race: z.lazy(() => codeableConceptSchema).optional(), - physiologicalCondition: z.lazy(() => codeableConceptSchema).optional(), - }) - .refine( - (value) => - [{ names: ['ageRange', 'ageCodeableConcept'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const practitionerQualificationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - period: z.lazy(() => periodSchema).optional(), - issuer: z.lazy(() => referenceSchema).optional(), - }), - ) - -export const practitionerSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Practitioner'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - name: z - .lazy(() => humanNameSchema) - .array() - .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - address: z - .lazy(() => addressSchema) - .array() - .optional(), - gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), - _gender: z.lazy(() => elementSchema).optional(), - birthDate: dateSchema.optional(), - _birthDate: z.lazy(() => elementSchema).optional(), - photo: z - .lazy(() => attachmentSchema) - .array() - .optional(), - qualification: practitionerQualificationSchema.array().optional(), - communication: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - }), -) - -export const practitionerRoleAvailableTimeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - daysOfWeek: z - .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) - .array() - .optional(), - _daysOfWeek: z - .lazy(() => elementSchema) - .array() - .optional(), - allDay: booleanSchema.optional(), - _allDay: z.lazy(() => elementSchema).optional(), - availableStartTime: timeSchema.optional(), - _availableStartTime: z.lazy(() => elementSchema).optional(), - availableEndTime: timeSchema.optional(), - _availableEndTime: z.lazy(() => elementSchema).optional(), - }), - ) - -export const practitionerRoleNotAvailableSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - description: stringSchema, - _description: z.lazy(() => elementSchema).optional(), - during: z.lazy(() => periodSchema).optional(), - }), - ) - -export const practitionerRoleSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('PractitionerRole'), + resourceType: z.literal('QuestionnaireResponse'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -22697,222 +37326,516 @@ export const practitionerRoleSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - practitioner: z.lazy(() => referenceSchema).optional(), - organization: z.lazy(() => referenceSchema).optional(), - code: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - specialty: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - location: z + identifier: z.lazy(() => identifierSchema).optional(), + basedOn: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - healthcareService: z + partOf: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - availableTime: practitionerRoleAvailableTimeSchema.array().optional(), - notAvailable: practitionerRoleNotAvailableSchema.array().optional(), - availabilityExceptions: stringSchema.optional(), - _availabilityExceptions: z.lazy(() => elementSchema).optional(), - endpoint: z - .lazy(() => referenceSchema) + questionnaire: canonicalSchema.optional(), + _questionnaire: z.lazy(() => elementSchema).optional(), + status: z + .enum([ + 'amended', + 'completed', + 'entered-in-error', + 'in-progress', + 'stopped', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + authored: dateTimeSchema.optional(), + _authored: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + item: questionnaireResponseItemSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const rangeSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), -) + low: z.lazy(() => quantitySchema).optional(), + high: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const procedurePerformerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const ratioSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z + numerator: z.lazy(() => quantitySchema).optional(), + denominator: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const referenceSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - function: z.lazy(() => codeableConceptSchema).optional(), - actor: z.lazy(() => referenceSchema), - onBehalfOf: z.lazy(() => referenceSchema).optional(), - }), - ) + reference: stringSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + type: uriSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const reference = value as Record + return [ + 'reference', + '_reference', + 'identifier', + 'display', + '_display', + ].some((name) => reference[name] !== undefined) + }, + { + message: + 'ref-1: a Reference states a literal reference, identifier, or display.', + }, + ), +) as unknown as z.ZodType> -export const procedureFocalDeviceSchema: z.ZodType = +export const relatedArtifactSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z + .enum([ + 'citation', + 'composed-of', + 'depends-on', + 'derived-from', + 'documentation', + 'justification', + 'predecessor', + 'successor', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + label: stringSchema.optional(), + _label: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + citation: markdownSchema.optional(), + _citation: z.lazy(() => elementSchema).optional(), + url: urlSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + document: z.lazy(() => attachmentSchema).optional(), + resource: canonicalSchema.optional(), + _resource: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const relatedPersonCommunicationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - action: z.lazy(() => codeableConceptSchema).optional(), - manipulated: z.lazy(() => referenceSchema), - }), - ) + language: z.lazy(() => codeableConceptSchema), + preferred: booleanSchema.optional(), + _preferred: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const relatedPersonSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('RelatedPerson'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + relationship: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + name: z + .lazy(() => humanNameSchema) + .array() + .min(1) + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + address: z + .lazy(() => addressSchema) + .array() + .min(1) + .optional(), + photo: z + .lazy(() => attachmentSchema) + .array() + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + communication: relatedPersonCommunicationSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const procedureSchema: z.ZodType = z.lazy(() => +export const requestGroupActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Procedure'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) + prefix: stringSchema.optional(), + _prefix: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + textEquivalent: stringSchema.optional(), + _textEquivalent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) + documentation: z + .lazy(() => relatedArtifactSchema) .array() + .min(1) .optional(), - basedOn: z - .lazy(() => referenceSchema) + condition: requestGroupActionConditionSchema.array().min(1).optional(), + relatedAction: requestGroupActionRelatedActionSchema .array() + .min(1) .optional(), - partOf: z + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + timingAge: z.lazy(() => ageSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingDuration: z.lazy(() => durationSchema).optional(), + timingRange: z.lazy(() => rangeSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + participant: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - status: z.enum([ - 'completed', - 'entered-in-error', - 'in-progress', - 'not-done', - 'on-hold', - 'preparation', - 'stopped', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - performedDateTime: dateTimeSchema.optional(), - _performedDateTime: z.lazy(() => elementSchema).optional(), - performedPeriod: z.lazy(() => periodSchema).optional(), - performedString: stringSchema.optional(), - _performedString: z.lazy(() => elementSchema).optional(), - performedAge: z.lazy(() => ageSchema).optional(), - performedRange: z.lazy(() => rangeSchema).optional(), - recorder: z.lazy(() => referenceSchema).optional(), - asserter: z.lazy(() => referenceSchema).optional(), - performer: procedurePerformerSchema.array().optional(), - location: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() + type: z.lazy(() => codeableConceptSchema).optional(), + groupingBehavior: z + .enum(['logical-group', 'sentence-group', 'visual-group']) .optional(), - bodySite: z - .lazy(() => codeableConceptSchema) - .array() + _groupingBehavior: z.lazy(() => elementSchema).optional(), + selectionBehavior: z + .enum([ + 'all', + 'all-or-none', + 'any', + 'at-most-one', + 'exactly-one', + 'one-or-more', + ]) .optional(), - outcome: z.lazy(() => codeableConceptSchema).optional(), - report: z - .lazy(() => referenceSchema) - .array() + _selectionBehavior: z.lazy(() => elementSchema).optional(), + requiredBehavior: z + .enum(['could', 'must', 'must-unless-documented']) .optional(), - complication: z - .lazy(() => codeableConceptSchema) + _requiredBehavior: z.lazy(() => elementSchema).optional(), + precheckBehavior: z.enum(['no', 'yes']).optional(), + _precheckBehavior: z.lazy(() => elementSchema).optional(), + cardinalityBehavior: z.enum(['multiple', 'single']).optional(), + _cardinalityBehavior: z.lazy(() => elementSchema).optional(), + resource: z.lazy(() => referenceSchema).optional(), + action: z + .lazy(() => requestGroupActionSchema) .array() + .min(1) .optional(), - complicationDetail: z - .lazy(() => referenceSchema) + }) + .refine( + (value) => + [ + { + alternatives: [ + ['timingDateTime', '_timingDateTime'], + ['timingAge'], + ['timingPeriod'], + ['timingDuration'], + ['timingRange'], + ['timingTiming'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const requestGroupActionConditionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - followUp: z - .lazy(() => codeableConceptSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - note: z - .lazy(() => annotationSchema) + kind: z.enum(['applicability', 'start', 'stop']).optional(), + _kind: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'kind', shadow: '_kind' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const requestGroupActionRelatedActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - focalDevice: procedureFocalDeviceSchema.array().optional(), - usedReference: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - usedCode: z - .lazy(() => codeableConceptSchema) - .array() + actionId: idSchema.optional(), + _actionId: z.lazy(() => elementSchema).optional(), + relationship: z + .enum([ + 'after', + 'after-end', + 'after-start', + 'before', + 'before-end', + 'before-start', + 'concurrent', + 'concurrent-with-end', + 'concurrent-with-start', + ]) .optional(), + _relationship: z.lazy(() => elementSchema).optional(), + offsetDuration: z.lazy(() => durationSchema).optional(), + offsetRange: z.lazy(() => rangeSchema).optional(), }) .refine( (value) => [ { - names: [ - 'performedDateTime', - 'performedPeriod', - 'performedString', - 'performedAge', - 'performedRange', - ], + alternatives: [['offsetDuration'], ['offsetRange']], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -22920,177 +37843,371 @@ export const procedureSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'actionId', shadow: '_actionId' }, + { name: 'relationship', shadow: '_relationship' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const prodCharacteristicSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const requestGroupSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('RequestGroup'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + instantiatesCanonical: canonicalSchema + .nullable() + .array() + .min(1) + .optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + replaces: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + status: z + .enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + intent: z + .enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]) + .optional(), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + action: requestGroupActionSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'intent', shadow: '_intent' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const researchDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ResearchDefinition'), id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - height: z.lazy(() => quantitySchema).optional(), - width: z.lazy(() => quantitySchema).optional(), - depth: z.lazy(() => quantitySchema).optional(), - weight: z.lazy(() => quantitySchema).optional(), - nominalVolume: z.lazy(() => quantitySchema).optional(), - externalDiameter: z.lazy(() => quantitySchema).optional(), - shape: stringSchema.optional(), - _shape: z.lazy(() => elementSchema).optional(), - color: stringSchema.array().optional(), - _color: z - .lazy(() => elementSchema) - .array() - .optional(), - imprint: stringSchema.array().optional(), - _imprint: z - .lazy(() => elementSchema) - .array() - .optional(), - image: z - .lazy(() => attachmentSchema) + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) .array() + .min(1) .optional(), - scoring: z.lazy(() => codeableConceptSchema).optional(), - }), - ) - -export const productShelfLifeSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - period: z.lazy(() => quantitySchema), - specialPrecautionsForStorage: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - }), -) - -export const provenanceAgentSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + shortTitle: stringSchema.optional(), + _shortTitle: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - role: z - .lazy(() => codeableConceptSchema) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + comment: stringSchema.nullable().array().min(1).optional(), + _comment: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - who: z.lazy(() => referenceSchema), - onBehalfOf: z.lazy(() => referenceSchema).optional(), - }), -) - -export const provenanceEntitySchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + jurisdiction: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - role: z.enum([ - 'derivation', - 'quotation', - 'removal', - 'revision', - 'source', - ]), - _role: z.lazy(() => elementSchema).optional(), - what: z.lazy(() => referenceSchema), - agent: z - .lazy(() => provenanceAgentSchema) + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - }), -) - -export const provenanceSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - resourceType: z.literal('Provenance'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + author: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - extension: z - .lazy(() => extensionSchema) + editor: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + reviewer: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - target: z - .lazy(() => referenceSchema) - .array() - .min(1), - occurredPeriod: z.lazy(() => periodSchema).optional(), - occurredDateTime: dateTimeSchema.optional(), - _occurredDateTime: z.lazy(() => elementSchema).optional(), - recorded: instantSchema, - _recorded: z.lazy(() => elementSchema).optional(), - policy: uriSchema.array().optional(), - _policy: z - .lazy(() => elementSchema) + endorser: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - location: z.lazy(() => referenceSchema).optional(), - reason: z - .lazy(() => codeableConceptSchema) + relatedArtifact: z + .lazy(() => relatedArtifactSchema) .array() + .min(1) .optional(), - activity: z.lazy(() => codeableConceptSchema).optional(), - agent: provenanceAgentSchema.array().min(1), - entity: provenanceEntitySchema.array().optional(), - signature: z - .lazy(() => signatureSchema) + library: canonicalSchema.nullable().array().min(1).optional(), + _library: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), + population: z.lazy(() => referenceSchema), + exposure: z.lazy(() => referenceSchema).optional(), + exposureAlternative: z.lazy(() => referenceSchema).optional(), + outcome: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ - { names: ['occurredPeriod', 'occurredDateTime'], required: false }, + { + alternatives: [['subjectCodeableConcept'], ['subjectReference']], + required: false, + }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -23098,618 +38215,200 @@ export const provenanceSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'comment', shadow: '_comment', required: false }, + { name: 'library', shadow: '_library', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const quantitySchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - comparator: z.enum(['<', '<=', '>', '>=']).optional(), - _comparator: z.lazy(() => elementSchema).optional(), - unit: stringSchema.optional(), - _unit: z.lazy(() => elementSchema).optional(), - system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - }), -) - -export const questionnaireItemSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - linkId: stringSchema, - _linkId: z.lazy(() => elementSchema).optional(), - definition: uriSchema.optional(), - _definition: z.lazy(() => elementSchema).optional(), - code: z - .lazy(() => codingSchema) - .array() - .optional(), - prefix: stringSchema.optional(), - _prefix: z.lazy(() => elementSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - type: z.enum([ - 'attachment', - 'boolean', - 'choice', - 'date', - 'dateTime', - 'decimal', - 'display', - 'group', - 'integer', - 'open-choice', - 'quantity', - 'question', - 'reference', - 'string', - 'text', - 'time', - 'url', - ]), - _type: z.lazy(() => elementSchema).optional(), - enableWhen: questionnaireItemEnableWhenSchema.array().optional(), - enableBehavior: z.enum(['all', 'any']).optional(), - _enableBehavior: z.lazy(() => elementSchema).optional(), - required: booleanSchema.optional(), - _required: z.lazy(() => elementSchema).optional(), - repeats: booleanSchema.optional(), - _repeats: z.lazy(() => elementSchema).optional(), - readOnly: booleanSchema.optional(), - _readOnly: z.lazy(() => elementSchema).optional(), - maxLength: integerSchema.optional(), - _maxLength: z.lazy(() => elementSchema).optional(), - answerValueSet: canonicalSchema.optional(), - _answerValueSet: z.lazy(() => elementSchema).optional(), - answerOption: questionnaireItemAnswerOptionSchema.array().optional(), - initial: questionnaireItemInitialSchema.array().optional(), - item: z - .lazy(() => questionnaireItemSchema) - .array() - .optional(), - }), -) - -export const questionnaireItemEnableWhenSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - question: stringSchema, - _question: z.lazy(() => elementSchema).optional(), - operator: z.enum(['!=', '<', '<=', '=', '>', '>=', 'exists']), - _operator: z.lazy(() => elementSchema).optional(), - answerBoolean: booleanSchema.optional(), - _answerBoolean: z.lazy(() => elementSchema).optional(), - answerDecimal: decimalSchema.optional(), - _answerDecimal: z.lazy(() => elementSchema).optional(), - answerInteger: integerSchema.optional(), - _answerInteger: z.lazy(() => elementSchema).optional(), - answerDate: dateSchema.optional(), - _answerDate: z.lazy(() => elementSchema).optional(), - answerDateTime: dateTimeSchema.optional(), - _answerDateTime: z.lazy(() => elementSchema).optional(), - answerTime: timeSchema.optional(), - _answerTime: z.lazy(() => elementSchema).optional(), - answerString: stringSchema.optional(), - _answerString: z.lazy(() => elementSchema).optional(), - answerCoding: z.lazy(() => codingSchema).optional(), - answerQuantity: z.lazy(() => quantitySchema).optional(), - answerReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'answerBoolean', - 'answerDecimal', - 'answerInteger', - 'answerDate', - 'answerDateTime', - 'answerTime', - 'answerString', - 'answerCoding', - 'answerQuantity', - 'answerReference', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const questionnaireItemAnswerOptionSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - initialSelected: booleanSchema.optional(), - _initialSelected: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueInteger', - 'valueDate', - 'valueTime', - 'valueString', - 'valueCoding', - 'valueReference', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const questionnaireItemInitialSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueBoolean', - 'valueDecimal', - 'valueInteger', - 'valueDate', - 'valueDateTime', - 'valueTime', - 'valueString', - 'valueUri', - 'valueAttachment', - 'valueCoding', - 'valueQuantity', - 'valueReference', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) +) as unknown as z.ZodType> -export const questionnaireSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Questionnaire'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - derivedFrom: canonicalSchema.array().optional(), - _derivedFrom: z - .lazy(() => elementSchema) - .array() - .optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - subjectType: z - .enum([ - 'Account', - 'ActivityDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'EffectEvidenceSynthesis', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProduct', - 'MedicinalProductAuthorization', - 'MedicinalProductContraindication', - 'MedicinalProductIndication', - 'MedicinalProductIngredient', - 'MedicinalProductInteraction', - 'MedicinalProductManufactured', - 'MedicinalProductPackaged', - 'MedicinalProductPharmaceutical', - 'MedicinalProductUndesirableEffect', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'RiskEvidenceSynthesis', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'Substance', - 'SubstanceNucleicAcid', - 'SubstancePolymer', - 'SubstanceProtein', - 'SubstanceReferenceInformation', - 'SubstanceSourceMaterial', - 'SubstanceSpecification', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]) - .array() - .optional(), - _subjectType: z - .lazy(() => elementSchema) - .array() - .optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - code: z - .lazy(() => codingSchema) - .array() - .optional(), - item: questionnaireItemSchema.array().optional(), - }), -) - -export const questionnaireResponseItemSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const researchElementDefinitionCharacteristicSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - linkId: stringSchema, - _linkId: z.lazy(() => elementSchema).optional(), - definition: uriSchema.optional(), - _definition: z.lazy(() => elementSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - answer: questionnaireResponseItemAnswerSchema.array().optional(), - item: z - .lazy(() => questionnaireResponseItemSchema) + definitionCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + definitionCanonical: canonicalSchema.optional(), + _definitionCanonical: z.lazy(() => elementSchema).optional(), + definitionExpression: z.lazy(() => expressionSchema).optional(), + definitionDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + usageContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - }), - ) - -export const questionnaireResponseItemAnswerSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - item: z - .lazy(() => questionnaireResponseItemSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueBoolean', - 'valueDecimal', - 'valueInteger', - 'valueDate', - 'valueDateTime', - 'valueTime', - 'valueString', - 'valueUri', - 'valueAttachment', - 'valueCoding', - 'valueQuantity', - 'valueReference', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( + exclude: booleanSchema.optional(), + _exclude: z.lazy(() => elementSchema).optional(), + unitOfMeasure: z.lazy(() => codeableConceptSchema).optional(), + studyEffectiveDescription: stringSchema.optional(), + _studyEffectiveDescription: z.lazy(() => elementSchema).optional(), + studyEffectiveDateTime: dateTimeSchema.optional(), + _studyEffectiveDateTime: z.lazy(() => elementSchema).optional(), + studyEffectivePeriod: z.lazy(() => periodSchema).optional(), + studyEffectiveDuration: z.lazy(() => durationSchema).optional(), + studyEffectiveTiming: z.lazy(() => timingSchema).optional(), + studyEffectiveTimeFromStart: z.lazy(() => durationSchema).optional(), + studyEffectiveGroupMeasure: z + .enum([ + 'mean', + 'mean-of-mean', + 'mean-of-median', + 'median', + 'median-of-mean', + 'median-of-median', + ]) + .optional(), + _studyEffectiveGroupMeasure: z.lazy(() => elementSchema).optional(), + participantEffectiveDescription: stringSchema.optional(), + _participantEffectiveDescription: z.lazy(() => elementSchema).optional(), + participantEffectiveDateTime: dateTimeSchema.optional(), + _participantEffectiveDateTime: z.lazy(() => elementSchema).optional(), + participantEffectivePeriod: z.lazy(() => periodSchema).optional(), + participantEffectiveDuration: z.lazy(() => durationSchema).optional(), + participantEffectiveTiming: z.lazy(() => timingSchema).optional(), + participantEffectiveTimeFromStart: z + .lazy(() => durationSchema) + .optional(), + participantEffectiveGroupMeasure: z + .enum([ + 'mean', + 'mean-of-mean', + 'mean-of-median', + 'median', + 'median-of-mean', + 'median-of-median', + ]) + .optional(), + _participantEffectiveGroupMeasure: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['definitionCodeableConcept'], + ['definitionCanonical', '_definitionCanonical'], + ['definitionExpression'], + ['definitionDataRequirement'], + ], + required: true, + }, + { + alternatives: [ + ['studyEffectiveDateTime', '_studyEffectiveDateTime'], + ['studyEffectivePeriod'], + ['studyEffectiveDuration'], + ['studyEffectiveTiming'], + ], + required: false, + }, + { + alternatives: [ + ['participantEffectiveDateTime', '_participantEffectiveDateTime'], + ['participantEffectivePeriod'], + ['participantEffectiveDuration'], + ['participantEffectiveTiming'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const questionnaireResponseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('QuestionnaireResponse'), +export const researchElementDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ResearchElementDefinition'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -23721,711 +38420,294 @@ export const questionnaireResponseSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - basedOn: z - .lazy(() => referenceSchema) + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - partOf: z - .lazy(() => referenceSchema) + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + shortTitle: stringSchema.optional(), + _shortTitle: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - questionnaire: canonicalSchema.optional(), - _questionnaire: z.lazy(() => elementSchema).optional(), - status: z.enum([ - 'amended', - 'completed', - 'entered-in-error', - 'in-progress', - 'stopped', - ]), - _status: z.lazy(() => elementSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - authored: dateTimeSchema.optional(), - _authored: z.lazy(() => elementSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - source: z.lazy(() => referenceSchema).optional(), - item: questionnaireResponseItemSchema.array().optional(), - }), - ) - -export const rangeSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - low: z.lazy(() => quantitySchema).optional(), - high: z.lazy(() => quantitySchema).optional(), - }), -) - -export const ratioSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - numerator: z.lazy(() => quantitySchema).optional(), - denominator: z.lazy(() => quantitySchema).optional(), - }), -) - -export const referenceSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - reference: stringSchema.optional(), - _reference: z.lazy(() => elementSchema).optional(), - type: uriSchema.optional(), - _type: z.lazy(() => elementSchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - }), -) - -export const relatedArtifactSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + comment: stringSchema.nullable().array().min(1).optional(), + _comment: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - type: z.enum([ - 'citation', - 'composed-of', - 'depends-on', - 'derived-from', - 'documentation', - 'justification', - 'predecessor', - 'successor', - ]), - _type: z.lazy(() => elementSchema).optional(), - label: stringSchema.optional(), - _label: z.lazy(() => elementSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - citation: markdownSchema.optional(), - _citation: z.lazy(() => elementSchema).optional(), - url: urlSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - document: z.lazy(() => attachmentSchema).optional(), - resource: canonicalSchema.optional(), - _resource: z.lazy(() => elementSchema).optional(), - }), -) - -export const relatedPersonCommunicationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + jurisdiction: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - language: z.lazy(() => codeableConceptSchema), - preferred: booleanSchema.optional(), - _preferred: z.lazy(() => elementSchema).optional(), - }), - ) - -export const relatedPersonSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('RelatedPerson'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema), - relationship: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - name: z - .lazy(() => humanNameSchema) - .array() - .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), - _gender: z.lazy(() => elementSchema).optional(), - birthDate: dateSchema.optional(), - _birthDate: z.lazy(() => elementSchema).optional(), - address: z - .lazy(() => addressSchema) - .array() - .optional(), - photo: z - .lazy(() => attachmentSchema) - .array() - .optional(), - period: z.lazy(() => periodSchema).optional(), - communication: relatedPersonCommunicationSchema.array().optional(), - }), -) - -export const requestGroupActionSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - prefix: stringSchema.optional(), - _prefix: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - textEquivalent: stringSchema.optional(), - _textEquivalent: z.lazy(() => elementSchema).optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - code: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - documentation: z - .lazy(() => relatedArtifactSchema) - .array() - .optional(), - condition: requestGroupActionConditionSchema.array().optional(), - relatedAction: requestGroupActionRelatedActionSchema.array().optional(), - timingDateTime: dateTimeSchema.optional(), - _timingDateTime: z.lazy(() => elementSchema).optional(), - timingAge: z.lazy(() => ageSchema).optional(), - timingPeriod: z.lazy(() => periodSchema).optional(), - timingDuration: z.lazy(() => durationSchema).optional(), - timingRange: z.lazy(() => rangeSchema).optional(), - timingTiming: z.lazy(() => timingSchema).optional(), - participant: z - .lazy(() => referenceSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - groupingBehavior: z - .enum(['logical-group', 'sentence-group', 'visual-group']) - .optional(), - _groupingBehavior: z.lazy(() => elementSchema).optional(), - selectionBehavior: z - .enum([ - 'all', - 'all-or-none', - 'any', - 'at-most-one', - 'exactly-one', - 'one-or-more', - ]) - .optional(), - _selectionBehavior: z.lazy(() => elementSchema).optional(), - requiredBehavior: z - .enum(['could', 'must', 'must-unless-documented']) - .optional(), - _requiredBehavior: z.lazy(() => elementSchema).optional(), - precheckBehavior: z.enum(['no', 'yes']).optional(), - _precheckBehavior: z.lazy(() => elementSchema).optional(), - cardinalityBehavior: z.enum(['multiple', 'single']).optional(), - _cardinalityBehavior: z.lazy(() => elementSchema).optional(), - resource: z.lazy(() => referenceSchema).optional(), - action: z - .lazy(() => requestGroupActionSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'timingDateTime', - 'timingAge', - 'timingPeriod', - 'timingDuration', - 'timingRange', - 'timingTiming', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const requestGroupActionConditionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + author: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - kind: z.enum(['applicability', 'start', 'stop']), - _kind: z.lazy(() => elementSchema).optional(), - expression: z.lazy(() => expressionSchema).optional(), - }), - ) - -export const requestGroupActionRelatedActionSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - actionId: idSchema, - _actionId: z.lazy(() => elementSchema).optional(), - relationship: z.enum([ - 'after', - 'after-end', - 'after-start', - 'before', - 'before-end', - 'before-start', - 'concurrent', - 'concurrent-with-end', - 'concurrent-with-start', - ]), - _relationship: z.lazy(() => elementSchema).optional(), - offsetDuration: z.lazy(() => durationSchema).optional(), - offsetRange: z.lazy(() => rangeSchema).optional(), - }) - .refine( - (value) => - [{ names: ['offsetDuration', 'offsetRange'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + library: canonicalSchema.nullable().array().min(1).optional(), + _library: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + type: z.enum(['exposure', 'outcome', 'population']).optional(), + _type: z.lazy(() => elementSchema).optional(), + variableType: z + .enum(['continuous', 'descriptive', 'dichotomous']) + .optional(), + _variableType: z.lazy(() => elementSchema).optional(), + characteristic: researchElementDefinitionCharacteristicSchema + .array() + .min(1), + }) + .refine( + (value) => + [ + { + alternatives: [['subjectCodeableConcept'], ['subjectReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'comment', shadow: '_comment', required: false }, + { name: 'library', shadow: '_library', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const requestGroupSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('RequestGroup'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - replaces: z - .lazy(() => referenceSchema) - .array() - .optional(), - groupIdentifier: z.lazy(() => identifierSchema).optional(), - status: z.enum([ - 'active', - 'completed', - 'draft', - 'entered-in-error', - 'on-hold', - 'revoked', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - intent: z.enum([ - 'directive', - 'filler-order', - 'instance-order', - 'option', - 'order', - 'original-order', - 'plan', - 'proposal', - 'reflex-order', - ]), - _intent: z.lazy(() => elementSchema).optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: z.lazy(() => elementSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - action: requestGroupActionSchema.array().optional(), - }), -) - -export const researchDefinitionSchema: z.ZodType = +export const researchStudyArmSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('ResearchDefinition'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - shortTitle: stringSchema.optional(), - _shortTitle: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - comment: stringSchema.array().optional(), - _comment: z - .lazy(() => elementSchema) - .array() - .optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - author: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - editor: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - reviewer: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - endorser: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) - .array() - .optional(), - library: canonicalSchema.array().optional(), - _library: z - .lazy(() => elementSchema) - .array() - .optional(), - population: z.lazy(() => referenceSchema), - exposure: z.lazy(() => referenceSchema).optional(), - exposureAlternative: z.lazy(() => referenceSchema).optional(), - outcome: z.lazy(() => referenceSchema).optional(), }) - .refine( - (value) => - [ - { - names: ['subjectCodeableConcept', 'subjectReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const researchElementDefinitionCharacteristicSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - definitionCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - definitionCanonical: canonicalSchema.optional(), - _definitionCanonical: z.lazy(() => elementSchema).optional(), - definitionExpression: z.lazy(() => expressionSchema).optional(), - definitionDataRequirement: z - .lazy(() => dataRequirementSchema) - .optional(), - usageContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - exclude: booleanSchema.optional(), - _exclude: z.lazy(() => elementSchema).optional(), - unitOfMeasure: z.lazy(() => codeableConceptSchema).optional(), - studyEffectiveDescription: stringSchema.optional(), - _studyEffectiveDescription: z.lazy(() => elementSchema).optional(), - studyEffectiveDateTime: dateTimeSchema.optional(), - _studyEffectiveDateTime: z.lazy(() => elementSchema).optional(), - studyEffectivePeriod: z.lazy(() => periodSchema).optional(), - studyEffectiveDuration: z.lazy(() => durationSchema).optional(), - studyEffectiveTiming: z.lazy(() => timingSchema).optional(), - studyEffectiveTimeFromStart: z.lazy(() => durationSchema).optional(), - studyEffectiveGroupMeasure: z - .enum([ - 'mean', - 'mean-of-mean', - 'mean-of-median', - 'median', - 'median-of-mean', - 'median-of-median', - ]) - .optional(), - _studyEffectiveGroupMeasure: z.lazy(() => elementSchema).optional(), - participantEffectiveDescription: stringSchema.optional(), - _participantEffectiveDescription: z - .lazy(() => elementSchema) - .optional(), - participantEffectiveDateTime: dateTimeSchema.optional(), - _participantEffectiveDateTime: z.lazy(() => elementSchema).optional(), - participantEffectivePeriod: z.lazy(() => periodSchema).optional(), - participantEffectiveDuration: z.lazy(() => durationSchema).optional(), - participantEffectiveTiming: z.lazy(() => timingSchema).optional(), - participantEffectiveTimeFromStart: z - .lazy(() => durationSchema) - .optional(), - participantEffectiveGroupMeasure: z - .enum([ - 'mean', - 'mean-of-mean', - 'mean-of-median', - 'median', - 'median-of-mean', - 'median-of-median', - ]) - .optional(), - _participantEffectiveGroupMeasure: z - .lazy(() => elementSchema) - .optional(), + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: [ - 'definitionCodeableConcept', - 'definitionCanonical', - 'definitionExpression', - 'definitionDataRequirement', - ], - required: true, - }, - { - names: [ - 'studyEffectiveDateTime', - 'studyEffectivePeriod', - 'studyEffectiveDuration', - 'studyEffectiveTiming', - ], - required: false, - }, - { - names: [ - 'participantEffectiveDateTime', - 'participantEffectivePeriod', - 'participantEffectiveDuration', - 'participantEffectiveTiming', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> + +export const researchStudyObjectiveSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const researchElementDefinitionSchema: z.ZodType = +export const researchStudySchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('ResearchElementDefinition'), + resourceType: z.literal('ResearchStudy'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -24437,453 +38719,264 @@ export const researchElementDefinitionSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), title: stringSchema.optional(), _title: z.lazy(() => elementSchema).optional(), - shortTitle: stringSchema.optional(), - _shortTitle: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) + protocol: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - comment: stringSchema.array().optional(), - _comment: z - .lazy(() => elementSchema) + partOf: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() + status: z + .enum([ + 'active', + 'administratively-completed', + 'approved', + 'closed-to-accrual', + 'closed-to-accrual-and-intervention', + 'completed', + 'disapproved', + 'in-review', + 'temporarily-closed-to-accrual', + 'temporarily-closed-to-accrual-and-intervention', + 'withdrawn', + ]) .optional(), - jurisdiction: z + _status: z.lazy(() => elementSchema).optional(), + primaryPurposeType: z.lazy(() => codeableConceptSchema).optional(), + phase: z.lazy(() => codeableConceptSchema).optional(), + category: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z + focus: z .lazy(() => codeableConceptSchema) .array() - .optional(), - author: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - editor: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - reviewer: z - .lazy(() => contactDetailSchema) + .min(1) + .optional(), + condition: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - endorser: z + contact: z .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), relatedArtifact: z .lazy(() => relatedArtifactSchema) .array() + .min(1) .optional(), - library: canonicalSchema.array().optional(), - _library: z - .lazy(() => elementSchema) + keyword: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - type: z.enum(['exposure', 'outcome', 'population']), - _type: z.lazy(() => elementSchema).optional(), - variableType: z - .enum(['continuous', 'descriptive', 'dichotomous']) + location: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) .optional(), - _variableType: z.lazy(() => elementSchema).optional(), - characteristic: researchElementDefinitionCharacteristicSchema + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + enrollment: z + .lazy(() => referenceSchema) .array() - .min(1), + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + sponsor: z.lazy(() => referenceSchema).optional(), + principalInvestigator: z.lazy(() => referenceSchema).optional(), + site: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + reasonStopped: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + arm: researchStudyArmSchema.array().min(1).optional(), + objective: researchStudyObjectiveSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['subjectCodeableConcept', 'subjectReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) - -export const researchStudyArmSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - }), -) - -export const researchStudyObjectiveSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + ) as unknown as z.ZodType> -export const researchStudySchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('ResearchStudy'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - protocol: z - .lazy(() => referenceSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'administratively-completed', - 'approved', - 'closed-to-accrual', - 'closed-to-accrual-and-intervention', - 'completed', - 'disapproved', - 'in-review', - 'temporarily-closed-to-accrual', - 'temporarily-closed-to-accrual-and-intervention', - 'withdrawn', - ]), - _status: z.lazy(() => elementSchema).optional(), - primaryPurposeType: z.lazy(() => codeableConceptSchema).optional(), - phase: z.lazy(() => codeableConceptSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - focus: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - condition: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) - .array() - .optional(), - keyword: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - location: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - enrollment: z - .lazy(() => referenceSchema) - .array() - .optional(), - period: z.lazy(() => periodSchema).optional(), - sponsor: z.lazy(() => referenceSchema).optional(), - principalInvestigator: z.lazy(() => referenceSchema).optional(), - site: z - .lazy(() => referenceSchema) - .array() - .optional(), - reasonStopped: z.lazy(() => codeableConceptSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - arm: researchStudyArmSchema.array().optional(), - objective: researchStudyObjectiveSchema.array().optional(), - }), -) - -export const researchSubjectSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('ResearchSubject'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'candidate', - 'eligible', - 'follow-up', - 'ineligible', - 'not-registered', - 'off-study', - 'on-study', - 'on-study-intervention', - 'on-study-observation', - 'pending-on-study', - 'potential-candidate', - 'screening', - 'withdrawn', - ]), - _status: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - study: z.lazy(() => referenceSchema), - individual: z.lazy(() => referenceSchema), - assignedArm: stringSchema.optional(), - _assignedArm: z.lazy(() => elementSchema).optional(), - actualArm: stringSchema.optional(), - _actualArm: z.lazy(() => elementSchema).optional(), - consent: z.lazy(() => referenceSchema).optional(), - }), -) - -export const riskAssessmentPredictionSchema: z.ZodType = +export const researchSubjectSchema: z.ZodType> = z.lazy(() => z .strictObject({ + resourceType: z.literal('ResearchSubject'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - outcome: z.lazy(() => codeableConceptSchema).optional(), - probabilityDecimal: decimalSchema.optional(), - _probabilityDecimal: z.lazy(() => elementSchema).optional(), - probabilityRange: z.lazy(() => rangeSchema).optional(), - qualitativeRisk: z.lazy(() => codeableConceptSchema).optional(), - relativeRisk: decimalSchema.optional(), - _relativeRisk: z.lazy(() => elementSchema).optional(), - whenPeriod: z.lazy(() => periodSchema).optional(), - whenRange: z.lazy(() => rangeSchema).optional(), - rationale: stringSchema.optional(), - _rationale: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'candidate', + 'eligible', + 'follow-up', + 'ineligible', + 'not-registered', + 'off-study', + 'on-study', + 'on-study-intervention', + 'on-study-observation', + 'pending-on-study', + 'potential-candidate', + 'screening', + 'withdrawn', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + study: z.lazy(() => referenceSchema), + individual: z.lazy(() => referenceSchema), + assignedArm: stringSchema.optional(), + _assignedArm: z.lazy(() => elementSchema).optional(), + actualArm: stringSchema.optional(), + _actualArm: z.lazy(() => elementSchema).optional(), + consent: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['probabilityDecimal', 'probabilityRange'], - required: false, - }, - { names: ['whenPeriod', 'whenRange'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const riskAssessmentSchema: z.ZodType = z.lazy(() => +export const riskAssessmentPredictionSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('RiskAssessment'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - basedOn: z.lazy(() => referenceSchema).optional(), - parent: z.lazy(() => referenceSchema).optional(), - status: z.enum([ - 'amended', - 'cancelled', - 'corrected', - 'entered-in-error', - 'final', - 'preliminary', - 'registered', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - condition: z.lazy(() => referenceSchema).optional(), - performer: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - basis: z - .lazy(() => referenceSchema) - .array() - .optional(), - prediction: riskAssessmentPredictionSchema.array().optional(), - mitigation: stringSchema.optional(), - _mitigation: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), + outcome: z.lazy(() => codeableConceptSchema).optional(), + probabilityDecimal: decimalSchema.optional(), + _probabilityDecimal: z.lazy(() => elementSchema).optional(), + probabilityRange: z.lazy(() => rangeSchema).optional(), + qualitativeRisk: z.lazy(() => codeableConceptSchema).optional(), + relativeRisk: decimalSchema.optional(), + _relativeRisk: z.lazy(() => elementSchema).optional(), + whenPeriod: z.lazy(() => periodSchema).optional(), + whenRange: z.lazy(() => rangeSchema).optional(), + rationale: stringSchema.optional(), + _rationale: z.lazy(() => elementSchema).optional(), }) .refine( (value) => [ { - names: ['occurrenceDateTime', 'occurrencePeriod'], + alternatives: [ + ['probabilityDecimal', '_probabilityDecimal'], + ['probabilityRange'], + ], required: false, }, + { alternatives: [['whenPeriod'], ['whenRange']], required: false }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -24891,20 +38984,157 @@ export const riskAssessmentSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> + +export const riskAssessmentSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('RiskAssessment'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + basedOn: z.lazy(() => referenceSchema).optional(), + parent: z.lazy(() => referenceSchema).optional(), + status: z + .enum([ + 'amended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'preliminary', + 'registered', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + condition: z.lazy(() => referenceSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + basis: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + prediction: riskAssessmentPredictionSchema.array().min(1).optional(), + mitigation: stringSchema.optional(), + _mitigation: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const riskEvidenceSynthesisSampleSizeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const riskEvidenceSynthesisSampleSizeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), @@ -24912,20 +39142,28 @@ export const riskEvidenceSynthesisSampleSizeSchema: z.ZodType elementSchema).optional(), numberOfParticipants: integerSchema.optional(), _numberOfParticipants: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const riskEvidenceSynthesisRiskEstimateSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const riskEvidenceSynthesisRiskEstimateSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), @@ -24940,48 +39178,68 @@ export const riskEvidenceSynthesisRiskEstimateSchema: z.ZodType Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const riskEvidenceSynthesisCertaintySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const riskEvidenceSynthesisCertaintySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), rating: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), note: z .lazy(() => annotationSchema) .array() + .min(1) .optional(), certaintySubcomponent: riskEvidenceSynthesisCertaintyCertaintySubcomponentSchema .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const riskEvidenceSynthesisRiskEstimatePrecisionEstimateSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const riskEvidenceSynthesisRiskEstimatePrecisionEstimateSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), type: z.lazy(() => codeableConceptSchema).optional(), level: decimalSchema.optional(), @@ -24990,36 +39248,56 @@ export const riskEvidenceSynthesisRiskEstimatePrecisionEstimateSchema: z.ZodType _from: z.lazy(() => elementSchema).optional(), to: decimalSchema.optional(), _to: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const riskEvidenceSynthesisCertaintyCertaintySubcomponentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const riskEvidenceSynthesisCertaintyCertaintySubcomponentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), type: z.lazy(() => codeableConceptSchema).optional(), rating: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), note: z .lazy(() => annotationSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const riskEvidenceSynthesisSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const riskEvidenceSynthesisSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('RiskEvidenceSynthesis'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -25032,20 +39310,24 @@ export const riskEvidenceSynthesisSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), url: uriSchema.optional(), _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), version: stringSchema.optional(), _version: z.lazy(() => elementSchema).optional(), @@ -25053,7 +39335,7 @@ export const riskEvidenceSynthesisSchema: z.ZodType = _name: z.lazy(() => elementSchema).optional(), title: stringSchema.optional(), _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), _status: z.lazy(() => elementSchema).optional(), date: dateTimeSchema.optional(), _date: z.lazy(() => elementSchema).optional(), @@ -25062,20 +39344,24 @@ export const riskEvidenceSynthesisSchema: z.ZodType = contact: z .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), note: z .lazy(() => annotationSchema) .array() + .min(1) .optional(), useContext: z .lazy(() => usageContextSchema) .array() + .min(1) .optional(), jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), copyright: markdownSchema.optional(), _copyright: z.lazy(() => elementSchema).optional(), @@ -25087,26 +39373,32 @@ export const riskEvidenceSynthesisSchema: z.ZodType = topic: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), author: z .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), editor: z .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), reviewer: z .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), endorser: z .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), relatedArtifact: z .lazy(() => relatedArtifactSchema) .array() + .min(1) .optional(), synthesisType: z.lazy(() => codeableConceptSchema).optional(), studyType: z.lazy(() => codeableConceptSchema).optional(), @@ -25115,107 +39407,85 @@ export const riskEvidenceSynthesisSchema: z.ZodType = outcome: z.lazy(() => referenceSchema), sampleSize: riskEvidenceSynthesisSampleSizeSchema.optional(), riskEstimate: riskEvidenceSynthesisRiskEstimateSchema.optional(), - certainty: riskEvidenceSynthesisCertaintySchema.array().optional(), - }), - ) - -export const sampledDataSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - origin: z.lazy(() => quantitySchema), - period: decimalSchema, - _period: z.lazy(() => elementSchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - lowerLimit: decimalSchema.optional(), - _lowerLimit: z.lazy(() => elementSchema).optional(), - upperLimit: decimalSchema.optional(), - _upperLimit: z.lazy(() => elementSchema).optional(), - dimensions: positiveIntSchema, - _dimensions: z.lazy(() => elementSchema).optional(), - data: stringSchema.optional(), - _data: z.lazy(() => elementSchema).optional(), - }), -) - -export const scheduleSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Schedule'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - serviceCategory: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - serviceType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - specialty: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - actor: z - .lazy(() => referenceSchema) - .array() - .min(1), - planningHorizon: z.lazy(() => periodSchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - }), -) - -export const searchParameterComponentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - definition: canonicalSchema, - _definition: z.lazy(() => elementSchema).optional(), - expression: stringSchema, - _expression: z.lazy(() => elementSchema).optional(), - }), - ) + certainty: riskEvidenceSynthesisCertaintySchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const searchParameterSchema: z.ZodType = z.lazy( +export const sampledDataSchema: z.ZodType> = z.lazy( () => - z.strictObject({ - resourceType: z.literal('SearchParameter'), + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + origin: z.lazy(() => quantitySchema), + period: decimalSchema.optional(), + _period: z.lazy(() => elementSchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + lowerLimit: decimalSchema.optional(), + _lowerLimit: z.lazy(() => elementSchema).optional(), + upperLimit: decimalSchema.optional(), + _upperLimit: z.lazy(() => elementSchema).optional(), + dimensions: positiveIntSchema.optional(), + _dimensions: z.lazy(() => elementSchema).optional(), + data: stringSchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'period', shadow: '_period' }, + { name: 'dimensions', shadow: '_dimensions' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const scheduleSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Schedule'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -25227,429 +39497,1255 @@ export const searchParameterSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema, - _url: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - derivedFrom: canonicalSchema.optional(), - _derivedFrom: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema, - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - code: codeSchema, - _code: z.lazy(() => elementSchema).optional(), - base: z - .enum([ - 'Account', - 'ActivityDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'EffectEvidenceSynthesis', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProduct', - 'MedicinalProductAuthorization', - 'MedicinalProductContraindication', - 'MedicinalProductIndication', - 'MedicinalProductIngredient', - 'MedicinalProductInteraction', - 'MedicinalProductManufactured', - 'MedicinalProductPackaged', - 'MedicinalProductPharmaceutical', - 'MedicinalProductUndesirableEffect', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'RiskEvidenceSynthesis', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'Substance', - 'SubstanceNucleicAcid', - 'SubstancePolymer', - 'SubstanceProtein', - 'SubstanceReferenceInformation', - 'SubstanceSourceMaterial', - 'SubstanceSpecification', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]) + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + serviceCategory: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + serviceType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + actor: z + .lazy(() => referenceSchema) .array() .min(1), - _base: z - .lazy(() => elementSchema) + planningHorizon: z.lazy(() => periodSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const searchParameterComponentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + definition: canonicalSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'definition', shadow: '_definition' }, + { name: 'expression', shadow: '_expression' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const searchParameterSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('SearchParameter'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + derivedFrom: canonicalSchema.optional(), + _derivedFrom: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + base: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .nullable() + .array() + .min(1) + .optional(), + _base: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + type: z + .enum([ + 'composite', + 'date', + 'number', + 'quantity', + 'reference', + 'special', + 'string', + 'token', + 'uri', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + xpath: stringSchema.optional(), + _xpath: z.lazy(() => elementSchema).optional(), + xpathUsage: z + .enum(['distance', 'nearby', 'normal', 'other', 'phonetic']) + .optional(), + _xpathUsage: z.lazy(() => elementSchema).optional(), + target: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .nullable() + .array() + .min(1) + .optional(), + _target: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + multipleOr: booleanSchema.optional(), + _multipleOr: z.lazy(() => elementSchema).optional(), + multipleAnd: booleanSchema.optional(), + _multipleAnd: z.lazy(() => elementSchema).optional(), + comparator: z + .enum(['ap', 'eb', 'eq', 'ge', 'gt', 'le', 'lt', 'ne', 'sa']) + .nullable() + .array() + .min(1) + .optional(), + _comparator: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + modifier: z + .enum([ + 'above', + 'below', + 'contains', + 'exact', + 'identifier', + 'in', + 'missing', + 'not', + 'not-in', + 'ofType', + 'text', + 'type', + ]) + .nullable() + .array() + .min(1) + .optional(), + _modifier: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + chain: stringSchema.nullable().array().min(1).optional(), + _chain: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + component: searchParameterComponentSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + { name: 'description', shadow: '_description' }, + { name: 'code', shadow: '_code' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'base', shadow: '_base', required: true }, + { name: 'target', shadow: '_target', required: false }, + { name: 'comparator', shadow: '_comparator', required: false }, + { name: 'modifier', shadow: '_modifier', required: false }, + { name: 'chain', shadow: '_chain', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const serviceRequestSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ServiceRequest'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + instantiatesCanonical: canonicalSchema + .nullable() + .array() + .min(1) + .optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + replaces: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + requisition: z.lazy(() => identifierSchema).optional(), + status: z + .enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + intent: z + .enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]) + .optional(), + _intent: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + orderDetail: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + quantityQuantity: z.lazy(() => quantitySchema).optional(), + quantityRatio: z.lazy(() => ratioSchema).optional(), + quantityRange: z.lazy(() => rangeSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + asNeededBoolean: booleanSchema.optional(), + _asNeededBoolean: z.lazy(() => elementSchema).optional(), + asNeededCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performerType: z.lazy(() => codeableConceptSchema).optional(), + performer: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + locationCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + locationReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + insurance: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + specimen: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + bodySite: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + patientInstruction: stringSchema.optional(), + _patientInstruction: z.lazy(() => elementSchema).optional(), + relevantHistory: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['quantityQuantity'], + ['quantityRatio'], + ['quantityRange'], + ], + required: false, + }, + { + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], + ['occurrenceTiming'], + ], + required: false, + }, + { + alternatives: [ + ['asNeededBoolean', '_asNeededBoolean'], + ['asNeededCodeableConcept'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'intent', shadow: '_intent' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const signatureSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum([ - 'composite', - 'date', - 'number', - 'quantity', - 'reference', - 'special', - 'string', - 'token', - 'uri', - ]), - _type: z.lazy(() => elementSchema).optional(), - expression: stringSchema.optional(), - _expression: z.lazy(() => elementSchema).optional(), - xpath: stringSchema.optional(), - _xpath: z.lazy(() => elementSchema).optional(), - xpathUsage: z - .enum(['distance', 'nearby', 'normal', 'other', 'phonetic']) - .optional(), - _xpathUsage: z.lazy(() => elementSchema).optional(), - target: z - .enum([ - 'Account', - 'ActivityDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'EffectEvidenceSynthesis', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProduct', - 'MedicinalProductAuthorization', - 'MedicinalProductContraindication', - 'MedicinalProductIndication', - 'MedicinalProductIngredient', - 'MedicinalProductInteraction', - 'MedicinalProductManufactured', - 'MedicinalProductPackaged', - 'MedicinalProductPharmaceutical', - 'MedicinalProductUndesirableEffect', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'RiskEvidenceSynthesis', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'Substance', - 'SubstanceNucleicAcid', - 'SubstancePolymer', - 'SubstanceProtein', - 'SubstanceReferenceInformation', - 'SubstanceSourceMaterial', - 'SubstanceSpecification', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]) + type: z + .lazy(() => codingSchema) + .array() + .min(1), + when: instantSchema.optional(), + _when: z.lazy(() => elementSchema).optional(), + who: z.lazy(() => referenceSchema), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + targetFormat: codeSchema.optional(), + _targetFormat: z.lazy(() => elementSchema).optional(), + sigFormat: codeSchema.optional(), + _sigFormat: z.lazy(() => elementSchema).optional(), + data: base64BinarySchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'when', shadow: '_when' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const slotSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Slot'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) .array() + .min(1) .optional(), - _target: z - .lazy(() => elementSchema) + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - multipleOr: booleanSchema.optional(), - _multipleOr: z.lazy(() => elementSchema).optional(), - multipleAnd: booleanSchema.optional(), - _multipleAnd: z.lazy(() => elementSchema).optional(), - comparator: z - .enum(['ap', 'eb', 'eq', 'ge', 'gt', 'le', 'lt', 'ne', 'sa']) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - _comparator: z - .lazy(() => elementSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - modifier: z + serviceCategory: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + serviceType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + appointmentType: z.lazy(() => codeableConceptSchema).optional(), + schedule: z.lazy(() => referenceSchema), + status: z .enum([ - 'above', - 'below', - 'contains', - 'exact', - 'identifier', - 'in', - 'missing', - 'not', - 'not-in', - 'ofType', - 'text', - 'type', + 'busy', + 'busy-tentative', + 'busy-unavailable', + 'entered-in-error', + 'free', ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + start: instantSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: instantSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + overbooked: booleanSchema.optional(), + _overbooked: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'start', shadow: '_start' }, + { name: 'end', shadow: '_end' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const specimenCollectionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - _modifier: z - .lazy(() => elementSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - chain: stringSchema.array().optional(), - _chain: z - .lazy(() => elementSchema) + collector: z.lazy(() => referenceSchema).optional(), + collectedDateTime: dateTimeSchema.optional(), + _collectedDateTime: z.lazy(() => elementSchema).optional(), + collectedPeriod: z.lazy(() => periodSchema).optional(), + duration: z.lazy(() => durationSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + fastingStatusCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + fastingStatusDuration: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['collectedDateTime', '_collectedDateTime'], + ['collectedPeriod'], + ], + required: false, + }, + { + alternatives: [ + ['fastingStatusCodeableConcept'], + ['fastingStatusDuration'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const specimenProcessingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + procedure: z.lazy(() => codeableConceptSchema).optional(), + additive: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + timeDateTime: dateTimeSchema.optional(), + _timeDateTime: z.lazy(() => elementSchema).optional(), + timePeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['timeDateTime', '_timeDateTime'], ['timePeriod']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const specimenContainerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - component: searchParameterComponentSchema.array().optional(), - }), -) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + capacity: z.lazy(() => quantitySchema).optional(), + specimenQuantity: z.lazy(() => quantitySchema).optional(), + additiveCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + additiveReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['additiveCodeableConcept'], ['additiveReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const serviceRequestSchema: z.ZodType = z.lazy(() => +export const specimenSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('ServiceRequest'), + resourceType: z.literal('Specimen'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -25661,158 +40757,171 @@ export const serviceRequestSchema: z.ZodType = z.lazy(() => contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) - .array() + accessionIdentifier: z.lazy(() => identifierSchema).optional(), + status: z + .enum([ + 'available', + 'entered-in-error', + 'unavailable', + 'unsatisfactory', + ]) .optional(), - basedOn: z + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + receivedTime: dateTimeSchema.optional(), + _receivedTime: z.lazy(() => elementSchema).optional(), + parent: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - replaces: z + request: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - requisition: z.lazy(() => identifierSchema).optional(), - status: z.enum([ - 'active', - 'completed', - 'draft', - 'entered-in-error', - 'on-hold', - 'revoked', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - intent: z.enum([ - 'directive', - 'filler-order', - 'instance-order', - 'option', - 'order', - 'original-order', - 'plan', - 'proposal', - 'reflex-order', - ]), - _intent: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - orderDetail: z + collection: specimenCollectionSchema.optional(), + processing: specimenProcessingSchema.array().min(1).optional(), + container: specimenContainerSchema.array().min(1).optional(), + condition: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - quantityQuantity: z.lazy(() => quantitySchema).optional(), - quantityRatio: z.lazy(() => ratioSchema).optional(), - quantityRange: z.lazy(() => rangeSchema).optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - occurrenceTiming: z.lazy(() => timingSchema).optional(), - asNeededBoolean: booleanSchema.optional(), - _asNeededBoolean: z.lazy(() => elementSchema).optional(), - asNeededCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: z.lazy(() => elementSchema).optional(), - requester: z.lazy(() => referenceSchema).optional(), - performerType: z.lazy(() => codeableConceptSchema).optional(), - performer: z - .lazy(() => referenceSchema) + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - locationCode: z - .lazy(() => codeableConceptSchema) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const specimenDefinitionTypeTestedSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - locationReference: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - reasonCode: z + isDerived: booleanSchema.optional(), + _isDerived: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + preference: z.enum(['alternate', 'preferred']).optional(), + _preference: z.lazy(() => elementSchema).optional(), + container: specimenDefinitionTypeTestedContainerSchema.optional(), + requirement: stringSchema.optional(), + _requirement: z.lazy(() => elementSchema).optional(), + retentionTime: z.lazy(() => durationSchema).optional(), + rejectionCriterion: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - insurance: z - .lazy(() => referenceSchema) - .array() - .optional(), - supportingInfo: z - .lazy(() => referenceSchema) - .array() - .optional(), - specimen: z - .lazy(() => referenceSchema) + handling: specimenDefinitionTypeTestedHandlingSchema .array() + .min(1) .optional(), - bodySite: z - .lazy(() => codeableConceptSchema) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'preference', shadow: '_preference' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const specimenDefinitionTypeTestedContainerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - note: z - .lazy(() => annotationSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - patientInstruction: stringSchema.optional(), - _patientInstruction: z.lazy(() => elementSchema).optional(), - relevantHistory: z - .lazy(() => referenceSchema) + material: z.lazy(() => codeableConceptSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + cap: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + capacity: z.lazy(() => quantitySchema).optional(), + minimumVolumeQuantity: z.lazy(() => quantitySchema).optional(), + minimumVolumeString: stringSchema.optional(), + _minimumVolumeString: z.lazy(() => elementSchema).optional(), + additive: specimenDefinitionTypeTestedContainerAdditiveSchema .array() + .min(1) .optional(), + preparation: stringSchema.optional(), + _preparation: z.lazy(() => elementSchema).optional(), }) .refine( (value) => [ { - names: ['quantityQuantity', 'quantityRatio', 'quantityRange'], - required: false, - }, - { - names: [ - 'occurrenceDateTime', - 'occurrencePeriod', - 'occurrenceTiming', + alternatives: [ + ['minimumVolumeQuantity'], + ['minimumVolumeString', '_minimumVolumeString'], ], required: false, }, - { - names: ['asNeededBoolean', 'asNeededCodeableConcept'], - required: false, - }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -25820,424 +40929,93 @@ export const serviceRequestSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const signatureSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z - .lazy(() => codingSchema) - .array() - .min(1), - when: instantSchema, - _when: z.lazy(() => elementSchema).optional(), - who: z.lazy(() => referenceSchema), - onBehalfOf: z.lazy(() => referenceSchema).optional(), - targetFormat: codeSchema.optional(), - _targetFormat: z.lazy(() => elementSchema).optional(), - sigFormat: codeSchema.optional(), - _sigFormat: z.lazy(() => elementSchema).optional(), - data: base64BinarySchema.optional(), - _data: z.lazy(() => elementSchema).optional(), - }), -) - -export const slotSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Slot'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - serviceCategory: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - serviceType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - specialty: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - appointmentType: z.lazy(() => codeableConceptSchema).optional(), - schedule: z.lazy(() => referenceSchema), - status: z.enum([ - 'busy', - 'busy-tentative', - 'busy-unavailable', - 'entered-in-error', - 'free', - ]), - _status: z.lazy(() => elementSchema).optional(), - start: instantSchema, - _start: z.lazy(() => elementSchema).optional(), - end: instantSchema, - _end: z.lazy(() => elementSchema).optional(), - overbooked: booleanSchema.optional(), - _overbooked: z.lazy(() => elementSchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - }), -) - -export const specimenCollectionSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - collector: z.lazy(() => referenceSchema).optional(), - collectedDateTime: dateTimeSchema.optional(), - _collectedDateTime: z.lazy(() => elementSchema).optional(), - collectedPeriod: z.lazy(() => periodSchema).optional(), - duration: z.lazy(() => durationSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), - fastingStatusCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - fastingStatusDuration: z.lazy(() => durationSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['collectedDateTime', 'collectedPeriod'], - required: false, - }, - { - names: ['fastingStatusCodeableConcept', 'fastingStatusDuration'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const specimenProcessingSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - procedure: z.lazy(() => codeableConceptSchema).optional(), - additive: z - .lazy(() => referenceSchema) - .array() - .optional(), - timeDateTime: dateTimeSchema.optional(), - _timeDateTime: z.lazy(() => elementSchema).optional(), - timePeriod: z.lazy(() => periodSchema).optional(), - }) - .refine( - (value) => - [{ names: ['timeDateTime', 'timePeriod'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) +) as unknown as z.ZodType> -export const specimenContainerSchema: z.ZodType = z.lazy( - () => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - capacity: z.lazy(() => quantitySchema).optional(), - specimenQuantity: z.lazy(() => quantitySchema).optional(), - additiveCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - additiveReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['additiveCodeableConcept', 'additiveReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const specimenSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Specimen'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - accessionIdentifier: z.lazy(() => identifierSchema).optional(), - status: z - .enum(['available', 'entered-in-error', 'unavailable', 'unsatisfactory']) - .optional(), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - receivedTime: dateTimeSchema.optional(), - _receivedTime: z.lazy(() => elementSchema).optional(), - parent: z - .lazy(() => referenceSchema) - .array() - .optional(), - request: z - .lazy(() => referenceSchema) - .array() - .optional(), - collection: specimenCollectionSchema.optional(), - processing: specimenProcessingSchema.array().optional(), - container: specimenContainerSchema.array().optional(), - condition: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }), -) - -export const specimenDefinitionTypeTestedSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const specimenDefinitionTypeTestedHandlingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - isDerived: booleanSchema.optional(), - _isDerived: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - preference: z.enum(['alternate', 'preferred']), - _preference: z.lazy(() => elementSchema).optional(), - container: specimenDefinitionTypeTestedContainerSchema.optional(), - requirement: stringSchema.optional(), - _requirement: z.lazy(() => elementSchema).optional(), - retentionTime: z.lazy(() => durationSchema).optional(), - rejectionCriterion: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - handling: specimenDefinitionTypeTestedHandlingSchema.array().optional(), - }), - ) - -export const specimenDefinitionTypeTestedContainerSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - material: z.lazy(() => codeableConceptSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - cap: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - capacity: z.lazy(() => quantitySchema).optional(), - minimumVolumeQuantity: z.lazy(() => quantitySchema).optional(), - minimumVolumeString: stringSchema.optional(), - _minimumVolumeString: z.lazy(() => elementSchema).optional(), - additive: specimenDefinitionTypeTestedContainerAdditiveSchema - .array() - .optional(), - preparation: stringSchema.optional(), - _preparation: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['minimumVolumeQuantity', 'minimumVolumeString'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + temperatureQualifier: z.lazy(() => codeableConceptSchema).optional(), + temperatureRange: z.lazy(() => rangeSchema).optional(), + maxDuration: z.lazy(() => durationSchema).optional(), + instruction: stringSchema.optional(), + _instruction: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const specimenDefinitionTypeTestedHandlingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const specimenDefinitionTypeTestedContainerAdditiveSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - temperatureQualifier: z.lazy(() => codeableConceptSchema).optional(), - temperatureRange: z.lazy(() => rangeSchema).optional(), - maxDuration: z.lazy(() => durationSchema).optional(), - instruction: stringSchema.optional(), - _instruction: z.lazy(() => elementSchema).optional(), - }), - ) - -export const specimenDefinitionTypeTestedContainerAdditiveSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - additiveCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - additiveReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['additiveCodeableConcept', 'additiveReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( + additiveCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + additiveReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['additiveCodeableConcept'], ['additiveReference']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const specimenDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const specimenDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('SpecimenDefinition'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -26250,44 +41028,57 @@ export const specimenDefinitionSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z.lazy(() => identifierSchema).optional(), typeCollected: z.lazy(() => codeableConceptSchema).optional(), patientPreparation: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), timeAspect: stringSchema.optional(), _timeAspect: z.lazy(() => elementSchema).optional(), collection: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - typeTested: specimenDefinitionTypeTestedSchema.array().optional(), - }), - ) + typeTested: specimenDefinitionTypeTestedSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureDefinitionMappingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureDefinitionMappingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identity: idSchema, + identity: idSchema.optional(), _identity: z.lazy(() => elementSchema).optional(), uri: uriSchema.optional(), _uri: z.lazy(() => elementSchema).optional(), @@ -26295,69 +41086,134 @@ export const structureDefinitionMappingSchema: z.ZodType elementSchema).optional(), comment: stringSchema.optional(), _comment: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'identity', shadow: '_identity' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureDefinitionContextSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureDefinitionContextSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum(['element', 'extension', 'fhirpath']), + type: z.enum(['element', 'extension', 'fhirpath']).optional(), _type: z.lazy(() => elementSchema).optional(), - expression: stringSchema, + expression: stringSchema.optional(), _expression: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'type', shadow: '_type' }, + { name: 'expression', shadow: '_expression' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureDefinitionSnapshotSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureDefinitionSnapshotSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), element: z .lazy(() => elementDefinitionSchema) .array() .min(1), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureDefinitionDifferentialSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureDefinitionDifferentialSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), element: z .lazy(() => elementDefinitionSchema) .array() .min(1), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('StructureDefinition'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -26370,28 +41226,32 @@ export const structureDefinitionSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema, + url: uriSchema.optional(), _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), version: stringSchema.optional(), _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, + name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), title: stringSchema.optional(), _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), _status: z.lazy(() => elementSchema).optional(), experimental: booleanSchema.optional(), _experimental: z.lazy(() => elementSchema).optional(), @@ -26402,16 +41262,19 @@ export const structureDefinitionSchema: z.ZodType = contact: z .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), useContext: z .lazy(() => usageContextSchema) .array() + .min(1) .optional(), jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), purpose: markdownSchema.optional(), _purpose: z.lazy(() => elementSchema).optional(), @@ -26420,6 +41283,7 @@ export const structureDefinitionSchema: z.ZodType = keyword: z .lazy(() => codingSchema) .array() + .min(1) .optional(), fhirVersion: z .enum([ @@ -26448,18 +41312,22 @@ export const structureDefinitionSchema: z.ZodType = ]) .optional(), _fhirVersion: z.lazy(() => elementSchema).optional(), - mapping: structureDefinitionMappingSchema.array().optional(), - kind: z.enum(['complex-type', 'logical', 'primitive-type', 'resource']), + mapping: structureDefinitionMappingSchema.array().min(1).optional(), + kind: z + .enum(['complex-type', 'logical', 'primitive-type', 'resource']) + .optional(), _kind: z.lazy(() => elementSchema).optional(), - abstract: booleanSchema, + abstract: booleanSchema.optional(), _abstract: z.lazy(() => elementSchema).optional(), - context: structureDefinitionContextSchema.array().optional(), - contextInvariant: stringSchema.array().optional(), + context: structureDefinitionContextSchema.array().min(1).optional(), + contextInvariant: stringSchema.nullable().array().min(1).optional(), _contextInvariant: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - type: uriSchema, + type: uriSchema.optional(), _type: z.lazy(() => elementSchema).optional(), baseDefinition: canonicalSchema.optional(), _baseDefinition: z.lazy(() => elementSchema).optional(), @@ -26467,682 +41335,1253 @@ export const structureDefinitionSchema: z.ZodType = _derivation: z.lazy(() => elementSchema).optional(), snapshot: structureDefinitionSnapshotSchema.optional(), differential: structureDefinitionDifferentialSchema.optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + { name: 'kind', shadow: '_kind' }, + { name: 'abstract', shadow: '_abstract' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'contextInvariant', + shadow: '_contextInvariant', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureMapStructureSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureMapStructureSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: canonicalSchema, + url: canonicalSchema.optional(), _url: z.lazy(() => elementSchema).optional(), - mode: z.enum(['produced', 'queried', 'source', 'target']), + mode: z.enum(['produced', 'queried', 'source', 'target']).optional(), _mode: z.lazy(() => elementSchema).optional(), alias: stringSchema.optional(), _alias: z.lazy(() => elementSchema).optional(), documentation: stringSchema.optional(), _documentation: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'mode', shadow: '_mode' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureMapGroupSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const structureMapGroupSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: idSchema, + name: idSchema.optional(), _name: z.lazy(() => elementSchema).optional(), extends: idSchema.optional(), _extends: z.lazy(() => elementSchema).optional(), - typeMode: z.enum(['none', 'type-and-types', 'types']), + typeMode: z.enum(['none', 'type-and-types', 'types']).optional(), _typeMode: z.lazy(() => elementSchema).optional(), documentation: stringSchema.optional(), _documentation: z.lazy(() => elementSchema).optional(), input: structureMapGroupInputSchema.array().min(1), rule: structureMapGroupRuleSchema.array().min(1), - }), -) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'typeMode', shadow: '_typeMode' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureMapGroupInputSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureMapGroupInputSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: idSchema, + name: idSchema.optional(), _name: z.lazy(() => elementSchema).optional(), type: stringSchema.optional(), _type: z.lazy(() => elementSchema).optional(), - mode: z.enum(['source', 'target']), + mode: z.enum(['source', 'target']).optional(), _mode: z.lazy(() => elementSchema).optional(), documentation: stringSchema.optional(), _documentation: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'mode', shadow: '_mode' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureMapGroupRuleSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureMapGroupRuleSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: idSchema, + name: idSchema.optional(), _name: z.lazy(() => elementSchema).optional(), source: structureMapGroupRuleSourceSchema.array().min(1), - target: structureMapGroupRuleTargetSchema.array().optional(), + target: structureMapGroupRuleTargetSchema.array().min(1).optional(), rule: z .lazy(() => structureMapGroupRuleSchema) .array() + .min(1) .optional(), - dependent: structureMapGroupRuleDependentSchema.array().optional(), + dependent: structureMapGroupRuleDependentSchema.array().min(1).optional(), documentation: stringSchema.optional(), _documentation: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const structureMapGroupRuleSourceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + context: idSchema.optional(), + _context: z.lazy(() => elementSchema).optional(), + min: integerSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + type: stringSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + defaultValueBase64Binary: base64BinarySchema.optional(), + _defaultValueBase64Binary: z.lazy(() => elementSchema).optional(), + defaultValueBoolean: booleanSchema.optional(), + _defaultValueBoolean: z.lazy(() => elementSchema).optional(), + defaultValueCanonical: canonicalSchema.optional(), + _defaultValueCanonical: z.lazy(() => elementSchema).optional(), + defaultValueCode: codeSchema.optional(), + _defaultValueCode: z.lazy(() => elementSchema).optional(), + defaultValueDate: dateSchema.optional(), + _defaultValueDate: z.lazy(() => elementSchema).optional(), + defaultValueDateTime: dateTimeSchema.optional(), + _defaultValueDateTime: z.lazy(() => elementSchema).optional(), + defaultValueDecimal: decimalSchema.optional(), + _defaultValueDecimal: z.lazy(() => elementSchema).optional(), + defaultValueId: idSchema.optional(), + _defaultValueId: z.lazy(() => elementSchema).optional(), + defaultValueInstant: instantSchema.optional(), + _defaultValueInstant: z.lazy(() => elementSchema).optional(), + defaultValueInteger: integerSchema.optional(), + _defaultValueInteger: z.lazy(() => elementSchema).optional(), + defaultValueMarkdown: markdownSchema.optional(), + _defaultValueMarkdown: z.lazy(() => elementSchema).optional(), + defaultValueOid: oidSchema.optional(), + _defaultValueOid: z.lazy(() => elementSchema).optional(), + defaultValuePositiveInt: positiveIntSchema.optional(), + _defaultValuePositiveInt: z.lazy(() => elementSchema).optional(), + defaultValueString: stringSchema.optional(), + _defaultValueString: z.lazy(() => elementSchema).optional(), + defaultValueTime: timeSchema.optional(), + _defaultValueTime: z.lazy(() => elementSchema).optional(), + defaultValueUnsignedInt: unsignedIntSchema.optional(), + _defaultValueUnsignedInt: z.lazy(() => elementSchema).optional(), + defaultValueUri: uriSchema.optional(), + _defaultValueUri: z.lazy(() => elementSchema).optional(), + defaultValueUrl: urlSchema.optional(), + _defaultValueUrl: z.lazy(() => elementSchema).optional(), + defaultValueUuid: uuidSchema.optional(), + _defaultValueUuid: z.lazy(() => elementSchema).optional(), + defaultValueAddress: z.lazy(() => addressSchema).optional(), + defaultValueAge: z.lazy(() => ageSchema).optional(), + defaultValueAnnotation: z.lazy(() => annotationSchema).optional(), + defaultValueAttachment: z.lazy(() => attachmentSchema).optional(), + defaultValueCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + defaultValueCoding: z.lazy(() => codingSchema).optional(), + defaultValueContactPoint: z.lazy(() => contactPointSchema).optional(), + defaultValueCount: z.lazy(() => countSchema).optional(), + defaultValueDistance: z.lazy(() => distanceSchema).optional(), + defaultValueDuration: z.lazy(() => durationSchema).optional(), + defaultValueHumanName: z.lazy(() => humanNameSchema).optional(), + defaultValueIdentifier: z.lazy(() => identifierSchema).optional(), + defaultValueMoney: z.lazy(() => moneySchema).optional(), + defaultValuePeriod: z.lazy(() => periodSchema).optional(), + defaultValueQuantity: z.lazy(() => quantitySchema).optional(), + defaultValueRange: z.lazy(() => rangeSchema).optional(), + defaultValueRatio: z.lazy(() => ratioSchema).optional(), + defaultValueReference: z.lazy(() => referenceSchema).optional(), + defaultValueSampledData: z.lazy(() => sampledDataSchema).optional(), + defaultValueSignature: z.lazy(() => signatureSchema).optional(), + defaultValueTiming: z.lazy(() => timingSchema).optional(), + defaultValueContactDetail: z.lazy(() => contactDetailSchema).optional(), + defaultValueContributor: z.lazy(() => contributorSchema).optional(), + defaultValueDataRequirement: z + .lazy(() => dataRequirementSchema) + .optional(), + defaultValueExpression: z.lazy(() => expressionSchema).optional(), + defaultValueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + defaultValueRelatedArtifact: z + .lazy(() => relatedArtifactSchema) + .optional(), + defaultValueTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + defaultValueUsageContext: z.lazy(() => usageContextSchema).optional(), + defaultValueDosage: z.lazy(() => dosageSchema).optional(), + defaultValueMeta: z.lazy(() => metaSchema).optional(), + element: stringSchema.optional(), + _element: z.lazy(() => elementSchema).optional(), + listMode: z + .enum(['first', 'last', 'not_first', 'not_last', 'only_one']) + .optional(), + _listMode: z.lazy(() => elementSchema).optional(), + variable: idSchema.optional(), + _variable: z.lazy(() => elementSchema).optional(), + condition: stringSchema.optional(), + _condition: z.lazy(() => elementSchema).optional(), + check: stringSchema.optional(), + _check: z.lazy(() => elementSchema).optional(), + logMessage: stringSchema.optional(), + _logMessage: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['defaultValueBase64Binary', '_defaultValueBase64Binary'], + ['defaultValueBoolean', '_defaultValueBoolean'], + ['defaultValueCanonical', '_defaultValueCanonical'], + ['defaultValueCode', '_defaultValueCode'], + ['defaultValueDate', '_defaultValueDate'], + ['defaultValueDateTime', '_defaultValueDateTime'], + ['defaultValueDecimal', '_defaultValueDecimal'], + ['defaultValueId', '_defaultValueId'], + ['defaultValueInstant', '_defaultValueInstant'], + ['defaultValueInteger', '_defaultValueInteger'], + ['defaultValueMarkdown', '_defaultValueMarkdown'], + ['defaultValueOid', '_defaultValueOid'], + ['defaultValuePositiveInt', '_defaultValuePositiveInt'], + ['defaultValueString', '_defaultValueString'], + ['defaultValueTime', '_defaultValueTime'], + ['defaultValueUnsignedInt', '_defaultValueUnsignedInt'], + ['defaultValueUri', '_defaultValueUri'], + ['defaultValueUrl', '_defaultValueUrl'], + ['defaultValueUuid', '_defaultValueUuid'], + ['defaultValueAddress'], + ['defaultValueAge'], + ['defaultValueAnnotation'], + ['defaultValueAttachment'], + ['defaultValueCodeableConcept'], + ['defaultValueCoding'], + ['defaultValueContactPoint'], + ['defaultValueCount'], + ['defaultValueDistance'], + ['defaultValueDuration'], + ['defaultValueHumanName'], + ['defaultValueIdentifier'], + ['defaultValueMoney'], + ['defaultValuePeriod'], + ['defaultValueQuantity'], + ['defaultValueRange'], + ['defaultValueRatio'], + ['defaultValueReference'], + ['defaultValueSampledData'], + ['defaultValueSignature'], + ['defaultValueTiming'], + ['defaultValueContactDetail'], + ['defaultValueContributor'], + ['defaultValueDataRequirement'], + ['defaultValueExpression'], + ['defaultValueParameterDefinition'], + ['defaultValueRelatedArtifact'], + ['defaultValueTriggerDefinition'], + ['defaultValueUsageContext'], + ['defaultValueDosage'], + ['defaultValueMeta'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'context', shadow: '_context' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const structureMapGroupRuleTargetSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + context: idSchema.optional(), + _context: z.lazy(() => elementSchema).optional(), + contextType: z.enum(['type', 'variable']).optional(), + _contextType: z.lazy(() => elementSchema).optional(), + element: stringSchema.optional(), + _element: z.lazy(() => elementSchema).optional(), + variable: idSchema.optional(), + _variable: z.lazy(() => elementSchema).optional(), + listMode: z + .enum(['collate', 'first', 'last', 'share']) + .nullable() + .array() + .min(1) + .optional(), + _listMode: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + listRuleId: idSchema.optional(), + _listRuleId: z.lazy(() => elementSchema).optional(), + transform: z + .enum([ + 'append', + 'c', + 'cast', + 'cc', + 'copy', + 'cp', + 'create', + 'dateOp', + 'escape', + 'evaluate', + 'id', + 'pointer', + 'qty', + 'reference', + 'translate', + 'truncate', + 'uuid', + ]) + .optional(), + _transform: z.lazy(() => elementSchema).optional(), + parameter: structureMapGroupRuleTargetParameterSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'listMode', shadow: '_listMode', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const structureMapGroupRuleDependentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: idSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + variable: stringSchema.nullable().array().min(1).optional(), + _variable: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'variable', shadow: '_variable', required: true }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const structureMapGroupRuleTargetParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueId', '_valueId'], + ['valueString', '_valueString'], + ['valueBoolean', '_valueBoolean'], + ['valueInteger', '_valueInteger'], + ['valueDecimal', '_valueDecimal'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureMapGroupRuleSourceSchema: z.ZodType = - z.lazy(() => +export const structureMapSchema: z.ZodType> = z.lazy( + () => z .strictObject({ + resourceType: z.literal('StructureMap'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - context: idSchema, - _context: z.lazy(() => elementSchema).optional(), - min: integerSchema.optional(), - _min: z.lazy(() => elementSchema).optional(), - max: stringSchema.optional(), - _max: z.lazy(() => elementSchema).optional(), - type: stringSchema.optional(), - _type: z.lazy(() => elementSchema).optional(), - defaultValueBase64Binary: base64BinarySchema.optional(), - _defaultValueBase64Binary: z.lazy(() => elementSchema).optional(), - defaultValueBoolean: booleanSchema.optional(), - _defaultValueBoolean: z.lazy(() => elementSchema).optional(), - defaultValueCanonical: canonicalSchema.optional(), - _defaultValueCanonical: z.lazy(() => elementSchema).optional(), - defaultValueCode: codeSchema.optional(), - _defaultValueCode: z.lazy(() => elementSchema).optional(), - defaultValueDate: dateSchema.optional(), - _defaultValueDate: z.lazy(() => elementSchema).optional(), - defaultValueDateTime: dateTimeSchema.optional(), - _defaultValueDateTime: z.lazy(() => elementSchema).optional(), - defaultValueDecimal: decimalSchema.optional(), - _defaultValueDecimal: z.lazy(() => elementSchema).optional(), - defaultValueId: idSchema.optional(), - _defaultValueId: z.lazy(() => elementSchema).optional(), - defaultValueInstant: instantSchema.optional(), - _defaultValueInstant: z.lazy(() => elementSchema).optional(), - defaultValueInteger: integerSchema.optional(), - _defaultValueInteger: z.lazy(() => elementSchema).optional(), - defaultValueMarkdown: markdownSchema.optional(), - _defaultValueMarkdown: z.lazy(() => elementSchema).optional(), - defaultValueOid: oidSchema.optional(), - _defaultValueOid: z.lazy(() => elementSchema).optional(), - defaultValuePositiveInt: positiveIntSchema.optional(), - _defaultValuePositiveInt: z.lazy(() => elementSchema).optional(), - defaultValueString: stringSchema.optional(), - _defaultValueString: z.lazy(() => elementSchema).optional(), - defaultValueTime: timeSchema.optional(), - _defaultValueTime: z.lazy(() => elementSchema).optional(), - defaultValueUnsignedInt: unsignedIntSchema.optional(), - _defaultValueUnsignedInt: z.lazy(() => elementSchema).optional(), - defaultValueUri: uriSchema.optional(), - _defaultValueUri: z.lazy(() => elementSchema).optional(), - defaultValueUrl: urlSchema.optional(), - _defaultValueUrl: z.lazy(() => elementSchema).optional(), - defaultValueUuid: uuidSchema.optional(), - _defaultValueUuid: z.lazy(() => elementSchema).optional(), - defaultValueAddress: z.lazy(() => addressSchema).optional(), - defaultValueAge: z.lazy(() => ageSchema).optional(), - defaultValueAnnotation: z.lazy(() => annotationSchema).optional(), - defaultValueAttachment: z.lazy(() => attachmentSchema).optional(), - defaultValueCodeableConcept: z - .lazy(() => codeableConceptSchema) + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) .optional(), - defaultValueCoding: z.lazy(() => codingSchema).optional(), - defaultValueContactPoint: z.lazy(() => contactPointSchema).optional(), - defaultValueCount: z.lazy(() => countSchema).optional(), - defaultValueDistance: z.lazy(() => distanceSchema).optional(), - defaultValueDuration: z.lazy(() => durationSchema).optional(), - defaultValueHumanName: z.lazy(() => humanNameSchema).optional(), - defaultValueIdentifier: z.lazy(() => identifierSchema).optional(), - defaultValueMoney: z.lazy(() => moneySchema).optional(), - defaultValuePeriod: z.lazy(() => periodSchema).optional(), - defaultValueQuantity: z.lazy(() => quantitySchema).optional(), - defaultValueRange: z.lazy(() => rangeSchema).optional(), - defaultValueRatio: z.lazy(() => ratioSchema).optional(), - defaultValueReference: z.lazy(() => referenceSchema).optional(), - defaultValueSampledData: z.lazy(() => sampledDataSchema).optional(), - defaultValueSignature: z.lazy(() => signatureSchema).optional(), - defaultValueTiming: z.lazy(() => timingSchema).optional(), - defaultValueContactDetail: z.lazy(() => contactDetailSchema).optional(), - defaultValueContributor: z.lazy(() => contributorSchema).optional(), - defaultValueDataRequirement: z - .lazy(() => dataRequirementSchema) + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) .optional(), - defaultValueExpression: z.lazy(() => expressionSchema).optional(), - defaultValueParameterDefinition: z - .lazy(() => parameterDefinitionSchema) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) .optional(), - defaultValueRelatedArtifact: z - .lazy(() => relatedArtifactSchema) + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) .optional(), - defaultValueTriggerDefinition: z - .lazy(() => triggerDefinitionSchema) + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + structure: structureMapStructureSchema.array().min(1).optional(), + import: canonicalSchema.nullable().array().min(1).optional(), + _import: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) .optional(), - defaultValueUsageContext: z.lazy(() => usageContextSchema).optional(), - defaultValueDosage: z.lazy(() => dosageSchema).optional(), - defaultValueMeta: z.lazy(() => metaSchema).optional(), - element: stringSchema.optional(), - _element: z.lazy(() => elementSchema).optional(), - listMode: z - .enum(['first', 'last', 'not_first', 'not_last', 'only_one']) - .optional(), - _listMode: z.lazy(() => elementSchema).optional(), - variable: idSchema.optional(), - _variable: z.lazy(() => elementSchema).optional(), - condition: stringSchema.optional(), - _condition: z.lazy(() => elementSchema).optional(), - check: stringSchema.optional(), - _check: z.lazy(() => elementSchema).optional(), - logMessage: stringSchema.optional(), - _logMessage: z.lazy(() => elementSchema).optional(), + group: structureMapGroupSchema.array().min(1), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'import', shadow: '_import', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } }) .refine( - (value) => - [ - { - names: [ - 'defaultValueBase64Binary', - 'defaultValueBoolean', - 'defaultValueCanonical', - 'defaultValueCode', - 'defaultValueDate', - 'defaultValueDateTime', - 'defaultValueDecimal', - 'defaultValueId', - 'defaultValueInstant', - 'defaultValueInteger', - 'defaultValueMarkdown', - 'defaultValueOid', - 'defaultValuePositiveInt', - 'defaultValueString', - 'defaultValueTime', - 'defaultValueUnsignedInt', - 'defaultValueUri', - 'defaultValueUrl', - 'defaultValueUuid', - 'defaultValueAddress', - 'defaultValueAge', - 'defaultValueAnnotation', - 'defaultValueAttachment', - 'defaultValueCodeableConcept', - 'defaultValueCoding', - 'defaultValueContactPoint', - 'defaultValueCount', - 'defaultValueDistance', - 'defaultValueDuration', - 'defaultValueHumanName', - 'defaultValueIdentifier', - 'defaultValueMoney', - 'defaultValuePeriod', - 'defaultValueQuantity', - 'defaultValueRange', - 'defaultValueRatio', - 'defaultValueReference', - 'defaultValueSampledData', - 'defaultValueSignature', - 'defaultValueTiming', - 'defaultValueContactDetail', - 'defaultValueContributor', - 'defaultValueDataRequirement', - 'defaultValueExpression', - 'defaultValueParameterDefinition', - 'defaultValueRelatedArtifact', - 'defaultValueTriggerDefinition', - 'defaultValueUsageContext', - 'defaultValueDosage', - 'defaultValueMeta', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> -export const structureMapGroupRuleTargetSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const subscriptionChannelSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - context: idSchema.optional(), - _context: z.lazy(() => elementSchema).optional(), - contextType: z.enum(['type', 'variable']).optional(), - _contextType: z.lazy(() => elementSchema).optional(), - element: stringSchema.optional(), - _element: z.lazy(() => elementSchema).optional(), - variable: idSchema.optional(), - _variable: z.lazy(() => elementSchema).optional(), - listMode: z - .enum(['collate', 'first', 'last', 'share']) - .array() - .optional(), - _listMode: z - .lazy(() => elementSchema) - .array() - .optional(), - listRuleId: idSchema.optional(), - _listRuleId: z.lazy(() => elementSchema).optional(), - transform: z - .enum([ - 'append', - 'c', - 'cast', - 'cc', - 'copy', - 'cp', - 'create', - 'dateOp', - 'escape', - 'evaluate', - 'id', - 'pointer', - 'qty', - 'reference', - 'translate', - 'truncate', - 'uuid', - ]) - .optional(), - _transform: z.lazy(() => elementSchema).optional(), - parameter: structureMapGroupRuleTargetParameterSchema.array().optional(), - }), - ) - -export const structureMapGroupRuleDependentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() + type: z + .enum(['email', 'message', 'rest-hook', 'sms', 'websocket']) .optional(), - name: idSchema, - _name: z.lazy(() => elementSchema).optional(), - variable: stringSchema.array().min(1), - _variable: z + _type: z.lazy(() => elementSchema).optional(), + endpoint: urlSchema.optional(), + _endpoint: z.lazy(() => elementSchema).optional(), + payload: codeSchema.optional(), + _payload: z.lazy(() => elementSchema).optional(), + header: stringSchema.nullable().array().min(1).optional(), + _header: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'header', shadow: '_header', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureMapGroupRuleTargetParameterSchema: z.ZodType = - z.lazy(() => +export const subscriptionSchema: z.ZodType> = z.lazy( + () => z .strictObject({ + resourceType: z.literal('Subscription'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - valueId: idSchema.optional(), - _valueId: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'error', 'off', 'requested']).optional(), + _status: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + end: instantSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + reason: stringSchema.optional(), + _reason: z.lazy(() => elementSchema).optional(), + criteria: stringSchema.optional(), + _criteria: z.lazy(() => elementSchema).optional(), + error: stringSchema.optional(), + _error: z.lazy(() => elementSchema).optional(), + channel: subscriptionChannelSchema, + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'reason', shadow: '_reason' }, + { name: 'criteria', shadow: '_criteria' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: [ - 'valueId', - 'valueString', - 'valueBoolean', - 'valueInteger', - 'valueDecimal', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> -export const structureMapSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('StructureMap'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema, - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - structure: structureMapStructureSchema.array().optional(), - import: canonicalSchema.array().optional(), - _import: z - .lazy(() => elementSchema) - .array() - .optional(), - group: structureMapGroupSchema.array().min(1), - }), -) - -export const subscriptionChannelSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceInstanceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum(['email', 'message', 'rest-hook', 'sms', 'websocket']), - _type: z.lazy(() => elementSchema).optional(), - endpoint: urlSchema.optional(), - _endpoint: z.lazy(() => elementSchema).optional(), - payload: codeSchema.optional(), - _payload: z.lazy(() => elementSchema).optional(), - header: stringSchema.array().optional(), - _header: z - .lazy(() => elementSchema) + identifier: z.lazy(() => identifierSchema).optional(), + expiry: dateTimeSchema.optional(), + _expiry: z.lazy(() => elementSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const substanceIngredientSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), - ) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => ratioSchema).optional(), + substanceCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + substanceReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['substanceCodeableConcept'], + ['substanceReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const subscriptionSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Subscription'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - status: z.enum(['active', 'error', 'off', 'requested']), - _status: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactPointSchema) - .array() - .optional(), - end: instantSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), - reason: stringSchema, - _reason: z.lazy(() => elementSchema).optional(), - criteria: stringSchema, - _criteria: z.lazy(() => elementSchema).optional(), - error: stringSchema.optional(), - _error: z.lazy(() => elementSchema).optional(), - channel: subscriptionChannelSchema, - }), -) - -export const substanceInstanceSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const substanceSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Substance'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - expiry: dateTimeSchema.optional(), - _expiry: z.lazy(() => elementSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - }), -) - -export const substanceIngredientSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - quantity: z.lazy(() => ratioSchema).optional(), - substanceCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - substanceReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['substanceCodeableConcept', 'substanceReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + code: z.lazy(() => codeableConceptSchema), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + instance: substanceInstanceSchema.array().min(1).optional(), + ingredient: substanceIngredientSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Substance'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), - _status: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - instance: substanceInstanceSchema.array().optional(), - ingredient: substanceIngredientSchema.array().optional(), - }), -) - -export const substanceAmountReferenceRangeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceAmountReferenceRangeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), lowLimit: z.lazy(() => quantitySchema).optional(), highLimit: z.lazy(() => quantitySchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceAmountSchema: z.ZodType = z.lazy( - () => +export const substanceAmountSchema: z.ZodType> = + z.lazy(() => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), amountQuantity: z.lazy(() => quantitySchema).optional(), amountRange: z.lazy(() => rangeSchema).optional(), @@ -27157,12 +42596,19 @@ export const substanceAmountSchema: z.ZodType = z.lazy( (value) => [ { - names: ['amountQuantity', 'amountRange', 'amountString'], + alternatives: [ + ['amountQuantity'], + ['amountRange'], + ['amountString', '_amountString'], + ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -27170,20 +42616,30 @@ export const substanceAmountSchema: z.ZodType = z.lazy( message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), -) + ) as unknown as z.ZodType> -export const substanceNucleicAcidSubunitSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceNucleicAcidSubunitSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), subunit: integerSchema.optional(), _subunit: z.lazy(() => elementSchema).optional(), @@ -27194,22 +42650,33 @@ export const substanceNucleicAcidSubunitSchema: z.ZodType attachmentSchema).optional(), fivePrime: z.lazy(() => codeableConceptSchema).optional(), threePrime: z.lazy(() => codeableConceptSchema).optional(), - linkage: substanceNucleicAcidSubunitLinkageSchema.array().optional(), - sugar: substanceNucleicAcidSubunitSugarSchema.array().optional(), - }), - ) + linkage: substanceNucleicAcidSubunitLinkageSchema + .array() + .min(1) + .optional(), + sugar: substanceNucleicAcidSubunitSugarSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceNucleicAcidSubunitLinkageSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceNucleicAcidSubunitLinkageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), connectivity: stringSchema.optional(), _connectivity: z.lazy(() => elementSchema).optional(), @@ -27218,32 +42685,46 @@ export const substanceNucleicAcidSubunitLinkageSchema: z.ZodType elementSchema).optional(), residueSite: stringSchema.optional(), _residueSite: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceNucleicAcidSubunitSugarSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceNucleicAcidSubunitSugarSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z.lazy(() => identifierSchema).optional(), name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), residueSite: stringSchema.optional(), _residueSite: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceNucleicAcidSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceNucleicAcidSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('SubstanceNucleicAcid'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -27256,14 +42737,17 @@ export const substanceNucleicAcidSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), sequenceType: z.lazy(() => codeableConceptSchema).optional(), numberOfSubunits: integerSchema.optional(), @@ -27271,81 +42755,119 @@ export const substanceNucleicAcidSchema: z.ZodType = areaOfHybridisation: stringSchema.optional(), _areaOfHybridisation: z.lazy(() => elementSchema).optional(), oligoNucleotideType: z.lazy(() => codeableConceptSchema).optional(), - subunit: substanceNucleicAcidSubunitSchema.array().optional(), - }), - ) + subunit: substanceNucleicAcidSubunitSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substancePolymerMonomerSetSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substancePolymerMonomerSetSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), ratioType: z.lazy(() => codeableConceptSchema).optional(), startingMaterial: substancePolymerMonomerSetStartingMaterialSchema .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substancePolymerRepeatSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substancePolymerRepeatSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), numberOfUnits: integerSchema.optional(), _numberOfUnits: z.lazy(() => elementSchema).optional(), averageMolecularFormula: stringSchema.optional(), _averageMolecularFormula: z.lazy(() => elementSchema).optional(), repeatUnitAmountType: z.lazy(() => codeableConceptSchema).optional(), - repeatUnit: substancePolymerRepeatRepeatUnitSchema.array().optional(), - }), - ) + repeatUnit: substancePolymerRepeatRepeatUnitSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substancePolymerMonomerSetStartingMaterialSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substancePolymerMonomerSetStartingMaterialSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), material: z.lazy(() => codeableConceptSchema).optional(), type: z.lazy(() => codeableConceptSchema).optional(), isDefining: booleanSchema.optional(), _isDefining: z.lazy(() => elementSchema).optional(), amount: z.lazy(() => substanceAmountSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const substancePolymerRepeatRepeatUnitSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substancePolymerRepeatRepeatUnitSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), orientationOfPolymerisation: z .lazy(() => codeableConceptSchema) @@ -27356,101 +42878,189 @@ export const substancePolymerRepeatRepeatUnitSchema: z.ZodType Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substancePolymerRepeatRepeatUnitDegreeOfPolymerisationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substancePolymerRepeatRepeatUnitDegreeOfPolymerisationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), degree: z.lazy(() => codeableConceptSchema).optional(), amount: z.lazy(() => substanceAmountSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const substancePolymerRepeatRepeatUnitStructuralRepresentationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substancePolymerRepeatRepeatUnitStructuralRepresentationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), type: z.lazy(() => codeableConceptSchema).optional(), representation: stringSchema.optional(), _representation: z.lazy(() => elementSchema).optional(), attachment: z.lazy(() => attachmentSchema).optional(), - }), - ) - -export const substancePolymerSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('SubstancePolymer'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - class: z.lazy(() => codeableConceptSchema).optional(), - geometry: z.lazy(() => codeableConceptSchema).optional(), - copolymerConnectivity: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - modification: stringSchema.array().optional(), - _modification: z - .lazy(() => elementSchema) - .array() - .optional(), - monomerSet: substancePolymerMonomerSetSchema.array().optional(), - repeat: substancePolymerRepeatSchema.array().optional(), - }), -) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const substanceProteinSubunitSchema: z.ZodType = +export const substancePolymerSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + resourceType: z.literal('SubstancePolymer'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + class: z.lazy(() => codeableConceptSchema).optional(), + geometry: z.lazy(() => codeableConceptSchema).optional(), + copolymerConnectivity: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + modification: stringSchema.nullable().array().min(1).optional(), + _modification: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + monomerSet: substancePolymerMonomerSetSchema.array().min(1).optional(), + repeat: substancePolymerRepeatSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'modification', shadow: '_modification', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const substanceProteinSubunitSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), subunit: integerSchema.optional(), _subunit: z.lazy(() => elementSchema).optional(), @@ -27465,163 +43075,276 @@ export const substanceProteinSubunitSchema: z.ZodType identifierSchema).optional(), cTerminalModification: stringSchema.optional(), _cTerminalModification: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceProteinSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('SubstanceProtein'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequenceType: z.lazy(() => codeableConceptSchema).optional(), - numberOfSubunits: integerSchema.optional(), - _numberOfSubunits: z.lazy(() => elementSchema).optional(), - disulfideLinkage: stringSchema.array().optional(), - _disulfideLinkage: z - .lazy(() => elementSchema) - .array() - .optional(), - subunit: substanceProteinSubunitSchema.array().optional(), - }), -) +export const substanceProteinSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('SubstanceProtein'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequenceType: z.lazy(() => codeableConceptSchema).optional(), + numberOfSubunits: integerSchema.optional(), + _numberOfSubunits: z.lazy(() => elementSchema).optional(), + disulfideLinkage: stringSchema.nullable().array().min(1).optional(), + _disulfideLinkage: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + subunit: substanceProteinSubunitSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'disulfideLinkage', + shadow: '_disulfideLinkage', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const substanceReferenceInformationGeneSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceReferenceInformationGeneSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), geneSequenceOrigin: z.lazy(() => codeableConceptSchema).optional(), gene: z.lazy(() => codeableConceptSchema).optional(), source: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceReferenceInformationGeneElementSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceReferenceInformationGeneElementSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), type: z.lazy(() => codeableConceptSchema).optional(), element: z.lazy(() => identifierSchema).optional(), source: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const substanceReferenceInformationClassificationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceReferenceInformationClassificationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), domain: z.lazy(() => codeableConceptSchema).optional(), classification: z.lazy(() => codeableConceptSchema).optional(), subtype: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), source: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const substanceReferenceInformationTargetSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - target: z.lazy(() => identifierSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - interaction: z.lazy(() => codeableConceptSchema).optional(), - organism: z.lazy(() => codeableConceptSchema).optional(), - organismType: z.lazy(() => codeableConceptSchema).optional(), - amountQuantity: z.lazy(() => quantitySchema).optional(), - amountRange: z.lazy(() => rangeSchema).optional(), - amountString: stringSchema.optional(), - _amountString: z.lazy(() => elementSchema).optional(), - amountType: z.lazy(() => codeableConceptSchema).optional(), - source: z - .lazy(() => referenceSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['amountQuantity', 'amountRange', 'amountString'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( +export const substanceReferenceInformationTargetSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + target: z.lazy(() => identifierSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + interaction: z.lazy(() => codeableConceptSchema).optional(), + organism: z.lazy(() => codeableConceptSchema).optional(), + organismType: z.lazy(() => codeableConceptSchema).optional(), + amountQuantity: z.lazy(() => quantitySchema).optional(), + amountRange: z.lazy(() => rangeSchema).optional(), + amountString: stringSchema.optional(), + _amountString: z.lazy(() => elementSchema).optional(), + amountType: z.lazy(() => codeableConceptSchema).optional(), + source: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['amountQuantity'], + ['amountRange'], + ['amountString', '_amountString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceReferenceInformationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceReferenceInformationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('SubstanceReferenceInformation'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -27634,57 +43357,83 @@ export const substanceReferenceInformationSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), comment: stringSchema.optional(), _comment: z.lazy(() => elementSchema).optional(), - gene: substanceReferenceInformationGeneSchema.array().optional(), + gene: substanceReferenceInformationGeneSchema.array().min(1).optional(), geneElement: substanceReferenceInformationGeneElementSchema .array() + .min(1) .optional(), classification: substanceReferenceInformationClassificationSchema .array() + .min(1) .optional(), - target: substanceReferenceInformationTargetSchema.array().optional(), - }), - ) + target: substanceReferenceInformationTargetSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceSourceMaterialFractionDescriptionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceSourceMaterialFractionDescriptionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), fraction: stringSchema.optional(), _fraction: z.lazy(() => elementSchema).optional(), materialType: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const substanceSourceMaterialOrganismSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceSourceMaterialOrganismSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), family: z.lazy(() => codeableConceptSchema).optional(), genus: z.lazy(() => codeableConceptSchema).optional(), @@ -27692,59 +43441,86 @@ export const substanceSourceMaterialOrganismSchema: z.ZodType codeableConceptSchema).optional(), intraspecificDescription: stringSchema.optional(), _intraspecificDescription: z.lazy(() => elementSchema).optional(), - author: substanceSourceMaterialOrganismAuthorSchema.array().optional(), + author: substanceSourceMaterialOrganismAuthorSchema + .array() + .min(1) + .optional(), hybrid: substanceSourceMaterialOrganismHybridSchema.optional(), organismGeneral: substanceSourceMaterialOrganismOrganismGeneralSchema.optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceSourceMaterialPartDescriptionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceSourceMaterialPartDescriptionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), part: z.lazy(() => codeableConceptSchema).optional(), partLocation: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceSourceMaterialOrganismAuthorSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceSourceMaterialOrganismAuthorSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), authorType: z.lazy(() => codeableConceptSchema).optional(), authorDescription: stringSchema.optional(), _authorDescription: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceSourceMaterialOrganismHybridSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceSourceMaterialOrganismHybridSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), maternalOrganismId: stringSchema.optional(), _maternalOrganismId: z.lazy(() => elementSchema).optional(), @@ -27755,31 +43531,47 @@ export const substanceSourceMaterialOrganismHybridSchema: z.ZodType elementSchema).optional(), hybridType: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceSourceMaterialOrganismOrganismGeneralSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceSourceMaterialOrganismOrganismGeneralSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), kingdom: z.lazy(() => codeableConceptSchema).optional(), phylum: z.lazy(() => codeableConceptSchema).optional(), class: z.lazy(() => codeableConceptSchema).optional(), order: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const substanceSourceMaterialSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceSourceMaterialSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('SubstanceSourceMaterial'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -27792,14 +43584,17 @@ export const substanceSourceMaterialSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), sourceMaterialClass: z.lazy(() => codeableConceptSchema).optional(), sourceMaterialType: z.lazy(() => codeableConceptSchema).optional(), @@ -27810,134 +43605,234 @@ export const substanceSourceMaterialSchema: z.ZodType identifierSchema) .array() + .min(1) .optional(), - parentSubstanceName: stringSchema.array().optional(), + parentSubstanceName: stringSchema.nullable().array().min(1).optional(), _parentSubstanceName: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), countryOfOrigin: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - geographicalLocation: stringSchema.array().optional(), + geographicalLocation: stringSchema.nullable().array().min(1).optional(), _geographicalLocation: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), developmentStage: z.lazy(() => codeableConceptSchema).optional(), fractionDescription: substanceSourceMaterialFractionDescriptionSchema .array() + .min(1) .optional(), organism: substanceSourceMaterialOrganismSchema.optional(), partDescription: substanceSourceMaterialPartDescriptionSchema .array() + .min(1) .optional(), - }), - ) - -export const substanceSpecificationMoietySchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - role: z.lazy(() => codeableConceptSchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - stereochemistry: z.lazy(() => codeableConceptSchema).optional(), - opticalActivity: z.lazy(() => codeableConceptSchema).optional(), - molecularFormula: stringSchema.optional(), - _molecularFormula: z.lazy(() => elementSchema).optional(), - amountQuantity: z.lazy(() => quantitySchema).optional(), - amountString: stringSchema.optional(), - _amountString: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['amountQuantity', 'amountString'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + }) + .superRefine((value, context) => { + for (const group of [ { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + name: 'parentSubstanceName', + shadow: '_parentSubstanceName', + required: false, }, - ), - ) + { + name: 'geographicalLocation', + shadow: '_geographicalLocation', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const substanceSpecificationMoietySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + stereochemistry: z.lazy(() => codeableConceptSchema).optional(), + opticalActivity: z.lazy(() => codeableConceptSchema).optional(), + molecularFormula: stringSchema.optional(), + _molecularFormula: z.lazy(() => elementSchema).optional(), + amountQuantity: z.lazy(() => quantitySchema).optional(), + amountString: stringSchema.optional(), + _amountString: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['amountQuantity'], + ['amountString', '_amountString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceSpecificationPropertySchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - parameters: stringSchema.optional(), - _parameters: z.lazy(() => elementSchema).optional(), - definingSubstanceReference: z.lazy(() => referenceSchema).optional(), - definingSubstanceCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - amountQuantity: z.lazy(() => quantitySchema).optional(), - amountString: stringSchema.optional(), - _amountString: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'definingSubstanceReference', - 'definingSubstanceCodeableConcept', - ], - required: false, - }, - { names: ['amountQuantity', 'amountString'], required: false }, - ].every((group) => { - const present = group.names.filter( +export const substanceSpecificationPropertySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + parameters: stringSchema.optional(), + _parameters: z.lazy(() => elementSchema).optional(), + definingSubstanceReference: z.lazy(() => referenceSchema).optional(), + definingSubstanceCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + amountQuantity: z.lazy(() => quantitySchema).optional(), + amountString: stringSchema.optional(), + _amountString: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['definingSubstanceReference'], + ['definingSubstanceCodeableConcept'], + ], + required: false, + }, + { + alternatives: [ + ['amountQuantity'], + ['amountString', '_amountString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceSpecificationStructureSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceSpecificationStructureSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), stereochemistry: z.lazy(() => codeableConceptSchema).optional(), opticalActivity: z.lazy(() => codeableConceptSchema).optional(), @@ -27945,31 +43840,44 @@ export const substanceSpecificationStructureSchema: z.ZodType elementSchema).optional(), molecularFormulaByMoiety: stringSchema.optional(), _molecularFormulaByMoiety: z.lazy(() => elementSchema).optional(), - isotope: substanceSpecificationStructureIsotopeSchema.array().optional(), + isotope: substanceSpecificationStructureIsotopeSchema + .array() + .min(1) + .optional(), molecularWeight: z .lazy(() => substanceSpecificationStructureIsotopeMolecularWeightSchema) .optional(), source: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), representation: substanceSpecificationStructureRepresentationSchema .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceSpecificationCodeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceSpecificationCodeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), code: z.lazy(() => codeableConceptSchema).optional(), status: z.lazy(() => codeableConceptSchema).optional(), @@ -27980,23 +43888,32 @@ export const substanceSpecificationCodeSchema: z.ZodType referenceSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceSpecificationNameSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceSpecificationNameSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: stringSchema, + name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), type: z.lazy(() => codeableConceptSchema).optional(), status: z.lazy(() => codeableConceptSchema).optional(), @@ -28005,121 +43922,175 @@ export const substanceSpecificationNameSchema: z.ZodType codeableConceptSchema) .array() + .min(1) .optional(), domain: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), synonym: z .lazy(() => substanceSpecificationNameSchema) .array() + .min(1) .optional(), translation: z .lazy(() => substanceSpecificationNameSchema) .array() + .min(1) + .optional(), + official: substanceSpecificationNameOfficialSchema + .array() + .min(1) .optional(), - official: substanceSpecificationNameOfficialSchema.array().optional(), source: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceSpecificationStructureIsotopeMolecularWeightSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceSpecificationStructureIsotopeMolecularWeightSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), method: z.lazy(() => codeableConceptSchema).optional(), type: z.lazy(() => codeableConceptSchema).optional(), amount: z.lazy(() => quantitySchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const substanceSpecificationRelationshipSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - substanceReference: z.lazy(() => referenceSchema).optional(), - substanceCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - relationship: z.lazy(() => codeableConceptSchema).optional(), - isDefining: booleanSchema.optional(), - _isDefining: z.lazy(() => elementSchema).optional(), - amountQuantity: z.lazy(() => quantitySchema).optional(), - amountRange: z.lazy(() => rangeSchema).optional(), - amountRatio: z.lazy(() => ratioSchema).optional(), - amountString: stringSchema.optional(), - _amountString: z.lazy(() => elementSchema).optional(), - amountRatioLowLimit: z.lazy(() => ratioSchema).optional(), - amountType: z.lazy(() => codeableConceptSchema).optional(), - source: z - .lazy(() => referenceSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['substanceReference', 'substanceCodeableConcept'], - required: false, - }, - { - names: [ - 'amountQuantity', - 'amountRange', - 'amountRatio', - 'amountString', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( +export const substanceSpecificationRelationshipSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + substanceReference: z.lazy(() => referenceSchema).optional(), + substanceCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema).optional(), + isDefining: booleanSchema.optional(), + _isDefining: z.lazy(() => elementSchema).optional(), + amountQuantity: z.lazy(() => quantitySchema).optional(), + amountRange: z.lazy(() => rangeSchema).optional(), + amountRatio: z.lazy(() => ratioSchema).optional(), + amountString: stringSchema.optional(), + _amountString: z.lazy(() => elementSchema).optional(), + amountRatioLowLimit: z.lazy(() => ratioSchema).optional(), + amountType: z.lazy(() => codeableConceptSchema).optional(), + source: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['substanceReference'], + ['substanceCodeableConcept'], + ], + required: false, + }, + { + alternatives: [ + ['amountQuantity'], + ['amountRange'], + ['amountRatio'], + ['amountString', '_amountString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceSpecificationStructureIsotopeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceSpecificationStructureIsotopeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z.lazy(() => identifierSchema).optional(), name: z.lazy(() => codeableConceptSchema).optional(), @@ -28127,50 +44098,74 @@ export const substanceSpecificationStructureIsotopeSchema: z.ZodType quantitySchema).optional(), molecularWeight: substanceSpecificationStructureIsotopeMolecularWeightSchema.optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceSpecificationStructureRepresentationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceSpecificationStructureRepresentationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), type: z.lazy(() => codeableConceptSchema).optional(), representation: stringSchema.optional(), _representation: z.lazy(() => elementSchema).optional(), attachment: z.lazy(() => attachmentSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const substanceSpecificationNameOfficialSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceSpecificationNameOfficialSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), authority: z.lazy(() => codeableConceptSchema).optional(), status: z.lazy(() => codeableConceptSchema).optional(), date: dateTimeSchema.optional(), _date: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceSpecificationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceSpecificationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('SubstanceSpecification'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -28183,14 +44178,17 @@ export const substanceSpecificationSchema: z.ZodType contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z.lazy(() => identifierSchema).optional(), type: z.lazy(() => codeableConceptSchema).optional(), @@ -28201,54 +44199,161 @@ export const substanceSpecificationSchema: z.ZodType source: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), comment: stringSchema.optional(), _comment: z.lazy(() => elementSchema).optional(), - moiety: substanceSpecificationMoietySchema.array().optional(), - property: substanceSpecificationPropertySchema.array().optional(), + moiety: substanceSpecificationMoietySchema.array().min(1).optional(), + property: substanceSpecificationPropertySchema.array().min(1).optional(), referenceInformation: z.lazy(() => referenceSchema).optional(), structure: substanceSpecificationStructureSchema.optional(), - code: substanceSpecificationCodeSchema.array().optional(), - name: substanceSpecificationNameSchema.array().optional(), + code: substanceSpecificationCodeSchema.array().min(1).optional(), + name: substanceSpecificationNameSchema.array().min(1).optional(), molecularWeight: z .lazy(() => substanceSpecificationStructureIsotopeMolecularWeightSchema) .array() + .min(1) + .optional(), + relationship: substanceSpecificationRelationshipSchema + .array() + .min(1) .optional(), - relationship: substanceSpecificationRelationshipSchema.array().optional(), nucleicAcid: z.lazy(() => referenceSchema).optional(), polymer: z.lazy(() => referenceSchema).optional(), protein: z.lazy(() => referenceSchema).optional(), sourceMaterial: z.lazy(() => referenceSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const supplyDeliverySuppliedItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['itemCodeableConcept'], ['itemReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const supplyDeliverySuppliedItemSchema: z.ZodType = +export const supplyDeliverySchema: z.ZodType> = z.lazy(() => z .strictObject({ + resourceType: z.literal('SupplyDelivery'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['abandoned', 'completed', 'entered-in-error', 'in-progress']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + suppliedItem: supplyDeliverySuppliedItemSchema.optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + supplier: z.lazy(() => referenceSchema).optional(), + destination: z.lazy(() => referenceSchema).optional(), + receiver: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - itemReference: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ { - names: ['itemCodeableConcept', 'itemReference'], + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], + ['occurrenceTiming'], + ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -28256,77 +44361,55 @@ export const supplyDeliverySuppliedItemSchema: z.ZodType Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) + ) as unknown as z.ZodType> -export const supplyDeliverySchema: z.ZodType = z.lazy(() => +export const supplyRequestParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('SupplyDelivery'), id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z - .enum(['abandoned', 'completed', 'entered-in-error', 'in-progress']) - .optional(), - _status: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - suppliedItem: supplyDeliverySuppliedItemSchema.optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - occurrenceTiming: z.lazy(() => timingSchema).optional(), - supplier: z.lazy(() => referenceSchema).optional(), - destination: z.lazy(() => referenceSchema).optional(), - receiver: z - .lazy(() => referenceSchema) - .array() - .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), }) .refine( (value) => [ { - names: [ - 'occurrenceDateTime', - 'occurrencePeriod', - 'occurrenceTiming', + alternatives: [ + ['valueCodeableConcept'], + ['valueQuantity'], + ['valueRange'], + ['valueBoolean', '_valueBoolean'], ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -28334,44 +44417,111 @@ export const supplyDeliverySchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const supplyRequestParameterSchema: z.ZodType = +export const supplyRequestSchema: z.ZodType> = z.lazy(() => z .strictObject({ + resourceType: z.literal('SupplyRequest'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'active', + 'cancelled', + 'completed', + 'draft', + 'entered-in-error', + 'suspended', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema), + parameter: supplyRequestParameterSchema.array().min(1).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + supplier: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + deliverFrom: z.lazy(() => referenceSchema).optional(), + deliverTo: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ { - names: [ - 'valueCodeableConcept', - 'valueQuantity', - 'valueRange', - 'valueBoolean', + alternatives: [['itemCodeableConcept'], ['itemReference']], + required: true, + }, + { + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], + ['occurrenceTiming'], ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -28379,136 +44529,60 @@ export const supplyRequestParameterSchema: z.ZodType message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) - -export const supplyRequestSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - resourceType: z.literal('SupplyRequest'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z - .enum([ - 'active', - 'cancelled', - 'completed', - 'draft', - 'entered-in-error', - 'suspended', - 'unknown', - ]) - .optional(), - _status: z.lazy(() => elementSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - itemReference: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema), - parameter: supplyRequestParameterSchema.array().optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - occurrenceTiming: z.lazy(() => timingSchema).optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: z.lazy(() => elementSchema).optional(), - requester: z.lazy(() => referenceSchema).optional(), - supplier: z - .lazy(() => referenceSchema) - .array() - .optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - deliverFrom: z.lazy(() => referenceSchema).optional(), - deliverTo: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['itemCodeableConcept', 'itemReference'], required: true }, - { - names: [ - 'occurrenceDateTime', - 'occurrencePeriod', - 'occurrenceTiming', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) + ) as unknown as z.ZodType> -export const taskRestrictionSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - repetitions: positiveIntSchema.optional(), - _repetitions: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - recipient: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) +export const taskRestrictionSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + repetitions: positiveIntSchema.optional(), + _repetitions: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + recipient: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const taskInputSchema: z.ZodType = z.lazy(() => +export const taskInputSchema: z.ZodType> = z.lazy(() => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), type: z.lazy(() => codeableConceptSchema), valueBase64Binary: base64BinarySchema.optional(), @@ -28587,63 +44661,65 @@ export const taskInputSchema: z.ZodType = z.lazy(() => (value) => [ { - names: [ - 'valueBase64Binary', - 'valueBoolean', - 'valueCanonical', - 'valueCode', - 'valueDate', - 'valueDateTime', - 'valueDecimal', - 'valueId', - 'valueInstant', - 'valueInteger', - 'valueMarkdown', - 'valueOid', - 'valuePositiveInt', - 'valueString', - 'valueTime', - 'valueUnsignedInt', - 'valueUri', - 'valueUrl', - 'valueUuid', - 'valueAddress', - 'valueAge', - 'valueAnnotation', - 'valueAttachment', - 'valueCodeableConcept', - 'valueCoding', - 'valueContactPoint', - 'valueCount', - 'valueDistance', - 'valueDuration', - 'valueHumanName', - 'valueIdentifier', - 'valueMoney', - 'valuePeriod', - 'valueQuantity', - 'valueRange', - 'valueRatio', - 'valueReference', - 'valueSampledData', - 'valueSignature', - 'valueTiming', - 'valueContactDetail', - 'valueContributor', - 'valueDataRequirement', - 'valueExpression', - 'valueParameterDefinition', - 'valueRelatedArtifact', - 'valueTriggerDefinition', - 'valueUsageContext', - 'valueDosage', - 'valueMeta', + alternatives: [ + ['valueBase64Binary', '_valueBase64Binary'], + ['valueBoolean', '_valueBoolean'], + ['valueCanonical', '_valueCanonical'], + ['valueCode', '_valueCode'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueDecimal', '_valueDecimal'], + ['valueId', '_valueId'], + ['valueInstant', '_valueInstant'], + ['valueInteger', '_valueInteger'], + ['valueMarkdown', '_valueMarkdown'], + ['valueOid', '_valueOid'], + ['valuePositiveInt', '_valuePositiveInt'], + ['valueString', '_valueString'], + ['valueTime', '_valueTime'], + ['valueUnsignedInt', '_valueUnsignedInt'], + ['valueUri', '_valueUri'], + ['valueUrl', '_valueUrl'], + ['valueUuid', '_valueUuid'], + ['valueAddress'], + ['valueAge'], + ['valueAnnotation'], + ['valueAttachment'], + ['valueCodeableConcept'], + ['valueCoding'], + ['valueContactPoint'], + ['valueCount'], + ['valueDistance'], + ['valueDuration'], + ['valueHumanName'], + ['valueIdentifier'], + ['valueMoney'], + ['valuePeriod'], + ['valueQuantity'], + ['valueRange'], + ['valueRatio'], + ['valueReference'], + ['valueSampledData'], + ['valueSignature'], + ['valueTiming'], + ['valueContactDetail'], + ['valueContributor'], + ['valueDataRequirement'], + ['valueExpression'], + ['valueParameterDefinition'], + ['valueRelatedArtifact'], + ['valueTriggerDefinition'], + ['valueUsageContext'], + ['valueDosage'], + ['valueMeta'], ], required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -28651,345 +44727,462 @@ export const taskInputSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> + +export const taskOutputSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + valueMeta: z.lazy(() => metaSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueBase64Binary', '_valueBase64Binary'], + ['valueBoolean', '_valueBoolean'], + ['valueCanonical', '_valueCanonical'], + ['valueCode', '_valueCode'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueDecimal', '_valueDecimal'], + ['valueId', '_valueId'], + ['valueInstant', '_valueInstant'], + ['valueInteger', '_valueInteger'], + ['valueMarkdown', '_valueMarkdown'], + ['valueOid', '_valueOid'], + ['valuePositiveInt', '_valuePositiveInt'], + ['valueString', '_valueString'], + ['valueTime', '_valueTime'], + ['valueUnsignedInt', '_valueUnsignedInt'], + ['valueUri', '_valueUri'], + ['valueUrl', '_valueUrl'], + ['valueUuid', '_valueUuid'], + ['valueAddress'], + ['valueAge'], + ['valueAnnotation'], + ['valueAttachment'], + ['valueCodeableConcept'], + ['valueCoding'], + ['valueContactPoint'], + ['valueCount'], + ['valueDistance'], + ['valueDuration'], + ['valueHumanName'], + ['valueIdentifier'], + ['valueMoney'], + ['valuePeriod'], + ['valueQuantity'], + ['valueRange'], + ['valueRatio'], + ['valueReference'], + ['valueSampledData'], + ['valueSignature'], + ['valueTiming'], + ['valueContactDetail'], + ['valueContributor'], + ['valueDataRequirement'], + ['valueExpression'], + ['valueParameterDefinition'], + ['valueRelatedArtifact'], + ['valueTriggerDefinition'], + ['valueUsageContext'], + ['valueDosage'], + ['valueMeta'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const taskOutputSchema: z.ZodType = z.lazy(() => +export const taskSchema: z.ZodType> = z.lazy(() => z .strictObject({ + resourceType: z.literal('Task'), id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueCanonical: canonicalSchema.optional(), - _valueCanonical: z.lazy(() => elementSchema).optional(), - valueCode: codeSchema.optional(), - _valueCode: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueId: idSchema.optional(), - _valueId: z.lazy(() => elementSchema).optional(), - valueInstant: instantSchema.optional(), - _valueInstant: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueMarkdown: markdownSchema.optional(), - _valueMarkdown: z.lazy(() => elementSchema).optional(), - valueOid: oidSchema.optional(), - _valueOid: z.lazy(() => elementSchema).optional(), - valuePositiveInt: positiveIntSchema.optional(), - _valuePositiveInt: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueUnsignedInt: unsignedIntSchema.optional(), - _valueUnsignedInt: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueUrl: urlSchema.optional(), - _valueUrl: z.lazy(() => elementSchema).optional(), - valueUuid: uuidSchema.optional(), - _valueUuid: z.lazy(() => elementSchema).optional(), - valueAddress: z.lazy(() => addressSchema).optional(), - valueAge: z.lazy(() => ageSchema).optional(), - valueAnnotation: z.lazy(() => annotationSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueContactPoint: z.lazy(() => contactPointSchema).optional(), - valueCount: z.lazy(() => countSchema).optional(), - valueDistance: z.lazy(() => distanceSchema).optional(), - valueDuration: z.lazy(() => durationSchema).optional(), - valueHumanName: z.lazy(() => humanNameSchema).optional(), - valueIdentifier: z.lazy(() => identifierSchema).optional(), - valueMoney: z.lazy(() => moneySchema).optional(), - valuePeriod: z.lazy(() => periodSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueRatio: z.lazy(() => ratioSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - valueSampledData: z.lazy(() => sampledDataSchema).optional(), - valueSignature: z.lazy(() => signatureSchema).optional(), - valueTiming: z.lazy(() => timingSchema).optional(), - valueContactDetail: z.lazy(() => contactDetailSchema).optional(), - valueContributor: z.lazy(() => contributorSchema).optional(), - valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), - valueExpression: z.lazy(() => expressionSchema).optional(), - valueParameterDefinition: z - .lazy(() => parameterDefinitionSchema) + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + instantiatesCanonical: canonicalSchema.optional(), + _instantiatesCanonical: z.lazy(() => elementSchema).optional(), + instantiatesUri: uriSchema.optional(), + _instantiatesUri: z.lazy(() => elementSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'accepted', + 'cancelled', + 'completed', + 'draft', + 'entered-in-error', + 'failed', + 'in-progress', + 'on-hold', + 'ready', + 'received', + 'rejected', + 'requested', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + businessStatus: z.lazy(() => codeableConceptSchema).optional(), + intent: z + .enum([ + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + 'unknown', + ]) + .optional(), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + focus: z.lazy(() => referenceSchema).optional(), + for: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + executionPeriod: z.lazy(() => periodSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + lastModified: dateTimeSchema.optional(), + _lastModified: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performerType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) .optional(), - valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), - valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), - valueUsageContext: z.lazy(() => usageContextSchema).optional(), - valueDosage: z.lazy(() => dosageSchema).optional(), - valueMeta: z.lazy(() => metaSchema).optional(), + owner: z.lazy(() => referenceSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + reasonCode: z.lazy(() => codeableConceptSchema).optional(), + reasonReference: z.lazy(() => referenceSchema).optional(), + insurance: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + relevantHistory: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + restriction: taskRestrictionSchema.optional(), + input: taskInputSchema.array().min(1).optional(), + output: taskOutputSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'intent', shadow: '_intent' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: [ - 'valueBase64Binary', - 'valueBoolean', - 'valueCanonical', - 'valueCode', - 'valueDate', - 'valueDateTime', - 'valueDecimal', - 'valueId', - 'valueInstant', - 'valueInteger', - 'valueMarkdown', - 'valueOid', - 'valuePositiveInt', - 'valueString', - 'valueTime', - 'valueUnsignedInt', - 'valueUri', - 'valueUrl', - 'valueUuid', - 'valueAddress', - 'valueAge', - 'valueAnnotation', - 'valueAttachment', - 'valueCodeableConcept', - 'valueCoding', - 'valueContactPoint', - 'valueCount', - 'valueDistance', - 'valueDuration', - 'valueHumanName', - 'valueIdentifier', - 'valueMoney', - 'valuePeriod', - 'valueQuantity', - 'valueRange', - 'valueRatio', - 'valueReference', - 'valueSampledData', - 'valueSignature', - 'valueTiming', - 'valueContactDetail', - 'valueContributor', - 'valueDataRequirement', - 'valueExpression', - 'valueParameterDefinition', - 'valueRelatedArtifact', - 'valueTriggerDefinition', - 'valueUsageContext', - 'valueDosage', - 'valueMeta', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const taskSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Task'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - instantiatesCanonical: canonicalSchema.optional(), - _instantiatesCanonical: z.lazy(() => elementSchema).optional(), - instantiatesUri: uriSchema.optional(), - _instantiatesUri: z.lazy(() => elementSchema).optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - groupIdentifier: z.lazy(() => identifierSchema).optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'accepted', - 'cancelled', - 'completed', - 'draft', - 'entered-in-error', - 'failed', - 'in-progress', - 'on-hold', - 'ready', - 'received', - 'rejected', - 'requested', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - businessStatus: z.lazy(() => codeableConceptSchema).optional(), - intent: z.enum([ - 'filler-order', - 'instance-order', - 'option', - 'order', - 'original-order', - 'plan', - 'proposal', - 'reflex-order', - 'unknown', - ]), - _intent: z.lazy(() => elementSchema).optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - focus: z.lazy(() => referenceSchema).optional(), - for: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - executionPeriod: z.lazy(() => periodSchema).optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: z.lazy(() => elementSchema).optional(), - lastModified: dateTimeSchema.optional(), - _lastModified: z.lazy(() => elementSchema).optional(), - requester: z.lazy(() => referenceSchema).optional(), - performerType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - owner: z.lazy(() => referenceSchema).optional(), - location: z.lazy(() => referenceSchema).optional(), - reasonCode: z.lazy(() => codeableConceptSchema).optional(), - reasonReference: z.lazy(() => referenceSchema).optional(), - insurance: z - .lazy(() => referenceSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - relevantHistory: z - .lazy(() => referenceSchema) - .array() - .optional(), - restriction: taskRestrictionSchema.optional(), - input: taskInputSchema.array().optional(), - output: taskOutputSchema.array().optional(), - }), -) - -export const terminologyCapabilitiesSoftwareSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +) as unknown as z.ZodType> + +export const terminologyCapabilitiesSoftwareSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: stringSchema, + name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), version: stringSchema.optional(), _version: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const terminologyCapabilitiesImplementationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesImplementationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: stringSchema, + description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), url: urlSchema.optional(), _url: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'description', shadow: '_description' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const terminologyCapabilitiesCodeSystemSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesCodeSystemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), uri: canonicalSchema.optional(), _uri: z.lazy(() => elementSchema).optional(), version: terminologyCapabilitiesCodeSystemVersionSchema .array() + .min(1) .optional(), subsumption: booleanSchema.optional(), _subsumption: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const terminologyCapabilitiesExpansionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesExpansionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), hierarchical: booleanSchema.optional(), _hierarchical: z.lazy(() => elementSchema).optional(), @@ -28999,74 +45192,139 @@ export const terminologyCapabilitiesExpansionSchema: z.ZodType elementSchema).optional(), parameter: terminologyCapabilitiesExpansionParameterSchema .array() + .min(1) .optional(), textFilter: markdownSchema.optional(), _textFilter: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const terminologyCapabilitiesValidateCodeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesValidateCodeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - translations: booleanSchema, + translations: booleanSchema.optional(), _translations: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'translations', shadow: '_translations' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const terminologyCapabilitiesTranslationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesTranslationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - needsMap: booleanSchema, + needsMap: booleanSchema.optional(), _needsMap: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'needsMap', shadow: '_needsMap' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const terminologyCapabilitiesClosureSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesClosureSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), translation: booleanSchema.optional(), _translation: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const terminologyCapabilitiesCodeSystemVersionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesCodeSystemVersionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), code: stringSchema.optional(), _code: z.lazy(() => elementSchema).optional(), @@ -29074,66 +45332,222 @@ export const terminologyCapabilitiesCodeSystemVersionSchema: z.ZodType elementSchema).optional(), compositional: booleanSchema.optional(), _compositional: z.lazy(() => elementSchema).optional(), - language: codeSchema.array().optional(), + language: codeSchema.nullable().array().min(1).optional(), _language: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), filter: terminologyCapabilitiesCodeSystemVersionFilterSchema .array() + .min(1) .optional(), - property: codeSchema.array().optional(), + property: codeSchema.nullable().array().min(1).optional(), _property: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'language', shadow: '_language', required: false }, + { name: 'property', shadow: '_property', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const terminologyCapabilitiesExpansionParameterSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesExpansionParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: codeSchema, + name: codeSchema.optional(), _name: z.lazy(() => elementSchema).optional(), documentation: stringSchema.optional(), _documentation: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const terminologyCapabilitiesCodeSystemVersionFilterSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesCodeSystemVersionFilterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: codeSchema, + code: codeSchema.optional(), _code: z.lazy(() => elementSchema).optional(), - op: codeSchema.array().min(1), + op: codeSchema.nullable().array().min(1).optional(), _op: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [{ name: 'op', shadow: '_op', required: true }]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const terminologyCapabilitiesSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('TerminologyCapabilities'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -29146,14 +45560,17 @@ export const terminologyCapabilitiesSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), url: uriSchema.optional(), _url: z.lazy(() => elementSchema).optional(), @@ -29163,348 +45580,624 @@ export const terminologyCapabilitiesSchema: z.ZodType elementSchema).optional(), title: stringSchema.optional(), _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), _status: z.lazy(() => elementSchema).optional(), experimental: booleanSchema.optional(), _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema, + date: dateTimeSchema.optional(), _date: z.lazy(() => elementSchema).optional(), publisher: stringSchema.optional(), _publisher: z.lazy(() => elementSchema).optional(), contact: z .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), useContext: z .lazy(() => usageContextSchema) .array() + .min(1) .optional(), jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), purpose: markdownSchema.optional(), _purpose: z.lazy(() => elementSchema).optional(), copyright: markdownSchema.optional(), _copyright: z.lazy(() => elementSchema).optional(), - kind: z.enum(['capability', 'instance', 'requirements']), + kind: z.enum(['capability', 'instance', 'requirements']).optional(), _kind: z.lazy(() => elementSchema).optional(), software: terminologyCapabilitiesSoftwareSchema.optional(), implementation: terminologyCapabilitiesImplementationSchema.optional(), lockedDate: booleanSchema.optional(), _lockedDate: z.lazy(() => elementSchema).optional(), - codeSystem: terminologyCapabilitiesCodeSystemSchema.array().optional(), + codeSystem: terminologyCapabilitiesCodeSystemSchema + .array() + .min(1) + .optional(), expansion: terminologyCapabilitiesExpansionSchema.optional(), codeSearch: z.enum(['all', 'explicit']).optional(), _codeSearch: z.lazy(() => elementSchema).optional(), validateCode: terminologyCapabilitiesValidateCodeSchema.optional(), translation: terminologyCapabilitiesTranslationSchema.optional(), closure: terminologyCapabilitiesClosureSchema.optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'date', shadow: '_date' }, + { name: 'kind', shadow: '_kind' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportParticipantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testReportParticipantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum(['client', 'server', 'test-engine']), + type: z.enum(['client', 'server', 'test-engine']).optional(), _type: z.lazy(() => elementSchema).optional(), - uri: uriSchema, + uri: uriSchema.optional(), _uri: z.lazy(() => elementSchema).optional(), display: stringSchema.optional(), _display: z.lazy(() => elementSchema).optional(), - }), - ) - -export const testReportSetupSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - action: testReportSetupActionSchema.array().min(1), - }), -) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'type', shadow: '_type' }, + { name: 'uri', shadow: '_uri' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportTestSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - action: testReportTestActionSchema.array().min(1), - }), -) +export const testReportSetupSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + action: testReportSetupActionSchema.array().min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const testReportTeardownSchema: z.ZodType = +export const testReportTestSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + action: testReportTestActionSchema.array().min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const testReportTeardownSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), action: testReportTeardownActionSchema.array().min(1), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportSetupActionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testReportSetupActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), operation: testReportSetupActionOperationSchema.optional(), assert: testReportSetupActionAssertSchema.optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportTestActionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testReportTestActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), operation: z.lazy(() => testReportSetupActionOperationSchema).optional(), assert: z.lazy(() => testReportSetupActionAssertSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportTeardownActionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testReportTeardownActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), operation: z.lazy(() => testReportSetupActionOperationSchema), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportSetupActionOperationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testReportSetupActionOperationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - result: z.enum(['error', 'fail', 'pass', 'skip', 'warning']), + result: z.enum(['error', 'fail', 'pass', 'skip', 'warning']).optional(), _result: z.lazy(() => elementSchema).optional(), message: markdownSchema.optional(), _message: z.lazy(() => elementSchema).optional(), detail: uriSchema.optional(), _detail: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'result', shadow: '_result' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportSetupActionAssertSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testReportSetupActionAssertSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - result: z.enum(['error', 'fail', 'pass', 'skip', 'warning']), + result: z.enum(['error', 'fail', 'pass', 'skip', 'warning']).optional(), _result: z.lazy(() => elementSchema).optional(), message: markdownSchema.optional(), _message: z.lazy(() => elementSchema).optional(), detail: stringSchema.optional(), _detail: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'result', shadow: '_result' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('TestReport'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - status: z.enum([ - 'completed', - 'entered-in-error', - 'in-progress', - 'stopped', - 'waiting', - ]), - _status: z.lazy(() => elementSchema).optional(), - testScript: z.lazy(() => referenceSchema), - result: z.enum(['fail', 'pass', 'pending']), - _result: z.lazy(() => elementSchema).optional(), - score: decimalSchema.optional(), - _score: z.lazy(() => elementSchema).optional(), - tester: stringSchema.optional(), - _tester: z.lazy(() => elementSchema).optional(), - issued: dateTimeSchema.optional(), - _issued: z.lazy(() => elementSchema).optional(), - participant: testReportParticipantSchema.array().optional(), - setup: testReportSetupSchema.optional(), - test: testReportTestSchema.array().optional(), - teardown: testReportTeardownSchema.optional(), - }), -) - -export const testScriptOriginSchema: z.ZodType = z.lazy( +export const testReportSchema: z.ZodType> = z.lazy( () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - index: integerSchema, - _index: z.lazy(() => elementSchema).optional(), - profile: z.lazy(() => codingSchema), - }), -) + z + .strictObject({ + resourceType: z.literal('TestReport'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + status: z + .enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'stopped', + 'waiting', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + testScript: z.lazy(() => referenceSchema), + result: z.enum(['fail', 'pass', 'pending']).optional(), + _result: z.lazy(() => elementSchema).optional(), + score: decimalSchema.optional(), + _score: z.lazy(() => elementSchema).optional(), + tester: stringSchema.optional(), + _tester: z.lazy(() => elementSchema).optional(), + issued: dateTimeSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + participant: testReportParticipantSchema.array().min(1).optional(), + setup: testReportSetupSchema.optional(), + test: testReportTestSchema.array().min(1).optional(), + teardown: testReportTeardownSchema.optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'result', shadow: '_result' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const testScriptDestinationSchema: z.ZodType = +export const testScriptOriginSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + index: integerSchema.optional(), + _index: z.lazy(() => elementSchema).optional(), + profile: z.lazy(() => codingSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'index', shadow: '_index' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const testScriptDestinationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - index: integerSchema, + index: integerSchema.optional(), _index: z.lazy(() => elementSchema).optional(), profile: z.lazy(() => codingSchema), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'index', shadow: '_index' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptMetadataSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptMetadataSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - link: testScriptMetadataLinkSchema.array().optional(), + link: testScriptMetadataLinkSchema.array().min(1).optional(), capability: testScriptMetadataCapabilitySchema.array().min(1), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptFixtureSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const testScriptFixtureSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - autocreate: booleanSchema, + autocreate: booleanSchema.optional(), _autocreate: z.lazy(() => elementSchema).optional(), - autodelete: booleanSchema, + autodelete: booleanSchema.optional(), _autodelete: z.lazy(() => elementSchema).optional(), resource: z.lazy(() => referenceSchema).optional(), - }), -) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'autocreate', shadow: '_autocreate' }, + { name: 'autodelete', shadow: '_autodelete' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptVariableSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptVariableSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: stringSchema, + name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), defaultValue: stringSchema.optional(), _defaultValue: z.lazy(() => elementSchema).optional(), @@ -29520,175 +46213,352 @@ export const testScriptVariableSchema: z.ZodType = _path: z.lazy(() => elementSchema).optional(), sourceId: idSchema.optional(), _sourceId: z.lazy(() => elementSchema).optional(), - }), - ) - -export const testScriptSetupSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - action: testScriptSetupActionSchema.array().min(1), - }), -) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptTestSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - action: testScriptTestActionSchema.array().min(1), - }), -) +export const testScriptSetupSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + action: testScriptSetupActionSchema.array().min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const testScriptTeardownSchema: z.ZodType = +export const testScriptTestSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + action: testScriptTestActionSchema.array().min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const testScriptTeardownSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), action: testScriptTeardownActionSchema.array().min(1), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptMetadataLinkSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptMetadataLinkSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema, + url: uriSchema.optional(), _url: z.lazy(() => elementSchema).optional(), description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'url', shadow: '_url' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptMetadataCapabilitySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptMetadataCapabilitySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - required: booleanSchema, + required: booleanSchema.optional(), _required: z.lazy(() => elementSchema).optional(), - validated: booleanSchema, + validated: booleanSchema.optional(), _validated: z.lazy(() => elementSchema).optional(), description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - origin: integerSchema.array().optional(), + origin: integerSchema.nullable().array().min(1).optional(), _origin: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), destination: integerSchema.optional(), _destination: z.lazy(() => elementSchema).optional(), - link: uriSchema.array().optional(), + link: uriSchema.nullable().array().min(1).optional(), _link: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - capabilities: canonicalSchema, + capabilities: canonicalSchema.optional(), _capabilities: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'required', shadow: '_required' }, + { name: 'validated', shadow: '_validated' }, + { name: 'capabilities', shadow: '_capabilities' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'origin', shadow: '_origin', required: false }, + { name: 'link', shadow: '_link', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptSetupActionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptSetupActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), operation: testScriptSetupActionOperationSchema.optional(), assert: testScriptSetupActionAssertSchema.optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptTestActionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptTestActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), operation: z.lazy(() => testScriptSetupActionOperationSchema).optional(), assert: z.lazy(() => testScriptSetupActionAssertSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptTeardownActionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptTeardownActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), operation: z.lazy(() => testScriptSetupActionOperationSchema), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptSetupActionOperationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptSetupActionOperationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), type: z.lazy(() => codingSchema).optional(), resource: z @@ -29917,7 +46787,7 @@ export const testScriptSetupActionOperationSchema: z.ZodType elementSchema).optional(), destination: integerSchema.optional(), _destination: z.lazy(() => elementSchema).optional(), - encodeRequestUrl: booleanSchema, + encodeRequestUrl: booleanSchema.optional(), _encodeRequestUrl: z.lazy(() => elementSchema).optional(), method: z .enum(['delete', 'get', 'head', 'options', 'patch', 'post', 'put']) @@ -29929,6 +46799,7 @@ export const testScriptSetupActionOperationSchema: z.ZodType elementSchema).optional(), requestHeader: testScriptSetupActionOperationRequestHeaderSchema .array() + .min(1) .optional(), requestId: idSchema.optional(), _requestId: z.lazy(() => elementSchema).optional(), @@ -29940,20 +46811,46 @@ export const testScriptSetupActionOperationSchema: z.ZodType elementSchema).optional(), url: stringSchema.optional(), _url: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'encodeRequestUrl', shadow: '_encodeRequestUrl' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptSetupActionAssertSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptSetupActionAssertSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), label: stringSchema.optional(), _label: z.lazy(() => elementSchema).optional(), @@ -30242,195 +47139,494 @@ export const testScriptSetupActionAssertSchema: z.ZodType elementSchema).optional(), value: stringSchema.optional(), _value: z.lazy(() => elementSchema).optional(), - warningOnly: booleanSchema, + warningOnly: booleanSchema.optional(), _warningOnly: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'warningOnly', shadow: '_warningOnly' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const testScriptSetupActionOperationRequestHeaderSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + field: stringSchema.optional(), + _field: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'field', shadow: '_field' }, + { name: 'value', shadow: '_value' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const testScriptSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('TestScript'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + origin: testScriptOriginSchema.array().min(1).optional(), + destination: testScriptDestinationSchema.array().min(1).optional(), + metadata: testScriptMetadataSchema.optional(), + fixture: testScriptFixtureSchema.array().min(1).optional(), + profile: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + variable: testScriptVariableSchema.array().min(1).optional(), + setup: testScriptSetupSchema.optional(), + test: testScriptTestSchema.array().min(1).optional(), + teardown: testScriptTeardownSchema.optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const testScriptSetupActionOperationRequestHeaderSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const timingRepeatSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + boundsDuration: z.lazy(() => durationSchema).optional(), + boundsRange: z.lazy(() => rangeSchema).optional(), + boundsPeriod: z.lazy(() => periodSchema).optional(), + count: positiveIntSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + countMax: positiveIntSchema.optional(), + _countMax: z.lazy(() => elementSchema).optional(), + duration: decimalSchema.optional(), + _duration: z.lazy(() => elementSchema).optional(), + durationMax: decimalSchema.optional(), + _durationMax: z.lazy(() => elementSchema).optional(), + durationUnit: z + .enum(['a', 'd', 'h', 'min', 'mo', 's', 'wk']) + .optional(), + _durationUnit: z.lazy(() => elementSchema).optional(), + frequency: positiveIntSchema.optional(), + _frequency: z.lazy(() => elementSchema).optional(), + frequencyMax: positiveIntSchema.optional(), + _frequencyMax: z.lazy(() => elementSchema).optional(), + period: decimalSchema.optional(), + _period: z.lazy(() => elementSchema).optional(), + periodMax: decimalSchema.optional(), + _periodMax: z.lazy(() => elementSchema).optional(), + periodUnit: z.enum(['a', 'd', 'h', 'min', 'mo', 's', 'wk']).optional(), + _periodUnit: z.lazy(() => elementSchema).optional(), + dayOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .nullable() + .array() + .min(1) + .optional(), + _dayOfWeek: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + timeOfDay: timeSchema.nullable().array().min(1).optional(), + _timeOfDay: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + when: z + .enum([ + 'AC', + 'ACD', + 'ACM', + 'ACV', + 'AFT', + 'AFT.early', + 'AFT.late', + 'C', + 'CD', + 'CM', + 'CV', + 'EVE', + 'EVE.early', + 'EVE.late', + 'HS', + 'MORN', + 'MORN.early', + 'MORN.late', + 'NIGHT', + 'NOON', + 'PC', + 'PCD', + 'PCM', + 'PCV', + 'PHS', + 'WAKE', + ]) + .nullable() + .array() + .min(1) + .optional(), + _when: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + offset: unsignedIntSchema.optional(), + _offset: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['boundsDuration'], + ['boundsRange'], + ['boundsPeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'dayOfWeek', shadow: '_dayOfWeek', required: false }, + { name: 'timeOfDay', shadow: '_timeOfDay', required: false }, + { name: 'when', shadow: '_when', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const timingSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - field: stringSchema, - _field: z.lazy(() => elementSchema).optional(), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - }), - ) - -export const testScriptSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('TestScript'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema, - _url: z.lazy(() => elementSchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - origin: testScriptOriginSchema.array().optional(), - destination: testScriptDestinationSchema.array().optional(), - metadata: testScriptMetadataSchema.optional(), - fixture: testScriptFixtureSchema.array().optional(), - profile: z - .lazy(() => referenceSchema) - .array() - .optional(), - variable: testScriptVariableSchema.array().optional(), - setup: testScriptSetupSchema.optional(), - test: testScriptTestSchema.array().optional(), - teardown: testScriptTeardownSchema.optional(), - }), -) - -export const timingRepeatSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - boundsDuration: z.lazy(() => durationSchema).optional(), - boundsRange: z.lazy(() => rangeSchema).optional(), - boundsPeriod: z.lazy(() => periodSchema).optional(), - count: positiveIntSchema.optional(), - _count: z.lazy(() => elementSchema).optional(), - countMax: positiveIntSchema.optional(), - _countMax: z.lazy(() => elementSchema).optional(), - duration: decimalSchema.optional(), - _duration: z.lazy(() => elementSchema).optional(), - durationMax: decimalSchema.optional(), - _durationMax: z.lazy(() => elementSchema).optional(), - durationUnit: z.enum(['a', 'd', 'h', 'min', 'mo', 's', 'wk']).optional(), - _durationUnit: z.lazy(() => elementSchema).optional(), - frequency: positiveIntSchema.optional(), - _frequency: z.lazy(() => elementSchema).optional(), - frequencyMax: positiveIntSchema.optional(), - _frequencyMax: z.lazy(() => elementSchema).optional(), - period: decimalSchema.optional(), - _period: z.lazy(() => elementSchema).optional(), - periodMax: decimalSchema.optional(), - _periodMax: z.lazy(() => elementSchema).optional(), - periodUnit: z.enum(['a', 'd', 'h', 'min', 'mo', 's', 'wk']).optional(), - _periodUnit: z.lazy(() => elementSchema).optional(), - dayOfWeek: z - .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) - .array() - .optional(), - _dayOfWeek: z + event: dateTimeSchema.nullable().array().min(1).optional(), + _event: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - timeOfDay: timeSchema.array().optional(), - _timeOfDay: z - .lazy(() => elementSchema) + repeat: timingRepeatSchema.optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'event', shadow: '_event', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const triggerDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - when: z + type: z .enum([ - 'AC', - 'ACD', - 'ACM', - 'ACV', - 'AFT', - 'AFT.early', - 'AFT.late', - 'C', - 'CD', - 'CM', - 'CV', - 'EVE', - 'EVE.early', - 'EVE.late', - 'HS', - 'MORN', - 'MORN.early', - 'MORN.late', - 'NIGHT', - 'NOON', - 'PC', - 'PCD', - 'PCM', - 'PCV', - 'PHS', - 'WAKE', + 'data-access-ended', + 'data-accessed', + 'data-added', + 'data-changed', + 'data-modified', + 'data-removed', + 'named-event', + 'periodic', ]) - .array() .optional(), - _when: z - .lazy(() => elementSchema) + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + timingReference: z.lazy(() => referenceSchema).optional(), + timingDate: dateSchema.optional(), + _timingDate: z.lazy(() => elementSchema).optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + data: z + .lazy(() => dataRequirementSchema) .array() + .min(1) .optional(), - offset: unsignedIntSchema.optional(), - _offset: z.lazy(() => elementSchema).optional(), + condition: z.lazy(() => expressionSchema).optional(), }) .refine( (value) => [ { - names: ['boundsDuration', 'boundsRange', 'boundsPeriod'], + alternatives: [ + ['timingTiming'], + ['timingReference'], + ['timingDate', '_timingDate'], + ['timingDateTime', '_timingDateTime'], + ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -30438,31 +47634,30 @@ export const timingRepeatSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const timingSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - event: dateTimeSchema.array().optional(), - _event: z - .lazy(() => elementSchema) - .array() - .optional(), - repeat: timingRepeatSchema.optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - }), -) - -export const triggerDefinitionSchema: z.ZodType = z.lazy( +) as unknown as z.ZodType> + +export const usageContextSchema: z.ZodType> = z.lazy( () => z .strictObject({ @@ -30470,47 +47665,32 @@ export const triggerDefinitionSchema: z.ZodType = z.lazy( extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum([ - 'data-access-ended', - 'data-accessed', - 'data-added', - 'data-changed', - 'data-modified', - 'data-removed', - 'named-event', - 'periodic', - ]), - _type: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - timingTiming: z.lazy(() => timingSchema).optional(), - timingReference: z.lazy(() => referenceSchema).optional(), - timingDate: dateSchema.optional(), - _timingDate: z.lazy(() => elementSchema).optional(), - timingDateTime: dateTimeSchema.optional(), - _timingDateTime: z.lazy(() => elementSchema).optional(), - data: z - .lazy(() => dataRequirementSchema) - .array() - .optional(), - condition: z.lazy(() => expressionSchema).optional(), + code: z.lazy(() => codingSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ { - names: [ - 'timingTiming', - 'timingReference', - 'timingDate', - 'timingDateTime', + alternatives: [ + ['valueCodeableConcept'], + ['valueQuantity'], + ['valueRange'], + ['valueReference'], ], - required: false, + required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -30518,192 +47698,277 @@ export const triggerDefinitionSchema: z.ZodType = z.lazy( message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const valueSetComposeSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + lockedDate: dateSchema.optional(), + _lockedDate: z.lazy(() => elementSchema).optional(), + inactive: booleanSchema.optional(), + _inactive: z.lazy(() => elementSchema).optional(), + include: valueSetComposeIncludeSchema.array().min(1), + exclude: z + .lazy(() => valueSetComposeIncludeSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), -) + ) as unknown as z.ZodType> -export const usageContextSchema: z.ZodType = z.lazy(() => +export const valueSetExpansionSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() - .optional(), - code: z.lazy(() => codingSchema), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueCodeableConcept', - 'valueQuantity', - 'valueRange', - 'valueReference', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const valueSetComposeSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - lockedDate: dateSchema.optional(), - _lockedDate: z.lazy(() => elementSchema).optional(), - inactive: booleanSchema.optional(), - _inactive: z.lazy(() => elementSchema).optional(), - include: valueSetComposeIncludeSchema.array().min(1), - exclude: z - .lazy(() => valueSetComposeIncludeSchema) - .array() - .optional(), - }), -) - -export const valueSetExpansionSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: uriSchema.optional(), _identifier: z.lazy(() => elementSchema).optional(), - timestamp: dateTimeSchema, + timestamp: dateTimeSchema.optional(), _timestamp: z.lazy(() => elementSchema).optional(), total: integerSchema.optional(), _total: z.lazy(() => elementSchema).optional(), offset: integerSchema.optional(), _offset: z.lazy(() => elementSchema).optional(), - parameter: valueSetExpansionParameterSchema.array().optional(), - contains: valueSetExpansionContainsSchema.array().optional(), - }), -) + parameter: valueSetExpansionParameterSchema.array().min(1).optional(), + contains: valueSetExpansionContainsSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'timestamp', shadow: '_timestamp' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const valueSetComposeIncludeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const valueSetComposeIncludeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - concept: valueSetComposeIncludeConceptSchema.array().optional(), - filter: valueSetComposeIncludeFilterSchema.array().optional(), - valueSet: canonicalSchema.array().optional(), - _valueSet: z - .lazy(() => elementSchema) - .array() - .optional(), - }), - ) - -export const valueSetExpansionParameterSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueCode: codeSchema.optional(), - _valueCode: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueString', - 'valueBoolean', - 'valueInteger', - 'valueDecimal', - 'valueUri', - 'valueCode', - 'valueDateTime', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( + _system: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + concept: valueSetComposeIncludeConceptSchema.array().min(1).optional(), + filter: valueSetComposeIncludeFilterSchema.array().min(1).optional(), + valueSet: canonicalSchema.nullable().array().min(1).optional(), + _valueSet: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'valueSet', shadow: '_valueSet', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const valueSetExpansionParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueString', '_valueString'], + ['valueBoolean', '_valueBoolean'], + ['valueInteger', '_valueInteger'], + ['valueDecimal', '_valueDecimal'], + ['valueUri', '_valueUri'], + ['valueCode', '_valueCode'], + ['valueDateTime', '_valueDateTime'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const valueSetExpansionContainsSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const valueSetExpansionContainsSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), system: uriSchema.optional(), _system: z.lazy(() => elementSchema).optional(), @@ -30720,175 +47985,296 @@ export const valueSetExpansionContainsSchema: z.ZodType valueSetComposeIncludeConceptDesignationSchema) .array() + .min(1) .optional(), contains: z .lazy(() => valueSetExpansionContainsSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const valueSetComposeIncludeConceptSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const valueSetComposeIncludeConceptSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: codeSchema, + code: codeSchema.optional(), _code: z.lazy(() => elementSchema).optional(), display: stringSchema.optional(), _display: z.lazy(() => elementSchema).optional(), designation: valueSetComposeIncludeConceptDesignationSchema .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const valueSetComposeIncludeFilterSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const valueSetComposeIncludeFilterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - property: codeSchema, + property: codeSchema.optional(), _property: z.lazy(() => elementSchema).optional(), - op: z.enum([ - '=', - 'descendent-of', - 'exists', - 'generalizes', - 'in', - 'is-a', - 'is-not-a', - 'not-in', - 'regex', - ]), + op: z + .enum([ + '=', + 'descendent-of', + 'exists', + 'generalizes', + 'in', + 'is-a', + 'is-not-a', + 'not-in', + 'regex', + ]) + .optional(), _op: z.lazy(() => elementSchema).optional(), - value: stringSchema, + value: stringSchema.optional(), _value: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'property', shadow: '_property' }, + { name: 'op', shadow: '_op' }, + { name: 'value', shadow: '_value' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const valueSetComposeIncludeConceptDesignationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const valueSetComposeIncludeConceptDesignationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), language: codeSchema.optional(), _language: z.lazy(() => elementSchema).optional(), use: z.lazy(() => codingSchema).optional(), - value: stringSchema, + value: stringSchema.optional(), _value: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'value', shadow: '_value' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const valueSetSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('ValueSet'), - id: stringSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - immutable: booleanSchema.optional(), - _immutable: z.lazy(() => elementSchema).optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - compose: valueSetComposeSchema.optional(), - expansion: valueSetExpansionSchema.optional(), - }), -) - -export const verificationResultPrimarySourceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const valueSetSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ValueSet'), + id: stringSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + immutable: booleanSchema.optional(), + _immutable: z.lazy(() => elementSchema).optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + compose: valueSetComposeSchema.optional(), + expansion: valueSetExpansionSchema.optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const verificationResultPrimarySourceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), who: z.lazy(() => referenceSchema).optional(), type: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), communicationMethod: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), validationStatus: z.lazy(() => codeableConceptSchema).optional(), validationDate: dateTimeSchema.optional(), @@ -30897,21 +48283,30 @@ export const verificationResultPrimarySourceSchema: z.ZodType codeableConceptSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const verificationResultAttestationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const verificationResultAttestationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), who: z.lazy(() => referenceSchema).optional(), onBehalfOf: z.lazy(() => referenceSchema).optional(), @@ -30924,31 +48319,45 @@ export const verificationResultAttestationSchema: z.ZodType elementSchema).optional(), proxySignature: z.lazy(() => signatureSchema).optional(), sourceSignature: z.lazy(() => signatureSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const verificationResultValidatorSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const verificationResultValidatorSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), organization: z.lazy(() => referenceSchema), identityCertificate: stringSchema.optional(), _identityCertificate: z.lazy(() => elementSchema).optional(), attestationSignature: z.lazy(() => signatureSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const verificationResultSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const verificationResultSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('VerificationResult'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -30961,33 +48370,41 @@ export const verificationResultSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), target: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - targetLocation: stringSchema.array().optional(), + targetLocation: stringSchema.nullable().array().min(1).optional(), _targetLocation: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), need: z.lazy(() => codeableConceptSchema).optional(), - status: z.enum([ - 'attested', - 'in-process', - 'req-revalid', - 'reval-fail', - 'val-fail', - 'validated', - ]), + status: z + .enum([ + 'attested', + 'in-process', + 'req-revalid', + 'reval-fail', + 'val-fail', + 'validated', + ]) + .optional(), _status: z.lazy(() => elementSchema).optional(), statusDate: dateTimeSchema.optional(), _statusDate: z.lazy(() => elementSchema).optional(), @@ -30995,6 +48412,7 @@ export const verificationResultSchema: z.ZodType = validationProcess: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), frequency: z.lazy(() => timingSchema).optional(), lastPerformed: dateTimeSchema.optional(), @@ -31002,26 +48420,98 @@ export const verificationResultSchema: z.ZodType = nextScheduled: dateSchema.optional(), _nextScheduled: z.lazy(() => elementSchema).optional(), failureAction: z.lazy(() => codeableConceptSchema).optional(), - primarySource: verificationResultPrimarySourceSchema.array().optional(), + primarySource: verificationResultPrimarySourceSchema + .array() + .min(1) + .optional(), attestation: verificationResultAttestationSchema.optional(), - validator: verificationResultValidatorSchema.array().optional(), - }), - ) + validator: verificationResultValidatorSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'targetLocation', shadow: '_targetLocation', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const visionPrescriptionLensSpecificationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const visionPrescriptionLensSpecificationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), product: z.lazy(() => codeableConceptSchema), - eye: z.enum(['left', 'right']), + eye: z.enum(['left', 'right']).optional(), _eye: z.lazy(() => elementSchema).optional(), sphere: decimalSchema.optional(), _sphere: z.lazy(() => elementSchema).optional(), @@ -31029,7 +48519,10 @@ export const visionPrescriptionLensSpecificationSchema: z.ZodType elementSchema).optional(), axis: integerSchema.optional(), _axis: z.lazy(() => elementSchema).optional(), - prism: visionPrescriptionLensSpecificationPrismSchema.array().optional(), + prism: visionPrescriptionLensSpecificationPrismSchema + .array() + .min(1) + .optional(), add: decimalSchema.optional(), _add: z.lazy(() => elementSchema).optional(), power: decimalSchema.optional(), @@ -31046,32 +48539,84 @@ export const visionPrescriptionLensSpecificationSchema: z.ZodType annotationSchema) .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'eye', shadow: '_eye' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const visionPrescriptionLensSpecificationPrismSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const visionPrescriptionLensSpecificationPrismSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - amount: decimalSchema, + amount: decimalSchema.optional(), _amount: z.lazy(() => elementSchema).optional(), - base: z.enum(['down', 'in', 'out', 'up']), + base: z.enum(['down', 'in', 'out', 'up']).optional(), _base: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'amount', shadow: '_amount' }, + { name: 'base', shadow: '_base' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const visionPrescriptionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const visionPrescriptionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('VisionPrescription'), id: stringSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -31084,33 +48629,63 @@ export const visionPrescriptionSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), _status: z.lazy(() => elementSchema).optional(), - created: dateTimeSchema, + created: dateTimeSchema.optional(), _created: z.lazy(() => elementSchema).optional(), patient: z.lazy(() => referenceSchema), encounter: z.lazy(() => referenceSchema).optional(), - dateWritten: dateTimeSchema, + dateWritten: dateTimeSchema.optional(), _dateWritten: z.lazy(() => elementSchema).optional(), prescriber: z.lazy(() => referenceSchema), lensSpecification: visionPrescriptionLensSpecificationSchema .array() .min(1), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'created', shadow: '_created' }, + { name: 'dateWritten', shadow: '_dateWritten' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> export const base64BinarySchema: z.ZodType = z .string() @@ -31118,15 +48693,20 @@ export const base64BinarySchema: z.ZodType = z new RegExp('^[0-9a-zA-Z+/= \\t\\n\\r]*$'), 'Expected a FHIR base64Binary.', ) - .refine((value) => value.replace(/[ \t\n\r]/gu, '').length % 4 === 0, { - message: 'Expected a FHIR base64Binary.', - }) + .refine( + (value) => { + const length = value.replace(/[ \t\n\r]/gu, '').length + return length > 0 && length % 4 === 0 + }, + { message: 'Expected a FHIR base64Binary.' }, + ) export const booleanSchema: z.ZodType = z.boolean() export const canonicalSchema: z.ZodType = z .string() .regex(new RegExp('^(?:[^ \\t\\n\\r]*)$'), 'Expected a FHIR canonical.') + .min(1) export const codeSchema: z.ZodType = z .string() @@ -31143,6 +48723,9 @@ export const dateSchema: z.ZodType = z ), 'Expected a FHIR date.', ) + .refine(hasValidFhirCalendarDate, { + message: 'Expected a real Gregorian calendar date for FHIR date.', + }) export const dateTimeSchema: z.ZodType = z .string() @@ -31152,6 +48735,9 @@ export const dateTimeSchema: z.ZodType = z ), 'Expected a FHIR dateTime.', ) + .refine(hasValidFhirCalendarDate, { + message: 'Expected a real Gregorian calendar date for FHIR dateTime.', + }) export const decimalSchema: z.ZodType = z .number() @@ -31177,6 +48763,9 @@ export const instantSchema: z.ZodType = z ), 'Expected a FHIR instant.', ) + .refine(hasValidFhirCalendarDate, { + message: 'Expected a real Gregorian calendar date for FHIR instant.', + }) export const integerSchema: z.ZodType = z .number() @@ -31238,10 +48827,12 @@ export const unsignedIntSchema: z.ZodType = z export const uriSchema: z.ZodType = z .string() .regex(new RegExp('^(?:[^ \\t\\n\\r]*)$'), 'Expected a FHIR uri.') + .min(1) export const urlSchema: z.ZodType = z .string() .regex(new RegExp('^(?:[^ \\t\\n\\r]*)$'), 'Expected a FHIR url.') + .min(1) export const uuidSchema: z.ZodType = z .string() @@ -31252,7 +48843,7 @@ export const uuidSchema: z.ZodType = z 'Expected a FHIR uuid.', ) -export const xhtmlSchema: z.ZodType = z.string() +export const xhtmlSchema: z.ZodType = z.string().min(1) const modelledResources: Readonly> = { Account: accountSchema, @@ -31405,8 +48996,8 @@ const modelledResources: Readonly> = { const dispatchingResource = new WeakSet() -export const resourceSchema: z.ZodType = z - .custom( +export const resourceSchema: z.ZodType> = z + .custom>( (value) => typeof value === 'object' && value !== null && @@ -31418,9 +49009,14 @@ export const resourceSchema: z.ZodType = z ) .superRefine((value, ctx) => { const modelled = modelledResources[value.resourceType] - // A resource type the release defines but this package does not model keeps the open - // shape; reporting it as invalid would reject a conformant bundle. - if (modelled === undefined) return + if (modelled === undefined) { + ctx.addIssue({ + code: 'custom', + path: ['resourceType'], + message: 'Resource type is not published by this pinned FHIR release.', + }) + return + } // JSON cannot describe a resource that contains itself, but an in-memory object can, // and re-entering one would recur until the stack ran out. if (dispatchingResource.has(value)) return @@ -31436,6 +49032,23 @@ export const resourceSchema: z.ZodType = z } }) +/** Exact pinned release material and generated closure behind this module. */ +export const STRUCTURAL_SCHEMA_SOURCE = { + packageId: 'hl7.fhir.r4.core', + packageVersion: '4.0.1', + fhirVersion: '4.0.1', + archiveSha512: + '0981e52db3ffd9fede2809059d63c67b2837e5c9d749e5e0f16229c1784f178001616a9af65c34572f2a74c5a287fe3b4f4801297447deb3dacb9ad48b81b584', + structureCount: 206, +} as const + +/** Structural wire-boundary capabilities and deliberate limits. */ +export const STRUCTURAL_SCHEMA_CAPABILITIES = { + fullFhirPath: false, + preservesDecimalLexemesAfterJsonParse: false, + normativeConformanceValidator: 'official-fhir-validator', +} as const + /** * Invariants the release states for these paths that this module does not check. * @@ -31448,7 +49061,7 @@ export const UNCHECKED_CONSTRAINTS: Readonly< Account: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], ActivityDefinition: ['adf-0', 'dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], AdverseEvent: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], - Age: ['age-1', 'qty-3'], + Age: ['age-1'], AllergyIntolerance: [ 'ait-1', 'ait-2', @@ -31550,7 +49163,7 @@ export const UNCHECKED_CONSTRAINTS: Readonly< ], ContactPoint: ['cpt-2'], Contract: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], - Count: ['cnt-3', 'qty-3'], + Count: ['cnt-3'], Coverage: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], CoverageEligibilityRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], CoverageEligibilityResponse: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], @@ -31564,10 +49177,10 @@ export const UNCHECKED_CONSTRAINTS: Readonly< DeviceRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], DeviceUseStatement: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], DiagnosticReport: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], - Distance: ['dis-1', 'qty-3'], + Distance: ['dis-1'], DocumentManifest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], DocumentReference: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], - Duration: ['drt-1', 'qty-3'], + Duration: ['drt-1'], EffectEvidenceSynthesis: [ 'dom-2', 'dom-3', @@ -31732,7 +49345,6 @@ export const UNCHECKED_CONSTRAINTS: Readonly< PractitionerRole: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], Procedure: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], Provenance: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], - Quantity: ['qty-3'], Questionnaire: [ 'dom-2', 'dom-3', @@ -31760,7 +49372,6 @@ export const UNCHECKED_CONSTRAINTS: Readonly< 'QuestionnaireResponse.item': ['qrs-1'], Range: ['rng-2'], Ratio: ['rat-1'], - Reference: ['ref-1'], RelatedPerson: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], RequestGroup: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], 'RequestGroup.action': ['rqg-1'], diff --git a/packages/fhir/src/zod/r4b/schemas.ts b/packages/fhir/src/zod/r4b/schemas.ts index d0071c4f..a1628af8 100644 --- a/packages/fhir/src/zod/r4b/schemas.ts +++ b/packages/fhir/src/zod/r4b/schemas.ts @@ -8,7 +8,7 @@ // GENERATED FILE. Run `npm run generate:zod` after changing the generator or the release pin. // -/* eslint-disable sonarjs/regex-complexity, sonarjs/concise-regex, sonarjs/single-char-in-character-classes, sonarjs/single-character-alternation */ +/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion, sonarjs/no-nested-functions, sonarjs/regex-complexity, sonarjs/concise-regex, sonarjs/single-char-in-character-classes, sonarjs/single-character-alternation */ import type { Account as FhirAccount, @@ -691,390 +691,40 @@ import type { VisionPrescriptionLensSpecificationPrism as FhirVisionPrescriptionLensSpecificationPrism, } from 'fhir/r4b.js' import { z } from 'zod' -import { fhirDateTimeToEpoch } from '../support.js' +import type { FhirJson } from '../../r4/json.js' +import { compareFhirDateTimes, hasValidFhirCalendarDate } from '../support.js' // Generated from hl7.fhir.r4b.core#4.3.0 (FHIR 4.3.0). // 201 structures: every type the exposed roots reach. -export const accountCoverageSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - coverage: z.lazy(() => referenceSchema), - priority: positiveIntSchema.optional(), - _priority: z.lazy(() => elementSchema).optional(), - }), -) - -export const accountGuarantorSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - party: z.lazy(() => referenceSchema), - onHold: booleanSchema.optional(), - _onHold: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), -) - -export const accountSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Account'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'entered-in-error', - 'inactive', - 'on-hold', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - subject: z - .lazy(() => referenceSchema) - .array() - .optional(), - servicePeriod: z.lazy(() => periodSchema).optional(), - coverage: accountCoverageSchema.array().optional(), - owner: z.lazy(() => referenceSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - guarantor: accountGuarantorSchema.array().optional(), - partOf: z.lazy(() => referenceSchema).optional(), - }), -) - -export const activityDefinitionParticipantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum(['device', 'patient', 'practitioner', 'related-person']), - _type: z.lazy(() => elementSchema).optional(), - role: z.lazy(() => codeableConceptSchema).optional(), - }), - ) - -export const activityDefinitionDynamicValueSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - path: stringSchema, - _path: z.lazy(() => elementSchema).optional(), - expression: z.lazy(() => expressionSchema), - }), - ) - -export const activityDefinitionSchema: z.ZodType = +export const accountCoverageSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('ActivityDefinition'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - subjectCanonical: canonicalSchema.optional(), - _subjectCanonical: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - author: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - editor: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - reviewer: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - endorser: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) - .array() - .optional(), - library: canonicalSchema.array().optional(), - _library: z - .lazy(() => elementSchema) - .array() - .optional(), - kind: z - .enum([ - 'Appointment', - 'AppointmentResponse', - 'CarePlan', - 'Claim', - 'CommunicationRequest', - 'Contract', - 'DeviceRequest', - 'EnrollmentRequest', - 'ImmunizationRecommendation', - 'MedicationRequest', - 'NutritionOrder', - 'ServiceRequest', - 'SupplyRequest', - 'Task', - 'VisionPrescription', - ]) - .optional(), - _kind: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.optional(), - _profile: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - intent: z - .enum([ - 'directive', - 'filler-order', - 'instance-order', - 'option', - 'order', - 'original-order', - 'plan', - 'proposal', - 'reflex-order', - ]) - .optional(), - _intent: z.lazy(() => elementSchema).optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + coverage: z.lazy(() => referenceSchema), + priority: positiveIntSchema.optional(), _priority: z.lazy(() => elementSchema).optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: z.lazy(() => elementSchema).optional(), - timingTiming: z.lazy(() => timingSchema).optional(), - timingDateTime: dateTimeSchema.optional(), - _timingDateTime: z.lazy(() => elementSchema).optional(), - timingAge: z.lazy(() => ageSchema).optional(), - timingPeriod: z.lazy(() => periodSchema).optional(), - timingRange: z.lazy(() => rangeSchema).optional(), - timingDuration: z.lazy(() => durationSchema).optional(), - location: z.lazy(() => referenceSchema).optional(), - participant: activityDefinitionParticipantSchema.array().optional(), - productReference: z.lazy(() => referenceSchema).optional(), - productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - dosage: z - .lazy(() => dosageSchema) - .array() - .optional(), - bodySite: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - specimenRequirement: z - .lazy(() => referenceSchema) - .array() - .optional(), - observationRequirement: z - .lazy(() => referenceSchema) - .array() - .optional(), - observationResultRequirement: z - .lazy(() => referenceSchema) - .array() - .optional(), - transform: canonicalSchema.optional(), - _transform: z.lazy(() => elementSchema).optional(), - dynamicValue: activityDefinitionDynamicValueSchema.array().optional(), }) .refine( - (value) => - [ - { - names: [ - 'subjectCodeableConcept', - 'subjectReference', - 'subjectCanonical', - ], - required: false, - }, - { - names: [ - 'timingTiming', - 'timingDateTime', - 'timingAge', - 'timingPeriod', - 'timingRange', - 'timingDuration', - ], - required: false, - }, - { - names: ['productReference', 'productCodeableConcept'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const addressSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - use: z.enum(['billing', 'home', 'old', 'temp', 'work']).optional(), - _use: z.lazy(() => elementSchema).optional(), - type: z.enum(['both', 'physical', 'postal']).optional(), - _type: z.lazy(() => elementSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - line: stringSchema.array().optional(), - _line: z - .lazy(() => elementSchema) - .array() - .optional(), - city: stringSchema.optional(), - _city: z.lazy(() => elementSchema).optional(), - district: stringSchema.optional(), - _district: z.lazy(() => elementSchema).optional(), - state: stringSchema.optional(), - _state: z.lazy(() => elementSchema).optional(), - postalCode: stringSchema.optional(), - _postalCode: z.lazy(() => elementSchema).optional(), - country: stringSchema.optional(), - _country: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), -) - -export const administrableProductDefinitionPropertySchema: z.ZodType = +export const accountGuarantorSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -1082,115 +732,30 @@ export const administrableProductDefinitionPropertySchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - status: z.lazy(() => codeableConceptSchema).optional(), + party: z.lazy(() => referenceSchema), + onHold: booleanSchema.optional(), + _onHold: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), }) .refine( - (value) => - [ - { - names: [ - 'valueCodeableConcept', - 'valueQuantity', - 'valueDate', - 'valueBoolean', - 'valueAttachment', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const administrableProductDefinitionRouteOfAdministrationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - firstDose: z.lazy(() => quantitySchema).optional(), - maxSingleDose: z.lazy(() => quantitySchema).optional(), - maxDosePerDay: z.lazy(() => quantitySchema).optional(), - maxDosePerTreatmentPeriod: z.lazy(() => ratioSchema).optional(), - maxTreatmentPeriod: z.lazy(() => durationSchema).optional(), - targetSpecies: - administrableProductDefinitionRouteOfAdministrationTargetSpeciesSchema - .array() - .optional(), - }), - ) - -export const administrableProductDefinitionRouteOfAdministrationTargetSpeciesSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - withdrawalPeriod: - administrableProductDefinitionRouteOfAdministrationTargetSpeciesWithdrawalPeriodSchema - .array() - .optional(), - }), - ) - -export const administrableProductDefinitionRouteOfAdministrationTargetSpeciesWithdrawalPeriodSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - tissue: z.lazy(() => codeableConceptSchema), - value: z.lazy(() => quantitySchema), - supportingInformation: stringSchema.optional(), - _supportingInformation: z.lazy(() => elementSchema).optional(), - }), - ) - -export const administrableProductDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('AdministrableProductDefinition'), +export const accountSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Account'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -1202,451 +767,157 @@ export const administrableProductDefinitionSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - formOf: z - .lazy(() => referenceSchema) - .array() + status: z + .enum(['active', 'entered-in-error', 'inactive', 'on-hold', 'unknown']) .optional(), - administrableDoseForm: z.lazy(() => codeableConceptSchema).optional(), - unitOfPresentation: z.lazy(() => codeableConceptSchema).optional(), - producedFrom: z + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + subject: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - ingredient: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - device: z.lazy(() => referenceSchema).optional(), - property: administrableProductDefinitionPropertySchema.array().optional(), - routeOfAdministration: - administrableProductDefinitionRouteOfAdministrationSchema - .array() - .min(1), - }), - ) - -export const adverseEventSuspectEntitySchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - instance: z.lazy(() => referenceSchema), - causality: adverseEventSuspectEntityCausalitySchema.array().optional(), - }), - ) - -export const adverseEventSuspectEntityCausalitySchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - assessment: z.lazy(() => codeableConceptSchema).optional(), - productRelatedness: stringSchema.optional(), - _productRelatedness: z.lazy(() => elementSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + servicePeriod: z.lazy(() => periodSchema).optional(), + coverage: accountCoverageSchema.array().min(1).optional(), + owner: z.lazy(() => referenceSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + guarantor: accountGuarantorSchema.array().min(1).optional(), + partOf: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const adverseEventSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('AdverseEvent'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - actuality: z.enum(['actual', 'potential']), - _actuality: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - event: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - detected: dateTimeSchema.optional(), - _detected: z.lazy(() => elementSchema).optional(), - recordedDate: dateTimeSchema.optional(), - _recordedDate: z.lazy(() => elementSchema).optional(), - resultingCondition: z - .lazy(() => referenceSchema) - .array() - .optional(), - location: z.lazy(() => referenceSchema).optional(), - seriousness: z.lazy(() => codeableConceptSchema).optional(), - severity: z.lazy(() => codeableConceptSchema).optional(), - outcome: z.lazy(() => codeableConceptSchema).optional(), - recorder: z.lazy(() => referenceSchema).optional(), - contributor: z - .lazy(() => referenceSchema) - .array() - .optional(), - suspectEntity: adverseEventSuspectEntitySchema.array().optional(), - subjectMedicalHistory: z - .lazy(() => referenceSchema) - .array() - .optional(), - referenceDocument: z - .lazy(() => referenceSchema) - .array() - .optional(), - study: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) - -export const ageSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - comparator: z.enum(['<', '<=', '>', '>=']).optional(), - _comparator: z.lazy(() => elementSchema).optional(), - unit: stringSchema.optional(), - _unit: z.lazy(() => elementSchema).optional(), - system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - }), -) - -export const allergyIntoleranceReactionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const activityDefinitionParticipantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - substance: z.lazy(() => codeableConceptSchema).optional(), - manifestation: z - .lazy(() => codeableConceptSchema) - .array() - .min(1), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - onset: dateTimeSchema.optional(), - _onset: z.lazy(() => elementSchema).optional(), - severity: z.enum(['mild', 'moderate', 'severe']).optional(), - _severity: z.lazy(() => elementSchema).optional(), - exposureRoute: z.lazy(() => codeableConceptSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }), - ) - -export const allergyIntoleranceSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - resourceType: z.literal('AllergyIntolerance'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - clinicalStatus: z.lazy(() => codeableConceptSchema).optional(), - verificationStatus: z.lazy(() => codeableConceptSchema).optional(), - type: z.enum(['allergy', 'intolerance']).optional(), - _type: z.lazy(() => elementSchema).optional(), - category: z - .enum(['biologic', 'environment', 'food', 'medication']) - .array() - .optional(), - _category: z - .lazy(() => elementSchema) - .array() - .optional(), - criticality: z.enum(['high', 'low', 'unable-to-assess']).optional(), - _criticality: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - patient: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - onsetDateTime: dateTimeSchema.optional(), - _onsetDateTime: z.lazy(() => elementSchema).optional(), - onsetAge: z.lazy(() => ageSchema).optional(), - onsetPeriod: z.lazy(() => periodSchema).optional(), - onsetRange: z.lazy(() => rangeSchema).optional(), - onsetString: stringSchema.optional(), - _onsetString: z.lazy(() => elementSchema).optional(), - recordedDate: dateTimeSchema.optional(), - _recordedDate: z.lazy(() => elementSchema).optional(), - recorder: z.lazy(() => referenceSchema).optional(), - asserter: z.lazy(() => referenceSchema).optional(), - lastOccurrence: dateTimeSchema.optional(), - _lastOccurrence: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - reaction: allergyIntoleranceReactionSchema.array().optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'onsetDateTime', - 'onsetAge', - 'onsetPeriod', - 'onsetRange', - 'onsetString', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const annotationSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() + type: z + .enum(['device', 'patient', 'practitioner', 'related-person']) .optional(), - authorReference: z.lazy(() => referenceSchema).optional(), - authorString: stringSchema.optional(), - _authorString: z.lazy(() => elementSchema).optional(), - time: dateTimeSchema.optional(), - _time: z.lazy(() => elementSchema).optional(), - text: markdownSchema, - _text: z.lazy(() => elementSchema).optional(), + _type: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [{ names: ['authorReference', 'authorString'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const appointmentParticipantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const activityDefinitionDynamicValueSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - actor: z.lazy(() => referenceSchema).optional(), - required: z.enum(['information-only', 'optional', 'required']).optional(), - _required: z.lazy(() => elementSchema).optional(), - status: z.enum(['accepted', 'declined', 'needs-action', 'tentative']), - _status: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), - ) + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'path', shadow: '_path' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const appointmentSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Appointment'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'arrived', - 'booked', - 'cancelled', - 'checked-in', - 'entered-in-error', - 'fulfilled', - 'noshow', - 'pending', - 'proposed', - 'waitlist', - ]), - _status: z.lazy(() => elementSchema).optional(), - cancelationReason: z.lazy(() => codeableConceptSchema).optional(), - serviceCategory: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - serviceType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - specialty: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - appointmentType: z.lazy(() => codeableConceptSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - priority: unsignedIntSchema.optional(), - _priority: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - supportingInformation: z - .lazy(() => referenceSchema) - .array() - .optional(), - start: instantSchema.optional(), - _start: z.lazy(() => elementSchema).optional(), - end: instantSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), - minutesDuration: positiveIntSchema.optional(), - _minutesDuration: z.lazy(() => elementSchema).optional(), - slot: z - .lazy(() => referenceSchema) - .array() - .optional(), - created: dateTimeSchema.optional(), - _created: z.lazy(() => elementSchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - patientInstruction: stringSchema.optional(), - _patientInstruction: z.lazy(() => elementSchema).optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - participant: appointmentParticipantSchema.array().min(1), - requestedPeriod: z - .lazy(() => periodSchema) - .array() - .optional(), - }), -) - -export const appointmentResponseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('AppointmentResponse'), +export const activityDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ActivityDefinition'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -1658,455 +929,549 @@ export const appointmentResponseSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - appointment: z.lazy(() => referenceSchema), - start: instantSchema.optional(), - _start: z.lazy(() => elementSchema).optional(), - end: instantSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), - participantType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - actor: z.lazy(() => referenceSchema).optional(), - participantStatus: z.enum([ - 'accepted', - 'declined', - 'needs-action', - 'tentative', - ]), - _participantStatus: z.lazy(() => elementSchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - }), - ) - -export const attachmentSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - contentType: codeSchema.optional(), - _contentType: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - data: base64BinarySchema.optional(), - _data: z.lazy(() => elementSchema).optional(), - url: urlSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - size: unsignedIntSchema.optional(), - _size: z.lazy(() => elementSchema).optional(), - hash: base64BinarySchema.optional(), - _hash: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - creation: dateTimeSchema.optional(), - _creation: z.lazy(() => elementSchema).optional(), - }), -) - -export const auditEventAgentSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + subjectCanonical: canonicalSchema.optional(), + _subjectCanonical: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - role: z + jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - who: z.lazy(() => referenceSchema).optional(), - altId: stringSchema.optional(), - _altId: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - requestor: booleanSchema, - _requestor: z.lazy(() => elementSchema).optional(), - location: z.lazy(() => referenceSchema).optional(), - policy: uriSchema.array().optional(), - _policy: z + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + library: canonicalSchema.nullable().array().min(1).optional(), + _library: z .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + kind: z + .enum([ + 'Appointment', + 'AppointmentResponse', + 'CarePlan', + 'Claim', + 'CommunicationRequest', + 'Contract', + 'DeviceRequest', + 'EnrollmentRequest', + 'ImmunizationRecommendation', + 'MedicationRequest', + 'NutritionOrder', + 'ServiceRequest', + 'SupplyRequest', + 'Task', + 'VisionPrescription', + ]) + .optional(), + _kind: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + intent: z + .enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]) + .optional(), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + timingAge: z.lazy(() => ageSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingRange: z.lazy(() => rangeSchema).optional(), + timingDuration: z.lazy(() => durationSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + participant: activityDefinitionParticipantSchema + .array() + .min(1) + .optional(), + productReference: z.lazy(() => referenceSchema).optional(), + productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + dosage: z + .lazy(() => dosageSchema) .array() + .min(1) .optional(), - media: z.lazy(() => codingSchema).optional(), - network: auditEventAgentNetworkSchema.optional(), - purposeOfUse: z + bodySite: z .lazy(() => codeableConceptSchema) .array() + .min(1) + .optional(), + specimenRequirement: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + observationRequirement: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + observationResultRequirement: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), - }), -) + transform: canonicalSchema.optional(), + _transform: z.lazy(() => elementSchema).optional(), + dynamicValue: activityDefinitionDynamicValueSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['subjectCodeableConcept'], + ['subjectReference'], + ['subjectCanonical', '_subjectCanonical'], + ], + required: false, + }, + { + alternatives: [ + ['timingTiming'], + ['timingDateTime', '_timingDateTime'], + ['timingAge'], + ['timingPeriod'], + ['timingRange'], + ['timingDuration'], + ], + required: false, + }, + { + alternatives: [['productReference'], ['productCodeableConcept']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'library', shadow: '_library', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const auditEventSourceSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), +export const addressSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z + use: z.enum(['billing', 'home', 'old', 'temp', 'work']).optional(), + _use: z.lazy(() => elementSchema).optional(), + type: z.enum(['both', 'physical', 'postal']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + line: stringSchema.nullable().array().min(1).optional(), + _line: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + city: stringSchema.optional(), + _city: z.lazy(() => elementSchema).optional(), + district: stringSchema.optional(), + _district: z.lazy(() => elementSchema).optional(), + state: stringSchema.optional(), + _state: z.lazy(() => elementSchema).optional(), + postalCode: stringSchema.optional(), + _postalCode: z.lazy(() => elementSchema).optional(), + country: stringSchema.optional(), + _country: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'line', shadow: '_line', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const administrableProductDefinitionPropertySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - site: stringSchema.optional(), - _site: z.lazy(() => elementSchema).optional(), - observer: z.lazy(() => referenceSchema), - type: z - .lazy(() => codingSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), -) + type: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueCodeableConcept'], + ['valueQuantity'], + ['valueDate', '_valueDate'], + ['valueBoolean', '_valueBoolean'], + ['valueAttachment'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const auditEventEntitySchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const administrableProductDefinitionRouteOfAdministrationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - what: z.lazy(() => referenceSchema).optional(), - type: z.lazy(() => codingSchema).optional(), - role: z.lazy(() => codingSchema).optional(), - lifecycle: z.lazy(() => codingSchema).optional(), - securityLabel: z - .lazy(() => codingSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - query: base64BinarySchema.optional(), - _query: z.lazy(() => elementSchema).optional(), - detail: auditEventEntityDetailSchema.array().optional(), - }), -) + code: z.lazy(() => codeableConceptSchema), + firstDose: z.lazy(() => quantitySchema).optional(), + maxSingleDose: z.lazy(() => quantitySchema).optional(), + maxDosePerDay: z.lazy(() => quantitySchema).optional(), + maxDosePerTreatmentPeriod: z.lazy(() => ratioSchema).optional(), + maxTreatmentPeriod: z.lazy(() => durationSchema).optional(), + targetSpecies: + administrableProductDefinitionRouteOfAdministrationTargetSpeciesSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const auditEventAgentNetworkSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const administrableProductDefinitionRouteOfAdministrationTargetSpeciesSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - address: stringSchema.optional(), - _address: z.lazy(() => elementSchema).optional(), - type: z.enum(['1', '2', '3', '4', '5']).optional(), - _type: z.lazy(() => elementSchema).optional(), - }), - ) - -export const auditEventEntityDetailSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: stringSchema, - _type: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['valueString', 'valueBase64Binary'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const auditEventSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('AuditEvent'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codingSchema), - subtype: z - .lazy(() => codingSchema) - .array() - .optional(), - action: z.enum(['C', 'D', 'E', 'R', 'U']).optional(), - _action: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - recorded: instantSchema, - _recorded: z.lazy(() => elementSchema).optional(), - outcome: z.enum(['0', '12', '4', '8']).optional(), - _outcome: z.lazy(() => elementSchema).optional(), - outcomeDesc: stringSchema.optional(), - _outcomeDesc: z.lazy(() => elementSchema).optional(), - purposeOfEvent: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - agent: auditEventAgentSchema.array().min(1), - source: auditEventSourceSchema, - entity: auditEventEntitySchema.array().optional(), - }), -) - -export const basicSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Basic'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - subject: z.lazy(() => referenceSchema).optional(), - created: dateSchema.optional(), - _created: z.lazy(() => elementSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - }), -) - -export const binarySchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Binary'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - contentType: codeSchema, - _contentType: z.lazy(() => elementSchema).optional(), - securityContext: z.lazy(() => referenceSchema).optional(), - data: base64BinarySchema.optional(), - _data: z.lazy(() => elementSchema).optional(), - }), -) - -export const biologicallyDerivedProductCollectionSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - collector: z.lazy(() => referenceSchema).optional(), - source: z.lazy(() => referenceSchema).optional(), - collectedDateTime: dateTimeSchema.optional(), - _collectedDateTime: z.lazy(() => elementSchema).optional(), - collectedPeriod: z.lazy(() => periodSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['collectedDateTime', 'collectedPeriod'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const biologicallyDerivedProductProcessingSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - procedure: z.lazy(() => codeableConceptSchema).optional(), - additive: z.lazy(() => referenceSchema).optional(), - timeDateTime: dateTimeSchema.optional(), - _timeDateTime: z.lazy(() => elementSchema).optional(), - timePeriod: z.lazy(() => periodSchema).optional(), - }) - .refine( - (value) => - [{ names: ['timeDateTime', 'timePeriod'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const biologicallyDerivedProductManipulationSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + code: z.lazy(() => codeableConceptSchema), + withdrawalPeriod: + administrableProductDefinitionRouteOfAdministrationTargetSpeciesWithdrawalPeriodSchema .array() + .min(1) .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - timeDateTime: dateTimeSchema.optional(), - _timeDateTime: z.lazy(() => elementSchema).optional(), - timePeriod: z.lazy(() => periodSchema).optional(), - }) - .refine( - (value) => - [{ names: ['timeDateTime', 'timePeriod'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const biologicallyDerivedProductStorageSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const administrableProductDefinitionRouteOfAdministrationTargetSpeciesWithdrawalPeriodSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - temperature: decimalSchema.optional(), - _temperature: z.lazy(() => elementSchema).optional(), - scale: z.enum(['celsius', 'farenheit', 'kelvin']).optional(), - _scale: z.lazy(() => elementSchema).optional(), - duration: z.lazy(() => periodSchema).optional(), - }), - ) + tissue: z.lazy(() => codeableConceptSchema), + value: z.lazy(() => quantitySchema), + supportingInformation: stringSchema.optional(), + _supportingInformation: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const biologicallyDerivedProductSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('BiologicallyDerivedProduct'), +export const administrableProductDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('AdministrableProductDefinition'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -2118,736 +1483,726 @@ export const biologicallyDerivedProductSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - productCategory: z - .enum(['biologicalAgent', 'cells', 'fluid', 'organ', 'tissue']) - .optional(), - _productCategory: z.lazy(() => elementSchema).optional(), - productCode: z.lazy(() => codeableConceptSchema).optional(), - status: z.enum(['available', 'unavailable']).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), _status: z.lazy(() => elementSchema).optional(), - request: z + formOf: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - quantity: integerSchema.optional(), - _quantity: z.lazy(() => elementSchema).optional(), - parent: z + administrableDoseForm: z.lazy(() => codeableConceptSchema).optional(), + unitOfPresentation: z.lazy(() => codeableConceptSchema).optional(), + producedFrom: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - collection: biologicallyDerivedProductCollectionSchema.optional(), - processing: biologicallyDerivedProductProcessingSchema.array().optional(), - manipulation: biologicallyDerivedProductManipulationSchema.optional(), - storage: biologicallyDerivedProductStorageSchema.array().optional(), - }), - ) - -export const bodyStructureSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('BodyStructure'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - morphology: z.lazy(() => codeableConceptSchema).optional(), - location: z.lazy(() => codeableConceptSchema).optional(), - locationQualifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - image: z - .lazy(() => attachmentSchema) - .array() - .optional(), - patient: z.lazy(() => referenceSchema), - }), -) - -export const bundleLinkSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - relation: stringSchema, - _relation: z.lazy(() => elementSchema).optional(), - url: uriSchema, - _url: z.lazy(() => elementSchema).optional(), - }), -) - -export const bundleEntrySchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - link: z - .lazy(() => bundleLinkSchema) - .array() - .optional(), - fullUrl: uriSchema.optional(), - _fullUrl: z.lazy(() => elementSchema).optional(), - resource: z.lazy(() => resourceSchema).optional(), - search: bundleEntrySearchSchema.optional(), - request: bundleEntryRequestSchema.optional(), - response: bundleEntryResponseSchema.optional(), - }), -) - -export const bundleEntrySearchSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - mode: z.enum(['include', 'match', 'outcome']).optional(), - _mode: z.lazy(() => elementSchema).optional(), - score: decimalSchema.optional(), - _score: z.lazy(() => elementSchema).optional(), - }), -) - -export const bundleEntryRequestSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + ingredient: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + device: z.lazy(() => referenceSchema).optional(), + property: administrableProductDefinitionPropertySchema .array() + .min(1) .optional(), - method: z.enum(['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT']), - _method: z.lazy(() => elementSchema).optional(), - url: uriSchema, - _url: z.lazy(() => elementSchema).optional(), - ifNoneMatch: stringSchema.optional(), - _ifNoneMatch: z.lazy(() => elementSchema).optional(), - ifModifiedSince: instantSchema.optional(), - _ifModifiedSince: z.lazy(() => elementSchema).optional(), - ifMatch: stringSchema.optional(), - _ifMatch: z.lazy(() => elementSchema).optional(), - ifNoneExist: stringSchema.optional(), - _ifNoneExist: z.lazy(() => elementSchema).optional(), - }), - ) + routeOfAdministration: + administrableProductDefinitionRouteOfAdministrationSchema + .array() + .min(1), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const bundleEntryResponseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const adverseEventSuspectEntitySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - status: stringSchema, - _status: z.lazy(() => elementSchema).optional(), - location: uriSchema.optional(), - _location: z.lazy(() => elementSchema).optional(), - etag: stringSchema.optional(), - _etag: z.lazy(() => elementSchema).optional(), - lastModified: instantSchema.optional(), - _lastModified: z.lazy(() => elementSchema).optional(), - outcome: z.lazy(() => resourceSchema).optional(), - }), - ) - -export const bundleSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Bundle'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), - type: z.enum([ - 'batch', - 'batch-response', - 'collection', - 'document', - 'history', - 'message', - 'searchset', - 'transaction', - 'transaction-response', - ]), - _type: z.lazy(() => elementSchema).optional(), - timestamp: instantSchema.optional(), - _timestamp: z.lazy(() => elementSchema).optional(), - total: unsignedIntSchema.optional(), - _total: z.lazy(() => elementSchema).optional(), - link: bundleLinkSchema.array().optional(), - entry: bundleEntrySchema.array().optional(), - signature: z.lazy(() => signatureSchema).optional(), - }), -) - -export const capabilityStatementSoftwareSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + instance: z.lazy(() => referenceSchema), + causality: adverseEventSuspectEntityCausalitySchema .array() + .min(1) .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - releaseDate: dateTimeSchema.optional(), - _releaseDate: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const capabilityStatementImplementationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const adverseEventSuspectEntityCausalitySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: stringSchema, - _description: z.lazy(() => elementSchema).optional(), - url: urlSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - custodian: z.lazy(() => referenceSchema).optional(), - }), - ) + assessment: z.lazy(() => codeableConceptSchema).optional(), + productRelatedness: stringSchema.optional(), + _productRelatedness: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const capabilityStatementRestSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const adverseEventSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('AdverseEvent'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + actuality: z.enum(['actual', 'potential']).optional(), + _actuality: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + event: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + detected: dateTimeSchema.optional(), + _detected: z.lazy(() => elementSchema).optional(), + recordedDate: dateTimeSchema.optional(), + _recordedDate: z.lazy(() => elementSchema).optional(), + resultingCondition: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + location: z.lazy(() => referenceSchema).optional(), + seriousness: z.lazy(() => codeableConceptSchema).optional(), + severity: z.lazy(() => codeableConceptSchema).optional(), + outcome: z.lazy(() => codeableConceptSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + contributor: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + suspectEntity: adverseEventSuspectEntitySchema + .array() + .min(1) + .optional(), + subjectMedicalHistory: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + referenceDocument: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + study: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'actuality', shadow: '_actuality' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const ageSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const quantity = value as Record + const hasCode = + quantity.code !== undefined || quantity._code !== undefined + return ( + !hasCode || + quantity.system !== undefined || + quantity._system !== undefined + ) + }, + { message: 'qty-3: a coded Quantity requires its unit system.' }, + ), +) as unknown as z.ZodType> + +export const allergyIntoleranceReactionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - mode: z.enum(['client', 'server']), - _mode: z.lazy(() => elementSchema).optional(), - documentation: markdownSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - security: capabilityStatementRestSecuritySchema.optional(), - resource: capabilityStatementRestResourceSchema.array().optional(), - interaction: capabilityStatementRestInteractionSchema.array().optional(), - searchParam: z - .lazy(() => capabilityStatementRestResourceSearchParamSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - operation: z - .lazy(() => capabilityStatementRestResourceOperationSchema) + substance: z.lazy(() => codeableConceptSchema).optional(), + manifestation: z + .lazy(() => codeableConceptSchema) .array() - .optional(), - compartment: canonicalSchema.array().optional(), - _compartment: z - .lazy(() => elementSchema) + .min(1), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + onset: dateTimeSchema.optional(), + _onset: z.lazy(() => elementSchema).optional(), + severity: z.enum(['mild', 'moderate', 'severe']).optional(), + _severity: z.lazy(() => elementSchema).optional(), + exposureRoute: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const capabilityStatementMessagingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const allergyIntoleranceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('AllergyIntolerance'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - endpoint: capabilityStatementMessagingEndpointSchema.array().optional(), - reliableCache: unsignedIntSchema.optional(), - _reliableCache: z.lazy(() => elementSchema).optional(), - documentation: markdownSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - supportedMessage: capabilityStatementMessagingSupportedMessageSchema + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - }), - ) - -export const capabilityStatementDocumentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + clinicalStatus: z.lazy(() => codeableConceptSchema).optional(), + verificationStatus: z.lazy(() => codeableConceptSchema).optional(), + type: z.enum(['allergy', 'intolerance']).optional(), + _type: z.lazy(() => elementSchema).optional(), + category: z + .enum(['biologic', 'environment', 'food', 'medication']) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + _category: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - mode: z.enum(['consumer', 'producer']), - _mode: z.lazy(() => elementSchema).optional(), - documentation: markdownSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema, - _profile: z.lazy(() => elementSchema).optional(), - }), - ) + criticality: z.enum(['high', 'low', 'unable-to-assess']).optional(), + _criticality: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + patient: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + onsetDateTime: dateTimeSchema.optional(), + _onsetDateTime: z.lazy(() => elementSchema).optional(), + onsetAge: z.lazy(() => ageSchema).optional(), + onsetPeriod: z.lazy(() => periodSchema).optional(), + onsetRange: z.lazy(() => rangeSchema).optional(), + onsetString: stringSchema.optional(), + _onsetString: z.lazy(() => elementSchema).optional(), + recordedDate: dateTimeSchema.optional(), + _recordedDate: z.lazy(() => elementSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + asserter: z.lazy(() => referenceSchema).optional(), + lastOccurrence: dateTimeSchema.optional(), + _lastOccurrence: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + reaction: allergyIntoleranceReactionSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['onsetDateTime', '_onsetDateTime'], + ['onsetAge'], + ['onsetPeriod'], + ['onsetRange'], + ['onsetString', '_onsetString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'category', shadow: '_category', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const capabilityStatementRestSecuritySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const annotationSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + authorReference: z.lazy(() => referenceSchema).optional(), + authorString: stringSchema.optional(), + _authorString: z.lazy(() => elementSchema).optional(), + time: dateTimeSchema.optional(), + _time: z.lazy(() => elementSchema).optional(), + text: markdownSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['authorReference'], + ['authorString', '_authorString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'text', shadow: '_text' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const appointmentParticipantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - cors: booleanSchema.optional(), - _cors: z.lazy(() => elementSchema).optional(), - service: z + type: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - }), - ) - -export const capabilityStatementRestResourceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum([ - 'Account', - 'ActivityDefinition', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RegulatedAuthorization', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]), - _type: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.optional(), - _profile: z.lazy(() => elementSchema).optional(), - supportedProfile: canonicalSchema.array().optional(), - _supportedProfile: z - .lazy(() => elementSchema) - .array() + actor: z.lazy(() => referenceSchema).optional(), + required: z.enum(['information-only', 'optional', 'required']).optional(), + _required: z.lazy(() => elementSchema).optional(), + status: z + .enum(['accepted', 'declined', 'needs-action', 'tentative']) .optional(), - documentation: markdownSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - interaction: capabilityStatementRestResourceInteractionSchema - .array() - .optional(), - versioning: z - .enum(['no-version', 'versioned', 'versioned-update']) - .optional(), - _versioning: z.lazy(() => elementSchema).optional(), - readHistory: booleanSchema.optional(), - _readHistory: z.lazy(() => elementSchema).optional(), - updateCreate: booleanSchema.optional(), - _updateCreate: z.lazy(() => elementSchema).optional(), - conditionalCreate: booleanSchema.optional(), - _conditionalCreate: z.lazy(() => elementSchema).optional(), - conditionalRead: z - .enum(['full-support', 'modified-since', 'not-match', 'not-supported']) - .optional(), - _conditionalRead: z.lazy(() => elementSchema).optional(), - conditionalUpdate: booleanSchema.optional(), - _conditionalUpdate: z.lazy(() => elementSchema).optional(), - conditionalDelete: z - .enum(['multiple', 'not-supported', 'single']) - .optional(), - _conditionalDelete: z.lazy(() => elementSchema).optional(), - referencePolicy: z - .enum(['enforced', 'literal', 'local', 'logical', 'resolves']) - .array() - .optional(), - _referencePolicy: z - .lazy(() => elementSchema) - .array() - .optional(), - searchInclude: stringSchema.array().optional(), - _searchInclude: z - .lazy(() => elementSchema) - .array() - .optional(), - searchRevInclude: stringSchema.array().optional(), - _searchRevInclude: z - .lazy(() => elementSchema) - .array() - .optional(), - searchParam: capabilityStatementRestResourceSearchParamSchema - .array() - .optional(), - operation: capabilityStatementRestResourceOperationSchema - .array() - .optional(), - }), - ) - -export const capabilityStatementRestInteractionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.enum(['batch', 'history-system', 'search-system', 'transaction']), - _code: z.lazy(() => elementSchema).optional(), - documentation: markdownSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - }), - ) - -export const capabilityStatementRestResourceSearchParamSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - definition: canonicalSchema.optional(), - _definition: z.lazy(() => elementSchema).optional(), - type: z.enum([ - 'composite', - 'date', - 'number', - 'quantity', - 'reference', - 'special', - 'string', - 'token', - 'uri', - ]), - _type: z.lazy(() => elementSchema).optional(), - documentation: markdownSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - }), - ) - -export const capabilityStatementRestResourceOperationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - definition: canonicalSchema, - _definition: z.lazy(() => elementSchema).optional(), - documentation: markdownSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - }), - ) - -export const capabilityStatementMessagingEndpointSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - protocol: z.lazy(() => codingSchema), - address: urlSchema, - _address: z.lazy(() => elementSchema).optional(), - }), - ) - -export const capabilityStatementMessagingSupportedMessageSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - mode: z.enum(['receiver', 'sender']), - _mode: z.lazy(() => elementSchema).optional(), - definition: canonicalSchema, - _definition: z.lazy(() => elementSchema).optional(), - }), - ) + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const capabilityStatementRestResourceInteractionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.enum([ - 'create', - 'delete', - 'history-instance', - 'history-type', - 'patch', - 'read', - 'search-type', - 'update', - 'vread', - ]), - _code: z.lazy(() => elementSchema).optional(), - documentation: markdownSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - }), - ) +export const appointmentSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('Appointment'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'arrived', + 'booked', + 'cancelled', + 'checked-in', + 'entered-in-error', + 'fulfilled', + 'noshow', + 'pending', + 'proposed', + 'waitlist', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + cancelationReason: z.lazy(() => codeableConceptSchema).optional(), + serviceCategory: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + serviceType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + appointmentType: z.lazy(() => codeableConceptSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + priority: unsignedIntSchema.optional(), + _priority: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + start: instantSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: instantSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + minutesDuration: positiveIntSchema.optional(), + _minutesDuration: z.lazy(() => elementSchema).optional(), + slot: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + patientInstruction: stringSchema.optional(), + _patientInstruction: z.lazy(() => elementSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + participant: appointmentParticipantSchema.array().min(1), + requestedPeriod: z + .lazy(() => periodSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const capabilityStatementSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('CapabilityStatement'), +export const appointmentResponseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('AppointmentResponse'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -2859,144 +2214,101 @@ export const capabilityStatementSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema, - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - jurisdiction: z + appointment: z.lazy(() => referenceSchema), + start: instantSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: instantSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + participantType: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - kind: z.enum(['capability', 'instance', 'requirements']), - _kind: z.lazy(() => elementSchema).optional(), - instantiates: canonicalSchema.array().optional(), - _instantiates: z - .lazy(() => elementSchema) - .array() - .optional(), - imports: canonicalSchema.array().optional(), - _imports: z - .lazy(() => elementSchema) - .array() - .optional(), - software: capabilityStatementSoftwareSchema.optional(), - implementation: capabilityStatementImplementationSchema.optional(), - fhirVersion: z.enum([ - '0.0.80', - '0.0.81', - '0.0.82', - '0.01', - '0.05', - '0.06', - '0.11', - '0.4.0', - '0.5.0', - '1.0.0', - '1.0.1', - '1.0.2', - '1.1.0', - '1.4.0', - '1.6.0', - '1.8.0', - '3.0.0', - '3.0.1', - '3.0.2', - '3.3.0', - '3.5.0', - '4.0.0', - '4.0.1', - '4.1.0', - '4.3.0', - '4.3.0-cibuild', - '4.3.0-snapshot1', - ]), - _fhirVersion: z.lazy(() => elementSchema).optional(), - format: codeSchema.array().min(1), - _format: z - .lazy(() => elementSchema) - .array() - .optional(), - patchFormat: codeSchema.array().optional(), - _patchFormat: z - .lazy(() => elementSchema) - .array() - .optional(), - implementationGuide: canonicalSchema.array().optional(), - _implementationGuide: z - .lazy(() => elementSchema) - .array() + actor: z.lazy(() => referenceSchema).optional(), + participantStatus: z + .enum(['accepted', 'declined', 'needs-action', 'tentative']) .optional(), - rest: capabilityStatementRestSchema.array().optional(), - messaging: capabilityStatementMessagingSchema.array().optional(), - document: capabilityStatementDocumentSchema.array().optional(), - }), - ) + _participantStatus: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'participantStatus', shadow: '_participantStatus' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const carePlanActivitySchema: z.ZodType = z.lazy( +export const attachmentSchema: z.ZodType> = z.lazy( () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - outcomeCodeableConcept: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - outcomeReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - progress: z - .lazy(() => annotationSchema) - .array() - .optional(), - reference: z.lazy(() => referenceSchema).optional(), - detail: carePlanActivityDetailSchema.optional(), - }), -) + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + contentType: codeSchema.optional(), + _contentType: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + data: base64BinarySchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + url: urlSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + size: unsignedIntSchema.optional(), + _size: z.lazy(() => elementSchema).optional(), + hash: base64BinarySchema.optional(), + _hash: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + creation: dateTimeSchema.optional(), + _creation: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const carePlanActivityDetailSchema: z.ZodType = +export const auditEventAgentSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -3004,501 +2316,474 @@ export const carePlanActivityDetailSchema: z.ZodType extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - kind: z - .enum([ - 'Appointment', - 'CommunicationRequest', - 'DeviceRequest', - 'MedicationRequest', - 'NutritionOrder', - 'ServiceRequest', - 'Task', - 'VisionPrescription', - ]) - .optional(), - _kind: z.lazy(() => elementSchema).optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) + type: z.lazy(() => codeableConceptSchema).optional(), + role: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z + who: z.lazy(() => referenceSchema).optional(), + altId: stringSchema.optional(), + _altId: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + requestor: booleanSchema.optional(), + _requestor: z.lazy(() => elementSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + policy: uriSchema.nullable().array().min(1).optional(), + _policy: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - reasonCode: z + media: z.lazy(() => codingSchema).optional(), + network: auditEventAgentNetworkSchema.optional(), + purposeOfUse: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - reasonReference: z - .lazy(() => referenceSchema) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'requestor', shadow: '_requestor' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'policy', shadow: '_policy', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const auditEventSourceSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - goal: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - status: z.enum([ - 'cancelled', - 'completed', - 'entered-in-error', - 'in-progress', - 'not-started', - 'on-hold', - 'scheduled', - 'stopped', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: z.lazy(() => elementSchema).optional(), - scheduledTiming: z.lazy(() => timingSchema).optional(), - scheduledPeriod: z.lazy(() => periodSchema).optional(), - scheduledString: stringSchema.optional(), - _scheduledString: z.lazy(() => elementSchema).optional(), - location: z.lazy(() => referenceSchema).optional(), - performer: z - .lazy(() => referenceSchema) + site: stringSchema.optional(), + _site: z.lazy(() => elementSchema).optional(), + observer: z.lazy(() => referenceSchema), + type: z + .lazy(() => codingSchema) .array() + .min(1) .optional(), - productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - productReference: z.lazy(() => referenceSchema).optional(), - dailyAmount: z.lazy(() => quantitySchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), }) .refine( - (value) => - [ - { - names: ['scheduledTiming', 'scheduledPeriod', 'scheduledString'], - required: false, - }, - { - names: ['productCodeableConcept', 'productReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) - -export const carePlanSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('CarePlan'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - replaces: z - .lazy(() => referenceSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'completed', - 'draft', - 'entered-in-error', - 'on-hold', - 'revoked', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - intent: z.enum(['option', 'order', 'plan', 'proposal']), - _intent: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - created: dateTimeSchema.optional(), - _created: z.lazy(() => elementSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - contributor: z - .lazy(() => referenceSchema) - .array() - .optional(), - careTeam: z - .lazy(() => referenceSchema) - .array() - .optional(), - addresses: z - .lazy(() => referenceSchema) - .array() - .optional(), - supportingInfo: z - .lazy(() => referenceSchema) - .array() - .optional(), - goal: z - .lazy(() => referenceSchema) - .array() - .optional(), - activity: carePlanActivitySchema.array().optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }), -) - -export const careTeamParticipantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - role: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - member: z.lazy(() => referenceSchema).optional(), - onBehalfOf: z.lazy(() => referenceSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), - ) + ) as unknown as z.ZodType> -export const careTeamSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('CareTeam'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z - .enum(['active', 'entered-in-error', 'inactive', 'proposed', 'suspended']) - .optional(), - _status: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - participant: careTeamParticipantSchema.array().optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - managingOrganization: z - .lazy(() => referenceSchema) - .array() - .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }), -) - -export const catalogEntryRelatedEntrySchema: z.ZodType = +export const auditEventEntitySchema: z.ZodType> = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - relationtype: z.enum(['is-replaced-by', 'triggers']), - _relationtype: z.lazy(() => elementSchema).optional(), - item: z.lazy(() => referenceSchema), - }), - ) + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + what: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codingSchema).optional(), + role: z.lazy(() => codingSchema).optional(), + lifecycle: z.lazy(() => codingSchema).optional(), + securityLabel: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + query: base64BinarySchema.optional(), + _query: z.lazy(() => elementSchema).optional(), + detail: auditEventEntityDetailSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const catalogEntrySchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('CatalogEntry'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - orderable: booleanSchema, - _orderable: z.lazy(() => elementSchema).optional(), - referencedItem: z.lazy(() => referenceSchema), - additionalIdentifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - classification: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), - _status: z.lazy(() => elementSchema).optional(), - validityPeriod: z.lazy(() => periodSchema).optional(), - validTo: dateTimeSchema.optional(), - _validTo: z.lazy(() => elementSchema).optional(), - lastUpdated: dateTimeSchema.optional(), - _lastUpdated: z.lazy(() => elementSchema).optional(), - additionalCharacteristic: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - additionalClassification: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - relatedEntry: catalogEntryRelatedEntrySchema.array().optional(), - }), -) - -export const chargeItemPerformerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const auditEventAgentNetworkSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - function: z.lazy(() => codeableConceptSchema).optional(), - actor: z.lazy(() => referenceSchema), - }), - ) + address: stringSchema.optional(), + _address: z.lazy(() => elementSchema).optional(), + type: z.enum(['1', '2', '3', '4', '5']).optional(), + _type: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const chargeItemSchema: z.ZodType = z.lazy(() => +export const auditEventEntityDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('ChargeItem'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - definitionUri: uriSchema.array().optional(), - _definitionUri: z - .lazy(() => elementSchema) - .array() - .optional(), - definitionCanonical: canonicalSchema.array().optional(), - _definitionCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - status: z.enum([ - 'aborted', - 'billable', - 'billed', - 'entered-in-error', - 'not-billable', - 'planned', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - subject: z.lazy(() => referenceSchema), - context: z.lazy(() => referenceSchema).optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - occurrenceTiming: z.lazy(() => timingSchema).optional(), - performer: chargeItemPerformerSchema.array().optional(), - performingOrganization: z.lazy(() => referenceSchema).optional(), - requestingOrganization: z.lazy(() => referenceSchema).optional(), - costCenter: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - bodysite: z - .lazy(() => codeableConceptSchema) + type: stringSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueString', '_valueString'], + ['valueBase64Binary', '_valueBase64Binary'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const auditEventSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('AuditEvent'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codingSchema), + subtype: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + action: z.enum(['C', 'D', 'E', 'R', 'U']).optional(), + _action: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + recorded: instantSchema.optional(), + _recorded: z.lazy(() => elementSchema).optional(), + outcome: z.enum(['0', '12', '4', '8']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + outcomeDesc: stringSchema.optional(), + _outcomeDesc: z.lazy(() => elementSchema).optional(), + purposeOfEvent: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + agent: auditEventAgentSchema.array().min(1), + source: auditEventSourceSchema, + entity: auditEventEntitySchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'recorded', shadow: '_recorded' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const basicSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Basic'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) .array() + .min(1) .optional(), - factorOverride: decimalSchema.optional(), - _factorOverride: z.lazy(() => elementSchema).optional(), - priceOverride: z.lazy(() => moneySchema).optional(), - overrideReason: stringSchema.optional(), - _overrideReason: z.lazy(() => elementSchema).optional(), - enterer: z.lazy(() => referenceSchema).optional(), - enteredDate: dateTimeSchema.optional(), - _enteredDate: z.lazy(() => elementSchema).optional(), - reason: z - .lazy(() => codeableConceptSchema) + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - service: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - productReference: z.lazy(() => referenceSchema).optional(), - productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - account: z - .lazy(() => referenceSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - note: z - .lazy(() => annotationSchema) + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema).optional(), + created: dateSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const binarySchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Binary'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + contentType: codeSchema.optional(), + _contentType: z.lazy(() => elementSchema).optional(), + securityContext: z.lazy(() => referenceSchema).optional(), + data: base64BinarySchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'contentType', shadow: '_contentType' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const biologicallyDerivedProductCollectionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - supportingInformation: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), + collector: z.lazy(() => referenceSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + collectedDateTime: dateTimeSchema.optional(), + _collectedDateTime: z.lazy(() => elementSchema).optional(), + collectedPeriod: z.lazy(() => periodSchema).optional(), }) .refine( (value) => [ { - names: [ - 'occurrenceDateTime', - 'occurrencePeriod', - 'occurrenceTiming', + alternatives: [ + ['collectedDateTime', '_collectedDateTime'], + ['collectedPeriod'], ], required: false, }, - { - names: ['productReference', 'productCodeableConcept'], - required: false, - }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -3506,84 +2791,147 @@ export const chargeItemSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const chargeItemDefinitionApplicabilitySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const biologicallyDerivedProductProcessingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - language: stringSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - expression: stringSchema.optional(), - _expression: z.lazy(() => elementSchema).optional(), - }), - ) + procedure: z.lazy(() => codeableConceptSchema).optional(), + additive: z.lazy(() => referenceSchema).optional(), + timeDateTime: dateTimeSchema.optional(), + _timeDateTime: z.lazy(() => elementSchema).optional(), + timePeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['timeDateTime', '_timeDateTime'], ['timePeriod']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const chargeItemDefinitionPropertyGroupSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const biologicallyDerivedProductManipulationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - applicability: z - .lazy(() => chargeItemDefinitionApplicabilitySchema) - .array() - .optional(), - priceComponent: chargeItemDefinitionPropertyGroupPriceComponentSchema - .array() - .optional(), - }), - ) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + timeDateTime: dateTimeSchema.optional(), + _timeDateTime: z.lazy(() => elementSchema).optional(), + timePeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['timeDateTime', '_timeDateTime'], ['timePeriod']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const chargeItemDefinitionPropertyGroupPriceComponentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const biologicallyDerivedProductStorageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum([ - 'base', - 'deduction', - 'discount', - 'informational', - 'surcharge', - 'tax', - ]), - _type: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - amount: z.lazy(() => moneySchema).optional(), - }), - ) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + temperature: decimalSchema.optional(), + _temperature: z.lazy(() => elementSchema).optional(), + scale: z.enum(['celsius', 'farenheit', 'kelvin']).optional(), + _scale: z.lazy(() => elementSchema).optional(), + duration: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const chargeItemDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('ChargeItemDefinition'), +export const biologicallyDerivedProductSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('BiologicallyDerivedProduct'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -3595,137 +2943,166 @@ export const chargeItemDefinitionSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema, - _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - derivedFromUri: uriSchema.array().optional(), - _derivedFromUri: z - .lazy(() => elementSchema) + productCategory: z + .enum(['biologicalAgent', 'cells', 'fluid', 'organ', 'tissue']) + .optional(), + _productCategory: z.lazy(() => elementSchema).optional(), + productCode: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum(['available', 'unavailable']).optional(), + _status: z.lazy(() => elementSchema).optional(), + request: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - partOf: canonicalSchema.array().optional(), - _partOf: z - .lazy(() => elementSchema) + quantity: integerSchema.optional(), + _quantity: z.lazy(() => elementSchema).optional(), + parent: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - replaces: canonicalSchema.array().optional(), - _replaces: z - .lazy(() => elementSchema) + collection: biologicallyDerivedProductCollectionSchema.optional(), + processing: biologicallyDerivedProductProcessingSchema .array() + .min(1) .optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) + manipulation: biologicallyDerivedProductManipulationSchema.optional(), + storage: biologicallyDerivedProductStorageSchema .array() + .min(1) .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - instance: z - .lazy(() => referenceSchema) - .array() - .optional(), - applicability: chargeItemDefinitionApplicabilitySchema.array().optional(), - propertyGroup: chargeItemDefinitionPropertyGroupSchema.array().optional(), - }), - ) - -export const citationSummarySchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - style: z.lazy(() => codeableConceptSchema).optional(), - text: markdownSchema, - _text: z.lazy(() => elementSchema).optional(), - }), -) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const citationClassificationSchema: z.ZodType = +export const bodyStructureSchema: z.ZodType> = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - classifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - }), - ) + z + .strictObject({ + resourceType: z.literal('BodyStructure'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + morphology: z.lazy(() => codeableConceptSchema).optional(), + location: z.lazy(() => codeableConceptSchema).optional(), + locationQualifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + image: z + .lazy(() => attachmentSchema) + .array() + .min(1) + .optional(), + patient: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const citationStatusDateSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - activity: z.lazy(() => codeableConceptSchema), - actual: booleanSchema.optional(), - _actual: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema), - }), - ) +export const bundleLinkSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + relation: stringSchema.optional(), + _relation: z.lazy(() => elementSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'relation', shadow: '_relation' }, + { name: 'url', shadow: '_url' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const citationRelatesToSchema: z.ZodType = z.lazy( +export const bundleEntrySchema: z.ZodType> = z.lazy( () => z .strictObject({ @@ -3733,1164 +3110,1379 @@ export const citationRelatesToSchema: z.ZodType = z.lazy( extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - relationshipType: z.lazy(() => codeableConceptSchema), - targetClassifier: z - .lazy(() => codeableConceptSchema) + link: z + .lazy(() => bundleLinkSchema) .array() + .min(1) .optional(), - targetUri: uriSchema.optional(), - _targetUri: z.lazy(() => elementSchema).optional(), - targetIdentifier: z.lazy(() => identifierSchema).optional(), - targetReference: z.lazy(() => referenceSchema).optional(), - targetAttachment: z.lazy(() => attachmentSchema).optional(), + fullUrl: uriSchema.optional(), + _fullUrl: z.lazy(() => elementSchema).optional(), + resource: z.lazy(() => resourceSchema).optional(), + search: bundleEntrySearchSchema.optional(), + request: bundleEntryRequestSchema.optional(), + response: bundleEntryResponseSchema.optional(), }) .refine( - (value) => - [ - { - names: [ - 'targetUri', - 'targetIdentifier', - 'targetReference', - 'targetAttachment', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), -) +) as unknown as z.ZodType> -export const citationCitedArtifactSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const bundleEntrySearchSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - relatedIdentifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - dateAccessed: dateTimeSchema.optional(), - _dateAccessed: z.lazy(() => elementSchema).optional(), - version: citationCitedArtifactVersionSchema.optional(), - currentState: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - statusDate: citationCitedArtifactStatusDateSchema.array().optional(), - title: citationCitedArtifactTitleSchema.array().optional(), - abstract: citationCitedArtifactAbstractSchema.array().optional(), - part: citationCitedArtifactPartSchema.optional(), - relatesTo: citationCitedArtifactRelatesToSchema.array().optional(), - publicationForm: citationCitedArtifactPublicationFormSchema - .array() - .optional(), - webLocation: citationCitedArtifactWebLocationSchema.array().optional(), - classification: citationCitedArtifactClassificationSchema - .array() - .optional(), - contributorship: citationCitedArtifactContributorshipSchema.optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }), - ) + mode: z.enum(['include', 'match', 'outcome']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + score: decimalSchema.optional(), + _score: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const citationCitedArtifactVersionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const bundleEntryRequestSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - baseCitation: z.lazy(() => referenceSchema).optional(), - }), - ) + method: z + .enum(['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT']) + .optional(), + _method: z.lazy(() => elementSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + ifNoneMatch: stringSchema.optional(), + _ifNoneMatch: z.lazy(() => elementSchema).optional(), + ifModifiedSince: instantSchema.optional(), + _ifModifiedSince: z.lazy(() => elementSchema).optional(), + ifMatch: stringSchema.optional(), + _ifMatch: z.lazy(() => elementSchema).optional(), + ifNoneExist: stringSchema.optional(), + _ifNoneExist: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'method', shadow: '_method' }, + { name: 'url', shadow: '_url' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const citationCitedArtifactStatusDateSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const bundleEntryResponseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - activity: z.lazy(() => codeableConceptSchema), - actual: booleanSchema.optional(), - _actual: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema), - }), - ) + status: stringSchema.optional(), + _status: z.lazy(() => elementSchema).optional(), + location: uriSchema.optional(), + _location: z.lazy(() => elementSchema).optional(), + etag: stringSchema.optional(), + _etag: z.lazy(() => elementSchema).optional(), + lastModified: instantSchema.optional(), + _lastModified: z.lazy(() => elementSchema).optional(), + outcome: z.lazy(() => resourceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const citationCitedArtifactTitleSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const bundleSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Bundle'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + type: z + .enum([ + 'batch', + 'batch-response', + 'collection', + 'document', + 'history', + 'message', + 'searchset', + 'transaction', + 'transaction-response', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + timestamp: instantSchema.optional(), + _timestamp: z.lazy(() => elementSchema).optional(), + total: unsignedIntSchema.optional(), + _total: z.lazy(() => elementSchema).optional(), + link: bundleLinkSchema.array().min(1).optional(), + entry: bundleEntrySchema.array().min(1).optional(), + signature: z.lazy(() => signatureSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const capabilityStatementSoftwareSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - language: z.lazy(() => codeableConceptSchema).optional(), - text: markdownSchema, - _text: z.lazy(() => elementSchema).optional(), - }), - ) + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + releaseDate: dateTimeSchema.optional(), + _releaseDate: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const citationCitedArtifactAbstractSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementImplementationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - language: z.lazy(() => codeableConceptSchema).optional(), - text: markdownSchema, - _text: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - }), - ) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + url: urlSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + custodian: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'description', shadow: '_description' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const citationCitedArtifactPartSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementRestSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - value: stringSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - baseCitation: z.lazy(() => referenceSchema).optional(), - }), - ) - -export const citationCitedArtifactRelatesToSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - relationshipType: z.lazy(() => codeableConceptSchema), - targetClassifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - targetUri: uriSchema.optional(), - _targetUri: z.lazy(() => elementSchema).optional(), - targetIdentifier: z.lazy(() => identifierSchema).optional(), - targetReference: z.lazy(() => referenceSchema).optional(), - targetAttachment: z.lazy(() => attachmentSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'targetUri', - 'targetIdentifier', - 'targetReference', - 'targetAttachment', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const citationCitedArtifactPublicationFormSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + mode: z.enum(['client', 'server']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + security: capabilityStatementRestSecuritySchema.optional(), + resource: capabilityStatementRestResourceSchema.array().min(1).optional(), + interaction: capabilityStatementRestInteractionSchema .array() + .min(1) .optional(), - publishedIn: - citationCitedArtifactPublicationFormPublishedInSchema.optional(), - periodicRelease: - citationCitedArtifactPublicationFormPeriodicReleaseSchema.optional(), - articleDate: dateTimeSchema.optional(), - _articleDate: z.lazy(() => elementSchema).optional(), - lastRevisionDate: dateTimeSchema.optional(), - _lastRevisionDate: z.lazy(() => elementSchema).optional(), - language: z - .lazy(() => codeableConceptSchema) + searchParam: z + .lazy(() => capabilityStatementRestResourceSearchParamSchema) .array() + .min(1) .optional(), - accessionNumber: stringSchema.optional(), - _accessionNumber: z.lazy(() => elementSchema).optional(), - pageString: stringSchema.optional(), - _pageString: z.lazy(() => elementSchema).optional(), - firstPage: stringSchema.optional(), - _firstPage: z.lazy(() => elementSchema).optional(), - lastPage: stringSchema.optional(), - _lastPage: z.lazy(() => elementSchema).optional(), - pageCount: stringSchema.optional(), - _pageCount: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - }), - ) - -export const citationCitedArtifactWebLocationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + operation: z + .lazy(() => capabilityStatementRestResourceOperationSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + compartment: canonicalSchema.nullable().array().min(1).optional(), + _compartment: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'mode', shadow: '_mode' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'compartment', shadow: '_compartment', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const citationCitedArtifactClassificationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementMessagingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - classifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - whoClassified: - citationCitedArtifactClassificationWhoClassifiedSchema.optional(), - }), - ) - -export const citationCitedArtifactContributorshipSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + endpoint: capabilityStatementMessagingEndpointSchema .array() + .min(1) .optional(), - complete: booleanSchema.optional(), - _complete: z.lazy(() => elementSchema).optional(), - entry: citationCitedArtifactContributorshipEntrySchema.array().optional(), - summary: citationCitedArtifactContributorshipSummarySchema + reliableCache: unsignedIntSchema.optional(), + _reliableCache: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + supportedMessage: capabilityStatementMessagingSupportedMessageSchema .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const citationCitedArtifactPublicationFormPublishedInSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementDocumentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - publisher: z.lazy(() => referenceSchema).optional(), - publisherLocation: stringSchema.optional(), - _publisherLocation: z.lazy(() => elementSchema).optional(), - }), - ) + mode: z.enum(['consumer', 'producer']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'mode', shadow: '_mode' }, + { name: 'profile', shadow: '_profile' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const citationCitedArtifactPublicationFormPeriodicReleaseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementRestSecuritySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - citedMedium: z.lazy(() => codeableConceptSchema).optional(), - volume: stringSchema.optional(), - _volume: z.lazy(() => elementSchema).optional(), - issue: stringSchema.optional(), - _issue: z.lazy(() => elementSchema).optional(), - dateOfPublication: - citationCitedArtifactPublicationFormPeriodicReleaseDateOfPublicationSchema.optional(), - }), - ) - -export const citationCitedArtifactClassificationWhoClassifiedSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + cors: booleanSchema.optional(), + _cors: z.lazy(() => elementSchema).optional(), + service: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - person: z.lazy(() => referenceSchema).optional(), - organization: z.lazy(() => referenceSchema).optional(), - publisher: z.lazy(() => referenceSchema).optional(), - classifierCopyright: stringSchema.optional(), - _classifierCopyright: z.lazy(() => elementSchema).optional(), - freeToShare: booleanSchema.optional(), - _freeToShare: z.lazy(() => elementSchema).optional(), - }), - ) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const citationCitedArtifactContributorshipEntrySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementRestResourceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: z.lazy(() => humanNameSchema).optional(), - initials: stringSchema.optional(), - _initials: z.lazy(() => elementSchema).optional(), - collectiveName: stringSchema.optional(), - _collectiveName: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - affiliationInfo: - citationCitedArtifactContributorshipEntryAffiliationInfoSchema - .array() - .optional(), - address: z - .lazy(() => addressSchema) - .array() - .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - contributionType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - role: z.lazy(() => codeableConceptSchema).optional(), - contributionInstance: - citationCitedArtifactContributorshipEntryContributionInstanceSchema - .array() - .optional(), - correspondingContact: booleanSchema.optional(), - _correspondingContact: z.lazy(() => elementSchema).optional(), - listOrder: positiveIntSchema.optional(), - _listOrder: z.lazy(() => elementSchema).optional(), - }), - ) + type: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + supportedProfile: canonicalSchema.nullable().array().min(1).optional(), + _supportedProfile: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + interaction: capabilityStatementRestResourceInteractionSchema + .array() + .min(1) + .optional(), + versioning: z + .enum(['no-version', 'versioned', 'versioned-update']) + .optional(), + _versioning: z.lazy(() => elementSchema).optional(), + readHistory: booleanSchema.optional(), + _readHistory: z.lazy(() => elementSchema).optional(), + updateCreate: booleanSchema.optional(), + _updateCreate: z.lazy(() => elementSchema).optional(), + conditionalCreate: booleanSchema.optional(), + _conditionalCreate: z.lazy(() => elementSchema).optional(), + conditionalRead: z + .enum(['full-support', 'modified-since', 'not-match', 'not-supported']) + .optional(), + _conditionalRead: z.lazy(() => elementSchema).optional(), + conditionalUpdate: booleanSchema.optional(), + _conditionalUpdate: z.lazy(() => elementSchema).optional(), + conditionalDelete: z + .enum(['multiple', 'not-supported', 'single']) + .optional(), + _conditionalDelete: z.lazy(() => elementSchema).optional(), + referencePolicy: z + .enum(['enforced', 'literal', 'local', 'logical', 'resolves']) + .nullable() + .array() + .min(1) + .optional(), + _referencePolicy: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + searchInclude: stringSchema.nullable().array().min(1).optional(), + _searchInclude: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + searchRevInclude: stringSchema.nullable().array().min(1).optional(), + _searchRevInclude: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + searchParam: capabilityStatementRestResourceSearchParamSchema + .array() + .min(1) + .optional(), + operation: capabilityStatementRestResourceOperationSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'supportedProfile', + shadow: '_supportedProfile', + required: false, + }, + { + name: 'referencePolicy', + shadow: '_referencePolicy', + required: false, + }, + { name: 'searchInclude', shadow: '_searchInclude', required: false }, + { + name: 'searchRevInclude', + shadow: '_searchRevInclude', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const citationCitedArtifactContributorshipSummarySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementRestInteractionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - style: z.lazy(() => codeableConceptSchema).optional(), - source: z.lazy(() => codeableConceptSchema).optional(), - value: markdownSchema, - _value: z.lazy(() => elementSchema).optional(), - }), - ) + code: z + .enum(['batch', 'history-system', 'search-system', 'transaction']) + .optional(), + _code: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const citationCitedArtifactPublicationFormPeriodicReleaseDateOfPublicationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementRestResourceSearchParamSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - date: dateSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - year: stringSchema.optional(), - _year: z.lazy(() => elementSchema).optional(), - month: stringSchema.optional(), - _month: z.lazy(() => elementSchema).optional(), - day: stringSchema.optional(), - _day: z.lazy(() => elementSchema).optional(), - season: stringSchema.optional(), - _season: z.lazy(() => elementSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - }), - ) - -export const citationCitedArtifactContributorshipEntryAffiliationInfoSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - affiliation: stringSchema.optional(), - _affiliation: z.lazy(() => elementSchema).optional(), - role: stringSchema.optional(), - _role: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + definition: canonicalSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'composite', + 'date', + 'number', + 'quantity', + 'reference', + 'special', + 'string', + 'token', + 'uri', + ]) .optional(), - }), - ) + _type: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const citationCitedArtifactContributorshipEntryContributionInstanceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const capabilityStatementRestResourceOperationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - time: dateTimeSchema.optional(), - _time: z.lazy(() => elementSchema).optional(), - }), - ) - -export const citationSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Citation'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - author: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - editor: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - reviewer: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - endorser: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - summary: citationSummarySchema.array().optional(), - classification: citationClassificationSchema.array().optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - currentState: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - statusDate: citationStatusDateSchema.array().optional(), - relatesTo: citationRelatesToSchema.array().optional(), - citedArtifact: citationCitedArtifactSchema.optional(), - }), -) - -export const claimRelatedSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - claim: z.lazy(() => referenceSchema).optional(), - relationship: z.lazy(() => codeableConceptSchema).optional(), - reference: z.lazy(() => identifierSchema).optional(), - }), -) - -export const claimPayeeSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - party: z.lazy(() => referenceSchema).optional(), - }), -) - -export const claimCareTeamSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - provider: z.lazy(() => referenceSchema), - responsible: booleanSchema.optional(), - _responsible: z.lazy(() => elementSchema).optional(), - role: z.lazy(() => codeableConceptSchema).optional(), - qualification: z.lazy(() => codeableConceptSchema).optional(), - }), -) - -export const claimSupportingInfoSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - category: z.lazy(() => codeableConceptSchema), - code: z.lazy(() => codeableConceptSchema).optional(), - timingDate: dateSchema.optional(), - _timingDate: z.lazy(() => elementSchema).optional(), - timingPeriod: z.lazy(() => periodSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - reason: z.lazy(() => codeableConceptSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['timingDate', 'timingPeriod'], required: false }, - { - names: [ - 'valueBoolean', - 'valueString', - 'valueQuantity', - 'valueAttachment', - 'valueReference', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + definition: canonicalSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'definition', shadow: '_definition' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const claimDiagnosisSchema: z.ZodType = z.lazy(() => +export const capabilityStatementMessagingEndpointSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - diagnosisCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - diagnosisReference: z.lazy(() => referenceSchema).optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - onAdmission: z.lazy(() => codeableConceptSchema).optional(), - packageCode: z.lazy(() => codeableConceptSchema).optional(), + protocol: z.lazy(() => codingSchema), + address: urlSchema.optional(), + _address: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'address', shadow: '_address' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['diagnosisCodeableConcept', 'diagnosisReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const claimProcedureSchema: z.ZodType = z.lazy(() => +export const capabilityStatementMessagingSupportedMessageSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - procedureCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - procedureReference: z.lazy(() => referenceSchema).optional(), - udi: z - .lazy(() => referenceSchema) - .array() - .optional(), + mode: z.enum(['receiver', 'sender']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + definition: canonicalSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'mode', shadow: '_mode' }, + { name: 'definition', shadow: '_definition' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['procedureCodeableConcept', 'procedureReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const claimInsuranceSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - focal: booleanSchema, - _focal: z.lazy(() => elementSchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), - coverage: z.lazy(() => referenceSchema), - businessArrangement: stringSchema.optional(), - _businessArrangement: z.lazy(() => elementSchema).optional(), - preAuthRef: stringSchema.array().optional(), - _preAuthRef: z - .lazy(() => elementSchema) - .array() - .optional(), - claimResponse: z.lazy(() => referenceSchema).optional(), - }), -) +) as unknown as z.ZodType< + FhirJson +> -export const claimAccidentSchema: z.ZodType = z.lazy(() => +export const capabilityStatementRestResourceInteractionSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - date: dateSchema, - _date: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - locationAddress: z.lazy(() => addressSchema).optional(), - locationReference: z.lazy(() => referenceSchema).optional(), + code: z + .enum([ + 'create', + 'delete', + 'history-instance', + 'history-type', + 'patch', + 'read', + 'search-type', + 'update', + 'vread', + ]) + .optional(), + _code: z.lazy(() => elementSchema).optional(), + documentation: markdownSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { names: ['locationAddress', 'locationReference'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType< + FhirJson +> -export const claimItemSchema: z.ZodType = z.lazy(() => +export const capabilityStatementSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - id: stringSchema.optional(), + resourceType: z.literal('CapabilityStatement'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - careTeamSequence: positiveIntSchema.array().optional(), - _careTeamSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - diagnosisSequence: positiveIntSchema.array().optional(), - _diagnosisSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - procedureSequence: positiveIntSchema.array().optional(), - _procedureSequence: z - .lazy(() => elementSchema) + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - informationSequence: positiveIntSchema.array().optional(), - _informationSequence: z - .lazy(() => elementSchema) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - revenue: z.lazy(() => codeableConceptSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z + jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - programCode: z - .lazy(() => codeableConceptSchema) + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + kind: z.enum(['capability', 'instance', 'requirements']).optional(), + _kind: z.lazy(() => elementSchema).optional(), + instantiates: canonicalSchema.nullable().array().min(1).optional(), + _instantiates: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - servicedDate: dateSchema.optional(), - _servicedDate: z.lazy(() => elementSchema).optional(), - servicedPeriod: z.lazy(() => periodSchema).optional(), - locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - locationAddress: z.lazy(() => addressSchema).optional(), - locationReference: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - udi: z - .lazy(() => referenceSchema) + imports: canonicalSchema.nullable().array().min(1).optional(), + _imports: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), - subSite: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - encounter: z - .lazy(() => referenceSchema) - .array() - .optional(), - detail: claimItemDetailSchema.array().optional(), - }) - .refine( - (value) => - [ - { names: ['servicedDate', 'servicedPeriod'], required: false }, - { - names: [ - 'locationCodeableConcept', - 'locationAddress', - 'locationReference', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const claimItemDetailSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - revenue: z.lazy(() => codeableConceptSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - programCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - udi: z - .lazy(() => referenceSchema) - .array() - .optional(), - subDetail: claimItemDetailSubDetailSchema.array().optional(), - }), -) - -export const claimItemDetailSubDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - revenue: z.lazy(() => codeableConceptSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - programCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - udi: z - .lazy(() => referenceSchema) - .array() + software: capabilityStatementSoftwareSchema.optional(), + implementation: capabilityStatementImplementationSchema.optional(), + fhirVersion: z + .enum([ + '0.0.80', + '0.0.81', + '0.0.82', + '0.01', + '0.05', + '0.06', + '0.11', + '0.4.0', + '0.5.0', + '1.0.0', + '1.0.1', + '1.0.2', + '1.1.0', + '1.4.0', + '1.6.0', + '1.8.0', + '3.0.0', + '3.0.1', + '3.0.2', + '3.3.0', + '3.5.0', + '4.0.0', + '4.0.1', + '4.1.0', + '4.3.0', + '4.3.0-cibuild', + '4.3.0-snapshot1', + ]) .optional(), - }), - ) - -export const claimSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Claim'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - subType: z.lazy(() => codeableConceptSchema).optional(), - use: z.enum(['claim', 'preauthorization', 'predetermination']), - _use: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema), - billablePeriod: z.lazy(() => periodSchema).optional(), - created: dateTimeSchema, - _created: z.lazy(() => elementSchema).optional(), - enterer: z.lazy(() => referenceSchema).optional(), - insurer: z.lazy(() => referenceSchema).optional(), - provider: z.lazy(() => referenceSchema), - priority: z.lazy(() => codeableConceptSchema), - fundsReserve: z.lazy(() => codeableConceptSchema).optional(), - related: claimRelatedSchema.array().optional(), - prescription: z.lazy(() => referenceSchema).optional(), - originalPrescription: z.lazy(() => referenceSchema).optional(), - payee: claimPayeeSchema.optional(), - referral: z.lazy(() => referenceSchema).optional(), - facility: z.lazy(() => referenceSchema).optional(), - careTeam: claimCareTeamSchema.array().optional(), - supportingInfo: claimSupportingInfoSchema.array().optional(), - diagnosis: claimDiagnosisSchema.array().optional(), - procedure: claimProcedureSchema.array().optional(), - insurance: claimInsuranceSchema.array().min(1), - accident: claimAccidentSchema.optional(), - item: claimItemSchema.array().optional(), - total: z.lazy(() => moneySchema).optional(), - }), -) - -export const claimResponseItemSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + _fhirVersion: z.lazy(() => elementSchema).optional(), + format: codeSchema.nullable().array().min(1).optional(), + _format: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + patchFormat: codeSchema.nullable().array().min(1).optional(), + _patchFormat: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - itemSequence: positiveIntSchema, - _itemSequence: z.lazy(() => elementSchema).optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z + implementationGuide: canonicalSchema.nullable().array().min(1).optional(), + _implementationGuide: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - adjudication: claimResponseItemAdjudicationSchema.array().min(1), - detail: claimResponseItemDetailSchema.array().optional(), - }), -) + rest: capabilityStatementRestSchema.array().min(1).optional(), + messaging: capabilityStatementMessagingSchema.array().min(1).optional(), + document: capabilityStatementDocumentSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'date', shadow: '_date' }, + { name: 'kind', shadow: '_kind' }, + { name: 'fhirVersion', shadow: '_fhirVersion' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'instantiates', shadow: '_instantiates', required: false }, + { name: 'imports', shadow: '_imports', required: false }, + { name: 'format', shadow: '_format', required: true }, + { name: 'patchFormat', shadow: '_patchFormat', required: false }, + { + name: 'implementationGuide', + shadow: '_implementationGuide', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const claimResponseAddItemSchema: z.ZodType = +export const carePlanActivitySchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -4898,451 +4490,608 @@ export const claimResponseAddItemSchema: z.ZodType = extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - itemSequence: positiveIntSchema.array().optional(), - _itemSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - detailSequence: positiveIntSchema.array().optional(), - _detailSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - subdetailSequence: positiveIntSchema.array().optional(), - _subdetailSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - provider: z - .lazy(() => referenceSchema) - .array() - .optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - programCode: z + outcomeCodeableConcept: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - servicedDate: dateSchema.optional(), - _servicedDate: z.lazy(() => elementSchema).optional(), - servicedPeriod: z.lazy(() => periodSchema).optional(), - locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - locationAddress: z.lazy(() => addressSchema).optional(), - locationReference: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), - subSite: z - .lazy(() => codeableConceptSchema) + outcomeReference: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) + progress: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - adjudication: z - .lazy(() => claimResponseItemAdjudicationSchema) - .array() - .min(1), - detail: claimResponseAddItemDetailSchema.array().optional(), + reference: z.lazy(() => referenceSchema).optional(), + detail: carePlanActivityDetailSchema.optional(), }) .refine( - (value) => - [ - { names: ['servicedDate', 'servicedPeriod'], required: false }, - { - names: [ - 'locationCodeableConcept', - 'locationAddress', - 'locationReference', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const claimResponseItemAdjudicationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const carePlanActivityDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - category: z.lazy(() => codeableConceptSchema), - reason: z.lazy(() => codeableConceptSchema).optional(), - amount: z.lazy(() => moneySchema).optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - }), - ) - -export const claimResponseTotalSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() + kind: z + .enum([ + 'Appointment', + 'CommunicationRequest', + 'DeviceRequest', + 'MedicationRequest', + 'NutritionOrder', + 'ServiceRequest', + 'Task', + 'VisionPrescription', + ]) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + _kind: z.lazy(() => elementSchema).optional(), + instantiatesCanonical: canonicalSchema + .nullable() .array() + .min(1) .optional(), - category: z.lazy(() => codeableConceptSchema), - amount: z.lazy(() => moneySchema), - }), - ) - -export const claimResponsePaymentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + _instantiatesCanonical: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - adjustment: z.lazy(() => moneySchema).optional(), - adjustmentReason: z.lazy(() => codeableConceptSchema).optional(), - date: dateSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - amount: z.lazy(() => moneySchema), - identifier: z.lazy(() => identifierSchema).optional(), - }), - ) - -export const claimResponseProcessNoteSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + code: z.lazy(() => codeableConceptSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + reasonReference: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - number: positiveIntSchema.optional(), - _number: z.lazy(() => elementSchema).optional(), - type: z.enum(['display', 'print', 'printoper']).optional(), - _type: z.lazy(() => elementSchema).optional(), - text: stringSchema, - _text: z.lazy(() => elementSchema).optional(), - language: z.lazy(() => codeableConceptSchema).optional(), - }), - ) - -export const claimResponseInsuranceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + goal: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + status: z + .enum([ + 'cancelled', + 'completed', + 'entered-in-error', + 'in-progress', + 'not-started', + 'on-hold', + 'scheduled', + 'stopped', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + scheduledTiming: z.lazy(() => timingSchema).optional(), + scheduledPeriod: z.lazy(() => periodSchema).optional(), + scheduledString: stringSchema.optional(), + _scheduledString: z.lazy(() => elementSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + performer: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - focal: booleanSchema, - _focal: z.lazy(() => elementSchema).optional(), - coverage: z.lazy(() => referenceSchema), - businessArrangement: stringSchema.optional(), - _businessArrangement: z.lazy(() => elementSchema).optional(), - claimResponse: z.lazy(() => referenceSchema).optional(), - }), - ) + productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + productReference: z.lazy(() => referenceSchema).optional(), + dailyAmount: z.lazy(() => quantitySchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['scheduledTiming'], + ['scheduledPeriod'], + ['scheduledString', '_scheduledString'], + ], + required: false, + }, + { + alternatives: [['productCodeableConcept'], ['productReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const claimResponseErrorSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const carePlanSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CarePlan'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - itemSequence: positiveIntSchema.optional(), - _itemSequence: z.lazy(() => elementSchema).optional(), - detailSequence: positiveIntSchema.optional(), - _detailSequence: z.lazy(() => elementSchema).optional(), - subDetailSequence: positiveIntSchema.optional(), - _subDetailSequence: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema), - }), - ) - -export const claimResponseItemDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + instantiatesCanonical: canonicalSchema + .nullable() .array() + .min(1) .optional(), - detailSequence: positiveIntSchema, - _detailSequence: z.lazy(() => elementSchema).optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z + _instantiatesCanonical: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - adjudication: z - .lazy(() => claimResponseItemAdjudicationSchema) + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() .array() - .min(1), - subDetail: claimResponseItemDetailSubDetailSchema.array().optional(), - }), - ) - -export const claimResponseAddItemDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + .min(1) + .optional(), + basedOn: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + replaces: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + intent: z.enum(['option', 'order', 'plan', 'proposal']).optional(), + _intent: z.lazy(() => elementSchema).optional(), + category: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + contributor: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - adjudication: z - .lazy(() => claimResponseItemAdjudicationSchema) + careTeam: z + .lazy(() => referenceSchema) .array() - .min(1), - subDetail: claimResponseAddItemDetailSubDetailSchema.array().optional(), - }), - ) - -export const claimResponseItemDetailSubDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + .min(1) + .optional(), + addresses: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + supportingInfo: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - subDetailSequence: positiveIntSchema, - _subDetailSequence: z.lazy(() => elementSchema).optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) + goal: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - adjudication: z - .lazy(() => claimResponseItemAdjudicationSchema) + activity: carePlanActivitySchema.array().min(1).optional(), + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'intent', shadow: '_intent' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const claimResponseAddItemDetailSubDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const careTeamParticipantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z + role: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) + member: z.lazy(() => referenceSchema).optional(), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const careTeamSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CareTeam'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) .array() + .min(1) .optional(), - adjudication: z - .lazy(() => claimResponseItemAdjudicationSchema) - .array() - .min(1), - }), - ) - -export const claimResponseSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('ClaimResponse'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - subType: z.lazy(() => codeableConceptSchema).optional(), - use: z.enum(['claim', 'preauthorization', 'predetermination']), - _use: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema), - created: dateTimeSchema, - _created: z.lazy(() => elementSchema).optional(), - insurer: z.lazy(() => referenceSchema), - requestor: z.lazy(() => referenceSchema).optional(), - request: z.lazy(() => referenceSchema).optional(), - outcome: z.enum(['complete', 'error', 'partial', 'queued']), - _outcome: z.lazy(() => elementSchema).optional(), - disposition: stringSchema.optional(), - _disposition: z.lazy(() => elementSchema).optional(), - preAuthRef: stringSchema.optional(), - _preAuthRef: z.lazy(() => elementSchema).optional(), - preAuthPeriod: z.lazy(() => periodSchema).optional(), - payeeType: z.lazy(() => codeableConceptSchema).optional(), - item: claimResponseItemSchema.array().optional(), - addItem: claimResponseAddItemSchema.array().optional(), - adjudication: z - .lazy(() => claimResponseItemAdjudicationSchema) - .array() - .optional(), - total: claimResponseTotalSchema.array().optional(), - payment: claimResponsePaymentSchema.optional(), - fundsReserve: z.lazy(() => codeableConceptSchema).optional(), - formCode: z.lazy(() => codeableConceptSchema).optional(), - form: z.lazy(() => attachmentSchema).optional(), - processNote: claimResponseProcessNoteSchema.array().optional(), - communicationRequest: z - .lazy(() => referenceSchema) - .array() - .optional(), - insurance: claimResponseInsuranceSchema.array().optional(), - error: claimResponseErrorSchema.array().optional(), - }), -) - -export const clinicalImpressionInvestigationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema), - item: z - .lazy(() => referenceSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - }), - ) - -export const clinicalImpressionFindingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + status: z + .enum([ + 'active', + 'entered-in-error', + 'inactive', + 'proposed', + 'suspended', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + participant: careTeamParticipantSchema.array().min(1).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + managingOrganization: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const catalogEntryRelatedEntrySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - itemReference: z.lazy(() => referenceSchema).optional(), - basis: stringSchema.optional(), - _basis: z.lazy(() => elementSchema).optional(), - }), - ) + relationtype: z.enum(['is-replaced-by', 'triggers']).optional(), + _relationtype: z.lazy(() => elementSchema).optional(), + item: z.lazy(() => referenceSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'relationtype', shadow: '_relationtype' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const clinicalImpressionSchema: z.ZodType = - z.lazy(() => +export const catalogEntrySchema: z.ZodType> = z.lazy( + () => z .strictObject({ - resourceType: z.literal('ClinicalImpression'), + resourceType: z.literal('CatalogEntry'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -5354,153 +5103,247 @@ export const clinicalImpressionSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - status: z.enum(['completed', 'entered-in-error', 'in-progress']), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - effectiveDateTime: dateTimeSchema.optional(), - _effectiveDateTime: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - assessor: z.lazy(() => referenceSchema).optional(), - previous: z.lazy(() => referenceSchema).optional(), - problem: z - .lazy(() => referenceSchema) - .array() - .optional(), - investigation: clinicalImpressionInvestigationSchema.array().optional(), - protocol: uriSchema.array().optional(), - _protocol: z - .lazy(() => elementSchema) + type: z.lazy(() => codeableConceptSchema).optional(), + orderable: booleanSchema.optional(), + _orderable: z.lazy(() => elementSchema).optional(), + referencedItem: z.lazy(() => referenceSchema), + additionalIdentifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - summary: stringSchema.optional(), - _summary: z.lazy(() => elementSchema).optional(), - finding: clinicalImpressionFindingSchema.array().optional(), - prognosisCodeableConcept: z + classification: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - prognosisReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - supportingInfo: z - .lazy(() => referenceSchema) + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + validityPeriod: z.lazy(() => periodSchema).optional(), + validTo: dateTimeSchema.optional(), + _validTo: z.lazy(() => elementSchema).optional(), + lastUpdated: dateTimeSchema.optional(), + _lastUpdated: z.lazy(() => elementSchema).optional(), + additionalCharacteristic: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - note: z - .lazy(() => annotationSchema) + additionalClassification: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), + relatedEntry: catalogEntryRelatedEntrySchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'orderable', shadow: '_orderable' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['effectiveDateTime', 'effectivePeriod'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> -export const clinicalUseDefinitionContraindicationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const chargeItemPerformerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - diseaseSymptomProcedure: z.lazy(() => codeableReferenceSchema).optional(), - diseaseStatus: z.lazy(() => codeableReferenceSchema).optional(), - comorbidity: z - .lazy(() => codeableReferenceSchema) - .array() - .optional(), - indication: z - .lazy(() => referenceSchema) - .array() - .optional(), - otherTherapy: clinicalUseDefinitionContraindicationOtherTherapySchema - .array() - .optional(), - }), - ) + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const clinicalUseDefinitionIndicationSchema: z.ZodType = - z.lazy(() => +export const chargeItemSchema: z.ZodType> = z.lazy( + () => z .strictObject({ - id: stringSchema.optional(), + resourceType: z.literal('ChargeItem'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - diseaseSymptomProcedure: z - .lazy(() => codeableReferenceSchema) + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) .optional(), - diseaseStatus: z.lazy(() => codeableReferenceSchema).optional(), - comorbidity: z - .lazy(() => codeableReferenceSchema) + definitionUri: uriSchema.nullable().array().min(1).optional(), + _definitionUri: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + definitionCanonical: canonicalSchema + .nullable() + .array() + .min(1) + .optional(), + _definitionCanonical: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + status: z + .enum([ + 'aborted', + 'billable', + 'billed', + 'entered-in-error', + 'not-billable', + 'planned', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema), + context: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + performer: chargeItemPerformerSchema.array().min(1).optional(), + performingOrganization: z.lazy(() => referenceSchema).optional(), + requestingOrganization: z.lazy(() => referenceSchema).optional(), + costCenter: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + bodysite: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + factorOverride: decimalSchema.optional(), + _factorOverride: z.lazy(() => elementSchema).optional(), + priceOverride: z.lazy(() => moneySchema).optional(), + overrideReason: stringSchema.optional(), + _overrideReason: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + enteredDate: dateTimeSchema.optional(), + _enteredDate: z.lazy(() => elementSchema).optional(), + reason: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + service: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - intendedEffect: z.lazy(() => codeableReferenceSchema).optional(), - durationRange: z.lazy(() => rangeSchema).optional(), - durationString: stringSchema.optional(), - _durationString: z.lazy(() => elementSchema).optional(), - undesirableEffect: z + productReference: z.lazy(() => referenceSchema).optional(), + productCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + account: z .lazy(() => referenceSchema) .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) .optional(), - otherTherapy: z - .lazy(() => clinicalUseDefinitionContraindicationOtherTherapySchema) + supportingInformation: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), }) .refine( (value) => [ - { names: ['durationRange', 'durationString'], required: false }, + { + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], + ['occurrenceTiming'], + ], + required: false, + }, + { + alternatives: [['productReference'], ['productCodeableConcept']], + required: false, + }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -5508,124 +5351,204 @@ export const clinicalUseDefinitionIndicationSchema: z.ZodType { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'definitionUri', shadow: '_definitionUri', required: false }, + { + name: 'definitionCanonical', + shadow: '_definitionCanonical', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) +) as unknown as z.ZodType> -export const clinicalUseDefinitionInteractionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const chargeItemDefinitionApplicabilitySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - interactant: clinicalUseDefinitionInteractionInteractantSchema - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - effect: z.lazy(() => codeableReferenceSchema).optional(), - incidence: z.lazy(() => codeableConceptSchema).optional(), - management: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - }), - ) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + language: stringSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const clinicalUseDefinitionUndesirableEffectSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const chargeItemDefinitionPropertyGroupSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - symptomConditionEffect: z.lazy(() => codeableReferenceSchema).optional(), - classification: z.lazy(() => codeableConceptSchema).optional(), - frequencyOfOccurrence: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + applicability: z + .lazy(() => chargeItemDefinitionApplicabilitySchema) + .array() + .min(1) + .optional(), + priceComponent: chargeItemDefinitionPropertyGroupPriceComponentSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const clinicalUseDefinitionWarningSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const chargeItemDefinitionPropertyGroupPriceComponentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - }), - ) - -export const clinicalUseDefinitionContraindicationOtherTherapySchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() + type: z + .enum([ + 'base', + 'deduction', + 'discount', + 'informational', + 'surcharge', + 'tax', + ]) .optional(), - relationshipType: z.lazy(() => codeableConceptSchema), - therapy: z.lazy(() => codeableReferenceSchema), - }), - ) - -export const clinicalUseDefinitionInteractionInteractantSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - itemReference: z.lazy(() => referenceSchema).optional(), - itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['itemReference', 'itemCodeableConcept'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + _type: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const clinicalUseDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('ClinicalUseDefinition'), +export const chargeItemDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ChargeItemDefinition'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -5637,1187 +5560,1093 @@ export const clinicalUseDefinitionSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - type: z.enum([ - 'contraindication', - 'indication', - 'interaction', - 'undesirable-effect', - 'warning', - ]), - _type: z.lazy(() => elementSchema).optional(), - category: z + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + derivedFromUri: uriSchema.nullable().array().min(1).optional(), + _derivedFromUri: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + partOf: canonicalSchema.nullable().array().min(1).optional(), + _partOf: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + replaces: canonicalSchema.nullable().array().min(1).optional(), + _replaces: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - subject: z + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + instance: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - status: z.lazy(() => codeableConceptSchema).optional(), - contraindication: clinicalUseDefinitionContraindicationSchema.optional(), - indication: clinicalUseDefinitionIndicationSchema.optional(), - interaction: clinicalUseDefinitionInteractionSchema.optional(), - population: z - .lazy(() => referenceSchema) + applicability: chargeItemDefinitionApplicabilitySchema .array() + .min(1) .optional(), - undesirableEffect: - clinicalUseDefinitionUndesirableEffectSchema.optional(), - warning: clinicalUseDefinitionWarningSchema.optional(), - }), - ) + propertyGroup: chargeItemDefinitionPropertyGroupSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'status', shadow: '_status' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'derivedFromUri', shadow: '_derivedFromUri', required: false }, + { name: 'partOf', shadow: '_partOf', required: false }, + { name: 'replaces', shadow: '_replaces', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const codeSystemFilterSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const citationSummarySchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + style: z.lazy(() => codeableConceptSchema).optional(), + text: markdownSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'text', shadow: '_text' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const citationClassificationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: codeSchema, - _code: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - operator: z - .enum([ - '=', - 'descendent-of', - 'exists', - 'generalizes', - 'in', - 'is-a', - 'is-not-a', - 'not-in', - 'regex', - ]) - .array() - .min(1), - _operator: z - .lazy(() => elementSchema) + type: z.lazy(() => codeableConceptSchema).optional(), + classifier: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - }), -) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const codeSystemPropertySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const citationStatusDateSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: codeSchema, - _code: z.lazy(() => elementSchema).optional(), - uri: uriSchema.optional(), - _uri: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - type: z.enum([ - 'Coding', - 'boolean', - 'code', - 'dateTime', - 'decimal', - 'integer', - 'string', - ]), - _type: z.lazy(() => elementSchema).optional(), - }), - ) + activity: z.lazy(() => codeableConceptSchema), + actual: booleanSchema.optional(), + _actual: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const codeSystemConceptSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const citationRelatesToSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: codeSchema, - _code: z.lazy(() => elementSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - definition: stringSchema.optional(), - _definition: z.lazy(() => elementSchema).optional(), - designation: codeSystemConceptDesignationSchema.array().optional(), - property: codeSystemConceptPropertySchema.array().optional(), - concept: z - .lazy(() => codeSystemConceptSchema) + relationshipType: z.lazy(() => codeableConceptSchema), + targetClassifier: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - }), -) + targetUri: uriSchema.optional(), + _targetUri: z.lazy(() => elementSchema).optional(), + targetIdentifier: z.lazy(() => identifierSchema).optional(), + targetReference: z.lazy(() => referenceSchema).optional(), + targetAttachment: z.lazy(() => attachmentSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['targetUri', '_targetUri'], + ['targetIdentifier'], + ['targetReference'], + ['targetAttachment'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const codeSystemConceptDesignationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const citationCitedArtifactSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - use: z.lazy(() => codingSchema).optional(), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - }), - ) - -export const codeSystemConceptPropertySchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: codeSchema, - _code: z.lazy(() => elementSchema).optional(), - valueCode: codeSchema.optional(), - _valueCode: z.lazy(() => elementSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueCode', - 'valueCoding', - 'valueString', - 'valueInteger', - 'valueBoolean', - 'valueDateTime', - 'valueDecimal', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + relatedIdentifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + dateAccessed: dateTimeSchema.optional(), + _dateAccessed: z.lazy(() => elementSchema).optional(), + version: citationCitedArtifactVersionSchema.optional(), + currentState: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + statusDate: citationCitedArtifactStatusDateSchema + .array() + .min(1) + .optional(), + title: citationCitedArtifactTitleSchema.array().min(1).optional(), + abstract: citationCitedArtifactAbstractSchema.array().min(1).optional(), + part: citationCitedArtifactPartSchema.optional(), + relatesTo: citationCitedArtifactRelatesToSchema.array().min(1).optional(), + publicationForm: citationCitedArtifactPublicationFormSchema + .array() + .min(1) + .optional(), + webLocation: citationCitedArtifactWebLocationSchema + .array() + .min(1) + .optional(), + classification: citationCitedArtifactClassificationSchema + .array() + .min(1) + .optional(), + contributorship: citationCitedArtifactContributorshipSchema.optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const codeSystemSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('CodeSystem'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - caseSensitive: booleanSchema.optional(), - _caseSensitive: z.lazy(() => elementSchema).optional(), - valueSet: canonicalSchema.optional(), - _valueSet: z.lazy(() => elementSchema).optional(), - hierarchyMeaning: z - .enum(['classified-with', 'grouped-by', 'is-a', 'part-of']) - .optional(), - _hierarchyMeaning: z.lazy(() => elementSchema).optional(), - compositional: booleanSchema.optional(), - _compositional: z.lazy(() => elementSchema).optional(), - versionNeeded: booleanSchema.optional(), - _versionNeeded: z.lazy(() => elementSchema).optional(), - content: z.enum([ - 'complete', - 'example', - 'fragment', - 'not-present', - 'supplement', - ]), - _content: z.lazy(() => elementSchema).optional(), - supplements: canonicalSchema.optional(), - _supplements: z.lazy(() => elementSchema).optional(), - count: unsignedIntSchema.optional(), - _count: z.lazy(() => elementSchema).optional(), - filter: codeSystemFilterSchema.array().optional(), - property: codeSystemPropertySchema.array().optional(), - concept: codeSystemConceptSchema.array().optional(), - }), -) - -export const codeableConceptSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: z.string().optional(), +export const citationCitedArtifactVersionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - coding: z - .lazy(() => codingSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - text: stringSchema.optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + baseCitation: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'value', shadow: '_value' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const citationCitedArtifactStatusDateSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + activity: z.lazy(() => codeableConceptSchema), + actual: booleanSchema.optional(), + _actual: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const citationCitedArtifactTitleSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + language: z.lazy(() => codeableConceptSchema).optional(), + text: markdownSchema.optional(), _text: z.lazy(() => elementSchema).optional(), - }), -) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'text', shadow: '_text' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const codeableReferenceSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: z.string().optional(), +export const citationCitedArtifactAbstractSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - concept: z.lazy(() => codeableConceptSchema).optional(), - reference: z.lazy(() => referenceSchema).optional(), - }), -) - -export const codingSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - userSelected: booleanSchema.optional(), - _userSelected: z.lazy(() => elementSchema).optional(), - }), -) - -export const communicationPayloadSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - contentString: stringSchema.optional(), - _contentString: z.lazy(() => elementSchema).optional(), - contentAttachment: z.lazy(() => attachmentSchema).optional(), - contentReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['contentString', 'contentAttachment', 'contentReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + language: z.lazy(() => codeableConceptSchema).optional(), + text: markdownSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'text', shadow: '_text' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const communicationSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Communication'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - inResponseTo: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'completed', - 'entered-in-error', - 'in-progress', - 'not-done', - 'on-hold', - 'preparation', - 'stopped', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - medium: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - subject: z.lazy(() => referenceSchema).optional(), - topic: z.lazy(() => codeableConceptSchema).optional(), - about: z - .lazy(() => referenceSchema) - .array() - .optional(), - encounter: z.lazy(() => referenceSchema).optional(), - sent: dateTimeSchema.optional(), - _sent: z.lazy(() => elementSchema).optional(), - received: dateTimeSchema.optional(), - _received: z.lazy(() => elementSchema).optional(), - recipient: z - .lazy(() => referenceSchema) - .array() - .optional(), - sender: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - payload: communicationPayloadSchema.array().optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }), -) - -export const communicationRequestPayloadSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - contentString: stringSchema.optional(), - _contentString: z.lazy(() => elementSchema).optional(), - contentAttachment: z.lazy(() => attachmentSchema).optional(), - contentReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['contentString', 'contentAttachment', 'contentReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( +export const citationCitedArtifactPartSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + baseCitation: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const citationCitedArtifactRelatesToSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + relationshipType: z.lazy(() => codeableConceptSchema), + targetClassifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + targetUri: uriSchema.optional(), + _targetUri: z.lazy(() => elementSchema).optional(), + targetIdentifier: z.lazy(() => identifierSchema).optional(), + targetReference: z.lazy(() => referenceSchema).optional(), + targetAttachment: z.lazy(() => attachmentSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['targetUri', '_targetUri'], + ['targetIdentifier'], + ['targetReference'], + ['targetAttachment'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const communicationRequestSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - resourceType: z.literal('CommunicationRequest'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - replaces: z - .lazy(() => referenceSchema) - .array() - .optional(), - groupIdentifier: z.lazy(() => identifierSchema).optional(), - status: z.enum([ - 'active', - 'completed', - 'draft', - 'entered-in-error', - 'on-hold', - 'revoked', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: z.lazy(() => elementSchema).optional(), - medium: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - subject: z.lazy(() => referenceSchema).optional(), - about: z - .lazy(() => referenceSchema) - .array() - .optional(), - encounter: z.lazy(() => referenceSchema).optional(), - payload: communicationRequestPayloadSchema.array().optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: z.lazy(() => elementSchema).optional(), - requester: z.lazy(() => referenceSchema).optional(), - recipient: z - .lazy(() => referenceSchema) - .array() - .optional(), - sender: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['occurrenceDateTime', 'occurrencePeriod'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const compartmentDefinitionResourceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.enum([ - 'Account', - 'ActivityDefinition', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RegulatedAuthorization', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]), - _code: z.lazy(() => elementSchema).optional(), - param: stringSchema.array().optional(), - _param: z - .lazy(() => elementSchema) - .array() - .optional(), - documentation: stringSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - }), - ) - -export const compartmentDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('CompartmentDefinition'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), +export const citationCitedArtifactPublicationFormSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema, - _url: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) + publishedIn: + citationCitedArtifactPublicationFormPublishedInSchema.optional(), + periodicRelease: + citationCitedArtifactPublicationFormPeriodicReleaseSchema.optional(), + articleDate: dateTimeSchema.optional(), + _articleDate: z.lazy(() => elementSchema).optional(), + lastRevisionDate: dateTimeSchema.optional(), + _lastRevisionDate: z.lazy(() => elementSchema).optional(), + language: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - code: z.enum([ - 'Device', - 'Encounter', - 'Patient', - 'Practitioner', - 'RelatedPerson', - ]), - _code: z.lazy(() => elementSchema).optional(), - search: booleanSchema, - _search: z.lazy(() => elementSchema).optional(), - resource: compartmentDefinitionResourceSchema.array().optional(), - }), - ) + accessionNumber: stringSchema.optional(), + _accessionNumber: z.lazy(() => elementSchema).optional(), + pageString: stringSchema.optional(), + _pageString: z.lazy(() => elementSchema).optional(), + firstPage: stringSchema.optional(), + _firstPage: z.lazy(() => elementSchema).optional(), + lastPage: stringSchema.optional(), + _lastPage: z.lazy(() => elementSchema).optional(), + pageCount: stringSchema.optional(), + _pageCount: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const compositionAttesterSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const citationCitedArtifactWebLocationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - mode: z.enum(['legal', 'official', 'personal', 'professional']), - _mode: z.lazy(() => elementSchema).optional(), - time: dateTimeSchema.optional(), - _time: z.lazy(() => elementSchema).optional(), - party: z.lazy(() => referenceSchema).optional(), - }), - ) - -export const compositionRelatesToSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.enum(['appends', 'replaces', 'signs', 'transforms']), - _code: z.lazy(() => elementSchema).optional(), - targetIdentifier: z.lazy(() => identifierSchema).optional(), - targetReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['targetIdentifier', 'targetReference'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + type: z.lazy(() => codeableConceptSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const compositionEventSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const citationCitedArtifactClassificationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z + type: z.lazy(() => codeableConceptSchema).optional(), + classifier: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - period: z.lazy(() => periodSchema).optional(), - detail: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) + whoClassified: + citationCitedArtifactClassificationWhoClassifiedSchema.optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const compositionSectionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const citationCitedArtifactContributorshipSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - author: z - .lazy(() => referenceSchema) + complete: booleanSchema.optional(), + _complete: z.lazy(() => elementSchema).optional(), + entry: citationCitedArtifactContributorshipEntrySchema .array() + .min(1) .optional(), - focus: z.lazy(() => referenceSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - mode: z.enum(['changes', 'snapshot', 'working']).optional(), - _mode: z.lazy(() => elementSchema).optional(), - orderedBy: z.lazy(() => codeableConceptSchema).optional(), - entry: z - .lazy(() => referenceSchema) + summary: citationCitedArtifactContributorshipSummarySchema .array() + .min(1) .optional(), - emptyReason: z.lazy(() => codeableConceptSchema).optional(), - section: z - .lazy(() => compositionSectionSchema) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const citationCitedArtifactPublicationFormPublishedInSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), - ) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + publisher: z.lazy(() => referenceSchema).optional(), + publisherLocation: stringSchema.optional(), + _publisherLocation: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const compositionSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Composition'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - status: z.enum(['amended', 'entered-in-error', 'final', 'preliminary']), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema, - _date: z.lazy(() => elementSchema).optional(), - author: z - .lazy(() => referenceSchema) - .array() - .min(1), - title: stringSchema, - _title: z.lazy(() => elementSchema).optional(), - confidentiality: codeSchema.optional(), - _confidentiality: z.lazy(() => elementSchema).optional(), - attester: compositionAttesterSchema.array().optional(), - custodian: z.lazy(() => referenceSchema).optional(), - relatesTo: compositionRelatesToSchema.array().optional(), - event: compositionEventSchema.array().optional(), - section: compositionSectionSchema.array().optional(), - }), -) - -export const conceptMapGroupSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const citationCitedArtifactPublicationFormPeriodicReleaseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - source: uriSchema.optional(), - _source: z.lazy(() => elementSchema).optional(), - sourceVersion: stringSchema.optional(), - _sourceVersion: z.lazy(() => elementSchema).optional(), - target: uriSchema.optional(), - _target: z.lazy(() => elementSchema).optional(), - targetVersion: stringSchema.optional(), - _targetVersion: z.lazy(() => elementSchema).optional(), - element: conceptMapGroupElementSchema.array().min(1), - unmapped: conceptMapGroupUnmappedSchema.optional(), - }), -) - -export const conceptMapGroupElementSchema: z.ZodType = - z.lazy(() => - z.strictObject({ + citedMedium: z.lazy(() => codeableConceptSchema).optional(), + volume: stringSchema.optional(), + _volume: z.lazy(() => elementSchema).optional(), + issue: stringSchema.optional(), + _issue: z.lazy(() => elementSchema).optional(), + dateOfPublication: + citationCitedArtifactPublicationFormPeriodicReleaseDateOfPublicationSchema.optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const citationCitedArtifactClassificationWhoClassifiedSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - target: conceptMapGroupElementTargetSchema.array().optional(), - }), - ) + person: z.lazy(() => referenceSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + publisher: z.lazy(() => referenceSchema).optional(), + classifierCopyright: stringSchema.optional(), + _classifierCopyright: z.lazy(() => elementSchema).optional(), + freeToShare: booleanSchema.optional(), + _freeToShare: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const conceptMapGroupUnmappedSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const citationCitedArtifactContributorshipEntrySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - mode: z.enum(['fixed', 'other-map', 'provided']), - _mode: z.lazy(() => elementSchema).optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - url: canonicalSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - }), - ) + name: z.lazy(() => humanNameSchema).optional(), + initials: stringSchema.optional(), + _initials: z.lazy(() => elementSchema).optional(), + collectiveName: stringSchema.optional(), + _collectiveName: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + affiliationInfo: + citationCitedArtifactContributorshipEntryAffiliationInfoSchema + .array() + .min(1) + .optional(), + address: z + .lazy(() => addressSchema) + .array() + .min(1) + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + contributionType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + contributionInstance: + citationCitedArtifactContributorshipEntryContributionInstanceSchema + .array() + .min(1) + .optional(), + correspondingContact: booleanSchema.optional(), + _correspondingContact: z.lazy(() => elementSchema).optional(), + listOrder: positiveIntSchema.optional(), + _listOrder: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const conceptMapGroupElementTargetSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const citationCitedArtifactContributorshipSummarySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - equivalence: z.enum([ - 'disjoint', - 'equal', - 'equivalent', - 'inexact', - 'narrower', - 'relatedto', - 'specializes', - 'subsumes', - 'unmatched', - 'wider', - ]), - _equivalence: z.lazy(() => elementSchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - dependsOn: conceptMapGroupElementTargetDependsOnSchema.array().optional(), - product: z - .lazy(() => conceptMapGroupElementTargetDependsOnSchema) + type: z.lazy(() => codeableConceptSchema).optional(), + style: z.lazy(() => codeableConceptSchema).optional(), + source: z.lazy(() => codeableConceptSchema).optional(), + value: markdownSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'value', shadow: '_value' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const citationCitedArtifactPublicationFormPeriodicReleaseDateOfPublicationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), - ) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + year: stringSchema.optional(), + _year: z.lazy(() => elementSchema).optional(), + month: stringSchema.optional(), + _month: z.lazy(() => elementSchema).optional(), + day: stringSchema.optional(), + _day: z.lazy(() => elementSchema).optional(), + season: stringSchema.optional(), + _season: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const conceptMapGroupElementTargetDependsOnSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const citationCitedArtifactContributorshipEntryAffiliationInfoSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - property: uriSchema, - _property: z.lazy(() => elementSchema).optional(), - system: canonicalSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - }), - ) + affiliation: stringSchema.optional(), + _affiliation: z.lazy(() => elementSchema).optional(), + role: stringSchema.optional(), + _role: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const citationCitedArtifactContributorshipEntryContributionInstanceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + time: dateTimeSchema.optional(), + _time: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const conceptMapSchema: z.ZodType = z.lazy(() => +export const citationSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('ConceptMap'), + resourceType: z.literal('Citation'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -6829,25 +6658,32 @@ export const conceptMapSchema: z.ZodType = z.lazy(() => contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), url: uriSchema.optional(), _url: z.lazy(() => elementSchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), version: stringSchema.optional(), _version: z.lazy(() => elementSchema).optional(), name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), title: stringSchema.optional(), _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), _status: z.lazy(() => elementSchema).optional(), experimental: booleanSchema.optional(), _experimental: z.lazy(() => elementSchema).optional(), @@ -6858,380 +6694,241 @@ export const conceptMapSchema: z.ZodType = z.lazy(() => contact: z .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), useContext: z .lazy(() => usageContextSchema) .array() + .min(1) .optional(), jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), purpose: markdownSchema.optional(), _purpose: z.lazy(() => elementSchema).optional(), copyright: markdownSchema.optional(), _copyright: z.lazy(() => elementSchema).optional(), - sourceUri: uriSchema.optional(), - _sourceUri: z.lazy(() => elementSchema).optional(), - sourceCanonical: canonicalSchema.optional(), - _sourceCanonical: z.lazy(() => elementSchema).optional(), - targetUri: uriSchema.optional(), - _targetUri: z.lazy(() => elementSchema).optional(), - targetCanonical: canonicalSchema.optional(), - _targetCanonical: z.lazy(() => elementSchema).optional(), - group: conceptMapGroupSchema.array().optional(), - }) - .refine( - (value) => - [ - { names: ['sourceUri', 'sourceCanonical'], required: false }, - { names: ['targetUri', 'targetCanonical'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const conditionStageSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - summary: z.lazy(() => codeableConceptSchema).optional(), - assessment: z - .lazy(() => referenceSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - }), -) - -export const conditionEvidenceSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + author: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + editor: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - code: z - .lazy(() => codeableConceptSchema) + reviewer: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - detail: z - .lazy(() => referenceSchema) + endorser: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - }), -) - -export const conditionSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - resourceType: z.literal('Condition'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + summary: citationSummarySchema.array().min(1).optional(), + classification: citationClassificationSchema.array().min(1).optional(), + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - clinicalStatus: z.lazy(() => codeableConceptSchema).optional(), - verificationStatus: z.lazy(() => codeableConceptSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - severity: z.lazy(() => codeableConceptSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - bodySite: z + currentState: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - onsetDateTime: dateTimeSchema.optional(), - _onsetDateTime: z.lazy(() => elementSchema).optional(), - onsetAge: z.lazy(() => ageSchema).optional(), - onsetPeriod: z.lazy(() => periodSchema).optional(), - onsetRange: z.lazy(() => rangeSchema).optional(), - onsetString: stringSchema.optional(), - _onsetString: z.lazy(() => elementSchema).optional(), - abatementDateTime: dateTimeSchema.optional(), - _abatementDateTime: z.lazy(() => elementSchema).optional(), - abatementAge: z.lazy(() => ageSchema).optional(), - abatementPeriod: z.lazy(() => periodSchema).optional(), - abatementRange: z.lazy(() => rangeSchema).optional(), - abatementString: stringSchema.optional(), - _abatementString: z.lazy(() => elementSchema).optional(), - recordedDate: dateTimeSchema.optional(), - _recordedDate: z.lazy(() => elementSchema).optional(), - recorder: z.lazy(() => referenceSchema).optional(), - asserter: z.lazy(() => referenceSchema).optional(), - stage: conditionStageSchema.array().optional(), - evidence: conditionEvidenceSchema.array().optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), + statusDate: citationStatusDateSchema.array().min(1).optional(), + relatesTo: citationRelatesToSchema.array().min(1).optional(), + citedArtifact: citationCitedArtifactSchema.optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: [ - 'onsetDateTime', - 'onsetAge', - 'onsetPeriod', - 'onsetRange', - 'onsetString', - ], - required: false, - }, - { - names: [ - 'abatementDateTime', - 'abatementAge', - 'abatementPeriod', - 'abatementRange', - 'abatementString', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const consentPolicySchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - authority: uriSchema.optional(), - _authority: z.lazy(() => elementSchema).optional(), - uri: uriSchema.optional(), - _uri: z.lazy(() => elementSchema).optional(), - }), -) - -export const consentVerificationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - verified: booleanSchema, - _verified: z.lazy(() => elementSchema).optional(), - verifiedWith: z.lazy(() => referenceSchema).optional(), - verificationDate: dateTimeSchema.optional(), - _verificationDate: z.lazy(() => elementSchema).optional(), - }), - ) +) as unknown as z.ZodType> -export const consentProvisionSchema: z.ZodType = z.lazy( +export const claimRelatedSchema: z.ZodType> = z.lazy( () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum(['deny', 'permit']).optional(), - _type: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - actor: consentProvisionActorSchema.array().optional(), - action: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - securityLabel: z - .lazy(() => codingSchema) - .array() - .optional(), - purpose: z - .lazy(() => codingSchema) - .array() - .optional(), - class: z - .lazy(() => codingSchema) - .array() - .optional(), - code: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - dataPeriod: z.lazy(() => periodSchema).optional(), - data: consentProvisionDataSchema.array().optional(), - provision: z - .lazy(() => consentProvisionSchema) - .array() - .optional(), - }), -) + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + claim: z.lazy(() => referenceSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema).optional(), + reference: z.lazy(() => identifierSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const consentProvisionActorSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - role: z.lazy(() => codeableConceptSchema), - reference: z.lazy(() => referenceSchema), - }), - ) +export const claimPayeeSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + party: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const consentProvisionDataSchema: z.ZodType = +export const claimCareTeamSchema: z.ZodType> = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - meaning: z.enum(['authoredby', 'dependents', 'instance', 'related']), - _meaning: z.lazy(() => elementSchema).optional(), - reference: z.lazy(() => referenceSchema), - }), - ) + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + provider: z.lazy(() => referenceSchema), + responsible: booleanSchema.optional(), + _responsible: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + qualification: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const consentSchema: z.ZodType = z.lazy(() => +export const claimSupportingInfoSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Consent'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'draft', - 'entered-in-error', - 'inactive', - 'proposed', - 'rejected', - ]), - _status: z.lazy(() => elementSchema).optional(), - scope: z.lazy(() => codeableConceptSchema), - category: z - .lazy(() => codeableConceptSchema) - .array() - .min(1), - patient: z.lazy(() => referenceSchema).optional(), - dateTime: dateTimeSchema.optional(), - _dateTime: z.lazy(() => elementSchema).optional(), - performer: z - .lazy(() => referenceSchema) - .array() - .optional(), - organization: z - .lazy(() => referenceSchema) - .array() - .optional(), - sourceAttachment: z.lazy(() => attachmentSchema).optional(), - sourceReference: z.lazy(() => referenceSchema).optional(), - policy: consentPolicySchema.array().optional(), - policyRule: z.lazy(() => codeableConceptSchema).optional(), - verification: consentVerificationSchema.array().optional(), - provision: consentProvisionSchema.optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + category: z.lazy(() => codeableConceptSchema), + code: z.lazy(() => codeableConceptSchema).optional(), + timingDate: dateSchema.optional(), + _timingDate: z.lazy(() => elementSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + reason: z.lazy(() => codeableConceptSchema).optional(), }) .refine( (value) => [ - { names: ['sourceAttachment', 'sourceReference'], required: false }, + { + alternatives: [['timingDate', '_timingDate'], ['timingPeriod']], + required: false, + }, + { + alternatives: [ + ['valueBoolean', '_valueBoolean'], + ['valueString', '_valueString'], + ['valueQuantity'], + ['valueAttachment'], + ['valueReference'], + ], + required: false, + }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -7239,183 +6936,326 @@ export const consentSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const contactDetailSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - }), -) - -export const contactPointSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - system: z - .enum(['email', 'fax', 'other', 'pager', 'phone', 'sms', 'url']) - .optional(), - _system: z.lazy(() => elementSchema).optional(), - value: stringSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - use: z.enum(['home', 'mobile', 'old', 'temp', 'work']).optional(), - _use: z.lazy(() => elementSchema).optional(), - rank: positiveIntSchema.optional(), - _rank: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), -) - -export const contractContentDefinitionSchema: z.ZodType = +) as unknown as z.ZodType> + +export const claimDiagnosisSchema: z.ZodType> = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - subType: z.lazy(() => codeableConceptSchema).optional(), - publisher: z.lazy(() => referenceSchema).optional(), - publicationDate: dateTimeSchema.optional(), - _publicationDate: z.lazy(() => elementSchema).optional(), - publicationStatus: z.enum([ - 'amended', - 'appended', - 'cancelled', - 'disputed', - 'entered-in-error', - 'executable', - 'executed', - 'negotiable', - 'offered', - 'policy', - 'rejected', - 'renewed', - 'resolved', - 'revoked', - 'terminated', - ]), - _publicationStatus: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - }), - ) + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + diagnosisCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + diagnosisReference: z.lazy(() => referenceSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + onAdmission: z.lazy(() => codeableConceptSchema).optional(), + packageCode: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['diagnosisCodeableConcept'], + ['diagnosisReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const contractTermSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - issued: dateTimeSchema.optional(), - _issued: z.lazy(() => elementSchema).optional(), - applies: z.lazy(() => periodSchema).optional(), - topicCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - topicReference: z.lazy(() => referenceSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - subType: z.lazy(() => codeableConceptSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - securityLabel: contractTermSecurityLabelSchema.array().optional(), - offer: contractTermOfferSchema, - asset: contractTermAssetSchema.array().optional(), - action: contractTermActionSchema.array().optional(), - group: z - .lazy(() => contractTermSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['topicCodeableConcept', 'topicReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const contractSignerSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codingSchema), - party: z.lazy(() => referenceSchema), - signature: z - .lazy(() => signatureSchema) - .array() - .min(1), - }), -) - -export const contractFriendlySchema: z.ZodType = z.lazy( - () => +export const claimProcedureSchema: z.ZodType> = + z.lazy(() => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + procedureCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + procedureReference: z.lazy(() => referenceSchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), - contentAttachment: z.lazy(() => attachmentSchema).optional(), - contentReference: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ { - names: ['contentAttachment', 'contentReference'], + alternatives: [ + ['procedureCodeableConcept'], + ['procedureReference'], + ], required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const claimInsuranceSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + focal: booleanSchema.optional(), + _focal: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + coverage: z.lazy(() => referenceSchema), + businessArrangement: stringSchema.optional(), + _businessArrangement: z.lazy(() => elementSchema).optional(), + preAuthRef: stringSchema.nullable().array().min(1).optional(), + _preAuthRef: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + claimResponse: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'sequence', shadow: '_sequence' }, + { name: 'focal', shadow: '_focal' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'preAuthRef', shadow: '_preAuthRef', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const claimAccidentSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['locationAddress'], ['locationReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -7423,204 +7263,239 @@ export const contractFriendlySchema: z.ZodType = z.lazy( message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'date', shadow: '_date' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), -) + ) as unknown as z.ZodType> -export const contractLegalSchema: z.ZodType = z.lazy(() => +export const claimItemSchema: z.ZodType> = z.lazy(() => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - contentAttachment: z.lazy(() => attachmentSchema).optional(), - contentReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['contentAttachment', 'contentReference'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const contractRuleSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + careTeamSequence: positiveIntSchema.nullable().array().min(1).optional(), + _careTeamSequence: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + diagnosisSequence: positiveIntSchema.nullable().array().min(1).optional(), + _diagnosisSequence: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - contentAttachment: z.lazy(() => attachmentSchema).optional(), - contentReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['contentAttachment', 'contentReference'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const contractTermSecurityLabelSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + procedureSequence: positiveIntSchema.nullable().array().min(1).optional(), + _procedureSequence: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + informationSequence: positiveIntSchema + .nullable() .array() + .min(1) .optional(), - number: unsignedIntSchema.array().optional(), - _number: z + _informationSequence: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - classification: z.lazy(() => codingSchema), - category: z - .lazy(() => codingSchema) - .array() - .optional(), - control: z - .lazy(() => codingSchema) - .array() - .optional(), - }), - ) - -export const contractTermOfferSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - party: contractTermOfferPartySchema.array().optional(), - topic: z.lazy(() => referenceSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - decision: z.lazy(() => codeableConceptSchema).optional(), - decisionMode: z + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - answer: contractTermOfferAnswerSchema.array().optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - linkId: stringSchema.array().optional(), - _linkId: z - .lazy(() => elementSchema) - .array() - .optional(), - securityLabelNumber: unsignedIntSchema.array().optional(), - _securityLabelNumber: z - .lazy(() => elementSchema) - .array() - .optional(), - }), -) - -export const contractTermAssetSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - scope: z.lazy(() => codeableConceptSchema).optional(), - type: z + programCode: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - typeReference: z + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - subtype: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - relationship: z.lazy(() => codingSchema).optional(), - context: contractTermAssetContextSchema.array().optional(), - condition: stringSchema.optional(), - _condition: z.lazy(() => elementSchema).optional(), - periodType: z + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - period: z - .lazy(() => periodSchema) - .array() - .optional(), - usePeriod: z - .lazy(() => periodSchema) - .array() - .optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - linkId: stringSchema.array().optional(), - _linkId: z - .lazy(() => elementSchema) - .array() - .optional(), - answer: z - .lazy(() => contractTermOfferAnswerSchema) - .array() - .optional(), - securityLabelNumber: unsignedIntSchema.array().optional(), - _securityLabelNumber: z - .lazy(() => elementSchema) + encounter: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - valuedItem: contractTermAssetValuedItemSchema.array().optional(), - }), -) + detail: claimItemDetailSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['servicedDate', '_servicedDate'], + ['servicedPeriod'], + ], + required: false, + }, + { + alternatives: [ + ['locationCodeableConcept'], + ['locationAddress'], + ['locationReference'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'careTeamSequence', + shadow: '_careTeamSequence', + required: false, + }, + { + name: 'diagnosisSequence', + shadow: '_diagnosisSequence', + required: false, + }, + { + name: 'procedureSequence', + shadow: '_procedureSequence', + required: false, + }, + { + name: 'informationSequence', + shadow: '_informationSequence', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const contractTermActionSchema: z.ZodType = +export const claimItemDetailSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -7628,423 +7503,419 @@ export const contractTermActionSchema: z.ZodType = extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - subject: contractTermActionSubjectSchema.array().optional(), - intent: z.lazy(() => codeableConceptSchema), - linkId: stringSchema.array().optional(), - _linkId: z - .lazy(() => elementSchema) - .array() - .optional(), - status: z.lazy(() => codeableConceptSchema), - context: z.lazy(() => referenceSchema).optional(), - contextLinkId: stringSchema.array().optional(), - _contextLinkId: z - .lazy(() => elementSchema) - .array() - .optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - occurrenceTiming: z.lazy(() => timingSchema).optional(), - requester: z - .lazy(() => referenceSchema) - .array() - .optional(), - requesterLinkId: stringSchema.array().optional(), - _requesterLinkId: z - .lazy(() => elementSchema) - .array() - .optional(), - performerType: z + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - performerRole: z.lazy(() => codeableConceptSchema).optional(), - performer: z.lazy(() => referenceSchema).optional(), - performerLinkId: stringSchema.array().optional(), - _performerLinkId: z - .lazy(() => elementSchema) - .array() - .optional(), - reasonCode: z + programCode: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - reasonReference: z + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - reason: stringSchema.array().optional(), - _reason: z - .lazy(() => elementSchema) - .array() - .optional(), - reasonLinkId: stringSchema.array().optional(), - _reasonLinkId: z - .lazy(() => elementSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - securityLabelNumber: unsignedIntSchema.array().optional(), - _securityLabelNumber: z - .lazy(() => elementSchema) - .array() - .optional(), + subDetail: claimItemDetailSubDetailSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: [ - 'occurrenceDateTime', - 'occurrencePeriod', - 'occurrenceTiming', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const contractTermOfferPartySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const claimItemDetailSubDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - reference: z + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z .lazy(() => referenceSchema) .array() - .min(1), - role: z.lazy(() => codeableConceptSchema), - }), - ) + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const contractTermOfferAnswerSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueBoolean', - 'valueDecimal', - 'valueInteger', - 'valueDate', - 'valueDateTime', - 'valueTime', - 'valueString', - 'valueUri', - 'valueAttachment', - 'valueCoding', - 'valueQuantity', - 'valueReference', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const contractTermAssetContextSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const claimSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Claim'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - reference: z.lazy(() => referenceSchema).optional(), - code: z - .lazy(() => codeableConceptSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - }), - ) - -export const contractTermAssetValuedItemSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - entityCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - entityReference: z.lazy(() => referenceSchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), - effectiveTime: dateTimeSchema.optional(), - _effectiveTime: z.lazy(() => elementSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - points: decimalSchema.optional(), - _points: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - payment: stringSchema.optional(), - _payment: z.lazy(() => elementSchema).optional(), - paymentDate: dateTimeSchema.optional(), - _paymentDate: z.lazy(() => elementSchema).optional(), - responsible: z.lazy(() => referenceSchema).optional(), - recipient: z.lazy(() => referenceSchema).optional(), - linkId: stringSchema.array().optional(), - _linkId: z - .lazy(() => elementSchema) - .array() - .optional(), - securityLabelNumber: unsignedIntSchema.array().optional(), - _securityLabelNumber: z - .lazy(() => elementSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['entityCodeableConcept', 'entityReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + use: z.enum(['claim', 'preauthorization', 'predetermination']).optional(), + _use: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + billablePeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + insurer: z.lazy(() => referenceSchema).optional(), + provider: z.lazy(() => referenceSchema), + priority: z.lazy(() => codeableConceptSchema), + fundsReserve: z.lazy(() => codeableConceptSchema).optional(), + related: claimRelatedSchema.array().min(1).optional(), + prescription: z.lazy(() => referenceSchema).optional(), + originalPrescription: z.lazy(() => referenceSchema).optional(), + payee: claimPayeeSchema.optional(), + referral: z.lazy(() => referenceSchema).optional(), + facility: z.lazy(() => referenceSchema).optional(), + careTeam: claimCareTeamSchema.array().min(1).optional(), + supportingInfo: claimSupportingInfoSchema.array().min(1).optional(), + diagnosis: claimDiagnosisSchema.array().min(1).optional(), + procedure: claimProcedureSchema.array().min(1).optional(), + insurance: claimInsuranceSchema.array().min(1), + accident: claimAccidentSchema.optional(), + item: claimItemSchema.array().min(1).optional(), + total: z.lazy(() => moneySchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'use', shadow: '_use' }, + { name: 'created', shadow: '_created' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const contractTermActionSubjectSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const claimResponseItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - reference: z - .lazy(() => referenceSchema) + itemSequence: positiveIntSchema.optional(), + _itemSequence: z.lazy(() => elementSchema).optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() .array() - .min(1), - role: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + .min(1) + .optional(), + adjudication: claimResponseItemAdjudicationSchema.array().min(1), + detail: claimResponseItemDetailSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'itemSequence', shadow: '_itemSequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const contractSchema: z.ZodType = z.lazy(() => +export const claimResponseAddItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Contract'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) + itemSequence: positiveIntSchema.nullable().array().min(1).optional(), + _itemSequence: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - status: z - .enum([ - 'amended', - 'appended', - 'cancelled', - 'disputed', - 'entered-in-error', - 'executable', - 'executed', - 'negotiable', - 'offered', - 'policy', - 'rejected', - 'renewed', - 'resolved', - 'revoked', - 'terminated', - ]) - .optional(), - _status: z.lazy(() => elementSchema).optional(), - legalState: z.lazy(() => codeableConceptSchema).optional(), - instantiatesCanonical: z.lazy(() => referenceSchema).optional(), - instantiatesUri: uriSchema.optional(), - _instantiatesUri: z.lazy(() => elementSchema).optional(), - contentDerivative: z.lazy(() => codeableConceptSchema).optional(), - issued: dateTimeSchema.optional(), - _issued: z.lazy(() => elementSchema).optional(), - applies: z.lazy(() => periodSchema).optional(), - expirationType: z.lazy(() => codeableConceptSchema).optional(), - subject: z - .lazy(() => referenceSchema) + detailSequence: positiveIntSchema.nullable().array().min(1).optional(), + _detailSequence: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - authority: z - .lazy(() => referenceSchema) + subdetailSequence: positiveIntSchema.nullable().array().min(1).optional(), + _subdetailSequence: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - domain: z + provider: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - site: z - .lazy(() => referenceSchema) + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - alias: stringSchema.array().optional(), - _alias: z - .lazy(() => elementSchema) + programCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - author: z.lazy(() => referenceSchema).optional(), - scope: z.lazy(() => codeableConceptSchema).optional(), - topicCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - topicReference: z.lazy(() => referenceSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - subType: z + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - contentDefinition: contractContentDefinitionSchema.optional(), - term: contractTermSchema.array().optional(), - supportingInfo: z - .lazy(() => referenceSchema) + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - relevantHistory: z - .lazy(() => referenceSchema) + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) .array() - .optional(), - signer: contractSignerSchema.array().optional(), - friendly: contractFriendlySchema.array().optional(), - legal: contractLegalSchema.array().optional(), - rule: contractRuleSchema.array().optional(), - legallyBindingAttachment: z.lazy(() => attachmentSchema).optional(), - legallyBindingReference: z.lazy(() => referenceSchema).optional(), + .min(1), + detail: claimResponseAddItemDetailSchema.array().min(1).optional(), }) .refine( (value) => [ { - names: ['topicCodeableConcept', 'topicReference'], + alternatives: [ + ['servicedDate', '_servicedDate'], + ['servicedPeriod'], + ], required: false, }, { - names: ['legallyBindingAttachment', 'legallyBindingReference'], + alternatives: [ + ['locationCodeableConcept'], + ['locationAddress'], + ['locationReference'], + ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -8052,497 +7923,666 @@ export const contractSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, - ), -) - -export const contributorSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum(['author', 'editor', 'endorser', 'reviewer']), - _type: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - }), -) - -export const countSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - comparator: z.enum(['<', '<=', '>', '>=']).optional(), - _comparator: z.lazy(() => elementSchema).optional(), - unit: stringSchema.optional(), - _unit: z.lazy(() => elementSchema).optional(), - system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - }), -) - -export const coverageClassSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - }), -) - -export const coverageCostToBeneficiarySchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueMoney: z.lazy(() => moneySchema).optional(), - exception: coverageCostToBeneficiaryExceptionSchema.array().optional(), - }) - .refine( - (value) => - [{ names: ['valueQuantity', 'valueMoney'], required: true }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'itemSequence', shadow: '_itemSequence', required: false }, + { name: 'detailSequence', shadow: '_detailSequence', required: false }, { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + name: 'subdetailSequence', + shadow: '_subdetailSequence', + required: false, }, - ), - ) + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const coverageCostToBeneficiaryExceptionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const claimResponseItemAdjudicationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - period: z.lazy(() => periodSchema).optional(), - }), - ) + category: z.lazy(() => codeableConceptSchema), + reason: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const coverageSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Coverage'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - policyHolder: z.lazy(() => referenceSchema).optional(), - subscriber: z.lazy(() => referenceSchema).optional(), - subscriberId: stringSchema.optional(), - _subscriberId: z.lazy(() => elementSchema).optional(), - beneficiary: z.lazy(() => referenceSchema), - dependent: stringSchema.optional(), - _dependent: z.lazy(() => elementSchema).optional(), - relationship: z.lazy(() => codeableConceptSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - payor: z - .lazy(() => referenceSchema) - .array() - .min(1), - class: coverageClassSchema.array().optional(), - order: positiveIntSchema.optional(), - _order: z.lazy(() => elementSchema).optional(), - network: stringSchema.optional(), - _network: z.lazy(() => elementSchema).optional(), - costToBeneficiary: coverageCostToBeneficiarySchema.array().optional(), - subrogation: booleanSchema.optional(), - _subrogation: z.lazy(() => elementSchema).optional(), - contract: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) - -export const coverageEligibilityRequestSupportingInfoSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const claimResponseTotalSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - information: z.lazy(() => referenceSchema), - appliesToAll: booleanSchema.optional(), - _appliesToAll: z.lazy(() => elementSchema).optional(), - }), - ) + category: z.lazy(() => codeableConceptSchema), + amount: z.lazy(() => moneySchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const coverageEligibilityRequestInsuranceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const claimResponsePaymentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - focal: booleanSchema.optional(), - _focal: z.lazy(() => elementSchema).optional(), - coverage: z.lazy(() => referenceSchema), - businessArrangement: stringSchema.optional(), - _businessArrangement: z.lazy(() => elementSchema).optional(), - }), - ) + type: z.lazy(() => codeableConceptSchema), + adjustment: z.lazy(() => moneySchema).optional(), + adjustmentReason: z.lazy(() => codeableConceptSchema).optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema), + identifier: z.lazy(() => identifierSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const coverageEligibilityRequestItemSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const claimResponseProcessNoteSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - supportingInfoSequence: positiveIntSchema.array().optional(), - _supportingInfoSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema).optional(), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - provider: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - facility: z.lazy(() => referenceSchema).optional(), - diagnosis: coverageEligibilityRequestItemDiagnosisSchema - .array() - .optional(), - detail: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), - ) - -export const coverageEligibilityRequestItemDiagnosisSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - diagnosisCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - diagnosisReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['diagnosisCodeableConcept', 'diagnosisReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const coverageEligibilityRequestSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - resourceType: z.literal('CoverageEligibilityRequest'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), - _status: z.lazy(() => elementSchema).optional(), - priority: z.lazy(() => codeableConceptSchema).optional(), - purpose: z - .enum(['auth-requirements', 'benefits', 'discovery', 'validation']) - .array() - .min(1), - _purpose: z - .lazy(() => elementSchema) - .array() - .optional(), - patient: z.lazy(() => referenceSchema), - servicedDate: dateSchema.optional(), - _servicedDate: z.lazy(() => elementSchema).optional(), - servicedPeriod: z.lazy(() => periodSchema).optional(), - created: dateTimeSchema, - _created: z.lazy(() => elementSchema).optional(), - enterer: z.lazy(() => referenceSchema).optional(), - provider: z.lazy(() => referenceSchema).optional(), - insurer: z.lazy(() => referenceSchema), - facility: z.lazy(() => referenceSchema).optional(), - supportingInfo: coverageEligibilityRequestSupportingInfoSchema - .array() - .optional(), - insurance: coverageEligibilityRequestInsuranceSchema.array().optional(), - item: coverageEligibilityRequestItemSchema.array().optional(), - }) - .refine( - (value) => - [ - { names: ['servicedDate', 'servicedPeriod'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + number: positiveIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + type: z.enum(['display', 'print', 'printoper']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + language: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'text', shadow: '_text' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const coverageEligibilityResponseInsuranceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const claimResponseInsuranceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + focal: booleanSchema.optional(), + _focal: z.lazy(() => elementSchema).optional(), coverage: z.lazy(() => referenceSchema), - inforce: booleanSchema.optional(), - _inforce: z.lazy(() => elementSchema).optional(), - benefitPeriod: z.lazy(() => periodSchema).optional(), - item: coverageEligibilityResponseInsuranceItemSchema.array().optional(), - }), - ) + businessArrangement: stringSchema.optional(), + _businessArrangement: z.lazy(() => elementSchema).optional(), + claimResponse: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'sequence', shadow: '_sequence' }, + { name: 'focal', shadow: '_focal' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const coverageEligibilityResponseErrorSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const claimResponseErrorSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), + itemSequence: positiveIntSchema.optional(), + _itemSequence: z.lazy(() => elementSchema).optional(), + detailSequence: positiveIntSchema.optional(), + _detailSequence: z.lazy(() => elementSchema).optional(), + subDetailSequence: positiveIntSchema.optional(), + _subDetailSequence: z.lazy(() => elementSchema).optional(), code: z.lazy(() => codeableConceptSchema), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const coverageEligibilityResponseInsuranceItemSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const claimResponseItemDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema).optional(), - modifier: z - .lazy(() => codeableConceptSchema) + detailSequence: positiveIntSchema.optional(), + _detailSequence: z.lazy(() => elementSchema).optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - provider: z.lazy(() => referenceSchema).optional(), - excluded: booleanSchema.optional(), - _excluded: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - network: z.lazy(() => codeableConceptSchema).optional(), - unit: z.lazy(() => codeableConceptSchema).optional(), - term: z.lazy(() => codeableConceptSchema).optional(), - benefit: coverageEligibilityResponseInsuranceItemBenefitSchema + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1), + subDetail: claimResponseItemDetailSubDetailSchema .array() + .min(1) .optional(), - authorizationRequired: booleanSchema.optional(), - _authorizationRequired: z.lazy(() => elementSchema).optional(), - authorizationSupporting: z + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'detailSequence', shadow: '_detailSequence' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const claimResponseAddItemDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - authorizationUrl: uriSchema.optional(), - _authorizationUrl: z.lazy(() => elementSchema).optional(), - }), - ) + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1), + subDetail: claimResponseAddItemDetailSubDetailSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const coverageEligibilityResponseInsuranceItemBenefitSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - allowedUnsignedInt: unsignedIntSchema.optional(), - _allowedUnsignedInt: z.lazy(() => elementSchema).optional(), - allowedString: stringSchema.optional(), - _allowedString: z.lazy(() => elementSchema).optional(), - allowedMoney: z.lazy(() => moneySchema).optional(), - usedUnsignedInt: unsignedIntSchema.optional(), - _usedUnsignedInt: z.lazy(() => elementSchema).optional(), - usedString: stringSchema.optional(), - _usedString: z.lazy(() => elementSchema).optional(), - usedMoney: z.lazy(() => moneySchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['allowedUnsignedInt', 'allowedString', 'allowedMoney'], - required: false, - }, - { - names: ['usedUnsignedInt', 'usedString', 'usedMoney'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) +export const claimResponseItemDetailSubDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + subDetailSequence: positiveIntSchema.optional(), + _subDetailSequence: z.lazy(() => elementSchema).optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'subDetailSequence', shadow: '_subDetailSequence' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const claimResponseAddItemDetailSubDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const coverageEligibilityResponseSchema: z.ZodType = +export const claimResponseSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('CoverageEligibilityResponse'), + resourceType: z.literal('ClaimResponse'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -8554,510 +8594,426 @@ export const coverageEligibilityResponseSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), _status: z.lazy(() => elementSchema).optional(), - purpose: z - .enum(['auth-requirements', 'benefits', 'discovery', 'validation']) - .array() - .min(1), - _purpose: z - .lazy(() => elementSchema) - .array() + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + use: z + .enum(['claim', 'preauthorization', 'predetermination']) .optional(), + _use: z.lazy(() => elementSchema).optional(), patient: z.lazy(() => referenceSchema), - servicedDate: dateSchema.optional(), - _servicedDate: z.lazy(() => elementSchema).optional(), - servicedPeriod: z.lazy(() => periodSchema).optional(), - created: dateTimeSchema, + created: dateTimeSchema.optional(), _created: z.lazy(() => elementSchema).optional(), + insurer: z.lazy(() => referenceSchema), requestor: z.lazy(() => referenceSchema).optional(), - request: z.lazy(() => referenceSchema), - outcome: z.enum(['complete', 'error', 'partial', 'queued']), + request: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), _outcome: z.lazy(() => elementSchema).optional(), disposition: stringSchema.optional(), _disposition: z.lazy(() => elementSchema).optional(), - insurer: z.lazy(() => referenceSchema), - insurance: coverageEligibilityResponseInsuranceSchema - .array() - .optional(), preAuthRef: stringSchema.optional(), _preAuthRef: z.lazy(() => elementSchema).optional(), - form: z.lazy(() => codeableConceptSchema).optional(), - error: coverageEligibilityResponseErrorSchema.array().optional(), + preAuthPeriod: z.lazy(() => periodSchema).optional(), + payeeType: z.lazy(() => codeableConceptSchema).optional(), + item: claimResponseItemSchema.array().min(1).optional(), + addItem: claimResponseAddItemSchema.array().min(1).optional(), + adjudication: z + .lazy(() => claimResponseItemAdjudicationSchema) + .array() + .min(1) + .optional(), + total: claimResponseTotalSchema.array().min(1).optional(), + payment: claimResponsePaymentSchema.optional(), + fundsReserve: z.lazy(() => codeableConceptSchema).optional(), + formCode: z.lazy(() => codeableConceptSchema).optional(), + form: z.lazy(() => attachmentSchema).optional(), + processNote: claimResponseProcessNoteSchema.array().min(1).optional(), + communicationRequest: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + insurance: claimResponseInsuranceSchema.array().min(1).optional(), + error: claimResponseErrorSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'use', shadow: '_use' }, + { name: 'created', shadow: '_created' }, + { name: 'outcome', shadow: '_outcome' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { names: ['servicedDate', 'servicedPeriod'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const dataRequirementCodeFilterSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const clinicalImpressionInvestigationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - path: stringSchema.optional(), - _path: z.lazy(() => elementSchema).optional(), - searchParam: stringSchema.optional(), - _searchParam: z.lazy(() => elementSchema).optional(), - valueSet: canonicalSchema.optional(), - _valueSet: z.lazy(() => elementSchema).optional(), - code: z - .lazy(() => codingSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), - ) - -export const dataRequirementDateFilterSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - path: stringSchema.optional(), - _path: z.lazy(() => elementSchema).optional(), - searchParam: stringSchema.optional(), - _searchParam: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valuePeriod: z.lazy(() => periodSchema).optional(), - valueDuration: z.lazy(() => durationSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['valueDateTime', 'valuePeriod', 'valueDuration'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + code: z.lazy(() => codeableConceptSchema), + item: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const dataRequirementSortSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const clinicalImpressionFindingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - path: stringSchema, - _path: z.lazy(() => elementSchema).optional(), - direction: z.enum(['ascending', 'descending']), - _direction: z.lazy(() => elementSchema).optional(), - }), - ) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + basis: stringSchema.optional(), + _basis: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const dataRequirementSchema: z.ZodType = z.lazy( - () => - z - .strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum([ - 'Account', - 'ActivityDefinition', - 'Address', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'Age', - 'AllergyIntolerance', - 'Annotation', - 'Any', - 'Appointment', - 'AppointmentResponse', - 'Attachment', - 'AuditEvent', - 'BackboneElement', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'CodeableConcept', - 'CodeableReference', - 'Coding', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'ContactDetail', - 'ContactPoint', - 'Contract', - 'Contributor', - 'Count', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DataRequirement', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'Distance', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Dosage', - 'Duration', - 'Element', - 'ElementDefinition', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'Expression', - 'Extension', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'HumanName', - 'Identifier', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'MarketingStatus', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'Meta', - 'MolecularSequence', - 'Money', - 'MoneyQuantity', - 'NamingSystem', - 'Narrative', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'ParameterDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Period', - 'Person', - 'PlanDefinition', - 'Population', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'ProdCharacteristic', - 'ProductShelfLife', - 'Provenance', - 'Quantity', - 'Questionnaire', - 'QuestionnaireResponse', - 'Range', - 'Ratio', - 'RatioRange', - 'Reference', - 'RegulatedAuthorization', - 'RelatedArtifact', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'SampledData', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Signature', - 'SimpleQuantity', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'Timing', - 'TriggerDefinition', - 'Type', - 'UsageContext', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - 'base64Binary', - 'boolean', - 'canonical', - 'code', - 'date', - 'dateTime', - 'decimal', - 'id', - 'instant', - 'integer', - 'markdown', - 'oid', - 'positiveInt', - 'string', - 'time', - 'unsignedInt', - 'uri', - 'url', - 'uuid', - 'xhtml', - ]), - _type: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.array().optional(), - _profile: z - .lazy(() => elementSchema) - .array() - .optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - mustSupport: stringSchema.array().optional(), - _mustSupport: z - .lazy(() => elementSchema) - .array() - .optional(), - codeFilter: dataRequirementCodeFilterSchema.array().optional(), - dateFilter: dataRequirementDateFilterSchema.array().optional(), - limit: positiveIntSchema.optional(), - _limit: z.lazy(() => elementSchema).optional(), - sort: dataRequirementSortSchema.array().optional(), - }) - .refine( - (value) => - [ - { - names: ['subjectCodeableConcept', 'subjectReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const detectedIssueEvidenceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const clinicalImpressionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ClinicalImpression'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z - .lazy(() => codeableConceptSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - detail: z + status: z + .enum(['completed', 'entered-in-error', 'in-progress']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + assessor: z.lazy(() => referenceSchema).optional(), + previous: z.lazy(() => referenceSchema).optional(), + problem: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - }), - ) + investigation: clinicalImpressionInvestigationSchema + .array() + .min(1) + .optional(), + protocol: uriSchema.nullable().array().min(1).optional(), + _protocol: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + summary: stringSchema.optional(), + _summary: z.lazy(() => elementSchema).optional(), + finding: clinicalImpressionFindingSchema.array().min(1).optional(), + prognosisCodeableConcept: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + prognosisReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['effectiveDateTime', '_effectiveDateTime'], + ['effectivePeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'protocol', shadow: '_protocol', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const detectedIssueMitigationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const clinicalUseDefinitionContraindicationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - action: z.lazy(() => codeableConceptSchema), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - }), - ) + diseaseSymptomProcedure: z.lazy(() => codeableReferenceSchema).optional(), + diseaseStatus: z.lazy(() => codeableReferenceSchema).optional(), + comorbidity: z + .lazy(() => codeableReferenceSchema) + .array() + .min(1) + .optional(), + indication: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + otherTherapy: clinicalUseDefinitionContraindicationOtherTherapySchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const detectedIssueSchema: z.ZodType = z.lazy(() => +export const clinicalUseDefinitionIndicationSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('DetectedIssue'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) + diseaseSymptomProcedure: z.lazy(() => codeableReferenceSchema).optional(), + diseaseStatus: z.lazy(() => codeableReferenceSchema).optional(), + comorbidity: z + .lazy(() => codeableReferenceSchema) .array() + .min(1) .optional(), - status: z.enum([ - 'amended', - 'cancelled', - 'corrected', - 'entered-in-error', - 'final', - 'preliminary', - 'registered', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - severity: z.enum(['high', 'low', 'moderate']).optional(), - _severity: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema).optional(), - identifiedDateTime: dateTimeSchema.optional(), - _identifiedDateTime: z.lazy(() => elementSchema).optional(), - identifiedPeriod: z.lazy(() => periodSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - implicated: z + intendedEffect: z.lazy(() => codeableReferenceSchema).optional(), + durationRange: z.lazy(() => rangeSchema).optional(), + durationString: stringSchema.optional(), + _durationString: z.lazy(() => elementSchema).optional(), + undesirableEffect: z .lazy(() => referenceSchema) .array() + .min(1) + .optional(), + otherTherapy: z + .lazy(() => clinicalUseDefinitionContraindicationOtherTherapySchema) + .array() + .min(1) .optional(), - evidence: detectedIssueEvidenceSchema.array().optional(), - detail: stringSchema.optional(), - _detail: z.lazy(() => elementSchema).optional(), - reference: uriSchema.optional(), - _reference: z.lazy(() => elementSchema).optional(), - mitigation: detectedIssueMitigationSchema.array().optional(), }) .refine( (value) => [ { - names: ['identifiedDateTime', 'identifiedPeriod'], + alternatives: [ + ['durationRange'], + ['durationString', '_durationString'], + ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -9065,337 +9021,622 @@ export const detectedIssueSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const deviceUdiCarrierSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const clinicalUseDefinitionInteractionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - deviceIdentifier: stringSchema.optional(), - _deviceIdentifier: z.lazy(() => elementSchema).optional(), - issuer: uriSchema.optional(), - _issuer: z.lazy(() => elementSchema).optional(), - jurisdiction: uriSchema.optional(), - _jurisdiction: z.lazy(() => elementSchema).optional(), - carrierAIDC: base64BinarySchema.optional(), - _carrierAIDC: z.lazy(() => elementSchema).optional(), - carrierHRF: stringSchema.optional(), - _carrierHRF: z.lazy(() => elementSchema).optional(), - entryType: z - .enum(['barcode', 'card', 'manual', 'rfid', 'self-reported', 'unknown']) - .optional(), - _entryType: z.lazy(() => elementSchema).optional(), - }), -) - -export const deviceDeviceNameSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + interactant: clinicalUseDefinitionInteractionInteractantSchema .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + type: z.lazy(() => codeableConceptSchema).optional(), + effect: z.lazy(() => codeableReferenceSchema).optional(), + incidence: z.lazy(() => codeableConceptSchema).optional(), + management: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - type: z.enum([ - 'manufacturer-name', - 'model-name', - 'other', - 'patient-reported-name', - 'udi-label-name', - 'user-friendly-name', - ]), - _type: z.lazy(() => elementSchema).optional(), - }), -) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const deviceSpecializationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const clinicalUseDefinitionUndesirableEffectSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - systemType: z.lazy(() => codeableConceptSchema), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - }), - ) + symptomConditionEffect: z.lazy(() => codeableReferenceSchema).optional(), + classification: z.lazy(() => codeableConceptSchema).optional(), + frequencyOfOccurrence: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const deviceVersionSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - component: z.lazy(() => identifierSchema).optional(), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - }), -) - -export const devicePropertySchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - valueQuantity: z - .lazy(() => quantitySchema) - .array() - .optional(), - valueCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - }), -) - -export const deviceSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Device'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - definition: z.lazy(() => referenceSchema).optional(), - udiCarrier: deviceUdiCarrierSchema.array().optional(), - status: z - .enum(['active', 'entered-in-error', 'inactive', 'unknown']) - .optional(), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - distinctIdentifier: stringSchema.optional(), - _distinctIdentifier: z.lazy(() => elementSchema).optional(), - manufacturer: stringSchema.optional(), - _manufacturer: z.lazy(() => elementSchema).optional(), - manufactureDate: dateTimeSchema.optional(), - _manufactureDate: z.lazy(() => elementSchema).optional(), - expirationDate: dateTimeSchema.optional(), - _expirationDate: z.lazy(() => elementSchema).optional(), - lotNumber: stringSchema.optional(), - _lotNumber: z.lazy(() => elementSchema).optional(), - serialNumber: stringSchema.optional(), - _serialNumber: z.lazy(() => elementSchema).optional(), - deviceName: deviceDeviceNameSchema.array().optional(), - modelNumber: stringSchema.optional(), - _modelNumber: z.lazy(() => elementSchema).optional(), - partNumber: stringSchema.optional(), - _partNumber: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - specialization: deviceSpecializationSchema.array().optional(), - version: deviceVersionSchema.array().optional(), - property: devicePropertySchema.array().optional(), - patient: z.lazy(() => referenceSchema).optional(), - owner: z.lazy(() => referenceSchema).optional(), - contact: z - .lazy(() => contactPointSchema) - .array() - .optional(), - location: z.lazy(() => referenceSchema).optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - safety: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - parent: z.lazy(() => referenceSchema).optional(), - }), -) - -export const deviceDefinitionUdiDeviceIdentifierSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const clinicalUseDefinitionWarningSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - deviceIdentifier: stringSchema, - _deviceIdentifier: z.lazy(() => elementSchema).optional(), - issuer: uriSchema, - _issuer: z.lazy(() => elementSchema).optional(), - jurisdiction: uriSchema, - _jurisdiction: z.lazy(() => elementSchema).optional(), - }), - ) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const deviceDefinitionDeviceNameSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const clinicalUseDefinitionContraindicationOtherTherapySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - type: z.enum([ - 'manufacturer-name', - 'model-name', - 'other', - 'patient-reported-name', - 'udi-label-name', - 'user-friendly-name', - ]), - _type: z.lazy(() => elementSchema).optional(), - }), - ) + relationshipType: z.lazy(() => codeableConceptSchema), + therapy: z.lazy(() => codeableReferenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const deviceDefinitionSpecializationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const clinicalUseDefinitionInteractionInteractantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - systemType: stringSchema, - _systemType: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - }), - ) + itemReference: z.lazy(() => referenceSchema).optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['itemReference'], ['itemCodeableConcept']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const deviceDefinitionCapabilitySchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const clinicalUseDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ClinicalUseDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - description: z - .lazy(() => codeableConceptSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - }), - ) - -export const deviceDefinitionPropertySchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + type: z + .enum([ + 'contraindication', + 'indication', + 'interaction', + 'undesirable-effect', + 'warning', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + subject: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - valueQuantity: z - .lazy(() => quantitySchema) + status: z.lazy(() => codeableConceptSchema).optional(), + contraindication: clinicalUseDefinitionContraindicationSchema.optional(), + indication: clinicalUseDefinitionIndicationSchema.optional(), + interaction: clinicalUseDefinitionInteractionSchema.optional(), + population: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - valueCode: z - .lazy(() => codeableConceptSchema) + undesirableEffect: + clinicalUseDefinitionUndesirableEffectSchema.optional(), + warning: clinicalUseDefinitionWarningSchema.optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const codeSystemFilterSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + operator: z + .enum([ + '=', + 'descendent-of', + 'exists', + 'generalizes', + 'in', + 'is-a', + 'is-not-a', + 'not-in', + 'regex', + ]) + .nullable() + .array() + .min(1) + .optional(), + _operator: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'code', shadow: '_code' }, + { name: 'value', shadow: '_value' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'operator', shadow: '_operator', required: true }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const codeSystemPropertySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), - ) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + uri: uriSchema.optional(), + _uri: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'Coding', + 'boolean', + 'code', + 'dateTime', + 'decimal', + 'integer', + 'string', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'code', shadow: '_code' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const deviceDefinitionMaterialSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const codeSystemConceptSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - substance: z.lazy(() => codeableConceptSchema), - alternate: booleanSchema.optional(), - _alternate: z.lazy(() => elementSchema).optional(), - allergenicIndicator: booleanSchema.optional(), - _allergenicIndicator: z.lazy(() => elementSchema).optional(), - }), - ) + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + definition: stringSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + designation: codeSystemConceptDesignationSchema.array().min(1).optional(), + property: codeSystemConceptPropertySchema.array().min(1).optional(), + concept: z + .lazy(() => codeSystemConceptSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const codeSystemConceptDesignationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + use: z.lazy(() => codingSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'value', shadow: '_value' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const codeSystemConceptPropertySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueCode', '_valueCode'], + ['valueCoding'], + ['valueString', '_valueString'], + ['valueInteger', '_valueInteger'], + ['valueBoolean', '_valueBoolean'], + ['valueDateTime', '_valueDateTime'], + ['valueDecimal', '_valueDecimal'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const deviceDefinitionSchema: z.ZodType = z.lazy( +export const codeSystemSchema: z.ZodType> = z.lazy( () => z .strictObject({ - resourceType: z.literal('DeviceDefinition'), + resourceType: z.literal('CodeSystem'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -9407,344 +9648,226 @@ export const deviceDefinitionSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - udiDeviceIdentifier: deviceDefinitionUdiDeviceIdentifierSchema + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - manufacturerString: stringSchema.optional(), - _manufacturerString: z.lazy(() => elementSchema).optional(), - manufacturerReference: z.lazy(() => referenceSchema).optional(), - deviceName: deviceDefinitionDeviceNameSchema.array().optional(), - modelNumber: stringSchema.optional(), - _modelNumber: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - specialization: deviceDefinitionSpecializationSchema.array().optional(), - version: stringSchema.array().optional(), - _version: z - .lazy(() => elementSchema) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - safety: z + jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - shelfLifeStorage: z - .lazy(() => productShelfLifeSchema) - .array() - .optional(), - physicalCharacteristics: z - .lazy(() => prodCharacteristicSchema) - .optional(), - languageCode: z - .lazy(() => codeableConceptSchema) - .array() + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + caseSensitive: booleanSchema.optional(), + _caseSensitive: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema.optional(), + _valueSet: z.lazy(() => elementSchema).optional(), + hierarchyMeaning: z + .enum(['classified-with', 'grouped-by', 'is-a', 'part-of']) + .optional(), + _hierarchyMeaning: z.lazy(() => elementSchema).optional(), + compositional: booleanSchema.optional(), + _compositional: z.lazy(() => elementSchema).optional(), + versionNeeded: booleanSchema.optional(), + _versionNeeded: z.lazy(() => elementSchema).optional(), + content: z + .enum([ + 'complete', + 'example', + 'fragment', + 'not-present', + 'supplement', + ]) .optional(), - capability: deviceDefinitionCapabilitySchema.array().optional(), - property: deviceDefinitionPropertySchema.array().optional(), - owner: z.lazy(() => referenceSchema).optional(), - contact: z - .lazy(() => contactPointSchema) + _content: z.lazy(() => elementSchema).optional(), + supplements: canonicalSchema.optional(), + _supplements: z.lazy(() => elementSchema).optional(), + count: unsignedIntSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + filter: codeSystemFilterSchema.array().min(1).optional(), + property: codeSystemPropertySchema.array().min(1).optional(), + concept: codeSystemConceptSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'content', shadow: '_content' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const codeableConceptSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - onlineInformation: uriSchema.optional(), - _onlineInformation: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) + coding: z + .lazy(() => codingSchema) .array() + .min(1) .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - parentDevice: z.lazy(() => referenceSchema).optional(), - material: deviceDefinitionMaterialSchema.array().optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), }) .refine( - (value) => - [ - { - names: ['manufacturerString', 'manufacturerReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), -) + ) as unknown as z.ZodType> -export const deviceMetricCalibrationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const codeableReferenceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z + concept: z.lazy(() => codeableConceptSchema).optional(), + reference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const codingSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum(['gain', 'offset', 'two-point', 'unspecified']).optional(), - _type: z.lazy(() => elementSchema).optional(), - state: z - .enum([ - 'calibrated', - 'calibration-required', - 'not-calibrated', - 'unspecified', - ]) - .optional(), - _state: z.lazy(() => elementSchema).optional(), - time: instantSchema.optional(), - _time: z.lazy(() => elementSchema).optional(), - }), - ) - -export const deviceMetricSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('DeviceMetric'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - unit: z.lazy(() => codeableConceptSchema).optional(), - source: z.lazy(() => referenceSchema).optional(), - parent: z.lazy(() => referenceSchema).optional(), - operationalStatus: z - .enum(['entered-in-error', 'off', 'on', 'standby']) - .optional(), - _operationalStatus: z.lazy(() => elementSchema).optional(), - color: z - .enum([ - 'black', - 'blue', - 'cyan', - 'green', - 'magenta', - 'red', - 'white', - 'yellow', - ]) - .optional(), - _color: z.lazy(() => elementSchema).optional(), - category: z.enum(['calculation', 'measurement', 'setting', 'unspecified']), - _category: z.lazy(() => elementSchema).optional(), - measurementPeriod: z.lazy(() => timingSchema).optional(), - calibration: deviceMetricCalibrationSchema.array().optional(), - }), -) - -export const deviceRequestParameterSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueCodeableConcept', - 'valueQuantity', - 'valueRange', - 'valueBoolean', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + userSelected: booleanSchema.optional(), + _userSelected: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const deviceRequestSchema: z.ZodType = z.lazy(() => +export const communicationPayloadSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('DeviceRequest'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - priorRequest: z - .lazy(() => referenceSchema) - .array() - .optional(), - groupIdentifier: z.lazy(() => identifierSchema).optional(), - status: z - .enum([ - 'active', - 'completed', - 'draft', - 'entered-in-error', - 'on-hold', - 'revoked', - 'unknown', - ]) - .optional(), - _status: z.lazy(() => elementSchema).optional(), - intent: z.enum([ - 'directive', - 'filler-order', - 'instance-order', - 'option', - 'order', - 'original-order', - 'plan', - 'proposal', - 'reflex-order', - ]), - _intent: z.lazy(() => elementSchema).optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - codeReference: z.lazy(() => referenceSchema).optional(), - codeCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - parameter: deviceRequestParameterSchema.array().optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - occurrenceTiming: z.lazy(() => timingSchema).optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: z.lazy(() => elementSchema).optional(), - requester: z.lazy(() => referenceSchema).optional(), - performerType: z.lazy(() => codeableConceptSchema).optional(), - performer: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - insurance: z - .lazy(() => referenceSchema) - .array() - .optional(), - supportingInfo: z - .lazy(() => referenceSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - relevantHistory: z - .lazy(() => referenceSchema) - .array() - .optional(), + contentString: stringSchema.optional(), + _contentString: z.lazy(() => elementSchema).optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ - { names: ['codeReference', 'codeCodeableConcept'], required: true }, { - names: [ - 'occurrenceDateTime', - 'occurrencePeriod', - 'occurrenceTiming', + alternatives: [ + ['contentString', '_contentString'], + ['contentAttachment'], + ['contentReference'], ], - required: false, + required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -9752,14 +9875,18 @@ export const deviceRequestSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const deviceUseStatementSchema: z.ZodType = +export const communicationSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('DeviceUseStatement'), + resourceType: z.literal('Communication'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -9771,248 +9898,252 @@ export const deviceUseStatementSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) + .optional(), + instantiatesCanonical: canonicalSchema + .nullable() + .array() + .min(1) + .optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) .optional(), basedOn: z .lazy(() => referenceSchema) .array() + .min(1) + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + inResponseTo: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', + ]) .optional(), - status: z.enum([ - 'active', - 'completed', - 'entered-in-error', - 'intended', - 'on-hold', - 'stopped', - ]), _status: z.lazy(() => elementSchema).optional(), - subject: z.lazy(() => referenceSchema), - derivedFrom: z + statusReason: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + medium: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + subject: z.lazy(() => referenceSchema).optional(), + topic: z.lazy(() => codeableConceptSchema).optional(), + about: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - timingTiming: z.lazy(() => timingSchema).optional(), - timingPeriod: z.lazy(() => periodSchema).optional(), - timingDateTime: dateTimeSchema.optional(), - _timingDateTime: z.lazy(() => elementSchema).optional(), - recordedOn: dateTimeSchema.optional(), - _recordedOn: z.lazy(() => elementSchema).optional(), - source: z.lazy(() => referenceSchema).optional(), - device: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + sent: dateTimeSchema.optional(), + _sent: z.lazy(() => elementSchema).optional(), + received: dateTimeSchema.optional(), + _received: z.lazy(() => elementSchema).optional(), + recipient: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + sender: z.lazy(() => referenceSchema).optional(), reasonCode: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), reasonReference: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), + payload: communicationPayloadSchema.array().min(1).optional(), note: z .lazy(() => annotationSchema) .array() + .min(1) .optional(), }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) .refine( - (value) => - [ - { - names: ['timingTiming', 'timingPeriod', 'timingDateTime'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const diagnosticReportMediaSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const communicationRequestPayloadSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - link: z.lazy(() => referenceSchema), - }), - ) - -export const diagnosticReportSchema: z.ZodType = z.lazy( - () => - z - .strictObject({ - resourceType: z.literal('DiagnosticReport'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'amended', - 'appended', - 'cancelled', - 'corrected', - 'entered-in-error', - 'final', - 'partial', - 'preliminary', - 'registered', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - effectiveDateTime: dateTimeSchema.optional(), - _effectiveDateTime: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - issued: instantSchema.optional(), - _issued: z.lazy(() => elementSchema).optional(), - performer: z - .lazy(() => referenceSchema) - .array() - .optional(), - resultsInterpreter: z - .lazy(() => referenceSchema) - .array() - .optional(), - specimen: z - .lazy(() => referenceSchema) - .array() - .optional(), - result: z - .lazy(() => referenceSchema) - .array() - .optional(), - imagingStudy: z - .lazy(() => referenceSchema) - .array() - .optional(), - media: diagnosticReportMediaSchema.array().optional(), - conclusion: stringSchema.optional(), - _conclusion: z.lazy(() => elementSchema).optional(), - conclusionCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - presentedForm: z - .lazy(() => attachmentSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['effectiveDateTime', 'effectivePeriod'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( + contentString: stringSchema.optional(), + _contentString: z.lazy(() => elementSchema).optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['contentString', '_contentString'], + ['contentAttachment'], + ['contentReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const distanceSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - comparator: z.enum(['<', '<=', '>', '>=']).optional(), - _comparator: z.lazy(() => elementSchema).optional(), - unit: stringSchema.optional(), - _unit: z.lazy(() => elementSchema).optional(), - system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - }), -) - -export const documentManifestRelatedSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - ref: z.lazy(() => referenceSchema).optional(), - }), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const documentManifestSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('DocumentManifest'), +export const communicationRequestSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CommunicationRequest'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -10024,254 +10155,561 @@ export const documentManifestSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - masterIdentifier: z.lazy(() => identifierSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - status: z.enum(['current', 'entered-in-error', 'superseded']), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - created: dateTimeSchema.optional(), - _created: z.lazy(() => elementSchema).optional(), - author: z + basedOn: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - recipient: z + replaces: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - source: uriSchema.optional(), - _source: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - content: z + groupIdentifier: z.lazy(() => identifierSchema).optional(), + status: z + .enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + medium: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + subject: z.lazy(() => referenceSchema).optional(), + about: z .lazy(() => referenceSchema) .array() - .min(1), - related: documentManifestRelatedSchema.array().optional(), - }), -) - -export const documentReferenceRelatesToSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + .min(1) + .optional(), + encounter: z.lazy(() => referenceSchema).optional(), + payload: communicationRequestPayloadSchema.array().min(1).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + recipient: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + sender: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - code: z.enum(['appends', 'replaces', 'signs', 'transforms']), - _code: z.lazy(() => elementSchema).optional(), - target: z.lazy(() => referenceSchema), - }), - ) - -export const documentReferenceContentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + reasonReference: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - attachment: z.lazy(() => attachmentSchema), - format: z.lazy(() => codingSchema).optional(), - }), - ) + }) + .refine( + (value) => + [ + { + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const documentReferenceContextSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const compartmentDefinitionResourceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - encounter: z - .lazy(() => referenceSchema) - .array() - .optional(), - event: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - period: z.lazy(() => periodSchema).optional(), - facilityType: z.lazy(() => codeableConceptSchema).optional(), - practiceSetting: z.lazy(() => codeableConceptSchema).optional(), - sourcePatientInfo: z.lazy(() => referenceSchema).optional(), - related: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), - ) - -export const documentReferenceSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('DocumentReference'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), + code: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .optional(), + _code: z.lazy(() => elementSchema).optional(), + param: stringSchema.nullable().array().min(1).optional(), + _param: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'param', shadow: '_param', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const compartmentDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CompartmentDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), language: codeSchema.optional(), _language: z.lazy(() => elementSchema).optional(), text: z.lazy(() => narrativeSchema).optional(), contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - masterIdentifier: z.lazy(() => identifierSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['current', 'entered-in-error', 'superseded']), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), _status: z.lazy(() => elementSchema).optional(), - docStatus: z - .enum(['amended', 'entered-in-error', 'final', 'preliminary']) - .optional(), - _docStatus: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - subject: z.lazy(() => referenceSchema).optional(), - date: instantSchema.optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), _date: z.lazy(() => elementSchema).optional(), - author: z - .lazy(() => referenceSchema) + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - authenticator: z.lazy(() => referenceSchema).optional(), - custodian: z.lazy(() => referenceSchema).optional(), - relatesTo: documentReferenceRelatesToSchema.array().optional(), - description: stringSchema.optional(), + description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - securityLabel: z - .lazy(() => codeableConceptSchema) + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - content: documentReferenceContentSchema.array().min(1), - context: documentReferenceContextSchema.optional(), - }), -) - -export const dosageDoseAndRateSchema: z.ZodType = z.lazy( - () => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - doseRange: z.lazy(() => rangeSchema).optional(), - doseQuantity: z.lazy(() => quantitySchema).optional(), - rateRatio: z.lazy(() => ratioSchema).optional(), - rateRange: z.lazy(() => rangeSchema).optional(), - rateQuantity: z.lazy(() => quantitySchema).optional(), - }) - .refine( - (value) => - [ - { names: ['doseRange', 'doseQuantity'], required: false }, - { - names: ['rateRatio', 'rateRange', 'rateQuantity'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + code: z + .enum([ + 'Device', + 'Encounter', + 'Patient', + 'Practitioner', + 'RelatedPerson', + ]) + .optional(), + _code: z.lazy(() => elementSchema).optional(), + search: booleanSchema.optional(), + _search: z.lazy(() => elementSchema).optional(), + resource: compartmentDefinitionResourceSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + { name: 'code', shadow: '_code' }, + { name: 'search', shadow: '_search' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const dosageSchema: z.ZodType = z.lazy(() => +export const compositionAttesterSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - id: z.string().optional(), + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: integerSchema.optional(), - _sequence: z.lazy(() => elementSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - additionalInstruction: z - .lazy(() => codeableConceptSchema) + mode: z + .enum(['legal', 'official', 'personal', 'professional']) + .optional(), + _mode: z.lazy(() => elementSchema).optional(), + time: dateTimeSchema.optional(), + _time: z.lazy(() => elementSchema).optional(), + party: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'mode', shadow: '_mode' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const compositionRelatesToSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - patientInstruction: stringSchema.optional(), - _patientInstruction: z.lazy(() => elementSchema).optional(), - timing: z.lazy(() => timingSchema).optional(), - asNeededBoolean: booleanSchema.optional(), - _asNeededBoolean: z.lazy(() => elementSchema).optional(), - asNeededCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - site: z.lazy(() => codeableConceptSchema).optional(), - route: z.lazy(() => codeableConceptSchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - doseAndRate: dosageDoseAndRateSchema.array().optional(), - maxDosePerPeriod: z.lazy(() => ratioSchema).optional(), - maxDosePerAdministration: z.lazy(() => quantitySchema).optional(), - maxDosePerLifetime: z.lazy(() => quantitySchema).optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.enum(['appends', 'replaces', 'signs', 'transforms']).optional(), + _code: z.lazy(() => elementSchema).optional(), + targetIdentifier: z.lazy(() => identifierSchema).optional(), + targetReference: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ { - names: ['asNeededBoolean', 'asNeededCodeableConcept'], - required: false, + alternatives: [['targetIdentifier'], ['targetReference']], + required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -10279,894 +10717,523 @@ export const dosageSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const durationSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - comparator: z.enum(['<', '<=', '>', '>=']).optional(), - _comparator: z.lazy(() => elementSchema).optional(), - unit: stringSchema.optional(), - _unit: z.lazy(() => elementSchema).optional(), - system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - }), -) - -export const elementSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - }), -) - -export const elementDefinitionSlicingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - discriminator: elementDefinitionSlicingDiscriminatorSchema - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - ordered: booleanSchema.optional(), - _ordered: z.lazy(() => elementSchema).optional(), - rules: z.enum(['closed', 'open', 'openAtEnd']), - _rules: z.lazy(() => elementSchema).optional(), - }), - ) +) as unknown as z.ZodType> -export const elementDefinitionBaseSchema: z.ZodType = +export const compositionEventSchema: z.ZodType> = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - path: stringSchema, - _path: z.lazy(() => elementSchema).optional(), - min: unsignedIntSchema, - _min: z.lazy(() => elementSchema).optional(), - max: stringSchema, - _max: z.lazy(() => elementSchema).optional(), - }), - ) + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + detail: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const elementDefinitionTypeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const compositionSectionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: uriSchema, - _code: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.array().optional(), - _profile: z - .lazy(() => elementSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - targetProfile: canonicalSchema.array().optional(), - _targetProfile: z - .lazy(() => elementSchema) + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + author: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - aggregation: z - .enum(['bundled', 'contained', 'referenced']) + focus: z.lazy(() => referenceSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + mode: z.enum(['changes', 'snapshot', 'working']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + orderedBy: z.lazy(() => codeableConceptSchema).optional(), + entry: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - _aggregation: z - .lazy(() => elementSchema) + emptyReason: z.lazy(() => codeableConceptSchema).optional(), + section: z + .lazy(() => compositionSectionSchema) .array() + .min(1) .optional(), - versioning: z.enum(['either', 'independent', 'specific']).optional(), - _versioning: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const elementDefinitionExampleSchema: z.ZodType = - z.lazy(() => +export const compositionSchema: z.ZodType> = z.lazy( + () => z .strictObject({ - id: stringSchema.optional(), + resourceType: z.literal('Composition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - label: stringSchema, - _label: z.lazy(() => elementSchema).optional(), - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueCanonical: canonicalSchema.optional(), - _valueCanonical: z.lazy(() => elementSchema).optional(), - valueCode: codeSchema.optional(), - _valueCode: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueId: idSchema.optional(), - _valueId: z.lazy(() => elementSchema).optional(), - valueInstant: instantSchema.optional(), - _valueInstant: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueMarkdown: markdownSchema.optional(), - _valueMarkdown: z.lazy(() => elementSchema).optional(), - valueOid: oidSchema.optional(), - _valueOid: z.lazy(() => elementSchema).optional(), - valuePositiveInt: positiveIntSchema.optional(), - _valuePositiveInt: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueUnsignedInt: unsignedIntSchema.optional(), - _valueUnsignedInt: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueUrl: urlSchema.optional(), - _valueUrl: z.lazy(() => elementSchema).optional(), - valueUuid: uuidSchema.optional(), - _valueUuid: z.lazy(() => elementSchema).optional(), - valueAddress: z.lazy(() => addressSchema).optional(), - valueAge: z.lazy(() => ageSchema).optional(), - valueAnnotation: z.lazy(() => annotationSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueCodeableReference: z - .lazy(() => codeableReferenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) .optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueContactPoint: z.lazy(() => contactPointSchema).optional(), - valueCount: z.lazy(() => countSchema).optional(), - valueDistance: z.lazy(() => distanceSchema).optional(), - valueDuration: z.lazy(() => durationSchema).optional(), - valueHumanName: z.lazy(() => humanNameSchema).optional(), - valueIdentifier: z.lazy(() => identifierSchema).optional(), - valueMoney: z.lazy(() => moneySchema).optional(), - valuePeriod: z.lazy(() => periodSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueRatio: z.lazy(() => ratioSchema).optional(), - valueRatioRange: z.lazy(() => ratioRangeSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - valueSampledData: z.lazy(() => sampledDataSchema).optional(), - valueSignature: z.lazy(() => signatureSchema).optional(), - valueTiming: z.lazy(() => timingSchema).optional(), - valueContactDetail: z.lazy(() => contactDetailSchema).optional(), - valueContributor: z.lazy(() => contributorSchema).optional(), - valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), - valueExpression: z.lazy(() => expressionSchema).optional(), - valueParameterDefinition: z - .lazy(() => parameterDefinitionSchema) + identifier: z.lazy(() => identifierSchema).optional(), + status: z + .enum(['amended', 'entered-in-error', 'final', 'preliminary']) .optional(), - valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), - valueTriggerDefinition: z - .lazy(() => triggerDefinitionSchema) + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) .optional(), - valueUsageContext: z.lazy(() => usageContextSchema).optional(), - valueDosage: z.lazy(() => dosageSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + author: z + .lazy(() => referenceSchema) + .array() + .min(1), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + confidentiality: codeSchema.optional(), + _confidentiality: z.lazy(() => elementSchema).optional(), + attester: compositionAttesterSchema.array().min(1).optional(), + custodian: z.lazy(() => referenceSchema).optional(), + relatesTo: compositionRelatesToSchema.array().min(1).optional(), + event: compositionEventSchema.array().min(1).optional(), + section: compositionSectionSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'date', shadow: '_date' }, + { name: 'title', shadow: '_title' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: [ - 'valueBase64Binary', - 'valueBoolean', - 'valueCanonical', - 'valueCode', - 'valueDate', - 'valueDateTime', - 'valueDecimal', - 'valueId', - 'valueInstant', - 'valueInteger', - 'valueMarkdown', - 'valueOid', - 'valuePositiveInt', - 'valueString', - 'valueTime', - 'valueUnsignedInt', - 'valueUri', - 'valueUrl', - 'valueUuid', - 'valueAddress', - 'valueAge', - 'valueAnnotation', - 'valueAttachment', - 'valueCodeableConcept', - 'valueCodeableReference', - 'valueCoding', - 'valueContactPoint', - 'valueCount', - 'valueDistance', - 'valueDuration', - 'valueHumanName', - 'valueIdentifier', - 'valueMoney', - 'valuePeriod', - 'valueQuantity', - 'valueRange', - 'valueRatio', - 'valueRatioRange', - 'valueReference', - 'valueSampledData', - 'valueSignature', - 'valueTiming', - 'valueContactDetail', - 'valueContributor', - 'valueDataRequirement', - 'valueExpression', - 'valueParameterDefinition', - 'valueRelatedArtifact', - 'valueTriggerDefinition', - 'valueUsageContext', - 'valueDosage', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> -export const elementDefinitionConstraintSchema: z.ZodType = +export const conceptMapGroupSchema: z.ZodType> = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - key: idSchema, - _key: z.lazy(() => elementSchema).optional(), - requirements: stringSchema.optional(), - _requirements: z.lazy(() => elementSchema).optional(), - severity: z.enum(['error', 'warning']), - _severity: z.lazy(() => elementSchema).optional(), - human: stringSchema, - _human: z.lazy(() => elementSchema).optional(), - expression: stringSchema.optional(), - _expression: z.lazy(() => elementSchema).optional(), - xpath: stringSchema.optional(), - _xpath: z.lazy(() => elementSchema).optional(), - source: canonicalSchema.optional(), - _source: z.lazy(() => elementSchema).optional(), - }), - ) + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + source: uriSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + sourceVersion: stringSchema.optional(), + _sourceVersion: z.lazy(() => elementSchema).optional(), + target: uriSchema.optional(), + _target: z.lazy(() => elementSchema).optional(), + targetVersion: stringSchema.optional(), + _targetVersion: z.lazy(() => elementSchema).optional(), + element: conceptMapGroupElementSchema.array().min(1), + unmapped: conceptMapGroupUnmappedSchema.optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const elementDefinitionBindingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const conceptMapGroupElementSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - strength: z.enum(['example', 'extensible', 'preferred', 'required']), - _strength: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - valueSet: canonicalSchema.optional(), - _valueSet: z.lazy(() => elementSchema).optional(), - }), - ) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + target: conceptMapGroupElementTargetSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const elementDefinitionMappingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const conceptMapGroupUnmappedSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identity: idSchema, - _identity: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - map: stringSchema, - _map: z.lazy(() => elementSchema).optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + mode: z.enum(['fixed', 'other-map', 'provided']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + url: canonicalSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'mode', shadow: '_mode' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const conceptMapGroupElementTargetSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + equivalence: z + .enum([ + 'disjoint', + 'equal', + 'equivalent', + 'inexact', + 'narrower', + 'relatedto', + 'specializes', + 'subsumes', + 'unmatched', + 'wider', + ]) + .optional(), + _equivalence: z.lazy(() => elementSchema).optional(), comment: stringSchema.optional(), _comment: z.lazy(() => elementSchema).optional(), - }), - ) + dependsOn: conceptMapGroupElementTargetDependsOnSchema + .array() + .min(1) + .optional(), + product: z + .lazy(() => conceptMapGroupElementTargetDependsOnSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'equivalence', shadow: '_equivalence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const elementDefinitionSlicingDiscriminatorSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const conceptMapGroupElementTargetDependsOnSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum(['exists', 'pattern', 'profile', 'type', 'value']), - _type: z.lazy(() => elementSchema).optional(), - path: stringSchema, - _path: z.lazy(() => elementSchema).optional(), - }), - ) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + property: uriSchema.optional(), + _property: z.lazy(() => elementSchema).optional(), + system: canonicalSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'property', shadow: '_property' }, + { name: 'value', shadow: '_value' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const elementDefinitionSchema: z.ZodType = z.lazy( +export const conceptMapSchema: z.ZodType> = z.lazy( () => z .strictObject({ - id: z.string().optional(), + resourceType: z.literal('ConceptMap'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - path: stringSchema, - _path: z.lazy(() => elementSchema).optional(), - representation: z - .enum(['cdaText', 'typeAttr', 'xhtml', 'xmlAttr', 'xmlText']) - .array() - .optional(), - _representation: z - .lazy(() => elementSchema) - .array() - .optional(), - sliceName: stringSchema.optional(), - _sliceName: z.lazy(() => elementSchema).optional(), - sliceIsConstraining: booleanSchema.optional(), - _sliceIsConstraining: z.lazy(() => elementSchema).optional(), - label: stringSchema.optional(), - _label: z.lazy(() => elementSchema).optional(), - code: z - .lazy(() => codingSchema) + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - slicing: elementDefinitionSlicingSchema.optional(), - short: stringSchema.optional(), - _short: z.lazy(() => elementSchema).optional(), - definition: markdownSchema.optional(), - _definition: z.lazy(() => elementSchema).optional(), - comment: markdownSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - requirements: markdownSchema.optional(), - _requirements: z.lazy(() => elementSchema).optional(), - alias: stringSchema.array().optional(), - _alias: z - .lazy(() => elementSchema) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - min: unsignedIntSchema.optional(), - _min: z.lazy(() => elementSchema).optional(), - max: stringSchema.optional(), - _max: z.lazy(() => elementSchema).optional(), - base: elementDefinitionBaseSchema.optional(), - contentReference: uriSchema.optional(), - _contentReference: z.lazy(() => elementSchema).optional(), - type: elementDefinitionTypeSchema.array().optional(), - defaultValueBase64Binary: base64BinarySchema.optional(), - _defaultValueBase64Binary: z.lazy(() => elementSchema).optional(), - defaultValueBoolean: booleanSchema.optional(), - _defaultValueBoolean: z.lazy(() => elementSchema).optional(), - defaultValueCanonical: canonicalSchema.optional(), - _defaultValueCanonical: z.lazy(() => elementSchema).optional(), - defaultValueCode: codeSchema.optional(), - _defaultValueCode: z.lazy(() => elementSchema).optional(), - defaultValueDate: dateSchema.optional(), - _defaultValueDate: z.lazy(() => elementSchema).optional(), - defaultValueDateTime: dateTimeSchema.optional(), - _defaultValueDateTime: z.lazy(() => elementSchema).optional(), - defaultValueDecimal: decimalSchema.optional(), - _defaultValueDecimal: z.lazy(() => elementSchema).optional(), - defaultValueId: idSchema.optional(), - _defaultValueId: z.lazy(() => elementSchema).optional(), - defaultValueInstant: instantSchema.optional(), - _defaultValueInstant: z.lazy(() => elementSchema).optional(), - defaultValueInteger: integerSchema.optional(), - _defaultValueInteger: z.lazy(() => elementSchema).optional(), - defaultValueMarkdown: markdownSchema.optional(), - _defaultValueMarkdown: z.lazy(() => elementSchema).optional(), - defaultValueOid: oidSchema.optional(), - _defaultValueOid: z.lazy(() => elementSchema).optional(), - defaultValuePositiveInt: positiveIntSchema.optional(), - _defaultValuePositiveInt: z.lazy(() => elementSchema).optional(), - defaultValueString: stringSchema.optional(), - _defaultValueString: z.lazy(() => elementSchema).optional(), - defaultValueTime: timeSchema.optional(), - _defaultValueTime: z.lazy(() => elementSchema).optional(), - defaultValueUnsignedInt: unsignedIntSchema.optional(), - _defaultValueUnsignedInt: z.lazy(() => elementSchema).optional(), - defaultValueUri: uriSchema.optional(), - _defaultValueUri: z.lazy(() => elementSchema).optional(), - defaultValueUrl: urlSchema.optional(), - _defaultValueUrl: z.lazy(() => elementSchema).optional(), - defaultValueUuid: uuidSchema.optional(), - _defaultValueUuid: z.lazy(() => elementSchema).optional(), - defaultValueAddress: z.lazy(() => addressSchema).optional(), - defaultValueAge: z.lazy(() => ageSchema).optional(), - defaultValueAnnotation: z.lazy(() => annotationSchema).optional(), - defaultValueAttachment: z.lazy(() => attachmentSchema).optional(), - defaultValueCodeableConcept: z + jurisdiction: z .lazy(() => codeableConceptSchema) - .optional(), - defaultValueCodeableReference: z - .lazy(() => codeableReferenceSchema) - .optional(), - defaultValueCoding: z.lazy(() => codingSchema).optional(), - defaultValueContactPoint: z.lazy(() => contactPointSchema).optional(), - defaultValueCount: z.lazy(() => countSchema).optional(), - defaultValueDistance: z.lazy(() => distanceSchema).optional(), - defaultValueDuration: z.lazy(() => durationSchema).optional(), - defaultValueHumanName: z.lazy(() => humanNameSchema).optional(), - defaultValueIdentifier: z.lazy(() => identifierSchema).optional(), - defaultValueMoney: z.lazy(() => moneySchema).optional(), - defaultValuePeriod: z.lazy(() => periodSchema).optional(), - defaultValueQuantity: z.lazy(() => quantitySchema).optional(), - defaultValueRange: z.lazy(() => rangeSchema).optional(), - defaultValueRatio: z.lazy(() => ratioSchema).optional(), - defaultValueRatioRange: z.lazy(() => ratioRangeSchema).optional(), - defaultValueReference: z.lazy(() => referenceSchema).optional(), - defaultValueSampledData: z.lazy(() => sampledDataSchema).optional(), - defaultValueSignature: z.lazy(() => signatureSchema).optional(), - defaultValueTiming: z.lazy(() => timingSchema).optional(), - defaultValueContactDetail: z.lazy(() => contactDetailSchema).optional(), - defaultValueContributor: z.lazy(() => contributorSchema).optional(), - defaultValueDataRequirement: z - .lazy(() => dataRequirementSchema) - .optional(), - defaultValueExpression: z.lazy(() => expressionSchema).optional(), - defaultValueParameterDefinition: z - .lazy(() => parameterDefinitionSchema) - .optional(), - defaultValueRelatedArtifact: z - .lazy(() => relatedArtifactSchema) - .optional(), - defaultValueTriggerDefinition: z - .lazy(() => triggerDefinitionSchema) - .optional(), - defaultValueUsageContext: z.lazy(() => usageContextSchema).optional(), - defaultValueDosage: z.lazy(() => dosageSchema).optional(), - meaningWhenMissing: markdownSchema.optional(), - _meaningWhenMissing: z.lazy(() => elementSchema).optional(), - orderMeaning: stringSchema.optional(), - _orderMeaning: z.lazy(() => elementSchema).optional(), - fixedBase64Binary: base64BinarySchema.optional(), - _fixedBase64Binary: z.lazy(() => elementSchema).optional(), - fixedBoolean: booleanSchema.optional(), - _fixedBoolean: z.lazy(() => elementSchema).optional(), - fixedCanonical: canonicalSchema.optional(), - _fixedCanonical: z.lazy(() => elementSchema).optional(), - fixedCode: codeSchema.optional(), - _fixedCode: z.lazy(() => elementSchema).optional(), - fixedDate: dateSchema.optional(), - _fixedDate: z.lazy(() => elementSchema).optional(), - fixedDateTime: dateTimeSchema.optional(), - _fixedDateTime: z.lazy(() => elementSchema).optional(), - fixedDecimal: decimalSchema.optional(), - _fixedDecimal: z.lazy(() => elementSchema).optional(), - fixedId: idSchema.optional(), - _fixedId: z.lazy(() => elementSchema).optional(), - fixedInstant: instantSchema.optional(), - _fixedInstant: z.lazy(() => elementSchema).optional(), - fixedInteger: integerSchema.optional(), - _fixedInteger: z.lazy(() => elementSchema).optional(), - fixedMarkdown: markdownSchema.optional(), - _fixedMarkdown: z.lazy(() => elementSchema).optional(), - fixedOid: oidSchema.optional(), - _fixedOid: z.lazy(() => elementSchema).optional(), - fixedPositiveInt: positiveIntSchema.optional(), - _fixedPositiveInt: z.lazy(() => elementSchema).optional(), - fixedString: stringSchema.optional(), - _fixedString: z.lazy(() => elementSchema).optional(), - fixedTime: timeSchema.optional(), - _fixedTime: z.lazy(() => elementSchema).optional(), - fixedUnsignedInt: unsignedIntSchema.optional(), - _fixedUnsignedInt: z.lazy(() => elementSchema).optional(), - fixedUri: uriSchema.optional(), - _fixedUri: z.lazy(() => elementSchema).optional(), - fixedUrl: urlSchema.optional(), - _fixedUrl: z.lazy(() => elementSchema).optional(), - fixedUuid: uuidSchema.optional(), - _fixedUuid: z.lazy(() => elementSchema).optional(), - fixedAddress: z.lazy(() => addressSchema).optional(), - fixedAge: z.lazy(() => ageSchema).optional(), - fixedAnnotation: z.lazy(() => annotationSchema).optional(), - fixedAttachment: z.lazy(() => attachmentSchema).optional(), - fixedCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - fixedCodeableReference: z - .lazy(() => codeableReferenceSchema) - .optional(), - fixedCoding: z.lazy(() => codingSchema).optional(), - fixedContactPoint: z.lazy(() => contactPointSchema).optional(), - fixedCount: z.lazy(() => countSchema).optional(), - fixedDistance: z.lazy(() => distanceSchema).optional(), - fixedDuration: z.lazy(() => durationSchema).optional(), - fixedHumanName: z.lazy(() => humanNameSchema).optional(), - fixedIdentifier: z.lazy(() => identifierSchema).optional(), - fixedMoney: z.lazy(() => moneySchema).optional(), - fixedPeriod: z.lazy(() => periodSchema).optional(), - fixedQuantity: z.lazy(() => quantitySchema).optional(), - fixedRange: z.lazy(() => rangeSchema).optional(), - fixedRatio: z.lazy(() => ratioSchema).optional(), - fixedRatioRange: z.lazy(() => ratioRangeSchema).optional(), - fixedReference: z.lazy(() => referenceSchema).optional(), - fixedSampledData: z.lazy(() => sampledDataSchema).optional(), - fixedSignature: z.lazy(() => signatureSchema).optional(), - fixedTiming: z.lazy(() => timingSchema).optional(), - fixedContactDetail: z.lazy(() => contactDetailSchema).optional(), - fixedContributor: z.lazy(() => contributorSchema).optional(), - fixedDataRequirement: z.lazy(() => dataRequirementSchema).optional(), - fixedExpression: z.lazy(() => expressionSchema).optional(), - fixedParameterDefinition: z - .lazy(() => parameterDefinitionSchema) - .optional(), - fixedRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), - fixedTriggerDefinition: z - .lazy(() => triggerDefinitionSchema) - .optional(), - fixedUsageContext: z.lazy(() => usageContextSchema).optional(), - fixedDosage: z.lazy(() => dosageSchema).optional(), - patternBase64Binary: base64BinarySchema.optional(), - _patternBase64Binary: z.lazy(() => elementSchema).optional(), - patternBoolean: booleanSchema.optional(), - _patternBoolean: z.lazy(() => elementSchema).optional(), - patternCanonical: canonicalSchema.optional(), - _patternCanonical: z.lazy(() => elementSchema).optional(), - patternCode: codeSchema.optional(), - _patternCode: z.lazy(() => elementSchema).optional(), - patternDate: dateSchema.optional(), - _patternDate: z.lazy(() => elementSchema).optional(), - patternDateTime: dateTimeSchema.optional(), - _patternDateTime: z.lazy(() => elementSchema).optional(), - patternDecimal: decimalSchema.optional(), - _patternDecimal: z.lazy(() => elementSchema).optional(), - patternId: idSchema.optional(), - _patternId: z.lazy(() => elementSchema).optional(), - patternInstant: instantSchema.optional(), - _patternInstant: z.lazy(() => elementSchema).optional(), - patternInteger: integerSchema.optional(), - _patternInteger: z.lazy(() => elementSchema).optional(), - patternMarkdown: markdownSchema.optional(), - _patternMarkdown: z.lazy(() => elementSchema).optional(), - patternOid: oidSchema.optional(), - _patternOid: z.lazy(() => elementSchema).optional(), - patternPositiveInt: positiveIntSchema.optional(), - _patternPositiveInt: z.lazy(() => elementSchema).optional(), - patternString: stringSchema.optional(), - _patternString: z.lazy(() => elementSchema).optional(), - patternTime: timeSchema.optional(), - _patternTime: z.lazy(() => elementSchema).optional(), - patternUnsignedInt: unsignedIntSchema.optional(), - _patternUnsignedInt: z.lazy(() => elementSchema).optional(), - patternUri: uriSchema.optional(), - _patternUri: z.lazy(() => elementSchema).optional(), - patternUrl: urlSchema.optional(), - _patternUrl: z.lazy(() => elementSchema).optional(), - patternUuid: uuidSchema.optional(), - _patternUuid: z.lazy(() => elementSchema).optional(), - patternAddress: z.lazy(() => addressSchema).optional(), - patternAge: z.lazy(() => ageSchema).optional(), - patternAnnotation: z.lazy(() => annotationSchema).optional(), - patternAttachment: z.lazy(() => attachmentSchema).optional(), - patternCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - patternCodeableReference: z - .lazy(() => codeableReferenceSchema) - .optional(), - patternCoding: z.lazy(() => codingSchema).optional(), - patternContactPoint: z.lazy(() => contactPointSchema).optional(), - patternCount: z.lazy(() => countSchema).optional(), - patternDistance: z.lazy(() => distanceSchema).optional(), - patternDuration: z.lazy(() => durationSchema).optional(), - patternHumanName: z.lazy(() => humanNameSchema).optional(), - patternIdentifier: z.lazy(() => identifierSchema).optional(), - patternMoney: z.lazy(() => moneySchema).optional(), - patternPeriod: z.lazy(() => periodSchema).optional(), - patternQuantity: z.lazy(() => quantitySchema).optional(), - patternRange: z.lazy(() => rangeSchema).optional(), - patternRatio: z.lazy(() => ratioSchema).optional(), - patternRatioRange: z.lazy(() => ratioRangeSchema).optional(), - patternReference: z.lazy(() => referenceSchema).optional(), - patternSampledData: z.lazy(() => sampledDataSchema).optional(), - patternSignature: z.lazy(() => signatureSchema).optional(), - patternTiming: z.lazy(() => timingSchema).optional(), - patternContactDetail: z.lazy(() => contactDetailSchema).optional(), - patternContributor: z.lazy(() => contributorSchema).optional(), - patternDataRequirement: z.lazy(() => dataRequirementSchema).optional(), - patternExpression: z.lazy(() => expressionSchema).optional(), - patternParameterDefinition: z - .lazy(() => parameterDefinitionSchema) - .optional(), - patternRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), - patternTriggerDefinition: z - .lazy(() => triggerDefinitionSchema) - .optional(), - patternUsageContext: z.lazy(() => usageContextSchema).optional(), - patternDosage: z.lazy(() => dosageSchema).optional(), - example: elementDefinitionExampleSchema.array().optional(), - minValueDate: dateSchema.optional(), - _minValueDate: z.lazy(() => elementSchema).optional(), - minValueDateTime: dateTimeSchema.optional(), - _minValueDateTime: z.lazy(() => elementSchema).optional(), - minValueInstant: instantSchema.optional(), - _minValueInstant: z.lazy(() => elementSchema).optional(), - minValueTime: timeSchema.optional(), - _minValueTime: z.lazy(() => elementSchema).optional(), - minValueDecimal: decimalSchema.optional(), - _minValueDecimal: z.lazy(() => elementSchema).optional(), - minValueInteger: integerSchema.optional(), - _minValueInteger: z.lazy(() => elementSchema).optional(), - minValuePositiveInt: positiveIntSchema.optional(), - _minValuePositiveInt: z.lazy(() => elementSchema).optional(), - minValueUnsignedInt: unsignedIntSchema.optional(), - _minValueUnsignedInt: z.lazy(() => elementSchema).optional(), - minValueQuantity: z.lazy(() => quantitySchema).optional(), - maxValueDate: dateSchema.optional(), - _maxValueDate: z.lazy(() => elementSchema).optional(), - maxValueDateTime: dateTimeSchema.optional(), - _maxValueDateTime: z.lazy(() => elementSchema).optional(), - maxValueInstant: instantSchema.optional(), - _maxValueInstant: z.lazy(() => elementSchema).optional(), - maxValueTime: timeSchema.optional(), - _maxValueTime: z.lazy(() => elementSchema).optional(), - maxValueDecimal: decimalSchema.optional(), - _maxValueDecimal: z.lazy(() => elementSchema).optional(), - maxValueInteger: integerSchema.optional(), - _maxValueInteger: z.lazy(() => elementSchema).optional(), - maxValuePositiveInt: positiveIntSchema.optional(), - _maxValuePositiveInt: z.lazy(() => elementSchema).optional(), - maxValueUnsignedInt: unsignedIntSchema.optional(), - _maxValueUnsignedInt: z.lazy(() => elementSchema).optional(), - maxValueQuantity: z.lazy(() => quantitySchema).optional(), - maxLength: integerSchema.optional(), - _maxLength: z.lazy(() => elementSchema).optional(), - condition: idSchema.array().optional(), - _condition: z - .lazy(() => elementSchema) .array() + .min(1) .optional(), - constraint: elementDefinitionConstraintSchema.array().optional(), - mustSupport: booleanSchema.optional(), - _mustSupport: z.lazy(() => elementSchema).optional(), - isModifier: booleanSchema.optional(), - _isModifier: z.lazy(() => elementSchema).optional(), - isModifierReason: stringSchema.optional(), - _isModifierReason: z.lazy(() => elementSchema).optional(), - isSummary: booleanSchema.optional(), - _isSummary: z.lazy(() => elementSchema).optional(), - binding: elementDefinitionBindingSchema.optional(), - mapping: elementDefinitionMappingSchema.array().optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + sourceUri: uriSchema.optional(), + _sourceUri: z.lazy(() => elementSchema).optional(), + sourceCanonical: canonicalSchema.optional(), + _sourceCanonical: z.lazy(() => elementSchema).optional(), + targetUri: uriSchema.optional(), + _targetUri: z.lazy(() => elementSchema).optional(), + targetCanonical: canonicalSchema.optional(), + _targetCanonical: z.lazy(() => elementSchema).optional(), + group: conceptMapGroupSchema.array().min(1).optional(), }) .refine( (value) => [ { - names: [ - 'defaultValueBase64Binary', - 'defaultValueBoolean', - 'defaultValueCanonical', - 'defaultValueCode', - 'defaultValueDate', - 'defaultValueDateTime', - 'defaultValueDecimal', - 'defaultValueId', - 'defaultValueInstant', - 'defaultValueInteger', - 'defaultValueMarkdown', - 'defaultValueOid', - 'defaultValuePositiveInt', - 'defaultValueString', - 'defaultValueTime', - 'defaultValueUnsignedInt', - 'defaultValueUri', - 'defaultValueUrl', - 'defaultValueUuid', - 'defaultValueAddress', - 'defaultValueAge', - 'defaultValueAnnotation', - 'defaultValueAttachment', - 'defaultValueCodeableConcept', - 'defaultValueCodeableReference', - 'defaultValueCoding', - 'defaultValueContactPoint', - 'defaultValueCount', - 'defaultValueDistance', - 'defaultValueDuration', - 'defaultValueHumanName', - 'defaultValueIdentifier', - 'defaultValueMoney', - 'defaultValuePeriod', - 'defaultValueQuantity', - 'defaultValueRange', - 'defaultValueRatio', - 'defaultValueRatioRange', - 'defaultValueReference', - 'defaultValueSampledData', - 'defaultValueSignature', - 'defaultValueTiming', - 'defaultValueContactDetail', - 'defaultValueContributor', - 'defaultValueDataRequirement', - 'defaultValueExpression', - 'defaultValueParameterDefinition', - 'defaultValueRelatedArtifact', - 'defaultValueTriggerDefinition', - 'defaultValueUsageContext', - 'defaultValueDosage', - ], - required: false, - }, - { - names: [ - 'fixedBase64Binary', - 'fixedBoolean', - 'fixedCanonical', - 'fixedCode', - 'fixedDate', - 'fixedDateTime', - 'fixedDecimal', - 'fixedId', - 'fixedInstant', - 'fixedInteger', - 'fixedMarkdown', - 'fixedOid', - 'fixedPositiveInt', - 'fixedString', - 'fixedTime', - 'fixedUnsignedInt', - 'fixedUri', - 'fixedUrl', - 'fixedUuid', - 'fixedAddress', - 'fixedAge', - 'fixedAnnotation', - 'fixedAttachment', - 'fixedCodeableConcept', - 'fixedCodeableReference', - 'fixedCoding', - 'fixedContactPoint', - 'fixedCount', - 'fixedDistance', - 'fixedDuration', - 'fixedHumanName', - 'fixedIdentifier', - 'fixedMoney', - 'fixedPeriod', - 'fixedQuantity', - 'fixedRange', - 'fixedRatio', - 'fixedRatioRange', - 'fixedReference', - 'fixedSampledData', - 'fixedSignature', - 'fixedTiming', - 'fixedContactDetail', - 'fixedContributor', - 'fixedDataRequirement', - 'fixedExpression', - 'fixedParameterDefinition', - 'fixedRelatedArtifact', - 'fixedTriggerDefinition', - 'fixedUsageContext', - 'fixedDosage', - ], - required: false, - }, - { - names: [ - 'patternBase64Binary', - 'patternBoolean', - 'patternCanonical', - 'patternCode', - 'patternDate', - 'patternDateTime', - 'patternDecimal', - 'patternId', - 'patternInstant', - 'patternInteger', - 'patternMarkdown', - 'patternOid', - 'patternPositiveInt', - 'patternString', - 'patternTime', - 'patternUnsignedInt', - 'patternUri', - 'patternUrl', - 'patternUuid', - 'patternAddress', - 'patternAge', - 'patternAnnotation', - 'patternAttachment', - 'patternCodeableConcept', - 'patternCodeableReference', - 'patternCoding', - 'patternContactPoint', - 'patternCount', - 'patternDistance', - 'patternDuration', - 'patternHumanName', - 'patternIdentifier', - 'patternMoney', - 'patternPeriod', - 'patternQuantity', - 'patternRange', - 'patternRatio', - 'patternRatioRange', - 'patternReference', - 'patternSampledData', - 'patternSignature', - 'patternTiming', - 'patternContactDetail', - 'patternContributor', - 'patternDataRequirement', - 'patternExpression', - 'patternParameterDefinition', - 'patternRelatedArtifact', - 'patternTriggerDefinition', - 'patternUsageContext', - 'patternDosage', - ], - required: false, - }, - { - names: [ - 'minValueDate', - 'minValueDateTime', - 'minValueInstant', - 'minValueTime', - 'minValueDecimal', - 'minValueInteger', - 'minValuePositiveInt', - 'minValueUnsignedInt', - 'minValueQuantity', + alternatives: [ + ['sourceUri', '_sourceUri'], + ['sourceCanonical', '_sourceCanonical'], ], required: false, }, { - names: [ - 'maxValueDate', - 'maxValueDateTime', - 'maxValueInstant', - 'maxValueTime', - 'maxValueDecimal', - 'maxValueInteger', - 'maxValuePositiveInt', - 'maxValueUnsignedInt', - 'maxValueQuantity', + alternatives: [ + ['targetUri', '_targetUri'], + ['targetCanonical', '_targetCanonical'], ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -11174,299 +11241,416 @@ export const elementDefinitionSchema: z.ZodType = z.lazy( message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), -) +) as unknown as z.ZodType> -export const encounterStatusHistorySchema: z.ZodType = +export const conditionStageSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + summary: z.lazy(() => codeableConceptSchema).optional(), + assessment: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const conditionEvidenceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - status: z.enum([ - 'arrived', - 'cancelled', - 'entered-in-error', - 'finished', - 'in-progress', - 'onleave', - 'planned', - 'triaged', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema), - }), - ) - -export const encounterClassHistorySchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + code: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() + detail: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), - class: z.lazy(() => codingSchema), - period: z.lazy(() => periodSchema), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const encounterParticipantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const conditionSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Condition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + clinicalStatus: z.lazy(() => codeableConceptSchema).optional(), + verificationStatus: z.lazy(() => codeableConceptSchema).optional(), + category: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - period: z.lazy(() => periodSchema).optional(), - individual: z.lazy(() => referenceSchema).optional(), - }), - ) + severity: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + bodySite: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + onsetDateTime: dateTimeSchema.optional(), + _onsetDateTime: z.lazy(() => elementSchema).optional(), + onsetAge: z.lazy(() => ageSchema).optional(), + onsetPeriod: z.lazy(() => periodSchema).optional(), + onsetRange: z.lazy(() => rangeSchema).optional(), + onsetString: stringSchema.optional(), + _onsetString: z.lazy(() => elementSchema).optional(), + abatementDateTime: dateTimeSchema.optional(), + _abatementDateTime: z.lazy(() => elementSchema).optional(), + abatementAge: z.lazy(() => ageSchema).optional(), + abatementPeriod: z.lazy(() => periodSchema).optional(), + abatementRange: z.lazy(() => rangeSchema).optional(), + abatementString: stringSchema.optional(), + _abatementString: z.lazy(() => elementSchema).optional(), + recordedDate: dateTimeSchema.optional(), + _recordedDate: z.lazy(() => elementSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + asserter: z.lazy(() => referenceSchema).optional(), + stage: conditionStageSchema.array().min(1).optional(), + evidence: conditionEvidenceSchema.array().min(1).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['onsetDateTime', '_onsetDateTime'], + ['onsetAge'], + ['onsetPeriod'], + ['onsetRange'], + ['onsetString', '_onsetString'], + ], + required: false, + }, + { + alternatives: [ + ['abatementDateTime', '_abatementDateTime'], + ['abatementAge'], + ['abatementPeriod'], + ['abatementRange'], + ['abatementString', '_abatementString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const encounterDiagnosisSchema: z.ZodType = +export const consentPolicySchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + authority: uriSchema.optional(), + _authority: z.lazy(() => elementSchema).optional(), + uri: uriSchema.optional(), + _uri: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const consentVerificationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - condition: z.lazy(() => referenceSchema), - use: z.lazy(() => codeableConceptSchema).optional(), - rank: positiveIntSchema.optional(), - _rank: z.lazy(() => elementSchema).optional(), - }), - ) + verified: booleanSchema.optional(), + _verified: z.lazy(() => elementSchema).optional(), + verifiedWith: z.lazy(() => referenceSchema).optional(), + verificationDate: dateTimeSchema.optional(), + _verificationDate: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'verified', shadow: '_verified' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const encounterHospitalizationSchema: z.ZodType = +export const consentProvisionSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.enum(['deny', 'permit']).optional(), + _type: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + actor: consentProvisionActorSchema.array().min(1).optional(), + action: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + securityLabel: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + purpose: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + class: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + dataPeriod: z.lazy(() => periodSchema).optional(), + data: consentProvisionDataSchema.array().min(1).optional(), + provision: z + .lazy(() => consentProvisionSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const consentProvisionActorSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - preAdmissionIdentifier: z.lazy(() => identifierSchema).optional(), - origin: z.lazy(() => referenceSchema).optional(), - admitSource: z.lazy(() => codeableConceptSchema).optional(), - reAdmission: z.lazy(() => codeableConceptSchema).optional(), - dietPreference: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - specialCourtesy: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - specialArrangement: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - destination: z.lazy(() => referenceSchema).optional(), - dischargeDisposition: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + role: z.lazy(() => codeableConceptSchema), + reference: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const encounterLocationSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const consentProvisionDataSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - location: z.lazy(() => referenceSchema), - status: z.enum(['active', 'completed', 'planned', 'reserved']).optional(), - _status: z.lazy(() => elementSchema).optional(), - physicalType: z.lazy(() => codeableConceptSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), -) - -export const encounterSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Encounter'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'arrived', - 'cancelled', - 'entered-in-error', - 'finished', - 'in-progress', - 'onleave', - 'planned', - 'triaged', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusHistory: encounterStatusHistorySchema.array().optional(), - class: z.lazy(() => codingSchema), - classHistory: encounterClassHistorySchema.array().optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - serviceType: z.lazy(() => codeableConceptSchema).optional(), - priority: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - episodeOfCare: z - .lazy(() => referenceSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - participant: encounterParticipantSchema.array().optional(), - appointment: z - .lazy(() => referenceSchema) - .array() - .optional(), - period: z.lazy(() => periodSchema).optional(), - length: z.lazy(() => durationSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - diagnosis: encounterDiagnosisSchema.array().optional(), - account: z - .lazy(() => referenceSchema) - .array() - .optional(), - hospitalization: encounterHospitalizationSchema.optional(), - location: encounterLocationSchema.array().optional(), - serviceProvider: z.lazy(() => referenceSchema).optional(), - partOf: z.lazy(() => referenceSchema).optional(), - }), -) - -export const endpointSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Endpoint'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'entered-in-error', - 'error', - 'off', - 'suspended', - 'test', - ]), - _status: z.lazy(() => elementSchema).optional(), - connectionType: z.lazy(() => codingSchema), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - managingOrganization: z.lazy(() => referenceSchema).optional(), - contact: z - .lazy(() => contactPointSchema) - .array() - .optional(), - period: z.lazy(() => periodSchema).optional(), - payloadType: z - .lazy(() => codeableConceptSchema) - .array() - .min(1), - payloadMimeType: codeSchema.array().optional(), - _payloadMimeType: z - .lazy(() => elementSchema) - .array() - .optional(), - address: urlSchema, - _address: z.lazy(() => elementSchema).optional(), - header: stringSchema.array().optional(), - _header: z - .lazy(() => elementSchema) - .array() - .optional(), - }), -) - -export const enrollmentRequestSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('EnrollmentRequest'), + meaning: z + .enum(['authoredby', 'dependents', 'instance', 'related']) + .optional(), + _meaning: z.lazy(() => elementSchema).optional(), + reference: z.lazy(() => referenceSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'meaning', shadow: '_meaning' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const consentSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Consent'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -11478,296 +11662,353 @@ export const enrollmentRequestSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), status: z - .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .enum([ + 'active', + 'draft', + 'entered-in-error', + 'inactive', + 'proposed', + 'rejected', + ]) .optional(), _status: z.lazy(() => elementSchema).optional(), - created: dateTimeSchema.optional(), - _created: z.lazy(() => elementSchema).optional(), - insurer: z.lazy(() => referenceSchema).optional(), - provider: z.lazy(() => referenceSchema).optional(), - candidate: z.lazy(() => referenceSchema).optional(), - coverage: z.lazy(() => referenceSchema).optional(), - }), -) - -export const enrollmentResponseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('EnrollmentResponse'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) + scope: z.lazy(() => codeableConceptSchema), + category: z + .lazy(() => codeableConceptSchema) .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + .min(1), + patient: z.lazy(() => referenceSchema).optional(), + dateTime: dateTimeSchema.optional(), + _dateTime: z.lazy(() => elementSchema).optional(), + performer: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) + organization: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - status: z - .enum(['active', 'cancelled', 'draft', 'entered-in-error']) - .optional(), - _status: z.lazy(() => elementSchema).optional(), - request: z.lazy(() => referenceSchema).optional(), - outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), - _outcome: z.lazy(() => elementSchema).optional(), - disposition: stringSchema.optional(), - _disposition: z.lazy(() => elementSchema).optional(), - created: dateTimeSchema.optional(), - _created: z.lazy(() => elementSchema).optional(), - organization: z.lazy(() => referenceSchema).optional(), - requestProvider: z.lazy(() => referenceSchema).optional(), - }), - ) + sourceAttachment: z.lazy(() => attachmentSchema).optional(), + sourceReference: z.lazy(() => referenceSchema).optional(), + policy: consentPolicySchema.array().min(1).optional(), + policyRule: z.lazy(() => codeableConceptSchema).optional(), + verification: consentVerificationSchema.array().min(1).optional(), + provision: consentProvisionSchema.optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['sourceAttachment'], ['sourceReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const episodeOfCareStatusHistorySchema: z.ZodType = +export const contactDetailSchema: z.ZodType> = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'cancelled', - 'entered-in-error', - 'finished', - 'onhold', - 'planned', - 'waitlist', - ]), - _status: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema), - }), - ) + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const episodeOfCareDiagnosisSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const contactPointSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + system: z + .enum(['email', 'fax', 'other', 'pager', 'phone', 'sms', 'url']) + .optional(), + _system: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + use: z.enum(['home', 'mobile', 'old', 'temp', 'work']).optional(), + _use: z.lazy(() => elementSchema).optional(), + rank: positiveIntSchema.optional(), + _rank: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const contractContentDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - condition: z.lazy(() => referenceSchema), - role: z.lazy(() => codeableConceptSchema).optional(), - rank: positiveIntSchema.optional(), - _rank: z.lazy(() => elementSchema).optional(), - }), - ) + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + publisher: z.lazy(() => referenceSchema).optional(), + publicationDate: dateTimeSchema.optional(), + _publicationDate: z.lazy(() => elementSchema).optional(), + publicationStatus: z + .enum([ + 'amended', + 'appended', + 'cancelled', + 'disputed', + 'entered-in-error', + 'executable', + 'executed', + 'negotiable', + 'offered', + 'policy', + 'rejected', + 'renewed', + 'resolved', + 'revoked', + 'terminated', + ]) + .optional(), + _publicationStatus: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'publicationStatus', shadow: '_publicationStatus' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const episodeOfCareSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('EpisodeOfCare'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'cancelled', - 'entered-in-error', - 'finished', - 'onhold', - 'planned', - 'waitlist', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusHistory: episodeOfCareStatusHistorySchema.array().optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - diagnosis: episodeOfCareDiagnosisSchema.array().optional(), - patient: z.lazy(() => referenceSchema), - managingOrganization: z.lazy(() => referenceSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - referralRequest: z - .lazy(() => referenceSchema) - .array() - .optional(), - careManager: z.lazy(() => referenceSchema).optional(), - team: z - .lazy(() => referenceSchema) - .array() - .optional(), - account: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) - -export const eventDefinitionSchema: z.ZodType = z.lazy( +export const contractTermSchema: z.ZodType> = z.lazy( () => z .strictObject({ - resourceType: z.literal('EventDefinition'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) + identifier: z.lazy(() => identifierSchema).optional(), + issued: dateTimeSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + applies: z.lazy(() => periodSchema).optional(), + topicCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + topicReference: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subType: z.lazy(() => codeableConceptSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + securityLabel: contractTermSecurityLabelSchema .array() + .min(1) .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z - .lazy(() => codeableConceptSchema) + offer: contractTermOfferSchema, + asset: contractTermAssetSchema.array().min(1).optional(), + action: contractTermActionSchema.array().min(1).optional(), + group: z + .lazy(() => contractTermSchema) .array() + .min(1) .optional(), - author: z - .lazy(() => contactDetailSchema) + }) + .refine( + (value) => + [ + { + alternatives: [['topicCodeableConcept'], ['topicReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const contractSignerSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - editor: z - .lazy(() => contactDetailSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - reviewer: z - .lazy(() => contactDetailSchema) + type: z.lazy(() => codingSchema), + party: z.lazy(() => referenceSchema), + signature: z + .lazy(() => signatureSchema) .array() - .optional(), - endorser: z - .lazy(() => contactDetailSchema) + .min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const contractFriendlySchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - trigger: z - .lazy(() => triggerDefinitionSchema) - .array() - .min(1), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ { - names: ['subjectCodeableConcept', 'subjectReference'], - required: false, + alternatives: [['contentAttachment'], ['contentReference']], + required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -11775,307 +12016,561 @@ export const eventDefinitionSchema: z.ZodType = z.lazy( message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, - ), -) + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const evidenceVariableDefinitionSchema: z.ZodType = +export const contractLegalSchema: z.ZodType> = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - variableRole: z.lazy(() => codeableConceptSchema), - observed: z.lazy(() => referenceSchema).optional(), - intended: z.lazy(() => referenceSchema).optional(), - directnessMatch: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['contentAttachment'], ['contentReference']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const evidenceStatisticSchema: z.ZodType = z.lazy( +export const contractRuleSchema: z.ZodType> = z.lazy( () => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + contentAttachment: z.lazy(() => attachmentSchema).optional(), + contentReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['contentAttachment'], ['contentReference']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const contractTermSecurityLabelSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) + number: unsignedIntSchema.nullable().array().min(1).optional(), + _number: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - statisticType: z.lazy(() => codeableConceptSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - numberOfEvents: unsignedIntSchema.optional(), - _numberOfEvents: z.lazy(() => elementSchema).optional(), - numberAffected: unsignedIntSchema.optional(), - _numberAffected: z.lazy(() => elementSchema).optional(), - sampleSize: evidenceStatisticSampleSizeSchema.optional(), - attributeEstimate: evidenceStatisticAttributeEstimateSchema + classification: z.lazy(() => codingSchema), + category: z + .lazy(() => codingSchema) .array() + .min(1) .optional(), - modelCharacteristic: evidenceStatisticModelCharacteristicSchema + control: z + .lazy(() => codingSchema) .array() + .min(1) .optional(), - }), -) + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'number', shadow: '_number', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const evidenceCertaintySchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const contractTermOfferSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), + party: contractTermOfferPartySchema.array().min(1).optional(), + topic: z.lazy(() => referenceSchema).optional(), type: z.lazy(() => codeableConceptSchema).optional(), - rating: z.lazy(() => codeableConceptSchema).optional(), - rater: stringSchema.optional(), - _rater: z.lazy(() => elementSchema).optional(), - subcomponent: z - .lazy(() => evidenceCertaintySchema) + decision: z.lazy(() => codeableConceptSchema).optional(), + decisionMode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - }), -) - -export const evidenceStatisticSampleSizeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + answer: contractTermOfferAnswerSchema.array().min(1).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + linkId: stringSchema.nullable().array().min(1).optional(), + _linkId: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + securityLabelNumber: unsignedIntSchema + .nullable() .array() + .min(1) .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) + _securityLabelNumber: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - numberOfStudies: unsignedIntSchema.optional(), - _numberOfStudies: z.lazy(() => elementSchema).optional(), - numberOfParticipants: unsignedIntSchema.optional(), - _numberOfParticipants: z.lazy(() => elementSchema).optional(), - knownDataCount: unsignedIntSchema.optional(), - _knownDataCount: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'linkId', shadow: '_linkId', required: false }, + { + name: 'securityLabelNumber', + shadow: '_securityLabelNumber', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const evidenceStatisticAttributeEstimateSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const contractTermAssetSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) + scope: z.lazy(() => codeableConceptSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - level: decimalSchema.optional(), - _level: z.lazy(() => elementSchema).optional(), - range: z.lazy(() => rangeSchema).optional(), - attributeEstimate: z - .lazy(() => evidenceStatisticAttributeEstimateSchema) + typeReference: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - }), - ) - -export const evidenceStatisticModelCharacteristicSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + subtype: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + relationship: z.lazy(() => codingSchema).optional(), + context: contractTermAssetContextSchema.array().min(1).optional(), + condition: stringSchema.optional(), + _condition: z.lazy(() => elementSchema).optional(), + periodType: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema), - value: z.lazy(() => quantitySchema).optional(), - variable: evidenceStatisticModelCharacteristicVariableSchema + period: z + .lazy(() => periodSchema) .array() + .min(1) .optional(), - attributeEstimate: z - .lazy(() => evidenceStatisticAttributeEstimateSchema) + usePeriod: z + .lazy(() => periodSchema) .array() + .min(1) .optional(), - }), - ) + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + linkId: stringSchema.nullable().array().min(1).optional(), + _linkId: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + answer: z + .lazy(() => contractTermOfferAnswerSchema) + .array() + .min(1) + .optional(), + securityLabelNumber: unsignedIntSchema + .nullable() + .array() + .min(1) + .optional(), + _securityLabelNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + valuedItem: contractTermAssetValuedItemSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'linkId', shadow: '_linkId', required: false }, + { + name: 'securityLabelNumber', + shadow: '_securityLabelNumber', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const evidenceStatisticModelCharacteristicVariableSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const contractTermActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - variableDefinition: z.lazy(() => referenceSchema), - handling: z - .enum(['continuous', 'dichotomous', 'ordinal', 'polychotomous']) - .optional(), - _handling: z.lazy(() => elementSchema).optional(), - valueCategory: z - .lazy(() => codeableConceptSchema) + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subject: contractTermActionSubjectSchema.array().min(1).optional(), + intent: z.lazy(() => codeableConceptSchema), + linkId: stringSchema.nullable().array().min(1).optional(), + _linkId: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - valueQuantity: z - .lazy(() => quantitySchema) - .array() - .optional(), - valueRange: z - .lazy(() => rangeSchema) - .array() - .optional(), - }), - ) - -export const evidenceSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - resourceType: z.literal('Evidence'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + status: z.lazy(() => codeableConceptSchema), + context: z.lazy(() => referenceSchema).optional(), + contextLinkId: stringSchema.nullable().array().min(1).optional(), + _contextLinkId: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - extension: z - .lazy(() => extensionSchema) + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + requester: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + requesterLinkId: stringSchema.nullable().array().min(1).optional(), + _requesterLinkId: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) + performerType: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - citeAsReference: z.lazy(() => referenceSchema).optional(), - citeAsMarkdown: markdownSchema.optional(), - _citeAsMarkdown: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) + performerRole: z.lazy(() => codeableConceptSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + performerLinkId: stringSchema.nullable().array().min(1).optional(), + _performerLinkId: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) + reasonCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - author: z - .lazy(() => contactDetailSchema) + reasonReference: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - editor: z - .lazy(() => contactDetailSchema) + reason: stringSchema.nullable().array().min(1).optional(), + _reason: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - reviewer: z - .lazy(() => contactDetailSchema) + reasonLinkId: stringSchema.nullable().array().min(1).optional(), + _reasonLinkId: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - endorser: z - .lazy(() => contactDetailSchema) + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) + securityLabelNumber: unsignedIntSchema + .nullable() .array() + .min(1) .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - assertion: markdownSchema.optional(), - _assertion: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) + _securityLabelNumber: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - variableDefinition: evidenceVariableDefinitionSchema.array().min(1), - synthesisType: z.lazy(() => codeableConceptSchema).optional(), - studyType: z.lazy(() => codeableConceptSchema).optional(), - statistic: evidenceStatisticSchema.array().optional(), - certainty: evidenceCertaintySchema.array().optional(), }) .refine( (value) => [ - { names: ['citeAsReference', 'citeAsMarkdown'], required: false }, + { + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], + ['occurrenceTiming'], + ], + required: false, + }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -12083,173 +12578,372 @@ export const evidenceSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'linkId', shadow: '_linkId', required: false }, + { name: 'contextLinkId', shadow: '_contextLinkId', required: false }, + { + name: 'requesterLinkId', + shadow: '_requesterLinkId', + required: false, + }, + { + name: 'performerLinkId', + shadow: '_performerLinkId', + required: false, + }, + { name: 'reason', shadow: '_reason', required: false }, + { name: 'reasonLinkId', shadow: '_reasonLinkId', required: false }, + { + name: 'securityLabelNumber', + shadow: '_securityLabelNumber', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const evidenceReportSubjectSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const contractTermOfferPartySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - characteristic: evidenceReportSubjectCharacteristicSchema + reference: z + .lazy(() => referenceSchema) .array() + .min(1), + role: z.lazy(() => codeableConceptSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const contractTermOfferAnswerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) .optional(), - note: z - .lazy(() => annotationSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), - ) - -export const evidenceReportRelatesToSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.enum([ - 'amendedWith', - 'amends', - 'appendedWith', - 'appends', - 'replacedWith', - 'replaces', - 'transformedWith', - 'transforms', - ]), - _code: z.lazy(() => elementSchema).optional(), - targetIdentifier: z.lazy(() => identifierSchema).optional(), - targetReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['targetIdentifier', 'targetReference'], required: true }, - ].every((group) => { - const present = group.names.filter( + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueBoolean', '_valueBoolean'], + ['valueDecimal', '_valueDecimal'], + ['valueInteger', '_valueInteger'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueTime', '_valueTime'], + ['valueString', '_valueString'], + ['valueUri', '_valueUri'], + ['valueAttachment'], + ['valueCoding'], + ['valueQuantity'], + ['valueReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const evidenceReportSectionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const contractTermAssetContextSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - focus: z.lazy(() => codeableConceptSchema).optional(), - focusReference: z.lazy(() => referenceSchema).optional(), - author: z - .lazy(() => referenceSchema) + reference: z.lazy(() => referenceSchema).optional(), + code: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - text: z.lazy(() => narrativeSchema).optional(), - mode: z.enum(['changes', 'snapshot', 'working']).optional(), - _mode: z.lazy(() => elementSchema).optional(), - orderedBy: z.lazy(() => codeableConceptSchema).optional(), - entryClassifier: z - .lazy(() => codeableConceptSchema) + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const contractTermAssetValuedItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - entryReference: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - entryQuantity: z - .lazy(() => quantitySchema) + entityCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + entityReference: z.lazy(() => referenceSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + effectiveTime: dateTimeSchema.optional(), + _effectiveTime: z.lazy(() => elementSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + points: decimalSchema.optional(), + _points: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + payment: stringSchema.optional(), + _payment: z.lazy(() => elementSchema).optional(), + paymentDate: dateTimeSchema.optional(), + _paymentDate: z.lazy(() => elementSchema).optional(), + responsible: z.lazy(() => referenceSchema).optional(), + recipient: z.lazy(() => referenceSchema).optional(), + linkId: stringSchema.nullable().array().min(1).optional(), + _linkId: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - emptyReason: z.lazy(() => codeableConceptSchema).optional(), - section: z - .lazy(() => evidenceReportSectionSchema) + securityLabelNumber: unsignedIntSchema + .nullable() .array() + .min(1) .optional(), - }), - ) - -export const evidenceReportSubjectCharacteristicSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - valueReference: z.lazy(() => referenceSchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - exclude: booleanSchema.optional(), - _exclude: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueReference', - 'valueCodeableConcept', - 'valueBoolean', - 'valueQuantity', - 'valueRange', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( + _securityLabelNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['entityCodeableConcept'], ['entityReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'linkId', shadow: '_linkId', required: false }, { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + name: 'securityLabelNumber', + shadow: '_securityLabelNumber', + required: false, }, - ), - ) + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const contractTermActionSubjectSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + reference: z + .lazy(() => referenceSchema) + .array() + .min(1), + role: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const evidenceReportSchema: z.ZodType = z.lazy(() => +export const contractSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('EvidenceReport'), + resourceType: z.literal('Contract'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -12261,76 +12955,137 @@ export const evidenceReportSchema: z.ZodType = z.lazy(() => contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - relatedIdentifier: z - .lazy(() => identifierSchema) + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + status: z + .enum([ + 'amended', + 'appended', + 'cancelled', + 'disputed', + 'entered-in-error', + 'executable', + 'executed', + 'negotiable', + 'offered', + 'policy', + 'rejected', + 'renewed', + 'resolved', + 'revoked', + 'terminated', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + legalState: z.lazy(() => codeableConceptSchema).optional(), + instantiatesCanonical: z.lazy(() => referenceSchema).optional(), + instantiatesUri: uriSchema.optional(), + _instantiatesUri: z.lazy(() => elementSchema).optional(), + contentDerivative: z.lazy(() => codeableConceptSchema).optional(), + issued: dateTimeSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + applies: z.lazy(() => periodSchema).optional(), + expirationType: z.lazy(() => codeableConceptSchema).optional(), + subject: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - citeAsReference: z.lazy(() => referenceSchema).optional(), - citeAsMarkdown: markdownSchema.optional(), - _citeAsMarkdown: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - note: z - .lazy(() => annotationSchema) + authority: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) + domain: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - subject: evidenceReportSubjectSchema, - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) + site: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - author: z - .lazy(() => contactDetailSchema) + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + alias: stringSchema.nullable().array().min(1).optional(), + _alias: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - editor: z - .lazy(() => contactDetailSchema) + author: z.lazy(() => referenceSchema).optional(), + scope: z.lazy(() => codeableConceptSchema).optional(), + topicCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + topicReference: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subType: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - reviewer: z - .lazy(() => contactDetailSchema) + contentDefinition: contractContentDefinitionSchema.optional(), + term: contractTermSchema.array().min(1).optional(), + supportingInfo: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - endorser: z - .lazy(() => contactDetailSchema) + relevantHistory: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - relatesTo: evidenceReportRelatesToSchema.array().optional(), - section: evidenceReportSectionSchema.array().optional(), + signer: contractSignerSchema.array().min(1).optional(), + friendly: contractFriendlySchema.array().min(1).optional(), + legal: contractLegalSchema.array().min(1).optional(), + rule: contractRuleSchema.array().min(1).optional(), + legallyBindingAttachment: z.lazy(() => attachmentSchema).optional(), + legallyBindingReference: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ - { names: ['citeAsReference', 'citeAsMarkdown'], required: false }, + { + alternatives: [['topicCodeableConcept'], ['topicReference']], + required: false, + }, + { + alternatives: [ + ['legallyBindingAttachment'], + ['legallyBindingReference'], + ], + required: false, + }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -12338,75 +13093,145 @@ export const evidenceReportSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'alias', shadow: '_alias', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const evidenceVariableCharacteristicSchema: z.ZodType = - z.lazy(() => +export const contributorSchema: z.ZodType> = z.lazy( + () => z .strictObject({ - id: stringSchema.optional(), + id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + type: z.enum(['author', 'editor', 'endorser', 'reviewer']).optional(), + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - definitionReference: z.lazy(() => referenceSchema).optional(), - definitionCanonical: canonicalSchema.optional(), - _definitionCanonical: z.lazy(() => elementSchema).optional(), - definitionCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - definitionExpression: z.lazy(() => expressionSchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - device: z.lazy(() => referenceSchema).optional(), - exclude: booleanSchema.optional(), - _exclude: z.lazy(() => elementSchema).optional(), - timeFromStart: - evidenceVariableCharacteristicTimeFromStartSchema.optional(), - groupMeasure: z - .enum([ - 'mean', - 'mean-of-mean', - 'mean-of-median', - 'median', - 'median-of-mean', - 'median-of-median', - ]) - .optional(), - _groupMeasure: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'type', shadow: '_type' }, + { name: 'name', shadow: '_name' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: [ - 'definitionReference', - 'definitionCanonical', - 'definitionCodeableConcept', - 'definitionExpression', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> + +export const countSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const quantity = value as Record + const hasCode = + quantity.code !== undefined || quantity._code !== undefined + return ( + !hasCode || + quantity.system !== undefined || + quantity._system !== undefined + ) + }, + { message: 'qty-3: a coded Quantity requires its unit system.' }, + ), +) as unknown as z.ZodType> -export const evidenceVariableCategorySchema: z.ZodType = +export const coverageClassSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -12414,64 +13239,119 @@ export const evidenceVariableCategorySchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), + type: z.lazy(() => codeableConceptSchema), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'value', shadow: '_value' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['valueCodeableConcept', 'valueQuantity', 'valueRange'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const evidenceVariableCharacteristicTimeFromStartSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const coverageCostToBeneficiarySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - range: z.lazy(() => rangeSchema).optional(), - note: z - .lazy(() => annotationSchema) + type: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + exception: coverageCostToBeneficiaryExceptionSchema .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => + [ + { alternatives: [['valueQuantity'], ['valueMoney']], required: true }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const evidenceVariableSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('EvidenceVariable'), +export const coverageCostToBeneficiaryExceptionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + period: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const coverageSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Coverage'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -12483,429 +13363,653 @@ export const evidenceVariableSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - shortTitle: stringSchema.optional(), - _shortTitle: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - author: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - editor: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - reviewer: z - .lazy(() => contactDetailSchema) - .array() + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) .optional(), - endorser: z - .lazy(() => contactDetailSchema) + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + policyHolder: z.lazy(() => referenceSchema).optional(), + subscriber: z.lazy(() => referenceSchema).optional(), + subscriberId: stringSchema.optional(), + _subscriberId: z.lazy(() => elementSchema).optional(), + beneficiary: z.lazy(() => referenceSchema), + dependent: stringSchema.optional(), + _dependent: z.lazy(() => elementSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + payor: z + .lazy(() => referenceSchema) .array() - .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) + .min(1), + class: coverageClassSchema.array().min(1).optional(), + order: positiveIntSchema.optional(), + _order: z.lazy(() => elementSchema).optional(), + network: stringSchema.optional(), + _network: z.lazy(() => elementSchema).optional(), + costToBeneficiary: coverageCostToBeneficiarySchema + .array() + .min(1) + .optional(), + subrogation: booleanSchema.optional(), + _subrogation: z.lazy(() => elementSchema).optional(), + contract: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - actual: booleanSchema.optional(), - _actual: z.lazy(() => elementSchema).optional(), - characteristicCombination: z.enum(['intersection', 'union']).optional(), - _characteristicCombination: z.lazy(() => elementSchema).optional(), - characteristic: evidenceVariableCharacteristicSchema.array().optional(), - handling: z - .enum(['continuous', 'dichotomous', 'ordinal', 'polychotomous']) - .optional(), - _handling: z.lazy(() => elementSchema).optional(), - category: evidenceVariableCategorySchema.array().optional(), - }), -) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const exampleScenarioActorSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const coverageEligibilityRequestSupportingInfoSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - actorId: stringSchema, - _actorId: z.lazy(() => elementSchema).optional(), - type: z.enum(['entity', 'person']), - _type: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - }), - ) + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + information: z.lazy(() => referenceSchema), + appliesToAll: booleanSchema.optional(), + _appliesToAll: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const exampleScenarioInstanceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const coverageEligibilityRequestInsuranceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - resourceId: stringSchema, - _resourceId: z.lazy(() => elementSchema).optional(), - resourceType: z.enum([ - 'Account', - 'ActivityDefinition', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RegulatedAuthorization', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]), - _resourceType: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - version: exampleScenarioInstanceVersionSchema.array().optional(), - containedInstance: exampleScenarioInstanceContainedInstanceSchema - .array() - .optional(), - }), - ) + focal: booleanSchema.optional(), + _focal: z.lazy(() => elementSchema).optional(), + coverage: z.lazy(() => referenceSchema), + businessArrangement: stringSchema.optional(), + _businessArrangement: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const exampleScenarioProcessSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const coverageEligibilityRequestItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - title: stringSchema, - _title: z.lazy(() => elementSchema).optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - preConditions: markdownSchema.optional(), - _preConditions: z.lazy(() => elementSchema).optional(), - postConditions: markdownSchema.optional(), - _postConditions: z.lazy(() => elementSchema).optional(), - step: exampleScenarioProcessStepSchema.array().optional(), - }), - ) - -export const exampleScenarioInstanceVersionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + supportingInfoSequence: positiveIntSchema + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + _supportingInfoSequence: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - versionId: stringSchema, - _versionId: z.lazy(() => elementSchema).optional(), - description: markdownSchema, - _description: z.lazy(() => elementSchema).optional(), - }), - ) + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema).optional(), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + provider: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + facility: z.lazy(() => referenceSchema).optional(), + diagnosis: coverageEligibilityRequestItemDiagnosisSchema + .array() + .min(1) + .optional(), + detail: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'supportingInfoSequence', + shadow: '_supportingInfoSequence', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const exampleScenarioInstanceContainedInstanceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const coverageEligibilityRequestItemDiagnosisSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - resourceId: stringSchema, - _resourceId: z.lazy(() => elementSchema).optional(), - versionId: stringSchema.optional(), - _versionId: z.lazy(() => elementSchema).optional(), - }), - ) + diagnosisCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + diagnosisReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['diagnosisCodeableConcept'], + ['diagnosisReference'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const exampleScenarioProcessStepSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const coverageEligibilityRequestSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CoverageEligibilityRequest'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - process: z - .lazy(() => exampleScenarioProcessSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - pause: booleanSchema.optional(), - _pause: z.lazy(() => elementSchema).optional(), - operation: exampleScenarioProcessStepOperationSchema.optional(), - alternative: exampleScenarioProcessStepAlternativeSchema + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + priority: z.lazy(() => codeableConceptSchema).optional(), + purpose: z + .enum(['auth-requirements', 'benefits', 'discovery', 'validation']) + .nullable() .array() + .min(1) .optional(), - }), - ) + _purpose: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + patient: z.lazy(() => referenceSchema), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + provider: z.lazy(() => referenceSchema).optional(), + insurer: z.lazy(() => referenceSchema), + facility: z.lazy(() => referenceSchema).optional(), + supportingInfo: coverageEligibilityRequestSupportingInfoSchema + .array() + .min(1) + .optional(), + insurance: coverageEligibilityRequestInsuranceSchema + .array() + .min(1) + .optional(), + item: coverageEligibilityRequestItemSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['servicedDate', '_servicedDate'], + ['servicedPeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'created', shadow: '_created' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'purpose', shadow: '_purpose', required: true }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const exampleScenarioProcessStepOperationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const coverageEligibilityResponseInsuranceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - number: stringSchema, - _number: z.lazy(() => elementSchema).optional(), - type: stringSchema.optional(), - _type: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - initiator: stringSchema.optional(), - _initiator: z.lazy(() => elementSchema).optional(), - receiver: stringSchema.optional(), - _receiver: z.lazy(() => elementSchema).optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - initiatorActive: booleanSchema.optional(), - _initiatorActive: z.lazy(() => elementSchema).optional(), - receiverActive: booleanSchema.optional(), - _receiverActive: z.lazy(() => elementSchema).optional(), - request: z - .lazy(() => exampleScenarioInstanceContainedInstanceSchema) + coverage: z.lazy(() => referenceSchema), + inforce: booleanSchema.optional(), + _inforce: z.lazy(() => elementSchema).optional(), + benefitPeriod: z.lazy(() => periodSchema).optional(), + item: coverageEligibilityResponseInsuranceItemSchema + .array() + .min(1) .optional(), - response: z - .lazy(() => exampleScenarioInstanceContainedInstanceSchema) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const coverageEligibilityResponseErrorSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) .optional(), - }), - ) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const exampleScenarioProcessStepAlternativeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const coverageEligibilityResponseInsuranceItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - title: stringSchema, - _title: z.lazy(() => elementSchema).optional(), - description: markdownSchema.optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema).optional(), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + provider: z.lazy(() => referenceSchema).optional(), + excluded: booleanSchema.optional(), + _excluded: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - step: z - .lazy(() => exampleScenarioProcessStepSchema) + network: z.lazy(() => codeableConceptSchema).optional(), + unit: z.lazy(() => codeableConceptSchema).optional(), + term: z.lazy(() => codeableConceptSchema).optional(), + benefit: coverageEligibilityResponseInsuranceItemBenefitSchema .array() + .min(1) .optional(), - }), - ) + authorizationRequired: booleanSchema.optional(), + _authorizationRequired: z.lazy(() => elementSchema).optional(), + authorizationSupporting: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + authorizationUrl: uriSchema.optional(), + _authorizationUrl: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const exampleScenarioSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('ExampleScenario'), +export const coverageEligibilityResponseInsuranceItemBenefitSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + allowedUnsignedInt: unsignedIntSchema.optional(), + _allowedUnsignedInt: z.lazy(() => elementSchema).optional(), + allowedString: stringSchema.optional(), + _allowedString: z.lazy(() => elementSchema).optional(), + allowedMoney: z.lazy(() => moneySchema).optional(), + usedUnsignedInt: unsignedIntSchema.optional(), + _usedUnsignedInt: z.lazy(() => elementSchema).optional(), + usedString: stringSchema.optional(), + _usedString: z.lazy(() => elementSchema).optional(), + usedMoney: z.lazy(() => moneySchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['allowedUnsignedInt', '_allowedUnsignedInt'], + ['allowedString', '_allowedString'], + ['allowedMoney'], + ], + required: false, + }, + { + alternatives: [ + ['usedUnsignedInt', '_usedUnsignedInt'], + ['usedString', '_usedString'], + ['usedMoney'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const coverageEligibilityResponseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('CoverageEligibilityResponse'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -12917,163 +14021,534 @@ export const exampleScenarioSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) .optional(), - useContext: z - .lazy(() => usageContextSchema) + _status: z.lazy(() => elementSchema).optional(), + purpose: z + .enum(['auth-requirements', 'benefits', 'discovery', 'validation']) + .nullable() .array() + .min(1) .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) + _purpose: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - actor: exampleScenarioActorSchema.array().optional(), - instance: exampleScenarioInstanceSchema.array().optional(), - process: exampleScenarioProcessSchema.array().optional(), - workflow: canonicalSchema.array().optional(), - _workflow: z - .lazy(() => elementSchema) + patient: z.lazy(() => referenceSchema), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + requestor: z.lazy(() => referenceSchema).optional(), + request: z.lazy(() => referenceSchema), + outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + insurer: z.lazy(() => referenceSchema), + insurance: coverageEligibilityResponseInsuranceSchema .array() + .min(1) .optional(), - }), -) + preAuthRef: stringSchema.optional(), + _preAuthRef: z.lazy(() => elementSchema).optional(), + form: z.lazy(() => codeableConceptSchema).optional(), + error: coverageEligibilityResponseErrorSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['servicedDate', '_servicedDate'], + ['servicedPeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'created', shadow: '_created' }, + { name: 'outcome', shadow: '_outcome' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'purpose', shadow: '_purpose', required: true }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitRelatedSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const dataRequirementCodeFilterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + searchParam: stringSchema.optional(), + _searchParam: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema.optional(), + _valueSet: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codingSchema) .array() + .min(1) .optional(), - claim: z.lazy(() => referenceSchema).optional(), - relationship: z.lazy(() => codeableConceptSchema).optional(), - reference: z.lazy(() => identifierSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitPayeeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const dataRequirementDateFilterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - party: z.lazy(() => referenceSchema).optional(), - }), - ) + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + searchParam: stringSchema.optional(), + _searchParam: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueDateTime', '_valueDateTime'], + ['valuePeriod'], + ['valueDuration'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitCareTeamSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const dataRequirementSortSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - provider: z.lazy(() => referenceSchema), - responsible: booleanSchema.optional(), - _responsible: z.lazy(() => elementSchema).optional(), - role: z.lazy(() => codeableConceptSchema).optional(), - qualification: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + direction: z.enum(['ascending', 'descending']).optional(), + _direction: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'path', shadow: '_path' }, + { name: 'direction', shadow: '_direction' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitSupportingInfoSchema: z.ZodType = +export const dataRequirementSchema: z.ZodType> = z.lazy(() => z .strictObject({ - id: stringSchema.optional(), + id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + type: z + .enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Any', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'CodeableConcept', + 'CodeableReference', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'RatioRange', + 'Reference', + 'RegulatedAuthorization', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'Type', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.nullable().array().min(1).optional(), + _profile: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - category: z.lazy(() => codeableConceptSchema), - code: z.lazy(() => codeableConceptSchema).optional(), - timingDate: dateSchema.optional(), - _timingDate: z.lazy(() => elementSchema).optional(), - timingPeriod: z.lazy(() => periodSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - reason: z.lazy(() => codingSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + mustSupport: stringSchema.nullable().array().min(1).optional(), + _mustSupport: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + codeFilter: dataRequirementCodeFilterSchema.array().min(1).optional(), + dateFilter: dataRequirementDateFilterSchema.array().min(1).optional(), + limit: positiveIntSchema.optional(), + _limit: z.lazy(() => elementSchema).optional(), + sort: dataRequirementSortSchema.array().min(1).optional(), }) .refine( (value) => [ - { names: ['timingDate', 'timingPeriod'], required: false }, { - names: [ - 'valueBoolean', - 'valueString', - 'valueQuantity', - 'valueAttachment', - 'valueReference', - ], + alternatives: [['subjectCodeableConcept'], ['subjectReference']], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -13081,45 +14556,219 @@ export const explanationOfBenefitSupportingInfoSchema: z.ZodType { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'profile', shadow: '_profile', required: false }, + { name: 'mustSupport', shadow: '_mustSupport', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) + ) as unknown as z.ZodType> + +export const detectedIssueEvidenceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + detail: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const detectedIssueMitigationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + action: z.lazy(() => codeableConceptSchema), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitDiagnosisSchema: z.ZodType = +export const detectedIssueSchema: z.ZodType> = z.lazy(() => z .strictObject({ - id: stringSchema.optional(), + resourceType: z.literal('DetectedIssue'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - diagnosisCodeableConcept: z - .lazy(() => codeableConceptSchema) + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) .optional(), - diagnosisReference: z.lazy(() => referenceSchema).optional(), - type: z - .lazy(() => codeableConceptSchema) + status: z + .enum([ + 'amended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'preliminary', + 'registered', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + severity: z.enum(['high', 'low', 'moderate']).optional(), + _severity: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema).optional(), + identifiedDateTime: dateTimeSchema.optional(), + _identifiedDateTime: z.lazy(() => elementSchema).optional(), + identifiedPeriod: z.lazy(() => periodSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + implicated: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - onAdmission: z.lazy(() => codeableConceptSchema).optional(), - packageCode: z.lazy(() => codeableConceptSchema).optional(), + evidence: detectedIssueEvidenceSchema.array().min(1).optional(), + detail: stringSchema.optional(), + _detail: z.lazy(() => elementSchema).optional(), + reference: uriSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + mitigation: detectedIssueMitigationSchema.array().min(1).optional(), }) .refine( (value) => [ { - names: ['diagnosisCodeableConcept', 'diagnosisReference'], - required: true, + alternatives: [ + ['identifiedDateTime', '_identifiedDateTime'], + ['identifiedPeriod'], + ], + required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -13127,10 +14776,32 @@ export const explanationOfBenefitDiagnosisSchema: z.ZodType { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) + ) as unknown as z.ZodType> -export const explanationOfBenefitProcedureSchema: z.ZodType = +export const deviceUdiCarrierSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -13138,72 +14809,126 @@ export const explanationOfBenefitProcedureSchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + deviceIdentifier: stringSchema.optional(), + _deviceIdentifier: z.lazy(() => elementSchema).optional(), + issuer: uriSchema.optional(), + _issuer: z.lazy(() => elementSchema).optional(), + jurisdiction: uriSchema.optional(), + _jurisdiction: z.lazy(() => elementSchema).optional(), + carrierAIDC: base64BinarySchema.optional(), + _carrierAIDC: z.lazy(() => elementSchema).optional(), + carrierHRF: stringSchema.optional(), + _carrierHRF: z.lazy(() => elementSchema).optional(), + entryType: z + .enum([ + 'barcode', + 'card', + 'manual', + 'rfid', + 'self-reported', + 'unknown', + ]) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - type: z - .lazy(() => codeableConceptSchema) + _entryType: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const deviceDeviceNameSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - procedureCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - procedureReference: z.lazy(() => referenceSchema).optional(), - udi: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'manufacturer-name', + 'model-name', + 'other', + 'patient-reported-name', + 'udi-label-name', + 'user-friendly-name', + ]) .optional(), + _type: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['procedureCodeableConcept', 'procedureReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const explanationOfBenefitInsuranceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const deviceSpecializationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - focal: booleanSchema, - _focal: z.lazy(() => elementSchema).optional(), - coverage: z.lazy(() => referenceSchema), - preAuthRef: stringSchema.array().optional(), - _preAuthRef: z - .lazy(() => elementSchema) - .array() - .optional(), - }), - ) + systemType: z.lazy(() => codeableConceptSchema), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitAccidentSchema: z.ZodType = +export const deviceVersionSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -13211,143 +14936,43 @@ export const explanationOfBenefitAccidentSchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - date: dateSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), type: z.lazy(() => codeableConceptSchema).optional(), - locationAddress: z.lazy(() => addressSchema).optional(), - locationReference: z.lazy(() => referenceSchema).optional(), + component: z.lazy(() => identifierSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), }) - .refine( - (value) => - [ - { - names: ['locationAddress', 'locationReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const explanationOfBenefitItemSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - careTeamSequence: positiveIntSchema.array().optional(), - _careTeamSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - diagnosisSequence: positiveIntSchema.array().optional(), - _diagnosisSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - procedureSequence: positiveIntSchema.array().optional(), - _procedureSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - informationSequence: positiveIntSchema.array().optional(), - _informationSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - revenue: z.lazy(() => codeableConceptSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - programCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - servicedDate: dateSchema.optional(), - _servicedDate: z.lazy(() => elementSchema).optional(), - servicedPeriod: z.lazy(() => periodSchema).optional(), - locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - locationAddress: z.lazy(() => addressSchema).optional(), - locationReference: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - udi: z - .lazy(() => referenceSchema) - .array() - .optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), - subSite: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - encounter: z - .lazy(() => referenceSchema) - .array() - .optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) - .array() - .optional(), - adjudication: explanationOfBenefitItemAdjudicationSchema - .array() - .optional(), - detail: explanationOfBenefitItemDetailSchema.array().optional(), + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'value', shadow: '_value' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { names: ['servicedDate', 'servicedPeriod'], required: false }, - { - names: [ - 'locationCodeableConcept', - 'locationAddress', - 'locationReference', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const explanationOfBenefitAddItemSchema: z.ZodType = +export const devicePropertySchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -13355,320 +14980,473 @@ export const explanationOfBenefitAddItemSchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - itemSequence: positiveIntSchema.array().optional(), - _itemSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - detailSequence: positiveIntSchema.array().optional(), - _detailSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - subDetailSequence: positiveIntSchema.array().optional(), - _subDetailSequence: z - .lazy(() => elementSchema) - .array() - .optional(), - provider: z - .lazy(() => referenceSchema) - .array() - .optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - programCode: z - .lazy(() => codeableConceptSchema) + type: z.lazy(() => codeableConceptSchema), + valueQuantity: z + .lazy(() => quantitySchema) .array() + .min(1) .optional(), - servicedDate: dateSchema.optional(), - _servicedDate: z.lazy(() => elementSchema).optional(), - servicedPeriod: z.lazy(() => periodSchema).optional(), - locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - locationAddress: z.lazy(() => addressSchema).optional(), - locationReference: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), - subSite: z + valueCode: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) - .array() - .optional(), - adjudication: z - .lazy(() => explanationOfBenefitItemAdjudicationSchema) - .array() - .optional(), - detail: explanationOfBenefitAddItemDetailSchema.array().optional(), }) .refine( - (value) => - [ - { names: ['servicedDate', 'servicedPeriod'], required: false }, - { - names: [ - 'locationCodeableConcept', - 'locationAddress', - 'locationReference', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const explanationOfBenefitItemAdjudicationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const deviceSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Device'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - category: z.lazy(() => codeableConceptSchema), - reason: z.lazy(() => codeableConceptSchema).optional(), - amount: z.lazy(() => moneySchema).optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - }), - ) - -export const explanationOfBenefitTotalSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + definition: z.lazy(() => referenceSchema).optional(), + udiCarrier: deviceUdiCarrierSchema.array().min(1).optional(), + status: z + .enum(['active', 'entered-in-error', 'inactive', 'unknown']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - category: z.lazy(() => codeableConceptSchema), - amount: z.lazy(() => moneySchema), - }), - ) - -export const explanationOfBenefitPaymentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + distinctIdentifier: stringSchema.optional(), + _distinctIdentifier: z.lazy(() => elementSchema).optional(), + manufacturer: stringSchema.optional(), + _manufacturer: z.lazy(() => elementSchema).optional(), + manufactureDate: dateTimeSchema.optional(), + _manufactureDate: z.lazy(() => elementSchema).optional(), + expirationDate: dateTimeSchema.optional(), + _expirationDate: z.lazy(() => elementSchema).optional(), + lotNumber: stringSchema.optional(), + _lotNumber: z.lazy(() => elementSchema).optional(), + serialNumber: stringSchema.optional(), + _serialNumber: z.lazy(() => elementSchema).optional(), + deviceName: deviceDeviceNameSchema.array().min(1).optional(), + modelNumber: stringSchema.optional(), + _modelNumber: z.lazy(() => elementSchema).optional(), + partNumber: stringSchema.optional(), + _partNumber: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + specialization: deviceSpecializationSchema.array().min(1).optional(), + version: deviceVersionSchema.array().min(1).optional(), + property: devicePropertySchema.array().min(1).optional(), + patient: z.lazy(() => referenceSchema).optional(), + owner: z.lazy(() => referenceSchema).optional(), + contact: z + .lazy(() => contactPointSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + location: z.lazy(() => referenceSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - adjustment: z.lazy(() => moneySchema).optional(), - adjustmentReason: z.lazy(() => codeableConceptSchema).optional(), - date: dateSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - amount: z.lazy(() => moneySchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), - }), - ) + safety: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + parent: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitProcessNoteSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const deviceDefinitionUdiDeviceIdentifierSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - number: positiveIntSchema.optional(), - _number: z.lazy(() => elementSchema).optional(), - type: z.enum(['display', 'print', 'printoper']).optional(), - _type: z.lazy(() => elementSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - language: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + deviceIdentifier: stringSchema.optional(), + _deviceIdentifier: z.lazy(() => elementSchema).optional(), + issuer: uriSchema.optional(), + _issuer: z.lazy(() => elementSchema).optional(), + jurisdiction: uriSchema.optional(), + _jurisdiction: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'deviceIdentifier', shadow: '_deviceIdentifier' }, + { name: 'issuer', shadow: '_issuer' }, + { name: 'jurisdiction', shadow: '_jurisdiction' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitBenefitBalanceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const deviceDefinitionDeviceNameSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - category: z.lazy(() => codeableConceptSchema), - excluded: booleanSchema.optional(), - _excluded: z.lazy(() => elementSchema).optional(), name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - network: z.lazy(() => codeableConceptSchema).optional(), - unit: z.lazy(() => codeableConceptSchema).optional(), - term: z.lazy(() => codeableConceptSchema).optional(), - financial: explanationOfBenefitBenefitBalanceFinancialSchema - .array() + type: z + .enum([ + 'manufacturer-name', + 'model-name', + 'other', + 'patient-reported-name', + 'udi-label-name', + 'user-friendly-name', + ]) .optional(), - }), - ) + _type: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitItemDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const deviceDefinitionSpecializationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - revenue: z.lazy(() => codeableConceptSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - programCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - udi: z - .lazy(() => referenceSchema) - .array() - .optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) + systemType: stringSchema.optional(), + _systemType: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'systemType', shadow: '_systemType' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const deviceDefinitionCapabilitySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - adjudication: z - .lazy(() => explanationOfBenefitItemAdjudicationSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - subDetail: explanationOfBenefitItemDetailSubDetailSchema + type: z.lazy(() => codeableConceptSchema), + description: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitAddItemDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const deviceDefinitionPropertySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) + type: z.lazy(() => codeableConceptSchema), + valueQuantity: z + .lazy(() => quantitySchema) .array() + .min(1) .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) + valueCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - adjudication: z - .lazy(() => explanationOfBenefitItemAdjudicationSchema) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const deviceDefinitionMaterialSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - subDetail: explanationOfBenefitAddItemDetailSubDetailSchema + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), - ) + substance: z.lazy(() => codeableConceptSchema), + alternate: booleanSchema.optional(), + _alternate: z.lazy(() => elementSchema).optional(), + allergenicIndicator: booleanSchema.optional(), + _allergenicIndicator: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitBenefitBalanceFinancialSchema: z.ZodType = +export const deviceDefinitionSchema: z.ZodType> = z.lazy(() => z .strictObject({ - id: stringSchema.optional(), + resourceType: z.literal('DeviceDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - allowedUnsignedInt: unsignedIntSchema.optional(), - _allowedUnsignedInt: z.lazy(() => elementSchema).optional(), - allowedString: stringSchema.optional(), - _allowedString: z.lazy(() => elementSchema).optional(), - allowedMoney: z.lazy(() => moneySchema).optional(), - usedUnsignedInt: unsignedIntSchema.optional(), - _usedUnsignedInt: z.lazy(() => elementSchema).optional(), - usedMoney: z.lazy(() => moneySchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + udiDeviceIdentifier: deviceDefinitionUdiDeviceIdentifierSchema + .array() + .min(1) + .optional(), + manufacturerString: stringSchema.optional(), + _manufacturerString: z.lazy(() => elementSchema).optional(), + manufacturerReference: z.lazy(() => referenceSchema).optional(), + deviceName: deviceDefinitionDeviceNameSchema.array().min(1).optional(), + modelNumber: stringSchema.optional(), + _modelNumber: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + specialization: deviceDefinitionSpecializationSchema + .array() + .min(1) + .optional(), + version: stringSchema.nullable().array().min(1).optional(), + _version: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + safety: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + shelfLifeStorage: z + .lazy(() => productShelfLifeSchema) + .array() + .min(1) + .optional(), + physicalCharacteristics: z + .lazy(() => prodCharacteristicSchema) + .optional(), + languageCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + capability: deviceDefinitionCapabilitySchema.array().min(1).optional(), + property: deviceDefinitionPropertySchema.array().min(1).optional(), + owner: z.lazy(() => referenceSchema).optional(), + contact: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + onlineInformation: uriSchema.optional(), + _onlineInformation: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + parentDevice: z.lazy(() => referenceSchema).optional(), + material: deviceDefinitionMaterialSchema.array().min(1).optional(), }) .refine( (value) => [ { - names: ['allowedUnsignedInt', 'allowedString', 'allowedMoney'], + alternatives: [ + ['manufacturerString', '_manufacturerString'], + ['manufacturerReference'], + ], required: false, }, - { names: ['usedUnsignedInt', 'usedMoney'], required: false }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -13676,346 +15454,221 @@ export const explanationOfBenefitBenefitBalanceFinancialSchema: z.ZodType { + for (const group of [ + { name: 'version', shadow: '_version', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) + ) as unknown as z.ZodType> -export const explanationOfBenefitItemDetailSubDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const deviceMetricCalibrationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sequence: positiveIntSchema, - _sequence: z.lazy(() => elementSchema).optional(), - revenue: z.lazy(() => codeableConceptSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - programCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - udi: z - .lazy(() => referenceSchema) - .array() - .optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) - .array() - .optional(), - adjudication: z - .lazy(() => explanationOfBenefitItemAdjudicationSchema) - .array() + type: z.enum(['gain', 'offset', 'two-point', 'unspecified']).optional(), + _type: z.lazy(() => elementSchema).optional(), + state: z + .enum([ + 'calibrated', + 'calibration-required', + 'not-calibrated', + 'unspecified', + ]) .optional(), - }), - ) + _state: z.lazy(() => elementSchema).optional(), + time: instantSchema.optional(), + _time: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitAddItemDetailSubDetailSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - productOrService: z.lazy(() => codeableConceptSchema), - modifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - unitPrice: z.lazy(() => moneySchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - net: z.lazy(() => moneySchema).optional(), - noteNumber: positiveIntSchema.array().optional(), - _noteNumber: z - .lazy(() => elementSchema) - .array() - .optional(), - adjudication: z - .lazy(() => explanationOfBenefitItemAdjudicationSchema) - .array() - .optional(), - }), - ) +export const deviceMetricSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('DeviceMetric'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + unit: z.lazy(() => codeableConceptSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + parent: z.lazy(() => referenceSchema).optional(), + operationalStatus: z + .enum(['entered-in-error', 'off', 'on', 'standby']) + .optional(), + _operationalStatus: z.lazy(() => elementSchema).optional(), + color: z + .enum([ + 'black', + 'blue', + 'cyan', + 'green', + 'magenta', + 'red', + 'white', + 'yellow', + ]) + .optional(), + _color: z.lazy(() => elementSchema).optional(), + category: z + .enum(['calculation', 'measurement', 'setting', 'unspecified']) + .optional(), + _category: z.lazy(() => elementSchema).optional(), + measurementPeriod: z.lazy(() => timingSchema).optional(), + calibration: deviceMetricCalibrationSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'category', shadow: '_category' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const explanationOfBenefitSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('ExplanationOfBenefit'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), +export const deviceRequestParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - subType: z.lazy(() => codeableConceptSchema).optional(), - use: z.enum(['claim', 'preauthorization', 'predetermination']), - _use: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema), - billablePeriod: z.lazy(() => periodSchema).optional(), - created: dateTimeSchema, - _created: z.lazy(() => elementSchema).optional(), - enterer: z.lazy(() => referenceSchema).optional(), - insurer: z.lazy(() => referenceSchema), - provider: z.lazy(() => referenceSchema), - priority: z.lazy(() => codeableConceptSchema).optional(), - fundsReserveRequested: z.lazy(() => codeableConceptSchema).optional(), - fundsReserve: z.lazy(() => codeableConceptSchema).optional(), - related: explanationOfBenefitRelatedSchema.array().optional(), - prescription: z.lazy(() => referenceSchema).optional(), - originalPrescription: z.lazy(() => referenceSchema).optional(), - payee: explanationOfBenefitPayeeSchema.optional(), - referral: z.lazy(() => referenceSchema).optional(), - facility: z.lazy(() => referenceSchema).optional(), - claim: z.lazy(() => referenceSchema).optional(), - claimResponse: z.lazy(() => referenceSchema).optional(), - outcome: z.enum(['complete', 'error', 'partial', 'queued']), - _outcome: z.lazy(() => elementSchema).optional(), - disposition: stringSchema.optional(), - _disposition: z.lazy(() => elementSchema).optional(), - preAuthRef: stringSchema.array().optional(), - _preAuthRef: z - .lazy(() => elementSchema) - .array() - .optional(), - preAuthRefPeriod: z - .lazy(() => periodSchema) - .array() - .optional(), - careTeam: explanationOfBenefitCareTeamSchema.array().optional(), - supportingInfo: explanationOfBenefitSupportingInfoSchema - .array() - .optional(), - diagnosis: explanationOfBenefitDiagnosisSchema.array().optional(), - procedure: explanationOfBenefitProcedureSchema.array().optional(), - precedence: positiveIntSchema.optional(), - _precedence: z.lazy(() => elementSchema).optional(), - insurance: explanationOfBenefitInsuranceSchema.array().min(1), - accident: explanationOfBenefitAccidentSchema.optional(), - item: explanationOfBenefitItemSchema.array().optional(), - addItem: explanationOfBenefitAddItemSchema.array().optional(), - adjudication: z - .lazy(() => explanationOfBenefitItemAdjudicationSchema) - .array() - .optional(), - total: explanationOfBenefitTotalSchema.array().optional(), - payment: explanationOfBenefitPaymentSchema.optional(), - formCode: z.lazy(() => codeableConceptSchema).optional(), - form: z.lazy(() => attachmentSchema).optional(), - processNote: explanationOfBenefitProcessNoteSchema.array().optional(), - benefitPeriod: z.lazy(() => periodSchema).optional(), - benefitBalance: explanationOfBenefitBenefitBalanceSchema - .array() - .optional(), - }), - ) - -export const expressionSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - name: idSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - language: codeSchema, - _language: z.lazy(() => elementSchema).optional(), - expression: stringSchema.optional(), - _expression: z.lazy(() => elementSchema).optional(), - reference: uriSchema.optional(), - _reference: z.lazy(() => elementSchema).optional(), - }), -) - -export const extensionSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema, - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueCanonical: canonicalSchema.optional(), - _valueCanonical: z.lazy(() => elementSchema).optional(), - valueCode: codeSchema.optional(), - _valueCode: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueId: idSchema.optional(), - _valueId: z.lazy(() => elementSchema).optional(), - valueInstant: instantSchema.optional(), - _valueInstant: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueMarkdown: markdownSchema.optional(), - _valueMarkdown: z.lazy(() => elementSchema).optional(), - valueOid: oidSchema.optional(), - _valueOid: z.lazy(() => elementSchema).optional(), - valuePositiveInt: positiveIntSchema.optional(), - _valuePositiveInt: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueUnsignedInt: unsignedIntSchema.optional(), - _valueUnsignedInt: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueUrl: urlSchema.optional(), - _valueUrl: z.lazy(() => elementSchema).optional(), - valueUuid: uuidSchema.optional(), - _valueUuid: z.lazy(() => elementSchema).optional(), - valueAddress: z.lazy(() => addressSchema).optional(), - valueAge: z.lazy(() => ageSchema).optional(), - valueAnnotation: z.lazy(() => annotationSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueCodeableReference: z.lazy(() => codeableReferenceSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueContactPoint: z.lazy(() => contactPointSchema).optional(), - valueCount: z.lazy(() => countSchema).optional(), - valueDistance: z.lazy(() => distanceSchema).optional(), - valueDuration: z.lazy(() => durationSchema).optional(), - valueHumanName: z.lazy(() => humanNameSchema).optional(), - valueIdentifier: z.lazy(() => identifierSchema).optional(), - valueMoney: z.lazy(() => moneySchema).optional(), - valuePeriod: z.lazy(() => periodSchema).optional(), valueQuantity: z.lazy(() => quantitySchema).optional(), valueRange: z.lazy(() => rangeSchema).optional(), - valueRatio: z.lazy(() => ratioSchema).optional(), - valueRatioRange: z.lazy(() => ratioRangeSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - valueSampledData: z.lazy(() => sampledDataSchema).optional(), - valueSignature: z.lazy(() => signatureSchema).optional(), - valueTiming: z.lazy(() => timingSchema).optional(), - valueContactDetail: z.lazy(() => contactDetailSchema).optional(), - valueContributor: z.lazy(() => contributorSchema).optional(), - valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), - valueExpression: z.lazy(() => expressionSchema).optional(), - valueParameterDefinition: z - .lazy(() => parameterDefinitionSchema) - .optional(), - valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), - valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), - valueUsageContext: z.lazy(() => usageContextSchema).optional(), - valueDosage: z.lazy(() => dosageSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), }) .refine( (value) => [ { - names: [ - 'valueBase64Binary', - 'valueBoolean', - 'valueCanonical', - 'valueCode', - 'valueDate', - 'valueDateTime', - 'valueDecimal', - 'valueId', - 'valueInstant', - 'valueInteger', - 'valueMarkdown', - 'valueOid', - 'valuePositiveInt', - 'valueString', - 'valueTime', - 'valueUnsignedInt', - 'valueUri', - 'valueUrl', - 'valueUuid', - 'valueAddress', - 'valueAge', - 'valueAnnotation', - 'valueAttachment', - 'valueCodeableConcept', - 'valueCodeableReference', - 'valueCoding', - 'valueContactPoint', - 'valueCount', - 'valueDistance', - 'valueDuration', - 'valueHumanName', - 'valueIdentifier', - 'valueMoney', - 'valuePeriod', - 'valueQuantity', - 'valueRange', - 'valueRatio', - 'valueRatioRange', - 'valueReference', - 'valueSampledData', - 'valueSignature', - 'valueTiming', - 'valueContactDetail', - 'valueContributor', - 'valueDataRequirement', - 'valueExpression', - 'valueParameterDefinition', - 'valueRelatedArtifact', - 'valueTriggerDefinition', - 'valueUsageContext', - 'valueDosage', + alternatives: [ + ['valueCodeableConcept'], + ['valueQuantity'], + ['valueRange'], + ['valueBoolean', '_valueBoolean'], ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -14025,72 +15678,16 @@ export const extensionSchema: z.ZodType = z.lazy(() => }, ) .refine( - (value) => - ((value as { extension?: unknown[] }).extension?.length ?? 0) > 0 !== - Object.keys(value as object).some( - // A primitive sibling means the value element exists carrying only extensions, - // which ext-1 counts as the value being present. - (key) => key.startsWith('value') || key.startsWith('_value'), - ), - { - message: - 'ext-1: an extension has either extensions or a value, never both.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const familyMemberHistoryConditionSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - outcome: z.lazy(() => codeableConceptSchema).optional(), - contributedToDeath: booleanSchema.optional(), - _contributedToDeath: z.lazy(() => elementSchema).optional(), - onsetAge: z.lazy(() => ageSchema).optional(), - onsetRange: z.lazy(() => rangeSchema).optional(), - onsetPeriod: z.lazy(() => periodSchema).optional(), - onsetString: stringSchema.optional(), - _onsetString: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['onsetAge', 'onsetRange', 'onsetPeriod', 'onsetString'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) +) as unknown as z.ZodType> -export const familyMemberHistorySchema: z.ZodType = +export const deviceRequestSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('FamilyMemberHistory'), + resourceType: z.literal('DeviceRequest'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -14102,98 +15699,146 @@ export const familyMemberHistorySchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) + .optional(), + instantiatesCanonical: canonicalSchema + .nullable() + .array() + .min(1) .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), _instantiatesCanonical: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - instantiatesUri: uriSchema.array().optional(), + instantiatesUri: uriSchema.nullable().array().min(1).optional(), _instantiatesUri: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - status: z.enum([ - 'completed', - 'entered-in-error', - 'health-unknown', - 'partial', - ]), - _status: z.lazy(() => elementSchema).optional(), - dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), - patient: z.lazy(() => referenceSchema), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - relationship: z.lazy(() => codeableConceptSchema), - sex: z.lazy(() => codeableConceptSchema).optional(), - bornPeriod: z.lazy(() => periodSchema).optional(), - bornDate: dateSchema.optional(), - _bornDate: z.lazy(() => elementSchema).optional(), - bornString: stringSchema.optional(), - _bornString: z.lazy(() => elementSchema).optional(), - ageAge: z.lazy(() => ageSchema).optional(), - ageRange: z.lazy(() => rangeSchema).optional(), - ageString: stringSchema.optional(), - _ageString: z.lazy(() => elementSchema).optional(), - estimatedAge: booleanSchema.optional(), - _estimatedAge: z.lazy(() => elementSchema).optional(), - deceasedBoolean: booleanSchema.optional(), - _deceasedBoolean: z.lazy(() => elementSchema).optional(), - deceasedAge: z.lazy(() => ageSchema).optional(), - deceasedRange: z.lazy(() => rangeSchema).optional(), - deceasedDate: dateSchema.optional(), - _deceasedDate: z.lazy(() => elementSchema).optional(), - deceasedString: stringSchema.optional(), - _deceasedString: z.lazy(() => elementSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + priorRequest: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + status: z + .enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + intent: z + .enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]) + .optional(), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + codeReference: z.lazy(() => referenceSchema).optional(), + codeCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + parameter: deviceRequestParameterSchema.array().min(1).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performerType: z.lazy(() => codeableConceptSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), reasonCode: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), reasonReference: z .lazy(() => referenceSchema) .array() + .min(1) + .optional(), + insurance: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), note: z .lazy(() => annotationSchema) .array() + .min(1) + .optional(), + relevantHistory: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), - condition: familyMemberHistoryConditionSchema.array().optional(), }) .refine( (value) => [ { - names: ['bornPeriod', 'bornDate', 'bornString'], - required: false, + alternatives: [['codeReference'], ['codeCodeableConcept']], + required: true, }, - { names: ['ageAge', 'ageRange', 'ageString'], required: false }, { - names: [ - 'deceasedBoolean', - 'deceasedAge', - 'deceasedRange', - 'deceasedDate', - 'deceasedString', + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], + ['occurrenceTiming'], ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -14201,110 +15846,91 @@ export const familyMemberHistorySchema: z.ZodType = message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, - ), - ) - -export const flagSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Flag'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'entered-in-error', 'inactive']), - _status: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - subject: z.lazy(() => referenceSchema), - period: z.lazy(() => periodSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - }), -) - -export const goalTargetSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - measure: z.lazy(() => codeableConceptSchema).optional(), - detailQuantity: z.lazy(() => quantitySchema).optional(), - detailRange: z.lazy(() => rangeSchema).optional(), - detailCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - detailString: stringSchema.optional(), - _detailString: z.lazy(() => elementSchema).optional(), - detailBoolean: booleanSchema.optional(), - _detailBoolean: z.lazy(() => elementSchema).optional(), - detailInteger: integerSchema.optional(), - _detailInteger: z.lazy(() => elementSchema).optional(), - detailRatio: z.lazy(() => ratioSchema).optional(), - dueDate: dateSchema.optional(), - _dueDate: z.lazy(() => elementSchema).optional(), - dueDuration: z.lazy(() => durationSchema).optional(), - }) - .refine( - (value) => - [ + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'intent', shadow: '_intent' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ { - names: [ - 'detailQuantity', - 'detailRange', - 'detailCodeableConcept', - 'detailString', - 'detailBoolean', - 'detailInteger', - 'detailRatio', - ], + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', required: false, }, - { names: ['dueDate', 'dueDuration'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const goalSchema: z.ZodType = z.lazy(() => +export const deviceUseStatementSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Goal'), + resourceType: z.literal('DeviceUseStatement'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -14316,72 +15942,86 @@ export const goalSchema: z.ZodType = z.lazy(() => contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - lifecycleStatus: z.enum([ - 'accepted', - 'active', - 'cancelled', - 'completed', - 'entered-in-error', - 'on-hold', - 'planned', - 'proposed', - 'rejected', - ]), - _lifecycleStatus: z.lazy(() => elementSchema).optional(), - achievementStatus: z.lazy(() => codeableConceptSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) + basedOn: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - priority: z.lazy(() => codeableConceptSchema).optional(), - description: z.lazy(() => codeableConceptSchema), + status: z + .enum([ + 'active', + 'completed', + 'entered-in-error', + 'intended', + 'on-hold', + 'stopped', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), subject: z.lazy(() => referenceSchema), - startDate: dateSchema.optional(), - _startDate: z.lazy(() => elementSchema).optional(), - startCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - target: goalTargetSchema.array().optional(), - statusDate: dateSchema.optional(), - _statusDate: z.lazy(() => elementSchema).optional(), - statusReason: stringSchema.optional(), - _statusReason: z.lazy(() => elementSchema).optional(), - expressedBy: z.lazy(() => referenceSchema).optional(), - addresses: z + derivedFrom: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - outcomeCode: z + timingTiming: z.lazy(() => timingSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + recordedOn: dateTimeSchema.optional(), + _recordedOn: z.lazy(() => elementSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + device: z.lazy(() => referenceSchema), + reasonCode: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - outcomeReference: z + reasonReference: z .lazy(() => referenceSchema) .array() + .min(1) + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) .optional(), }) .refine( (value) => [ - { names: ['startDate', 'startCodeableConcept'], required: false }, + { + alternatives: [ + ['timingTiming'], + ['timingPeriod'], + ['timingDateTime', '_timingDateTime'], + ], + required: false, + }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -14389,484 +16029,176 @@ export const goalSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const graphDefinitionLinkSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const diagnosticReportMediaSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - path: stringSchema.optional(), - _path: z.lazy(() => elementSchema).optional(), - sliceName: stringSchema.optional(), - _sliceName: z.lazy(() => elementSchema).optional(), - min: integerSchema.optional(), - _min: z.lazy(() => elementSchema).optional(), - max: stringSchema.optional(), - _max: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - target: graphDefinitionLinkTargetSchema.array().optional(), - }), - ) - -export const graphDefinitionLinkTargetSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum([ - 'Account', - 'ActivityDefinition', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RegulatedAuthorization', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]), - _type: z.lazy(() => elementSchema).optional(), - params: stringSchema.optional(), - _params: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.optional(), - _profile: z.lazy(() => elementSchema).optional(), - compartment: graphDefinitionLinkTargetCompartmentSchema - .array() - .optional(), - link: z - .lazy(() => graphDefinitionLinkSchema) - .array() - .optional(), - }), - ) + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + link: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const graphDefinitionLinkTargetCompartmentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - use: z.enum(['condition', 'requirement']), - _use: z.lazy(() => elementSchema).optional(), - code: z.enum([ - 'Device', - 'Encounter', - 'Patient', - 'Practitioner', - 'RelatedPerson', - ]), - _code: z.lazy(() => elementSchema).optional(), - rule: z.enum(['custom', 'different', 'identical', 'matching']), - _rule: z.lazy(() => elementSchema).optional(), - expression: stringSchema.optional(), - _expression: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - }), - ) - -export const graphDefinitionSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('GraphDefinition'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - start: z.enum([ - 'Account', - 'ActivityDefinition', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RegulatedAuthorization', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]), - _start: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.optional(), - _profile: z.lazy(() => elementSchema).optional(), - link: graphDefinitionLinkSchema.array().optional(), - }), -) - -export const groupCharacteristicSchema: z.ZodType = +export const diagnosticReportSchema: z.ZodType> = z.lazy(() => z .strictObject({ - id: stringSchema.optional(), + resourceType: z.literal('DiagnosticReport'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'amended', + 'appended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'partial', + 'preliminary', + 'registered', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) .optional(), code: z.lazy(() => codeableConceptSchema), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - exclude: booleanSchema, - _exclude: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + issued: instantSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + performer: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + resultsInterpreter: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + specimen: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + result: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + imagingStudy: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + media: diagnosticReportMediaSchema.array().min(1).optional(), + conclusion: stringSchema.optional(), + _conclusion: z.lazy(() => elementSchema).optional(), + conclusionCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + presentedForm: z + .lazy(() => attachmentSchema) + .array() + .min(1) + .optional(), }) .refine( (value) => [ { - names: [ - 'valueCodeableConcept', - 'valueBoolean', - 'valueQuantity', - 'valueRange', - 'valueReference', + alternatives: [ + ['effectiveDateTime', '_effectiveDateTime'], + ['effectivePeriod'], ], - required: true, + required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -14874,83 +16206,100 @@ export const groupCharacteristicSchema: z.ZodType = message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) + ) as unknown as z.ZodType> -export const groupMemberSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - entity: z.lazy(() => referenceSchema), - period: z.lazy(() => periodSchema).optional(), - inactive: booleanSchema.optional(), - _inactive: z.lazy(() => elementSchema).optional(), - }), -) - -export const groupSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Group'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - type: z.enum([ - 'animal', - 'device', - 'medication', - 'person', - 'practitioner', - 'substance', - ]), - _type: z.lazy(() => elementSchema).optional(), - actual: booleanSchema, - _actual: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - quantity: unsignedIntSchema.optional(), - _quantity: z.lazy(() => elementSchema).optional(), - managingEntity: z.lazy(() => referenceSchema).optional(), - characteristic: groupCharacteristicSchema.array().optional(), - member: groupMemberSchema.array().optional(), - }), -) - -export const guidanceResponseSchema: z.ZodType = z.lazy( - () => +export const distanceSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const quantity = value as Record + const hasCode = + quantity.code !== undefined || quantity._code !== undefined + return ( + !hasCode || + quantity.system !== undefined || + quantity._system !== undefined + ) + }, + { message: 'qty-3: a coded Quantity requires its unit system.' }, + ), +) as unknown as z.ZodType> + +export const documentManifestRelatedSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + ref: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const documentManifestSchema: z.ZodType> = + z.lazy(() => z .strictObject({ - resourceType: z.literal('GuidanceResponse'), + resourceType: z.literal('DocumentManifest'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -14962,151 +16311,191 @@ export const guidanceResponseSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - requestIdentifier: z.lazy(() => identifierSchema).optional(), + masterIdentifier: z.lazy(() => identifierSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) + .optional(), + status: z + .enum(['current', 'entered-in-error', 'superseded']) .optional(), - moduleUri: uriSchema.optional(), - _moduleUri: z.lazy(() => elementSchema).optional(), - moduleCanonical: canonicalSchema.optional(), - _moduleCanonical: z.lazy(() => elementSchema).optional(), - moduleCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - status: z.enum([ - 'data-requested', - 'data-required', - 'entered-in-error', - 'failure', - 'in-progress', - 'success', - ]), _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - performer: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + author: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - evaluationMessage: z + recipient: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - outputParameters: z.lazy(() => referenceSchema).optional(), - result: z.lazy(() => referenceSchema).optional(), - dataRequirement: z - .lazy(() => dataRequirementSchema) + source: uriSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + content: z + .lazy(() => referenceSchema) .array() - .optional(), + .min(1), + related: documentManifestRelatedSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['moduleUri', 'moduleCanonical', 'moduleCodeableConcept'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), -) + ) as unknown as z.ZodType> -export const healthcareServiceEligibilitySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const documentReferenceRelatesToSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - comment: markdownSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - }), - ) + code: z.enum(['appends', 'replaces', 'signs', 'transforms']).optional(), + _code: z.lazy(() => elementSchema).optional(), + target: z.lazy(() => referenceSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const healthcareServiceAvailableTimeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const documentReferenceContentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - daysOfWeek: z - .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) - .array() - .optional(), - _daysOfWeek: z - .lazy(() => elementSchema) - .array() - .optional(), - allDay: booleanSchema.optional(), - _allDay: z.lazy(() => elementSchema).optional(), - availableStartTime: timeSchema.optional(), - _availableStartTime: z.lazy(() => elementSchema).optional(), - availableEndTime: timeSchema.optional(), - _availableEndTime: z.lazy(() => elementSchema).optional(), - }), - ) + attachment: z.lazy(() => attachmentSchema), + format: z.lazy(() => codingSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const healthcareServiceNotAvailableSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const documentReferenceContextSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: stringSchema, - _description: z.lazy(() => elementSchema).optional(), - during: z.lazy(() => periodSchema).optional(), - }), - ) + encounter: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + event: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + facilityType: z.lazy(() => codeableConceptSchema).optional(), + practiceSetting: z.lazy(() => codeableConceptSchema).optional(), + sourcePatientInfo: z.lazy(() => referenceSchema).optional(), + related: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const healthcareServiceSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('HealthcareService'), +export const documentReferenceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('DocumentReference'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -15118,509 +16507,594 @@ export const healthcareServiceSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), + masterIdentifier: z.lazy(() => identifierSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - providedBy: z.lazy(() => referenceSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() + status: z.enum(['current', 'entered-in-error', 'superseded']).optional(), + _status: z.lazy(() => elementSchema).optional(), + docStatus: z + .enum(['amended', 'entered-in-error', 'final', 'preliminary']) .optional(), - specialty: z + _docStatus: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + category: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - location: z - .lazy(() => referenceSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - extraDetails: markdownSchema.optional(), - _extraDetails: z.lazy(() => elementSchema).optional(), - photo: z.lazy(() => attachmentSchema).optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - coverageArea: z + subject: z.lazy(() => referenceSchema).optional(), + date: instantSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + author: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - serviceProvisionCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - eligibility: healthcareServiceEligibilitySchema.array().optional(), - program: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - characteristic: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - communication: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - referralMethod: z + authenticator: z.lazy(() => referenceSchema).optional(), + custodian: z.lazy(() => referenceSchema).optional(), + relatesTo: documentReferenceRelatesToSchema.array().min(1).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + securityLabel: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - appointmentRequired: booleanSchema.optional(), - _appointmentRequired: z.lazy(() => elementSchema).optional(), - availableTime: healthcareServiceAvailableTimeSchema.array().optional(), - notAvailable: healthcareServiceNotAvailableSchema.array().optional(), - availabilityExceptions: stringSchema.optional(), - _availabilityExceptions: z.lazy(() => elementSchema).optional(), - endpoint: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) - -export const humanNameSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - use: z - .enum([ - 'anonymous', - 'maiden', - 'nickname', - 'official', - 'old', - 'temp', - 'usual', - ]) - .optional(), - _use: z.lazy(() => elementSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - family: stringSchema.optional(), - _family: z.lazy(() => elementSchema).optional(), - given: stringSchema.array().optional(), - _given: z - .lazy(() => elementSchema) - .array() - .optional(), - prefix: stringSchema.array().optional(), - _prefix: z - .lazy(() => elementSchema) - .array() - .optional(), - suffix: stringSchema.array().optional(), - _suffix: z - .lazy(() => elementSchema) - .array() - .optional(), - period: z.lazy(() => periodSchema).optional(), - }), -) - -export const identifierSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - use: z.enum(['official', 'old', 'secondary', 'temp', 'usual']).optional(), - _use: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - value: stringSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - assigner: z.lazy(() => referenceSchema).optional(), - }), -) - -export const imagingStudySeriesSchema: z.ZodType = - z.lazy(() => - z.strictObject({ + content: documentReferenceContentSchema.array().min(1), + context: documentReferenceContextSchema.optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const dosageDoseAndRateSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z + type: z.lazy(() => codeableConceptSchema).optional(), + doseRange: z.lazy(() => rangeSchema).optional(), + doseQuantity: z.lazy(() => quantitySchema).optional(), + rateRatio: z.lazy(() => ratioSchema).optional(), + rateRange: z.lazy(() => rangeSchema).optional(), + rateQuantity: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => + [ + { alternatives: [['doseRange'], ['doseQuantity']], required: false }, + { + alternatives: [['rateRatio'], ['rateRange'], ['rateQuantity']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const dosageSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - uid: idSchema, - _uid: z.lazy(() => elementSchema).optional(), - number: unsignedIntSchema.optional(), - _number: z.lazy(() => elementSchema).optional(), - modality: z.lazy(() => codingSchema), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - numberOfInstances: unsignedIntSchema.optional(), - _numberOfInstances: z.lazy(() => elementSchema).optional(), - endpoint: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - bodySite: z.lazy(() => codingSchema).optional(), - laterality: z.lazy(() => codingSchema).optional(), - specimen: z - .lazy(() => referenceSchema) + sequence: integerSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + additionalInstruction: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - started: dateTimeSchema.optional(), - _started: z.lazy(() => elementSchema).optional(), - performer: imagingStudySeriesPerformerSchema.array().optional(), - instance: imagingStudySeriesInstanceSchema.array().optional(), - }), - ) + patientInstruction: stringSchema.optional(), + _patientInstruction: z.lazy(() => elementSchema).optional(), + timing: z.lazy(() => timingSchema).optional(), + asNeededBoolean: booleanSchema.optional(), + _asNeededBoolean: z.lazy(() => elementSchema).optional(), + asNeededCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + site: z.lazy(() => codeableConceptSchema).optional(), + route: z.lazy(() => codeableConceptSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + doseAndRate: dosageDoseAndRateSchema.array().min(1).optional(), + maxDosePerPeriod: z.lazy(() => ratioSchema).optional(), + maxDosePerAdministration: z.lazy(() => quantitySchema).optional(), + maxDosePerLifetime: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['asNeededBoolean', '_asNeededBoolean'], + ['asNeededCodeableConcept'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const imagingStudySeriesPerformerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const durationSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const quantity = value as Record + const hasCode = + quantity.code !== undefined || quantity._code !== undefined + return ( + !hasCode || + quantity.system !== undefined || + quantity._system !== undefined + ) + }, + { message: 'qty-3: a coded Quantity requires its unit system.' }, + ), +) as unknown as z.ZodType> + +export const elementSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - function: z.lazy(() => codeableConceptSchema).optional(), - actor: z.lazy(() => referenceSchema), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const imagingStudySeriesInstanceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const elementDefinitionSlicingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + discriminator: elementDefinitionSlicingDiscriminatorSchema .array() + .min(1) .optional(), - uid: idSchema, - _uid: z.lazy(() => elementSchema).optional(), - sopClass: z.lazy(() => codingSchema), - number: unsignedIntSchema.optional(), - _number: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - }), - ) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + ordered: booleanSchema.optional(), + _ordered: z.lazy(() => elementSchema).optional(), + rules: z.enum(['closed', 'open', 'openAtEnd']).optional(), + _rules: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'rules', shadow: '_rules' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const imagingStudySchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('ImagingStudy'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'available', - 'cancelled', - 'entered-in-error', - 'registered', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - modality: z - .lazy(() => codingSchema) - .array() - .optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - started: dateTimeSchema.optional(), - _started: z.lazy(() => elementSchema).optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - referrer: z.lazy(() => referenceSchema).optional(), - interpreter: z - .lazy(() => referenceSchema) - .array() - .optional(), - endpoint: z - .lazy(() => referenceSchema) - .array() - .optional(), - numberOfSeries: unsignedIntSchema.optional(), - _numberOfSeries: z.lazy(() => elementSchema).optional(), - numberOfInstances: unsignedIntSchema.optional(), - _numberOfInstances: z.lazy(() => elementSchema).optional(), - procedureReference: z.lazy(() => referenceSchema).optional(), - procedureCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - location: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - series: imagingStudySeriesSchema.array().optional(), - }), -) - -export const immunizationPerformerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const elementDefinitionBaseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - function: z.lazy(() => codeableConceptSchema).optional(), - actor: z.lazy(() => referenceSchema), - }), - ) + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + min: unsignedIntSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'path', shadow: '_path' }, + { name: 'min', shadow: '_min' }, + { name: 'max', shadow: '_max' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const immunizationEducationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const elementDefinitionTypeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + code: uriSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.nullable().array().min(1).optional(), + _profile: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - documentType: stringSchema.optional(), - _documentType: z.lazy(() => elementSchema).optional(), - reference: uriSchema.optional(), - _reference: z.lazy(() => elementSchema).optional(), - publicationDate: dateTimeSchema.optional(), - _publicationDate: z.lazy(() => elementSchema).optional(), - presentationDate: dateTimeSchema.optional(), - _presentationDate: z.lazy(() => elementSchema).optional(), - }), - ) - -export const immunizationReactionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + targetProfile: canonicalSchema.nullable().array().min(1).optional(), + _targetProfile: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + aggregation: z + .enum(['bundled', 'contained', 'referenced']) + .nullable() .array() + .min(1) .optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - detail: z.lazy(() => referenceSchema).optional(), - reported: booleanSchema.optional(), - _reported: z.lazy(() => elementSchema).optional(), - }), - ) - -export const immunizationProtocolAppliedSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - series: stringSchema.optional(), - _series: z.lazy(() => elementSchema).optional(), - authority: z.lazy(() => referenceSchema).optional(), - targetDisease: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - doseNumberPositiveInt: positiveIntSchema.optional(), - _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), - doseNumberString: stringSchema.optional(), - _doseNumberString: z.lazy(() => elementSchema).optional(), - seriesDosesPositiveInt: positiveIntSchema.optional(), - _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), - seriesDosesString: stringSchema.optional(), - _seriesDosesString: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['doseNumberPositiveInt', 'doseNumberString'], - required: true, - }, - { - names: ['seriesDosesPositiveInt', 'seriesDosesString'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + _aggregation: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + versioning: z.enum(['either', 'independent', 'specific']).optional(), + _versioning: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'profile', shadow: '_profile', required: false }, + { name: 'targetProfile', shadow: '_targetProfile', required: false }, + { name: 'aggregation', shadow: '_aggregation', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const immunizationSchema: z.ZodType = z.lazy(() => +export const elementDefinitionExampleSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Immunization'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['completed', 'entered-in-error', 'not-done']), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - vaccineCode: z.lazy(() => codeableConceptSchema), - patient: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrenceString: stringSchema.optional(), - _occurrenceString: z.lazy(() => elementSchema).optional(), - recorded: dateTimeSchema.optional(), - _recorded: z.lazy(() => elementSchema).optional(), - primarySource: booleanSchema.optional(), - _primarySource: z.lazy(() => elementSchema).optional(), - reportOrigin: z.lazy(() => codeableConceptSchema).optional(), - location: z.lazy(() => referenceSchema).optional(), - manufacturer: z.lazy(() => referenceSchema).optional(), - lotNumber: stringSchema.optional(), - _lotNumber: z.lazy(() => elementSchema).optional(), - expirationDate: dateSchema.optional(), - _expirationDate: z.lazy(() => elementSchema).optional(), - site: z.lazy(() => codeableConceptSchema).optional(), - route: z.lazy(() => codeableConceptSchema).optional(), - doseQuantity: z.lazy(() => quantitySchema).optional(), - performer: immunizationPerformerSchema.array().optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - isSubpotent: booleanSchema.optional(), - _isSubpotent: z.lazy(() => elementSchema).optional(), - subpotentReason: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - education: immunizationEducationSchema.array().optional(), - programEligibility: z - .lazy(() => codeableConceptSchema) - .array() + label: stringSchema.optional(), + _label: z.lazy(() => elementSchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCodeableReference: z.lazy(() => codeableReferenceSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueRatioRange: z.lazy(() => ratioRangeSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) .optional(), - fundingSource: z.lazy(() => codeableConceptSchema).optional(), - reaction: immunizationReactionSchema.array().optional(), - protocolApplied: immunizationProtocolAppliedSchema.array().optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), }) .refine( (value) => [ - { names: ['occurrenceDateTime', 'occurrenceString'], required: true }, + { + alternatives: [ + ['valueBase64Binary', '_valueBase64Binary'], + ['valueBoolean', '_valueBoolean'], + ['valueCanonical', '_valueCanonical'], + ['valueCode', '_valueCode'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueDecimal', '_valueDecimal'], + ['valueId', '_valueId'], + ['valueInstant', '_valueInstant'], + ['valueInteger', '_valueInteger'], + ['valueMarkdown', '_valueMarkdown'], + ['valueOid', '_valueOid'], + ['valuePositiveInt', '_valuePositiveInt'], + ['valueString', '_valueString'], + ['valueTime', '_valueTime'], + ['valueUnsignedInt', '_valueUnsignedInt'], + ['valueUri', '_valueUri'], + ['valueUrl', '_valueUrl'], + ['valueUuid', '_valueUuid'], + ['valueAddress'], + ['valueAge'], + ['valueAnnotation'], + ['valueAttachment'], + ['valueCodeableConcept'], + ['valueCodeableReference'], + ['valueCoding'], + ['valueContactPoint'], + ['valueCount'], + ['valueDistance'], + ['valueDuration'], + ['valueHumanName'], + ['valueIdentifier'], + ['valueMoney'], + ['valuePeriod'], + ['valueQuantity'], + ['valueRange'], + ['valueRatio'], + ['valueRatioRange'], + ['valueReference'], + ['valueSampledData'], + ['valueSignature'], + ['valueTiming'], + ['valueContactDetail'], + ['valueContributor'], + ['valueDataRequirement'], + ['valueExpression'], + ['valueParameterDefinition'], + ['valueRelatedArtifact'], + ['valueTriggerDefinition'], + ['valueUsageContext'], + ['valueDosage'], + ], + required: true, + }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -15628,712 +17102,1059 @@ export const immunizationSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'label', shadow: '_label' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const immunizationEvaluationSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - resourceType: z.literal('ImmunizationEvaluation'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['completed', 'entered-in-error']), - _status: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - authority: z.lazy(() => referenceSchema).optional(), - targetDisease: z.lazy(() => codeableConceptSchema), - immunizationEvent: z.lazy(() => referenceSchema), - doseStatus: z.lazy(() => codeableConceptSchema), - doseStatusReason: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - series: stringSchema.optional(), - _series: z.lazy(() => elementSchema).optional(), - doseNumberPositiveInt: positiveIntSchema.optional(), - _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), - doseNumberString: stringSchema.optional(), - _doseNumberString: z.lazy(() => elementSchema).optional(), - seriesDosesPositiveInt: positiveIntSchema.optional(), - _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), - seriesDosesString: stringSchema.optional(), - _seriesDosesString: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['doseNumberPositiveInt', 'doseNumberString'], - required: false, - }, - { - names: ['seriesDosesPositiveInt', 'seriesDosesString'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) +) as unknown as z.ZodType> -export const immunizationRecommendationRecommendationSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - vaccineCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - targetDisease: z.lazy(() => codeableConceptSchema).optional(), - contraindicatedVaccineCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - forecastStatus: z.lazy(() => codeableConceptSchema), - forecastReason: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - dateCriterion: - immunizationRecommendationRecommendationDateCriterionSchema - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - series: stringSchema.optional(), - _series: z.lazy(() => elementSchema).optional(), - doseNumberPositiveInt: positiveIntSchema.optional(), - _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), - doseNumberString: stringSchema.optional(), - _doseNumberString: z.lazy(() => elementSchema).optional(), - seriesDosesPositiveInt: positiveIntSchema.optional(), - _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), - seriesDosesString: stringSchema.optional(), - _seriesDosesString: z.lazy(() => elementSchema).optional(), - supportingImmunization: z - .lazy(() => referenceSchema) - .array() - .optional(), - supportingPatientInformation: z - .lazy(() => referenceSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['doseNumberPositiveInt', 'doseNumberString'], - required: false, - }, - { - names: ['seriesDosesPositiveInt', 'seriesDosesString'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const immunizationRecommendationRecommendationDateCriterionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const elementDefinitionConstraintSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z + key: idSchema.optional(), + _key: z.lazy(() => elementSchema).optional(), + requirements: stringSchema.optional(), + _requirements: z.lazy(() => elementSchema).optional(), + severity: z.enum(['error', 'warning']).optional(), + _severity: z.lazy(() => elementSchema).optional(), + human: stringSchema.optional(), + _human: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + xpath: stringSchema.optional(), + _xpath: z.lazy(() => elementSchema).optional(), + source: canonicalSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'key', shadow: '_key' }, + { name: 'severity', shadow: '_severity' }, + { name: 'human', shadow: '_human' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const elementDefinitionBindingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema), - value: dateTimeSchema, - _value: z.lazy(() => elementSchema).optional(), - }), - ) + strength: z + .enum(['example', 'extensible', 'preferred', 'required']) + .optional(), + _strength: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema.optional(), + _valueSet: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'strength', shadow: '_strength' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const immunizationRecommendationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('ImmunizationRecommendation'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), +export const elementDefinitionMappingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identity: idSchema.optional(), + _identity: z.lazy(() => elementSchema).optional(), language: codeSchema.optional(), _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + map: stringSchema.optional(), + _map: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'identity', shadow: '_identity' }, + { name: 'map', shadow: '_map' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const elementDefinitionSlicingDiscriminatorSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + type: z + .enum(['exists', 'pattern', 'profile', 'type', 'value']) .optional(), + _type: z.lazy(() => elementSchema).optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'type', shadow: '_type' }, + { name: 'path', shadow: '_path' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const elementDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + representation: z + .enum(['cdaText', 'typeAttr', 'xhtml', 'xmlAttr', 'xmlText']) + .nullable() .array() + .min(1) .optional(), - patient: z.lazy(() => referenceSchema), - date: dateTimeSchema, - _date: z.lazy(() => elementSchema).optional(), - authority: z.lazy(() => referenceSchema).optional(), - recommendation: immunizationRecommendationRecommendationSchema + _representation: z + .lazy(() => elementSchema) + .nullable() .array() - .min(1), - }), - ) + .min(1) + .optional(), + sliceName: stringSchema.optional(), + _sliceName: z.lazy(() => elementSchema).optional(), + sliceIsConstraining: booleanSchema.optional(), + _sliceIsConstraining: z.lazy(() => elementSchema).optional(), + label: stringSchema.optional(), + _label: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + slicing: elementDefinitionSlicingSchema.optional(), + short: stringSchema.optional(), + _short: z.lazy(() => elementSchema).optional(), + definition: markdownSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + comment: markdownSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + requirements: markdownSchema.optional(), + _requirements: z.lazy(() => elementSchema).optional(), + alias: stringSchema.nullable().array().min(1).optional(), + _alias: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + min: unsignedIntSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + base: elementDefinitionBaseSchema.optional(), + contentReference: uriSchema.optional(), + _contentReference: z.lazy(() => elementSchema).optional(), + type: elementDefinitionTypeSchema.array().min(1).optional(), + defaultValueBase64Binary: base64BinarySchema.optional(), + _defaultValueBase64Binary: z.lazy(() => elementSchema).optional(), + defaultValueBoolean: booleanSchema.optional(), + _defaultValueBoolean: z.lazy(() => elementSchema).optional(), + defaultValueCanonical: canonicalSchema.optional(), + _defaultValueCanonical: z.lazy(() => elementSchema).optional(), + defaultValueCode: codeSchema.optional(), + _defaultValueCode: z.lazy(() => elementSchema).optional(), + defaultValueDate: dateSchema.optional(), + _defaultValueDate: z.lazy(() => elementSchema).optional(), + defaultValueDateTime: dateTimeSchema.optional(), + _defaultValueDateTime: z.lazy(() => elementSchema).optional(), + defaultValueDecimal: decimalSchema.optional(), + _defaultValueDecimal: z.lazy(() => elementSchema).optional(), + defaultValueId: idSchema.optional(), + _defaultValueId: z.lazy(() => elementSchema).optional(), + defaultValueInstant: instantSchema.optional(), + _defaultValueInstant: z.lazy(() => elementSchema).optional(), + defaultValueInteger: integerSchema.optional(), + _defaultValueInteger: z.lazy(() => elementSchema).optional(), + defaultValueMarkdown: markdownSchema.optional(), + _defaultValueMarkdown: z.lazy(() => elementSchema).optional(), + defaultValueOid: oidSchema.optional(), + _defaultValueOid: z.lazy(() => elementSchema).optional(), + defaultValuePositiveInt: positiveIntSchema.optional(), + _defaultValuePositiveInt: z.lazy(() => elementSchema).optional(), + defaultValueString: stringSchema.optional(), + _defaultValueString: z.lazy(() => elementSchema).optional(), + defaultValueTime: timeSchema.optional(), + _defaultValueTime: z.lazy(() => elementSchema).optional(), + defaultValueUnsignedInt: unsignedIntSchema.optional(), + _defaultValueUnsignedInt: z.lazy(() => elementSchema).optional(), + defaultValueUri: uriSchema.optional(), + _defaultValueUri: z.lazy(() => elementSchema).optional(), + defaultValueUrl: urlSchema.optional(), + _defaultValueUrl: z.lazy(() => elementSchema).optional(), + defaultValueUuid: uuidSchema.optional(), + _defaultValueUuid: z.lazy(() => elementSchema).optional(), + defaultValueAddress: z.lazy(() => addressSchema).optional(), + defaultValueAge: z.lazy(() => ageSchema).optional(), + defaultValueAnnotation: z.lazy(() => annotationSchema).optional(), + defaultValueAttachment: z.lazy(() => attachmentSchema).optional(), + defaultValueCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + defaultValueCodeableReference: z + .lazy(() => codeableReferenceSchema) + .optional(), + defaultValueCoding: z.lazy(() => codingSchema).optional(), + defaultValueContactPoint: z.lazy(() => contactPointSchema).optional(), + defaultValueCount: z.lazy(() => countSchema).optional(), + defaultValueDistance: z.lazy(() => distanceSchema).optional(), + defaultValueDuration: z.lazy(() => durationSchema).optional(), + defaultValueHumanName: z.lazy(() => humanNameSchema).optional(), + defaultValueIdentifier: z.lazy(() => identifierSchema).optional(), + defaultValueMoney: z.lazy(() => moneySchema).optional(), + defaultValuePeriod: z.lazy(() => periodSchema).optional(), + defaultValueQuantity: z.lazy(() => quantitySchema).optional(), + defaultValueRange: z.lazy(() => rangeSchema).optional(), + defaultValueRatio: z.lazy(() => ratioSchema).optional(), + defaultValueRatioRange: z.lazy(() => ratioRangeSchema).optional(), + defaultValueReference: z.lazy(() => referenceSchema).optional(), + defaultValueSampledData: z.lazy(() => sampledDataSchema).optional(), + defaultValueSignature: z.lazy(() => signatureSchema).optional(), + defaultValueTiming: z.lazy(() => timingSchema).optional(), + defaultValueContactDetail: z.lazy(() => contactDetailSchema).optional(), + defaultValueContributor: z.lazy(() => contributorSchema).optional(), + defaultValueDataRequirement: z + .lazy(() => dataRequirementSchema) + .optional(), + defaultValueExpression: z.lazy(() => expressionSchema).optional(), + defaultValueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + defaultValueRelatedArtifact: z + .lazy(() => relatedArtifactSchema) + .optional(), + defaultValueTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + defaultValueUsageContext: z.lazy(() => usageContextSchema).optional(), + defaultValueDosage: z.lazy(() => dosageSchema).optional(), + meaningWhenMissing: markdownSchema.optional(), + _meaningWhenMissing: z.lazy(() => elementSchema).optional(), + orderMeaning: stringSchema.optional(), + _orderMeaning: z.lazy(() => elementSchema).optional(), + fixedBase64Binary: base64BinarySchema.optional(), + _fixedBase64Binary: z.lazy(() => elementSchema).optional(), + fixedBoolean: booleanSchema.optional(), + _fixedBoolean: z.lazy(() => elementSchema).optional(), + fixedCanonical: canonicalSchema.optional(), + _fixedCanonical: z.lazy(() => elementSchema).optional(), + fixedCode: codeSchema.optional(), + _fixedCode: z.lazy(() => elementSchema).optional(), + fixedDate: dateSchema.optional(), + _fixedDate: z.lazy(() => elementSchema).optional(), + fixedDateTime: dateTimeSchema.optional(), + _fixedDateTime: z.lazy(() => elementSchema).optional(), + fixedDecimal: decimalSchema.optional(), + _fixedDecimal: z.lazy(() => elementSchema).optional(), + fixedId: idSchema.optional(), + _fixedId: z.lazy(() => elementSchema).optional(), + fixedInstant: instantSchema.optional(), + _fixedInstant: z.lazy(() => elementSchema).optional(), + fixedInteger: integerSchema.optional(), + _fixedInteger: z.lazy(() => elementSchema).optional(), + fixedMarkdown: markdownSchema.optional(), + _fixedMarkdown: z.lazy(() => elementSchema).optional(), + fixedOid: oidSchema.optional(), + _fixedOid: z.lazy(() => elementSchema).optional(), + fixedPositiveInt: positiveIntSchema.optional(), + _fixedPositiveInt: z.lazy(() => elementSchema).optional(), + fixedString: stringSchema.optional(), + _fixedString: z.lazy(() => elementSchema).optional(), + fixedTime: timeSchema.optional(), + _fixedTime: z.lazy(() => elementSchema).optional(), + fixedUnsignedInt: unsignedIntSchema.optional(), + _fixedUnsignedInt: z.lazy(() => elementSchema).optional(), + fixedUri: uriSchema.optional(), + _fixedUri: z.lazy(() => elementSchema).optional(), + fixedUrl: urlSchema.optional(), + _fixedUrl: z.lazy(() => elementSchema).optional(), + fixedUuid: uuidSchema.optional(), + _fixedUuid: z.lazy(() => elementSchema).optional(), + fixedAddress: z.lazy(() => addressSchema).optional(), + fixedAge: z.lazy(() => ageSchema).optional(), + fixedAnnotation: z.lazy(() => annotationSchema).optional(), + fixedAttachment: z.lazy(() => attachmentSchema).optional(), + fixedCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + fixedCodeableReference: z.lazy(() => codeableReferenceSchema).optional(), + fixedCoding: z.lazy(() => codingSchema).optional(), + fixedContactPoint: z.lazy(() => contactPointSchema).optional(), + fixedCount: z.lazy(() => countSchema).optional(), + fixedDistance: z.lazy(() => distanceSchema).optional(), + fixedDuration: z.lazy(() => durationSchema).optional(), + fixedHumanName: z.lazy(() => humanNameSchema).optional(), + fixedIdentifier: z.lazy(() => identifierSchema).optional(), + fixedMoney: z.lazy(() => moneySchema).optional(), + fixedPeriod: z.lazy(() => periodSchema).optional(), + fixedQuantity: z.lazy(() => quantitySchema).optional(), + fixedRange: z.lazy(() => rangeSchema).optional(), + fixedRatio: z.lazy(() => ratioSchema).optional(), + fixedRatioRange: z.lazy(() => ratioRangeSchema).optional(), + fixedReference: z.lazy(() => referenceSchema).optional(), + fixedSampledData: z.lazy(() => sampledDataSchema).optional(), + fixedSignature: z.lazy(() => signatureSchema).optional(), + fixedTiming: z.lazy(() => timingSchema).optional(), + fixedContactDetail: z.lazy(() => contactDetailSchema).optional(), + fixedContributor: z.lazy(() => contributorSchema).optional(), + fixedDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + fixedExpression: z.lazy(() => expressionSchema).optional(), + fixedParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + fixedRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + fixedTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), + fixedUsageContext: z.lazy(() => usageContextSchema).optional(), + fixedDosage: z.lazy(() => dosageSchema).optional(), + patternBase64Binary: base64BinarySchema.optional(), + _patternBase64Binary: z.lazy(() => elementSchema).optional(), + patternBoolean: booleanSchema.optional(), + _patternBoolean: z.lazy(() => elementSchema).optional(), + patternCanonical: canonicalSchema.optional(), + _patternCanonical: z.lazy(() => elementSchema).optional(), + patternCode: codeSchema.optional(), + _patternCode: z.lazy(() => elementSchema).optional(), + patternDate: dateSchema.optional(), + _patternDate: z.lazy(() => elementSchema).optional(), + patternDateTime: dateTimeSchema.optional(), + _patternDateTime: z.lazy(() => elementSchema).optional(), + patternDecimal: decimalSchema.optional(), + _patternDecimal: z.lazy(() => elementSchema).optional(), + patternId: idSchema.optional(), + _patternId: z.lazy(() => elementSchema).optional(), + patternInstant: instantSchema.optional(), + _patternInstant: z.lazy(() => elementSchema).optional(), + patternInteger: integerSchema.optional(), + _patternInteger: z.lazy(() => elementSchema).optional(), + patternMarkdown: markdownSchema.optional(), + _patternMarkdown: z.lazy(() => elementSchema).optional(), + patternOid: oidSchema.optional(), + _patternOid: z.lazy(() => elementSchema).optional(), + patternPositiveInt: positiveIntSchema.optional(), + _patternPositiveInt: z.lazy(() => elementSchema).optional(), + patternString: stringSchema.optional(), + _patternString: z.lazy(() => elementSchema).optional(), + patternTime: timeSchema.optional(), + _patternTime: z.lazy(() => elementSchema).optional(), + patternUnsignedInt: unsignedIntSchema.optional(), + _patternUnsignedInt: z.lazy(() => elementSchema).optional(), + patternUri: uriSchema.optional(), + _patternUri: z.lazy(() => elementSchema).optional(), + patternUrl: urlSchema.optional(), + _patternUrl: z.lazy(() => elementSchema).optional(), + patternUuid: uuidSchema.optional(), + _patternUuid: z.lazy(() => elementSchema).optional(), + patternAddress: z.lazy(() => addressSchema).optional(), + patternAge: z.lazy(() => ageSchema).optional(), + patternAnnotation: z.lazy(() => annotationSchema).optional(), + patternAttachment: z.lazy(() => attachmentSchema).optional(), + patternCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + patternCodeableReference: z + .lazy(() => codeableReferenceSchema) + .optional(), + patternCoding: z.lazy(() => codingSchema).optional(), + patternContactPoint: z.lazy(() => contactPointSchema).optional(), + patternCount: z.lazy(() => countSchema).optional(), + patternDistance: z.lazy(() => distanceSchema).optional(), + patternDuration: z.lazy(() => durationSchema).optional(), + patternHumanName: z.lazy(() => humanNameSchema).optional(), + patternIdentifier: z.lazy(() => identifierSchema).optional(), + patternMoney: z.lazy(() => moneySchema).optional(), + patternPeriod: z.lazy(() => periodSchema).optional(), + patternQuantity: z.lazy(() => quantitySchema).optional(), + patternRange: z.lazy(() => rangeSchema).optional(), + patternRatio: z.lazy(() => ratioSchema).optional(), + patternRatioRange: z.lazy(() => ratioRangeSchema).optional(), + patternReference: z.lazy(() => referenceSchema).optional(), + patternSampledData: z.lazy(() => sampledDataSchema).optional(), + patternSignature: z.lazy(() => signatureSchema).optional(), + patternTiming: z.lazy(() => timingSchema).optional(), + patternContactDetail: z.lazy(() => contactDetailSchema).optional(), + patternContributor: z.lazy(() => contributorSchema).optional(), + patternDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + patternExpression: z.lazy(() => expressionSchema).optional(), + patternParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + patternRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + patternTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + patternUsageContext: z.lazy(() => usageContextSchema).optional(), + patternDosage: z.lazy(() => dosageSchema).optional(), + example: elementDefinitionExampleSchema.array().min(1).optional(), + minValueDate: dateSchema.optional(), + _minValueDate: z.lazy(() => elementSchema).optional(), + minValueDateTime: dateTimeSchema.optional(), + _minValueDateTime: z.lazy(() => elementSchema).optional(), + minValueInstant: instantSchema.optional(), + _minValueInstant: z.lazy(() => elementSchema).optional(), + minValueTime: timeSchema.optional(), + _minValueTime: z.lazy(() => elementSchema).optional(), + minValueDecimal: decimalSchema.optional(), + _minValueDecimal: z.lazy(() => elementSchema).optional(), + minValueInteger: integerSchema.optional(), + _minValueInteger: z.lazy(() => elementSchema).optional(), + minValuePositiveInt: positiveIntSchema.optional(), + _minValuePositiveInt: z.lazy(() => elementSchema).optional(), + minValueUnsignedInt: unsignedIntSchema.optional(), + _minValueUnsignedInt: z.lazy(() => elementSchema).optional(), + minValueQuantity: z.lazy(() => quantitySchema).optional(), + maxValueDate: dateSchema.optional(), + _maxValueDate: z.lazy(() => elementSchema).optional(), + maxValueDateTime: dateTimeSchema.optional(), + _maxValueDateTime: z.lazy(() => elementSchema).optional(), + maxValueInstant: instantSchema.optional(), + _maxValueInstant: z.lazy(() => elementSchema).optional(), + maxValueTime: timeSchema.optional(), + _maxValueTime: z.lazy(() => elementSchema).optional(), + maxValueDecimal: decimalSchema.optional(), + _maxValueDecimal: z.lazy(() => elementSchema).optional(), + maxValueInteger: integerSchema.optional(), + _maxValueInteger: z.lazy(() => elementSchema).optional(), + maxValuePositiveInt: positiveIntSchema.optional(), + _maxValuePositiveInt: z.lazy(() => elementSchema).optional(), + maxValueUnsignedInt: unsignedIntSchema.optional(), + _maxValueUnsignedInt: z.lazy(() => elementSchema).optional(), + maxValueQuantity: z.lazy(() => quantitySchema).optional(), + maxLength: integerSchema.optional(), + _maxLength: z.lazy(() => elementSchema).optional(), + condition: idSchema.nullable().array().min(1).optional(), + _condition: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + constraint: elementDefinitionConstraintSchema.array().min(1).optional(), + mustSupport: booleanSchema.optional(), + _mustSupport: z.lazy(() => elementSchema).optional(), + isModifier: booleanSchema.optional(), + _isModifier: z.lazy(() => elementSchema).optional(), + isModifierReason: stringSchema.optional(), + _isModifierReason: z.lazy(() => elementSchema).optional(), + isSummary: booleanSchema.optional(), + _isSummary: z.lazy(() => elementSchema).optional(), + binding: elementDefinitionBindingSchema.optional(), + mapping: elementDefinitionMappingSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['defaultValueBase64Binary', '_defaultValueBase64Binary'], + ['defaultValueBoolean', '_defaultValueBoolean'], + ['defaultValueCanonical', '_defaultValueCanonical'], + ['defaultValueCode', '_defaultValueCode'], + ['defaultValueDate', '_defaultValueDate'], + ['defaultValueDateTime', '_defaultValueDateTime'], + ['defaultValueDecimal', '_defaultValueDecimal'], + ['defaultValueId', '_defaultValueId'], + ['defaultValueInstant', '_defaultValueInstant'], + ['defaultValueInteger', '_defaultValueInteger'], + ['defaultValueMarkdown', '_defaultValueMarkdown'], + ['defaultValueOid', '_defaultValueOid'], + ['defaultValuePositiveInt', '_defaultValuePositiveInt'], + ['defaultValueString', '_defaultValueString'], + ['defaultValueTime', '_defaultValueTime'], + ['defaultValueUnsignedInt', '_defaultValueUnsignedInt'], + ['defaultValueUri', '_defaultValueUri'], + ['defaultValueUrl', '_defaultValueUrl'], + ['defaultValueUuid', '_defaultValueUuid'], + ['defaultValueAddress'], + ['defaultValueAge'], + ['defaultValueAnnotation'], + ['defaultValueAttachment'], + ['defaultValueCodeableConcept'], + ['defaultValueCodeableReference'], + ['defaultValueCoding'], + ['defaultValueContactPoint'], + ['defaultValueCount'], + ['defaultValueDistance'], + ['defaultValueDuration'], + ['defaultValueHumanName'], + ['defaultValueIdentifier'], + ['defaultValueMoney'], + ['defaultValuePeriod'], + ['defaultValueQuantity'], + ['defaultValueRange'], + ['defaultValueRatio'], + ['defaultValueRatioRange'], + ['defaultValueReference'], + ['defaultValueSampledData'], + ['defaultValueSignature'], + ['defaultValueTiming'], + ['defaultValueContactDetail'], + ['defaultValueContributor'], + ['defaultValueDataRequirement'], + ['defaultValueExpression'], + ['defaultValueParameterDefinition'], + ['defaultValueRelatedArtifact'], + ['defaultValueTriggerDefinition'], + ['defaultValueUsageContext'], + ['defaultValueDosage'], + ], + required: false, + }, + { + alternatives: [ + ['fixedBase64Binary', '_fixedBase64Binary'], + ['fixedBoolean', '_fixedBoolean'], + ['fixedCanonical', '_fixedCanonical'], + ['fixedCode', '_fixedCode'], + ['fixedDate', '_fixedDate'], + ['fixedDateTime', '_fixedDateTime'], + ['fixedDecimal', '_fixedDecimal'], + ['fixedId', '_fixedId'], + ['fixedInstant', '_fixedInstant'], + ['fixedInteger', '_fixedInteger'], + ['fixedMarkdown', '_fixedMarkdown'], + ['fixedOid', '_fixedOid'], + ['fixedPositiveInt', '_fixedPositiveInt'], + ['fixedString', '_fixedString'], + ['fixedTime', '_fixedTime'], + ['fixedUnsignedInt', '_fixedUnsignedInt'], + ['fixedUri', '_fixedUri'], + ['fixedUrl', '_fixedUrl'], + ['fixedUuid', '_fixedUuid'], + ['fixedAddress'], + ['fixedAge'], + ['fixedAnnotation'], + ['fixedAttachment'], + ['fixedCodeableConcept'], + ['fixedCodeableReference'], + ['fixedCoding'], + ['fixedContactPoint'], + ['fixedCount'], + ['fixedDistance'], + ['fixedDuration'], + ['fixedHumanName'], + ['fixedIdentifier'], + ['fixedMoney'], + ['fixedPeriod'], + ['fixedQuantity'], + ['fixedRange'], + ['fixedRatio'], + ['fixedRatioRange'], + ['fixedReference'], + ['fixedSampledData'], + ['fixedSignature'], + ['fixedTiming'], + ['fixedContactDetail'], + ['fixedContributor'], + ['fixedDataRequirement'], + ['fixedExpression'], + ['fixedParameterDefinition'], + ['fixedRelatedArtifact'], + ['fixedTriggerDefinition'], + ['fixedUsageContext'], + ['fixedDosage'], + ], + required: false, + }, + { + alternatives: [ + ['patternBase64Binary', '_patternBase64Binary'], + ['patternBoolean', '_patternBoolean'], + ['patternCanonical', '_patternCanonical'], + ['patternCode', '_patternCode'], + ['patternDate', '_patternDate'], + ['patternDateTime', '_patternDateTime'], + ['patternDecimal', '_patternDecimal'], + ['patternId', '_patternId'], + ['patternInstant', '_patternInstant'], + ['patternInteger', '_patternInteger'], + ['patternMarkdown', '_patternMarkdown'], + ['patternOid', '_patternOid'], + ['patternPositiveInt', '_patternPositiveInt'], + ['patternString', '_patternString'], + ['patternTime', '_patternTime'], + ['patternUnsignedInt', '_patternUnsignedInt'], + ['patternUri', '_patternUri'], + ['patternUrl', '_patternUrl'], + ['patternUuid', '_patternUuid'], + ['patternAddress'], + ['patternAge'], + ['patternAnnotation'], + ['patternAttachment'], + ['patternCodeableConcept'], + ['patternCodeableReference'], + ['patternCoding'], + ['patternContactPoint'], + ['patternCount'], + ['patternDistance'], + ['patternDuration'], + ['patternHumanName'], + ['patternIdentifier'], + ['patternMoney'], + ['patternPeriod'], + ['patternQuantity'], + ['patternRange'], + ['patternRatio'], + ['patternRatioRange'], + ['patternReference'], + ['patternSampledData'], + ['patternSignature'], + ['patternTiming'], + ['patternContactDetail'], + ['patternContributor'], + ['patternDataRequirement'], + ['patternExpression'], + ['patternParameterDefinition'], + ['patternRelatedArtifact'], + ['patternTriggerDefinition'], + ['patternUsageContext'], + ['patternDosage'], + ], + required: false, + }, + { + alternatives: [ + ['minValueDate', '_minValueDate'], + ['minValueDateTime', '_minValueDateTime'], + ['minValueInstant', '_minValueInstant'], + ['minValueTime', '_minValueTime'], + ['minValueDecimal', '_minValueDecimal'], + ['minValueInteger', '_minValueInteger'], + ['minValuePositiveInt', '_minValuePositiveInt'], + ['minValueUnsignedInt', '_minValueUnsignedInt'], + ['minValueQuantity'], + ], + required: false, + }, + { + alternatives: [ + ['maxValueDate', '_maxValueDate'], + ['maxValueDateTime', '_maxValueDateTime'], + ['maxValueInstant', '_maxValueInstant'], + ['maxValueTime', '_maxValueTime'], + ['maxValueDecimal', '_maxValueDecimal'], + ['maxValueInteger', '_maxValueInteger'], + ['maxValuePositiveInt', '_maxValuePositiveInt'], + ['maxValueUnsignedInt', '_maxValueUnsignedInt'], + ['maxValueQuantity'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'path', shadow: '_path' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'representation', shadow: '_representation', required: false }, + { name: 'alias', shadow: '_alias', required: false }, + { name: 'condition', shadow: '_condition', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const implementationGuideDependsOnSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const encounterStatusHistorySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - uri: canonicalSchema, - _uri: z.lazy(() => elementSchema).optional(), - packageId: idSchema.optional(), - _packageId: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - }), - ) - -export const implementationGuideGlobalSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum([ - 'Account', - 'ActivityDefinition', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RegulatedAuthorization', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]), - _type: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema, - _profile: z.lazy(() => elementSchema).optional(), - }), - ) + status: z + .enum([ + 'arrived', + 'cancelled', + 'entered-in-error', + 'finished', + 'in-progress', + 'onleave', + 'planned', + 'triaged', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const implementationGuideDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const encounterClassHistorySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - grouping: implementationGuideDefinitionGroupingSchema.array().optional(), - resource: implementationGuideDefinitionResourceSchema.array().min(1), - page: implementationGuideDefinitionPageSchema.optional(), - parameter: implementationGuideDefinitionParameterSchema - .array() - .optional(), - template: implementationGuideDefinitionTemplateSchema.array().optional(), - }), - ) + class: z.lazy(() => codingSchema), + period: z.lazy(() => periodSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const implementationGuideManifestSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const encounterParticipantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - rendering: urlSchema.optional(), - _rendering: z.lazy(() => elementSchema).optional(), - resource: implementationGuideManifestResourceSchema.array().min(1), - page: implementationGuideManifestPageSchema.array().optional(), - image: stringSchema.array().optional(), - _image: z - .lazy(() => elementSchema) - .array() - .optional(), - other: stringSchema.array().optional(), - _other: z - .lazy(() => elementSchema) + type: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - }), - ) + period: z.lazy(() => periodSchema).optional(), + individual: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const implementationGuideDefinitionGroupingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const encounterDiagnosisSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - }), - ) - -export const implementationGuideDefinitionResourceSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - reference: z.lazy(() => referenceSchema), - fhirVersion: z - .enum([ - '0.0.80', - '0.0.81', - '0.0.82', - '0.01', - '0.05', - '0.06', - '0.11', - '0.4.0', - '0.5.0', - '1.0.0', - '1.0.1', - '1.0.2', - '1.1.0', - '1.4.0', - '1.6.0', - '1.8.0', - '3.0.0', - '3.0.1', - '3.0.2', - '3.3.0', - '3.5.0', - '4.0.0', - '4.0.1', - '4.1.0', - '4.3.0', - '4.3.0-cibuild', - '4.3.0-snapshot1', - ]) - .array() - .optional(), - _fhirVersion: z - .lazy(() => elementSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - exampleBoolean: booleanSchema.optional(), - _exampleBoolean: z.lazy(() => elementSchema).optional(), - exampleCanonical: canonicalSchema.optional(), - _exampleCanonical: z.lazy(() => elementSchema).optional(), - groupingId: idSchema.optional(), - _groupingId: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['exampleBoolean', 'exampleCanonical'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const implementationGuideDefinitionPageSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - nameUrl: urlSchema.optional(), - _nameUrl: z.lazy(() => elementSchema).optional(), - nameReference: z.lazy(() => referenceSchema).optional(), - title: stringSchema, - _title: z.lazy(() => elementSchema).optional(), - generation: z.enum(['generated', 'html', 'markdown', 'xml']), - _generation: z.lazy(() => elementSchema).optional(), - page: z - .lazy(() => implementationGuideDefinitionPageSchema) - .array() - .optional(), - }) - .refine( - (value) => - [{ names: ['nameUrl', 'nameReference'], required: true }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + condition: z.lazy(() => referenceSchema), + use: z.lazy(() => codeableConceptSchema).optional(), + rank: positiveIntSchema.optional(), + _rank: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const implementationGuideDefinitionParameterSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const encounterHospitalizationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.enum([ - 'apply', - 'expansion-parameter', - 'generate-json', - 'generate-turtle', - 'generate-xml', - 'html-template', - 'path-pages', - 'path-resource', - 'path-tx-cache', - 'rule-broken-links', - ]), - _code: z.lazy(() => elementSchema).optional(), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - }), - ) - -export const implementationGuideDefinitionTemplateSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + preAdmissionIdentifier: z.lazy(() => identifierSchema).optional(), + origin: z.lazy(() => referenceSchema).optional(), + admitSource: z.lazy(() => codeableConceptSchema).optional(), + reAdmission: z.lazy(() => codeableConceptSchema).optional(), + dietPreference: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + specialCourtesy: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - code: codeSchema, - _code: z.lazy(() => elementSchema).optional(), - source: stringSchema, - _source: z.lazy(() => elementSchema).optional(), - scope: stringSchema.optional(), - _scope: z.lazy(() => elementSchema).optional(), - }), - ) - -export const implementationGuideManifestResourceSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - reference: z.lazy(() => referenceSchema), - exampleBoolean: booleanSchema.optional(), - _exampleBoolean: z.lazy(() => elementSchema).optional(), - exampleCanonical: canonicalSchema.optional(), - _exampleCanonical: z.lazy(() => elementSchema).optional(), - relativePath: urlSchema.optional(), - _relativePath: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['exampleBoolean', 'exampleCanonical'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + specialArrangement: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + destination: z.lazy(() => referenceSchema).optional(), + dischargeDisposition: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const implementationGuideManifestPageSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const encounterLocationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - anchor: stringSchema.array().optional(), - _anchor: z - .lazy(() => elementSchema) - .array() - .optional(), - }), - ) + location: z.lazy(() => referenceSchema), + status: z.enum(['active', 'completed', 'planned', 'reserved']).optional(), + _status: z.lazy(() => elementSchema).optional(), + physicalType: z.lazy(() => codeableConceptSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const implementationGuideSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('ImplementationGuide'), +export const encounterSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Encounter'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -16345,72 +18166,8016 @@ export const implementationGuideSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'arrived', + 'cancelled', + 'entered-in-error', + 'finished', + 'in-progress', + 'onleave', + 'planned', + 'triaged', + 'unknown', + ]) .optional(), - url: uriSchema, - _url: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) + statusHistory: encounterStatusHistorySchema.array().min(1).optional(), + class: z.lazy(() => codingSchema), + classHistory: encounterClassHistorySchema.array().min(1).optional(), + type: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) + serviceType: z.lazy(() => codeableConceptSchema).optional(), + priority: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + episodeOfCare: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - jurisdiction: z + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + participant: encounterParticipantSchema.array().min(1).optional(), + appointment: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + length: z.lazy(() => durationSchema).optional(), + reasonCode: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - packageId: idSchema, - _packageId: z.lazy(() => elementSchema).optional(), - license: z - .enum([ - '0BSD', - 'AAL', - 'ADSL', - 'AFL-1.1', - 'AFL-1.2', - 'AFL-2.0', - 'AFL-2.1', - 'AFL-3.0', - 'AGPL-1.0-only', - 'AGPL-1.0-or-later', - 'AGPL-3.0-only', - 'AGPL-3.0-or-later', - 'AMDPLPA', - 'AML', - 'AMPAS', - 'ANTLR-PD', - 'APAFML', - 'APL-1.0', - 'APSL-1.0', - 'APSL-1.1', - 'APSL-1.2', + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + diagnosis: encounterDiagnosisSchema.array().min(1).optional(), + account: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + hospitalization: encounterHospitalizationSchema.optional(), + location: encounterLocationSchema.array().min(1).optional(), + serviceProvider: z.lazy(() => referenceSchema).optional(), + partOf: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const endpointSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Endpoint'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'active', + 'entered-in-error', + 'error', + 'off', + 'suspended', + 'test', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + connectionType: z.lazy(() => codingSchema), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + contact: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + payloadType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1), + payloadMimeType: codeSchema.nullable().array().min(1).optional(), + _payloadMimeType: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + address: urlSchema.optional(), + _address: z.lazy(() => elementSchema).optional(), + header: stringSchema.nullable().array().min(1).optional(), + _header: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'address', shadow: '_address' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'payloadMimeType', + shadow: '_payloadMimeType', + required: false, + }, + { name: 'header', shadow: '_header', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const enrollmentRequestSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('EnrollmentRequest'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + insurer: z.lazy(() => referenceSchema).optional(), + provider: z.lazy(() => referenceSchema).optional(), + candidate: z.lazy(() => referenceSchema).optional(), + coverage: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const enrollmentResponseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('EnrollmentResponse'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + request: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + requestProvider: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const episodeOfCareStatusHistorySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'active', + 'cancelled', + 'entered-in-error', + 'finished', + 'onhold', + 'planned', + 'waitlist', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const episodeOfCareDiagnosisSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + condition: z.lazy(() => referenceSchema), + role: z.lazy(() => codeableConceptSchema).optional(), + rank: positiveIntSchema.optional(), + _rank: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const episodeOfCareSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('EpisodeOfCare'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'active', + 'cancelled', + 'entered-in-error', + 'finished', + 'onhold', + 'planned', + 'waitlist', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusHistory: episodeOfCareStatusHistorySchema + .array() + .min(1) + .optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + diagnosis: episodeOfCareDiagnosisSchema.array().min(1).optional(), + patient: z.lazy(() => referenceSchema), + managingOrganization: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + referralRequest: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + careManager: z.lazy(() => referenceSchema).optional(), + team: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + account: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const eventDefinitionSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('EventDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + trigger: z + .lazy(() => triggerDefinitionSchema) + .array() + .min(1), + }) + .refine( + (value) => + [ + { + alternatives: [['subjectCodeableConcept'], ['subjectReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const evidenceVariableDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + variableRole: z.lazy(() => codeableConceptSchema), + observed: z.lazy(() => referenceSchema).optional(), + intended: z.lazy(() => referenceSchema).optional(), + directnessMatch: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const evidenceStatisticSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + statisticType: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + numberOfEvents: unsignedIntSchema.optional(), + _numberOfEvents: z.lazy(() => elementSchema).optional(), + numberAffected: unsignedIntSchema.optional(), + _numberAffected: z.lazy(() => elementSchema).optional(), + sampleSize: evidenceStatisticSampleSizeSchema.optional(), + attributeEstimate: evidenceStatisticAttributeEstimateSchema + .array() + .min(1) + .optional(), + modelCharacteristic: evidenceStatisticModelCharacteristicSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const evidenceCertaintySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + rating: z.lazy(() => codeableConceptSchema).optional(), + rater: stringSchema.optional(), + _rater: z.lazy(() => elementSchema).optional(), + subcomponent: z + .lazy(() => evidenceCertaintySchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const evidenceStatisticSampleSizeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + numberOfStudies: unsignedIntSchema.optional(), + _numberOfStudies: z.lazy(() => elementSchema).optional(), + numberOfParticipants: unsignedIntSchema.optional(), + _numberOfParticipants: z.lazy(() => elementSchema).optional(), + knownDataCount: unsignedIntSchema.optional(), + _knownDataCount: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const evidenceStatisticAttributeEstimateSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + level: decimalSchema.optional(), + _level: z.lazy(() => elementSchema).optional(), + range: z.lazy(() => rangeSchema).optional(), + attributeEstimate: z + .lazy(() => evidenceStatisticAttributeEstimateSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const evidenceStatisticModelCharacteristicSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + value: z.lazy(() => quantitySchema).optional(), + variable: evidenceStatisticModelCharacteristicVariableSchema + .array() + .min(1) + .optional(), + attributeEstimate: z + .lazy(() => evidenceStatisticAttributeEstimateSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const evidenceStatisticModelCharacteristicVariableSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + variableDefinition: z.lazy(() => referenceSchema), + handling: z + .enum(['continuous', 'dichotomous', 'ordinal', 'polychotomous']) + .optional(), + _handling: z.lazy(() => elementSchema).optional(), + valueCategory: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + valueQuantity: z + .lazy(() => quantitySchema) + .array() + .min(1) + .optional(), + valueRange: z + .lazy(() => rangeSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const evidenceSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Evidence'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + citeAsReference: z.lazy(() => referenceSchema).optional(), + citeAsMarkdown: markdownSchema.optional(), + _citeAsMarkdown: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + assertion: markdownSchema.optional(), + _assertion: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + variableDefinition: evidenceVariableDefinitionSchema.array().min(1), + synthesisType: z.lazy(() => codeableConceptSchema).optional(), + studyType: z.lazy(() => codeableConceptSchema).optional(), + statistic: evidenceStatisticSchema.array().min(1).optional(), + certainty: evidenceCertaintySchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['citeAsReference'], + ['citeAsMarkdown', '_citeAsMarkdown'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const evidenceReportSubjectSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + characteristic: evidenceReportSubjectCharacteristicSchema + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const evidenceReportRelatesToSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z + .enum([ + 'amendedWith', + 'amends', + 'appendedWith', + 'appends', + 'replacedWith', + 'replaces', + 'transformedWith', + 'transforms', + ]) + .optional(), + _code: z.lazy(() => elementSchema).optional(), + targetIdentifier: z.lazy(() => identifierSchema).optional(), + targetReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['targetIdentifier'], ['targetReference']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const evidenceReportSectionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + focus: z.lazy(() => codeableConceptSchema).optional(), + focusReference: z.lazy(() => referenceSchema).optional(), + author: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + text: z.lazy(() => narrativeSchema).optional(), + mode: z.enum(['changes', 'snapshot', 'working']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + orderedBy: z.lazy(() => codeableConceptSchema).optional(), + entryClassifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + entryReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + entryQuantity: z + .lazy(() => quantitySchema) + .array() + .min(1) + .optional(), + emptyReason: z.lazy(() => codeableConceptSchema).optional(), + section: z + .lazy(() => evidenceReportSectionSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const evidenceReportSubjectCharacteristicSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + valueReference: z.lazy(() => referenceSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + exclude: booleanSchema.optional(), + _exclude: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueReference'], + ['valueCodeableConcept'], + ['valueBoolean', '_valueBoolean'], + ['valueQuantity'], + ['valueRange'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const evidenceReportSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('EvidenceReport'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + relatedIdentifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + citeAsReference: z.lazy(() => referenceSchema).optional(), + citeAsMarkdown: markdownSchema.optional(), + _citeAsMarkdown: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + subject: evidenceReportSubjectSchema, + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatesTo: evidenceReportRelatesToSchema.array().min(1).optional(), + section: evidenceReportSectionSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['citeAsReference'], + ['citeAsMarkdown', '_citeAsMarkdown'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const evidenceVariableCharacteristicSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + definitionReference: z.lazy(() => referenceSchema).optional(), + definitionCanonical: canonicalSchema.optional(), + _definitionCanonical: z.lazy(() => elementSchema).optional(), + definitionCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + definitionExpression: z.lazy(() => expressionSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + device: z.lazy(() => referenceSchema).optional(), + exclude: booleanSchema.optional(), + _exclude: z.lazy(() => elementSchema).optional(), + timeFromStart: + evidenceVariableCharacteristicTimeFromStartSchema.optional(), + groupMeasure: z + .enum([ + 'mean', + 'mean-of-mean', + 'mean-of-median', + 'median', + 'median-of-mean', + 'median-of-median', + ]) + .optional(), + _groupMeasure: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['definitionReference'], + ['definitionCanonical', '_definitionCanonical'], + ['definitionCodeableConcept'], + ['definitionExpression'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const evidenceVariableCategorySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueCodeableConcept'], + ['valueQuantity'], + ['valueRange'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const evidenceVariableCharacteristicTimeFromStartSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + range: z.lazy(() => rangeSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const evidenceVariableSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('EvidenceVariable'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + shortTitle: stringSchema.optional(), + _shortTitle: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + actual: booleanSchema.optional(), + _actual: z.lazy(() => elementSchema).optional(), + characteristicCombination: z.enum(['intersection', 'union']).optional(), + _characteristicCombination: z.lazy(() => elementSchema).optional(), + characteristic: evidenceVariableCharacteristicSchema + .array() + .min(1) + .optional(), + handling: z + .enum(['continuous', 'dichotomous', 'ordinal', 'polychotomous']) + .optional(), + _handling: z.lazy(() => elementSchema).optional(), + category: evidenceVariableCategorySchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const exampleScenarioActorSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + actorId: stringSchema.optional(), + _actorId: z.lazy(() => elementSchema).optional(), + type: z.enum(['entity', 'person']).optional(), + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'actorId', shadow: '_actorId' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const exampleScenarioInstanceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + resourceId: stringSchema.optional(), + _resourceId: z.lazy(() => elementSchema).optional(), + resourceType: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .optional(), + _resourceType: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + version: exampleScenarioInstanceVersionSchema.array().min(1).optional(), + containedInstance: exampleScenarioInstanceContainedInstanceSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'resourceId', shadow: '_resourceId' }, + { name: 'resourceType', shadow: '_resourceType' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const exampleScenarioProcessSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + preConditions: markdownSchema.optional(), + _preConditions: z.lazy(() => elementSchema).optional(), + postConditions: markdownSchema.optional(), + _postConditions: z.lazy(() => elementSchema).optional(), + step: exampleScenarioProcessStepSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'title', shadow: '_title' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const exampleScenarioInstanceVersionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + versionId: stringSchema.optional(), + _versionId: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'versionId', shadow: '_versionId' }, + { name: 'description', shadow: '_description' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const exampleScenarioInstanceContainedInstanceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + resourceId: stringSchema.optional(), + _resourceId: z.lazy(() => elementSchema).optional(), + versionId: stringSchema.optional(), + _versionId: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'resourceId', shadow: '_resourceId' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const exampleScenarioProcessStepSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + process: z + .lazy(() => exampleScenarioProcessSchema) + .array() + .min(1) + .optional(), + pause: booleanSchema.optional(), + _pause: z.lazy(() => elementSchema).optional(), + operation: exampleScenarioProcessStepOperationSchema.optional(), + alternative: exampleScenarioProcessStepAlternativeSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const exampleScenarioProcessStepOperationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + number: stringSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + type: stringSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + initiator: stringSchema.optional(), + _initiator: z.lazy(() => elementSchema).optional(), + receiver: stringSchema.optional(), + _receiver: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + initiatorActive: booleanSchema.optional(), + _initiatorActive: z.lazy(() => elementSchema).optional(), + receiverActive: booleanSchema.optional(), + _receiverActive: z.lazy(() => elementSchema).optional(), + request: z + .lazy(() => exampleScenarioInstanceContainedInstanceSchema) + .optional(), + response: z + .lazy(() => exampleScenarioInstanceContainedInstanceSchema) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'number', shadow: '_number' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const exampleScenarioProcessStepAlternativeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + step: z + .lazy(() => exampleScenarioProcessStepSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'title', shadow: '_title' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const exampleScenarioSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ExampleScenario'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + actor: exampleScenarioActorSchema.array().min(1).optional(), + instance: exampleScenarioInstanceSchema.array().min(1).optional(), + process: exampleScenarioProcessSchema.array().min(1).optional(), + workflow: canonicalSchema.nullable().array().min(1).optional(), + _workflow: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'workflow', shadow: '_workflow', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const explanationOfBenefitRelatedSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + claim: z.lazy(() => referenceSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema).optional(), + reference: z.lazy(() => identifierSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitPayeeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + party: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitCareTeamSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + provider: z.lazy(() => referenceSchema), + responsible: booleanSchema.optional(), + _responsible: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + qualification: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitSupportingInfoSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + category: z.lazy(() => codeableConceptSchema), + code: z.lazy(() => codeableConceptSchema).optional(), + timingDate: dateSchema.optional(), + _timingDate: z.lazy(() => elementSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + reason: z.lazy(() => codingSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['timingDate', '_timingDate'], ['timingPeriod']], + required: false, + }, + { + alternatives: [ + ['valueBoolean', '_valueBoolean'], + ['valueString', '_valueString'], + ['valueQuantity'], + ['valueAttachment'], + ['valueReference'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitDiagnosisSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + diagnosisCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + diagnosisReference: z.lazy(() => referenceSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + onAdmission: z.lazy(() => codeableConceptSchema).optional(), + packageCode: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['diagnosisCodeableConcept'], + ['diagnosisReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitProcedureSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + procedureCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + procedureReference: z.lazy(() => referenceSchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['procedureCodeableConcept'], + ['procedureReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitInsuranceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + focal: booleanSchema.optional(), + _focal: z.lazy(() => elementSchema).optional(), + coverage: z.lazy(() => referenceSchema), + preAuthRef: stringSchema.nullable().array().min(1).optional(), + _preAuthRef: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'focal', shadow: '_focal' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'preAuthRef', shadow: '_preAuthRef', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitAccidentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['locationAddress'], ['locationReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + careTeamSequence: positiveIntSchema.nullable().array().min(1).optional(), + _careTeamSequence: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + diagnosisSequence: positiveIntSchema.nullable().array().min(1).optional(), + _diagnosisSequence: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + procedureSequence: positiveIntSchema.nullable().array().min(1).optional(), + _procedureSequence: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + informationSequence: positiveIntSchema + .nullable() + .array() + .min(1) + .optional(), + _informationSequence: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + encounter: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: explanationOfBenefitItemAdjudicationSchema + .array() + .min(1) + .optional(), + detail: explanationOfBenefitItemDetailSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['servicedDate', '_servicedDate'], + ['servicedPeriod'], + ], + required: false, + }, + { + alternatives: [ + ['locationCodeableConcept'], + ['locationAddress'], + ['locationReference'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'careTeamSequence', + shadow: '_careTeamSequence', + required: false, + }, + { + name: 'diagnosisSequence', + shadow: '_diagnosisSequence', + required: false, + }, + { + name: 'procedureSequence', + shadow: '_procedureSequence', + required: false, + }, + { + name: 'informationSequence', + shadow: '_informationSequence', + required: false, + }, + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitAddItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + itemSequence: positiveIntSchema.nullable().array().min(1).optional(), + _itemSequence: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + detailSequence: positiveIntSchema.nullable().array().min(1).optional(), + _detailSequence: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + subDetailSequence: positiveIntSchema.nullable().array().min(1).optional(), + _subDetailSequence: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + provider: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + servicedDate: dateSchema.optional(), + _servicedDate: z.lazy(() => elementSchema).optional(), + servicedPeriod: z.lazy(() => periodSchema).optional(), + locationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + locationAddress: z.lazy(() => addressSchema).optional(), + locationReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + subSite: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .min(1) + .optional(), + detail: explanationOfBenefitAddItemDetailSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['servicedDate', '_servicedDate'], + ['servicedPeriod'], + ], + required: false, + }, + { + alternatives: [ + ['locationCodeableConcept'], + ['locationAddress'], + ['locationReference'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'itemSequence', shadow: '_itemSequence', required: false }, + { name: 'detailSequence', shadow: '_detailSequence', required: false }, + { + name: 'subDetailSequence', + shadow: '_subDetailSequence', + required: false, + }, + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitItemAdjudicationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + category: z.lazy(() => codeableConceptSchema), + reason: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitTotalSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + category: z.lazy(() => codeableConceptSchema), + amount: z.lazy(() => moneySchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitPaymentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + adjustment: z.lazy(() => moneySchema).optional(), + adjustmentReason: z.lazy(() => codeableConceptSchema).optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitProcessNoteSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + number: positiveIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + type: z.enum(['display', 'print', 'printoper']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + language: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitBenefitBalanceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + category: z.lazy(() => codeableConceptSchema), + excluded: booleanSchema.optional(), + _excluded: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + network: z.lazy(() => codeableConceptSchema).optional(), + unit: z.lazy(() => codeableConceptSchema).optional(), + term: z.lazy(() => codeableConceptSchema).optional(), + financial: explanationOfBenefitBenefitBalanceFinancialSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitItemDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .min(1) + .optional(), + subDetail: explanationOfBenefitItemDetailSubDetailSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitAddItemDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .min(1) + .optional(), + subDetail: explanationOfBenefitAddItemDetailSubDetailSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitBenefitBalanceFinancialSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + allowedUnsignedInt: unsignedIntSchema.optional(), + _allowedUnsignedInt: z.lazy(() => elementSchema).optional(), + allowedString: stringSchema.optional(), + _allowedString: z.lazy(() => elementSchema).optional(), + allowedMoney: z.lazy(() => moneySchema).optional(), + usedUnsignedInt: unsignedIntSchema.optional(), + _usedUnsignedInt: z.lazy(() => elementSchema).optional(), + usedMoney: z.lazy(() => moneySchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['allowedUnsignedInt', '_allowedUnsignedInt'], + ['allowedString', '_allowedString'], + ['allowedMoney'], + ], + required: false, + }, + { + alternatives: [ + ['usedUnsignedInt', '_usedUnsignedInt'], + ['usedMoney'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const explanationOfBenefitItemDetailSubDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + revenue: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + programCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + udi: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'sequence', shadow: '_sequence' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const explanationOfBenefitAddItemDetailSubDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + productOrService: z.lazy(() => codeableConceptSchema), + modifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + unitPrice: z.lazy(() => moneySchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + net: z.lazy(() => moneySchema).optional(), + noteNumber: positiveIntSchema.nullable().array().min(1).optional(), + _noteNumber: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'noteNumber', shadow: '_noteNumber', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const explanationOfBenefitSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ExplanationOfBenefit'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subType: z.lazy(() => codeableConceptSchema).optional(), + use: z.enum(['claim', 'preauthorization', 'predetermination']).optional(), + _use: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + billablePeriod: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + insurer: z.lazy(() => referenceSchema), + provider: z.lazy(() => referenceSchema), + priority: z.lazy(() => codeableConceptSchema).optional(), + fundsReserveRequested: z.lazy(() => codeableConceptSchema).optional(), + fundsReserve: z.lazy(() => codeableConceptSchema).optional(), + related: explanationOfBenefitRelatedSchema.array().min(1).optional(), + prescription: z.lazy(() => referenceSchema).optional(), + originalPrescription: z.lazy(() => referenceSchema).optional(), + payee: explanationOfBenefitPayeeSchema.optional(), + referral: z.lazy(() => referenceSchema).optional(), + facility: z.lazy(() => referenceSchema).optional(), + claim: z.lazy(() => referenceSchema).optional(), + claimResponse: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + preAuthRef: stringSchema.nullable().array().min(1).optional(), + _preAuthRef: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + preAuthRefPeriod: z + .lazy(() => periodSchema) + .array() + .min(1) + .optional(), + careTeam: explanationOfBenefitCareTeamSchema.array().min(1).optional(), + supportingInfo: explanationOfBenefitSupportingInfoSchema + .array() + .min(1) + .optional(), + diagnosis: explanationOfBenefitDiagnosisSchema.array().min(1).optional(), + procedure: explanationOfBenefitProcedureSchema.array().min(1).optional(), + precedence: positiveIntSchema.optional(), + _precedence: z.lazy(() => elementSchema).optional(), + insurance: explanationOfBenefitInsuranceSchema.array().min(1), + accident: explanationOfBenefitAccidentSchema.optional(), + item: explanationOfBenefitItemSchema.array().min(1).optional(), + addItem: explanationOfBenefitAddItemSchema.array().min(1).optional(), + adjudication: z + .lazy(() => explanationOfBenefitItemAdjudicationSchema) + .array() + .min(1) + .optional(), + total: explanationOfBenefitTotalSchema.array().min(1).optional(), + payment: explanationOfBenefitPaymentSchema.optional(), + formCode: z.lazy(() => codeableConceptSchema).optional(), + form: z.lazy(() => attachmentSchema).optional(), + processNote: explanationOfBenefitProcessNoteSchema + .array() + .min(1) + .optional(), + benefitPeriod: z.lazy(() => periodSchema).optional(), + benefitBalance: explanationOfBenefitBenefitBalanceSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'use', shadow: '_use' }, + { name: 'created', shadow: '_created' }, + { name: 'outcome', shadow: '_outcome' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'preAuthRef', shadow: '_preAuthRef', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const expressionSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + name: idSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + reference: uriSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'language', shadow: '_language' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const extensionSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCodeableReference: z.lazy(() => codeableReferenceSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueRatioRange: z.lazy(() => ratioRangeSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueBase64Binary', '_valueBase64Binary'], + ['valueBoolean', '_valueBoolean'], + ['valueCanonical', '_valueCanonical'], + ['valueCode', '_valueCode'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueDecimal', '_valueDecimal'], + ['valueId', '_valueId'], + ['valueInstant', '_valueInstant'], + ['valueInteger', '_valueInteger'], + ['valueMarkdown', '_valueMarkdown'], + ['valueOid', '_valueOid'], + ['valuePositiveInt', '_valuePositiveInt'], + ['valueString', '_valueString'], + ['valueTime', '_valueTime'], + ['valueUnsignedInt', '_valueUnsignedInt'], + ['valueUri', '_valueUri'], + ['valueUrl', '_valueUrl'], + ['valueUuid', '_valueUuid'], + ['valueAddress'], + ['valueAge'], + ['valueAnnotation'], + ['valueAttachment'], + ['valueCodeableConcept'], + ['valueCodeableReference'], + ['valueCoding'], + ['valueContactPoint'], + ['valueCount'], + ['valueDistance'], + ['valueDuration'], + ['valueHumanName'], + ['valueIdentifier'], + ['valueMoney'], + ['valuePeriod'], + ['valueQuantity'], + ['valueRange'], + ['valueRatio'], + ['valueRatioRange'], + ['valueReference'], + ['valueSampledData'], + ['valueSignature'], + ['valueTiming'], + ['valueContactDetail'], + ['valueContributor'], + ['valueDataRequirement'], + ['valueExpression'], + ['valueParameterDefinition'], + ['valueRelatedArtifact'], + ['valueTriggerDefinition'], + ['valueUsageContext'], + ['valueDosage'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => + ((value as { extension?: unknown[] }).extension?.length ?? 0) > 0 !== + Object.keys(value as object).some( + // A primitive sibling means the value element exists carrying only extensions, + // which ext-1 counts as the value being present. + (key) => key.startsWith('value') || key.startsWith('_value'), + ), + { + message: + 'ext-1: an extension has either extensions or a value, never both.', + }, + ), +) as unknown as z.ZodType> + +export const familyMemberHistoryConditionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + outcome: z.lazy(() => codeableConceptSchema).optional(), + contributedToDeath: booleanSchema.optional(), + _contributedToDeath: z.lazy(() => elementSchema).optional(), + onsetAge: z.lazy(() => ageSchema).optional(), + onsetRange: z.lazy(() => rangeSchema).optional(), + onsetPeriod: z.lazy(() => periodSchema).optional(), + onsetString: stringSchema.optional(), + _onsetString: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['onsetAge'], + ['onsetRange'], + ['onsetPeriod'], + ['onsetString', '_onsetString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const familyMemberHistorySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('FamilyMemberHistory'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + instantiatesCanonical: canonicalSchema + .nullable() + .array() + .min(1) + .optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + status: z + .enum(['completed', 'entered-in-error', 'health-unknown', 'partial']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), + patient: z.lazy(() => referenceSchema), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + relationship: z.lazy(() => codeableConceptSchema), + sex: z.lazy(() => codeableConceptSchema).optional(), + bornPeriod: z.lazy(() => periodSchema).optional(), + bornDate: dateSchema.optional(), + _bornDate: z.lazy(() => elementSchema).optional(), + bornString: stringSchema.optional(), + _bornString: z.lazy(() => elementSchema).optional(), + ageAge: z.lazy(() => ageSchema).optional(), + ageRange: z.lazy(() => rangeSchema).optional(), + ageString: stringSchema.optional(), + _ageString: z.lazy(() => elementSchema).optional(), + estimatedAge: booleanSchema.optional(), + _estimatedAge: z.lazy(() => elementSchema).optional(), + deceasedBoolean: booleanSchema.optional(), + _deceasedBoolean: z.lazy(() => elementSchema).optional(), + deceasedAge: z.lazy(() => ageSchema).optional(), + deceasedRange: z.lazy(() => rangeSchema).optional(), + deceasedDate: dateSchema.optional(), + _deceasedDate: z.lazy(() => elementSchema).optional(), + deceasedString: stringSchema.optional(), + _deceasedString: z.lazy(() => elementSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + condition: familyMemberHistoryConditionSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['bornPeriod'], + ['bornDate', '_bornDate'], + ['bornString', '_bornString'], + ], + required: false, + }, + { + alternatives: [ + ['ageAge'], + ['ageRange'], + ['ageString', '_ageString'], + ], + required: false, + }, + { + alternatives: [ + ['deceasedBoolean', '_deceasedBoolean'], + ['deceasedAge'], + ['deceasedRange'], + ['deceasedDate', '_deceasedDate'], + ['deceasedString', '_deceasedString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const flagSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Flag'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema), + period: z.lazy(() => periodSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const goalTargetSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + measure: z.lazy(() => codeableConceptSchema).optional(), + detailQuantity: z.lazy(() => quantitySchema).optional(), + detailRange: z.lazy(() => rangeSchema).optional(), + detailCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + detailString: stringSchema.optional(), + _detailString: z.lazy(() => elementSchema).optional(), + detailBoolean: booleanSchema.optional(), + _detailBoolean: z.lazy(() => elementSchema).optional(), + detailInteger: integerSchema.optional(), + _detailInteger: z.lazy(() => elementSchema).optional(), + detailRatio: z.lazy(() => ratioSchema).optional(), + dueDate: dateSchema.optional(), + _dueDate: z.lazy(() => elementSchema).optional(), + dueDuration: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['detailQuantity'], + ['detailRange'], + ['detailCodeableConcept'], + ['detailString', '_detailString'], + ['detailBoolean', '_detailBoolean'], + ['detailInteger', '_detailInteger'], + ['detailRatio'], + ], + required: false, + }, + { + alternatives: [['dueDate', '_dueDate'], ['dueDuration']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const goalSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Goal'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + lifecycleStatus: z + .enum([ + 'accepted', + 'active', + 'cancelled', + 'completed', + 'entered-in-error', + 'on-hold', + 'planned', + 'proposed', + 'rejected', + ]) + .optional(), + _lifecycleStatus: z.lazy(() => elementSchema).optional(), + achievementStatus: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + priority: z.lazy(() => codeableConceptSchema).optional(), + description: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema), + startDate: dateSchema.optional(), + _startDate: z.lazy(() => elementSchema).optional(), + startCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + target: goalTargetSchema.array().min(1).optional(), + statusDate: dateSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), + statusReason: stringSchema.optional(), + _statusReason: z.lazy(() => elementSchema).optional(), + expressedBy: z.lazy(() => referenceSchema).optional(), + addresses: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + outcomeCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + outcomeReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['startDate', '_startDate'], + ['startCodeableConcept'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'lifecycleStatus', shadow: '_lifecycleStatus' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const graphDefinitionLinkSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + sliceName: stringSchema.optional(), + _sliceName: z.lazy(() => elementSchema).optional(), + min: integerSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + target: graphDefinitionLinkTargetSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const graphDefinitionLinkTargetSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + params: stringSchema.optional(), + _params: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + compartment: graphDefinitionLinkTargetCompartmentSchema + .array() + .min(1) + .optional(), + link: z + .lazy(() => graphDefinitionLinkSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const graphDefinitionLinkTargetCompartmentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + use: z.enum(['condition', 'requirement']).optional(), + _use: z.lazy(() => elementSchema).optional(), + code: z + .enum([ + 'Device', + 'Encounter', + 'Patient', + 'Practitioner', + 'RelatedPerson', + ]) + .optional(), + _code: z.lazy(() => elementSchema).optional(), + rule: z.enum(['custom', 'different', 'identical', 'matching']).optional(), + _rule: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'use', shadow: '_use' }, + { name: 'code', shadow: '_code' }, + { name: 'rule', shadow: '_rule' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const graphDefinitionSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('GraphDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + start: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .optional(), + _start: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + link: graphDefinitionLinkSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + { name: 'start', shadow: '_start' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const groupCharacteristicSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + exclude: booleanSchema.optional(), + _exclude: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueCodeableConcept'], + ['valueBoolean', '_valueBoolean'], + ['valueQuantity'], + ['valueRange'], + ['valueReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'exclude', shadow: '_exclude' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const groupMemberSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + entity: z.lazy(() => referenceSchema), + period: z.lazy(() => periodSchema).optional(), + inactive: booleanSchema.optional(), + _inactive: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const groupSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Group'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'animal', + 'device', + 'medication', + 'person', + 'practitioner', + 'substance', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + actual: booleanSchema.optional(), + _actual: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + quantity: unsignedIntSchema.optional(), + _quantity: z.lazy(() => elementSchema).optional(), + managingEntity: z.lazy(() => referenceSchema).optional(), + characteristic: groupCharacteristicSchema.array().min(1).optional(), + member: groupMemberSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'type', shadow: '_type' }, + { name: 'actual', shadow: '_actual' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const guidanceResponseSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('GuidanceResponse'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + requestIdentifier: z.lazy(() => identifierSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + moduleUri: uriSchema.optional(), + _moduleUri: z.lazy(() => elementSchema).optional(), + moduleCanonical: canonicalSchema.optional(), + _moduleCanonical: z.lazy(() => elementSchema).optional(), + moduleCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + status: z + .enum([ + 'data-requested', + 'data-required', + 'entered-in-error', + 'failure', + 'in-progress', + 'success', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + evaluationMessage: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + outputParameters: z.lazy(() => referenceSchema).optional(), + result: z.lazy(() => referenceSchema).optional(), + dataRequirement: z + .lazy(() => dataRequirementSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['moduleUri', '_moduleUri'], + ['moduleCanonical', '_moduleCanonical'], + ['moduleCodeableConcept'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const healthcareServiceEligibilitySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + comment: markdownSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const healthcareServiceAvailableTimeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + daysOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .nullable() + .array() + .min(1) + .optional(), + _daysOfWeek: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + allDay: booleanSchema.optional(), + _allDay: z.lazy(() => elementSchema).optional(), + availableStartTime: timeSchema.optional(), + _availableStartTime: z.lazy(() => elementSchema).optional(), + availableEndTime: timeSchema.optional(), + _availableEndTime: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'daysOfWeek', shadow: '_daysOfWeek', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const healthcareServiceNotAvailableSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + during: z.lazy(() => periodSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'description', shadow: '_description' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const healthcareServiceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('HealthcareService'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + providedBy: z.lazy(() => referenceSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + location: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + extraDetails: markdownSchema.optional(), + _extraDetails: z.lazy(() => elementSchema).optional(), + photo: z.lazy(() => attachmentSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + coverageArea: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + serviceProvisionCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + eligibility: healthcareServiceEligibilitySchema.array().min(1).optional(), + program: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + characteristic: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + communication: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + referralMethod: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + appointmentRequired: booleanSchema.optional(), + _appointmentRequired: z.lazy(() => elementSchema).optional(), + availableTime: healthcareServiceAvailableTimeSchema + .array() + .min(1) + .optional(), + notAvailable: healthcareServiceNotAvailableSchema + .array() + .min(1) + .optional(), + availabilityExceptions: stringSchema.optional(), + _availabilityExceptions: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const humanNameSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + use: z + .enum([ + 'anonymous', + 'maiden', + 'nickname', + 'official', + 'old', + 'temp', + 'usual', + ]) + .optional(), + _use: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + family: stringSchema.optional(), + _family: z.lazy(() => elementSchema).optional(), + given: stringSchema.nullable().array().min(1).optional(), + _given: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + prefix: stringSchema.nullable().array().min(1).optional(), + _prefix: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + suffix: stringSchema.nullable().array().min(1).optional(), + _suffix: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'given', shadow: '_given', required: false }, + { name: 'prefix', shadow: '_prefix', required: false }, + { name: 'suffix', shadow: '_suffix', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const identifierSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + use: z + .enum(['official', 'old', 'secondary', 'temp', 'usual']) + .optional(), + _use: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + assigner: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const imagingStudySeriesSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + uid: idSchema.optional(), + _uid: z.lazy(() => elementSchema).optional(), + number: unsignedIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + modality: z.lazy(() => codingSchema), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + numberOfInstances: unsignedIntSchema.optional(), + _numberOfInstances: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + bodySite: z.lazy(() => codingSchema).optional(), + laterality: z.lazy(() => codingSchema).optional(), + specimen: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + started: dateTimeSchema.optional(), + _started: z.lazy(() => elementSchema).optional(), + performer: imagingStudySeriesPerformerSchema.array().min(1).optional(), + instance: imagingStudySeriesInstanceSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'uid', shadow: '_uid' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const imagingStudySeriesPerformerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const imagingStudySeriesInstanceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + uid: idSchema.optional(), + _uid: z.lazy(() => elementSchema).optional(), + sopClass: z.lazy(() => codingSchema), + number: unsignedIntSchema.optional(), + _number: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'uid', shadow: '_uid' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const imagingStudySchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('ImagingStudy'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'available', + 'cancelled', + 'entered-in-error', + 'registered', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + modality: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + started: dateTimeSchema.optional(), + _started: z.lazy(() => elementSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + referrer: z.lazy(() => referenceSchema).optional(), + interpreter: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + numberOfSeries: unsignedIntSchema.optional(), + _numberOfSeries: z.lazy(() => elementSchema).optional(), + numberOfInstances: unsignedIntSchema.optional(), + _numberOfInstances: z.lazy(() => elementSchema).optional(), + procedureReference: z.lazy(() => referenceSchema).optional(), + procedureCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + location: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + series: imagingStudySeriesSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const immunizationPerformerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const immunizationEducationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + documentType: stringSchema.optional(), + _documentType: z.lazy(() => elementSchema).optional(), + reference: uriSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + publicationDate: dateTimeSchema.optional(), + _publicationDate: z.lazy(() => elementSchema).optional(), + presentationDate: dateTimeSchema.optional(), + _presentationDate: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const immunizationReactionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + detail: z.lazy(() => referenceSchema).optional(), + reported: booleanSchema.optional(), + _reported: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const immunizationProtocolAppliedSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + series: stringSchema.optional(), + _series: z.lazy(() => elementSchema).optional(), + authority: z.lazy(() => referenceSchema).optional(), + targetDisease: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + doseNumberPositiveInt: positiveIntSchema.optional(), + _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), + doseNumberString: stringSchema.optional(), + _doseNumberString: z.lazy(() => elementSchema).optional(), + seriesDosesPositiveInt: positiveIntSchema.optional(), + _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), + seriesDosesString: stringSchema.optional(), + _seriesDosesString: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['doseNumberPositiveInt', '_doseNumberPositiveInt'], + ['doseNumberString', '_doseNumberString'], + ], + required: true, + }, + { + alternatives: [ + ['seriesDosesPositiveInt', '_seriesDosesPositiveInt'], + ['seriesDosesString', '_seriesDosesString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const immunizationSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('Immunization'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['completed', 'entered-in-error', 'not-done']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + vaccineCode: z.lazy(() => codeableConceptSchema), + patient: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrenceString: stringSchema.optional(), + _occurrenceString: z.lazy(() => elementSchema).optional(), + recorded: dateTimeSchema.optional(), + _recorded: z.lazy(() => elementSchema).optional(), + primarySource: booleanSchema.optional(), + _primarySource: z.lazy(() => elementSchema).optional(), + reportOrigin: z.lazy(() => codeableConceptSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + manufacturer: z.lazy(() => referenceSchema).optional(), + lotNumber: stringSchema.optional(), + _lotNumber: z.lazy(() => elementSchema).optional(), + expirationDate: dateSchema.optional(), + _expirationDate: z.lazy(() => elementSchema).optional(), + site: z.lazy(() => codeableConceptSchema).optional(), + route: z.lazy(() => codeableConceptSchema).optional(), + doseQuantity: z.lazy(() => quantitySchema).optional(), + performer: immunizationPerformerSchema.array().min(1).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + isSubpotent: booleanSchema.optional(), + _isSubpotent: z.lazy(() => elementSchema).optional(), + subpotentReason: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + education: immunizationEducationSchema.array().min(1).optional(), + programEligibility: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + fundingSource: z.lazy(() => codeableConceptSchema).optional(), + reaction: immunizationReactionSchema.array().min(1).optional(), + protocolApplied: immunizationProtocolAppliedSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrenceString', '_occurrenceString'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const immunizationEvaluationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ImmunizationEvaluation'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z.enum(['completed', 'entered-in-error']).optional(), + _status: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + authority: z.lazy(() => referenceSchema).optional(), + targetDisease: z.lazy(() => codeableConceptSchema), + immunizationEvent: z.lazy(() => referenceSchema), + doseStatus: z.lazy(() => codeableConceptSchema), + doseStatusReason: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + series: stringSchema.optional(), + _series: z.lazy(() => elementSchema).optional(), + doseNumberPositiveInt: positiveIntSchema.optional(), + _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), + doseNumberString: stringSchema.optional(), + _doseNumberString: z.lazy(() => elementSchema).optional(), + seriesDosesPositiveInt: positiveIntSchema.optional(), + _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), + seriesDosesString: stringSchema.optional(), + _seriesDosesString: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['doseNumberPositiveInt', '_doseNumberPositiveInt'], + ['doseNumberString', '_doseNumberString'], + ], + required: false, + }, + { + alternatives: [ + ['seriesDosesPositiveInt', '_seriesDosesPositiveInt'], + ['seriesDosesString', '_seriesDosesString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const immunizationRecommendationRecommendationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + vaccineCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + targetDisease: z.lazy(() => codeableConceptSchema).optional(), + contraindicatedVaccineCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + forecastStatus: z.lazy(() => codeableConceptSchema), + forecastReason: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + dateCriterion: immunizationRecommendationRecommendationDateCriterionSchema + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + series: stringSchema.optional(), + _series: z.lazy(() => elementSchema).optional(), + doseNumberPositiveInt: positiveIntSchema.optional(), + _doseNumberPositiveInt: z.lazy(() => elementSchema).optional(), + doseNumberString: stringSchema.optional(), + _doseNumberString: z.lazy(() => elementSchema).optional(), + seriesDosesPositiveInt: positiveIntSchema.optional(), + _seriesDosesPositiveInt: z.lazy(() => elementSchema).optional(), + seriesDosesString: stringSchema.optional(), + _seriesDosesString: z.lazy(() => elementSchema).optional(), + supportingImmunization: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + supportingPatientInformation: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['doseNumberPositiveInt', '_doseNumberPositiveInt'], + ['doseNumberString', '_doseNumberString'], + ], + required: false, + }, + { + alternatives: [ + ['seriesDosesPositiveInt', '_seriesDosesPositiveInt'], + ['seriesDosesString', '_seriesDosesString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const immunizationRecommendationRecommendationDateCriterionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + value: dateTimeSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'value', shadow: '_value' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const immunizationRecommendationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ImmunizationRecommendation'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + patient: z.lazy(() => referenceSchema), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + authority: z.lazy(() => referenceSchema).optional(), + recommendation: immunizationRecommendationRecommendationSchema + .array() + .min(1), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'date', shadow: '_date' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideDependsOnSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + uri: canonicalSchema.optional(), + _uri: z.lazy(() => elementSchema).optional(), + packageId: idSchema.optional(), + _packageId: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'uri', shadow: '_uri' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideGlobalSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'type', shadow: '_type' }, + { name: 'profile', shadow: '_profile' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + grouping: implementationGuideDefinitionGroupingSchema + .array() + .min(1) + .optional(), + resource: implementationGuideDefinitionResourceSchema.array().min(1), + page: implementationGuideDefinitionPageSchema.optional(), + parameter: implementationGuideDefinitionParameterSchema + .array() + .min(1) + .optional(), + template: implementationGuideDefinitionTemplateSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideManifestSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + rendering: urlSchema.optional(), + _rendering: z.lazy(() => elementSchema).optional(), + resource: implementationGuideManifestResourceSchema.array().min(1), + page: implementationGuideManifestPageSchema.array().min(1).optional(), + image: stringSchema.nullable().array().min(1).optional(), + _image: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + other: stringSchema.nullable().array().min(1).optional(), + _other: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'image', shadow: '_image', required: false }, + { name: 'other', shadow: '_other', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideDefinitionGroupingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideDefinitionResourceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + reference: z.lazy(() => referenceSchema), + fhirVersion: z + .enum([ + '0.0.80', + '0.0.81', + '0.0.82', + '0.01', + '0.05', + '0.06', + '0.11', + '0.4.0', + '0.5.0', + '1.0.0', + '1.0.1', + '1.0.2', + '1.1.0', + '1.4.0', + '1.6.0', + '1.8.0', + '3.0.0', + '3.0.1', + '3.0.2', + '3.3.0', + '3.5.0', + '4.0.0', + '4.0.1', + '4.1.0', + '4.3.0', + '4.3.0-cibuild', + '4.3.0-snapshot1', + ]) + .nullable() + .array() + .min(1) + .optional(), + _fhirVersion: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + exampleBoolean: booleanSchema.optional(), + _exampleBoolean: z.lazy(() => elementSchema).optional(), + exampleCanonical: canonicalSchema.optional(), + _exampleCanonical: z.lazy(() => elementSchema).optional(), + groupingId: idSchema.optional(), + _groupingId: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['exampleBoolean', '_exampleBoolean'], + ['exampleCanonical', '_exampleCanonical'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'fhirVersion', shadow: '_fhirVersion', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideDefinitionPageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + nameUrl: urlSchema.optional(), + _nameUrl: z.lazy(() => elementSchema).optional(), + nameReference: z.lazy(() => referenceSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + generation: z.enum(['generated', 'html', 'markdown', 'xml']).optional(), + _generation: z.lazy(() => elementSchema).optional(), + page: z + .lazy(() => implementationGuideDefinitionPageSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['nameUrl', '_nameUrl'], ['nameReference']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'title', shadow: '_title' }, + { name: 'generation', shadow: '_generation' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideDefinitionParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z + .enum([ + 'apply', + 'expansion-parameter', + 'generate-json', + 'generate-turtle', + 'generate-xml', + 'html-template', + 'path-pages', + 'path-resource', + 'path-tx-cache', + 'rule-broken-links', + ]) + .optional(), + _code: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'code', shadow: '_code' }, + { name: 'value', shadow: '_value' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideDefinitionTemplateSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + source: stringSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + scope: stringSchema.optional(), + _scope: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'code', shadow: '_code' }, + { name: 'source', shadow: '_source' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideManifestResourceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + reference: z.lazy(() => referenceSchema), + exampleBoolean: booleanSchema.optional(), + _exampleBoolean: z.lazy(() => elementSchema).optional(), + exampleCanonical: canonicalSchema.optional(), + _exampleCanonical: z.lazy(() => elementSchema).optional(), + relativePath: urlSchema.optional(), + _relativePath: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['exampleBoolean', '_exampleBoolean'], + ['exampleCanonical', '_exampleCanonical'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideManifestPageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + anchor: stringSchema.nullable().array().min(1).optional(), + _anchor: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'anchor', shadow: '_anchor', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const implementationGuideSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ImplementationGuide'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + packageId: idSchema.optional(), + _packageId: z.lazy(() => elementSchema).optional(), + license: z + .enum([ + '0BSD', + 'AAL', + 'ADSL', + 'AFL-1.1', + 'AFL-1.2', + 'AFL-2.0', + 'AFL-2.1', + 'AFL-3.0', + 'AGPL-1.0-only', + 'AGPL-1.0-or-later', + 'AGPL-3.0-only', + 'AGPL-3.0-or-later', + 'AMDPLPA', + 'AML', + 'AMPAS', + 'ANTLR-PD', + 'APAFML', + 'APL-1.0', + 'APSL-1.0', + 'APSL-1.1', + 'APSL-1.2', 'APSL-2.0', 'Abstyles', 'Adobe-2006', @@ -16738,620 +26503,4323 @@ export const implementationGuideSchema: z.ZodType = 'zlib-acknowledgement', ]) .optional(), - _license: z.lazy(() => elementSchema).optional(), - fhirVersion: z - .enum([ - '0.0.80', - '0.0.81', - '0.0.82', - '0.01', - '0.05', - '0.06', - '0.11', - '0.4.0', - '0.5.0', - '1.0.0', - '1.0.1', - '1.0.2', - '1.1.0', - '1.4.0', - '1.6.0', - '1.8.0', - '3.0.0', - '3.0.1', - '3.0.2', - '3.3.0', - '3.5.0', - '4.0.0', - '4.0.1', - '4.1.0', - '4.3.0', - '4.3.0-cibuild', - '4.3.0-snapshot1', - ]) + _license: z.lazy(() => elementSchema).optional(), + fhirVersion: z + .enum([ + '0.0.80', + '0.0.81', + '0.0.82', + '0.01', + '0.05', + '0.06', + '0.11', + '0.4.0', + '0.5.0', + '1.0.0', + '1.0.1', + '1.0.2', + '1.1.0', + '1.4.0', + '1.6.0', + '1.8.0', + '3.0.0', + '3.0.1', + '3.0.2', + '3.3.0', + '3.5.0', + '4.0.0', + '4.0.1', + '4.1.0', + '4.3.0', + '4.3.0-cibuild', + '4.3.0-snapshot1', + ]) + .nullable() + .array() + .min(1) + .optional(), + _fhirVersion: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + dependsOn: implementationGuideDependsOnSchema.array().min(1).optional(), + global: implementationGuideGlobalSchema.array().min(1).optional(), + definition: implementationGuideDefinitionSchema.optional(), + manifest: implementationGuideManifestSchema.optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + { name: 'packageId', shadow: '_packageId' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'fhirVersion', shadow: '_fhirVersion', required: true }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const ingredientManufacturerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + role: z.enum(['actual', 'allowed', 'possible']).optional(), + _role: z.lazy(() => elementSchema).optional(), + manufacturer: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const ingredientSubstanceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableReferenceSchema), + strength: ingredientSubstanceStrengthSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const ingredientSubstanceStrengthSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + presentationRatio: z.lazy(() => ratioSchema).optional(), + presentationRatioRange: z.lazy(() => ratioRangeSchema).optional(), + textPresentation: stringSchema.optional(), + _textPresentation: z.lazy(() => elementSchema).optional(), + concentrationRatio: z.lazy(() => ratioSchema).optional(), + concentrationRatioRange: z.lazy(() => ratioRangeSchema).optional(), + textConcentration: stringSchema.optional(), + _textConcentration: z.lazy(() => elementSchema).optional(), + measurementPoint: stringSchema.optional(), + _measurementPoint: z.lazy(() => elementSchema).optional(), + country: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + referenceStrength: ingredientSubstanceStrengthReferenceStrengthSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['presentationRatio'], ['presentationRatioRange']], + required: false, + }, + { + alternatives: [['concentrationRatio'], ['concentrationRatioRange']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const ingredientSubstanceStrengthReferenceStrengthSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + substance: z.lazy(() => codeableReferenceSchema).optional(), + strengthRatio: z.lazy(() => ratioSchema).optional(), + strengthRatioRange: z.lazy(() => ratioRangeSchema).optional(), + measurementPoint: stringSchema.optional(), + _measurementPoint: z.lazy(() => elementSchema).optional(), + country: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['strengthRatio'], ['strengthRatioRange']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const ingredientSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('Ingredient'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + for: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + role: z.lazy(() => codeableConceptSchema), + function: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + allergenicIndicator: booleanSchema.optional(), + _allergenicIndicator: z.lazy(() => elementSchema).optional(), + manufacturer: ingredientManufacturerSchema.array().min(1).optional(), + substance: ingredientSubstanceSchema, + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const insurancePlanContactSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + purpose: z.lazy(() => codeableConceptSchema).optional(), + name: z.lazy(() => humanNameSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + address: z.lazy(() => addressSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanCoverageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + network: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + benefit: insurancePlanCoverageBenefitSchema.array().min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanPlanSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + coverageArea: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + network: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + generalCost: insurancePlanPlanGeneralCostSchema.array().min(1).optional(), + specificCost: insurancePlanPlanSpecificCostSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanCoverageBenefitSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + requirement: stringSchema.optional(), + _requirement: z.lazy(() => elementSchema).optional(), + limit: insurancePlanCoverageBenefitLimitSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanPlanGeneralCostSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + groupSize: positiveIntSchema.optional(), + _groupSize: z.lazy(() => elementSchema).optional(), + cost: z.lazy(() => moneySchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanPlanSpecificCostSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + category: z.lazy(() => codeableConceptSchema), + benefit: insurancePlanPlanSpecificCostBenefitSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanCoverageBenefitLimitSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + value: z.lazy(() => quantitySchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanPlanSpecificCostBenefitSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + cost: insurancePlanPlanSpecificCostBenefitCostSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const insurancePlanPlanSpecificCostBenefitCostSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + applicability: z.lazy(() => codeableConceptSchema).optional(), + qualifiers: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + value: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const insurancePlanSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('InsurancePlan'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + alias: stringSchema.nullable().array().min(1).optional(), + _alias: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + ownedBy: z.lazy(() => referenceSchema).optional(), + administeredBy: z.lazy(() => referenceSchema).optional(), + coverageArea: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + contact: insurancePlanContactSchema.array().min(1).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + network: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + coverage: insurancePlanCoverageSchema.array().min(1).optional(), + plan: insurancePlanPlanSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'alias', shadow: '_alias', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const invoiceParticipantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const invoiceLineItemSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + sequence: positiveIntSchema.optional(), + _sequence: z.lazy(() => elementSchema).optional(), + chargeItemReference: z.lazy(() => referenceSchema).optional(), + chargeItemCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + priceComponent: invoiceLineItemPriceComponentSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['chargeItemReference'], + ['chargeItemCodeableConcept'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const invoiceLineItemPriceComponentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z + .enum([ + 'base', + 'deduction', + 'discount', + 'informational', + 'surcharge', + 'tax', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const invoiceSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Invoice'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['balanced', 'cancelled', 'draft', 'entered-in-error', 'issued']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + cancelledReason: stringSchema.optional(), + _cancelledReason: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + recipient: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + participant: invoiceParticipantSchema.array().min(1).optional(), + issuer: z.lazy(() => referenceSchema).optional(), + account: z.lazy(() => referenceSchema).optional(), + lineItem: invoiceLineItemSchema.array().min(1).optional(), + totalPriceComponent: z + .lazy(() => invoiceLineItemPriceComponentSchema) + .array() + .min(1) + .optional(), + totalNet: z.lazy(() => moneySchema).optional(), + totalGross: z.lazy(() => moneySchema).optional(), + paymentTerms: markdownSchema.optional(), + _paymentTerms: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const librarySchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Library'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + parameter: z + .lazy(() => parameterDefinitionSchema) + .array() + .min(1) + .optional(), + dataRequirement: z + .lazy(() => dataRequirementSchema) + .array() + .min(1) + .optional(), + content: z + .lazy(() => attachmentSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['subjectCodeableConcept'], ['subjectReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const linkageItemSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.enum(['alternate', 'historical', 'source']).optional(), + _type: z.lazy(() => elementSchema).optional(), + resource: z.lazy(() => referenceSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const linkageSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Linkage'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + item: linkageItemSchema.array().min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const listEntrySchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + flag: z.lazy(() => codeableConceptSchema).optional(), + deleted: booleanSchema.optional(), + _deleted: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + item: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const listSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('List'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z.enum(['current', 'entered-in-error', 'retired']).optional(), + _status: z.lazy(() => elementSchema).optional(), + mode: z.enum(['changes', 'snapshot', 'working']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + orderedBy: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + entry: listEntrySchema.array().min(1).optional(), + emptyReason: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'mode', shadow: '_mode' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const locationPositionSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + longitude: decimalSchema.optional(), + _longitude: z.lazy(() => elementSchema).optional(), + latitude: decimalSchema.optional(), + _latitude: z.lazy(() => elementSchema).optional(), + altitude: decimalSchema.optional(), + _altitude: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'longitude', shadow: '_longitude' }, + { name: 'latitude', shadow: '_latitude' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const locationHoursOfOperationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + daysOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .nullable() + .array() + .min(1) + .optional(), + _daysOfWeek: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + allDay: booleanSchema.optional(), + _allDay: z.lazy(() => elementSchema).optional(), + openingTime: timeSchema.optional(), + _openingTime: z.lazy(() => elementSchema).optional(), + closingTime: timeSchema.optional(), + _closingTime: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'daysOfWeek', shadow: '_daysOfWeek', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const locationSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Location'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z.enum(['active', 'inactive', 'suspended']).optional(), + _status: z.lazy(() => elementSchema).optional(), + operationalStatus: z.lazy(() => codingSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + alias: stringSchema.nullable().array().min(1).optional(), + _alias: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + mode: z.enum(['instance', 'kind']).optional(), + _mode: z.lazy(() => elementSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + address: z.lazy(() => addressSchema).optional(), + physicalType: z.lazy(() => codeableConceptSchema).optional(), + position: locationPositionSchema.optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + partOf: z.lazy(() => referenceSchema).optional(), + hoursOfOperation: locationHoursOfOperationSchema + .array() + .min(1) + .optional(), + availabilityExceptions: stringSchema.optional(), + _availabilityExceptions: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'alias', shadow: '_alias', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const manufacturedItemDefinitionPropertySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueCodeableConcept'], + ['valueQuantity'], + ['valueDate', '_valueDate'], + ['valueBoolean', '_valueBoolean'], + ['valueAttachment'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const manufacturedItemDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ManufacturedItemDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + manufacturedDoseForm: z.lazy(() => codeableConceptSchema), + unitOfPresentation: z.lazy(() => codeableConceptSchema).optional(), + manufacturer: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + ingredient: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + property: manufacturedItemDefinitionPropertySchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const marketingStatusSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + country: z.lazy(() => codeableConceptSchema).optional(), + jurisdiction: z.lazy(() => codeableConceptSchema).optional(), + status: z.lazy(() => codeableConceptSchema), + dateRange: z.lazy(() => periodSchema).optional(), + restoreDate: dateTimeSchema.optional(), + _restoreDate: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const measureGroupSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + population: measureGroupPopulationSchema.array().min(1).optional(), + stratifier: measureGroupStratifierSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const measureSupplementalDataSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + usage: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureGroupPopulationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureGroupStratifierSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema).optional(), + component: measureGroupStratifierComponentSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureGroupStratifierComponentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + criteria: z.lazy(() => expressionSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Measure'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + library: canonicalSchema.nullable().array().min(1).optional(), + _library: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + disclaimer: markdownSchema.optional(), + _disclaimer: z.lazy(() => elementSchema).optional(), + scoring: z.lazy(() => codeableConceptSchema).optional(), + compositeScoring: z.lazy(() => codeableConceptSchema).optional(), + type: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + riskAdjustment: stringSchema.optional(), + _riskAdjustment: z.lazy(() => elementSchema).optional(), + rateAggregation: stringSchema.optional(), + _rateAggregation: z.lazy(() => elementSchema).optional(), + rationale: markdownSchema.optional(), + _rationale: z.lazy(() => elementSchema).optional(), + clinicalRecommendationStatement: markdownSchema.optional(), + _clinicalRecommendationStatement: z.lazy(() => elementSchema).optional(), + improvementNotation: z.lazy(() => codeableConceptSchema).optional(), + definition: markdownSchema.nullable().array().min(1).optional(), + _definition: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + guidance: markdownSchema.optional(), + _guidance: z.lazy(() => elementSchema).optional(), + group: measureGroupSchema.array().min(1).optional(), + supplementalData: measureSupplementalDataSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['subjectCodeableConcept'], ['subjectReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'library', shadow: '_library', required: false }, + { name: 'definition', shadow: '_definition', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureReportGroupSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + population: measureReportGroupPopulationSchema.array().min(1).optional(), + measureScore: z.lazy(() => quantitySchema).optional(), + stratifier: measureReportGroupStratifierSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureReportGroupPopulationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + count: integerSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + subjectResults: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureReportGroupStratifierSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + stratum: measureReportGroupStratifierStratumSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureReportGroupStratifierStratumSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + value: z.lazy(() => codeableConceptSchema).optional(), + component: measureReportGroupStratifierStratumComponentSchema + .array() + .min(1) + .optional(), + population: measureReportGroupStratifierStratumPopulationSchema + .array() + .min(1) + .optional(), + measureScore: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const measureReportGroupStratifierStratumComponentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + value: z.lazy(() => codeableConceptSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const measureReportGroupStratifierStratumPopulationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + count: integerSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + subjectResults: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const measureReportSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MeasureReport'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z.enum(['complete', 'error', 'pending']).optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z + .enum(['data-collection', 'individual', 'subject-list', 'summary']) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + measure: canonicalSchema.optional(), + _measure: z.lazy(() => elementSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + reporter: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema), + improvementNotation: z.lazy(() => codeableConceptSchema).optional(), + group: measureReportGroupSchema.array().min(1).optional(), + evaluatedResource: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'type', shadow: '_type' }, + { name: 'measure', shadow: '_measure' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const mediaSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Media'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + modality: z.lazy(() => codeableConceptSchema).optional(), + view: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + createdDateTime: dateTimeSchema.optional(), + _createdDateTime: z.lazy(() => elementSchema).optional(), + createdPeriod: z.lazy(() => periodSchema).optional(), + issued: instantSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + operator: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + deviceName: stringSchema.optional(), + _deviceName: z.lazy(() => elementSchema).optional(), + device: z.lazy(() => referenceSchema).optional(), + height: positiveIntSchema.optional(), + _height: z.lazy(() => elementSchema).optional(), + width: positiveIntSchema.optional(), + _width: z.lazy(() => elementSchema).optional(), + frames: positiveIntSchema.optional(), + _frames: z.lazy(() => elementSchema).optional(), + duration: decimalSchema.optional(), + _duration: z.lazy(() => elementSchema).optional(), + content: z.lazy(() => attachmentSchema), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['createdDateTime', '_createdDateTime'], + ['createdPeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationIngredientSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + isActive: booleanSchema.optional(), + _isActive: z.lazy(() => elementSchema).optional(), + strength: z.lazy(() => ratioSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['itemCodeableConcept'], ['itemReference']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationBatchSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + lotNumber: stringSchema.optional(), + _lotNumber: z.lazy(() => elementSchema).optional(), + expirationDate: dateTimeSchema.optional(), + _expirationDate: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const medicationSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('Medication'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), + _status: z.lazy(() => elementSchema).optional(), + manufacturer: z.lazy(() => referenceSchema).optional(), + form: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => ratioSchema).optional(), + ingredient: medicationIngredientSchema.array().min(1).optional(), + batch: medicationBatchSchema.optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const medicationAdministrationPerformerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationAdministrationDosageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + site: z.lazy(() => codeableConceptSchema).optional(), + route: z.lazy(() => codeableConceptSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + dose: z.lazy(() => quantitySchema).optional(), + rateRatio: z.lazy(() => ratioSchema).optional(), + rateQuantity: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => + [ + { alternatives: [['rateRatio'], ['rateQuantity']], required: false }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationAdministrationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicationAdministration'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + instantiates: uriSchema.nullable().array().min(1).optional(), + _instantiates: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'stopped', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + medicationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema), + context: z.lazy(() => referenceSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + performer: medicationAdministrationPerformerSchema + .array() + .min(1) + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + request: z.lazy(() => referenceSchema).optional(), + device: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + dosage: medicationAdministrationDosageSchema.optional(), + eventHistory: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['medicationCodeableConcept'], + ['medicationReference'], + ], + required: true, + }, + { + alternatives: [ + ['effectiveDateTime', '_effectiveDateTime'], + ['effectivePeriod'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'instantiates', shadow: '_instantiates', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationDispensePerformerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationDispenseSubstitutionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + wasSubstituted: booleanSchema.optional(), + _wasSubstituted: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + reason: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + responsibleParty: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'wasSubstituted', shadow: '_wasSubstituted' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationDispenseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicationDispense'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'cancelled', + 'completed', + 'declined', + 'entered-in-error', + 'in-progress', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReasonCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + statusReasonReference: z.lazy(() => referenceSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + medicationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + context: z.lazy(() => referenceSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + performer: medicationDispensePerformerSchema.array().min(1).optional(), + location: z.lazy(() => referenceSchema).optional(), + authorizingPrescription: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + daysSupply: z.lazy(() => quantitySchema).optional(), + whenPrepared: dateTimeSchema.optional(), + _whenPrepared: z.lazy(() => elementSchema).optional(), + whenHandedOver: dateTimeSchema.optional(), + _whenHandedOver: z.lazy(() => elementSchema).optional(), + destination: z.lazy(() => referenceSchema).optional(), + receiver: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + dosageInstruction: z + .lazy(() => dosageSchema) + .array() + .min(1) + .optional(), + substitution: medicationDispenseSubstitutionSchema.optional(), + detectedIssue: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + eventHistory: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['statusReasonCodeableConcept'], + ['statusReasonReference'], + ], + required: false, + }, + { + alternatives: [ + ['medicationCodeableConcept'], + ['medicationReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationKnowledgeRelatedMedicationKnowledgeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + reference: z + .lazy(() => referenceSchema) + .array() + .min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const medicationKnowledgeMonographSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationKnowledgeIngredientSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + isActive: booleanSchema.optional(), + _isActive: z.lazy(() => elementSchema).optional(), + strength: z.lazy(() => ratioSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['itemCodeableConcept'], ['itemReference']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationKnowledgeCostSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + source: stringSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + cost: z.lazy(() => moneySchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationKnowledgeMonitoringProgramSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationKnowledgeAdministrationGuidelinesSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + dosage: medicationKnowledgeAdministrationGuidelinesDosageSchema + .array() + .min(1) + .optional(), + indicationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + indicationReference: z.lazy(() => referenceSchema).optional(), + patientCharacteristics: + medicationKnowledgeAdministrationGuidelinesPatientCharacteristicsSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['indicationCodeableConcept'], + ['indicationReference'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const medicationKnowledgeMedicineClassificationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + classification: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const medicationKnowledgePackagingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationKnowledgeDrugCharacteristicSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueCodeableConcept'], + ['valueString', '_valueString'], + ['valueQuantity'], + ['valueBase64Binary', '_valueBase64Binary'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationKnowledgeRegulatorySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + regulatoryAuthority: z.lazy(() => referenceSchema), + substitution: medicationKnowledgeRegulatorySubstitutionSchema + .array() + .min(1) + .optional(), + schedule: medicationKnowledgeRegulatoryScheduleSchema + .array() + .min(1) + .optional(), + maxDispense: medicationKnowledgeRegulatoryMaxDispenseSchema.optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationKnowledgeKineticsSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + areaUnderCurve: z + .lazy(() => quantitySchema) + .array() + .min(1) + .optional(), + lethalDose50: z + .lazy(() => quantitySchema) + .array() + .min(1) + .optional(), + halfLifePeriod: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const medicationKnowledgeAdministrationGuidelinesDosageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + dosage: z + .lazy(() => dosageSchema) .array() .min(1), - _fhirVersion: z + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const medicationKnowledgeAdministrationGuidelinesPatientCharacteristicsSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + characteristicCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + characteristicQuantity: z.lazy(() => quantitySchema).optional(), + value: stringSchema.nullable().array().min(1).optional(), + _value: z .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['characteristicCodeableConcept'], + ['characteristicQuantity'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'value', shadow: '_value', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const medicationKnowledgeRegulatorySubstitutionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + allowed: booleanSchema.optional(), + _allowed: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'allowed', shadow: '_allowed' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const medicationKnowledgeRegulatoryScheduleSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - dependsOn: implementationGuideDependsOnSchema.array().optional(), - global: implementationGuideGlobalSchema.array().optional(), - definition: implementationGuideDefinitionSchema.optional(), - manifest: implementationGuideManifestSchema.optional(), - }), - ) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + schedule: z.lazy(() => codeableConceptSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const ingredientManufacturerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationKnowledgeRegulatoryMaxDispenseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - role: z.enum(['actual', 'allowed', 'possible']).optional(), - _role: z.lazy(() => elementSchema).optional(), - manufacturer: z.lazy(() => referenceSchema), - }), - ) + quantity: z.lazy(() => quantitySchema), + period: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const ingredientSubstanceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const medicationKnowledgeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicationKnowledge'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableReferenceSchema), - strength: ingredientSubstanceStrengthSchema.array().optional(), - }), - ) - -export const ingredientSubstanceStrengthSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - presentationRatio: z.lazy(() => ratioSchema).optional(), - presentationRatioRange: z.lazy(() => ratioRangeSchema).optional(), - textPresentation: stringSchema.optional(), - _textPresentation: z.lazy(() => elementSchema).optional(), - concentrationRatio: z.lazy(() => ratioSchema).optional(), - concentrationRatioRange: z.lazy(() => ratioRangeSchema).optional(), - textConcentration: stringSchema.optional(), - _textConcentration: z.lazy(() => elementSchema).optional(), - measurementPoint: stringSchema.optional(), - _measurementPoint: z.lazy(() => elementSchema).optional(), - country: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - referenceStrength: ingredientSubstanceStrengthReferenceStrengthSchema - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['presentationRatio', 'presentationRatioRange'], - required: false, - }, - { - names: ['concentrationRatio', 'concentrationRatioRange'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const ingredientSubstanceStrengthReferenceStrengthSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + code: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), + _status: z.lazy(() => elementSchema).optional(), + manufacturer: z.lazy(() => referenceSchema).optional(), + doseForm: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => quantitySchema).optional(), + synonym: stringSchema.nullable().array().min(1).optional(), + _synonym: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + relatedMedicationKnowledge: + medicationKnowledgeRelatedMedicationKnowledgeSchema .array() + .min(1) .optional(), - substance: z.lazy(() => codeableReferenceSchema).optional(), - strengthRatio: z.lazy(() => ratioSchema).optional(), - strengthRatioRange: z.lazy(() => ratioRangeSchema).optional(), - measurementPoint: stringSchema.optional(), - _measurementPoint: z.lazy(() => elementSchema).optional(), - country: z - .lazy(() => codeableConceptSchema) + associatedMedication: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + productType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + monograph: medicationKnowledgeMonographSchema.array().min(1).optional(), + ingredient: medicationKnowledgeIngredientSchema.array().min(1).optional(), + preparationInstruction: markdownSchema.optional(), + _preparationInstruction: z.lazy(() => elementSchema).optional(), + intendedRoute: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + cost: medicationKnowledgeCostSchema.array().min(1).optional(), + monitoringProgram: medicationKnowledgeMonitoringProgramSchema + .array() + .min(1) + .optional(), + administrationGuidelines: + medicationKnowledgeAdministrationGuidelinesSchema .array() + .min(1) .optional(), - }) - .refine( - (value) => - [ - { names: ['strengthRatio', 'strengthRatioRange'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + medicineClassification: medicationKnowledgeMedicineClassificationSchema + .array() + .min(1) + .optional(), + packaging: medicationKnowledgePackagingSchema.optional(), + drugCharacteristic: medicationKnowledgeDrugCharacteristicSchema + .array() + .min(1) + .optional(), + contraindication: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + regulatory: medicationKnowledgeRegulatorySchema.array().min(1).optional(), + kinetics: medicationKnowledgeKineticsSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'synonym', shadow: '_synonym', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const ingredientSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Ingredient'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - for: z - .lazy(() => referenceSchema) - .array() - .optional(), - role: z.lazy(() => codeableConceptSchema), - function: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - allergenicIndicator: booleanSchema.optional(), - _allergenicIndicator: z.lazy(() => elementSchema).optional(), - manufacturer: ingredientManufacturerSchema.array().optional(), - substance: ingredientSubstanceSchema, - }), -) - -export const insurancePlanContactSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationRequestDispenseRequestSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - purpose: z.lazy(() => codeableConceptSchema).optional(), - name: z.lazy(() => humanNameSchema).optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - address: z.lazy(() => addressSchema).optional(), - }), - ) + initialFill: medicationRequestDispenseRequestInitialFillSchema.optional(), + dispenseInterval: z.lazy(() => durationSchema).optional(), + validityPeriod: z.lazy(() => periodSchema).optional(), + numberOfRepeatsAllowed: unsignedIntSchema.optional(), + _numberOfRepeatsAllowed: z.lazy(() => elementSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + expectedSupplyDuration: z.lazy(() => durationSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const insurancePlanCoverageSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicationRequestSubstitutionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - network: z - .lazy(() => referenceSchema) - .array() - .optional(), - benefit: insurancePlanCoverageBenefitSchema.array().min(1), - }), - ) + allowedBoolean: booleanSchema.optional(), + _allowedBoolean: z.lazy(() => elementSchema).optional(), + allowedCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + reason: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['allowedBoolean', '_allowedBoolean'], + ['allowedCodeableConcept'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const insurancePlanPlanSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const medicationRequestDispenseRequestInitialFillSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) + quantity: z.lazy(() => quantitySchema).optional(), + duration: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const medicationRequestSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MedicationRequest'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - coverageArea: z - .lazy(() => referenceSchema) + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - network: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - generalCost: insurancePlanPlanGeneralCostSchema.array().optional(), - specificCost: insurancePlanPlanSpecificCostSchema.array().optional(), - }), -) - -export const insurancePlanCoverageBenefitSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) + .optional(), + status: z + .enum([ + 'active', + 'cancelled', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'stopped', + 'unknown', + ]) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + intent: z + .enum([ + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]) .optional(), - type: z.lazy(() => codeableConceptSchema), - requirement: stringSchema.optional(), - _requirement: z.lazy(() => elementSchema).optional(), - limit: insurancePlanCoverageBenefitLimitSchema.array().optional(), - }), - ) - -export const insurancePlanPlanGeneralCostSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + _intent: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + reportedBoolean: booleanSchema.optional(), + _reportedBoolean: z.lazy(() => elementSchema).optional(), + reportedReference: z.lazy(() => referenceSchema).optional(), + medicationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + supportingInformation: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - groupSize: positiveIntSchema.optional(), - _groupSize: z.lazy(() => elementSchema).optional(), - cost: z.lazy(() => moneySchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - }), - ) - -export const insurancePlanPlanSpecificCostSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + performerType: z.lazy(() => codeableConceptSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + reasonReference: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - category: z.lazy(() => codeableConceptSchema), - benefit: insurancePlanPlanSpecificCostBenefitSchema.array().optional(), - }), - ) - -export const insurancePlanCoverageBenefitLimitSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + instantiatesCanonical: canonicalSchema + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + _instantiatesCanonical: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - value: z.lazy(() => quantitySchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - }), - ) - -export const insurancePlanPlanSpecificCostBenefitSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + basedOn: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - cost: insurancePlanPlanSpecificCostBenefitCostSchema.array().optional(), - }), - ) - -export const insurancePlanPlanSpecificCostBenefitCostSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + groupIdentifier: z.lazy(() => identifierSchema).optional(), + courseOfTherapyType: z.lazy(() => codeableConceptSchema).optional(), + insurance: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - applicability: z.lazy(() => codeableConceptSchema).optional(), - qualifiers: z - .lazy(() => codeableConceptSchema) + dosageInstruction: z + .lazy(() => dosageSchema) .array() + .min(1) .optional(), - value: z.lazy(() => quantitySchema).optional(), - }), - ) - -export const insurancePlanSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('InsurancePlan'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), - _status: z.lazy(() => elementSchema).optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - alias: stringSchema.array().optional(), - _alias: z - .lazy(() => elementSchema) - .array() - .optional(), - period: z.lazy(() => periodSchema).optional(), - ownedBy: z.lazy(() => referenceSchema).optional(), - administeredBy: z.lazy(() => referenceSchema).optional(), - coverageArea: z - .lazy(() => referenceSchema) - .array() - .optional(), - contact: insurancePlanContactSchema.array().optional(), - endpoint: z - .lazy(() => referenceSchema) - .array() - .optional(), - network: z - .lazy(() => referenceSchema) - .array() - .optional(), - coverage: insurancePlanCoverageSchema.array().optional(), - plan: insurancePlanPlanSchema.array().optional(), - }), -) - -export const invoiceParticipantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + dispenseRequest: medicationRequestDispenseRequestSchema.optional(), + substitution: medicationRequestSubstitutionSchema.optional(), + priorPrescription: z.lazy(() => referenceSchema).optional(), + detectedIssue: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + eventHistory: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - role: z.lazy(() => codeableConceptSchema).optional(), - actor: z.lazy(() => referenceSchema), - }), - ) - -export const invoiceLineItemSchema: z.ZodType = z.lazy( - () => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - sequence: positiveIntSchema.optional(), - _sequence: z.lazy(() => elementSchema).optional(), - chargeItemReference: z.lazy(() => referenceSchema).optional(), - chargeItemCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - priceComponent: invoiceLineItemPriceComponentSchema.array().optional(), - }) - .refine( - (value) => - [ - { - names: ['chargeItemReference', 'chargeItemCodeableConcept'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( + }) + .refine( + (value) => + [ + { + alternatives: [ + ['reportedBoolean', '_reportedBoolean'], + ['reportedReference'], + ], + required: false, + }, + { + alternatives: [ + ['medicationCodeableConcept'], + ['medicationReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'intent', shadow: '_intent' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, }, - ), -) - -export const invoiceLineItemPriceComponentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum([ - 'base', - 'deduction', - 'discount', - 'informational', - 'surcharge', - 'tax', - ]), - _type: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - amount: z.lazy(() => moneySchema).optional(), - }), - ) + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const invoiceSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Invoice'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'balanced', - 'cancelled', - 'draft', - 'entered-in-error', - 'issued', - ]), - _status: z.lazy(() => elementSchema).optional(), - cancelledReason: stringSchema.optional(), - _cancelledReason: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - recipient: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - participant: invoiceParticipantSchema.array().optional(), - issuer: z.lazy(() => referenceSchema).optional(), - account: z.lazy(() => referenceSchema).optional(), - lineItem: invoiceLineItemSchema.array().optional(), - totalPriceComponent: z - .lazy(() => invoiceLineItemPriceComponentSchema) - .array() - .optional(), - totalNet: z.lazy(() => moneySchema).optional(), - totalGross: z.lazy(() => moneySchema).optional(), - paymentTerms: markdownSchema.optional(), - _paymentTerms: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }), -) - -export const librarySchema: z.ZodType = z.lazy(() => +export const medicationStatementSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Library'), + resourceType: z.literal('MedicationStatement'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -17363,112 +30831,110 @@ export const librarySchema: z.ZodType = z.lazy(() => contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z - .lazy(() => codeableConceptSchema) + basedOn: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - author: z - .lazy(() => contactDetailSchema) + partOf: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - editor: z - .lazy(() => contactDetailSchema) - .array() + status: z + .enum([ + 'active', + 'completed', + 'entered-in-error', + 'intended', + 'not-taken', + 'on-hold', + 'stopped', + 'unknown', + ]) .optional(), - reviewer: z - .lazy(() => contactDetailSchema) + _status: z.lazy(() => elementSchema).optional(), + statusReason: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - endorser: z - .lazy(() => contactDetailSchema) + category: z.lazy(() => codeableConceptSchema).optional(), + medicationCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + medicationReference: z.lazy(() => referenceSchema).optional(), + subject: z.lazy(() => referenceSchema), + context: z.lazy(() => referenceSchema).optional(), + effectiveDateTime: dateTimeSchema.optional(), + _effectiveDateTime: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + dateAsserted: dateTimeSchema.optional(), + _dateAsserted: z.lazy(() => elementSchema).optional(), + informationSource: z.lazy(() => referenceSchema).optional(), + derivedFrom: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) + reasonCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - parameter: z - .lazy(() => parameterDefinitionSchema) + reasonReference: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - dataRequirement: z - .lazy(() => dataRequirementSchema) + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - content: z - .lazy(() => attachmentSchema) + dosage: z + .lazy(() => dosageSchema) .array() + .min(1) .optional(), }) .refine( (value) => [ { - names: ['subjectCodeableConcept', 'subjectReference'], + alternatives: [ + ['medicationCodeableConcept'], + ['medicationReference'], + ], + required: true, + }, + { + alternatives: [ + ['effectiveDateTime', '_effectiveDateTime'], + ['effectivePeriod'], + ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -17476,456 +30942,297 @@ export const librarySchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const linkageItemSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum(['alternate', 'historical', 'source']), - _type: z.lazy(() => elementSchema).optional(), - resource: z.lazy(() => referenceSchema), - }), -) - -export const linkageSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Linkage'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - item: linkageItemSchema.array().min(1), - }), -) - -export const listEntrySchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - flag: z.lazy(() => codeableConceptSchema).optional(), - deleted: booleanSchema.optional(), - _deleted: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - item: z.lazy(() => referenceSchema), - }), -) - -export const listSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('List'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['current', 'entered-in-error', 'retired']), - _status: z.lazy(() => elementSchema).optional(), - mode: z.enum(['changes', 'snapshot', 'working']), - _mode: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - source: z.lazy(() => referenceSchema).optional(), - orderedBy: z.lazy(() => codeableConceptSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - entry: listEntrySchema.array().optional(), - emptyReason: z.lazy(() => codeableConceptSchema).optional(), - }), -) - -export const locationPositionSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +) as unknown as z.ZodType> + +export const medicinalProductDefinitionContactSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - longitude: decimalSchema, - _longitude: z.lazy(() => elementSchema).optional(), - latitude: decimalSchema, - _latitude: z.lazy(() => elementSchema).optional(), - altitude: decimalSchema.optional(), - _altitude: z.lazy(() => elementSchema).optional(), - }), -) + type: z.lazy(() => codeableConceptSchema).optional(), + contact: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const locationHoursOfOperationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductDefinitionNameSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - daysOfWeek: z - .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + productName: stringSchema.optional(), + _productName: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + namePart: medicinalProductDefinitionNameNamePartSchema .array() + .min(1) .optional(), - _daysOfWeek: z - .lazy(() => elementSchema) + countryLanguage: medicinalProductDefinitionNameCountryLanguageSchema .array() + .min(1) .optional(), - allDay: booleanSchema.optional(), - _allDay: z.lazy(() => elementSchema).optional(), - openingTime: timeSchema.optional(), - _openingTime: z.lazy(() => elementSchema).optional(), - closingTime: timeSchema.optional(), - _closingTime: z.lazy(() => elementSchema).optional(), - }), - ) - -export const locationSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Location'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'inactive', 'suspended']).optional(), - _status: z.lazy(() => elementSchema).optional(), - operationalStatus: z.lazy(() => codingSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - alias: stringSchema.array().optional(), - _alias: z - .lazy(() => elementSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - mode: z.enum(['instance', 'kind']).optional(), - _mode: z.lazy(() => elementSchema).optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - address: z.lazy(() => addressSchema).optional(), - physicalType: z.lazy(() => codeableConceptSchema).optional(), - position: locationPositionSchema.optional(), - managingOrganization: z.lazy(() => referenceSchema).optional(), - partOf: z.lazy(() => referenceSchema).optional(), - hoursOfOperation: locationHoursOfOperationSchema.array().optional(), - availabilityExceptions: stringSchema.optional(), - _availabilityExceptions: z.lazy(() => elementSchema).optional(), - endpoint: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) - -export const manufacturedItemDefinitionPropertySchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueCodeableConcept', - 'valueQuantity', - 'valueDate', - 'valueBoolean', - 'valueAttachment', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'productName', shadow: '_productName' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const manufacturedItemDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('ManufacturedItemDefinition'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), +export const medicinalProductDefinitionCrossReferenceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - manufacturedDoseForm: z.lazy(() => codeableConceptSchema), - unitOfPresentation: z.lazy(() => codeableConceptSchema).optional(), - manufacturer: z - .lazy(() => referenceSchema) - .array() - .optional(), - ingredient: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - property: manufacturedItemDefinitionPropertySchema.array().optional(), - }), - ) + product: z.lazy(() => codeableReferenceSchema), + type: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const marketingStatusSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - country: z.lazy(() => codeableConceptSchema).optional(), - jurisdiction: z.lazy(() => codeableConceptSchema).optional(), - status: z.lazy(() => codeableConceptSchema), - dateRange: z.lazy(() => periodSchema).optional(), - restoreDate: dateTimeSchema.optional(), - _restoreDate: z.lazy(() => elementSchema).optional(), - }), -) - -export const measureGroupSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - population: measureGroupPopulationSchema.array().optional(), - stratifier: measureGroupStratifierSchema.array().optional(), - }), -) - -export const measureSupplementalDataSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductDefinitionOperationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - usage: z - .lazy(() => codeableConceptSchema) + type: z.lazy(() => codeableReferenceSchema).optional(), + effectiveDate: z.lazy(() => periodSchema).optional(), + organization: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - criteria: z.lazy(() => expressionSchema), - }), - ) + confidentialityIndicator: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const measureGroupPopulationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductDefinitionCharacteristicSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - criteria: z.lazy(() => expressionSchema), - }), - ) + type: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueCodeableConcept'], + ['valueQuantity'], + ['valueDate', '_valueDate'], + ['valueBoolean', '_valueBoolean'], + ['valueAttachment'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const measureGroupStratifierSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductDefinitionNameNamePartSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - criteria: z.lazy(() => expressionSchema).optional(), - component: measureGroupStratifierComponentSchema.array().optional(), - }), - ) + part: stringSchema.optional(), + _part: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'part', shadow: '_part' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const measureGroupStratifierComponentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const medicinalProductDefinitionNameCountryLanguageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - criteria: z.lazy(() => expressionSchema), - }), - ) + country: z.lazy(() => codeableConceptSchema), + jurisdiction: z.lazy(() => codeableConceptSchema).optional(), + language: z.lazy(() => codeableConceptSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const measureSchema: z.ZodType = z.lazy(() => +export const medicinalProductDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Measure'), + resourceType: z.literal('MedicinalProductDefinition'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -17937,308 +31244,366 @@ export const measureSchema: z.ZodType = z.lazy(() => contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + domain: z.lazy(() => codeableConceptSchema).optional(), version: stringSchema.optional(), _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + statusDate: dateTimeSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) + combinedPharmaceuticalDoseForm: z + .lazy(() => codeableConceptSchema) + .optional(), + route: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - jurisdiction: z + indication: markdownSchema.optional(), + _indication: z.lazy(() => elementSchema).optional(), + legalStatusOfSupply: z.lazy(() => codeableConceptSchema).optional(), + additionalMonitoringIndicator: z + .lazy(() => codeableConceptSchema) + .optional(), + specialMeasures: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z + pediatricUseIndicator: z.lazy(() => codeableConceptSchema).optional(), + classification: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - author: z - .lazy(() => contactDetailSchema) + marketingStatus: z + .lazy(() => marketingStatusSchema) .array() + .min(1) .optional(), - editor: z - .lazy(() => contactDetailSchema) + packagedMedicinalProduct: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - reviewer: z - .lazy(() => contactDetailSchema) + ingredient: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - endorser: z - .lazy(() => contactDetailSchema) + impurity: z + .lazy(() => codeableReferenceSchema) .array() + .min(1) .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) + attachedDocument: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - library: canonicalSchema.array().optional(), - _library: z - .lazy(() => elementSchema) + masterFile: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - disclaimer: markdownSchema.optional(), - _disclaimer: z.lazy(() => elementSchema).optional(), - scoring: z.lazy(() => codeableConceptSchema).optional(), - compositeScoring: z.lazy(() => codeableConceptSchema).optional(), - type: z - .lazy(() => codeableConceptSchema) + contact: medicinalProductDefinitionContactSchema .array() + .min(1) .optional(), - riskAdjustment: stringSchema.optional(), - _riskAdjustment: z.lazy(() => elementSchema).optional(), - rateAggregation: stringSchema.optional(), - _rateAggregation: z.lazy(() => elementSchema).optional(), - rationale: markdownSchema.optional(), - _rationale: z.lazy(() => elementSchema).optional(), - clinicalRecommendationStatement: markdownSchema.optional(), - _clinicalRecommendationStatement: z.lazy(() => elementSchema).optional(), - improvementNotation: z.lazy(() => codeableConceptSchema).optional(), - definition: markdownSchema.array().optional(), - _definition: z - .lazy(() => elementSchema) + clinicalTrial: z + .lazy(() => referenceSchema) .array() + .min(1) + .optional(), + code: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + name: medicinalProductDefinitionNameSchema.array().min(1), + crossReference: medicinalProductDefinitionCrossReferenceSchema + .array() + .min(1) + .optional(), + operation: medicinalProductDefinitionOperationSchema + .array() + .min(1) + .optional(), + characteristic: medicinalProductDefinitionCharacteristicSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const messageDefinitionFocusSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) .optional(), - guidance: markdownSchema.optional(), - _guidance: z.lazy(() => elementSchema).optional(), - group: measureGroupSchema.array().optional(), - supplementalData: measureSupplementalDataSchema.array().optional(), + _code: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + min: unsignedIntSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'code', shadow: '_code' }, + { name: 'min', shadow: '_min' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['subjectCodeableConcept', 'subjectReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const measureReportGroupSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - population: measureReportGroupPopulationSchema.array().optional(), - measureScore: z.lazy(() => quantitySchema).optional(), - stratifier: measureReportGroupStratifierSchema.array().optional(), - }), - ) - -export const measureReportGroupPopulationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - count: integerSchema.optional(), - _count: z.lazy(() => elementSchema).optional(), - subjectResults: z.lazy(() => referenceSchema).optional(), - }), - ) - -export const measureReportGroupStratifierSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - stratum: measureReportGroupStratifierStratumSchema.array().optional(), - }), - ) - -export const measureReportGroupStratifierStratumSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - value: z.lazy(() => codeableConceptSchema).optional(), - component: measureReportGroupStratifierStratumComponentSchema - .array() - .optional(), - population: measureReportGroupStratifierStratumPopulationSchema - .array() - .optional(), - measureScore: z.lazy(() => quantitySchema).optional(), - }), - ) - -export const measureReportGroupStratifierStratumComponentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - value: z.lazy(() => codeableConceptSchema), - }), - ) +) as unknown as z.ZodType> -export const measureReportGroupStratifierStratumPopulationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const messageDefinitionAllowedResponseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - count: integerSchema.optional(), - _count: z.lazy(() => elementSchema).optional(), - subjectResults: z.lazy(() => referenceSchema).optional(), - }), - ) + message: canonicalSchema.optional(), + _message: z.lazy(() => elementSchema).optional(), + situation: markdownSchema.optional(), + _situation: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'message', shadow: '_message' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const measureReportSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('MeasureReport'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['complete', 'error', 'pending']), - _status: z.lazy(() => elementSchema).optional(), - type: z.enum(['data-collection', 'individual', 'subject-list', 'summary']), - _type: z.lazy(() => elementSchema).optional(), - measure: canonicalSchema, - _measure: z.lazy(() => elementSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - reporter: z.lazy(() => referenceSchema).optional(), - period: z.lazy(() => periodSchema), - improvementNotation: z.lazy(() => codeableConceptSchema).optional(), - group: measureReportGroupSchema.array().optional(), - evaluatedResource: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) - -export const mediaSchema: z.ZodType = z.lazy(() => +export const messageDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Media'), + resourceType: z.literal('MessageDefinition'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -18250,78 +31615,110 @@ export const mediaSchema: z.ZodType = z.lazy(() => contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - basedOn: z - .lazy(() => referenceSchema) + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + replaces: canonicalSchema.nullable().array().min(1).optional(), + _replaces: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - partOf: z - .lazy(() => referenceSchema) + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - status: z.enum([ - 'completed', - 'entered-in-error', - 'in-progress', - 'not-done', - 'on-hold', - 'preparation', - 'stopped', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - modality: z.lazy(() => codeableConceptSchema).optional(), - view: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - createdDateTime: dateTimeSchema.optional(), - _createdDateTime: z.lazy(() => elementSchema).optional(), - createdPeriod: z.lazy(() => periodSchema).optional(), - issued: instantSchema.optional(), - _issued: z.lazy(() => elementSchema).optional(), - operator: z.lazy(() => referenceSchema).optional(), - reasonCode: z + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), - deviceName: stringSchema.optional(), - _deviceName: z.lazy(() => elementSchema).optional(), - device: z.lazy(() => referenceSchema).optional(), - height: positiveIntSchema.optional(), - _height: z.lazy(() => elementSchema).optional(), - width: positiveIntSchema.optional(), - _width: z.lazy(() => elementSchema).optional(), - frames: positiveIntSchema.optional(), - _frames: z.lazy(() => elementSchema).optional(), - duration: decimalSchema.optional(), - _duration: z.lazy(() => elementSchema).optional(), - content: z.lazy(() => attachmentSchema), - note: z - .lazy(() => annotationSchema) + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + base: canonicalSchema.optional(), + _base: z.lazy(() => elementSchema).optional(), + parent: canonicalSchema.nullable().array().min(1).optional(), + _parent: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + eventCoding: z.lazy(() => codingSchema).optional(), + eventUri: uriSchema.optional(), + _eventUri: z.lazy(() => elementSchema).optional(), + category: z.enum(['consequence', 'currency', 'notification']).optional(), + _category: z.lazy(() => elementSchema).optional(), + focus: messageDefinitionFocusSchema.array().min(1).optional(), + responseRequired: z + .enum(['always', 'never', 'on-error', 'on-success']) + .optional(), + _responseRequired: z.lazy(() => elementSchema).optional(), + allowedResponse: messageDefinitionAllowedResponseSchema + .array() + .min(1) + .optional(), + graph: canonicalSchema.nullable().array().min(1).optional(), + _graph: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), }) .refine( (value) => [ - { names: ['createdDateTime', 'createdPeriod'], required: false }, + { + alternatives: [['eventCoding'], ['eventUri', '_eventUri']], + required: true, + }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -18329,164 +31726,224 @@ export const mediaSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'date', shadow: '_date' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'replaces', shadow: '_replaces', required: false }, + { name: 'parent', shadow: '_parent', required: false }, + { name: 'graph', shadow: '_graph', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const medicationIngredientSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - itemReference: z.lazy(() => referenceSchema).optional(), - isActive: booleanSchema.optional(), - _isActive: z.lazy(() => elementSchema).optional(), - strength: z.lazy(() => ratioSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['itemCodeableConcept', 'itemReference'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) +export const messageHeaderDestinationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + target: z.lazy(() => referenceSchema).optional(), + endpoint: urlSchema.optional(), + _endpoint: z.lazy(() => elementSchema).optional(), + receiver: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'endpoint', shadow: '_endpoint' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationBatchSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const messageHeaderSourceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - lotNumber: stringSchema.optional(), - _lotNumber: z.lazy(() => elementSchema).optional(), - expirationDate: dateTimeSchema.optional(), - _expirationDate: z.lazy(() => elementSchema).optional(), - }), -) - -export const medicationSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Medication'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), - _status: z.lazy(() => elementSchema).optional(), - manufacturer: z.lazy(() => referenceSchema).optional(), - form: z.lazy(() => codeableConceptSchema).optional(), - amount: z.lazy(() => ratioSchema).optional(), - ingredient: medicationIngredientSchema.array().optional(), - batch: medicationBatchSchema.optional(), - }), -) - -export const medicationAdministrationPerformerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + software: stringSchema.optional(), + _software: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + contact: z.lazy(() => contactPointSchema).optional(), + endpoint: urlSchema.optional(), + _endpoint: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'endpoint', shadow: '_endpoint' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const messageHeaderResponseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - function: z.lazy(() => codeableConceptSchema).optional(), - actor: z.lazy(() => referenceSchema), - }), - ) - -export const medicationAdministrationDosageSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - site: z.lazy(() => codeableConceptSchema).optional(), - route: z.lazy(() => codeableConceptSchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - dose: z.lazy(() => quantitySchema).optional(), - rateRatio: z.lazy(() => ratioSchema).optional(), - rateQuantity: z.lazy(() => quantitySchema).optional(), - }) - .refine( - (value) => - [{ names: ['rateRatio', 'rateQuantity'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + identifier: idSchema.optional(), + _identifier: z.lazy(() => elementSchema).optional(), + code: z.enum(['fatal-error', 'ok', 'transient-error']).optional(), + _code: z.lazy(() => elementSchema).optional(), + details: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'identifier', shadow: '_identifier' }, + { name: 'code', shadow: '_code' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationAdministrationSchema: z.ZodType = +export const messageHeaderSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('MedicationAdministration'), + resourceType: z.literal('MessageHeader'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -18498,91 +31955,50 @@ export const medicationAdministrationSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - instantiates: uriSchema.array().optional(), - _instantiates: z - .lazy(() => elementSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'completed', - 'entered-in-error', - 'in-progress', - 'not-done', - 'on-hold', - 'stopped', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - medicationCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - medicationReference: z.lazy(() => referenceSchema).optional(), - subject: z.lazy(() => referenceSchema), - context: z.lazy(() => referenceSchema).optional(), - supportingInformation: z - .lazy(() => referenceSchema) - .array() - .optional(), - effectiveDateTime: dateTimeSchema.optional(), - _effectiveDateTime: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - performer: medicationAdministrationPerformerSchema.array().optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - request: z.lazy(() => referenceSchema).optional(), - device: z - .lazy(() => referenceSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - dosage: medicationAdministrationDosageSchema.optional(), - eventHistory: z + eventCoding: z.lazy(() => codingSchema).optional(), + eventUri: uriSchema.optional(), + _eventUri: z.lazy(() => elementSchema).optional(), + destination: messageHeaderDestinationSchema.array().min(1).optional(), + sender: z.lazy(() => referenceSchema).optional(), + enterer: z.lazy(() => referenceSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + source: messageHeaderSourceSchema, + responsible: z.lazy(() => referenceSchema).optional(), + reason: z.lazy(() => codeableConceptSchema).optional(), + response: messageHeaderResponseSchema.optional(), + focus: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), + definition: canonicalSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), }) .refine( (value) => [ { - names: ['medicationCodeableConcept', 'medicationReference'], + alternatives: [['eventCoding'], ['eventUri', '_eventUri']], required: true, }, - { names: ['effectiveDateTime', 'effectivePeriod'], required: true }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -18590,730 +32006,1348 @@ export const medicationAdministrationSchema: z.ZodType Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) + ) as unknown as z.ZodType> + +export const metaSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + versionId: idSchema.optional(), + _versionId: z.lazy(() => elementSchema).optional(), + lastUpdated: instantSchema.optional(), + _lastUpdated: z.lazy(() => elementSchema).optional(), + source: uriSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.nullable().array().min(1).optional(), + _profile: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + security: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + tag: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'profile', shadow: '_profile', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const molecularSequenceReferenceSeqSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + chromosome: z.lazy(() => codeableConceptSchema).optional(), + genomeBuild: stringSchema.optional(), + _genomeBuild: z.lazy(() => elementSchema).optional(), + orientation: z.enum(['antisense', 'sense']).optional(), + _orientation: z.lazy(() => elementSchema).optional(), + referenceSeqId: z.lazy(() => codeableConceptSchema).optional(), + referenceSeqPointer: z.lazy(() => referenceSchema).optional(), + referenceSeqString: stringSchema.optional(), + _referenceSeqString: z.lazy(() => elementSchema).optional(), + strand: z.enum(['crick', 'watson']).optional(), + _strand: z.lazy(() => elementSchema).optional(), + windowStart: integerSchema.optional(), + _windowStart: z.lazy(() => elementSchema).optional(), + windowEnd: integerSchema.optional(), + _windowEnd: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const molecularSequenceVariantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + observedAllele: stringSchema.optional(), + _observedAllele: z.lazy(() => elementSchema).optional(), + referenceAllele: stringSchema.optional(), + _referenceAllele: z.lazy(() => elementSchema).optional(), + cigar: stringSchema.optional(), + _cigar: z.lazy(() => elementSchema).optional(), + variantPointer: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const molecularSequenceQualitySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.enum(['indel', 'snp', 'unknown']).optional(), + _type: z.lazy(() => elementSchema).optional(), + standardSequence: z.lazy(() => codeableConceptSchema).optional(), + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + score: z.lazy(() => quantitySchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + truthTP: decimalSchema.optional(), + _truthTP: z.lazy(() => elementSchema).optional(), + queryTP: decimalSchema.optional(), + _queryTP: z.lazy(() => elementSchema).optional(), + truthFN: decimalSchema.optional(), + _truthFN: z.lazy(() => elementSchema).optional(), + queryFP: decimalSchema.optional(), + _queryFP: z.lazy(() => elementSchema).optional(), + gtFP: decimalSchema.optional(), + _gtFP: z.lazy(() => elementSchema).optional(), + precision: decimalSchema.optional(), + _precision: z.lazy(() => elementSchema).optional(), + recall: decimalSchema.optional(), + _recall: z.lazy(() => elementSchema).optional(), + fScore: decimalSchema.optional(), + _fScore: z.lazy(() => elementSchema).optional(), + roc: molecularSequenceQualityRocSchema.optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationDispensePerformerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const molecularSequenceRepositorySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - function: z.lazy(() => codeableConceptSchema).optional(), - actor: z.lazy(() => referenceSchema), - }), - ) + type: z + .enum(['directlink', 'login', 'oauth', 'openapi', 'other']) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + datasetId: stringSchema.optional(), + _datasetId: z.lazy(() => elementSchema).optional(), + variantsetId: stringSchema.optional(), + _variantsetId: z.lazy(() => elementSchema).optional(), + readsetId: stringSchema.optional(), + _readsetId: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationDispenseSubstitutionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const molecularSequenceStructureVariantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - wasSubstituted: booleanSchema, - _wasSubstituted: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - reason: z - .lazy(() => codeableConceptSchema) + variantType: z.lazy(() => codeableConceptSchema).optional(), + exact: booleanSchema.optional(), + _exact: z.lazy(() => elementSchema).optional(), + length: integerSchema.optional(), + _length: z.lazy(() => elementSchema).optional(), + outer: molecularSequenceStructureVariantOuterSchema.optional(), + inner: molecularSequenceStructureVariantInnerSchema.optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const molecularSequenceQualityRocSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - responsibleParty: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), - ) - -export const medicationDispenseSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - resourceType: z.literal('MedicationDispense'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'cancelled', - 'completed', - 'declined', - 'entered-in-error', - 'in-progress', - 'on-hold', - 'preparation', - 'stopped', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReasonCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - statusReasonReference: z.lazy(() => referenceSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - medicationCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - medicationReference: z.lazy(() => referenceSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - context: z.lazy(() => referenceSchema).optional(), - supportingInformation: z - .lazy(() => referenceSchema) - .array() - .optional(), - performer: medicationDispensePerformerSchema.array().optional(), - location: z.lazy(() => referenceSchema).optional(), - authorizingPrescription: z - .lazy(() => referenceSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - daysSupply: z.lazy(() => quantitySchema).optional(), - whenPrepared: dateTimeSchema.optional(), - _whenPrepared: z.lazy(() => elementSchema).optional(), - whenHandedOver: dateTimeSchema.optional(), - _whenHandedOver: z.lazy(() => elementSchema).optional(), - destination: z.lazy(() => referenceSchema).optional(), - receiver: z - .lazy(() => referenceSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - dosageInstruction: z - .lazy(() => dosageSchema) - .array() - .optional(), - substitution: medicationDispenseSubstitutionSchema.optional(), - detectedIssue: z - .lazy(() => referenceSchema) - .array() - .optional(), - eventHistory: z - .lazy(() => referenceSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: ['statusReasonCodeableConcept', 'statusReasonReference'], - required: false, - }, - { - names: ['medicationCodeableConcept', 'medicationReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + score: integerSchema.nullable().array().min(1).optional(), + _score: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + numTP: integerSchema.nullable().array().min(1).optional(), + _numTP: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + numFP: integerSchema.nullable().array().min(1).optional(), + _numFP: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + numFN: integerSchema.nullable().array().min(1).optional(), + _numFN: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + precision: decimalSchema.nullable().array().min(1).optional(), + _precision: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + sensitivity: decimalSchema.nullable().array().min(1).optional(), + _sensitivity: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + fMeasure: decimalSchema.nullable().array().min(1).optional(), + _fMeasure: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'score', shadow: '_score', required: false }, + { name: 'numTP', shadow: '_numTP', required: false }, + { name: 'numFP', shadow: '_numFP', required: false }, + { name: 'numFN', shadow: '_numFN', required: false }, + { name: 'precision', shadow: '_precision', required: false }, + { name: 'sensitivity', shadow: '_sensitivity', required: false }, + { name: 'fMeasure', shadow: '_fMeasure', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeRelatedMedicationKnowledgeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const molecularSequenceStructureVariantOuterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - reference: z - .lazy(() => referenceSchema) - .array() - .min(1), - }), - ) + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeMonographSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const molecularSequenceStructureVariantInnerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - source: z.lazy(() => referenceSchema).optional(), - }), - ) - -export const medicationKnowledgeIngredientSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - itemReference: z.lazy(() => referenceSchema).optional(), - isActive: booleanSchema.optional(), - _isActive: z.lazy(() => elementSchema).optional(), - strength: z.lazy(() => ratioSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['itemCodeableConcept', 'itemReference'], required: true }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + start: integerSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: integerSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeCostSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const molecularSequenceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('MolecularSequence'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - source: stringSchema.optional(), - _source: z.lazy(() => elementSchema).optional(), - cost: z.lazy(() => moneySchema), - }), - ) + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + type: z.enum(['aa', 'dna', 'rna']).optional(), + _type: z.lazy(() => elementSchema).optional(), + coordinateSystem: integerSchema.optional(), + _coordinateSystem: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema).optional(), + specimen: z.lazy(() => referenceSchema).optional(), + device: z.lazy(() => referenceSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + referenceSeq: molecularSequenceReferenceSeqSchema.optional(), + variant: molecularSequenceVariantSchema.array().min(1).optional(), + observedSeq: stringSchema.optional(), + _observedSeq: z.lazy(() => elementSchema).optional(), + quality: molecularSequenceQualitySchema.array().min(1).optional(), + readCoverage: integerSchema.optional(), + _readCoverage: z.lazy(() => elementSchema).optional(), + repository: molecularSequenceRepositorySchema.array().min(1).optional(), + pointer: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + structureVariant: molecularSequenceStructureVariantSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'coordinateSystem', shadow: '_coordinateSystem' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeMonitoringProgramSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const moneySchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + currency: codeSchema.optional(), + _currency: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const namingSystemUniqueIdSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - }), - ) + type: z.enum(['oid', 'other', 'uri', 'uuid']).optional(), + _type: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + preferred: booleanSchema.optional(), + _preferred: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'type', shadow: '_type' }, + { name: 'value', shadow: '_value' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeAdministrationGuidelinesSchema: z.ZodType = - z.lazy(() => +export const namingSystemSchema: z.ZodType> = z.lazy( + () => z .strictObject({ - id: stringSchema.optional(), + resourceType: z.literal('NamingSystem'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + kind: z.enum(['codesystem', 'identifier', 'root']).optional(), + _kind: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) .optional(), - dosage: medicationKnowledgeAdministrationGuidelinesDosageSchema + responsible: stringSchema.optional(), + _responsible: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - indicationCodeableConcept: z + jurisdiction: z .lazy(() => codeableConceptSchema) + .array() + .min(1) .optional(), - indicationReference: z.lazy(() => referenceSchema).optional(), - patientCharacteristics: - medicationKnowledgeAdministrationGuidelinesPatientCharacteristicsSchema - .array() - .optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + uniqueId: namingSystemUniqueIdSchema.array().min(1), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + { name: 'kind', shadow: '_kind' }, + { name: 'date', shadow: '_date' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['indicationCodeableConcept', 'indicationReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> -export const medicationKnowledgeMedicineClassificationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const narrativeSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['additional', 'empty', 'extensions', 'generated']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + div: xhtmlSchema.optional(), + _div: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'div', shadow: '_div' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const nutritionOrderOralDietSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - classification: z + type: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - }), - ) + schedule: z + .lazy(() => timingSchema) + .array() + .min(1) + .optional(), + nutrient: nutritionOrderOralDietNutrientSchema.array().min(1).optional(), + texture: nutritionOrderOralDietTextureSchema.array().min(1).optional(), + fluidConsistencyType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + instruction: stringSchema.optional(), + _instruction: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgePackagingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const nutritionOrderSupplementSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), type: z.lazy(() => codeableConceptSchema).optional(), + productName: stringSchema.optional(), + _productName: z.lazy(() => elementSchema).optional(), + schedule: z + .lazy(() => timingSchema) + .array() + .min(1) + .optional(), quantity: z.lazy(() => quantitySchema).optional(), - }), - ) - -export const medicationKnowledgeDrugCharacteristicSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueCodeableConcept', - 'valueString', - 'valueQuantity', - 'valueBase64Binary', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + instruction: stringSchema.optional(), + _instruction: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeRegulatorySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const nutritionOrderEnteralFormulaSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - regulatoryAuthority: z.lazy(() => referenceSchema), - substitution: medicationKnowledgeRegulatorySubstitutionSchema + baseFormulaType: z.lazy(() => codeableConceptSchema).optional(), + baseFormulaProductName: stringSchema.optional(), + _baseFormulaProductName: z.lazy(() => elementSchema).optional(), + additiveType: z.lazy(() => codeableConceptSchema).optional(), + additiveProductName: stringSchema.optional(), + _additiveProductName: z.lazy(() => elementSchema).optional(), + caloricDensity: z.lazy(() => quantitySchema).optional(), + routeofAdministration: z.lazy(() => codeableConceptSchema).optional(), + administration: nutritionOrderEnteralFormulaAdministrationSchema .array() + .min(1) .optional(), - schedule: medicationKnowledgeRegulatoryScheduleSchema.array().optional(), - maxDispense: medicationKnowledgeRegulatoryMaxDispenseSchema.optional(), - }), - ) + maxVolumeToDeliver: z.lazy(() => quantitySchema).optional(), + administrationInstruction: stringSchema.optional(), + _administrationInstruction: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeKineticsSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const nutritionOrderOralDietNutrientSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - areaUnderCurve: z - .lazy(() => quantitySchema) + modifier: z.lazy(() => codeableConceptSchema).optional(), + amount: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const nutritionOrderOralDietTextureSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() - .optional(), - lethalDose50: z - .lazy(() => quantitySchema) + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - halfLifePeriod: z.lazy(() => durationSchema).optional(), - }), - ) + modifier: z.lazy(() => codeableConceptSchema).optional(), + foodType: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeAdministrationGuidelinesDosageSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const nutritionOrderEnteralFormulaAdministrationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - dosage: z - .lazy(() => dosageSchema) - .array() - .min(1), - }), - ) + schedule: z.lazy(() => timingSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + rateQuantity: z.lazy(() => quantitySchema).optional(), + rateRatio: z.lazy(() => ratioSchema).optional(), + }) + .refine( + (value) => + [ + { alternatives: [['rateQuantity'], ['rateRatio']], required: false }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const medicationKnowledgeAdministrationGuidelinesPatientCharacteristicsSchema: z.ZodType = +export const nutritionOrderSchema: z.ZodType> = z.lazy(() => z .strictObject({ - id: stringSchema.optional(), + resourceType: z.literal('NutritionOrder'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - characteristicCodeableConcept: z - .lazy(() => codeableConceptSchema) + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + instantiatesCanonical: canonicalSchema + .nullable() + .array() + .min(1) + .optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) .optional(), - characteristicQuantity: z.lazy(() => quantitySchema).optional(), - value: stringSchema.array().optional(), - _value: z + instantiates: uriSchema.nullable().array().min(1).optional(), + _instantiates: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), + status: z + .enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + intent: z + .enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]) + .optional(), + _intent: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + dateTime: dateTimeSchema.optional(), + _dateTime: z.lazy(() => elementSchema).optional(), + orderer: z.lazy(() => referenceSchema).optional(), + allergyIntolerance: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + foodPreferenceModifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + excludeFoodModifier: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + oralDiet: nutritionOrderOralDietSchema.optional(), + supplement: nutritionOrderSupplementSchema.array().min(1).optional(), + enteralFormula: nutritionOrderEnteralFormulaSchema.optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'intent', shadow: '_intent' }, + { name: 'dateTime', shadow: '_dateTime' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + { name: 'instantiates', shadow: '_instantiates', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } }) .refine( - (value) => - [ - { - names: [ - 'characteristicCodeableConcept', - 'characteristicQuantity', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const medicationKnowledgeRegulatorySubstitutionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const nutritionProductNutrientSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - allowed: booleanSchema, - _allowed: z.lazy(() => elementSchema).optional(), - }), - ) - -export const medicationKnowledgeRegulatoryScheduleSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + item: z.lazy(() => codeableReferenceSchema).optional(), + amount: z + .lazy(() => ratioSchema) .array() + .min(1) .optional(), - schedule: z.lazy(() => codeableConceptSchema), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationKnowledgeRegulatoryMaxDispenseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const nutritionProductIngredientSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - quantity: z.lazy(() => quantitySchema), - period: z.lazy(() => durationSchema).optional(), - }), - ) - -export const medicationKnowledgeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('MedicationKnowledge'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), - _status: z.lazy(() => elementSchema).optional(), - manufacturer: z.lazy(() => referenceSchema).optional(), - doseForm: z.lazy(() => codeableConceptSchema).optional(), - amount: z.lazy(() => quantitySchema).optional(), - synonym: stringSchema.array().optional(), - _synonym: z - .lazy(() => elementSchema) - .array() - .optional(), - relatedMedicationKnowledge: - medicationKnowledgeRelatedMedicationKnowledgeSchema.array().optional(), - associatedMedication: z - .lazy(() => referenceSchema) - .array() - .optional(), - productType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - monograph: medicationKnowledgeMonographSchema.array().optional(), - ingredient: medicationKnowledgeIngredientSchema.array().optional(), - preparationInstruction: markdownSchema.optional(), - _preparationInstruction: z.lazy(() => elementSchema).optional(), - intendedRoute: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - cost: medicationKnowledgeCostSchema.array().optional(), - monitoringProgram: medicationKnowledgeMonitoringProgramSchema - .array() - .optional(), - administrationGuidelines: - medicationKnowledgeAdministrationGuidelinesSchema.array().optional(), - medicineClassification: medicationKnowledgeMedicineClassificationSchema - .array() - .optional(), - packaging: medicationKnowledgePackagingSchema.optional(), - drugCharacteristic: medicationKnowledgeDrugCharacteristicSchema - .array() - .optional(), - contraindication: z - .lazy(() => referenceSchema) + item: z.lazy(() => codeableReferenceSchema), + amount: z + .lazy(() => ratioSchema) .array() + .min(1) .optional(), - regulatory: medicationKnowledgeRegulatorySchema.array().optional(), - kinetics: medicationKnowledgeKineticsSchema.array().optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationRequestDispenseRequestSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const nutritionProductProductCharacteristicSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - initialFill: medicationRequestDispenseRequestInitialFillSchema.optional(), - dispenseInterval: z.lazy(() => durationSchema).optional(), - validityPeriod: z.lazy(() => periodSchema).optional(), - numberOfRepeatsAllowed: unsignedIntSchema.optional(), - _numberOfRepeatsAllowed: z.lazy(() => elementSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - expectedSupplyDuration: z.lazy(() => durationSchema).optional(), - performer: z.lazy(() => referenceSchema).optional(), - }), - ) - -export const medicationRequestSubstitutionSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - allowedBoolean: booleanSchema.optional(), - _allowedBoolean: z.lazy(() => elementSchema).optional(), - allowedCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - reason: z.lazy(() => codeableConceptSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['allowedBoolean', 'allowedCodeableConcept'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( + type: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueCodeableConcept'], + ['valueString', '_valueString'], + ['valueQuantity'], + ['valueBase64Binary', '_valueBase64Binary'], + ['valueAttachment'], + ['valueBoolean', '_valueBoolean'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationRequestDispenseRequestInitialFillSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const nutritionProductInstanceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), quantity: z.lazy(() => quantitySchema).optional(), - duration: z.lazy(() => durationSchema).optional(), - }), - ) + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + lotNumber: stringSchema.optional(), + _lotNumber: z.lazy(() => elementSchema).optional(), + expiry: dateTimeSchema.optional(), + _expiry: z.lazy(() => elementSchema).optional(), + useBy: dateTimeSchema.optional(), + _useBy: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicationRequestSchema: z.ZodType = z.lazy( - () => +export const nutritionProductSchema: z.ZodType> = + z.lazy(() => z .strictObject({ - resourceType: z.literal('MedicationRequest'), + resourceType: z.literal('NutritionProduct'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -19325,146 +33359,224 @@ export const medicationRequestSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'cancelled', - 'completed', - 'draft', - 'entered-in-error', - 'on-hold', - 'stopped', - 'unknown', - ]), + status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - intent: z.enum([ - 'filler-order', - 'instance-order', - 'option', - 'order', - 'original-order', - 'plan', - 'proposal', - 'reflex-order', - ]), - _intent: z.lazy(() => elementSchema).optional(), category: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: z.lazy(() => elementSchema).optional(), - reportedBoolean: booleanSchema.optional(), - _reportedBoolean: z.lazy(() => elementSchema).optional(), - reportedReference: z.lazy(() => referenceSchema).optional(), - medicationCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - medicationReference: z.lazy(() => referenceSchema).optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - supportingInformation: z - .lazy(() => referenceSchema) - .array() - .optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: z.lazy(() => elementSchema).optional(), - requester: z.lazy(() => referenceSchema).optional(), - performer: z.lazy(() => referenceSchema).optional(), - performerType: z.lazy(() => codeableConceptSchema).optional(), - recorder: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z + code: z.lazy(() => codeableConceptSchema).optional(), + manufacturer: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) + nutrient: nutritionProductNutrientSchema.array().min(1).optional(), + ingredient: nutritionProductIngredientSchema.array().min(1).optional(), + knownAllergen: z + .lazy(() => codeableReferenceSchema) .array() + .min(1) .optional(), - groupIdentifier: z.lazy(() => identifierSchema).optional(), - courseOfTherapyType: z.lazy(() => codeableConceptSchema).optional(), - insurance: z - .lazy(() => referenceSchema) + productCharacteristic: nutritionProductProductCharacteristicSchema .array() + .min(1) .optional(), + instance: nutritionProductInstanceSchema.optional(), note: z .lazy(() => annotationSchema) .array() - .optional(), - dosageInstruction: z - .lazy(() => dosageSchema) - .array() - .optional(), - dispenseRequest: medicationRequestDispenseRequestSchema.optional(), - substitution: medicationRequestSubstitutionSchema.optional(), - priorPrescription: z.lazy(() => referenceSchema).optional(), - detectedIssue: z - .lazy(() => referenceSchema) - .array() - .optional(), - eventHistory: z - .lazy(() => referenceSchema) - .array() + .min(1) .optional(), }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) .refine( - (value) => - [ - { - names: ['reportedBoolean', 'reportedReference'], - required: false, - }, - { - names: ['medicationCodeableConcept', 'medicationReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), -) + ) as unknown as z.ZodType> -export const medicationStatementSchema: z.ZodType = - z.lazy(() => +export const observationReferenceRangeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + low: z.lazy(() => quantitySchema).optional(), + high: z.lazy(() => quantitySchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + appliesTo: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + age: z.lazy(() => rangeSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const range = value as { + low?: unknown + high?: unknown + text?: unknown + _text?: unknown + } + return ( + range.low !== undefined || + range.high !== undefined || + range.text !== undefined || + range._text !== undefined + ) + }, + { message: 'obs-3: a reference range states a low, a high, or text.' }, + ), +) as unknown as z.ZodType> + +export const observationComponentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), + interpretation: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + referenceRange: z + .lazy(() => observationReferenceRangeSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueQuantity'], + ['valueCodeableConcept'], + ['valueString', '_valueString'], + ['valueBoolean', '_valueBoolean'], + ['valueInteger', '_valueInteger'], + ['valueRange'], + ['valueRatio'], + ['valueSampledData'], + ['valueTime', '_valueTime'], + ['valueDateTime', '_valueDateTime'], + ['valuePeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const observation = value as Record + const hasValue = Object.keys(observation).some( + (key) => + (key.startsWith('value') || key.startsWith('_value')) && + observation[key] !== undefined, + ) + return observation.dataAbsentReason === undefined || !hasValue + }, + { message: 'obs-6: dataAbsentReason is present only when no value is.' }, + ), +) as unknown as z.ZodType> + +export const observationSchema: z.ZodType> = z.lazy( + () => z .strictObject({ - resourceType: z.literal('MedicationStatement'), + resourceType: z.literal('Observation'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -19476,90 +33588,153 @@ export const medicationStatementSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), basedOn: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), partOf: z .lazy(() => referenceSchema) .array() + .min(1) + .optional(), + status: z + .enum([ + 'amended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'preliminary', + 'registered', + 'unknown', + ]) .optional(), - status: z.enum([ - 'active', - 'completed', - 'entered-in-error', - 'intended', - 'not-taken', - 'on-hold', - 'stopped', - 'unknown', - ]), _status: z.lazy(() => elementSchema).optional(), - statusReason: z + category: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - medicationCodeableConcept: z - .lazy(() => codeableConceptSchema) + code: z.lazy(() => codeableConceptSchema), + subject: z.lazy(() => referenceSchema).optional(), + focus: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), - medicationReference: z.lazy(() => referenceSchema).optional(), - subject: z.lazy(() => referenceSchema), - context: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), effectiveDateTime: dateTimeSchema.optional(), _effectiveDateTime: z.lazy(() => elementSchema).optional(), effectivePeriod: z.lazy(() => periodSchema).optional(), - dateAsserted: dateTimeSchema.optional(), - _dateAsserted: z.lazy(() => elementSchema).optional(), - informationSource: z.lazy(() => referenceSchema).optional(), - derivedFrom: z + effectiveTiming: z.lazy(() => timingSchema).optional(), + effectiveInstant: instantSchema.optional(), + _effectiveInstant: z.lazy(() => elementSchema).optional(), + issued: instantSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + performer: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - reasonCode: z + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), + interpretation: z .lazy(() => codeableConceptSchema) .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() + .min(1) .optional(), note: z .lazy(() => annotationSchema) .array() + .min(1) + .optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + specimen: z.lazy(() => referenceSchema).optional(), + device: z.lazy(() => referenceSchema).optional(), + referenceRange: observationReferenceRangeSchema + .array() + .min(1) + .optional(), + hasMember: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), - dosage: z - .lazy(() => dosageSchema) + derivedFrom: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), + component: observationComponentSchema.array().min(1).optional(), }) .refine( (value) => [ { - names: ['medicationCodeableConcept', 'medicationReference'], - required: true, + alternatives: [ + ['effectiveDateTime', '_effectiveDateTime'], + ['effectivePeriod'], + ['effectiveTiming'], + ['effectiveInstant', '_effectiveInstant'], + ], + required: false, }, { - names: ['effectiveDateTime', 'effectivePeriod'], + alternatives: [ + ['valueQuantity'], + ['valueCodeableConcept'], + ['valueString', '_valueString'], + ['valueBoolean', '_valueBoolean'], + ['valueInteger', '_valueInteger'], + ['valueRange'], + ['valueRatio'], + ['valueSampledData'], + ['valueTime', '_valueTime'], + ['valueDateTime', '_valueDateTime'], + ['valuePeriod'], + ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -19567,175 +33742,769 @@ export const medicationStatementSchema: z.ZodType = message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ) + .refine( + (value) => { + const observation = value as Record + const hasValue = Object.keys(observation).some( + (key) => + (key.startsWith('value') || key.startsWith('_value')) && + observation[key] !== undefined, + ) + return observation.dataAbsentReason === undefined || !hasValue + }, + { + message: 'obs-6: dataAbsentReason is present only when no value is.', + }, ), - ) +) as unknown as z.ZodType> -export const medicinalProductDefinitionContactSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const observationDefinitionQuantitativeDetailsSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - contact: z.lazy(() => referenceSchema), - }), - ) + customaryUnit: z.lazy(() => codeableConceptSchema).optional(), + unit: z.lazy(() => codeableConceptSchema).optional(), + conversionFactor: decimalSchema.optional(), + _conversionFactor: z.lazy(() => elementSchema).optional(), + decimalPrecision: integerSchema.optional(), + _decimalPrecision: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const medicinalProductDefinitionNameSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const observationDefinitionQualifiedIntervalSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + category: z.enum(['absolute', 'critical', 'reference']).optional(), + _category: z.lazy(() => elementSchema).optional(), + range: z.lazy(() => rangeSchema).optional(), + context: z.lazy(() => codeableConceptSchema).optional(), + appliesTo: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + age: z.lazy(() => rangeSchema).optional(), + gestationalAge: z.lazy(() => rangeSchema).optional(), + condition: stringSchema.optional(), + _condition: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const observationDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ObservationDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + permittedDataType: z + .enum([ + 'CodeableConcept', + 'Period', + 'Quantity', + 'Range', + 'Ratio', + 'SampledData', + 'boolean', + 'dateTime', + 'integer', + 'string', + 'time', + ]) + .nullable() + .array() + .min(1) + .optional(), + _permittedDataType: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + multipleResultsAllowed: booleanSchema.optional(), + _multipleResultsAllowed: z.lazy(() => elementSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + preferredReportName: stringSchema.optional(), + _preferredReportName: z.lazy(() => elementSchema).optional(), + quantitativeDetails: + observationDefinitionQuantitativeDetailsSchema.optional(), + qualifiedInterval: observationDefinitionQualifiedIntervalSchema + .array() + .min(1) + .optional(), + validCodedValueSet: z.lazy(() => referenceSchema).optional(), + normalCodedValueSet: z.lazy(() => referenceSchema).optional(), + abnormalCodedValueSet: z.lazy(() => referenceSchema).optional(), + criticalCodedValueSet: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'permittedDataType', + shadow: '_permittedDataType', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const operationDefinitionParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: codeSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + use: z.enum(['in', 'out']).optional(), + _use: z.lazy(() => elementSchema).optional(), + min: integerSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Any', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'CodeableConcept', + 'CodeableReference', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'RatioRange', + 'Reference', + 'RegulatedAuthorization', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'Type', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + _type: z.lazy(() => elementSchema).optional(), + targetProfile: canonicalSchema.nullable().array().min(1).optional(), + _targetProfile: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - productName: stringSchema, - _productName: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - namePart: medicinalProductDefinitionNameNamePartSchema.array().optional(), - countryLanguage: medicinalProductDefinitionNameCountryLanguageSchema - .array() + searchType: z + .enum([ + 'composite', + 'date', + 'number', + 'quantity', + 'reference', + 'special', + 'string', + 'token', + 'uri', + ]) .optional(), - }), - ) - -export const medicinalProductDefinitionCrossReferenceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + _searchType: z.lazy(() => elementSchema).optional(), + binding: operationDefinitionParameterBindingSchema.optional(), + referencedFrom: operationDefinitionParameterReferencedFromSchema .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + part: z + .lazy(() => operationDefinitionParameterSchema) .array() + .min(1) .optional(), - product: z.lazy(() => codeableReferenceSchema), - type: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'use', shadow: '_use' }, + { name: 'min', shadow: '_min' }, + { name: 'max', shadow: '_max' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'targetProfile', shadow: '_targetProfile', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductDefinitionOperationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const operationDefinitionOverloadSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableReferenceSchema).optional(), - effectiveDate: z.lazy(() => periodSchema).optional(), - organization: z - .lazy(() => referenceSchema) + parameterName: stringSchema.nullable().array().min(1).optional(), + _parameterName: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - confidentialityIndicator: z.lazy(() => codeableConceptSchema).optional(), - }), - ) - -export const medicinalProductDefinitionCharacteristicSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueCodeableConcept', - 'valueQuantity', - 'valueDate', - 'valueBoolean', - 'valueAttachment', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'parameterName', shadow: '_parameterName', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductDefinitionNameNamePartSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const operationDefinitionParameterBindingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - part: stringSchema, - _part: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - }), - ) + strength: z + .enum(['example', 'extensible', 'preferred', 'required']) + .optional(), + _strength: z.lazy(() => elementSchema).optional(), + valueSet: canonicalSchema.optional(), + _valueSet: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'strength', shadow: '_strength' }, + { name: 'valueSet', shadow: '_valueSet' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const medicinalProductDefinitionNameCountryLanguageSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const operationDefinitionParameterReferencedFromSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - country: z.lazy(() => codeableConceptSchema), - jurisdiction: z.lazy(() => codeableConceptSchema).optional(), - language: z.lazy(() => codeableConceptSchema), - }), - ) + source: stringSchema.optional(), + _source: z.lazy(() => elementSchema).optional(), + sourceId: stringSchema.optional(), + _sourceId: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'source', shadow: '_source' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const medicinalProductDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('MedicinalProductDefinition'), +export const operationDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('OperationDefinition'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -19747,285 +34516,524 @@ export const medicinalProductDefinitionSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - domain: z.lazy(() => codeableConceptSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), version: stringSchema.optional(), _version: z.lazy(() => elementSchema).optional(), - status: z.lazy(() => codeableConceptSchema).optional(), - statusDate: dateTimeSchema.optional(), - _statusDate: z.lazy(() => elementSchema).optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - combinedPharmaceuticalDoseForm: z - .lazy(() => codeableConceptSchema) - .optional(), - route: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - indication: markdownSchema.optional(), - _indication: z.lazy(() => elementSchema).optional(), - legalStatusOfSupply: z.lazy(() => codeableConceptSchema).optional(), - additionalMonitoringIndicator: z - .lazy(() => codeableConceptSchema) - .optional(), - specialMeasures: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - pediatricUseIndicator: z.lazy(() => codeableConceptSchema).optional(), - classification: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - marketingStatus: z - .lazy(() => marketingStatusSchema) + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + kind: z.enum(['operation', 'query']).optional(), + _kind: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - packagedMedicinalProduct: z - .lazy(() => codeableConceptSchema) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - ingredient: z + jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - impurity: z - .lazy(() => codeableReferenceSchema) + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + affectsState: booleanSchema.optional(), + _affectsState: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + comment: markdownSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + base: canonicalSchema.optional(), + _base: z.lazy(() => elementSchema).optional(), + resource: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .nullable() .array() + .min(1) .optional(), - attachedDocument: z - .lazy(() => referenceSchema) + _resource: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - masterFile: z - .lazy(() => referenceSchema) + system: booleanSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + type: booleanSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + instance: booleanSchema.optional(), + _instance: z.lazy(() => elementSchema).optional(), + inputProfile: canonicalSchema.optional(), + _inputProfile: z.lazy(() => elementSchema).optional(), + outputProfile: canonicalSchema.optional(), + _outputProfile: z.lazy(() => elementSchema).optional(), + parameter: operationDefinitionParameterSchema.array().min(1).optional(), + overload: operationDefinitionOverloadSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + { name: 'kind', shadow: '_kind' }, + { name: 'code', shadow: '_code' }, + { name: 'system', shadow: '_system' }, + { name: 'type', shadow: '_type' }, + { name: 'instance', shadow: '_instance' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'resource', shadow: '_resource', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const operationOutcomeIssueSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - contact: medicinalProductDefinitionContactSchema.array().optional(), - clinicalTrial: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), + severity: z.enum(['error', 'fatal', 'information', 'warning']).optional(), + _severity: z.lazy(() => elementSchema).optional(), code: z - .lazy(() => codingSchema) - .array() + .enum([ + 'business-rule', + 'code-invalid', + 'conflict', + 'deleted', + 'duplicate', + 'exception', + 'expired', + 'extension', + 'forbidden', + 'incomplete', + 'informational', + 'invalid', + 'invariant', + 'lock-error', + 'login', + 'multiple-matches', + 'no-store', + 'not-found', + 'not-supported', + 'processing', + 'required', + 'security', + 'structure', + 'suppressed', + 'throttled', + 'timeout', + 'too-costly', + 'too-long', + 'transient', + 'unknown', + 'value', + ]) .optional(), - name: medicinalProductDefinitionNameSchema.array().min(1), - crossReference: medicinalProductDefinitionCrossReferenceSchema + _code: z.lazy(() => elementSchema).optional(), + details: z.lazy(() => codeableConceptSchema).optional(), + diagnostics: stringSchema.optional(), + _diagnostics: z.lazy(() => elementSchema).optional(), + location: stringSchema.nullable().array().min(1).optional(), + _location: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - operation: medicinalProductDefinitionOperationSchema.array().optional(), - characteristic: medicinalProductDefinitionCharacteristicSchema + expression: stringSchema.nullable().array().min(1).optional(), + _expression: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'severity', shadow: '_severity' }, + { name: 'code', shadow: '_code' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'location', shadow: '_location', required: false }, + { name: 'expression', shadow: '_expression', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const messageDefinitionFocusSchema: z.ZodType = +export const operationOutcomeSchema: z.ZodType> = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.enum([ - 'Account', - 'ActivityDefinition', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RegulatedAuthorization', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]), - _code: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.optional(), - _profile: z.lazy(() => elementSchema).optional(), - min: unsignedIntSchema, - _min: z.lazy(() => elementSchema).optional(), - max: stringSchema.optional(), - _max: z.lazy(() => elementSchema).optional(), - }), - ) + z + .strictObject({ + resourceType: z.literal('OperationOutcome'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + issue: operationOutcomeIssueSchema.array().min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const messageDefinitionAllowedResponseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const organizationContactSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - message: canonicalSchema, - _message: z.lazy(() => elementSchema).optional(), - situation: markdownSchema.optional(), - _situation: z.lazy(() => elementSchema).optional(), - }), - ) + purpose: z.lazy(() => codeableConceptSchema).optional(), + name: z.lazy(() => humanNameSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + address: z.lazy(() => addressSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const messageDefinitionSchema: z.ZodType = z.lazy( +export const organizationSchema: z.ZodType> = z.lazy( () => z .strictObject({ - resourceType: z.literal('MessageDefinition'), + resourceType: z.literal('Organization'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -20037,173 +35045,116 @@ export const messageDefinitionSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - replaces: canonicalSchema.array().optional(), - _replaces: z - .lazy(() => elementSchema) - .array() - .optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema, - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + type: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - base: canonicalSchema.optional(), - _base: z.lazy(() => elementSchema).optional(), - parent: canonicalSchema.array().optional(), - _parent: z + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + alias: stringSchema.nullable().array().min(1).optional(), + _alias: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - eventCoding: z.lazy(() => codingSchema).optional(), - eventUri: uriSchema.optional(), - _eventUri: z.lazy(() => elementSchema).optional(), - category: z - .enum(['consequence', 'currency', 'notification']) - .optional(), - _category: z.lazy(() => elementSchema).optional(), - focus: messageDefinitionFocusSchema.array().optional(), - responseRequired: z - .enum(['always', 'never', 'on-error', 'on-success']) + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) .optional(), - _responseRequired: z.lazy(() => elementSchema).optional(), - allowedResponse: messageDefinitionAllowedResponseSchema + address: z + .lazy(() => addressSchema) .array() + .min(1) .optional(), - graph: canonicalSchema.array().optional(), - _graph: z - .lazy(() => elementSchema) + partOf: z.lazy(() => referenceSchema).optional(), + contact: organizationContactSchema.array().min(1).optional(), + endpoint: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), }) + .superRefine((value, context) => { + for (const group of [ + { name: 'alias', shadow: '_alias', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) .refine( - (value) => - [{ names: ['eventCoding', 'eventUri'], required: true }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), -) - -export const messageHeaderDestinationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - target: z.lazy(() => referenceSchema).optional(), - endpoint: urlSchema, - _endpoint: z.lazy(() => elementSchema).optional(), - receiver: z.lazy(() => referenceSchema).optional(), - }), - ) - -export const messageHeaderSourceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - software: stringSchema.optional(), - _software: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - contact: z.lazy(() => contactPointSchema).optional(), - endpoint: urlSchema, - _endpoint: z.lazy(() => elementSchema).optional(), - }), - ) - -export const messageHeaderResponseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: idSchema, - _identifier: z.lazy(() => elementSchema).optional(), - code: z.enum(['fatal-error', 'ok', 'transient-error']), - _code: z.lazy(() => elementSchema).optional(), - details: z.lazy(() => referenceSchema).optional(), - }), - ) +) as unknown as z.ZodType> -export const messageHeaderSchema: z.ZodType = z.lazy(() => +export const organizationAffiliationSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('MessageHeader'), + resourceType: z.literal('OrganizationAffiliation'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -20215,317 +35166,309 @@ export const messageHeaderSchema: z.ZodType = z.lazy(() => contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - eventCoding: z.lazy(() => codingSchema).optional(), - eventUri: uriSchema.optional(), - _eventUri: z.lazy(() => elementSchema).optional(), - destination: messageHeaderDestinationSchema.array().optional(), - sender: z.lazy(() => referenceSchema).optional(), - enterer: z.lazy(() => referenceSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - source: messageHeaderSourceSchema, - responsible: z.lazy(() => referenceSchema).optional(), - reason: z.lazy(() => codeableConceptSchema).optional(), - response: messageHeaderResponseSchema.optional(), - focus: z + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + participatingOrganization: z.lazy(() => referenceSchema).optional(), + network: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - definition: canonicalSchema.optional(), - _definition: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [{ names: ['eventCoding', 'eventUri'], required: true }].every( - (group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const metaSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - versionId: idSchema.optional(), - _versionId: z.lazy(() => elementSchema).optional(), - lastUpdated: instantSchema.optional(), - _lastUpdated: z.lazy(() => elementSchema).optional(), - source: uriSchema.optional(), - _source: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.array().optional(), - _profile: z - .lazy(() => elementSchema) - .array() - .optional(), - security: z - .lazy(() => codingSchema) - .array() - .optional(), - tag: z - .lazy(() => codingSchema) - .array() - .optional(), - }), -) - -export const molecularSequenceReferenceSeqSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + code: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + location: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + healthcareService: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - chromosome: z.lazy(() => codeableConceptSchema).optional(), - genomeBuild: stringSchema.optional(), - _genomeBuild: z.lazy(() => elementSchema).optional(), - orientation: z.enum(['antisense', 'sense']).optional(), - _orientation: z.lazy(() => elementSchema).optional(), - referenceSeqId: z.lazy(() => codeableConceptSchema).optional(), - referenceSeqPointer: z.lazy(() => referenceSchema).optional(), - referenceSeqString: stringSchema.optional(), - _referenceSeqString: z.lazy(() => elementSchema).optional(), - strand: z.enum(['crick', 'watson']).optional(), - _strand: z.lazy(() => elementSchema).optional(), - windowStart: integerSchema.optional(), - _windowStart: z.lazy(() => elementSchema).optional(), - windowEnd: integerSchema.optional(), - _windowEnd: z.lazy(() => elementSchema).optional(), - }), - ) - -export const molecularSequenceVariantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + telecom: z + .lazy(() => contactPointSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + endpoint: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - start: integerSchema.optional(), - _start: z.lazy(() => elementSchema).optional(), - end: integerSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), - observedAllele: stringSchema.optional(), - _observedAllele: z.lazy(() => elementSchema).optional(), - referenceAllele: stringSchema.optional(), - _referenceAllele: z.lazy(() => elementSchema).optional(), - cigar: stringSchema.optional(), - _cigar: z.lazy(() => elementSchema).optional(), - variantPointer: z.lazy(() => referenceSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const molecularSequenceQualitySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const packagedProductDefinitionLegalStatusOfSupplySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum(['indel', 'snp', 'unknown']), - _type: z.lazy(() => elementSchema).optional(), - standardSequence: z.lazy(() => codeableConceptSchema).optional(), - start: integerSchema.optional(), - _start: z.lazy(() => elementSchema).optional(), - end: integerSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), - score: z.lazy(() => quantitySchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - truthTP: decimalSchema.optional(), - _truthTP: z.lazy(() => elementSchema).optional(), - queryTP: decimalSchema.optional(), - _queryTP: z.lazy(() => elementSchema).optional(), - truthFN: decimalSchema.optional(), - _truthFN: z.lazy(() => elementSchema).optional(), - queryFP: decimalSchema.optional(), - _queryFP: z.lazy(() => elementSchema).optional(), - gtFP: decimalSchema.optional(), - _gtFP: z.lazy(() => elementSchema).optional(), - precision: decimalSchema.optional(), - _precision: z.lazy(() => elementSchema).optional(), - recall: decimalSchema.optional(), - _recall: z.lazy(() => elementSchema).optional(), - fScore: decimalSchema.optional(), - _fScore: z.lazy(() => elementSchema).optional(), - roc: molecularSequenceQualityRocSchema.optional(), - }), - ) + code: z.lazy(() => codeableConceptSchema).optional(), + jurisdiction: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const molecularSequenceRepositorySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const packagedProductDefinitionPackageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum(['directlink', 'login', 'oauth', 'openapi', 'other']), - _type: z.lazy(() => elementSchema).optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - datasetId: stringSchema.optional(), - _datasetId: z.lazy(() => elementSchema).optional(), - variantsetId: stringSchema.optional(), - _variantsetId: z.lazy(() => elementSchema).optional(), - readsetId: stringSchema.optional(), - _readsetId: z.lazy(() => elementSchema).optional(), - }), - ) - -export const molecularSequenceStructureVariantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + type: z.lazy(() => codeableConceptSchema).optional(), + quantity: integerSchema.optional(), + _quantity: z.lazy(() => elementSchema).optional(), + material: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - variantType: z.lazy(() => codeableConceptSchema).optional(), - exact: booleanSchema.optional(), - _exact: z.lazy(() => elementSchema).optional(), - length: integerSchema.optional(), - _length: z.lazy(() => elementSchema).optional(), - outer: molecularSequenceStructureVariantOuterSchema.optional(), - inner: molecularSequenceStructureVariantInnerSchema.optional(), - }), - ) - -export const molecularSequenceQualityRocSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + alternateMaterial: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + shelfLifeStorage: packagedProductDefinitionPackageShelfLifeStorageSchema .array() + .min(1) .optional(), - score: integerSchema.array().optional(), - _score: z - .lazy(() => elementSchema) + manufacturer: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - numTP: integerSchema.array().optional(), - _numTP: z - .lazy(() => elementSchema) + property: packagedProductDefinitionPackagePropertySchema .array() + .min(1) .optional(), - numFP: integerSchema.array().optional(), - _numFP: z - .lazy(() => elementSchema) + containedItem: packagedProductDefinitionPackageContainedItemSchema .array() + .min(1) .optional(), - numFN: integerSchema.array().optional(), - _numFN: z - .lazy(() => elementSchema) + package: z + .lazy(() => packagedProductDefinitionPackageSchema) .array() + .min(1) .optional(), - precision: decimalSchema.array().optional(), - _precision: z - .lazy(() => elementSchema) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const packagedProductDefinitionPackageShelfLifeStorageSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - sensitivity: decimalSchema.array().optional(), - _sensitivity: z - .lazy(() => elementSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - fMeasure: decimalSchema.array().optional(), - _fMeasure: z - .lazy(() => elementSchema) + type: z.lazy(() => codeableConceptSchema).optional(), + periodDuration: z.lazy(() => durationSchema).optional(), + periodString: stringSchema.optional(), + _periodString: z.lazy(() => elementSchema).optional(), + specialPrecautionsForStorage: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => + [ + { + alternatives: [ + ['periodDuration'], + ['periodString', '_periodString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const molecularSequenceStructureVariantOuterSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const packagedProductDefinitionPackagePropertySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - start: integerSchema.optional(), - _start: z.lazy(() => elementSchema).optional(), - end: integerSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), - }), - ) + type: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueCodeableConcept'], + ['valueQuantity'], + ['valueDate', '_valueDate'], + ['valueBoolean', '_valueBoolean'], + ['valueAttachment'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const molecularSequenceStructureVariantInnerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const packagedProductDefinitionPackageContainedItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - start: integerSchema.optional(), - _start: z.lazy(() => elementSchema).optional(), - end: integerSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), - }), - ) + item: z.lazy(() => codeableReferenceSchema), + amount: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const molecularSequenceSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('MolecularSequence'), +export const packagedProductDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('PackagedProductDefinition'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -20537,521 +35480,912 @@ export const molecularSequenceSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - type: z.enum(['aa', 'dna', 'rna']).optional(), - _type: z.lazy(() => elementSchema).optional(), - coordinateSystem: integerSchema, - _coordinateSystem: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema).optional(), - specimen: z.lazy(() => referenceSchema).optional(), - device: z.lazy(() => referenceSchema).optional(), - performer: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - referenceSeq: molecularSequenceReferenceSeqSchema.optional(), - variant: molecularSequenceVariantSchema.array().optional(), - observedSeq: stringSchema.optional(), - _observedSeq: z.lazy(() => elementSchema).optional(), - quality: molecularSequenceQualitySchema.array().optional(), - readCoverage: integerSchema.optional(), - _readCoverage: z.lazy(() => elementSchema).optional(), - repository: molecularSequenceRepositorySchema.array().optional(), - pointer: z - .lazy(() => referenceSchema) - .array() - .optional(), - structureVariant: molecularSequenceStructureVariantSchema - .array() - .optional(), - }), -) - -export const moneySchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - currency: codeSchema.optional(), - _currency: z.lazy(() => elementSchema).optional(), - }), -) - -export const namingSystemUniqueIdSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + packageFor: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + status: z.lazy(() => codeableConceptSchema).optional(), + statusDate: dateTimeSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), + containedItemQuantity: z + .lazy(() => quantitySchema) .array() + .min(1) .optional(), - type: z.enum(['oid', 'other', 'uri', 'uuid']), - _type: z.lazy(() => elementSchema).optional(), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - preferred: booleanSchema.optional(), - _preferred: z.lazy(() => elementSchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), - ) - -export const namingSystemSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('NamingSystem'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - kind: z.enum(['codesystem', 'identifier', 'root']), - _kind: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema, - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - responsible: stringSchema.optional(), - _responsible: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - uniqueId: namingSystemUniqueIdSchema.array().min(1), - }), -) - -export const narrativeSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - status: z.enum(['additional', 'empty', 'extensions', 'generated']), - _status: z.lazy(() => elementSchema).optional(), - div: xhtmlSchema, - _div: z.lazy(() => elementSchema).optional(), - }), -) - -export const nutritionOrderOralDietSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + legalStatusOfSupply: packagedProductDefinitionLegalStatusOfSupplySchema .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + marketingStatus: z + .lazy(() => marketingStatusSchema) .array() + .min(1) .optional(), - type: z + characteristic: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - schedule: z - .lazy(() => timingSchema) + copackagedIndicator: booleanSchema.optional(), + _copackagedIndicator: z.lazy(() => elementSchema).optional(), + manufacturer: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - nutrient: nutritionOrderOralDietNutrientSchema.array().optional(), - texture: nutritionOrderOralDietTextureSchema.array().optional(), - fluidConsistencyType: z - .lazy(() => codeableConceptSchema) + package: packagedProductDefinitionPackageSchema.optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const parameterDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + name: codeSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + use: z.enum(['in', 'out']).optional(), + _use: z.lazy(() => elementSchema).optional(), + min: integerSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + documentation: stringSchema.optional(), + _documentation: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'Account', + 'ActivityDefinition', + 'Address', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'Age', + 'AllergyIntolerance', + 'Annotation', + 'Any', + 'Appointment', + 'AppointmentResponse', + 'Attachment', + 'AuditEvent', + 'BackboneElement', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'CodeableConcept', + 'CodeableReference', + 'Coding', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'ContactDetail', + 'ContactPoint', + 'Contract', + 'Contributor', + 'Count', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DataRequirement', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'Distance', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Dosage', + 'Duration', + 'Element', + 'ElementDefinition', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'Expression', + 'Extension', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'HumanName', + 'Identifier', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'MarketingStatus', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'Meta', + 'MolecularSequence', + 'Money', + 'MoneyQuantity', + 'NamingSystem', + 'Narrative', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'ParameterDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Period', + 'Person', + 'PlanDefinition', + 'Population', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'ProdCharacteristic', + 'ProductShelfLife', + 'Provenance', + 'Quantity', + 'Questionnaire', + 'QuestionnaireResponse', + 'Range', + 'Ratio', + 'RatioRange', + 'Reference', + 'RegulatedAuthorization', + 'RelatedArtifact', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'SampledData', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Signature', + 'SimpleQuantity', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'Timing', + 'TriggerDefinition', + 'Type', + 'UsageContext', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + 'base64Binary', + 'boolean', + 'canonical', + 'code', + 'date', + 'dateTime', + 'decimal', + 'id', + 'instant', + 'integer', + 'markdown', + 'oid', + 'positiveInt', + 'string', + 'time', + 'unsignedInt', + 'uri', + 'url', + 'uuid', + 'xhtml', + ]) .optional(), - instruction: stringSchema.optional(), - _instruction: z.lazy(() => elementSchema).optional(), - }), - ) + _type: z.lazy(() => elementSchema).optional(), + profile: canonicalSchema.optional(), + _profile: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'use', shadow: '_use' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const nutritionOrderSupplementSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const parametersParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - productName: stringSchema.optional(), - _productName: z.lazy(() => elementSchema).optional(), - schedule: z - .lazy(() => timingSchema) - .array() - .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - instruction: stringSchema.optional(), - _instruction: z.lazy(() => elementSchema).optional(), - }), - ) - -export const nutritionOrderEnteralFormulaSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) .optional(), - baseFormulaType: z.lazy(() => codeableConceptSchema).optional(), - baseFormulaProductName: stringSchema.optional(), - _baseFormulaProductName: z.lazy(() => elementSchema).optional(), - additiveType: z.lazy(() => codeableConceptSchema).optional(), - additiveProductName: stringSchema.optional(), - _additiveProductName: z.lazy(() => elementSchema).optional(), - caloricDensity: z.lazy(() => quantitySchema).optional(), - routeofAdministration: z.lazy(() => codeableConceptSchema).optional(), - administration: nutritionOrderEnteralFormulaAdministrationSchema + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + valueMeta: z.lazy(() => metaSchema).optional(), + resource: z.lazy(() => resourceSchema).optional(), + part: z + .lazy(() => parametersParameterSchema) .array() + .min(1) .optional(), - maxVolumeToDeliver: z.lazy(() => quantitySchema).optional(), - administrationInstruction: stringSchema.optional(), - _administrationInstruction: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueBase64Binary', '_valueBase64Binary'], + ['valueBoolean', '_valueBoolean'], + ['valueCanonical', '_valueCanonical'], + ['valueCode', '_valueCode'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueDecimal', '_valueDecimal'], + ['valueId', '_valueId'], + ['valueInstant', '_valueInstant'], + ['valueInteger', '_valueInteger'], + ['valueMarkdown', '_valueMarkdown'], + ['valueOid', '_valueOid'], + ['valuePositiveInt', '_valuePositiveInt'], + ['valueString', '_valueString'], + ['valueTime', '_valueTime'], + ['valueUnsignedInt', '_valueUnsignedInt'], + ['valueUri', '_valueUri'], + ['valueUrl', '_valueUrl'], + ['valueUuid', '_valueUuid'], + ['valueAddress'], + ['valueAge'], + ['valueAnnotation'], + ['valueAttachment'], + ['valueCodeableConcept'], + ['valueCoding'], + ['valueContactPoint'], + ['valueCount'], + ['valueDistance'], + ['valueDuration'], + ['valueHumanName'], + ['valueIdentifier'], + ['valueMoney'], + ['valuePeriod'], + ['valueQuantity'], + ['valueRange'], + ['valueRatio'], + ['valueReference'], + ['valueSampledData'], + ['valueSignature'], + ['valueTiming'], + ['valueContactDetail'], + ['valueContributor'], + ['valueDataRequirement'], + ['valueExpression'], + ['valueParameterDefinition'], + ['valueRelatedArtifact'], + ['valueTriggerDefinition'], + ['valueUsageContext'], + ['valueDosage'], + ['valueMeta'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const nutritionOrderOralDietNutrientSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifier: z.lazy(() => codeableConceptSchema).optional(), - amount: z.lazy(() => quantitySchema).optional(), - }), - ) +export const parametersSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('Parameters'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + parameter: parametersParameterSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const nutritionOrderOralDietTextureSchema: z.ZodType = +export const patientContactSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + relationship: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + name: z.lazy(() => humanNameSchema).optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + address: z.lazy(() => addressSchema).optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const patientCommunicationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifier: z.lazy(() => codeableConceptSchema).optional(), - foodType: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + language: z.lazy(() => codeableConceptSchema), + preferred: booleanSchema.optional(), + _preferred: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const nutritionOrderEnteralFormulaAdministrationSchema: z.ZodType = - z.lazy(() => +export const patientLinkSchema: z.ZodType> = z.lazy( + () => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - schedule: z.lazy(() => timingSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - rateQuantity: z.lazy(() => quantitySchema).optional(), - rateRatio: z.lazy(() => ratioSchema).optional(), + other: z.lazy(() => referenceSchema), + type: z + .enum(['refer', 'replaced-by', 'replaces', 'seealso']) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [{ names: ['rateQuantity', 'rateRatio'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> -export const nutritionOrderSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('NutritionOrder'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiates: uriSchema.array().optional(), - _instantiates: z - .lazy(() => elementSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'completed', - 'draft', - 'entered-in-error', - 'on-hold', - 'revoked', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - intent: z.enum([ - 'directive', - 'filler-order', - 'instance-order', - 'option', - 'order', - 'original-order', - 'plan', - 'proposal', - 'reflex-order', - ]), - _intent: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - dateTime: dateTimeSchema, - _dateTime: z.lazy(() => elementSchema).optional(), - orderer: z.lazy(() => referenceSchema).optional(), - allergyIntolerance: z - .lazy(() => referenceSchema) - .array() - .optional(), - foodPreferenceModifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - excludeFoodModifier: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - oralDiet: nutritionOrderOralDietSchema.optional(), - supplement: nutritionOrderSupplementSchema.array().optional(), - enteralFormula: nutritionOrderEnteralFormulaSchema.optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }), -) - -export const nutritionProductNutrientSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const patientSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Patient'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - item: z.lazy(() => codeableReferenceSchema).optional(), - amount: z - .lazy(() => ratioSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - }), - ) - -export const nutritionProductIngredientSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + name: z + .lazy(() => humanNameSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + telecom: z + .lazy(() => contactPointSchema) .array() + .min(1) .optional(), - item: z.lazy(() => codeableReferenceSchema), - amount: z - .lazy(() => ratioSchema) + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + deceasedBoolean: booleanSchema.optional(), + _deceasedBoolean: z.lazy(() => elementSchema).optional(), + deceasedDateTime: dateTimeSchema.optional(), + _deceasedDateTime: z.lazy(() => elementSchema).optional(), + address: z + .lazy(() => addressSchema) .array() + .min(1) .optional(), - }), - ) + maritalStatus: z.lazy(() => codeableConceptSchema).optional(), + multipleBirthBoolean: booleanSchema.optional(), + _multipleBirthBoolean: z.lazy(() => elementSchema).optional(), + multipleBirthInteger: integerSchema.optional(), + _multipleBirthInteger: z.lazy(() => elementSchema).optional(), + photo: z + .lazy(() => attachmentSchema) + .array() + .min(1) + .optional(), + contact: patientContactSchema.array().min(1).optional(), + communication: patientCommunicationSchema.array().min(1).optional(), + generalPractitioner: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + link: patientLinkSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['deceasedBoolean', '_deceasedBoolean'], + ['deceasedDateTime', '_deceasedDateTime'], + ], + required: false, + }, + { + alternatives: [ + ['multipleBirthBoolean', '_multipleBirthBoolean'], + ['multipleBirthInteger', '_multipleBirthInteger'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const nutritionProductProductCharacteristicSchema: z.ZodType = +export const paymentNoticeSchema: z.ZodType> = z.lazy(() => z .strictObject({ - id: stringSchema.optional(), + resourceType: z.literal('PaymentNotice'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: z.lazy(() => elementSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + request: z.lazy(() => referenceSchema).optional(), + response: z.lazy(() => referenceSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + provider: z.lazy(() => referenceSchema).optional(), + payment: z.lazy(() => referenceSchema), + paymentDate: dateSchema.optional(), + _paymentDate: z.lazy(() => elementSchema).optional(), + payee: z.lazy(() => referenceSchema).optional(), + recipient: z.lazy(() => referenceSchema), + amount: z.lazy(() => moneySchema), + paymentStatus: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'created', shadow: '_created' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: [ - 'valueCodeableConcept', - 'valueString', - 'valueQuantity', - 'valueBase64Binary', - 'valueAttachment', - 'valueBoolean', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const nutritionProductInstanceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const paymentReconciliationDetailSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - identifier: z - .lazy(() => identifierSchema) + identifier: z.lazy(() => identifierSchema).optional(), + predecessor: z.lazy(() => identifierSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + request: z.lazy(() => referenceSchema).optional(), + submitter: z.lazy(() => referenceSchema).optional(), + response: z.lazy(() => referenceSchema).optional(), + date: dateSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + responsible: z.lazy(() => referenceSchema).optional(), + payee: z.lazy(() => referenceSchema).optional(), + amount: z.lazy(() => moneySchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const paymentReconciliationProcessNoteSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - lotNumber: stringSchema.optional(), - _lotNumber: z.lazy(() => elementSchema).optional(), - expiry: dateTimeSchema.optional(), - _expiry: z.lazy(() => elementSchema).optional(), - useBy: dateTimeSchema.optional(), - _useBy: z.lazy(() => elementSchema).optional(), - }), - ) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.enum(['display', 'print', 'printoper']).optional(), + _type: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const nutritionProductSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('NutritionProduct'), +export const paymentReconciliationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('PaymentReconciliation'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -21063,172 +36397,137 @@ export const nutritionProductSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - status: z.enum(['active', 'entered-in-error', 'inactive']), - _status: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - manufacturer: z - .lazy(() => referenceSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - nutrient: nutritionProductNutrientSchema.array().optional(), - ingredient: nutritionProductIngredientSchema.array().optional(), - knownAllergen: z - .lazy(() => codeableReferenceSchema) - .array() + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) .optional(), - productCharacteristic: nutritionProductProductCharacteristicSchema + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + created: dateTimeSchema.optional(), + _created: z.lazy(() => elementSchema).optional(), + paymentIssuer: z.lazy(() => referenceSchema).optional(), + request: z.lazy(() => referenceSchema).optional(), + requestor: z.lazy(() => referenceSchema).optional(), + outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), + _outcome: z.lazy(() => elementSchema).optional(), + disposition: stringSchema.optional(), + _disposition: z.lazy(() => elementSchema).optional(), + paymentDate: dateSchema.optional(), + _paymentDate: z.lazy(() => elementSchema).optional(), + paymentAmount: z.lazy(() => moneySchema), + paymentIdentifier: z.lazy(() => identifierSchema).optional(), + detail: paymentReconciliationDetailSchema.array().min(1).optional(), + formCode: z.lazy(() => codeableConceptSchema).optional(), + processNote: paymentReconciliationProcessNoteSchema .array() + .min(1) .optional(), - instance: nutritionProductInstanceSchema.optional(), - note: z - .lazy(() => annotationSchema) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'created', shadow: '_created' }, + { name: 'paymentDate', shadow: '_paymentDate' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const periodSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), -) - -export const observationReferenceRangeSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - low: z.lazy(() => quantitySchema).optional(), - high: z.lazy(() => quantitySchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - appliesTo: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - age: z.lazy(() => rangeSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => { - const range = value as { - low?: unknown - high?: unknown - text?: unknown - } - return ( - range.low !== undefined || - range.high !== undefined || - range.text !== undefined - ) - }, - { message: 'obs-3: a reference range states a low, a high, or text.' }, - ), - ) + start: dateTimeSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: dateTimeSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const period = value as { start?: string; end?: string } + if (period.start === undefined || period.end === undefined) return true + const ordering = compareFhirDateTimes(period.start, period.end) + // A malformed endpoint is a malformed date, not an ordering fault; the release says + // as much with start.hasValue(). Reporting both would name the wrong problem. + if (ordering === undefined) return true + return ordering <= 0 + }, + { message: 'Period.start must not be later than Period.end.' }, + ), +) as unknown as z.ZodType> -export const observationComponentSchema: z.ZodType = - z.lazy(() => +export const personLinkSchema: z.ZodType> = z.lazy( + () => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueRatio: z.lazy(() => ratioSchema).optional(), - valueSampledData: z.lazy(() => sampledDataSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valuePeriod: z.lazy(() => periodSchema).optional(), - dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), - interpretation: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - referenceRange: z - .lazy(() => observationReferenceRangeSchema) - .array() - .optional(), + target: z.lazy(() => referenceSchema), + assurance: z.enum(['level1', 'level2', 'level3', 'level4']).optional(), + _assurance: z.lazy(() => elementSchema).optional(), }) .refine( - (value) => - [ - { - names: [ - 'valueQuantity', - 'valueCodeableConcept', - 'valueString', - 'valueBoolean', - 'valueInteger', - 'valueRange', - 'valueRatio', - 'valueSampledData', - 'valueTime', - 'valueDateTime', - 'valuePeriod', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ) - .refine( - (value) => { - const observation = value as Record - const hasValue = Object.keys(observation).some( - (key) => key.startsWith('value') && observation[key] !== undefined, - ) - return observation.dataAbsentReason === undefined || !hasValue - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: 'obs-6: dataAbsentReason is present only when no value is.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> -export const observationSchema: z.ZodType = z.lazy(() => +export const personSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Observation'), + resourceType: z.literal('Person'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -21240,1006 +36539,1148 @@ export const observationSchema: z.ZodType = z.lazy(() => contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'amended', - 'cancelled', - 'corrected', - 'entered-in-error', - 'final', - 'preliminary', - 'registered', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - subject: z.lazy(() => referenceSchema).optional(), - focus: z - .lazy(() => referenceSchema) - .array() - .optional(), - encounter: z.lazy(() => referenceSchema).optional(), - effectiveDateTime: dateTimeSchema.optional(), - _effectiveDateTime: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - effectiveTiming: z.lazy(() => timingSchema).optional(), - effectiveInstant: instantSchema.optional(), - _effectiveInstant: z.lazy(() => elementSchema).optional(), - issued: instantSchema.optional(), - _issued: z.lazy(() => elementSchema).optional(), - performer: z - .lazy(() => referenceSchema) - .array() - .optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueRatio: z.lazy(() => ratioSchema).optional(), - valueSampledData: z.lazy(() => sampledDataSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valuePeriod: z.lazy(() => periodSchema).optional(), - dataAbsentReason: z.lazy(() => codeableConceptSchema).optional(), - interpretation: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) + name: z + .lazy(() => humanNameSchema) .array() + .min(1) .optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - specimen: z.lazy(() => referenceSchema).optional(), - device: z.lazy(() => referenceSchema).optional(), - referenceRange: observationReferenceRangeSchema.array().optional(), - hasMember: z - .lazy(() => referenceSchema) + telecom: z + .lazy(() => contactPointSchema) .array() + .min(1) .optional(), - derivedFrom: z - .lazy(() => referenceSchema) + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + address: z + .lazy(() => addressSchema) .array() + .min(1) .optional(), - component: observationComponentSchema.array().optional(), + photo: z.lazy(() => attachmentSchema).optional(), + managingOrganization: z.lazy(() => referenceSchema).optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + link: personLinkSchema.array().min(1).optional(), }) .refine( - (value) => - [ - { - names: [ - 'effectiveDateTime', - 'effectivePeriod', - 'effectiveTiming', - 'effectiveInstant', - ], - required: false, - }, - { - names: [ - 'valueQuantity', - 'valueCodeableConcept', - 'valueString', - 'valueBoolean', - 'valueInteger', - 'valueRange', - 'valueRatio', - 'valueSampledData', - 'valueTime', - 'valueDateTime', - 'valuePeriod', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ) - .refine( - (value) => { - const observation = value as Record - const hasValue = Object.keys(observation).some( - (key) => key.startsWith('value') && observation[key] !== undefined, - ) - return observation.dataAbsentReason === undefined || !hasValue - }, - { message: 'obs-6: dataAbsentReason is present only when no value is.' }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const observationDefinitionQuantitativeDetailsSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - customaryUnit: z.lazy(() => codeableConceptSchema).optional(), - unit: z.lazy(() => codeableConceptSchema).optional(), - conversionFactor: decimalSchema.optional(), - _conversionFactor: z.lazy(() => elementSchema).optional(), - decimalPrecision: integerSchema.optional(), - _decimalPrecision: z.lazy(() => elementSchema).optional(), - }), - ) +) as unknown as z.ZodType> -export const observationDefinitionQualifiedIntervalSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const planDefinitionGoalSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - category: z.enum(['absolute', 'critical', 'reference']).optional(), - _category: z.lazy(() => elementSchema).optional(), - range: z.lazy(() => rangeSchema).optional(), - context: z.lazy(() => codeableConceptSchema).optional(), - appliesTo: z + category: z.lazy(() => codeableConceptSchema).optional(), + description: z.lazy(() => codeableConceptSchema), + priority: z.lazy(() => codeableConceptSchema).optional(), + start: z.lazy(() => codeableConceptSchema).optional(), + addresses: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), - _gender: z.lazy(() => elementSchema).optional(), - age: z.lazy(() => rangeSchema).optional(), - gestationalAge: z.lazy(() => rangeSchema).optional(), - condition: stringSchema.optional(), - _condition: z.lazy(() => elementSchema).optional(), - }), - ) - -export const observationDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('ObservationDefinition'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + documentation: z + .lazy(() => relatedArtifactSchema) .array() + .min(1) .optional(), + target: planDefinitionGoalTargetSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const planDefinitionActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - category: z + prefix: stringSchema.optional(), + _prefix: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + textEquivalent: stringSchema.optional(), + _textEquivalent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + code: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema), - identifier: z - .lazy(() => identifierSchema) + reason: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - permittedDataType: z - .enum([ - 'CodeableConcept', - 'Period', - 'Quantity', - 'Range', - 'Ratio', - 'SampledData', - 'boolean', - 'dateTime', - 'integer', - 'string', - 'time', - ]) + documentation: z + .lazy(() => relatedArtifactSchema) .array() + .min(1) .optional(), - _permittedDataType: z + goalId: idSchema.nullable().array().min(1).optional(), + _goalId: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - multipleResultsAllowed: booleanSchema.optional(), - _multipleResultsAllowed: z.lazy(() => elementSchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - preferredReportName: stringSchema.optional(), - _preferredReportName: z.lazy(() => elementSchema).optional(), - quantitativeDetails: - observationDefinitionQuantitativeDetailsSchema.optional(), - qualifiedInterval: observationDefinitionQualifiedIntervalSchema + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + subjectCanonical: canonicalSchema.optional(), + _subjectCanonical: z.lazy(() => elementSchema).optional(), + trigger: z + .lazy(() => triggerDefinitionSchema) + .array() + .min(1) + .optional(), + condition: planDefinitionActionConditionSchema.array().min(1).optional(), + input: z + .lazy(() => dataRequirementSchema) + .array() + .min(1) + .optional(), + output: z + .lazy(() => dataRequirementSchema) .array() + .min(1) .optional(), - validCodedValueSet: z.lazy(() => referenceSchema).optional(), - normalCodedValueSet: z.lazy(() => referenceSchema).optional(), - abnormalCodedValueSet: z.lazy(() => referenceSchema).optional(), - criticalCodedValueSet: z.lazy(() => referenceSchema).optional(), - }), - ) - -export const operationDefinitionParameterSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + relatedAction: planDefinitionActionRelatedActionSchema .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + timingAge: z.lazy(() => ageSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingDuration: z.lazy(() => durationSchema).optional(), + timingRange: z.lazy(() => rangeSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + participant: planDefinitionActionParticipantSchema .array() + .min(1) .optional(), - name: codeSchema, - _name: z.lazy(() => elementSchema).optional(), - use: z.enum(['in', 'out']), - _use: z.lazy(() => elementSchema).optional(), - min: integerSchema, - _min: z.lazy(() => elementSchema).optional(), - max: stringSchema, - _max: z.lazy(() => elementSchema).optional(), - documentation: stringSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - type: z + type: z.lazy(() => codeableConceptSchema).optional(), + groupingBehavior: z + .enum(['logical-group', 'sentence-group', 'visual-group']) + .optional(), + _groupingBehavior: z.lazy(() => elementSchema).optional(), + selectionBehavior: z .enum([ - 'Account', - 'ActivityDefinition', - 'Address', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'Age', - 'AllergyIntolerance', - 'Annotation', - 'Any', - 'Appointment', - 'AppointmentResponse', - 'Attachment', - 'AuditEvent', - 'BackboneElement', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'CodeableConcept', - 'CodeableReference', - 'Coding', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'ContactDetail', - 'ContactPoint', - 'Contract', - 'Contributor', - 'Count', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DataRequirement', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'Distance', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Dosage', - 'Duration', - 'Element', - 'ElementDefinition', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'Expression', - 'Extension', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'HumanName', - 'Identifier', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'MarketingStatus', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'Meta', - 'MolecularSequence', - 'Money', - 'MoneyQuantity', - 'NamingSystem', - 'Narrative', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'ParameterDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Period', - 'Person', - 'PlanDefinition', - 'Population', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'ProdCharacteristic', - 'ProductShelfLife', - 'Provenance', - 'Quantity', - 'Questionnaire', - 'QuestionnaireResponse', - 'Range', - 'Ratio', - 'RatioRange', - 'Reference', - 'RegulatedAuthorization', - 'RelatedArtifact', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'SampledData', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Signature', - 'SimpleQuantity', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'Timing', - 'TriggerDefinition', - 'Type', - 'UsageContext', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - 'base64Binary', - 'boolean', - 'canonical', - 'code', - 'date', - 'dateTime', - 'decimal', - 'id', - 'instant', - 'integer', - 'markdown', - 'oid', - 'positiveInt', - 'string', - 'time', - 'unsignedInt', - 'uri', - 'url', - 'uuid', - 'xhtml', + 'all', + 'all-or-none', + 'any', + 'at-most-one', + 'exactly-one', + 'one-or-more', ]) .optional(), - _type: z.lazy(() => elementSchema).optional(), - targetProfile: canonicalSchema.array().optional(), - _targetProfile: z - .lazy(() => elementSchema) + _selectionBehavior: z.lazy(() => elementSchema).optional(), + requiredBehavior: z + .enum(['could', 'must', 'must-unless-documented']) + .optional(), + _requiredBehavior: z.lazy(() => elementSchema).optional(), + precheckBehavior: z.enum(['no', 'yes']).optional(), + _precheckBehavior: z.lazy(() => elementSchema).optional(), + cardinalityBehavior: z.enum(['multiple', 'single']).optional(), + _cardinalityBehavior: z.lazy(() => elementSchema).optional(), + definitionCanonical: canonicalSchema.optional(), + _definitionCanonical: z.lazy(() => elementSchema).optional(), + definitionUri: uriSchema.optional(), + _definitionUri: z.lazy(() => elementSchema).optional(), + transform: canonicalSchema.optional(), + _transform: z.lazy(() => elementSchema).optional(), + dynamicValue: planDefinitionActionDynamicValueSchema + .array() + .min(1) + .optional(), + action: z + .lazy(() => planDefinitionActionSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['subjectCodeableConcept'], + ['subjectReference'], + ['subjectCanonical', '_subjectCanonical'], + ], + required: false, + }, + { + alternatives: [ + ['timingDateTime', '_timingDateTime'], + ['timingAge'], + ['timingPeriod'], + ['timingDuration'], + ['timingRange'], + ['timingTiming'], + ], + required: false, + }, + { + alternatives: [ + ['definitionCanonical', '_definitionCanonical'], + ['definitionUri', '_definitionUri'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'goalId', shadow: '_goalId', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const planDefinitionGoalTargetSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - searchType: z - .enum([ - 'composite', - 'date', - 'number', - 'quantity', - 'reference', - 'special', - 'string', - 'token', - 'uri', - ]) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) .optional(), - _searchType: z.lazy(() => elementSchema).optional(), - binding: operationDefinitionParameterBindingSchema.optional(), - referencedFrom: operationDefinitionParameterReferencedFromSchema + measure: z.lazy(() => codeableConceptSchema).optional(), + detailQuantity: z.lazy(() => quantitySchema).optional(), + detailRange: z.lazy(() => rangeSchema).optional(), + detailCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + due: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['detailQuantity'], + ['detailRange'], + ['detailCodeableConcept'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const planDefinitionActionConditionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - part: z - .lazy(() => operationDefinitionParameterSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - }), - ) + kind: z.enum(['applicability', 'start', 'stop']).optional(), + _kind: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'kind', shadow: '_kind' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const operationDefinitionOverloadSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const planDefinitionActionRelatedActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - parameterName: stringSchema.array().optional(), - _parameterName: z - .lazy(() => elementSchema) + actionId: idSchema.optional(), + _actionId: z.lazy(() => elementSchema).optional(), + relationship: z + .enum([ + 'after', + 'after-end', + 'after-start', + 'before', + 'before-end', + 'before-start', + 'concurrent', + 'concurrent-with-end', + 'concurrent-with-start', + ]) + .optional(), + _relationship: z.lazy(() => elementSchema).optional(), + offsetDuration: z.lazy(() => durationSchema).optional(), + offsetRange: z.lazy(() => rangeSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['offsetDuration'], ['offsetRange']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'actionId', shadow: '_actionId' }, + { name: 'relationship', shadow: '_relationship' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const planDefinitionActionParticipantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - }), - ) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z + .enum(['device', 'patient', 'practitioner', 'related-person']) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const operationDefinitionParameterBindingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const planDefinitionActionDynamicValueSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - strength: z.enum(['example', 'extensible', 'preferred', 'required']), - _strength: z.lazy(() => elementSchema).optional(), - valueSet: canonicalSchema, - _valueSet: z.lazy(() => elementSchema).optional(), - }), - ) + path: stringSchema.optional(), + _path: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const planDefinitionSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('PlanDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + subjectCanonical: canonicalSchema.optional(), + _subjectCanonical: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + author: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + editor: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + reviewer: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + endorser: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + library: canonicalSchema.nullable().array().min(1).optional(), + _library: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + goal: planDefinitionGoalSchema.array().min(1).optional(), + action: planDefinitionActionSchema.array().min(1).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['subjectCodeableConcept'], + ['subjectReference'], + ['subjectCanonical', '_subjectCanonical'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'library', shadow: '_library', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const operationDefinitionParameterReferencedFromSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const practitionerQualificationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - source: stringSchema, - _source: z.lazy(() => elementSchema).optional(), - sourceId: stringSchema.optional(), - _sourceId: z.lazy(() => elementSchema).optional(), - }), - ) - -export const operationDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('OperationDefinition'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - kind: z.enum(['operation', 'query']), - _kind: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - affectsState: booleanSchema.optional(), - _affectsState: z.lazy(() => elementSchema).optional(), - code: codeSchema, - _code: z.lazy(() => elementSchema).optional(), - comment: markdownSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - base: canonicalSchema.optional(), - _base: z.lazy(() => elementSchema).optional(), - resource: z - .enum([ - 'Account', - 'ActivityDefinition', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RegulatedAuthorization', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]) - .array() - .optional(), - _resource: z - .lazy(() => elementSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - system: booleanSchema, - _system: z.lazy(() => elementSchema).optional(), - type: booleanSchema, - _type: z.lazy(() => elementSchema).optional(), - instance: booleanSchema, - _instance: z.lazy(() => elementSchema).optional(), - inputProfile: canonicalSchema.optional(), - _inputProfile: z.lazy(() => elementSchema).optional(), - outputProfile: canonicalSchema.optional(), - _outputProfile: z.lazy(() => elementSchema).optional(), - parameter: operationDefinitionParameterSchema.array().optional(), - overload: operationDefinitionOverloadSchema.array().optional(), - }), - ) + code: z.lazy(() => codeableConceptSchema), + period: z.lazy(() => periodSchema).optional(), + issuer: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const operationOutcomeIssueSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const practitionerSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('Practitioner'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + name: z + .lazy(() => humanNameSchema) + .array() + .min(1) + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + address: z + .lazy(() => addressSchema) + .array() + .min(1) + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + photo: z + .lazy(() => attachmentSchema) + .array() + .min(1) + .optional(), + qualification: practitionerQualificationSchema + .array() + .min(1) + .optional(), + communication: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const practitionerRoleAvailableTimeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - severity: z.enum(['error', 'fatal', 'information', 'warning']), - _severity: z.lazy(() => elementSchema).optional(), - code: z.enum([ - 'business-rule', - 'code-invalid', - 'conflict', - 'deleted', - 'duplicate', - 'exception', - 'expired', - 'extension', - 'forbidden', - 'incomplete', - 'informational', - 'invalid', - 'invariant', - 'lock-error', - 'login', - 'multiple-matches', - 'no-store', - 'not-found', - 'not-supported', - 'processing', - 'required', - 'security', - 'structure', - 'suppressed', - 'throttled', - 'timeout', - 'too-costly', - 'too-long', - 'transient', - 'unknown', - 'value', - ]), - _code: z.lazy(() => elementSchema).optional(), - details: z.lazy(() => codeableConceptSchema).optional(), - diagnostics: stringSchema.optional(), - _diagnostics: z.lazy(() => elementSchema).optional(), - location: stringSchema.array().optional(), - _location: z - .lazy(() => elementSchema) + daysOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .nullable() .array() + .min(1) .optional(), - expression: stringSchema.array().optional(), - _expression: z + _daysOfWeek: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - }), - ) + allDay: booleanSchema.optional(), + _allDay: z.lazy(() => elementSchema).optional(), + availableStartTime: timeSchema.optional(), + _availableStartTime: z.lazy(() => elementSchema).optional(), + availableEndTime: timeSchema.optional(), + _availableEndTime: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'daysOfWeek', shadow: '_daysOfWeek', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const operationOutcomeSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('OperationOutcome'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), +export const practitionerRoleNotAvailableSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - issue: operationOutcomeIssueSchema.array().min(1), - }), -) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + during: z.lazy(() => periodSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'description', shadow: '_description' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const organizationContactSchema: z.ZodType = +export const practitionerRoleSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + resourceType: z.literal('PractitionerRole'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + practitioner: z.lazy(() => referenceSchema).optional(), + organization: z.lazy(() => referenceSchema).optional(), + code: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + location: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + healthcareService: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + availableTime: practitionerRoleAvailableTimeSchema + .array() + .min(1) + .optional(), + notAvailable: practitionerRoleNotAvailableSchema + .array() + .min(1) + .optional(), + availabilityExceptions: stringSchema.optional(), + _availabilityExceptions: z.lazy(() => elementSchema).optional(), + endpoint: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const procedurePerformerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - purpose: z.lazy(() => codeableConceptSchema).optional(), - name: z.lazy(() => humanNameSchema).optional(), - telecom: z - .lazy(() => contactPointSchema) + function: z.lazy(() => codeableConceptSchema).optional(), + actor: z.lazy(() => referenceSchema), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const procedureFocalDeviceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - address: z.lazy(() => addressSchema).optional(), - }), - ) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + action: z.lazy(() => codeableConceptSchema).optional(), + manipulated: z.lazy(() => referenceSchema), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const organizationSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Organization'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - type: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - alias: stringSchema.array().optional(), - _alias: z - .lazy(() => elementSchema) - .array() - .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - address: z - .lazy(() => addressSchema) - .array() - .optional(), - partOf: z.lazy(() => referenceSchema).optional(), - contact: organizationContactSchema.array().optional(), - endpoint: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) - -export const organizationAffiliationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('OrganizationAffiliation'), +export const procedureSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Procedure'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -22251,159 +37692,365 @@ export const organizationAffiliationSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - organization: z.lazy(() => referenceSchema).optional(), - participatingOrganization: z.lazy(() => referenceSchema).optional(), - network: z - .lazy(() => referenceSchema) + instantiatesCanonical: canonicalSchema + .nullable() .array() + .min(1) .optional(), - code: z - .lazy(() => codeableConceptSchema) + _instantiatesCanonical: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - specialty: z - .lazy(() => codeableConceptSchema) + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - location: z + basedOn: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - healthcareService: z + partOf: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - telecom: z - .lazy(() => contactPointSchema) + status: z + .enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'not-done', + 'on-hold', + 'preparation', + 'stopped', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + performedDateTime: dateTimeSchema.optional(), + _performedDateTime: z.lazy(() => elementSchema).optional(), + performedPeriod: z.lazy(() => periodSchema).optional(), + performedString: stringSchema.optional(), + _performedString: z.lazy(() => elementSchema).optional(), + performedAge: z.lazy(() => ageSchema).optional(), + performedRange: z.lazy(() => rangeSchema).optional(), + recorder: z.lazy(() => referenceSchema).optional(), + asserter: z.lazy(() => referenceSchema).optional(), + performer: procedurePerformerSchema.array().min(1).optional(), + location: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - endpoint: z + reasonReference: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - }), - ) - -export const packagedProductDefinitionLegalStatusOfSupplySchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + bodySite: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + outcome: z.lazy(() => codeableConceptSchema).optional(), + report: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + complication: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - jurisdiction: z.lazy(() => codeableConceptSchema).optional(), - }), - ) - -export const packagedProductDefinitionPackageSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + complicationDetail: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + followUp: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - quantity: integerSchema.optional(), - _quantity: z.lazy(() => elementSchema).optional(), - material: z - .lazy(() => codeableConceptSchema) + focalDevice: procedureFocalDeviceSchema.array().min(1).optional(), + usedReference: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - alternateMaterial: z + usedCode: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - shelfLifeStorage: packagedProductDefinitionPackageShelfLifeStorageSchema + }) + .refine( + (value) => + [ + { + alternatives: [ + ['performedDateTime', '_performedDateTime'], + ['performedPeriod'], + ['performedString', '_performedString'], + ['performedAge'], + ['performedRange'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const prodCharacteristicSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - manufacturer: z - .lazy(() => referenceSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - property: packagedProductDefinitionPackagePropertySchema + height: z.lazy(() => quantitySchema).optional(), + width: z.lazy(() => quantitySchema).optional(), + depth: z.lazy(() => quantitySchema).optional(), + weight: z.lazy(() => quantitySchema).optional(), + nominalVolume: z.lazy(() => quantitySchema).optional(), + externalDiameter: z.lazy(() => quantitySchema).optional(), + shape: stringSchema.optional(), + _shape: z.lazy(() => elementSchema).optional(), + color: stringSchema.nullable().array().min(1).optional(), + _color: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - containedItem: packagedProductDefinitionPackageContainedItemSchema + imprint: stringSchema.nullable().array().min(1).optional(), + _imprint: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - package: z - .lazy(() => packagedProductDefinitionPackageSchema) + image: z + .lazy(() => attachmentSchema) .array() + .min(1) .optional(), - }), - ) + scoring: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'color', shadow: '_color', required: false }, + { name: 'imprint', shadow: '_imprint', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const packagedProductDefinitionPackageShelfLifeStorageSchema: z.ZodType = +export const productShelfLifeSchema: z.ZodType> = z.lazy(() => z .strictObject({ - id: stringSchema.optional(), + id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - periodDuration: z.lazy(() => durationSchema).optional(), - periodString: stringSchema.optional(), - _periodString: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + type: z.lazy(() => codeableConceptSchema), + period: z.lazy(() => quantitySchema), specialPrecautionsForStorage: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), }) .refine( - (value) => - [ - { names: ['periodDuration', 'periodString'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const packagedProductDefinitionPackagePropertySchema: z.ZodType = +export const provenanceAgentSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -22411,364 +38058,31 @@ export const packagedProductDefinitionPackagePropertySchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + role: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + who: z.lazy(() => referenceSchema), + onBehalfOf: z.lazy(() => referenceSchema).optional(), }) .refine( - (value) => - [ - { - names: [ - 'valueCodeableConcept', - 'valueQuantity', - 'valueDate', - 'valueBoolean', - 'valueAttachment', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) - -export const packagedProductDefinitionPackageContainedItemSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - item: z.lazy(() => codeableReferenceSchema), - amount: z.lazy(() => quantitySchema).optional(), - }), - ) - -export const packagedProductDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('PackagedProductDefinition'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - packageFor: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.lazy(() => codeableConceptSchema).optional(), - statusDate: dateTimeSchema.optional(), - _statusDate: z.lazy(() => elementSchema).optional(), - containedItemQuantity: z - .lazy(() => quantitySchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - legalStatusOfSupply: packagedProductDefinitionLegalStatusOfSupplySchema - .array() - .optional(), - marketingStatus: z - .lazy(() => marketingStatusSchema) - .array() - .optional(), - characteristic: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - copackagedIndicator: booleanSchema.optional(), - _copackagedIndicator: z.lazy(() => elementSchema).optional(), - manufacturer: z - .lazy(() => referenceSchema) - .array() - .optional(), - package: packagedProductDefinitionPackageSchema.optional(), - }), - ) - -export const parameterDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: codeSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - use: z.enum(['in', 'out']), - _use: z.lazy(() => elementSchema).optional(), - min: integerSchema.optional(), - _min: z.lazy(() => elementSchema).optional(), - max: stringSchema.optional(), - _max: z.lazy(() => elementSchema).optional(), - documentation: stringSchema.optional(), - _documentation: z.lazy(() => elementSchema).optional(), - type: z.enum([ - 'Account', - 'ActivityDefinition', - 'Address', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'Age', - 'AllergyIntolerance', - 'Annotation', - 'Any', - 'Appointment', - 'AppointmentResponse', - 'Attachment', - 'AuditEvent', - 'BackboneElement', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'CodeableConcept', - 'CodeableReference', - 'Coding', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'ContactDetail', - 'ContactPoint', - 'Contract', - 'Contributor', - 'Count', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DataRequirement', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'Distance', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Dosage', - 'Duration', - 'Element', - 'ElementDefinition', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'Expression', - 'Extension', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'HumanName', - 'Identifier', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'MarketingStatus', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'Meta', - 'MolecularSequence', - 'Money', - 'MoneyQuantity', - 'NamingSystem', - 'Narrative', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'ParameterDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Period', - 'Person', - 'PlanDefinition', - 'Population', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'ProdCharacteristic', - 'ProductShelfLife', - 'Provenance', - 'Quantity', - 'Questionnaire', - 'QuestionnaireResponse', - 'Range', - 'Ratio', - 'RatioRange', - 'Reference', - 'RegulatedAuthorization', - 'RelatedArtifact', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'SampledData', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Signature', - 'SimpleQuantity', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'Timing', - 'TriggerDefinition', - 'Type', - 'UsageContext', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - 'base64Binary', - 'boolean', - 'canonical', - 'code', - 'date', - 'dateTime', - 'decimal', - 'id', - 'instant', - 'integer', - 'markdown', - 'oid', - 'positiveInt', - 'string', - 'time', - 'unsignedInt', - 'uri', - 'url', - 'uuid', - 'xhtml', - ]), - _type: z.lazy(() => elementSchema).optional(), - profile: canonicalSchema.optional(), - _profile: z.lazy(() => elementSchema).optional(), - }), - ) + ) as unknown as z.ZodType> -export const parametersParameterSchema: z.ZodType = +export const provenanceEntitySchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -22776,153 +38090,123 @@ export const parametersParameterSchema: z.ZodType = extension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + role: z + .enum(['derivation', 'quotation', 'removal', 'revision', 'source']) + .optional(), + _role: z.lazy(() => elementSchema).optional(), + what: z.lazy(() => referenceSchema), + agent: z + .lazy(() => provenanceAgentSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'role', shadow: '_role' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const provenanceSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('Provenance'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueCanonical: canonicalSchema.optional(), - _valueCanonical: z.lazy(() => elementSchema).optional(), - valueCode: codeSchema.optional(), - _valueCode: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueId: idSchema.optional(), - _valueId: z.lazy(() => elementSchema).optional(), - valueInstant: instantSchema.optional(), - _valueInstant: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueMarkdown: markdownSchema.optional(), - _valueMarkdown: z.lazy(() => elementSchema).optional(), - valueOid: oidSchema.optional(), - _valueOid: z.lazy(() => elementSchema).optional(), - valuePositiveInt: positiveIntSchema.optional(), - _valuePositiveInt: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueUnsignedInt: unsignedIntSchema.optional(), - _valueUnsignedInt: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueUrl: urlSchema.optional(), - _valueUrl: z.lazy(() => elementSchema).optional(), - valueUuid: uuidSchema.optional(), - _valueUuid: z.lazy(() => elementSchema).optional(), - valueAddress: z.lazy(() => addressSchema).optional(), - valueAge: z.lazy(() => ageSchema).optional(), - valueAnnotation: z.lazy(() => annotationSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueContactPoint: z.lazy(() => contactPointSchema).optional(), - valueCount: z.lazy(() => countSchema).optional(), - valueDistance: z.lazy(() => distanceSchema).optional(), - valueDuration: z.lazy(() => durationSchema).optional(), - valueHumanName: z.lazy(() => humanNameSchema).optional(), - valueIdentifier: z.lazy(() => identifierSchema).optional(), - valueMoney: z.lazy(() => moneySchema).optional(), - valuePeriod: z.lazy(() => periodSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueRatio: z.lazy(() => ratioSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - valueSampledData: z.lazy(() => sampledDataSchema).optional(), - valueSignature: z.lazy(() => signatureSchema).optional(), - valueTiming: z.lazy(() => timingSchema).optional(), - valueContactDetail: z.lazy(() => contactDetailSchema).optional(), - valueContributor: z.lazy(() => contributorSchema).optional(), - valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), - valueExpression: z.lazy(() => expressionSchema).optional(), - valueParameterDefinition: z - .lazy(() => parameterDefinitionSchema) + target: z + .lazy(() => referenceSchema) + .array() + .min(1), + occurredPeriod: z.lazy(() => periodSchema).optional(), + occurredDateTime: dateTimeSchema.optional(), + _occurredDateTime: z.lazy(() => elementSchema).optional(), + recorded: instantSchema.optional(), + _recorded: z.lazy(() => elementSchema).optional(), + policy: uriSchema.nullable().array().min(1).optional(), + _policy: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) .optional(), - valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), - valueTriggerDefinition: z - .lazy(() => triggerDefinitionSchema) + location: z.lazy(() => referenceSchema).optional(), + reason: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) .optional(), - valueUsageContext: z.lazy(() => usageContextSchema).optional(), - valueDosage: z.lazy(() => dosageSchema).optional(), - valueMeta: z.lazy(() => metaSchema).optional(), - resource: z.lazy(() => resourceSchema).optional(), - part: z - .lazy(() => parametersParameterSchema) + activity: z.lazy(() => codeableConceptSchema).optional(), + agent: provenanceAgentSchema.array().min(1), + entity: provenanceEntitySchema.array().min(1).optional(), + signature: z + .lazy(() => signatureSchema) .array() + .min(1) .optional(), }) .refine( (value) => [ { - names: [ - 'valueBase64Binary', - 'valueBoolean', - 'valueCanonical', - 'valueCode', - 'valueDate', - 'valueDateTime', - 'valueDecimal', - 'valueId', - 'valueInstant', - 'valueInteger', - 'valueMarkdown', - 'valueOid', - 'valuePositiveInt', - 'valueString', - 'valueTime', - 'valueUnsignedInt', - 'valueUri', - 'valueUrl', - 'valueUuid', - 'valueAddress', - 'valueAge', - 'valueAnnotation', - 'valueAttachment', - 'valueCodeableConcept', - 'valueCoding', - 'valueContactPoint', - 'valueCount', - 'valueDistance', - 'valueDuration', - 'valueHumanName', - 'valueIdentifier', - 'valueMoney', - 'valuePeriod', - 'valueQuantity', - 'valueRange', - 'valueRatio', - 'valueReference', - 'valueSampledData', - 'valueSignature', - 'valueTiming', - 'valueContactDetail', - 'valueContributor', - 'valueDataRequirement', - 'valueExpression', - 'valueParameterDefinition', - 'valueRelatedArtifact', - 'valueTriggerDefinition', - 'valueUsageContext', - 'valueDosage', - 'valueMeta', + alternatives: [ + ['occurredPeriod'], + ['occurredDateTime', '_occurredDateTime'], ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -22930,165 +38214,433 @@ export const parametersParameterSchema: z.ZodType = message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'recorded', shadow: '_recorded' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'policy', shadow: '_policy', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) +) as unknown as z.ZodType> -export const parametersSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Parameters'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - parameter: parametersParameterSchema.array().optional(), - }), -) - -export const patientContactSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - relationship: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - name: z.lazy(() => humanNameSchema).optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - address: z.lazy(() => addressSchema).optional(), - gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), - _gender: z.lazy(() => elementSchema).optional(), - organization: z.lazy(() => referenceSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - }), -) - -export const patientCommunicationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const quantitySchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + value: decimalSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + comparator: z.enum(['<', '<=', '>', '>=']).optional(), + _comparator: z.lazy(() => elementSchema).optional(), + unit: stringSchema.optional(), + _unit: z.lazy(() => elementSchema).optional(), + system: uriSchema.optional(), + _system: z.lazy(() => elementSchema).optional(), + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const quantity = value as Record + const hasCode = + quantity.code !== undefined || quantity._code !== undefined + return ( + !hasCode || + quantity.system !== undefined || + quantity._system !== undefined + ) + }, + { message: 'qty-3: a coded Quantity requires its unit system.' }, + ), +) as unknown as z.ZodType> + +export const questionnaireItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - language: z.lazy(() => codeableConceptSchema), - preferred: booleanSchema.optional(), - _preferred: z.lazy(() => elementSchema).optional(), - }), - ) + linkId: stringSchema.optional(), + _linkId: z.lazy(() => elementSchema).optional(), + definition: uriSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + code: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + prefix: stringSchema.optional(), + _prefix: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + type: z + .enum([ + 'attachment', + 'boolean', + 'choice', + 'date', + 'dateTime', + 'decimal', + 'display', + 'group', + 'integer', + 'open-choice', + 'quantity', + 'question', + 'reference', + 'string', + 'text', + 'time', + 'url', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + enableWhen: questionnaireItemEnableWhenSchema.array().min(1).optional(), + enableBehavior: z.enum(['all', 'any']).optional(), + _enableBehavior: z.lazy(() => elementSchema).optional(), + required: booleanSchema.optional(), + _required: z.lazy(() => elementSchema).optional(), + repeats: booleanSchema.optional(), + _repeats: z.lazy(() => elementSchema).optional(), + readOnly: booleanSchema.optional(), + _readOnly: z.lazy(() => elementSchema).optional(), + maxLength: integerSchema.optional(), + _maxLength: z.lazy(() => elementSchema).optional(), + answerValueSet: canonicalSchema.optional(), + _answerValueSet: z.lazy(() => elementSchema).optional(), + answerOption: questionnaireItemAnswerOptionSchema + .array() + .min(1) + .optional(), + initial: questionnaireItemInitialSchema.array().min(1).optional(), + item: z + .lazy(() => questionnaireItemSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'linkId', shadow: '_linkId' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const questionnaireItemEnableWhenSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + question: stringSchema.optional(), + _question: z.lazy(() => elementSchema).optional(), + operator: z.enum(['!=', '<', '<=', '=', '>', '>=', 'exists']).optional(), + _operator: z.lazy(() => elementSchema).optional(), + answerBoolean: booleanSchema.optional(), + _answerBoolean: z.lazy(() => elementSchema).optional(), + answerDecimal: decimalSchema.optional(), + _answerDecimal: z.lazy(() => elementSchema).optional(), + answerInteger: integerSchema.optional(), + _answerInteger: z.lazy(() => elementSchema).optional(), + answerDate: dateSchema.optional(), + _answerDate: z.lazy(() => elementSchema).optional(), + answerDateTime: dateTimeSchema.optional(), + _answerDateTime: z.lazy(() => elementSchema).optional(), + answerTime: timeSchema.optional(), + _answerTime: z.lazy(() => elementSchema).optional(), + answerString: stringSchema.optional(), + _answerString: z.lazy(() => elementSchema).optional(), + answerCoding: z.lazy(() => codingSchema).optional(), + answerQuantity: z.lazy(() => quantitySchema).optional(), + answerReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['answerBoolean', '_answerBoolean'], + ['answerDecimal', '_answerDecimal'], + ['answerInteger', '_answerInteger'], + ['answerDate', '_answerDate'], + ['answerDateTime', '_answerDateTime'], + ['answerTime', '_answerTime'], + ['answerString', '_answerString'], + ['answerCoding'], + ['answerQuantity'], + ['answerReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'question', shadow: '_question' }, + { name: 'operator', shadow: '_operator' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const questionnaireItemAnswerOptionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + initialSelected: booleanSchema.optional(), + _initialSelected: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueInteger', '_valueInteger'], + ['valueDate', '_valueDate'], + ['valueTime', '_valueTime'], + ['valueString', '_valueString'], + ['valueCoding'], + ['valueReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const patientLinkSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - other: z.lazy(() => referenceSchema), - type: z.enum(['refer', 'replaced-by', 'replaces', 'seealso']), - _type: z.lazy(() => elementSchema).optional(), - }), -) - -export const patientSchema: z.ZodType = z.lazy(() => +export const questionnaireItemInitialSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Patient'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - name: z - .lazy(() => humanNameSchema) - .array() - .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), - _gender: z.lazy(() => elementSchema).optional(), - birthDate: dateSchema.optional(), - _birthDate: z.lazy(() => elementSchema).optional(), - deceasedBoolean: booleanSchema.optional(), - _deceasedBoolean: z.lazy(() => elementSchema).optional(), - deceasedDateTime: dateTimeSchema.optional(), - _deceasedDateTime: z.lazy(() => elementSchema).optional(), - address: z - .lazy(() => addressSchema) - .array() - .optional(), - maritalStatus: z.lazy(() => codeableConceptSchema).optional(), - multipleBirthBoolean: booleanSchema.optional(), - _multipleBirthBoolean: z.lazy(() => elementSchema).optional(), - multipleBirthInteger: integerSchema.optional(), - _multipleBirthInteger: z.lazy(() => elementSchema).optional(), - photo: z - .lazy(() => attachmentSchema) - .array() - .optional(), - contact: patientContactSchema.array().optional(), - communication: patientCommunicationSchema.array().optional(), - generalPractitioner: z - .lazy(() => referenceSchema) - .array() - .optional(), - managingOrganization: z.lazy(() => referenceSchema).optional(), - link: patientLinkSchema.array().optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ - { names: ['deceasedBoolean', 'deceasedDateTime'], required: false }, { - names: ['multipleBirthBoolean', 'multipleBirthInteger'], - required: false, + alternatives: [ + ['valueBoolean', '_valueBoolean'], + ['valueDecimal', '_valueDecimal'], + ['valueInteger', '_valueInteger'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueTime', '_valueTime'], + ['valueString', '_valueString'], + ['valueUri', '_valueUri'], + ['valueAttachment'], + ['valueCoding'], + ['valueQuantity'], + ['valueReference'], + ], + required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -23096,102 +38648,466 @@ export const patientSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const paymentNoticeSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('PaymentNotice'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), - _status: z.lazy(() => elementSchema).optional(), - request: z.lazy(() => referenceSchema).optional(), - response: z.lazy(() => referenceSchema).optional(), - created: dateTimeSchema, - _created: z.lazy(() => elementSchema).optional(), - provider: z.lazy(() => referenceSchema).optional(), - payment: z.lazy(() => referenceSchema), - paymentDate: dateSchema.optional(), - _paymentDate: z.lazy(() => elementSchema).optional(), - payee: z.lazy(() => referenceSchema).optional(), - recipient: z.lazy(() => referenceSchema), - amount: z.lazy(() => moneySchema), - paymentStatus: z.lazy(() => codeableConceptSchema).optional(), - }), -) - -export const paymentReconciliationDetailSchema: z.ZodType = +) as unknown as z.ZodType> + +export const questionnaireSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + resourceType: z.literal('Questionnaire'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + derivedFrom: canonicalSchema.nullable().array().min(1).optional(), + _derivedFrom: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectType: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .nullable() + .array() + .min(1) + .optional(), + _subjectType: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + code: z + .lazy(() => codingSchema) + .array() + .min(1) + .optional(), + item: questionnaireItemSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'derivedFrom', shadow: '_derivedFrom', required: false }, + { name: 'subjectType', shadow: '_subjectType', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const questionnaireResponseItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - predecessor: z.lazy(() => identifierSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - request: z.lazy(() => referenceSchema).optional(), - submitter: z.lazy(() => referenceSchema).optional(), - response: z.lazy(() => referenceSchema).optional(), - date: dateSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - responsible: z.lazy(() => referenceSchema).optional(), - payee: z.lazy(() => referenceSchema).optional(), - amount: z.lazy(() => moneySchema).optional(), - }), - ) + linkId: stringSchema.optional(), + _linkId: z.lazy(() => elementSchema).optional(), + definition: uriSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + text: stringSchema.optional(), + _text: z.lazy(() => elementSchema).optional(), + answer: questionnaireResponseItemAnswerSchema.array().min(1).optional(), + item: z + .lazy(() => questionnaireResponseItemSchema) + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'linkId', shadow: '_linkId' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const paymentReconciliationProcessNoteSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const questionnaireResponseItemAnswerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum(['display', 'print', 'printoper']).optional(), - _type: z.lazy(() => elementSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - }), - ) + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + item: z + .lazy(() => questionnaireResponseItemSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueBoolean', '_valueBoolean'], + ['valueDecimal', '_valueDecimal'], + ['valueInteger', '_valueInteger'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueTime', '_valueTime'], + ['valueString', '_valueString'], + ['valueUri', '_valueUri'], + ['valueAttachment'], + ['valueCoding'], + ['valueQuantity'], + ['valueReference'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const paymentReconciliationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('PaymentReconciliation'), +export const questionnaireResponseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('QuestionnaireResponse'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -23203,453 +39119,228 @@ export const paymentReconciliationSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) + identifier: z.lazy(() => identifierSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + partOf: z + .lazy(() => referenceSchema) .array() + .min(1) + .optional(), + questionnaire: canonicalSchema.optional(), + _questionnaire: z.lazy(() => elementSchema).optional(), + status: z + .enum([ + 'amended', + 'completed', + 'entered-in-error', + 'in-progress', + 'stopped', + ]) .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), _status: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - created: dateTimeSchema, - _created: z.lazy(() => elementSchema).optional(), - paymentIssuer: z.lazy(() => referenceSchema).optional(), - request: z.lazy(() => referenceSchema).optional(), - requestor: z.lazy(() => referenceSchema).optional(), - outcome: z.enum(['complete', 'error', 'partial', 'queued']).optional(), - _outcome: z.lazy(() => elementSchema).optional(), - disposition: stringSchema.optional(), - _disposition: z.lazy(() => elementSchema).optional(), - paymentDate: dateSchema, - _paymentDate: z.lazy(() => elementSchema).optional(), - paymentAmount: z.lazy(() => moneySchema), - paymentIdentifier: z.lazy(() => identifierSchema).optional(), - detail: paymentReconciliationDetailSchema.array().optional(), - formCode: z.lazy(() => codeableConceptSchema).optional(), - processNote: paymentReconciliationProcessNoteSchema.array().optional(), - }), - ) + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + authored: dateTimeSchema.optional(), + _authored: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + source: z.lazy(() => referenceSchema).optional(), + item: questionnaireResponseItemSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const periodSchema: z.ZodType = z.lazy(() => +export const rangeSchema: z.ZodType> = z.lazy(() => z .strictObject({ id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - start: dateTimeSchema.optional(), - _start: z.lazy(() => elementSchema).optional(), - end: dateTimeSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), + low: z.lazy(() => quantitySchema).optional(), + high: z.lazy(() => quantitySchema).optional(), }) .refine( - (value) => { - const period = value as { start?: string; end?: string } - if (period.start === undefined || period.end === undefined) return true - const start = fhirDateTimeToEpoch(period.start) - const end = fhirDateTimeToEpoch(period.end) - // A malformed endpoint is a malformed date, not an ordering fault; the release says - // as much with start.hasValue(). Reporting both would name the wrong problem. - if (Number.isNaN(start) || Number.isNaN(end)) return true - return start <= end - }, - { message: 'Period.start must not be later than Period.end.' }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const personLinkSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - target: z.lazy(() => referenceSchema), - assurance: z.enum(['level1', 'level2', 'level3', 'level4']).optional(), - _assurance: z.lazy(() => elementSchema).optional(), - }), -) - -export const personSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Person'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - name: z - .lazy(() => humanNameSchema) - .array() - .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), - _gender: z.lazy(() => elementSchema).optional(), - birthDate: dateSchema.optional(), - _birthDate: z.lazy(() => elementSchema).optional(), - address: z - .lazy(() => addressSchema) - .array() - .optional(), - photo: z.lazy(() => attachmentSchema).optional(), - managingOrganization: z.lazy(() => referenceSchema).optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - link: personLinkSchema.array().optional(), - }), -) - -export const planDefinitionGoalSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - description: z.lazy(() => codeableConceptSchema), - priority: z.lazy(() => codeableConceptSchema).optional(), - start: z.lazy(() => codeableConceptSchema).optional(), - addresses: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - documentation: z - .lazy(() => relatedArtifactSchema) - .array() - .optional(), - target: planDefinitionGoalTargetSchema.array().optional(), - }), - ) - -export const planDefinitionActionSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - prefix: stringSchema.optional(), - _prefix: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - textEquivalent: stringSchema.optional(), - _textEquivalent: z.lazy(() => elementSchema).optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - code: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reason: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - documentation: z - .lazy(() => relatedArtifactSchema) - .array() - .optional(), - goalId: idSchema.array().optional(), - _goalId: z - .lazy(() => elementSchema) - .array() - .optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - subjectCanonical: canonicalSchema.optional(), - _subjectCanonical: z.lazy(() => elementSchema).optional(), - trigger: z - .lazy(() => triggerDefinitionSchema) - .array() - .optional(), - condition: planDefinitionActionConditionSchema.array().optional(), - input: z - .lazy(() => dataRequirementSchema) - .array() - .optional(), - output: z - .lazy(() => dataRequirementSchema) - .array() - .optional(), - relatedAction: planDefinitionActionRelatedActionSchema - .array() - .optional(), - timingDateTime: dateTimeSchema.optional(), - _timingDateTime: z.lazy(() => elementSchema).optional(), - timingAge: z.lazy(() => ageSchema).optional(), - timingPeriod: z.lazy(() => periodSchema).optional(), - timingDuration: z.lazy(() => durationSchema).optional(), - timingRange: z.lazy(() => rangeSchema).optional(), - timingTiming: z.lazy(() => timingSchema).optional(), - participant: planDefinitionActionParticipantSchema.array().optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - groupingBehavior: z - .enum(['logical-group', 'sentence-group', 'visual-group']) - .optional(), - _groupingBehavior: z.lazy(() => elementSchema).optional(), - selectionBehavior: z - .enum([ - 'all', - 'all-or-none', - 'any', - 'at-most-one', - 'exactly-one', - 'one-or-more', - ]) - .optional(), - _selectionBehavior: z.lazy(() => elementSchema).optional(), - requiredBehavior: z - .enum(['could', 'must', 'must-unless-documented']) - .optional(), - _requiredBehavior: z.lazy(() => elementSchema).optional(), - precheckBehavior: z.enum(['no', 'yes']).optional(), - _precheckBehavior: z.lazy(() => elementSchema).optional(), - cardinalityBehavior: z.enum(['multiple', 'single']).optional(), - _cardinalityBehavior: z.lazy(() => elementSchema).optional(), - definitionCanonical: canonicalSchema.optional(), - _definitionCanonical: z.lazy(() => elementSchema).optional(), - definitionUri: uriSchema.optional(), - _definitionUri: z.lazy(() => elementSchema).optional(), - transform: canonicalSchema.optional(), - _transform: z.lazy(() => elementSchema).optional(), - dynamicValue: planDefinitionActionDynamicValueSchema.array().optional(), - action: z - .lazy(() => planDefinitionActionSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'subjectCodeableConcept', - 'subjectReference', - 'subjectCanonical', - ], - required: false, - }, - { - names: [ - 'timingDateTime', - 'timingAge', - 'timingPeriod', - 'timingDuration', - 'timingRange', - 'timingTiming', - ], - required: false, - }, - { - names: ['definitionCanonical', 'definitionUri'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const planDefinitionGoalTargetSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - measure: z.lazy(() => codeableConceptSchema).optional(), - detailQuantity: z.lazy(() => quantitySchema).optional(), - detailRange: z.lazy(() => rangeSchema).optional(), - detailCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - due: z.lazy(() => durationSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['detailQuantity', 'detailRange', 'detailCodeableConcept'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) +) as unknown as z.ZodType> -export const planDefinitionActionConditionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const ratioSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - kind: z.enum(['applicability', 'start', 'stop']), - _kind: z.lazy(() => elementSchema).optional(), - expression: z.lazy(() => expressionSchema).optional(), - }), - ) + numerator: z.lazy(() => quantitySchema).optional(), + denominator: z.lazy(() => quantitySchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const planDefinitionActionRelatedActionSchema: z.ZodType = - z.lazy(() => +export const ratioRangeSchema: z.ZodType> = z.lazy( + () => z .strictObject({ - id: stringSchema.optional(), + id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - actionId: idSchema, - _actionId: z.lazy(() => elementSchema).optional(), - relationship: z.enum([ - 'after', - 'after-end', - 'after-start', - 'before', - 'before-end', - 'before-start', - 'concurrent', - 'concurrent-with-end', - 'concurrent-with-start', - ]), - _relationship: z.lazy(() => elementSchema).optional(), - offsetDuration: z.lazy(() => durationSchema).optional(), - offsetRange: z.lazy(() => rangeSchema).optional(), + lowNumerator: z.lazy(() => quantitySchema).optional(), + highNumerator: z.lazy(() => quantitySchema).optional(), + denominator: z.lazy(() => quantitySchema).optional(), }) .refine( - (value) => - [{ names: ['offsetDuration', 'offsetRange'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> -export const planDefinitionActionParticipantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const referenceSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum(['device', 'patient', 'practitioner', 'related-person']), + reference: stringSchema.optional(), + _reference: z.lazy(() => elementSchema).optional(), + type: uriSchema.optional(), _type: z.lazy(() => elementSchema).optional(), - role: z.lazy(() => codeableConceptSchema).optional(), - }), - ) + identifier: z.lazy(() => identifierSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ) + .refine( + (value) => { + const reference = value as Record + return [ + 'reference', + '_reference', + 'identifier', + 'display', + '_display', + ].some((name) => reference[name] !== undefined) + }, + { + message: + 'ref-1: a Reference states a literal reference, identifier, or display.', + }, + ), +) as unknown as z.ZodType> -export const planDefinitionActionDynamicValueSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const regulatedAuthorizationCaseSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - path: stringSchema.optional(), - _path: z.lazy(() => elementSchema).optional(), - expression: z.lazy(() => expressionSchema).optional(), - }), - ) + identifier: z.lazy(() => identifierSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + status: z.lazy(() => codeableConceptSchema).optional(), + datePeriod: z.lazy(() => periodSchema).optional(), + dateDateTime: dateTimeSchema.optional(), + _dateDateTime: z.lazy(() => elementSchema).optional(), + application: z + .lazy(() => regulatedAuthorizationCaseSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['datePeriod'], ['dateDateTime', '_dateDateTime']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const planDefinitionSchema: z.ZodType = z.lazy(() => +export const regulatedAuthorizationSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('PlanDefinition'), + resourceType: z.literal('RegulatedAuthorization'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -23661,352 +39352,660 @@ export const planDefinitionSchema: z.ZodType = z.lazy(() => contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - subjectCanonical: canonicalSchema.optional(), - _subjectCanonical: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) + subject: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), + type: z.lazy(() => codeableConceptSchema).optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z + region: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z + status: z.lazy(() => codeableConceptSchema).optional(), + statusDate: dateTimeSchema.optional(), + _statusDate: z.lazy(() => elementSchema).optional(), + validityPeriod: z.lazy(() => periodSchema).optional(), + indication: z.lazy(() => codeableReferenceSchema).optional(), + intendedUse: z.lazy(() => codeableConceptSchema).optional(), + basis: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - author: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - editor: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - reviewer: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - endorser: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) - .array() - .optional(), - library: canonicalSchema.array().optional(), - _library: z - .lazy(() => elementSchema) - .array() - .optional(), - goal: planDefinitionGoalSchema.array().optional(), - action: planDefinitionActionSchema.array().optional(), + holder: z.lazy(() => referenceSchema).optional(), + regulator: z.lazy(() => referenceSchema).optional(), + case: regulatedAuthorizationCaseSchema.optional(), }) .refine( - (value) => - [ - { - names: [ - 'subjectCodeableConcept', - 'subjectReference', - 'subjectCanonical', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const practitionerQualificationSchema: z.ZodType = +export const relatedArtifactSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z + .enum([ + 'citation', + 'composed-of', + 'depends-on', + 'derived-from', + 'documentation', + 'justification', + 'predecessor', + 'successor', + ]) + .optional(), + _type: z.lazy(() => elementSchema).optional(), + label: stringSchema.optional(), + _label: z.lazy(() => elementSchema).optional(), + display: stringSchema.optional(), + _display: z.lazy(() => elementSchema).optional(), + citation: markdownSchema.optional(), + _citation: z.lazy(() => elementSchema).optional(), + url: urlSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + document: z.lazy(() => attachmentSchema).optional(), + resource: canonicalSchema.optional(), + _resource: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const relatedPersonCommunicationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - period: z.lazy(() => periodSchema).optional(), - issuer: z.lazy(() => referenceSchema).optional(), - }), - ) + language: z.lazy(() => codeableConceptSchema), + preferred: booleanSchema.optional(), + _preferred: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const practitionerSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Practitioner'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - name: z - .lazy(() => humanNameSchema) - .array() - .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - address: z - .lazy(() => addressSchema) - .array() - .optional(), - gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), - _gender: z.lazy(() => elementSchema).optional(), - birthDate: dateSchema.optional(), - _birthDate: z.lazy(() => elementSchema).optional(), - photo: z - .lazy(() => attachmentSchema) - .array() - .optional(), - qualification: practitionerQualificationSchema.array().optional(), - communication: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - }), -) - -export const practitionerRoleAvailableTimeSchema: z.ZodType = +export const relatedPersonSchema: z.ZodType> = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - daysOfWeek: z - .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) - .array() - .optional(), - _daysOfWeek: z - .lazy(() => elementSchema) - .array() - .optional(), - allDay: booleanSchema.optional(), - _allDay: z.lazy(() => elementSchema).optional(), - availableStartTime: timeSchema.optional(), - _availableStartTime: z.lazy(() => elementSchema).optional(), - availableEndTime: timeSchema.optional(), - _availableEndTime: z.lazy(() => elementSchema).optional(), - }), - ) + z + .strictObject({ + resourceType: z.literal('RelatedPerson'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema), + relationship: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + name: z + .lazy(() => humanNameSchema) + .array() + .min(1) + .optional(), + telecom: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), + _gender: z.lazy(() => elementSchema).optional(), + birthDate: dateSchema.optional(), + _birthDate: z.lazy(() => elementSchema).optional(), + address: z + .lazy(() => addressSchema) + .array() + .min(1) + .optional(), + photo: z + .lazy(() => attachmentSchema) + .array() + .min(1) + .optional(), + period: z.lazy(() => periodSchema).optional(), + communication: relatedPersonCommunicationSchema + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const practitionerRoleNotAvailableSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const requestGroupActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: stringSchema, + prefix: stringSchema.optional(), + _prefix: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - during: z.lazy(() => periodSchema).optional(), - }), - ) - -export const practitionerRoleSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('PractitionerRole'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - practitioner: z.lazy(() => referenceSchema).optional(), - organization: z.lazy(() => referenceSchema).optional(), + textEquivalent: stringSchema.optional(), + _textEquivalent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), code: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - specialty: z - .lazy(() => codeableConceptSchema) + documentation: z + .lazy(() => relatedArtifactSchema) .array() + .min(1) .optional(), - location: z - .lazy(() => referenceSchema) + condition: requestGroupActionConditionSchema.array().min(1).optional(), + relatedAction: requestGroupActionRelatedActionSchema .array() + .min(1) .optional(), - healthcareService: z + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + timingAge: z.lazy(() => ageSchema).optional(), + timingPeriod: z.lazy(() => periodSchema).optional(), + timingDuration: z.lazy(() => durationSchema).optional(), + timingRange: z.lazy(() => rangeSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + participant: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() + type: z.lazy(() => codeableConceptSchema).optional(), + groupingBehavior: z + .enum(['logical-group', 'sentence-group', 'visual-group']) .optional(), - availableTime: practitionerRoleAvailableTimeSchema.array().optional(), - notAvailable: practitionerRoleNotAvailableSchema.array().optional(), - availabilityExceptions: stringSchema.optional(), - _availabilityExceptions: z.lazy(() => elementSchema).optional(), - endpoint: z - .lazy(() => referenceSchema) + _groupingBehavior: z.lazy(() => elementSchema).optional(), + selectionBehavior: z + .enum([ + 'all', + 'all-or-none', + 'any', + 'at-most-one', + 'exactly-one', + 'one-or-more', + ]) + .optional(), + _selectionBehavior: z.lazy(() => elementSchema).optional(), + requiredBehavior: z + .enum(['could', 'must', 'must-unless-documented']) + .optional(), + _requiredBehavior: z.lazy(() => elementSchema).optional(), + precheckBehavior: z.enum(['no', 'yes']).optional(), + _precheckBehavior: z.lazy(() => elementSchema).optional(), + cardinalityBehavior: z.enum(['multiple', 'single']).optional(), + _cardinalityBehavior: z.lazy(() => elementSchema).optional(), + resource: z.lazy(() => referenceSchema).optional(), + action: z + .lazy(() => requestGroupActionSchema) .array() + .min(1) .optional(), - }), -) + }) + .refine( + (value) => + [ + { + alternatives: [ + ['timingDateTime', '_timingDateTime'], + ['timingAge'], + ['timingPeriod'], + ['timingDuration'], + ['timingRange'], + ['timingTiming'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const procedurePerformerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const requestGroupActionConditionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - function: z.lazy(() => codeableConceptSchema).optional(), - actor: z.lazy(() => referenceSchema), - onBehalfOf: z.lazy(() => referenceSchema).optional(), - }), - ) + kind: z.enum(['applicability', 'start', 'stop']).optional(), + _kind: z.lazy(() => elementSchema).optional(), + expression: z.lazy(() => expressionSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'kind', shadow: '_kind' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const procedureFocalDeviceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const requestGroupActionRelatedActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - action: z.lazy(() => codeableConceptSchema).optional(), - manipulated: z.lazy(() => referenceSchema), - }), - ) + actionId: idSchema.optional(), + _actionId: z.lazy(() => elementSchema).optional(), + relationship: z + .enum([ + 'after', + 'after-end', + 'after-start', + 'before', + 'before-end', + 'before-start', + 'concurrent', + 'concurrent-with-end', + 'concurrent-with-start', + ]) + .optional(), + _relationship: z.lazy(() => elementSchema).optional(), + offsetDuration: z.lazy(() => durationSchema).optional(), + offsetRange: z.lazy(() => rangeSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['offsetDuration'], ['offsetRange']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'actionId', shadow: '_actionId' }, + { name: 'relationship', shadow: '_relationship' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const requestGroupSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('RequestGroup'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + instantiatesCanonical: canonicalSchema + .nullable() + .array() + .min(1) + .optional(), + _instantiatesCanonical: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + replaces: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + status: z + .enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + intent: z + .enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]) + .optional(), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + author: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + action: requestGroupActionSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'intent', shadow: '_intent' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const procedureSchema: z.ZodType = z.lazy(() => +export const researchDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('Procedure'), + resourceType: z.literal('ResearchDefinition'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -24018,123 +40017,134 @@ export const procedureSchema: z.ZodType = z.lazy(() => contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + shortTitle: stringSchema.optional(), + _shortTitle: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + comment: stringSchema.nullable().array().min(1).optional(), + _comment: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - status: z.enum([ - 'completed', - 'entered-in-error', - 'in-progress', - 'not-done', - 'on-hold', - 'preparation', - 'stopped', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - performedDateTime: dateTimeSchema.optional(), - _performedDateTime: z.lazy(() => elementSchema).optional(), - performedPeriod: z.lazy(() => periodSchema).optional(), - performedString: stringSchema.optional(), - _performedString: z.lazy(() => elementSchema).optional(), - performedAge: z.lazy(() => ageSchema).optional(), - performedRange: z.lazy(() => rangeSchema).optional(), - recorder: z.lazy(() => referenceSchema).optional(), - asserter: z.lazy(() => referenceSchema).optional(), - performer: procedurePerformerSchema.array().optional(), - location: z.lazy(() => referenceSchema).optional(), - reasonCode: z + jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - bodySite: z + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - outcome: z.lazy(() => codeableConceptSchema).optional(), - report: z - .lazy(() => referenceSchema) - .array() - .optional(), - complication: z - .lazy(() => codeableConceptSchema) + author: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - complicationDetail: z - .lazy(() => referenceSchema) + editor: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - followUp: z - .lazy(() => codeableConceptSchema) + reviewer: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - note: z - .lazy(() => annotationSchema) + endorser: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - focalDevice: procedureFocalDeviceSchema.array().optional(), - usedReference: z - .lazy(() => referenceSchema) + relatedArtifact: z + .lazy(() => relatedArtifactSchema) .array() + .min(1) .optional(), - usedCode: z - .lazy(() => codeableConceptSchema) + library: canonicalSchema.nullable().array().min(1).optional(), + _library: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), + population: z.lazy(() => referenceSchema), + exposure: z.lazy(() => referenceSchema).optional(), + exposureAlternative: z.lazy(() => referenceSchema).optional(), + outcome: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ { - names: [ - 'performedDateTime', - 'performedPeriod', - 'performedString', - 'performedAge', - 'performedRange', - ], + alternatives: [['subjectCodeableConcept'], ['subjectReference']], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -24142,177 +40152,344 @@ export const procedureSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'comment', shadow: '_comment', required: false }, + { name: 'library', shadow: '_library', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const prodCharacteristicSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: z.string().optional(), +export const researchElementDefinitionCharacteristicSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - height: z.lazy(() => quantitySchema).optional(), - width: z.lazy(() => quantitySchema).optional(), - depth: z.lazy(() => quantitySchema).optional(), - weight: z.lazy(() => quantitySchema).optional(), - nominalVolume: z.lazy(() => quantitySchema).optional(), - externalDiameter: z.lazy(() => quantitySchema).optional(), - shape: stringSchema.optional(), - _shape: z.lazy(() => elementSchema).optional(), - color: stringSchema.array().optional(), - _color: z - .lazy(() => elementSchema) + definitionCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + definitionCanonical: canonicalSchema.optional(), + _definitionCanonical: z.lazy(() => elementSchema).optional(), + definitionExpression: z.lazy(() => expressionSchema).optional(), + definitionDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + usageContext: z + .lazy(() => usageContextSchema) .array() + .min(1) + .optional(), + exclude: booleanSchema.optional(), + _exclude: z.lazy(() => elementSchema).optional(), + unitOfMeasure: z.lazy(() => codeableConceptSchema).optional(), + studyEffectiveDescription: stringSchema.optional(), + _studyEffectiveDescription: z.lazy(() => elementSchema).optional(), + studyEffectiveDateTime: dateTimeSchema.optional(), + _studyEffectiveDateTime: z.lazy(() => elementSchema).optional(), + studyEffectivePeriod: z.lazy(() => periodSchema).optional(), + studyEffectiveDuration: z.lazy(() => durationSchema).optional(), + studyEffectiveTiming: z.lazy(() => timingSchema).optional(), + studyEffectiveTimeFromStart: z.lazy(() => durationSchema).optional(), + studyEffectiveGroupMeasure: z + .enum([ + 'mean', + 'mean-of-mean', + 'mean-of-median', + 'median', + 'median-of-mean', + 'median-of-median', + ]) .optional(), - imprint: stringSchema.array().optional(), - _imprint: z - .lazy(() => elementSchema) - .array() + _studyEffectiveGroupMeasure: z.lazy(() => elementSchema).optional(), + participantEffectiveDescription: stringSchema.optional(), + _participantEffectiveDescription: z.lazy(() => elementSchema).optional(), + participantEffectiveDateTime: dateTimeSchema.optional(), + _participantEffectiveDateTime: z.lazy(() => elementSchema).optional(), + participantEffectivePeriod: z.lazy(() => periodSchema).optional(), + participantEffectiveDuration: z.lazy(() => durationSchema).optional(), + participantEffectiveTiming: z.lazy(() => timingSchema).optional(), + participantEffectiveTimeFromStart: z + .lazy(() => durationSchema) + .optional(), + participantEffectiveGroupMeasure: z + .enum([ + 'mean', + 'mean-of-mean', + 'mean-of-median', + 'median', + 'median-of-mean', + 'median-of-median', + ]) .optional(), - image: z - .lazy(() => attachmentSchema) + _participantEffectiveGroupMeasure: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['definitionCodeableConcept'], + ['definitionCanonical', '_definitionCanonical'], + ['definitionExpression'], + ['definitionDataRequirement'], + ], + required: true, + }, + { + alternatives: [ + ['studyEffectiveDateTime', '_studyEffectiveDateTime'], + ['studyEffectivePeriod'], + ['studyEffectiveDuration'], + ['studyEffectiveTiming'], + ], + required: false, + }, + { + alternatives: [ + ['participantEffectiveDateTime', '_participantEffectiveDateTime'], + ['participantEffectivePeriod'], + ['participantEffectiveDuration'], + ['participantEffectiveTiming'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const researchElementDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ResearchElementDefinition'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) .array() + .min(1) .optional(), - scoring: z.lazy(() => codeableConceptSchema).optional(), - }), - ) - -export const productShelfLifeSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - type: z.lazy(() => codeableConceptSchema), - period: z.lazy(() => quantitySchema), - specialPrecautionsForStorage: z - .lazy(() => codeableConceptSchema) + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - }), -) - -export const provenanceAgentSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + shortTitle: stringSchema.optional(), + _shortTitle: z.lazy(() => elementSchema).optional(), + subtitle: stringSchema.optional(), + _subtitle: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + subjectReference: z.lazy(() => referenceSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + comment: stringSchema.nullable().array().min(1).optional(), + _comment: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - role: z - .lazy(() => codeableConceptSchema) + useContext: z + .lazy(() => usageContextSchema) .array() + .min(1) .optional(), - who: z.lazy(() => referenceSchema), - onBehalfOf: z.lazy(() => referenceSchema).optional(), - }), -) - -export const provenanceEntitySchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) + jurisdiction: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + usage: stringSchema.optional(), + _usage: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + approvalDate: dateSchema.optional(), + _approvalDate: z.lazy(() => elementSchema).optional(), + lastReviewDate: dateSchema.optional(), + _lastReviewDate: z.lazy(() => elementSchema).optional(), + effectivePeriod: z.lazy(() => periodSchema).optional(), + topic: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - role: z.enum([ - 'derivation', - 'quotation', - 'removal', - 'revision', - 'source', - ]), - _role: z.lazy(() => elementSchema).optional(), - what: z.lazy(() => referenceSchema), - agent: z - .lazy(() => provenanceAgentSchema) + author: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - }), -) - -export const provenanceSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - resourceType: z.literal('Provenance'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) + editor: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - extension: z - .lazy(() => extensionSchema) + reviewer: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + endorser: z + .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), - target: z - .lazy(() => referenceSchema) + relatedArtifact: z + .lazy(() => relatedArtifactSchema) .array() - .min(1), - occurredPeriod: z.lazy(() => periodSchema).optional(), - occurredDateTime: dateTimeSchema.optional(), - _occurredDateTime: z.lazy(() => elementSchema).optional(), - recorded: instantSchema, - _recorded: z.lazy(() => elementSchema).optional(), - policy: uriSchema.array().optional(), - _policy: z + .min(1) + .optional(), + library: canonicalSchema.nullable().array().min(1).optional(), + _library: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - location: z.lazy(() => referenceSchema).optional(), - reason: z - .lazy(() => codeableConceptSchema) - .array() + type: z.enum(['exposure', 'outcome', 'population']).optional(), + _type: z.lazy(() => elementSchema).optional(), + variableType: z + .enum(['continuous', 'descriptive', 'dichotomous']) .optional(), - activity: z.lazy(() => codeableConceptSchema).optional(), - agent: provenanceAgentSchema.array().min(1), - entity: provenanceEntitySchema.array().optional(), - signature: z - .lazy(() => signatureSchema) + _variableType: z.lazy(() => elementSchema).optional(), + characteristic: researchElementDefinitionCharacteristicSchema .array() - .optional(), + .min(1), }) .refine( (value) => [ - { names: ['occurredPeriod', 'occurredDateTime'], required: false }, + { + alternatives: [['subjectCodeableConcept'], ['subjectReference']], + required: false, + }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -24320,162 +40497,79 @@ export const provenanceSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'comment', shadow: '_comment', required: false }, + { name: 'library', shadow: '_library', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const quantitySchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - value: decimalSchema.optional(), - _value: z.lazy(() => elementSchema).optional(), - comparator: z.enum(['<', '<=', '>', '>=']).optional(), - _comparator: z.lazy(() => elementSchema).optional(), - unit: stringSchema.optional(), - _unit: z.lazy(() => elementSchema).optional(), - system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - code: codeSchema.optional(), - _code: z.lazy(() => elementSchema).optional(), - }), -) - -export const questionnaireItemSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - linkId: stringSchema, - _linkId: z.lazy(() => elementSchema).optional(), - definition: uriSchema.optional(), - _definition: z.lazy(() => elementSchema).optional(), - code: z - .lazy(() => codingSchema) - .array() - .optional(), - prefix: stringSchema.optional(), - _prefix: z.lazy(() => elementSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - type: z.enum([ - 'attachment', - 'boolean', - 'choice', - 'date', - 'dateTime', - 'decimal', - 'display', - 'group', - 'integer', - 'open-choice', - 'quantity', - 'question', - 'reference', - 'string', - 'text', - 'time', - 'url', - ]), - _type: z.lazy(() => elementSchema).optional(), - enableWhen: questionnaireItemEnableWhenSchema.array().optional(), - enableBehavior: z.enum(['all', 'any']).optional(), - _enableBehavior: z.lazy(() => elementSchema).optional(), - required: booleanSchema.optional(), - _required: z.lazy(() => elementSchema).optional(), - repeats: booleanSchema.optional(), - _repeats: z.lazy(() => elementSchema).optional(), - readOnly: booleanSchema.optional(), - _readOnly: z.lazy(() => elementSchema).optional(), - maxLength: integerSchema.optional(), - _maxLength: z.lazy(() => elementSchema).optional(), - answerValueSet: canonicalSchema.optional(), - _answerValueSet: z.lazy(() => elementSchema).optional(), - answerOption: questionnaireItemAnswerOptionSchema.array().optional(), - initial: questionnaireItemInitialSchema.array().optional(), - item: z - .lazy(() => questionnaireItemSchema) - .array() - .optional(), - }), -) - -export const questionnaireItemEnableWhenSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - question: stringSchema, - _question: z.lazy(() => elementSchema).optional(), - operator: z.enum(['!=', '<', '<=', '=', '>', '>=', 'exists']), - _operator: z.lazy(() => elementSchema).optional(), - answerBoolean: booleanSchema.optional(), - _answerBoolean: z.lazy(() => elementSchema).optional(), - answerDecimal: decimalSchema.optional(), - _answerDecimal: z.lazy(() => elementSchema).optional(), - answerInteger: integerSchema.optional(), - _answerInteger: z.lazy(() => elementSchema).optional(), - answerDate: dateSchema.optional(), - _answerDate: z.lazy(() => elementSchema).optional(), - answerDateTime: dateTimeSchema.optional(), - _answerDateTime: z.lazy(() => elementSchema).optional(), - answerTime: timeSchema.optional(), - _answerTime: z.lazy(() => elementSchema).optional(), - answerString: stringSchema.optional(), - _answerString: z.lazy(() => elementSchema).optional(), - answerCoding: z.lazy(() => codingSchema).optional(), - answerQuantity: z.lazy(() => quantitySchema).optional(), - answerReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'answerBoolean', - 'answerDecimal', - 'answerInteger', - 'answerDate', - 'answerDateTime', - 'answerTime', - 'answerString', - 'answerCoding', - 'answerQuantity', - 'answerReference', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) +) as unknown as z.ZodType> -export const questionnaireItemAnswerOptionSchema: z.ZodType = +export const researchStudyArmSchema: z.ZodType> = z.lazy(() => z .strictObject({ @@ -24483,851 +40577,454 @@ export const questionnaireItemAnswerOptionSchema: z.ZodType extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - initialSelected: booleanSchema.optional(), - _initialSelected: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), }) - .refine( - (value) => - [ - { - names: [ - 'valueInteger', - 'valueDate', - 'valueTime', - 'valueString', - 'valueCoding', - 'valueReference', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const questionnaireItemInitialSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: [ - 'valueBoolean', - 'valueDecimal', - 'valueInteger', - 'valueDate', - 'valueDateTime', - 'valueTime', - 'valueString', - 'valueUri', - 'valueAttachment', - 'valueCoding', - 'valueQuantity', - 'valueReference', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const questionnaireSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Questionnaire'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - derivedFrom: canonicalSchema.array().optional(), - _derivedFrom: z - .lazy(() => elementSchema) - .array() - .optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - subjectType: z - .enum([ - 'Account', - 'ActivityDefinition', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RegulatedAuthorization', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]) - .array() - .optional(), - _subjectType: z - .lazy(() => elementSchema) - .array() - .optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - code: z - .lazy(() => codingSchema) - .array() - .optional(), - item: questionnaireItemSchema.array().optional(), - }), -) - -export const questionnaireResponseItemSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const researchStudyObjectiveSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - linkId: stringSchema, - _linkId: z.lazy(() => elementSchema).optional(), - definition: uriSchema.optional(), - _definition: z.lazy(() => elementSchema).optional(), - text: stringSchema.optional(), - _text: z.lazy(() => elementSchema).optional(), - answer: questionnaireResponseItemAnswerSchema.array().optional(), - item: z - .lazy(() => questionnaireResponseItemSchema) - .array() - .optional(), - }), - ) + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const questionnaireResponseItemAnswerSchema: z.ZodType = +export const researchStudySchema: z.ZodType> = z.lazy(() => z .strictObject({ - id: stringSchema.optional(), + resourceType: z.literal('ResearchStudy'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + protocol: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'active', + 'administratively-completed', + 'approved', + 'closed-to-accrual', + 'closed-to-accrual-and-intervention', + 'completed', + 'disapproved', + 'in-review', + 'temporarily-closed-to-accrual', + 'temporarily-closed-to-accrual-and-intervention', + 'withdrawn', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + primaryPurposeType: z.lazy(() => codeableConceptSchema).optional(), + phase: z.lazy(() => codeableConceptSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + focus: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + condition: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + relatedArtifact: z + .lazy(() => relatedArtifactSchema) + .array() + .min(1) + .optional(), + keyword: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + location: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + enrollment: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + period: z.lazy(() => periodSchema).optional(), + sponsor: z.lazy(() => referenceSchema).optional(), + principalInvestigator: z.lazy(() => referenceSchema).optional(), + site: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - item: z - .lazy(() => questionnaireResponseItemSchema) + reasonStopped: z.lazy(() => codeableConceptSchema).optional(), + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), + arm: researchStudyArmSchema.array().min(1).optional(), + objective: researchStudyObjectiveSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: [ - 'valueBoolean', - 'valueDecimal', - 'valueInteger', - 'valueDate', - 'valueDateTime', - 'valueTime', - 'valueString', - 'valueUri', - 'valueAttachment', - 'valueCoding', - 'valueQuantity', - 'valueReference', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) - -export const questionnaireResponseSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('QuestionnaireResponse'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - questionnaire: canonicalSchema.optional(), - _questionnaire: z.lazy(() => elementSchema).optional(), - status: z.enum([ - 'amended', - 'completed', - 'entered-in-error', - 'in-progress', - 'stopped', - ]), - _status: z.lazy(() => elementSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - authored: dateTimeSchema.optional(), - _authored: z.lazy(() => elementSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - source: z.lazy(() => referenceSchema).optional(), - item: questionnaireResponseItemSchema.array().optional(), - }), - ) + ) as unknown as z.ZodType> -export const rangeSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - low: z.lazy(() => quantitySchema).optional(), - high: z.lazy(() => quantitySchema).optional(), - }), -) - -export const ratioSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - numerator: z.lazy(() => quantitySchema).optional(), - denominator: z.lazy(() => quantitySchema).optional(), - }), -) - -export const ratioRangeSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - lowNumerator: z.lazy(() => quantitySchema).optional(), - highNumerator: z.lazy(() => quantitySchema).optional(), - denominator: z.lazy(() => quantitySchema).optional(), - }), -) - -export const referenceSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - reference: stringSchema.optional(), - _reference: z.lazy(() => elementSchema).optional(), - type: uriSchema.optional(), - _type: z.lazy(() => elementSchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - }), -) - -export const regulatedAuthorizationCaseSchema: z.ZodType = +export const researchSubjectSchema: z.ZodType> = z.lazy(() => z .strictObject({ - id: stringSchema.optional(), + resourceType: z.literal('ResearchSubject'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - status: z.lazy(() => codeableConceptSchema).optional(), - datePeriod: z.lazy(() => periodSchema).optional(), - dateDateTime: dateTimeSchema.optional(), - _dateDateTime: z.lazy(() => elementSchema).optional(), - application: z - .lazy(() => regulatedAuthorizationCaseSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) + .optional(), + status: z + .enum([ + 'candidate', + 'eligible', + 'follow-up', + 'ineligible', + 'not-registered', + 'off-study', + 'on-study', + 'on-study-intervention', + 'on-study-observation', + 'pending-on-study', + 'potential-candidate', + 'screening', + 'withdrawn', + ]) .optional(), + _status: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + study: z.lazy(() => referenceSchema), + individual: z.lazy(() => referenceSchema), + assignedArm: stringSchema.optional(), + _assignedArm: z.lazy(() => elementSchema).optional(), + actualArm: stringSchema.optional(), + _actualArm: z.lazy(() => elementSchema).optional(), + consent: z.lazy(() => referenceSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [{ names: ['datePeriod', 'dateDateTime'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) - -export const regulatedAuthorizationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - resourceType: z.literal('RegulatedAuthorization'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - subject: z - .lazy(() => referenceSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - region: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - status: z.lazy(() => codeableConceptSchema).optional(), - statusDate: dateTimeSchema.optional(), - _statusDate: z.lazy(() => elementSchema).optional(), - validityPeriod: z.lazy(() => periodSchema).optional(), - indication: z.lazy(() => codeableReferenceSchema).optional(), - intendedUse: z.lazy(() => codeableConceptSchema).optional(), - basis: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - holder: z.lazy(() => referenceSchema).optional(), - regulator: z.lazy(() => referenceSchema).optional(), - case: regulatedAuthorizationCaseSchema.optional(), - }), - ) - -export const relatedArtifactSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.enum([ - 'citation', - 'composed-of', - 'depends-on', - 'derived-from', - 'documentation', - 'justification', - 'predecessor', - 'successor', - ]), - _type: z.lazy(() => elementSchema).optional(), - label: stringSchema.optional(), - _label: z.lazy(() => elementSchema).optional(), - display: stringSchema.optional(), - _display: z.lazy(() => elementSchema).optional(), - citation: markdownSchema.optional(), - _citation: z.lazy(() => elementSchema).optional(), - url: urlSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - document: z.lazy(() => attachmentSchema).optional(), - resource: canonicalSchema.optional(), - _resource: z.lazy(() => elementSchema).optional(), - }), -) + ) as unknown as z.ZodType> -export const relatedPersonCommunicationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const riskAssessmentPredictionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - language: z.lazy(() => codeableConceptSchema), - preferred: booleanSchema.optional(), - _preferred: z.lazy(() => elementSchema).optional(), - }), - ) + outcome: z.lazy(() => codeableConceptSchema).optional(), + probabilityDecimal: decimalSchema.optional(), + _probabilityDecimal: z.lazy(() => elementSchema).optional(), + probabilityRange: z.lazy(() => rangeSchema).optional(), + qualitativeRisk: z.lazy(() => codeableConceptSchema).optional(), + relativeRisk: decimalSchema.optional(), + _relativeRisk: z.lazy(() => elementSchema).optional(), + whenPeriod: z.lazy(() => periodSchema).optional(), + whenRange: z.lazy(() => rangeSchema).optional(), + rationale: stringSchema.optional(), + _rationale: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['probabilityDecimal', '_probabilityDecimal'], + ['probabilityRange'], + ], + required: false, + }, + { alternatives: [['whenPeriod'], ['whenRange']], required: false }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const relatedPersonSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('RelatedPerson'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema), - relationship: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - name: z - .lazy(() => humanNameSchema) - .array() - .optional(), - telecom: z - .lazy(() => contactPointSchema) - .array() - .optional(), - gender: z.enum(['female', 'male', 'other', 'unknown']).optional(), - _gender: z.lazy(() => elementSchema).optional(), - birthDate: dateSchema.optional(), - _birthDate: z.lazy(() => elementSchema).optional(), - address: z - .lazy(() => addressSchema) - .array() - .optional(), - photo: z - .lazy(() => attachmentSchema) - .array() - .optional(), - period: z.lazy(() => periodSchema).optional(), - communication: relatedPersonCommunicationSchema.array().optional(), - }), -) - -export const requestGroupActionSchema: z.ZodType = +export const riskAssessmentSchema: z.ZodType> = z.lazy(() => z .strictObject({ - id: stringSchema.optional(), + resourceType: z.literal('RiskAssessment'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - prefix: stringSchema.optional(), - _prefix: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - textEquivalent: stringSchema.optional(), - _textEquivalent: z.lazy(() => elementSchema).optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - code: z - .lazy(() => codeableConceptSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - documentation: z - .lazy(() => relatedArtifactSchema) - .array() + basedOn: z.lazy(() => referenceSchema).optional(), + parent: z.lazy(() => referenceSchema).optional(), + status: z + .enum([ + 'amended', + 'cancelled', + 'corrected', + 'entered-in-error', + 'final', + 'preliminary', + 'registered', + 'unknown', + ]) .optional(), - condition: requestGroupActionConditionSchema.array().optional(), - relatedAction: requestGroupActionRelatedActionSchema.array().optional(), - timingDateTime: dateTimeSchema.optional(), - _timingDateTime: z.lazy(() => elementSchema).optional(), - timingAge: z.lazy(() => ageSchema).optional(), - timingPeriod: z.lazy(() => periodSchema).optional(), - timingDuration: z.lazy(() => durationSchema).optional(), - timingRange: z.lazy(() => rangeSchema).optional(), - timingTiming: z.lazy(() => timingSchema).optional(), - participant: z - .lazy(() => referenceSchema) + _status: z.lazy(() => elementSchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + condition: z.lazy(() => referenceSchema).optional(), + performer: z.lazy(() => referenceSchema).optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - groupingBehavior: z - .enum(['logical-group', 'sentence-group', 'visual-group']) - .optional(), - _groupingBehavior: z.lazy(() => elementSchema).optional(), - selectionBehavior: z - .enum([ - 'all', - 'all-or-none', - 'any', - 'at-most-one', - 'exactly-one', - 'one-or-more', - ]) + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), - _selectionBehavior: z.lazy(() => elementSchema).optional(), - requiredBehavior: z - .enum(['could', 'must', 'must-unless-documented']) + basis: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), - _requiredBehavior: z.lazy(() => elementSchema).optional(), - precheckBehavior: z.enum(['no', 'yes']).optional(), - _precheckBehavior: z.lazy(() => elementSchema).optional(), - cardinalityBehavior: z.enum(['multiple', 'single']).optional(), - _cardinalityBehavior: z.lazy(() => elementSchema).optional(), - resource: z.lazy(() => referenceSchema).optional(), - action: z - .lazy(() => requestGroupActionSchema) + prediction: riskAssessmentPredictionSchema.array().min(1).optional(), + mitigation: stringSchema.optional(), + _mitigation: z.lazy(() => elementSchema).optional(), + note: z + .lazy(() => annotationSchema) .array() + .min(1) .optional(), }) .refine( (value) => [ { - names: [ - 'timingDateTime', - 'timingAge', - 'timingPeriod', - 'timingDuration', - 'timingRange', - 'timingTiming', + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -25335,172 +41032,196 @@ export const requestGroupActionSchema: z.ZodType = message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) + ) as unknown as z.ZodType> -export const requestGroupActionConditionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - kind: z.enum(['applicability', 'start', 'stop']), - _kind: z.lazy(() => elementSchema).optional(), - expression: z.lazy(() => expressionSchema).optional(), - }), - ) - -export const requestGroupActionRelatedActionSchema: z.ZodType = - z.lazy(() => +export const sampledDataSchema: z.ZodType> = z.lazy( + () => z .strictObject({ - id: stringSchema.optional(), + id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - actionId: idSchema, - _actionId: z.lazy(() => elementSchema).optional(), - relationship: z.enum([ - 'after', - 'after-end', - 'after-start', - 'before', - 'before-end', - 'before-start', - 'concurrent', - 'concurrent-with-end', - 'concurrent-with-start', - ]), - _relationship: z.lazy(() => elementSchema).optional(), - offsetDuration: z.lazy(() => durationSchema).optional(), - offsetRange: z.lazy(() => rangeSchema).optional(), + origin: z.lazy(() => quantitySchema), + period: decimalSchema.optional(), + _period: z.lazy(() => elementSchema).optional(), + factor: decimalSchema.optional(), + _factor: z.lazy(() => elementSchema).optional(), + lowerLimit: decimalSchema.optional(), + _lowerLimit: z.lazy(() => elementSchema).optional(), + upperLimit: decimalSchema.optional(), + _upperLimit: z.lazy(() => elementSchema).optional(), + dimensions: positiveIntSchema.optional(), + _dimensions: z.lazy(() => elementSchema).optional(), + data: stringSchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'period', shadow: '_period' }, + { name: 'dimensions', shadow: '_dimensions' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [{ names: ['offsetDuration', 'offsetRange'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> + +export const scheduleSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Schedule'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + active: booleanSchema.optional(), + _active: z.lazy(() => elementSchema).optional(), + serviceCategory: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + serviceType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + actor: z + .lazy(() => referenceSchema) + .array() + .min(1), + planningHorizon: z.lazy(() => periodSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const searchParameterComponentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + definition: canonicalSchema.optional(), + _definition: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'definition', shadow: '_definition' }, + { name: 'expression', shadow: '_expression' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const requestGroupSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('RequestGroup'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - replaces: z - .lazy(() => referenceSchema) - .array() - .optional(), - groupIdentifier: z.lazy(() => identifierSchema).optional(), - status: z.enum([ - 'active', - 'completed', - 'draft', - 'entered-in-error', - 'on-hold', - 'revoked', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - intent: z.enum([ - 'directive', - 'filler-order', - 'instance-order', - 'option', - 'order', - 'original-order', - 'plan', - 'proposal', - 'reflex-order', - ]), - _intent: z.lazy(() => elementSchema).optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: z.lazy(() => elementSchema).optional(), - author: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - action: requestGroupActionSchema.array().optional(), - }), -) - -export const researchDefinitionSchema: z.ZodType = +export const searchParameterSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('ResearchDefinition'), + resourceType: z.literal('SearchParameter'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -25512,37 +41233,30 @@ export const researchDefinitionSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), url: uriSchema.optional(), _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), version: stringSchema.optional(), _version: z.lazy(() => elementSchema).optional(), name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - shortTitle: stringSchema.optional(), - _shortTitle: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), + derivedFrom: canonicalSchema.optional(), + _derivedFrom: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), _status: z.lazy(() => elementSchema).optional(), experimental: booleanSchema.optional(), _experimental: z.lazy(() => elementSchema).optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), date: dateTimeSchema.optional(), _date: z.lazy(() => elementSchema).optional(), publisher: stringSchema.optional(), @@ -25550,209 +41264,493 @@ export const researchDefinitionSchema: z.ZodType = contact: z .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - comment: stringSchema.array().optional(), - _comment: z - .lazy(() => elementSchema) - .array() - .optional(), useContext: z .lazy(() => usageContextSchema) .array() + .min(1) .optional(), jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), purpose: markdownSchema.optional(), _purpose: z.lazy(() => elementSchema).optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - author: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - editor: z - .lazy(() => contactDetailSchema) + code: codeSchema.optional(), + _code: z.lazy(() => elementSchema).optional(), + base: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .nullable() .array() + .min(1) .optional(), - reviewer: z - .lazy(() => contactDetailSchema) + _base: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - endorser: z - .lazy(() => contactDetailSchema) - .array() + type: z + .enum([ + 'composite', + 'date', + 'number', + 'quantity', + 'reference', + 'special', + 'string', + 'token', + 'uri', + ]) .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) + _type: z.lazy(() => elementSchema).optional(), + expression: stringSchema.optional(), + _expression: z.lazy(() => elementSchema).optional(), + xpath: stringSchema.optional(), + _xpath: z.lazy(() => elementSchema).optional(), + xpathUsage: z + .enum(['distance', 'nearby', 'normal', 'other', 'phonetic']) + .optional(), + _xpathUsage: z.lazy(() => elementSchema).optional(), + target: z + .enum([ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'DomainResource', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', + ]) + .nullable() .array() + .min(1) .optional(), - library: canonicalSchema.array().optional(), - _library: z + _target: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - population: z.lazy(() => referenceSchema), - exposure: z.lazy(() => referenceSchema).optional(), - exposureAlternative: z.lazy(() => referenceSchema).optional(), - outcome: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['subjectCodeableConcept', 'subjectReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const researchElementDefinitionCharacteristicSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) + multipleOr: booleanSchema.optional(), + _multipleOr: z.lazy(() => elementSchema).optional(), + multipleAnd: booleanSchema.optional(), + _multipleAnd: z.lazy(() => elementSchema).optional(), + comparator: z + .enum(['ap', 'eb', 'eq', 'ge', 'gt', 'le', 'lt', 'ne', 'sa']) + .nullable() .array() + .min(1) .optional(), - definitionCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - definitionCanonical: canonicalSchema.optional(), - _definitionCanonical: z.lazy(() => elementSchema).optional(), - definitionExpression: z.lazy(() => expressionSchema).optional(), - definitionDataRequirement: z - .lazy(() => dataRequirementSchema) - .optional(), - usageContext: z - .lazy(() => usageContextSchema) + _comparator: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - exclude: booleanSchema.optional(), - _exclude: z.lazy(() => elementSchema).optional(), - unitOfMeasure: z.lazy(() => codeableConceptSchema).optional(), - studyEffectiveDescription: stringSchema.optional(), - _studyEffectiveDescription: z.lazy(() => elementSchema).optional(), - studyEffectiveDateTime: dateTimeSchema.optional(), - _studyEffectiveDateTime: z.lazy(() => elementSchema).optional(), - studyEffectivePeriod: z.lazy(() => periodSchema).optional(), - studyEffectiveDuration: z.lazy(() => durationSchema).optional(), - studyEffectiveTiming: z.lazy(() => timingSchema).optional(), - studyEffectiveTimeFromStart: z.lazy(() => durationSchema).optional(), - studyEffectiveGroupMeasure: z + modifier: z .enum([ - 'mean', - 'mean-of-mean', - 'mean-of-median', - 'median', - 'median-of-mean', - 'median-of-median', + 'above', + 'below', + 'contains', + 'exact', + 'identifier', + 'in', + 'missing', + 'not', + 'not-in', + 'ofType', + 'text', + 'type', ]) + .nullable() + .array() + .min(1) .optional(), - _studyEffectiveGroupMeasure: z.lazy(() => elementSchema).optional(), - participantEffectiveDescription: stringSchema.optional(), - _participantEffectiveDescription: z + _modifier: z .lazy(() => elementSchema) + .nullable() + .array() + .min(1) .optional(), - participantEffectiveDateTime: dateTimeSchema.optional(), - _participantEffectiveDateTime: z.lazy(() => elementSchema).optional(), - participantEffectivePeriod: z.lazy(() => periodSchema).optional(), - participantEffectiveDuration: z.lazy(() => durationSchema).optional(), - participantEffectiveTiming: z.lazy(() => timingSchema).optional(), - participantEffectiveTimeFromStart: z - .lazy(() => durationSchema) - .optional(), - participantEffectiveGroupMeasure: z - .enum([ - 'mean', - 'mean-of-mean', - 'mean-of-median', - 'median', - 'median-of-mean', - 'median-of-median', - ]) - .optional(), - _participantEffectiveGroupMeasure: z + chain: stringSchema.nullable().array().min(1).optional(), + _chain: z .lazy(() => elementSchema) + .nullable() + .array() + .min(1) .optional(), + component: searchParameterComponentSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + { name: 'description', shadow: '_description' }, + { name: 'code', shadow: '_code' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'base', shadow: '_base', required: true }, + { name: 'target', shadow: '_target', required: false }, + { name: 'comparator', shadow: '_comparator', required: false }, + { name: 'modifier', shadow: '_modifier', required: false }, + { name: 'chain', shadow: '_chain', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } }) .refine( - (value) => - [ - { - names: [ - 'definitionCodeableConcept', - 'definitionCanonical', - 'definitionExpression', - 'definitionDataRequirement', - ], - required: true, - }, - { - names: [ - 'studyEffectiveDateTime', - 'studyEffectivePeriod', - 'studyEffectiveDuration', - 'studyEffectiveTiming', - ], - required: false, - }, - { - names: [ - 'participantEffectiveDateTime', - 'participantEffectivePeriod', - 'participantEffectiveDuration', - 'participantEffectiveTiming', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const researchElementDefinitionSchema: z.ZodType = +export const serviceRequestSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('ResearchElementDefinition'), + resourceType: z.literal('ServiceRequest'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -25764,120 +41762,199 @@ export const researchElementDefinitionSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - shortTitle: stringSchema.optional(), - _shortTitle: z.lazy(() => elementSchema).optional(), - subtitle: stringSchema.optional(), - _subtitle: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - subjectCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - subjectReference: z.lazy(() => referenceSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) + instantiatesCanonical: canonicalSchema + .nullable() .array() + .min(1) .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - comment: stringSchema.array().optional(), - _comment: z + _instantiatesCanonical: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - useContext: z - .lazy(() => usageContextSchema) + instantiatesUri: uriSchema.nullable().array().min(1).optional(), + _instantiatesUri: z + .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - jurisdiction: z + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + replaces: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + requisition: z.lazy(() => identifierSchema).optional(), + status: z + .enum([ + 'active', + 'completed', + 'draft', + 'entered-in-error', + 'on-hold', + 'revoked', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + intent: z + .enum([ + 'directive', + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + ]) + .optional(), + _intent: z.lazy(() => elementSchema).optional(), + category: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - usage: stringSchema.optional(), - _usage: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - approvalDate: dateSchema.optional(), - _approvalDate: z.lazy(() => elementSchema).optional(), - lastReviewDate: dateSchema.optional(), - _lastReviewDate: z.lazy(() => elementSchema).optional(), - effectivePeriod: z.lazy(() => periodSchema).optional(), - topic: z + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + doNotPerform: booleanSchema.optional(), + _doNotPerform: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + orderDetail: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - author: z - .lazy(() => contactDetailSchema) + quantityQuantity: z.lazy(() => quantitySchema).optional(), + quantityRatio: z.lazy(() => ratioSchema).optional(), + quantityRange: z.lazy(() => rangeSchema).optional(), + subject: z.lazy(() => referenceSchema), + encounter: z.lazy(() => referenceSchema).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + asNeededBoolean: booleanSchema.optional(), + _asNeededBoolean: z.lazy(() => elementSchema).optional(), + asNeededCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performerType: z.lazy(() => codeableConceptSchema).optional(), + performer: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - editor: z - .lazy(() => contactDetailSchema) + locationCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - reviewer: z - .lazy(() => contactDetailSchema) + locationReference: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - endorser: z - .lazy(() => contactDetailSchema) + reasonCode: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) + reasonReference: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - library: canonicalSchema.array().optional(), - _library: z - .lazy(() => elementSchema) + insurance: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - type: z.enum(['exposure', 'outcome', 'population']), - _type: z.lazy(() => elementSchema).optional(), - variableType: z - .enum(['continuous', 'descriptive', 'dichotomous']) + supportingInfo: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), - _variableType: z.lazy(() => elementSchema).optional(), - characteristic: researchElementDefinitionCharacteristicSchema + specimen: z + .lazy(() => referenceSchema) .array() - .min(1), + .min(1) + .optional(), + bodySite: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + patientInstruction: stringSchema.optional(), + _patientInstruction: z.lazy(() => elementSchema).optional(), + relevantHistory: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), }) .refine( (value) => [ { - names: ['subjectCodeableConcept', 'subjectReference'], + alternatives: [ + ['quantityQuantity'], + ['quantityRatio'], + ['quantityRange'], + ], + required: false, + }, + { + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], + ['occurrenceTiming'], + ], + required: false, + }, + { + alternatives: [ + ['asNeededBoolean', '_asNeededBoolean'], + ['asNeededCodeableConcept'], + ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -25885,439 +41962,138 @@ export const researchElementDefinitionSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - }), -) - -export const researchStudyObjectiveSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - }), - ) - -export const researchStudySchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('ResearchStudy'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - protocol: z - .lazy(() => referenceSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'active', - 'administratively-completed', - 'approved', - 'closed-to-accrual', - 'closed-to-accrual-and-intervention', - 'completed', - 'disapproved', - 'in-review', - 'temporarily-closed-to-accrual', - 'temporarily-closed-to-accrual-and-intervention', - 'withdrawn', - ]), - _status: z.lazy(() => elementSchema).optional(), - primaryPurposeType: z.lazy(() => codeableConceptSchema).optional(), - phase: z.lazy(() => codeableConceptSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - focus: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - condition: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - relatedArtifact: z - .lazy(() => relatedArtifactSchema) - .array() - .optional(), - keyword: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - location: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - enrollment: z - .lazy(() => referenceSchema) - .array() - .optional(), - period: z.lazy(() => periodSchema).optional(), - sponsor: z.lazy(() => referenceSchema).optional(), - principalInvestigator: z.lazy(() => referenceSchema).optional(), - site: z - .lazy(() => referenceSchema) - .array() - .optional(), - reasonStopped: z.lazy(() => codeableConceptSchema).optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - arm: researchStudyArmSchema.array().optional(), - objective: researchStudyObjectiveSchema.array().optional(), - }), -) - -export const researchSubjectSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('ResearchSubject'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum([ - 'candidate', - 'eligible', - 'follow-up', - 'ineligible', - 'not-registered', - 'off-study', - 'on-study', - 'on-study-intervention', - 'on-study-observation', - 'pending-on-study', - 'potential-candidate', - 'screening', - 'withdrawn', - ]), - _status: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - study: z.lazy(() => referenceSchema), - individual: z.lazy(() => referenceSchema), - assignedArm: stringSchema.optional(), - _assignedArm: z.lazy(() => elementSchema).optional(), - actualArm: stringSchema.optional(), - _actualArm: z.lazy(() => elementSchema).optional(), - consent: z.lazy(() => referenceSchema).optional(), - }), -) - -export const riskAssessmentPredictionSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - outcome: z.lazy(() => codeableConceptSchema).optional(), - probabilityDecimal: decimalSchema.optional(), - _probabilityDecimal: z.lazy(() => elementSchema).optional(), - probabilityRange: z.lazy(() => rangeSchema).optional(), - qualitativeRisk: z.lazy(() => codeableConceptSchema).optional(), - relativeRisk: decimalSchema.optional(), - _relativeRisk: z.lazy(() => elementSchema).optional(), - whenPeriod: z.lazy(() => periodSchema).optional(), - whenRange: z.lazy(() => rangeSchema).optional(), - rationale: stringSchema.optional(), - _rationale: z.lazy(() => elementSchema).optional(), + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'intent', shadow: '_intent' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) - .refine( - (value) => - [ - { - names: ['probabilityDecimal', 'probabilityRange'], - required: false, - }, - { names: ['whenPeriod', 'whenRange'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + .superRefine((value, context) => { + for (const group of [ + { + name: 'instantiatesCanonical', + shadow: '_instantiatesCanonical', + required: false, + }, + { + name: 'instantiatesUri', + shadow: '_instantiatesUri', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) + ) as unknown as z.ZodType> -export const riskAssessmentSchema: z.ZodType = z.lazy(() => +export const signatureSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('RiskAssessment'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), + id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - basedOn: z.lazy(() => referenceSchema).optional(), - parent: z.lazy(() => referenceSchema).optional(), - status: z.enum([ - 'amended', - 'cancelled', - 'corrected', - 'entered-in-error', - 'final', - 'preliminary', - 'registered', - 'unknown', - ]), - _status: z.lazy(() => elementSchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - condition: z.lazy(() => referenceSchema).optional(), - performer: z.lazy(() => referenceSchema).optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - basis: z - .lazy(() => referenceSchema) - .array() - .optional(), - prediction: riskAssessmentPredictionSchema.array().optional(), - mitigation: stringSchema.optional(), - _mitigation: z.lazy(() => elementSchema).optional(), - note: z - .lazy(() => annotationSchema) + type: z + .lazy(() => codingSchema) .array() - .optional(), + .min(1), + when: instantSchema.optional(), + _when: z.lazy(() => elementSchema).optional(), + who: z.lazy(() => referenceSchema), + onBehalfOf: z.lazy(() => referenceSchema).optional(), + targetFormat: codeSchema.optional(), + _targetFormat: z.lazy(() => elementSchema).optional(), + sigFormat: codeSchema.optional(), + _sigFormat: z.lazy(() => elementSchema).optional(), + data: base64BinarySchema.optional(), + _data: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'when', shadow: '_when' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: ['occurrenceDateTime', 'occurrencePeriod'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const sampledDataSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - origin: z.lazy(() => quantitySchema), - period: decimalSchema, - _period: z.lazy(() => elementSchema).optional(), - factor: decimalSchema.optional(), - _factor: z.lazy(() => elementSchema).optional(), - lowerLimit: decimalSchema.optional(), - _lowerLimit: z.lazy(() => elementSchema).optional(), - upperLimit: decimalSchema.optional(), - _upperLimit: z.lazy(() => elementSchema).optional(), - dimensions: positiveIntSchema, - _dimensions: z.lazy(() => elementSchema).optional(), - data: stringSchema.optional(), - _data: z.lazy(() => elementSchema).optional(), - }), -) - -export const scheduleSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Schedule'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - active: booleanSchema.optional(), - _active: z.lazy(() => elementSchema).optional(), - serviceCategory: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - serviceType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - specialty: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - actor: z - .lazy(() => referenceSchema) - .array() - .min(1), - planningHorizon: z.lazy(() => periodSchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - }), -) - -export const searchParameterComponentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - definition: canonicalSchema, - _definition: z.lazy(() => elementSchema).optional(), - expression: stringSchema, - _expression: z.lazy(() => elementSchema).optional(), - }), - ) +) as unknown as z.ZodType> -export const searchParameterSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - resourceType: z.literal('SearchParameter'), +export const slotSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Slot'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -26329,419 +42105,263 @@ export const searchParameterSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema, - _url: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - derivedFrom: canonicalSchema.optional(), - _derivedFrom: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema, - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - jurisdiction: z + serviceCategory: z .lazy(() => codeableConceptSchema) .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - code: codeSchema, - _code: z.lazy(() => elementSchema).optional(), - base: z - .enum([ - 'Account', - 'ActivityDefinition', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RegulatedAuthorization', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', - ]) - .array() - .min(1), - _base: z - .lazy(() => elementSchema) + .min(1) + .optional(), + serviceType: z + .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - type: z.enum([ - 'composite', - 'date', - 'number', - 'quantity', - 'reference', - 'special', - 'string', - 'token', - 'uri', - ]), - _type: z.lazy(() => elementSchema).optional(), - expression: stringSchema.optional(), - _expression: z.lazy(() => elementSchema).optional(), - xpath: stringSchema.optional(), - _xpath: z.lazy(() => elementSchema).optional(), - xpathUsage: z - .enum(['distance', 'nearby', 'normal', 'other', 'phonetic']) + specialty: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) .optional(), - _xpathUsage: z.lazy(() => elementSchema).optional(), - target: z + appointmentType: z.lazy(() => codeableConceptSchema).optional(), + schedule: z.lazy(() => referenceSchema), + status: z .enum([ - 'Account', - 'ActivityDefinition', - 'AdministrableProductDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Citation', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'ClinicalUseDefinition', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceReport', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'Ingredient', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'ManufacturedItemDefinition', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProductDefinition', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'NutritionProduct', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'PackagedProductDefinition', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RegulatedAuthorization', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'SubscriptionStatus', - 'SubscriptionTopic', - 'Substance', - 'SubstanceDefinition', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', + 'busy', + 'busy-tentative', + 'busy-unavailable', + 'entered-in-error', + 'free', ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + start: instantSchema.optional(), + _start: z.lazy(() => elementSchema).optional(), + end: instantSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + overbooked: booleanSchema.optional(), + _overbooked: z.lazy(() => elementSchema).optional(), + comment: stringSchema.optional(), + _comment: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'start', shadow: '_start' }, + { name: 'end', shadow: '_end' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const specimenCollectionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - _target: z - .lazy(() => elementSchema) + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + collector: z.lazy(() => referenceSchema).optional(), + collectedDateTime: dateTimeSchema.optional(), + _collectedDateTime: z.lazy(() => elementSchema).optional(), + collectedPeriod: z.lazy(() => periodSchema).optional(), + duration: z.lazy(() => durationSchema).optional(), + quantity: z.lazy(() => quantitySchema).optional(), + method: z.lazy(() => codeableConceptSchema).optional(), + bodySite: z.lazy(() => codeableConceptSchema).optional(), + fastingStatusCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + fastingStatusDuration: z.lazy(() => durationSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['collectedDateTime', '_collectedDateTime'], + ['collectedPeriod'], + ], + required: false, + }, + { + alternatives: [ + ['fastingStatusCodeableConcept'], + ['fastingStatusDuration'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const specimenProcessingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - multipleOr: booleanSchema.optional(), - _multipleOr: z.lazy(() => elementSchema).optional(), - multipleAnd: booleanSchema.optional(), - _multipleAnd: z.lazy(() => elementSchema).optional(), - comparator: z - .enum(['ap', 'eb', 'eq', 'ge', 'gt', 'le', 'lt', 'ne', 'sa']) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - _comparator: z - .lazy(() => elementSchema) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + procedure: z.lazy(() => codeableConceptSchema).optional(), + additive: z + .lazy(() => referenceSchema) .array() + .min(1) .optional(), - modifier: z - .enum([ - 'above', - 'below', - 'contains', - 'exact', - 'identifier', - 'in', - 'missing', - 'not', - 'not-in', - 'ofType', - 'text', - 'type', - ]) + timeDateTime: dateTimeSchema.optional(), + _timeDateTime: z.lazy(() => elementSchema).optional(), + timePeriod: z.lazy(() => periodSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['timeDateTime', '_timeDateTime'], ['timePeriod']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const specimenContainerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - _modifier: z - .lazy(() => elementSchema) + modifierExtension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - chain: stringSchema.array().optional(), - _chain: z - .lazy(() => elementSchema) + identifier: z + .lazy(() => identifierSchema) .array() + .min(1) .optional(), - component: searchParameterComponentSchema.array().optional(), - }), -) + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + capacity: z.lazy(() => quantitySchema).optional(), + specimenQuantity: z.lazy(() => quantitySchema).optional(), + additiveCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + additiveReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['additiveCodeableConcept'], ['additiveReference']], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const serviceRequestSchema: z.ZodType = z.lazy(() => +export const specimenSchema: z.ZodType> = z.lazy(() => z .strictObject({ - resourceType: z.literal('ServiceRequest'), + resourceType: z.literal('Specimen'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), meta: z.lazy(() => metaSchema).optional(), @@ -26753,463 +42373,87 @@ export const serviceRequestSchema: z.ZodType = z.lazy(() => contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), - instantiatesCanonical: canonicalSchema.array().optional(), - _instantiatesCanonical: z - .lazy(() => elementSchema) - .array() - .optional(), - instantiatesUri: uriSchema.array().optional(), - _instantiatesUri: z - .lazy(() => elementSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - replaces: z - .lazy(() => referenceSchema) - .array() + accessionIdentifier: z.lazy(() => identifierSchema).optional(), + status: z + .enum([ + 'available', + 'entered-in-error', + 'unavailable', + 'unsatisfactory', + ]) .optional(), - requisition: z.lazy(() => identifierSchema).optional(), - status: z.enum([ - 'active', - 'completed', - 'draft', - 'entered-in-error', - 'on-hold', - 'revoked', - 'unknown', - ]), _status: z.lazy(() => elementSchema).optional(), - intent: z.enum([ - 'directive', - 'filler-order', - 'instance-order', - 'option', - 'order', - 'original-order', - 'plan', - 'proposal', - 'reflex-order', - ]), - _intent: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - doNotPerform: booleanSchema.optional(), - _doNotPerform: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - orderDetail: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - quantityQuantity: z.lazy(() => quantitySchema).optional(), - quantityRatio: z.lazy(() => ratioSchema).optional(), - quantityRange: z.lazy(() => rangeSchema).optional(), - subject: z.lazy(() => referenceSchema), - encounter: z.lazy(() => referenceSchema).optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - occurrenceTiming: z.lazy(() => timingSchema).optional(), - asNeededBoolean: booleanSchema.optional(), - _asNeededBoolean: z.lazy(() => elementSchema).optional(), - asNeededCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: z.lazy(() => elementSchema).optional(), - requester: z.lazy(() => referenceSchema).optional(), - performerType: z.lazy(() => codeableConceptSchema).optional(), - performer: z - .lazy(() => referenceSchema) - .array() - .optional(), - locationCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - locationReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - insurance: z - .lazy(() => referenceSchema) - .array() - .optional(), - supportingInfo: z + type: z.lazy(() => codeableConceptSchema).optional(), + subject: z.lazy(() => referenceSchema).optional(), + receivedTime: dateTimeSchema.optional(), + _receivedTime: z.lazy(() => elementSchema).optional(), + parent: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - specimen: z + request: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - bodySite: z + collection: specimenCollectionSchema.optional(), + processing: specimenProcessingSchema.array().min(1).optional(), + container: specimenContainerSchema.array().min(1).optional(), + condition: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), note: z .lazy(() => annotationSchema) .array() - .optional(), - patientInstruction: stringSchema.optional(), - _patientInstruction: z.lazy(() => elementSchema).optional(), - relevantHistory: z - .lazy(() => referenceSchema) - .array() + .min(1) .optional(), }) .refine( - (value) => - [ - { - names: ['quantityQuantity', 'quantityRatio', 'quantityRange'], - required: false, - }, - { - names: [ - 'occurrenceDateTime', - 'occurrencePeriod', - 'occurrenceTiming', - ], - required: false, - }, - { - names: ['asNeededBoolean', 'asNeededCodeableConcept'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const signatureSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z - .lazy(() => codingSchema) - .array() - .min(1), - when: instantSchema, - _when: z.lazy(() => elementSchema).optional(), - who: z.lazy(() => referenceSchema), - onBehalfOf: z.lazy(() => referenceSchema).optional(), - targetFormat: codeSchema.optional(), - _targetFormat: z.lazy(() => elementSchema).optional(), - sigFormat: codeSchema.optional(), - _sigFormat: z.lazy(() => elementSchema).optional(), - data: base64BinarySchema.optional(), - _data: z.lazy(() => elementSchema).optional(), - }), -) - -export const slotSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Slot'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - serviceCategory: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - serviceType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - specialty: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - appointmentType: z.lazy(() => codeableConceptSchema).optional(), - schedule: z.lazy(() => referenceSchema), - status: z.enum([ - 'busy', - 'busy-tentative', - 'busy-unavailable', - 'entered-in-error', - 'free', - ]), - _status: z.lazy(() => elementSchema).optional(), - start: instantSchema, - _start: z.lazy(() => elementSchema).optional(), - end: instantSchema, - _end: z.lazy(() => elementSchema).optional(), - overbooked: booleanSchema.optional(), - _overbooked: z.lazy(() => elementSchema).optional(), - comment: stringSchema.optional(), - _comment: z.lazy(() => elementSchema).optional(), - }), -) - -export const specimenCollectionSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - collector: z.lazy(() => referenceSchema).optional(), - collectedDateTime: dateTimeSchema.optional(), - _collectedDateTime: z.lazy(() => elementSchema).optional(), - collectedPeriod: z.lazy(() => periodSchema).optional(), - duration: z.lazy(() => durationSchema).optional(), - quantity: z.lazy(() => quantitySchema).optional(), - method: z.lazy(() => codeableConceptSchema).optional(), - bodySite: z.lazy(() => codeableConceptSchema).optional(), - fastingStatusCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - fastingStatusDuration: z.lazy(() => durationSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['collectedDateTime', 'collectedPeriod'], - required: false, - }, - { - names: ['fastingStatusCodeableConcept', 'fastingStatusDuration'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) - -export const specimenProcessingSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - procedure: z.lazy(() => codeableConceptSchema).optional(), - additive: z - .lazy(() => referenceSchema) - .array() - .optional(), - timeDateTime: dateTimeSchema.optional(), - _timeDateTime: z.lazy(() => elementSchema).optional(), - timePeriod: z.lazy(() => periodSchema).optional(), - }) - .refine( - (value) => - [{ names: ['timeDateTime', 'timePeriod'], required: false }].every( - (group) => { - const present = group.names.filter( - (name) => - (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }, - ), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) +) as unknown as z.ZodType> -export const specimenContainerSchema: z.ZodType = z.lazy( - () => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - capacity: z.lazy(() => quantitySchema).optional(), - specimenQuantity: z.lazy(() => quantitySchema).optional(), - additiveCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - additiveReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['additiveCodeableConcept', 'additiveReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const specimenSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Specimen'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - accessionIdentifier: z.lazy(() => identifierSchema).optional(), - status: z - .enum(['available', 'entered-in-error', 'unavailable', 'unsatisfactory']) - .optional(), - _status: z.lazy(() => elementSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - subject: z.lazy(() => referenceSchema).optional(), - receivedTime: dateTimeSchema.optional(), - _receivedTime: z.lazy(() => elementSchema).optional(), - parent: z - .lazy(() => referenceSchema) - .array() - .optional(), - request: z - .lazy(() => referenceSchema) - .array() - .optional(), - collection: specimenCollectionSchema.optional(), - processing: specimenProcessingSchema.array().optional(), - container: specimenContainerSchema.array().optional(), - condition: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - }), -) - -export const specimenDefinitionTypeTestedSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const specimenDefinitionTypeTestedSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), isDerived: booleanSchema.optional(), _isDerived: z.lazy(() => elementSchema).optional(), type: z.lazy(() => codeableConceptSchema).optional(), - preference: z.enum(['alternate', 'preferred']), + preference: z.enum(['alternate', 'preferred']).optional(), _preference: z.lazy(() => elementSchema).optional(), container: specimenDefinitionTypeTestedContainerSchema.optional(), requirement: stringSchema.optional(), @@ -27218,118 +42462,176 @@ export const specimenDefinitionTypeTestedSchema: z.ZodType codeableConceptSchema) .array() + .min(1) + .optional(), + handling: specimenDefinitionTypeTestedHandlingSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'preference', shadow: '_preference' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const specimenDefinitionTypeTestedContainerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) .optional(), - handling: specimenDefinitionTypeTestedHandlingSchema.array().optional(), - }), - ) - -export const specimenDefinitionTypeTestedContainerSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - material: z.lazy(() => codeableConceptSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - cap: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - capacity: z.lazy(() => quantitySchema).optional(), - minimumVolumeQuantity: z.lazy(() => quantitySchema).optional(), - minimumVolumeString: stringSchema.optional(), - _minimumVolumeString: z.lazy(() => elementSchema).optional(), - additive: specimenDefinitionTypeTestedContainerAdditiveSchema - .array() - .optional(), - preparation: stringSchema.optional(), - _preparation: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['minimumVolumeQuantity', 'minimumVolumeString'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( + material: z.lazy(() => codeableConceptSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + cap: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + capacity: z.lazy(() => quantitySchema).optional(), + minimumVolumeQuantity: z.lazy(() => quantitySchema).optional(), + minimumVolumeString: stringSchema.optional(), + _minimumVolumeString: z.lazy(() => elementSchema).optional(), + additive: specimenDefinitionTypeTestedContainerAdditiveSchema + .array() + .min(1) + .optional(), + preparation: stringSchema.optional(), + _preparation: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['minimumVolumeQuantity'], + ['minimumVolumeString', '_minimumVolumeString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const specimenDefinitionTypeTestedHandlingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const specimenDefinitionTypeTestedHandlingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), temperatureQualifier: z.lazy(() => codeableConceptSchema).optional(), temperatureRange: z.lazy(() => rangeSchema).optional(), maxDuration: z.lazy(() => durationSchema).optional(), instruction: stringSchema.optional(), _instruction: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const specimenDefinitionTypeTestedContainerAdditiveSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - additiveCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - additiveReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['additiveCodeableConcept', 'additiveReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( +export const specimenDefinitionTypeTestedContainerAdditiveSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + additiveCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + additiveReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['additiveCodeableConcept'], ['additiveReference']], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const specimenDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const specimenDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('SpecimenDefinition'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -27342,44 +42644,57 @@ export const specimenDefinitionSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z.lazy(() => identifierSchema).optional(), typeCollected: z.lazy(() => codeableConceptSchema).optional(), patientPreparation: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), timeAspect: stringSchema.optional(), _timeAspect: z.lazy(() => elementSchema).optional(), collection: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - typeTested: specimenDefinitionTypeTestedSchema.array().optional(), - }), - ) + typeTested: specimenDefinitionTypeTestedSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureDefinitionMappingSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureDefinitionMappingSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identity: idSchema, + identity: idSchema.optional(), _identity: z.lazy(() => elementSchema).optional(), uri: uriSchema.optional(), _uri: z.lazy(() => elementSchema).optional(), @@ -27387,69 +42702,134 @@ export const structureDefinitionMappingSchema: z.ZodType elementSchema).optional(), comment: stringSchema.optional(), _comment: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'identity', shadow: '_identity' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureDefinitionContextSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureDefinitionContextSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum(['element', 'extension', 'fhirpath']), + type: z.enum(['element', 'extension', 'fhirpath']).optional(), _type: z.lazy(() => elementSchema).optional(), - expression: stringSchema, + expression: stringSchema.optional(), _expression: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'type', shadow: '_type' }, + { name: 'expression', shadow: '_expression' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureDefinitionSnapshotSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureDefinitionSnapshotSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), element: z .lazy(() => elementDefinitionSchema) .array() .min(1), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureDefinitionDifferentialSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureDefinitionDifferentialSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), element: z .lazy(() => elementDefinitionSchema) .array() .min(1), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('StructureDefinition'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -27462,28 +42842,32 @@ export const structureDefinitionSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema, + url: uriSchema.optional(), _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), version: stringSchema.optional(), _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, + name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), title: stringSchema.optional(), _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), _status: z.lazy(() => elementSchema).optional(), experimental: booleanSchema.optional(), _experimental: z.lazy(() => elementSchema).optional(), @@ -27494,16 +42878,19 @@ export const structureDefinitionSchema: z.ZodType = contact: z .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), useContext: z .lazy(() => usageContextSchema) .array() + .min(1) .optional(), jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), purpose: markdownSchema.optional(), _purpose: z.lazy(() => elementSchema).optional(), @@ -27512,6 +42899,7 @@ export const structureDefinitionSchema: z.ZodType = keyword: z .lazy(() => codingSchema) .array() + .min(1) .optional(), fhirVersion: z .enum([ @@ -27545,18 +42933,22 @@ export const structureDefinitionSchema: z.ZodType = ]) .optional(), _fhirVersion: z.lazy(() => elementSchema).optional(), - mapping: structureDefinitionMappingSchema.array().optional(), - kind: z.enum(['complex-type', 'logical', 'primitive-type', 'resource']), + mapping: structureDefinitionMappingSchema.array().min(1).optional(), + kind: z + .enum(['complex-type', 'logical', 'primitive-type', 'resource']) + .optional(), _kind: z.lazy(() => elementSchema).optional(), - abstract: booleanSchema, + abstract: booleanSchema.optional(), _abstract: z.lazy(() => elementSchema).optional(), - context: structureDefinitionContextSchema.array().optional(), - contextInvariant: stringSchema.array().optional(), + context: structureDefinitionContextSchema.array().min(1).optional(), + contextInvariant: stringSchema.nullable().array().min(1).optional(), _contextInvariant: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - type: uriSchema, + type: uriSchema.optional(), _type: z.lazy(() => elementSchema).optional(), baseDefinition: canonicalSchema.optional(), _baseDefinition: z.lazy(() => elementSchema).optional(), @@ -27564,303 +42956,514 @@ export const structureDefinitionSchema: z.ZodType = _derivation: z.lazy(() => elementSchema).optional(), snapshot: structureDefinitionSnapshotSchema.optional(), differential: structureDefinitionDifferentialSchema.optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + { name: 'kind', shadow: '_kind' }, + { name: 'abstract', shadow: '_abstract' }, + { name: 'type', shadow: '_type' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'contextInvariant', + shadow: '_contextInvariant', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureMapStructureSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureMapStructureSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: canonicalSchema, + url: canonicalSchema.optional(), _url: z.lazy(() => elementSchema).optional(), - mode: z.enum(['produced', 'queried', 'source', 'target']), + mode: z.enum(['produced', 'queried', 'source', 'target']).optional(), _mode: z.lazy(() => elementSchema).optional(), alias: stringSchema.optional(), _alias: z.lazy(() => elementSchema).optional(), documentation: stringSchema.optional(), _documentation: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'mode', shadow: '_mode' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureMapGroupSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const structureMapGroupSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: idSchema, + name: idSchema.optional(), _name: z.lazy(() => elementSchema).optional(), extends: idSchema.optional(), _extends: z.lazy(() => elementSchema).optional(), - typeMode: z.enum(['none', 'type-and-types', 'types']), + typeMode: z.enum(['none', 'type-and-types', 'types']).optional(), _typeMode: z.lazy(() => elementSchema).optional(), documentation: stringSchema.optional(), _documentation: z.lazy(() => elementSchema).optional(), input: structureMapGroupInputSchema.array().min(1), rule: structureMapGroupRuleSchema.array().min(1), - }), -) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'typeMode', shadow: '_typeMode' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureMapGroupInputSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureMapGroupInputSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: idSchema, + name: idSchema.optional(), _name: z.lazy(() => elementSchema).optional(), type: stringSchema.optional(), _type: z.lazy(() => elementSchema).optional(), - mode: z.enum(['source', 'target']), + mode: z.enum(['source', 'target']).optional(), _mode: z.lazy(() => elementSchema).optional(), documentation: stringSchema.optional(), _documentation: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'name', shadow: '_name' }, + { name: 'mode', shadow: '_mode' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureMapGroupRuleSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureMapGroupRuleSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: idSchema, + name: idSchema.optional(), _name: z.lazy(() => elementSchema).optional(), source: structureMapGroupRuleSourceSchema.array().min(1), - target: structureMapGroupRuleTargetSchema.array().optional(), + target: structureMapGroupRuleTargetSchema.array().min(1).optional(), rule: z .lazy(() => structureMapGroupRuleSchema) .array() + .min(1) .optional(), - dependent: structureMapGroupRuleDependentSchema.array().optional(), + dependent: structureMapGroupRuleDependentSchema.array().min(1).optional(), documentation: stringSchema.optional(), _documentation: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureMapGroupRuleSourceSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - context: idSchema, - _context: z.lazy(() => elementSchema).optional(), - min: integerSchema.optional(), - _min: z.lazy(() => elementSchema).optional(), - max: stringSchema.optional(), - _max: z.lazy(() => elementSchema).optional(), - type: stringSchema.optional(), - _type: z.lazy(() => elementSchema).optional(), - defaultValueBase64Binary: base64BinarySchema.optional(), - _defaultValueBase64Binary: z.lazy(() => elementSchema).optional(), - defaultValueBoolean: booleanSchema.optional(), - _defaultValueBoolean: z.lazy(() => elementSchema).optional(), - defaultValueCanonical: canonicalSchema.optional(), - _defaultValueCanonical: z.lazy(() => elementSchema).optional(), - defaultValueCode: codeSchema.optional(), - _defaultValueCode: z.lazy(() => elementSchema).optional(), - defaultValueDate: dateSchema.optional(), - _defaultValueDate: z.lazy(() => elementSchema).optional(), - defaultValueDateTime: dateTimeSchema.optional(), - _defaultValueDateTime: z.lazy(() => elementSchema).optional(), - defaultValueDecimal: decimalSchema.optional(), - _defaultValueDecimal: z.lazy(() => elementSchema).optional(), - defaultValueId: idSchema.optional(), - _defaultValueId: z.lazy(() => elementSchema).optional(), - defaultValueInstant: instantSchema.optional(), - _defaultValueInstant: z.lazy(() => elementSchema).optional(), - defaultValueInteger: integerSchema.optional(), - _defaultValueInteger: z.lazy(() => elementSchema).optional(), - defaultValueMarkdown: markdownSchema.optional(), - _defaultValueMarkdown: z.lazy(() => elementSchema).optional(), - defaultValueOid: oidSchema.optional(), - _defaultValueOid: z.lazy(() => elementSchema).optional(), - defaultValuePositiveInt: positiveIntSchema.optional(), - _defaultValuePositiveInt: z.lazy(() => elementSchema).optional(), - defaultValueString: stringSchema.optional(), - _defaultValueString: z.lazy(() => elementSchema).optional(), - defaultValueTime: timeSchema.optional(), - _defaultValueTime: z.lazy(() => elementSchema).optional(), - defaultValueUnsignedInt: unsignedIntSchema.optional(), - _defaultValueUnsignedInt: z.lazy(() => elementSchema).optional(), - defaultValueUri: uriSchema.optional(), - _defaultValueUri: z.lazy(() => elementSchema).optional(), - defaultValueUrl: urlSchema.optional(), - _defaultValueUrl: z.lazy(() => elementSchema).optional(), - defaultValueUuid: uuidSchema.optional(), - _defaultValueUuid: z.lazy(() => elementSchema).optional(), - defaultValueAddress: z.lazy(() => addressSchema).optional(), - defaultValueAge: z.lazy(() => ageSchema).optional(), - defaultValueAnnotation: z.lazy(() => annotationSchema).optional(), - defaultValueAttachment: z.lazy(() => attachmentSchema).optional(), - defaultValueCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - defaultValueCoding: z.lazy(() => codingSchema).optional(), - defaultValueContactPoint: z.lazy(() => contactPointSchema).optional(), - defaultValueCount: z.lazy(() => countSchema).optional(), - defaultValueDistance: z.lazy(() => distanceSchema).optional(), - defaultValueDuration: z.lazy(() => durationSchema).optional(), - defaultValueHumanName: z.lazy(() => humanNameSchema).optional(), - defaultValueIdentifier: z.lazy(() => identifierSchema).optional(), - defaultValueMoney: z.lazy(() => moneySchema).optional(), - defaultValuePeriod: z.lazy(() => periodSchema).optional(), - defaultValueQuantity: z.lazy(() => quantitySchema).optional(), - defaultValueRange: z.lazy(() => rangeSchema).optional(), - defaultValueRatio: z.lazy(() => ratioSchema).optional(), - defaultValueReference: z.lazy(() => referenceSchema).optional(), - defaultValueSampledData: z.lazy(() => sampledDataSchema).optional(), - defaultValueSignature: z.lazy(() => signatureSchema).optional(), - defaultValueTiming: z.lazy(() => timingSchema).optional(), - defaultValueContactDetail: z.lazy(() => contactDetailSchema).optional(), - defaultValueContributor: z.lazy(() => contributorSchema).optional(), - defaultValueDataRequirement: z - .lazy(() => dataRequirementSchema) - .optional(), - defaultValueExpression: z.lazy(() => expressionSchema).optional(), - defaultValueParameterDefinition: z - .lazy(() => parameterDefinitionSchema) - .optional(), - defaultValueRelatedArtifact: z - .lazy(() => relatedArtifactSchema) - .optional(), - defaultValueTriggerDefinition: z - .lazy(() => triggerDefinitionSchema) - .optional(), - defaultValueUsageContext: z.lazy(() => usageContextSchema).optional(), - defaultValueDosage: z.lazy(() => dosageSchema).optional(), - defaultValueMeta: z.lazy(() => metaSchema).optional(), - element: stringSchema.optional(), - _element: z.lazy(() => elementSchema).optional(), - listMode: z - .enum(['first', 'last', 'not_first', 'not_last', 'only_one']) - .optional(), - _listMode: z.lazy(() => elementSchema).optional(), - variable: idSchema.optional(), - _variable: z.lazy(() => elementSchema).optional(), - condition: stringSchema.optional(), - _condition: z.lazy(() => elementSchema).optional(), - check: stringSchema.optional(), - _check: z.lazy(() => elementSchema).optional(), - logMessage: stringSchema.optional(), - _logMessage: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'defaultValueBase64Binary', - 'defaultValueBoolean', - 'defaultValueCanonical', - 'defaultValueCode', - 'defaultValueDate', - 'defaultValueDateTime', - 'defaultValueDecimal', - 'defaultValueId', - 'defaultValueInstant', - 'defaultValueInteger', - 'defaultValueMarkdown', - 'defaultValueOid', - 'defaultValuePositiveInt', - 'defaultValueString', - 'defaultValueTime', - 'defaultValueUnsignedInt', - 'defaultValueUri', - 'defaultValueUrl', - 'defaultValueUuid', - 'defaultValueAddress', - 'defaultValueAge', - 'defaultValueAnnotation', - 'defaultValueAttachment', - 'defaultValueCodeableConcept', - 'defaultValueCoding', - 'defaultValueContactPoint', - 'defaultValueCount', - 'defaultValueDistance', - 'defaultValueDuration', - 'defaultValueHumanName', - 'defaultValueIdentifier', - 'defaultValueMoney', - 'defaultValuePeriod', - 'defaultValueQuantity', - 'defaultValueRange', - 'defaultValueRatio', - 'defaultValueReference', - 'defaultValueSampledData', - 'defaultValueSignature', - 'defaultValueTiming', - 'defaultValueContactDetail', - 'defaultValueContributor', - 'defaultValueDataRequirement', - 'defaultValueExpression', - 'defaultValueParameterDefinition', - 'defaultValueRelatedArtifact', - 'defaultValueTriggerDefinition', - 'defaultValueUsageContext', - 'defaultValueDosage', - 'defaultValueMeta', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( +export const structureMapGroupRuleSourceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + context: idSchema.optional(), + _context: z.lazy(() => elementSchema).optional(), + min: integerSchema.optional(), + _min: z.lazy(() => elementSchema).optional(), + max: stringSchema.optional(), + _max: z.lazy(() => elementSchema).optional(), + type: stringSchema.optional(), + _type: z.lazy(() => elementSchema).optional(), + defaultValueBase64Binary: base64BinarySchema.optional(), + _defaultValueBase64Binary: z.lazy(() => elementSchema).optional(), + defaultValueBoolean: booleanSchema.optional(), + _defaultValueBoolean: z.lazy(() => elementSchema).optional(), + defaultValueCanonical: canonicalSchema.optional(), + _defaultValueCanonical: z.lazy(() => elementSchema).optional(), + defaultValueCode: codeSchema.optional(), + _defaultValueCode: z.lazy(() => elementSchema).optional(), + defaultValueDate: dateSchema.optional(), + _defaultValueDate: z.lazy(() => elementSchema).optional(), + defaultValueDateTime: dateTimeSchema.optional(), + _defaultValueDateTime: z.lazy(() => elementSchema).optional(), + defaultValueDecimal: decimalSchema.optional(), + _defaultValueDecimal: z.lazy(() => elementSchema).optional(), + defaultValueId: idSchema.optional(), + _defaultValueId: z.lazy(() => elementSchema).optional(), + defaultValueInstant: instantSchema.optional(), + _defaultValueInstant: z.lazy(() => elementSchema).optional(), + defaultValueInteger: integerSchema.optional(), + _defaultValueInteger: z.lazy(() => elementSchema).optional(), + defaultValueMarkdown: markdownSchema.optional(), + _defaultValueMarkdown: z.lazy(() => elementSchema).optional(), + defaultValueOid: oidSchema.optional(), + _defaultValueOid: z.lazy(() => elementSchema).optional(), + defaultValuePositiveInt: positiveIntSchema.optional(), + _defaultValuePositiveInt: z.lazy(() => elementSchema).optional(), + defaultValueString: stringSchema.optional(), + _defaultValueString: z.lazy(() => elementSchema).optional(), + defaultValueTime: timeSchema.optional(), + _defaultValueTime: z.lazy(() => elementSchema).optional(), + defaultValueUnsignedInt: unsignedIntSchema.optional(), + _defaultValueUnsignedInt: z.lazy(() => elementSchema).optional(), + defaultValueUri: uriSchema.optional(), + _defaultValueUri: z.lazy(() => elementSchema).optional(), + defaultValueUrl: urlSchema.optional(), + _defaultValueUrl: z.lazy(() => elementSchema).optional(), + defaultValueUuid: uuidSchema.optional(), + _defaultValueUuid: z.lazy(() => elementSchema).optional(), + defaultValueAddress: z.lazy(() => addressSchema).optional(), + defaultValueAge: z.lazy(() => ageSchema).optional(), + defaultValueAnnotation: z.lazy(() => annotationSchema).optional(), + defaultValueAttachment: z.lazy(() => attachmentSchema).optional(), + defaultValueCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + defaultValueCoding: z.lazy(() => codingSchema).optional(), + defaultValueContactPoint: z.lazy(() => contactPointSchema).optional(), + defaultValueCount: z.lazy(() => countSchema).optional(), + defaultValueDistance: z.lazy(() => distanceSchema).optional(), + defaultValueDuration: z.lazy(() => durationSchema).optional(), + defaultValueHumanName: z.lazy(() => humanNameSchema).optional(), + defaultValueIdentifier: z.lazy(() => identifierSchema).optional(), + defaultValueMoney: z.lazy(() => moneySchema).optional(), + defaultValuePeriod: z.lazy(() => periodSchema).optional(), + defaultValueQuantity: z.lazy(() => quantitySchema).optional(), + defaultValueRange: z.lazy(() => rangeSchema).optional(), + defaultValueRatio: z.lazy(() => ratioSchema).optional(), + defaultValueReference: z.lazy(() => referenceSchema).optional(), + defaultValueSampledData: z.lazy(() => sampledDataSchema).optional(), + defaultValueSignature: z.lazy(() => signatureSchema).optional(), + defaultValueTiming: z.lazy(() => timingSchema).optional(), + defaultValueContactDetail: z.lazy(() => contactDetailSchema).optional(), + defaultValueContributor: z.lazy(() => contributorSchema).optional(), + defaultValueDataRequirement: z + .lazy(() => dataRequirementSchema) + .optional(), + defaultValueExpression: z.lazy(() => expressionSchema).optional(), + defaultValueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + defaultValueRelatedArtifact: z + .lazy(() => relatedArtifactSchema) + .optional(), + defaultValueTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + defaultValueUsageContext: z.lazy(() => usageContextSchema).optional(), + defaultValueDosage: z.lazy(() => dosageSchema).optional(), + defaultValueMeta: z.lazy(() => metaSchema).optional(), + element: stringSchema.optional(), + _element: z.lazy(() => elementSchema).optional(), + listMode: z + .enum(['first', 'last', 'not_first', 'not_last', 'only_one']) + .optional(), + _listMode: z.lazy(() => elementSchema).optional(), + variable: idSchema.optional(), + _variable: z.lazy(() => elementSchema).optional(), + condition: stringSchema.optional(), + _condition: z.lazy(() => elementSchema).optional(), + check: stringSchema.optional(), + _check: z.lazy(() => elementSchema).optional(), + logMessage: stringSchema.optional(), + _logMessage: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['defaultValueBase64Binary', '_defaultValueBase64Binary'], + ['defaultValueBoolean', '_defaultValueBoolean'], + ['defaultValueCanonical', '_defaultValueCanonical'], + ['defaultValueCode', '_defaultValueCode'], + ['defaultValueDate', '_defaultValueDate'], + ['defaultValueDateTime', '_defaultValueDateTime'], + ['defaultValueDecimal', '_defaultValueDecimal'], + ['defaultValueId', '_defaultValueId'], + ['defaultValueInstant', '_defaultValueInstant'], + ['defaultValueInteger', '_defaultValueInteger'], + ['defaultValueMarkdown', '_defaultValueMarkdown'], + ['defaultValueOid', '_defaultValueOid'], + ['defaultValuePositiveInt', '_defaultValuePositiveInt'], + ['defaultValueString', '_defaultValueString'], + ['defaultValueTime', '_defaultValueTime'], + ['defaultValueUnsignedInt', '_defaultValueUnsignedInt'], + ['defaultValueUri', '_defaultValueUri'], + ['defaultValueUrl', '_defaultValueUrl'], + ['defaultValueUuid', '_defaultValueUuid'], + ['defaultValueAddress'], + ['defaultValueAge'], + ['defaultValueAnnotation'], + ['defaultValueAttachment'], + ['defaultValueCodeableConcept'], + ['defaultValueCoding'], + ['defaultValueContactPoint'], + ['defaultValueCount'], + ['defaultValueDistance'], + ['defaultValueDuration'], + ['defaultValueHumanName'], + ['defaultValueIdentifier'], + ['defaultValueMoney'], + ['defaultValuePeriod'], + ['defaultValueQuantity'], + ['defaultValueRange'], + ['defaultValueRatio'], + ['defaultValueReference'], + ['defaultValueSampledData'], + ['defaultValueSignature'], + ['defaultValueTiming'], + ['defaultValueContactDetail'], + ['defaultValueContributor'], + ['defaultValueDataRequirement'], + ['defaultValueExpression'], + ['defaultValueParameterDefinition'], + ['defaultValueRelatedArtifact'], + ['defaultValueTriggerDefinition'], + ['defaultValueUsageContext'], + ['defaultValueDosage'], + ['defaultValueMeta'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'context', shadow: '_context' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureMapGroupRuleTargetSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureMapGroupRuleTargetSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), context: idSchema.optional(), _context: z.lazy(() => elementSchema).optional(), @@ -27872,11 +43475,15 @@ export const structureMapGroupRuleTargetSchema: z.ZodType elementSchema).optional(), listMode: z .enum(['collate', 'first', 'last', 'share']) + .nullable() .array() + .min(1) .optional(), _listMode: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), listRuleId: idSchema.optional(), _listRuleId: z.lazy(() => elementSchema).optional(), @@ -27902,233 +43509,556 @@ export const structureMapGroupRuleTargetSchema: z.ZodType elementSchema).optional(), - parameter: structureMapGroupRuleTargetParameterSchema.array().optional(), - }), - ) + parameter: structureMapGroupRuleTargetParameterSchema + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'listMode', shadow: '_listMode', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureMapGroupRuleDependentSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const structureMapGroupRuleDependentSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: idSchema, + name: idSchema.optional(), _name: z.lazy(() => elementSchema).optional(), - variable: stringSchema.array().min(1), + variable: stringSchema.nullable().array().min(1).optional(), _variable: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'variable', shadow: '_variable', required: true }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const structureMapGroupRuleTargetParameterSchema: z.ZodType = - z.lazy(() => +export const structureMapGroupRuleTargetParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueId', '_valueId'], + ['valueString', '_valueString'], + ['valueBoolean', '_valueBoolean'], + ['valueInteger', '_valueInteger'], + ['valueDecimal', '_valueDecimal'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const structureMapSchema: z.ZodType> = z.lazy( + () => z .strictObject({ - id: stringSchema.optional(), + resourceType: z.literal('StructureMap'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - valueId: idSchema.optional(), - _valueId: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + structure: structureMapStructureSchema.array().min(1).optional(), + import: canonicalSchema.nullable().array().min(1).optional(), + _import: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + group: structureMapGroupSchema.array().min(1), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'import', shadow: '_import', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } }) .refine( - (value) => - [ - { - names: [ - 'valueId', - 'valueString', - 'valueBoolean', - 'valueInteger', - 'valueDecimal', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), + (value) => Object.keys(value as object).some((key) => key !== 'id'), { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', + message: 'ele-1: an element must have a value or children beyond id.', }, ), - ) +) as unknown as z.ZodType> -export const structureMapSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('StructureMap'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema, - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - structure: structureMapStructureSchema.array().optional(), - import: canonicalSchema.array().optional(), - _import: z - .lazy(() => elementSchema) - .array() - .optional(), - group: structureMapGroupSchema.array().min(1), - }), -) - -export const subscriptionChannelSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const subscriptionChannelSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + type: z + .enum(['email', 'message', 'rest-hook', 'sms', 'websocket']) .optional(), - type: z.enum(['email', 'message', 'rest-hook', 'sms', 'websocket']), _type: z.lazy(() => elementSchema).optional(), endpoint: urlSchema.optional(), _endpoint: z.lazy(() => elementSchema).optional(), payload: codeSchema.optional(), _payload: z.lazy(() => elementSchema).optional(), - header: stringSchema.array().optional(), + header: stringSchema.nullable().array().min(1).optional(), _header: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'header', shadow: '_header', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const subscriptionSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Subscription'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - status: z.enum(['active', 'error', 'off', 'requested']), - _status: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactPointSchema) - .array() - .optional(), - end: instantSchema.optional(), - _end: z.lazy(() => elementSchema).optional(), - reason: stringSchema, - _reason: z.lazy(() => elementSchema).optional(), - criteria: stringSchema, - _criteria: z.lazy(() => elementSchema).optional(), - error: stringSchema.optional(), - _error: z.lazy(() => elementSchema).optional(), - channel: subscriptionChannelSchema, - }), -) - -export const subscriptionStatusNotificationEventSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const subscriptionSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('Subscription'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + status: z.enum(['active', 'error', 'off', 'requested']).optional(), + _status: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactPointSchema) + .array() + .min(1) + .optional(), + end: instantSchema.optional(), + _end: z.lazy(() => elementSchema).optional(), + reason: stringSchema.optional(), + _reason: z.lazy(() => elementSchema).optional(), + criteria: stringSchema.optional(), + _criteria: z.lazy(() => elementSchema).optional(), + error: stringSchema.optional(), + _error: z.lazy(() => elementSchema).optional(), + channel: subscriptionChannelSchema, + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'reason', shadow: '_reason' }, + { name: 'criteria', shadow: '_criteria' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const subscriptionStatusNotificationEventSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - eventNumber: stringSchema, + eventNumber: stringSchema.optional(), _eventNumber: z.lazy(() => elementSchema).optional(), timestamp: instantSchema.optional(), _timestamp: z.lazy(() => elementSchema).optional(), @@ -28136,13 +44066,36 @@ export const subscriptionStatusNotificationEventSchema: z.ZodType referenceSchema) .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'eventNumber', shadow: '_eventNumber' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const subscriptionStatusSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const subscriptionStatusSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('SubscriptionStatus'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -28155,29 +44108,35 @@ export const subscriptionStatusSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), status: z.enum(['active', 'error', 'off', 'requested']).optional(), _status: z.lazy(() => elementSchema).optional(), - type: z.enum([ - 'event-notification', - 'handshake', - 'heartbeat', - 'query-event', - 'query-status', - ]), + type: z + .enum([ + 'event-notification', + 'handshake', + 'heartbeat', + 'query-event', + 'query-status', + ]) + .optional(), _type: z.lazy(() => elementSchema).optional(), eventsSinceSubscriptionStart: stringSchema.optional(), _eventsSinceSubscriptionStart: z.lazy(() => elementSchema).optional(), notificationEvent: subscriptionStatusNotificationEventSchema .array() + .min(1) .optional(), subscription: z.lazy(() => referenceSchema), topic: canonicalSchema.optional(), @@ -28185,78 +44144,204 @@ export const subscriptionStatusSchema: z.ZodType = error: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const subscriptionTopicResourceTriggerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const subscriptionTopicResourceTriggerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - resource: uriSchema, + resource: uriSchema.optional(), _resource: z.lazy(() => elementSchema).optional(), supportedInteraction: z .enum(['create', 'delete', 'update']) + .nullable() .array() + .min(1) .optional(), _supportedInteraction: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), queryCriteria: subscriptionTopicResourceTriggerQueryCriteriaSchema.optional(), fhirPathCriteria: stringSchema.optional(), _fhirPathCriteria: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'resource', shadow: '_resource' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { + name: 'supportedInteraction', + shadow: '_supportedInteraction', + required: false, + }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const subscriptionTopicEventTriggerSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const subscriptionTopicEventTriggerSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), event: z.lazy(() => codeableConceptSchema), - resource: uriSchema, + resource: uriSchema.optional(), _resource: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'resource', shadow: '_resource' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const subscriptionTopicCanFilterBySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const subscriptionTopicCanFilterBySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), resource: uriSchema.optional(), _resource: z.lazy(() => elementSchema).optional(), - filterParameter: stringSchema, + filterParameter: stringSchema.optional(), _filterParameter: z.lazy(() => elementSchema).optional(), filterDefinition: uriSchema.optional(), _filterDefinition: z.lazy(() => elementSchema).optional(), @@ -28278,53 +44363,202 @@ export const subscriptionTopicCanFilterBySchema: z.ZodType elementSchema) + .nullable() .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'filterParameter', shadow: '_filterParameter' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'modifier', shadow: '_modifier', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const subscriptionTopicNotificationShapeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const subscriptionTopicNotificationShapeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - resource: uriSchema, + resource: uriSchema.optional(), _resource: z.lazy(() => elementSchema).optional(), - include: stringSchema.array().optional(), + include: stringSchema.nullable().array().min(1).optional(), _include: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - revInclude: stringSchema.array().optional(), + revInclude: stringSchema.nullable().array().min(1).optional(), _revInclude: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'resource', shadow: '_resource' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'include', shadow: '_include', required: false }, + { name: 'revInclude', shadow: '_revInclude', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const subscriptionTopicResourceTriggerQueryCriteriaSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const subscriptionTopicResourceTriggerQueryCriteriaSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), previous: stringSchema.optional(), _previous: z.lazy(() => elementSchema).optional(), @@ -28336,12 +44570,20 @@ export const subscriptionTopicResourceTriggerQueryCriteriaSchema: z.ZodType elementSchema).optional(), requireBoth: booleanSchema.optional(), _requireBoth: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const subscriptionTopicSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const subscriptionTopicSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('SubscriptionTopic'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -28354,31 +44596,37 @@ export const subscriptionTopicSchema: z.ZodType = z.lazy( contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema, + url: uriSchema.optional(), _url: z.lazy(() => elementSchema).optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), version: stringSchema.optional(), _version: z.lazy(() => elementSchema).optional(), title: stringSchema.optional(), _title: z.lazy(() => elementSchema).optional(), - derivedFrom: canonicalSchema.array().optional(), + derivedFrom: canonicalSchema.nullable().array().min(1).optional(), _derivedFrom: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), _status: z.lazy(() => elementSchema).optional(), experimental: booleanSchema.optional(), _experimental: z.lazy(() => elementSchema).optional(), @@ -28389,16 +44637,19 @@ export const subscriptionTopicSchema: z.ZodType = z.lazy( contact: z .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), useContext: z .lazy(() => usageContextSchema) .array() + .min(1) .optional(), jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), purpose: markdownSchema.optional(), _purpose: z.lazy(() => elementSchema).optional(), @@ -28411,234 +44662,371 @@ export const subscriptionTopicSchema: z.ZodType = z.lazy( effectivePeriod: z.lazy(() => periodSchema).optional(), resourceTrigger: subscriptionTopicResourceTriggerSchema .array() + .min(1) + .optional(), + eventTrigger: subscriptionTopicEventTriggerSchema + .array() + .min(1) .optional(), - eventTrigger: subscriptionTopicEventTriggerSchema.array().optional(), - canFilterBy: subscriptionTopicCanFilterBySchema.array().optional(), + canFilterBy: subscriptionTopicCanFilterBySchema.array().min(1).optional(), notificationShape: subscriptionTopicNotificationShapeSchema .array() + .min(1) .optional(), - }), -) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'status', shadow: '_status' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'derivedFrom', shadow: '_derivedFrom', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceInstanceSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const substanceInstanceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z.lazy(() => identifierSchema).optional(), expiry: dateTimeSchema.optional(), _expiry: z.lazy(() => elementSchema).optional(), quantity: z.lazy(() => quantitySchema).optional(), - }), -) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceIngredientSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - quantity: z.lazy(() => ratioSchema).optional(), - substanceCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - substanceReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['substanceCodeableConcept', 'substanceReference'], - required: true, - }, - ].every((group) => { - const present = group.names.filter( +export const substanceIngredientSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + quantity: z.lazy(() => ratioSchema).optional(), + substanceCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + substanceReference: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['substanceCodeableConcept'], + ['substanceReference'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Substance'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), - _status: z.lazy(() => elementSchema).optional(), - category: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - code: z.lazy(() => codeableConceptSchema), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - instance: substanceInstanceSchema.array().optional(), - ingredient: substanceIngredientSchema.array().optional(), - }), -) - -export const substanceDefinitionMoietySchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - role: z.lazy(() => codeableConceptSchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - stereochemistry: z.lazy(() => codeableConceptSchema).optional(), - opticalActivity: z.lazy(() => codeableConceptSchema).optional(), - molecularFormula: stringSchema.optional(), - _molecularFormula: z.lazy(() => elementSchema).optional(), - amountQuantity: z.lazy(() => quantitySchema).optional(), - amountString: stringSchema.optional(), - _amountString: z.lazy(() => elementSchema).optional(), - measurementType: z.lazy(() => codeableConceptSchema).optional(), - }) - .refine( - (value) => - [ - { names: ['amountQuantity', 'amountString'], required: false }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) +export const substanceSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('Substance'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z.enum(['active', 'entered-in-error', 'inactive']).optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + code: z.lazy(() => codeableConceptSchema), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + instance: substanceInstanceSchema.array().min(1).optional(), + ingredient: substanceIngredientSchema.array().min(1).optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceDefinitionPropertySchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - type: z.lazy(() => codeableConceptSchema), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueCodeableConcept', - 'valueQuantity', - 'valueDate', - 'valueBoolean', - 'valueAttachment', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( +export const substanceDefinitionMoietySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + role: z.lazy(() => codeableConceptSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + stereochemistry: z.lazy(() => codeableConceptSchema).optional(), + opticalActivity: z.lazy(() => codeableConceptSchema).optional(), + molecularFormula: stringSchema.optional(), + _molecularFormula: z.lazy(() => elementSchema).optional(), + amountQuantity: z.lazy(() => quantitySchema).optional(), + amountString: stringSchema.optional(), + _amountString: z.lazy(() => elementSchema).optional(), + measurementType: z.lazy(() => codeableConceptSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['amountQuantity'], + ['amountString', '_amountString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceDefinitionMolecularWeightSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceDefinitionPropertySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueCodeableConcept'], + ['valueQuantity'], + ['valueDate', '_valueDate'], + ['valueBoolean', '_valueBoolean'], + ['valueAttachment'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const substanceDefinitionMolecularWeightSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), method: z.lazy(() => codeableConceptSchema).optional(), type: z.lazy(() => codeableConceptSchema).optional(), amount: z.lazy(() => quantitySchema), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceDefinitionStructureSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceDefinitionStructureSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), stereochemistry: z.lazy(() => codeableConceptSchema).optional(), opticalActivity: z.lazy(() => codeableConceptSchema).optional(), @@ -28652,28 +45040,39 @@ export const substanceDefinitionStructureSchema: z.ZodType codeableConceptSchema) .array() + .min(1) .optional(), sourceDocument: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), representation: substanceDefinitionStructureRepresentationSchema .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceDefinitionCodeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceDefinitionCodeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), code: z.lazy(() => codeableConceptSchema).optional(), status: z.lazy(() => codeableConceptSchema).optional(), @@ -28682,27 +45081,37 @@ export const substanceDefinitionCodeSchema: z.ZodType annotationSchema) .array() + .min(1) .optional(), source: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceDefinitionNameSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceDefinitionNameSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: stringSchema, + name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), type: z.lazy(() => codeableConceptSchema).optional(), status: z.lazy(() => codeableConceptSchema).optional(), @@ -28711,100 +45120,144 @@ export const substanceDefinitionNameSchema: z.ZodType codeableConceptSchema) .array() + .min(1) .optional(), domain: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), synonym: z .lazy(() => substanceDefinitionNameSchema) .array() + .min(1) .optional(), translation: z .lazy(() => substanceDefinitionNameSchema) .array() + .min(1) .optional(), - official: substanceDefinitionNameOfficialSchema.array().optional(), + official: substanceDefinitionNameOfficialSchema.array().min(1).optional(), source: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceDefinitionRelationshipSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - substanceDefinitionReference: z.lazy(() => referenceSchema).optional(), - substanceDefinitionCodeableConcept: z - .lazy(() => codeableConceptSchema) - .optional(), - type: z.lazy(() => codeableConceptSchema), - isDefining: booleanSchema.optional(), - _isDefining: z.lazy(() => elementSchema).optional(), - amountQuantity: z.lazy(() => quantitySchema).optional(), - amountRatio: z.lazy(() => ratioSchema).optional(), - amountString: stringSchema.optional(), - _amountString: z.lazy(() => elementSchema).optional(), - ratioHighLimitAmount: z.lazy(() => ratioSchema).optional(), - comparator: z.lazy(() => codeableConceptSchema).optional(), - source: z - .lazy(() => referenceSchema) - .array() - .optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'substanceDefinitionReference', - 'substanceDefinitionCodeableConcept', - ], - required: false, - }, - { - names: ['amountQuantity', 'amountRatio', 'amountString'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( +export const substanceDefinitionRelationshipSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + substanceDefinitionReference: z.lazy(() => referenceSchema).optional(), + substanceDefinitionCodeableConcept: z + .lazy(() => codeableConceptSchema) + .optional(), + type: z.lazy(() => codeableConceptSchema), + isDefining: booleanSchema.optional(), + _isDefining: z.lazy(() => elementSchema).optional(), + amountQuantity: z.lazy(() => quantitySchema).optional(), + amountRatio: z.lazy(() => ratioSchema).optional(), + amountString: stringSchema.optional(), + _amountString: z.lazy(() => elementSchema).optional(), + ratioHighLimitAmount: z.lazy(() => ratioSchema).optional(), + comparator: z.lazy(() => codeableConceptSchema).optional(), + source: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['substanceDefinitionReference'], + ['substanceDefinitionCodeableConcept'], + ], + required: false, + }, + { + alternatives: [ + ['amountQuantity'], + ['amountRatio'], + ['amountString', '_amountString'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceDefinitionSourceMaterialSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceDefinitionSourceMaterialSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), type: z.lazy(() => codeableConceptSchema).optional(), genus: z.lazy(() => codeableConceptSchema).optional(), @@ -28813,52 +45266,77 @@ export const substanceDefinitionSourceMaterialSchema: z.ZodType codeableConceptSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceDefinitionStructureRepresentationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceDefinitionStructureRepresentationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), type: z.lazy(() => codeableConceptSchema).optional(), representation: stringSchema.optional(), _representation: z.lazy(() => elementSchema).optional(), format: z.lazy(() => codeableConceptSchema).optional(), document: z.lazy(() => referenceSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const substanceDefinitionNameOfficialSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceDefinitionNameOfficialSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), authority: z.lazy(() => codeableConceptSchema).optional(), status: z.lazy(() => codeableConceptSchema).optional(), date: dateTimeSchema.optional(), _date: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const substanceDefinitionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const substanceDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('SubstanceDefinition'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -28871,18 +45349,22 @@ export const substanceDefinitionSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) .optional(), version: stringSchema.optional(), _version: z.lazy(() => elementSchema).optional(), @@ -28890,148 +45372,89 @@ export const substanceDefinitionSchema: z.ZodType = classification: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), domain: z.lazy(() => codeableConceptSchema).optional(), grade: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), informationSource: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), note: z .lazy(() => annotationSchema) .array() + .min(1) .optional(), manufacturer: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), supplier: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - moiety: substanceDefinitionMoietySchema.array().optional(), - property: substanceDefinitionPropertySchema.array().optional(), + moiety: substanceDefinitionMoietySchema.array().min(1).optional(), + property: substanceDefinitionPropertySchema.array().min(1).optional(), molecularWeight: substanceDefinitionMolecularWeightSchema .array() + .min(1) .optional(), structure: substanceDefinitionStructureSchema.optional(), - code: substanceDefinitionCodeSchema.array().optional(), - name: substanceDefinitionNameSchema.array().optional(), - relationship: substanceDefinitionRelationshipSchema.array().optional(), + code: substanceDefinitionCodeSchema.array().min(1).optional(), + name: substanceDefinitionNameSchema.array().min(1).optional(), + relationship: substanceDefinitionRelationshipSchema + .array() + .min(1) + .optional(), sourceMaterial: substanceDefinitionSourceMaterialSchema.optional(), - }), - ) - -export const supplyDeliverySuppliedItemSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - quantity: z.lazy(() => quantitySchema).optional(), - itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - itemReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: ['itemCodeableConcept', 'itemReference'], - required: false, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const supplyDeliverySchema: z.ZodType = z.lazy(() => +export const supplyDeliverySuppliedItemSchema: z.ZodType< + FhirJson +> = z.lazy(() => z - .strictObject({ - resourceType: z.literal('SupplyDelivery'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), + .strictObject({ + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z - .enum(['abandoned', 'completed', 'entered-in-error', 'in-progress']) - .optional(), - _status: z.lazy(() => elementSchema).optional(), - patient: z.lazy(() => referenceSchema).optional(), - type: z.lazy(() => codeableConceptSchema).optional(), - suppliedItem: supplyDeliverySuppliedItemSchema.optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - occurrenceTiming: z.lazy(() => timingSchema).optional(), - supplier: z.lazy(() => referenceSchema).optional(), - destination: z.lazy(() => referenceSchema).optional(), - receiver: z - .lazy(() => referenceSchema) - .array() - .optional(), + quantity: z.lazy(() => quantitySchema).optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ { - names: [ - 'occurrenceDateTime', - 'occurrencePeriod', - 'occurrenceTiming', - ], + alternatives: [['itemCodeableConcept'], ['itemReference']], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -29039,44 +45462,92 @@ export const supplyDeliverySchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const supplyRequestParameterSchema: z.ZodType = +export const supplyDeliverySchema: z.ZodType> = z.lazy(() => z .strictObject({ - id: stringSchema.optional(), + resourceType: z.literal('SupplyDelivery'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum(['abandoned', 'completed', 'entered-in-error', 'in-progress']) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + patient: z.lazy(() => referenceSchema).optional(), + type: z.lazy(() => codeableConceptSchema).optional(), + suppliedItem: supplyDeliverySuppliedItemSchema.optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + supplier: z.lazy(() => referenceSchema).optional(), + destination: z.lazy(() => referenceSchema).optional(), + receiver: z + .lazy(() => referenceSchema) + .array() + .min(1) .optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), }) .refine( (value) => [ { - names: [ - 'valueCodeableConcept', - 'valueQuantity', - 'valueRange', - 'valueBoolean', + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], + ['occurrenceTiming'], ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -29084,93 +45555,55 @@ export const supplyRequestParameterSchema: z.ZodType message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), - ) + ) as unknown as z.ZodType> -export const supplyRequestSchema: z.ZodType = z.lazy(() => +export const supplyRequestParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - resourceType: z.literal('SupplyRequest'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), + id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - status: z - .enum([ - 'active', - 'cancelled', - 'completed', - 'draft', - 'entered-in-error', - 'suspended', - 'unknown', - ]) - .optional(), - _status: z.lazy(() => elementSchema).optional(), - category: z.lazy(() => codeableConceptSchema).optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - itemReference: z.lazy(() => referenceSchema).optional(), - quantity: z.lazy(() => quantitySchema), - parameter: supplyRequestParameterSchema.array().optional(), - occurrenceDateTime: dateTimeSchema.optional(), - _occurrenceDateTime: z.lazy(() => elementSchema).optional(), - occurrencePeriod: z.lazy(() => periodSchema).optional(), - occurrenceTiming: z.lazy(() => timingSchema).optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: z.lazy(() => elementSchema).optional(), - requester: z.lazy(() => referenceSchema).optional(), - supplier: z - .lazy(() => referenceSchema) - .array() - .optional(), - reasonCode: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - reasonReference: z - .lazy(() => referenceSchema) - .array() - .optional(), - deliverFrom: z.lazy(() => referenceSchema).optional(), - deliverTo: z.lazy(() => referenceSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), }) .refine( (value) => [ - { names: ['itemCodeableConcept', 'itemReference'], required: true }, { - names: [ - 'occurrenceDateTime', - 'occurrencePeriod', - 'occurrenceTiming', + alternatives: [ + ['valueCodeableConcept'], + ['valueQuantity'], + ['valueRange'], + ['valueBoolean', '_valueBoolean'], ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -29178,42 +45611,172 @@ export const supplyRequestSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> -export const taskRestrictionSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - repetitions: positiveIntSchema.optional(), - _repetitions: z.lazy(() => elementSchema).optional(), - period: z.lazy(() => periodSchema).optional(), - recipient: z - .lazy(() => referenceSchema) - .array() - .optional(), - }), -) +export const supplyRequestSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + resourceType: z.literal('SupplyRequest'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'active', + 'cancelled', + 'completed', + 'draft', + 'entered-in-error', + 'suspended', + 'unknown', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + category: z.lazy(() => codeableConceptSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + itemCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + itemReference: z.lazy(() => referenceSchema).optional(), + quantity: z.lazy(() => quantitySchema), + parameter: supplyRequestParameterSchema.array().min(1).optional(), + occurrenceDateTime: dateTimeSchema.optional(), + _occurrenceDateTime: z.lazy(() => elementSchema).optional(), + occurrencePeriod: z.lazy(() => periodSchema).optional(), + occurrenceTiming: z.lazy(() => timingSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + supplier: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + reasonCode: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + reasonReference: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + deliverFrom: z.lazy(() => referenceSchema).optional(), + deliverTo: z.lazy(() => referenceSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [['itemCodeableConcept'], ['itemReference']], + required: true, + }, + { + alternatives: [ + ['occurrenceDateTime', '_occurrenceDateTime'], + ['occurrencePeriod'], + ['occurrenceTiming'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const taskRestrictionSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + repetitions: positiveIntSchema.optional(), + _repetitions: z.lazy(() => elementSchema).optional(), + period: z.lazy(() => periodSchema).optional(), + recipient: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const taskInputSchema: z.ZodType = z.lazy(() => +export const taskInputSchema: z.ZodType> = z.lazy(() => z .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), type: z.lazy(() => codeableConceptSchema), valueBase64Binary: base64BinarySchema.optional(), @@ -29292,63 +45855,65 @@ export const taskInputSchema: z.ZodType = z.lazy(() => (value) => [ { - names: [ - 'valueBase64Binary', - 'valueBoolean', - 'valueCanonical', - 'valueCode', - 'valueDate', - 'valueDateTime', - 'valueDecimal', - 'valueId', - 'valueInstant', - 'valueInteger', - 'valueMarkdown', - 'valueOid', - 'valuePositiveInt', - 'valueString', - 'valueTime', - 'valueUnsignedInt', - 'valueUri', - 'valueUrl', - 'valueUuid', - 'valueAddress', - 'valueAge', - 'valueAnnotation', - 'valueAttachment', - 'valueCodeableConcept', - 'valueCoding', - 'valueContactPoint', - 'valueCount', - 'valueDistance', - 'valueDuration', - 'valueHumanName', - 'valueIdentifier', - 'valueMoney', - 'valuePeriod', - 'valueQuantity', - 'valueRange', - 'valueRatio', - 'valueReference', - 'valueSampledData', - 'valueSignature', - 'valueTiming', - 'valueContactDetail', - 'valueContributor', - 'valueDataRequirement', - 'valueExpression', - 'valueParameterDefinition', - 'valueRelatedArtifact', - 'valueTriggerDefinition', - 'valueUsageContext', - 'valueDosage', - 'valueMeta', + alternatives: [ + ['valueBase64Binary', '_valueBase64Binary'], + ['valueBoolean', '_valueBoolean'], + ['valueCanonical', '_valueCanonical'], + ['valueCode', '_valueCode'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueDecimal', '_valueDecimal'], + ['valueId', '_valueId'], + ['valueInstant', '_valueInstant'], + ['valueInteger', '_valueInteger'], + ['valueMarkdown', '_valueMarkdown'], + ['valueOid', '_valueOid'], + ['valuePositiveInt', '_valuePositiveInt'], + ['valueString', '_valueString'], + ['valueTime', '_valueTime'], + ['valueUnsignedInt', '_valueUnsignedInt'], + ['valueUri', '_valueUri'], + ['valueUrl', '_valueUrl'], + ['valueUuid', '_valueUuid'], + ['valueAddress'], + ['valueAge'], + ['valueAnnotation'], + ['valueAttachment'], + ['valueCodeableConcept'], + ['valueCoding'], + ['valueContactPoint'], + ['valueCount'], + ['valueDistance'], + ['valueDuration'], + ['valueHumanName'], + ['valueIdentifier'], + ['valueMoney'], + ['valuePeriod'], + ['valueQuantity'], + ['valueRange'], + ['valueRatio'], + ['valueReference'], + ['valueSampledData'], + ['valueSignature'], + ['valueTiming'], + ['valueContactDetail'], + ['valueContributor'], + ['valueDataRequirement'], + ['valueExpression'], + ['valueParameterDefinition'], + ['valueRelatedArtifact'], + ['valueTriggerDefinition'], + ['valueUsageContext'], + ['valueDosage'], + ['valueMeta'], ], required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -29356,345 +45921,462 @@ export const taskInputSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) +) as unknown as z.ZodType> + +export const taskOutputSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + type: z.lazy(() => codeableConceptSchema), + valueBase64Binary: base64BinarySchema.optional(), + _valueBase64Binary: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueCanonical: canonicalSchema.optional(), + _valueCanonical: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDate: dateSchema.optional(), + _valueDate: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueId: idSchema.optional(), + _valueId: z.lazy(() => elementSchema).optional(), + valueInstant: instantSchema.optional(), + _valueInstant: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueMarkdown: markdownSchema.optional(), + _valueMarkdown: z.lazy(() => elementSchema).optional(), + valueOid: oidSchema.optional(), + _valueOid: z.lazy(() => elementSchema).optional(), + valuePositiveInt: positiveIntSchema.optional(), + _valuePositiveInt: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueTime: timeSchema.optional(), + _valueTime: z.lazy(() => elementSchema).optional(), + valueUnsignedInt: unsignedIntSchema.optional(), + _valueUnsignedInt: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueUrl: urlSchema.optional(), + _valueUrl: z.lazy(() => elementSchema).optional(), + valueUuid: uuidSchema.optional(), + _valueUuid: z.lazy(() => elementSchema).optional(), + valueAddress: z.lazy(() => addressSchema).optional(), + valueAge: z.lazy(() => ageSchema).optional(), + valueAnnotation: z.lazy(() => annotationSchema).optional(), + valueAttachment: z.lazy(() => attachmentSchema).optional(), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueCoding: z.lazy(() => codingSchema).optional(), + valueContactPoint: z.lazy(() => contactPointSchema).optional(), + valueCount: z.lazy(() => countSchema).optional(), + valueDistance: z.lazy(() => distanceSchema).optional(), + valueDuration: z.lazy(() => durationSchema).optional(), + valueHumanName: z.lazy(() => humanNameSchema).optional(), + valueIdentifier: z.lazy(() => identifierSchema).optional(), + valueMoney: z.lazy(() => moneySchema).optional(), + valuePeriod: z.lazy(() => periodSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueRatio: z.lazy(() => ratioSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), + valueSampledData: z.lazy(() => sampledDataSchema).optional(), + valueSignature: z.lazy(() => signatureSchema).optional(), + valueTiming: z.lazy(() => timingSchema).optional(), + valueContactDetail: z.lazy(() => contactDetailSchema).optional(), + valueContributor: z.lazy(() => contributorSchema).optional(), + valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), + valueExpression: z.lazy(() => expressionSchema).optional(), + valueParameterDefinition: z + .lazy(() => parameterDefinitionSchema) + .optional(), + valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), + valueTriggerDefinition: z + .lazy(() => triggerDefinitionSchema) + .optional(), + valueUsageContext: z.lazy(() => usageContextSchema).optional(), + valueDosage: z.lazy(() => dosageSchema).optional(), + valueMeta: z.lazy(() => metaSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueBase64Binary', '_valueBase64Binary'], + ['valueBoolean', '_valueBoolean'], + ['valueCanonical', '_valueCanonical'], + ['valueCode', '_valueCode'], + ['valueDate', '_valueDate'], + ['valueDateTime', '_valueDateTime'], + ['valueDecimal', '_valueDecimal'], + ['valueId', '_valueId'], + ['valueInstant', '_valueInstant'], + ['valueInteger', '_valueInteger'], + ['valueMarkdown', '_valueMarkdown'], + ['valueOid', '_valueOid'], + ['valuePositiveInt', '_valuePositiveInt'], + ['valueString', '_valueString'], + ['valueTime', '_valueTime'], + ['valueUnsignedInt', '_valueUnsignedInt'], + ['valueUri', '_valueUri'], + ['valueUrl', '_valueUrl'], + ['valueUuid', '_valueUuid'], + ['valueAddress'], + ['valueAge'], + ['valueAnnotation'], + ['valueAttachment'], + ['valueCodeableConcept'], + ['valueCoding'], + ['valueContactPoint'], + ['valueCount'], + ['valueDistance'], + ['valueDuration'], + ['valueHumanName'], + ['valueIdentifier'], + ['valueMoney'], + ['valuePeriod'], + ['valueQuantity'], + ['valueRange'], + ['valueRatio'], + ['valueReference'], + ['valueSampledData'], + ['valueSignature'], + ['valueTiming'], + ['valueContactDetail'], + ['valueContributor'], + ['valueDataRequirement'], + ['valueExpression'], + ['valueParameterDefinition'], + ['valueRelatedArtifact'], + ['valueTriggerDefinition'], + ['valueUsageContext'], + ['valueDosage'], + ['valueMeta'], + ], + required: true, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const taskOutputSchema: z.ZodType = z.lazy(() => +export const taskSchema: z.ZodType> = z.lazy(() => z .strictObject({ - id: stringSchema.optional(), + resourceType: z.literal('Task'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.lazy(() => codeableConceptSchema), - valueBase64Binary: base64BinarySchema.optional(), - _valueBase64Binary: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueCanonical: canonicalSchema.optional(), - _valueCanonical: z.lazy(() => elementSchema).optional(), - valueCode: codeSchema.optional(), - _valueCode: z.lazy(() => elementSchema).optional(), - valueDate: dateSchema.optional(), - _valueDate: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueId: idSchema.optional(), - _valueId: z.lazy(() => elementSchema).optional(), - valueInstant: instantSchema.optional(), - _valueInstant: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueMarkdown: markdownSchema.optional(), - _valueMarkdown: z.lazy(() => elementSchema).optional(), - valueOid: oidSchema.optional(), - _valueOid: z.lazy(() => elementSchema).optional(), - valuePositiveInt: positiveIntSchema.optional(), - _valuePositiveInt: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueTime: timeSchema.optional(), - _valueTime: z.lazy(() => elementSchema).optional(), - valueUnsignedInt: unsignedIntSchema.optional(), - _valueUnsignedInt: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueUrl: urlSchema.optional(), - _valueUrl: z.lazy(() => elementSchema).optional(), - valueUuid: uuidSchema.optional(), - _valueUuid: z.lazy(() => elementSchema).optional(), - valueAddress: z.lazy(() => addressSchema).optional(), - valueAge: z.lazy(() => ageSchema).optional(), - valueAnnotation: z.lazy(() => annotationSchema).optional(), - valueAttachment: z.lazy(() => attachmentSchema).optional(), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueCoding: z.lazy(() => codingSchema).optional(), - valueContactPoint: z.lazy(() => contactPointSchema).optional(), - valueCount: z.lazy(() => countSchema).optional(), - valueDistance: z.lazy(() => distanceSchema).optional(), - valueDuration: z.lazy(() => durationSchema).optional(), - valueHumanName: z.lazy(() => humanNameSchema).optional(), - valueIdentifier: z.lazy(() => identifierSchema).optional(), - valueMoney: z.lazy(() => moneySchema).optional(), - valuePeriod: z.lazy(() => periodSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueRatio: z.lazy(() => ratioSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - valueSampledData: z.lazy(() => sampledDataSchema).optional(), - valueSignature: z.lazy(() => signatureSchema).optional(), - valueTiming: z.lazy(() => timingSchema).optional(), - valueContactDetail: z.lazy(() => contactDetailSchema).optional(), - valueContributor: z.lazy(() => contributorSchema).optional(), - valueDataRequirement: z.lazy(() => dataRequirementSchema).optional(), - valueExpression: z.lazy(() => expressionSchema).optional(), - valueParameterDefinition: z - .lazy(() => parameterDefinitionSchema) + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + instantiatesCanonical: canonicalSchema.optional(), + _instantiatesCanonical: z.lazy(() => elementSchema).optional(), + instantiatesUri: uriSchema.optional(), + _instantiatesUri: z.lazy(() => elementSchema).optional(), + basedOn: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + groupIdentifier: z.lazy(() => identifierSchema).optional(), + partOf: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + status: z + .enum([ + 'accepted', + 'cancelled', + 'completed', + 'draft', + 'entered-in-error', + 'failed', + 'in-progress', + 'on-hold', + 'ready', + 'received', + 'rejected', + 'requested', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + statusReason: z.lazy(() => codeableConceptSchema).optional(), + businessStatus: z.lazy(() => codeableConceptSchema).optional(), + intent: z + .enum([ + 'filler-order', + 'instance-order', + 'option', + 'order', + 'original-order', + 'plan', + 'proposal', + 'reflex-order', + 'unknown', + ]) + .optional(), + _intent: z.lazy(() => elementSchema).optional(), + priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), + _priority: z.lazy(() => elementSchema).optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + focus: z.lazy(() => referenceSchema).optional(), + for: z.lazy(() => referenceSchema).optional(), + encounter: z.lazy(() => referenceSchema).optional(), + executionPeriod: z.lazy(() => periodSchema).optional(), + authoredOn: dateTimeSchema.optional(), + _authoredOn: z.lazy(() => elementSchema).optional(), + lastModified: dateTimeSchema.optional(), + _lastModified: z.lazy(() => elementSchema).optional(), + requester: z.lazy(() => referenceSchema).optional(), + performerType: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) .optional(), - valueRelatedArtifact: z.lazy(() => relatedArtifactSchema).optional(), - valueTriggerDefinition: z.lazy(() => triggerDefinitionSchema).optional(), - valueUsageContext: z.lazy(() => usageContextSchema).optional(), - valueDosage: z.lazy(() => dosageSchema).optional(), - valueMeta: z.lazy(() => metaSchema).optional(), + owner: z.lazy(() => referenceSchema).optional(), + location: z.lazy(() => referenceSchema).optional(), + reasonCode: z.lazy(() => codeableConceptSchema).optional(), + reasonReference: z.lazy(() => referenceSchema).optional(), + insurance: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + note: z + .lazy(() => annotationSchema) + .array() + .min(1) + .optional(), + relevantHistory: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + restriction: taskRestrictionSchema.optional(), + input: taskInputSchema.array().min(1).optional(), + output: taskOutputSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'intent', shadow: '_intent' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } }) .refine( - (value) => - [ - { - names: [ - 'valueBase64Binary', - 'valueBoolean', - 'valueCanonical', - 'valueCode', - 'valueDate', - 'valueDateTime', - 'valueDecimal', - 'valueId', - 'valueInstant', - 'valueInteger', - 'valueMarkdown', - 'valueOid', - 'valuePositiveInt', - 'valueString', - 'valueTime', - 'valueUnsignedInt', - 'valueUri', - 'valueUrl', - 'valueUuid', - 'valueAddress', - 'valueAge', - 'valueAnnotation', - 'valueAttachment', - 'valueCodeableConcept', - 'valueCoding', - 'valueContactPoint', - 'valueCount', - 'valueDistance', - 'valueDuration', - 'valueHumanName', - 'valueIdentifier', - 'valueMoney', - 'valuePeriod', - 'valueQuantity', - 'valueRange', - 'valueRatio', - 'valueReference', - 'valueSampledData', - 'valueSignature', - 'valueTiming', - 'valueContactDetail', - 'valueContributor', - 'valueDataRequirement', - 'valueExpression', - 'valueParameterDefinition', - 'valueRelatedArtifact', - 'valueTriggerDefinition', - 'valueUsageContext', - 'valueDosage', - 'valueMeta', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const taskSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('Task'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - instantiatesCanonical: canonicalSchema.optional(), - _instantiatesCanonical: z.lazy(() => elementSchema).optional(), - instantiatesUri: uriSchema.optional(), - _instantiatesUri: z.lazy(() => elementSchema).optional(), - basedOn: z - .lazy(() => referenceSchema) - .array() - .optional(), - groupIdentifier: z.lazy(() => identifierSchema).optional(), - partOf: z - .lazy(() => referenceSchema) - .array() - .optional(), - status: z.enum([ - 'accepted', - 'cancelled', - 'completed', - 'draft', - 'entered-in-error', - 'failed', - 'in-progress', - 'on-hold', - 'ready', - 'received', - 'rejected', - 'requested', - ]), - _status: z.lazy(() => elementSchema).optional(), - statusReason: z.lazy(() => codeableConceptSchema).optional(), - businessStatus: z.lazy(() => codeableConceptSchema).optional(), - intent: z.enum([ - 'filler-order', - 'instance-order', - 'option', - 'order', - 'original-order', - 'plan', - 'proposal', - 'reflex-order', - 'unknown', - ]), - _intent: z.lazy(() => elementSchema).optional(), - priority: z.enum(['asap', 'routine', 'stat', 'urgent']).optional(), - _priority: z.lazy(() => elementSchema).optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - focus: z.lazy(() => referenceSchema).optional(), - for: z.lazy(() => referenceSchema).optional(), - encounter: z.lazy(() => referenceSchema).optional(), - executionPeriod: z.lazy(() => periodSchema).optional(), - authoredOn: dateTimeSchema.optional(), - _authoredOn: z.lazy(() => elementSchema).optional(), - lastModified: dateTimeSchema.optional(), - _lastModified: z.lazy(() => elementSchema).optional(), - requester: z.lazy(() => referenceSchema).optional(), - performerType: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - owner: z.lazy(() => referenceSchema).optional(), - location: z.lazy(() => referenceSchema).optional(), - reasonCode: z.lazy(() => codeableConceptSchema).optional(), - reasonReference: z.lazy(() => referenceSchema).optional(), - insurance: z - .lazy(() => referenceSchema) - .array() - .optional(), - note: z - .lazy(() => annotationSchema) - .array() - .optional(), - relevantHistory: z - .lazy(() => referenceSchema) - .array() - .optional(), - restriction: taskRestrictionSchema.optional(), - input: taskInputSchema.array().optional(), - output: taskOutputSchema.array().optional(), - }), -) - -export const terminologyCapabilitiesSoftwareSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +) as unknown as z.ZodType> + +export const terminologyCapabilitiesSoftwareSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: stringSchema, + name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), version: stringSchema.optional(), _version: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const terminologyCapabilitiesImplementationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesImplementationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - description: stringSchema, + description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), url: urlSchema.optional(), _url: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'description', shadow: '_description' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const terminologyCapabilitiesCodeSystemSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesCodeSystemSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), uri: canonicalSchema.optional(), _uri: z.lazy(() => elementSchema).optional(), version: terminologyCapabilitiesCodeSystemVersionSchema .array() + .min(1) .optional(), subsumption: booleanSchema.optional(), _subsumption: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const terminologyCapabilitiesExpansionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesExpansionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), hierarchical: booleanSchema.optional(), _hierarchical: z.lazy(() => elementSchema).optional(), @@ -29704,74 +46386,139 @@ export const terminologyCapabilitiesExpansionSchema: z.ZodType elementSchema).optional(), parameter: terminologyCapabilitiesExpansionParameterSchema .array() + .min(1) .optional(), textFilter: markdownSchema.optional(), _textFilter: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const terminologyCapabilitiesValidateCodeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesValidateCodeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - translations: booleanSchema, + translations: booleanSchema.optional(), _translations: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'translations', shadow: '_translations' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const terminologyCapabilitiesTranslationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesTranslationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - needsMap: booleanSchema, + needsMap: booleanSchema.optional(), _needsMap: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'needsMap', shadow: '_needsMap' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const terminologyCapabilitiesClosureSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesClosureSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), translation: booleanSchema.optional(), _translation: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const terminologyCapabilitiesCodeSystemVersionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesCodeSystemVersionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), code: stringSchema.optional(), _code: z.lazy(() => elementSchema).optional(), @@ -29779,66 +46526,222 @@ export const terminologyCapabilitiesCodeSystemVersionSchema: z.ZodType elementSchema).optional(), compositional: booleanSchema.optional(), _compositional: z.lazy(() => elementSchema).optional(), - language: codeSchema.array().optional(), + language: codeSchema.nullable().array().min(1).optional(), _language: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), filter: terminologyCapabilitiesCodeSystemVersionFilterSchema .array() + .min(1) .optional(), - property: codeSchema.array().optional(), + property: codeSchema.nullable().array().min(1).optional(), _property: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'language', shadow: '_language', required: false }, + { name: 'property', shadow: '_property', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const terminologyCapabilitiesExpansionParameterSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesExpansionParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: codeSchema, + name: codeSchema.optional(), _name: z.lazy(() => elementSchema).optional(), documentation: stringSchema.optional(), _documentation: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const terminologyCapabilitiesCodeSystemVersionFilterSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesCodeSystemVersionFilterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: codeSchema, + code: codeSchema.optional(), _code: z.lazy(() => elementSchema).optional(), - op: codeSchema.array().min(1), + op: codeSchema.nullable().array().min(1).optional(), _op: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [{ name: 'op', shadow: '_op', required: true }]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const terminologyCapabilitiesSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const terminologyCapabilitiesSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('TerminologyCapabilities'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -29851,14 +46754,17 @@ export const terminologyCapabilitiesSchema: z.ZodType resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), url: uriSchema.optional(), _url: z.lazy(() => elementSchema).optional(), @@ -29868,348 +46774,624 @@ export const terminologyCapabilitiesSchema: z.ZodType elementSchema).optional(), title: stringSchema.optional(), _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), _status: z.lazy(() => elementSchema).optional(), experimental: booleanSchema.optional(), _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema, + date: dateTimeSchema.optional(), _date: z.lazy(() => elementSchema).optional(), publisher: stringSchema.optional(), _publisher: z.lazy(() => elementSchema).optional(), contact: z .lazy(() => contactDetailSchema) .array() + .min(1) .optional(), description: markdownSchema.optional(), _description: z.lazy(() => elementSchema).optional(), useContext: z .lazy(() => usageContextSchema) .array() + .min(1) .optional(), jurisdiction: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), purpose: markdownSchema.optional(), _purpose: z.lazy(() => elementSchema).optional(), copyright: markdownSchema.optional(), _copyright: z.lazy(() => elementSchema).optional(), - kind: z.enum(['capability', 'instance', 'requirements']), + kind: z.enum(['capability', 'instance', 'requirements']).optional(), _kind: z.lazy(() => elementSchema).optional(), software: terminologyCapabilitiesSoftwareSchema.optional(), implementation: terminologyCapabilitiesImplementationSchema.optional(), lockedDate: booleanSchema.optional(), _lockedDate: z.lazy(() => elementSchema).optional(), - codeSystem: terminologyCapabilitiesCodeSystemSchema.array().optional(), + codeSystem: terminologyCapabilitiesCodeSystemSchema + .array() + .min(1) + .optional(), expansion: terminologyCapabilitiesExpansionSchema.optional(), codeSearch: z.enum(['all', 'explicit']).optional(), _codeSearch: z.lazy(() => elementSchema).optional(), validateCode: terminologyCapabilitiesValidateCodeSchema.optional(), translation: terminologyCapabilitiesTranslationSchema.optional(), closure: terminologyCapabilitiesClosureSchema.optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'date', shadow: '_date' }, + { name: 'kind', shadow: '_kind' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportParticipantSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testReportParticipantSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum(['client', 'server', 'test-engine']), + type: z.enum(['client', 'server', 'test-engine']).optional(), _type: z.lazy(() => elementSchema).optional(), - uri: uriSchema, + uri: uriSchema.optional(), _uri: z.lazy(() => elementSchema).optional(), display: stringSchema.optional(), _display: z.lazy(() => elementSchema).optional(), - }), - ) - -export const testReportSetupSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - action: testReportSetupActionSchema.array().min(1), - }), -) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'type', shadow: '_type' }, + { name: 'uri', shadow: '_uri' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportTestSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - action: testReportTestActionSchema.array().min(1), - }), -) +export const testReportSetupSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + action: testReportSetupActionSchema.array().min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const testReportTeardownSchema: z.ZodType = +export const testReportTestSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + action: testReportTestActionSchema.array().min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const testReportTeardownSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), action: testReportTeardownActionSchema.array().min(1), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportSetupActionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testReportSetupActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), operation: testReportSetupActionOperationSchema.optional(), assert: testReportSetupActionAssertSchema.optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportTestActionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testReportTestActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), operation: z.lazy(() => testReportSetupActionOperationSchema).optional(), assert: z.lazy(() => testReportSetupActionAssertSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportTeardownActionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testReportTeardownActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), operation: z.lazy(() => testReportSetupActionOperationSchema), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportSetupActionOperationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testReportSetupActionOperationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - result: z.enum(['error', 'fail', 'pass', 'skip', 'warning']), + result: z.enum(['error', 'fail', 'pass', 'skip', 'warning']).optional(), _result: z.lazy(() => elementSchema).optional(), message: markdownSchema.optional(), _message: z.lazy(() => elementSchema).optional(), detail: uriSchema.optional(), _detail: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'result', shadow: '_result' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportSetupActionAssertSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testReportSetupActionAssertSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - result: z.enum(['error', 'fail', 'pass', 'skip', 'warning']), + result: z.enum(['error', 'fail', 'pass', 'skip', 'warning']).optional(), _result: z.lazy(() => elementSchema).optional(), message: markdownSchema.optional(), _message: z.lazy(() => elementSchema).optional(), detail: stringSchema.optional(), _detail: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'result', shadow: '_result' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testReportSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('TestReport'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - identifier: z.lazy(() => identifierSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - status: z.enum([ - 'completed', - 'entered-in-error', - 'in-progress', - 'stopped', - 'waiting', - ]), - _status: z.lazy(() => elementSchema).optional(), - testScript: z.lazy(() => referenceSchema), - result: z.enum(['fail', 'pass', 'pending']), - _result: z.lazy(() => elementSchema).optional(), - score: decimalSchema.optional(), - _score: z.lazy(() => elementSchema).optional(), - tester: stringSchema.optional(), - _tester: z.lazy(() => elementSchema).optional(), - issued: dateTimeSchema.optional(), - _issued: z.lazy(() => elementSchema).optional(), - participant: testReportParticipantSchema.array().optional(), - setup: testReportSetupSchema.optional(), - test: testReportTestSchema.array().optional(), - teardown: testReportTeardownSchema.optional(), - }), -) - -export const testScriptOriginSchema: z.ZodType = z.lazy( +export const testReportSchema: z.ZodType> = z.lazy( () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - index: integerSchema, - _index: z.lazy(() => elementSchema).optional(), - profile: z.lazy(() => codingSchema), - }), -) + z + .strictObject({ + resourceType: z.literal('TestReport'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + identifier: z.lazy(() => identifierSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + status: z + .enum([ + 'completed', + 'entered-in-error', + 'in-progress', + 'stopped', + 'waiting', + ]) + .optional(), + _status: z.lazy(() => elementSchema).optional(), + testScript: z.lazy(() => referenceSchema), + result: z.enum(['fail', 'pass', 'pending']).optional(), + _result: z.lazy(() => elementSchema).optional(), + score: decimalSchema.optional(), + _score: z.lazy(() => elementSchema).optional(), + tester: stringSchema.optional(), + _tester: z.lazy(() => elementSchema).optional(), + issued: dateTimeSchema.optional(), + _issued: z.lazy(() => elementSchema).optional(), + participant: testReportParticipantSchema.array().min(1).optional(), + setup: testReportSetupSchema.optional(), + test: testReportTestSchema.array().min(1).optional(), + teardown: testReportTeardownSchema.optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'result', shadow: '_result' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const testScriptDestinationSchema: z.ZodType = +export const testScriptOriginSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + index: integerSchema.optional(), + _index: z.lazy(() => elementSchema).optional(), + profile: z.lazy(() => codingSchema), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'index', shadow: '_index' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const testScriptDestinationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - index: integerSchema, + index: integerSchema.optional(), _index: z.lazy(() => elementSchema).optional(), profile: z.lazy(() => codingSchema), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'index', shadow: '_index' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptMetadataSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptMetadataSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - link: testScriptMetadataLinkSchema.array().optional(), + link: testScriptMetadataLinkSchema.array().min(1).optional(), capability: testScriptMetadataCapabilitySchema.array().min(1), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptFixtureSchema: z.ZodType = z.lazy( - () => - z.strictObject({ +export const testScriptFixtureSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - autocreate: booleanSchema, + autocreate: booleanSchema.optional(), _autocreate: z.lazy(() => elementSchema).optional(), - autodelete: booleanSchema, + autodelete: booleanSchema.optional(), _autodelete: z.lazy(() => elementSchema).optional(), resource: z.lazy(() => referenceSchema).optional(), - }), -) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'autocreate', shadow: '_autocreate' }, + { name: 'autodelete', shadow: '_autodelete' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptVariableSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptVariableSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - name: stringSchema, + name: stringSchema.optional(), _name: z.lazy(() => elementSchema).optional(), defaultValue: stringSchema.optional(), _defaultValue: z.lazy(() => elementSchema).optional(), @@ -30225,175 +47407,352 @@ export const testScriptVariableSchema: z.ZodType = _path: z.lazy(() => elementSchema).optional(), sourceId: idSchema.optional(), _sourceId: z.lazy(() => elementSchema).optional(), - }), - ) - -export const testScriptSetupSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - action: testScriptSetupActionSchema.array().min(1), - }), -) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptTestSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - description: stringSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - action: testScriptTestActionSchema.array().min(1), - }), -) +export const testScriptSetupSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + action: testScriptSetupActionSchema.array().min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> -export const testScriptTeardownSchema: z.ZodType = +export const testScriptTestSchema: z.ZodType> = z.lazy(() => - z.strictObject({ + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + description: stringSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + action: testScriptTestActionSchema.array().min(1), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), + ) as unknown as z.ZodType> + +export const testScriptTeardownSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), action: testScriptTeardownActionSchema.array().min(1), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptMetadataLinkSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptMetadataLinkSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - url: uriSchema, + url: uriSchema.optional(), _url: z.lazy(() => elementSchema).optional(), description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'url', shadow: '_url' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptMetadataCapabilitySchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptMetadataCapabilitySchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - required: booleanSchema, + required: booleanSchema.optional(), _required: z.lazy(() => elementSchema).optional(), - validated: booleanSchema, + validated: booleanSchema.optional(), _validated: z.lazy(() => elementSchema).optional(), description: stringSchema.optional(), _description: z.lazy(() => elementSchema).optional(), - origin: integerSchema.array().optional(), + origin: integerSchema.nullable().array().min(1).optional(), _origin: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), destination: integerSchema.optional(), _destination: z.lazy(() => elementSchema).optional(), - link: uriSchema.array().optional(), + link: uriSchema.nullable().array().min(1).optional(), _link: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - capabilities: canonicalSchema, + capabilities: canonicalSchema.optional(), _capabilities: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'required', shadow: '_required' }, + { name: 'validated', shadow: '_validated' }, + { name: 'capabilities', shadow: '_capabilities' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'origin', shadow: '_origin', required: false }, + { name: 'link', shadow: '_link', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptSetupActionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptSetupActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), operation: testScriptSetupActionOperationSchema.optional(), assert: testScriptSetupActionAssertSchema.optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptTestActionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptTestActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), operation: z.lazy(() => testScriptSetupActionOperationSchema).optional(), assert: z.lazy(() => testScriptSetupActionAssertSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptTeardownActionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptTeardownActionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), operation: z.lazy(() => testScriptSetupActionOperationSchema), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptSetupActionOperationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptSetupActionOperationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), type: z.lazy(() => codingSchema).optional(), resource: z @@ -30618,7 +47977,7 @@ export const testScriptSetupActionOperationSchema: z.ZodType elementSchema).optional(), destination: integerSchema.optional(), _destination: z.lazy(() => elementSchema).optional(), - encodeRequestUrl: booleanSchema, + encodeRequestUrl: booleanSchema.optional(), _encodeRequestUrl: z.lazy(() => elementSchema).optional(), method: z .enum(['delete', 'get', 'head', 'options', 'patch', 'post', 'put']) @@ -30630,6 +47989,7 @@ export const testScriptSetupActionOperationSchema: z.ZodType elementSchema).optional(), requestHeader: testScriptSetupActionOperationRequestHeaderSchema .array() + .min(1) .optional(), requestId: idSchema.optional(), _requestId: z.lazy(() => elementSchema).optional(), @@ -30641,20 +48001,46 @@ export const testScriptSetupActionOperationSchema: z.ZodType elementSchema).optional(), url: stringSchema.optional(), _url: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'encodeRequestUrl', shadow: '_encodeRequestUrl' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const testScriptSetupActionAssertSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const testScriptSetupActionAssertSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), label: stringSchema.optional(), _label: z.lazy(() => elementSchema).optional(), @@ -30939,195 +48325,494 @@ export const testScriptSetupActionAssertSchema: z.ZodType elementSchema).optional(), value: stringSchema.optional(), _value: z.lazy(() => elementSchema).optional(), - warningOnly: booleanSchema, + warningOnly: booleanSchema.optional(), _warningOnly: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'warningOnly', shadow: '_warningOnly' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const testScriptSetupActionOperationRequestHeaderSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + field: stringSchema.optional(), + _field: z.lazy(() => elementSchema).optional(), + value: stringSchema.optional(), + _value: z.lazy(() => elementSchema).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'field', shadow: '_field' }, + { name: 'value', shadow: '_value' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> + +export const testScriptSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + resourceType: z.literal('TestScript'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z.lazy(() => identifierSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + origin: testScriptOriginSchema.array().min(1).optional(), + destination: testScriptDestinationSchema.array().min(1).optional(), + metadata: testScriptMetadataSchema.optional(), + fixture: testScriptFixtureSchema.array().min(1).optional(), + profile: z + .lazy(() => referenceSchema) + .array() + .min(1) + .optional(), + variable: testScriptVariableSchema.array().min(1).optional(), + setup: testScriptSetupSchema.optional(), + test: testScriptTestSchema.array().min(1).optional(), + teardown: testScriptTeardownSchema.optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'url', shadow: '_url' }, + { name: 'name', shadow: '_name' }, + { name: 'status', shadow: '_status' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> -export const testScriptSetupActionOperationRequestHeaderSchema: z.ZodType = - z.lazy(() => - z.strictObject({ - id: stringSchema.optional(), +export const timingRepeatSchema: z.ZodType> = z.lazy( + () => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + boundsDuration: z.lazy(() => durationSchema).optional(), + boundsRange: z.lazy(() => rangeSchema).optional(), + boundsPeriod: z.lazy(() => periodSchema).optional(), + count: positiveIntSchema.optional(), + _count: z.lazy(() => elementSchema).optional(), + countMax: positiveIntSchema.optional(), + _countMax: z.lazy(() => elementSchema).optional(), + duration: decimalSchema.optional(), + _duration: z.lazy(() => elementSchema).optional(), + durationMax: decimalSchema.optional(), + _durationMax: z.lazy(() => elementSchema).optional(), + durationUnit: z + .enum(['a', 'd', 'h', 'min', 'mo', 's', 'wk']) + .optional(), + _durationUnit: z.lazy(() => elementSchema).optional(), + frequency: positiveIntSchema.optional(), + _frequency: z.lazy(() => elementSchema).optional(), + frequencyMax: positiveIntSchema.optional(), + _frequencyMax: z.lazy(() => elementSchema).optional(), + period: decimalSchema.optional(), + _period: z.lazy(() => elementSchema).optional(), + periodMax: decimalSchema.optional(), + _periodMax: z.lazy(() => elementSchema).optional(), + periodUnit: z.enum(['a', 'd', 'h', 'min', 'mo', 's', 'wk']).optional(), + _periodUnit: z.lazy(() => elementSchema).optional(), + dayOfWeek: z + .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) + .nullable() + .array() + .min(1) + .optional(), + _dayOfWeek: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + timeOfDay: timeSchema.nullable().array().min(1).optional(), + _timeOfDay: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + when: z + .enum([ + 'AC', + 'ACD', + 'ACM', + 'ACV', + 'AFT', + 'AFT.early', + 'AFT.late', + 'C', + 'CD', + 'CM', + 'CV', + 'EVE', + 'EVE.early', + 'EVE.late', + 'HS', + 'MORN', + 'MORN.early', + 'MORN.late', + 'NIGHT', + 'NOON', + 'PC', + 'PCD', + 'PCM', + 'PCV', + 'PHS', + 'WAKE', + ]) + .nullable() + .array() + .min(1) + .optional(), + _when: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + offset: unsignedIntSchema.optional(), + _offset: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['boundsDuration'], + ['boundsRange'], + ['boundsPeriod'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + for (const group of [ + { name: 'dayOfWeek', shadow: '_dayOfWeek', required: false }, + { name: 'timeOfDay', shadow: '_timeOfDay', required: false }, + { name: 'when', shadow: '_when', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const timingSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - field: stringSchema, - _field: z.lazy(() => elementSchema).optional(), - value: stringSchema, - _value: z.lazy(() => elementSchema).optional(), - }), - ) - -export const testScriptSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('TestScript'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema, - _url: z.lazy(() => elementSchema).optional(), - identifier: z.lazy(() => identifierSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - origin: testScriptOriginSchema.array().optional(), - destination: testScriptDestinationSchema.array().optional(), - metadata: testScriptMetadataSchema.optional(), - fixture: testScriptFixtureSchema.array().optional(), - profile: z - .lazy(() => referenceSchema) - .array() - .optional(), - variable: testScriptVariableSchema.array().optional(), - setup: testScriptSetupSchema.optional(), - test: testScriptTestSchema.array().optional(), - teardown: testScriptTeardownSchema.optional(), - }), -) - -export const timingRepeatSchema: z.ZodType = z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - boundsDuration: z.lazy(() => durationSchema).optional(), - boundsRange: z.lazy(() => rangeSchema).optional(), - boundsPeriod: z.lazy(() => periodSchema).optional(), - count: positiveIntSchema.optional(), - _count: z.lazy(() => elementSchema).optional(), - countMax: positiveIntSchema.optional(), - _countMax: z.lazy(() => elementSchema).optional(), - duration: decimalSchema.optional(), - _duration: z.lazy(() => elementSchema).optional(), - durationMax: decimalSchema.optional(), - _durationMax: z.lazy(() => elementSchema).optional(), - durationUnit: z.enum(['a', 'd', 'h', 'min', 'mo', 's', 'wk']).optional(), - _durationUnit: z.lazy(() => elementSchema).optional(), - frequency: positiveIntSchema.optional(), - _frequency: z.lazy(() => elementSchema).optional(), - frequencyMax: positiveIntSchema.optional(), - _frequencyMax: z.lazy(() => elementSchema).optional(), - period: decimalSchema.optional(), - _period: z.lazy(() => elementSchema).optional(), - periodMax: decimalSchema.optional(), - _periodMax: z.lazy(() => elementSchema).optional(), - periodUnit: z.enum(['a', 'd', 'h', 'min', 'mo', 's', 'wk']).optional(), - _periodUnit: z.lazy(() => elementSchema).optional(), - dayOfWeek: z - .enum(['fri', 'mon', 'sat', 'sun', 'thu', 'tue', 'wed']) - .array() - .optional(), - _dayOfWeek: z + event: dateTimeSchema.nullable().array().min(1).optional(), + _event: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), - timeOfDay: timeSchema.array().optional(), - _timeOfDay: z - .lazy(() => elementSchema) + repeat: timingRepeatSchema.optional(), + code: z.lazy(() => codeableConceptSchema).optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'event', shadow: '_event', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const triggerDefinitionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: z.string().optional(), + extension: z + .lazy(() => extensionSchema) .array() + .min(1) .optional(), - when: z + type: z .enum([ - 'AC', - 'ACD', - 'ACM', - 'ACV', - 'AFT', - 'AFT.early', - 'AFT.late', - 'C', - 'CD', - 'CM', - 'CV', - 'EVE', - 'EVE.early', - 'EVE.late', - 'HS', - 'MORN', - 'MORN.early', - 'MORN.late', - 'NIGHT', - 'NOON', - 'PC', - 'PCD', - 'PCM', - 'PCV', - 'PHS', - 'WAKE', + 'data-access-ended', + 'data-accessed', + 'data-added', + 'data-changed', + 'data-modified', + 'data-removed', + 'named-event', + 'periodic', ]) - .array() .optional(), - _when: z - .lazy(() => elementSchema) + _type: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + timingTiming: z.lazy(() => timingSchema).optional(), + timingReference: z.lazy(() => referenceSchema).optional(), + timingDate: dateSchema.optional(), + _timingDate: z.lazy(() => elementSchema).optional(), + timingDateTime: dateTimeSchema.optional(), + _timingDateTime: z.lazy(() => elementSchema).optional(), + data: z + .lazy(() => dataRequirementSchema) .array() + .min(1) .optional(), - offset: unsignedIntSchema.optional(), - _offset: z.lazy(() => elementSchema).optional(), + condition: z.lazy(() => expressionSchema).optional(), }) .refine( (value) => [ { - names: ['boundsDuration', 'boundsRange', 'boundsPeriod'], + alternatives: [ + ['timingTiming'], + ['timingReference'], + ['timingDate', '_timingDate'], + ['timingDateTime', '_timingDateTime'], + ], required: false, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -31135,31 +48820,30 @@ export const timingRepeatSchema: z.ZodType = z.lazy(() => message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'type', shadow: '_type' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, ), -) - -export const timingSchema: z.ZodType = z.lazy(() => - z.strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - event: dateTimeSchema.array().optional(), - _event: z - .lazy(() => elementSchema) - .array() - .optional(), - repeat: timingRepeatSchema.optional(), - code: z.lazy(() => codeableConceptSchema).optional(), - }), -) - -export const triggerDefinitionSchema: z.ZodType = z.lazy( +) as unknown as z.ZodType> + +export const usageContextSchema: z.ZodType> = z.lazy( () => z .strictObject({ @@ -31167,47 +48851,32 @@ export const triggerDefinitionSchema: z.ZodType = z.lazy( extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - type: z.enum([ - 'data-access-ended', - 'data-accessed', - 'data-added', - 'data-changed', - 'data-modified', - 'data-removed', - 'named-event', - 'periodic', - ]), - _type: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - timingTiming: z.lazy(() => timingSchema).optional(), - timingReference: z.lazy(() => referenceSchema).optional(), - timingDate: dateSchema.optional(), - _timingDate: z.lazy(() => elementSchema).optional(), - timingDateTime: dateTimeSchema.optional(), - _timingDateTime: z.lazy(() => elementSchema).optional(), - data: z - .lazy(() => dataRequirementSchema) - .array() - .optional(), - condition: z.lazy(() => expressionSchema).optional(), + code: z.lazy(() => codingSchema), + valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), + valueQuantity: z.lazy(() => quantitySchema).optional(), + valueRange: z.lazy(() => rangeSchema).optional(), + valueReference: z.lazy(() => referenceSchema).optional(), }) .refine( (value) => [ { - names: [ - 'timingTiming', - 'timingReference', - 'timingDate', - 'timingDateTime', + alternatives: [ + ['valueCodeableConcept'], + ['valueQuantity'], + ['valueRange'], + ['valueReference'], ], - required: false, + required: true, }, ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, + const present = group.alternatives.filter((alternative) => + alternative.some( + (name) => + (value as Record)[name] !== undefined, + ), ).length return group.required ? present === 1 : present <= 1 }), @@ -31215,192 +48884,277 @@ export const triggerDefinitionSchema: z.ZodType = z.lazy( message: 'A choice element carries exactly one alternative when required, and at most one otherwise.', }, + ) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, + ), +) as unknown as z.ZodType> + +export const valueSetComposeSchema: z.ZodType> = + z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + lockedDate: dateSchema.optional(), + _lockedDate: z.lazy(() => elementSchema).optional(), + inactive: booleanSchema.optional(), + _inactive: z.lazy(() => elementSchema).optional(), + include: valueSetComposeIncludeSchema.array().min(1), + exclude: z + .lazy(() => valueSetComposeIncludeSchema) + .array() + .min(1) + .optional(), + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { + message: 'ele-1: an element must have a value or children beyond id.', + }, ), -) + ) as unknown as z.ZodType> -export const usageContextSchema: z.ZodType = z.lazy(() => +export const valueSetExpansionSchema: z.ZodType< + FhirJson +> = z.lazy(() => z .strictObject({ - id: z.string().optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - code: z.lazy(() => codingSchema), - valueCodeableConcept: z.lazy(() => codeableConceptSchema).optional(), - valueQuantity: z.lazy(() => quantitySchema).optional(), - valueRange: z.lazy(() => rangeSchema).optional(), - valueReference: z.lazy(() => referenceSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueCodeableConcept', - 'valueQuantity', - 'valueRange', - 'valueReference', - ], - required: true, - }, - ].every((group) => { - const present = group.names.filter( - (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), -) - -export const valueSetComposeSchema: z.ZodType = z.lazy( - () => - z.strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - lockedDate: dateSchema.optional(), - _lockedDate: z.lazy(() => elementSchema).optional(), - inactive: booleanSchema.optional(), - _inactive: z.lazy(() => elementSchema).optional(), - include: valueSetComposeIncludeSchema.array().min(1), - exclude: z - .lazy(() => valueSetComposeIncludeSchema) - .array() - .optional(), - }), -) - -export const valueSetExpansionSchema: z.ZodType = z.lazy( - () => - z.strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: uriSchema.optional(), _identifier: z.lazy(() => elementSchema).optional(), - timestamp: dateTimeSchema, + timestamp: dateTimeSchema.optional(), _timestamp: z.lazy(() => elementSchema).optional(), total: integerSchema.optional(), _total: z.lazy(() => elementSchema).optional(), offset: integerSchema.optional(), _offset: z.lazy(() => elementSchema).optional(), - parameter: valueSetExpansionParameterSchema.array().optional(), - contains: valueSetExpansionContainsSchema.array().optional(), - }), -) + parameter: valueSetExpansionParameterSchema.array().min(1).optional(), + contains: valueSetExpansionContainsSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'timestamp', shadow: '_timestamp' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const valueSetComposeIncludeSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const valueSetComposeIncludeSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), system: uriSchema.optional(), - _system: z.lazy(() => elementSchema).optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - concept: valueSetComposeIncludeConceptSchema.array().optional(), - filter: valueSetComposeIncludeFilterSchema.array().optional(), - valueSet: canonicalSchema.array().optional(), - _valueSet: z - .lazy(() => elementSchema) - .array() - .optional(), - }), - ) - -export const valueSetExpansionParameterSchema: z.ZodType = - z.lazy(() => - z - .strictObject({ - id: stringSchema.optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - name: stringSchema, - _name: z.lazy(() => elementSchema).optional(), - valueString: stringSchema.optional(), - _valueString: z.lazy(() => elementSchema).optional(), - valueBoolean: booleanSchema.optional(), - _valueBoolean: z.lazy(() => elementSchema).optional(), - valueInteger: integerSchema.optional(), - _valueInteger: z.lazy(() => elementSchema).optional(), - valueDecimal: decimalSchema.optional(), - _valueDecimal: z.lazy(() => elementSchema).optional(), - valueUri: uriSchema.optional(), - _valueUri: z.lazy(() => elementSchema).optional(), - valueCode: codeSchema.optional(), - _valueCode: z.lazy(() => elementSchema).optional(), - valueDateTime: dateTimeSchema.optional(), - _valueDateTime: z.lazy(() => elementSchema).optional(), - }) - .refine( - (value) => - [ - { - names: [ - 'valueString', - 'valueBoolean', - 'valueInteger', - 'valueDecimal', - 'valueUri', - 'valueCode', - 'valueDateTime', - ], - required: false, - }, - ].every((group) => { - const present = group.names.filter( + _system: z.lazy(() => elementSchema).optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + concept: valueSetComposeIncludeConceptSchema.array().min(1).optional(), + filter: valueSetComposeIncludeFilterSchema.array().min(1).optional(), + valueSet: canonicalSchema.nullable().array().min(1).optional(), + _valueSet: z + .lazy(() => elementSchema) + .nullable() + .array() + .min(1) + .optional(), + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'valueSet', shadow: '_valueSet', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const valueSetExpansionParameterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ + id: stringSchema.optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + valueString: stringSchema.optional(), + _valueString: z.lazy(() => elementSchema).optional(), + valueBoolean: booleanSchema.optional(), + _valueBoolean: z.lazy(() => elementSchema).optional(), + valueInteger: integerSchema.optional(), + _valueInteger: z.lazy(() => elementSchema).optional(), + valueDecimal: decimalSchema.optional(), + _valueDecimal: z.lazy(() => elementSchema).optional(), + valueUri: uriSchema.optional(), + _valueUri: z.lazy(() => elementSchema).optional(), + valueCode: codeSchema.optional(), + _valueCode: z.lazy(() => elementSchema).optional(), + valueDateTime: dateTimeSchema.optional(), + _valueDateTime: z.lazy(() => elementSchema).optional(), + }) + .refine( + (value) => + [ + { + alternatives: [ + ['valueString', '_valueString'], + ['valueBoolean', '_valueBoolean'], + ['valueInteger', '_valueInteger'], + ['valueDecimal', '_valueDecimal'], + ['valueUri', '_valueUri'], + ['valueCode', '_valueCode'], + ['valueDateTime', '_valueDateTime'], + ], + required: false, + }, + ].every((group) => { + const present = group.alternatives.filter((alternative) => + alternative.some( (name) => (value as Record)[name] !== undefined, - ).length - return group.required ? present === 1 : present <= 1 - }), - { - message: - 'A choice element carries exactly one alternative when required, and at most one otherwise.', - }, - ), - ) + ), + ).length + return group.required ? present === 1 : present <= 1 + }), + { + message: + 'A choice element carries exactly one alternative when required, and at most one otherwise.', + }, + ) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'name', shadow: '_name' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const valueSetExpansionContainsSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const valueSetExpansionContainsSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), system: uriSchema.optional(), _system: z.lazy(() => elementSchema).optional(), @@ -31417,175 +49171,296 @@ export const valueSetExpansionContainsSchema: z.ZodType valueSetComposeIncludeConceptDesignationSchema) .array() + .min(1) .optional(), contains: z .lazy(() => valueSetExpansionContainsSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const valueSetComposeIncludeConceptSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const valueSetComposeIncludeConceptSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - code: codeSchema, + code: codeSchema.optional(), _code: z.lazy(() => elementSchema).optional(), display: stringSchema.optional(), _display: z.lazy(() => elementSchema).optional(), designation: valueSetComposeIncludeConceptDesignationSchema .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'code', shadow: '_code' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const valueSetComposeIncludeFilterSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const valueSetComposeIncludeFilterSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - property: codeSchema, + property: codeSchema.optional(), _property: z.lazy(() => elementSchema).optional(), - op: z.enum([ - '=', - 'descendent-of', - 'exists', - 'generalizes', - 'in', - 'is-a', - 'is-not-a', - 'not-in', - 'regex', - ]), + op: z + .enum([ + '=', + 'descendent-of', + 'exists', + 'generalizes', + 'in', + 'is-a', + 'is-not-a', + 'not-in', + 'regex', + ]) + .optional(), _op: z.lazy(() => elementSchema).optional(), - value: stringSchema, + value: stringSchema.optional(), _value: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'property', shadow: '_property' }, + { name: 'op', shadow: '_op' }, + { name: 'value', shadow: '_value' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const valueSetComposeIncludeConceptDesignationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const valueSetComposeIncludeConceptDesignationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), language: codeSchema.optional(), _language: z.lazy(() => elementSchema).optional(), use: z.lazy(() => codingSchema).optional(), - value: stringSchema, + value: stringSchema.optional(), _value: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'value', shadow: '_value' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const valueSetSchema: z.ZodType = z.lazy(() => - z.strictObject({ - resourceType: z.literal('ValueSet'), - id: idSchema.optional(), - _id: z.lazy(() => elementSchema).optional(), - meta: z.lazy(() => metaSchema).optional(), - implicitRules: uriSchema.optional(), - _implicitRules: z.lazy(() => elementSchema).optional(), - language: codeSchema.optional(), - _language: z.lazy(() => elementSchema).optional(), - text: z.lazy(() => narrativeSchema).optional(), - contained: z - .lazy(() => resourceSchema) - .array() - .optional(), - extension: z - .lazy(() => extensionSchema) - .array() - .optional(), - modifierExtension: z - .lazy(() => extensionSchema) - .array() - .optional(), - url: uriSchema.optional(), - _url: z.lazy(() => elementSchema).optional(), - identifier: z - .lazy(() => identifierSchema) - .array() - .optional(), - version: stringSchema.optional(), - _version: z.lazy(() => elementSchema).optional(), - name: stringSchema.optional(), - _name: z.lazy(() => elementSchema).optional(), - title: stringSchema.optional(), - _title: z.lazy(() => elementSchema).optional(), - status: z.enum(['active', 'draft', 'retired', 'unknown']), - _status: z.lazy(() => elementSchema).optional(), - experimental: booleanSchema.optional(), - _experimental: z.lazy(() => elementSchema).optional(), - date: dateTimeSchema.optional(), - _date: z.lazy(() => elementSchema).optional(), - publisher: stringSchema.optional(), - _publisher: z.lazy(() => elementSchema).optional(), - contact: z - .lazy(() => contactDetailSchema) - .array() - .optional(), - description: markdownSchema.optional(), - _description: z.lazy(() => elementSchema).optional(), - useContext: z - .lazy(() => usageContextSchema) - .array() - .optional(), - jurisdiction: z - .lazy(() => codeableConceptSchema) - .array() - .optional(), - immutable: booleanSchema.optional(), - _immutable: z.lazy(() => elementSchema).optional(), - purpose: markdownSchema.optional(), - _purpose: z.lazy(() => elementSchema).optional(), - copyright: markdownSchema.optional(), - _copyright: z.lazy(() => elementSchema).optional(), - compose: valueSetComposeSchema.optional(), - expansion: valueSetExpansionSchema.optional(), - }), -) - -export const verificationResultPrimarySourceSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const valueSetSchema: z.ZodType> = z.lazy(() => + z + .strictObject({ + resourceType: z.literal('ValueSet'), + id: idSchema.optional(), + _id: z.lazy(() => elementSchema).optional(), + meta: z.lazy(() => metaSchema).optional(), + implicitRules: uriSchema.optional(), + _implicitRules: z.lazy(() => elementSchema).optional(), + language: codeSchema.optional(), + _language: z.lazy(() => elementSchema).optional(), + text: z.lazy(() => narrativeSchema).optional(), + contained: z + .lazy(() => resourceSchema) + .array() + .min(1) + .optional(), + extension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + modifierExtension: z + .lazy(() => extensionSchema) + .array() + .min(1) + .optional(), + url: uriSchema.optional(), + _url: z.lazy(() => elementSchema).optional(), + identifier: z + .lazy(() => identifierSchema) + .array() + .min(1) + .optional(), + version: stringSchema.optional(), + _version: z.lazy(() => elementSchema).optional(), + name: stringSchema.optional(), + _name: z.lazy(() => elementSchema).optional(), + title: stringSchema.optional(), + _title: z.lazy(() => elementSchema).optional(), + status: z.enum(['active', 'draft', 'retired', 'unknown']).optional(), + _status: z.lazy(() => elementSchema).optional(), + experimental: booleanSchema.optional(), + _experimental: z.lazy(() => elementSchema).optional(), + date: dateTimeSchema.optional(), + _date: z.lazy(() => elementSchema).optional(), + publisher: stringSchema.optional(), + _publisher: z.lazy(() => elementSchema).optional(), + contact: z + .lazy(() => contactDetailSchema) + .array() + .min(1) + .optional(), + description: markdownSchema.optional(), + _description: z.lazy(() => elementSchema).optional(), + useContext: z + .lazy(() => usageContextSchema) + .array() + .min(1) + .optional(), + jurisdiction: z + .lazy(() => codeableConceptSchema) + .array() + .min(1) + .optional(), + immutable: booleanSchema.optional(), + _immutable: z.lazy(() => elementSchema).optional(), + purpose: markdownSchema.optional(), + _purpose: z.lazy(() => elementSchema).optional(), + copyright: markdownSchema.optional(), + _copyright: z.lazy(() => elementSchema).optional(), + compose: valueSetComposeSchema.optional(), + expansion: valueSetExpansionSchema.optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> + +export const verificationResultPrimarySourceSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), who: z.lazy(() => referenceSchema).optional(), type: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), communicationMethod: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), validationStatus: z.lazy(() => codeableConceptSchema).optional(), validationDate: dateTimeSchema.optional(), @@ -31594,21 +49469,30 @@ export const verificationResultPrimarySourceSchema: z.ZodType codeableConceptSchema) .array() + .min(1) .optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const verificationResultAttestationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const verificationResultAttestationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), who: z.lazy(() => referenceSchema).optional(), onBehalfOf: z.lazy(() => referenceSchema).optional(), @@ -31621,31 +49505,45 @@ export const verificationResultAttestationSchema: z.ZodType elementSchema).optional(), proxySignature: z.lazy(() => signatureSchema).optional(), sourceSignature: z.lazy(() => signatureSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const verificationResultValidatorSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const verificationResultValidatorSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), organization: z.lazy(() => referenceSchema), identityCertificate: stringSchema.optional(), _identityCertificate: z.lazy(() => elementSchema).optional(), attestationSignature: z.lazy(() => signatureSchema).optional(), - }), - ) + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const verificationResultSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const verificationResultSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('VerificationResult'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -31658,33 +49556,41 @@ export const verificationResultSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), target: z .lazy(() => referenceSchema) .array() + .min(1) .optional(), - targetLocation: stringSchema.array().optional(), + targetLocation: stringSchema.nullable().array().min(1).optional(), _targetLocation: z .lazy(() => elementSchema) + .nullable() .array() + .min(1) .optional(), need: z.lazy(() => codeableConceptSchema).optional(), - status: z.enum([ - 'attested', - 'in-process', - 'req-revalid', - 'reval-fail', - 'val-fail', - 'validated', - ]), + status: z + .enum([ + 'attested', + 'in-process', + 'req-revalid', + 'reval-fail', + 'val-fail', + 'validated', + ]) + .optional(), _status: z.lazy(() => elementSchema).optional(), statusDate: dateTimeSchema.optional(), _statusDate: z.lazy(() => elementSchema).optional(), @@ -31692,6 +49598,7 @@ export const verificationResultSchema: z.ZodType = validationProcess: z .lazy(() => codeableConceptSchema) .array() + .min(1) .optional(), frequency: z.lazy(() => timingSchema).optional(), lastPerformed: dateTimeSchema.optional(), @@ -31699,26 +49606,98 @@ export const verificationResultSchema: z.ZodType = nextScheduled: dateSchema.optional(), _nextScheduled: z.lazy(() => elementSchema).optional(), failureAction: z.lazy(() => codeableConceptSchema).optional(), - primarySource: verificationResultPrimarySourceSchema.array().optional(), + primarySource: verificationResultPrimarySourceSchema + .array() + .min(1) + .optional(), attestation: verificationResultAttestationSchema.optional(), - validator: verificationResultValidatorSchema.array().optional(), - }), - ) + validator: verificationResultValidatorSchema.array().min(1).optional(), + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'status', shadow: '_status' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .superRefine((value, context) => { + for (const group of [ + { name: 'targetLocation', shadow: '_targetLocation', required: false }, + ]) { + const record = value as Record + const values = record[group.name] as unknown[] | undefined + const shadows = record[group.shadow] as unknown[] | undefined + if (group.required && values === undefined && shadows === undefined) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required repeating primitive needs values or primitive metadata.', + }) + continue + } + if ( + values !== undefined && + shadows !== undefined && + values.length !== shadows.length + ) { + context.addIssue({ + code: 'custom', + path: [group.shadow], + message: + 'FHIR primitive value and metadata arrays must have equal length.', + }) + continue + } + const length = values?.length ?? shadows?.length ?? 0 + for (let index = 0; index < length; index += 1) { + if ( + (values?.[index] ?? null) === null && + (shadows?.[index] ?? null) === null + ) { + context.addIssue({ + code: 'custom', + path: [group.name, index], + message: + 'A repeating primitive slot needs a value or primitive metadata.', + }) + } + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const visionPrescriptionLensSpecificationSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const visionPrescriptionLensSpecificationSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), product: z.lazy(() => codeableConceptSchema), - eye: z.enum(['left', 'right']), + eye: z.enum(['left', 'right']).optional(), _eye: z.lazy(() => elementSchema).optional(), sphere: decimalSchema.optional(), _sphere: z.lazy(() => elementSchema).optional(), @@ -31726,7 +49705,10 @@ export const visionPrescriptionLensSpecificationSchema: z.ZodType elementSchema).optional(), axis: integerSchema.optional(), _axis: z.lazy(() => elementSchema).optional(), - prism: visionPrescriptionLensSpecificationPrismSchema.array().optional(), + prism: visionPrescriptionLensSpecificationPrismSchema + .array() + .min(1) + .optional(), add: decimalSchema.optional(), _add: z.lazy(() => elementSchema).optional(), power: decimalSchema.optional(), @@ -31743,32 +49725,84 @@ export const visionPrescriptionLensSpecificationSchema: z.ZodType annotationSchema) .array() + .min(1) .optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [{ name: 'eye', shadow: '_eye' }]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> -export const visionPrescriptionLensSpecificationPrismSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const visionPrescriptionLensSpecificationPrismSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ id: stringSchema.optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), - amount: decimalSchema, + amount: decimalSchema.optional(), _amount: z.lazy(() => elementSchema).optional(), - base: z.enum(['down', 'in', 'out', 'up']), + base: z.enum(['down', 'in', 'out', 'up']).optional(), _base: z.lazy(() => elementSchema).optional(), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'amount', shadow: '_amount' }, + { name: 'base', shadow: '_base' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType< + FhirJson +> -export const visionPrescriptionSchema: z.ZodType = - z.lazy(() => - z.strictObject({ +export const visionPrescriptionSchema: z.ZodType< + FhirJson +> = z.lazy(() => + z + .strictObject({ resourceType: z.literal('VisionPrescription'), id: idSchema.optional(), _id: z.lazy(() => elementSchema).optional(), @@ -31781,33 +49815,63 @@ export const visionPrescriptionSchema: z.ZodType = contained: z .lazy(() => resourceSchema) .array() + .min(1) .optional(), extension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), modifierExtension: z .lazy(() => extensionSchema) .array() + .min(1) .optional(), identifier: z .lazy(() => identifierSchema) .array() + .min(1) + .optional(), + status: z + .enum(['active', 'cancelled', 'draft', 'entered-in-error']) .optional(), - status: z.enum(['active', 'cancelled', 'draft', 'entered-in-error']), _status: z.lazy(() => elementSchema).optional(), - created: dateTimeSchema, + created: dateTimeSchema.optional(), _created: z.lazy(() => elementSchema).optional(), patient: z.lazy(() => referenceSchema), encounter: z.lazy(() => referenceSchema).optional(), - dateWritten: dateTimeSchema, + dateWritten: dateTimeSchema.optional(), _dateWritten: z.lazy(() => elementSchema).optional(), prescriber: z.lazy(() => referenceSchema), lensSpecification: visionPrescriptionLensSpecificationSchema .array() .min(1), - }), - ) + }) + .superRefine((value, context) => { + const record = value as Record + for (const group of [ + { name: 'status', shadow: '_status' }, + { name: 'created', shadow: '_created' }, + { name: 'dateWritten', shadow: '_dateWritten' }, + ]) { + if ( + record[group.name] === undefined && + record[group.shadow] === undefined + ) { + context.addIssue({ + code: 'custom', + path: [group.name], + message: + 'A required primitive needs a value or primitive metadata.', + }) + } + } + }) + .refine( + (value) => Object.keys(value as object).some((key) => key !== 'id'), + { message: 'ele-1: an element must have a value or children beyond id.' }, + ), +) as unknown as z.ZodType> export const base64BinarySchema: z.ZodType = z .string() @@ -31815,15 +49879,20 @@ export const base64BinarySchema: z.ZodType = z new RegExp('^[0-9a-zA-Z+/= \\t\\n\\r]*$'), 'Expected a FHIR base64Binary.', ) - .refine((value) => value.replace(/[ \t\n\r]/gu, '').length % 4 === 0, { - message: 'Expected a FHIR base64Binary.', - }) + .refine( + (value) => { + const length = value.replace(/[ \t\n\r]/gu, '').length + return length > 0 && length % 4 === 0 + }, + { message: 'Expected a FHIR base64Binary.' }, + ) export const booleanSchema: z.ZodType = z.boolean() export const canonicalSchema: z.ZodType = z .string() .regex(new RegExp('^(?:[^ \\t\\n\\r]*)$'), 'Expected a FHIR canonical.') + .min(1) export const codeSchema: z.ZodType = z .string() @@ -31840,6 +49909,9 @@ export const dateSchema: z.ZodType = z ), 'Expected a FHIR date.', ) + .refine(hasValidFhirCalendarDate, { + message: 'Expected a real Gregorian calendar date for FHIR date.', + }) export const dateTimeSchema: z.ZodType = z .string() @@ -31849,6 +49921,9 @@ export const dateTimeSchema: z.ZodType = z ), 'Expected a FHIR dateTime.', ) + .refine(hasValidFhirCalendarDate, { + message: 'Expected a real Gregorian calendar date for FHIR dateTime.', + }) export const decimalSchema: z.ZodType = z .number() @@ -31874,6 +49949,9 @@ export const instantSchema: z.ZodType = z ), 'Expected a FHIR instant.', ) + .refine(hasValidFhirCalendarDate, { + message: 'Expected a real Gregorian calendar date for FHIR instant.', + }) export const integerSchema: z.ZodType = z .number() @@ -31935,10 +50013,12 @@ export const unsignedIntSchema: z.ZodType = z export const uriSchema: z.ZodType = z .string() .regex(new RegExp('^(?:[^ \\t\\n\\r]*)$'), 'Expected a FHIR uri.') + .min(1) export const urlSchema: z.ZodType = z .string() .regex(new RegExp('^(?:[^ \\t\\n\\r]*)$'), 'Expected a FHIR url.') + .min(1) export const uuidSchema: z.ZodType = z .string() @@ -31949,7 +50029,7 @@ export const uuidSchema: z.ZodType = z 'Expected a FHIR uuid.', ) -export const xhtmlSchema: z.ZodType = z.string() +export const xhtmlSchema: z.ZodType = z.string().min(1) const modelledResources: Readonly> = { Account: accountSchema, @@ -32097,8 +50177,8 @@ const modelledResources: Readonly> = { const dispatchingResource = new WeakSet() -export const resourceSchema: z.ZodType = z - .custom( +export const resourceSchema: z.ZodType> = z + .custom>( (value) => typeof value === 'object' && value !== null && @@ -32110,9 +50190,14 @@ export const resourceSchema: z.ZodType = z ) .superRefine((value, ctx) => { const modelled = modelledResources[value.resourceType] - // A resource type the release defines but this package does not model keeps the open - // shape; reporting it as invalid would reject a conformant bundle. - if (modelled === undefined) return + if (modelled === undefined) { + ctx.addIssue({ + code: 'custom', + path: ['resourceType'], + message: 'Resource type is not published by this pinned FHIR release.', + }) + return + } // JSON cannot describe a resource that contains itself, but an in-memory object can, // and re-entering one would recur until the stack ran out. if (dispatchingResource.has(value)) return @@ -32128,6 +50213,23 @@ export const resourceSchema: z.ZodType = z } }) +/** Exact pinned release material and generated closure behind this module. */ +export const STRUCTURAL_SCHEMA_SOURCE = { + packageId: 'hl7.fhir.r4b.core', + packageVersion: '4.3.0', + fhirVersion: '4.3.0', + archiveSha512: + '943ac3376cd2fca94d75c26e43f2609c192723d180240b197517c3dcb757cc258c727a6f69cca7f82f7fc7540779e96d1a2eec17ff8048945b79315b01a16933', + structureCount: 201, +} as const + +/** Structural wire-boundary capabilities and deliberate limits. */ +export const STRUCTURAL_SCHEMA_CAPABILITIES = { + fullFhirPath: false, + preservesDecimalLexemesAfterJsonParse: false, + normativeConformanceValidator: 'official-fhir-validator', +} as const + /** * Invariants the release states for these paths that this module does not check. * @@ -32152,7 +50254,7 @@ export const UNCHECKED_CONSTRAINTS: Readonly< 'AdministrableProductDefinition.contained': ['dom-r4b'], AdverseEvent: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], 'AdverseEvent.contained': ['dom-r4b'], - Age: ['age-1', 'qty-3'], + Age: ['age-1'], AllergyIntolerance: [ 'ait-1', 'ait-2', @@ -32283,7 +50385,7 @@ export const UNCHECKED_CONSTRAINTS: Readonly< ContactPoint: ['cpt-2'], Contract: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], 'Contract.contained': ['dom-r4b'], - Count: ['cnt-3', 'qty-3'], + Count: ['cnt-3'], Coverage: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], 'Coverage.contained': ['dom-r4b'], CoverageEligibilityRequest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], @@ -32307,12 +50409,12 @@ export const UNCHECKED_CONSTRAINTS: Readonly< 'DeviceUseStatement.contained': ['dom-r4b'], DiagnosticReport: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], 'DiagnosticReport.contained': ['dom-r4b'], - Distance: ['dis-1', 'qty-3'], + Distance: ['dis-1'], DocumentManifest: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], 'DocumentManifest.contained': ['dom-r4b'], DocumentReference: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], 'DocumentReference.contained': ['dom-r4b'], - Duration: ['drt-1', 'qty-3'], + Duration: ['drt-1'], ElementDefinition: [ 'eld-11', 'eld-13', @@ -32516,7 +50618,6 @@ export const UNCHECKED_CONSTRAINTS: Readonly< 'Procedure.contained': ['dom-r4b'], Provenance: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], 'Provenance.contained': ['dom-r4b'], - Quantity: ['qty-3'], Questionnaire: [ 'dom-2', 'dom-3', @@ -32549,7 +50650,6 @@ export const UNCHECKED_CONSTRAINTS: Readonly< Range: ['rng-2'], Ratio: ['rat-1'], RatioRange: ['inv-1', 'inv-2'], - Reference: ['ref-1'], RegulatedAuthorization: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], 'RegulatedAuthorization.contained': ['dom-r4b'], RelatedPerson: ['dom-2', 'dom-3', 'dom-4', 'dom-5', 'dom-6'], diff --git a/packages/fhir/src/zod/support.ts b/packages/fhir/src/zod/support.ts index 69e1aed7..778545ba 100644 --- a/packages/fhir/src/zod/support.ts +++ b/packages/fhir/src/zod/support.ts @@ -6,17 +6,92 @@ // SPDX-License-Identifier: MIT // +interface ComparableFhirDateTime { + readonly epochSecond: number + /** Leap seconds sort after the preceding second and before the next POSIX second. */ + readonly phase: 0 | 1 + readonly fraction: string +} + +const FHIR_TIMESTAMP = + /^(?\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:)(?[0-5]\d|60)(?:\.(?\d+))?(?Z|[+-]\d{2}:\d{2})$/u +const FHIR_PARTIAL_DATE = /^\d{4}(?:-\d{2}(?:-\d{2})?)?$/u + +const comparableFhirDateTime = ( + value: string, +): ComparableFhirDateTime | undefined => { + const timestamp = FHIR_TIMESTAMP.exec(value)?.groups + if (timestamp !== undefined) { + const leap = timestamp.second === '60' + const parsed = Date.parse( + `${timestamp.prefix}${leap ? '59' : timestamp.second}${timestamp.offset}`, + ) + if (!Number.isFinite(parsed)) return undefined + return { + epochSecond: parsed / 1000 + (leap ? 1 : 0), + phase: leap ? 0 : 1, + fraction: timestamp.fraction ?? '', + } + } + if (!FHIR_PARTIAL_DATE.test(value)) return undefined + let suffix = '' + if (value.length === 4) suffix = '-01-01' + else if (value.length === 7) suffix = '-01' + const parsed = Date.parse(`${value}${suffix}T00:00:00Z`) + return Number.isFinite(parsed) ? + { epochSecond: parsed / 1000, phase: 1, fraction: '' } + : undefined +} + +const compareFraction = (left: string, right: string): -1 | 0 | 1 => { + const width = Math.max(left.length, right.length) + const normalizedLeft = left.padEnd(width, '0') + const normalizedRight = right.padEnd(width, '0') + if (normalizedLeft === normalizedRight) return 0 + return normalizedLeft < normalizedRight ? -1 : 1 +} + /** - * A FHIR dateTime as an epoch value that orders correctly. - * - * R4 admits a leap second, which no JavaScript date parser accepts; mapping it to `:59` keeps the - * instant within the second it belongs to, which is all an ordering comparison needs. A value - * stated to lower precision parses at the start of the period it names, which is the comparison - * FHIR intends. + * Compares FHIR dateTime values without JavaScript Date's millisecond truncation. * - * Returns `NaN` for a value the primitive's own pattern would already have rejected; a caller - * comparing two endpoints treats that as "not comparable" rather than as "out of order", so a - * malformed date is reported as malformed and not as an ordering fault. + * Partial values resolve to the start of the period they name, matching Grove's release-owned + * ordering policy. Leap seconds retain their distinct position before the following POSIX second. + * `undefined` means an endpoint is malformed; primitive validation reports that defect separately. + */ +export const compareFhirDateTimes = ( + left: string, + right: string, +): -1 | 0 | 1 | undefined => { + const first = comparableFhirDateTime(left) + const second = comparableFhirDateTime(right) + if (first === undefined || second === undefined) return undefined + if (first.epochSecond !== second.epochSecond) { + return first.epochSecond < second.epochSecond ? -1 : 1 + } + if (first.phase !== second.phase) return first.phase < second.phase ? -1 : 1 + return compareFraction(first.fraction, second.fraction) +} + +const FULL_FHIR_DATE = /^(\d{4})-(\d{2})-(\d{2})(?:T|$)/u + +const isLeapYear = (year: number): boolean => + year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) + +const daysInMonth = (year: number, month: number): number => { + if (month === 2) return isLeapYear(year) ? 29 : 28 + if ([4, 6, 9, 11].includes(month)) return 30 + return 31 +} + +/** + * Complements the release-owned primitive regex with Gregorian calendar validity. + * Partial FHIR dates have no day to validate and remain valid at this layer. */ -export const fhirDateTimeToEpoch = (value: string): number => - Date.parse(value.replace(/:60(?=\.|Z|[+-]|$)/u, ':59')) +export const hasValidFhirCalendarDate = (value: string): boolean => { + const match = FULL_FHIR_DATE.exec(value) + if (match === null) return true + const year = Number(match[1]) + const month = Number(match[2]) + const day = Number(match[3]) + return day >= 1 && day <= daysInMonth(year, month) +} diff --git a/packages/fhir/test/exchange-corpus.test.ts b/packages/fhir/test/exchange-corpus.test.ts new file mode 100644 index 00000000..e0d5cf6d --- /dev/null +++ b/packages/fhir/test/exchange-corpus.test.ts @@ -0,0 +1,629 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/* eslint-disable sonarjs/no-clear-text-protocols -- The FHIR R4 lifecycle and terminology canonicals are normative HTTP URIs. */ + +import { readFileSync } from 'node:fs' +import { + parseGroveMobileExchangeBundle, + parseGroveMobileRetractionBundle, +} from '../src/r4/index.js' + +interface JsonPatchOperation { + readonly op: 'add' | 'remove' | 'replace' + readonly path: string + readonly value?: unknown +} + +interface CorpusCase { + readonly id: string + readonly base: 'mobile-exchange' | 'mobile-retraction' + readonly patch: readonly JsonPatchOperation[] + readonly expectedRule: { + readonly code: string + readonly reason: string + readonly location: string + readonly severity: 'error' + } +} + +interface ExchangeCorpus { + readonly schemaVersion: 1 + readonly cases: readonly CorpusCase[] +} + +const fixture = (name: string): unknown => + JSON.parse( + readFileSync( + new URL( + `../fixtures/conformance/mobile-exchange/${name}`, + import.meta.url, + ), + 'utf8', + ), + ) + +const bases = { + 'mobile-exchange': fixture('exchange-bundle.json'), + 'mobile-retraction': fixture('retraction-bundle.json'), +} as const +const corpus = fixture('corpus.json') as ExchangeCorpus + +const pointerComponents = (pointer: string): string[] => + pointer + .slice(1) + .split('/') + .map((component) => component.replaceAll('~1', '/').replaceAll('~0', '~')) + +const patched = ( + base: unknown, + operations: readonly JsonPatchOperation[], +): unknown => { + const result = structuredClone(base) + for (const operation of operations) { + const components = pointerComponents(operation.path) + const property = components.pop() + if (property === undefined) + throw new Error('A JSON Patch path is required.') + let parent = result as Record | unknown[] + for (const component of components) { + const next = + Array.isArray(parent) ? parent[Number(component)] : parent[component] + if (typeof next !== 'object' || next === null) { + throw new Error(`Patch path does not resolve: ${operation.path}`) + } + parent = next as Record | unknown[] + } + if (Array.isArray(parent)) { + const index = Number(property) + if (operation.op === 'remove') parent.splice(index, 1) + else if (operation.op === 'add') parent.splice(index, 0, operation.value) + else parent[index] = operation.value + } else if (operation.op === 'remove') { + Reflect.deleteProperty(parent, property) + } else { + parent[property] = operation.value + } + } + return result +} + +describe('shared Grove Mobile exchange corpus', () => { + it('accepts both normative positive graphs', () => { + expect(parseGroveMobileExchangeBundle(bases['mobile-exchange']).ok).toBe( + true, + ) + expect( + parseGroveMobileRetractionBundle(bases['mobile-retraction']).ok, + ).toBe(true) + }) + + it('accepts a directly claimed HealthKit application snapshot with its product identifier', () => { + const input = patched(bases['mobile-exchange'], [ + { + op: 'replace', + path: '/entry/1/resource/meta/profile/0', + value: + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-application-device', + }, + { + op: 'add', + path: '/entry/1/resource/identifier/1', + value: { + type: { + coding: [ + { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-identifier-type', + code: 'apple-bundle-id', + }, + ], + }, + system: + 'https://grovealliance.org/fhir/healthkit/NamingSystem/apple-bundle-id', + value: 'org.example.study', + }, + }, + ]) + expect(parseGroveMobileExchangeBundle(input).ok).toBe(true) + }) + + it('does not mistake an Expression.reference URI for a FHIR Reference', () => { + const input = patched(bases['mobile-exchange'], [ + { + op: 'add', + path: '/entry/2/resource/extension', + value: [ + { + url: 'https://example.org/fhir/StructureDefinition/rule-expression', + valueExpression: { + language: 'text/fhirpath', + reference: 'https://example.org/fhir/Library/measurement-rule', + }, + }, + ], + }, + ]) + expect(parseGroveMobileExchangeBundle(input).ok).toBe(true) + }) + + it('reports the actual unresolved reference path instead of assuming subject', () => { + const input = patched(bases['mobile-exchange'], [ + { + op: 'add', + path: '/entry/2/resource/derivedFrom', + value: [{ reference: 'urn:uuid:00000000-0000-5000-8000-000000000000' }], + }, + ]) + const result = parseGroveMobileExchangeBundle(input) + expect(result.ok).toBe(false) + if (result.ok) return + expect( + result.issues.find( + ({ code }) => code === 'mobile-exchange.resolved-reference', + )?.location, + ).toBe('Bundle.entry[2].resource.derivedFrom[0].reference') + }) + + it('keeps Grove Identifier systems dedicated to one role per graph', () => { + const sourceSystem = ( + bases['mobile-exchange'] as { + entry: Array<{ + resource: { identifier?: Array<{ system: string }> } + }> + } + ).entry[2]?.resource.identifier?.[0]?.system + const input = patched(bases['mobile-exchange'], [ + { + op: 'replace', + path: '/entry/2/resource/identifier/1/system', + value: sourceSystem, + }, + ]) + const result = parseGroveMobileExchangeBundle(input) + expect(result.ok).toBe(false) + if (result.ok) return + expect(result.issues).toContainEqual( + expect.objectContaining({ + code: 'mobile-exchange.identity-system-role', + }), + ) + }) + + it('requires the exact native Recording Document beside a SensorKit ECG', () => { + const input = patched(bases['mobile-exchange'], [ + { + op: 'replace', + path: '/entry/2/resource/meta/profile', + value: [ + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-ecg-observation', + 'https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-ecg-observation', + ], + }, + ]) + const result = parseGroveMobileExchangeBundle(input) + expect(result.ok).toBe(false) + if (result.ok) return + expect(result.issues).toContainEqual( + expect.objectContaining({ code: 'mobile-output.hybrid-companion' }), + ) + }) + + it('rejects catalog-owned adapter source markers on source-neutral output', () => { + const input = patched(bases['mobile-exchange'], [ + { + op: 'add', + path: '/entry/2/resource/extension/1', + value: { + url: 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-source-type-extension', + valueCode: 'HKQuantityTypeIdentifierHeartRate', + }, + }, + ]) + const result = parseGroveMobileExchangeBundle(input) + expect(result.ok).toBe(false) + if (!result.ok) { + expect( + result.issues.some(({ message }) => + message.includes('mobile-exchange.adapter-source-marker'), + ), + ).toBe(true) + } + }) + + it('requires the exact catalog source marker on an adapter-profiled output', () => { + const input = patched(bases['mobile-exchange'], [ + { + op: 'add', + path: '/entry/2/resource/meta/profile/1', + value: + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-observation', + }, + { + op: 'replace', + path: '/entry/3/resource/meta/profile/0', + value: + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-conversion-provenance', + }, + ]) + const result = parseGroveMobileExchangeBundle(input) + expect(result.ok).toBe(false) + if (!result.ok) { + expect( + result.issues.some(({ message }) => + message.includes('mobile-exchange.adapter-source-marker'), + ), + ).toBe(true) + } + }) + + it('represents Health Connect DataOrigin.packageName as a logical Device agent, never a Bundle Device', () => { + const healthConnectGraph = patched(bases['mobile-exchange'], [ + { + op: 'add', + path: '/entry/2/resource/meta/profile/1', + value: + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-observation', + }, + { + op: 'add', + path: '/entry/2/resource/extension/1', + value: { + url: 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-record-type', + valueCode: 'HeartRateRecord', + }, + }, + { + op: 'replace', + path: '/entry/3/resource/meta/profile/0', + value: + 'https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-conversion-provenance', + }, + { + op: 'add', + path: '/entry/3/resource/entity/0/agent', + value: [ + { + type: { + coding: [ + { + system: + 'http://terminology.hl7.org/CodeSystem/provenance-participant-type', + code: 'enterer', + }, + ], + }, + who: { + type: 'Device', + identifier: { + system: + 'https://grovealliance.org/fhir/health-connect/NamingSystem/android-package-name', + value: 'org.example.health.writer', + }, + }, + }, + ], + }, + ]) + expect(parseGroveMobileExchangeBundle(healthConnectGraph).ok).toBe(true) + + const literalDataOrigin = patched(healthConnectGraph, [ + { + op: 'add', + path: '/entry/3/resource/entity/0/agent/0/who/reference', + value: 'urn:uuid:8f87a88a-8744-5116-8901-9274f62472ac', + }, + ]) + const result = parseGroveMobileExchangeBundle(literalDataOrigin) + expect(result.ok).toBe(false) + if (!result.ok) { + expect( + result.issues.some(({ message }) => + message.includes('health-connect.data-origin-application'), + ), + ).toBe(true) + } + }) + + it.each(corpus.cases)( + 'rejects $id with stable producer rule $expectedRule.code', + ({ base, patch, expectedRule }) => { + const input = patched(bases[base], patch) + const result = + base === 'mobile-exchange' ? + parseGroveMobileExchangeBundle(input) + : parseGroveMobileRetractionBundle(input) + expect(result.ok).toBe(false) + if (result.ok) return + const emitted = result.issues.find( + ({ code }) => code === expectedRule.code, + ) + expect(emitted).toBeDefined() + expect( + emitted === undefined ? undefined : ( + { + code: emitted.code, + reason: emitted.reason, + location: emitted.location, + severity: emitted.severity, + } + ), + ).toEqual(expectedRule) + }, + ) + + it.each([ + { + name: 'missing quantity value', + patch: [ + { + op: 'remove' as const, + path: '/entry/2/resource/valueQuantity/value', + }, + ], + }, + { + name: 'impossible effective calendar date', + patch: [ + { + op: 'replace' as const, + path: '/entry/2/resource/effectiveDateTime', + value: '2026-02-30T08:30:00-07:00', + }, + ], + }, + { + name: 'duplicated primary clinical coding', + patch: [ + { + op: 'add' as const, + path: '/entry/2/resource/code/coding/1', + value: { system: 'http://loinc.org', code: '8867-4' }, + }, + ], + }, + { + name: 'duplicated category coding', + patch: [ + { + op: 'add' as const, + path: '/entry/2/resource/category/0/coding/1', + value: { + system: + 'http://terminology.hl7.org/CodeSystem/observation-category', + code: 'vital-signs', + }, + }, + ], + }, + { + name: 'unprofiled Device snapshot', + patch: [ + { + op: 'remove' as const, + path: '/entry/1/resource/meta', + }, + ], + }, + { + name: 'Device snapshot with an additional arbitrary profile', + patch: [ + { + op: 'add' as const, + path: '/entry/1/resource/meta/profile/1', + value: 'https://example.org/fhir/StructureDefinition/extra-device', + }, + ], + }, + { + name: 'empty contained array in an active graph', + patch: [ + { + op: 'add' as const, + path: '/entry/2/resource/contained', + value: [], + }, + ], + }, + { + name: 'HealthKit application Device without its product identifier', + patch: [ + { + op: 'replace' as const, + path: '/entry/1/resource/meta/profile/0', + value: + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-application-device', + }, + ], + }, + { + name: 'application Device with an extra identity role', + patch: [ + { + op: 'add' as const, + path: '/entry/1/resource/identifier/1', + value: { + system: + 'https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1', + value: 'v2:test-key:1:Fz7vAPqR5b-ZOS8DwWQn3q7MogUtBVdlb3vYhRZniGU', + type: { + coding: [ + { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role', + code: 'source-record', + }, + ], + }, + }, + }, + ], + }, + { + name: 'relative reference to another Bundle entry', + patch: [ + { + op: 'replace' as const, + path: '/entry/2/resource/subject/reference', + value: 'Patient/GroveMobileExchangePatientExample', + }, + ], + }, + { + name: 'duplicated active exchange profile claim', + patch: [ + { + op: 'add' as const, + path: '/meta/profile/1', + value: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle', + }, + ], + }, + { + name: 'simultaneous active and retraction profile claims', + patch: [ + { + op: 'add' as const, + path: '/meta/profile/1', + value: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle', + }, + ], + }, + { + name: 'duplicated semantic Observation profile claim', + patch: [ + { + op: 'add' as const, + path: '/entry/2/resource/meta/profile/1', + value: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate', + }, + ], + }, + { + name: 'adapter output profile under source-neutral Provenance', + patch: [ + { + op: 'add' as const, + path: '/entry/2/resource/meta/profile/1', + value: + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-observation', + }, + ], + }, + { + name: 'adapter Provenance targeting a source-neutral output', + patch: [ + { + op: 'replace' as const, + path: '/entry/3/resource/meta/profile/0', + value: + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance', + }, + ], + }, + { + name: 'duplicated transform lifecycle coding', + patch: [ + { + op: 'add' as const, + path: '/entry/3/resource/activity/coding/1', + value: { + system: 'http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle', + code: 'transform', + }, + }, + ], + }, + { + name: 'different ISO lifecycle coding beside transform', + patch: [ + { + op: 'add' as const, + path: '/entry/3/resource/activity/coding/1', + value: { + system: 'http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle', + code: 'originate', + }, + }, + ], + }, + { + name: 'different Grove lifecycle coding in active event', + patch: [ + { + op: 'add' as const, + path: '/entry/3/resource/activity/coding/1', + value: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-lifecycle-event', + code: 'future-lifecycle-code', + }, + }, + ], + }, + ])('rejects adversarial $name input', ({ patch }) => { + expect( + parseGroveMobileExchangeBundle(patched(bases['mobile-exchange'], patch)) + .ok, + ).toBe(false) + }) + + it.each([ + { + name: 'different Grove lifecycle coding beside retraction', + value: { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-lifecycle-event', + code: 'future-lifecycle-code', + }, + }, + { + name: 'ISO lifecycle coding in retraction event', + value: { + system: 'http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle', + code: 'originate', + }, + }, + ])('rejects adversarial retraction $name', ({ value }) => { + expect( + parseGroveMobileRetractionBundle( + patched(bases['mobile-retraction'], [ + { + op: 'add', + path: '/entry/0/resource/activity/coding/1', + value, + }, + ]), + ).ok, + ).toBe(false) + }) + + it('rejects even an empty contained array in a retraction graph', () => { + const result = parseGroveMobileRetractionBundle( + patched(bases['mobile-retraction'], [ + { + op: 'add', + path: '/entry/0/resource/contained', + value: [], + }, + ]), + ) + expect(result.ok).toBe(false) + if (!result.ok) { + expect( + result.issues.some( + ({ code }) => + code === 'mobile-exchange.contained-resource-prohibited', + ), + ).toBe(true) + } + }) +}) diff --git a/packages/fhir/test/exchange-protocol.test.ts b/packages/fhir/test/exchange-protocol.test.ts new file mode 100644 index 00000000..e220ee4d --- /dev/null +++ b/packages/fhir/test/exchange-protocol.test.ts @@ -0,0 +1,438 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + parseAbsoluteUri, + type AbsoluteUri, + type Result, +} from '../src/core/index.js' +import { deriveConformanceVectorOpaqueIdentifier } from '../src/mobile/identity.js' +import { + createEntryIdentity, + deriveEntryFullUrl, + deriveEntryNodeIdentifier, + deriveEventIdentifier, + deriveOpaqueIdentifier, + entryIdentifierName, + encodeLengthFramedUtf8, + groveExchangeProtocol, + isEntryNodeIdentityValue, + isOpaqueIdentityValue, + validateDeploymentIdentity, + type DeploymentIdentityInput, + type GroveOpaqueIdentityComponents, + type GroveOpaqueIdentityKind, +} from '../src/mobile/index.js' + +const unwrap = (result: Result): Value => { + if (!result.ok) throw new Error(result.issues[0]?.message) + return result.value +} + +const uri = (value: string): AbsoluteUri => unwrap(parseAbsoluteUri(value)) + +const conformanceDeployment = { + opaqueIdentifierSystems: { + 'source-record': uri( + 'https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1', + ), + 'source-output': uri( + 'https://study.example.org/fhir/NamingSystem/grove-source-output-v2/test-key/1', + ), + 'writer-record': uri( + 'https://study.example.org/fhir/NamingSystem/grove-writer-record-v2/test-key/1', + ), + 'provider-record': uri( + 'https://study.example.org/fhir/NamingSystem/grove-provider-record-v2/test-key/1', + ), + 'provider-output': uri( + 'https://study.example.org/fhir/NamingSystem/grove-provider-output-v2/test-key/1', + ), + 'source-artifact': uri( + 'https://study.example.org/fhir/NamingSystem/grove-source-artifact-v2/test-key/1', + ), + 'provider-artifact': uri( + 'https://study.example.org/fhir/NamingSystem/grove-provider-artifact-v2/test-key/1', + ), + 'source-context': uri( + 'https://study.example.org/fhir/NamingSystem/grove-source-context-v2/test-key/1', + ), + 'recording-device': uri( + 'https://study.example.org/fhir/NamingSystem/grove-recording-device-v2/test-key/1', + ), + 'device-snapshot': uri( + 'https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v2/test-key/1', + ), + }, + eventIdentifierSystem: uri(groveExchangeProtocol.testVectors.event.system), + entryNodeIdentifierSystem: uri( + groveExchangeProtocol.testVectors.entryNode.system, + ), + keyId: groveExchangeProtocol.testVectors.keyId, + keyEpoch: groveExchangeProtocol.testVectors.epoch, + secretBase64Url: Buffer.from( + groveExchangeProtocol.testVectors.keyHex, + 'hex', + ).toString('base64url'), + producerInstance: groveExchangeProtocol.testVectors.event.producerInstance, +} as const satisfies DeploymentIdentityInput + +const runtimeDeployment: DeploymentIdentityInput = { + ...conformanceDeployment, + secretBase64Url: 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', +} + +const identityVectors = groveExchangeProtocol.testVectors.identities +const invalidIdentityVectors = + groveExchangeProtocol.testVectors.invalidIdentities + +const deriveVectorIdentity = ( + kind: Kind, + components: GroveOpaqueIdentityComponents[Kind], +) => + deriveConformanceVectorOpaqueIdentifier( + conformanceDeployment, + kind, + components, + ) + +describe('Grove exchange protocol v2 identity', () => { + it('fails closed without throwing at every untyped identity boundary', () => { + const invalidValues = [null, undefined, 42, 'wrong-shape', Symbol('x')] + for (const invalid of invalidValues) { + const operations = [ + () => validateDeploymentIdentity(invalid), + () => encodeLengthFramedUtf8(invalid as never), + () => deriveEntryFullUrl(invalid as never), + () => entryIdentifierName(invalid as never), + () => createEntryIdentity(invalid as never), + () => + deriveOpaqueIdentifier(invalid as never, 'source-record', [ + 'healthkit', + 'type', + 'scope-system', + 'scope-value', + 'native-id', + ]), + () => + deriveOpaqueIdentifier( + runtimeDeployment, + 'source-record', + invalid as never, + ), + () => + deriveEntryNodeIdentifier( + runtimeDeployment, + invalid as never, + 'conversion-provenance', + '0', + ), + ] + for (const operation of operations) { + expect(operation).not.toThrow() + expect(operation().ok).toBe(false) + } + } + + const cyclic: Record = {} + cyclic.self = cyclic + expect(() => validateDeploymentIdentity(cyclic as never)).not.toThrow() + expect(validateDeploymentIdentity(cyclic as never).ok).toBe(false) + }) + + it.each(identityVectors)( + 'matches the shared $identityKind HMAC vector byte for byte', + (vector) => { + const derived = deriveVectorIdentity( + vector.identityKind, + vector.components, + ) + expect(derived).toMatchObject({ + ok: true, + value: { value: vector.value }, + }) + }, + ) + + it('matches the shared event, entry-node, and fullUrl vectors', () => { + const vector = groveExchangeProtocol.testVectors + const event = unwrap( + deriveEventIdentifier(runtimeDeployment, vector.event.sequence), + ) + expect(event).toEqual({ + system: vector.event.system, + value: vector.event.value, + role: 'event', + }) + + const node = unwrap( + deriveEntryNodeIdentifier( + runtimeDeployment, + event, + vector.entryNode.role, + vector.entryNode.ordinal, + ), + ) + expect(node.value).toBe(vector.entryNode.value) + expect(deriveEntryFullUrl(node)).toEqual({ + ok: true, + value: vector.entryNode.fullUrl, + }) + const fullUrlVector = vector.fullUrls[0] + expect( + deriveEntryFullUrl({ + system: uri(fullUrlVector.system), + value: fullUrlVector.value, + }), + ).toEqual({ + ok: true, + value: fullUrlVector.fullUrl, + }) + }) + + it('length-frames tuples without delimiter collisions', () => { + expect(unwrap(encodeLengthFramedUtf8(['']))).toEqual( + Uint8Array.from([0, 0, 0, 0]), + ) + expect(unwrap(encodeLengthFramedUtf8(['a', 'bc']))).not.toEqual( + unwrap(encodeLengthFramedUtf8(['ab', 'c'])), + ) + expect(unwrap(encodeLengthFramedUtf8(['a|b', 'c']))).not.toEqual( + unwrap(encodeLengthFramedUtf8(['a', 'b|c'])), + ) + }) + + it('returns an out-of-range Result when the runtime cannot allocate UTF-8 bytes', () => { + const original = Object.getOwnPropertyDescriptor(globalThis, 'TextEncoder') + Object.defineProperty(globalThis, 'TextEncoder', { + configurable: true, + value: class { + encode(): Uint8Array { + throw new RangeError('simulated allocation failure') + } + }, + }) + try { + expect(() => encodeLengthFramedUtf8(['field'])).not.toThrow() + expect(encodeLengthFramedUtf8(['field'])).toMatchObject({ + ok: false, + issues: [{ code: 'out-of-range', path: [0] }], + }) + } finally { + if (original === undefined) { + Reflect.deleteProperty(globalThis, 'TextEncoder') + } else { + Object.defineProperty(globalThis, 'TextEncoder', original) + } + } + }) + + it('fails closed for the public test key at public APIs and malformed key spaces', () => { + expect(validateDeploymentIdentity(conformanceDeployment).ok).toBe(false) + expect( + validateDeploymentIdentity({ + ...runtimeDeployment, + keyUse: 'conformance-testing', + }).ok, + ).toBe(false) + const sourceVector = identityVectors[0] + expect( + deriveOpaqueIdentifier( + conformanceDeployment, + sourceVector.identityKind, + sourceVector.components as never, + ).ok, + ).toBe(false) + expect( + validateDeploymentIdentity({ + ...runtimeDeployment, + opaqueIdentifierSystems: { + ...runtimeDeployment.opaqueIdentifierSystems, + 'source-record': + runtimeDeployment.opaqueIdentifierSystems['source-output'], + }, + }).ok, + ).toBe(false) + const withWrongKinds = { + ...runtimeDeployment, + opaqueIdentifierSystems: Object.fromEntries( + Object.entries(runtimeDeployment.opaqueIdentifierSystems).map( + ([kind, system], index) => [ + index === 0 ? 'unknown-kind' : kind, + system, + ], + ), + ), + } as unknown as DeploymentIdentityInput + expect(validateDeploymentIdentity(withWrongKinds).ok).toBe(false) + for (const badSystem of [ + 'https://例.example/identity', + 'https://example.org/%ZZ', + 'https://example.org/identity value', + ]) { + expect( + validateDeploymentIdentity({ + ...runtimeDeployment, + eventIdentifierSystem: badSystem as AbsoluteUri, + }).ok, + ).toBe(false) + } + }) + + it('rejects unknown identity kinds and every wrong component arity at runtime', () => { + const source = identityVectors.find( + ({ identityKind }) => identityKind === 'source-output', + ) + if (source === undefined) throw new Error('Missing source-output vector.') + expect( + deriveOpaqueIdentifier( + runtimeDeployment, + 'unknown-kind' as GroveOpaqueIdentityKind, + source.components as never, + ).ok, + ).toBe(false) + expect( + deriveOpaqueIdentifier( + runtimeDeployment, + 'source-output', + source.components.slice(0, -1) as never, + ).ok, + ).toBe(false) + expect( + deriveOpaqueIdentifier(runtimeDeployment, 'source-output', [ + ...source.components, + 'extra', + ] as never).ok, + ).toBe(false) + }) + + it('rejects empty or malformed Unicode only at the typed opaque-identity boundary', () => { + for (const vector of identityVectors) { + for (const invalid of ['', 'prefix\ud800suffix']) { + const components: string[] = [...vector.components] + components[0] = invalid + const result = deriveOpaqueIdentifier( + runtimeDeployment, + vector.identityKind, + components as never, + ) + expect(result.ok).toBe(false) + if (!result.ok) { + expect(result.issues[0]?.path).toEqual(['components', 0]) + } + } + } + }) + + it('rejects provider coordinates under every generic source identity kind', () => { + const invalidProviderCoordinates = [ + { + kind: 'source-record', + components: [ + 'oura', + 'daily_activity', + 'https://accounts.example.org', + 'patient-001', + 'activity-001', + ], + }, + { + kind: 'source-output', + components: [ + 'withings', + 'getmeas:9+10', + 'https://accounts.example.org', + 'patient-001', + '17348211', + 'blood-pressure-panel', + 'single', + ], + }, + { + kind: 'source-artifact', + components: [ + 'google-health-api', + 'heart-rate', + 'https://accounts.example.org', + 'patient-001', + 'recording-001', + 'provider-recording', + '0', + ], + }, + ] as const + + for (const { kind, components } of invalidProviderCoordinates) { + const result = deriveOpaqueIdentifier( + runtimeDeployment, + kind, + components as never, + ) + expect(result).toMatchObject({ + ok: false, + issues: [{ code: 'invalid-code', path: ['components', 0] }], + }) + } + }) + + it('rejects every shared invalid opaque-identity vector', () => { + expect(invalidIdentityVectors).toHaveLength(4) + for (const vector of invalidIdentityVectors) { + const result = deriveOpaqueIdentifier( + runtimeDeployment, + vector.identityKind, + vector.components as never, + ) + expect(result.ok).toBe(false) + if (!result.ok) { + expect(result.issues[0]?.path).toEqual([ + 'components', + vector.expectedError === 'empty-component' ? + vector.components.indexOf('') + : 0, + ]) + } + } + }) + + it('binds entry-node derivation to this producer typed event identity', () => { + const event = unwrap( + deriveEventIdentifier( + runtimeDeployment, + groveExchangeProtocol.testVectors.event.sequence, + ), + ) + expect( + deriveEntryNodeIdentifier( + runtimeDeployment, + { system: event.system, value: event.value }, + 'conversion-provenance', + '0', + ).ok, + ).toBe(false) + expect( + deriveEntryNodeIdentifier( + runtimeDeployment, + { + ...event, + value: 'e2:13ed2fea-25b0-4d0d-9a26-c6d2a7f057a2:1', + }, + 'conversion-provenance', + '0', + ).ok, + ).toBe(false) + }) + + it('rejects noncanonical base64url digest spellings', () => { + const noncanonicalDigest = `${'A'.repeat(42)}B` + expect(isOpaqueIdentityValue(`v2:key:1:${noncanonicalDigest}`)).toBe(false) + expect( + isEntryNodeIdentityValue(`n2:resource:0:${noncanonicalDigest}`), + ).toBe(false) + }) +}) diff --git a/packages/fhir/test/exports.test.ts b/packages/fhir/test/exports.test.ts index 1a284787..5efaf9b1 100644 --- a/packages/fhir/test/exports.test.ts +++ b/packages/fhir/test/exports.test.ts @@ -16,6 +16,8 @@ type HasMeasurementBuilder = 'buildProviderMeasurementBundle' extends keyof T ? true : false type HasRecordingBuilder = 'buildProviderRecordingBundle' extends keyof T ? true : false +type HasRetractionBuilder = + 'buildProviderRetractionBundle' extends keyof T ? true : false type HasInternalPackageGraph = 'groveFhirPackageGraph' extends keyof T ? true : false @@ -23,8 +25,10 @@ describe('public entry-point boundaries', () => { it('keeps provider-specific APIs out of the source-neutral entry points', () => { expect('buildProviderMeasurementBundle' in root).toBe(false) expect('buildProviderRecordingBundle' in root).toBe(false) + expect('buildProviderRetractionBundle' in root).toBe(false) expect('buildProviderMeasurementBundle' in mobile).toBe(false) expect('buildProviderRecordingBundle' in mobile).toBe(false) + expect('buildProviderRetractionBundle' in mobile).toBe(false) expect(JSON.stringify(mobile.sharedMobileMeasurementCatalog)).not.toMatch( /healthkit|health-connect|sensorkit|google-health-api|oura|withings|sourceTokens/u, ) @@ -35,14 +39,19 @@ describe('public entry-point boundaries', () => { expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() expectTypeOf< HasMeasurementBuilder >().toEqualTypeOf() expectTypeOf< HasRecordingBuilder >().toEqualTypeOf() + expectTypeOf< + HasRetractionBuilder + >().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() expectTypeOf< HasInternalPackageGraph @@ -55,14 +64,23 @@ describe('public entry-point boundaries', () => { it('exposes the closed provider facade only from Provider', () => { expect(typeof provider.buildProviderMeasurementBundle).toBe('function') expect(typeof provider.buildProviderRecordingBundle).toBe('function') + expect(typeof provider.buildProviderRetractionBundle).toBe('function') expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() }) it('exposes bounded generated version and package metadata', () => { expect(root.groveFhirVersion).toBe('4.0.1') - expect(root.groveFhirContractVersion).toBe('0.5.0') + expect(root.groveFhirContractVersion).toBe('0.6.0') + expect(root.groveExchangeProtocol.protocolVersion).toBe(2) + expect(root.groveMobileContract.version).toBe('0.6.0') + expect( + root.groveMobileContract.identity.resourceIdentifierPriority, + ).toEqual( + root.groveExchangeProtocol.entryIdentity.resourceIdentifierPriority, + ) expect(mobile.groveMobilePackageMetadata.packageId).toBe( 'org.grovealliance.fhir.mobile', ) @@ -71,14 +89,16 @@ describe('public entry-point boundaries', () => { ) expectTypeOf(root.groveFhirVersion).toEqualTypeOf<'4.0.1'>() - expectTypeOf(root.groveFhirContractVersion).toEqualTypeOf<'0.5.0'>() + expectTypeOf(root.groveFhirContractVersion).toEqualTypeOf<'0.6.0'>() }) it('exposes owner-exclusive measurements only from the Provider contract', () => { expect(Object.keys(provider.adapterMeasurementCatalog)).toEqual([ 'healthkit', 'health-connect', - 'providers', + 'withings', + 'oura', + 'google-health', ]) const standHour = provider.adapterMeasurementCatalog.healthkit['apple-stand-hour'] @@ -88,6 +108,36 @@ describe('public entry-point boundaries', () => { expect(standHour).not.toHaveProperty('generation') expect(Object.isFrozen(provider.adapterMeasurementCatalog)).toBe(true) expect(Object.isFrozen(standHour)).toBe(true) + expect(provider.healthKitApplicationDeviceIdentity).toMatchObject({ + profile: + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-application-device', + snapshotIdentifierRole: 'device-snapshot', + bundleIdentifier: { + system: + 'https://grovealliance.org/fhir/healthkit/NamingSystem/apple-bundle-id', + typeCode: 'apple-bundle-id', + cardinality: '1..1', + }, + }) + expect(Object.isFrozen(provider.healthKitApplicationDeviceIdentity)).toBe( + true, + ) + expect( + Object.isFrozen( + provider.healthKitApplicationDeviceIdentity.bundleIdentifier, + ), + ).toBe(true) + const clinicalAdmission = provider.healthKitClinicalRecordAdmission + expect(clinicalAdmission.fhirRepresentation.fixedValue).toBe( + clinicalAdmission.admittedFHIRRelease, + ) + expect( + provider.groveRecordingFormatRegistry.formats[ + clinicalAdmission.payloadFormat + ].contentType, + ).toBe('application/fhir+json') + expect(Object.isFrozen(clinicalAdmission)).toBe(true) + expect(Object.isFrozen(clinicalAdmission.fhirRepresentation)).toBe(true) }) it('deeply freezes every generated contract read by public builders', () => { @@ -95,7 +145,10 @@ describe('public entry-point boundaries', () => { expect(Object.isFrozen(mobile.sharedMobileMeasurementCatalog)).toBe(true) expect(Object.isFrozen(heartRate)).toBe(true) expect(Object.isFrozen(heartRate.code)).toBe(true) - expect(Object.isFrozen(provider.providerScalarMappings)).toBe(true) + expect(Object.isFrozen(provider.providerScalarOutputRoles)).toBe(true) + expect(Object.isFrozen(provider.providerScalarOutputDiscriminators)).toBe( + true, + ) expect(provider.providerRecordEffectiveRules.oura.daily_activity.kind).toBe( 'complete-civil-day-period', ) @@ -103,6 +156,7 @@ describe('public entry-point boundaries', () => { expect( Object.isFrozen(provider.groveProviderPackageMetadata.dependencies), ).toBe(true) + expect(Object.isFrozen(provider.providerRawOutputDiscriminators)).toBe(true) expect(Reflect.set(heartRate.code, 'code', 'mutated-clinical-code')).toBe( false, ) diff --git a/packages/fhir/test/fhir-json.test.ts b/packages/fhir/test/fhir-json.test.ts new file mode 100644 index 00000000..8a7905ae --- /dev/null +++ b/packages/fhir/test/fhir-json.test.ts @@ -0,0 +1,253 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { expectTypeOf } from 'expect-type' +import type { Observation as R4Observation } from 'fhir/r4.js' +import type { FhirJson } from '../src/r4/index.js' +import { + STRUCTURAL_SCHEMA_CAPABILITIES as r4Capabilities, + STRUCTURAL_SCHEMA_SOURCE as r4Source, + capabilityStatementSchema as r4CapabilityStatementSchema, + canonicalSchema as r4CanonicalSchema, + extensionSchema as r4ExtensionSchema, + humanNameSchema as r4HumanNameSchema, + observationSchema as r4ObservationSchema, + quantitySchema as r4QuantitySchema, + questionnaireResponseItemAnswerSchema as r4QuestionnaireResponseItemAnswerSchema, + uriSchema as r4UriSchema, + urlSchema as r4UrlSchema, +} from '../src/zod/r4/index.js' +import { + STRUCTURAL_SCHEMA_CAPABILITIES as r4bCapabilities, + STRUCTURAL_SCHEMA_SOURCE as r4bSource, + capabilityStatementSchema as r4bCapabilityStatementSchema, + canonicalSchema as r4bCanonicalSchema, + extensionSchema as r4bExtensionSchema, + humanNameSchema as r4bHumanNameSchema, + observationSchema as r4bObservationSchema, + quantitySchema as r4bQuantitySchema, + questionnaireResponseItemAnswerSchema as r4bQuestionnaireResponseItemAnswerSchema, + uriSchema as r4bUriSchema, + urlSchema as r4bUrlSchema, +} from '../src/zod/r4b/index.js' + +const releases = [ + { + name: 'R4', + capabilities: r4Capabilities, + source: r4Source, + capabilityStatement: r4CapabilityStatementSchema, + canonical: r4CanonicalSchema, + extension: r4ExtensionSchema, + humanName: r4HumanNameSchema, + observation: r4ObservationSchema, + quantity: r4QuantitySchema, + questionnaireAnswer: r4QuestionnaireResponseItemAnswerSchema, + uri: r4UriSchema, + url: r4UrlSchema, + }, + { + name: 'R4B', + capabilities: r4bCapabilities, + source: r4bSource, + capabilityStatement: r4bCapabilityStatementSchema, + canonical: r4bCanonicalSchema, + extension: r4bExtensionSchema, + humanName: r4bHumanNameSchema, + observation: r4bObservationSchema, + quantity: r4bQuantitySchema, + questionnaireAnswer: r4bQuestionnaireResponseItemAnswerSchema, + uri: r4bUriSchema, + url: r4bUrlSchema, + }, +] as const + +describe.each(releases)('$name generated FHIR JSON boundary', (release) => { + it('accepts required primitives carried only by metadata', () => { + const absent = { + extension: [ + { + // Fixed by FHIR R4/R4B and never fetched. + url: 'http://hl7.org/fhir/StructureDefinition/data-absent-reason', + valueCode: 'unknown', + }, + ], + } + expect( + release.observation.safeParse({ + resourceType: 'Observation', + _status: absent, + code: { text: 'Unavailable measurement' }, + }).success, + ).toBe(true) + expect( + release.capabilityStatement.safeParse({ + resourceType: 'CapabilityStatement', + status: 'active', + date: '2026-08-27T12:00:00Z', + kind: 'instance', + fhirVersion: '4.0.1', + _format: [absent], + }).success, + ).toBe(true) + }) + + it('accepts aligned repeating primitive null slots and metadata', () => { + const metadata = (label: string) => ({ + extension: [ + { + url: 'https://example.org/fhir/StructureDefinition/slot-metadata', + valueString: label, + }, + ], + }) + expect(release.humanName.safeParse({ given: ['Ada'] }).success).toBe(true) + expect( + release.humanName.safeParse({ + given: [null], + _given: [metadata('extension-only')], + }).success, + ).toBe(true) + expect( + release.humanName.safeParse({ + given: [null, 'Ada', null], + _given: [metadata('leading'), null, metadata('trailing')], + }).success, + ).toBe(true) + }) + + it('rejects unaligned, empty, and doubly empty primitive slots', () => { + expect( + release.humanName.safeParse({ + given: ['Ada'], + _given: [{ id: 'first' }, { id: 'extra' }], + }).success, + ).toBe(false) + expect( + release.humanName.safeParse({ given: [null], _given: [null] }).success, + ).toBe(false) + expect(release.humanName.safeParse({ given: [] }).success).toBe(false) + expect(release.humanName.safeParse({ _given: [] }).success).toBe(false) + }) + + it('counts primitive shadow properties as their value choice', () => { + const extensionOnly = { + url: 'https://example.org/fhir/StructureDefinition/outer', + _valueString: { + extension: [ + { + url: 'https://example.org/fhir/StructureDefinition/inner', + valueBoolean: true, + }, + ], + }, + } + expect(release.extension.safeParse(extensionOnly).success).toBe(true) + expect( + release.extension.safeParse({ + ...extensionOnly, + _valueBoolean: { id: 'second-choice' }, + }).success, + ).toBe(false) + expect( + release.questionnaireAnswer.safeParse({ + _valueString: { + extension: [ + { + url: 'https://example.org/fhir/StructureDefinition/answer-source', + valueString: 'extension-only answer', + }, + ], + }, + }).success, + ).toBe(true) + }) + + it('rejects empty primitive metadata objects', () => { + expect( + release.humanName.safeParse({ given: [null], _given: [{}] }).success, + ).toBe(false) + expect( + release.humanName.safeParse({ + given: [null], + _given: [{ id: 'id-is-not-content' }], + }).success, + ).toBe(false) + expect( + release.extension.safeParse({ + url: 'https://example.org/fhir/StructureDefinition/outer', + _valueString: {}, + }).success, + ).toBe(false) + }) + + it('applies core invariants to primitive metadata-only values', () => { + const metadata = { + extension: [ + { + url: 'https://example.org/fhir/StructureDefinition/metadata', + valueString: 'present', + }, + ], + } + expect( + release.observation.safeParse({ + resourceType: 'Observation', + status: 'final', + code: { text: 'Unavailable measurement' }, + dataAbsentReason: { text: 'Unknown' }, + _valueString: metadata, + }).success, + ).toBe(false) + expect( + release.observation.safeParse({ + resourceType: 'Observation', + status: 'final', + code: { text: 'Measured value' }, + referenceRange: [{ _text: metadata }], + }).success, + ).toBe(true) + expect(release.quantity.safeParse({ _code: metadata }).success).toBe(false) + expect( + release.quantity.safeParse({ + _code: metadata, + _system: metadata, + }).success, + ).toBe(true) + }) + + it('rejects empty emitted canonical, uri, and url primitives', () => { + expect(release.canonical.safeParse('').success).toBe(false) + expect(release.uri.safeParse('').success).toBe(false) + expect(release.url.safeParse('').success).toBe(false) + }) + + it('states its decimal and conformance capability limits', () => { + expect(release.capabilities).toEqual({ + fullFhirPath: false, + preservesDecimalLexemesAfterJsonParse: false, + normativeConformanceValidator: 'official-fhir-validator', + }) + }) + + it('publishes its exact pinned package generation source', () => { + expect(release.source.fhirVersion).toBe( + release.name === 'R4' ? '4.0.1' : '4.3.0', + ) + expect(release.source.packageVersion).toBe(release.source.fhirVersion) + expect(release.source.structureCount).toBeGreaterThan(200) + expect(release.source.archiveSha512).toMatch(/^[\da-f]{128}$/u) + }) +}) + +it('types primitive JSON values as optional when a shadow can carry them', () => { + type WireObservation = FhirJson + expectTypeOf().toEqualTypeOf< + R4Observation['status'] | undefined + >() +}) diff --git a/packages/fhir/test/foundation-boundaries.test.ts b/packages/fhir/test/foundation-boundaries.test.ts index 72cd10b5..56e950de 100644 --- a/packages/fhir/test/foundation-boundaries.test.ts +++ b/packages/fhir/test/foundation-boundaries.test.ts @@ -224,6 +224,36 @@ describe('bounded R4 schema invariants', () => { ).toBe(true) }) + it('orders sub-millisecond fractions without truncation', () => { + expect( + periodSchema.safeParse({ + start: '2026-01-01T00:00:00.0002Z', + end: '2026-01-01T00:00:00.0001Z', + }).success, + ).toBe(false) + expect( + periodSchema.safeParse({ + start: '2026-01-01T00:00:00.0001Z', + end: '2026-01-01T00:00:00.0002Z', + }).success, + ).toBe(true) + }) + + it('keeps a leap second before the following wall-clock second', () => { + expect( + periodSchema.safeParse({ + start: '2026-12-31T23:59:60.999999Z', + end: '2027-01-01T00:00:00Z', + }).success, + ).toBe(true) + expect( + periodSchema.safeParse({ + start: '2027-01-01T00:00:00Z', + end: '2026-12-31T23:59:60.999999Z', + }).success, + ).toBe(false) + }) + it('types every temporal field, not only the ones an example happened to use', () => { expect( provenanceSchema.safeParse({ diff --git a/packages/fhir/test/foundation.test.ts b/packages/fhir/test/foundation.test.ts index 36d547bf..cc15f8c4 100644 --- a/packages/fhir/test/foundation.test.ts +++ b/packages/fhir/test/foundation.test.ts @@ -17,7 +17,7 @@ import { ok, parseAbsoluteUri, parseCanonical, - parseCollectionBundle, + parseR4CollectionBundle, parseDevice, parseDocumentReference, parseFhirId, @@ -29,7 +29,7 @@ import { parseSpecimen, parseSupportedR4Resource, parseUrnUuid, - type CollectionBundle, + type R4CollectionBundle, type DocumentReference, type Observation, type QuantityValue, @@ -85,6 +85,31 @@ describe('deepFreeze', () => { expect(() => deepFreeze(parent)).not.toThrow() expect(Object.isFrozen(parent)).toBe(true) }) + + it('still freezes mutable descendants of an already frozen parent', () => { + const child = { value: 1 } + const parent = Object.freeze({ child }) + + deepFreeze(parent) + + expect(Object.isFrozen(child)).toBe(true) + }) + + it('does not invoke accessors while traversing properties', () => { + let invocationCount = 0 + const value = Object.defineProperty({}, 'computed', { + get: () => { + invocationCount += 1 + return { value: 1 } + }, + enumerable: true, + }) + + deepFreeze(value) + + expect(invocationCount).toBe(0) + expect(Object.isFrozen(value)).toBe(true) + }) }) describe('R4 foundation', () => { @@ -99,6 +124,63 @@ describe('R4 foundation', () => { expectTypeOf(result).toExtend>() }) + it('isolates parsed resources from later caller mutation', () => { + const input = structuredClone(observation) as unknown as { + meta: { profile: string[] } + valueQuantity: { value: number } + } + const result = parseObservation(input) + expect(result.ok).toBe(true) + if (!result.ok) return + + input.meta.profile.push('https://example.org/changed-after-parse') + input.valueQuantity.value = 999 + + expect(result.value.meta?.profile).toHaveLength(1) + expect(result.value.valueQuantity?.value).toBe(64) + }) + + const hostileInputs: ReadonlyArray = [ + ['null', null], + ['undefined', undefined], + ['bigint', 1n], + ['non-finite number', Number.NaN], + ['class instance', new Date()], + [ + 'throwing getter', + Object.defineProperty({}, 'status', { + get: () => { + throw new Error('must not run') + }, + enumerable: true, + }), + ], + [ + 'hostile proxy', + new Proxy( + {}, + { + ownKeys: () => { + throw new Error('hostile proxy') + }, + }, + ), + ], + ] + for (const [label, input] of hostileInputs) { + it(`reports ${label} parser input without throwing`, () => { + expect(() => parseObservation(input)).not.toThrow() + expect(parseObservation(input).ok).toBe(false) + }) + } + + it('reports cyclic parser input without throwing', () => { + const cyclic: Record = { ...observation } + cyclic.self = cyclic + expect(() => parseObservation(cyclic)).not.toThrow() + expect(parseObservation(cyclic).ok).toBe(false) + }) + it('rejects unknown fields instead of silently stripping them', () => { const result = parseObservation({ ...observation, @@ -168,9 +250,9 @@ describe('R4 foundation', () => { }, ], } - const result = parseCollectionBundle(bundle) + const result = parseR4CollectionBundle(bundle) expect(result.ok).toBe(true) - expectTypeOf(result).toExtend>() + expectTypeOf(result).toExtend>() }) it('parses every bounded graph resource and the supported-resource union', () => { @@ -251,7 +333,7 @@ describe('Result composition', () => { value: 6, }) const failure = err('invalid-type', 'wrong value', ['value']) - expect(mapResult(failure, () => 0)).toBe(failure) + expect(mapResult(failure, () => 0)).toEqual(failure) expect(collectResults([ok(1), ok(2)])).toEqual({ ok: true, value: [1, 2], @@ -291,6 +373,39 @@ describe('Result composition', () => { warnings: [warning], }) }) + + it('reports malformed JavaScript Result inputs without throwing', () => { + const cyclic: Record = {} + cyclic.self = cyclic + const hostile = new Proxy( + {}, + { + get: () => { + throw new Error('hostile result') + }, + }, + ) + for (const malformed of [null, undefined, 42, 'invalid', cyclic, hostile]) { + const map = () => + mapResult(malformed as Result, (value) => value * 2) + const collect = () => + collectResults(malformed as ReadonlyArray>) + expect(map).not.toThrow() + expect(map().ok).toBe(false) + expect(collect).not.toThrow() + expect(collect().ok).toBe(false) + } + const collected = collectResults([ + ok(1), + { ok: false, issues: null } as unknown as Result, + ]) + expect(collected.ok).toBe(false) + if (!collected.ok) { + expect(collected.issues).toContainEqual( + expect.objectContaining({ code: 'invalid-type', path: [1] }), + ) + } + }) }) describe('validated primitives', () => { diff --git a/packages/fhir/test/mobile.test.ts b/packages/fhir/test/mobile.test.ts index be857dc4..ec4a5cf4 100644 --- a/packages/fhir/test/mobile.test.ts +++ b/packages/fhir/test/mobile.test.ts @@ -20,8 +20,8 @@ import { canonicalizeMobileEffectiveInstant, createEntryIdentity, deriveEntryFullUrl, + encodeLengthFramedUtf8, groveFhirContractVersion, - groveFhirExchangeIdentity, groveFhirVersion, groveMobilePackageMetadata, mobileEffectiveCanonicalizationVectors, @@ -41,17 +41,21 @@ const instant = (value: string): FhirInstant => unwrap(parseFhirInstant(value)) describe('source-neutral Mobile contract', () => { it('exports only source-neutral data, types, identity, and time contracts', () => { expect(groveFhirVersion).toBe('4.0.1') - expect(groveFhirContractVersion).toBe('0.5.0') + expect(groveFhirContractVersion).toBe('0.6.0') expect(groveMobilePackageMetadata.packageId).toBe( 'org.grovealliance.fhir.mobile', ) expect(Object.isFrozen(groveMobilePackageMetadata)).toBe(true) + expect(mobile.groveExchangeProtocol.protocolVersion).toBe(2) + expect(mobile.groveMobileContract.version).toBe('0.6.0') + expect(Object.isFrozen(mobile.groveMobileContract)).toBe(true) + expect('groveFhirExchangeIdentity' in mobile).toBe(false) expect('buildProviderMeasurementBundle' in mobile).toBe(false) expect('providerAdapterCatalog' in mobile).toBe(false) expect('groveFhirProfileCanonicals' in mobile).toBe(false) expect('PROFILES' in mobile).toBe(false) expect('providerAdapterCatalog' in mobileContract).toBe(false) - expect('providerScalarMappings' in mobileContract).toBe(false) + expect('providerScalarOutputRoles' in mobileContract).toBe(false) expect('groveProviderPackageMetadata' in mobileContract).toBe(false) expect('adapterMeasurementCatalog' in mobileContract).toBe(false) }) @@ -95,50 +99,46 @@ describe('source-neutral Mobile contract', () => { }) describe('Mobile exchange entry identity', () => { - it('names an identifier by joining its system and value with a vertical bar', () => { + it('names an identifier with unambiguous unsigned-32-bit length framing', () => { + const expected = unwrap( + encodeLengthFramedUtf8(['https://example.org/source', 'record-1']), + ) expect( entryIdentifierName({ system: 'https://example.org/source' as never, value: 'record-1', }), - ).toEqual({ ok: true, value: 'https://example.org/source|record-1' }) + ).toEqual({ ok: true, value: expected }) }) - it('admits a vertical bar in a composed value but not in the system', () => { - // A composed identifier is built from vertical bars, so only the system may not carry one. - expect( + it('admits separators in either component without tuple collisions', () => { + const first = unwrap( entryIdentifierName({ - system: 'https://example.org/writer' as never, - value: 'v1:com.withings.wiscale2|17348211', + system: 'https://example.org/a;b' as never, + value: 'c', }), - ).toEqual({ - ok: true, - value: 'https://example.org/writer|v1:com.withings.wiscale2|17348211', - }) - expect( + ) + const second = unwrap( entryIdentifierName({ - system: 'https://example.org/a|b' as never, - value: 'record-1', - }).ok, - ).toBe(false) + system: 'https://example.org/a' as never, + value: 'b;c', + }), + ) + expect(first).not.toEqual(second) }) - it.each(groveFhirExchangeIdentity.vectors)( - 'matches the IG $case vector', - (vector) => { - const derived = deriveEntryFullUrl({ + it('matches the protocol Unicode-value and escaped-system UUID-v5 vector', () => { + const vector = mobile.groveExchangeProtocol.testVectors.fullUrls[0] + expect( + deriveEntryFullUrl({ system: uri(vector.system), value: vector.value, - }) - // A rejection vector states no URN: the system carries a separator, so the name would be - // ambiguous and the derivation refuses it. - if (vector.fullUrl === null) { - expect(derived.ok).toBe(false) - return - } - expect(derived).toEqual({ ok: true, value: vector.fullUrl }) - }, - ) + }), + ).toEqual({ + ok: true, + value: vector.fullUrl, + }) + }) it('retains a complete business Identifier and optional repository id', () => { const identifier = { @@ -161,8 +161,10 @@ describe('Mobile exchange entry identity', () => { it.each([ { system: '/relative', value: 'record-1' }, + { system: 'https://例.example/識別子', value: 'record-1' }, + { system: 'https://example.org/%ZZ', value: 'record-1' }, + { system: 'https://example.org/a b', value: 'record-1' }, { system: 'https://example.org/identifiers', value: '' }, - { system: 'https://example.org/identifiers', value: ' \t\n ' }, { system: 'https://example.org/identifiers', value: 'invalid-\ud800' }, { system: 'https://example.org/identifiers', value: 'invalid-\udc00' }, ])('rejects incomplete or non-Unicode-scalar identity %#', (input) => { @@ -184,6 +186,15 @@ describe('Mobile exchange entry identity', () => { false, ) }) + + it('preserves nonempty whitespace because Identifier.value is lexical data', () => { + expect( + entryIdentifierName({ + system: uri('https://example.org/identifiers'), + value: ' \t\n ', + }).ok, + ).toBe(true) + }) }) describe('Mobile effective-time canonicalization', () => { diff --git a/packages/fhir/test/provider-contract.test.ts b/packages/fhir/test/provider-contract.test.ts new file mode 100644 index 00000000..f3db997c --- /dev/null +++ b/packages/fhir/test/provider-contract.test.ts @@ -0,0 +1,479 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { readFileSync } from 'node:fs' +import { assert, double, oneof, property } from 'fast-check' +import { + baseInput, + dateTime, + end, + heartRateMeasurement, + resources, + resourceIdentity, + start, + unwrap, + uri, +} from './provider-test-support.js' +import { parseGroveMobileExchangeBundle } from '../src/index.js' +import { + createEntryIdentity, + deriveEntryFullUrl, + entryIdentifierName, + groveExchangeProtocol, +} from '../src/mobile/index.js' +import { + buildProviderMeasurementBundle, + parseNormalizedProviderRecord, + parseProviderMeasurementBundleInput, + providerAdapterCatalog, + providerRawOutputDiscriminators, + providerRawOutputRoles, + providerScalarOutputDiscriminators, + providerScalarOutputRoles, + type ConnectedProviderRecord, + type ProviderMeasurementBundleInput, +} from '../src/providers/index.js' + +describe('Exchange entry identity', () => { + it.each(groveExchangeProtocol.testVectors.fullUrls)( + 'matches the protocol $id vector', + (vector) => { + const derived = deriveEntryFullUrl({ + system: uri(vector.system), + value: vector.value, + }) + expect(derived).toEqual({ ok: true, value: vector.fullUrl }) + }, + ) + + it('length framing admits separators without collisions', () => { + expect( + deriveEntryFullUrl({ + system: uri('https://example.org/a;b'), + value: 'x|y', + }).ok, + ).toBe(true) + }) + + it('rejects emitted quantities outside their catalog-owned domains', () => { + const valid = unwrap( + buildProviderMeasurementBundle( + baseInput('withings', 'getmeas:6', { + kind: 'body-fat-percentage', + value: 0, + effective: { kind: 'date-time', value: dateTime }, + }), + ), + ) + const tampered = structuredClone(valid) + const observation = tampered.entry.find( + ({ resource }) => resource.resourceType === 'Observation', + )?.resource + if (observation?.resourceType !== 'Observation') { + throw new Error('Expected a body-fat Observation.') + } + if (observation.valueQuantity === undefined) { + throw new Error('Expected a body-fat Quantity result.') + } + const invalidBundle = { + ...tampered, + entry: tampered.entry.map((entry) => + entry.resource === observation ? + { + ...entry, + resource: { + ...observation, + valueQuantity: { + ...observation.valueQuantity, + value: 100.1, + }, + }, + } + : entry, + ), + } + const parsed = parseGroveMobileExchangeBundle(invalidBundle) + expect(parsed.ok).toBe(false) + if (!parsed.ok) { + expect( + parsed.issues.some(({ message }) => + message.includes('mobile-body-fat-percentage.value-domain'), + ), + ).toBe(true) + } + }) + + it('rejects isolated UTF-16 surrogates before UUID derivation', () => { + expect( + deriveEntryFullUrl({ + system: uri('https://example.org/identifiers'), + value: 'invalid-\ud800', + }).ok, + ).toBe(false) + expect( + deriveEntryFullUrl({ + system: uri('https://example.org/identifiers'), + value: 'invalid-\udc00', + }).ok, + ).toBe(false) + expect( + deriveEntryFullUrl({ + system: uri('https://example.org/identifiers'), + value: 'valid-😀', + }).ok, + ).toBe(true) + }) + + it('rejects incomplete entry identifiers and invalid repository ids', () => { + expect( + entryIdentifierName({ + system: '/relative' as never, + value: 'record-1', + }).ok, + ).toBe(false) + expect( + entryIdentifierName({ + system: uri('https://example.org/identifiers'), + value: '', + }).ok, + ).toBe(false) + expect( + entryIdentifierName({ + system: uri('https://example.org/identifiers'), + value: ' \t\n ', + }).ok, + ).toBe(true) + expect( + createEntryIdentity( + { + system: uri('https://example.org/identifiers'), + value: 'record-1', + }, + 'invalid/id' as never, + ).ok, + ).toBe(false) + }) +}) + +describe('Provider-neutral normalization contract', () => { + it.each([ + 'google-health-active-energy.json', + 'oura-daily-activity.json', + 'oura-sleep-duration.json', + 'withings-body-weight.json', + ])('accepts the normalized %s fixture', (name) => { + const fixture: unknown = JSON.parse( + readFileSync(new URL(`../fixtures/normalized/${name}`, import.meta.url), { + encoding: 'utf8', + }), + ) + expect(parseNormalizedProviderRecord(fixture).ok).toBe(true) + }) + + it('normalizes multi-output serialization order without mutating caller data', () => { + const fixture = JSON.parse( + readFileSync( + new URL( + '../fixtures/normalized/oura-daily-activity.json', + import.meta.url, + ), + { encoding: 'utf8' }, + ), + ) as { measurements: Array<{ kind: string }> } + const callerOrder = fixture.measurements.map(({ kind }) => kind) + const result = parseNormalizedProviderRecord(fixture) + expect(result.ok).toBe(true) + if (!result.ok) return + expect(result.value.measurements.map(({ kind }) => kind)).toEqual([ + 'step-count', + 'active-energy', + 'distance', + ]) + expect(fixture.measurements.map(({ kind }) => kind)).toEqual(callerOrder) + expect(Object.isFrozen(result.value.measurements)).toBe(true) + }) + + it('rejects raw response fields rather than silently stripping them', () => { + const fixture = JSON.parse( + readFileSync( + new URL( + '../fixtures/normalized/oura-sleep-duration.json', + import.meta.url, + ), + { encoding: 'utf8' }, + ), + ) as Record + expect( + parseNormalizedProviderRecord({ + ...fixture, + rawVendorResponse: { readiness: 97 }, + }).ok, + ).toBe(false) + }) + + it('requires the provider-dependent scope assurance declared by the catalog', () => { + const fixture = JSON.parse( + readFileSync( + new URL( + '../fixtures/normalized/oura-sleep-duration.json', + import.meta.url, + ), + { encoding: 'utf8' }, + ), + ) as { + source: { + providerScopeIdentifier: Record + } + } + const { system, value } = fixture.source.providerScopeIdentifier + expect( + parseNormalizedProviderRecord({ + ...fixture, + source: { + ...fixture.source, + providerScopeIdentifier: { system, value }, + }, + }).ok, + ).toBe(false) + + expect( + parseNormalizedProviderRecord({ + ...fixture, + source: { + ...fixture.source, + providerScopeIdentifier: { + ...fixture.source.providerScopeIdentifier, + assurance: 'deployment-scoped-account-pseudonym', + }, + }, + }).ok, + ).toBe(false) + + const accountScoped = baseInput( + 'withings', + 'getmeas:11', + heartRateMeasurement, + ) + expect( + parseNormalizedProviderRecord({ + source: { + ...accountScoped.source, + providerScopeIdentifier: { + system: 'https://example.org/provider-key-spaces', + value: 'withings-global', + assurance: 'documented-global-key-space', + }, + }, + measurements: accountScoped.measurements, + }).ok, + ).toBe(false) + }) + + it('strictly parses the complete graph input and preserves identity strings verbatim', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const result = parseProviderMeasurementBundleInput({ + ...input, + source: { + ...input.source, + providerScopeIdentifier: { + ...input.source.providerScopeIdentifier, + value: ' pseudonym-with-significant-spaces ', + }, + }, + }) + expect(result.ok).toBe(true) + if (!result.ok) return + expect(result.value.source.providerScopeIdentifier.value).toBe( + ' pseudonym-with-significant-spaces ', + ) + + expect( + buildProviderMeasurementBundle({ + ...input, + rawVendorResponse: { heart_rate: 64 }, + } as ProviderMeasurementBundleInput & { + rawVendorResponse: unknown + }).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + rawVendorResponse: { heart_rate: 64 }, + }, + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + }) + + it('rejects an arbitrary adapter and a mismatched source token', () => { + const dataOrigin = { + sourceDeviceToken: 'oura', + name: 'Oura', + } + expect( + parseNormalizedProviderRecord({ + source: { + adapter: { kind: 'mobile', provider: 'oura' }, + providerScopeIdentifier: { + system: 'https://example.org/provider-key-spaces', + value: 'oura-document-uuid-global', + assurance: 'documented-global-key-space', + }, + sourceType: 'sleep', + sourceNativeId: 'sleep-1', + dataOrigin, + }, + measurements: [ + { + kind: 'sleep-duration', + value: 7.4, + effective: { kind: 'period', start, end }, + }, + ], + }).ok, + ).toBe(false) + + expect( + parseNormalizedProviderRecord({ + source: { + adapter: { kind: 'providers', provider: 'oura' }, + providerScopeIdentifier: { + system: 'https://example.org/provider-key-spaces', + value: 'oura-document-uuid-global', + assurance: 'documented-global-key-space', + }, + sourceType: 'daily_readiness', + sourceNativeId: 'readiness-1', + dataOrigin, + }, + measurements: [heartRateMeasurement], + }).ok, + ).toBe(false) + }) + + it('correlates provider, source token, and measurement at compile time', () => { + const withingsSource = { + adapter: { kind: 'providers', provider: 'withings' }, + providerScopeIdentifier: { + system: uri('https://example.org/deployments/provider-accounts'), + value: 'pseudonym-withings-001', + assurance: 'deployment-scoped-account-pseudonym', + }, + sourceType: 'getmeas:11', + sourceNativeId: 'heart-rate-1', + dataOrigin: { + sourceDeviceToken: 'withings', + name: 'Withings', + }, + } as const + const valid: ConnectedProviderRecord<'withings', 'getmeas:11'> = { + source: withingsSource, + measurements: [heartRateMeasurement], + } + expect(valid.measurements[0].kind).toBe('heart-rate') + + type OuraSleep = ConnectedProviderRecord<'oura', 'sleep'> + const source = { + ...withingsSource, + adapter: { kind: 'providers', provider: 'oura' }, + sourceType: 'sleep', + } as const + const invalidMeasurement = { + kind: 'blood-glucose', + value: 95, + effective: { kind: 'date-time', value: dateTime }, + specimen: { + identity: resourceIdentity('https://example.org/specimens', 'blood'), + }, + } + // @ts-expect-error Oura sleep admits sleep-duration, never glucose. + const invalid: OuraSleep = { source, measurements: [invalidMeasurement] } + expect(invalid).toBeDefined() + }) + + it('generates only catalog-owned scalar source tokens', () => { + expect(Object.hasOwn(providerScalarOutputRoles.withings, 'getmeas:9')).toBe( + false, + ) + expect(providerScalarOutputRoles.withings['getmeas:9+10']).toEqual({ + 'blood-pressure': 'blood-pressure-panel', + }) + expect(providerScalarOutputDiscriminators.withings['getmeas:9+10']).toEqual( + { + 'blood-pressure': 'single', + }, + ) + }) + + it('keeps native-recording output role and discriminator as distinct coordinates', () => { + expect(providerAdapterCatalog.recordingDocument).toMatchObject({ + outputRole: 'native-recording', + outputDiscriminator: 'single', + }) + expect(providerRawOutputRoles.withings.activityIntraday).toBe( + 'native-recording', + ) + expect(providerRawOutputDiscriminators.withings.activityIntraday).toBe( + 'single', + ) + }) +}) + +describe('Provider builder properties', () => { + it('preserves every finite positive heart-rate value', () => { + assert( + property( + double({ + min: 0.01, + max: 400, + noNaN: true, + noDefaultInfinity: true, + }), + (value) => { + const result = buildProviderMeasurementBundle( + baseInput('withings', 'getmeas:11', { + kind: 'heart-rate', + value, + effective: { kind: 'date-time', value: dateTime }, + }), + ) + expect(result.ok).toBe(true) + if (!result.ok) return + expect( + resources(result).find( + (resource) => resource.resourceType === 'Observation', + )?.valueQuantity?.value, + ).toBe(value) + }, + ), + ) + }) + + it('rejects every oxygen saturation outside the percentage range', () => { + assert( + property( + oneof( + double({ min: -10_000, max: -Number.MIN_VALUE, noNaN: true }), + double({ min: 100.000_001, max: 10_000, noNaN: true }), + ), + (value) => { + expect( + buildProviderMeasurementBundle( + baseInput('withings', 'getmeas:54', { + kind: 'oxygen-saturation', + value, + effective: { kind: 'date-time', value: dateTime }, + }), + ).ok, + ).toBe(false) + }, + ), + ) + }) +}) diff --git a/packages/fhir/test/provider-identity.test.ts b/packages/fhir/test/provider-identity.test.ts new file mode 100644 index 00000000..3ca77889 --- /dev/null +++ b/packages/fhir/test/provider-identity.test.ts @@ -0,0 +1,667 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + application, + baseInput, + bloodPressureMeasurement, + dailyEnd, + dateTime, + deploymentIdentity, + end, + heartRateMeasurement, + resources, + start, + study, + uri, +} from './provider-test-support.js' +import { sharedMobileMeasurementCatalog } from '../src/mobile/index.js' +import { + buildProviderMeasurementBundle, + parseProviderMeasurementBundleInput, + type ProviderMeasurementBundleInput, +} from '../src/providers/index.js' + +describe('Provider R4 graph builder', () => { + it('does not infer identity disclosure from unrelated substring collisions', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + expect( + buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + sourceNativeId: '1', + }, + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(true) + }) + + it('derives exact source, conversion and exchange identifiers in their proper namespaces', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const result = buildProviderMeasurementBundle(input) + expect(result.ok).toBe(true) + if (!result.ok) return + + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + if (observation?.resourceType !== 'Observation') { + throw new Error('The graph did not contain its Observation.') + } + expect(observation.identifier?.map(({ system }) => system)).toEqual([ + deploymentIdentity.opaqueIdentifierSystems['provider-record'], + ( + deploymentIdentity.opaqueIdentifierSystems as Readonly< + Record + > + )['provider-output'], + ]) + for (const identity of observation.identifier ?? []) { + expect(identity.value).toMatch(/^v2:test-key:1:[A-Za-z0-9_-]{43}$/u) + } + expect(result.value.identifier.system).toBe( + deploymentIdentity.eventIdentifierSystem, + ) + const provenanceEntry = result.value.entry.find( + (entry) => entry.resource.resourceType === 'Provenance', + ) + expect(provenanceEntry?.extension?.[0]?.valueIdentifier?.system).toBe( + deploymentIdentity.entryNodeIdentifierSystem, + ) + const serialized = JSON.stringify(result.value) + expect(serialized).not.toContain(input.source.sourceNativeId) + expect(serialized).not.toContain(input.source.providerScopeIdentifier.value) + }) + + it('places an explicitly governed native Identifier on exactly the designated primary Observation', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const nativeSystem = uri( + 'https://example.org/repositories/withings-account-7/heart-rate-records', + ) + const result = buildProviderMeasurementBundle({ + ...input, + nativeIdentifierDisclosure: { + system: nativeSystem, + nativeId: input.source.sourceNativeId, + type: { text: 'Withings account-scoped heart-rate id' }, + }, + }) + expect(result.ok).toBe(true) + if (!result.ok) return + + const carryingResources = result.value.entry.filter(({ resource }) => + ( + resource as { + readonly identifier?: ReadonlyArray<{ + readonly system?: string + readonly value?: string + }> + } + ).identifier?.some( + ({ system, value }) => + system === nativeSystem && value === input.source.sourceNativeId, + ), + ) + expect(carryingResources).toHaveLength(1) + const primary = carryingResources[0]?.resource + expect(primary?.resourceType).toBe('Observation') + if (primary?.resourceType !== 'Observation') return + expect(primary.code.coding?.[0]?.code).toBe( + sharedMobileMeasurementCatalog['heart-rate'].code.code, + ) + expect( + primary.identifier?.find(({ system }) => system === nativeSystem), + ).toEqual({ + system: nativeSystem, + value: input.source.sourceNativeId, + type: { text: 'Withings account-scoped heart-rate id' }, + }) + }) + + it('emits a writer version only with its writer identity while allowing an unversioned identity', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const writerRecord = { + applicationIdentifier: { + system: uri('https://example.org/applications'), + value: 'writer-app', + }, + nativeRecordId: 'writer-record-1', + } as const + const unversioned = buildProviderMeasurementBundle({ + ...input, + source: { ...input.source, writerRecord }, + } as ProviderMeasurementBundleInput) + const versioned = buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + writerRecord: { ...writerRecord, version: '0' }, + }, + eventSequence: '2', + } as ProviderMeasurementBundleInput) + expect(unversioned.ok && versioned.ok).toBe(true) + if (!unversioned.ok || !versioned.ok) return + + const observation = (result: typeof unversioned) => + resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + const writerVersionUrl = + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version' + const hasWriterIdentity = (resource: ReturnType) => + resource?.identifier?.some((candidate) => + candidate.type?.coding?.some(({ code }) => code === 'writer-record'), + ) + + expect(hasWriterIdentity(observation(unversioned))).toBe(true) + expect( + observation(unversioned)?.extension?.some( + ({ url }) => url === writerVersionUrl, + ), + ).not.toBe(true) + expect(hasWriterIdentity(observation(versioned))).toBe(true) + expect(observation(versioned)?.extension).toEqual( + expect.arrayContaining([{ url: writerVersionUrl, valueString: '0' }]), + ) + expect( + parseProviderMeasurementBundleInput({ + ...input, + source: { ...input.source, writerRecordVersion: '1' }, + }).ok, + ).toBe(false) + }) + + it('treats one supported grouped catalog mapping as one designated Provider output', () => { + const input = baseInput( + 'withings', + 'getmeas:9+10', + bloodPressureMeasurement, + ) + const nativeSystem = uri( + 'https://example.org/repositories/withings-account-7/blood-pressure-records', + ) + const result = buildProviderMeasurementBundle({ + ...input, + nativeIdentifierDisclosure: { + system: nativeSystem, + nativeId: input.source.sourceNativeId, + }, + }) + expect(result.ok).toBe(true) + if (!result.ok) return + const carrying = result.value.entry.filter(({ resource }) => { + const identifiers = ( + resource as { + readonly identifier?: ReadonlyArray<{ + readonly system?: string + readonly value?: string + }> + } + ).identifier + return identifiers?.some( + ({ system, value }) => + system === nativeSystem && value === input.source.sourceNativeId, + ) + }) + expect(carrying).toHaveLength(1) + expect(carrying[0]?.resource.resourceType).toBe('Observation') + }) + + it.each([ + { + name: 'a value different from the reconciliation source id', + disclosure: (input: ProviderMeasurementBundleInput) => ({ + system: uri('https://example.org/repositories/provider-records'), + nativeId: `${input.source.sourceNativeId}-wrong`, + }), + }, + { + name: 'a relative key-space system', + disclosure: (input: ProviderMeasurementBundleInput) => ({ + system: 'provider-records', + nativeId: input.source.sourceNativeId, + }), + }, + { + name: 'the Grove event identity system', + disclosure: (input: ProviderMeasurementBundleInput) => ({ + system: input.deploymentIdentity.eventIdentifierSystem, + nativeId: input.source.sourceNativeId, + }), + }, + { + name: 'a Grove opaque identity system', + disclosure: (input: ProviderMeasurementBundleInput) => ({ + system: + input.deploymentIdentity.opaqueIdentifierSystems['source-record'], + nativeId: input.source.sourceNativeId, + }), + }, + { + name: 'a Grove graph-role Identifier.type coding', + disclosure: (input: ProviderMeasurementBundleInput) => ({ + system: uri('https://example.org/repositories/provider-records'), + nativeId: input.source.sourceNativeId, + type: { + coding: [ + { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role', + code: 'source-record', + }, + ], + }, + }), + }, + ])('rejects governed source disclosure using $name', ({ disclosure }) => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const candidate = { + ...input, + nativeIdentifierDisclosure: disclosure(input), + } as unknown as ProviderMeasurementBundleInput + expect(parseProviderMeasurementBundleInput(candidate).ok).toBe(false) + expect(buildProviderMeasurementBundle(candidate).ok).toBe(false) + }) + + it('rejects native disclosure for a catalog-ambiguous multi-output Provider row', () => { + const input = baseInput('oura', 'daily_activity', { + kind: 'distance', + value: 6_123, + effective: { kind: 'period', start, end: dailyEnd }, + }) + const candidate = { + ...input, + nativeIdentifierDisclosure: { + system: uri( + 'https://example.org/repositories/oura-account-7/daily-activity-records', + ), + nativeId: input.source.sourceNativeId, + }, + } as ProviderMeasurementBundleInput + const parsed = parseProviderMeasurementBundleInput(candidate) + expect(parsed.ok).toBe(false) + if (!parsed.ok) { + expect(parsed.issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + code: 'value-mismatch', + path: ['nativeIdentifierDisclosure'], + }), + ]), + ) + } + expect(buildProviderMeasurementBundle(candidate).ok).toBe(false) + }) + + it.each([ + ' leading', + 'trailing ', + 'two spaces', + 'tab\tcode', + 'control\u0001code', + ])('rejects non-lexical FHIR code %p in native Identifier.type', (code) => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + expect( + parseProviderMeasurementBundleInput({ + ...input, + nativeIdentifierDisclosure: { + system: 'https://example.org/repositories/provider-records', + nativeId: input.source.sourceNativeId, + type: { + coding: [ + { + system: 'https://example.org/fhir/CodeSystem/identifier-type', + code, + }, + ], + }, + }, + }).ok, + ).toBe(false) + }) + + it('matches the frozen Provider composed source and output vectors', () => { + const input = baseInput('google-health-api', 'steps', { + kind: 'step-count', + value: 123, + effective: { kind: 'period', start, end }, + }) + const result = buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + providerScopeIdentifier: { + system: uri('https://provider.example.org/accounts'), + value: 'account-001', + assurance: 'deployment-scoped-account-pseudonym', + }, + sourceNativeId: 'steps-2026-08-20T16:00:00Z', + }, + } as ProviderMeasurementBundleInput) + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(observation?.identifier).toHaveLength(2) + expect(observation?.identifier?.map((entry) => entry.value)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^v2:test-key:1:[A-Za-z0-9_-]{43}$/u), + ]), + ) + + const unicodeInput = baseInput('oura', 'sleep', { + kind: 'sleep-duration', + value: 7, + effective: { kind: 'period', start, end }, + }) + const unicodeResult = buildProviderMeasurementBundle({ + ...unicodeInput, + source: { + ...unicodeInput.source, + providerScopeIdentifier: { + system: uri('https://example.org/provider-key-spaces'), + value: 'oura-document-uuid-global', + assurance: 'documented-global-key-space', + }, + sourceNativeId: 'résumé-\u0001', + }, + } as ProviderMeasurementBundleInput) + const unicodeObservation = resources(unicodeResult).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(unicodeObservation?.identifier?.[0]?.value).toMatch( + /^v2:test-key:1:[A-Za-z0-9_-]{43}$/u, + ) + expect(JSON.stringify(unicodeResult)).not.toContain('résumé-\u0001') + }) + + it('changes only event identities when the durable event sequence changes', () => { + const first = buildProviderMeasurementBundle( + baseInput('withings', 'getmeas:11', heartRateMeasurement), + ) + const second = buildProviderMeasurementBundle({ + ...baseInput('withings', 'getmeas:11', heartRateMeasurement), + eventSequence: '2', + }) + expect(first.ok && second.ok).toBe(true) + if (!first.ok || !second.ok) return + + const firstObservation = resources(first).find( + (resource) => resource.resourceType === 'Observation', + ) + const secondObservation = resources(second).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(firstObservation?.identifier).toEqual(secondObservation?.identifier) + expect(first.value.identifier).not.toEqual(second.value.identifier) + }) + + it('adds a gateway reference only with explicit mediation evidence', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const converterGateway = buildProviderMeasurementBundle({ + ...input, + gatewayApplication: { + kind: 'converter-application', + roleAssurance: 'mediated-or-routed-measurement', + }, + }) + expect(converterGateway.ok).toBe(true) + if (!converterGateway.ok) return + expect(converterGateway.value.entry).toHaveLength(4) + const converterObservation = resources(converterGateway).find( + (resource) => resource.resourceType === 'Observation', + ) + const converterApplicationEntry = converterGateway.value.entry.find( + (entry) => + entry.resource.resourceType === 'Device' && + entry.resource.deviceName?.[0]?.name === application.name, + ) + expect( + converterObservation?.extension?.find( + ({ url }) => + url === + 'http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice', + )?.valueReference?.reference, + ).toBe(converterApplicationEntry?.fullUrl) + + expect( + buildProviderMeasurementBundle({ + ...input, + gatewayApplication: { + kind: 'converter-application', + roleAssurance: 'conversion-only', + }, + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + }) + + it('builds a fully attributed graph with distinct gateway and authorized hardware identity', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const result = buildProviderMeasurementBundle({ + ...input, + application: { + ...input.application, + manufacturer: 'Grove Alliance', + host: { + sourceDeviceToken: 'converter-host', + name: 'Converter phone', + manufacturer: 'Example hardware', + modelNumber: 'Phone 1', + operatingSystemVersion: '20.0', + }, + }, + gatewayApplication: { + kind: 'distinct-application', + roleAssurance: 'mediated-or-routed-measurement', + application: { + sourceDeviceToken: 'gateway-app', + name: 'Gateway app', + version: '2.1.0', + manufacturer: 'Gateway vendor', + host: { + sourceDeviceToken: 'gateway-host', + name: 'Gateway server', + operatingSystemVersion: 'Linux 7', + }, + }, + }, + source: { + ...input.source, + dataOrigin: { + ...input.source.dataOrigin, + version: '3.0.0', + manufacturer: 'Source vendor', + host: { + sourceDeviceToken: 'source-host', + name: 'Source host', + operatingSystemVersion: 'Provider runtime 3', + }, + }, + recordingDevice: { + stableUnitToken: 'hardware-1', + subjectIdentifier: { + system: uri('https://example.org/participants'), + value: 'participant-pseudonym-001', + }, + identityScope: 'authorized-hardware', + disclosureAuthorization: 'authorized-for-exchange', + name: 'Chest strap', + manufacturer: 'Recorder vendor', + modelNumber: 'Model 1', + }, + }, + researchStudyReferences: [study('study-1')], + } as ProviderMeasurementBundleInput) + expect(result.ok).toBe(true) + if (!result.ok) return + expect(result.value.entry).toHaveLength(9) + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(observation?.device?.reference).toMatch(/^urn:uuid:/u) + expect( + observation?.extension?.some( + ({ url }) => + url === + 'http://hl7.org/fhir/StructureDefinition/workflow-researchStudy', + ), + ).toBe(true) + const hostEntries = result.value.entry.filter( + ({ resource }) => + resource.resourceType === 'Device' && + resource.meta?.profile?.includes( + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device', + ), + ) + expect(hostEntries).toHaveLength(3) + for (const applicationDevice of resources(result)) { + if ( + applicationDevice.resourceType !== 'Device' || + !applicationDevice.meta?.profile?.includes( + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device', + ) + ) { + continue + } + expect(applicationDevice.parent?.reference).toMatch(/^urn:uuid:/u) + expect( + hostEntries.some( + ({ fullUrl }) => fullUrl === applicationDevice.parent?.reference, + ), + ).toBe(true) + } + }) + + it('requires an explicit privacy scope for recording-device identity and never accepts a serial number', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const result = buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + recordingDevice: { + stableUnitToken: 'device-pseudonym-1', + subjectIdentifier: { + system: uri('https://example.org/participants'), + value: 'participant-pseudonym-001', + }, + identityScope: 'deployment-scoped', + name: 'Connected scale', + }, + }, + } as ProviderMeasurementBundleInput) + expect(result.ok).toBe(true) + if (!result.ok) return + const recordingDevice = resources(result).find( + (resource) => + resource.resourceType === 'Device' && + resource.deviceName?.[0]?.name === 'Connected scale', + ) + expect(recordingDevice).toBeDefined() + if (recordingDevice?.resourceType !== 'Device') { + throw new Error('The graph did not contain its recording Device.') + } + expect(recordingDevice).not.toHaveProperty('serialNumber') + expect( + recordingDevice.identifier?.map(({ type }) => type?.coding?.[0]?.code), + ).toEqual(['recording-device', 'device-snapshot']) + const recordingEntry = result.value.entry.find( + ({ resource }) => resource === recordingDevice, + ) + expect( + recordingEntry?.extension?.[0]?.valueIdentifier?.type?.coding?.[0]?.code, + ).toBe('device-snapshot') + + expect( + buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + recordingDevice: { + stableUnitToken: 'global-hardware-id', + subjectIdentifier: { + system: uri('https://example.org/participants'), + value: 'example', + }, + identityScope: 'authorized-hardware', + serialNumber: 'SERIAL-123', + }, + }, + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + }) + + it('rejects arbitrary source codings instead of treating lineage as a clinical code', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + expect( + buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + sourceTypeCoding: { + system: 'https://provider.example/source-types', + code: 'broad-summary-record', + }, + }, + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + }) + + it('accepts only unique, unambiguous typed ResearchStudy references', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const local = study('local-1') + const absolute = study('remote-1') + expect( + buildProviderMeasurementBundle({ + ...input, + researchStudyReferences: [local, absolute], + }).ok, + ).toBe(true) + + const duplicate = parseProviderMeasurementBundleInput({ + ...input, + researchStudyReferences: [local, local], + }) + expect(duplicate.ok).toBe(false) + if (!duplicate.ok) { + expect(duplicate.issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ code: 'duplicate-identifier' }), + ]), + ) + } + expect( + parseProviderMeasurementBundleInput({ + ...input, + researchStudyReferences: [ + { + type: 'ResearchStudy', + identifier: { + system: 'https://research.example/invalid system', + value: 'remote-1', + }, + }, + ], + }).ok, + ).toBe(false) + expect( + parseProviderMeasurementBundleInput({ + ...baseInput('withings', 'getmeas:54', { + kind: 'oxygen-saturation', + value: 98, + effective: { kind: 'date-time', value: dateTime }, + }), + measurements: [ + { + kind: 'oxygen-saturation', + value: 0, + effective: { kind: 'date-time', value: dateTime }, + }, + ], + }).ok, + ).toBe(true) + }) +}) diff --git a/packages/fhir/test/provider-mapping.test.ts b/packages/fhir/test/provider-mapping.test.ts new file mode 100644 index 00000000..59678b7b --- /dev/null +++ b/packages/fhir/test/provider-mapping.test.ts @@ -0,0 +1,593 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + baseInput, + bloodPressureMeasurement, + dailyEnd, + dateTime, + end, + instant, + mutableRecord, + resources, + scalarCases, + start, + unwrap, +} from './provider-test-support.js' +import { parseFhirId, parseGroveMobileExchangeBundle } from '../src/index.js' +import { + sharedMobileMeasurementCatalog, + type MobileMeasurement, +} from '../src/mobile/index.js' +import { + adapterMeasurementCatalog, + buildProviderMeasurementBundle, + providerAdapterCatalog, + providerRecordEffectiveRules, + providerScalarOutputDiscriminators, + providerScalarOutputRoles, + type ConnectedProvider, + type ProviderMeasurementBundleInput, +} from '../src/providers/index.js' + +interface ProviderContractRow { + readonly id: ConnectedProvider + readonly measurementOwner: keyof typeof adapterMeasurementCatalog + readonly observationProfile: string +} + +const providerContractRow = (provider: ConnectedProvider) => { + const row = ( + providerAdapterCatalog.providers as unknown as readonly ProviderContractRow[] + ).find(({ id }) => id === provider) + if (row === undefined) + throw new Error(`Missing Provider row for ${provider}.`) + return row +} + +describe('Provider R4 graph builder', () => { + it.each(scalarCases)( + 'builds the admitted $provider/$sourceType $measurement.kind graph', + ({ provider, sourceType, measurement }) => { + const result = buildProviderMeasurementBundle( + baseInput(provider, sourceType, measurement), + ) + expect(result.ok).toBe(true) + if (!result.ok) return + + expect(result.value.type).toBe('collection') + expect(result.value.entry).toHaveLength(4) + expect('total' in result.value).toBe(false) + expect(Object.isFrozen(result.value)).toBe(true) + + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(observation?.meta?.profile).toEqual([ + `https://grovealliance.org/fhir/mobile/StructureDefinition/${sharedMobileMeasurementCatalog[measurement.kind].profile}`, + providerContractRow(provider).observationProfile, + ]) + expect(observation?.extension).toEqual( + expect.arrayContaining([ + { + url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider', + valueCode: provider, + }, + { + url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type', + valueCode: `${provider}/${sourceType}`, + }, + ]), + ) + expect( + observation?.extension?.some( + ({ url }) => + url === + 'http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice', + ), + ).toBe(false) + expect(observation?.code.coding).toHaveLength(1) + expect(observation?.id).toBeUndefined() + }, + ) + + it.each([ + { + provider: 'oura', + sourceType: 'daily_cardiovascular_age', + measurement: { + kind: 'oura-cardiovascular-age', + value: 38, + effective: { kind: 'period', start, end: dailyEnd }, + }, + semanticProfile: + 'https://grovealliance.org/fhir/oura/StructureDefinition/oura-cardiovascular-age', + code: 'oura-cardiovascular-age', + }, + { + provider: 'withings', + sourceType: 'getmeas:155', + measurement: { + kind: 'withings-vascular-age', + value: 45, + effective: { kind: 'date-time', value: dateTime }, + }, + semanticProfile: + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-vascular-age', + code: 'withings-vascular-age', + }, + ] as const)( + 'emits $provider platform-exclusive age under its exact semantic and vendor profiles', + ({ provider, sourceType, measurement, semanticProfile, code }) => { + const result = buildProviderMeasurementBundle( + baseInput( + provider, + sourceType, + measurement as unknown as MobileMeasurement, + ), + ) + expect(result.ok).toBe(true) + if (!result.ok) return + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(observation?.meta?.profile).toEqual([ + semanticProfile, + providerContractRow(provider).observationProfile, + ]) + expect(observation?.code.coding).toEqual([ + expect.objectContaining({ code }), + ]) + expect(observation?.valueQuantity).toEqual( + expect.objectContaining({ + system: 'http://unitsofmeasure.org', + code: 'a', + }), + ) + }, + ) + + it('rejects generic-parent, cross-vendor, cross-marker, and cross-owner Provider claims', () => { + const built = buildProviderMeasurementBundle( + baseInput('withings', 'getmeas:11', { + kind: 'heart-rate', + value: 64, + effective: { kind: 'date-time', value: dateTime }, + }), + ) + expect(built.ok).toBe(true) + if (!built.ok) return + + const mutateObservation = ( + mutate: (observation: Record) => void, + ) => { + const bundle = structuredClone(built.value) + const observation = bundle.entry.find( + ({ resource }) => resource.resourceType === 'Observation', + )?.resource + if (observation?.resourceType !== 'Observation') { + throw new Error('Expected a Provider Observation.') + } + mutate(observation) + return parseGroveMobileExchangeBundle(bundle) + } + + expect( + mutateObservation((observation) => { + mutableRecord(observation.meta, 'Observation.meta').profile = [ + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate', + providerAdapterCatalog.adapterProfile, + ] + }).ok, + ).toBe(false) + expect( + mutateObservation((observation) => { + mutableRecord(observation.meta, 'Observation.meta').profile = [ + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate', + providerContractRow('oura').observationProfile, + ] + }).ok, + ).toBe(false) + expect( + mutateObservation((observation) => { + const extensions = observation.extension + if (!Array.isArray(extensions)) throw new Error('Missing extensions.') + const marker: unknown = extensions.find( + (extension) => + mutableRecord(extension, 'Observation.extension').url === + providerAdapterCatalog.providerExtension.url, + ) + mutableRecord(marker, 'Provider marker').valueCode = 'oura' + }).ok, + ).toBe(false) + + const crossOwner = baseInput('oura', 'daily_cardiovascular_age', { + kind: 'oura-cardiovascular-age', + value: 38, + effective: { kind: 'period', start, end: dailyEnd }, + } as unknown as MobileMeasurement) + expect( + buildProviderMeasurementBundle({ + ...crossOwner, + measurements: [ + { + kind: 'withings-vascular-age', + value: 45, + effective: { kind: 'date-time', value: dateTime }, + }, + ], + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + }) + + it('builds every exact provider/source/measurement mapping in the generated catalog', () => { + interface ExampleDefinition { + readonly effective: 'Period' | 'dateTime' | 'dateTime-or-Period' + readonly allowedValues?: readonly string[] + readonly quantity?: { readonly example?: number } | null + } + const sharedDefinitions = + sharedMobileMeasurementCatalog as unknown as Readonly< + Record + > + const ownerDefinitions = adapterMeasurementCatalog as unknown as Readonly< + Record>> + > + const exampleMeasurement = ( + provider: ConnectedProvider, + kind: string, + ): MobileMeasurement | undefined => { + if (kind === 'blood-pressure') return bloodPressureMeasurement + if (kind === 'sleep-stage') { + return { + kind: 'sleep-stage', + stage: 'light', + effective: { kind: 'period', start, end }, + } + } + const row = providerContractRow(provider) + const definition = + sharedDefinitions[kind] ?? + ownerDefinitions[row.measurementOwner]?.[kind] + if (definition === undefined) return undefined + const effective = + definition.effective === 'Period' ? + ({ kind: 'period', start, end } as const) + : ({ kind: 'date-time', value: dateTime } as const) + const value = + definition.allowedValues?.[0] ?? definition.quantity?.example ?? 72 + return { kind, value, effective } as MobileMeasurement + } + const exhaustiveMappings = providerScalarOutputRoles as Readonly< + Record< + ConnectedProvider, + Readonly>>> + > + > + const exhaustiveDiscriminators = + providerScalarOutputDiscriminators as Readonly< + Record< + ConnectedProvider, + Readonly>>> + > + > + let checked = 0 + for (const [provider, sourceMappings] of Object.entries( + exhaustiveMappings, + )) { + for (const [sourceType, mappings] of Object.entries(sourceMappings)) { + const discriminatorMappings = + exhaustiveDiscriminators[provider as ConnectedProvider][sourceType] + expect(discriminatorMappings).toBeDefined() + if (discriminatorMappings === undefined) { + throw new Error( + `Missing output discriminators for ${provider}/${sourceType}.`, + ) + } + for (const measurementKind of Object.keys(mappings)) { + expect(discriminatorMappings[measurementKind]).toBeDefined() + const measurement = exampleMeasurement( + provider as ConnectedProvider, + measurementKind, + ) + expect(measurement).toBeDefined() + if (measurement === undefined) continue + const effectiveRule = ( + providerRecordEffectiveRules as Readonly< + Record> | undefined> + > + )[provider]?.[sourceType] + const sourceMeasurement = + effectiveRule === undefined ? measurement : ( + { + ...measurement, + effective: { kind: 'period', start, end: dailyEnd }, + } + ) + expect( + buildProviderMeasurementBundle( + baseInput( + provider as ConnectedProvider, + sourceType, + sourceMeasurement as MobileMeasurement, + ), + ).ok, + ).toBe(true) + checked += 1 + } + } + } + expect(checked).toBeGreaterThan(scalarCases.length) + }) + + it('does not invent uncatalogued physiologic or duration lower bounds', () => { + expect( + buildProviderMeasurementBundle( + baseInput('google-health-api', 'active-energy-burned', { + kind: 'active-energy', + value: 0, + effective: { kind: 'period', start, end: start }, + }), + ).ok, + ).toBe(true) + expect( + buildProviderMeasurementBundle( + baseInput('withings', 'getmeas:9+10', { + kind: 'blood-pressure', + systolic: 0, + diastolic: 0, + effective: { kind: 'date-time', value: dateTime }, + }), + ).ok, + ).toBe(true) + }) + + it('rejects negative and zero-duration step-count resources at the profiled graph boundary', () => { + const built = buildProviderMeasurementBundle( + baseInput('google-health-api', 'steps', { + kind: 'step-count', + value: 42, + effective: { kind: 'period', start, end }, + }), + ) + expect(built.ok).toBe(true) + if (!built.ok) return + + const mutateObservation = ( + mutate: (observation: Record) => void, + ) => { + const bundle = structuredClone(built.value) + const observation = bundle.entry.find( + ({ resource }) => resource.resourceType === 'Observation', + )?.resource + if (observation?.resourceType !== 'Observation') { + throw new Error('Expected a step-count Observation.') + } + mutate(observation) + return parseGroveMobileExchangeBundle(bundle) + } + + expect( + mutateObservation((observation) => { + mutableRecord( + observation.valueQuantity, + 'Observation.valueQuantity', + ).value = -1 + }).ok, + ).toBe(false) + expect( + mutateObservation((observation) => { + const period = mutableRecord( + observation.effectivePeriod, + 'Observation.effectivePeriod', + ) + period.end = period.start + }).ok, + ).toBe(false) + }) + + it('atomically builds every present Oura daily-activity output with one complete Provenance target set', () => { + const dailyActivity = baseInput('oura', 'daily_activity', { + kind: 'distance', + value: 6_123, + effective: { kind: 'period', start, end: dailyEnd }, + }) + const result = buildProviderMeasurementBundle({ + ...dailyActivity, + measurements: [ + { + kind: 'distance', + value: 6_123, + effective: { kind: 'period', start, end: dailyEnd }, + }, + { + kind: 'active-energy', + value: 430, + effective: { kind: 'period', start, end: dailyEnd }, + }, + { + kind: 'step-count', + value: 8_234, + effective: { kind: 'period', start, end: dailyEnd }, + }, + ], + } as ProviderMeasurementBundleInput) + expect(result.ok).toBe(true) + if (!result.ok) return + + const observations = result.value.entry.filter( + ({ resource }) => resource.resourceType === 'Observation', + ) + expect(observations).toHaveLength(3) + expect( + observations.map(({ resource }) => resource.meta?.profile?.[0]), + ).toEqual([ + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-step-count', + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-active-energy', + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-distance', + ]) + const provenance = result.value.entry.find( + ({ resource }) => resource.resourceType === 'Provenance', + )?.resource + expect(provenance?.resourceType).toBe('Provenance') + if (provenance?.resourceType !== 'Provenance') return + expect(provenance.target).toHaveLength(observations.length) + expect( + new Set(provenance.target.map(({ reference }) => reference)), + ).toEqual(new Set(observations.map(({ fullUrl }) => fullUrl))) + }) + + it.each([ + ['one', ['step-count']], + ['two', ['step-count', 'distance']], + ] as const)( + 'admits a %s-output subset when those are the fields present in an Oura daily-activity record', + (_label, kinds) => { + const candidates = { + 'step-count': { + kind: 'step-count', + value: 8_234, + effective: { kind: 'period', start, end: dailyEnd }, + }, + distance: { + kind: 'distance', + value: 6_123, + effective: { kind: 'period', start, end: dailyEnd }, + }, + } as const + const input = baseInput('oura', 'daily_activity', candidates[kinds[0]]) + const result = buildProviderMeasurementBundle({ + ...input, + measurements: kinds.map((kind) => candidates[kind]), + } as unknown as ProviderMeasurementBundleInput) + expect(result.ok).toBe(true) + if (!result.ok) return + expect( + result.value.entry.filter( + ({ resource }) => resource.resourceType === 'Observation', + ), + ).toHaveLength(kinds.length) + }, + ) + + it('requires every catalogued daily source to use one shared complete civil-day Period', () => { + const input = baseInput('oura', 'daily_activity', { + kind: 'step-count', + value: 8_234, + effective: { kind: 'period', start, end: dailyEnd }, + }) + expect( + buildProviderMeasurementBundle({ + ...input, + measurements: [ + { + kind: 'step-count', + value: 8_234, + effective: { kind: 'period', start, end }, + }, + ], + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle({ + ...input, + measurements: [ + input.measurements[0], + { + kind: 'distance', + value: 6_123, + effective: { + kind: 'period', + start, + end: instant('2026-08-22T00:00:00Z'), + }, + }, + ], + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle( + baseInput('withings', 'getactivity:steps', { + kind: 'step-count', + value: 8_234, + effective: { kind: 'period', start, end }, + }), + ).ok, + ).toBe(false) + const daylightSavingStart = instant('2026-11-01T00:00:00-07:00') + const daylightSavingEnd = instant('2026-11-02T00:00:00-08:00') + expect( + buildProviderMeasurementBundle( + baseInput('withings', 'getactivity:steps', { + kind: 'step-count', + value: 8_234, + effective: { + kind: 'period', + start: daylightSavingStart, + end: daylightSavingEnd, + }, + }), + ).ok, + ).toBe(true) + }) + + it('rejects duplicate, unsupported, invalid, and non-emitted output inputs before constructing a partial graph', () => { + const input = baseInput('oura', 'daily_activity', { + kind: 'step-count', + value: 8_234, + effective: { kind: 'period', start, end: dailyEnd }, + }) + expect( + buildProviderMeasurementBundle({ + ...input, + measurements: [], + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle({ + ...input, + measurements: [input.measurements[0], input.measurements[0]], + } as ProviderMeasurementBundleInput).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle({ + ...input, + measurements: [ + input.measurements[0], + { + kind: 'body-weight', + value: 72, + effective: { kind: 'date-time', value: dateTime }, + }, + ], + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle({ + ...input, + measurements: [ + { + kind: 'step-count', + value: -1, + effective: { kind: 'period', start, end: dailyEnd }, + }, + ], + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle({ + ...input, + repositoryIds: { + observations: { + distance: unwrap(parseFhirId('not-emitted')), + }, + }, + }).ok, + ).toBe(false) + }) +}) diff --git a/packages/fhir/test/provider-semantics.test.ts b/packages/fhir/test/provider-semantics.test.ts new file mode 100644 index 00000000..b6b0fcfa --- /dev/null +++ b/packages/fhir/test/provider-semantics.test.ts @@ -0,0 +1,741 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + application, + baseInput, + bloodPressureMeasurement, + dateTime, + deploymentIdentity, + end, + heartRateMeasurement, + instant, + resourceIdentity, + resources, + start, + unwrap, + uri, +} from './provider-test-support.js' +import { parseFhirId } from '../src/index.js' +import { + canonicalizeMobileEffectiveInstant, + deriveEntryFullUrl, + mobileEffectiveCanonicalizationVectors, + type MobileMeasurement, +} from '../src/mobile/index.js' +import { deriveProviderIdentities } from '../src/providers/identity.js' +import { + buildProviderMeasurementBundle, + parseNormalizedProviderRecord, + parseProviderMeasurementBundleInput, + providerOutputCoordinates, + type ProviderMeasurementBundleInput, +} from '../src/providers/index.js' + +describe('Provider R4 graph builder', () => { + it('rounds Mobile effective instants before enforcing ordering and catalog-owned duration rules', () => { + const collapsed = baseInput('oura', 'sleep', { + kind: 'sleep-duration', + value: 0.1, + effective: { + kind: 'period', + start: instant('2026-08-20T12:00:00.0001Z'), + end: instant('2026-08-20T12:00:00.0002Z'), + }, + }) + expect(buildProviderMeasurementBundle(collapsed).ok).toBe(true) + expect( + buildProviderMeasurementBundle( + baseInput('google-health-api', 'steps', { + kind: 'step-count', + value: 1, + effective: { + kind: 'period', + start: instant('2026-08-20T12:00:00.0001Z'), + end: instant('2026-08-20T12:00:00.0002Z'), + }, + }), + ).ok, + ).toBe(false) + + const input = baseInput('oura', 'sleep', { + kind: 'sleep-duration', + value: 0.1, + effective: { + kind: 'period', + start: instant('2026-08-20T12:00:00.0004Z'), + end: instant('2026-08-20T12:00:00.0006Z'), + }, + }) + const result = buildProviderMeasurementBundle(input) + expect(result.ok).toBe(true) + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(observation?.effectivePeriod).toEqual({ + start: '2026-08-20T12:00:00.000Z', + end: '2026-08-20T12:00:00.001Z', + }) + expect( + buildProviderMeasurementBundle({ + ...input, + measurements: [ + { + ...input.measurements[0], + effective: { + kind: 'period', + start: instant('2026-08-20T12:00:00.0006Z'), + end: instant('2026-08-20T12:00:00.0004Z'), + }, + }, + ], + } as ProviderMeasurementBundleInput).ok, + ).toBe(false) + }) + + it('requires identifier-only typed logical subjects', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + expect( + parseProviderMeasurementBundleInput({ + ...input, + subject: 'Patient/example', + }).ok, + ).toBe(false) + expect( + parseProviderMeasurementBundleInput({ + ...input, + subject: { ...input.subject, reference: 'Patient/example' }, + }).ok, + ).toBe(false) + expect( + parseProviderMeasurementBundleInput({ + ...input, + subject: { + type: 'Patient', + identifier: { + system: 'https://example.org/patient-pseudonyms', + value: 'patient-example', + }, + }, + }).ok, + ).toBe(false) + }) + + it.each(mobileEffectiveCanonicalizationVectors)( + 'matches the IG Mobile effective-time vector $id', + ({ input, output }) => { + expect(canonicalizeMobileEffectiveInstant(input)).toEqual({ + ok: true, + value: output, + }) + }, + ) + + it('fails closed for invalid input and a rounding carry outside FHIR four-digit years', () => { + expect(canonicalizeMobileEffectiveInstant('not-an-instant').ok).toBe(false) + expect( + canonicalizeMobileEffectiveInstant('9999-12-31T23:59:59.9996Z').ok, + ).toBe(false) + }) + + it('serializes a canonical effectiveDateTime while preserving its source offset', () => { + const input = baseInput('withings', 'getmeas:11', { + ...heartRateMeasurement, + effective: { + kind: 'date-time', + value: instant('2026-08-20T08:30:00.251500001-07:00'), + }, + }) + const parsed = parseProviderMeasurementBundleInput(input) + expect(parsed.ok).toBe(true) + if (!parsed.ok) return + expect(parsed.value.measurements[0].effective).toEqual({ + kind: 'date-time', + value: '2026-08-20T08:30:00.252-07:00', + }) + + const result = buildProviderMeasurementBundle(input) + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(observation?.effectiveDateTime).toBe('2026-08-20T08:30:00.252-07:00') + }) + + it('derives every urn:uuid edge from a complete business identifier', () => { + const result = buildProviderMeasurementBundle( + baseInput('withings', 'getmeas:11', heartRateMeasurement), + ) + expect(result.ok).toBe(true) + if (!result.ok) return + + for (const entry of result.value.entry) { + expect(entry.fullUrl).toMatch(/^urn:uuid:/u) + const businessIdentifier = entry.extension?.[0]?.valueIdentifier + expect(businessIdentifier?.system).toBeDefined() + expect(businessIdentifier?.value).toBeDefined() + expect( + deriveEntryFullUrl({ + system: uri(businessIdentifier?.system ?? ''), + value: businessIdentifier?.value ?? '', + }), + ).toEqual({ ok: true, value: entry.fullUrl }) + } + }) + + it('constructs one composite Withings blood-pressure panel', () => { + const result = buildProviderMeasurementBundle( + baseInput('withings', 'getmeas:9+10', bloodPressureMeasurement), + ) + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(observation?.component).toHaveLength(2) + expect(observation?.valueQuantity).toBeUndefined() + }) + + it('uses optional Resource.id values only when supplied by a repository', () => { + const result = buildProviderMeasurementBundle({ + ...baseInput('withings', 'getmeas:11', heartRateMeasurement), + repositoryIds: { + bundle: unwrap(parseFhirId('bundle-42')), + observations: { + 'heart-rate': unwrap(parseFhirId('observation-42')), + }, + provenance: unwrap(parseFhirId('provenance-42')), + }, + }) + expect(result.ok).toBe(true) + if (!result.ok) return + expect(result.value.id).toBe('bundle-42') + expect( + resources(result).find( + (resource) => resource.resourceType === 'Observation', + )?.id, + ).toBe('observation-42') + }) + + it('omits optional attribution and device fields while preserving repository device ids', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const result = buildProviderMeasurementBundle({ + ...input, + application: { + ...application, + id: unwrap(parseFhirId('application-42')), + name: application.name, + }, + source: { + adapter: input.source.adapter, + providerScopeIdentifier: input.source.providerScopeIdentifier, + sourceType: input.source.sourceType, + sourceNativeId: input.source.sourceNativeId, + dataOrigin: input.source.dataOrigin, + recordingDevice: { + stableUnitToken: 'minimal-device', + subjectIdentifier: { + system: uri('https://example.org/participants'), + value: 'participant-pseudonym-001', + }, + id: unwrap(parseFhirId('recording-device-42')), + identityScope: 'deployment-scoped', + }, + }, + } as ProviderMeasurementBundleInput) + expect(result.ok).toBe(true) + if (!result.ok) return + + const observation = resources(result).find( + (resource) => resource.resourceType === 'Observation', + ) + expect(observation?.extension).toEqual( + expect.arrayContaining([ + { + url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type', + valueCode: 'withings/getmeas:11', + }, + ]), + ) + const minimalDevice = resources(result).find( + (resource) => resource.id === 'recording-device-42', + ) + expect(minimalDevice).not.toHaveProperty('deviceName') + expect(minimalDevice).not.toHaveProperty('manufacturer') + expect(minimalDevice).not.toHaveProperty('modelNumber') + }) + + it('enforces only catalog-owned scalar value domains at the runtime boundary', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + expect( + parseProviderMeasurementBundleInput({ + ...input, + measurements: [ + { + ...heartRateMeasurement, + effective: { kind: 'date-time', value: 'not-an-instant' }, + }, + ], + }).ok, + ).toBe(false) + expect( + parseProviderMeasurementBundleInput({ + ...input, + measurements: [{ ...heartRateMeasurement, value: 0 }], + }).ok, + ).toBe(true) + expect( + parseProviderMeasurementBundleInput({ + ...baseInput('google-health-api', 'steps', { + kind: 'step-count', + value: 1, + effective: { kind: 'period', start, end }, + }), + measurements: [ + { + kind: 'step-count', + value: 1.5, + effective: { kind: 'period', start, end }, + }, + ], + }).ok, + ).toBe(false) + expect( + parseProviderMeasurementBundleInput({ + ...baseInput('withings', 'getmeas:6', { + kind: 'body-fat-percentage', + value: 0, + effective: { kind: 'date-time', value: dateTime }, + }), + measurements: [ + { + kind: 'body-fat-percentage', + value: 100.1, + effective: { kind: 'date-time', value: dateTime }, + }, + ], + }).ok, + ).toBe(false) + expect( + parseProviderMeasurementBundleInput( + baseInput('withings', 'getmeas:6', { + kind: 'body-fat-percentage', + value: 0, + effective: { kind: 'date-time', value: dateTime }, + }), + ).ok, + ).toBe(true) + expect( + parseProviderMeasurementBundleInput({ + ...baseInput('withings', 'getmeas:54', { + kind: 'oxygen-saturation', + value: 98, + effective: { kind: 'date-time', value: dateTime }, + }), + measurements: [ + { + kind: 'oxygen-saturation', + value: 101, + effective: { kind: 'date-time', value: dateTime }, + }, + ], + }).ok, + ).toBe(false) + }) + + it('rejects a source-native identity containing an isolated surrogate', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const invalid = { + ...input, + source: { ...input.source, sourceNativeId: 'invalid-\ud800' }, + } as ProviderMeasurementBundleInput + expect(parseProviderMeasurementBundleInput(invalid).ok).toBe(false) + expect(buildProviderMeasurementBundle(invalid).ok).toBe(false) + }) + + it('rejects Unicode IRIs wherever provider inputs require an absolute URI', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + expect( + parseProviderMeasurementBundleInput({ + ...input, + source: { + ...input.source, + providerScopeIdentifier: { + ...input.source.providerScopeIdentifier, + system: 'https://例え.example/provider-accounts', + }, + }, + }).ok, + ).toBe(false) + expect( + parseProviderMeasurementBundleInput({ + ...input, + deploymentIdentity: { + ...input.deploymentIdentity, + eventIdentifierSystem: 'https://例え.example/events', + }, + }).ok, + ).toBe(false) + }) + + it('fails closed at every direct Provider identity primitive boundary', () => { + const valid = { + provider: 'withings', + providerScopeIdentifier: { + system: uri('https://example.org/provider-accounts'), + value: 'account-pseudonym', + assurance: 'deployment-scoped-account-pseudonym', + }, + sourceType: 'getmeas:11', + sourceNativeId: 'source-record-1', + outputs: [ + { + kind: 'provider-output', + outputRole: 'heart-rate', + outputDiscriminator: 'single', + }, + ], + eventSequence: '1', + deployment: deploymentIdentity, + } as const + expect( + deriveProviderIdentities({ + ...valid, + providerScopeIdentifier: { + ...valid.providerScopeIdentifier, + system: '/relative' as never, + }, + }).ok, + ).toBe(false) + expect( + deriveProviderIdentities({ + ...valid, + outputs: [ + { + kind: 'provider-output', + outputRole: 'heart-rate', + outputDiscriminator: ' ', + }, + ], + }).ok, + ).toBe(false) + expect( + deriveProviderIdentities({ + ...valid, + outputs: [...valid.outputs, ...valid.outputs], + }).ok, + ).toBe(false) + expect( + deriveProviderIdentities({ + ...valid, + eventSequence: '0', + }).ok, + ).toBe(false) + }) + + it('reports malformed Provider parser and identity inputs without throwing', () => { + const cyclic: Record = {} + cyclic.self = cyclic + for (const invalid of [null, undefined, 42, 'invalid', cyclic]) { + const operations = [ + () => parseNormalizedProviderRecord(invalid), + () => parseProviderMeasurementBundleInput(invalid), + () => buildProviderMeasurementBundle(invalid as never), + () => deriveProviderIdentities(invalid as never), + ] + for (const operation of operations) { + expect(operation).not.toThrow() + expect(operation().ok).toBe(false) + } + } + }) + + it('does not mint identities for selectors outside the pinned Provider catalog', () => { + const valid = { + provider: 'withings', + providerScopeIdentifier: { + system: uri('https://example.org/provider-accounts'), + value: 'account-pseudonym', + assurance: 'deployment-scoped-account-pseudonym', + }, + sourceType: 'getmeas:11', + sourceNativeId: 'source-record-1', + outputs: [ + { + kind: 'provider-output', + outputRole: 'heart-rate', + outputDiscriminator: 'single', + }, + ], + eventSequence: '1', + deployment: deploymentIdentity, + } as const + expect(deriveProviderIdentities(valid).ok).toBe(true) + expect( + deriveProviderIdentities({ + ...valid, + provider: 'invented-provider', + } as never).ok, + ).toBe(false) + expect( + deriveProviderIdentities({ ...valid, sourceType: 'invented-source' }).ok, + ).toBe(false) + expect( + deriveProviderIdentities({ + ...valid, + outputs: [ + { + kind: 'provider-output', + outputRole: 'invented-output', + outputDiscriminator: 'single', + }, + ], + }).ok, + ).toBe(false) + expect( + deriveProviderIdentities({ + ...valid, + outputs: [ + { + kind: 'provider-output', + outputRole: 'heart-rate', + outputDiscriminator: 'invented-discriminator', + }, + ], + }).ok, + ).toBe(false) + }) + + it('uses both exact grouped Withings output coordinates in the HMAC preimage', () => { + const input = baseInput( + 'withings', + 'getmeas:9+10', + bloodPressureMeasurement, + ) + const coordinates = providerOutputCoordinates( + 'withings', + 'getmeas:9+10', + 'blood-pressure', + ) + expect(coordinates).toEqual({ + outputRole: 'blood-pressure-panel', + outputDiscriminator: 'single', + }) + if (coordinates === undefined) return + + const expected = deriveProviderIdentities({ + provider: 'withings', + providerScopeIdentifier: input.source.providerScopeIdentifier, + sourceType: input.source.sourceType, + sourceNativeId: input.source.sourceNativeId, + outputs: [{ kind: 'provider-output', ...coordinates }], + eventSequence: input.eventSequence, + deployment: input.deploymentIdentity, + }) + const bundle = buildProviderMeasurementBundle(input) + expect(expected.ok && bundle.ok).toBe(true) + if (!expected.ok || !bundle.ok) return + const observation = resources(bundle).find( + ({ resourceType }) => resourceType === 'Observation', + ) + if (observation?.resourceType !== 'Observation') { + throw new Error('The grouped output did not emit an Observation.') + } + const sourceOutput = observation.identifier?.find((candidate) => + candidate.type?.coding?.some(({ code }) => code === 'source-output'), + ) + const expectedOutput = expected.value.outputs[0] + expect(expectedOutput).toBeDefined() + expect(sourceOutput).toMatchObject({ + system: expectedOutput?.system, + value: expectedOutput?.value, + }) + }) + + it.each([ + [ + 'provider account value', + (input: ProviderMeasurementBundleInput) => ({ + ...input, + source: { + ...input.source, + providerScopeIdentifier: { + ...input.source.providerScopeIdentifier, + value: ' \t ', + }, + }, + }), + ], + [ + 'source native id', + (input: ProviderMeasurementBundleInput) => ({ + ...input, + source: { ...input.source, sourceNativeId: '\n ' }, + }), + ], + [ + 'source type', + (input: ProviderMeasurementBundleInput) => ({ + ...input, + source: { ...input.source, sourceType: ' ' }, + }), + ], + [ + 'converter source-device token', + (input: ProviderMeasurementBundleInput) => ({ + ...input, + application: { + ...input.application, + sourceDeviceToken: '\t', + }, + }), + ], + [ + 'data-origin source-device token', + (input: ProviderMeasurementBundleInput) => ({ + ...input, + source: { + ...input.source, + dataOrigin: { + ...input.source.dataOrigin, + sourceDeviceToken: ' ', + }, + }, + }), + ], + ] as const)('rejects a whitespace-only %s', (_name, mutate) => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + expect(parseProviderMeasurementBundleInput(mutate(input)).ok).toBe(false) + }) + + it.each(['converter', 'gateway', 'data-origin', 'recording-device'] as const)( + 'rejects an invalid Unicode %s business identity', + (role) => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + const invalidToken = 'invalid-\ud800' + let candidate: ProviderMeasurementBundleInput + if (role === 'converter') { + candidate = { + ...input, + application: { + ...input.application, + sourceDeviceToken: invalidToken, + }, + } + } else if (role === 'gateway') { + candidate = { + ...input, + gatewayApplication: { + kind: 'distinct-application', + roleAssurance: 'mediated-or-routed-measurement', + application: { + sourceDeviceToken: invalidToken, + name: 'Invalid gateway identity', + }, + }, + } + } else if (role === 'data-origin') { + candidate = { + ...input, + source: { + ...input.source, + dataOrigin: { + ...input.source.dataOrigin, + sourceDeviceToken: invalidToken, + }, + }, + } as ProviderMeasurementBundleInput + } else { + candidate = { + ...input, + source: { + ...input.source, + recordingDevice: { + stableUnitToken: invalidToken, + subjectIdentifier: { + system: uri('https://example.org/participants'), + value: 'example', + }, + identityScope: 'deployment-scoped', + }, + }, + } as ProviderMeasurementBundleInput + } + expect(parseProviderMeasurementBundleInput(candidate).ok).toBe(false) + expect(buildProviderMeasurementBundle(candidate).ok).toBe(false) + }, + ) + + it.each([ + ['google-health-api', 'heart-rate', heartRateMeasurement], + [ + 'google-health-api', + 'blood-glucose', + { + kind: 'blood-glucose', + value: 94, + effective: { kind: 'date-time', value: dateTime }, + specimen: { + identity: resourceIdentity( + 'https://example.org/specimens', + 'whole-blood', + ), + }, + }, + ], + [ + 'oura', + 'sleep', + { + kind: 'sleep-stage', + stage: 'deep', + effective: { kind: 'period', start, end }, + }, + ], + ] as const)( + 'fails closed for non-scalar or unsupported %s/%s data', + (provider, sourceType, measurement) => { + const result = buildProviderMeasurementBundle( + baseInput( + provider, + sourceType, + measurement as unknown as MobileMeasurement, + ), + ) + expect(result.ok).toBe(false) + }, + ) + + it('rejects non-canonical events and conflicting facts for one device snapshot', () => { + const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + expect( + buildProviderMeasurementBundle({ + ...input, + eventSequence: '0', + }).ok, + ).toBe(false) + expect( + buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + dataOrigin: input.application, + }, + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(true) + expect( + buildProviderMeasurementBundle({ + ...input, + source: { + ...input.source, + dataOrigin: { + ...input.source.dataOrigin, + sourceDeviceToken: input.application.sourceDeviceToken, + }, + }, + } as unknown as ProviderMeasurementBundleInput).ok, + ).toBe(false) + }) +}) diff --git a/packages/fhir/test/provider-test-support.ts b/packages/fhir/test/provider-test-support.ts new file mode 100644 index 00000000..d1564350 --- /dev/null +++ b/packages/fhir/test/provider-test-support.ts @@ -0,0 +1,268 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import type { AbsoluteUri, FhirInstant } from '../src/core/index.js' +import { + parseAbsoluteUri, + parseFhirInstant, + type Result, +} from '../src/index.js' +import type { + ApplicationDeviceInput, + DeploymentIdentityInput, + MobileMeasurement, + ResourceIdentityInput, +} from '../src/mobile/index.js' +import type { + buildProviderMeasurementBundle, + ConnectedProvider, + ProviderPatientReferenceInput, + ProviderMeasurementBundleInput, + ProviderResearchStudyReferenceInput, +} from '../src/providers/index.js' +import type { GroveMobileExchangeBundle } from '../src/r4/index.js' + +export const unwrap = (result: Result): T => { + if (!result.ok) { + throw new Error(result.issues.map((issue) => issue.message).join('\n')) + } + return result.value +} + +export const uri = (value: string): AbsoluteUri => + unwrap(parseAbsoluteUri(value)) +export const instant = (value: string): FhirInstant => + unwrap(parseFhirInstant(value)) +export const patient: ProviderPatientReferenceInput = { + type: 'Patient', + identifier: { + system: uri('https://example.org/deployments/patient-pseudonyms'), + value: 'patient-example', + assurance: 'deployment-scoped-pseudonym', + }, +} +export const study = (value: string): ProviderResearchStudyReferenceInput => ({ + type: 'ResearchStudy', + identifier: { + system: uri('https://example.org/deployments/research-studies'), + value, + }, +}) +export const resourceIdentity = ( + system: string, + value: string, +): ResourceIdentityInput => ({ + identifier: { system: uri(system), value }, +}) + +export const application: ApplicationDeviceInput = { + sourceDeviceToken: 'converter-app', + name: 'Example converter', + version: '0.0.0', +} + +export const deploymentIdentity: DeploymentIdentityInput = { + opaqueIdentifierSystems: { + 'source-record': uri('https://example.org/identity/source-record/test/1'), + 'source-output': uri('https://example.org/identity/source-output/test/1'), + 'writer-record': uri('https://example.org/identity/writer-record/test/1'), + 'provider-record': uri( + 'https://example.org/identity/provider-record/test/1', + ), + 'provider-output': uri( + 'https://example.org/identity/provider-output/test/1', + ), + 'source-artifact': uri( + 'https://example.org/identity/source-artifact/test/1', + ), + 'provider-artifact': uri( + 'https://example.org/identity/provider-artifact/test/1', + ), + 'source-context': uri('https://example.org/identity/source-context/test/1'), + 'recording-device': uri( + 'https://example.org/identity/recording-device/test/1', + ), + 'device-snapshot': uri( + 'https://example.org/identity/device-snapshot/test/1', + ), + }, + eventIdentifierSystem: uri('https://example.org/identity/event'), + entryNodeIdentifierSystem: uri('https://example.org/identity/entry-node'), + keyId: 'test-key', + keyEpoch: '1', + secretBase64Url: 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', + producerInstance: '1f5c58aa-6ec6-4e79-a682-829a9debd3f5', +} + +export const dateTime: FhirInstant = instant('2026-08-20T12:00:00Z') +export const start: FhirInstant = instant('2026-08-20T00:00:00Z') +export const end: FhirInstant = instant('2026-08-20T12:00:00Z') +export const dailyEnd: FhirInstant = instant('2026-08-21T00:00:00Z') + +export const heartRateMeasurement: Extract< + MobileMeasurement, + { readonly kind: 'heart-rate' } +> = { + kind: 'heart-rate', + value: 64, + effective: { kind: 'date-time', value: dateTime }, +} + +export const bloodPressureMeasurement: Extract< + MobileMeasurement, + { readonly kind: 'blood-pressure' } +> = { + kind: 'blood-pressure', + systolic: 118, + diastolic: 76, + effective: { kind: 'date-time', value: dateTime }, +} + +export const baseInput = ( + provider: ConnectedProvider, + sourceType: string, + measurement: MobileMeasurement, +): ProviderMeasurementBundleInput => + ({ + subject: patient, + measurements: [measurement], + source: { + adapter: { kind: 'providers', provider }, + providerScopeIdentifier: + provider === 'oura' ? + { + system: uri('https://example.org/provider-key-spaces'), + value: 'oura-document-uuid-global', + assurance: 'documented-global-key-space', + } + : { + system: uri('https://example.org/deployments/provider-accounts'), + value: `pseudonym-${provider}-001`, + assurance: 'deployment-scoped-account-pseudonym', + }, + sourceType, + sourceNativeId: `native-${provider}-${sourceType}`, + recordingMethod: 'automatically-recorded', + dataOrigin: { + sourceDeviceToken: `data-origin-${provider}`, + name: provider, + }, + }, + application, + eventSequence: '1', + deploymentIdentity, + occurred: instant('2026-08-20T12:00:00Z'), + recorded: instant('2026-08-20T12:02:00Z'), + assembled: instant('2026-08-20T12:03:00Z'), + }) as ProviderMeasurementBundleInput + +export const scalarCases: ReadonlyArray<{ + readonly provider: ConnectedProvider + readonly sourceType: string + readonly measurement: MobileMeasurement +}> = [ + { + provider: 'google-health-api', + sourceType: 'weight', + measurement: { + kind: 'body-weight', + value: 72.5, + effective: { kind: 'date-time', value: dateTime }, + }, + }, + { + provider: 'google-health-api', + sourceType: 'core-body-temperature', + measurement: { + kind: 'body-temperature', + value: 36.8, + effective: { kind: 'date-time', value: dateTime }, + }, + }, + { + provider: 'google-health-api', + sourceType: 'height', + measurement: { + kind: 'body-height', + value: 178, + effective: { kind: 'date-time', value: dateTime }, + }, + }, + { + provider: 'google-health-api', + sourceType: 'steps', + measurement: { + kind: 'step-count', + value: 8234, + effective: { kind: 'period', start, end }, + }, + }, + { + provider: 'oura', + sourceType: 'daily_activity', + measurement: { + kind: 'distance', + value: 6123, + effective: { kind: 'period', start, end: dailyEnd }, + }, + }, + { + provider: 'google-health-api', + sourceType: 'active-energy-burned', + measurement: { + kind: 'active-energy', + value: 430, + effective: { kind: 'period', start, end }, + }, + }, + { + provider: 'oura', + sourceType: 'sleep', + measurement: { + kind: 'sleep-duration', + value: 7.4, + effective: { kind: 'period', start, end }, + }, + }, + { + provider: 'withings', + sourceType: 'getmeas:9+10', + measurement: bloodPressureMeasurement, + }, + { + provider: 'withings', + sourceType: 'getmeas:11', + measurement: heartRateMeasurement, + }, + { + provider: 'withings', + sourceType: 'getmeas:54', + measurement: { + kind: 'oxygen-saturation', + value: 98, + effective: { kind: 'date-time', value: dateTime }, + }, + }, +] + +export const resources = ( + result: ReturnType, +): ReadonlyArray => { + if (!result.ok) throw new Error(JSON.stringify(result.issues)) + return result.value.entry.map((entry) => entry.resource) +} + +export const mutableRecord = ( + value: unknown, + label: string, +): Record => { + if (typeof value !== 'object' || value === null || Array.isArray(value)) { + throw new Error(`Expected ${label} to be a JSON object.`) + } + return value as Record +} diff --git a/packages/fhir/test/providers.test.ts b/packages/fhir/test/providers.test.ts deleted file mode 100644 index 078e05b2..00000000 --- a/packages/fhir/test/providers.test.ts +++ /dev/null @@ -1,1772 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { readFileSync } from 'node:fs' -import { assert, double, oneof, property } from 'fast-check' -import type { - FhirInstant, - PatientReference, - PositiveInteger, -} from '../src/core/index.js' -import { - parseAbsoluteUri, - parseFhirId, - parseFhirInstant, - parsePatientReference, - parsePositiveInteger, - parseResearchStudyReference, - type Result, -} from '../src/index.js' -import { - canonicalizeMobileEffectiveInstant, - entryIdentifierName, - createEntryIdentity, - deriveEntryFullUrl, - groveFhirExchangeIdentity, - mobileEffectiveCanonicalizationVectors, - sharedMobileMeasurementCatalog, - type ApplicationDeviceInput, - type MobileMeasurement, -} from '../src/mobile/index.js' -import { deriveProviderIdentities } from '../src/providers/identity.js' -import { - adapterMeasurementCatalog, - buildProviderMeasurementBundle, - providerRecordEffectiveRules, - providerScalarMappings, - parseProviderMeasurementBundleInput, - parseNormalizedProviderRecord, - type ConnectedProvider, - type ConnectedProviderRecord, - type ProviderMeasurementBundleInput, -} from '../src/providers/index.js' - -const unwrap = (result: Result): T => { - if (!result.ok) { - throw new Error(result.issues.map((issue) => issue.message).join('\n')) - } - return result.value -} - -const uri = (value: string) => unwrap(parseAbsoluteUri(value)) -const instant = (value: string): FhirInstant => unwrap(parseFhirInstant(value)) -const patient = (value: string): PatientReference => - unwrap(parsePatientReference(value)) -const positive = (value: number): PositiveInteger => - unwrap(parsePositiveInteger(value)) -const resourceIdentity = (system: string, value: string) => ({ - identifier: { system: uri(system), value }, -}) - -const application: ApplicationDeviceInput = { - identity: resourceIdentity( - 'https://example.org/applications', - 'converter-app', - ), - name: 'Example converter', - version: '0.0.0', -} - -const dateTime = instant('2026-08-20T12:00:00Z') -const start = instant('2026-08-20T00:00:00Z') -const end = instant('2026-08-20T12:00:00Z') -const dailyEnd = instant('2026-08-21T00:00:00Z') - -const heartRateMeasurement = { - kind: 'heart-rate', - value: 64, - effective: { kind: 'date-time', value: dateTime }, -} as const satisfies MobileMeasurement - -const bloodPressureMeasurement = { - kind: 'blood-pressure', - systolic: 118, - diastolic: 76, - effective: { kind: 'date-time', value: dateTime }, -} as const satisfies MobileMeasurement - -const baseInput = ( - provider: ConnectedProvider, - sourceType: string, - measurement: MobileMeasurement, -): ProviderMeasurementBundleInput => - ({ - subject: patient('Patient/example'), - measurements: [measurement], - source: { - adapter: { kind: 'providers', provider }, - providerAccountIdentifier: { - system: uri('https://example.org/deployments/provider-accounts'), - value: `pseudonym-${provider}-001`, - assurance: 'deployment-scoped-pseudonym', - }, - sourceType, - sourceNativeId: `native-${provider}-${sourceType}`, - recordingMethod: 'automatically-recorded', - dataOrigin: { - identity: resourceIdentity( - 'https://example.org/data-origins', - provider, - ), - name: provider, - }, - }, - application, - eventSequence: positive(1), - graphIdentifierSystem: uri( - 'urn:grove:provider-graph:org.grovealliance.example', - ), - issued: instant('2026-08-20T12:01:00Z'), - recorded: instant('2026-08-20T12:02:00Z'), - }) as ProviderMeasurementBundleInput - -const scalarCases = [ - { - provider: 'google-health-api', - sourceType: 'weight', - measurement: { - kind: 'body-weight', - value: 72.5, - effective: { kind: 'date-time', value: dateTime }, - }, - }, - { - provider: 'google-health-api', - sourceType: 'core-body-temperature', - measurement: { - kind: 'body-temperature', - value: 36.8, - effective: { kind: 'date-time', value: dateTime }, - }, - }, - { - provider: 'google-health-api', - sourceType: 'height', - measurement: { - kind: 'body-height', - value: 178, - effective: { kind: 'date-time', value: dateTime }, - }, - }, - { - provider: 'google-health-api', - sourceType: 'steps', - measurement: { - kind: 'step-count', - value: 8234, - effective: { kind: 'period', start, end }, - }, - }, - { - provider: 'oura', - sourceType: 'daily_activity', - measurement: { - kind: 'distance', - value: 6123, - effective: { kind: 'period', start, end: dailyEnd }, - }, - }, - { - provider: 'google-health-api', - sourceType: 'active-energy-burned', - measurement: { - kind: 'active-energy', - value: 430, - effective: { kind: 'period', start, end }, - }, - }, - { - provider: 'oura', - sourceType: 'sleep', - measurement: { - kind: 'sleep-duration', - value: 7.4, - effective: { kind: 'period', start, end }, - }, - }, - { - provider: 'withings', - sourceType: 'getmeas:9+10', - measurement: bloodPressureMeasurement, - }, - { - provider: 'withings', - sourceType: 'getmeas:11', - measurement: heartRateMeasurement, - }, - { - provider: 'withings', - sourceType: 'getmeas:54', - measurement: { - kind: 'oxygen-saturation', - value: 98, - effective: { kind: 'date-time', value: dateTime }, - }, - }, -] as const satisfies ReadonlyArray<{ - readonly provider: ConnectedProvider - readonly sourceType: string - readonly measurement: MobileMeasurement -}> - -const resources = ( - result: ReturnType, -) => { - if (!result.ok) throw new Error(JSON.stringify(result.issues)) - return result.value.entry.map((entry) => entry.resource) -} - -describe('Provider R4 graph builder', () => { - it.each(scalarCases)( - 'builds the admitted $provider/$sourceType $measurement.kind graph', - ({ provider, sourceType, measurement }) => { - const result = buildProviderMeasurementBundle( - baseInput(provider, sourceType, measurement), - ) - expect(result.ok).toBe(true) - if (!result.ok) return - - expect(result.value.type).toBe('collection') - expect(result.value.entry).toHaveLength(4) - expect('total' in result.value).toBe(false) - expect(Object.isFrozen(result.value)).toBe(true) - - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', - ) - expect(observation?.meta?.profile).toEqual([ - `https://grovealliance.org/fhir/mobile/StructureDefinition/${sharedMobileMeasurementCatalog[measurement.kind].profile}`, - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation', - ]) - expect(observation?.extension).toEqual( - expect.arrayContaining([ - { - url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider', - valueCode: provider, - }, - { - url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type', - valueCode: `${provider}/${sourceType}`, - }, - ]), - ) - expect( - observation?.extension?.some( - ({ url }) => - url === - 'http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice', - ), - ).toBe(false) - expect(observation?.code.coding).toHaveLength(1) - expect(observation?.id).toBeUndefined() - }, - ) - - it('builds every exact provider/source/measurement mapping in the generated catalog', () => { - const ownerExclusiveKinds = new Set( - Object.values(adapterMeasurementCatalog).flatMap((catalog) => - Object.keys(catalog), - ), - ) - const exampleMeasurement = ( - kind: string, - ): MobileMeasurement | undefined => { - if (!Object.hasOwn(sharedMobileMeasurementCatalog, kind)) return undefined - if (kind === 'blood-pressure') return bloodPressureMeasurement - if (kind === 'sleep-stage') { - return { - kind: 'sleep-stage', - stage: 'light', - effective: { kind: 'period', start, end }, - } - } - const definition = - sharedMobileMeasurementCatalog[kind as MobileMeasurement['kind']] - const effective = - definition.effective === 'Period' ? - ({ kind: 'period', start, end } as const) - : ({ kind: 'date-time', value: dateTime } as const) - const value = - 'allowedValues' in definition ? definition.allowedValues[0] : 72 - return { kind, value, effective } as MobileMeasurement - } - const exhaustiveMappings = providerScalarMappings as Readonly< - Record< - ConnectedProvider, - Readonly>>> - > - > - let checked = 0 - for (const [provider, sourceMappings] of Object.entries( - exhaustiveMappings, - )) { - for (const [sourceType, mappings] of Object.entries(sourceMappings)) { - for (const measurementKind of Object.keys(mappings)) { - const measurement = exampleMeasurement(measurementKind) - if (measurement === undefined) { - // Owner-exclusive mappings stay contract data outside the facade. - expect(ownerExclusiveKinds.has(measurementKind)).toBe(true) - continue - } - const effectiveRule = ( - providerRecordEffectiveRules as Readonly< - Record> | undefined> - > - )[provider]?.[sourceType] - const sourceMeasurement = - effectiveRule === undefined ? measurement : ( - { - ...measurement, - effective: { kind: 'period', start, end: dailyEnd }, - } - ) - expect( - buildProviderMeasurementBundle( - baseInput( - provider as ConnectedProvider, - sourceType, - sourceMeasurement as MobileMeasurement, - ), - ).ok, - ).toBe(true) - checked += 1 - } - } - } - expect(checked).toBeGreaterThan(scalarCases.length) - }) - - it('atomically builds every present Oura daily-activity output with one complete Provenance target set', () => { - const dailyActivity = baseInput('oura', 'daily_activity', { - kind: 'distance', - value: 6_123, - effective: { kind: 'period', start, end: dailyEnd }, - }) - const result = buildProviderMeasurementBundle({ - ...dailyActivity, - measurements: [ - { - kind: 'distance', - value: 6_123, - effective: { kind: 'period', start, end: dailyEnd }, - }, - { - kind: 'active-energy', - value: 430, - effective: { kind: 'period', start, end: dailyEnd }, - }, - { - kind: 'step-count', - value: 8_234, - effective: { kind: 'period', start, end: dailyEnd }, - }, - ], - } as ProviderMeasurementBundleInput) - expect(result.ok).toBe(true) - if (!result.ok) return - - const observations = result.value.entry.filter( - ({ resource }) => resource.resourceType === 'Observation', - ) - expect(observations).toHaveLength(3) - expect( - observations.map(({ resource }) => resource.meta?.profile?.[0]), - ).toEqual([ - 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-step-count', - 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-active-energy', - 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-distance', - ]) - const provenance = result.value.entry.find( - ({ resource }) => resource.resourceType === 'Provenance', - )?.resource - expect(provenance?.resourceType).toBe('Provenance') - if (provenance?.resourceType !== 'Provenance') return - expect(provenance.target).toHaveLength(observations.length) - expect( - new Set(provenance.target.map(({ reference }) => reference)), - ).toEqual(new Set(observations.map(({ fullUrl }) => fullUrl))) - }) - - it.each([ - ['one', ['step-count']], - ['two', ['step-count', 'distance']], - ] as const)( - 'admits a %s-output subset when those are the fields present in an Oura daily-activity record', - (_label, kinds) => { - const candidates = { - 'step-count': { - kind: 'step-count', - value: 8_234, - effective: { kind: 'period', start, end: dailyEnd }, - }, - distance: { - kind: 'distance', - value: 6_123, - effective: { kind: 'period', start, end: dailyEnd }, - }, - } as const - const input = baseInput('oura', 'daily_activity', candidates[kinds[0]]) - const result = buildProviderMeasurementBundle({ - ...input, - measurements: kinds.map((kind) => candidates[kind]), - } as unknown as ProviderMeasurementBundleInput) - expect(result.ok).toBe(true) - if (!result.ok) return - expect( - result.value.entry.filter( - ({ resource }) => resource.resourceType === 'Observation', - ), - ).toHaveLength(kinds.length) - }, - ) - - it('rejects recursively encoded identity leakage through scalar metadata', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - expect( - buildProviderMeasurementBundle({ - ...input, - source: { ...input.source, sourceNativeId: 'native id/42' }, - application: { - ...input.application, - manufacturer: 'encoded-native%2520id%252F42', - }, - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) - expect( - parseNormalizedProviderRecord({ - source: { - ...input.source, - providerAccountIdentifier: { - ...input.source.providerAccountIdentifier, - value: 'account value', - }, - dataOrigin: { - ...input.source.dataOrigin, - name: 'encoded-account+value', - }, - }, - measurements: input.measurements, - }).ok, - ).toBe(false) - }) - - it('requires every catalogued daily source to use one shared complete civil-day Period', () => { - const input = baseInput('oura', 'daily_activity', { - kind: 'step-count', - value: 8_234, - effective: { kind: 'period', start, end: dailyEnd }, - }) - expect( - buildProviderMeasurementBundle({ - ...input, - measurements: [ - { - kind: 'step-count', - value: 8_234, - effective: { kind: 'period', start, end }, - }, - ], - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) - expect( - buildProviderMeasurementBundle({ - ...input, - measurements: [ - input.measurements[0], - { - kind: 'distance', - value: 6_123, - effective: { - kind: 'period', - start, - end: instant('2026-08-22T00:00:00Z'), - }, - }, - ], - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) - expect( - buildProviderMeasurementBundle( - baseInput('withings', 'getactivity:steps', { - kind: 'step-count', - value: 8_234, - effective: { kind: 'period', start, end }, - }), - ).ok, - ).toBe(false) - const daylightSavingStart = instant('2026-11-01T00:00:00-07:00') - const daylightSavingEnd = instant('2026-11-02T00:00:00-08:00') - expect( - buildProviderMeasurementBundle( - baseInput('withings', 'getactivity:steps', { - kind: 'step-count', - value: 8_234, - effective: { - kind: 'period', - start: daylightSavingStart, - end: daylightSavingEnd, - }, - }), - ).ok, - ).toBe(true) - }) - - it('rejects duplicate, unsupported, invalid, and non-emitted output inputs before constructing a partial graph', () => { - const input = baseInput('oura', 'daily_activity', { - kind: 'step-count', - value: 8_234, - effective: { kind: 'period', start, end: dailyEnd }, - }) - expect( - buildProviderMeasurementBundle({ - ...input, - measurements: [], - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) - expect( - buildProviderMeasurementBundle({ - ...input, - measurements: [input.measurements[0], input.measurements[0]], - } as ProviderMeasurementBundleInput).ok, - ).toBe(false) - expect( - buildProviderMeasurementBundle({ - ...input, - measurements: [ - input.measurements[0], - { - kind: 'body-weight', - value: 72, - effective: { kind: 'date-time', value: dateTime }, - }, - ], - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) - expect( - buildProviderMeasurementBundle({ - ...input, - measurements: [ - input.measurements[0], - { - kind: 'active-energy', - value: -1, - effective: { kind: 'period', start, end: dailyEnd }, - }, - ], - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) - expect( - buildProviderMeasurementBundle({ - ...input, - repositoryIds: { - observations: { - distance: unwrap(parseFhirId('not-emitted')), - }, - }, - }).ok, - ).toBe(false) - }) - - it('treats effective values as emitted metadata and rejects native-id leakage', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - expect( - buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, - sourceNativeId: '2026-08-20T12:00:00.000Z', - }, - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) - }) - - it('derives exact source, conversion and exchange identifiers in their proper namespaces', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const result = buildProviderMeasurementBundle(input) - expect(result.ok).toBe(true) - if (!result.ok) return - - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', - ) - expect(observation?.identifier?.map(({ system }) => system)).toEqual([ - 'https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id', - ]) - for (const identity of observation?.identifier ?? []) { - expect(identity.value).toMatch(/^(v1:[^|]+(\|[^|]+)+|[^|]+)$/u) - } - expect(result.value.identifier?.system).toBe( - 'urn:grove:provider-graph:org.grovealliance.example', - ) - const provenanceEntry = result.value.entry.find( - (entry) => entry.resource.resourceType === 'Provenance', - ) - expect(provenanceEntry?.extension?.[0]?.valueIdentifier?.system).toBe( - 'urn:grove:provider-graph:org.grovealliance.example', - ) - // The provider's own record key travels in the clear: it is a vendor row key, not - // participant data, and the same value is already public when the vendor's app writes it - // into HealthKit or Health Connect. - expect(JSON.stringify(result.value)).toContain(input.source.sourceNativeId) - // The account component is a deployment-minted pseudonym, never a vendor-side account key. - expect(input.source.providerAccountIdentifier.value).toMatch(/^pseudonym-/u) - }) - - it('matches the frozen Provider composed source and output vectors', () => { - const input = baseInput('google-health-api', 'steps', { - kind: 'step-count', - value: 123, - effective: { kind: 'period', start, end }, - }) - const result = buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, - providerAccountIdentifier: { - system: uri('https://provider.example.org/accounts'), - value: 'account-001', - assurance: 'deployment-scoped-pseudonym', - }, - sourceNativeId: 'steps-2026-08-20T16:00:00Z', - }, - } as ProviderMeasurementBundleInput) - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', - ) - // Google Health is account-scoped, and a single-measurement record is a one-to-one - // conversion, so the source record is the Observation's only identity. - expect(observation?.identifier?.map((entry) => entry.value)).toEqual([ - 'v1:google-health-api|account-001|steps|steps-2026-08-20T16:00:00Z', - ]) - - const unicodeInput = baseInput('oura', 'sleep', { - kind: 'sleep-duration', - value: 7, - effective: { kind: 'period', start, end }, - }) - const unicodeResult = buildProviderMeasurementBundle({ - ...unicodeInput, - source: { - ...unicodeInput.source, - providerAccountIdentifier: { - system: uri('https://provider.example.org/accounts'), - value: 'café\n"\\', - assurance: 'deployment-scoped-pseudonym', - }, - sourceNativeId: 'résumé-\u0001', - }, - } as ProviderMeasurementBundleInput) - const unicodeObservation = resources(unicodeResult).find( - (resource) => resource.resourceType === 'Observation', - ) - // Oura documents its document ids as unique across every account, so the key is carried - // through exactly as Oura supplied it, non-ASCII included. - expect(unicodeObservation?.identifier?.[0]?.value).toBe('résumé-\u0001') - }) - - it('changes only event identities when the durable event sequence changes', () => { - const first = buildProviderMeasurementBundle( - baseInput('withings', 'getmeas:11', heartRateMeasurement), - ) - const second = buildProviderMeasurementBundle({ - ...baseInput('withings', 'getmeas:11', heartRateMeasurement), - eventSequence: positive(2), - graphIdentifierSystem: uri( - 'urn:grove:provider-graph:org.grovealliance.example', - ), - }) - expect(first.ok && second.ok).toBe(true) - if (!first.ok || !second.ok) return - - const firstObservation = resources(first).find( - (resource) => resource.resourceType === 'Observation', - ) - const secondObservation = resources(second).find( - (resource) => resource.resourceType === 'Observation', - ) - expect(firstObservation?.identifier).toEqual(secondObservation?.identifier) - expect(first.value.identifier).not.toEqual(second.value.identifier) - }) - - it('adds a gateway reference only with explicit mediation evidence', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const converterGateway = buildProviderMeasurementBundle({ - ...input, - gatewayApplication: { - kind: 'converter-application', - roleAssurance: 'mediated-or-routed-measurement', - }, - }) - expect(converterGateway.ok).toBe(true) - if (!converterGateway.ok) return - expect(converterGateway.value.entry).toHaveLength(4) - const converterObservation = resources(converterGateway).find( - (resource) => resource.resourceType === 'Observation', - ) - const converterApplicationEntry = converterGateway.value.entry.find( - (entry) => - entry.resource.resourceType === 'Device' && - entry.resource.identifier?.[0]?.value === 'converter-app', - ) - expect( - converterObservation?.extension?.find( - ({ url }) => - url === - 'http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice', - )?.valueReference?.reference, - ).toBe(converterApplicationEntry?.fullUrl) - - expect( - buildProviderMeasurementBundle({ - ...input, - gatewayApplication: { - kind: 'converter-application', - roleAssurance: 'conversion-only', - }, - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) - }) - - it('builds a fully attributed graph with distinct gateway and authorized hardware identity', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const result = buildProviderMeasurementBundle({ - ...input, - application: { - ...input.application, - manufacturer: 'Grove Alliance', - }, - gatewayApplication: { - kind: 'distinct-application', - roleAssurance: 'mediated-or-routed-measurement', - application: { - identity: resourceIdentity( - 'https://example.org/applications', - 'gateway-app', - ), - name: 'Gateway app', - version: '2.1.0', - manufacturer: 'Gateway vendor', - }, - }, - source: { - ...input.source, - dataOrigin: { - ...input.source.dataOrigin, - version: '3.0.0', - manufacturer: 'Source vendor', - }, - recordingDevice: { - identity: resourceIdentity( - 'https://example.org/authorized-hardware', - 'hardware-1', - ), - identityScope: 'authorized-hardware', - disclosureAuthorization: 'authorized-for-exchange', - name: 'Chest strap', - manufacturer: 'Recorder vendor', - modelNumber: 'Model 1', - }, - }, - researchStudyReferences: [ - unwrap(parseResearchStudyReference('ResearchStudy/study-1')), - ], - } as ProviderMeasurementBundleInput) - expect(result.ok).toBe(true) - if (!result.ok) return - expect(result.value.entry).toHaveLength(6) - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', - ) - expect(observation?.device?.reference).toMatch(/^urn:uuid:/u) - expect( - observation?.extension?.some( - ({ url }) => - url === - 'http://hl7.org/fhir/StructureDefinition/workflow-researchStudy', - ), - ).toBe(true) - }) - - it('requires an explicit privacy scope for recording-device identity and never accepts a serial number', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const result = buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, - recordingDevice: { - identity: resourceIdentity( - 'https://example.org/deployments/recording-devices', - 'device-pseudonym-1', - ), - identityScope: 'deployment-scoped', - name: 'Connected scale', - }, - }, - } as ProviderMeasurementBundleInput) - expect(result.ok).toBe(true) - if (!result.ok) return - const recordingDevice = resources(result).find( - (resource) => - resource.resourceType === 'Device' && - resource.identifier?.[0]?.value === 'device-pseudonym-1', - ) - expect(recordingDevice).toBeDefined() - expect(recordingDevice).not.toHaveProperty('serialNumber') - - expect( - buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, - recordingDevice: { - identity: resourceIdentity( - 'https://example.org/hardware-identifiers', - 'global-hardware-id', - ), - identityScope: 'authorized-hardware', - serialNumber: 'SERIAL-123', - }, - }, - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) - }) - - it('rejects arbitrary source codings instead of treating lineage as a clinical code', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - expect( - buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, - sourceTypeCoding: { - system: 'https://provider.example/source-types', - code: 'broad-summary-record', - }, - }, - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) - }) - - it('accepts only unique, unambiguous typed ResearchStudy references', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const local = unwrap(parseResearchStudyReference('ResearchStudy/local-1')) - const absolute = unwrap( - parseResearchStudyReference( - 'https://research.example/fhir/ResearchStudy/remote-1', - ), - ) - expect( - buildProviderMeasurementBundle({ - ...input, - researchStudyReferences: [local, absolute], - }).ok, - ).toBe(true) - - const duplicate = parseProviderMeasurementBundleInput({ - ...input, - researchStudyReferences: [local, local], - }) - expect(duplicate.ok).toBe(false) - if (!duplicate.ok) { - expect(duplicate.issues).toEqual( - expect.arrayContaining([ - expect.objectContaining({ code: 'duplicate-identifier' }), - ]), - ) - } - expect( - parseProviderMeasurementBundleInput({ - ...input, - researchStudyReferences: [ - 'https://research.example/fhir/ResearchStudy/remote-1?version=2', - ], - }).ok, - ).toBe(false) - expect( - parseProviderMeasurementBundleInput({ - ...baseInput('withings', 'getmeas:54', { - kind: 'oxygen-saturation', - value: 98, - effective: { kind: 'date-time', value: dateTime }, - }), - measurements: [ - { - kind: 'oxygen-saturation', - value: 0, - effective: { kind: 'date-time', value: dateTime }, - }, - ], - }).ok, - ).toBe(true) - }) - - it('rounds Mobile effective instants before enforcing a non-zero Period', () => { - const collapsed = baseInput('oura', 'sleep', { - kind: 'sleep-duration', - value: 0.1, - effective: { - kind: 'period', - start: instant('2026-08-20T12:00:00.0001Z'), - end: instant('2026-08-20T12:00:00.0002Z'), - }, - }) - expect(buildProviderMeasurementBundle(collapsed).ok).toBe(false) - - const input = baseInput('oura', 'sleep', { - kind: 'sleep-duration', - value: 0.1, - effective: { - kind: 'period', - start: instant('2026-08-20T12:00:00.0004Z'), - end: instant('2026-08-20T12:00:00.0006Z'), - }, - }) - const result = buildProviderMeasurementBundle(input) - expect(result.ok).toBe(true) - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', - ) - expect(observation?.effectivePeriod).toEqual({ - start: '2026-08-20T12:00:00.000Z', - end: '2026-08-20T12:00:00.001Z', - }) - expect( - buildProviderMeasurementBundle({ - ...input, - measurements: [ - { - ...input.measurements[0], - effective: { - kind: 'period', - start: instant('2026-08-20T12:00:00.0006Z'), - end: instant('2026-08-20T12:00:00.0004Z'), - }, - }, - ], - } as ProviderMeasurementBundleInput).ok, - ).toBe(false) - }) - - it.each(mobileEffectiveCanonicalizationVectors)( - 'matches the IG Mobile effective-time vector $id', - ({ input, output }) => { - expect(canonicalizeMobileEffectiveInstant(input)).toEqual({ - ok: true, - value: output, - }) - }, - ) - - it('fails closed for invalid input and a rounding carry outside FHIR four-digit years', () => { - expect(canonicalizeMobileEffectiveInstant('not-an-instant').ok).toBe(false) - expect( - canonicalizeMobileEffectiveInstant('9999-12-31T23:59:59.9996Z').ok, - ).toBe(false) - }) - - it('serializes a canonical effectiveDateTime while preserving its source offset', () => { - const input = baseInput('withings', 'getmeas:11', { - ...heartRateMeasurement, - effective: { - kind: 'date-time', - value: instant('2026-08-20T08:30:00.251500001-07:00'), - }, - }) - const parsed = parseProviderMeasurementBundleInput(input) - expect(parsed.ok).toBe(true) - if (!parsed.ok) return - expect(parsed.value.measurements[0].effective).toEqual({ - kind: 'date-time', - value: '2026-08-20T08:30:00.252-07:00', - }) - - const result = buildProviderMeasurementBundle(input) - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', - ) - expect(observation?.effectiveDateTime).toBe('2026-08-20T08:30:00.252-07:00') - }) - - it('derives every urn:uuid edge from a complete business identifier', () => { - const result = buildProviderMeasurementBundle( - baseInput('withings', 'getmeas:11', heartRateMeasurement), - ) - expect(result.ok).toBe(true) - if (!result.ok) return - - for (const entry of result.value.entry) { - expect(entry.fullUrl).toMatch(/^urn:uuid:/u) - const businessIdentifier = entry.extension?.[0]?.valueIdentifier - expect(businessIdentifier?.system).toBeDefined() - expect(businessIdentifier?.value).toBeDefined() - expect( - deriveEntryFullUrl({ - system: uri(businessIdentifier?.system ?? ''), - value: businessIdentifier?.value ?? '', - }), - ).toEqual({ ok: true, value: entry.fullUrl }) - } - }) - - it('constructs one composite Withings blood-pressure panel', () => { - const result = buildProviderMeasurementBundle( - baseInput('withings', 'getmeas:9+10', bloodPressureMeasurement), - ) - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', - ) - expect(observation?.component).toHaveLength(2) - expect(observation?.valueQuantity).toBeUndefined() - }) - - it('uses optional Resource.id values only when supplied by a repository', () => { - const result = buildProviderMeasurementBundle({ - ...baseInput('withings', 'getmeas:11', heartRateMeasurement), - repositoryIds: { - bundle: unwrap(parseFhirId('bundle-42')), - observations: { - 'heart-rate': unwrap(parseFhirId('observation-42')), - }, - provenance: unwrap(parseFhirId('provenance-42')), - }, - }) - expect(result.ok).toBe(true) - if (!result.ok) return - expect(result.value.id).toBe('bundle-42') - expect( - resources(result).find( - (resource) => resource.resourceType === 'Observation', - )?.id, - ).toBe('observation-42') - }) - - it('omits optional attribution and device fields while preserving repository device ids', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const result = buildProviderMeasurementBundle({ - ...input, - application: { - identity: { - ...application.identity, - id: unwrap(parseFhirId('application-42')), - }, - name: application.name, - }, - source: { - adapter: input.source.adapter, - providerAccountIdentifier: input.source.providerAccountIdentifier, - sourceType: input.source.sourceType, - sourceNativeId: input.source.sourceNativeId, - dataOrigin: input.source.dataOrigin, - recordingDevice: { - identity: { - ...resourceIdentity( - 'https://example.org/deployments/recording-devices', - 'minimal-device', - ), - id: unwrap(parseFhirId('recording-device-42')), - }, - identityScope: 'deployment-scoped', - }, - }, - } as ProviderMeasurementBundleInput) - expect(result.ok).toBe(true) - if (!result.ok) return - - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', - ) - expect(observation?.extension).toEqual( - expect.arrayContaining([ - { - url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type', - valueCode: 'withings/getmeas:11', - }, - ]), - ) - const minimalDevice = resources(result).find( - (resource) => resource.id === 'recording-device-42', - ) - expect(minimalDevice).not.toHaveProperty('deviceName') - expect(minimalDevice).not.toHaveProperty('manufacturer') - expect(minimalDevice).not.toHaveProperty('modelNumber') - }) - - it('rejects invalid scalar ranges at the strict runtime boundary', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - expect( - parseProviderMeasurementBundleInput({ - ...input, - measurements: [ - { - ...heartRateMeasurement, - effective: { kind: 'date-time', value: 'not-an-instant' }, - }, - ], - }).ok, - ).toBe(false) - expect( - parseProviderMeasurementBundleInput({ - ...input, - measurements: [{ ...heartRateMeasurement, value: 0 }], - }).ok, - ).toBe(false) - expect( - parseProviderMeasurementBundleInput({ - ...baseInput('google-health-api', 'steps', { - kind: 'step-count', - value: 1, - effective: { kind: 'period', start, end }, - }), - measurements: [ - { - kind: 'step-count', - value: 1.5, - effective: { kind: 'period', start, end }, - }, - ], - }).ok, - ).toBe(false) - expect( - parseProviderMeasurementBundleInput({ - ...baseInput('withings', 'getmeas:54', { - kind: 'oxygen-saturation', - value: 98, - effective: { kind: 'date-time', value: dateTime }, - }), - measurements: [ - { - kind: 'oxygen-saturation', - value: 101, - effective: { kind: 'date-time', value: dateTime }, - }, - ], - }).ok, - ).toBe(false) - }) - - it('rejects a source-native identity containing an isolated surrogate', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const invalid = { - ...input, - source: { ...input.source, sourceNativeId: 'invalid-\ud800' }, - } as ProviderMeasurementBundleInput - expect(parseProviderMeasurementBundleInput(invalid).ok).toBe(false) - expect(buildProviderMeasurementBundle(invalid).ok).toBe(false) - }) - - it('fails closed at every direct Provider identity primitive boundary', () => { - const valid = { - provider: 'withings', - providerAccountIdentifier: { - system: uri('https://example.org/provider-accounts'), - value: 'account-pseudonym', - }, - sourceType: 'getmeas:11', - sourceNativeId: 'source-record-1', - outputDiscriminators: ['heart-rate'], - eventSequence: positive(1), - graphIdentifierSystem: uri( - 'urn:grove:provider-graph:org.grovealliance.example', - ), - } as const - expect( - deriveProviderIdentities({ - ...valid, - providerAccountIdentifier: { - ...valid.providerAccountIdentifier, - system: '/relative' as never, - }, - }).ok, - ).toBe(false) - expect( - deriveProviderIdentities({ - ...valid, - outputDiscriminators: [' '], - }).ok, - ).toBe(false) - expect( - deriveProviderIdentities({ - ...valid, - outputDiscriminators: ['heart-rate', 'heart-rate'], - }).ok, - ).toBe(false) - expect( - deriveProviderIdentities({ - ...valid, - eventSequence: 0 as never, - }).ok, - ).toBe(false) - }) - - it.each([ - [ - 'source native id', - (input: ProviderMeasurementBundleInput) => ({ - ...input, - application: { - ...input.application, - manufacturer: `Leaked ${input.source.sourceNativeId}`, - }, - }), - ], - [ - 'provider account pseudonym', - (input: ProviderMeasurementBundleInput) => ({ - ...input, - source: { - ...input.source, - dataOrigin: { - ...input.source.dataOrigin, - name: `Origin ${input.source.providerAccountIdentifier.value}`, - }, - }, - }), - ], - ] as const)( - 'rejects %s leakage through emitted metadata', - (_name, mutate) => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const candidate = mutate(input) as ProviderMeasurementBundleInput - expect(parseProviderMeasurementBundleInput(candidate).ok).toBe(false) - expect(buildProviderMeasurementBundle(candidate).ok).toBe(false) - if (_name === 'provider account pseudonym') { - expect( - parseNormalizedProviderRecord({ - source: candidate.source, - measurements: candidate.measurements, - }).ok, - ).toBe(false) - } - }, - ) - - it.each([ - [ - 'provider account value', - (input: ProviderMeasurementBundleInput) => ({ - ...input, - source: { - ...input.source, - providerAccountIdentifier: { - ...input.source.providerAccountIdentifier, - value: ' \t ', - }, - }, - }), - ], - [ - 'source native id', - (input: ProviderMeasurementBundleInput) => ({ - ...input, - source: { ...input.source, sourceNativeId: '\n ' }, - }), - ], - [ - 'source type', - (input: ProviderMeasurementBundleInput) => ({ - ...input, - source: { ...input.source, sourceType: ' ' }, - }), - ], - [ - 'converter identifier', - (input: ProviderMeasurementBundleInput) => ({ - ...input, - application: { - ...input.application, - identity: { - ...input.application.identity, - identifier: { - ...input.application.identity.identifier, - value: '\t', - }, - }, - }, - }), - ], - [ - 'data-origin identifier', - (input: ProviderMeasurementBundleInput) => ({ - ...input, - source: { - ...input.source, - dataOrigin: { - ...input.source.dataOrigin, - identity: { - ...input.source.dataOrigin.identity, - identifier: { - ...input.source.dataOrigin.identity.identifier, - value: ' ', - }, - }, - }, - }, - }), - ], - ] as const)('rejects a whitespace-only %s', (_name, mutate) => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - expect(parseProviderMeasurementBundleInput(mutate(input)).ok).toBe(false) - }) - - it.each(['converter', 'gateway', 'data-origin', 'recording-device'] as const)( - 'rejects an invalid Unicode %s business identity', - (role) => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const invalidIdentity = resourceIdentity( - 'https://example.org/identifiers', - 'invalid-\ud800', - ) - let candidate: ProviderMeasurementBundleInput - if (role === 'converter') { - candidate = { - ...input, - application: { ...input.application, identity: invalidIdentity }, - } - } else if (role === 'gateway') { - candidate = { - ...input, - gatewayApplication: { - kind: 'distinct-application', - roleAssurance: 'mediated-or-routed-measurement', - application: { - identity: invalidIdentity, - name: 'Invalid gateway identity', - }, - }, - } - } else if (role === 'data-origin') { - candidate = { - ...input, - source: { - ...input.source, - dataOrigin: { - ...input.source.dataOrigin, - identity: invalidIdentity, - }, - }, - } as ProviderMeasurementBundleInput - } else { - candidate = { - ...input, - source: { - ...input.source, - recordingDevice: { - identity: invalidIdentity, - identityScope: 'deployment-scoped', - }, - }, - } as ProviderMeasurementBundleInput - } - expect(parseProviderMeasurementBundleInput(candidate).ok).toBe(false) - expect(buildProviderMeasurementBundle(candidate).ok).toBe(false) - }, - ) - - it.each([ - ['google-health-api', 'heart-rate', heartRateMeasurement], - [ - 'google-health-api', - 'blood-glucose', - { - kind: 'blood-glucose', - value: 94, - effective: { kind: 'date-time', value: dateTime }, - specimen: { - identity: resourceIdentity( - 'https://example.org/specimens', - 'whole-blood', - ), - }, - }, - ], - [ - 'oura', - 'sleep', - { - kind: 'sleep-stage', - stage: 'deep', - effective: { kind: 'period', start, end }, - }, - ], - ] as const)( - 'fails closed for non-scalar or unsupported %s/%s data', - (provider, sourceType, measurement) => { - const result = buildProviderMeasurementBundle( - baseInput( - provider, - sourceType, - measurement as unknown as MobileMeasurement, - ), - ) - expect(result.ok).toBe(false) - }, - ) - - it('rejects non-positive event sequences and duplicate graph identities', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - expect( - buildProviderMeasurementBundle({ - ...input, - eventSequence: 0, - } as ProviderMeasurementBundleInput).ok, - ).toBe(false) - expect( - buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, - dataOrigin: { - ...input.source.dataOrigin, - identity: application.identity, - }, - }, - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) - }) -}) - -describe('Exchange entry identity', () => { - it.each(groveFhirExchangeIdentity.vectors)( - 'matches the IG $case vector', - (vector) => { - const derived = deriveEntryFullUrl({ - system: uri(vector.system), - value: vector.value, - }) - // A rejection vector states no URN: the system carries a separator, so the name would be - // ambiguous and the derivation refuses it. - if (vector.fullUrl === null) { - expect(derived.ok).toBe(false) - return - } - expect(derived).toEqual({ ok: true, value: vector.fullUrl }) - }, - ) - - it('rejects isolated UTF-16 surrogates before UUID derivation', () => { - expect( - deriveEntryFullUrl({ - system: uri('https://example.org/identifiers'), - value: 'invalid-\ud800', - }).ok, - ).toBe(false) - expect( - deriveEntryFullUrl({ - system: uri('https://example.org/identifiers'), - value: 'invalid-\udc00', - }).ok, - ).toBe(false) - expect( - deriveEntryFullUrl({ - system: uri('https://example.org/identifiers'), - value: 'valid-😀', - }).ok, - ).toBe(true) - }) - - it('rejects incomplete entry identifiers and invalid repository ids', () => { - expect( - entryIdentifierName({ - system: '/relative' as never, - value: 'record-1', - }).ok, - ).toBe(false) - expect( - entryIdentifierName({ - system: uri('https://example.org/identifiers'), - value: '', - }).ok, - ).toBe(false) - expect( - entryIdentifierName({ - system: uri('https://example.org/identifiers'), - value: ' \t\n ', - }).ok, - ).toBe(false) - expect( - createEntryIdentity( - { - system: uri('https://example.org/identifiers'), - value: 'record-1', - }, - 'invalid/id' as never, - ).ok, - ).toBe(false) - }) -}) - -describe('Provider-neutral normalization contract', () => { - it.each([ - 'google-health-active-energy.json', - 'oura-daily-activity.json', - 'oura-sleep-duration.json', - 'withings-body-weight.json', - ])('accepts the normalized %s fixture', (name) => { - const fixture: unknown = JSON.parse( - readFileSync(new URL(`../fixtures/normalized/${name}`, import.meta.url), { - encoding: 'utf8', - }), - ) - expect(parseNormalizedProviderRecord(fixture).ok).toBe(true) - }) - - it('normalizes multi-output serialization order without mutating caller data', () => { - const fixture = JSON.parse( - readFileSync( - new URL( - '../fixtures/normalized/oura-daily-activity.json', - import.meta.url, - ), - { encoding: 'utf8' }, - ), - ) as { measurements: Array<{ kind: string }> } - const callerOrder = fixture.measurements.map(({ kind }) => kind) - const result = parseNormalizedProviderRecord(fixture) - expect(result.ok).toBe(true) - if (!result.ok) return - expect(result.value.measurements.map(({ kind }) => kind)).toEqual([ - 'step-count', - 'active-energy', - 'distance', - ]) - expect(fixture.measurements.map(({ kind }) => kind)).toEqual(callerOrder) - expect(Object.isFrozen(result.value.measurements)).toBe(true) - }) - - it('rejects raw response fields rather than silently stripping them', () => { - const fixture = JSON.parse( - readFileSync( - new URL( - '../fixtures/normalized/oura-sleep-duration.json', - import.meta.url, - ), - { encoding: 'utf8' }, - ), - ) as Record - expect( - parseNormalizedProviderRecord({ - ...fixture, - rawVendorResponse: { readiness: 97 }, - }).ok, - ).toBe(false) - }) - - it('requires the caller to attest that the account identifier is deployment-scoped and pseudonymous', () => { - const fixture = JSON.parse( - readFileSync( - new URL( - '../fixtures/normalized/oura-sleep-duration.json', - import.meta.url, - ), - { encoding: 'utf8' }, - ), - ) as { - source: { - providerAccountIdentifier: Record - } - } - const { system, value } = fixture.source.providerAccountIdentifier - expect( - parseNormalizedProviderRecord({ - ...fixture, - source: { - ...fixture.source, - providerAccountIdentifier: { system, value }, - }, - }).ok, - ).toBe(false) - }) - - it('strictly parses the complete graph input and preserves identity strings verbatim', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const result = parseProviderMeasurementBundleInput({ - ...input, - source: { - ...input.source, - providerAccountIdentifier: { - ...input.source.providerAccountIdentifier, - value: ' pseudonym-with-significant-spaces ', - }, - }, - }) - expect(result.ok).toBe(true) - if (!result.ok) return - expect(result.value.source.providerAccountIdentifier.value).toBe( - ' pseudonym-with-significant-spaces ', - ) - - expect( - buildProviderMeasurementBundle({ - ...input, - rawVendorResponse: { heart_rate: 64 }, - } as ProviderMeasurementBundleInput & { - rawVendorResponse: unknown - }).ok, - ).toBe(false) - expect( - buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, - rawVendorResponse: { heart_rate: 64 }, - }, - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) - }) - - it('rejects an arbitrary adapter and a mismatched source token', () => { - const dataOrigin = { - identity: resourceIdentity('https://example.org/data-origins', 'oura'), - name: 'Oura', - } - expect( - parseNormalizedProviderRecord({ - source: { - adapter: { kind: 'mobile', provider: 'oura' }, - providerAccountIdentifier: { - system: 'https://example.org/deployments/provider-accounts', - value: 'pseudonym-oura-001', - assurance: 'deployment-scoped-pseudonym', - }, - sourceType: 'sleep', - sourceNativeId: 'sleep-1', - dataOrigin, - }, - measurements: [ - { - kind: 'sleep-duration', - value: 7.4, - effective: { kind: 'period', start, end }, - }, - ], - }).ok, - ).toBe(false) - - expect( - parseNormalizedProviderRecord({ - source: { - adapter: { kind: 'providers', provider: 'oura' }, - providerAccountIdentifier: { - system: 'https://example.org/deployments/provider-accounts', - value: 'pseudonym-oura-001', - assurance: 'deployment-scoped-pseudonym', - }, - sourceType: 'daily_readiness', - sourceNativeId: 'readiness-1', - dataOrigin, - }, - measurements: [heartRateMeasurement], - }).ok, - ).toBe(false) - }) - - it('correlates provider, source token, and measurement at compile time', () => { - const withingsSource = { - adapter: { kind: 'providers', provider: 'withings' }, - providerAccountIdentifier: { - system: uri('https://example.org/deployments/provider-accounts'), - value: 'pseudonym-withings-001', - assurance: 'deployment-scoped-pseudonym', - }, - sourceType: 'getmeas:11', - sourceNativeId: 'heart-rate-1', - dataOrigin: { - identity: resourceIdentity( - 'https://example.org/data-origins', - 'withings', - ), - name: 'Withings', - }, - } as const - const valid: ConnectedProviderRecord<'withings', 'getmeas:11'> = { - source: withingsSource, - measurements: [heartRateMeasurement], - } - expect(valid.measurements[0].kind).toBe('heart-rate') - - type OuraSleep = ConnectedProviderRecord<'oura', 'sleep'> - const source = { - ...withingsSource, - adapter: { kind: 'providers', provider: 'oura' }, - sourceType: 'sleep', - } as const - const invalidMeasurement = { - kind: 'blood-glucose', - value: 95, - effective: { kind: 'date-time', value: dateTime }, - specimen: { - identity: resourceIdentity('https://example.org/specimens', 'blood'), - }, - } - // @ts-expect-error Oura sleep admits sleep-duration, never glucose. - const invalid: OuraSleep = { source, measurements: [invalidMeasurement] } - expect(invalid).toBeDefined() - }) - - it('generates only catalog-owned scalar source tokens', () => { - expect(Object.hasOwn(providerScalarMappings.withings, 'getmeas:9')).toBe( - false, - ) - expect(providerScalarMappings.withings['getmeas:9+10']).toEqual({ - 'blood-pressure': 'blood-pressure-panel', - }) - }) -}) - -describe('Provider builder properties', () => { - it('preserves every finite positive heart-rate value', () => { - assert( - property( - double({ - min: 0.01, - max: 400, - noNaN: true, - noDefaultInfinity: true, - }), - (value) => { - const result = buildProviderMeasurementBundle( - baseInput('withings', 'getmeas:11', { - kind: 'heart-rate', - value, - effective: { kind: 'date-time', value: dateTime }, - }), - ) - expect(result.ok).toBe(true) - if (!result.ok) return - expect( - resources(result).find( - (resource) => resource.resourceType === 'Observation', - )?.valueQuantity?.value, - ).toBe(value) - }, - ), - ) - }) - - it('rejects every oxygen saturation outside the percentage range', () => { - assert( - property( - oneof( - double({ min: -10_000, max: -Number.MIN_VALUE, noNaN: true }), - double({ min: 100.000_001, max: 10_000, noNaN: true }), - ), - (value) => { - expect( - buildProviderMeasurementBundle( - baseInput('withings', 'getmeas:54', { - kind: 'oxygen-saturation', - value, - effective: { kind: 'date-time', value: dateTime }, - }), - ).ok, - ).toBe(false) - }, - ), - ) - }) -}) diff --git a/packages/fhir/test/questionnaire-builder.test.ts b/packages/fhir/test/questionnaire-builder.test.ts new file mode 100644 index 00000000..63eb3d3f --- /dev/null +++ b/packages/fhir/test/questionnaire-builder.test.ts @@ -0,0 +1,637 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { expectTypeOf } from 'expect-type' +import { + codingSystem, + preflightItems, + questionnaire, + questionnaireInput, + response, + responseInput, + url, +} from './questionnaire-test-support.js' +import { + buildQuestionnaire, + buildQuestionnaireResponse, + parseQuestionnaire, + parseQuestionnaireResponse, + preflightQuestionnairePair, + type GroveQuestionnaire, + type QuestionnaireInput, + type QuestionnaireResponseInput, +} from '../src/index.js' +/* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 canonicals are normative HTTP URIs. */ + +describe('Questionnaire R4 builders', () => { + it('stamps the exact Grove profiles and owned extensions', () => { + expect(questionnaire.meta?.profile).toEqual([ + 'https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire', + ]) + expect(questionnaire.extension).toEqual([ + { + url: 'http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm', + valueCoding: { + system: 'http://hl7.org/fhir/version-algorithm', + code: 'semver', + }, + }, + ]) + expect(response.meta?.profile).toEqual([ + 'https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response', + ]) + expect(response.extension).toEqual([ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode', + valueCodeableConcept: { + coding: [ + { + system: + 'http://terminology.hl7.org/CodeSystem/v3-ParticipationMode', + code: 'ELECTRONIC', + }, + ], + }, + }, + ]) + }) + + it('returns immutable, plain R4 JSON', () => { + expect(Object.isFrozen(questionnaire)).toBe(true) + expect(Object.isFrozen(questionnaire.item[0]?.item)).toBe(true) + expect(Object.isFrozen(response)).toBe(true) + expect(JSON.parse(JSON.stringify(questionnaire))).toEqual(questionnaire) + expectTypeOf(questionnaire).toExtend() + }) + + it('preserves admitted FHIR primitive metadata at every supported level', () => { + const input = { + ...questionnaire, + _title: { + extension: [ + { + url: 'https://example.org/fhir/StructureDefinition/localized-value', + valueString: 'en-US', + }, + ], + }, + item: questionnaire.item.map((item, index) => + index === 0 ? + { + ...item, + _text: { + extension: [ + { + url: 'https://example.org/fhir/StructureDefinition/localized-value', + valueString: 'en-US', + }, + ], + }, + } + : item, + ), + } + + const parsed = parseQuestionnaire(input) + expect(parsed.ok).toBe(true) + if (!parsed.ok) return + expect(parsed.value._title).toEqual(input._title) + expect(parsed.value.item[0]?._text).toEqual(input.item[0]?._text) + }) + + it('rejects unknown fields and reference questions instead of stripping them', () => { + expect( + parseQuestionnaire({ ...questionnaire, vendorPayload: true }).ok, + ).toBe(false) + const referenceQuestionnaire = { + ...questionnaire, + item: [{ linkId: 'person', text: 'Person', type: 'reference' }], + } + expect(parseQuestionnaire(referenceQuestionnaire).ok).toBe(false) + }) + + it('rejects caller attempts to replace owned profile extensions', () => { + const result = buildQuestionnaire({ + ...questionnaireInput, + extensions: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm', + valueCoding: { + system: 'http://hl7.org/fhir/version-algorithm', + code: 'semver', + }, + }, + ], + }) + expect(result.ok).toBe(false) + }) + + it('rejects duplicated linkIds before construction', () => { + const result = buildQuestionnaire({ + ...questionnaireInput, + items: [questionnaireInput.items[0], questionnaireInput.items[0]], + }) + expect(result.ok).toBe(false) + if (result.ok) return + expect(result.issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ code: 'duplicate-identifier' }), + ]), + ) + }) + + it('returns Result failures for malformed JavaScript inputs without throwing', () => { + const malformedQuestionnaires = [ + undefined, + null, + {}, + { ...questionnaireInput, unknownRootField: true }, + { + ...questionnaireInput, + items: [ + { + linkId: 'strict-item', + text: 'Strict item', + type: 'string', + unknownItemField: true, + }, + ], + }, + ] + for (const candidate of malformedQuestionnaires) { + const invoke = () => buildQuestionnaire(candidate as QuestionnaireInput) + expect(invoke).not.toThrow() + expect(invoke().ok).toBe(false) + } + + const malformedResponses = [ + undefined, + null, + {}, + { ...responseInput(), unknownRootField: true }, + { + ...responseInput(), + items: [ + { + linkId: 'strict-item', + text: 'Strict item', + answer: [{ valueString: 'answer', unknownAnswerField: true }], + }, + ], + }, + ] + for (const candidate of malformedResponses) { + const invoke = () => + buildQuestionnaireResponse(candidate as QuestionnaireResponseInput) + expect(invoke).not.toThrow() + expect(invoke().ok).toBe(false) + } + }) + + it.each([ + [ + 'an empty group', + { linkId: 'empty-group', text: 'Empty group', type: 'group' }, + ], + [ + 'required on a display item', + { + linkId: 'required-display', + text: 'Display', + type: 'display', + required: false, + }, + ], + [ + 'children on a display item', + { + linkId: 'parent-display', + text: 'Display', + type: 'display', + item: [{ linkId: 'child', text: 'Child', type: 'string' }], + }, + ], + [ + 'answerOption together with initial', + { + linkId: 'choice', + text: 'Choice', + type: 'choice', + answerOption: [ + { valueCoding: { system: codingSystem, code: 'option' } }, + ], + initial: [{ valueCoding: { system: codingSystem, code: 'option' } }], + }, + ], + ] as const)('rejects %s', (_name, item) => { + const result = buildQuestionnaire({ + ...questionnaireInput, + items: [item], + } as unknown as QuestionnaireInput) + expect(result.ok).toBe(false) + }) + + it('enforces base R4 Questionnaire and response item invariants when parsing', () => { + expect( + parseQuestionnaire({ + ...questionnaire, + item: [{ linkId: 'empty-group', type: 'group' }], + }).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + item: [ + { + linkId: 'display', + text: 'Display', + type: 'display', + required: false, + }, + ], + }).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + item: [ + { + linkId: 'choice', + text: 'Choice', + type: 'choice', + answerOption: [{ valueString: 'option' }], + initial: [{ valueString: 'option' }], + }, + ], + }).ok, + ).toBe(false) + + const mixedItem = { + linkId: 'mixed', + text: 'Mixed', + answer: [{ valueString: 'answer' }], + item: [{ linkId: 'child', text: 'Child' }], + } + expect( + parseQuestionnaireResponse({ ...response, item: [mixedItem] }).ok, + ).toBe(false) + expect( + buildQuestionnaireResponse({ + ...responseInput(), + items: [mixedItem], + }).ok, + ).toBe(false) + }) + + it('requires the exact Grove profiles at every public parse boundary', () => { + const unprofiledQuestionnaire = { ...questionnaire, meta: undefined } + const unprofiledResponse = { ...response, meta: undefined } + expect(parseQuestionnaire(unprofiledQuestionnaire).ok).toBe(false) + expect(parseQuestionnaireResponse(unprofiledResponse).ok).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + meta: { + profile: [ + ...(questionnaire.meta?.profile ?? []), + 'https://example.org/fhir/StructureDefinition/extra', + ], + }, + }).ok, + ).toBe(false) + expect( + parseQuestionnaireResponse({ + ...response, + meta: { + profile: [ + ...(response.meta?.profile ?? []), + 'https://example.org/fhir/StructureDefinition/extra', + ], + }, + }).ok, + ).toBe(false) + expect( + preflightQuestionnairePair(unprofiledQuestionnaire, response).ok, + ).toBe(false) + expect( + preflightQuestionnairePair(questionnaire, unprofiledResponse).ok, + ).toBe(false) + }) + + it('requires an HTTP(S) versioned instrument canonical and complete response Identifier', () => { + expect( + parseQuestionnaireResponse({ + ...response, + questionnaire: 'urn:example:questionnaire|2.0.0', + }).ok, + ).toBe(false) + expect( + parseQuestionnaireResponse({ + ...response, + questionnaire: `${url}#fragment|2.0.0`, + }).ok, + ).toBe(false) + expect( + parseQuestionnaireResponse({ + ...response, + identifier: { value: 'response-1' }, + }).ok, + ).toBe(false) + expect( + parseQuestionnaireResponse({ + ...response, + identifier: { + system: 'https://例え.example/questionnaire-responses', + value: 'response-1', + }, + }).ok, + ).toBe(false) + }) + + it.each([ + [ + 'code on display', + { + linkId: 'display-code', + text: 'Display', + type: 'display', + code: [{ system: codingSystem, code: 'display' }], + }, + ], + [ + 'readOnly on display', + { + linkId: 'display-read-only', + text: 'Display', + type: 'display', + readOnly: false, + }, + ], + [ + 'answerOption on boolean', + { + linkId: 'boolean-options', + text: 'Boolean', + type: 'boolean', + answerOption: [{ valueString: 'not-admitted' }], + }, + ], + [ + 'answerValueSet on attachment', + { + linkId: 'attachment-value-set', + text: 'Attachment', + type: 'attachment', + answerValueSet: 'https://example.org/fhir/ValueSet/not-admitted', + }, + ], + [ + 'a non-boolean exists operand', + { + linkId: 'exists', + text: 'Exists', + type: 'string', + enableWhen: [ + { + question: 'exists', + operator: 'exists', + answerString: 'true', + }, + ], + }, + ], + [ + 'initial on group', + { + linkId: 'group-initial', + text: 'Group', + type: 'group', + initial: [{ valueString: 'not-admitted' }], + item: [{ linkId: 'child', text: 'Child', type: 'string' }], + }, + ], + [ + 'multiple initial values without repeats', + { + linkId: 'multiple-initial', + text: 'Multiple initial', + type: 'string', + initial: [{ valueString: 'one' }, { valueString: 'two' }], + }, + ], + [ + 'a linkId longer than 255 code points', + { + linkId: '😀'.repeat(256), + text: 'Long linkId', + type: 'string', + }, + ], + ] as const)('enforces %s', (_name, item) => { + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [item], + } as unknown as QuestionnaireInput).ok, + ).toBe(false) + }) + + it('requires authored initial and answerOption values to match item type', () => { + for (const item of [ + { + linkId: 'integer-initial', + text: 'Integer initial', + type: 'integer', + initial: [{ valueString: '1' }], + }, + { + linkId: 'date-initial', + text: 'Date initial', + type: 'date', + initial: [{ valueTime: '12:00:00' }], + }, + { + linkId: 'choice-option', + text: 'Choice option', + type: 'choice', + answerOption: [{ valueString: 'uncoded' }], + }, + { + linkId: 'integer-option', + text: 'Integer option', + type: 'integer', + answerOption: [{ valueDate: '2026-08-20' }], + }, + ] as const) { + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [item], + } as unknown as QuestionnaireInput).ok, + ).toBe(false) + } + + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'open-choice-option', + text: 'Open choice option', + type: 'open-choice', + answerOption: [{ valueString: 'uncoded' }], + }, + { + linkId: 'integer-initial', + text: 'Integer initial', + type: 'integer', + initial: [{ valueInteger: 1 }], + }, + ], + }).ok, + ).toBe(true) + }) + + it('limits answerValueSet to choice and open-choice items', () => { + for (const type of ['integer', 'date', 'string'] as const) { + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: `${type}-value-set`, + text: `${type} value set`, + type, + answerValueSet: 'https://example.org/fhir/ValueSet/options', + }, + ], + }).ok, + ).toBe(false) + } + + for (const type of ['choice', 'open-choice'] as const) { + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: `${type}-value-set`, + text: `${type} value set`, + type, + answerValueSet: 'https://example.org/fhir/ValueSet/options', + }, + ], + }).ok, + ).toBe(true) + } + + expect( + parseQuestionnaire({ + ...questionnaire, + item: [ + { + linkId: 'string-value-set', + text: 'String value set', + type: 'string', + answerValueSet: 'https://example.org/fhir/ValueSet/options', + }, + ], + }).ok, + ).toBe(false) + }) + + it('rejects valueless Extension objects throughout builder inputs', () => { + expect( + buildQuestionnaire({ + ...questionnaireInput, + extensions: [ + { url: 'https://example.org/fhir/StructureDefinition/empty' }, + ], + }).ok, + ).toBe(false) + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'empty-extension', + text: 'Empty extension', + type: 'string', + extension: [ + { url: 'https://example.org/fhir/StructureDefinition/empty' }, + ], + }, + ], + }).ok, + ).toBe(false) + expect( + buildQuestionnaireResponse({ + ...responseInput(), + extensions: [ + { url: 'https://example.org/fhir/StructureDefinition/empty' }, + ], + }).ok, + ).toBe(false) + }) + + it('enforces R4 ResourceType subject codes and signed integer bounds', () => { + expect( + buildQuestionnaire({ + ...questionnaireInput, + subjectTypes: ['Banana'], + } as unknown as QuestionnaireInput).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + subjectType: ['Banana'], + }).ok, + ).toBe(false) + + for (const valueInteger of [2_147_483_648, -2_147_483_649]) { + expect( + preflightItems( + [{ linkId: 'integer', text: 'Integer', type: 'integer' }], + [ + { + linkId: 'integer', + text: 'Integer', + answer: [{ valueInteger }], + }, + ], + ).ok, + ).toBe(false) + } + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'integer-option', + text: 'Integer option', + type: 'integer', + answerOption: [{ valueInteger: 2_147_483_648 }], + }, + ], + }).ok, + ).toBe(false) + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'long-text', + text: 'Long text', + type: 'string', + maxLength: 2_147_483_648, + }, + ], + }).ok, + ).toBe(false) + }) +}) diff --git a/packages/fhir/test/questionnaire-extension-contract.test.ts b/packages/fhir/test/questionnaire-extension-contract.test.ts new file mode 100644 index 00000000..b4dfc912 --- /dev/null +++ b/packages/fhir/test/questionnaire-extension-contract.test.ts @@ -0,0 +1,756 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + codingSystem, + preflightItems, + questionnaire, + questionnaireExtensions, + questionnaireInput, + response, + responseInput, + targetConstraint, +} from './questionnaire-test-support.js' +import { + buildQuestionnaire, + buildQuestionnaireResponse, + parseQuestionnaire, + parseQuestionnaireResponse, + type QuestionnaireInput, +} from '../src/index.js' +import { + compareR4Temporal, + isR4Date, + isR4DateTime, + isR4Time, +} from '../src/questionnaire/temporal.js' + +describe('Questionnaire R4 builders', () => { + it('enforces hidden and itemWeight slice cardinality and value types', () => { + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'duplicate-hidden', + text: 'Duplicate hidden', + type: 'string', + extension: [ + { url: questionnaireExtensions.hidden, valueBoolean: true }, + { url: questionnaireExtensions.hidden, valueBoolean: false }, + ], + }, + ], + }).ok, + ).toBe(false) + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'wrong-hidden', + text: 'Wrong hidden', + type: 'string', + extension: [ + { url: questionnaireExtensions.hidden, valueString: 'true' }, + ], + }, + ], + }).ok, + ).toBe(false) + + expect( + buildQuestionnaireResponse({ + ...responseInput(), + items: [ + { + linkId: 'answer-weight', + text: 'Answer weight', + answer: [ + { + extension: [ + { + url: questionnaireExtensions.itemWeight, + valueInteger: 1, + }, + ], + valueCoding: { system: codingSystem, code: 'answer-weight' }, + }, + ], + }, + ], + }).ok, + ).toBe(false) + expect( + buildQuestionnaireResponse({ + ...responseInput(), + items: [ + { + linkId: 'duplicate-answer-weight', + text: 'Duplicate answer weight', + answer: [ + { + extension: [ + { + url: questionnaireExtensions.itemWeight, + valueDecimal: 1, + }, + { + url: questionnaireExtensions.itemWeight, + valueDecimal: 2, + }, + ], + valueCoding: { + system: codingSystem, + code: 'duplicate-answer-weight', + }, + }, + ], + }, + ], + }).ok, + ).toBe(false) + + const weightedCoding = { + system: codingSystem, + code: 'weighted', + extension: [{ url: questionnaireExtensions.itemWeight, valueInteger: 1 }], + } + const duplicateWeightedCoding = { + system: codingSystem, + code: 'duplicate-weight', + extension: [ + { url: questionnaireExtensions.itemWeight, valueDecimal: 1 }, + { url: questionnaireExtensions.itemWeight, valueDecimal: 2 }, + ], + } + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'wrong-weight', + text: 'Wrong weight', + type: 'choice', + answerOption: [{ valueCoding: weightedCoding }], + }, + ], + }).ok, + ).toBe(false) + expect( + buildQuestionnaireResponse({ + ...responseInput(), + items: [ + { + linkId: 'weighted', + text: 'Weighted', + answer: [{ valueCoding: weightedCoding }], + }, + ], + }).ok, + ).toBe(false) + expect( + buildQuestionnaireResponse({ + ...responseInput(), + items: [ + { + linkId: 'duplicate-weight', + text: 'Duplicate weight', + answer: [{ valueCoding: duplicateWeightedCoding }], + }, + ], + }).ok, + ).toBe(false) + }) + + it('rejects empty answer value elements', () => { + for (const [type, answer] of [ + ['string', { valueString: '' }], + ['url', { valueUri: '' }], + ['choice', { valueCoding: {} }], + ['quantity', { valueQuantity: {} }], + ['attachment', { valueAttachment: {} }], + ] as const) { + expect( + preflightItems( + [{ linkId: 'empty', text: 'Empty', type }], + [{ linkId: 'empty', text: 'Empty', answer: [answer] }], + ).ok, + ).toBe(false) + } + }) + + it('enforces answered text and qrs-2 outside pair preflight', () => { + const duplicateAnswers = [ + { + linkId: 'duplicate', + text: 'Duplicate', + answer: [{ valueString: 'one' }], + }, + { + linkId: 'duplicate', + text: 'Duplicate', + answer: [{ valueString: 'two' }], + }, + ] + expect( + buildQuestionnaireResponse({ + ...responseInput(), + items: duplicateAnswers, + }).ok, + ).toBe(false) + expect( + parseQuestionnaireResponse({ + ...response, + item: duplicateAnswers, + }).ok, + ).toBe(false) + + expect( + parseQuestionnaireResponse({ + ...response, + item: [ + { + linkId: 'answered-without-text', + answer: [{ valueString: 'answer' }], + }, + ], + }).ok, + ).toBe(false) + }) + + it('validates authored and answer temporal values as R4 primitives', () => { + for (const invalidAuthored of [ + '2026-08-20', + '2026-08-20T12:00:00', + '2026-02-29T12:00:00Z', + '2026-08-20T24:00:00Z', + ]) { + expect( + parseQuestionnaireResponse({ + ...response, + authored: invalidAuthored, + }).ok, + ).toBe(false) + } + + for (const [type, answer] of [ + ['date', { valueDate: '2026-02-29' }], + ['dateTime', { valueDateTime: '2026-08-20T12:00:00' }], + ['dateTime', { valueDateTime: '2026-13' }], + ['time', { valueTime: '24:00:00' }], + ] as const) { + expect( + preflightItems( + [{ linkId: 'temporal', text: 'Temporal', type }], + [ + { + linkId: 'temporal', + text: 'Temporal', + answer: [answer], + }, + ], + ).ok, + ).toBe(false) + } + + expect( + preflightItems( + [ + { linkId: 'partial-date', text: 'Date', type: 'date' }, + { + linkId: 'partial-date-time', + text: 'Date time', + type: 'dateTime', + }, + ], + [ + { + linkId: 'partial-date', + text: 'Date', + answer: [{ valueDate: '2026-08' }], + }, + { + linkId: 'partial-date-time', + text: 'Date time', + answer: [{ valueDateTime: '2026' }], + }, + ], + ).ok, + ).toBe(true) + }) + + it('covers R4 temporal validation and comparison boundaries', () => { + for (const candidate of [ + '0000', + '2026-00', + '2026-13', + '2026-02-29', + '2024-04-31', + 'not-a-date', + ]) { + expect(isR4Date(candidate)).toBe(false) + } + for (const candidate of [ + '2026', + '2026-08', + '2024-02-29', + '2026-04-30', + '2026-01-31', + ]) { + expect(isR4Date(candidate)).toBe(true) + } + expect(isR4Date(undefined)).toBe(false) + + for (const candidate of ['24:00:00', '12:60:00', '12:00:61', '12:00']) { + expect(isR4Time(candidate)).toBe(false) + } + expect(isR4Time(null)).toBe(false) + expect(isR4Time('00:00:00')).toBe(true) + expect(isR4Time('23:59:60.123')).toBe(true) + + expect(isR4DateTime(null)).toBe(false) + expect(isR4DateTime('2026-08')).toBe(true) + expect(isR4DateTime('2026-02-29T12:00:00Z')).toBe(false) + expect(isR4DateTime('2026-08-20T12:00:00+14:01')).toBe(false) + expect(isR4DateTime('2026-08-20T12:00:00Z')).toBe(true) + + expect(compareR4Temporal('bad', '2026', 'date')).toBeUndefined() + expect(compareR4Temporal('2026', 'bad', 'date')).toBeUndefined() + expect(compareR4Temporal('2026', '2027', 'date')).toBe(-1) + expect(compareR4Temporal('2027', '2026', 'date')).toBe(1) + expect(compareR4Temporal('2026', '2026', 'date')).toBe(0) + expect(compareR4Temporal('2026-02', '2026-03', 'date')).toBe(-1) + expect(compareR4Temporal('2026-03', '2026-02', 'date')).toBe(1) + expect(compareR4Temporal('2026-02-02', '2026-02-01', 'date')).toBe(1) + expect(compareR4Temporal('2026-02-01', '2026-02-01', 'date')).toBe(0) + expect(compareR4Temporal('2026', '2026-01', 'date')).toBeUndefined() + + expect(compareR4Temporal('bad', '12:00:00', 'time')).toBeUndefined() + expect(compareR4Temporal('12:00:00', 'bad', 'time')).toBeUndefined() + expect(compareR4Temporal('11:00:00', '12:00:00', 'time')).toBe(-1) + expect(compareR4Temporal('12:01:00', '12:00:00', 'time')).toBe(1) + expect(compareR4Temporal('12:00:01', '12:00:00', 'time')).toBe(1) + expect(compareR4Temporal('12:00:00.01', '12:00:00.1', 'time')).toBe(-1) + expect(compareR4Temporal('12:00:00.10', '12:00:00.1', 'time')).toBe(0) + + const instant = '2026-08-20T12:00:00Z' + expect(compareR4Temporal('bad', instant, 'dateTime')).toBeUndefined() + expect(compareR4Temporal(instant, 'bad', 'dateTime')).toBeUndefined() + expect(compareR4Temporal('2026-08', instant, 'dateTime')).toBeUndefined() + expect(compareR4Temporal(instant, '2026-08', 'dateTime')).toBeUndefined() + expect( + compareR4Temporal( + '2026-08-20T12:00:00+02:00', + '2026-08-20T10:30:00Z', + 'dateTime', + ), + ).toBe(-1) + expect(compareR4Temporal(instant, instant, 'dateTime')).toBe(0) + expect(compareR4Temporal('2026-08', '2026-09', 'dateTime')).toBe(-1) + expect(compareR4Temporal('2026', '2026-08', 'dateTime')).toBeUndefined() + }) + + it('validates Questionnaire lifecycle dates and effectivePeriod locally', () => { + expect( + parseQuestionnaire({ ...questionnaire, date: '2026-02-29' }).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + approvalDate: '2026-02-29', + }).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + lastReviewDate: '2026-08-20T12:00:00Z', + }).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + effectivePeriod: { start: 'not-a-date-time' }, + }).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + effectivePeriod: { + start: '2026-08-20T12:00:00Z', + end: '2026-08-20T13:00:00+02:00', + }, + }).ok, + ).toBe(false) + expect( + parseQuestionnaire({ + ...questionnaire, + date: '2026-08', + approvalDate: '2026-08-20', + lastReviewDate: '2026-08', + effectivePeriod: { + start: '2026-08-20T12:00:00+02:00', + end: '2026-08-20T11:00:00Z', + }, + }).ok, + ).toBe(true) + }) + + it('rejects duplicate and wrong-typed portable constraint extensions', () => { + const duplicate = buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'duplicate-minimum', + text: 'Duplicate minimum', + type: 'string', + extension: [ + { url: questionnaireExtensions.minLength, valueInteger: 1 }, + { url: questionnaireExtensions.minLength, valueInteger: 2 }, + ], + }, + ], + }) + expect(duplicate.ok).toBe(false) + if (!duplicate.ok) { + expect(duplicate.issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ code: 'duplicate-identifier' }), + ]), + ) + } + + for (const item of [ + { + linkId: 'wrong-minimum-length', + text: 'Wrong minimum length', + type: 'string', + extension: [ + { url: questionnaireExtensions.minLength, valueString: '1' }, + ], + }, + { + linkId: 'wrong-value-bound', + text: 'Wrong value bound', + type: 'integer', + extension: [ + { url: questionnaireExtensions.minValue, valueString: '1' }, + ], + }, + { + linkId: 'wrong-maximum-size', + text: 'Wrong maximum size', + type: 'attachment', + extension: [ + { url: questionnaireExtensions.maxSize, valueInteger: 1024 }, + ], + }, + { + linkId: 'wrong-unit-option', + text: 'Wrong unit option', + type: 'quantity', + extension: [ + { url: questionnaireExtensions.unitOption, valueString: 'kg' }, + ], + }, + { + linkId: 'wrong-exclusive-option', + text: 'Wrong exclusive option', + type: 'choice', + answerOption: [ + { + extension: [ + { + url: questionnaireExtensions.optionExclusive, + valueString: 'true', + }, + ], + valueCoding: { system: codingSystem, code: 'none' }, + }, + ], + }, + ] as const) { + const result = buildQuestionnaire({ + ...questionnaireInput, + items: [item], + } as unknown as QuestionnaireInput) + expect(result.ok).toBe(false) + if (!result.ok) { + expect(result.issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ code: 'invalid-type' }), + ]), + ) + } + } + }) + + it('accepts the complete portable constraint and expression surface', () => { + const result = buildQuestionnaire({ + ...questionnaireInput, + extensions: [ + { + url: questionnaireExtensions.variable, + valueExpression: { + language: 'text/fhirpath', + name: 'rootScore', + expression: '0', + }, + }, + targetConstraint('root-valid'), + ], + items: [ + { + linkId: 'text', + text: 'Text', + type: 'text', + maxLength: 8, + extension: [ + { url: questionnaireExtensions.minLength, valueInteger: 2 }, + ], + }, + { + linkId: 'decimal', + text: 'Decimal', + type: 'decimal', + extension: [ + { url: questionnaireExtensions.minValue, valueDecimal: 1 }, + { url: questionnaireExtensions.maxValue, valueDecimal: 5 }, + { + url: questionnaireExtensions.maxDecimalPlaces, + valueInteger: 2, + }, + ], + }, + { + linkId: 'quantity', + text: 'Quantity', + type: 'quantity', + extension: [ + { + url: questionnaireExtensions.minQuantity, + valueQuantity: { + value: 1, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + { + url: questionnaireExtensions.maxQuantity, + valueQuantity: { + value: 5, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + { + url: questionnaireExtensions.unitOption, + valueCoding: { + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + ], + }, + { + linkId: 'attachment', + text: 'Attachment', + type: 'attachment', + repeats: true, + extension: [ + { url: questionnaireExtensions.mimeType, valueCode: 'image/png' }, + { url: questionnaireExtensions.maxSize, valueDecimal: 1024 }, + { url: questionnaireExtensions.minOccurs, valueInteger: 1 }, + { url: questionnaireExtensions.maxOccurs, valueInteger: 2 }, + { + url: questionnaireExtensions.variable, + valueExpression: { + language: 'text/fhirpath', + name: 'localScore', + expression: '0', + }, + }, + targetConstraint('item-valid'), + ], + }, + ], + }) + expect(result.ok).toBe(true) + }) + + it.each([ + [ + 'text length placement', + { + linkId: 'invalid', + text: 'Invalid', + type: 'boolean', + extension: [ + { url: questionnaireExtensions.minLength, valueInteger: 1 }, + ], + }, + 'invalid-choice', + ], + [ + 'text bound order', + { + linkId: 'invalid', + text: 'Invalid', + type: 'string', + maxLength: 2, + extension: [ + { url: questionnaireExtensions.minLength, valueInteger: 3 }, + ], + }, + 'out-of-range', + ], + [ + 'decimal placement', + { + linkId: 'invalid', + text: 'Invalid', + type: 'integer', + extension: [ + { + url: questionnaireExtensions.maxDecimalPlaces, + valueInteger: 2, + }, + ], + }, + 'invalid-choice', + ], + [ + 'value bound datatype', + { + linkId: 'invalid', + text: 'Invalid', + type: 'date', + extension: [{ url: questionnaireExtensions.minValue, valueInteger: 1 }], + }, + 'invalid-type', + ], + [ + 'quantity bound order', + { + linkId: 'invalid', + text: 'Invalid', + type: 'quantity', + extension: [ + { + url: questionnaireExtensions.minQuantity, + valueQuantity: { + value: 5, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + { + url: questionnaireExtensions.maxQuantity, + valueQuantity: { + value: 1, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + ], + }, + 'out-of-range', + ], + [ + 'unit placement', + { + linkId: 'invalid', + text: 'Invalid', + type: 'string', + extension: [ + { + url: questionnaireExtensions.unitOption, + valueCoding: { + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + ], + }, + 'invalid-choice', + ], + [ + 'attachment placement', + { + linkId: 'invalid', + text: 'Invalid', + type: 'string', + extension: [ + { url: questionnaireExtensions.mimeType, valueCode: 'image/png' }, + ], + }, + 'invalid-choice', + ], + [ + 'occurrence placement', + { + linkId: 'invalid', + text: 'Invalid', + type: 'choice', + extension: [ + { url: questionnaireExtensions.minOccurs, valueInteger: 1 }, + ], + }, + 'invalid-choice', + ], + ] as const)('rejects invalid %s constraints', (_name, item, code) => { + const result = buildQuestionnaire({ + ...questionnaireInput, + items: [item], + } as unknown as QuestionnaireInput) + expect(result.ok).toBe(false) + if (result.ok) return + expect(result.issues).toEqual( + expect.arrayContaining([expect.objectContaining({ code })]), + ) + }) + + it('rejects malformed expressions, reserved variables, and duplicate target keys', () => { + const result = buildQuestionnaire({ + ...questionnaireInput, + extensions: [ + { + url: questionnaireExtensions.variable, + valueExpression: { + language: 'text/fhirpath', + name: 'context', + expression: '0', + }, + }, + targetConstraint('duplicate-key'), + ], + items: [ + { + linkId: 'invalid-expression', + text: 'Invalid expression', + type: 'string', + extension: [ + { + url: questionnaireExtensions.calculatedExpression, + valueExpression: { + language: 'text/cql', + expression: ' ', + }, + }, + targetConstraint('duplicate-key'), + ], + }, + ], + }) + expect(result.ok).toBe(false) + if (result.ok) return + expect(new Set(result.issues.map(({ code }) => code))).toEqual( + new Set(['invalid-code', 'invalid-type', 'duplicate-identifier']), + ) + }) +}) diff --git a/packages/fhir/test/questionnaire-input-surface.test.ts b/packages/fhir/test/questionnaire-input-surface.test.ts new file mode 100644 index 00000000..4e63436b --- /dev/null +++ b/packages/fhir/test/questionnaire-input-surface.test.ts @@ -0,0 +1,286 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + authored, + questionnaireInput, + responseInput, + unwrap, +} from './questionnaire-test-support.js' +import { + buildQuestionnaire, + buildQuestionnaireResponse, + parseAbsoluteUri, + parseFhirId, + parseQuestionnaireResponse, + type QuestionnaireInput, + type QuestionnaireResponseInput, +} from '../src/index.js' + +describe('Questionnaire R4 builders', () => { + it('reports the complete bounded Questionnaire input validation surface', () => { + const result = buildQuestionnaire({ + ...questionnaireInput, + url: '/relative', + version: '01.0.0', + id: 'invalid/id', + date: 'not-an-instant', + subjectTypes: ['Patient', ''], + extensions: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/rendering-styleSensitive', + valueBoolean: true, + }, + ], + items: [ + { linkId: '', type: 'string', repeats: true }, + { + linkId: 'exclusive', + text: 'Exclusive rules', + type: 'choice', + enableWhen: [ + { question: 'a', operator: 'exists', answerBoolean: true }, + { question: 'b', operator: 'exists', answerBoolean: true }, + ], + initial: [{ valueString: 'initial' }], + answerOption: [{ valueString: 'option' }], + answerValueSet: 'https://example.org/ValueSet/options', + extension: [ + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression', + valueExpression: { + language: 'text/fhirpath', + expression: 'true', + }, + }, + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', + valueExpression: { + language: 'text/fhirpath', + expression: "'x'", + }, + }, + { + url: 'http://hl7.org/fhir/StructureDefinition/rendering-styleSensitive', + valueBoolean: true, + }, + ], + }, + ], + } as unknown as QuestionnaireInput) + expect(result.ok).toBe(false) + if (result.ok) return + expect(new Set(result.issues.map(({ code }) => code))).toEqual( + new Set([ + 'invalid-uri', + 'invalid-code', + 'invalid-type', + 'invalid-identifier', + 'invalid-date-time', + 'missing-required', + 'invalid-choice', + 'invalid-reference', + ]), + ) + }) + + it('preserves every admitted optional Questionnaire and response field', () => { + const fullQuestionnaire = buildQuestionnaire({ + ...questionnaireInput, + id: unwrap(parseFhirId('questionnaire-1')), + date: authored, + description: 'A complete test instrument.', + purpose: 'Coverage of bounded optional fields.', + extensions: [ + { + url: 'https://example.org/fhir/StructureDefinition/instrument-note', + valueString: 'note', + }, + ], + }) + expect(fullQuestionnaire.ok).toBe(true) + if (!fullQuestionnaire.ok) return + expect(fullQuestionnaire.value.id).toBe('questionnaire-1') + expect(fullQuestionnaire.value.description).toBe( + 'A complete test instrument.', + ) + + const fullResponse = buildQuestionnaireResponse({ + ...responseInput(), + id: unwrap(parseFhirId('response-1')), + author: { type: 'Practitioner', reference: 'Practitioner/author' }, + source: { type: 'Patient', reference: 'Patient/example' }, + extensions: [ + { + url: 'https://example.org/fhir/StructureDefinition/response-note', + valueString: 'note', + }, + ], + }) + expect(fullResponse.ok).toBe(true) + if (!fullResponse.ok) return + expect(fullResponse.value.author?.reference).toBe('Practitioner/author') + expect(fullResponse.value.source?.reference).toBe('Patient/example') + }) + + it('supports identifier-only typed response actors and rejects disallowed source targets', () => { + const logical = buildQuestionnaireResponse({ + ...responseInput(), + author: { + type: 'PractitionerRole', + identifier: { + system: unwrap( + parseAbsoluteUri('https://example.org/practitioner-roles'), + ), + value: 'role-7', + }, + }, + source: { + type: 'RelatedPerson', + identifier: { + system: unwrap(parseAbsoluteUri('https://example.org/respondents')), + value: 'respondent-9', + }, + }, + }) + expect(logical.ok).toBe(true) + if (!logical.ok) return + expect(logical.value.author?.type).toBe('PractitionerRole') + expect(logical.value.source?.identifier?.value).toBe('respondent-9') + + expect( + parseQuestionnaireResponse({ + ...logical.value, + source: { type: 'Organization', reference: 'Organization/example' }, + }).ok, + ).toBe(false) + }) + + it('parses literal, historical, absolute, and contained response references without redundant type', () => { + const built = buildQuestionnaireResponse(responseInput()) + expect(built.ok).toBe(true) + if (!built.ok) return + + expect( + parseQuestionnaireResponse({ + ...built.value, + subject: { reference: 'Patient/example/_history/2' }, + author: { + reference: 'https://example.org/fhir/Practitioner/author/_history/7', + }, + source: { reference: 'RelatedPerson/respondent' }, + }).ok, + ).toBe(true) + expect( + parseQuestionnaireResponse({ + ...built.value, + contained: [{ resourceType: 'Patient', id: 'contained-subject' }], + subject: { reference: '#contained-subject' }, + }).ok, + ).toBe(true) + }) + + it('rejects conflicting, unresolved, and untyped logical response references', () => { + const built = buildQuestionnaireResponse(responseInput()) + expect(built.ok).toBe(true) + if (!built.ok) return + const invalid = [ + { + ...built.value, + subject: { type: 'Observation', reference: 'Patient/example' }, + }, + { ...built.value, subject: { reference: 'unresolved-reference' } }, + { + ...built.value, + subject: { reference: 'https://repository.example.org/fhir/Patient' }, + }, + { + ...built.value, + subject: { + identifier: { + system: 'https://example.org/patients', + value: 'patient-1', + }, + }, + }, + { + ...built.value, + contained: [{ resourceType: 'Patient', id: 'contained-subject' }], + subject: { + type: 'Observation', + reference: '#contained-subject', + }, + }, + { + ...built.value, + contained: [{ resourceType: 'Patient', id: 'other-subject' }], + subject: { reference: '#missing-subject' }, + }, + ] + for (const response of invalid) { + expect(parseQuestionnaireResponse(response as never).ok).toBe(false) + } + }) + + it('reports the complete bounded QuestionnaireResponse input validation surface', () => { + const result = buildQuestionnaireResponse({ + questionnaire: 'https://example.org/Questionnaire/pain', + identifier: { system: '/relative', value: ' ' }, + status: 'completed', + id: 'invalid/id', + subject: { + type: 'Patient', + reference: 'Patient/example/_history/2', + }, + authored: 'not-an-instant', + author: { type: 'Device', reference: ' ' }, + source: { type: 'Organization', reference: '' }, + extensions: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode', + valueCodeableConcept: { text: 'duplicate' }, + }, + ], + items: [ + { + linkId: 'parent', + item: [ + { + linkId: 'direct-child', + answer: [{ valueString: 'missing repeated text' }], + }, + ], + answer: [ + { + valueString: 'parent answer', + item: [ + { + linkId: 'answer-child', + answer: [{ valueString: 'also missing repeated text' }], + }, + ], + }, + ], + }, + ], + } as unknown as QuestionnaireResponseInput) + expect(result.ok).toBe(false) + if (result.ok) return + expect(new Set(result.issues.map(({ code }) => code))).toEqual( + new Set([ + 'invalid-uri', + 'invalid-code', + 'invalid-identifier', + 'invalid-date-time', + 'invalid-reference', + 'duplicate-identifier', + 'missing-required', + ]), + ) + }) +}) diff --git a/packages/fhir/test/questionnaire-preflight-constraints.test.ts b/packages/fhir/test/questionnaire-preflight-constraints.test.ts new file mode 100644 index 00000000..593303d3 --- /dev/null +++ b/packages/fhir/test/questionnaire-preflight-constraints.test.ts @@ -0,0 +1,508 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + codingSystem, + preflightItems, + questionnaireExtensions, + questionnaireInput, + unwrap, +} from './questionnaire-test-support.js' +import { + buildQuestionnaire, + parseAbsoluteUri, + parseCanonical, +} from '../src/index.js' + +describe('Questionnaire pair preflight', () => { + it('enforces text length and decimal precision constraints', () => { + const textItem = { + linkId: 'text', + text: 'Text', + type: 'text', + maxLength: 4, + extension: [{ url: questionnaireExtensions.minLength, valueInteger: 2 }], + } as const + expect( + preflightItems( + [textItem], + [{ linkId: 'text', text: 'Text', answer: [{ valueString: 'x' }] }], + ).ok, + ).toBe(false) + expect( + preflightItems( + [textItem], + [ + { + linkId: 'text', + text: 'Text', + answer: [{ valueString: '12345' }], + }, + ], + ).ok, + ).toBe(false) + + const oneCodePointItem = { + linkId: 'unicode', + text: 'Unicode', + type: 'string', + maxLength: 1, + extension: [{ url: questionnaireExtensions.minLength, valueInteger: 1 }], + } as const + expect( + preflightItems( + [oneCodePointItem], + [ + { + linkId: 'unicode', + text: 'Unicode', + answer: [{ valueString: '😀' }], + }, + ], + ).ok, + ).toBe(true) + expect( + preflightItems( + [oneCodePointItem], + [ + { + linkId: 'unicode', + text: 'Unicode', + answer: [{ valueString: '😀x' }], + }, + ], + ).ok, + ).toBe(false) + + const decimalItem = { + linkId: 'decimal', + text: 'Decimal', + type: 'decimal', + extension: [ + { + url: questionnaireExtensions.maxDecimalPlaces, + valueInteger: 2, + }, + ], + } as const + expect( + preflightItems( + [decimalItem], + [ + { + linkId: 'decimal', + text: 'Decimal', + answer: [{ valueDecimal: 1.234 }], + }, + ], + ).ok, + ).toBe(false) + expect( + preflightItems( + [decimalItem], + [ + { + linkId: 'decimal', + text: 'Decimal', + answer: [{ valueDecimal: 1.25 }], + }, + ], + ).ok, + ).toBe(true) + }) + + it('orders dateTime bounds by instant and time bounds by clock value', () => { + const dateTimeItem = { + linkId: 'date-time', + text: 'Date time', + type: 'dateTime', + extension: [ + { + url: questionnaireExtensions.minValue, + valueDateTime: '2026-08-20T12:00:00+02:00', + }, + { + url: questionnaireExtensions.maxValue, + valueDateTime: '2026-08-20T11:00:00Z', + }, + ], + } as const + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [dateTimeItem], + }).ok, + ).toBe(true) + expect( + preflightItems( + [dateTimeItem], + [ + { + linkId: 'date-time', + text: 'Date time', + answer: [{ valueDateTime: '2026-08-20T10:30:00Z' }], + }, + ], + ).ok, + ).toBe(true) + + const timeItem = { + linkId: 'time', + text: 'Time', + type: 'time', + extension: [ + { + url: questionnaireExtensions.minValue, + valueTime: '12:00:00.0', + }, + { + url: questionnaireExtensions.maxValue, + valueTime: '12:00:00', + }, + ], + } as const + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [timeItem], + }).ok, + ).toBe(true) + expect( + preflightItems( + [timeItem], + [ + { + linkId: 'time', + text: 'Time', + answer: [{ valueTime: '12:00:00.000' }], + }, + ], + ).ok, + ).toBe(true) + }) + + it('evaluates temporal enableWhen values semantically', () => { + expect( + preflightItems( + [ + { linkId: 'source', text: 'Source', type: 'dateTime' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableWhen: [ + { + question: 'source', + operator: '>', + answerDateTime: '2026-08-20T12:00:00+02:00', + }, + ], + }, + ], + [ + { + linkId: 'source', + text: 'Source', + answer: [{ valueDateTime: '2026-08-20T10:30:00Z' }], + }, + { + linkId: 'dependent', + text: 'Dependent', + answer: [{ valueString: 'enabled' }], + }, + ], + ).ok, + ).toBe(true) + + expect( + preflightItems( + [ + { linkId: 'source', text: 'Source', type: 'time' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableWhen: [ + { + question: 'source', + operator: '=', + answerTime: '12:00:00.0', + }, + ], + }, + ], + [ + { + linkId: 'source', + text: 'Source', + answer: [{ valueTime: '12:00:00' }], + }, + { + linkId: 'dependent', + text: 'Dependent', + answer: [{ valueString: 'enabled' }], + }, + ], + ).ok, + ).toBe(true) + }) + + it('enforces scalar and comparable quantity bounds', () => { + expect( + preflightItems( + [ + { + linkId: 'integer', + text: 'Integer', + type: 'integer', + extension: [ + { url: questionnaireExtensions.minValue, valueInteger: 2 }, + { url: questionnaireExtensions.maxValue, valueInteger: 4 }, + ], + }, + ], + [ + { + linkId: 'integer', + text: 'Integer', + answer: [{ valueInteger: 5 }], + }, + ], + ).ok, + ).toBe(false) + + const quantityItem = { + linkId: 'quantity', + text: 'Quantity', + type: 'quantity', + extension: [ + { + url: questionnaireExtensions.minQuantity, + valueQuantity: { + value: 1, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + { + url: questionnaireExtensions.maxQuantity, + valueQuantity: { + value: 5, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + ], + } as const + expect( + preflightItems( + [quantityItem], + [ + { + linkId: 'quantity', + text: 'Quantity', + answer: [ + { + valueQuantity: { + value: 3, + system: 'http://unitsofmeasure.org', + code: 'lb', + }, + }, + ], + }, + ], + ).ok, + ).toBe(false) + expect( + preflightItems( + [quantityItem], + [ + { + linkId: 'quantity', + text: 'Quantity', + answer: [ + { + valueQuantity: { + value: 3, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + ], + }, + ], + ).ok, + ).toBe(true) + }) + + it('enforces inline and ValueSet-certified quantity units', () => { + const unitValueSet = unwrap( + parseCanonical('https://example.org/fhir/ValueSet/units|1.0.0'), + ) + const quantityAnswer = [ + { + linkId: 'quantity', + text: 'Quantity', + answer: [ + { + valueQuantity: { + value: 3, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + ], + }, + ] as const + const inline = [ + { + linkId: 'quantity', + text: 'Quantity', + type: 'quantity', + extension: [ + { + url: questionnaireExtensions.unitOption, + valueCoding: { + system: 'http://unitsofmeasure.org', + code: 'lb', + }, + }, + ], + }, + ] as const + expect(preflightItems(inline, quantityAnswer).ok).toBe(false) + + const valueSet = [ + { + linkId: 'quantity', + text: 'Quantity', + type: 'quantity', + extension: [ + { + url: questionnaireExtensions.unitValueSet, + valueCanonical: unitValueSet, + }, + ], + }, + ] as const + expect(preflightItems(valueSet, quantityAnswer).ok).toBe(false) + expect( + preflightItems(valueSet, quantityAnswer, { + valueSets: [ + { + canonical: unitValueSet, + concepts: [ + { + system: unwrap(parseAbsoluteUri('http://unitsofmeasure.org')), + code: 'kg', + }, + ], + }, + ], + }).ok, + ).toBe(true) + }) + + it('enforces attachment MIME type and declared maximum size', () => { + const item = { + linkId: 'attachment', + text: 'Attachment', + type: 'attachment', + extension: [ + { url: questionnaireExtensions.mimeType, valueCode: 'image/png' }, + { url: questionnaireExtensions.maxSize, valueDecimal: 4 }, + ], + } as const + expect( + preflightItems( + [item], + [ + { + linkId: 'attachment', + text: 'Attachment', + answer: [ + { + valueAttachment: { + contentType: 'text/plain', + size: 5, + data: 'dGVzdA==', + }, + }, + ], + }, + ], + ).ok, + ).toBe(false) + expect( + preflightItems( + [item], + [ + { + linkId: 'attachment', + text: 'Attachment', + answer: [ + { + valueAttachment: { + contentType: 'image/png', + size: 4, + data: 'dGVzdA==', + }, + }, + ], + }, + ], + ).ok, + ).toBe(true) + }) + + it('enforces answer occurrence limits and exclusive options', () => { + const item = { + linkId: 'choice', + text: 'Choice', + type: 'choice', + repeats: true, + extension: [ + { url: questionnaireExtensions.minOccurs, valueInteger: 2 }, + { url: questionnaireExtensions.maxOccurs, valueInteger: 2 }, + ], + answerOption: [ + { + extension: [ + { + url: questionnaireExtensions.optionExclusive, + valueBoolean: true, + }, + ], + valueCoding: { system: codingSystem, code: 'none' }, + }, + { valueCoding: { system: codingSystem, code: 'pain' } }, + { valueCoding: { system: codingSystem, code: 'fatigue' } }, + ], + } as const + const responseItem = (codes: readonly string[]) => [ + { + linkId: 'choice', + text: 'Choice', + answer: codes.map((code) => ({ + valueCoding: { system: codingSystem, code }, + })), + }, + ] + expect(preflightItems([item], responseItem(['pain'])).ok).toBe(false) + expect( + preflightItems([item], responseItem(['pain', 'fatigue', 'pain'])).ok, + ).toBe(false) + expect(preflightItems([item], responseItem(['none', 'pain'])).ok).toBe( + false, + ) + expect(preflightItems([item], responseItem(['pain', 'fatigue'])).ok).toBe( + true, + ) + }) +}) diff --git a/packages/fhir/test/questionnaire-preflight-expressions.test.ts b/packages/fhir/test/questionnaire-preflight-expressions.test.ts new file mode 100644 index 00000000..df852aad --- /dev/null +++ b/packages/fhir/test/questionnaire-preflight-expressions.test.ts @@ -0,0 +1,500 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { assert, property, string } from 'fast-check' +import { + codingSystem, + preflightItems, + questionnaire, + questionnaireExtensions, + response, + responseInput, + targetConstraint, + unwrap, +} from './questionnaire-test-support.js' +import { + buildQuestionnaireResponse, + parseCanonical, + preflightQuestionnairePair, +} from '../src/index.js' + +describe('Questionnaire pair preflight', () => { + it('returns non-blocking warnings for warning target constraints', () => { + const warningQuestionnaire = { + ...questionnaire, + extension: [ + ...(questionnaire.extension ?? []), + targetConstraint('warning-constraint', 'warning'), + ], + } + const result = preflightQuestionnairePair(warningQuestionnaire, response) + expect(result.ok).toBe(true) + if (!result.ok) return + expect(result.warnings).toEqual([ + expect.objectContaining({ + severity: 'warning', + code: 'external-validation-required', + }), + ]) + }) + + it('fails closed for calculated expressions while preserving mixed warnings', () => { + const expressionQuestionnaire = { + ...questionnaire, + extension: [ + ...(questionnaire.extension ?? []), + targetConstraint('warning-constraint', 'warning'), + targetConstraint('blocking-constraint'), + ], + item: questionnaire.item.map((item, index) => + index === 1 ? + { + ...item, + extension: [ + { + url: questionnaireExtensions.calculatedExpression, + valueExpression: { + language: 'text/fhirpath', + expression: "'calculated'", + }, + }, + ], + } + : item, + ), + } + expect( + preflightQuestionnairePair(expressionQuestionnaire, response).ok, + ).toBe(false) + const mixed = preflightQuestionnairePair(expressionQuestionnaire, response) + if (mixed.ok) return + expect(new Set(mixed.issues.map(({ severity }) => severity))).toEqual( + new Set(['error', 'warning']), + ) + expect( + preflightQuestionnairePair(expressionQuestionnaire, { + ...response, + status: 'in-progress', + }).ok, + ).toBe(true) + }) + + it('does not treat initialExpression population as completed validation', () => { + const initialExpressionItem = { + linkId: 'populated', + text: 'Populated', + type: 'string', + extension: [ + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', + valueExpression: { + language: 'text/fhirpath', + expression: "'suggested'", + }, + }, + ], + } as const + expect(preflightItems([initialExpressionItem], []).ok).toBe(true) + expect( + preflightItems([{ ...initialExpressionItem, required: true }], []).ok, + ).toBe(false) + }) + + it.each([ + ['boolean', { valueBoolean: false }], + ['decimal', { valueDecimal: 1.25 }], + ['integer', { valueInteger: 2 }], + ['date', { valueDate: '2026-08-20' }], + ['dateTime', { valueDateTime: '2026-08-20T12:00:00-07:00' }], + ['time', { valueTime: '12:00:00' }], + ['string', { valueString: 'short answer' }], + ['text', { valueString: 'long answer' }], + ['url', { valueUri: 'https://example.org/answer' }], + ['choice', { valueCoding: { system: codingSystem, code: 'coded-answer' } }], + [ + 'open-choice', + { valueCoding: { system: codingSystem, code: 'coded-answer' } }, + ], + ['open-choice', { valueString: 'free text' }], + [ + 'attachment', + { + valueAttachment: { + contentType: 'text/plain', + data: 'YW5zd2Vy', + title: 'answer.txt', + }, + }, + ], + [ + 'quantity', + { + valueQuantity: { + value: 3, + system: 'http://unitsofmeasure.org', + code: 'kg', + }, + }, + ], + ] as const)( + 'accepts the supported %s answer representation', + (type, answer) => { + const result = preflightItems( + [{ linkId: 'answer', text: 'Answer', type }], + [{ linkId: 'answer', text: 'Answer', answer: [answer] }], + ) + expect(result.ok).toBe(true) + }, + ) + + it('admits free text for open-choice despite closed coded options', () => { + const result = preflightItems( + [ + { + linkId: 'answer', + text: 'Answer', + type: 'open-choice', + answerOption: [ + { valueCoding: { system: codingSystem, code: 'coded-answer' } }, + ], + }, + ], + [ + { + linkId: 'answer', + text: 'Answer', + answer: [{ valueString: 'free text' }], + }, + ], + ) + expect(result.ok).toBe(true) + }) + + it.each([ + ['<', 6], + ['<=', 5], + ['>', 4], + ['>=', 5], + ['!=', 4], + ] as const)('evaluates the %s enableWhen operator', (operator, expected) => { + const result = preflightItems( + [ + { linkId: 'source', text: 'Source', type: 'integer' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableWhen: [ + { question: 'source', operator, answerInteger: expected }, + ], + }, + ], + [ + { + linkId: 'source', + text: 'Source', + answer: [{ valueInteger: 5 }], + }, + { + linkId: 'dependent', + text: 'Dependent', + answer: [{ valueString: 'enabled' }], + }, + ], + ) + expect(result.ok).toBe(true) + }) + + it('compares Coding and Quantity enableWhen values semantically', () => { + const quantity = { + value: 3, + system: 'http://unitsofmeasure.org', + code: 'kg', + } + const result = preflightItems( + [ + { linkId: 'coded', text: 'Coded', type: 'choice' }, + { linkId: 'quantity', text: 'Quantity', type: 'quantity' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableBehavior: 'all', + enableWhen: [ + { + question: 'coded', + operator: '=', + answerCoding: { system: codingSystem, code: 'yes' }, + }, + { + question: 'quantity', + operator: '=', + answerQuantity: quantity, + }, + ], + }, + ], + [ + { + linkId: 'coded', + text: 'Coded', + answer: [{ valueCoding: { system: codingSystem, code: 'yes' } }], + }, + { + linkId: 'quantity', + text: 'Quantity', + answer: [{ valueQuantity: quantity }], + }, + { + linkId: 'dependent', + text: 'Dependent', + answer: [{ valueString: 'enabled' }], + }, + ], + ) + expect(result.ok).toBe(true) + }) + + it('supports exists and any enablement without treating absence as data', () => { + const result = preflightItems( + [ + { linkId: 'present', text: 'Present', type: 'boolean' }, + { linkId: 'absent', text: 'Absent', type: 'boolean' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableBehavior: 'any', + enableWhen: [ + { + question: 'present', + operator: 'exists', + answerBoolean: false, + }, + { + question: 'absent', + operator: 'exists', + answerBoolean: false, + }, + ], + }, + ], + [ + { + linkId: 'present', + text: 'Present', + answer: [{ valueBoolean: true }], + }, + { + linkId: 'dependent', + text: 'Dependent', + answer: [{ valueString: 'enabled' }], + }, + ], + ) + expect(result.ok).toBe(true) + }) + + it('fails closed when enableWhen cannot be evaluated', () => { + const invalidExists = preflightItems( + [ + { linkId: 'source', text: 'Source', type: 'string' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableWhen: [ + { + question: 'source', + operator: 'exists', + answerString: 'not-a-boolean', + }, + ], + }, + ], + [], + ) + expect(invalidExists.ok).toBe(false) + + const incomparable = preflightItems( + [ + { linkId: 'source', text: 'Source', type: 'string' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableWhen: [{ question: 'source', operator: '>', answerInteger: 2 }], + }, + ], + [ + { + linkId: 'source', + text: 'Source', + answer: [{ valueString: 'two' }], + }, + ], + ) + expect(incomparable.ok).toBe(false) + expect( + preflightItems( + [ + { linkId: 'source', text: 'Source', type: 'string' }, + { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableWhen: [ + { question: 'source', operator: '>', answerInteger: 2 }, + ], + }, + ], + [], + {}, + 'in-progress', + ).ok, + ).toBe(true) + }) + + it('requires external evaluation of completed FHIRPath enablement', () => { + const items = [ + { + linkId: 'conditional', + text: 'Conditional', + type: 'string', + extension: [ + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression', + valueExpression: { + language: 'text/fhirpath', + expression: "%resource.status = 'completed'", + }, + }, + ], + }, + ] as const + expect(preflightItems(items, []).ok).toBe(false) + expect(preflightItems(items, [], {}, 'in-progress').ok).toBe(true) + }) + + it('reports duplicate and invalid questionnaire topology', () => { + const duplicateAndUnknown = [ + { linkId: 'duplicate', text: 'First', type: 'string' }, + { + linkId: 'duplicate', + text: 'Second', + type: 'string', + enableWhen: [ + { question: 'missing', operator: '=', answerString: 'yes' }, + ], + }, + ] as const + const result = preflightItems(duplicateAndUnknown, []) + expect(result.ok).toBe(false) + if (result.ok) return + expect(new Set(result.issues.map(({ code }) => code))).toEqual( + new Set(['duplicate-identifier', 'invalid-reference']), + ) + }) + + it('reports response text, cardinality, grouping, and nesting failures', () => { + const result = preflightItems( + [ + { linkId: 'single', text: 'Single', type: 'string' }, + { + linkId: 'group', + text: 'Group', + type: 'group', + item: [{ linkId: 'child', text: 'Child', type: 'string' }], + }, + ], + [ + { + linkId: 'single', + text: 'Wrong text', + answer: [{ valueString: 'one' }, { valueString: 'two' }], + }, + { + linkId: 'group', + text: 'Group', + answer: [{ valueString: 'not admitted' }], + }, + { linkId: 'unknown', text: 'Unknown' }, + { linkId: 'single', text: 'Single' }, + ], + ) + expect(result.ok).toBe(false) + if (result.ok) return + expect(new Set(result.issues.map(({ code }) => code))).toEqual( + new Set([ + 'duplicate-identifier', + 'invalid-reference', + 'invalid-type', + 'out-of-range', + 'value-mismatch', + ]), + ) + }) + + it('rejects a resolved ValueSet nonmember and duplicate expansions', () => { + const valueSetCanonical = unwrap( + parseCanonical('https://example.org/fhir/ValueSet/mood|1.0.0'), + ) + const items = [ + { + linkId: 'mood', + text: 'Mood', + type: 'choice', + answerValueSet: valueSetCanonical, + }, + ] as const + const responseItems = [ + { + linkId: 'mood', + text: 'Mood', + answer: [ + { valueCoding: { system: codingSystem, code: 'not-admitted' } }, + ], + }, + ] as const + const expansion = { + canonical: valueSetCanonical, + concepts: [{ system: codingSystem, code: 'admitted' }], + } + const result = preflightItems(items, responseItems, { + valueSets: [expansion, expansion], + }) + expect(result.ok).toBe(false) + if (result.ok) return + expect(new Set(result.issues.map(({ code }) => code))).toEqual( + new Set(['duplicate-identifier', 'value-mismatch']), + ) + }) + + it('rejects entered-in-error answer data', () => { + expect( + preflightQuestionnairePair(questionnaire, { + ...response, + status: 'entered-in-error', + }).ok, + ).toBe(false) + }) + + it('preserves arbitrary textual answers under property testing', () => { + expect(() => + assert( + property(string({ minLength: 1 }), (value) => { + const candidate = unwrap( + buildQuestionnaireResponse(responseInput(value)), + ) + const result = preflightQuestionnairePair(questionnaire, candidate) + return result.ok + }), + ), + ).not.toThrow() + }) +}) diff --git a/packages/fhir/test/questionnaire-preflight.test.ts b/packages/fhir/test/questionnaire-preflight.test.ts new file mode 100644 index 00000000..6241f803 --- /dev/null +++ b/packages/fhir/test/questionnaire-preflight.test.ts @@ -0,0 +1,347 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + codingSystem, + questionnaire, + questionnaireInput, + response, + responseInput, + unwrap, + url, +} from './questionnaire-test-support.js' +import { + buildQuestionnaire, + buildQuestionnaireResponse, + parseAbsoluteUri, + parseCanonical, + preflightQuestionnairePair, +} from '../src/index.js' + +describe('Questionnaire pair preflight', () => { + it('accepts an exact, nested Questionnaire/Response pair', () => { + const result = preflightQuestionnairePair(questionnaire, response) + expect(result.ok).toBe(true) + }) + + it('requires the authoritative version and completion envelopes', () => { + const incompleteQuestionnaire = { + ...questionnaire, + version: 'not-semver', + extension: [], + } + const incompleteResponse = { + ...response, + questionnaire: `${url}|not-semver`, + extension: [], + } + + const result = preflightQuestionnairePair( + incompleteQuestionnaire, + incompleteResponse, + ) + expect(result.ok).toBe(false) + if (result.ok) return + expect(new Set(result.issues.map(({ code }) => code))).toEqual( + new Set(['invalid-code', 'invalid-uri', 'missing-required']), + ) + expect( + result.issues.some( + ({ path, message }) => + path.join('.') === 'questionnaire.extension' && + message.includes('version-algorithm'), + ), + ).toBe(true) + expect( + result.issues.some( + ({ path, message }) => + path.join('.') === 'response.extension' && + message.includes('completion-mode'), + ), + ).toBe(true) + }) + + it('rejects a mismatched instrument version', () => { + const result = preflightQuestionnairePair(questionnaire, { + ...response, + questionnaire: `${url}|2.0.1`, + }) + expect(result.ok).toBe(false) + if (result.ok) return + expect(result.issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ code: 'value-mismatch' }), + ]), + ) + }) + + it('requires the typed response subject to be admitted by Questionnaire.subjectType', () => { + const mismatched = buildQuestionnaireResponse({ + ...responseInput(), + subject: { type: 'Observation', reference: 'Observation/example' }, + }) + expect(mismatched.ok).toBe(true) + if (!mismatched.ok) return + const result = preflightQuestionnairePair(questionnaire, mismatched.value) + expect(result.ok).toBe(false) + if (!result.ok) { + expect(result.issues).toContainEqual( + expect.objectContaining({ + code: 'value-mismatch', + path: ['response', 'subject', 'type'], + }), + ) + } + }) + + it('resolves literal and contained subject types against Questionnaire subjectType', () => { + expect( + preflightQuestionnairePair(questionnaire, { + ...response, + subject: { reference: 'Patient/example/_history/2' }, + }).ok, + ).toBe(true) + expect( + preflightQuestionnairePair(questionnaire, { + ...response, + contained: [{ resourceType: 'Patient', id: 'contained-subject' }], + subject: { reference: '#contained-subject' }, + }).ok, + ).toBe(true) + expect( + preflightQuestionnairePair(questionnaire, { + ...response, + subject: { type: 'Observation', reference: 'Patient/example' }, + }).ok, + ).toBe(false) + }) + + it('rejects an answer with the wrong datatype', () => { + const raw = structuredClone(response) + const answer = raw.item?.[0]?.item?.[0]?.answer?.[0] + if (answer === undefined) throw new Error('Missing test answer.') + const invalid = { ...answer, valueString: 'yes' } + const result = preflightQuestionnairePair(questionnaire, { + ...raw, + item: [ + { + ...raw.item?.[0], + item: [ + { + ...raw.item?.[0]?.item?.[0], + answer: [invalid], + }, + ], + }, + ], + }) + expect(result.ok).toBe(false) + }) + + it('rejects question children placed outside their answer context', () => { + const hasPain = response.item?.[0]?.item?.[0] + const nested = hasPain?.answer?.[0]?.item + if (hasPain === undefined || nested === undefined) { + throw new Error('Missing nested test items.') + } + const invalidResponse = { + ...response, + item: [ + { + ...response.item?.[0], + item: [ + { + ...hasPain, + answer: [{ valueBoolean: true }], + item: nested, + }, + ], + }, + response.item?.[1], + ], + } + expect(preflightQuestionnairePair(questionnaire, invalidResponse).ok).toBe( + false, + ) + }) + + it('rejects answers outside inline options', () => { + const health = response.item?.[0] + const hasPain = health?.item?.[0] + const hasPainAnswer = hasPain?.answer?.[0] + const severity = hasPainAnswer?.item?.[0] + const severityAnswer = severity?.answer?.[0] + const coding = severityAnswer?.valueCoding + if ( + health === undefined || + hasPain === undefined || + hasPainAnswer === undefined || + severity === undefined || + severityAnswer === undefined || + coding === undefined + ) { + throw new Error('Missing coded test answer.') + } + const invalidResponse = { + ...response, + item: [ + { + ...health, + item: [ + { + ...hasPain, + answer: [ + { + ...hasPainAnswer, + item: [ + { + ...severity, + answer: [ + { + ...severityAnswer, + valueCoding: { ...coding, code: 'moderate' }, + }, + ], + }, + ], + }, + ], + }, + ], + }, + response.item?.[1], + ], + } + expect(preflightQuestionnairePair(questionnaire, invalidResponse).ok).toBe( + false, + ) + }) + + it('requires enabled items in completed and amended responses', () => { + const missing = { ...response, item: response.item?.slice(1) } + expect(preflightQuestionnairePair(questionnaire, missing).ok).toBe(false) + expect( + preflightQuestionnairePair(questionnaire, { + ...missing, + status: 'amended', + }).ok, + ).toBe(false) + expect( + preflightQuestionnairePair(questionnaire, { + ...missing, + status: 'in-progress', + }).ok, + ).toBe(true) + }) + + it('evaluates conditional enablement and rejects disabled answers', () => { + const health = response.item?.[0] + const hasPain = health?.item?.[0] + if (health === undefined || hasPain === undefined) { + throw new Error('Missing conditional test items.') + } + const noPain = { + ...response, + item: [ + { + ...health, + item: [ + { + ...hasPain, + answer: [{ valueBoolean: false }], + }, + ], + }, + response.item?.[1], + ], + } + expect(preflightQuestionnairePair(questionnaire, noPain).ok).toBe(true) + + const disabledAnswer = { + ...noPain, + item: [ + { + ...noPain.item[0], + item: [ + { + ...noPain.item[0]?.item?.[0], + answer: [ + { + valueBoolean: false, + item: [ + { + linkId: 'pain-severity', + text: 'How severe is the pain?', + answer: [ + { + valueCoding: { system: codingSystem, code: 'mild' }, + }, + ], + }, + ], + }, + ], + }, + ], + }, + noPain.item[1], + ], + } + expect(preflightQuestionnairePair(questionnaire, disabledAnswer).ok).toBe( + false, + ) + }) + + it('fails closed for unresolved ValueSets and accepts normalized expansions', () => { + const valueSetCanonical = unwrap( + parseCanonical('https://example.org/fhir/ValueSet/mood|1.0.0'), + ) + const moodSystem = unwrap( + parseAbsoluteUri('https://example.org/fhir/CodeSystem/mood'), + ) + const valueSetQuestionnaire = unwrap( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'mood', + text: 'Mood', + type: 'choice', + required: true, + answerValueSet: valueSetCanonical, + }, + ], + }), + ) + const valueSetResponse = unwrap( + buildQuestionnaireResponse({ + ...responseInput(), + items: [ + { + linkId: 'mood', + text: 'Mood', + answer: [{ valueCoding: { system: moodSystem, code: 'calm' } }], + }, + ], + }), + ) + expect( + preflightQuestionnairePair(valueSetQuestionnaire, valueSetResponse).ok, + ).toBe(false) + expect( + preflightQuestionnairePair(valueSetQuestionnaire, valueSetResponse, { + valueSets: [ + { + canonical: valueSetCanonical, + concepts: [{ system: moodSystem, code: 'calm' }], + }, + ], + }).ok, + ).toBe(true) + }) +}) diff --git a/packages/fhir/test/questionnaire-test-support.ts b/packages/fhir/test/questionnaire-test-support.ts new file mode 100644 index 00000000..d949657a --- /dev/null +++ b/packages/fhir/test/questionnaire-test-support.ts @@ -0,0 +1,238 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + buildQuestionnaire, + buildQuestionnaireResponse, + parseAbsoluteUri, + parseCanonical, + parseFhirInstant, + parsePatientReference, + parseSemVer, + preflightQuestionnairePair, + type AbsoluteUri, + type Canonical, + type FhirInstant, + type GroveQuestionnaire, + type GroveQuestionnaireResponse, + type QuestionnaireInput, + type QuestionnaireItemInput, + type QuestionnairePair, + type QuestionnairePreflightOptions, + type QuestionnaireResponseInput, + type Result, + type SemVer, +} from '../src/index.js' + +export const unwrap = (result: Result): T => { + if (!result.ok) { + throw new Error(result.issues.map((entry) => entry.message).join('\n')) + } + return result.value +} + +export const url: AbsoluteUri = unwrap( + parseAbsoluteUri('https://example.org/fhir/Questionnaire/pain'), +) +export const version: SemVer = unwrap(parseSemVer('2.0.0')) +export const questionnaireCanonical: Canonical = unwrap( + parseCanonical(`${url}|${version}`), +) +export const authored: FhirInstant = unwrap( + parseFhirInstant('2026-08-20T12:00:00-07:00'), +) +export const subject: string = unwrap(parsePatientReference('Patient/example')) +export const codingSystem: AbsoluteUri = unwrap( + parseAbsoluteUri('https://example.org/CodeSystem/pain'), +) + +type QuestionnaireExtensionInput = NonNullable< + QuestionnaireItemInput['extension'] +>[number] + +export const questionnaireExtensions: Readonly< + Record< + | 'calculatedExpression' + | 'hidden' + | 'itemWeight' + | 'maxDecimalPlaces' + | 'maxOccurs' + | 'maxQuantity' + | 'maxSize' + | 'maxValue' + | 'mimeType' + | 'minLength' + | 'minOccurs' + | 'minQuantity' + | 'minValue' + | 'optionExclusive' + | 'targetConstraint' + | 'unitOption' + | 'unitValueSet' + | 'variable', + string + > +> = { + calculatedExpression: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression', + maxDecimalPlaces: 'http://hl7.org/fhir/StructureDefinition/maxDecimalPlaces', + maxOccurs: 'http://hl7.org/fhir/StructureDefinition/questionnaire-maxOccurs', + maxQuantity: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-maxQuantity', + maxSize: 'http://hl7.org/fhir/StructureDefinition/maxSize', + maxValue: 'http://hl7.org/fhir/StructureDefinition/maxValue', + hidden: 'http://hl7.org/fhir/StructureDefinition/questionnaire-hidden', + itemWeight: 'http://hl7.org/fhir/StructureDefinition/itemWeight', + mimeType: 'http://hl7.org/fhir/StructureDefinition/mimeType', + minLength: 'http://hl7.org/fhir/StructureDefinition/minLength', + minOccurs: 'http://hl7.org/fhir/StructureDefinition/questionnaire-minOccurs', + minQuantity: + 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-minQuantity', + minValue: 'http://hl7.org/fhir/StructureDefinition/minValue', + optionExclusive: + 'http://hl7.org/fhir/StructureDefinition/questionnaire-optionExclusive', + targetConstraint: 'http://hl7.org/fhir/StructureDefinition/targetConstraint', + unitOption: + 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption', + unitValueSet: + 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitValueSet', + variable: 'http://hl7.org/fhir/StructureDefinition/variable', +} as const + +export const targetConstraint = ( + key: string, + severity: 'error' | 'warning' = 'error', +): QuestionnaireExtensionInput => ({ + url: questionnaireExtensions.targetConstraint, + extension: [ + { url: 'key', valueId: key }, + { url: 'severity', valueCode: severity }, + { url: 'human', valueString: `Satisfy ${key}.` }, + { + url: 'expression', + valueExpression: { + language: 'text/fhirpath', + expression: 'true', + }, + }, + ], +}) + +export const questionnaireInput: QuestionnaireInput = { + url, + version, + name: 'PainQuestionnaire', + title: 'Pain Questionnaire', + status: 'active', + subjectTypes: ['Patient'], + items: [ + { + linkId: 'health', + text: 'Health', + type: 'group', + required: true, + item: [ + { + linkId: 'has-pain', + text: 'Are you in pain?', + type: 'boolean', + required: true, + item: [ + { + linkId: 'pain-severity', + text: 'How severe is the pain?', + type: 'choice', + required: true, + enableWhen: [ + { + question: 'has-pain', + operator: '=', + answerBoolean: true, + }, + ], + answerOption: [ + { valueCoding: { system: codingSystem, code: 'mild' } }, + { valueCoding: { system: codingSystem, code: 'severe' } }, + ], + }, + ], + }, + ], + }, + { + linkId: 'notes', + text: 'Additional notes', + type: 'text', + }, + ], +} + +export const responseInput = ( + note = 'Rest helps.', +): QuestionnaireResponseInput => ({ + questionnaire: questionnaireCanonical, + identifier: { + system: unwrap(parseAbsoluteUri('https://example.org/submissions')), + value: 'submission-1', + }, + status: 'completed', + subject: { type: 'Patient', reference: subject }, + authored, + items: [ + { + linkId: 'health', + text: 'Health', + item: [ + { + linkId: 'has-pain', + text: 'Are you in pain?', + answer: [ + { + valueBoolean: true, + item: [ + { + linkId: 'pain-severity', + text: 'How severe is the pain?', + answer: [ + { + valueCoding: { system: codingSystem, code: 'mild' }, + }, + ], + }, + ], + }, + ], + }, + ], + }, + { + linkId: 'notes', + text: 'Additional notes', + answer: [{ valueString: note }], + }, + ], +}) + +export const questionnaire: GroveQuestionnaire = unwrap( + buildQuestionnaire(questionnaireInput), +) +export const response: GroveQuestionnaireResponse = unwrap( + buildQuestionnaireResponse(responseInput()), +) + +export const preflightItems = ( + questionnaireItems: ReadonlyArray>>, + responseItems: ReadonlyArray>>, + options: QuestionnairePreflightOptions = {}, + status: 'amended' | 'completed' | 'in-progress' = 'completed', +): Result => + preflightQuestionnairePair( + { ...questionnaire, item: questionnaireItems }, + { ...response, status, item: responseItems }, + options, + ) diff --git a/packages/fhir/test/questionnaire-types.test.ts b/packages/fhir/test/questionnaire-types.test.ts new file mode 100644 index 00000000..9d1851e3 --- /dev/null +++ b/packages/fhir/test/questionnaire-types.test.ts @@ -0,0 +1,42 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + parseSemVer, + type QuestionnaireResponseAnswerInput, +} from '../src/index.js' + +describe('Questionnaire type contract', () => { + it('requires exactly one answer value at compile time', () => { + const answer: QuestionnaireResponseAnswerInput = { valueBoolean: true } + expect(answer.valueBoolean).toBe(true) + + // @ts-expect-error Multiple value[x] members are not a valid answer. + const invalid: QuestionnaireResponseAnswerInput = { + valueBoolean: true, + valueString: 'true', + } + expect(invalid).toBeDefined() + }) + + it.each([ + '0.0.0', + '1.2.3-alpha.1', + '1.2.3+build.0042', + '1.2.3-alpha.1+build.0042', + ])('accepts SemVer %s', (candidate) => { + expect(parseSemVer(candidate).ok).toBe(true) + }) + + it.each(['1.2', '01.2.3', '1.02.3', '1.2.3-01', '1.2.3+'])( + 'rejects non-SemVer %s', + (candidate) => { + expect(parseSemVer(candidate).ok).toBe(false) + }, + ) +}) diff --git a/packages/fhir/test/questionnaire.test.ts b/packages/fhir/test/questionnaire.test.ts deleted file mode 100644 index e8b01d42..00000000 --- a/packages/fhir/test/questionnaire.test.ts +++ /dev/null @@ -1,2937 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { expectTypeOf } from 'expect-type' -import { assert, property, string } from 'fast-check' -import { - buildQuestionnaire, - buildQuestionnaireResponse, - parseAbsoluteUri, - parseCanonical, - parseFhirId, - parseFhirInstant, - parsePatientReference, - parseQuestionnaire, - parseQuestionnaireResponse, - parseSemVer, - preflightQuestionnairePair, - type GroveQuestionnaire, - type QuestionnaireInput, - type QuestionnairePreflightOptions, - type QuestionnaireResponseAnswerInput, - type QuestionnaireResponseInput, - type Result, -} from '../src/index.js' -import { - compareR4Temporal, - isR4Date, - isR4DateTime, - isR4Time, -} from '../src/questionnaire/temporal.js' - -/* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 canonicals are normative HTTP URIs. */ - -const unwrap = (result: Result): T => { - if (!result.ok) { - throw new Error(result.issues.map((entry) => entry.message).join('\n')) - } - return result.value -} - -const url = unwrap( - parseAbsoluteUri('https://example.org/fhir/Questionnaire/pain'), -) -const version = unwrap(parseSemVer('2.0.0')) -const questionnaireCanonical = unwrap(parseCanonical(`${url}|${version}`)) -const authored = unwrap(parseFhirInstant('2026-08-20T12:00:00-07:00')) -const subject = unwrap(parsePatientReference('Patient/example')) -const codingSystem = unwrap( - parseAbsoluteUri('https://example.org/CodeSystem/pain'), -) - -const questionnaireExtensions = { - calculatedExpression: - 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression', - maxDecimalPlaces: 'http://hl7.org/fhir/StructureDefinition/maxDecimalPlaces', - maxOccurs: 'http://hl7.org/fhir/StructureDefinition/questionnaire-maxOccurs', - maxQuantity: - 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-maxQuantity', - maxSize: 'http://hl7.org/fhir/StructureDefinition/maxSize', - maxValue: 'http://hl7.org/fhir/StructureDefinition/maxValue', - hidden: 'http://hl7.org/fhir/StructureDefinition/questionnaire-hidden', - itemWeight: 'http://hl7.org/fhir/StructureDefinition/itemWeight', - mimeType: 'http://hl7.org/fhir/StructureDefinition/mimeType', - minLength: 'http://hl7.org/fhir/StructureDefinition/minLength', - minOccurs: 'http://hl7.org/fhir/StructureDefinition/questionnaire-minOccurs', - minQuantity: - 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-minQuantity', - minValue: 'http://hl7.org/fhir/StructureDefinition/minValue', - optionExclusive: - 'http://hl7.org/fhir/StructureDefinition/questionnaire-optionExclusive', - targetConstraint: 'http://hl7.org/fhir/StructureDefinition/targetConstraint', - unitOption: - 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption', - unitValueSet: - 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitValueSet', - variable: 'http://hl7.org/fhir/StructureDefinition/variable', -} as const - -const targetConstraint = ( - key: string, - severity: 'error' | 'warning' = 'error', -) => ({ - url: questionnaireExtensions.targetConstraint, - extension: [ - { url: 'key', valueId: key }, - { url: 'severity', valueCode: severity }, - { url: 'human', valueString: `Satisfy ${key}.` }, - { - url: 'expression', - valueExpression: { - language: 'text/fhirpath', - expression: 'true', - }, - }, - ], -}) - -const questionnaireInput = { - url, - version, - name: 'PainQuestionnaire', - title: 'Pain Questionnaire', - status: 'active', - subjectTypes: ['Patient'], - items: [ - { - linkId: 'health', - text: 'Health', - type: 'group', - required: true, - item: [ - { - linkId: 'has-pain', - text: 'Are you in pain?', - type: 'boolean', - required: true, - item: [ - { - linkId: 'pain-severity', - text: 'How severe is the pain?', - type: 'choice', - required: true, - enableWhen: [ - { - question: 'has-pain', - operator: '=', - answerBoolean: true, - }, - ], - answerOption: [ - { valueCoding: { system: codingSystem, code: 'mild' } }, - { valueCoding: { system: codingSystem, code: 'severe' } }, - ], - }, - ], - }, - ], - }, - { - linkId: 'notes', - text: 'Additional notes', - type: 'text', - }, - ], -} as const satisfies QuestionnaireInput - -const responseInput = (note = 'Rest helps.') => - ({ - questionnaire: questionnaireCanonical, - identifier: { - system: unwrap(parseAbsoluteUri('https://example.org/submissions')), - value: 'submission-1', - }, - status: 'completed', - subject, - authored, - items: [ - { - linkId: 'health', - text: 'Health', - item: [ - { - linkId: 'has-pain', - text: 'Are you in pain?', - answer: [ - { - valueBoolean: true, - item: [ - { - linkId: 'pain-severity', - text: 'How severe is the pain?', - answer: [ - { - valueCoding: { system: codingSystem, code: 'mild' }, - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - linkId: 'notes', - text: 'Additional notes', - answer: [{ valueString: note }], - }, - ], - }) as const satisfies QuestionnaireResponseInput - -const questionnaire = unwrap(buildQuestionnaire(questionnaireInput)) -const response = unwrap(buildQuestionnaireResponse(responseInput())) - -const preflightItems = ( - questionnaireItems: ReadonlyArray>>, - responseItems: ReadonlyArray>>, - options: QuestionnairePreflightOptions = {}, - status: 'amended' | 'completed' | 'in-progress' = 'completed', -) => - preflightQuestionnairePair( - { ...questionnaire, item: questionnaireItems }, - { ...response, status, item: responseItems }, - options, - ) - -describe('Questionnaire R4 builders', () => { - it('stamps the exact Grove profiles and owned extensions', () => { - expect(questionnaire.meta?.profile).toEqual([ - 'https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire', - ]) - expect(questionnaire.extension).toEqual([ - { - url: 'http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm', - valueCoding: { - system: 'http://hl7.org/fhir/version-algorithm', - code: 'semver', - }, - }, - ]) - expect(response.meta?.profile).toEqual([ - 'https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response', - ]) - expect(response.extension).toEqual([ - { - url: 'http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode', - valueCodeableConcept: { - coding: [ - { - system: - 'http://terminology.hl7.org/CodeSystem/v3-ParticipationMode', - code: 'ELECTRONIC', - }, - ], - }, - }, - ]) - }) - - it('returns immutable, plain R4 JSON', () => { - expect(Object.isFrozen(questionnaire)).toBe(true) - expect(Object.isFrozen(questionnaire.item[0]?.item)).toBe(true) - expect(Object.isFrozen(response)).toBe(true) - expect(JSON.parse(JSON.stringify(questionnaire))).toEqual(questionnaire) - expectTypeOf(questionnaire).toExtend() - }) - - it('preserves admitted FHIR primitive metadata at every supported level', () => { - const input = { - ...questionnaire, - _title: { - extension: [ - { - url: 'https://example.org/fhir/StructureDefinition/localized-value', - valueString: 'en-US', - }, - ], - }, - item: questionnaire.item.map((item, index) => - index === 0 ? - { - ...item, - _text: { id: 'health-text-metadata' }, - } - : item, - ), - } - - const parsed = parseQuestionnaire(input) - expect(parsed.ok).toBe(true) - if (!parsed.ok) return - expect(parsed.value._title).toEqual(input._title) - expect(parsed.value.item[0]?._text).toEqual({ - id: 'health-text-metadata', - }) - }) - - it('rejects unknown fields and reference questions instead of stripping them', () => { - expect( - parseQuestionnaire({ ...questionnaire, vendorPayload: true }).ok, - ).toBe(false) - const referenceQuestionnaire = { - ...questionnaire, - item: [{ linkId: 'person', text: 'Person', type: 'reference' }], - } - expect(parseQuestionnaire(referenceQuestionnaire).ok).toBe(false) - }) - - it('rejects caller attempts to replace owned profile extensions', () => { - const result = buildQuestionnaire({ - ...questionnaireInput, - extensions: [ - { - url: 'http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm', - valueCoding: { - system: 'http://hl7.org/fhir/version-algorithm', - code: 'semver', - }, - }, - ], - }) - expect(result.ok).toBe(false) - }) - - it('rejects duplicated linkIds before construction', () => { - const result = buildQuestionnaire({ - ...questionnaireInput, - items: [questionnaireInput.items[0], questionnaireInput.items[0]], - }) - expect(result.ok).toBe(false) - if (result.ok) return - expect(result.issues).toEqual( - expect.arrayContaining([ - expect.objectContaining({ code: 'duplicate-identifier' }), - ]), - ) - }) - - it('returns Result failures for malformed JavaScript inputs without throwing', () => { - const malformedQuestionnaires = [ - undefined, - null, - {}, - { ...questionnaireInput, unknownRootField: true }, - { - ...questionnaireInput, - items: [ - { - linkId: 'strict-item', - text: 'Strict item', - type: 'string', - unknownItemField: true, - }, - ], - }, - ] - for (const candidate of malformedQuestionnaires) { - const invoke = () => buildQuestionnaire(candidate as QuestionnaireInput) - expect(invoke).not.toThrow() - expect(invoke().ok).toBe(false) - } - - const malformedResponses = [ - undefined, - null, - {}, - { ...responseInput(), unknownRootField: true }, - { - ...responseInput(), - items: [ - { - linkId: 'strict-item', - text: 'Strict item', - answer: [{ valueString: 'answer', unknownAnswerField: true }], - }, - ], - }, - ] - for (const candidate of malformedResponses) { - const invoke = () => - buildQuestionnaireResponse(candidate as QuestionnaireResponseInput) - expect(invoke).not.toThrow() - expect(invoke().ok).toBe(false) - } - }) - - it.each([ - [ - 'an empty group', - { linkId: 'empty-group', text: 'Empty group', type: 'group' }, - ], - [ - 'required on a display item', - { - linkId: 'required-display', - text: 'Display', - type: 'display', - required: false, - }, - ], - [ - 'children on a display item', - { - linkId: 'parent-display', - text: 'Display', - type: 'display', - item: [{ linkId: 'child', text: 'Child', type: 'string' }], - }, - ], - [ - 'answerOption together with initial', - { - linkId: 'choice', - text: 'Choice', - type: 'choice', - answerOption: [ - { valueCoding: { system: codingSystem, code: 'option' } }, - ], - initial: [{ valueCoding: { system: codingSystem, code: 'option' } }], - }, - ], - ] as const)('rejects %s', (_name, item) => { - const result = buildQuestionnaire({ - ...questionnaireInput, - items: [item], - } as unknown as QuestionnaireInput) - expect(result.ok).toBe(false) - }) - - it('enforces base R4 Questionnaire and response item invariants when parsing', () => { - expect( - parseQuestionnaire({ - ...questionnaire, - item: [{ linkId: 'empty-group', type: 'group' }], - }).ok, - ).toBe(false) - expect( - parseQuestionnaire({ - ...questionnaire, - item: [ - { - linkId: 'display', - text: 'Display', - type: 'display', - required: false, - }, - ], - }).ok, - ).toBe(false) - expect( - parseQuestionnaire({ - ...questionnaire, - item: [ - { - linkId: 'choice', - text: 'Choice', - type: 'choice', - answerOption: [{ valueString: 'option' }], - initial: [{ valueString: 'option' }], - }, - ], - }).ok, - ).toBe(false) - - const mixedItem = { - linkId: 'mixed', - text: 'Mixed', - answer: [{ valueString: 'answer' }], - item: [{ linkId: 'child', text: 'Child' }], - } - expect( - parseQuestionnaireResponse({ ...response, item: [mixedItem] }).ok, - ).toBe(false) - expect( - buildQuestionnaireResponse({ - ...responseInput(), - items: [mixedItem], - }).ok, - ).toBe(false) - }) - - it('requires the exact Grove profiles at every public parse boundary', () => { - const unprofiledQuestionnaire = { ...questionnaire, meta: undefined } - const unprofiledResponse = { ...response, meta: undefined } - expect(parseQuestionnaire(unprofiledQuestionnaire).ok).toBe(false) - expect(parseQuestionnaireResponse(unprofiledResponse).ok).toBe(false) - expect( - parseQuestionnaire({ - ...questionnaire, - meta: { - profile: [ - ...(questionnaire.meta?.profile ?? []), - 'https://example.org/fhir/StructureDefinition/extra', - ], - }, - }).ok, - ).toBe(false) - expect( - parseQuestionnaireResponse({ - ...response, - meta: { - profile: [ - ...(response.meta?.profile ?? []), - 'https://example.org/fhir/StructureDefinition/extra', - ], - }, - }).ok, - ).toBe(false) - expect( - preflightQuestionnairePair(unprofiledQuestionnaire, response).ok, - ).toBe(false) - expect( - preflightQuestionnairePair(questionnaire, unprofiledResponse).ok, - ).toBe(false) - }) - - it.each([ - [ - 'code on display', - { - linkId: 'display-code', - text: 'Display', - type: 'display', - code: [{ system: codingSystem, code: 'display' }], - }, - ], - [ - 'readOnly on display', - { - linkId: 'display-read-only', - text: 'Display', - type: 'display', - readOnly: false, - }, - ], - [ - 'answerOption on boolean', - { - linkId: 'boolean-options', - text: 'Boolean', - type: 'boolean', - answerOption: [{ valueString: 'not-admitted' }], - }, - ], - [ - 'answerValueSet on attachment', - { - linkId: 'attachment-value-set', - text: 'Attachment', - type: 'attachment', - answerValueSet: 'https://example.org/fhir/ValueSet/not-admitted', - }, - ], - [ - 'a non-boolean exists operand', - { - linkId: 'exists', - text: 'Exists', - type: 'string', - enableWhen: [ - { - question: 'exists', - operator: 'exists', - answerString: 'true', - }, - ], - }, - ], - [ - 'initial on group', - { - linkId: 'group-initial', - text: 'Group', - type: 'group', - initial: [{ valueString: 'not-admitted' }], - item: [{ linkId: 'child', text: 'Child', type: 'string' }], - }, - ], - [ - 'multiple initial values without repeats', - { - linkId: 'multiple-initial', - text: 'Multiple initial', - type: 'string', - initial: [{ valueString: 'one' }, { valueString: 'two' }], - }, - ], - [ - 'a linkId longer than 255 code points', - { - linkId: '😀'.repeat(256), - text: 'Long linkId', - type: 'string', - }, - ], - ] as const)('enforces %s', (_name, item) => { - expect( - buildQuestionnaire({ - ...questionnaireInput, - items: [item], - } as unknown as QuestionnaireInput).ok, - ).toBe(false) - }) - - it('requires authored initial and answerOption values to match item type', () => { - for (const item of [ - { - linkId: 'integer-initial', - text: 'Integer initial', - type: 'integer', - initial: [{ valueString: '1' }], - }, - { - linkId: 'date-initial', - text: 'Date initial', - type: 'date', - initial: [{ valueTime: '12:00:00' }], - }, - { - linkId: 'choice-option', - text: 'Choice option', - type: 'choice', - answerOption: [{ valueString: 'uncoded' }], - }, - { - linkId: 'integer-option', - text: 'Integer option', - type: 'integer', - answerOption: [{ valueDate: '2026-08-20' }], - }, - ] as const) { - expect( - buildQuestionnaire({ - ...questionnaireInput, - items: [item], - } as unknown as QuestionnaireInput).ok, - ).toBe(false) - } - - expect( - buildQuestionnaire({ - ...questionnaireInput, - items: [ - { - linkId: 'open-choice-option', - text: 'Open choice option', - type: 'open-choice', - answerOption: [{ valueString: 'uncoded' }], - }, - { - linkId: 'integer-initial', - text: 'Integer initial', - type: 'integer', - initial: [{ valueInteger: 1 }], - }, - ], - }).ok, - ).toBe(true) - }) - - it('limits answerValueSet to choice and open-choice items', () => { - for (const type of ['integer', 'date', 'string'] as const) { - expect( - buildQuestionnaire({ - ...questionnaireInput, - items: [ - { - linkId: `${type}-value-set`, - text: `${type} value set`, - type, - answerValueSet: 'https://example.org/fhir/ValueSet/options', - }, - ], - }).ok, - ).toBe(false) - } - - for (const type of ['choice', 'open-choice'] as const) { - expect( - buildQuestionnaire({ - ...questionnaireInput, - items: [ - { - linkId: `${type}-value-set`, - text: `${type} value set`, - type, - answerValueSet: 'https://example.org/fhir/ValueSet/options', - }, - ], - }).ok, - ).toBe(true) - } - - expect( - parseQuestionnaire({ - ...questionnaire, - item: [ - { - linkId: 'string-value-set', - text: 'String value set', - type: 'string', - answerValueSet: 'https://example.org/fhir/ValueSet/options', - }, - ], - }).ok, - ).toBe(false) - }) - - it('rejects valueless Extension objects throughout builder inputs', () => { - expect( - buildQuestionnaire({ - ...questionnaireInput, - extensions: [ - { url: 'https://example.org/fhir/StructureDefinition/empty' }, - ], - }).ok, - ).toBe(false) - expect( - buildQuestionnaire({ - ...questionnaireInput, - items: [ - { - linkId: 'empty-extension', - text: 'Empty extension', - type: 'string', - extension: [ - { url: 'https://example.org/fhir/StructureDefinition/empty' }, - ], - }, - ], - }).ok, - ).toBe(false) - expect( - buildQuestionnaireResponse({ - ...responseInput(), - extensions: [ - { url: 'https://example.org/fhir/StructureDefinition/empty' }, - ], - }).ok, - ).toBe(false) - }) - - it('enforces R4 ResourceType subject codes and signed integer bounds', () => { - expect( - buildQuestionnaire({ - ...questionnaireInput, - subjectTypes: ['Banana'], - }).ok, - ).toBe(false) - expect( - parseQuestionnaire({ - ...questionnaire, - subjectType: ['Banana'], - }).ok, - ).toBe(false) - - for (const valueInteger of [2_147_483_648, -2_147_483_649]) { - expect( - preflightItems( - [{ linkId: 'integer', text: 'Integer', type: 'integer' }], - [ - { - linkId: 'integer', - text: 'Integer', - answer: [{ valueInteger }], - }, - ], - ).ok, - ).toBe(false) - } - expect( - buildQuestionnaire({ - ...questionnaireInput, - items: [ - { - linkId: 'integer-option', - text: 'Integer option', - type: 'integer', - answerOption: [{ valueInteger: 2_147_483_648 }], - }, - ], - }).ok, - ).toBe(false) - expect( - buildQuestionnaire({ - ...questionnaireInput, - items: [ - { - linkId: 'long-text', - text: 'Long text', - type: 'string', - maxLength: 2_147_483_648, - }, - ], - }).ok, - ).toBe(false) - }) - - it('enforces hidden and itemWeight slice cardinality and value types', () => { - expect( - buildQuestionnaire({ - ...questionnaireInput, - items: [ - { - linkId: 'duplicate-hidden', - text: 'Duplicate hidden', - type: 'string', - extension: [ - { url: questionnaireExtensions.hidden, valueBoolean: true }, - { url: questionnaireExtensions.hidden, valueBoolean: false }, - ], - }, - ], - }).ok, - ).toBe(false) - expect( - buildQuestionnaire({ - ...questionnaireInput, - items: [ - { - linkId: 'wrong-hidden', - text: 'Wrong hidden', - type: 'string', - extension: [ - { url: questionnaireExtensions.hidden, valueString: 'true' }, - ], - }, - ], - }).ok, - ).toBe(false) - - expect( - buildQuestionnaireResponse({ - ...responseInput(), - items: [ - { - linkId: 'answer-weight', - text: 'Answer weight', - answer: [ - { - extension: [ - { - url: questionnaireExtensions.itemWeight, - valueInteger: 1, - }, - ], - valueCoding: { system: codingSystem, code: 'answer-weight' }, - }, - ], - }, - ], - }).ok, - ).toBe(false) - expect( - buildQuestionnaireResponse({ - ...responseInput(), - items: [ - { - linkId: 'duplicate-answer-weight', - text: 'Duplicate answer weight', - answer: [ - { - extension: [ - { - url: questionnaireExtensions.itemWeight, - valueDecimal: 1, - }, - { - url: questionnaireExtensions.itemWeight, - valueDecimal: 2, - }, - ], - valueCoding: { - system: codingSystem, - code: 'duplicate-answer-weight', - }, - }, - ], - }, - ], - }).ok, - ).toBe(false) - - const weightedCoding = { - system: codingSystem, - code: 'weighted', - extension: [{ url: questionnaireExtensions.itemWeight, valueInteger: 1 }], - } - const duplicateWeightedCoding = { - system: codingSystem, - code: 'duplicate-weight', - extension: [ - { url: questionnaireExtensions.itemWeight, valueDecimal: 1 }, - { url: questionnaireExtensions.itemWeight, valueDecimal: 2 }, - ], - } - expect( - buildQuestionnaire({ - ...questionnaireInput, - items: [ - { - linkId: 'wrong-weight', - text: 'Wrong weight', - type: 'choice', - answerOption: [{ valueCoding: weightedCoding }], - }, - ], - }).ok, - ).toBe(false) - expect( - buildQuestionnaireResponse({ - ...responseInput(), - items: [ - { - linkId: 'weighted', - text: 'Weighted', - answer: [{ valueCoding: weightedCoding }], - }, - ], - }).ok, - ).toBe(false) - expect( - buildQuestionnaireResponse({ - ...responseInput(), - items: [ - { - linkId: 'duplicate-weight', - text: 'Duplicate weight', - answer: [{ valueCoding: duplicateWeightedCoding }], - }, - ], - }).ok, - ).toBe(false) - }) - - it('rejects empty answer value elements', () => { - for (const [type, answer] of [ - ['string', { valueString: '' }], - ['url', { valueUri: '' }], - ['choice', { valueCoding: {} }], - ['quantity', { valueQuantity: {} }], - ['attachment', { valueAttachment: {} }], - ] as const) { - expect( - preflightItems( - [{ linkId: 'empty', text: 'Empty', type }], - [{ linkId: 'empty', text: 'Empty', answer: [answer] }], - ).ok, - ).toBe(false) - } - }) - - it('enforces answered text and qrs-2 outside pair preflight', () => { - const duplicateAnswers = [ - { - linkId: 'duplicate', - text: 'Duplicate', - answer: [{ valueString: 'one' }], - }, - { - linkId: 'duplicate', - text: 'Duplicate', - answer: [{ valueString: 'two' }], - }, - ] - expect( - buildQuestionnaireResponse({ - ...responseInput(), - items: duplicateAnswers, - }).ok, - ).toBe(false) - expect( - parseQuestionnaireResponse({ - ...response, - item: duplicateAnswers, - }).ok, - ).toBe(false) - - expect( - parseQuestionnaireResponse({ - ...response, - item: [ - { - linkId: 'answered-without-text', - answer: [{ valueString: 'answer' }], - }, - ], - }).ok, - ).toBe(false) - }) - - it('validates authored and answer temporal values as R4 primitives', () => { - for (const invalidAuthored of [ - '2026-08-20', - '2026-08-20T12:00:00', - '2026-02-29T12:00:00Z', - '2026-08-20T24:00:00Z', - ]) { - expect( - parseQuestionnaireResponse({ - ...response, - authored: invalidAuthored, - }).ok, - ).toBe(false) - } - - for (const [type, answer] of [ - ['date', { valueDate: '2026-02-29' }], - ['dateTime', { valueDateTime: '2026-08-20T12:00:00' }], - ['dateTime', { valueDateTime: '2026-13' }], - ['time', { valueTime: '24:00:00' }], - ] as const) { - expect( - preflightItems( - [{ linkId: 'temporal', text: 'Temporal', type }], - [ - { - linkId: 'temporal', - text: 'Temporal', - answer: [answer], - }, - ], - ).ok, - ).toBe(false) - } - - expect( - preflightItems( - [ - { linkId: 'partial-date', text: 'Date', type: 'date' }, - { - linkId: 'partial-date-time', - text: 'Date time', - type: 'dateTime', - }, - ], - [ - { - linkId: 'partial-date', - text: 'Date', - answer: [{ valueDate: '2026-08' }], - }, - { - linkId: 'partial-date-time', - text: 'Date time', - answer: [{ valueDateTime: '2026' }], - }, - ], - ).ok, - ).toBe(true) - }) - - it('covers R4 temporal validation and comparison boundaries', () => { - for (const candidate of [ - '0000', - '2026-00', - '2026-13', - '2026-02-29', - '2024-04-31', - 'not-a-date', - ]) { - expect(isR4Date(candidate)).toBe(false) - } - for (const candidate of [ - '2026', - '2026-08', - '2024-02-29', - '2026-04-30', - '2026-01-31', - ]) { - expect(isR4Date(candidate)).toBe(true) - } - expect(isR4Date(undefined)).toBe(false) - - for (const candidate of ['24:00:00', '12:60:00', '12:00:61', '12:00']) { - expect(isR4Time(candidate)).toBe(false) - } - expect(isR4Time(null)).toBe(false) - expect(isR4Time('00:00:00')).toBe(true) - expect(isR4Time('23:59:60.123')).toBe(true) - - expect(isR4DateTime(null)).toBe(false) - expect(isR4DateTime('2026-08')).toBe(true) - expect(isR4DateTime('2026-02-29T12:00:00Z')).toBe(false) - expect(isR4DateTime('2026-08-20T12:00:00+14:01')).toBe(false) - expect(isR4DateTime('2026-08-20T12:00:00Z')).toBe(true) - - expect(compareR4Temporal('bad', '2026', 'date')).toBeUndefined() - expect(compareR4Temporal('2026', 'bad', 'date')).toBeUndefined() - expect(compareR4Temporal('2026', '2027', 'date')).toBe(-1) - expect(compareR4Temporal('2027', '2026', 'date')).toBe(1) - expect(compareR4Temporal('2026', '2026', 'date')).toBe(0) - expect(compareR4Temporal('2026-02', '2026-03', 'date')).toBe(-1) - expect(compareR4Temporal('2026-03', '2026-02', 'date')).toBe(1) - expect(compareR4Temporal('2026-02-02', '2026-02-01', 'date')).toBe(1) - expect(compareR4Temporal('2026-02-01', '2026-02-01', 'date')).toBe(0) - expect(compareR4Temporal('2026', '2026-01', 'date')).toBeUndefined() - - expect(compareR4Temporal('bad', '12:00:00', 'time')).toBeUndefined() - expect(compareR4Temporal('12:00:00', 'bad', 'time')).toBeUndefined() - expect(compareR4Temporal('11:00:00', '12:00:00', 'time')).toBe(-1) - expect(compareR4Temporal('12:01:00', '12:00:00', 'time')).toBe(1) - expect(compareR4Temporal('12:00:01', '12:00:00', 'time')).toBe(1) - expect(compareR4Temporal('12:00:00.01', '12:00:00.1', 'time')).toBe(-1) - expect(compareR4Temporal('12:00:00.10', '12:00:00.1', 'time')).toBe(0) - - const instant = '2026-08-20T12:00:00Z' - expect(compareR4Temporal('bad', instant, 'dateTime')).toBeUndefined() - expect(compareR4Temporal(instant, 'bad', 'dateTime')).toBeUndefined() - expect(compareR4Temporal('2026-08', instant, 'dateTime')).toBeUndefined() - expect(compareR4Temporal(instant, '2026-08', 'dateTime')).toBeUndefined() - expect( - compareR4Temporal( - '2026-08-20T12:00:00+02:00', - '2026-08-20T10:30:00Z', - 'dateTime', - ), - ).toBe(-1) - expect(compareR4Temporal(instant, instant, 'dateTime')).toBe(0) - expect(compareR4Temporal('2026-08', '2026-09', 'dateTime')).toBe(-1) - expect(compareR4Temporal('2026', '2026-08', 'dateTime')).toBeUndefined() - }) - - it('validates Questionnaire lifecycle dates and effectivePeriod locally', () => { - expect( - parseQuestionnaire({ ...questionnaire, date: '2026-02-29' }).ok, - ).toBe(false) - expect( - parseQuestionnaire({ - ...questionnaire, - approvalDate: '2026-02-29', - }).ok, - ).toBe(false) - expect( - parseQuestionnaire({ - ...questionnaire, - lastReviewDate: '2026-08-20T12:00:00Z', - }).ok, - ).toBe(false) - expect( - parseQuestionnaire({ - ...questionnaire, - effectivePeriod: { start: 'not-a-date-time' }, - }).ok, - ).toBe(false) - expect( - parseQuestionnaire({ - ...questionnaire, - effectivePeriod: { - start: '2026-08-20T12:00:00Z', - end: '2026-08-20T13:00:00+02:00', - }, - }).ok, - ).toBe(false) - expect( - parseQuestionnaire({ - ...questionnaire, - date: '2026-08', - approvalDate: '2026-08-20', - lastReviewDate: '2026-08', - effectivePeriod: { - start: '2026-08-20T12:00:00+02:00', - end: '2026-08-20T11:00:00Z', - }, - }).ok, - ).toBe(true) - }) - - it('rejects duplicate and wrong-typed portable constraint extensions', () => { - const duplicate = buildQuestionnaire({ - ...questionnaireInput, - items: [ - { - linkId: 'duplicate-minimum', - text: 'Duplicate minimum', - type: 'string', - extension: [ - { url: questionnaireExtensions.minLength, valueInteger: 1 }, - { url: questionnaireExtensions.minLength, valueInteger: 2 }, - ], - }, - ], - }) - expect(duplicate.ok).toBe(false) - if (!duplicate.ok) { - expect(duplicate.issues).toEqual( - expect.arrayContaining([ - expect.objectContaining({ code: 'duplicate-identifier' }), - ]), - ) - } - - for (const item of [ - { - linkId: 'wrong-minimum-length', - text: 'Wrong minimum length', - type: 'string', - extension: [ - { url: questionnaireExtensions.minLength, valueString: '1' }, - ], - }, - { - linkId: 'wrong-value-bound', - text: 'Wrong value bound', - type: 'integer', - extension: [ - { url: questionnaireExtensions.minValue, valueString: '1' }, - ], - }, - { - linkId: 'wrong-maximum-size', - text: 'Wrong maximum size', - type: 'attachment', - extension: [ - { url: questionnaireExtensions.maxSize, valueInteger: 1024 }, - ], - }, - { - linkId: 'wrong-unit-option', - text: 'Wrong unit option', - type: 'quantity', - extension: [ - { url: questionnaireExtensions.unitOption, valueString: 'kg' }, - ], - }, - { - linkId: 'wrong-exclusive-option', - text: 'Wrong exclusive option', - type: 'choice', - answerOption: [ - { - extension: [ - { - url: questionnaireExtensions.optionExclusive, - valueString: 'true', - }, - ], - valueCoding: { system: codingSystem, code: 'none' }, - }, - ], - }, - ] as const) { - const result = buildQuestionnaire({ - ...questionnaireInput, - items: [item], - } as unknown as QuestionnaireInput) - expect(result.ok).toBe(false) - if (!result.ok) { - expect(result.issues).toEqual( - expect.arrayContaining([ - expect.objectContaining({ code: 'invalid-type' }), - ]), - ) - } - } - }) - - it('accepts the complete portable constraint and expression surface', () => { - const result = buildQuestionnaire({ - ...questionnaireInput, - extensions: [ - { - url: questionnaireExtensions.variable, - valueExpression: { - language: 'text/fhirpath', - name: 'rootScore', - expression: '0', - }, - }, - targetConstraint('root-valid'), - ], - items: [ - { - linkId: 'text', - text: 'Text', - type: 'text', - maxLength: 8, - extension: [ - { url: questionnaireExtensions.minLength, valueInteger: 2 }, - ], - }, - { - linkId: 'decimal', - text: 'Decimal', - type: 'decimal', - extension: [ - { url: questionnaireExtensions.minValue, valueDecimal: 1 }, - { url: questionnaireExtensions.maxValue, valueDecimal: 5 }, - { - url: questionnaireExtensions.maxDecimalPlaces, - valueInteger: 2, - }, - ], - }, - { - linkId: 'quantity', - text: 'Quantity', - type: 'quantity', - extension: [ - { - url: questionnaireExtensions.minQuantity, - valueQuantity: { - value: 1, - system: 'http://unitsofmeasure.org', - code: 'kg', - }, - }, - { - url: questionnaireExtensions.maxQuantity, - valueQuantity: { - value: 5, - system: 'http://unitsofmeasure.org', - code: 'kg', - }, - }, - { - url: questionnaireExtensions.unitOption, - valueCoding: { - system: 'http://unitsofmeasure.org', - code: 'kg', - }, - }, - ], - }, - { - linkId: 'attachment', - text: 'Attachment', - type: 'attachment', - repeats: true, - extension: [ - { url: questionnaireExtensions.mimeType, valueCode: 'image/png' }, - { url: questionnaireExtensions.maxSize, valueDecimal: 1024 }, - { url: questionnaireExtensions.minOccurs, valueInteger: 1 }, - { url: questionnaireExtensions.maxOccurs, valueInteger: 2 }, - { - url: questionnaireExtensions.variable, - valueExpression: { - language: 'text/fhirpath', - name: 'localScore', - expression: '0', - }, - }, - targetConstraint('item-valid'), - ], - }, - ], - }) - expect(result.ok).toBe(true) - }) - - it.each([ - [ - 'text length placement', - { - linkId: 'invalid', - text: 'Invalid', - type: 'boolean', - extension: [ - { url: questionnaireExtensions.minLength, valueInteger: 1 }, - ], - }, - 'invalid-choice', - ], - [ - 'text bound order', - { - linkId: 'invalid', - text: 'Invalid', - type: 'string', - maxLength: 2, - extension: [ - { url: questionnaireExtensions.minLength, valueInteger: 3 }, - ], - }, - 'out-of-range', - ], - [ - 'decimal placement', - { - linkId: 'invalid', - text: 'Invalid', - type: 'integer', - extension: [ - { - url: questionnaireExtensions.maxDecimalPlaces, - valueInteger: 2, - }, - ], - }, - 'invalid-choice', - ], - [ - 'value bound datatype', - { - linkId: 'invalid', - text: 'Invalid', - type: 'date', - extension: [{ url: questionnaireExtensions.minValue, valueInteger: 1 }], - }, - 'invalid-type', - ], - [ - 'quantity bound order', - { - linkId: 'invalid', - text: 'Invalid', - type: 'quantity', - extension: [ - { - url: questionnaireExtensions.minQuantity, - valueQuantity: { - value: 5, - system: 'http://unitsofmeasure.org', - code: 'kg', - }, - }, - { - url: questionnaireExtensions.maxQuantity, - valueQuantity: { - value: 1, - system: 'http://unitsofmeasure.org', - code: 'kg', - }, - }, - ], - }, - 'out-of-range', - ], - [ - 'unit placement', - { - linkId: 'invalid', - text: 'Invalid', - type: 'string', - extension: [ - { - url: questionnaireExtensions.unitOption, - valueCoding: { - system: 'http://unitsofmeasure.org', - code: 'kg', - }, - }, - ], - }, - 'invalid-choice', - ], - [ - 'attachment placement', - { - linkId: 'invalid', - text: 'Invalid', - type: 'string', - extension: [ - { url: questionnaireExtensions.mimeType, valueCode: 'image/png' }, - ], - }, - 'invalid-choice', - ], - [ - 'occurrence placement', - { - linkId: 'invalid', - text: 'Invalid', - type: 'choice', - extension: [ - { url: questionnaireExtensions.minOccurs, valueInteger: 1 }, - ], - }, - 'invalid-choice', - ], - ] as const)('rejects invalid %s constraints', (_name, item, code) => { - const result = buildQuestionnaire({ - ...questionnaireInput, - items: [item], - } as unknown as QuestionnaireInput) - expect(result.ok).toBe(false) - if (result.ok) return - expect(result.issues).toEqual( - expect.arrayContaining([expect.objectContaining({ code })]), - ) - }) - - it('rejects malformed expressions, reserved variables, and duplicate target keys', () => { - const result = buildQuestionnaire({ - ...questionnaireInput, - extensions: [ - { - url: questionnaireExtensions.variable, - valueExpression: { - language: 'text/fhirpath', - name: 'context', - expression: '0', - }, - }, - targetConstraint('duplicate-key'), - ], - items: [ - { - linkId: 'invalid-expression', - text: 'Invalid expression', - type: 'string', - extension: [ - { - url: questionnaireExtensions.calculatedExpression, - valueExpression: { - language: 'text/cql', - expression: ' ', - }, - }, - targetConstraint('duplicate-key'), - ], - }, - ], - }) - expect(result.ok).toBe(false) - if (result.ok) return - expect(new Set(result.issues.map(({ code }) => code))).toEqual( - new Set(['invalid-code', 'invalid-type', 'duplicate-identifier']), - ) - }) - - it('reports the complete bounded Questionnaire input validation surface', () => { - const result = buildQuestionnaire({ - ...questionnaireInput, - url: '/relative', - version: '01.0.0', - id: 'invalid/id', - date: 'not-an-instant', - subjectTypes: ['Patient', ''], - extensions: [ - { - url: 'http://hl7.org/fhir/StructureDefinition/rendering-styleSensitive', - valueBoolean: true, - }, - ], - items: [ - { linkId: '', type: 'string', repeats: true }, - { - linkId: 'exclusive', - text: 'Exclusive rules', - type: 'choice', - enableWhen: [ - { question: 'a', operator: 'exists', answerBoolean: true }, - { question: 'b', operator: 'exists', answerBoolean: true }, - ], - initial: [{ valueString: 'initial' }], - answerOption: [{ valueString: 'option' }], - answerValueSet: 'https://example.org/ValueSet/options', - extension: [ - { - url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression', - valueExpression: { - language: 'text/fhirpath', - expression: 'true', - }, - }, - { - url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', - valueExpression: { - language: 'text/fhirpath', - expression: "'x'", - }, - }, - { - url: 'http://hl7.org/fhir/StructureDefinition/rendering-styleSensitive', - valueBoolean: true, - }, - ], - }, - ], - } as unknown as QuestionnaireInput) - expect(result.ok).toBe(false) - if (result.ok) return - expect(new Set(result.issues.map(({ code }) => code))).toEqual( - new Set([ - 'invalid-uri', - 'invalid-code', - 'invalid-type', - 'invalid-identifier', - 'invalid-date-time', - 'missing-required', - 'invalid-choice', - 'invalid-reference', - ]), - ) - }) - - it('preserves every admitted optional Questionnaire and response field', () => { - const fullQuestionnaire = buildQuestionnaire({ - ...questionnaireInput, - id: unwrap(parseFhirId('questionnaire-1')), - date: authored, - description: 'A complete test instrument.', - purpose: 'Coverage of bounded optional fields.', - extensions: [ - { - url: 'https://example.org/fhir/StructureDefinition/instrument-note', - valueString: 'note', - }, - ], - }) - expect(fullQuestionnaire.ok).toBe(true) - if (!fullQuestionnaire.ok) return - expect(fullQuestionnaire.value.id).toBe('questionnaire-1') - expect(fullQuestionnaire.value.description).toBe( - 'A complete test instrument.', - ) - - const fullResponse = buildQuestionnaireResponse({ - ...responseInput(), - id: unwrap(parseFhirId('response-1')), - authorReference: 'Practitioner/author', - sourceReference: 'Patient/example', - extensions: [ - { - url: 'https://example.org/fhir/StructureDefinition/response-note', - valueString: 'note', - }, - ], - }) - expect(fullResponse.ok).toBe(true) - if (!fullResponse.ok) return - expect(fullResponse.value.author?.reference).toBe('Practitioner/author') - expect(fullResponse.value.source?.reference).toBe('Patient/example') - }) - - it('reports the complete bounded QuestionnaireResponse input validation surface', () => { - const result = buildQuestionnaireResponse({ - questionnaire: 'https://example.org/Questionnaire/pain', - identifier: { system: '/relative', value: ' ' }, - status: 'completed', - id: 'invalid/id', - subject: 'Patient/example/_history/2', - authored: 'not-an-instant', - authorReference: ' ', - sourceReference: '', - extensions: [ - { - url: 'http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode', - valueCodeableConcept: { text: 'duplicate' }, - }, - ], - items: [ - { - linkId: 'parent', - item: [ - { - linkId: 'direct-child', - answer: [{ valueString: 'missing repeated text' }], - }, - ], - answer: [ - { - valueString: 'parent answer', - item: [ - { - linkId: 'answer-child', - answer: [{ valueString: 'also missing repeated text' }], - }, - ], - }, - ], - }, - ], - } as unknown as QuestionnaireResponseInput) - expect(result.ok).toBe(false) - if (result.ok) return - expect(new Set(result.issues.map(({ code }) => code))).toEqual( - new Set([ - 'invalid-uri', - 'invalid-code', - 'invalid-identifier', - 'invalid-date-time', - 'invalid-reference', - 'duplicate-identifier', - 'missing-required', - ]), - ) - }) -}) - -describe('Questionnaire pair preflight', () => { - it('accepts an exact, nested Questionnaire/Response pair', () => { - const result = preflightQuestionnairePair(questionnaire, response) - expect(result.ok).toBe(true) - }) - - it('requires the authoritative version and completion envelopes', () => { - const incompleteQuestionnaire = { - ...questionnaire, - version: 'not-semver', - extension: [], - } - const incompleteResponse = { - ...response, - questionnaire: `${url}|not-semver`, - extension: [], - } - - const result = preflightQuestionnairePair( - incompleteQuestionnaire, - incompleteResponse, - ) - expect(result.ok).toBe(false) - if (result.ok) return - expect(new Set(result.issues.map(({ code }) => code))).toEqual( - new Set(['invalid-code', 'invalid-uri', 'missing-required']), - ) - expect( - result.issues.some( - ({ path, message }) => - path.join('.') === 'questionnaire.extension' && - message.includes('version-algorithm'), - ), - ).toBe(true) - expect( - result.issues.some( - ({ path, message }) => - path.join('.') === 'response.extension' && - message.includes('completion-mode'), - ), - ).toBe(true) - }) - - it('rejects a mismatched instrument version', () => { - const result = preflightQuestionnairePair(questionnaire, { - ...response, - questionnaire: `${url}|2.0.1`, - }) - expect(result.ok).toBe(false) - if (result.ok) return - expect(result.issues).toEqual( - expect.arrayContaining([ - expect.objectContaining({ code: 'value-mismatch' }), - ]), - ) - }) - - it('rejects an answer with the wrong datatype', () => { - const raw = structuredClone(response) - const answer = raw.item?.[0]?.item?.[0]?.answer?.[0] - if (answer === undefined) throw new Error('Missing test answer.') - const invalid = { ...answer, valueString: 'yes' } - const result = preflightQuestionnairePair(questionnaire, { - ...raw, - item: [ - { - ...raw.item?.[0], - item: [ - { - ...raw.item?.[0]?.item?.[0], - answer: [invalid], - }, - ], - }, - ], - }) - expect(result.ok).toBe(false) - }) - - it('rejects question children placed outside their answer context', () => { - const hasPain = response.item?.[0]?.item?.[0] - const nested = hasPain?.answer?.[0]?.item - if (hasPain === undefined || nested === undefined) { - throw new Error('Missing nested test items.') - } - const invalidResponse = { - ...response, - item: [ - { - ...response.item?.[0], - item: [ - { - ...hasPain, - answer: [{ valueBoolean: true }], - item: nested, - }, - ], - }, - response.item?.[1], - ], - } - expect(preflightQuestionnairePair(questionnaire, invalidResponse).ok).toBe( - false, - ) - }) - - it('rejects answers outside inline options', () => { - const health = response.item?.[0] - const hasPain = health?.item?.[0] - const hasPainAnswer = hasPain?.answer?.[0] - const severity = hasPainAnswer?.item?.[0] - const severityAnswer = severity?.answer?.[0] - const coding = severityAnswer?.valueCoding - if ( - health === undefined || - hasPain === undefined || - hasPainAnswer === undefined || - severity === undefined || - severityAnswer === undefined || - coding === undefined - ) { - throw new Error('Missing coded test answer.') - } - const invalidResponse = { - ...response, - item: [ - { - ...health, - item: [ - { - ...hasPain, - answer: [ - { - ...hasPainAnswer, - item: [ - { - ...severity, - answer: [ - { - ...severityAnswer, - valueCoding: { ...coding, code: 'moderate' }, - }, - ], - }, - ], - }, - ], - }, - ], - }, - response.item?.[1], - ], - } - expect(preflightQuestionnairePair(questionnaire, invalidResponse).ok).toBe( - false, - ) - }) - - it('requires enabled items in completed and amended responses', () => { - const missing = { ...response, item: response.item?.slice(1) } - expect(preflightQuestionnairePair(questionnaire, missing).ok).toBe(false) - expect( - preflightQuestionnairePair(questionnaire, { - ...missing, - status: 'amended', - }).ok, - ).toBe(false) - expect( - preflightQuestionnairePair(questionnaire, { - ...missing, - status: 'in-progress', - }).ok, - ).toBe(true) - }) - - it('evaluates conditional enablement and rejects disabled answers', () => { - const health = response.item?.[0] - const hasPain = health?.item?.[0] - if (health === undefined || hasPain === undefined) { - throw new Error('Missing conditional test items.') - } - const noPain = { - ...response, - item: [ - { - ...health, - item: [ - { - ...hasPain, - answer: [{ valueBoolean: false }], - }, - ], - }, - response.item?.[1], - ], - } - expect(preflightQuestionnairePair(questionnaire, noPain).ok).toBe(true) - - const disabledAnswer = { - ...noPain, - item: [ - { - ...noPain.item[0], - item: [ - { - ...noPain.item[0]?.item?.[0], - answer: [ - { - valueBoolean: false, - item: [ - { - linkId: 'pain-severity', - text: 'How severe is the pain?', - answer: [ - { - valueCoding: { system: codingSystem, code: 'mild' }, - }, - ], - }, - ], - }, - ], - }, - ], - }, - noPain.item[1], - ], - } - expect(preflightQuestionnairePair(questionnaire, disabledAnswer).ok).toBe( - false, - ) - }) - - it('fails closed for unresolved ValueSets and accepts normalized expansions', () => { - const valueSetCanonical = unwrap( - parseCanonical('https://example.org/fhir/ValueSet/mood|1.0.0'), - ) - const moodSystem = unwrap( - parseAbsoluteUri('https://example.org/fhir/CodeSystem/mood'), - ) - const valueSetQuestionnaire = unwrap( - buildQuestionnaire({ - ...questionnaireInput, - items: [ - { - linkId: 'mood', - text: 'Mood', - type: 'choice', - required: true, - answerValueSet: valueSetCanonical, - }, - ], - }), - ) - const valueSetResponse = unwrap( - buildQuestionnaireResponse({ - ...responseInput(), - items: [ - { - linkId: 'mood', - text: 'Mood', - answer: [{ valueCoding: { system: moodSystem, code: 'calm' } }], - }, - ], - }), - ) - expect( - preflightQuestionnairePair(valueSetQuestionnaire, valueSetResponse).ok, - ).toBe(false) - expect( - preflightQuestionnairePair(valueSetQuestionnaire, valueSetResponse, { - valueSets: [ - { - canonical: valueSetCanonical, - concepts: [{ system: moodSystem, code: 'calm' }], - }, - ], - }).ok, - ).toBe(true) - }) - - it('enforces text length and decimal precision constraints', () => { - const textItem = { - linkId: 'text', - text: 'Text', - type: 'text', - maxLength: 4, - extension: [{ url: questionnaireExtensions.minLength, valueInteger: 2 }], - } as const - expect( - preflightItems( - [textItem], - [{ linkId: 'text', text: 'Text', answer: [{ valueString: 'x' }] }], - ).ok, - ).toBe(false) - expect( - preflightItems( - [textItem], - [ - { - linkId: 'text', - text: 'Text', - answer: [{ valueString: '12345' }], - }, - ], - ).ok, - ).toBe(false) - - const oneCodePointItem = { - linkId: 'unicode', - text: 'Unicode', - type: 'string', - maxLength: 1, - extension: [{ url: questionnaireExtensions.minLength, valueInteger: 1 }], - } as const - expect( - preflightItems( - [oneCodePointItem], - [ - { - linkId: 'unicode', - text: 'Unicode', - answer: [{ valueString: '😀' }], - }, - ], - ).ok, - ).toBe(true) - expect( - preflightItems( - [oneCodePointItem], - [ - { - linkId: 'unicode', - text: 'Unicode', - answer: [{ valueString: '😀x' }], - }, - ], - ).ok, - ).toBe(false) - - const decimalItem = { - linkId: 'decimal', - text: 'Decimal', - type: 'decimal', - extension: [ - { - url: questionnaireExtensions.maxDecimalPlaces, - valueInteger: 2, - }, - ], - } as const - expect( - preflightItems( - [decimalItem], - [ - { - linkId: 'decimal', - text: 'Decimal', - answer: [{ valueDecimal: 1.234 }], - }, - ], - ).ok, - ).toBe(false) - expect( - preflightItems( - [decimalItem], - [ - { - linkId: 'decimal', - text: 'Decimal', - answer: [{ valueDecimal: 1.25 }], - }, - ], - ).ok, - ).toBe(true) - }) - - it('orders dateTime bounds by instant and time bounds by clock value', () => { - const dateTimeItem = { - linkId: 'date-time', - text: 'Date time', - type: 'dateTime', - extension: [ - { - url: questionnaireExtensions.minValue, - valueDateTime: '2026-08-20T12:00:00+02:00', - }, - { - url: questionnaireExtensions.maxValue, - valueDateTime: '2026-08-20T11:00:00Z', - }, - ], - } as const - expect( - buildQuestionnaire({ - ...questionnaireInput, - items: [dateTimeItem], - }).ok, - ).toBe(true) - expect( - preflightItems( - [dateTimeItem], - [ - { - linkId: 'date-time', - text: 'Date time', - answer: [{ valueDateTime: '2026-08-20T10:30:00Z' }], - }, - ], - ).ok, - ).toBe(true) - - const timeItem = { - linkId: 'time', - text: 'Time', - type: 'time', - extension: [ - { - url: questionnaireExtensions.minValue, - valueTime: '12:00:00.0', - }, - { - url: questionnaireExtensions.maxValue, - valueTime: '12:00:00', - }, - ], - } as const - expect( - buildQuestionnaire({ - ...questionnaireInput, - items: [timeItem], - }).ok, - ).toBe(true) - expect( - preflightItems( - [timeItem], - [ - { - linkId: 'time', - text: 'Time', - answer: [{ valueTime: '12:00:00.000' }], - }, - ], - ).ok, - ).toBe(true) - }) - - it('evaluates temporal enableWhen values semantically', () => { - expect( - preflightItems( - [ - { linkId: 'source', text: 'Source', type: 'dateTime' }, - { - linkId: 'dependent', - text: 'Dependent', - type: 'string', - enableWhen: [ - { - question: 'source', - operator: '>', - answerDateTime: '2026-08-20T12:00:00+02:00', - }, - ], - }, - ], - [ - { - linkId: 'source', - text: 'Source', - answer: [{ valueDateTime: '2026-08-20T10:30:00Z' }], - }, - { - linkId: 'dependent', - text: 'Dependent', - answer: [{ valueString: 'enabled' }], - }, - ], - ).ok, - ).toBe(true) - - expect( - preflightItems( - [ - { linkId: 'source', text: 'Source', type: 'time' }, - { - linkId: 'dependent', - text: 'Dependent', - type: 'string', - enableWhen: [ - { - question: 'source', - operator: '=', - answerTime: '12:00:00.0', - }, - ], - }, - ], - [ - { - linkId: 'source', - text: 'Source', - answer: [{ valueTime: '12:00:00' }], - }, - { - linkId: 'dependent', - text: 'Dependent', - answer: [{ valueString: 'enabled' }], - }, - ], - ).ok, - ).toBe(true) - }) - - it('enforces scalar and comparable quantity bounds', () => { - expect( - preflightItems( - [ - { - linkId: 'integer', - text: 'Integer', - type: 'integer', - extension: [ - { url: questionnaireExtensions.minValue, valueInteger: 2 }, - { url: questionnaireExtensions.maxValue, valueInteger: 4 }, - ], - }, - ], - [ - { - linkId: 'integer', - text: 'Integer', - answer: [{ valueInteger: 5 }], - }, - ], - ).ok, - ).toBe(false) - - const quantityItem = { - linkId: 'quantity', - text: 'Quantity', - type: 'quantity', - extension: [ - { - url: questionnaireExtensions.minQuantity, - valueQuantity: { - value: 1, - system: 'http://unitsofmeasure.org', - code: 'kg', - }, - }, - { - url: questionnaireExtensions.maxQuantity, - valueQuantity: { - value: 5, - system: 'http://unitsofmeasure.org', - code: 'kg', - }, - }, - ], - } as const - expect( - preflightItems( - [quantityItem], - [ - { - linkId: 'quantity', - text: 'Quantity', - answer: [ - { - valueQuantity: { - value: 3, - system: 'http://unitsofmeasure.org', - code: 'lb', - }, - }, - ], - }, - ], - ).ok, - ).toBe(false) - expect( - preflightItems( - [quantityItem], - [ - { - linkId: 'quantity', - text: 'Quantity', - answer: [ - { - valueQuantity: { - value: 3, - system: 'http://unitsofmeasure.org', - code: 'kg', - }, - }, - ], - }, - ], - ).ok, - ).toBe(true) - }) - - it('enforces inline and ValueSet-certified quantity units', () => { - const unitValueSet = unwrap( - parseCanonical('https://example.org/fhir/ValueSet/units|1.0.0'), - ) - const quantityAnswer = [ - { - linkId: 'quantity', - text: 'Quantity', - answer: [ - { - valueQuantity: { - value: 3, - system: 'http://unitsofmeasure.org', - code: 'kg', - }, - }, - ], - }, - ] as const - const inline = [ - { - linkId: 'quantity', - text: 'Quantity', - type: 'quantity', - extension: [ - { - url: questionnaireExtensions.unitOption, - valueCoding: { - system: 'http://unitsofmeasure.org', - code: 'lb', - }, - }, - ], - }, - ] as const - expect(preflightItems(inline, quantityAnswer).ok).toBe(false) - - const valueSet = [ - { - linkId: 'quantity', - text: 'Quantity', - type: 'quantity', - extension: [ - { - url: questionnaireExtensions.unitValueSet, - valueCanonical: unitValueSet, - }, - ], - }, - ] as const - expect(preflightItems(valueSet, quantityAnswer).ok).toBe(false) - expect( - preflightItems(valueSet, quantityAnswer, { - valueSets: [ - { - canonical: unitValueSet, - concepts: [ - { - system: unwrap(parseAbsoluteUri('http://unitsofmeasure.org')), - code: 'kg', - }, - ], - }, - ], - }).ok, - ).toBe(true) - }) - - it('enforces attachment MIME type and declared maximum size', () => { - const item = { - linkId: 'attachment', - text: 'Attachment', - type: 'attachment', - extension: [ - { url: questionnaireExtensions.mimeType, valueCode: 'image/png' }, - { url: questionnaireExtensions.maxSize, valueDecimal: 4 }, - ], - } as const - expect( - preflightItems( - [item], - [ - { - linkId: 'attachment', - text: 'Attachment', - answer: [ - { - valueAttachment: { - contentType: 'text/plain', - size: 5, - data: 'dGVzdA==', - }, - }, - ], - }, - ], - ).ok, - ).toBe(false) - expect( - preflightItems( - [item], - [ - { - linkId: 'attachment', - text: 'Attachment', - answer: [ - { - valueAttachment: { - contentType: 'image/png', - size: 4, - data: 'dGVzdA==', - }, - }, - ], - }, - ], - ).ok, - ).toBe(true) - }) - - it('enforces answer occurrence limits and exclusive options', () => { - const item = { - linkId: 'choice', - text: 'Choice', - type: 'choice', - repeats: true, - extension: [ - { url: questionnaireExtensions.minOccurs, valueInteger: 2 }, - { url: questionnaireExtensions.maxOccurs, valueInteger: 2 }, - ], - answerOption: [ - { - extension: [ - { - url: questionnaireExtensions.optionExclusive, - valueBoolean: true, - }, - ], - valueCoding: { system: codingSystem, code: 'none' }, - }, - { valueCoding: { system: codingSystem, code: 'pain' } }, - { valueCoding: { system: codingSystem, code: 'fatigue' } }, - ], - } as const - const responseItem = (codes: readonly string[]) => [ - { - linkId: 'choice', - text: 'Choice', - answer: codes.map((code) => ({ - valueCoding: { system: codingSystem, code }, - })), - }, - ] - expect(preflightItems([item], responseItem(['pain'])).ok).toBe(false) - expect( - preflightItems([item], responseItem(['pain', 'fatigue', 'pain'])).ok, - ).toBe(false) - expect(preflightItems([item], responseItem(['none', 'pain'])).ok).toBe( - false, - ) - expect(preflightItems([item], responseItem(['pain', 'fatigue'])).ok).toBe( - true, - ) - }) - - it('returns non-blocking warnings for warning target constraints', () => { - const warningQuestionnaire = { - ...questionnaire, - extension: [ - ...(questionnaire.extension ?? []), - targetConstraint('warning-constraint', 'warning'), - ], - } - const result = preflightQuestionnairePair(warningQuestionnaire, response) - expect(result.ok).toBe(true) - if (!result.ok) return - expect(result.warnings).toEqual([ - expect.objectContaining({ - severity: 'warning', - code: 'external-validation-required', - }), - ]) - }) - - it('fails closed for calculated expressions while preserving mixed warnings', () => { - const expressionQuestionnaire = { - ...questionnaire, - extension: [ - ...(questionnaire.extension ?? []), - targetConstraint('warning-constraint', 'warning'), - targetConstraint('blocking-constraint'), - ], - item: questionnaire.item.map((item, index) => - index === 1 ? - { - ...item, - extension: [ - { - url: questionnaireExtensions.calculatedExpression, - valueExpression: { - language: 'text/fhirpath', - expression: "'calculated'", - }, - }, - ], - } - : item, - ), - } - expect( - preflightQuestionnairePair(expressionQuestionnaire, response).ok, - ).toBe(false) - const mixed = preflightQuestionnairePair(expressionQuestionnaire, response) - if (mixed.ok) return - expect(new Set(mixed.issues.map(({ severity }) => severity))).toEqual( - new Set(['error', 'warning']), - ) - expect( - preflightQuestionnairePair(expressionQuestionnaire, { - ...response, - status: 'in-progress', - }).ok, - ).toBe(true) - }) - - it('does not treat initialExpression population as completed validation', () => { - const initialExpressionItem = { - linkId: 'populated', - text: 'Populated', - type: 'string', - extension: [ - { - url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', - valueExpression: { - language: 'text/fhirpath', - expression: "'suggested'", - }, - }, - ], - } as const - expect(preflightItems([initialExpressionItem], []).ok).toBe(true) - expect( - preflightItems([{ ...initialExpressionItem, required: true }], []).ok, - ).toBe(false) - }) - - it.each([ - ['boolean', { valueBoolean: false }], - ['decimal', { valueDecimal: 1.25 }], - ['integer', { valueInteger: 2 }], - ['date', { valueDate: '2026-08-20' }], - ['dateTime', { valueDateTime: '2026-08-20T12:00:00-07:00' }], - ['time', { valueTime: '12:00:00' }], - ['string', { valueString: 'short answer' }], - ['text', { valueString: 'long answer' }], - ['url', { valueUri: 'https://example.org/answer' }], - ['choice', { valueCoding: { system: codingSystem, code: 'coded-answer' } }], - [ - 'open-choice', - { valueCoding: { system: codingSystem, code: 'coded-answer' } }, - ], - ['open-choice', { valueString: 'free text' }], - [ - 'attachment', - { - valueAttachment: { - contentType: 'text/plain', - data: 'YW5zd2Vy', - title: 'answer.txt', - }, - }, - ], - [ - 'quantity', - { - valueQuantity: { - value: 3, - system: 'http://unitsofmeasure.org', - code: 'kg', - }, - }, - ], - ] as const)( - 'accepts the supported %s answer representation', - (type, answer) => { - const result = preflightItems( - [{ linkId: 'answer', text: 'Answer', type }], - [{ linkId: 'answer', text: 'Answer', answer: [answer] }], - ) - expect(result.ok).toBe(true) - }, - ) - - it('admits free text for open-choice despite closed coded options', () => { - const result = preflightItems( - [ - { - linkId: 'answer', - text: 'Answer', - type: 'open-choice', - answerOption: [ - { valueCoding: { system: codingSystem, code: 'coded-answer' } }, - ], - }, - ], - [ - { - linkId: 'answer', - text: 'Answer', - answer: [{ valueString: 'free text' }], - }, - ], - ) - expect(result.ok).toBe(true) - }) - - it.each([ - ['<', 6], - ['<=', 5], - ['>', 4], - ['>=', 5], - ['!=', 4], - ] as const)('evaluates the %s enableWhen operator', (operator, expected) => { - const result = preflightItems( - [ - { linkId: 'source', text: 'Source', type: 'integer' }, - { - linkId: 'dependent', - text: 'Dependent', - type: 'string', - enableWhen: [ - { question: 'source', operator, answerInteger: expected }, - ], - }, - ], - [ - { - linkId: 'source', - text: 'Source', - answer: [{ valueInteger: 5 }], - }, - { - linkId: 'dependent', - text: 'Dependent', - answer: [{ valueString: 'enabled' }], - }, - ], - ) - expect(result.ok).toBe(true) - }) - - it('compares Coding and Quantity enableWhen values semantically', () => { - const quantity = { - value: 3, - system: 'http://unitsofmeasure.org', - code: 'kg', - } - const result = preflightItems( - [ - { linkId: 'coded', text: 'Coded', type: 'choice' }, - { linkId: 'quantity', text: 'Quantity', type: 'quantity' }, - { - linkId: 'dependent', - text: 'Dependent', - type: 'string', - enableBehavior: 'all', - enableWhen: [ - { - question: 'coded', - operator: '=', - answerCoding: { system: codingSystem, code: 'yes' }, - }, - { - question: 'quantity', - operator: '=', - answerQuantity: quantity, - }, - ], - }, - ], - [ - { - linkId: 'coded', - text: 'Coded', - answer: [{ valueCoding: { system: codingSystem, code: 'yes' } }], - }, - { - linkId: 'quantity', - text: 'Quantity', - answer: [{ valueQuantity: quantity }], - }, - { - linkId: 'dependent', - text: 'Dependent', - answer: [{ valueString: 'enabled' }], - }, - ], - ) - expect(result.ok).toBe(true) - }) - - it('supports exists and any enablement without treating absence as data', () => { - const result = preflightItems( - [ - { linkId: 'present', text: 'Present', type: 'boolean' }, - { linkId: 'absent', text: 'Absent', type: 'boolean' }, - { - linkId: 'dependent', - text: 'Dependent', - type: 'string', - enableBehavior: 'any', - enableWhen: [ - { - question: 'present', - operator: 'exists', - answerBoolean: false, - }, - { - question: 'absent', - operator: 'exists', - answerBoolean: false, - }, - ], - }, - ], - [ - { - linkId: 'present', - text: 'Present', - answer: [{ valueBoolean: true }], - }, - { - linkId: 'dependent', - text: 'Dependent', - answer: [{ valueString: 'enabled' }], - }, - ], - ) - expect(result.ok).toBe(true) - }) - - it('fails closed when enableWhen cannot be evaluated', () => { - const invalidExists = preflightItems( - [ - { linkId: 'source', text: 'Source', type: 'string' }, - { - linkId: 'dependent', - text: 'Dependent', - type: 'string', - enableWhen: [ - { - question: 'source', - operator: 'exists', - answerString: 'not-a-boolean', - }, - ], - }, - ], - [], - ) - expect(invalidExists.ok).toBe(false) - - const incomparable = preflightItems( - [ - { linkId: 'source', text: 'Source', type: 'string' }, - { - linkId: 'dependent', - text: 'Dependent', - type: 'string', - enableWhen: [{ question: 'source', operator: '>', answerInteger: 2 }], - }, - ], - [ - { - linkId: 'source', - text: 'Source', - answer: [{ valueString: 'two' }], - }, - ], - ) - expect(incomparable.ok).toBe(false) - expect( - preflightItems( - [ - { linkId: 'source', text: 'Source', type: 'string' }, - { - linkId: 'dependent', - text: 'Dependent', - type: 'string', - enableWhen: [ - { question: 'source', operator: '>', answerInteger: 2 }, - ], - }, - ], - [], - {}, - 'in-progress', - ).ok, - ).toBe(true) - }) - - it('requires external evaluation of completed FHIRPath enablement', () => { - const items = [ - { - linkId: 'conditional', - text: 'Conditional', - type: 'string', - extension: [ - { - url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression', - valueExpression: { - language: 'text/fhirpath', - expression: "%resource.status = 'completed'", - }, - }, - ], - }, - ] as const - expect(preflightItems(items, []).ok).toBe(false) - expect(preflightItems(items, [], {}, 'in-progress').ok).toBe(true) - }) - - it('reports duplicate and invalid questionnaire topology', () => { - const duplicateAndUnknown = [ - { linkId: 'duplicate', text: 'First', type: 'string' }, - { - linkId: 'duplicate', - text: 'Second', - type: 'string', - enableWhen: [ - { question: 'missing', operator: '=', answerString: 'yes' }, - ], - }, - ] as const - const result = preflightItems(duplicateAndUnknown, []) - expect(result.ok).toBe(false) - if (result.ok) return - expect(new Set(result.issues.map(({ code }) => code))).toEqual( - new Set(['duplicate-identifier', 'invalid-reference']), - ) - }) - - it('reports response text, cardinality, grouping, and nesting failures', () => { - const result = preflightItems( - [ - { linkId: 'single', text: 'Single', type: 'string' }, - { - linkId: 'group', - text: 'Group', - type: 'group', - item: [{ linkId: 'child', text: 'Child', type: 'string' }], - }, - ], - [ - { - linkId: 'single', - text: 'Wrong text', - answer: [{ valueString: 'one' }, { valueString: 'two' }], - }, - { - linkId: 'group', - text: 'Group', - answer: [{ valueString: 'not admitted' }], - }, - { linkId: 'unknown', text: 'Unknown' }, - { linkId: 'single', text: 'Single' }, - ], - ) - expect(result.ok).toBe(false) - if (result.ok) return - expect(new Set(result.issues.map(({ code }) => code))).toEqual( - new Set([ - 'duplicate-identifier', - 'invalid-reference', - 'invalid-type', - 'out-of-range', - 'value-mismatch', - ]), - ) - }) - - it('rejects a resolved ValueSet nonmember and duplicate expansions', () => { - const valueSetCanonical = unwrap( - parseCanonical('https://example.org/fhir/ValueSet/mood|1.0.0'), - ) - const items = [ - { - linkId: 'mood', - text: 'Mood', - type: 'choice', - answerValueSet: valueSetCanonical, - }, - ] as const - const responseItems = [ - { - linkId: 'mood', - text: 'Mood', - answer: [ - { valueCoding: { system: codingSystem, code: 'not-admitted' } }, - ], - }, - ] as const - const expansion = { - canonical: valueSetCanonical, - concepts: [{ system: codingSystem, code: 'admitted' }], - } - const result = preflightItems(items, responseItems, { - valueSets: [expansion, expansion], - }) - expect(result.ok).toBe(false) - if (result.ok) return - expect(new Set(result.issues.map(({ code }) => code))).toEqual( - new Set(['duplicate-identifier', 'value-mismatch']), - ) - }) - - it('rejects entered-in-error answer data', () => { - expect( - preflightQuestionnairePair(questionnaire, { - ...response, - status: 'entered-in-error', - }).ok, - ).toBe(false) - }) - - it('preserves arbitrary textual answers under property testing', () => { - expect(() => - assert( - property(string({ minLength: 1 }), (value) => { - const candidate = unwrap( - buildQuestionnaireResponse(responseInput(value)), - ) - const result = preflightQuestionnairePair(questionnaire, candidate) - return result.ok - }), - ), - ).not.toThrow() - }) -}) - -describe('Questionnaire type contract', () => { - it('requires exactly one answer value at compile time', () => { - const answer: QuestionnaireResponseAnswerInput = { valueBoolean: true } - expect(answer.valueBoolean).toBe(true) - - // @ts-expect-error Multiple value[x] members are not a valid answer. - const invalid: QuestionnaireResponseAnswerInput = { - valueBoolean: true, - valueString: 'true', - } - expect(invalid).toBeDefined() - }) - - it.each([ - '0.0.0', - '1.2.3-alpha.1', - '1.2.3+build.0042', - '1.2.3-alpha.1+build.0042', - ])('accepts SemVer %s', (candidate) => { - expect(parseSemVer(candidate).ok).toBe(true) - }) - - it.each(['1.2', '01.2.3', '1.02.3', '1.2.3-01', '1.2.3+'])( - 'rejects non-SemVer %s', - (candidate) => { - expect(parseSemVer(candidate).ok).toBe(false) - }, - ) -}) diff --git a/packages/fhir/test/recording.test.ts b/packages/fhir/test/recording.test.ts index 8883b90b..61b29917 100644 --- a/packages/fhir/test/recording.test.ts +++ b/packages/fhir/test/recording.test.ts @@ -13,14 +13,16 @@ import { parseAbsoluteUri, parseFhirId, parseFhirInstant, - parsePatientReference, + parseGroveMobileExchangeBundle, parsePositiveInteger, type FhirInstant, type Result, } from '../src/index.js' +import { adapterSourceMarkerClaims } from '../src/mobile/measurement-catalog.generated.js' import { buildProviderRecordingBundle, - providerRawMappings, + healthKitClinicalRecordAdmission, + providerRawOutputRoles, encodeRecordingBytes, parseCanonicalBase64, parseProviderRecordingBundleInput, @@ -41,15 +43,52 @@ const unwrap = (result: Result): T => { const uri = (value: string) => unwrap(parseAbsoluteUri(value)) const instant = (value: string): FhirInstant => unwrap(parseFhirInstant(value)) -const identity = (system: string, value: string) => ({ - identifier: { system: uri(system), value }, -}) - const converter: ApplicationDeviceInput = { - identity: identity('https://example.org/applications', 'converter'), + sourceDeviceToken: 'converter-instance-7f3a', name: 'Grove converter', version: '0.0.0', } +const patient = { + type: 'Patient', + identifier: { + system: uri('https://example.org/deployments/patient-pseudonyms'), + value: 'patient-example', + assurance: 'deployment-scoped-pseudonym', + }, +} as const + +const deploymentIdentity = { + opaqueIdentifierSystems: { + 'source-record': uri('https://example.org/identity/source-record/test/1'), + 'source-output': uri('https://example.org/identity/source-output/test/1'), + 'writer-record': uri('https://example.org/identity/writer-record/test/1'), + 'provider-record': uri( + 'https://example.org/identity/provider-record/test/1', + ), + 'provider-output': uri( + 'https://example.org/identity/provider-output/test/1', + ), + 'source-artifact': uri( + 'https://example.org/identity/source-artifact/test/1', + ), + 'provider-artifact': uri( + 'https://example.org/identity/provider-artifact/test/1', + ), + 'source-context': uri('https://example.org/identity/source-context/test/1'), + 'recording-device': uri( + 'https://example.org/identity/recording-device/test/1', + ), + 'device-snapshot': uri( + 'https://example.org/identity/device-snapshot/test/1', + ), + }, + eventIdentifierSystem: uri('https://example.org/identity/event'), + entryNodeIdentifierSystem: uri('https://example.org/identity/entry-node'), + keyId: 'test-key', + keyEpoch: '1', + secretBase64Url: 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', + producerInstance: '1f5c58aa-6ec6-4e79-a682-829a9debd3f5', +} as const const rawInput = ( provider: ConnectedRawProvider, @@ -58,18 +97,22 @@ const rawInput = ( ({ source: { adapter: { kind: 'providers', provider }, - providerAccountIdentifier: { - system: uri('https://provider.example.org/accounts'), - value: 'account-pseudonym-001', - assurance: 'deployment-scoped-pseudonym', - }, + providerScopeIdentifier: + provider === 'oura' ? + { + system: uri('https://example.org/provider-key-spaces'), + value: 'oura-document-uuid-global', + assurance: 'documented-global-key-space', + } + : { + system: uri('https://provider.example.org/accounts'), + value: 'account-pseudonym-001', + assurance: 'deployment-scoped-account-pseudonym', + }, sourceType, sourceNativeId: `native-record-${provider}-${sourceType}`, dataOrigin: { - identity: identity( - 'https://example.org/provider-applications', - `origin-${provider}`, - ), + sourceDeviceToken: `origin-${provider}`, name: `${provider} source`, }, }, @@ -83,22 +126,91 @@ const rawInput = ( payloadAssertion: 'caller-authorized-opaque-payload', dataBase64: unwrap(encodeRecordingBytes(Uint8Array.of(1, 2, 3))), }, - subject: unwrap(parsePatientReference('Patient/example')), + subject: patient, application: converter, - eventSequence: unwrap(parsePositiveInteger(1)), - graphIdentifierSystem: uri( - 'urn:grove:provider-graph:org.grovealliance.example', - ), + eventSequence: '1', + deploymentIdentity, documentDate: instant('2026-08-20T17:00:01Z'), + occurred: instant('2026-08-20T17:00:00Z'), recorded: instant('2026-08-20T17:00:02Z'), + assembled: instant('2026-08-20T17:00:03Z'), }) as ProviderRecordingBundleInput -const recordingSources = Object.entries(providerRawMappings).flatMap( +const recordingSources = Object.entries(providerRawOutputRoles).flatMap( ([provider, sources]) => Object.keys(sources).map((sourceType) => [provider, sourceType] as const), ) +type MutableJsonObject = Record + +const mutableObject = (value: unknown, label: string): MutableJsonObject => { + if (typeof value !== 'object' || value === null || Array.isArray(value)) { + throw new Error(`Expected ${label} to be a JSON object.`) + } + return value as MutableJsonObject +} + +const mutableRecordingGraph = (): { + readonly bundle: unknown + readonly document: MutableJsonObject +} => { + const built = buildProviderRecordingBundle( + rawInput('google-health-api', 'heart-rate'), + ) + if (!built.ok) throw new Error('Expected a valid recording graph fixture.') + const bundle = structuredClone(built.value) + const entries = mutableObject(bundle, 'Bundle').entry + if (!Array.isArray(entries)) throw new Error('Expected Bundle.entry.') + const documentEntry: unknown = entries.find( + (entry: unknown) => + mutableObject(mutableObject(entry, 'entry').resource, 'resource') + .resourceType === 'DocumentReference', + ) + if (documentEntry === undefined) { + throw new Error('Expected a recording DocumentReference entry.') + } + return { + bundle, + document: mutableObject( + mutableObject(documentEntry, 'DocumentReference entry').resource, + 'DocumentReference', + ), + } +} + describe('Provider native recording graph', () => { + it('enforces the catalog provider scope for native recordings', () => { + const oura = rawInput('oura', 'heartrate') + expect( + parseProviderRecordingBundleInput({ + ...oura, + source: { + ...oura.source, + providerScopeIdentifier: { + system: uri('https://example.org/provider-accounts'), + value: 'account-pseudonym', + assurance: 'deployment-scoped-account-pseudonym', + }, + }, + }).ok, + ).toBe(false) + + const withings = rawInput('withings', 'activityIntraday') + expect( + parseProviderRecordingBundleInput({ + ...withings, + source: { + ...withings.source, + providerScopeIdentifier: { + system: uri('https://example.org/provider-key-spaces'), + value: 'withings-global', + assurance: 'documented-global-key-space', + }, + }, + }).ok, + ).toBe(false) + }) + it.each([ 'google-health-heart-rate-recording.json', 'oura-heart-rate-recording.json', @@ -137,7 +249,7 @@ describe('Provider native recording graph', () => { if (document?.resourceType !== 'DocumentReference') return expect(document.meta?.profile).toEqual([ 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-recording-document', + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document', ]) expect(document.extension).toEqual([ { @@ -149,15 +261,254 @@ describe('Provider native recording graph', () => { valueCode: `${provider}/${sourceType}`, }, ]) - // A recording document is a one-to-one conversion, so the source record identifies it and - // no second output namespace appears. expect(document.identifier?.map(({ system }) => system)).toEqual([ - 'https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id', + deploymentIdentity.opaqueIdentifierSystems['provider-record'], + ( + deploymentIdentity.opaqueIdentifierSystems as Readonly< + Record + > + )['provider-output'], + ( + deploymentIdentity.opaqueIdentifierSystems as Readonly< + Record + > + )['provider-artifact'], ]) expect(document.id).toBeUndefined() }, ) + it('keeps optional Attachment.title presentation text optional end to end', () => { + const input = rawInput('oura', 'heartrate') + const attachment = structuredClone(input.attachment) + Reflect.deleteProperty(attachment, 'title') + const result = buildProviderRecordingBundle({ ...input, attachment }) + expect(result.ok).toBe(true) + if (!result.ok) return + const document = result.value.entry.find( + ({ resource }) => resource.resourceType === 'DocumentReference', + )?.resource + expect(document?.resourceType).toBe('DocumentReference') + if (document?.resourceType !== 'DocumentReference') return + expect(document.content[0]?.attachment.title).toBeUndefined() + expect(parseGroveMobileExchangeBundle(result.value).ok).toBe(true) + }) + + it('keeps an unversioned writer identity and never emits a version without it', () => { + const input = rawInput('withings', 'activityIntraday') + const writerRecord = { + applicationIdentifier: { + system: uri('https://example.org/applications'), + value: 'writer-app', + }, + nativeRecordId: 'writer-record-1', + } as const + const unversioned = buildProviderRecordingBundle({ + ...input, + source: { ...input.source, writerRecord }, + }) + const versioned = buildProviderRecordingBundle({ + ...input, + source: { + ...input.source, + writerRecord: { ...writerRecord, version: '7' }, + }, + eventSequence: '2', + }) + expect(unversioned.ok && versioned.ok).toBe(true) + if (!unversioned.ok || !versioned.ok) return + const document = (result: typeof unversioned) => + result.value.entry.find( + ({ resource }) => resource.resourceType === 'DocumentReference', + )?.resource + const writerVersionUrl = + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version' + const hasWriterIdentity = (resource: ReturnType) => + resource?.resourceType === 'DocumentReference' && + resource.identifier?.some((candidate) => + candidate.type?.coding?.some(({ code }) => code === 'writer-record'), + ) + + expect(hasWriterIdentity(document(unversioned))).toBe(true) + expect( + document(unversioned)?.extension?.some( + ({ url }) => url === writerVersionUrl, + ), + ).not.toBe(true) + expect(hasWriterIdentity(document(versioned))).toBe(true) + expect(document(versioned)?.extension).toEqual( + expect.arrayContaining([{ url: writerVersionUrl, valueString: '7' }]), + ) + expect( + parseProviderRecordingBundleInput({ + ...input, + source: { ...input.source, writerRecordVersion: '1' }, + }).ok, + ).toBe(false) + }) + + it('accepts the catalog-owned direct-only HealthKit clinical document claim', () => { + const { bundle, document } = mutableRecordingGraph() + const representation = healthKitClinicalRecordAdmission.fhirRepresentation + const healthKitMarker = adapterSourceMarkerClaims.find( + ({ adapter }) => adapter === 'healthkit', + )?.markers[0] + if (healthKitMarker === undefined) { + throw new Error('Expected the generated HealthKit source marker.') + } + document.meta = { + profile: [healthKitClinicalRecordAdmission.profile], + } + document.extension = [ + { + url: representation.extensionUrl, + [representation.valueElement]: representation.fixedValue, + }, + { + url: healthKitMarker.url, + valueCode: 'HKClinicalTypeIdentifierAllergyRecord', + }, + ] + document.type = { + coding: [ + { + system: + 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-clinical-record-type', + code: 'allergy-record', + }, + ], + } + document.content = [ + { + format: { + system: + 'https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format', + version: '0.6.0', + code: healthKitClinicalRecordAdmission.payloadFormat, + }, + attachment: { + contentType: 'application/fhir+json', + title: 'Provider-issued AllergyIntolerance', + data: 'eyJyZXNvdXJjZVR5cGUiOiJBbGxlcmd5SW50b2xlcmFuY2UiLCJwYXRpZW50Ijp7ImlkZW50aWZpZXIiOnsic3lzdGVtIjoiaHR0cHM6Ly9leGFtcGxlLm9yZy9wYXRpZW50IiwidmFsdWUiOiJwc2V1ZG9ueW0ifX19', + size: 123, + hash: 'YfNHVrD+ah32NnVUmsFrxFQajhE=', + }, + }, + ] + const entries = mutableObject(bundle, 'Bundle').entry + if (!Array.isArray(entries)) throw new Error('Expected Bundle.entry.') + const provenanceEntry: unknown = entries.find( + (entry: unknown) => + mutableObject(mutableObject(entry, 'entry').resource, 'resource') + .resourceType === 'Provenance', + ) + if (provenanceEntry === undefined) { + throw new Error('Expected conversion Provenance.') + } + const provenance = mutableObject( + mutableObject(provenanceEntry, 'Provenance entry').resource, + 'Provenance', + ) + provenance.meta = { + profile: [ + 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-conversion-provenance', + ], + } + + const wrongDirectProfiles = structuredClone(bundle) + const wrongEntries = mutableObject( + wrongDirectProfiles, + 'wrong-profile Bundle', + ).entry + if (!Array.isArray(wrongEntries)) throw new Error('Expected Bundle.entry.') + const wrongDocumentEntry: unknown = wrongEntries.find( + (entry: unknown) => + mutableObject(mutableObject(entry, 'entry').resource, 'resource') + .resourceType === 'DocumentReference', + ) + if (wrongDocumentEntry === undefined) { + throw new Error('Expected DocumentReference.') + } + mutableObject( + mutableObject(wrongDocumentEntry, 'DocumentReference entry').resource, + 'DocumentReference', + ).meta = { + profile: [ + 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', + healthKitClinicalRecordAdmission.profile, + ], + } + expect(parseGroveMobileExchangeBundle(bundle).ok).toBe(true) + expect(parseGroveMobileExchangeBundle(wrongDirectProfiles).ok).toBe(false) + + const clinicalDocumentIn = ( + candidate: unknown, + ): Record => { + const candidateEntries = mutableObject(candidate, 'clinical Bundle').entry + if (!Array.isArray(candidateEntries)) { + throw new Error('Expected Bundle.entry.') + } + const candidateDocumentEntry: unknown = candidateEntries.find( + (entry: unknown) => + mutableObject(mutableObject(entry, 'entry').resource, 'resource') + .resourceType === representation.resourceType, + ) + if (candidateDocumentEntry === undefined) { + throw new Error(`Expected ${representation.resourceType}.`) + } + return mutableObject( + mutableObject(candidateDocumentEntry, 'clinical entry').resource, + 'clinical DocumentReference', + ) + } + const invalidRepresentations = [ + (candidate: Record) => { + Reflect.deleteProperty(candidate, 'extension') + }, + (candidate: Record) => { + candidate.extension = [ + { + url: representation.extensionUrl, + [representation.valueElement]: representation.fixedValue, + }, + { + url: representation.extensionUrl, + [representation.valueElement]: representation.fixedValue, + }, + ] + }, + (candidate: Record) => { + candidate.extension = [ + { + url: representation.extensionUrl, + [representation.valueElement]: + healthKitClinicalRecordAdmission.rejectedFHIRReleases[0], + }, + ] + }, + (candidate: Record) => { + candidate.extension = [ + { + url: representation.extensionUrl, + valueString: representation.fixedValue, + }, + ] + }, + ] + for (const mutate of invalidRepresentations) { + const candidate = structuredClone(bundle) + mutate(clinicalDocumentIn(candidate)) + const result = parseGroveMobileExchangeBundle(candidate) + expect(result.ok).toBe(false) + if (result.ok) continue + expect( + result.issues.some((issue) => + issue.message.includes('healthkit-clinical-record.fhir-release'), + ), + ).toBe(true) + } + }) + it('emits exact embedded bytes, computed SHA-1 integrity, and a complete audit graph', () => { const input = rawInput('google-health-api', 'heart-rate') const result = buildProviderRecordingBundle(input) @@ -184,7 +535,7 @@ describe('Provider native recording graph', () => { format: { system: 'https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format', - version: '0.5.0', + version: '0.6.0', code: 'provider-recording', display: 'Provider Recording', }, @@ -196,34 +547,141 @@ describe('Provider native recording graph', () => { expect(provenance?.resourceType).toBe('Provenance') if (provenance?.resourceType !== 'Provenance') return expect(provenance.meta?.profile).toEqual([ - 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance', + 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance', ]) expect(provenance.target).toEqual([{ reference: documentEntry.fullUrl }]) expect(provenance.entity).toHaveLength(1) expect(provenance.entity?.[0]?.what.identifier?.system).toBe( - 'https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id', + deploymentIdentity.opaqueIdentifierSystems['provider-record'], ) const serialized = JSON.stringify(result.value) - // The provider's own record key travels in the clear; the caller's payload assertion does not. - expect(serialized).toContain(input.source.sourceNativeId) + expect(serialized).not.toContain(input.source.sourceNativeId) + expect(serialized).not.toContain(input.source.providerScopeIdentifier.value) expect(serialized).not.toContain(input.attachment.payloadAssertion) expect(serialized).not.toContain('payloadAssertion') }) + it.each([ + { + name: 'missing source-artifact identity', + mutate: (document: MutableJsonObject) => { + const identifiers = document.identifier + if (!Array.isArray(identifiers)) + throw new Error('Expected identifiers.') + document.identifier = identifiers.filter((candidate) => { + const identifier = mutableObject(candidate, 'Identifier') + const type = mutableObject(identifier.type, 'Identifier.type') + const codings = type.coding + return !( + Array.isArray(codings) && + codings.some( + (coding) => + mutableObject(coding, 'Identifier.type.coding').code === + 'source-artifact', + ) + ) + }) + }, + }, + { + name: 'unadmitted source-context identity role', + mutate: (document: MutableJsonObject) => { + const identifiers = document.identifier + if (!Array.isArray(identifiers)) + throw new Error('Expected identifiers.') + const sourceArtifact = mutableObject( + identifiers[2], + 'source-artifact Identifier', + ) + const type = mutableObject(sourceArtifact.type, 'Identifier.type') + const codings = type.coding + if (!Array.isArray(codings)) throw new Error('Expected type coding.') + mutableObject(codings[0], 'Identifier.type.coding').code = + 'source-context' + }, + }, + { + name: 'wrong recording registry version', + mutate: (document: MutableJsonObject) => { + const content = document.content + if (!Array.isArray(content)) throw new Error('Expected content.') + const format = mutableObject( + mutableObject(content[0], 'content').format, + 'content.format', + ) + format.version = '0.5.0' + }, + }, + { + name: 'wrong recording media type', + mutate: (document: MutableJsonObject) => { + const content = document.content + if (!Array.isArray(content)) throw new Error('Expected content.') + const attachment = mutableObject( + mutableObject(content[0], 'content').attachment, + 'content.attachment', + ) + attachment.contentType = 'application/json' + }, + }, + { + name: 'second content entry', + mutate: (document: MutableJsonObject) => { + const content = document.content + if (!Array.isArray(content)) throw new Error('Expected content.') + content.push(structuredClone(content[0])) + }, + }, + { + name: 'wrong embedded byte count', + mutate: (document: MutableJsonObject) => { + const content = document.content + if (!Array.isArray(content)) throw new Error('Expected content.') + const attachment = mutableObject( + mutableObject(content[0], 'content').attachment, + 'content.attachment', + ) + attachment.size = 4 + }, + }, + { + name: 'wrong embedded SHA-1 digest', + mutate: (document: MutableJsonObject) => { + const content = document.content + if (!Array.isArray(content)) throw new Error('Expected content.') + const attachment = mutableObject( + mutableObject(content[0], 'content').attachment, + 'content.attachment', + ) + attachment.hash = 'AAAAAAAAAAAAAAAAAAAAAAAAAAA=' + }, + }, + ...(['type', 'subject', 'date'] as const).map((property) => ({ + name: `missing required ${property}`, + mutate: (document: MutableJsonObject) => { + Reflect.deleteProperty(document, property) + }, + })), + ])('rejects a recording document with $name', ({ mutate }) => { + const { bundle, document } = mutableRecordingGraph() + mutate(document) + expect(parseGroveMobileExchangeBundle(bundle).ok).toBe(false) + }) + it('matches the frozen raw source/output identity vectors', () => { const input = rawInput('google-health-api', 'heart-rate') const result = buildProviderRecordingBundle({ ...input, source: { ...input.source, - providerAccountIdentifier: { + providerScopeIdentifier: { system: uri('https://provider.example.org/accounts'), value: 'account-001', - assurance: 'deployment-scoped-pseudonym', + assurance: 'deployment-scoped-account-pseudonym', }, sourceNativeId: 'heart-rate-2026-08-20', }, - }) + } as ProviderRecordingBundleInput) expect(result.ok).toBe(true) if (!result.ok) return const document = result.value.entry.find( @@ -232,11 +690,116 @@ describe('Provider native recording graph', () => { if (document?.resourceType !== 'DocumentReference') { throw new Error('Missing recording document') } - // Google Health is account-scoped, and a recording document is a one-to-one conversion, so - // the source record is the document's only identity. - expect(document.identifier?.map(({ value }) => value)).toEqual([ - 'v1:google-health-api|account-001|heart-rate|heart-rate-2026-08-20', - ]) + expect(document.identifier).toHaveLength(3) + for (const entry of document.identifier ?? []) { + expect(entry.value).toMatch(/^v2:test-key:1:[A-Za-z0-9_-]{43}$/u) + } + }) + + it('places an explicitly governed native Identifier only on the sole recording DocumentReference', () => { + const input = rawInput('google-health-api', 'heart-rate') + const nativeSystem = uri( + 'https://example.org/repositories/google-account-4/recordings', + ) + const result = buildProviderRecordingBundle({ + ...input, + nativeIdentifierDisclosure: { + system: nativeSystem, + nativeId: input.source.sourceNativeId, + type: { + coding: [ + { + system: uri('https://example.org/identifier-types'), + code: 'provider-record-id', + display: 'Provider record id', + }, + ], + }, + }, + }) + expect(result.ok).toBe(true) + if (!result.ok) return + + const carryingResources = result.value.entry.filter(({ resource }) => + ( + resource as { + readonly identifier?: ReadonlyArray<{ + readonly system?: string + readonly value?: string + }> + } + ).identifier?.some( + ({ system, value }) => + system === nativeSystem && value === input.source.sourceNativeId, + ), + ) + expect(carryingResources).toHaveLength(1) + const document = carryingResources[0]?.resource + expect(document?.resourceType).toBe('DocumentReference') + if (document?.resourceType !== 'DocumentReference') return + expect( + document.identifier?.find(({ system }) => system === nativeSystem), + ).toEqual({ + system: nativeSystem, + value: input.source.sourceNativeId, + type: { + coding: [ + { + system: 'https://example.org/identifier-types', + code: 'provider-record-id', + display: 'Provider record id', + }, + ], + }, + }) + }) + + it.each([ + { + name: 'a mismatched native value', + disclosure: (input: ProviderRecordingBundleInput) => ({ + system: uri('https://example.org/repositories/recordings'), + nativeId: `${input.source.sourceNativeId}-wrong`, + }), + }, + { + name: 'a relative key-space system', + disclosure: (input: ProviderRecordingBundleInput) => ({ + system: 'recordings', + nativeId: input.source.sourceNativeId, + }), + }, + { + name: 'the Grove entry identity system', + disclosure: (input: ProviderRecordingBundleInput) => ({ + system: input.deploymentIdentity.entryNodeIdentifierSystem, + nativeId: input.source.sourceNativeId, + }), + }, + { + name: 'a Grove graph-role type coding', + disclosure: (input: ProviderRecordingBundleInput) => ({ + system: uri('https://example.org/repositories/recordings'), + nativeId: input.source.sourceNativeId, + type: { + coding: [ + { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role', + code: 'source-output', + }, + ], + }, + }), + }, + ])('rejects recording source disclosure using $name', ({ disclosure }) => { + const input = rawInput('oura', 'heartrate') + const candidate = { + ...input, + nativeIdentifierDisclosure: disclosure(input), + } as unknown as ProviderRecordingBundleInput + expect(parseProviderRecordingBundleInput(candidate).ok).toBe(false) + expect(buildProviderRecordingBundle(candidate).ok).toBe(false) }) it('supports an immutable external attachment without copying or fetching it', () => { @@ -433,143 +996,31 @@ describe('Provider native recording graph', () => { expect(parseImmutableRecordingUrl('not an absolute URL').ok).toBe(false) }) - it.each(['title', 'url', 'repository', 'application'] as const)( - 'rejects sourceNativeId leakage through %s metadata', - (location) => { - const input = rawInput('google-health-api', 'heart-rate') - const nativeId = input.source.sourceNativeId - let candidate: unknown - if (location === 'title') { - candidate = { - ...input, - attachment: { - ...input.attachment, - title: `Recording ${nativeId}`, - }, - } - } else if (location === 'url') { - candidate = { - ...input, - attachment: { - kind: 'external', - contentType: unwrap( - parseMediaType('application/vnd.grovealliance.provider+json'), - ), - title: 'Authorized recording', - format: 'provider-recording', - payloadAssertion: 'caller-authorized-opaque-payload', - url: `https://objects.example.org/${nativeId}`, - size: unwrap(parsePositiveInteger(3)), - hash: unwrap(parseSha1Base64('cDeAcZjCKn0rCAc3HXY3eahP388=')), - immutabilityAssurance: 'immutable-version-specific', - }, - } - } else if (location === 'repository') { - candidate = { ...input, repositoryIds: { document: nativeId } } - } else { - candidate = { - ...input, - application: { - ...input.application, - name: `Converter ${nativeId}`, - }, - } - } - expect( - buildProviderRecordingBundle(candidate as ProviderRecordingBundleInput) - .ok, - ).toBe(false) - }, - ) - - it('rejects provider-account pseudonym leakage through emitted metadata', () => { - const input = rawInput('google-health-api', 'heart-rate') - expect( - buildProviderRecordingBundle({ - ...input, - attachment: { - ...input.attachment, - title: `Recording ${input.source.providerAccountIdentifier.value}`, - }, - }).ok, - ).toBe(false) - }) - - it('rejects identity leakage through external and computed Attachment.hash metadata', () => { - const input = rawInput('google-health-api', 'heart-rate') - const emittedHash = 'cDeAcZjCKn0rCAc3HXY3eahP388=' - const privateSource = { - ...input.source, - sourceNativeId: emittedHash, + it('reports malformed recording inputs without throwing', () => { + const cyclic: Record = {} + cyclic.self = cyclic + for (const invalid of [null, undefined, 42, 'invalid', cyclic]) { + expect(() => parseProviderRecordingBundleInput(invalid)).not.toThrow() + expect(parseProviderRecordingBundleInput(invalid).ok).toBe(false) + expect(() => buildProviderRecordingBundle(invalid as never)).not.toThrow() + expect(buildProviderRecordingBundle(invalid as never).ok).toBe(false) } - expect( - buildProviderRecordingBundle({ - ...input, - source: privateSource, - }).ok, - ).toBe(false) - expect( - buildProviderRecordingBundle({ - ...input, - source: privateSource, - attachment: { - kind: 'external', - contentType: unwrap( - parseMediaType('application/vnd.grovealliance.provider+json'), - ), - title: 'Authorized recording', - format: 'provider-recording', - payloadAssertion: 'verified-sanitized-input', - url: unwrap( - parseImmutableRecordingUrl( - 'https://objects.example.org/recordings/version-42', - ), - ), - size: unwrap(parsePositiveInteger(3)), - hash: unwrap(parseSha1Base64(emittedHash)), - immutabilityAssurance: 'immutable-version-specific', - }, - }).ok, - ).toBe(false) }) - it('rejects recursively percent-encoded identity leakage through attachment URLs', () => { - const input = rawInput('google-health-api', 'heart-rate') - const privateValue = 'native record/42' - const encoded = encodeURIComponent(encodeURIComponent(privateValue)) - expect( - buildProviderRecordingBundle({ - ...input, - source: { ...input.source, sourceNativeId: privateValue }, - attachment: { - kind: 'external', - contentType: unwrap( - parseMediaType('application/vnd.grovealliance.provider+json'), - ), - title: 'Authorized recording', - format: 'provider-recording', - payloadAssertion: 'verified-sanitized-input', - url: unwrap( - parseImmutableRecordingUrl( - `https://objects.example.org/recordings/${encoded}`, - ), - ), - size: unwrap(parsePositiveInteger(3)), - hash: unwrap(parseSha1Base64('cDeAcZjCKn0rCAc3HXY3eahP388=')), - immutabilityAssurance: 'immutable-version-specific', - }, - }).ok, - ).toBe(false) - }) - - it('rejects duplicate business identities in the resource graph', () => { + it('deduplicates one application snapshot across participation roles', () => { const input = rawInput('withings', 'sleepIntraday') + const result = buildProviderRecordingBundle({ + ...input, + source: { ...input.source, dataOrigin: input.application }, + }) + expect(result.ok).toBe(true) + if (!result.ok) return expect( - buildProviderRecordingBundle({ - ...input, - source: { ...input.source, dataOrigin: input.application }, - }).ok, - ).toBe(false) + result.value.entry.filter( + ({ resource }) => resource.resourceType === 'Device', + ), + ).toHaveLength(1) + expect(result.value.entry).toHaveLength(3) }) it.each(['source', 'application', 'data-origin'] as const)( @@ -588,7 +1039,7 @@ describe('Provider native recording graph', () => { ...input, application: { ...input.application, - identity: identity('https://example.org/applications', invalid), + sourceDeviceToken: invalid, }, } } else { @@ -598,7 +1049,7 @@ describe('Provider native recording graph', () => { ...input.source, dataOrigin: { ...input.source.dataOrigin, - identity: identity('https://example.org/data-origins', invalid), + sourceDeviceToken: invalid, }, }, } @@ -615,8 +1066,8 @@ describe('Provider native recording graph', () => { ...input, source: { ...input.source, - providerAccountIdentifier: { - ...input.source.providerAccountIdentifier, + providerScopeIdentifier: { + ...input.source.providerScopeIdentifier, value: ' ', }, }, @@ -637,36 +1088,24 @@ describe('Provider native recording graph', () => { }), ], [ - 'converter identifier', + 'converter source-device token', (input: ProviderRecordingBundleInput) => ({ ...input, application: { ...input.application, - identity: { - ...input.application.identity, - identifier: { - ...input.application.identity.identifier, - value: ' ', - }, - }, + sourceDeviceToken: ' ', }, }), ], [ - 'data-origin identifier', + 'data-origin source-device token', (input: ProviderRecordingBundleInput) => ({ ...input, source: { ...input.source, dataOrigin: { ...input.source.dataOrigin, - identity: { - ...input.source.dataOrigin.identity, - identifier: { - ...input.source.dataOrigin.identity.identifier, - value: ' \t ', - }, - }, + sourceDeviceToken: ' \t ', }, }, }), @@ -703,7 +1142,7 @@ describe('Provider native recording graph', () => { rawInput('google-health-api', 'heart-rate'), ) expectTypeOf(result).toExtend< - Result + Result >() }) }) diff --git a/packages/fhir/test/retraction.test.ts b/packages/fhir/test/retraction.test.ts new file mode 100644 index 00000000..4682cf1c --- /dev/null +++ b/packages/fhir/test/retraction.test.ts @@ -0,0 +1,526 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + parseAbsoluteUri, + parseFhirInstant, + type FhirInstant, + type Result, +} from '../src/core/index.js' +import type { DeploymentIdentityInput } from '../src/mobile/index.js' +import { + buildProviderMeasurementBundle, + buildProviderRecordingBundle, + buildProviderRetractionBundle, + encodeRecordingBytes, + parseMediaType, + parseProviderRetractionInput, + providerOutputCoordinates, + type ProviderMeasurementBundleInput, + type ProviderRecordingBundleInput, + type ProviderRetractionInput, +} from '../src/providers/index.js' +import { parseGroveMobileRetractionBundle } from '../src/r4/index.js' + +const unwrap = (result: Result): Value => { + if (!result.ok) { + throw new Error(result.issues.map(({ message }) => message).join('\n')) + } + return result.value +} + +const uri = (value: string) => unwrap(parseAbsoluteUri(value)) +const instant = (value: string): FhirInstant => unwrap(parseFhirInstant(value)) +const patient = { + type: 'Patient', + identifier: { + system: uri('https://example.org/deployments/patient-pseudonyms'), + value: 'patient-example', + assurance: 'deployment-scoped-pseudonym', + }, +} as const + +const deploymentIdentity = { + opaqueIdentifierSystems: { + 'source-record': uri('https://example.org/identity/source-record/key/1'), + 'source-output': uri('https://example.org/identity/source-output/key/1'), + 'writer-record': uri('https://example.org/identity/writer-record/key/1'), + 'provider-record': uri( + 'https://example.org/identity/provider-record/key/1', + ), + 'provider-output': uri( + 'https://example.org/identity/provider-output/key/1', + ), + 'source-artifact': uri( + 'https://example.org/identity/source-artifact/key/1', + ), + 'provider-artifact': uri( + 'https://example.org/identity/provider-artifact/key/1', + ), + 'source-context': uri('https://example.org/identity/source-context/key/1'), + 'recording-device': uri( + 'https://example.org/identity/recording-device/key/1', + ), + 'device-snapshot': uri( + 'https://example.org/identity/device-snapshot/key/1', + ), + }, + eventIdentifierSystem: uri('https://example.org/identity/event'), + entryNodeIdentifierSystem: uri('https://example.org/identity/entry-node'), + keyId: 'unit-key', + keyEpoch: '1', + secretBase64Url: 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', + producerInstance: '9ae7b610-bac2-4f13-97b4-53b84b8a90cf', +} as const satisfies DeploymentIdentityInput + +const source = { + adapter: { kind: 'providers', provider: 'withings' }, + providerScopeIdentifier: { + system: uri('https://example.org/provider-accounts'), + value: 'participant-pseudonym', + assurance: 'deployment-scoped-account-pseudonym', + }, + sourceType: 'getmeas:11', + sourceNativeId: 'provider-record-17348211', + recordingMethod: 'automatically-recorded', + dataOrigin: { + sourceDeviceToken: 'withings-cloud', + name: 'Withings', + }, +} as const + +const activeInput = { + subject: patient, + measurements: [ + { + kind: 'heart-rate', + value: 64, + effective: { + kind: 'date-time', + value: instant('2026-08-20T12:00:00Z'), + }, + }, + ], + source, + application: { + sourceDeviceToken: 'converter-build-42', + name: 'Grove converter', + version: '0.6.0', + build: '42', + }, + eventSequence: '100', + deploymentIdentity, + occurred: instant('2026-08-20T12:00:00Z'), + recorded: instant('2026-08-20T12:02:00Z'), + assembled: instant('2026-08-20T12:03:00Z'), +} as const satisfies ProviderMeasurementBundleInput + +const outputCoordinates = providerOutputCoordinates( + 'withings', + 'getmeas:11', + 'heart-rate', +) +if (outputCoordinates === undefined) { + throw new Error( + 'The test requires the catalog-owned heart-rate output coordinates.', + ) +} + +const retractionInput = { + source: { + provider: source.adapter.provider, + providerScopeIdentifier: source.providerScopeIdentifier, + sourceType: source.sourceType, + sourceNativeId: source.sourceNativeId, + }, + targets: [ + { + role: 'primary-output', + resourceType: 'Observation', + ...outputCoordinates, + }, + ], + application: activeInput.application, + eventSequence: '101', + deploymentIdentity, + occurred: instant('2026-08-21T10:00:00Z'), + recorded: instant('2026-08-21T10:01:00Z'), + assembled: instant('2026-08-21T10:02:00Z'), +} as const satisfies ProviderRetractionInput + +describe('Provider source-record retraction', () => { + it('rejects a provider-scope assurance that contradicts the catalog', () => { + expect( + parseProviderRetractionInput({ + ...retractionInput, + source: { + ...retractionInput.source, + providerScopeIdentifier: { + system: uri('https://example.org/provider-key-spaces'), + value: 'withings-global', + assurance: 'documented-global-key-space', + }, + }, + }).ok, + ).toBe(false) + }) + + it('reports malformed retraction inputs without throwing', () => { + const cyclic: Record = {} + cyclic.self = cyclic + for (const invalid of [null, undefined, 42, 'invalid', cyclic]) { + expect(() => parseProviderRetractionInput(invalid)).not.toThrow() + expect(parseProviderRetractionInput(invalid).ok).toBe(false) + expect(() => + buildProviderRetractionBundle(invalid as never), + ).not.toThrow() + expect(buildProviderRetractionBundle(invalid as never).ok).toBe(false) + } + }) + it('targets the exact prior output identity in a separate append-only event', () => { + const active = unwrap(buildProviderMeasurementBundle(activeInput)) + const priorObservation = active.entry.find( + ({ resource }) => resource.resourceType === 'Observation', + )?.resource + if (priorObservation?.resourceType !== 'Observation') { + throw new Error('The active graph did not contain its Observation.') + } + const priorOutput = priorObservation.identifier?.find((candidate) => + candidate.type?.coding?.some(({ code }) => code === 'source-output'), + ) + + const retraction = unwrap(buildProviderRetractionBundle(retractionInput)) + expect(retraction.meta?.profile).toEqual([ + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle', + ]) + expect(retraction.entry).toHaveLength(2) + const provenance = retraction.entry.find( + ({ resource }) => resource.resourceType === 'Provenance', + )?.resource + expect( + retraction.entry.some( + ({ resource }) => resource.resourceType === 'Device', + ), + ).toBe(true) + if (provenance === undefined) { + throw new Error('The retraction graph did not contain Provenance.') + } + expect(provenance.resourceType).toBe('Provenance') + if (provenance.resourceType !== 'Provenance') return + expect(provenance.target).toEqual([ + { + extension: [ + { + url: 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-role', + valueCode: 'primary-output', + }, + ], + type: 'Observation', + identifier: priorOutput, + }, + ]) + expect(provenance.target[0]).not.toHaveProperty('reference') + expect(provenance.activity?.coding).toContainEqual( + expect.objectContaining({ + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-lifecycle-event', + code: 'source-record-retracted', + }), + ) + expect(provenance.occurredDateTime).toBe(retractionInput.occurred) + expect(provenance.recorded).toBe(retractionInput.recorded) + expect(retraction.timestamp).toBe(retractionInput.assembled) + expect(JSON.stringify(retraction)).not.toMatch(/entered-in-error/u) + }) + + it('rejects additional direct profiles on retraction Provenance', () => { + const retraction = structuredClone( + unwrap(buildProviderRetractionBundle(retractionInput)), + ) + const provenance = retraction.entry.find( + ({ resource }) => resource.resourceType === 'Provenance', + )?.resource + if (provenance?.resourceType !== 'Provenance') { + throw new Error('The retraction graph did not contain Provenance.') + } + const profiles = provenance.meta?.profile as + Array | undefined + profiles?.push( + 'https://example.org/fhir/StructureDefinition/unrelated-provenance', + ) + + const parsed = parseGroveMobileRetractionBundle(retraction) + expect(parsed.ok).toBe(false) + if (parsed.ok) return + expect(parsed.issues).toContainEqual( + expect.objectContaining({ + code: 'mobile-retraction.provenance-profile', + }), + ) + }) + + it('uses the selected source-output identity when retracting a source artifact', () => { + const recordingInput = { + source: { + adapter: { kind: 'providers', provider: 'withings' }, + providerScopeIdentifier: source.providerScopeIdentifier, + sourceType: 'activityIntraday', + sourceNativeId: 'provider-record-activity-1', + dataOrigin: source.dataOrigin, + }, + attachment: { + kind: 'embedded', + contentType: unwrap( + parseMediaType('application/vnd.grovealliance.provider+json'), + ), + title: 'Authorized minimized provider recording', + format: 'provider-recording', + payloadAssertion: 'verified-sanitized-input', + dataBase64: unwrap(encodeRecordingBytes(Uint8Array.of(1, 2, 3))), + }, + subject: activeInput.subject, + application: activeInput.application, + eventSequence: '200', + deploymentIdentity, + documentDate: instant('2026-08-20T12:01:00Z'), + occurred: instant('2026-08-20T12:00:00Z'), + recorded: instant('2026-08-20T12:02:00Z'), + assembled: instant('2026-08-20T12:03:00Z'), + } as const satisfies ProviderRecordingBundleInput + const active = unwrap(buildProviderRecordingBundle(recordingInput)) + const document = active.entry.find( + ({ resource }) => resource.resourceType === 'DocumentReference', + )?.resource + if (document?.resourceType !== 'DocumentReference') { + throw new Error( + 'The active recording graph did not contain its document.', + ) + } + const sourceOutput = document.identifier?.find((candidate) => + candidate.type?.coding?.some(({ code }) => code === 'source-output'), + ) + const sourceArtifact = document.identifier?.find((candidate) => + candidate.type?.coding?.some(({ code }) => code === 'source-artifact'), + ) + expect(sourceOutput).toBeDefined() + expect(sourceArtifact).toBeDefined() + expect(sourceOutput).not.toEqual(sourceArtifact) + + const retraction = unwrap( + buildProviderRetractionBundle({ + source: { + provider: 'withings', + providerScopeIdentifier: source.providerScopeIdentifier, + sourceType: 'activityIntraday', + sourceNativeId: 'provider-record-activity-1', + }, + targets: [ + { + role: 'source-artifact', + resourceType: 'DocumentReference', + formatCode: 'provider-recording', + partIndex: '0', + }, + ], + application: activeInput.application, + eventSequence: '201', + deploymentIdentity, + occurred: instant('2026-08-21T10:00:00Z'), + recorded: instant('2026-08-21T10:01:00Z'), + assembled: instant('2026-08-21T10:02:00Z'), + }), + ) + const provenance = retraction.entry.find( + ({ resource }) => resource.resourceType === 'Provenance', + )?.resource + if (provenance?.resourceType !== 'Provenance') { + throw new Error('The retraction graph did not contain Provenance.') + } + expect(provenance.target[0]?.identifier).toEqual(sourceOutput) + }) + + it('targets the exact prior recording-device snapshot', () => { + const stableUnitToken = 'recording-unit-1' + const active = unwrap( + buildProviderMeasurementBundle({ + ...activeInput, + source: { + ...activeInput.source, + recordingDevice: { + stableUnitToken, + subjectIdentifier: { + system: uri('https://example.org/participants'), + value: 'participant-pseudonym-001', + }, + identityScope: 'deployment-scoped', + name: 'Recorder', + }, + }, + }), + ) + const device = active.entry.find( + ({ resource }) => + resource.resourceType === 'Device' && + resource.meta?.profile?.includes( + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-device', + ), + )?.resource + if (device?.resourceType !== 'Device') { + throw new Error('The active graph did not contain its recording Device.') + } + const snapshot = device.identifier?.find((candidate) => + candidate.type?.coding?.some(({ code }) => code === 'device-snapshot'), + ) + + const retraction = unwrap( + buildProviderRetractionBundle({ + ...retractionInput, + eventSequence: '102', + targets: [ + { + role: 'device-snapshot', + resourceType: 'Device', + priorEventSequence: activeInput.eventSequence, + deviceRole: 'recording-device', + sourceDeviceToken: stableUnitToken, + }, + ], + }), + ) + const provenance = retraction.entry.find( + ({ resource }) => resource.resourceType === 'Provenance', + )?.resource + if (provenance?.resourceType !== 'Provenance') { + throw new Error('The retraction graph did not contain Provenance.') + } + expect(provenance.target[0]?.identifier).toEqual(snapshot) + }) + + it('requires a device snapshot event to precede the retraction event', () => { + const target = { + role: 'device-snapshot', + resourceType: 'Device', + priorEventSequence: '9999999999999999999999999999999999999999', + deviceRole: 'application', + sourceDeviceToken: activeInput.application.sourceDeviceToken, + } as const + const eventSequence = '10000000000000000000000000000000000000000' + + expect( + parseProviderRetractionInput({ + ...retractionInput, + eventSequence, + targets: [target], + }).ok, + ).toBe(true) + expect( + parseProviderRetractionInput({ + ...retractionInput, + eventSequence, + targets: [{ ...target, priorEventSequence: eventSequence }], + }).ok, + ).toBe(false) + expect( + parseProviderRetractionInput({ + ...retractionInput, + eventSequence, + targets: [ + { + ...target, + priorEventSequence: '10000000000000000000000000000000000000001', + }, + ], + }).ok, + ).toBe(false) + }) + + it('emits retraction targets in a deterministic canonical order', () => { + const deviceTarget = { + role: 'device-snapshot', + resourceType: 'Device', + priorEventSequence: activeInput.eventSequence, + deviceRole: 'application', + sourceDeviceToken: activeInput.application.sourceDeviceToken, + } as const + const primaryTarget = retractionInput.targets[0] + const forward = unwrap( + buildProviderRetractionBundle({ + ...retractionInput, + targets: [primaryTarget, deviceTarget], + }), + ) + const reversed = unwrap( + buildProviderRetractionBundle({ + ...retractionInput, + targets: [deviceTarget, primaryTarget], + }), + ) + + expect(reversed).toEqual(forward) + }) + + it('returns structured issues for malformed JavaScript input', () => { + expect(() => + buildProviderRetractionBundle( + undefined as unknown as ProviderRetractionInput, + ), + ).not.toThrow() + expect( + parseProviderRetractionInput({ + ...retractionInput, + unexpected: true, + }).ok, + ).toBe(false) + expect( + parseProviderRetractionInput({ + ...retractionInput, + deploymentIdentity: { + ...deploymentIdentity, + secretBase64Url: 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8', + }, + }).ok, + ).toBe(false) + }) + + it('rejects a duplicated logical target and role/type mismatch', () => { + const duplicated = buildProviderRetractionBundle({ + ...retractionInput, + targets: [retractionInput.targets[0], retractionInput.targets[0]], + }) + expect(duplicated.ok).toBe(false) + + const mismatched = buildProviderRetractionBundle({ + ...retractionInput, + targets: [ + { + role: 'source-artifact', + resourceType: 'Observation', + formatCode: 'provider-recording', + partIndex: '0', + }, + ], + } as unknown as ProviderRetractionInput) + expect(mismatched.ok).toBe(false) + }) + + it('rejects a source type outside the provider catalog', () => { + const result = parseProviderRetractionInput({ + ...retractionInput, + source: { + ...retractionInput.source, + sourceType: 'unregistered-source-type', + }, + }) + expect(result.ok).toBe(false) + if (!result.ok) { + expect(result.issues).toContainEqual( + expect.objectContaining({ path: ['source', 'sourceType'] }), + ) + } + }) +}) diff --git a/packages/fhir/test/zod-generation-invariants.test.ts b/packages/fhir/test/zod-generation-invariants.test.ts index 8f599b4e..ce1999ea 100644 --- a/packages/fhir/test/zod-generation-invariants.test.ts +++ b/packages/fhir/test/zod-generation-invariants.test.ts @@ -150,7 +150,6 @@ const UNCHECKED_KEYS: Readonly> = { 'ppc-5', 'prr-1', 'qrs-1', - 'qty-3', 'que-0', 'que-1', 'que-10', @@ -169,7 +168,6 @@ const UNCHECKED_KEYS: Readonly> = { 'ras-2', 'rat-1', 'red-0', - 'ref-1', 'rng-2', 'rqg-1', 'rsd-0', @@ -378,7 +376,6 @@ const UNCHECKED_KEYS: Readonly> = { 'ppc-5', 'prr-1', 'qrs-1', - 'qty-3', 'que-0', 'que-10', 'que-11', @@ -396,7 +393,6 @@ const UNCHECKED_KEYS: Readonly> = { 'ras-2', 'rat-1', 'red-0', - 'ref-1', 'rng-2', 'rqg-1', 'rsd-0', @@ -499,7 +495,7 @@ describe.each(RELEASES)('generated %s schemas', (release) => { it('rejects properties the release does not define', () => { // FHIR JSON is closed; a plain object would strip an unmodelled property silently. expect(text).not.toMatch(/z\.object\(/) - expect(text).toContain('z.strictObject(') + expect(text).toMatch(/\.strictObject\(\{/u) }) it('keeps the extensions a primitive may carry', () => { @@ -545,7 +541,14 @@ describe.each(RELEASES)('generated %s schemas', (release) => { UNCHECKED_KEYS[release], ) // Anything implemented must not also be reported as unchecked. - for (const implemented of ['ext-1', 'per-1', 'obs-3', 'obs-6']) { + for (const implemented of [ + 'ext-1', + 'per-1', + 'obs-3', + 'obs-6', + 'qty-3', + 'ref-1', + ]) { expect(keys.has(implemented)).toBe(false) } }) diff --git a/packages/fhir/test/zod-generator-boundary.test.ts b/packages/fhir/test/zod-generator-boundary.test.ts new file mode 100644 index 00000000..68fd2b52 --- /dev/null +++ b/packages/fhir/test/zod-generator-boundary.test.ts @@ -0,0 +1,160 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { assertSafeGeneratorInput } from '../scripts/zod-generator-input.mjs' + +interface GeneratorElement { + readonly path: string + readonly min: unknown + readonly max: unknown + readonly type?: ReadonlyArray>> + readonly contentReference?: unknown + readonly minValueInteger?: unknown + readonly maxValueInteger?: unknown +} + +const structure = ( + name: string, + elements: readonly GeneratorElement[] = [{ path: name, min: 0, max: '1' }], +) => ({ + resourceType: 'StructureDefinition', + name, + kind: 'complex-type', + snapshot: { element: elements }, +}) + +const input = ( + structures: ReadonlyMap, + declaredTypes: ReadonlySet = new Set(), +) => ({ + structures: new Map(structures), + valueSets: new Map(), + codeSystems: new Map(), + declaredTypes: new Set(declaredTypes), +}) + +describe('Zod generator package boundary', () => { + it('admits a safe package while allowing reserved words as quoted property keys', () => { + const definition = structure('Example', [ + { path: 'Example', min: 0, max: '1' }, + { + path: 'Example.class', + min: 0, + max: '1', + type: [{ code: 'string' }], + }, + ]) + + expect(() => + assertSafeGeneratorInput(input(new Map([['Example', definition]]))), + ).not.toThrow() + }) + + it.each([ + ['quote', 'Bad";globalThis.compromised=true;//'], + ['newline', 'Bad\nexport const compromised = true'], + ['backtick', 'Bad`compromised`'], + ['template interpolation', 'Bad${globalThis.compromised=true}'], + ['regular-expression metacharacters', 'Bad.*'], + ['reserved word', 'class'], + ])('rejects a %s in a structure/type identifier', (_label, malicious) => { + expect(() => + assertSafeGeneratorInput( + input(new Map([[malicious, structure(malicious)]])), + ), + ).toThrow(/FHIR name/u) + }) + + it.each([ + ['quote', 'Example.bad";globalThis.compromised=true;//'], + ['newline', 'Example.bad\ncompromised'], + ['backtick', 'Example.bad`compromised`'], + ['template interpolation', 'Example.bad${compromised}'], + ['regular-expression metacharacters', 'Example.bad.*'], + ])('rejects a %s in an element path before rendering', (_label, path) => { + const definition = structure('Example', [ + { path: 'Example', min: 0, max: '1' }, + { path, min: 0, max: '1', type: [{ code: 'string' }] }, + ]) + + expect(() => + assertSafeGeneratorInput(input(new Map([['Example', definition]]))), + ).toThrow(/FHIR path/u) + }) + + it('rejects an executable choice type code before deriving a property or schema name', () => { + const definition = structure('Example', [ + { path: 'Example', min: 0, max: '1' }, + { + path: 'Example.value[x]', + min: 0, + max: '1', + type: [{ code: 'string`);globalThis.compromised=true;//' }], + }, + ]) + + expect(() => + assertSafeGeneratorInput(input(new Map([['Example', definition]]))), + ).toThrow(/type\[0\]\.code/u) + }) + + it.each([ + ['minimum', '0);globalThis.compromised=true;//', '1'], + ['maximum', 0, '1);globalThis.compromised=true;//'], + ])( + 'rejects a non-numeric %s cardinality before interpolation', + (_label, min, max) => { + const definition = structure('Example', [{ path: 'Example', min, max }]) + + expect(() => + assertSafeGeneratorInput(input(new Map([['Example', definition]]))), + ).toThrow(/must be/u) + }, + ) + + it('rejects an unsafe local content-reference path', () => { + const definition = structure('Example', [ + { path: 'Example', min: 0, max: '1' }, + { + path: 'Example.node', + min: 0, + max: '*', + contentReference: '#Example.node.*', + }, + ]) + + expect(() => + assertSafeGeneratorInput(input(new Map([['Example', definition]]))), + ).toThrow(/contentReference/u) + }) + + it('rejects distinct release paths that collapse to one generated identifier', () => { + const parent = structure('Foo', [ + { path: 'Foo', min: 0, max: '1' }, + { + path: 'Foo.bar', + min: 0, + max: '1', + type: [{ code: 'BackboneElement' }], + }, + ]) + const collidingRoot = structure('FooBar') + + expect(() => + assertSafeGeneratorInput( + input( + new Map([ + ['Foo', parent], + ['FooBar', collidingRoot], + ]), + new Set(['FooBar']), + ), + ), + ).toThrow(/identifier fooBarSchema collides/u) + }) +}) diff --git a/packages/fhir/test/zod-schemas.test.ts b/packages/fhir/test/zod-schemas.test.ts index 8cd0816b..216de322 100644 --- a/packages/fhir/test/zod-schemas.test.ts +++ b/packages/fhir/test/zod-schemas.test.ts @@ -7,16 +7,23 @@ // import { + base64BinarySchema, bundleSchema, + capabilityStatementSchema, + dateSchema, + dateTimeSchema, + decimalSchema, medicationAdministrationSchema, medicationStatementSchema, observationSchema, documentReferenceSchema, patientSchema, positiveIntSchema, + instantSchema, resourceSchema, unsignedIntSchema, visionPrescriptionSchema, + xhtmlSchema, } from '../src/zod/r4/index.js' import { bundleSchema as r4bBundle, @@ -67,12 +74,67 @@ describe('generated Zod schemas', () => { ).toThrow() }) + it('rejects impossible calendar dates and non-finite decimals', () => { + expect(dateSchema.safeParse('2025-02-29').success).toBe(false) + expect(dateTimeSchema.safeParse('2026-04-31T12:00:00Z').success).toBe(false) + expect(instantSchema.safeParse('2026-02-30T12:00:00Z').success).toBe(false) + expect(dateSchema.safeParse('2024-02-29').success).toBe(true) + expect(decimalSchema.safeParse(Number.NaN).success).toBe(false) + expect(decimalSchema.safeParse(Number.POSITIVE_INFINITY).success).toBe( + false, + ) + }) + + it('rejects empty string-like primitive values', () => { + expect(base64BinarySchema.safeParse('').success).toBe(false) + expect(base64BinarySchema.safeParse(' \t\n').success).toBe(false) + expect(xhtmlSchema.safeParse('').success).toBe(false) + }) + it('rejects a missing required element', () => { const withoutStatus: Record = { ...observation } delete withoutStatus.status expect(() => observationSchema.parse(withoutStatus)).toThrow() }) + it('accepts required primitives represented only by primitive metadata', () => { + const absent = { + extension: [ + { + // This canonical is fixed by FHIR R4 and is never fetched. + url: 'http://hl7.org/fhir/StructureDefinition/data-absent-reason', + valueCode: 'unknown', + }, + ], + } + const withoutStatus: Record = { + ...observation, + _status: absent, + } + delete withoutStatus.status + expect(observationSchema.safeParse(withoutStatus).success).toBe(true) + + expect( + capabilityStatementSchema.safeParse({ + resourceType: 'CapabilityStatement', + status: 'active', + date: '2026-08-27T12:00:00Z', + kind: 'instance', + fhirVersion: '4.0.1', + _format: [absent], + }).success, + ).toBe(true) + expect( + capabilityStatementSchema.safeParse({ + resourceType: 'CapabilityStatement', + status: 'active', + date: '2026-08-27T12:00:00Z', + kind: 'instance', + fhirVersion: '4.0.1', + }).success, + ).toBe(false) + }) + it('rejects the wrong resourceType', () => { expect(() => observationSchema.parse({ ...observation, resourceType: 'Patient' }), @@ -119,6 +181,27 @@ describe('generated Zod schemas', () => { ).toThrow() }) + it('enforces the base Reference presence invariant recursively', () => { + expect(() => + observationSchema.parse({ ...observation, subject: {} }), + ).toThrow() + expect(() => + observationSchema.parse({ + ...observation, + subject: { type: 'Patient' }, + }), + ).toThrow() + }) + + it('requires a Quantity unit system whenever a unit code is present', () => { + expect(() => + observationSchema.parse({ + ...observation, + valueQuantity: { value: 72, code: '/min' }, + }), + ).toThrow() + }) + it('bounds the integer primitives the specification bounds', () => { // positiveInt and unsignedInt state their range as a lexical pattern rather than a min. expect(() => positiveIntSchema.parse(0)).toThrow() @@ -209,14 +292,12 @@ describe('polymorphic resource slots', () => { ).toBe(false) }) - it('leaves a resource type the release does not publish open', () => { - // A type outside the release is the only remaining unmodelled case, and rejecting it would - // refuse a bundle over a gap in this package rather than a fault in the data. + it('rejects a resource type the pinned release does not publish', () => { expect( bundleSchema.safeParse( bundleCarrying({ resourceType: 'SomethingNotInR4', status: 'draft' }), ).success, - ).toBe(true) + ).toBe(false) }) it('validates a Bundle nested inside a Bundle', () => { From ed71902d21164e23eac0bdc8f3e917fbabca8764 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Fri, 28 Aug 2026 18:55:14 -0700 Subject: [PATCH 03/31] Fix FHIR validation boundary gaps --- .../fhir/scripts/generate-zod-schemas.mjs | 19 +- packages/fhir/scripts/zod-generator-input.mjs | 29 + .../src/r4/active-observation-semantics.ts | 84 +-- packages/fhir/src/r4/profile-semantics.ts | 48 +- packages/fhir/src/zod/r4/schemas.ts | 31 +- packages/fhir/src/zod/r4b/schemas.ts | 31 +- packages/fhir/test/diagnostics.test.ts | 70 +++ packages/fhir/test/exchange-protocol.test.ts | 83 ++- packages/fhir/test/foundation.test.ts | 53 ++ .../fhir/test/observation-semantics.test.ts | 515 ++++++++++++++++++ .../test/profile-semantics-boundaries.test.ts | 388 +++++++++++++ packages/fhir/test/provider-identity.test.ts | 12 +- packages/fhir/test/provider-semantics.test.ts | 110 ++++ packages/fhir/test/retraction.test.ts | 6 +- .../fhir/test/zod-generator-boundary.test.ts | 38 ++ packages/fhir/test/zod-schemas.test.ts | 3 +- 16 files changed, 1388 insertions(+), 132 deletions(-) create mode 100644 packages/fhir/test/diagnostics.test.ts create mode 100644 packages/fhir/test/observation-semantics.test.ts create mode 100644 packages/fhir/test/profile-semantics-boundaries.test.ts diff --git a/packages/fhir/scripts/generate-zod-schemas.mjs b/packages/fhir/scripts/generate-zod-schemas.mjs index 597dbede..0d37ccde 100644 --- a/packages/fhir/scripts/generate-zod-schemas.mjs +++ b/packages/fhir/scripts/generate-zod-schemas.mjs @@ -10,9 +10,10 @@ * Generate Zod runtime schemas from a FHIR release's own StructureDefinitions. * * Nothing here is transcribed by hand. Cardinality, choice types, required-binding enumerations - * and even the primitive regexes come from the published definitions, so a schema cannot drift - * from the specification it claims to implement, and a second release costs one argument rather - * than a second corpus. + * and string primitive regexes come from the published definitions. Numeric primitive patterns + * are checked at the package boundary and rendered as equivalent value-space constraints because + * JSON parsing has already discarded their lexical form. A second release still costs one + * argument rather than a second corpus. * * Usage: * node scripts/generate-zod-schemas.mjs --release r4 @@ -802,6 +803,8 @@ function renderStructure(structure, context, pending) { constrained = `${constrained}.min(${bounds.min})` if (bounds.max !== undefined) constrained = `${constrained}.max(${bounds.max})` + if (root === 'positiveInt') constrained = `${constrained}.positive()` + if (root === 'unsignedInt') constrained = `${constrained}.nonnegative()` } if (['date', 'dateTime', 'instant'].includes(root)) { constrained = `${constrained}.refine(hasValidFhirCalendarDate, { message: ${JSON.stringify(`Expected a real Gregorian calendar date for FHIR ${root}.`)} })` @@ -809,14 +812,6 @@ function renderStructure(structure, context, pending) { if (['canonical', 'uri', 'url', 'xhtml'].includes(root)) { constrained = `${constrained}.min(1)` } - if (regex && base.startsWith('z.number()')) { - // positiveInt and unsignedInt state their bound as a lexical pattern; testing the string - // form applies the specification's own constraint rather than a hand-chosen one. - const source = JSON.stringify(`^(?:${regex})$`) - constrained = `${constrained}.refine((value) => new RegExp(${source}).test(String(value)), { - message: ${JSON.stringify(`Expected a valid FHIR ${root}.`)}, -})` - } const output = base === 'z.boolean()' ? 'boolean' : base.startsWith('z.number()') ? 'number' @@ -1093,7 +1088,7 @@ const HEADER = `// // GENERATED FILE. Run \`npm run generate:zod\` after changing the generator or the release pin. // -/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion, sonarjs/no-nested-functions, sonarjs/regex-complexity, sonarjs/concise-regex, sonarjs/single-char-in-character-classes, sonarjs/single-character-alternation */ +/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion, sonarjs/no-nested-functions, sonarjs/regex-complexity, sonarjs/concise-regex, sonarjs/single-character-alternation */ ` diff --git a/packages/fhir/scripts/zod-generator-input.mjs b/packages/fhir/scripts/zod-generator-input.mjs index 41b26658..c86fa009 100644 --- a/packages/fhir/scripts/zod-generator-input.mjs +++ b/packages/fhir/scripts/zod-generator-input.mjs @@ -11,6 +11,17 @@ const ELEMENT_SEGMENT = /^[A-Za-z][A-Za-z0-9]*(?:\[x\])?$/u const CARDINALITY = /^(?:0|[1-9]\d*)$/u const FHIR_PATH_SYSTEM_TYPE = /^http:\/\/hl7\.org\/fhirpath\/System\.[A-Za-z][A-Za-z0-9]*$/u +const FHIR_REGEX_EXTENSION = 'http://hl7.org/fhir/StructureDefinition/regex' + +// JavaScript receives FHIR JSON numbers after their lexical representation has been parsed. +// Keep the release pattern as an integrity assertion, then render the equivalent value-space +// constraint without copying package-owned text into generated TypeScript. +const NUMERIC_PRIMITIVE_REGEXES = new Map([ + ['decimal', '-?(0|[1-9][0-9]*)(\\.[0-9]+)?([eE][+-]?[0-9]+)?'], + ['integer', '-?([0]|([1-9][0-9]*))'], + ['positiveInt', '[1-9][0-9]*'], + ['unsignedInt', '[0]|([1-9][0-9]*)'], +]) const RESERVED_WORDS = new Set([ 'await', @@ -267,6 +278,24 @@ const validateStructure = ( if (!Array.isArray(elements) || elements.length === 0) { fail(`${name}.snapshot.element`, elements, 'must be a non-empty array') } + const expectedNumericRegex = NUMERIC_PRIMITIVE_REGEXES.get(name) + if (expectedNumericRegex !== undefined) { + const valueElement = elements.find( + (element) => element?.path === `${name}.value`, + ) + const patterns = (valueElement?.type ?? []).flatMap((type) => + (type?.extension ?? []) + .filter((extension) => extension?.url === FHIR_REGEX_EXTENSION) + .map((extension) => extension.valueString), + ) + if (patterns.length !== 1 || patterns[0] !== expectedNumericRegex) { + fail( + `${name}.value regex`, + patterns, + `must equal the expected ${name} lexical constraint`, + ) + } + } for (const [index, element] of elements.entries()) { const label = `${name}.snapshot.element[${index}]` if ( diff --git a/packages/fhir/src/r4/active-observation-semantics.ts b/packages/fhir/src/r4/active-observation-semantics.ts index 7d78b9ff..fab9c121 100644 --- a/packages/fhir/src/r4/active-observation-semantics.ts +++ b/packages/fhir/src/r4/active-observation-semantics.ts @@ -129,9 +129,8 @@ const PROVIDER_OBSERVATION_PROFILES: ReadonlySet = new Set( const hasExactProviderObservationClaim = ( observation: UnknownRecord, match: MeasurementDefinition | null, + directProfiles: readonly string[], ): boolean => { - const profiles = asRecord(observation.meta)?.profile - const directProfiles = Array.isArray(profiles) ? profiles : [] const providerMarkers = Array.isArray(observation.extension) ? observation.extension.filter( @@ -163,11 +162,10 @@ const hasExactProviderObservationClaim = ( if ( provider === undefined || semanticProfile === undefined || - !Array.isArray(profiles) || - profiles.length !== 2 || - new Set(profiles).size !== 2 || - !profiles.includes(semanticProfile) || - !profiles.includes(provider.observationProfile) || + directProfiles.length !== 2 || + new Set(directProfiles).size !== 2 || + !directProfiles.includes(semanticProfile) || + !directProfiles.includes(provider.observationProfile) || (match.owner !== undefined && provider.measurementOwner !== match.owner) ) { return false @@ -181,40 +179,50 @@ const setsEqual = ( ): boolean => left.size === right.size && [...left].every((value) => right.has(value)) +interface ObservationProfileAdmission { + readonly match: MeasurementDefinition | null + readonly directProfiles: readonly string[] +} + const admittedObservationProfile = ( profiles: unknown, -): MeasurementDefinition | null | undefined => { - if ( - !Array.isArray(profiles) || - profiles.length === 0 || - profiles.some((profile) => typeof profile !== 'string') - ) { - return undefined - } - const direct = new Set(profiles as readonly string[]) - if (direct.size !== profiles.length) return undefined +): ObservationProfileAdmission | undefined => { + if (!Array.isArray(profiles) || profiles.length === 0) return undefined + const directProfiles = profiles.filter( + (profile): profile is string => typeof profile === 'string', + ) + if (directProfiles.length !== profiles.length) return undefined + const direct = new Set(directProfiles) + if (direct.size !== directProfiles.length) return undefined if ( direct.size === 1 && [...direct].every((profile) => SENSORKIT_PLATFORM_PROFILES.has(profile)) ) { - return null + return { match: null, directProfiles } + } + if (setsEqual(direct, SENSORKIT_HYBRID_PROFILES)) { + return { match: null, directProfiles } } - if (setsEqual(direct, SENSORKIT_HYBRID_PROFILES)) return null - const semantic = [...direct].filter((profile) => + const semanticProfiles = [...direct].filter((profile) => SHARED_SEMANTIC_PROFILES.has(profile), ) + const [semanticProfile, ...additionalSemanticProfiles] = semanticProfiles const adapters = [...direct].filter((profile) => ADAPTER_OBSERVATION_PROFILES.has(profile), ) if ( - semantic.length !== 1 || + semanticProfile === undefined || + additionalSemanticProfiles.length > 0 || adapters.length > 1 || - direct.size !== semantic.length + adapters.length + direct.size !== semanticProfiles.length + adapters.length ) { return undefined } - return MEASUREMENT_BY_PROFILE.get(semantic[0] ?? '') ?? null + return { + match: MEASUREMENT_BY_PROFILE.get(semanticProfile) ?? null, + directProfiles, + } } /** Whether an Observation directly declares one exact catalog-admitted semantic profile shape. */ @@ -223,9 +231,16 @@ export const hasAdmittedMobileObservationProfile = ( ): boolean => { const observation = asRecord(resource) if (observation?.resourceType !== 'Observation') return false - const match = admittedObservationProfile(asRecord(observation.meta)?.profile) + const admission = admittedObservationProfile( + asRecord(observation.meta)?.profile, + ) return ( - match !== undefined && hasExactProviderObservationClaim(observation, match) + admission !== undefined && + hasExactProviderObservationClaim( + observation, + admission.match, + admission.directProfiles, + ) ) } @@ -496,13 +511,14 @@ const validateCodeableConceptResult = ( ): void => { const result = asRecord(observation.valueCodeableConcept) const codings = Array.isArray(result?.coding) ? result.coding : [] + const allowedValues = new Set(match.allowedValues ?? []) const admitted = codings.filter((candidate) => { const coding = asRecord(candidate) return ( coding !== undefined && coding.system === match.resultCodeSystem && typeof coding.code === 'string' && - (match.allowedValues ?? []).includes(coding.code) + allowedValues.has(coding.code) ) }) const invalidSameSystem = codings.some((candidate) => { @@ -510,8 +526,7 @@ const validateCodeableConceptResult = ( return ( coding !== undefined && coding.system === match.resultCodeSystem && - (typeof coding.code !== 'string' || - !(match.allowedValues ?? []).includes(coding.code)) + (typeof coding.code !== 'string' || !allowedValues.has(coding.code)) ) }) if (admitted.length !== 1 || invalidSameSystem) { @@ -644,10 +659,16 @@ export const validateMobileObservationSemantics = ( ): void => { const observation = asRecord(resource) if (observation?.resourceType !== 'Observation') return - const match = admittedObservationProfile(asRecord(observation.meta)?.profile) + const admission = admittedObservationProfile( + asRecord(observation.meta)?.profile, + ) if ( - match === undefined || - !hasExactProviderObservationClaim(observation, match) + admission === undefined || + !hasExactProviderObservationClaim( + observation, + admission.match, + admission.directProfiles, + ) ) { addIssue( context, @@ -657,6 +678,7 @@ export const validateMobileObservationSemantics = ( ) return } + const { match } = admission if (match === null) return if (observation.status !== 'final') { addIssue( diff --git a/packages/fhir/src/r4/profile-semantics.ts b/packages/fhir/src/r4/profile-semantics.ts index 03800a3b..27b213e3 100644 --- a/packages/fhir/src/r4/profile-semantics.ts +++ b/packages/fhir/src/r4/profile-semantics.ts @@ -68,31 +68,32 @@ const ADAPTER_ONLY_OUTPUT_PROFILE_CLAIMS = [ groveProfileClaims.healthConnectSpecimenClaim, ...groveProfileClaims.healthKitPlatformExclusiveResourceClaims, ] as const -const ADAPTER_ONLY_OUTPUT_TYPES: ReadonlySet = new Set( - groveMobileContract.lifecycle.adapterOnlyOutputProfileClaims.resourceTypes, +type AdapterOnlyOutputProfileClaim = + (typeof ADAPTER_ONLY_OUTPUT_PROFILE_CLAIMS)[number] +const ADAPTER_ONLY_OUTPUT_PROFILE_CLAIM_BY_TYPE: ReadonlyMap< + string, + AdapterOnlyOutputProfileClaim +> = new Map( + ADAPTER_ONLY_OUTPUT_PROFILE_CLAIMS.map((claim) => [ + claim.resourceType, + claim, + ]), ) - /** Whether an adapter-only output declares its exact catalog-owned single profile. */ export const hasAdmittedAdapterOnlyOutputProfile = ( resource: unknown, ): boolean => { const record = asRecord(resource) - if ( - typeof record?.resourceType !== 'string' || - !ADAPTER_ONLY_OUTPUT_TYPES.has(record.resourceType) - ) { - return true - } - const claim = ADAPTER_ONLY_OUTPUT_PROFILE_CLAIMS.find( - ({ resourceType }) => resourceType === record.resourceType, + if (typeof record?.resourceType !== 'string') return true + const claim = ADAPTER_ONLY_OUTPUT_PROFILE_CLAIM_BY_TYPE.get( + record.resourceType, ) - const expectedProfile = claim?.profile + if (claim === undefined) return true const profiles = asRecord(record.meta)?.profile return ( - typeof expectedProfile === 'string' && Array.isArray(profiles) && profiles.length === 1 && - profiles[0] === expectedProfile + profiles[0] === claim.profile ) } @@ -122,9 +123,7 @@ const exactProfileClaim = ( ) }) if (matching.length !== 1) return undefined - const claim = matching[0] - if (claim === undefined) return undefined - return claim + return matching.at(0) } /** Whether an active DocumentReference declares one exact catalog-admitted direct profile mode. */ @@ -464,19 +463,14 @@ const validateAdapterOnlyOutputProfile = ( context: z.core.$RefinementCtx, path: ReadonlyArray, ) => { - if ( - typeof resource.resourceType !== 'string' || - !ADAPTER_ONLY_OUTPUT_TYPES.has(resource.resourceType) - ) { - return - } - const matchingClaims = ADAPTER_ONLY_OUTPUT_PROFILE_CLAIMS.filter( - ({ resourceType }) => resourceType === resource.resourceType, + if (typeof resource.resourceType !== 'string') return + const claim = ADAPTER_ONLY_OUTPUT_PROFILE_CLAIM_BY_TYPE.get( + resource.resourceType, ) - const claim = matchingClaims[0] + if (claim === undefined) return const exactProfile = hasAdmittedAdapterOnlyOutputProfile(resource) const requiredRoles: ReadonlySet = new Set( - claim?.requiredIdentifierRoles ?? [], + claim.requiredIdentifierRoles, ) const invalidRoles = [...requiredRoles].some((role) => roleCounts.get(role) !== 1) || diff --git a/packages/fhir/src/zod/r4/schemas.ts b/packages/fhir/src/zod/r4/schemas.ts index 246370ac..b3dd8478 100644 --- a/packages/fhir/src/zod/r4/schemas.ts +++ b/packages/fhir/src/zod/r4/schemas.ts @@ -8,7 +8,7 @@ // GENERATED FILE. Run `npm run generate:zod` after changing the generator or the release pin. // -/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion, sonarjs/no-nested-functions, sonarjs/regex-complexity, sonarjs/concise-regex, sonarjs/single-char-in-character-classes, sonarjs/single-character-alternation */ +/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion, sonarjs/no-nested-functions, sonarjs/regex-complexity, sonarjs/concise-regex, sonarjs/single-character-alternation */ import type { Account as FhirAccount, @@ -48739,17 +48739,7 @@ export const dateTimeSchema: z.ZodType = z message: 'Expected a real Gregorian calendar date for FHIR dateTime.', }) -export const decimalSchema: z.ZodType = z - .number() - .refine( - (value) => - new RegExp('^(?:-?(0|[1-9][0-9]*)(\\.[0-9]+)?([eE][+-]?[0-9]+)?)$').test( - String(value), - ), - { - message: 'Expected a valid FHIR decimal.', - }, - ) +export const decimalSchema: z.ZodType = z.number() export const idSchema: z.ZodType = z .string() @@ -48772,12 +48762,6 @@ export const integerSchema: z.ZodType = z .int() .min(-2147483648) .max(2147483647) - .refine( - (value) => new RegExp('^(?:-?([0]|([1-9][0-9]*)))$').test(String(value)), - { - message: 'Expected a valid FHIR integer.', - }, - ) export const markdownSchema: z.ZodType = z .string() @@ -48795,9 +48779,7 @@ export const positiveIntSchema: z.ZodType = z .int() .min(-2147483648) .max(2147483647) - .refine((value) => new RegExp('^(?:[1-9][0-9]*)$').test(String(value)), { - message: 'Expected a valid FHIR positiveInt.', - }) + .positive() export const stringSchema: z.ZodType = z .string() @@ -48817,12 +48799,7 @@ export const unsignedIntSchema: z.ZodType = z .int() .min(-2147483648) .max(2147483647) - .refine( - (value) => new RegExp('^(?:[0]|([1-9][0-9]*))$').test(String(value)), - { - message: 'Expected a valid FHIR unsignedInt.', - }, - ) + .nonnegative() export const uriSchema: z.ZodType = z .string() diff --git a/packages/fhir/src/zod/r4b/schemas.ts b/packages/fhir/src/zod/r4b/schemas.ts index a1628af8..7f294f0d 100644 --- a/packages/fhir/src/zod/r4b/schemas.ts +++ b/packages/fhir/src/zod/r4b/schemas.ts @@ -8,7 +8,7 @@ // GENERATED FILE. Run `npm run generate:zod` after changing the generator or the release pin. // -/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion, sonarjs/no-nested-functions, sonarjs/regex-complexity, sonarjs/concise-regex, sonarjs/single-char-in-character-classes, sonarjs/single-character-alternation */ +/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion, sonarjs/no-nested-functions, sonarjs/regex-complexity, sonarjs/concise-regex, sonarjs/single-character-alternation */ import type { Account as FhirAccount, @@ -49925,17 +49925,7 @@ export const dateTimeSchema: z.ZodType = z message: 'Expected a real Gregorian calendar date for FHIR dateTime.', }) -export const decimalSchema: z.ZodType = z - .number() - .refine( - (value) => - new RegExp('^(?:-?(0|[1-9][0-9]*)(\\.[0-9]+)?([eE][+-]?[0-9]+)?)$').test( - String(value), - ), - { - message: 'Expected a valid FHIR decimal.', - }, - ) +export const decimalSchema: z.ZodType = z.number() export const idSchema: z.ZodType = z .string() @@ -49958,12 +49948,6 @@ export const integerSchema: z.ZodType = z .int() .min(-2147483648) .max(2147483647) - .refine( - (value) => new RegExp('^(?:-?([0]|([1-9][0-9]*)))$').test(String(value)), - { - message: 'Expected a valid FHIR integer.', - }, - ) export const markdownSchema: z.ZodType = z .string() @@ -49981,9 +49965,7 @@ export const positiveIntSchema: z.ZodType = z .int() .min(-2147483648) .max(2147483647) - .refine((value) => new RegExp('^(?:[1-9][0-9]*)$').test(String(value)), { - message: 'Expected a valid FHIR positiveInt.', - }) + .positive() export const stringSchema: z.ZodType = z .string() @@ -50003,12 +49985,7 @@ export const unsignedIntSchema: z.ZodType = z .int() .min(-2147483648) .max(2147483647) - .refine( - (value) => new RegExp('^(?:[0]|([1-9][0-9]*))$').test(String(value)), - { - message: 'Expected a valid FHIR unsignedInt.', - }, - ) + .nonnegative() export const uriSchema: z.ZodType = z .string() diff --git a/packages/fhir/test/diagnostics.test.ts b/packages/fhir/test/diagnostics.test.ts new file mode 100644 index 00000000..3474ba47 --- /dev/null +++ b/packages/fhir/test/diagnostics.test.ts @@ -0,0 +1,70 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { groveExchangeRuleDiagnostics } from '../src/mobile/measurement-catalog.generated.js' +import { + decodeGroveRuleDiagnostic, + encodeGroveRuleDiagnostic, + groveRuleIssue, + type GroveExchangeRuleCode, +} from '../src/r4/diagnostics.js' + +const ruleCodes = Object.keys( + groveExchangeRuleDiagnostics, +) as GroveExchangeRuleCode[] + +describe('stable Grove rule diagnostics', () => { + it.each(ruleCodes)( + 'round-trips %s with its public reason and location', + (code) => { + const path = ['entry', 3, 'resource', 'target', 2, 'extension', 0] + const issue = groveRuleIssue(code, path) + const encoded = encodeGroveRuleDiagnostic(code, path, 'fallback') + + expect(decodeGroveRuleDiagnostic(encoded)).toEqual({ + code, + reason: groveExchangeRuleDiagnostics[code].reason, + location: issue.location, + severity: groveExchangeRuleDiagnostics[code].severity, + }) + }, + ) + + it('uses the stable first-target location when no target ordinal is available', () => { + expect( + groveRuleIssue('mobile-retraction.logical-target', []).location, + ).toBe('Provenance.target[0]') + }) + + it('rejects malformed or forged encoded diagnostics', () => { + const code = ruleCodes[0] + if (code === undefined) throw new Error('The rule catalog is empty.') + const valid = encodeGroveRuleDiagnostic(code, [], 'fallback') + const prefix = valid.slice(0, valid.indexOf('{')) + const reason = groveExchangeRuleDiagnostics[code].reason + const cases = [ + 'not encoded', + `${prefix}{`, + `${prefix}${JSON.stringify({ code: 42, reason, location: 'Bundle', severity: 'error' })}`, + `${prefix}${JSON.stringify({ code: 'unknown', reason, location: 'Bundle', severity: 'error' })}`, + `${prefix}${JSON.stringify({ code, reason: 'forged', location: 'Bundle', severity: 'error' })}`, + `${prefix}${JSON.stringify({ code, reason, location: '', severity: 'error' })}`, + `${prefix}${JSON.stringify({ code, reason, location: 42, severity: 'error' })}`, + `${prefix}${JSON.stringify({ code, reason, location: 'Bundle', severity: 'warning' })}`, + ] + + for (const candidate of cases) { + expect(decodeGroveRuleDiagnostic(candidate)).toBeUndefined() + } + expect( + decodeGroveRuleDiagnostic( + encodeGroveRuleDiagnostic('unknown', [], 'fallback'), + ), + ).toBeUndefined() + }) +}) diff --git a/packages/fhir/test/exchange-protocol.test.ts b/packages/fhir/test/exchange-protocol.test.ts index e220ee4d..d45a0579 100644 --- a/packages/fhir/test/exchange-protocol.test.ts +++ b/packages/fhir/test/exchange-protocol.test.ts @@ -11,17 +11,22 @@ import { type AbsoluteUri, type Result, } from '../src/core/index.js' -import { deriveConformanceVectorOpaqueIdentifier } from '../src/mobile/identity.js' +import { + containsIsolatedSurrogate, + deriveConformanceVectorOpaqueIdentifier, +} from '../src/mobile/identity.js' import { createEntryIdentity, deriveEntryFullUrl, deriveEntryNodeIdentifier, + deriveEntryNodeValue, deriveEventIdentifier, deriveOpaqueIdentifier, entryIdentifierName, encodeLengthFramedUtf8, groveExchangeProtocol, isEntryNodeIdentityValue, + isEventIdentityValue, isOpaqueIdentityValue, validateDeploymentIdentity, type DeploymentIdentityInput, @@ -284,6 +289,30 @@ describe('Grove exchange protocol v2 identity', () => { } }) + it.each([ + { opaqueIdentifierSystems: null }, + { opaqueIdentifierSystems: [] }, + { keyId: 42 }, + { keyId: 'not a token' }, + { keyEpoch: 1 }, + { keyEpoch: '0' }, + { producerInstance: 42 }, + { producerInstance: runtimeDeployment.producerInstance.toUpperCase() }, + { secretBase64Url: 42 }, + { secretBase64Url: '*' }, + { secretBase64Url: 'A' }, + { secretBase64Url: 'AA' }, + { eventIdentifierSystem: '/relative' }, + { entryNodeIdentifierSystem: '/relative' }, + ])('rejects malformed deployment field set %#', (replacement) => { + expect( + validateDeploymentIdentity({ + ...runtimeDeployment, + ...replacement, + }).ok, + ).toBe(false) + }) + it('rejects unknown identity kinds and every wrong component arity at runtime', () => { const source = identityVectors.find( ({ identityKind }) => identityKind === 'source-output', @@ -311,6 +340,25 @@ describe('Grove exchange protocol v2 identity', () => { ).toBe(false) }) + it('rejects non-string identity components after validating their arity', () => { + const source = identityVectors.find( + ({ identityKind }) => identityKind === 'source-output', + ) + if (source === undefined) throw new Error('Missing source-output vector.') + const components: unknown[] = [...source.components] + components[1] = 42 + expect( + deriveOpaqueIdentifier( + runtimeDeployment, + 'source-output', + components as never, + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'invalid-identifier', path: ['components'] }], + }) + }) + it('rejects empty or malformed Unicode only at the typed opaque-identity boundary', () => { for (const vector of identityVectors) { for (const invalid of ['', 'prefix\ud800suffix']) { @@ -428,6 +476,39 @@ describe('Grove exchange protocol v2 identity', () => { ).toBe(false) }) + it('validates event and entry-node lexical boundaries independently', () => { + const event = unwrap( + deriveEventIdentifier( + runtimeDeployment, + groveExchangeProtocol.testVectors.event.sequence, + ), + ) + expect(deriveEventIdentifier(runtimeDeployment, 1 as never).ok).toBe(false) + expect(deriveEventIdentifier(runtimeDeployment, '0').ok).toBe(false) + expect( + deriveEntryNodeValue('/relative', event.value, 'resource', '0').ok, + ).toBe(false) + expect( + deriveEntryNodeValue(event.system, event.value, 42 as never, '0').ok, + ).toBe(false) + expect( + deriveEntryNodeValue(event.system, event.value, 'Uppercase', '0').ok, + ).toBe(false) + expect( + deriveEntryNodeValue(event.system, event.value, 'resource', 0 as never) + .ok, + ).toBe(false) + expect( + deriveEntryNodeValue(event.system, event.value, 'resource', '01').ok, + ).toBe(false) + expect( + deriveEntryNodeIdentifier(runtimeDeployment, event, 'Uppercase', '0').ok, + ).toBe(false) + expect(isEventIdentityValue(42 as never)).toBe(false) + expect(isEventIdentityValue('e2:not-a-uuid:1')).toBe(false) + expect(containsIsolatedSurrogate(42 as never)).toBe(true) + }) + it('rejects noncanonical base64url digest spellings', () => { const noncanonicalDigest = `${'A'.repeat(42)}B` expect(isOpaqueIdentityValue(`v2:key:1:${noncanonicalDigest}`)).toBe(false) diff --git a/packages/fhir/test/foundation.test.ts b/packages/fhir/test/foundation.test.ts index cc15f8c4..5181f6f2 100644 --- a/packages/fhir/test/foundation.test.ts +++ b/packages/fhir/test/foundation.test.ts @@ -8,6 +8,7 @@ import { expectTypeOf } from 'expect-type' import { compareFhirInstants, deepFreeze } from '../src/core/index.js' +import { cloneJsonValue } from '../src/core/json.js' import { collectResults, err, @@ -405,6 +406,58 @@ describe('Result composition', () => { expect.objectContaining({ code: 'invalid-type', path: [1] }), ) } + + const validIssue = { + severity: 'error', + code: 'invalid-type', + path: ['value'], + message: 'Invalid value.', + } as const + const malformedIssues = [ + null, + { ...validIssue, severity: 'fatal' }, + { ...validIssue, code: 42 }, + { ...validIssue, path: 'value' }, + { ...validIssue, path: [true] }, + { ...validIssue, message: 42 }, + { ...validIssue, reason: 42 }, + { ...validIssue, location: 42 }, + ] + for (const malformedIssue of malformedIssues) { + expect( + mapResult( + { + ok: true, + value: 1, + warnings: [malformedIssue], + } as unknown as Result, + (value) => value, + ).ok, + ).toBe(false) + } + }) +}) + +describe('hostile JSON snapshots', () => { + it('rejects symbol properties and non-data array elements', () => { + const object = { value: 1, [Symbol('metadata')]: 2 } + const array = [1] + Object.defineProperty(array, '0', { + enumerable: true, + get: () => 1, + }) + + expect(cloneJsonValue(object).ok).toBe(false) + expect(cloneJsonValue(array).ok).toBe(false) + }) + + it('rejects extra array properties and invalid nested values', () => { + const arrayWithProperty = [1] as number[] & { label?: string } + arrayWithProperty.label = 'not JSON array data' + + expect(cloneJsonValue(arrayWithProperty).ok).toBe(false) + expect(cloneJsonValue([Number.POSITIVE_INFINITY]).ok).toBe(false) + expect(cloneJsonValue({ nested: Number.NaN }).ok).toBe(false) }) }) diff --git a/packages/fhir/test/observation-semantics.test.ts b/packages/fhir/test/observation-semantics.test.ts new file mode 100644 index 00000000..d9911266 --- /dev/null +++ b/packages/fhir/test/observation-semantics.test.ts @@ -0,0 +1,515 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +/* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 fixes the LOINC, UCUM, and Observation category systems to HTTP URIs. */ + +import { z } from 'zod' +import { + groveProfileClaims, + sharedMobileMeasurementCatalog, +} from '../src/mobile/measurement-catalog.generated.js' +import { + adapterMeasurementCatalog, + groveProviderProfileCanonicals, +} from '../src/providers/contract.generated.js' +import { + hasAdmittedMobileObservationProfile, + validateMobileObservationSemantics, +} from '../src/r4/active-observation-semantics.js' +import { decodeGroveRuleDiagnostic } from '../src/r4/diagnostics.js' + +interface MeasurementDefinition { + readonly id: string + readonly profile: string + readonly code: Readonly<{ system: string; code: string }> + readonly requiredCodings?: ReadonlyArray< + Readonly<{ system: string; code: string }> + > + readonly category?: Readonly<{ system: string; code: string }> + readonly effective: 'Period' | 'dateTime' | 'dateTime-or-Period' + readonly method?: Readonly<{ code: string }> + readonly methodChoice?: readonly string[] + readonly valueKind: + 'codeableConcept' | 'components' | 'dateTime' | 'grouping' | 'quantity' + readonly quantity?: Readonly<{ + system: string + code: string + unit: string + }> | null + readonly resultCodeSystem?: string + readonly allowedValues?: readonly string[] + readonly components?: ReadonlyArray< + Readonly<{ + id: string + system: string + code: string + cardinality?: string + quantity: Readonly<{ system: string; code: string; unit: string }> + }> + > +} + +const sharedDefinitions = sharedMobileMeasurementCatalog as unknown as Readonly< + Record +> +const healthKitDefinitions = adapterMeasurementCatalog.healthkit as unknown as + Readonly> | undefined +const profileCanonicals = groveProviderProfileCanonicals as Readonly< + Record +> +const aggregationSystem = + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-aggregation-method' + +const definition = ( + kind: string, + owner: 'healthkit' | 'shared' = 'shared', +): MeasurementDefinition => { + const value = + owner === 'shared' ? sharedDefinitions[kind] : healthKitDefinitions?.[kind] + if (value === undefined) + throw new Error(`Missing ${owner} ${kind} catalog row.`) + return value +} + +const observationFor = ( + row: MeasurementDefinition, +): Record => { + const profile = profileCanonicals[row.profile] + if (profile === undefined) + throw new Error(`Missing canonical for ${row.profile}.`) + const observation: Record = { + resourceType: 'Observation', + meta: { profile: [profile] }, + status: 'final', + code: { + coding: [row.code, ...(row.requiredCodings ?? [])], + }, + } + if (row.category !== undefined) { + observation.category = [{ coding: [row.category] }] + } + if (row.effective === 'Period') { + observation.effectivePeriod = { + start: '2026-08-20T00:00:00Z', + end: '2026-08-20T01:00:00Z', + } + } else { + observation.effectiveDateTime = '2026-08-20T00:00:00Z' + } + if (row.method !== undefined) { + observation.method = { + coding: [{ system: aggregationSystem, code: row.method.code }], + } + } else if (row.methodChoice !== undefined) { + observation.method = { + coding: [{ system: aggregationSystem, code: row.methodChoice[0] }], + } + } + switch (row.valueKind) { + case 'quantity': + observation.valueQuantity = { + value: 1, + system: row.quantity?.system, + code: row.quantity?.code, + unit: row.quantity?.unit, + } + break + case 'codeableConcept': + observation.valueCodeableConcept = { + coding: [ + { + system: row.resultCodeSystem, + code: row.allowedValues?.[0], + }, + ], + } + break + case 'dateTime': + observation.valueDateTime = '1985-04-12' + break + case 'grouping': + observation.hasMember = [{ reference: 'Observation/member' }] + break + case 'components': + observation.component = (row.components ?? []) + .filter(({ cardinality }) => cardinality !== '0..1') + .map((component) => ({ + code: { + coding: [{ system: component.system, code: component.code }], + }, + valueQuantity: { + value: 1, + system: component.quantity.system, + code: component.quantity.code, + unit: component.quantity.unit, + }, + })) + break + } + return observation +} + +const semanticMessages = (resource: unknown): readonly string[] => { + const result = z + .unknown() + .superRefine((value, context) => { + validateMobileObservationSemantics(value, context, ['resource']) + }) + .safeParse(resource) + return result.success ? [] : result.error.issues.map(({ message }) => message) +} + +const expectRule = (resource: unknown, code: string): void => { + const expectedCode = code.replaceAll('\\', '') + expect( + semanticMessages(resource).some((message) => { + const fallbackSeparator = message.indexOf(': ') + const actualCode = + decodeGroveRuleDiagnostic(message)?.code ?? + (fallbackSeparator === -1 ? message : ( + message.slice(0, fallbackSeparator) + )) + return actualCode === expectedCode + }), + ).toBe(true) +} + +describe('catalog-driven Observation semantic boundaries', () => { + it('admits only complete direct profile modes, including both SensorKit modes', () => { + const heartRate = observationFor(definition('heart-rate')) + const platformProfile = + groveProfileClaims.sensorKitPlatformExclusiveClaims.profiles[0] + const hybridProfiles = + groveProfileClaims.sensorKitHybridObservationClaims.profiles + + expect(hasAdmittedMobileObservationProfile(heartRate)).toBe(true) + expect( + hasAdmittedMobileObservationProfile({ + resourceType: 'Observation', + meta: { profile: [platformProfile] }, + }), + ).toBe(true) + expect( + hasAdmittedMobileObservationProfile({ + resourceType: 'Observation', + meta: { profile: hybridProfiles }, + }), + ).toBe(true) + expect( + semanticMessages({ + resourceType: 'Observation', + meta: { + profile: [ + groveProfileClaims.observationAdapterClaim.sharedSensorProfiles[0], + ], + }, + }), + ).toEqual([]) + expect(hasAdmittedMobileObservationProfile(null)).toBe(false) + expect( + hasAdmittedMobileObservationProfile({ resourceType: 'Patient' }), + ).toBe(false) + + for (const profiles of [ + undefined, + [], + [42], + ['https://example.org/unknown'], + [ + profileCanonicals[definition('heart-rate').profile], + profileCanonicals[definition('step-count').profile], + ], + [ + profileCanonicals[definition('heart-rate').profile], + profileCanonicals[definition('heart-rate').profile], + ], + ]) { + expect( + hasAdmittedMobileObservationProfile({ + resourceType: 'Observation', + meta: { profile: profiles }, + }), + ).toBe(false) + } + }) + + it('rejects incomplete connected-provider and general semantic claims', () => { + const withingsExclusiveDefinition = ( + adapterMeasurementCatalog.withings as unknown as Record< + string, + MeasurementDefinition + > + )['withings-vascular-age'] + if (withingsExclusiveDefinition === undefined) + throw new Error('Missing Withings vascular-age catalog row.') + const withingsExclusive = observationFor(withingsExclusiveDefinition) + const unknownProfile = structuredClone( + observationFor(definition('heart-rate')), + ) + unknownProfile.meta = { profile: ['https://example.org/unknown'] } + + expectRule(unknownProfile, 'mobile-output\\.semantic-profile') + expect(semanticMessages({ resourceType: 'Patient' })).toEqual([]) + + // An owner-specific connected-provider profile is incomplete without its + // provider envelope and exact marker. + withingsExclusive.meta = { + profile: [profileCanonicals[withingsExclusiveDefinition.profile]], + } + expectRule(withingsExclusive, 'mobile-output\\.semantic-profile') + }) + + it.each([ + { + name: 'non-final status', + mutate: (observation: Record) => { + observation.status = 'preliminary' + }, + code: 'mobile-heart-rate\\.status', + }, + { + name: 'missing coding list', + mutate: (observation: Record) => { + observation.code = {} + }, + code: 'mobile-heart-rate\\.code', + }, + { + name: 'duplicate primary code', + mutate: (observation: Record) => { + const code = observation.code as { coding: unknown[] } + code.coding.push(structuredClone(code.coding[0])) + }, + code: 'mobile-heart-rate\\.code', + }, + { + name: 'wrong primary code', + mutate: (observation: Record) => { + const code = observation.code as { + coding: Array> + } + code.coding[0] = { system: 'http://loinc.org', code: 'not-admitted' } + }, + code: 'mobile-heart-rate\\.code', + }, + { + name: 'missing category', + mutate: (observation: Record) => { + Reflect.deleteProperty(observation, 'category') + }, + code: 'mobile-heart-rate\\.category', + }, + { + name: 'additional same-system category', + mutate: (observation: Record) => { + const category = observation.category as Array<{ + coding: Array> + }> + category[0]?.coding.push({ + system: 'http://terminology.hl7.org/CodeSystem/observation-category', + code: 'laboratory', + }) + }, + code: 'mobile-heart-rate\\.category', + }, + { + name: 'neither flexible effective choice', + mutate: (observation: Record) => { + Reflect.deleteProperty(observation, 'effectiveDateTime') + }, + code: 'mobile-heart-rate\\.effective', + }, + { + name: 'both flexible effective choices', + mutate: (observation: Record) => { + observation.effectivePeriod = { + start: '2026-08-20T00:00:00Z', + end: '2026-08-20T01:00:00Z', + } + }, + code: 'mobile-heart-rate\\.effective', + }, + { + name: 'missing quantity value', + mutate: (observation: Record) => { + const quantity = observation.valueQuantity as Record + Reflect.deleteProperty(quantity, 'value') + }, + code: 'mobile-output\\.fixed-quantity-unit', + }, + ...['system', 'code', 'unit'].map((field) => ({ + name: `invalid quantity ${field}`, + mutate: (observation: Record) => { + const quantity = observation.valueQuantity as Record + quantity[field] = 'not-admitted' + }, + code: 'mobile-output\\.fixed-quantity-unit', + })), + { + name: 'quantity comparator', + mutate: (observation: Record) => { + const quantity = observation.valueQuantity as Record + quantity.comparator = '<' + }, + code: 'mobile-output\\.fixed-quantity-unit', + }, + ])('rejects a heart-rate Observation with $name', ({ mutate, code }) => { + const observation = observationFor(definition('heart-rate')) + mutate(observation) + expectRule(observation, code) + }) + + it('enforces required supplemental codes and uncategorized measurements', () => { + const resting = observationFor(definition('resting-heart-rate')) + expect(semanticMessages(resting)).toEqual([]) + const restingCode = resting.code as { coding: unknown[] } + restingCode.coding.pop() + expectRule(resting, 'mobile-resting-heart-rate\\.required-coding') + + const sleep = observationFor(definition('sleep-duration')) + sleep.category = [ + { coding: [{ system: 'http://loinc.org', code: 'sleep' }] }, + ] + expectRule(sleep, 'mobile-sleep-duration\\.category') + }) + + it('enforces fixed and choice aggregation methods', () => { + const fixed = observationFor(definition('deep-sleep-duration')) + fixed.method = undefined + expectRule(fixed, 'mobile-deep-sleep-duration\\.method') + + const choice = observationFor(definition('respiratory-rate-average')) + choice.method = {} + expectRule(choice, 'mobile-respiratory-rate-average\\.method') + choice.method = { + coding: [ + { system: aggregationSystem, code: 'not-admitted' }, + { system: aggregationSystem, code: 'also-not-admitted' }, + ], + } + expectRule(choice, 'mobile-respiratory-rate-average\\.method') + }) + + it('enforces bounded effective periods and quantity value domains', () => { + const weight = observationFor(definition('body-weight')) + Reflect.deleteProperty(weight, 'effectiveDateTime') + expectRule(weight, 'mobile-body-weight\\.effective') + + const steps = observationFor(definition('step-count')) + steps.effectivePeriod = { start: '2026-08-20T00:00:00Z' } + expectRule(steps, 'mobile-step-count\\.effective') + steps.effectivePeriod = { + start: '2026-08-20T00:00:00Z', + end: '2026-08-20T00:00:00Z', + } + expectRule(steps, 'mobile-step-count\\.nonzero-period') + ;(steps.valueQuantity as Record).value = -1 + expectRule(steps, 'mobile-step-count\\.value-domain') + ;(steps.valueQuantity as Record).value = 0.5 + expectRule(steps, 'mobile-step-count\\.value-domain') + + const saturation = observationFor(definition('oxygen-saturation')) + ;(saturation.valueQuantity as Record).value = 100 + expect(semanticMessages(saturation)).toEqual([]) + ;(saturation.valueQuantity as Record).value = 101 + expectRule(saturation, 'mobile-oxygen-saturation\\.value-domain') + }) + + it('enforces closed coded, date-time, and grouping result shapes', () => { + const coded = observationFor(definition('intermenstrual-bleeding')) + expect(semanticMessages(coded)).toEqual([]) + coded.valueCodeableConcept = {} + expectRule(coded, 'mobile-intermenstrual-bleeding\\.coded-result') + coded.valueCodeableConcept = { + coding: [ + { system: 'https://example.org/other', code: 'present' }, + { + system: + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-intermenstrual-bleeding', + code: 'not-admitted', + }, + ], + } + expectRule(coded, 'mobile-intermenstrual-bleeding\\.coded-result') + + const dateOfBirth = observationFor(definition('date-of-birth', 'healthkit')) + expect(semanticMessages(dateOfBirth)).toEqual([]) + Reflect.deleteProperty(dateOfBirth, 'valueDateTime') + expectRule(dateOfBirth, 'mobile-date-of-birth\\.date-time-result') + + const food = observationFor(definition('food-correlation', 'healthkit')) + expect(semanticMessages(food)).toEqual([]) + food.hasMember = 'not-a-reference-list' + expectRule(food, 'mobile-food-correlation\\.members') + food.hasMember = [] + expectRule(food, 'mobile-food-correlation\\.members') + }) + + it('enforces distinct required blood-pressure components and their units', () => { + const row = definition('blood-pressure') + const missing = observationFor(row) + missing.component = [] + expectRule(missing, 'mobile-blood-pressure\\.systolic') + + const duplicate = observationFor(row) + const components = duplicate.component as unknown[] + components.push(structuredClone(components[0])) + expectRule(duplicate, 'mobile-blood-pressure\\.systolic') + + const shared = observationFor(row) + const sharedComponents = shared.component as Array> + const first = sharedComponents[0] + const second = sharedComponents[1] + if (first === undefined || second === undefined) + throw new Error('Expected both blood-pressure components.') + first.code = { + coding: [ + ...(first.code as { coding: unknown[] }).coding, + ...(second.code as { coding: unknown[] }).coding, + ], + } + shared.component = [first] + expectRule(shared, 'mobile-blood-pressure\\.diastolic') + + const malformed = observationFor(row) + const malformedComponent = ( + malformed.component as Array> + )[0] + if (malformedComponent === undefined) + throw new Error('Expected a blood-pressure component.') + malformedComponent.code = { + coding: [ + { system: 'http://loinc.org', code: '8480-6' }, + { system: 'http://loinc.org', code: 'not-admitted' }, + ], + } + const quantity = malformedComponent.valueQuantity as Record + quantity.comparator = '<' + quantity.system = 'https://example.org/not-ucum' + quantity.code = 'not-admitted' + quantity.unit = 'not-admitted' + expectRule(malformed, 'mobile-blood-pressure\\.systolic') + + const audiogram = observationFor(definition('audiogram-panel', 'healthkit')) + const optionalComponent = definition('audiogram-panel', 'healthkit') + .components?.[0] + if (optionalComponent === undefined) + throw new Error('Expected an optional audiogram component.') + audiogram.component = [ + { + code: { + coding: [ + { system: optionalComponent.system, code: optionalComponent.code }, + ], + }, + valueQuantity: {}, + }, + ] + expectRule(audiogram, `mobile-audiogram-panel\\.${optionalComponent.id}`) + }) +}) diff --git a/packages/fhir/test/profile-semantics-boundaries.test.ts b/packages/fhir/test/profile-semantics-boundaries.test.ts new file mode 100644 index 00000000..c5831fd6 --- /dev/null +++ b/packages/fhir/test/profile-semantics-boundaries.test.ts @@ -0,0 +1,388 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { z } from 'zod' +import { + adapterSourceMarkerClaims, + groveProfileClaims, +} from '../src/mobile/measurement-catalog.generated.js' +import { + healthKitApplicationDeviceIdentity, + healthKitClinicalRecordAdmission, +} from '../src/providers/contract.generated.js' +import { decodeGroveRuleDiagnostic } from '../src/r4/diagnostics.js' +import { + hasAdmittedActiveDeviceProfile, + hasAdmittedActiveDocumentReferenceProfile, + hasAdmittedAdapterOnlyOutputProfile, + validateProfiledResource, +} from '../src/r4/profile-semantics.js' + +const messagesFor = ( + resource: unknown, + roles: ReadonlyMap = new Map(), +): readonly string[] => { + const result = z + .unknown() + .superRefine((value, context) => { + validateProfiledResource(value, roles, context, ['resource']) + }) + .safeParse(resource) + return result.success ? [] : result.error.issues.map(({ message }) => message) +} + +const expectRule = ( + resource: unknown, + code: string, + roles?: ReadonlyMap, +): void => { + const expectedCode = code.replaceAll('\\', '') + expect( + messagesFor(resource, roles).some((message) => { + const fallbackSeparator = message.indexOf(': ') + const actualCode = + decodeGroveRuleDiagnostic(message)?.code ?? + (fallbackSeparator === -1 ? message : ( + message.slice(0, fallbackSeparator) + )) + return actualCode === expectedCode + }), + ).toBe(true) +} + +const roles = (...values: readonly string[]): ReadonlyMap => + new Map(values.map((value) => [value, 1])) + +describe('profile-specific resource semantic boundaries', () => { + it('rejects malformed direct profile sets before resource-specific checks', () => { + const recordingProfile = + groveProfileClaims.activeDeviceClaims[0].profiles[0] + const documentProfiles = + groveProfileClaims.providerRecordingDocumentClaim.profiles + expect(hasAdmittedActiveDeviceProfile(null)).toBe(false) + expect( + hasAdmittedActiveDeviceProfile({ + resourceType: 'Device', + meta: { profile: [recordingProfile] }, + }), + ).toBe(true) + expect( + hasAdmittedActiveDocumentReferenceProfile({ + resourceType: 'DocumentReference', + meta: { profile: documentProfiles }, + }), + ).toBe(true) + + for (const profile of [ + undefined, + [42], + [recordingProfile, recordingProfile], + ]) { + const device = { resourceType: 'Device', meta: { profile } } + expect(hasAdmittedActiveDeviceProfile(device)).toBe(false) + expectRule(device, 'mobile-support\\.device-profile') + } + expect( + hasAdmittedActiveDocumentReferenceProfile({ resourceType: 'Patient' }), + ).toBe(false) + expect(messagesFor(null)).toEqual([]) + }) + + it('enforces exact Device identity modes after admitting the profile', () => { + const recordingProfile = groveProfileClaims.activeDeviceClaims.find( + ({ id }) => id === 'mobile-recording-device', + )?.profiles[0] + const hostProfile = groveProfileClaims.activeDeviceClaims.find( + ({ id }) => id === 'mobile-host-device', + )?.profiles[0] + if (recordingProfile === undefined || hostProfile === undefined) + throw new Error('Missing Device profile claim.') + + const recording = { + resourceType: 'Device', + meta: { profile: [recordingProfile] }, + identifier: [], + } + expectRule( + recording, + 'mobile-support\\.device-profile', + roles('recording-device'), + ) + expectRule( + recording, + 'mobile-recording-device\\.identities', + roles('recording-device', 'device-snapshot'), + ) + + expectRule( + { + resourceType: 'Device', + meta: { profile: [hostProfile] }, + identifier: [], + }, + 'mobile-host-device\\.identity', + roles('device-snapshot'), + ) + }) + + it('requires the typed Apple bundle identifier on a HealthKit application', () => { + const definition = healthKitApplicationDeviceIdentity.bundleIdentifier + const device: { + resourceType: string + meta: { profile: string[] } + identifier: Array<{ + type: { coding: Array<{ system: string; code: string }> } + system: string + value: string + }> + } = { + resourceType: 'Device', + meta: { profile: [healthKitApplicationDeviceIdentity.profile] }, + identifier: [ + { + type: { + coding: [ + { system: definition.typeSystem, code: definition.typeCode }, + ], + }, + system: definition.system, + value: 'org.example.study', + }, + ], + } + expect(messagesFor(device, roles('device-snapshot'))).toEqual([]) + + for (const mutate of [ + (candidate: typeof device) => { + candidate.identifier = [] + }, + (candidate: typeof device) => { + const identifier = candidate.identifier.at(0) + if (identifier === undefined) throw new Error('Missing identifier.') + identifier.type.coding.push({ + system: definition.typeSystem, + code: 'other', + }) + }, + (candidate: typeof device) => { + const identifier = candidate.identifier.at(0) + if (identifier === undefined) throw new Error('Missing identifier.') + identifier.system = 'https://example.org/wrong-system' + }, + (candidate: typeof device) => { + const identifier = candidate.identifier.at(0) + if (identifier === undefined) throw new Error('Missing identifier.') + identifier.value = ' ' + }, + ]) { + const candidate = structuredClone(device) + mutate(candidate) + expectRule( + candidate, + 'healthkit-application-device\\.bundle-identifier', + roles('device-snapshot'), + ) + } + }) + + it('rejects incomplete recording documents with their specific rules', () => { + const profile = groveProfileClaims.sensorRecordingDocumentClaim.profiles[0] + const document = { + resourceType: 'DocumentReference', + meta: { profile: [profile] }, + } + const requiredRoles = roles( + 'source-record', + 'source-output', + 'source-artifact', + ) + + expectRule( + { ...document, meta: { profile: ['https://example.org/unknown'] } }, + 'mobile-output\\.document-profile', + ) + expectRule(document, 'mobile-recording-document\\.identities') + expectRule( + document, + 'mobile-recording-document\\.identity-roles', + roles('source-record', 'source-output', 'source-artifact', 'unexpected'), + ) + expectRule( + document, + 'mobile-recording-document\\.identity-roles', + new Map([ + ['source-record', 1], + ['source-output', 1], + ['source-artifact', 1], + ['writer-record', 2], + ]), + ) + expectRule( + document, + 'mobile-recording-document\\.required-metadata', + requiredRoles, + ) + expectRule( + document, + 'mobile-recording-document\\.attachment', + requiredRoles, + ) + expectRule(document, 'mobile-recording-document\\.format', requiredRoles) + }) + + it('fails closed for malformed embedded recording data and registry metadata', () => { + const profile = groveProfileClaims.sensorRecordingDocumentClaim.profiles[0] + const requiredRoles = roles( + 'source-record', + 'source-output', + 'source-artifact', + ) + const document = { + resourceType: 'DocumentReference', + meta: { profile: [profile] }, + type: {}, + subject: {}, + date: '2026-08-20T00:00:00Z', + content: [ + { + attachment: { + data: 'AQID', + contentType: 'application/octet-stream', + size: 3, + hash: 'cDeAcZjCKn0rCAc3HXY3eahP388=', + }, + format: { + system: + 'https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format', + version: '0.6.0', + code: 'sensor-recording', + }, + }, + ], + } + for (const data of ['%%%%', '', 'AAAA']) { + const candidate = structuredClone(document) + const content = candidate.content.at(0) + if (content === undefined) throw new Error('Missing recording content.') + content.attachment.data = data + expectRule( + candidate, + 'mobile-recording-document\\.embedded-integrity', + requiredRoles, + ) + } + const invalidFormat = structuredClone(document) + const formatContent = invalidFormat.content.at(0) + if (formatContent === undefined) + throw new Error('Missing recording content.') + formatContent.format = { + system: 'https://example.org/wrong', + version: 'future', + code: 'unknown', + } + expectRule( + invalidFormat, + 'mobile-recording-document\\.format', + requiredRoles, + ) + }) + + it('requires the exact QuestionnaireResponse profile', () => { + const profile = + groveProfileClaims.activeQuestionnaireResponseClaim.profiles[0] + expect( + messagesFor({ + resourceType: 'QuestionnaireResponse', + meta: { profile: [profile] }, + }), + ).toEqual([]) + expectRule( + { resourceType: 'QuestionnaireResponse', meta: { profile: [] } }, + 'mobile-support\\.questionnaire-response-profile', + ) + }) + + it('closes adapter-only output profiles, identity roles, and source markers', () => { + const claim = groveProfileClaims.healthConnectSpecimenClaim + const specimen = { + resourceType: claim.resourceType, + meta: { profile: [claim.profile] }, + identifier: [], + } + const requiredRoles = roles(...claim.requiredIdentifierRoles) + + expect( + hasAdmittedAdapterOnlyOutputProfile({ resourceType: 'Patient' }), + ).toBe(true) + expect(hasAdmittedAdapterOnlyOutputProfile({})).toBe(true) + expect(messagesFor({})).toEqual([]) + expect(hasAdmittedAdapterOnlyOutputProfile(specimen)).toBe(true) + expectRule(specimen, 'mobile-output\\.adapter-only-profile', requiredRoles) + expectRule( + { ...specimen, meta: { profile: ['https://example.org/unknown'] } }, + 'mobile-output\\.adapter-only-profile', + requiredRoles, + ) + expectRule(specimen, 'mobile-output\\.adapter-only-profile', new Map()) + expectRule( + specimen, + 'mobile-output\\.adapter-only-profile', + roles(...claim.requiredIdentifierRoles, 'unexpected'), + ) + + const healthKitClaim = + groveProfileClaims.healthKitPlatformExclusiveResourceClaims[0] + const marker = adapterSourceMarkerClaims + .find(({ adapter }) => adapter === 'healthkit') + ?.markers.find(({ resourceTypes }) => + (resourceTypes as readonly string[]).includes( + healthKitClaim.resourceType, + ), + ) + if (marker === undefined) + throw new Error('Missing HealthKit source marker.') + const healthKitResource = { + resourceType: healthKitClaim.resourceType, + meta: { profile: [healthKitClaim.profile] }, + extension: [{ url: marker.url, valueCode: '' }], + } + expectRule( + healthKitResource, + 'mobile-exchange\\.adapter-source-marker', + roles(...healthKitClaim.requiredIdentifierRoles), + ) + + const neutral = { + resourceType: healthKitClaim.resourceType, + meta: { profile: ['https://example.org/neutral'] }, + extension: [{ url: marker.url, valueCode: 'owned-marker' }], + } + expectRule(neutral, 'mobile-exchange\\.adapter-source-marker') + }) + + it('validates the fixed FHIR release declaration on clinical documents', () => { + const claim = groveProfileClaims.healthKitClinicalRecordDocumentClaim + const definition = healthKitClinicalRecordAdmission.fhirRepresentation + const document = { + resourceType: 'DocumentReference', + meta: { profile: claim.profiles }, + extension: [ + { + url: definition.extensionUrl, + [definition.valueElement]: definition.fixedValue, + valueString: definition.fixedValue, + }, + ], + } + expectRule( + document, + 'healthkit-clinical-record\\.fhir-release', + roles(...claim.requiredIdentifierRoles), + ) + }) +}) diff --git a/packages/fhir/test/provider-identity.test.ts b/packages/fhir/test/provider-identity.test.ts index 3ca77889..58e31d27 100644 --- a/packages/fhir/test/provider-identity.test.ts +++ b/packages/fhir/test/provider-identity.test.ts @@ -513,16 +513,20 @@ describe('Provider R4 graph builder', () => { const hostEntries = result.value.entry.filter( ({ resource }) => resource.resourceType === 'Device' && - resource.meta?.profile?.includes( - 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device', + resource.meta?.profile?.some( + (profile) => + profile === + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device', ), ) expect(hostEntries).toHaveLength(3) for (const applicationDevice of resources(result)) { if ( applicationDevice.resourceType !== 'Device' || - !applicationDevice.meta?.profile?.includes( - 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device', + !applicationDevice.meta?.profile?.some( + (profile) => + profile === + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device', ) ) { continue diff --git a/packages/fhir/test/provider-semantics.test.ts b/packages/fhir/test/provider-semantics.test.ts index b6b0fcfa..d1bfd770 100644 --- a/packages/fhir/test/provider-semantics.test.ts +++ b/packages/fhir/test/provider-semantics.test.ts @@ -26,6 +26,7 @@ import { canonicalizeMobileEffectiveInstant, deriveEntryFullUrl, mobileEffectiveCanonicalizationVectors, + sharedMobileMeasurementCatalog, type MobileMeasurement, } from '../src/mobile/index.js' import { deriveProviderIdentities } from '../src/providers/identity.js' @@ -36,6 +37,11 @@ import { providerOutputCoordinates, type ProviderMeasurementBundleInput, } from '../src/providers/index.js' +import { + providerMeasurementDefinition, + providerMeasurementProfile, + providerObservationProfile, +} from '../src/providers/measurement-definition.js' describe('Provider R4 graph builder', () => { it('rounds Mobile effective instants before enforcing ordering and catalog-owned duration rules', () => { @@ -433,6 +439,110 @@ describe('Provider R4 graph builder', () => { eventSequence: '0', }).ok, ).toBe(false) + + const malformed = [ + { ...valid, provider: 42 }, + { ...valid, sourceType: 42 }, + { ...valid, outputs: 42 }, + { ...valid, outputs: [] }, + { ...valid, outputs: [null] }, + { + ...valid, + outputs: [{ ...valid.outputs[0], unexpected: true }], + }, + { + ...valid, + providerScopeIdentifier: { + ...valid.providerScopeIdentifier, + value: 42, + }, + }, + { + ...valid, + providerScopeIdentifier: { + ...valid.providerScopeIdentifier, + value: ' ', + }, + }, + { + ...valid, + providerScopeIdentifier: { + ...valid.providerScopeIdentifier, + assurance: 'documented-global-key-space', + }, + }, + { ...valid, sourceNativeId: 42 }, + { ...valid, sourceNativeId: ' ' }, + { ...valid, provenanceNodeRole: 'unknown' }, + ] + for (const candidate of malformed) { + expect(deriveProviderIdentities(candidate as never).ok).toBe(false) + } + + expect( + deriveProviderIdentities({ + ...valid, + outputs: [], + provenanceNodeRole: 'retraction-provenance', + }).ok, + ).toBe(true) + + const raw = { + ...valid, + provider: 'oura', + sourceType: 'heartrate', + providerScopeIdentifier: { + ...valid.providerScopeIdentifier, + assurance: 'documented-global-key-space', + }, + outputs: [ + { + kind: 'provider-artifact', + formatCode: 'provider-recording', + partIndex: '0', + }, + ], + } as const + expect(deriveProviderIdentities(raw).ok).toBe(true) + expect( + deriveProviderIdentities({ + ...raw, + outputs: [{ ...raw.outputs[0], partIndex: '01' }], + }).ok, + ).toBe(false) + expect( + deriveProviderIdentities({ + ...raw, + outputs: [{ ...raw.outputs[0], formatCode: 'invented' }], + }).ok, + ).toBe(false) + }) + + it('resolves only shared or exact provider-owned measurement definitions', () => { + expect(providerMeasurementDefinition('withings', 'heart-rate')).toBe( + sharedMobileMeasurementCatalog['heart-rate'], + ) + expect( + providerMeasurementProfile('withings', 'withings-vascular-age'), + ).toBe( + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-vascular-age', + ) + expect(providerObservationProfile('withings')).toBe( + 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation', + ) + expect( + providerMeasurementDefinition('withings', 'oura-cardiovascular-age'), + ).toBeUndefined() + expect( + providerMeasurementDefinition('unknown' as never, 'heart-rate'), + ).toBe(sharedMobileMeasurementCatalog['heart-rate']) + expect( + providerMeasurementDefinition('unknown' as never, 'unknown'), + ).toBeUndefined() + expect( + providerMeasurementProfile('unknown' as never, 'unknown'), + ).toBeUndefined() + expect(providerObservationProfile('unknown' as never)).toBeUndefined() }) it('reports malformed Provider parser and identity inputs without throwing', () => { diff --git a/packages/fhir/test/retraction.test.ts b/packages/fhir/test/retraction.test.ts index 4682cf1c..b90b71d8 100644 --- a/packages/fhir/test/retraction.test.ts +++ b/packages/fhir/test/retraction.test.ts @@ -366,8 +366,10 @@ describe('Provider source-record retraction', () => { const device = active.entry.find( ({ resource }) => resource.resourceType === 'Device' && - resource.meta?.profile?.includes( - 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-device', + resource.meta?.profile?.some( + (profile) => + profile === + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-device', ), )?.resource if (device?.resourceType !== 'Device') { diff --git a/packages/fhir/test/zod-generator-boundary.test.ts b/packages/fhir/test/zod-generator-boundary.test.ts index 68fd2b52..3487a485 100644 --- a/packages/fhir/test/zod-generator-boundary.test.ts +++ b/packages/fhir/test/zod-generator-boundary.test.ts @@ -18,6 +18,33 @@ interface GeneratorElement { readonly maxValueInteger?: unknown } +const numericPrimitive = (name: string, regex: string) => ({ + resourceType: 'StructureDefinition', + name, + kind: 'primitive-type', + snapshot: { + element: [ + { path: name, min: 0, max: '1' }, + { + path: `${name}.value`, + min: 0, + max: '1', + type: [ + { + code: 'http://hl7.org/fhirpath/System.String', + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/regex', + valueString: regex, + }, + ], + }, + ], + }, + ], + }, +}) + const structure = ( name: string, elements: readonly GeneratorElement[] = [{ path: name, min: 0, max: '1' }], @@ -103,6 +130,17 @@ describe('Zod generator package boundary', () => { ).toThrow(/type\[0\]\.code/u) }) + it('rejects a changed numeric primitive pattern before generation', () => { + const definition = numericPrimitive( + 'positiveInt', + '[1-9][0-9]*);globalThis.compromised=true;//', + ) + + expect(() => + assertSafeGeneratorInput(input(new Map([['positiveInt', definition]]))), + ).toThrow(/positiveInt\.value regex/u) + }) + it.each([ ['minimum', '0);globalThis.compromised=true;//', '1'], ['maximum', 0, '1);globalThis.compromised=true;//'], diff --git a/packages/fhir/test/zod-schemas.test.ts b/packages/fhir/test/zod-schemas.test.ts index 216de322..6e02e9ae 100644 --- a/packages/fhir/test/zod-schemas.test.ts +++ b/packages/fhir/test/zod-schemas.test.ts @@ -203,7 +203,8 @@ describe('generated Zod schemas', () => { }) it('bounds the integer primitives the specification bounds', () => { - // positiveInt and unsignedInt state their range as a lexical pattern rather than a min. + // The package boundary verifies the published lexical patterns; runtime schemas enforce the + // equivalent value-space constraints after JSON parsing has discarded the original lexeme. expect(() => positiveIntSchema.parse(0)).toThrow() expect(positiveIntSchema.parse(1)).toBe(1) expect(() => unsignedIntSchema.parse(-1)).toThrow() From 251ce03bc721b3eac0b87944b6f4c4bf0449f7b6 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Sat, 29 Aug 2026 15:55:23 -0700 Subject: [PATCH 04/31] Align TypeScript with the authoritative Grove FHIR contract --- packages/fhir/README.md | 2 +- .../catalog/measurement-capabilities.json | 2 +- .../fhir/fixtures/conformance/manifest.json | 8 +- .../conformance/mobile-exchange/corpus.json | 100 ++++-- .../mobile-exchange/exchange-bundle.json | 129 +++++-- .../mobile-exchange/retraction-bundle.json | 22 +- .../resources/mobile-active-energy.json | 34 +- .../resources/mobile-basal-energy.json | 34 +- ...le-blood-glucose-unspecified-specimen.json | 34 +- .../resources/mobile-blood-pressure.json | 34 +- .../resources/mobile-body-fat-percentage.json | 34 +- .../resources/mobile-body-height.json | 34 +- .../resources/mobile-body-temperature.json | 34 +- .../resources/mobile-body-water-mass.json | 34 +- .../resources/mobile-body-weight.json | 34 +- .../resources/mobile-bone-mass.json | 34 +- .../resources/mobile-deep-sleep-duration.json | 34 +- .../resources/mobile-distance.json | 34 +- .../mobile-electrodermal-activity.json | 34 +- .../resources/mobile-flights-climbed.json | 34 +- .../mobile-heart-rate-variability-rmssd.json | 34 +- .../mobile-heart-rate-variability-sdnn.json | 34 +- .../resources/mobile-heart-rate.json | 34 +- .../resources/mobile-lean-body-mass.json | 34 +- .../mobile-light-sleep-duration.json | 34 +- ...obile-oxygen-saturation-daily-average.json | 34 +- .../resources/mobile-oxygen-saturation.json | 34 +- .../resources/mobile-rem-sleep-duration.json | 34 +- .../mobile-respiratory-rate-average.json | 34 +- .../resources/mobile-resting-heart-rate.json | 262 -------------- .../mobile-resting-heart-rate.json.license | 3 - .../resources/mobile-skin-temperature.json | 34 +- .../mobile-sleep-awake-duration.json | 34 +- .../resources/mobile-sleep-duration.json | 34 +- .../resources/mobile-sleep-heart-rate.json | 34 +- .../resources/mobile-step-count.json | 34 +- .../conformance/resources/mobile-vo2-max.json | 34 +- .../conformance/resources/mobile-workout.json | 34 +- ...-api-resting-heart-rate-daily-average.json | 34 +- .../provider-oura-daily-activity.json | 54 +-- ...provider-oura-oura-cardiovascular-age.json | 34 +- .../provider-oura-oura-readiness-score.json | 34 +- .../provider-primary-output-retraction.json | 20 +- .../provider-source-artifact-retraction.json | 20 +- .../provider-withings-body-fat-mass.json | 34 +- ...ovider-withings-corrected-qt-interval.json | 34 +- ...der-withings-extracellular-water-mass.json | 34 +- ...der-withings-intracellular-water-mass.json | 34 +- .../provider-withings-muscle-mass.json | 34 +- .../provider-withings-pr-interval.json | 34 +- .../provider-withings-qrs-duration.json | 34 +- .../provider-withings-qt-interval.json | 34 +- ...-withings-sleeping-heart-rate-average.json | 340 ------------------ ...s-sleeping-heart-rate-average.json.license | 3 - ...-atrial-fibrillation-notification-ecg.json | 34 +- ...-atrial-fibrillation-notification-ppg.json | 34 +- ...-withings-withings-nerve-health-score.json | 34 +- ...withings-withings-pulse-wave-velocity.json | 34 +- ...ovider-withings-withings-vascular-age.json | 34 +- ...-withings-withings-visceral-fat-index.json | 34 +- ...ecording-google-health-api-heart-rate.json | 43 ++- .../resources/recording-oura-heartrate.json | 43 ++- .../recording-withings-activityintraday.json | 43 ++- .../recording-withings-sleepintraday.json | 43 ++- .../google-health-heart-rate-recording.json | 2 +- .../normalized/oura-heart-rate-recording.json | 2 +- .../withings-activity-intraday-recording.json | 2 +- .../withings-sleep-intraday-recording.json | 2 +- packages/fhir/grove-fhir.json | 4 +- packages/fhir/scripts/browser-smoke.mjs | 4 +- .../scripts/generate-conformance-fixtures.mjs | 28 +- .../scripts/generate-measurement-catalog.mjs | 23 +- packages/fhir/scripts/package-smoke.mjs | 2 +- packages/fhir/src/mobile/contract.ts | 2 +- packages/fhir/src/mobile/identity.ts | 18 +- .../mobile/measurement-catalog.generated.ts | 153 ++++---- packages/fhir/src/mobile/types.ts | 4 +- .../fhir/src/providers/contract.generated.ts | 129 +++---- packages/fhir/src/providers/identity.ts | 2 +- packages/fhir/src/providers/recording.ts | 18 +- packages/fhir/src/r4/exchange-envelope.ts | 6 +- packages/fhir/src/r4/graph-schema-utils.ts | 39 +- packages/fhir/src/r4/profile-semantics.ts | 4 +- .../fhir/src/r4/retraction-graph-semantics.ts | 2 +- packages/fhir/test/exchange-corpus.test.ts | 6 +- packages/fhir/test/exchange-protocol.test.ts | 30 +- packages/fhir/test/exports.test.ts | 2 +- packages/fhir/test/mobile.test.ts | 2 +- .../test/profile-semantics-boundaries.test.ts | 1 - packages/fhir/test/provider-identity.test.ts | 6 +- packages/fhir/test/recording.test.ts | 20 +- packages/fhir/test/retraction.test.ts | 4 +- 92 files changed, 1399 insertions(+), 1853 deletions(-) delete mode 100644 packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json delete mode 100644 packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json.license delete mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json delete mode 100644 packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json.license diff --git a/packages/fhir/README.md b/packages/fhir/README.md index 99560806..d3812513 100644 --- a/packages/fhir/README.md +++ b/packages/fhir/README.md @@ -155,7 +155,7 @@ Four conventions are visible in that call and hold throughout the package: This is why the example parses its literals instead of passing raw strings. - **You supply governed identity inputs; the package derives the graph.** The deployment owns the key, key-epoch-specific Identifier systems, producer instance, monotonic event sequence, account pseudonym, source-native id, and all event times. - Source and output identifiers use the Grove v2 domain-separated HMAC contract; internal `urn:uuid` links use UUIDv5 over the selected complete Identifier pair. + Source and output identifiers use the Grove v0 domain-separated HMAC contract; internal `urn:uuid` links use UUIDv5 over the selected complete Identifier pair. An exact retry reuses the event sequence, `occurred`, `recorded`, `assembled`, and payload. A changed source revision or payload is a new event with a new sequence. - **Results are frozen plain JSON.** Serialize them, store them, send them; there are no classes and no hidden state. diff --git a/packages/fhir/catalog/measurement-capabilities.json b/packages/fhir/catalog/measurement-capabilities.json index ed06cade..5bf57edb 100644 --- a/packages/fhir/catalog/measurement-capabilities.json +++ b/packages/fhir/catalog/measurement-capabilities.json @@ -1,5 +1,5 @@ { - "schemaVersion": 1, + "schemaVersion": 0, "igVersion": "0.6.0", "canonicalBase": "https://grovealliance.org/fhir", "recordConversion": { diff --git a/packages/fhir/fixtures/conformance/manifest.json b/packages/fhir/fixtures/conformance/manifest.json index 52f69305..02ac6044 100644 --- a/packages/fhir/fixtures/conformance/manifest.json +++ b/packages/fhir/fixtures/conformance/manifest.json @@ -1,5 +1,5 @@ { - "schemaVersion": 1, + "schemaVersion": 0, "fhirVersion": "4.0.1", "producer": { "name": "Grove TypeScript", "version": "0.0.0" }, "packages": [ @@ -244,12 +244,6 @@ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" ] }, - { - "path": "resources/provider-withings-sleeping-heart-rate-average.json", - "requiredProfiles": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, { "path": "resources/provider-withings-body-fat-mass.json", "requiredProfiles": [ diff --git a/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json b/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json index bf1b5f96..ec65a47d 100644 --- a/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json +++ b/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json @@ -1,5 +1,5 @@ { - "schemaVersion": 1, + "schemaVersion": 0, "version": "0.6.0", "bases": [ { "id": "mobile-exchange", "path": "exchange-bundle.json" }, @@ -80,7 +80,7 @@ ], "expectedRule": { "code": "mobile-exchange.event-identity", - "reason": "Bundle.identifier.value must be the canonical e2 producer UUID and positive sequence form.", + "reason": "Bundle.identifier.value must be the canonical e0 producer UUID and positive sequence form.", "location": "Bundle.identifier.value", "severity": "error" } @@ -92,7 +92,7 @@ { "op": "replace", "path": "/entry/0/extension/0/valueIdentifier/value", - "value": "n2:patient:0:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "value": "n0:patient:0:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" } ], "expectedRule": { @@ -120,7 +120,7 @@ { "op": "replace", "path": "/entry/2/resource/identifier/1/system", - "value": "https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1" + "value": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1" } ], "expectedRule": { @@ -187,7 +187,7 @@ ], "expectedRule": { "code": "mobile-retraction.opaque-target", - "reason": "A retraction target must use the exact canonical v2 HMAC identity previously emitted.", + "reason": "A retraction target must use the exact canonical v0 HMAC identity previously emitted.", "location": "Provenance.target[0].identifier.value", "severity": "error" } @@ -267,12 +267,12 @@ { "op": "replace", "path": "/entry/2/resource/subject/reference", - "value": "urn:uuid:8f87a88a-8744-5116-8901-9274f62472ac" + "value": "urn:uuid:aef62dba-db7e-5a99-a854-3b5ec312312f" } ], "expectedRule": { "code": "mobile-exchange.reference-target-type", - "reason": "An Observation subject resolves to a Patient entry, not merely to any existing fullUrl.", + "reason": "Every governed Patient reference resolves to a Patient entry, not merely to any existing fullUrl.", "location": "Observation.subject.reference", "severity": "error" } @@ -301,7 +301,7 @@ { "op": "add", "path": "/entry/3/resource/entity/0/what/reference", - "value": "urn:uuid:3314ab4c-4ab3-536f-a556-e3b6ff97762d" + "value": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" } ], "expectedRule": { @@ -347,8 +347,8 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1", - "value": "v2:test-key:1:1y-KjnPSgOBDI5pE69EVN8U7Oaen_WM4cPL-sBxDKgw" + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" } } } @@ -376,7 +376,7 @@ ], "expectedRule": { "code": "mobile-exchange.reference-shape", - "reason": "A governed Reference is exclusively resolving-literal or identifier-only logical, never both.", + "reason": "Each governed path has its declared singular or repeating shape and contains valid Reference objects that are exclusively resolving-literal or identifier-only logical, never both.", "location": "Observation.subject", "severity": "error" } @@ -398,7 +398,7 @@ ], "expectedRule": { "code": "mobile-exchange.logical-patient-reference", - "reason": "An identifier-only logical Patient Reference carries the exact Patient type and one complete absolute-system pseudonym Identifier.", + "reason": "An identifier-only logical Patient Reference carries the exact Patient type and one complete absolute-system pseudonym Identifier without a Grove role or protocol-reserved system.", "location": "Observation.subject", "severity": "error" } @@ -490,8 +490,8 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1", - "value": "v2:test-key:1:1y-KjnPSgOBDI5pE69EVN8U7Oaen_WM4cPL-sBxDKgw" + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" }, { "type": { @@ -502,13 +502,13 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v2/test-key/1", - "value": "v2:test-key:1:_sudqhgnrCDWXSytfxliI_fzd0qjFUMFobOIMP6Z5gw" + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" } ], "status": "current", "subject": { - "reference": "urn:uuid:3314ab4c-4ab3-536f-a556-e3b6ff97762d" + "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" }, "date": "2026-08-19T17:30:00Z", "content": [ @@ -551,6 +551,66 @@ "severity": "error" } }, + { + "id": "questionnaire-response-subject-wrong-target", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/4/resource/subject", + "value": { + "reference": "urn:uuid:aef62dba-db7e-5a99-a854-3b5ec312312f" + } + } + ], + "expectedRule": { + "code": "mobile-exchange.reference-target-type", + "reason": "Every governed Patient reference resolves to a Patient entry, not merely to any existing fullUrl.", + "location": "QuestionnaireResponse.subject.reference", + "severity": "error" + } + }, + { + "id": "questionnaire-response-subject-reserved-system", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/4/resource/subject/identifier/system", + "value": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ], + "expectedRule": { + "code": "mobile-exchange.logical-patient-reference", + "reason": "An identifier-only logical Patient Reference carries the exact Patient type and one complete absolute-system pseudonym Identifier without a Grove role or protocol-reserved system.", + "location": "QuestionnaireResponse.subject.identifier.system", + "severity": "error" + } + }, + { + "id": "questionnaire-response-subject-grove-role", + "base": "mobile-exchange", + "patch": [ + { + "op": "add", + "path": "/entry/4/resource/subject/identifier/type", + "value": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + ], + "expectedRule": { + "code": "mobile-exchange.logical-patient-reference", + "reason": "An identifier-only logical Patient Reference carries the exact Patient type and one complete absolute-system pseudonym Identifier without a Grove role or protocol-reserved system.", + "location": "QuestionnaireResponse.subject.identifier.type", + "severity": "error" + } + }, { "id": "disconnected-supporting-patient", "base": "mobile-exchange", @@ -559,7 +619,7 @@ "op": "add", "path": "/entry/-", "value": { - "fullUrl": "urn:uuid:19e3b5cf-5c03-57a3-a9ff-6c0cb3025341", + "fullUrl": "urn:uuid:27d2d3da-934e-52bd-ad54-d720752df13f", "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -572,8 +632,8 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v2", - "value": "n2:patient:1:wTdrAaKPt3xdV3VbfjLhG5fqVbRBmQe1q_UMGSBeopU" + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:1:qN9xaTdoz-LfvONC7FgEUNMz06zyWhfzD0khPhtjVig" } } ], diff --git a/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json b/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json index 24e5c74a..a7028e75 100644 --- a/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json +++ b/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json @@ -17,8 +17,8 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-event-v2", - "value": "e2:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42" + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42" }, "entry": [ { @@ -35,12 +35,12 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v2", - "value": "n2:patient:0:wMhqO_kIRVFR8etQrCbgX0FXhz7h3f-LnsfUhRcrC9A" + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:BMLA8cDb0x8fjNhv70m6l8dDSHIk2zWfOScLokevBQQ" } } ], - "fullUrl": "urn:uuid:3314ab4c-4ab3-536f-a556-e3b6ff97762d", + "fullUrl": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085", "resource": { "resourceType": "Patient", "id": "GroveMobileExchangePatientExample", @@ -66,12 +66,12 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v2/test-key/1", - "value": "v2:test-key:1:UEdJx7eiKElpKK_oPmjTu3fK072oUz8JKwgtF0CBe8A" + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:Q2sME_dGFj94xyprI1HtMokER94GYsHINZi0ilnsnRY" } } ], - "fullUrl": "urn:uuid:8f87a88a-8744-5116-8901-9274f62472ac", + "fullUrl": "urn:uuid:aef62dba-db7e-5a99-a854-3b5ec312312f", "resource": { "resourceType": "Device", "id": "GroveMobileExchangeApplicationExample", @@ -90,8 +90,8 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v2/test-key/1", - "value": "v2:test-key:1:UEdJx7eiKElpKK_oPmjTu3fK072oUz8JKwgtF0CBe8A" + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:Q2sME_dGFj94xyprI1HtMokER94GYsHINZi0ilnsnRY" } ], "deviceName": [ @@ -114,12 +114,12 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v2/test-key/1", - "value": "v2:test-key:1:_sudqhgnrCDWXSytfxliI_fzd0qjFUMFobOIMP6Z5gw" + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" } } ], - "fullUrl": "urn:uuid:13bc1990-e0d8-57cf-8772-8f959664241d", + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", "resource": { "resourceType": "Observation", "id": "GroveMobileExchangeHeartRateExample", @@ -147,8 +147,8 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1", - "value": "v2:test-key:1:1y-KjnPSgOBDI5pE69EVN8U7Oaen_WM4cPL-sBxDKgw" + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" }, { "type": { @@ -159,15 +159,15 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v2/test-key/1", - "value": "v2:test-key:1:_sudqhgnrCDWXSytfxliI_fzd0qjFUMFobOIMP6Z5gw" + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" } ], "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", "valueReference": { - "reference": "urn:uuid:8f87a88a-8744-5116-8901-9274f62472ac" + "reference": "urn:uuid:aef62dba-db7e-5a99-a854-3b5ec312312f" } } ], @@ -184,10 +184,13 @@ } ], "subject": { - "reference": "urn:uuid:3314ab4c-4ab3-536f-a556-e3b6ff97762d" + "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" }, "performer": [ - { "reference": "urn:uuid:3314ab4c-4ab3-536f-a556-e3b6ff97762d" } + { "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" } + ], + "derivedFrom": [ + { "reference": "urn:uuid:e516dd51-04d0-502d-abcf-b5c870915c17" } ], "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", "issued": "2026-08-20T17:30:02Z", @@ -213,12 +216,12 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v2", - "value": "n2:conversion-provenance:0:SwGD7C4DT5_9kgIOQ9h7W8I4UdwJPuEOnkh2TgQVwko" + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:8JmcQF7rmULm9uJBkHWruJwfMu3GJTxGWqXWn2DGqWk" } } ], - "fullUrl": "urn:uuid:9908feb7-0370-5f06-a689-f8afa210eb41", + "fullUrl": "urn:uuid:71abc484-b9ee-511e-b22a-5b35d026d620", "resource": { "resourceType": "Provenance", "id": "GroveMobileExchangeProvenanceExample", @@ -248,7 +251,7 @@ ] }, "who": { - "reference": "urn:uuid:8f87a88a-8744-5116-8901-9274f62472ac" + "reference": "urn:uuid:aef62dba-db7e-5a99-a854-3b5ec312312f" } } ], @@ -266,18 +269,90 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1", - "value": "v2:test-key:1:1y-KjnPSgOBDI5pE69EVN8U7Oaen_WM4cPL-sBxDKgw" + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" } } } ], "target": [ - { "reference": "urn:uuid:13bc1990-e0d8-57cf-8772-8f959664241d" } + { "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" } ], "occurredDateTime": "2026-08-20T10:30:02-07:00", "recorded": "2026-08-20T17:30:02Z" } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:kkYuCTNdLr58lk-fG2HJJQyMfv6quSSBFKSNI331b3k" + } + } + ], + "fullUrl": "urn:uuid:e516dd51-04d0-502d-abcf-b5c870915c17", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } } ], "timestamp": "2026-08-20T17:30:02Z" diff --git a/packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json b/packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json index 85752411..a22b0179 100644 --- a/packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json +++ b/packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json @@ -17,8 +17,8 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-event-v2", - "value": "e2:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:43" + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:43" }, "entry": [ { @@ -35,12 +35,12 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v2", - "value": "n2:retraction-provenance:0:kry9tA8wKNb5VGuzulTG-msyl6iRqHJuR2y1eK-7VOc" + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:retraction-provenance:0:o0Qrv-QfsfhozEJ5jeXVloNGbKrhYM9Rk6eziKoPmV4" } } ], - "fullUrl": "urn:uuid:39f1693a-d1b3-597c-998f-0837dbd1d6d1", + "fullUrl": "urn:uuid:f99194b8-aaf8-5fdb-91fa-19309bdd6716", "resource": { "resourceType": "Provenance", "id": "GroveMobileRetractionProvenanceExample", @@ -81,8 +81,8 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v2/test-key/1", - "value": "v2:test-key:1:4hdZeDHmzYotDYvBYG1H5YZPDtfGbMiEftruwDvait4" + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:JUZooa4NZX-rlMn9-NC8W078xzEuUO9X3Xo1mgk0Vac" } } } @@ -101,8 +101,8 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1", - "value": "v2:test-key:1:1y-KjnPSgOBDI5pE69EVN8U7Oaen_WM4cPL-sBxDKgw" + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" } } } @@ -126,8 +126,8 @@ } ] }, - "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v2/test-key/1", - "value": "v2:test-key:1:_sudqhgnrCDWXSytfxliI_fzd0qjFUMFobOIMP6Z5gw" + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" } } ], diff --git a/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json b/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json index 877fadac..371d7b2b 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:1" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:1" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:hUNfkRjsHis9rWj4K6qNLC-jFLQKKWL2Uh6UYxYBy9I" + "value": "v0:fixture-key:1:LycACgbJZx-uRos8zb-0w0PEXMdM7LoW4Sx1IvbEOC0" } } ], - "fullUrl": "urn:uuid:623544dc-4d5f-5814-9d3d-3064f83c39fa", + "fullUrl": "urn:uuid:fff33fbe-2314-590f-b6b1-26c530f6c4ec", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:Vu0a2YcqoF0_Kmt53rvO840SVdAxu8nPfHv7sZnTABw", + "value": "v0:fixture-key:1:cBsbfJ2FTYUguk5OcwFuDMb03zuen7zrG5xlFAniFTo", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:hUNfkRjsHis9rWj4K6qNLC-jFLQKKWL2Uh6UYxYBy9I", + "value": "v0:fixture-key:1:LycACgbJZx-uRos8zb-0w0PEXMdM7LoW4Sx1IvbEOC0", "type": { "coding": [ { @@ -132,11 +132,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:likPDb5FjrpWDRZ0rPo5XUmjfcOT7rGpsg5T6dCWODs" + "value": "v0:fixture-key:1:NDTrpLTq9kiSi5FYG4ZY37w64LhMOM9ADTH9-v65Ydg" } } ], - "fullUrl": "urn:uuid:2c249665-5425-548b-a009-422bd516b0e0", + "fullUrl": "urn:uuid:43fca3bd-f88d-5c9f-aee2-c1823335ee58", "resource": { "resourceType": "Device", "meta": { @@ -147,7 +147,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:likPDb5FjrpWDRZ0rPo5XUmjfcOT7rGpsg5T6dCWODs", + "value": "v0:fixture-key:1:NDTrpLTq9kiSi5FYG4ZY37w64LhMOM9ADTH9-v65Ydg", "type": { "coding": [ { @@ -181,11 +181,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:e2j1gdQw2SwNKr5tOCv1YnM8xF-NxUolN2oVdQ9vrMI" + "value": "v0:fixture-key:1:U1GXWdV08ZaO2_cubyu1oaGFDXhnzLx8h_JwZVf6hUU" } } ], - "fullUrl": "urn:uuid:f6cd5dc4-f09e-56b0-a457-afd61347504e", + "fullUrl": "urn:uuid:536d5716-4fb1-5959-baf9-3eac3a86d493", "resource": { "resourceType": "Device", "meta": { @@ -196,7 +196,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:e2j1gdQw2SwNKr5tOCv1YnM8xF-NxUolN2oVdQ9vrMI", + "value": "v0:fixture-key:1:U1GXWdV08ZaO2_cubyu1oaGFDXhnzLx8h_JwZVf6hUU", "type": { "coding": [ { @@ -244,11 +244,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:BzzQh80b6pPy829ESNyCiTXdG-DRf0tbuj1NZT0GvtA" + "value": "n0:conversion-provenance:0:JSuiGqhmUZK1zgym0F6SZEhogZ906ZSkQhTZqK76W5s" } } ], - "fullUrl": "urn:uuid:1bb2312b-a95b-5608-9fa7-61308ca3d104", + "fullUrl": "urn:uuid:f6732476-2d2b-5b8f-baf8-af76c3e56564", "resource": { "resourceType": "Provenance", "meta": { @@ -257,7 +257,7 @@ ] }, "target": [ - { "reference": "urn:uuid:623544dc-4d5f-5814-9d3d-3064f83c39fa" } + { "reference": "urn:uuid:fff33fbe-2314-590f-b6b1-26c530f6c4ec" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -284,7 +284,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:f6cd5dc4-f09e-56b0-a457-afd61347504e" + "reference": "urn:uuid:536d5716-4fb1-5959-baf9-3eac3a86d493" } } ], @@ -294,7 +294,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:Vu0a2YcqoF0_Kmt53rvO840SVdAxu8nPfHv7sZnTABw", + "value": "v0:fixture-key:1:cBsbfJ2FTYUguk5OcwFuDMb03zuen7zrG5xlFAniFTo", "type": { "coding": [ { @@ -318,7 +318,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:2c249665-5425-548b-a009-422bd516b0e0" + "reference": "urn:uuid:43fca3bd-f88d-5c9f-aee2-c1823335ee58" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json b/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json index e953257e..d526f2fd 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:2" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:2" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:tof8kHTSRnd3Qox8cyYy7Trj59RBB5ctSu7xwykXafk" + "value": "v0:fixture-key:1:bGqlV4JlMEN1EGtpQcfgYP75jVLT5SCtsBfvwHIq8Yw" } } ], - "fullUrl": "urn:uuid:ba77bf34-3274-5ee3-85b3-0846d7870737", + "fullUrl": "urn:uuid:5a6a734e-af3b-51a9-85c0-7823a9e40c9b", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:Nc4nBJazF3IhkS_aoV_cRugPi_PauEGuVoAeb3JUJ48", + "value": "v0:fixture-key:1:J_lqguXT9TuoBwwFhLXLFidICzFsaqScnK0TqMeAB-I", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:tof8kHTSRnd3Qox8cyYy7Trj59RBB5ctSu7xwykXafk", + "value": "v0:fixture-key:1:bGqlV4JlMEN1EGtpQcfgYP75jVLT5SCtsBfvwHIq8Yw", "type": { "coding": [ { @@ -132,11 +132,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:rOfDhVWbEC3J_msDqtXEJ2Qb5M-0HSfHFMAeOvkC0qs" + "value": "v0:fixture-key:1:W1F_GPOEQhXLa4op2E407dR0glREa1hcySXn8NQqIcc" } } ], - "fullUrl": "urn:uuid:420ade87-e452-505a-a153-f069b0e88265", + "fullUrl": "urn:uuid:e39f62eb-c867-532e-a955-db7eb7354ad9", "resource": { "resourceType": "Device", "meta": { @@ -147,7 +147,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:rOfDhVWbEC3J_msDqtXEJ2Qb5M-0HSfHFMAeOvkC0qs", + "value": "v0:fixture-key:1:W1F_GPOEQhXLa4op2E407dR0glREa1hcySXn8NQqIcc", "type": { "coding": [ { @@ -181,11 +181,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:qEp85FoL6U1CRgvupplIUAz4Fo1KjSRFndZLUU8kTRU" + "value": "v0:fixture-key:1:u5TL9xXam0QJkWk5kHlvukPvutOg61R5brwRjRupWoY" } } ], - "fullUrl": "urn:uuid:927c2fbb-a5c2-5ea7-aa96-08b3f15d66e9", + "fullUrl": "urn:uuid:ca0593ae-cc72-5870-93e6-799df283ea91", "resource": { "resourceType": "Device", "meta": { @@ -196,7 +196,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:qEp85FoL6U1CRgvupplIUAz4Fo1KjSRFndZLUU8kTRU", + "value": "v0:fixture-key:1:u5TL9xXam0QJkWk5kHlvukPvutOg61R5brwRjRupWoY", "type": { "coding": [ { @@ -244,11 +244,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:b4gGw5G3fzDLigIZMN9EC50VyLvstLXvjY2l3oy_sGw" + "value": "n0:conversion-provenance:0:bPr9qu85WDJRHQ_RIPRZRurly-o77lEQL6RYw1bsNog" } } ], - "fullUrl": "urn:uuid:4916b7cd-f8d1-5ddb-9a14-a55731877f53", + "fullUrl": "urn:uuid:02d8d13a-725f-5a98-8adb-7bdbe1d2c8ac", "resource": { "resourceType": "Provenance", "meta": { @@ -257,7 +257,7 @@ ] }, "target": [ - { "reference": "urn:uuid:ba77bf34-3274-5ee3-85b3-0846d7870737" } + { "reference": "urn:uuid:5a6a734e-af3b-51a9-85c0-7823a9e40c9b" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -284,7 +284,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:927c2fbb-a5c2-5ea7-aa96-08b3f15d66e9" + "reference": "urn:uuid:ca0593ae-cc72-5870-93e6-799df283ea91" } } ], @@ -294,7 +294,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:Nc4nBJazF3IhkS_aoV_cRugPi_PauEGuVoAeb3JUJ48", + "value": "v0:fixture-key:1:J_lqguXT9TuoBwwFhLXLFidICzFsaqScnK0TqMeAB-I", "type": { "coding": [ { @@ -318,7 +318,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:420ade87-e452-505a-a153-f069b0e88265" + "reference": "urn:uuid:e39f62eb-c867-532e-a955-db7eb7354ad9" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json b/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json index 40a04db4..e88508d5 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:3" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:3" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:wU-7VZxFRZGNCrwlBOECjEPYmjl7mAsK_kyXmHtaG68" + "value": "v0:fixture-key:1:sIHAUnrc78LCCIylboGtuD4FkMHZ5IcR9_DscGIbDZA" } } ], - "fullUrl": "urn:uuid:5695bce2-b1c9-50c9-9b24-1f74cfd2defa", + "fullUrl": "urn:uuid:26835628-7045-5579-8509-ced6196c4250", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:_fLMU3KSLn8Iu61CRQYpPS9H2OBRz_7V1QWJXSnD5Eo", + "value": "v0:fixture-key:1:27EvW0bT37TNTuvwrW8l4xOSfYIZ4S1ve49v2O8NLmc", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:wU-7VZxFRZGNCrwlBOECjEPYmjl7mAsK_kyXmHtaG68", + "value": "v0:fixture-key:1:sIHAUnrc78LCCIylboGtuD4FkMHZ5IcR9_DscGIbDZA", "type": { "coding": [ { @@ -124,11 +124,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:TV7SWW_WZgyeo8sglzZ0jExkXwIoRn8k2JuNLXWBkvo" + "value": "v0:fixture-key:1:xoJ9_x4-WlVdj2qnI_cuEu_h-kqCU_k3oJypbqVKAu0" } } ], - "fullUrl": "urn:uuid:b52e371d-2d28-5eff-8423-775113d08e3c", + "fullUrl": "urn:uuid:75eef62c-12cb-597b-b50b-e7bfc7492aa9", "resource": { "resourceType": "Device", "meta": { @@ -139,7 +139,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:TV7SWW_WZgyeo8sglzZ0jExkXwIoRn8k2JuNLXWBkvo", + "value": "v0:fixture-key:1:xoJ9_x4-WlVdj2qnI_cuEu_h-kqCU_k3oJypbqVKAu0", "type": { "coding": [ { @@ -173,11 +173,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:urPyM4-DaIvxKij6cJYUVuk_Af2lFfrZ60SlvcVCEUI" + "value": "v0:fixture-key:1:uZjSlR8HhG3r4orDE1tamL-7ADdF63SWpfVP3gi9PFk" } } ], - "fullUrl": "urn:uuid:aba81b80-2d1e-5661-a24b-da63951e0c5f", + "fullUrl": "urn:uuid:23ee24a9-6a50-50db-aa09-b10818b5c5d9", "resource": { "resourceType": "Device", "meta": { @@ -188,7 +188,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:urPyM4-DaIvxKij6cJYUVuk_Af2lFfrZ60SlvcVCEUI", + "value": "v0:fixture-key:1:uZjSlR8HhG3r4orDE1tamL-7ADdF63SWpfVP3gi9PFk", "type": { "coding": [ { @@ -236,11 +236,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:RAfPzgJF0Ued-HNqD6NgoKm4LXPmhrIXjiswttjVAp4" + "value": "n0:conversion-provenance:0:sHgw9yxq8eUbRbaTd3vKq88xsxm3beKvqRPkO5x4og8" } } ], - "fullUrl": "urn:uuid:e550c720-a79b-5fea-952b-26807d890f23", + "fullUrl": "urn:uuid:68e61fb6-a6d4-5a6e-9b53-e4805ae0cad0", "resource": { "resourceType": "Provenance", "meta": { @@ -249,7 +249,7 @@ ] }, "target": [ - { "reference": "urn:uuid:5695bce2-b1c9-50c9-9b24-1f74cfd2defa" } + { "reference": "urn:uuid:26835628-7045-5579-8509-ced6196c4250" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -276,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:aba81b80-2d1e-5661-a24b-da63951e0c5f" + "reference": "urn:uuid:23ee24a9-6a50-50db-aa09-b10818b5c5d9" } } ], @@ -286,7 +286,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:_fLMU3KSLn8Iu61CRQYpPS9H2OBRz_7V1QWJXSnD5Eo", + "value": "v0:fixture-key:1:27EvW0bT37TNTuvwrW8l4xOSfYIZ4S1ve49v2O8NLmc", "type": { "coding": [ { @@ -310,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:b52e371d-2d28-5eff-8423-775113d08e3c" + "reference": "urn:uuid:75eef62c-12cb-597b-b50b-e7bfc7492aa9" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json b/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json index 6ced73a5..8045ab66 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:4" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:4" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:KjMs_5Z98EM7M1K6bocf3dhsJaVnjtZ5pzvqNerZCgk" + "value": "v0:fixture-key:1:h_61-KWtwbgmorpjpV-hiBahMW9-fvRxBFWS04MHpZE" } } ], - "fullUrl": "urn:uuid:7b8c648b-2755-5849-a83e-a3c2f79a10b3", + "fullUrl": "urn:uuid:d0c0a2a4-8c81-5907-96ad-834c557418b0", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:lW2AnLDPykLNrwVliVjBURtgQxUmjX14dyMmjwJjvXE", + "value": "v0:fixture-key:1:WuyZHg4lYDvE4FhjSBqArtkRJOGxBtiM7vDOOT9R98o", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:KjMs_5Z98EM7M1K6bocf3dhsJaVnjtZ5pzvqNerZCgk", + "value": "v0:fixture-key:1:h_61-KWtwbgmorpjpV-hiBahMW9-fvRxBFWS04MHpZE", "type": { "coding": [ { @@ -154,11 +154,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:0pxSLYyzgHNNruap6eMTXhN-D9vApkbHKf6UOCkWeAA" + "value": "v0:fixture-key:1:7EV1uB106bpRYwJR9HZKAN1rb0oq6FZj9UsFj44FATU" } } ], - "fullUrl": "urn:uuid:5d0c9510-347f-59ad-8cc5-12b5551dfb7a", + "fullUrl": "urn:uuid:5458fdad-053a-55ae-a659-ef8cba4cc18f", "resource": { "resourceType": "Device", "meta": { @@ -169,7 +169,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:0pxSLYyzgHNNruap6eMTXhN-D9vApkbHKf6UOCkWeAA", + "value": "v0:fixture-key:1:7EV1uB106bpRYwJR9HZKAN1rb0oq6FZj9UsFj44FATU", "type": { "coding": [ { @@ -203,11 +203,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:NGLBYjP9Dq9zj_e4R4WphOIbfaj2rO1IKS5xlMjOMXo" + "value": "v0:fixture-key:1:StGlLYOvF2jMhvbe3CuauF8rp7vuRc6brEJfxxgE0Kg" } } ], - "fullUrl": "urn:uuid:c4ae7203-7b58-54c2-9169-35a2deecadda", + "fullUrl": "urn:uuid:6a8866f4-f3eb-5f62-b099-17c43cbe4d40", "resource": { "resourceType": "Device", "meta": { @@ -218,7 +218,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:NGLBYjP9Dq9zj_e4R4WphOIbfaj2rO1IKS5xlMjOMXo", + "value": "v0:fixture-key:1:StGlLYOvF2jMhvbe3CuauF8rp7vuRc6brEJfxxgE0Kg", "type": { "coding": [ { @@ -266,11 +266,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:O906sk_10OKvpUfAlH82r4aVcwSp-nvQW9AmjNVVczw" + "value": "n0:conversion-provenance:0:HSbFZAF1J0Z2AiQllTCYg47pEddf3_bqHBCtM_kKHTc" } } ], - "fullUrl": "urn:uuid:acb0d854-54f9-57ae-b2f2-9d176e85df51", + "fullUrl": "urn:uuid:e643c8be-4880-58e2-a645-64479de7bd05", "resource": { "resourceType": "Provenance", "meta": { @@ -279,7 +279,7 @@ ] }, "target": [ - { "reference": "urn:uuid:7b8c648b-2755-5849-a83e-a3c2f79a10b3" } + { "reference": "urn:uuid:d0c0a2a4-8c81-5907-96ad-834c557418b0" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -306,7 +306,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:c4ae7203-7b58-54c2-9169-35a2deecadda" + "reference": "urn:uuid:6a8866f4-f3eb-5f62-b099-17c43cbe4d40" } } ], @@ -316,7 +316,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:lW2AnLDPykLNrwVliVjBURtgQxUmjX14dyMmjwJjvXE", + "value": "v0:fixture-key:1:WuyZHg4lYDvE4FhjSBqArtkRJOGxBtiM7vDOOT9R98o", "type": { "coding": [ { @@ -340,7 +340,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:5d0c9510-347f-59ad-8cc5-12b5551dfb7a" + "reference": "urn:uuid:5458fdad-053a-55ae-a659-ef8cba4cc18f" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json b/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json index 13fba565..0a14f389 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:5" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:5" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:qcWNB4ioRgosOhigXqLo236vVq8UmpeMjxz5ftYVVO0" + "value": "v0:fixture-key:1:CKv-2R1W4otUFhAE3dZ_GJwX4LjRSCYU12bc8TFxhu0" } } ], - "fullUrl": "urn:uuid:64c1ff29-36a9-5284-aba0-d243656cd1c8", + "fullUrl": "urn:uuid:2d4505ad-24d1-53b0-b8bc-ba6c0021bd20", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:sQp4MBeBxJ8xNX6Ab5Nrs1AVbUT_mkjrLUGAcraOIAw", + "value": "v0:fixture-key:1:WxoGom7ZNS31zsgBLsLtuMnFQ-Zb2PV_UbLkLzxlVwE", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:qcWNB4ioRgosOhigXqLo236vVq8UmpeMjxz5ftYVVO0", + "value": "v0:fixture-key:1:CKv-2R1W4otUFhAE3dZ_GJwX4LjRSCYU12bc8TFxhu0", "type": { "coding": [ { @@ -124,11 +124,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:V-eifyyiDduzD3EwBiqbiC4OBL8skWdZdKR8wWlUjgE" + "value": "v0:fixture-key:1:jFa4dFMQh0ReZZ1PAqwe-uI9UcTq5j2IlwWALGF5Vs8" } } ], - "fullUrl": "urn:uuid:7406c83a-c51d-513e-98c4-2d9e85d3ffef", + "fullUrl": "urn:uuid:47afd496-5885-5729-816d-fcbce4f015bb", "resource": { "resourceType": "Device", "meta": { @@ -139,7 +139,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:V-eifyyiDduzD3EwBiqbiC4OBL8skWdZdKR8wWlUjgE", + "value": "v0:fixture-key:1:jFa4dFMQh0ReZZ1PAqwe-uI9UcTq5j2IlwWALGF5Vs8", "type": { "coding": [ { @@ -173,11 +173,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:hHFSobV3E0PtndBhIBT_buq1Zk0VXreuN_3z0ZnKWII" + "value": "v0:fixture-key:1:dMnfWEQk2xChFyh1BU6bUz9-invN_CJx4a7K5VJwnoA" } } ], - "fullUrl": "urn:uuid:62183acb-8e9d-5e2f-a65b-7229964065df", + "fullUrl": "urn:uuid:7dca5c46-8cbe-5996-a7dc-e1d38cc12bf4", "resource": { "resourceType": "Device", "meta": { @@ -188,7 +188,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:hHFSobV3E0PtndBhIBT_buq1Zk0VXreuN_3z0ZnKWII", + "value": "v0:fixture-key:1:dMnfWEQk2xChFyh1BU6bUz9-invN_CJx4a7K5VJwnoA", "type": { "coding": [ { @@ -236,11 +236,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:y9p0wl98ovA-2fnCmvTJypjA5Vlrx-SVxkrXrzQcryw" + "value": "n0:conversion-provenance:0:JXf4d0INS6AhdsWU91DRhrSqJ2KDrn6Owd6DutdAz7s" } } ], - "fullUrl": "urn:uuid:a809c0d3-d9d2-5d86-8d78-39031a41dc35", + "fullUrl": "urn:uuid:8488a61c-011b-5ba8-8cf6-0efe011d95b5", "resource": { "resourceType": "Provenance", "meta": { @@ -249,7 +249,7 @@ ] }, "target": [ - { "reference": "urn:uuid:64c1ff29-36a9-5284-aba0-d243656cd1c8" } + { "reference": "urn:uuid:2d4505ad-24d1-53b0-b8bc-ba6c0021bd20" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -276,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:62183acb-8e9d-5e2f-a65b-7229964065df" + "reference": "urn:uuid:7dca5c46-8cbe-5996-a7dc-e1d38cc12bf4" } } ], @@ -286,7 +286,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:sQp4MBeBxJ8xNX6Ab5Nrs1AVbUT_mkjrLUGAcraOIAw", + "value": "v0:fixture-key:1:WxoGom7ZNS31zsgBLsLtuMnFQ-Zb2PV_UbLkLzxlVwE", "type": { "coding": [ { @@ -310,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:7406c83a-c51d-513e-98c4-2d9e85d3ffef" + "reference": "urn:uuid:47afd496-5885-5729-816d-fcbce4f015bb" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-height.json b/packages/fhir/fixtures/conformance/resources/mobile-body-height.json index f46d326b..727107cc 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-height.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-height.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:6" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:6" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:d9Fng8711pHYFae9CkmqLfUQ9sDxtP_JVAbEzQyJCkY" + "value": "v0:fixture-key:1:lS0QSYana6P-mPdj0GPjtR0-h3xqSULeep0f1ocR0Q4" } } ], - "fullUrl": "urn:uuid:de5c75b0-e861-5954-9378-0f603d75a2ad", + "fullUrl": "urn:uuid:9e59de7e-0518-5df2-ac5e-54685bd09a8a", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:hoaya6AcIo6qhTyA_H_REs0Ru4wkdnTVGAvabB6eGZs", + "value": "v0:fixture-key:1:i73_ClycuTs0StcJTYSAMPNYRgSA_kzfiagjX50PQMQ", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:d9Fng8711pHYFae9CkmqLfUQ9sDxtP_JVAbEzQyJCkY", + "value": "v0:fixture-key:1:lS0QSYana6P-mPdj0GPjtR0-h3xqSULeep0f1ocR0Q4", "type": { "coding": [ { @@ -136,11 +136,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:Roe1aB4-W2k8fuYsrh-IoJwUdt26cVWv9NRthHBNn_U" + "value": "v0:fixture-key:1:nkrVMmK5Z25j6D3O4YFbpTz9gcI7nYUYvMMhJgWqk2I" } } ], - "fullUrl": "urn:uuid:9ba44d16-edab-5b14-96c5-6c306777cedf", + "fullUrl": "urn:uuid:c7fdc3f1-5bba-524a-93df-19600a63219a", "resource": { "resourceType": "Device", "meta": { @@ -151,7 +151,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:Roe1aB4-W2k8fuYsrh-IoJwUdt26cVWv9NRthHBNn_U", + "value": "v0:fixture-key:1:nkrVMmK5Z25j6D3O4YFbpTz9gcI7nYUYvMMhJgWqk2I", "type": { "coding": [ { @@ -185,11 +185,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:gAR8INBvNzlTMKzvBj2ghpzHndeTFSEalg-VJPykKhY" + "value": "v0:fixture-key:1:JE_Aqlhn9MnxX9wx4t8kJfLES5YcXywYLTzTB97A-Ec" } } ], - "fullUrl": "urn:uuid:11353867-ec18-5c06-b35c-93f4e9710098", + "fullUrl": "urn:uuid:559b0b4e-5460-5ab7-9a53-c3267ecaa7af", "resource": { "resourceType": "Device", "meta": { @@ -200,7 +200,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:gAR8INBvNzlTMKzvBj2ghpzHndeTFSEalg-VJPykKhY", + "value": "v0:fixture-key:1:JE_Aqlhn9MnxX9wx4t8kJfLES5YcXywYLTzTB97A-Ec", "type": { "coding": [ { @@ -248,11 +248,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:zP9yHJniUtAMG2YAmoyy26umVdVxnOOX9xBoboL0Ql8" + "value": "n0:conversion-provenance:0:xznW5Dtyrp_tkOdtvAdJ4XrJLURFAb463IsGE_0Fz6o" } } ], - "fullUrl": "urn:uuid:30a63761-a7f6-55e5-9850-5a5f47c77629", + "fullUrl": "urn:uuid:f9680bea-996f-5f05-aefa-758aaece4bf1", "resource": { "resourceType": "Provenance", "meta": { @@ -261,7 +261,7 @@ ] }, "target": [ - { "reference": "urn:uuid:de5c75b0-e861-5954-9378-0f603d75a2ad" } + { "reference": "urn:uuid:9e59de7e-0518-5df2-ac5e-54685bd09a8a" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -288,7 +288,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:11353867-ec18-5c06-b35c-93f4e9710098" + "reference": "urn:uuid:559b0b4e-5460-5ab7-9a53-c3267ecaa7af" } } ], @@ -298,7 +298,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:hoaya6AcIo6qhTyA_H_REs0Ru4wkdnTVGAvabB6eGZs", + "value": "v0:fixture-key:1:i73_ClycuTs0StcJTYSAMPNYRgSA_kzfiagjX50PQMQ", "type": { "coding": [ { @@ -322,7 +322,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:9ba44d16-edab-5b14-96c5-6c306777cedf" + "reference": "urn:uuid:c7fdc3f1-5bba-524a-93df-19600a63219a" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json b/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json index d67ddb7c..5a7fc61c 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:7" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:7" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:yoER3Zd7tX6f4ByZmKSfaeqYkkTEE-rPc2Qt59XTclw" + "value": "v0:fixture-key:1:0xwJuyM9Oj7kD9ci-wffqZeinUCEg_Aisu4THXZqzu0" } } ], - "fullUrl": "urn:uuid:521ec2f4-8138-5538-9a7f-df0690eb0b6a", + "fullUrl": "urn:uuid:991342d3-ad32-5672-a092-cea731aa446d", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:E84Nt2m-BegHKDiHFkVDfyWk3AVMdm12xG1ODcKbVZk", + "value": "v0:fixture-key:1:kglrsTbI-sisN9jpTRtdE0mf3W_iLOyNBWx6GQ54xQg", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:yoER3Zd7tX6f4ByZmKSfaeqYkkTEE-rPc2Qt59XTclw", + "value": "v0:fixture-key:1:0xwJuyM9Oj7kD9ci-wffqZeinUCEg_Aisu4THXZqzu0", "type": { "coding": [ { @@ -136,11 +136,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:FedpU6eibK7zsoMuQdcOL6RNBo39KXsBTI5QU7xDzKc" + "value": "v0:fixture-key:1:H2r0u1o9Dno6Uy4_UDnq2S31gXiT9aTLQ6W9BR3RA9E" } } ], - "fullUrl": "urn:uuid:c0980002-c5b7-5c31-98ff-f4a484408c56", + "fullUrl": "urn:uuid:def97c74-f47d-54ab-a249-d81dd508ea0c", "resource": { "resourceType": "Device", "meta": { @@ -151,7 +151,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:FedpU6eibK7zsoMuQdcOL6RNBo39KXsBTI5QU7xDzKc", + "value": "v0:fixture-key:1:H2r0u1o9Dno6Uy4_UDnq2S31gXiT9aTLQ6W9BR3RA9E", "type": { "coding": [ { @@ -185,11 +185,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:YYAwvFTpHuMZtZr_Wcsu4RaZ2q-2TTylSrkKdx67Rdw" + "value": "v0:fixture-key:1:FtVDXLqRlsdnWhRUEUCd9tzd5DUdLpPXw0X7QM0rBhI" } } ], - "fullUrl": "urn:uuid:6d00c90c-e221-5856-9cdc-8b0174a6abb3", + "fullUrl": "urn:uuid:b4924f51-3fc2-590f-9659-21e430b4df06", "resource": { "resourceType": "Device", "meta": { @@ -200,7 +200,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:YYAwvFTpHuMZtZr_Wcsu4RaZ2q-2TTylSrkKdx67Rdw", + "value": "v0:fixture-key:1:FtVDXLqRlsdnWhRUEUCd9tzd5DUdLpPXw0X7QM0rBhI", "type": { "coding": [ { @@ -248,11 +248,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:M-J3CJ8bnST_su7f0CJSdRhl4krNugSlFE3mUX_nb9I" + "value": "n0:conversion-provenance:0:-rZfHFRZQvg1O-TQfGbqlkvRpL2apKTfsgrv3ezhgmU" } } ], - "fullUrl": "urn:uuid:30383105-87de-58b7-884a-5e06fee030bc", + "fullUrl": "urn:uuid:076db598-eef8-518e-9b1c-d5cbdb574d39", "resource": { "resourceType": "Provenance", "meta": { @@ -261,7 +261,7 @@ ] }, "target": [ - { "reference": "urn:uuid:521ec2f4-8138-5538-9a7f-df0690eb0b6a" } + { "reference": "urn:uuid:991342d3-ad32-5672-a092-cea731aa446d" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -288,7 +288,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:6d00c90c-e221-5856-9cdc-8b0174a6abb3" + "reference": "urn:uuid:b4924f51-3fc2-590f-9659-21e430b4df06" } } ], @@ -298,7 +298,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:E84Nt2m-BegHKDiHFkVDfyWk3AVMdm12xG1ODcKbVZk", + "value": "v0:fixture-key:1:kglrsTbI-sisN9jpTRtdE0mf3W_iLOyNBWx6GQ54xQg", "type": { "coding": [ { @@ -322,7 +322,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:c0980002-c5b7-5c31-98ff-f4a484408c56" + "reference": "urn:uuid:def97c74-f47d-54ab-a249-d81dd508ea0c" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json b/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json index 56d4b10b..c1e79035 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:8" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:8" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:h9kGKBCHIgGjAcm9EiiOm6sfAbCpmB-GQy1oykjz8wQ" + "value": "v0:fixture-key:1:KcKSTEKCMUmSLMXdnqW-kAJHPCGqCtRhmZ5hP5-F58k" } } ], - "fullUrl": "urn:uuid:b7f5136c-eea4-5438-aa80-2fb29ef82696", + "fullUrl": "urn:uuid:011209c1-efd2-5615-b48d-7267fb8f587d", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:5XQDUzVcIcQNjBpRWfS-I_BT2KpigimyXXwA682S-5s", + "value": "v0:fixture-key:1:8BGOdrODZOHaIi5_vcptrhCvIp9pXLGBex92mcxbFWc", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:h9kGKBCHIgGjAcm9EiiOm6sfAbCpmB-GQy1oykjz8wQ", + "value": "v0:fixture-key:1:KcKSTEKCMUmSLMXdnqW-kAJHPCGqCtRhmZ5hP5-F58k", "type": { "coding": [ { @@ -124,11 +124,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:-2zVZ4txzSTB6-XppERPQK6oqqL0SwJTW9J7jOh0EIc" + "value": "v0:fixture-key:1:KBU_fiP09JGvryRvEZWtIf1i90yfqC_o6ewWCsBvTSc" } } ], - "fullUrl": "urn:uuid:a2c48014-937d-5005-bed8-e7ef0b0bacda", + "fullUrl": "urn:uuid:a2b1a3b7-2aa7-539c-a900-f1e0a8345a13", "resource": { "resourceType": "Device", "meta": { @@ -139,7 +139,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:-2zVZ4txzSTB6-XppERPQK6oqqL0SwJTW9J7jOh0EIc", + "value": "v0:fixture-key:1:KBU_fiP09JGvryRvEZWtIf1i90yfqC_o6ewWCsBvTSc", "type": { "coding": [ { @@ -173,11 +173,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:GaBqtWuQqnijksVa1NTG0qeo1smeMumnpwkkQCGTa9M" + "value": "v0:fixture-key:1:kwYq49ze78zEF1E8fXhHPgjMxt1ovZafXb6h2T0x-yk" } } ], - "fullUrl": "urn:uuid:aff37b9c-be2f-5c75-8259-05362029c74d", + "fullUrl": "urn:uuid:c0960bc7-f32d-5bc3-a7a4-09bb09504534", "resource": { "resourceType": "Device", "meta": { @@ -188,7 +188,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:GaBqtWuQqnijksVa1NTG0qeo1smeMumnpwkkQCGTa9M", + "value": "v0:fixture-key:1:kwYq49ze78zEF1E8fXhHPgjMxt1ovZafXb6h2T0x-yk", "type": { "coding": [ { @@ -236,11 +236,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:_YhVUy6nf5scLFK7jX-qoNE217b3rRBucnx8z1tKU30" + "value": "n0:conversion-provenance:0:ImvOlb_AJ5BnIOM2MfGr9duCtXlQo6JDZBERq3S1vD8" } } ], - "fullUrl": "urn:uuid:861a9ac4-08d6-5415-8df7-1d5e9fe82d0b", + "fullUrl": "urn:uuid:b82881c7-a010-519e-af56-703c8f328f5e", "resource": { "resourceType": "Provenance", "meta": { @@ -249,7 +249,7 @@ ] }, "target": [ - { "reference": "urn:uuid:b7f5136c-eea4-5438-aa80-2fb29ef82696" } + { "reference": "urn:uuid:011209c1-efd2-5615-b48d-7267fb8f587d" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -276,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:aff37b9c-be2f-5c75-8259-05362029c74d" + "reference": "urn:uuid:c0960bc7-f32d-5bc3-a7a4-09bb09504534" } } ], @@ -286,7 +286,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:5XQDUzVcIcQNjBpRWfS-I_BT2KpigimyXXwA682S-5s", + "value": "v0:fixture-key:1:8BGOdrODZOHaIi5_vcptrhCvIp9pXLGBex92mcxbFWc", "type": { "coding": [ { @@ -310,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:a2c48014-937d-5005-bed8-e7ef0b0bacda" + "reference": "urn:uuid:a2b1a3b7-2aa7-539c-a900-f1e0a8345a13" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json b/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json index 5fdbb11f..bba28080 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:9" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:9" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:ViQ5nBSYkWaGjgIOfrC0Cs6GI6uRnUKVuVs8dsOUYJo" + "value": "v0:fixture-key:1:ikY1sCJq7ZCjIGDZ-WA0UvDbDbFi8qfkM_PYHXagJbQ" } } ], - "fullUrl": "urn:uuid:7fc5da96-9f22-5cde-8cc9-b889045f5390", + "fullUrl": "urn:uuid:21266e9b-0e9c-5b9f-b625-37d12c3fd1b7", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:n430W8JBTayb8eVy_kI9tGK9M6ZDc2Dey0A5KzyXdyA", + "value": "v0:fixture-key:1:IBIE7eiXHQUPEZS7lzzC8j4DhYMU186jXgbefxDYR40", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:ViQ5nBSYkWaGjgIOfrC0Cs6GI6uRnUKVuVs8dsOUYJo", + "value": "v0:fixture-key:1:ikY1sCJq7ZCjIGDZ-WA0UvDbDbFi8qfkM_PYHXagJbQ", "type": { "coding": [ { @@ -136,11 +136,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:pGpKK7LYGcor-3wzfgIdlwR28wJC2e-xMoTGNcmAQpw" + "value": "v0:fixture-key:1:4fwTGS-w5KNNRuvXElRWQ0k1wmPyVsywbcoR7vuHvUo" } } ], - "fullUrl": "urn:uuid:d4fb1bec-f360-5dff-a871-80c79236b34e", + "fullUrl": "urn:uuid:12813d7d-be61-5da2-af9e-5fba50ebe4ba", "resource": { "resourceType": "Device", "meta": { @@ -151,7 +151,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:pGpKK7LYGcor-3wzfgIdlwR28wJC2e-xMoTGNcmAQpw", + "value": "v0:fixture-key:1:4fwTGS-w5KNNRuvXElRWQ0k1wmPyVsywbcoR7vuHvUo", "type": { "coding": [ { @@ -185,11 +185,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:7_O0HF3rl6mXRn1oPfGTdra8dFBKlNiCuAJL_fNIrwo" + "value": "v0:fixture-key:1:Kt2LgM4ys7hckDgWMZuwshW4x_uSmcPjB8XB5Howui0" } } ], - "fullUrl": "urn:uuid:9dc94f05-5c77-525c-99a7-e94f92715b33", + "fullUrl": "urn:uuid:d1f4eef8-89b3-5952-866a-998a0a0e5bad", "resource": { "resourceType": "Device", "meta": { @@ -200,7 +200,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:7_O0HF3rl6mXRn1oPfGTdra8dFBKlNiCuAJL_fNIrwo", + "value": "v0:fixture-key:1:Kt2LgM4ys7hckDgWMZuwshW4x_uSmcPjB8XB5Howui0", "type": { "coding": [ { @@ -248,11 +248,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:IHeCdNY_GBiWa47yEPhQw4qD35VQEtNQvlosx5HgZq4" + "value": "n0:conversion-provenance:0:9Ik9SMiyAYvhRBM566jFJU2Z99kwPJO9hZ7gCOYA-_k" } } ], - "fullUrl": "urn:uuid:05d228db-9f2f-5252-9b7a-e45dc027cad7", + "fullUrl": "urn:uuid:a2c7aae9-1014-5b67-bc48-26b7a4cb65a0", "resource": { "resourceType": "Provenance", "meta": { @@ -261,7 +261,7 @@ ] }, "target": [ - { "reference": "urn:uuid:7fc5da96-9f22-5cde-8cc9-b889045f5390" } + { "reference": "urn:uuid:21266e9b-0e9c-5b9f-b625-37d12c3fd1b7" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -288,7 +288,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:9dc94f05-5c77-525c-99a7-e94f92715b33" + "reference": "urn:uuid:d1f4eef8-89b3-5952-866a-998a0a0e5bad" } } ], @@ -298,7 +298,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:n430W8JBTayb8eVy_kI9tGK9M6ZDc2Dey0A5KzyXdyA", + "value": "v0:fixture-key:1:IBIE7eiXHQUPEZS7lzzC8j4DhYMU186jXgbefxDYR40", "type": { "coding": [ { @@ -322,7 +322,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:d4fb1bec-f360-5dff-a871-80c79236b34e" + "reference": "urn:uuid:12813d7d-be61-5da2-af9e-5fba50ebe4ba" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json b/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json index ee01fe59..a4e6bc71 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:10" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:10" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:3rbLqhNYeNnDB1dGdK0zMIURMBrsGPVl9tKRpYJuo6I" + "value": "v0:fixture-key:1:aBvPRliVxlOZKqhr6Q3Jyj9MM77CD8lEczzB0vw4PtA" } } ], - "fullUrl": "urn:uuid:7ee96abb-9672-5924-9053-7b8055044d12", + "fullUrl": "urn:uuid:6b5ea6d7-2c92-5510-ba1f-f995de70bd27", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:IS4eY2DFXzYkNRSYFYU_FS3PV5xgR7Zi-F0Ze6ro7yQ", + "value": "v0:fixture-key:1:nviR_nQRpK91BIeM0wRmRPO1IKJpSZAqx980Bj4ZE50", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:3rbLqhNYeNnDB1dGdK0zMIURMBrsGPVl9tKRpYJuo6I", + "value": "v0:fixture-key:1:aBvPRliVxlOZKqhr6Q3Jyj9MM77CD8lEczzB0vw4PtA", "type": { "coding": [ { @@ -124,11 +124,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:AFhh8uFcvjFZUUjP2BbK6rQBbl8GVMtrSXoRkKADwW0" + "value": "v0:fixture-key:1:9-IltYX5VPvJ6JIKK6WfmZ-NRa4b5lONWt0QPWIAm48" } } ], - "fullUrl": "urn:uuid:9aafe683-ff70-5e6f-8f2e-0d47708f715a", + "fullUrl": "urn:uuid:4609626d-627d-54bb-8e80-a0694b386d9c", "resource": { "resourceType": "Device", "meta": { @@ -139,7 +139,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:AFhh8uFcvjFZUUjP2BbK6rQBbl8GVMtrSXoRkKADwW0", + "value": "v0:fixture-key:1:9-IltYX5VPvJ6JIKK6WfmZ-NRa4b5lONWt0QPWIAm48", "type": { "coding": [ { @@ -173,11 +173,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:DI3p3QmuQOm_TqIxevxlk4n1IKGz6xKSz--LPt8C8uM" + "value": "v0:fixture-key:1:HyentO5mz54bN1oU8dwpNpGIwjGaJlkj7Ujg1HvVy3I" } } ], - "fullUrl": "urn:uuid:b890e4a8-8ed9-5c35-a930-90da5a89137b", + "fullUrl": "urn:uuid:af05eabe-bdae-5fc1-8515-8632dc115687", "resource": { "resourceType": "Device", "meta": { @@ -188,7 +188,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:DI3p3QmuQOm_TqIxevxlk4n1IKGz6xKSz--LPt8C8uM", + "value": "v0:fixture-key:1:HyentO5mz54bN1oU8dwpNpGIwjGaJlkj7Ujg1HvVy3I", "type": { "coding": [ { @@ -236,11 +236,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:4JYtJvg5s5dqWMDSu9_6kH1CJUkPDC5HoErSVze-_kw" + "value": "n0:conversion-provenance:0:SSey1ttqPqMR4AflrsB5eKNcr-DhZ0z9Sdkg_Z5gvE0" } } ], - "fullUrl": "urn:uuid:88fa466b-024a-5d36-867a-eab9fc7fcc06", + "fullUrl": "urn:uuid:55a5abc3-e3e8-5469-892c-8d7c91c09f05", "resource": { "resourceType": "Provenance", "meta": { @@ -249,7 +249,7 @@ ] }, "target": [ - { "reference": "urn:uuid:7ee96abb-9672-5924-9053-7b8055044d12" } + { "reference": "urn:uuid:6b5ea6d7-2c92-5510-ba1f-f995de70bd27" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -276,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:b890e4a8-8ed9-5c35-a930-90da5a89137b" + "reference": "urn:uuid:af05eabe-bdae-5fc1-8515-8632dc115687" } } ], @@ -286,7 +286,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:IS4eY2DFXzYkNRSYFYU_FS3PV5xgR7Zi-F0Ze6ro7yQ", + "value": "v0:fixture-key:1:nviR_nQRpK91BIeM0wRmRPO1IKJpSZAqx980Bj4ZE50", "type": { "coding": [ { @@ -310,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:9aafe683-ff70-5e6f-8f2e-0d47708f715a" + "reference": "urn:uuid:4609626d-627d-54bb-8e80-a0694b386d9c" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json index 40ecb6cb..2e444e69 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:11" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:11" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:OhgkKcSIJTZQDgzR07JK97OEHcNiYmO-1lTQwjJ0ytc" + "value": "v0:fixture-key:1:Xnf3G1rIoAI-q4QVXBBvPkehNiAdCYRR4mbLvZkF0eE" } } ], - "fullUrl": "urn:uuid:a3fdc563-2b7a-5c7c-ac3b-e40563166e02", + "fullUrl": "urn:uuid:8f50510c-d9e1-55e0-968f-d468f650a2bf", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:t8d7IXwysPPh1De_qhKiAfN3dXsC9VwI6zZp6bD0l40", + "value": "v0:fixture-key:1:TCynCJxssAt4DKR6-a8p8A25iLlU9LB_s1EPaePLqKU", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:OhgkKcSIJTZQDgzR07JK97OEHcNiYmO-1lTQwjJ0ytc", + "value": "v0:fixture-key:1:Xnf3G1rIoAI-q4QVXBBvPkehNiAdCYRR4mbLvZkF0eE", "type": { "coding": [ { @@ -137,11 +137,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:6vVZncgNn4pPXCqtE4LOWMgYCbfQYiefnOfilHR0EeU" + "value": "v0:fixture-key:1:zjMgtZF1_WNxGQz_vUBTVxdogh6EcWgF0fnJ1y7dVow" } } ], - "fullUrl": "urn:uuid:41176715-cf6c-5230-998c-d9d68409bbd4", + "fullUrl": "urn:uuid:618ecd91-ad82-57b9-9c0f-b6ea3b69a76b", "resource": { "resourceType": "Device", "meta": { @@ -152,7 +152,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:6vVZncgNn4pPXCqtE4LOWMgYCbfQYiefnOfilHR0EeU", + "value": "v0:fixture-key:1:zjMgtZF1_WNxGQz_vUBTVxdogh6EcWgF0fnJ1y7dVow", "type": { "coding": [ { @@ -186,11 +186,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:SxKWGhLJZtu5LyrKvoAwvXfMJWLPrVR94fgdHyeVL5I" + "value": "v0:fixture-key:1:B2dz6Hre6_NHZxYSJHMNsG-uDpdEhDapwMVcz1SR_BA" } } ], - "fullUrl": "urn:uuid:7284f88f-d0d5-50c7-84ba-c4658ec92c62", + "fullUrl": "urn:uuid:64bfef0a-3d97-58cd-956e-8baa7c1f0217", "resource": { "resourceType": "Device", "meta": { @@ -201,7 +201,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:SxKWGhLJZtu5LyrKvoAwvXfMJWLPrVR94fgdHyeVL5I", + "value": "v0:fixture-key:1:B2dz6Hre6_NHZxYSJHMNsG-uDpdEhDapwMVcz1SR_BA", "type": { "coding": [ { @@ -249,11 +249,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:2begnAju9fHwtX1cps3EiMVhsqJYkbWkrUbKDDMFnUc" + "value": "n0:conversion-provenance:0:FfiBKfjUXMHGR5-SZO6c_SLhoLnifAG1ibSV9sW1K0U" } } ], - "fullUrl": "urn:uuid:38412fcd-f787-5fcc-b0a8-4cdc77728152", + "fullUrl": "urn:uuid:72d90e29-c7e7-545c-90bd-c42199825277", "resource": { "resourceType": "Provenance", "meta": { @@ -262,7 +262,7 @@ ] }, "target": [ - { "reference": "urn:uuid:a3fdc563-2b7a-5c7c-ac3b-e40563166e02" } + { "reference": "urn:uuid:8f50510c-d9e1-55e0-968f-d468f650a2bf" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -289,7 +289,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:7284f88f-d0d5-50c7-84ba-c4658ec92c62" + "reference": "urn:uuid:64bfef0a-3d97-58cd-956e-8baa7c1f0217" } } ], @@ -299,7 +299,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:t8d7IXwysPPh1De_qhKiAfN3dXsC9VwI6zZp6bD0l40", + "value": "v0:fixture-key:1:TCynCJxssAt4DKR6-a8p8A25iLlU9LB_s1EPaePLqKU", "type": { "coding": [ { @@ -323,7 +323,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:41176715-cf6c-5230-998c-d9d68409bbd4" + "reference": "urn:uuid:618ecd91-ad82-57b9-9c0f-b6ea3b69a76b" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-distance.json b/packages/fhir/fixtures/conformance/resources/mobile-distance.json index 6cf7f27c..3bca00ea 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-distance.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-distance.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:12" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:12" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:4Xm4N-HGeL0wfT796USMczzqIt2_UYHgOgsym9eE3vs" + "value": "v0:fixture-key:1:fHVg4ZAoSyzRoC4co_eCnbyEkz3bGc-MjiZDIfpCjRg" } } ], - "fullUrl": "urn:uuid:285f706e-8106-5bc1-8968-b04f30f89a39", + "fullUrl": "urn:uuid:a316b980-80ef-574b-8c92-f9ea4098c4cf", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:ORDRv8X7Kiwng77Sq9Iy_lho8jcPec7cnXVvaevWFEc", + "value": "v0:fixture-key:1:TXoHUQohPJ8ryT_B_A4JXlEeOz9zuZOS0c4OxCDHycY", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:4Xm4N-HGeL0wfT796USMczzqIt2_UYHgOgsym9eE3vs", + "value": "v0:fixture-key:1:fHVg4ZAoSyzRoC4co_eCnbyEkz3bGc-MjiZDIfpCjRg", "type": { "coding": [ { @@ -127,11 +127,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:ljmH930Ln2GwRNJ8zxNH1GQ7UCL80JMwS1vxs7042fQ" + "value": "v0:fixture-key:1:BsReyBbLVpNg7dz2pyPiH27BYmfz8sXY8mip5ohsDsI" } } ], - "fullUrl": "urn:uuid:9bd84bff-ca3b-5efe-b0d3-eac985a86209", + "fullUrl": "urn:uuid:5784f8ea-7c3c-53fb-91a6-d102609163b3", "resource": { "resourceType": "Device", "meta": { @@ -142,7 +142,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:ljmH930Ln2GwRNJ8zxNH1GQ7UCL80JMwS1vxs7042fQ", + "value": "v0:fixture-key:1:BsReyBbLVpNg7dz2pyPiH27BYmfz8sXY8mip5ohsDsI", "type": { "coding": [ { @@ -176,11 +176,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:IfolXmU541fCYhtMJPmNDtLZzlUd_T7C1mUq7gOMVfQ" + "value": "v0:fixture-key:1:gYmc80HvYh0cNa1N-DVgQFgVHF-31EMwtPjP-JBSj9U" } } ], - "fullUrl": "urn:uuid:a7b17eb4-dca8-5357-ae47-f316ba331af1", + "fullUrl": "urn:uuid:9dd72b24-695e-5058-b521-42985d983e88", "resource": { "resourceType": "Device", "meta": { @@ -191,7 +191,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:IfolXmU541fCYhtMJPmNDtLZzlUd_T7C1mUq7gOMVfQ", + "value": "v0:fixture-key:1:gYmc80HvYh0cNa1N-DVgQFgVHF-31EMwtPjP-JBSj9U", "type": { "coding": [ { @@ -239,11 +239,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:3lMkEryrw0OilBdBD1v0h0IKbzsZmJOLiRi8-hTKXSg" + "value": "n0:conversion-provenance:0:DLKzTcfXTY-hdSLv9GmCMjZpw3ouRC4CPcG1pf4tSvA" } } ], - "fullUrl": "urn:uuid:2440341d-6a4c-5495-afb5-7bbd12bed542", + "fullUrl": "urn:uuid:e101cbaf-9a6b-585e-913a-6636fba01c24", "resource": { "resourceType": "Provenance", "meta": { @@ -252,7 +252,7 @@ ] }, "target": [ - { "reference": "urn:uuid:285f706e-8106-5bc1-8968-b04f30f89a39" } + { "reference": "urn:uuid:a316b980-80ef-574b-8c92-f9ea4098c4cf" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -279,7 +279,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:a7b17eb4-dca8-5357-ae47-f316ba331af1" + "reference": "urn:uuid:9dd72b24-695e-5058-b521-42985d983e88" } } ], @@ -289,7 +289,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:ORDRv8X7Kiwng77Sq9Iy_lho8jcPec7cnXVvaevWFEc", + "value": "v0:fixture-key:1:TXoHUQohPJ8ryT_B_A4JXlEeOz9zuZOS0c4OxCDHycY", "type": { "coding": [ { @@ -313,7 +313,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:9bd84bff-ca3b-5efe-b0d3-eac985a86209" + "reference": "urn:uuid:5784f8ea-7c3c-53fb-91a6-d102609163b3" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json b/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json index 9923e319..5cf67f58 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:13" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:13" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:QaFq1UO4YCJMyKZETAa_k_mO7XWhwkPAPbA3hcsGo8o" + "value": "v0:fixture-key:1:BlsXMWS8VeTKUY0-gRERFgFfSQX3KOWfQ9cWnbVJYDU" } } ], - "fullUrl": "urn:uuid:c13bb15f-8146-544c-8606-efcff6713d2f", + "fullUrl": "urn:uuid:05e2b23b-2b07-5454-9dd2-378d69ee2150", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:5Z-4dkeRWqP7Othp8dWZkFLwHhlt-6uPKljYJjYJ3PA", + "value": "v0:fixture-key:1:4YedtTQr9n-nubQkvlBwC-1mOoLtiknhWp4RTtKkSlM", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:QaFq1UO4YCJMyKZETAa_k_mO7XWhwkPAPbA3hcsGo8o", + "value": "v0:fixture-key:1:BlsXMWS8VeTKUY0-gRERFgFfSQX3KOWfQ9cWnbVJYDU", "type": { "coding": [ { @@ -129,11 +129,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:gH9QaLt9-FrRUuQwSFDiko_20-FUCzw_Qlc33lpfJqc" + "value": "v0:fixture-key:1:u-1rXN1Mxg6FOcZeVAsxYBKbRr1DMHadZTocKEEUOx4" } } ], - "fullUrl": "urn:uuid:6b18ef20-a8e1-5263-a763-47b8e9f781c5", + "fullUrl": "urn:uuid:c74adc7d-e2cc-5fe2-8288-704a23bd131e", "resource": { "resourceType": "Device", "meta": { @@ -144,7 +144,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:gH9QaLt9-FrRUuQwSFDiko_20-FUCzw_Qlc33lpfJqc", + "value": "v0:fixture-key:1:u-1rXN1Mxg6FOcZeVAsxYBKbRr1DMHadZTocKEEUOx4", "type": { "coding": [ { @@ -178,11 +178,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:QAlMzQ1sodRgk8FLUo9kPs0ZiQCwdGwP2HOO4lqHSvs" + "value": "v0:fixture-key:1:-S0Gqyi7O_7YfEiA8qAsF_fbOCQKVviT4-XvrnxqtBE" } } ], - "fullUrl": "urn:uuid:04a5de5e-805e-5c55-9816-79186de3730b", + "fullUrl": "urn:uuid:2127a0df-3555-5ec9-bf92-62b908a8691f", "resource": { "resourceType": "Device", "meta": { @@ -193,7 +193,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:QAlMzQ1sodRgk8FLUo9kPs0ZiQCwdGwP2HOO4lqHSvs", + "value": "v0:fixture-key:1:-S0Gqyi7O_7YfEiA8qAsF_fbOCQKVviT4-XvrnxqtBE", "type": { "coding": [ { @@ -241,11 +241,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:2mMqPMmYFerH2KE9y48zxMXF-nP8yKqHHmY-AQnr9VY" + "value": "n0:conversion-provenance:0:o6HbMwcpACffZfDBRcw3uhTXFHQO8CAKI_kDG78YUHw" } } ], - "fullUrl": "urn:uuid:83781f3b-a79d-57d5-876b-34897fc5e1d4", + "fullUrl": "urn:uuid:62cb671e-5836-5c0c-91bd-583d7486256a", "resource": { "resourceType": "Provenance", "meta": { @@ -254,7 +254,7 @@ ] }, "target": [ - { "reference": "urn:uuid:c13bb15f-8146-544c-8606-efcff6713d2f" } + { "reference": "urn:uuid:05e2b23b-2b07-5454-9dd2-378d69ee2150" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -281,7 +281,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:04a5de5e-805e-5c55-9816-79186de3730b" + "reference": "urn:uuid:2127a0df-3555-5ec9-bf92-62b908a8691f" } } ], @@ -291,7 +291,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:5Z-4dkeRWqP7Othp8dWZkFLwHhlt-6uPKljYJjYJ3PA", + "value": "v0:fixture-key:1:4YedtTQr9n-nubQkvlBwC-1mOoLtiknhWp4RTtKkSlM", "type": { "coding": [ { @@ -315,7 +315,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:6b18ef20-a8e1-5263-a763-47b8e9f781c5" + "reference": "urn:uuid:c74adc7d-e2cc-5fe2-8288-704a23bd131e" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json b/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json index 17a9ed57..88b85c16 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:14" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:14" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:9F2FoOz7Fr0tgc4_x8ANXITQ-OOtmLTEO36VAPyq41w" + "value": "v0:fixture-key:1:Jd_HuLq4yPD61i7SXwNGSVKzGVRlPQhpCRkuTKhjqOE" } } ], - "fullUrl": "urn:uuid:3f7f1483-cb67-5dd5-a801-f1cdc6ebd7fe", + "fullUrl": "urn:uuid:1b278f81-bafa-5e5a-aa91-820281798dc1", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:KonZRzCBLyhG1NY4_mJRRtS4BIv4M3OnvYtdJ9Dwxs0", + "value": "v0:fixture-key:1:vgABOZcd_2aMgE45wa5IupFp8PcTzVbBph9MSC6nbzE", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:9F2FoOz7Fr0tgc4_x8ANXITQ-OOtmLTEO36VAPyq41w", + "value": "v0:fixture-key:1:Jd_HuLq4yPD61i7SXwNGSVKzGVRlPQhpCRkuTKhjqOE", "type": { "coding": [ { @@ -127,11 +127,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:NHdlpVZDWK6LWi_l9gk-fjVVXVE2D1MOxg6WxZd2_wY" + "value": "v0:fixture-key:1:5JsSfWJISELeuXbZe-bRaI8TDU1PGIoSZtz60Z2X7HM" } } ], - "fullUrl": "urn:uuid:af49e404-2505-5279-92af-e801873a52eb", + "fullUrl": "urn:uuid:ca82e833-a64a-5080-8f55-e25ec09ffb3b", "resource": { "resourceType": "Device", "meta": { @@ -142,7 +142,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:NHdlpVZDWK6LWi_l9gk-fjVVXVE2D1MOxg6WxZd2_wY", + "value": "v0:fixture-key:1:5JsSfWJISELeuXbZe-bRaI8TDU1PGIoSZtz60Z2X7HM", "type": { "coding": [ { @@ -176,11 +176,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:-FvHhIPZp52KT3IzFTM307FK_UaiyUvjk2AjHfLzc3Q" + "value": "v0:fixture-key:1:svLLBnin4pxJFIYsSL1qNT0ARP9QqSEBxlOWDkkz9DQ" } } ], - "fullUrl": "urn:uuid:cf0df766-31c3-57f1-a1e8-fb71badac697", + "fullUrl": "urn:uuid:d2359082-1541-585e-8e66-8bae2a65011a", "resource": { "resourceType": "Device", "meta": { @@ -191,7 +191,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:-FvHhIPZp52KT3IzFTM307FK_UaiyUvjk2AjHfLzc3Q", + "value": "v0:fixture-key:1:svLLBnin4pxJFIYsSL1qNT0ARP9QqSEBxlOWDkkz9DQ", "type": { "coding": [ { @@ -239,11 +239,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:RsJoNU8RpAmAFZIgxNlT-RN4Bfyx5ZdJwVqkQMGv8rU" + "value": "n0:conversion-provenance:0:e88H16uxr9KnpgkeHQZBgQ7JaHfHbJ0iV8PAKr8vop8" } } ], - "fullUrl": "urn:uuid:c76286dc-1f59-573d-8038-78b66e559228", + "fullUrl": "urn:uuid:0713c33b-6273-530c-9915-44dc21a60b55", "resource": { "resourceType": "Provenance", "meta": { @@ -252,7 +252,7 @@ ] }, "target": [ - { "reference": "urn:uuid:3f7f1483-cb67-5dd5-a801-f1cdc6ebd7fe" } + { "reference": "urn:uuid:1b278f81-bafa-5e5a-aa91-820281798dc1" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -279,7 +279,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:cf0df766-31c3-57f1-a1e8-fb71badac697" + "reference": "urn:uuid:d2359082-1541-585e-8e66-8bae2a65011a" } } ], @@ -289,7 +289,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:KonZRzCBLyhG1NY4_mJRRtS4BIv4M3OnvYtdJ9Dwxs0", + "value": "v0:fixture-key:1:vgABOZcd_2aMgE45wa5IupFp8PcTzVbBph9MSC6nbzE", "type": { "coding": [ { @@ -313,7 +313,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:af49e404-2505-5279-92af-e801873a52eb" + "reference": "urn:uuid:ca82e833-a64a-5080-8f55-e25ec09ffb3b" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json index 754efa85..34094738 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:16" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:16" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:ZnLIL6SSF3Gja1m0o306OZkJGQul41LPQn7hA5pWDM8" + "value": "v0:fixture-key:1:S5vVITWsjX9mcvy8pBYaU_lPwGfNl8M6BijEtChtbCU" } } ], - "fullUrl": "urn:uuid:0586ab21-3183-575d-9b79-ca7655102535", + "fullUrl": "urn:uuid:f6e3927c-3d0a-5a3c-8fde-eb54caeca7b7", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:QKU7Mn9Azdb0uXzKWXJKx_4AIhS_qsa2-XVtYaelQKM", + "value": "v0:fixture-key:1:AEZUZCTuy5drfmVoPO_ot7LunYUw68sNqiXSV5fSBko", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:ZnLIL6SSF3Gja1m0o306OZkJGQul41LPQn7hA5pWDM8", + "value": "v0:fixture-key:1:S5vVITWsjX9mcvy8pBYaU_lPwGfNl8M6BijEtChtbCU", "type": { "coding": [ { @@ -129,11 +129,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:o1xxMg_I2PM2GIkxuit_iyHr4nudEulGfq56MGinQTw" + "value": "v0:fixture-key:1:1GzMX6TN8GutnhmfmYulPCxpQfkJgPHuDr46ZX8DhTY" } } ], - "fullUrl": "urn:uuid:73e7cee5-b56e-52a1-b897-d7a2a9c2b4de", + "fullUrl": "urn:uuid:375bbe70-10e1-5992-b3f9-0e4eb5612ae8", "resource": { "resourceType": "Device", "meta": { @@ -144,7 +144,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:o1xxMg_I2PM2GIkxuit_iyHr4nudEulGfq56MGinQTw", + "value": "v0:fixture-key:1:1GzMX6TN8GutnhmfmYulPCxpQfkJgPHuDr46ZX8DhTY", "type": { "coding": [ { @@ -178,11 +178,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:uA_D8Zy9-9gEoM-xCNwgrwaH7P60a3t2EkRQ1Tq1sf0" + "value": "v0:fixture-key:1:OG_EL_fZjoyFtd7M2on8ei01lKoevG6MNr3bfeuT5ec" } } ], - "fullUrl": "urn:uuid:16aa054b-a1b0-56e8-bd63-ece074f4913b", + "fullUrl": "urn:uuid:c2e33326-2fed-5e61-84af-db7558330215", "resource": { "resourceType": "Device", "meta": { @@ -193,7 +193,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:uA_D8Zy9-9gEoM-xCNwgrwaH7P60a3t2EkRQ1Tq1sf0", + "value": "v0:fixture-key:1:OG_EL_fZjoyFtd7M2on8ei01lKoevG6MNr3bfeuT5ec", "type": { "coding": [ { @@ -241,11 +241,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:ggTth36Vc35tjDEjq0uTZwVVhE30YDaza0XeATNjWho" + "value": "n0:conversion-provenance:0:4M2ckiaROaQsqFL7zobE316ingS1pOnTxc05MfhitdQ" } } ], - "fullUrl": "urn:uuid:1d782e60-75b2-58de-b0f9-bbebb745af55", + "fullUrl": "urn:uuid:c989d2f9-98e7-5835-b736-95cda36e4f12", "resource": { "resourceType": "Provenance", "meta": { @@ -254,7 +254,7 @@ ] }, "target": [ - { "reference": "urn:uuid:0586ab21-3183-575d-9b79-ca7655102535" } + { "reference": "urn:uuid:f6e3927c-3d0a-5a3c-8fde-eb54caeca7b7" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -281,7 +281,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:16aa054b-a1b0-56e8-bd63-ece074f4913b" + "reference": "urn:uuid:c2e33326-2fed-5e61-84af-db7558330215" } } ], @@ -291,7 +291,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:QKU7Mn9Azdb0uXzKWXJKx_4AIhS_qsa2-XVtYaelQKM", + "value": "v0:fixture-key:1:AEZUZCTuy5drfmVoPO_ot7LunYUw68sNqiXSV5fSBko", "type": { "coding": [ { @@ -315,7 +315,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:73e7cee5-b56e-52a1-b897-d7a2a9c2b4de" + "reference": "urn:uuid:375bbe70-10e1-5992-b3f9-0e4eb5612ae8" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json index 52dbc1a2..dbece90f 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:17" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:17" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:60eY9xJ1BGUKk02Wzxgbw8I-KsJkvA6QmeqDHUea5cI" + "value": "v0:fixture-key:1:CZXatvohITV673olsXmQKz38AcBWcMpvz5he0Qm5VKI" } } ], - "fullUrl": "urn:uuid:137fe5e0-b993-59e2-9bbb-d01b6822b871", + "fullUrl": "urn:uuid:8270c509-bfdf-5d38-a815-b7e2214c5058", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:3SuDnTSHb_y1kwQTccpLSh5rdP0Qva9KPCWHyEPmNvo", + "value": "v0:fixture-key:1:tkdht7TFVXQyq6IHNvYVQ8tNFVyUPgbZXNMZPxXED6U", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:60eY9xJ1BGUKk02Wzxgbw8I-KsJkvA6QmeqDHUea5cI", + "value": "v0:fixture-key:1:CZXatvohITV673olsXmQKz38AcBWcMpvz5he0Qm5VKI", "type": { "coding": [ { @@ -124,11 +124,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:SH0IL9WB76tc2lwGI9J70EPhdFxAisS-WQ4aSdsR1Qg" + "value": "v0:fixture-key:1:OVqOPjrQEoSJS--PZePeSkqILMB9dA0xef1_SpJ4viY" } } ], - "fullUrl": "urn:uuid:9c3b7af2-e57b-5aa4-a641-f803c710cabd", + "fullUrl": "urn:uuid:1ac62dcf-7508-5504-8cf9-88aa67a19f05", "resource": { "resourceType": "Device", "meta": { @@ -139,7 +139,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:SH0IL9WB76tc2lwGI9J70EPhdFxAisS-WQ4aSdsR1Qg", + "value": "v0:fixture-key:1:OVqOPjrQEoSJS--PZePeSkqILMB9dA0xef1_SpJ4viY", "type": { "coding": [ { @@ -173,11 +173,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:aONwM6Z7mj9j_sHLBy2z7oJ1BRb0j2uummA3OVRLzxs" + "value": "v0:fixture-key:1:RMkmXi5iDHPmp9alpqCtmszW5AIvyyRqxxRMY_nwChw" } } ], - "fullUrl": "urn:uuid:a9c59fa6-4464-5e8a-a09b-34f4e6cb7597", + "fullUrl": "urn:uuid:dc091ace-e49f-5779-bcad-c1b7724d6484", "resource": { "resourceType": "Device", "meta": { @@ -188,7 +188,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:aONwM6Z7mj9j_sHLBy2z7oJ1BRb0j2uummA3OVRLzxs", + "value": "v0:fixture-key:1:RMkmXi5iDHPmp9alpqCtmszW5AIvyyRqxxRMY_nwChw", "type": { "coding": [ { @@ -236,11 +236,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:F8Eww65FNK0JXLReGmb39_Jelr5uRaoowR8DRk3D5TI" + "value": "n0:conversion-provenance:0:qtQ7FdqRyP8fcGw_zIozpXqNB9VIxfITbTY4xqoSj3Y" } } ], - "fullUrl": "urn:uuid:2114ba63-5ea2-5848-826a-94999484e6a2", + "fullUrl": "urn:uuid:6cfc6400-6efa-5c4a-8345-f481d5f52388", "resource": { "resourceType": "Provenance", "meta": { @@ -249,7 +249,7 @@ ] }, "target": [ - { "reference": "urn:uuid:137fe5e0-b993-59e2-9bbb-d01b6822b871" } + { "reference": "urn:uuid:8270c509-bfdf-5d38-a815-b7e2214c5058" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -276,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:a9c59fa6-4464-5e8a-a09b-34f4e6cb7597" + "reference": "urn:uuid:dc091ace-e49f-5779-bcad-c1b7724d6484" } } ], @@ -286,7 +286,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:3SuDnTSHb_y1kwQTccpLSh5rdP0Qva9KPCWHyEPmNvo", + "value": "v0:fixture-key:1:tkdht7TFVXQyq6IHNvYVQ8tNFVyUPgbZXNMZPxXED6U", "type": { "coding": [ { @@ -310,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:9c3b7af2-e57b-5aa4-a641-f803c710cabd" + "reference": "urn:uuid:1ac62dcf-7508-5504-8cf9-88aa67a19f05" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json index 41dcc64f..96c8c9c1 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:15" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:15" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:dyDbUB4KgbwSNmbNun1rnvosFUVDw3nUfZAShMJWJOw" + "value": "v0:fixture-key:1:bmuym0sEonI5tHUX1mpn1Rm7zoBhUOUWrWJSXt-J4b8" } } ], - "fullUrl": "urn:uuid:20edb4a3-51d4-53a7-8d85-4232af3926f0", + "fullUrl": "urn:uuid:d977eb59-e38c-5c58-a9e1-d6aaab8006c4", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:i8sTWTfdx-WRqKbe-SxdHQSgUpcCHr-uHI7HrL08BQo", + "value": "v0:fixture-key:1:tJcKn-_0jxgFDvTYEiVfnjgPQy6bjsEgJ6whvNnqwjM", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:dyDbUB4KgbwSNmbNun1rnvosFUVDw3nUfZAShMJWJOw", + "value": "v0:fixture-key:1:bmuym0sEonI5tHUX1mpn1Rm7zoBhUOUWrWJSXt-J4b8", "type": { "coding": [ { @@ -136,11 +136,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:rcaBJJ9K36ymlV0oqVjf6ocLjvTjeGh399SuNGTcBQ4" + "value": "v0:fixture-key:1:_jJrqQjnriMYhYAfnPCdEg3KSuVIV2CPuLJ5jQLUbqA" } } ], - "fullUrl": "urn:uuid:1989133d-9c27-565d-9aee-2aeaf956b28e", + "fullUrl": "urn:uuid:b49b2e14-6bf2-572d-b927-ffc5dbb2de0e", "resource": { "resourceType": "Device", "meta": { @@ -151,7 +151,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:rcaBJJ9K36ymlV0oqVjf6ocLjvTjeGh399SuNGTcBQ4", + "value": "v0:fixture-key:1:_jJrqQjnriMYhYAfnPCdEg3KSuVIV2CPuLJ5jQLUbqA", "type": { "coding": [ { @@ -185,11 +185,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:8LYUDLu5wN6d4xiDgy4_fvWIsv-wpn3c9o6YNNhoreA" + "value": "v0:fixture-key:1:iu_gT-exxcFt4UuQNJUirFWc_H7mb3n2H2opZGP7JFc" } } ], - "fullUrl": "urn:uuid:1c8ecea2-d107-5698-bddf-b038400227e2", + "fullUrl": "urn:uuid:fa8f73d9-94a9-5603-a4d6-401a04ea1714", "resource": { "resourceType": "Device", "meta": { @@ -200,7 +200,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:8LYUDLu5wN6d4xiDgy4_fvWIsv-wpn3c9o6YNNhoreA", + "value": "v0:fixture-key:1:iu_gT-exxcFt4UuQNJUirFWc_H7mb3n2H2opZGP7JFc", "type": { "coding": [ { @@ -248,11 +248,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:kq4uOeM8fb1oCMVFL_tw7zK3r7M4GGjQltJcGikZA9U" + "value": "n0:conversion-provenance:0:KeJVdr0G0FQra4gqbd817-DGBzsTeQyRmXS4w9wqvx4" } } ], - "fullUrl": "urn:uuid:d7cf8988-42eb-532b-a46b-652943a1fa88", + "fullUrl": "urn:uuid:1b3fd21e-a7b2-5ded-b129-7c9cf4cccaf0", "resource": { "resourceType": "Provenance", "meta": { @@ -261,7 +261,7 @@ ] }, "target": [ - { "reference": "urn:uuid:20edb4a3-51d4-53a7-8d85-4232af3926f0" } + { "reference": "urn:uuid:d977eb59-e38c-5c58-a9e1-d6aaab8006c4" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -288,7 +288,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:1c8ecea2-d107-5698-bddf-b038400227e2" + "reference": "urn:uuid:fa8f73d9-94a9-5603-a4d6-401a04ea1714" } } ], @@ -298,7 +298,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:i8sTWTfdx-WRqKbe-SxdHQSgUpcCHr-uHI7HrL08BQo", + "value": "v0:fixture-key:1:tJcKn-_0jxgFDvTYEiVfnjgPQy6bjsEgJ6whvNnqwjM", "type": { "coding": [ { @@ -322,7 +322,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:1989133d-9c27-565d-9aee-2aeaf956b28e" + "reference": "urn:uuid:b49b2e14-6bf2-572d-b927-ffc5dbb2de0e" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json b/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json index a5669c0c..62688e09 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:18" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:18" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:M3mCXstdoV5mPI_uEj3BK2M2mx8ZwQNQqnhlH-3-RrA" + "value": "v0:fixture-key:1:TZXCBXOpPixQ2BNvn0NbH2dqlgzPFN7dffcFH7JRopU" } } ], - "fullUrl": "urn:uuid:d8a72ccb-5213-5c4d-b1b6-3e0ef81351af", + "fullUrl": "urn:uuid:bc7ae0a2-0fc8-5ace-89e5-45f4c40a5dcb", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:VJf8NJTHb8NA1xWAzyXlFWzi3sDnTuY-trFQHK4STpY", + "value": "v0:fixture-key:1:8l5WdJ-o5mNFdCSbs5y9t3UvOl60nYnd583KpnzMSKM", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:M3mCXstdoV5mPI_uEj3BK2M2mx8ZwQNQqnhlH-3-RrA", + "value": "v0:fixture-key:1:TZXCBXOpPixQ2BNvn0NbH2dqlgzPFN7dffcFH7JRopU", "type": { "coding": [ { @@ -124,11 +124,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:c9MTJ_MumzoBlID_WBUSFTXVLhhvmFPjMSlsZKDED5w" + "value": "v0:fixture-key:1:2yXt6DebFvXDTeSlzgzyFye9iuh92QGZMLIL4n0jN80" } } ], - "fullUrl": "urn:uuid:80c7119e-d3ad-5817-a256-b5dca5738a6c", + "fullUrl": "urn:uuid:913d1293-e271-513f-b01f-ebb18d782d5f", "resource": { "resourceType": "Device", "meta": { @@ -139,7 +139,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:c9MTJ_MumzoBlID_WBUSFTXVLhhvmFPjMSlsZKDED5w", + "value": "v0:fixture-key:1:2yXt6DebFvXDTeSlzgzyFye9iuh92QGZMLIL4n0jN80", "type": { "coding": [ { @@ -173,11 +173,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:gQzM9idbGlLqdXLn6VDj-P4JqwtgD1lkCR8t5VcAr2w" + "value": "v0:fixture-key:1:KJTEBWuDAWHHJ5xERnRW8CcL1X4cUMxv0uoTuNNohQ8" } } ], - "fullUrl": "urn:uuid:1f5c2740-cb04-5cc5-8350-07aaaf3e54d9", + "fullUrl": "urn:uuid:57d9a45a-44d5-5cf9-9545-4e386990fee6", "resource": { "resourceType": "Device", "meta": { @@ -188,7 +188,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:gQzM9idbGlLqdXLn6VDj-P4JqwtgD1lkCR8t5VcAr2w", + "value": "v0:fixture-key:1:KJTEBWuDAWHHJ5xERnRW8CcL1X4cUMxv0uoTuNNohQ8", "type": { "coding": [ { @@ -236,11 +236,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:bXfTWoMfhRdyksa4P96FvJq43jM_vpSTy6IjL-ux9oQ" + "value": "n0:conversion-provenance:0:32bLLYB_SafBR6GtMo9KZHVV2SiFvcclBMoKe-Tki1Y" } } ], - "fullUrl": "urn:uuid:017be1e1-c42e-555d-b461-023ed631084e", + "fullUrl": "urn:uuid:19b3591a-7abf-5469-a12a-f47f00d1e5c4", "resource": { "resourceType": "Provenance", "meta": { @@ -249,7 +249,7 @@ ] }, "target": [ - { "reference": "urn:uuid:d8a72ccb-5213-5c4d-b1b6-3e0ef81351af" } + { "reference": "urn:uuid:bc7ae0a2-0fc8-5ace-89e5-45f4c40a5dcb" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -276,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:1f5c2740-cb04-5cc5-8350-07aaaf3e54d9" + "reference": "urn:uuid:57d9a45a-44d5-5cf9-9545-4e386990fee6" } } ], @@ -286,7 +286,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:VJf8NJTHb8NA1xWAzyXlFWzi3sDnTuY-trFQHK4STpY", + "value": "v0:fixture-key:1:8l5WdJ-o5mNFdCSbs5y9t3UvOl60nYnd583KpnzMSKM", "type": { "coding": [ { @@ -310,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:80c7119e-d3ad-5817-a256-b5dca5738a6c" + "reference": "urn:uuid:913d1293-e271-513f-b01f-ebb18d782d5f" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json index 27e05b20..0591d372 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:19" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:19" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:Kh4-pKwP8yYYMcNe6yowsX20rPUPq8QE-ewi7iJZiiQ" + "value": "v0:fixture-key:1:a8s1HpBI9_4qjgDcZG1wbl8Opf3E51HAkTK3f2QsUYg" } } ], - "fullUrl": "urn:uuid:287eaf2f-5bed-5969-9ce1-034ade0e29c9", + "fullUrl": "urn:uuid:4efc2828-d56e-533c-973a-822c1bfa4e23", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:iQf-YAFLu0qdwElSD1qMoEnP6d-8L0ppBg4yoERcn6o", + "value": "v0:fixture-key:1:uIT4xv9B-PBwKmJAw7VglCZ1hAYn6BSJF577LrFO2ZA", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:Kh4-pKwP8yYYMcNe6yowsX20rPUPq8QE-ewi7iJZiiQ", + "value": "v0:fixture-key:1:a8s1HpBI9_4qjgDcZG1wbl8Opf3E51HAkTK3f2QsUYg", "type": { "coding": [ { @@ -137,11 +137,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:KgMuJEdOw-zlw_Xe9dwVYDLfykPVUNqcl2imwF29i10" + "value": "v0:fixture-key:1:By5ZV5MsYx3CBGT8S2VIwR4j7Rv6dE81YMIVOeBOtSo" } } ], - "fullUrl": "urn:uuid:167ce386-5528-5010-80c2-f1925b415f32", + "fullUrl": "urn:uuid:5d918d67-4fa9-55ee-b070-98dcf22a5fae", "resource": { "resourceType": "Device", "meta": { @@ -152,7 +152,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:KgMuJEdOw-zlw_Xe9dwVYDLfykPVUNqcl2imwF29i10", + "value": "v0:fixture-key:1:By5ZV5MsYx3CBGT8S2VIwR4j7Rv6dE81YMIVOeBOtSo", "type": { "coding": [ { @@ -186,11 +186,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:Hvt9NljrBk6a-V8nRriNE-BHIxfdnq7NIhNrOahXMTM" + "value": "v0:fixture-key:1:gAk_xo_m3VIoAq8WwnPjv8FyKliF_r2JVORqOlVXzQk" } } ], - "fullUrl": "urn:uuid:83a2b7a5-7a74-5e9d-94e8-86823f3662ca", + "fullUrl": "urn:uuid:4fe4116e-b120-5c76-b6b4-c45669831277", "resource": { "resourceType": "Device", "meta": { @@ -201,7 +201,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:Hvt9NljrBk6a-V8nRriNE-BHIxfdnq7NIhNrOahXMTM", + "value": "v0:fixture-key:1:gAk_xo_m3VIoAq8WwnPjv8FyKliF_r2JVORqOlVXzQk", "type": { "coding": [ { @@ -249,11 +249,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:iQV6LGuHNuQ4oq-2qw8zD3mFvpJe_uwR5j7SkYy3FPE" + "value": "n0:conversion-provenance:0:iom5w6J85R7VsUmEG6UnuzPUqpuUXfUcEUI7pogG9IA" } } ], - "fullUrl": "urn:uuid:8cc3ef28-3e9b-5647-a2bd-67061231df49", + "fullUrl": "urn:uuid:6e7dd3df-58b6-522c-a757-ebb39306085c", "resource": { "resourceType": "Provenance", "meta": { @@ -262,7 +262,7 @@ ] }, "target": [ - { "reference": "urn:uuid:287eaf2f-5bed-5969-9ce1-034ade0e29c9" } + { "reference": "urn:uuid:4efc2828-d56e-533c-973a-822c1bfa4e23" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -289,7 +289,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:83a2b7a5-7a74-5e9d-94e8-86823f3662ca" + "reference": "urn:uuid:4fe4116e-b120-5c76-b6b4-c45669831277" } } ], @@ -299,7 +299,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:iQf-YAFLu0qdwElSD1qMoEnP6d-8L0ppBg4yoERcn6o", + "value": "v0:fixture-key:1:uIT4xv9B-PBwKmJAw7VglCZ1hAYn6BSJF577LrFO2ZA", "type": { "coding": [ { @@ -323,7 +323,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:167ce386-5528-5010-80c2-f1925b415f32" + "reference": "urn:uuid:5d918d67-4fa9-55ee-b070-98dcf22a5fae" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json index a522017d..cd2dea4d 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:21" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:21" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:pz2bc_14nzpN1nf7SeqIO64j0IeZx5pne3cGLUt9EvI" + "value": "v0:fixture-key:1:Foy5lK9amkmeg12XbXQYMrT-TUBizAOPdTfE7c8MsIE" } } ], - "fullUrl": "urn:uuid:ae129fff-cc5c-57ed-b2c9-e7b5251a5dc6", + "fullUrl": "urn:uuid:43ad632f-c572-576f-8fef-5b703ca7cd84", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:LJpPA6SgcBh3mS33fNozvvsvFZ7oF8s8BGlqzNRtHXc", + "value": "v0:fixture-key:1:osGy-Ko7LLVWyvk64uBV-JuE2OlQbjgV9yXagYzTplA", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:pz2bc_14nzpN1nf7SeqIO64j0IeZx5pne3cGLUt9EvI", + "value": "v0:fixture-key:1:Foy5lK9amkmeg12XbXQYMrT-TUBizAOPdTfE7c8MsIE", "type": { "coding": [ { @@ -137,11 +137,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:ExeKDTle-oh9jLx-X9p8xW5HikI4oKs7NlOS-7xTSjc" + "value": "v0:fixture-key:1:JRw2RshEpj2tnoPVOkAi7BWBqTQu2B2oX5wgLXZiX3c" } } ], - "fullUrl": "urn:uuid:6e12b116-b0a3-54f0-88c1-6d1d7f3613af", + "fullUrl": "urn:uuid:f0661a79-cb88-5556-a043-744b7b78328c", "resource": { "resourceType": "Device", "meta": { @@ -152,7 +152,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:ExeKDTle-oh9jLx-X9p8xW5HikI4oKs7NlOS-7xTSjc", + "value": "v0:fixture-key:1:JRw2RshEpj2tnoPVOkAi7BWBqTQu2B2oX5wgLXZiX3c", "type": { "coding": [ { @@ -186,11 +186,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:V86cGZ2kLwl_AHtTzOq8rdDxjc7VlQ9ikQC3BSEkhes" + "value": "v0:fixture-key:1:uIYGTaUTHfKiwEm5jVkr_z9QOzRbCzr3NeyStHgnB7Y" } } ], - "fullUrl": "urn:uuid:b269432e-d07b-5e50-b2de-711a4e636220", + "fullUrl": "urn:uuid:99e59cb2-5f78-5b22-96f2-6c3467d5b7d1", "resource": { "resourceType": "Device", "meta": { @@ -201,7 +201,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:V86cGZ2kLwl_AHtTzOq8rdDxjc7VlQ9ikQC3BSEkhes", + "value": "v0:fixture-key:1:uIYGTaUTHfKiwEm5jVkr_z9QOzRbCzr3NeyStHgnB7Y", "type": { "coding": [ { @@ -249,11 +249,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:m0hsqsMK-mI8buNMlPn7jXuJZeWsw38i-VBhiKzZtRs" + "value": "n0:conversion-provenance:0:S4UXghjlk4gU1D48Oolvx5KhsOA-BiiIGLy8DuV88F0" } } ], - "fullUrl": "urn:uuid:36b2b622-fd3f-58b0-9462-9933872d0def", + "fullUrl": "urn:uuid:21a750dd-da67-5b44-a03a-0cdd3cdd0cfc", "resource": { "resourceType": "Provenance", "meta": { @@ -262,7 +262,7 @@ ] }, "target": [ - { "reference": "urn:uuid:ae129fff-cc5c-57ed-b2c9-e7b5251a5dc6" } + { "reference": "urn:uuid:43ad632f-c572-576f-8fef-5b703ca7cd84" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -289,7 +289,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:b269432e-d07b-5e50-b2de-711a4e636220" + "reference": "urn:uuid:99e59cb2-5f78-5b22-96f2-6c3467d5b7d1" } } ], @@ -299,7 +299,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:LJpPA6SgcBh3mS33fNozvvsvFZ7oF8s8BGlqzNRtHXc", + "value": "v0:fixture-key:1:osGy-Ko7LLVWyvk64uBV-JuE2OlQbjgV9yXagYzTplA", "type": { "coding": [ { @@ -323,7 +323,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:6e12b116-b0a3-54f0-88c1-6d1d7f3613af" + "reference": "urn:uuid:f0661a79-cb88-5556-a043-744b7b78328c" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json index a5f1f1f5..9e8b13ca 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:20" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:20" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:uC4-CLTnHTiVl6yISs2FQ53EwZmM56mWAxoFQzB3PZ0" + "value": "v0:fixture-key:1:HPgVl-lF8_XjYKtNbZqZoeUvyJx6cwlNxaJ3AgW6mG8" } } ], - "fullUrl": "urn:uuid:982583dd-9d31-5624-9952-fbc8218182c9", + "fullUrl": "urn:uuid:ba01f42d-adfb-532e-8909-cf8ff6e1d183", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:rBfmxEHQDXZC8-jWUzQz8czUa64bqdkH8NckUE-RiR0", + "value": "v0:fixture-key:1:sJSuge3Kkid_TsVY7p-oSU7CUFGvHXOZEVfyi6GRsvs", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:uC4-CLTnHTiVl6yISs2FQ53EwZmM56mWAxoFQzB3PZ0", + "value": "v0:fixture-key:1:HPgVl-lF8_XjYKtNbZqZoeUvyJx6cwlNxaJ3AgW6mG8", "type": { "coding": [ { @@ -136,11 +136,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:K3HkBVarnKxvVz-ItYI6QIobe5p66qLbAbgyRGj3ft4" + "value": "v0:fixture-key:1:U37Z6ve6J62eVasgsTcFZNjogwaV5RSx0vOFIcZnowQ" } } ], - "fullUrl": "urn:uuid:504ac48f-1656-51fe-81ac-7757ecc4b958", + "fullUrl": "urn:uuid:ef4c6dd5-5186-52bd-81ff-c35ceff514cd", "resource": { "resourceType": "Device", "meta": { @@ -151,7 +151,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:K3HkBVarnKxvVz-ItYI6QIobe5p66qLbAbgyRGj3ft4", + "value": "v0:fixture-key:1:U37Z6ve6J62eVasgsTcFZNjogwaV5RSx0vOFIcZnowQ", "type": { "coding": [ { @@ -185,11 +185,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:aBUSMXl5wcjkpazCl7Y_HAP7U3xodq9qgk76F91KxfQ" + "value": "v0:fixture-key:1:fs_lzSgI2UvEDgi6o7MZqbAjw2DzA9xn0PCEWBSfRjo" } } ], - "fullUrl": "urn:uuid:c72b00b2-52cb-5dc2-8a42-f9d8db034b1c", + "fullUrl": "urn:uuid:a26ecd38-abeb-5d40-b1fb-7b6217720f23", "resource": { "resourceType": "Device", "meta": { @@ -200,7 +200,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:aBUSMXl5wcjkpazCl7Y_HAP7U3xodq9qgk76F91KxfQ", + "value": "v0:fixture-key:1:fs_lzSgI2UvEDgi6o7MZqbAjw2DzA9xn0PCEWBSfRjo", "type": { "coding": [ { @@ -248,11 +248,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:axbqoCfibKd_O3FuzXZCr82AHhKET3ABs5qBRZEsLGM" + "value": "n0:conversion-provenance:0:vCXndC-N8tGe3pKoAEF3lUnqguUQbAFW-Ntf2LkQHQg" } } ], - "fullUrl": "urn:uuid:d31566dd-6f8c-5797-98e8-7e8bba8a52c3", + "fullUrl": "urn:uuid:464a25cc-b778-556b-98d6-637239e26837", "resource": { "resourceType": "Provenance", "meta": { @@ -261,7 +261,7 @@ ] }, "target": [ - { "reference": "urn:uuid:982583dd-9d31-5624-9952-fbc8218182c9" } + { "reference": "urn:uuid:ba01f42d-adfb-532e-8909-cf8ff6e1d183" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -288,7 +288,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:c72b00b2-52cb-5dc2-8a42-f9d8db034b1c" + "reference": "urn:uuid:a26ecd38-abeb-5d40-b1fb-7b6217720f23" } } ], @@ -298,7 +298,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:rBfmxEHQDXZC8-jWUzQz8czUa64bqdkH8NckUE-RiR0", + "value": "v0:fixture-key:1:sJSuge3Kkid_TsVY7p-oSU7CUFGvHXOZEVfyi6GRsvs", "type": { "coding": [ { @@ -322,7 +322,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:504ac48f-1656-51fe-81ac-7757ecc4b958" + "reference": "urn:uuid:ef4c6dd5-5186-52bd-81ff-c35ceff514cd" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json index 08899c5a..aedf9d15 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:22" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:22" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:74do0jSkuDfKuenVUUcy0Haqjndk7yEFyWA9053M-eY" + "value": "v0:fixture-key:1:ldt-blmNxjKdqbyUa4I71AKfMfwlCmOatocVyPgQyxs" } } ], - "fullUrl": "urn:uuid:02194ed4-cc34-5427-8086-fcc96af34e45", + "fullUrl": "urn:uuid:3e90f50b-622a-53e2-9479-a27ada726090", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:PzR6lkMO5KVY-dmv14Rd-vSvQe-8fjsMGWYQF3HT0fw", + "value": "v0:fixture-key:1:RKGg7_SVjLJ-TW2IEyKdFAqEFIyLZ6mZjuKs4CBBC9U", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:74do0jSkuDfKuenVUUcy0Haqjndk7yEFyWA9053M-eY", + "value": "v0:fixture-key:1:ldt-blmNxjKdqbyUa4I71AKfMfwlCmOatocVyPgQyxs", "type": { "coding": [ { @@ -137,11 +137,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:YCE1SS0miZABgACiuZAaNJojg0lELFd6skkEVCZAAB4" + "value": "v0:fixture-key:1:idFKgbnmzoK3YLYXQ9N6KsqXS3CAPEPqADjwroI9Bsg" } } ], - "fullUrl": "urn:uuid:f02ebbf1-0679-56a7-bcb5-9cafc2ad8a61", + "fullUrl": "urn:uuid:ca2701fb-c3c9-5019-9a86-da7b9aa683d2", "resource": { "resourceType": "Device", "meta": { @@ -152,7 +152,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:YCE1SS0miZABgACiuZAaNJojg0lELFd6skkEVCZAAB4", + "value": "v0:fixture-key:1:idFKgbnmzoK3YLYXQ9N6KsqXS3CAPEPqADjwroI9Bsg", "type": { "coding": [ { @@ -186,11 +186,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:BQ4HlGVpPu3nubyFC0IRbCDMaKBVW0K5MQ8fPLhGZw0" + "value": "v0:fixture-key:1:h1-IrXtzorIt0CuTA5q7b8m8r6AzfjVwt8gA8BpTkZA" } } ], - "fullUrl": "urn:uuid:971871ab-2052-50d7-a122-c6bdbc8ccc9c", + "fullUrl": "urn:uuid:e575699a-b0df-5dc5-9d5a-e1ee8f319c1d", "resource": { "resourceType": "Device", "meta": { @@ -201,7 +201,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:BQ4HlGVpPu3nubyFC0IRbCDMaKBVW0K5MQ8fPLhGZw0", + "value": "v0:fixture-key:1:h1-IrXtzorIt0CuTA5q7b8m8r6AzfjVwt8gA8BpTkZA", "type": { "coding": [ { @@ -249,11 +249,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:VjIqz9kMJoV2559rrdJqlPf1G0QnsAMsDZ3O3X_e9H8" + "value": "n0:conversion-provenance:0:G3eA3tIQdES8WikO3gyu5o7DnXbrq-Rc2xFFRMP9KVA" } } ], - "fullUrl": "urn:uuid:c48d93ce-bd44-52f3-bafd-22fb07c28c65", + "fullUrl": "urn:uuid:5b2461b4-1c63-5b4f-b75f-d9f0f8e78d0c", "resource": { "resourceType": "Provenance", "meta": { @@ -262,7 +262,7 @@ ] }, "target": [ - { "reference": "urn:uuid:02194ed4-cc34-5427-8086-fcc96af34e45" } + { "reference": "urn:uuid:3e90f50b-622a-53e2-9479-a27ada726090" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -289,7 +289,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:971871ab-2052-50d7-a122-c6bdbc8ccc9c" + "reference": "urn:uuid:e575699a-b0df-5dc5-9d5a-e1ee8f319c1d" } } ], @@ -299,7 +299,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:PzR6lkMO5KVY-dmv14Rd-vSvQe-8fjsMGWYQF3HT0fw", + "value": "v0:fixture-key:1:RKGg7_SVjLJ-TW2IEyKdFAqEFIyLZ6mZjuKs4CBBC9U", "type": { "coding": [ { @@ -323,7 +323,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:f02ebbf1-0679-56a7-bcb5-9cafc2ad8a61" + "reference": "urn:uuid:ca2701fb-c3c9-5019-9a86-da7b9aa683d2" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json b/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json index abedd530..72738188 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:23" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:23" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:wy9-Uph5fs_HJNnUTps5cFe8iYuZlikYcoNL7hgIOXk" + "value": "v0:fixture-key:1:r10WUFXbDXvxtZpsCniA4V0TxQygNJFC5AEv23Q2oXU" } } ], - "fullUrl": "urn:uuid:fc7e37fe-b91c-5bf1-a159-e6c33dd63f44", + "fullUrl": "urn:uuid:a1134f0f-22c8-5c88-add6-5fb90cabf8c2", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:1rFsF0Psn65i5MhnBzXWzIJ__WF67rlZycsEC2JTkdg", + "value": "v0:fixture-key:1:TrGlFtP5wdk-NzsSnMruKHD25VHWmapGR8rUdWfjLEk", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:wy9-Uph5fs_HJNnUTps5cFe8iYuZlikYcoNL7hgIOXk", + "value": "v0:fixture-key:1:r10WUFXbDXvxtZpsCniA4V0TxQygNJFC5AEv23Q2oXU", "type": { "coding": [ { @@ -135,11 +135,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:qI6tkrqoQ3Kan47MQpPJX26Ad5HRTnk91pRTHWhIPD4" + "value": "v0:fixture-key:1:Suyzh8Jk_ZVJrdhLmLMe8ydUYu_IJuKhg8MVfBTTsjw" } } ], - "fullUrl": "urn:uuid:063ecd77-4aa0-5be2-ad80-1f9dd90394ec", + "fullUrl": "urn:uuid:a4e868e2-8d17-5b8b-9a65-d26a146f5af0", "resource": { "resourceType": "Device", "meta": { @@ -150,7 +150,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:qI6tkrqoQ3Kan47MQpPJX26Ad5HRTnk91pRTHWhIPD4", + "value": "v0:fixture-key:1:Suyzh8Jk_ZVJrdhLmLMe8ydUYu_IJuKhg8MVfBTTsjw", "type": { "coding": [ { @@ -184,11 +184,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:1dLX2SKN2UKqG7-cYWKWag_gvU92cRMTi5hXWQqbasM" + "value": "v0:fixture-key:1:Z2HsC_lKlXKDL6bi7wIzi2ovDucUm2-W2e0TLsG-WI0" } } ], - "fullUrl": "urn:uuid:bb6720e1-6b84-5c3e-95a1-da0f08b03cc5", + "fullUrl": "urn:uuid:119259a9-5f5d-5b2b-b86d-f71052244ad1", "resource": { "resourceType": "Device", "meta": { @@ -199,7 +199,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:1dLX2SKN2UKqG7-cYWKWag_gvU92cRMTi5hXWQqbasM", + "value": "v0:fixture-key:1:Z2HsC_lKlXKDL6bi7wIzi2ovDucUm2-W2e0TLsG-WI0", "type": { "coding": [ { @@ -247,11 +247,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:o3IalVxfclA_0ZXvjerPqhbrJGkXuRut15sEGBvzpTo" + "value": "n0:conversion-provenance:0:2N9RBu-o6BhoV6evxl2ZIn8X-bRd76K7CyySv3zfNuE" } } ], - "fullUrl": "urn:uuid:74b4fda5-d211-5795-9fad-98f795eb773b", + "fullUrl": "urn:uuid:5a2b779a-59f5-5c57-8ca8-410e6bebc4c0", "resource": { "resourceType": "Provenance", "meta": { @@ -260,7 +260,7 @@ ] }, "target": [ - { "reference": "urn:uuid:fc7e37fe-b91c-5bf1-a159-e6c33dd63f44" } + { "reference": "urn:uuid:a1134f0f-22c8-5c88-add6-5fb90cabf8c2" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -287,7 +287,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:bb6720e1-6b84-5c3e-95a1-da0f08b03cc5" + "reference": "urn:uuid:119259a9-5f5d-5b2b-b86d-f71052244ad1" } } ], @@ -297,7 +297,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:1rFsF0Psn65i5MhnBzXWzIJ__WF67rlZycsEC2JTkdg", + "value": "v0:fixture-key:1:TrGlFtP5wdk-NzsSnMruKHD25VHWmapGR8rUdWfjLEk", "type": { "coding": [ { @@ -321,7 +321,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:063ecd77-4aa0-5be2-ad80-1f9dd90394ec" + "reference": "urn:uuid:a4e868e2-8d17-5b8b-9a65-d26a146f5af0" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json b/packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json deleted file mode 100644 index e68baf64..00000000 --- a/packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json +++ /dev/null @@ -1,262 +0,0 @@ -{ - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, - "identifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|24|exchange-bundle" - }, - "type": "collection", - "timestamp": "2026-08-20T12:02:00Z", - "entry": [ - { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", - "valueIdentifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|daily-resting-heart-rate|source-resting-heart-rate" - } - } - ], - "fullUrl": "urn:uuid:96ef5a1e-7a8f-5bdf-b120-113f1b0a5ef5", - "resource": { - "resourceType": "Observation", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-resting-heart-rate", - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-observation" - ] - }, - "extension": [ - { - "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", - "valueCode": "google-health-api" - }, - { - "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", - "valueCode": "google-health-api/daily-resting-heart-rate" - }, - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", - "valueCoding": { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", - "code": "automatically-recorded" - } - } - ], - "identifier": [ - { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|daily-resting-heart-rate|source-resting-heart-rate" - } - ], - "status": "final", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "activity", - "display": "Activity" - } - ], - "text": "Activity" - } - ], - "code": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement", - "code": "resting-heart-rate" - } - ] - }, - "subject": { "reference": "Patient/example" }, - "effectivePeriod": { - "start": "2026-08-19T22:40:00.000-07:00", - "end": "2026-08-20T06:55:00.000-07:00" - }, - "issued": "2026-08-20T12:01:00Z", - "valueQuantity": { - "value": 58, - "unit": "beats/minute", - "system": "http://unitsofmeasure.org", - "code": "/min" - }, - "method": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-aggregation-method", - "code": "daily-mean", - "display": "Daily mean" - } - ], - "text": "Daily mean" - } - } - }, - { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", - "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" - } - } - ], - "fullUrl": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e", - "resource": { - "resourceType": "Device", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" - ] - }, - "identifier": [ - { - "system": "https://grovealliance.org/fhir/testing/data-origins", - "value": "connected-provider" - } - ], - "status": "active", - "deviceName": [ - { - "name": "Synthetic connected provider", - "type": "user-friendly-name" - } - ] - } - }, - { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", - "valueIdentifier": { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" - } - } - ], - "fullUrl": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6", - "resource": { - "resourceType": "Device", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" - ] - }, - "identifier": [ - { - "system": "https://grovealliance.org/fhir/testing/applications", - "value": "grove-ts-0.2" - } - ], - "status": "active", - "deviceName": [ - { - "name": "Grove TypeScript conformance producer", - "type": "user-friendly-name" - } - ], - "version": [ - { - "type": { - "coding": [ - { - "system": "urn:iso:std:iso:11073:10101", - "code": "531975", - "display": "Software revision" - } - ] - }, - "value": "0.0.0" - } - ] - } - }, - { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-identifier", - "valueIdentifier": { - "system": "urn:grove:provider-graph:org.grovealliance.example", - "value": "google-health-api|24|conversion-provenance" - } - } - ], - "fullUrl": "urn:uuid:f146dbac-bc58-5e09-b014-b0599dfc6a4b", - "resource": { - "resourceType": "Provenance", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/provider-conversion-provenance" - ] - }, - "target": [ - { "reference": "urn:uuid:96ef5a1e-7a8f-5bdf-b120-113f1b0a5ef5" } - ], - "recorded": "2026-08-20T12:02:00Z", - "activity": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", - "code": "transform", - "display": "Transform/Translate Record Lifecycle Event" - } - ], - "text": "Transform/Translate Record Lifecycle Event" - }, - "agent": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", - "code": "assembler", - "display": "Assembler" - } - ], - "text": "Assembler" - }, - "who": { - "reference": "urn:uuid:e000a26a-c10e-54eb-9573-1f0721d6ade6" - } - } - ], - "entity": [ - { - "role": "source", - "what": { - "identifier": { - "system": "https://grovealliance.org/fhir/providers/NamingSystem/provider-source-record-id", - "value": "v1:google-health-api|account-google-health-api|daily-resting-heart-rate|source-resting-heart-rate" - } - }, - "agent": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", - "code": "enterer", - "display": "Enterer" - } - ], - "text": "Enterer" - }, - "who": { - "reference": "urn:uuid:8ade7702-2fc8-5fc3-b489-25894b62ae2e" - } - } - ] - } - ] - } - } - ] -} diff --git a/packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json.license b/packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json.license deleted file mode 100644 index fa4e8d92..00000000 --- a/packages/fhir/fixtures/conformance/resources/mobile-resting-heart-rate.json.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json b/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json index c73f03db..6e5814b4 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:24" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:24" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:OPgVVQo-3vB0P8wfU6EZgrmsFPE3ASKqJLQMzoZ6f_4" + "value": "v0:fixture-key:1:fdLtyB1vt86GnGXA2wJPWN5tMekIYkeKXWnBPMvtvsc" } } ], - "fullUrl": "urn:uuid:2306c7c3-ee62-52b9-a381-75b067203094", + "fullUrl": "urn:uuid:83c3b3f4-b249-5e84-a42f-680a9c7cb75c", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:rYPq44R2whk-DQQA4WbtFjoPYLBRjkVfi3j9Odic-Cw", + "value": "v0:fixture-key:1:jnbU2fWZpeemaU1jjrjbCQ6EkOKBh5itBxi1bLLujW0", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:OPgVVQo-3vB0P8wfU6EZgrmsFPE3ASKqJLQMzoZ6f_4", + "value": "v0:fixture-key:1:fdLtyB1vt86GnGXA2wJPWN5tMekIYkeKXWnBPMvtvsc", "type": { "coding": [ { @@ -124,11 +124,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:7DRSkHTWPTJrwR7ZE_cfDazlFcNJv3aO5LEkTe9Qy8M" + "value": "v0:fixture-key:1:xBHRY4vXBxZ2fEDb8AicBvFVjYOStAf_p_DKiN7rnoo" } } ], - "fullUrl": "urn:uuid:5e361e72-9af6-5a43-8982-e8e72d3acaa7", + "fullUrl": "urn:uuid:64c16cac-7015-51b2-92e2-6bc3ac450a3a", "resource": { "resourceType": "Device", "meta": { @@ -139,7 +139,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:7DRSkHTWPTJrwR7ZE_cfDazlFcNJv3aO5LEkTe9Qy8M", + "value": "v0:fixture-key:1:xBHRY4vXBxZ2fEDb8AicBvFVjYOStAf_p_DKiN7rnoo", "type": { "coding": [ { @@ -173,11 +173,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:lZ6KGumnLTW38-i20z8u_8kqp66JTj4af-1HVhejeFs" + "value": "v0:fixture-key:1:uZEfoSsNnjS4K9JmUnCoUz_vO2fD_l8IPkRhi946zPo" } } ], - "fullUrl": "urn:uuid:7026fc91-3968-58d8-8afa-4486d0d5f643", + "fullUrl": "urn:uuid:648c018e-0d55-5039-987a-08ad126aeac9", "resource": { "resourceType": "Device", "meta": { @@ -188,7 +188,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:lZ6KGumnLTW38-i20z8u_8kqp66JTj4af-1HVhejeFs", + "value": "v0:fixture-key:1:uZEfoSsNnjS4K9JmUnCoUz_vO2fD_l8IPkRhi946zPo", "type": { "coding": [ { @@ -236,11 +236,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:xP9z_G8IUzJ4hRcWa8rTuns4Tr9rYS1bQM1r_I5YsCQ" + "value": "n0:conversion-provenance:0:MtBt0Wkhm18_pyA_rsaK91FIZ0_QarfjD9ZKfe1L4U0" } } ], - "fullUrl": "urn:uuid:1044c0b8-4e09-5ef5-ac6d-b6d44d86cb6a", + "fullUrl": "urn:uuid:5097c006-bede-5794-b1b9-ccebfcba4748", "resource": { "resourceType": "Provenance", "meta": { @@ -249,7 +249,7 @@ ] }, "target": [ - { "reference": "urn:uuid:2306c7c3-ee62-52b9-a381-75b067203094" } + { "reference": "urn:uuid:83c3b3f4-b249-5e84-a42f-680a9c7cb75c" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -276,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:7026fc91-3968-58d8-8afa-4486d0d5f643" + "reference": "urn:uuid:648c018e-0d55-5039-987a-08ad126aeac9" } } ], @@ -286,7 +286,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:rYPq44R2whk-DQQA4WbtFjoPYLBRjkVfi3j9Odic-Cw", + "value": "v0:fixture-key:1:jnbU2fWZpeemaU1jjrjbCQ6EkOKBh5itBxi1bLLujW0", "type": { "coding": [ { @@ -310,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:5e361e72-9af6-5a43-8982-e8e72d3acaa7" + "reference": "urn:uuid:64c16cac-7015-51b2-92e2-6bc3ac450a3a" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json index 505189d6..46f4927c 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:25" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:25" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:sJcUcs4VwEVIa3dqXjiSo_vdAXo8cxdCDBwc4EdxSXo" + "value": "v0:fixture-key:1:RrQhI3fnS7enR0ln-yibj1hlouMBhA3GI8L9NVWkON4" } } ], - "fullUrl": "urn:uuid:850e3576-5190-5c99-a859-2797a7263c8e", + "fullUrl": "urn:uuid:e63294c3-551a-5e44-8555-92a844716e30", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:m95PvVsdr0AKhMfUZt1K9o_5zlz9OmHPQLbHLBEsaIs", + "value": "v0:fixture-key:1:Ox1oQ-oDGcR1WotanmbfWhWK2iBcfBG6TEDfZmgHBm4", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:sJcUcs4VwEVIa3dqXjiSo_vdAXo8cxdCDBwc4EdxSXo", + "value": "v0:fixture-key:1:RrQhI3fnS7enR0ln-yibj1hlouMBhA3GI8L9NVWkON4", "type": { "coding": [ { @@ -137,11 +137,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:RmXP5foemvhNM5kgvSo-8h9fhQD-abNyQ1-N5Na8-D8" + "value": "v0:fixture-key:1:jxJJDBZztrLVbB7pyk4VKRDVrbOCbEIqcdIdqJmFsJw" } } ], - "fullUrl": "urn:uuid:5af1e779-4b55-5b83-afe0-f01908ec7d0f", + "fullUrl": "urn:uuid:3900cdcb-8c1a-55b9-b0dd-b02fb6023801", "resource": { "resourceType": "Device", "meta": { @@ -152,7 +152,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:RmXP5foemvhNM5kgvSo-8h9fhQD-abNyQ1-N5Na8-D8", + "value": "v0:fixture-key:1:jxJJDBZztrLVbB7pyk4VKRDVrbOCbEIqcdIdqJmFsJw", "type": { "coding": [ { @@ -186,11 +186,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:LxTJK0oYfZ0GnmSQ7cNedoLg1Ug4k1UMiwRAU92ala0" + "value": "v0:fixture-key:1:N8JAO-8OSs3AhGY2IxA7UNiM9whJhJqGnzFplOsRHOo" } } ], - "fullUrl": "urn:uuid:2d89e8f7-ba76-5501-b905-0e045507e2ca", + "fullUrl": "urn:uuid:c57e5208-b0a4-5ac2-bef1-6c4eefa43bc8", "resource": { "resourceType": "Device", "meta": { @@ -201,7 +201,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:LxTJK0oYfZ0GnmSQ7cNedoLg1Ug4k1UMiwRAU92ala0", + "value": "v0:fixture-key:1:N8JAO-8OSs3AhGY2IxA7UNiM9whJhJqGnzFplOsRHOo", "type": { "coding": [ { @@ -249,11 +249,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:tnLyeUCtJuyPfvuAobQ0YR_MRoaluLng_oni4JWltng" + "value": "n0:conversion-provenance:0:SiMEd3qZlkyTXV_qGX4ZkIvuHd8Fm1jNouSrwNjZJ-I" } } ], - "fullUrl": "urn:uuid:46525a86-3466-5731-88ff-5da588b95285", + "fullUrl": "urn:uuid:88a35473-0b28-556c-8673-32b1acf1ce4e", "resource": { "resourceType": "Provenance", "meta": { @@ -262,7 +262,7 @@ ] }, "target": [ - { "reference": "urn:uuid:850e3576-5190-5c99-a859-2797a7263c8e" } + { "reference": "urn:uuid:e63294c3-551a-5e44-8555-92a844716e30" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -289,7 +289,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:2d89e8f7-ba76-5501-b905-0e045507e2ca" + "reference": "urn:uuid:c57e5208-b0a4-5ac2-bef1-6c4eefa43bc8" } } ], @@ -299,7 +299,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:m95PvVsdr0AKhMfUZt1K9o_5zlz9OmHPQLbHLBEsaIs", + "value": "v0:fixture-key:1:Ox1oQ-oDGcR1WotanmbfWhWK2iBcfBG6TEDfZmgHBm4", "type": { "coding": [ { @@ -323,7 +323,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:5af1e779-4b55-5b83-afe0-f01908ec7d0f" + "reference": "urn:uuid:3900cdcb-8c1a-55b9-b0dd-b02fb6023801" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json index 622cb759..41dc50eb 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:26" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:26" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:NLUKdxH2Bu9L0FILQLNJCtIhFMmwOFfAJbLBVNdTgqU" + "value": "v0:fixture-key:1:BnHqjF7z-fvb4wgudNxtBNc9e0Td37yuHakVXYwIpdA" } } ], - "fullUrl": "urn:uuid:05244da9-1825-5826-b0a1-0c71861a6842", + "fullUrl": "urn:uuid:c7d05f45-e906-5225-ac65-22dc09975c6a", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:ngybYnAViMXOMr2U5sQ4vlDLVyCqV8tjmEoLFIbtAd0", + "value": "v0:fixture-key:1:tq2KjCGUlwWO9GMzdQoVswuS6YBF6h5Wpd1BnbXhhYc", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:NLUKdxH2Bu9L0FILQLNJCtIhFMmwOFfAJbLBVNdTgqU", + "value": "v0:fixture-key:1:BnHqjF7z-fvb4wgudNxtBNc9e0Td37yuHakVXYwIpdA", "type": { "coding": [ { @@ -127,11 +127,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:F-jVtzp1WAoTIXEgsgTOIS9j4hhepV_Di4WqS7_bSXM" + "value": "v0:fixture-key:1:eAmZ5wOIQlO_MYAqnpwJRFjvli30bYvkdRtDO1wMfOQ" } } ], - "fullUrl": "urn:uuid:1703fa05-5a33-5d03-8c55-b11fb257f88e", + "fullUrl": "urn:uuid:83d5b122-5954-54c7-b4b2-c0fa6e8ef9b6", "resource": { "resourceType": "Device", "meta": { @@ -142,7 +142,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:F-jVtzp1WAoTIXEgsgTOIS9j4hhepV_Di4WqS7_bSXM", + "value": "v0:fixture-key:1:eAmZ5wOIQlO_MYAqnpwJRFjvli30bYvkdRtDO1wMfOQ", "type": { "coding": [ { @@ -176,11 +176,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:zPcfG7xgqixhjIIJ2woXGyq4HhuCrwEbG2VYjvBVdO0" + "value": "v0:fixture-key:1:7MURadTgoXGjqOMEPmM18FYdUWAp08tL56ZbkpMj6HA" } } ], - "fullUrl": "urn:uuid:6dc3aacc-4ed2-5935-868b-683217dd45ee", + "fullUrl": "urn:uuid:6eae3163-c8bf-5af0-a2aa-898f30fedbaa", "resource": { "resourceType": "Device", "meta": { @@ -191,7 +191,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:zPcfG7xgqixhjIIJ2woXGyq4HhuCrwEbG2VYjvBVdO0", + "value": "v0:fixture-key:1:7MURadTgoXGjqOMEPmM18FYdUWAp08tL56ZbkpMj6HA", "type": { "coding": [ { @@ -239,11 +239,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:Xzycdixp8jYP3zAiTHoccUKLp0l2zUcUUzoyxaNHIdU" + "value": "n0:conversion-provenance:0:KOdHjEeddjWKfXuVEGFAN1GhoEkZv3s2YSypNw3sJ40" } } ], - "fullUrl": "urn:uuid:9f1fb7d0-9f73-5bb0-abaa-2179683a9481", + "fullUrl": "urn:uuid:5e0b87cc-e153-5c3e-a848-1732a6dd7702", "resource": { "resourceType": "Provenance", "meta": { @@ -252,7 +252,7 @@ ] }, "target": [ - { "reference": "urn:uuid:05244da9-1825-5826-b0a1-0c71861a6842" } + { "reference": "urn:uuid:c7d05f45-e906-5225-ac65-22dc09975c6a" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -279,7 +279,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:6dc3aacc-4ed2-5935-868b-683217dd45ee" + "reference": "urn:uuid:6eae3163-c8bf-5af0-a2aa-898f30fedbaa" } } ], @@ -289,7 +289,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:ngybYnAViMXOMr2U5sQ4vlDLVyCqV8tjmEoLFIbtAd0", + "value": "v0:fixture-key:1:tq2KjCGUlwWO9GMzdQoVswuS6YBF6h5Wpd1BnbXhhYc", "type": { "coding": [ { @@ -313,7 +313,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:1703fa05-5a33-5d03-8c55-b11fb257f88e" + "reference": "urn:uuid:83d5b122-5954-54c7-b4b2-c0fa6e8ef9b6" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json b/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json index 4d86dab2..47ae750a 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:27" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:27" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:-ydU3t1w_BNle9TqOcbDNcouP-BX5H8BcRZo-twcKGk" + "value": "v0:fixture-key:1:v_p29kjZUa7EaT8C-ggI4-rA-62O_X1K0UchTFhf7YU" } } ], - "fullUrl": "urn:uuid:f4350c5c-addb-5b37-a6e4-f70b70c7c204", + "fullUrl": "urn:uuid:7f60454b-9cc2-58ee-bedd-defb9560013f", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:xbJ-iE9UQxhPtbJcTMFNlvl2E1rw_RFHGl-cR_afGps", + "value": "v0:fixture-key:1:gOtALovyEyY_gqwlVxmn-BvfUaxigGpRasfr7UORnlM", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:-ydU3t1w_BNle9TqOcbDNcouP-BX5H8BcRZo-twcKGk", + "value": "v0:fixture-key:1:v_p29kjZUa7EaT8C-ggI4-rA-62O_X1K0UchTFhf7YU", "type": { "coding": [ { @@ -140,11 +140,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:m4yPR6-T33YEk187BDUDcWabUI_5Wwu452efX4AaLm0" + "value": "v0:fixture-key:1:dNoKM-UEFi5MYBVp7UEtHG2Ao0--GXrzOSQuQxAxeRg" } } ], - "fullUrl": "urn:uuid:260fcfea-e93b-5a8f-adb7-20fe6a38b754", + "fullUrl": "urn:uuid:5c611825-6542-516b-ba90-66684d349d39", "resource": { "resourceType": "Device", "meta": { @@ -155,7 +155,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:m4yPR6-T33YEk187BDUDcWabUI_5Wwu452efX4AaLm0", + "value": "v0:fixture-key:1:dNoKM-UEFi5MYBVp7UEtHG2Ao0--GXrzOSQuQxAxeRg", "type": { "coding": [ { @@ -189,11 +189,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:CNHz0YMev5m3ShuC3Kebp4EwElYtojeBTFJYEIrg-D8" + "value": "v0:fixture-key:1:MDAetuhZk3l44pAAh5PaWGoW9IO9PGRm6X1ch4jI94M" } } ], - "fullUrl": "urn:uuid:a9a32d06-04d9-5278-a924-670d7bc766b4", + "fullUrl": "urn:uuid:cf7a92be-643f-504b-b60b-8f1c24c2aa5f", "resource": { "resourceType": "Device", "meta": { @@ -204,7 +204,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:CNHz0YMev5m3ShuC3Kebp4EwElYtojeBTFJYEIrg-D8", + "value": "v0:fixture-key:1:MDAetuhZk3l44pAAh5PaWGoW9IO9PGRm6X1ch4jI94M", "type": { "coding": [ { @@ -252,11 +252,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:vrW_O3cLSq7pn0a-04qbdRgIM4yITIaOddDA4Qp1uOY" + "value": "n0:conversion-provenance:0:DA75S6ZIPeJEPB6N1IgISQ79mrz3BS0sfaQ_CPg_4Bo" } } ], - "fullUrl": "urn:uuid:d8607389-4279-54a2-8f2e-2c8a2842e7fa", + "fullUrl": "urn:uuid:4c4d3370-e8d3-5721-a321-9a6d8edc8abc", "resource": { "resourceType": "Provenance", "meta": { @@ -265,7 +265,7 @@ ] }, "target": [ - { "reference": "urn:uuid:f4350c5c-addb-5b37-a6e4-f70b70c7c204" } + { "reference": "urn:uuid:7f60454b-9cc2-58ee-bedd-defb9560013f" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -292,7 +292,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:a9a32d06-04d9-5278-a924-670d7bc766b4" + "reference": "urn:uuid:cf7a92be-643f-504b-b60b-8f1c24c2aa5f" } } ], @@ -302,7 +302,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:xbJ-iE9UQxhPtbJcTMFNlvl2E1rw_RFHGl-cR_afGps", + "value": "v0:fixture-key:1:gOtALovyEyY_gqwlVxmn-BvfUaxigGpRasfr7UORnlM", "type": { "coding": [ { @@ -326,7 +326,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:260fcfea-e93b-5a8f-adb7-20fe6a38b754" + "reference": "urn:uuid:5c611825-6542-516b-ba90-66684d349d39" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-step-count.json b/packages/fhir/fixtures/conformance/resources/mobile-step-count.json index 69bbddaf..4c47a737 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-step-count.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-step-count.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:28" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:28" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:WGqZAqQU-biHsH5k-uG4BKM95u30CHO8TRD8tQx-52o" + "value": "v0:fixture-key:1:8mwhbk99yf3qpijHJ2VWzxAsXnypD6VVECucMVPuirw" } } ], - "fullUrl": "urn:uuid:438d43e3-26cf-55be-bc89-96acd497ede4", + "fullUrl": "urn:uuid:def6a4c8-a9f0-5d9b-a512-98d5dbfca87a", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:zjV-47AY0X438_8imhgO2vHu27AE-x8kUCjG-nGh74o", + "value": "v0:fixture-key:1:9zGYMkV1hVhzIUOw3odddQ_0JDsGiR_ZOl7_sV4ylaY", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:WGqZAqQU-biHsH5k-uG4BKM95u30CHO8TRD8tQx-52o", + "value": "v0:fixture-key:1:8mwhbk99yf3qpijHJ2VWzxAsXnypD6VVECucMVPuirw", "type": { "coding": [ { @@ -132,11 +132,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:hj-500Omt5CToIwO7T2ZIXacSAreh3YcnPVvKvLxNII" + "value": "v0:fixture-key:1:xaoyrihBfXy4uJJ28nRdUxKMsq08tNIl26Ucwjp7iQQ" } } ], - "fullUrl": "urn:uuid:d56441eb-cf67-585f-ba35-d3c329b65add", + "fullUrl": "urn:uuid:0a836d40-fd1f-5d14-a41b-a8525341c001", "resource": { "resourceType": "Device", "meta": { @@ -147,7 +147,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:hj-500Omt5CToIwO7T2ZIXacSAreh3YcnPVvKvLxNII", + "value": "v0:fixture-key:1:xaoyrihBfXy4uJJ28nRdUxKMsq08tNIl26Ucwjp7iQQ", "type": { "coding": [ { @@ -181,11 +181,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:VqZKJe_tfkaYF7ANsPk3PxPhF5EPlZGBfXkY6NyqMXg" + "value": "v0:fixture-key:1:-_UQxypI0SjyF6fEwjQ5odDh_ER349OWAWVuEytoLMM" } } ], - "fullUrl": "urn:uuid:0e2180df-172f-5273-a8d9-4705487cc527", + "fullUrl": "urn:uuid:456ac9d1-6a74-5fd7-9ca9-282783f29731", "resource": { "resourceType": "Device", "meta": { @@ -196,7 +196,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:VqZKJe_tfkaYF7ANsPk3PxPhF5EPlZGBfXkY6NyqMXg", + "value": "v0:fixture-key:1:-_UQxypI0SjyF6fEwjQ5odDh_ER349OWAWVuEytoLMM", "type": { "coding": [ { @@ -244,11 +244,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:p4Bb3RHFQNPqKMfld4OQ9J-nXopJz5GdWOIxaLug0R0" + "value": "n0:conversion-provenance:0:cD_VWhvlrj683Wch8scpXL5DtlefkyZA-WfO372_inM" } } ], - "fullUrl": "urn:uuid:5be6bdd9-d758-50dc-ae46-8252511af490", + "fullUrl": "urn:uuid:19777a0c-5c43-567f-b887-e9ec799d80bb", "resource": { "resourceType": "Provenance", "meta": { @@ -257,7 +257,7 @@ ] }, "target": [ - { "reference": "urn:uuid:438d43e3-26cf-55be-bc89-96acd497ede4" } + { "reference": "urn:uuid:def6a4c8-a9f0-5d9b-a512-98d5dbfca87a" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -284,7 +284,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:0e2180df-172f-5273-a8d9-4705487cc527" + "reference": "urn:uuid:456ac9d1-6a74-5fd7-9ca9-282783f29731" } } ], @@ -294,7 +294,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:zjV-47AY0X438_8imhgO2vHu27AE-x8kUCjG-nGh74o", + "value": "v0:fixture-key:1:9zGYMkV1hVhzIUOw3odddQ_0JDsGiR_ZOl7_sV4ylaY", "type": { "coding": [ { @@ -318,7 +318,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:d56441eb-cf67-585f-ba35-d3c329b65add" + "reference": "urn:uuid:0a836d40-fd1f-5d14-a41b-a8525341c001" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json b/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json index e9018251..a66888a5 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:29" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:29" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:y1QuJ7hfGk7zwtwTHj5_Fwg8BipVuY9aQKIGY-YmGxY" + "value": "v0:fixture-key:1:byw_l-KxsVtgYYiKT-e5xUOjsnJOeELyiUftu7HeVCA" } } ], - "fullUrl": "urn:uuid:027e2c38-29b6-5209-bf70-10f7c4948814", + "fullUrl": "urn:uuid:f0075728-43f1-5fe6-95d9-53613a722202", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:ezvIpUivUDmQBnlRULi9SnbNSB3d96yhaPRlBF21pjE", + "value": "v0:fixture-key:1:tfPeQl3pTiKfgjBUofMUoVbrLlwsNZn1cHtIxccehpY", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:y1QuJ7hfGk7zwtwTHj5_Fwg8BipVuY9aQKIGY-YmGxY", + "value": "v0:fixture-key:1:byw_l-KxsVtgYYiKT-e5xUOjsnJOeELyiUftu7HeVCA", "type": { "coding": [ { @@ -129,11 +129,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:_Y2QNP-2XNnfie17gelY7kaafP-2ltXEvQ76QhRr98w" + "value": "v0:fixture-key:1:mEOIRyxLXC_8bNJ1cmx0JcKaPGXE2oW4a0PL094BZWk" } } ], - "fullUrl": "urn:uuid:addc23e6-931e-5948-81ad-c1361ccc3420", + "fullUrl": "urn:uuid:48c66d02-a1aa-540f-acf7-c768a7c96979", "resource": { "resourceType": "Device", "meta": { @@ -144,7 +144,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:_Y2QNP-2XNnfie17gelY7kaafP-2ltXEvQ76QhRr98w", + "value": "v0:fixture-key:1:mEOIRyxLXC_8bNJ1cmx0JcKaPGXE2oW4a0PL094BZWk", "type": { "coding": [ { @@ -178,11 +178,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:jLj49odbMlRoLaVLJX5O6SK82PjRttC5InrGJPz9zNk" + "value": "v0:fixture-key:1:JUAIgKxQnM_ad5eD24yDcecDG_XelEy6eEdzZKT7zi0" } } ], - "fullUrl": "urn:uuid:f96648d5-5733-58cc-ae77-6ca173f0f942", + "fullUrl": "urn:uuid:d4b2afc0-724c-5834-aced-2254893693c5", "resource": { "resourceType": "Device", "meta": { @@ -193,7 +193,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:jLj49odbMlRoLaVLJX5O6SK82PjRttC5InrGJPz9zNk", + "value": "v0:fixture-key:1:JUAIgKxQnM_ad5eD24yDcecDG_XelEy6eEdzZKT7zi0", "type": { "coding": [ { @@ -241,11 +241,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:g-h8ZNobhWcYYUOfsE0AqLdRYDHq1Kq0imymekcXVLk" + "value": "n0:conversion-provenance:0:ri56YyNp3A9fJTTp-1IagZQJnuquHZ6s4vQx60awN9I" } } ], - "fullUrl": "urn:uuid:920158b5-ec2c-5c17-9516-99dc7970345b", + "fullUrl": "urn:uuid:1e759f0a-1feb-5565-b08e-4dbe3e9b53d2", "resource": { "resourceType": "Provenance", "meta": { @@ -254,7 +254,7 @@ ] }, "target": [ - { "reference": "urn:uuid:027e2c38-29b6-5209-bf70-10f7c4948814" } + { "reference": "urn:uuid:f0075728-43f1-5fe6-95d9-53613a722202" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -281,7 +281,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:f96648d5-5733-58cc-ae77-6ca173f0f942" + "reference": "urn:uuid:d4b2afc0-724c-5834-aced-2254893693c5" } } ], @@ -291,7 +291,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:ezvIpUivUDmQBnlRULi9SnbNSB3d96yhaPRlBF21pjE", + "value": "v0:fixture-key:1:tfPeQl3pTiKfgjBUofMUoVbrLlwsNZn1cHtIxccehpY", "type": { "coding": [ { @@ -315,7 +315,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:addc23e6-931e-5948-81ad-c1361ccc3420" + "reference": "urn:uuid:48c66d02-a1aa-540f-acf7-c768a7c96979" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-workout.json b/packages/fhir/fixtures/conformance/resources/mobile-workout.json index 4595e6ee..c5854299 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-workout.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-workout.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:30" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:30" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:bkcjuqFK9r7EYstavDxeB12JSLv5F6KNeLRYIkIadCA" + "value": "v0:fixture-key:1:7Xrr8q_a0KNfW6JdKupGPUOm4HTozWQ99TksbjKEvYc" } } ], - "fullUrl": "urn:uuid:113c473d-3cf3-57b5-bcf5-f19b16e4e8fc", + "fullUrl": "urn:uuid:39ed2a93-60f9-5643-a707-d4da21771ca3", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:thqKd9HD2EQem0OiJCH3nlctn93jJBEyqSdl78-66CM", + "value": "v0:fixture-key:1:LcqDQSNcMDxugFlPHpH5RFXlEN4jj61vsIebOIwHBus", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:bkcjuqFK9r7EYstavDxeB12JSLv5F6KNeLRYIkIadCA", + "value": "v0:fixture-key:1:7Xrr8q_a0KNfW6JdKupGPUOm4HTozWQ99TksbjKEvYc", "type": { "coding": [ { @@ -134,11 +134,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:BGuFhqjFeFfs0-Qgf00AVbu7_r1z5MDTmXMJNj3uCsc" + "value": "v0:fixture-key:1:6nMZv2pJPKEo82zRsPoCm8_Bzt1S1EZ_AyEFy1KGgtA" } } ], - "fullUrl": "urn:uuid:3408d591-5cf1-501c-a88b-799d372ae788", + "fullUrl": "urn:uuid:fda6fbb7-3989-5f51-8167-3a6114856c2a", "resource": { "resourceType": "Device", "meta": { @@ -149,7 +149,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:BGuFhqjFeFfs0-Qgf00AVbu7_r1z5MDTmXMJNj3uCsc", + "value": "v0:fixture-key:1:6nMZv2pJPKEo82zRsPoCm8_Bzt1S1EZ_AyEFy1KGgtA", "type": { "coding": [ { @@ -183,11 +183,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:EioXEBFCGCgE99RBqs323faTsPOkRTnlxatrnLU0tUg" + "value": "v0:fixture-key:1:a7Dc9F2riqMjx8rq2NTJLmiF4GBdJSbO4Rtj9kp1njc" } } ], - "fullUrl": "urn:uuid:86a0ab55-0f00-5d71-98e1-8a01504f10e1", + "fullUrl": "urn:uuid:01837f45-5907-5a36-86c4-e518cc98d3de", "resource": { "resourceType": "Device", "meta": { @@ -198,7 +198,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:EioXEBFCGCgE99RBqs323faTsPOkRTnlxatrnLU0tUg", + "value": "v0:fixture-key:1:a7Dc9F2riqMjx8rq2NTJLmiF4GBdJSbO4Rtj9kp1njc", "type": { "coding": [ { @@ -246,11 +246,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:UyapMnpmGzNs5utTNKfeWbDCVAmyEtlbo8r-rZ5J-Os" + "value": "n0:conversion-provenance:0:W9MmnB630oKz8k7NTcsS7eag8L4yDQnnpOFHDf-m1oo" } } ], - "fullUrl": "urn:uuid:09f73288-ea2d-58cb-afe5-56d5686e8f66", + "fullUrl": "urn:uuid:5f6e4358-2bf6-5f07-b679-ac7a5bf91aac", "resource": { "resourceType": "Provenance", "meta": { @@ -259,7 +259,7 @@ ] }, "target": [ - { "reference": "urn:uuid:113c473d-3cf3-57b5-bcf5-f19b16e4e8fc" } + { "reference": "urn:uuid:39ed2a93-60f9-5643-a707-d4da21771ca3" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -286,7 +286,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:86a0ab55-0f00-5d71-98e1-8a01504f10e1" + "reference": "urn:uuid:01837f45-5907-5a36-86c4-e518cc98d3de" } } ], @@ -296,7 +296,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:thqKd9HD2EQem0OiJCH3nlctn93jJBEyqSdl78-66CM", + "value": "v0:fixture-key:1:LcqDQSNcMDxugFlPHpH5RFXlEN4jj61vsIebOIwHBus", "type": { "coding": [ { @@ -320,7 +320,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:3408d591-5cf1-501c-a88b-799d372ae788" + "reference": "urn:uuid:fda6fbb7-3989-5f51-8167-3a6114856c2a" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json b/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json index 10b4a9d2..b2122cf6 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json +++ b/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:31" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:31" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:-vWNbpGxC9yPeIH3Rb8iHCw6D2FqZ3VB-KfE2P0OtnI" + "value": "v0:fixture-key:1:bOgI-QR3GvHruf1pWLzIRIPn3UomHEkWtri0pIvy0X0" } } ], - "fullUrl": "urn:uuid:538b4b82-cb94-53fb-9ab2-b5ce4d3415d1", + "fullUrl": "urn:uuid:0286e181-761e-5a07-931f-2e415f5ef041", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:jczOkpWlxBPJaIzeniJl_W2BzHPfkzt3WgRMZ7Dgo-A", + "value": "v0:fixture-key:1:40Iy1wmaK8u1RiYFs3w-vdPd5r_vC9_FOJcZm9L47Ug", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:-vWNbpGxC9yPeIH3Rb8iHCw6D2FqZ3VB-KfE2P0OtnI", + "value": "v0:fixture-key:1:bOgI-QR3GvHruf1pWLzIRIPn3UomHEkWtri0pIvy0X0", "type": { "coding": [ { @@ -142,11 +142,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:XD4XTH_UnJ7EfAUTg1oEF7qRPhx44wcJtlXM5oPuXA8" + "value": "v0:fixture-key:1:TqpEGThlZqYZypD6eQEFZXoaz7En7wSNZ2Phvr0Kigg" } } ], - "fullUrl": "urn:uuid:070da471-11e4-5fe7-8cdd-17356844348c", + "fullUrl": "urn:uuid:1c9af85c-9462-5fbb-b19b-619207d49ddc", "resource": { "resourceType": "Device", "meta": { @@ -157,7 +157,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:XD4XTH_UnJ7EfAUTg1oEF7qRPhx44wcJtlXM5oPuXA8", + "value": "v0:fixture-key:1:TqpEGThlZqYZypD6eQEFZXoaz7En7wSNZ2Phvr0Kigg", "type": { "coding": [ { @@ -191,11 +191,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:ek76hvS0ALQ26mLLbodG0X0OsK2rCEqg5UCwDYex0WQ" + "value": "v0:fixture-key:1:ZZTtK-U3HfYobIKdA52R7jROS-LXygWsb6GS_HKq55I" } } ], - "fullUrl": "urn:uuid:f3ac0fad-b36c-5381-b298-aa566cc1dda5", + "fullUrl": "urn:uuid:4f0e4237-13f6-5a5d-92af-aa70cea16d8d", "resource": { "resourceType": "Device", "meta": { @@ -206,7 +206,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:ek76hvS0ALQ26mLLbodG0X0OsK2rCEqg5UCwDYex0WQ", + "value": "v0:fixture-key:1:ZZTtK-U3HfYobIKdA52R7jROS-LXygWsb6GS_HKq55I", "type": { "coding": [ { @@ -254,11 +254,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:A_WStZ8unmNnjz-GX3YoyC80P2XUWHcih_dybPzOwak" + "value": "n0:conversion-provenance:0:3WWEy8RBZ7hJLE3Mr8nXzyIpx6REeHiokAzs_WIkRP8" } } ], - "fullUrl": "urn:uuid:0ec4d89c-79f4-5e2b-8ebe-1cee16f8a08d", + "fullUrl": "urn:uuid:fe869ae8-658e-50c6-a4d5-a3ea19fed148", "resource": { "resourceType": "Provenance", "meta": { @@ -267,7 +267,7 @@ ] }, "target": [ - { "reference": "urn:uuid:538b4b82-cb94-53fb-9ab2-b5ce4d3415d1" } + { "reference": "urn:uuid:0286e181-761e-5a07-931f-2e415f5ef041" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -294,7 +294,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:f3ac0fad-b36c-5381-b298-aa566cc1dda5" + "reference": "urn:uuid:4f0e4237-13f6-5a5d-92af-aa70cea16d8d" } } ], @@ -304,7 +304,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:jczOkpWlxBPJaIzeniJl_W2BzHPfkzt3WgRMZ7Dgo-A", + "value": "v0:fixture-key:1:40Iy1wmaK8u1RiYFs3w-vdPd5r_vC9_FOJcZm9L47Ug", "type": { "coding": [ { @@ -328,7 +328,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:070da471-11e4-5fe7-8cdd-17356844348c" + "reference": "urn:uuid:1c9af85c-9462-5fbb-b19b-619207d49ddc" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json b/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json index 8bb2deb2..f0c20c80 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:49" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:48" }, "type": "collection", "timestamp": "2026-08-21T07:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:9LAbJZScNRkDfjciUcutA0SIA09N6yzddW6_40K7p04" + "value": "v0:fixture-key:1:ZW-l9DWDo6F8KQtTUw0c0jARFGJw7IVvv_uUABMaLL8" } } ], - "fullUrl": "urn:uuid:60573ccb-7eba-55f7-84f5-bd080cd7a096", + "fullUrl": "urn:uuid:455c32a1-29f0-51c6-8fbc-99d9b1ffbcd1", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:0roAYJCspncl8UFF8gvN2_DKuBKmJsbXIBZsHKtAXks", + "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:9LAbJZScNRkDfjciUcutA0SIA09N6yzddW6_40K7p04", + "value": "v0:fixture-key:1:ZW-l9DWDo6F8KQtTUw0c0jARFGJw7IVvv_uUABMaLL8", "type": { "coding": [ { @@ -132,11 +132,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:l6a6EFuUCQFFQSM6ea2u6YHgDGbN3H_GmOu8ulZc1Mg" + "value": "v0:fixture-key:1:jLcaQl4VlDZK-a6tYFi6RzphgjIdgSocnwNXiIKnNug" } } ], - "fullUrl": "urn:uuid:fa3b3a4e-e36e-5866-ad5e-e44008b2a012", + "fullUrl": "urn:uuid:5966d687-aa4f-5965-a4b2-cf84a9c6f248", "resource": { "resourceType": "Observation", "meta": { @@ -165,7 +165,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:0roAYJCspncl8UFF8gvN2_DKuBKmJsbXIBZsHKtAXks", + "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc", "type": { "coding": [ { @@ -177,7 +177,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:l6a6EFuUCQFFQSM6ea2u6YHgDGbN3H_GmOu8ulZc1Mg", + "value": "v0:fixture-key:1:jLcaQl4VlDZK-a6tYFi6RzphgjIdgSocnwNXiIKnNug", "type": { "coding": [ { @@ -230,11 +230,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:ECIMIs_0Lq43DhjyaeYN6y9JmqtdNm92Ak311sANg_g" + "value": "v0:fixture-key:1:E45pGFqeE8kQDlMSBXc05YscVQdR0EFm3jgZWqrGP4k" } } ], - "fullUrl": "urn:uuid:34b3ee3a-7727-5e4b-a8a1-0d69e23ea85f", + "fullUrl": "urn:uuid:88799d8b-82b2-5ba0-94b5-96c13acd9f8a", "resource": { "resourceType": "Observation", "meta": { @@ -263,7 +263,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:0roAYJCspncl8UFF8gvN2_DKuBKmJsbXIBZsHKtAXks", + "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc", "type": { "coding": [ { @@ -275,7 +275,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:ECIMIs_0Lq43DhjyaeYN6y9JmqtdNm92Ak311sANg_g", + "value": "v0:fixture-key:1:E45pGFqeE8kQDlMSBXc05YscVQdR0EFm3jgZWqrGP4k", "type": { "coding": [ { @@ -323,11 +323,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:PuQZ4Xis65pRj9JXayG7RPqZX1y0_-MgpBJwt6Y6nPY" + "value": "v0:fixture-key:1:P0jj07qi0PyO4vEU1lcP36UlMqKaO-mxMgv1ZKNKBYI" } } ], - "fullUrl": "urn:uuid:3977f4a9-344f-50a7-9bbb-5ece3b9fd015", + "fullUrl": "urn:uuid:d32c76f1-7a31-5342-b7c8-cd9f20064de3", "resource": { "resourceType": "Device", "meta": { @@ -338,7 +338,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:PuQZ4Xis65pRj9JXayG7RPqZX1y0_-MgpBJwt6Y6nPY", + "value": "v0:fixture-key:1:P0jj07qi0PyO4vEU1lcP36UlMqKaO-mxMgv1ZKNKBYI", "type": { "coding": [ { @@ -372,11 +372,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:BiLe29RLAJjeGcFc8Ya3zb-yEsOAsoKE_W8c4MaVQAE" + "value": "v0:fixture-key:1:ERW6WdlhAGDHJ1V_q9pZsma1U6S_NzLxrFsIf4CE-SQ" } } ], - "fullUrl": "urn:uuid:05ae6de1-cddd-5b61-82e7-c12c0c35c749", + "fullUrl": "urn:uuid:70d10730-ee46-5c11-b27b-274a19a1c9b8", "resource": { "resourceType": "Device", "meta": { @@ -387,7 +387,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:BiLe29RLAJjeGcFc8Ya3zb-yEsOAsoKE_W8c4MaVQAE", + "value": "v0:fixture-key:1:ERW6WdlhAGDHJ1V_q9pZsma1U6S_NzLxrFsIf4CE-SQ", "type": { "coding": [ { @@ -435,11 +435,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:072e7erAPHzJqp18Iecwyt80VRYaOxcVfgeBpxHZBV4" + "value": "n0:conversion-provenance:0:BsM8PuZ48RNmemYKpVpQEZCPDNRPRIzR5GqIBLy8p6g" } } ], - "fullUrl": "urn:uuid:a8fb3286-c6f3-54f9-ac8f-b749fb87a45c", + "fullUrl": "urn:uuid:b4b754f4-c374-5391-8a3e-edf1ab5e7dd5", "resource": { "resourceType": "Provenance", "meta": { @@ -448,9 +448,9 @@ ] }, "target": [ - { "reference": "urn:uuid:60573ccb-7eba-55f7-84f5-bd080cd7a096" }, - { "reference": "urn:uuid:fa3b3a4e-e36e-5866-ad5e-e44008b2a012" }, - { "reference": "urn:uuid:34b3ee3a-7727-5e4b-a8a1-0d69e23ea85f" } + { "reference": "urn:uuid:455c32a1-29f0-51c6-8fbc-99d9b1ffbcd1" }, + { "reference": "urn:uuid:5966d687-aa4f-5965-a4b2-cf84a9c6f248" }, + { "reference": "urn:uuid:88799d8b-82b2-5ba0-94b5-96c13acd9f8a" } ], "occurredDateTime": "2026-08-21T07:00:00Z", "recorded": "2026-08-21T07:02:00Z", @@ -477,7 +477,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:05ae6de1-cddd-5b61-82e7-c12c0c35c749" + "reference": "urn:uuid:70d10730-ee46-5c11-b27b-274a19a1c9b8" } } ], @@ -487,7 +487,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:0roAYJCspncl8UFF8gvN2_DKuBKmJsbXIBZsHKtAXks", + "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc", "type": { "coding": [ { @@ -511,7 +511,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:3977f4a9-344f-50a7-9bbb-5ece3b9fd015" + "reference": "urn:uuid:d32c76f1-7a31-5342-b7c8-cd9f20064de3" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json index de065ef8..4f46ed3b 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:32" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:32" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:w22OQpSd6BHV11gV3gGmjfypjG9h0hMzXj2HE0HMcK8" + "value": "v0:fixture-key:1:VkvVjJoPr21ib2OR1ukyFv4PEwmfnfIADgZkrdI0OAY" } } ], - "fullUrl": "urn:uuid:98b9c214-2168-51ee-ba2a-c7b5c818d61e", + "fullUrl": "urn:uuid:8f4caaf7-2b81-5387-aecb-51048bd560df", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:bMJIOFm8WX8UdsKhlujt9hPADkeh8DNMV-baupj_kKc", + "value": "v0:fixture-key:1:6JYnSRBq-28f00cb2qTI45a4PwviE2ZkDZ0CgfxOBu8", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:w22OQpSd6BHV11gV3gGmjfypjG9h0hMzXj2HE0HMcK8", + "value": "v0:fixture-key:1:VkvVjJoPr21ib2OR1ukyFv4PEwmfnfIADgZkrdI0OAY", "type": { "coding": [ { @@ -132,11 +132,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:bA77wMxXOHtLwzFovIWuUCHgNM4OYv47m2GhPR2M9mk" + "value": "v0:fixture-key:1:h5Rb3MFdNKfsjqqCI-RcuHTVXXud55bMyIae_ByOKKk" } } ], - "fullUrl": "urn:uuid:7978f9e9-eae2-547f-8313-10c3751a57c1", + "fullUrl": "urn:uuid:bfa210b1-746d-5e96-82a7-0ca4ab299873", "resource": { "resourceType": "Device", "meta": { @@ -147,7 +147,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:bA77wMxXOHtLwzFovIWuUCHgNM4OYv47m2GhPR2M9mk", + "value": "v0:fixture-key:1:h5Rb3MFdNKfsjqqCI-RcuHTVXXud55bMyIae_ByOKKk", "type": { "coding": [ { @@ -181,11 +181,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:HwWqIZL5n6dXYm-Cyg4WnPdjiB8LKJDQMaiiFFowQSU" + "value": "v0:fixture-key:1:OHeAUXdlghyg_Gho66VlpfMOcbKOwauPDAaCiOlaaDU" } } ], - "fullUrl": "urn:uuid:89b496ef-4b88-5d28-97b5-ab36d8b3136b", + "fullUrl": "urn:uuid:b7c37e2b-ba62-562d-94e4-7c034814ef4e", "resource": { "resourceType": "Device", "meta": { @@ -196,7 +196,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:HwWqIZL5n6dXYm-Cyg4WnPdjiB8LKJDQMaiiFFowQSU", + "value": "v0:fixture-key:1:OHeAUXdlghyg_Gho66VlpfMOcbKOwauPDAaCiOlaaDU", "type": { "coding": [ { @@ -244,11 +244,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:y1SHC2zDcK4svQBUYRcAFOtqE5Ls-qnowOMrIpPlhv4" + "value": "n0:conversion-provenance:0:c_XcuO0rRflq1E3Kny_pHsLUXqP-JCMIfXlxfwArKoo" } } ], - "fullUrl": "urn:uuid:e37256bc-4b0a-5b06-9c2a-5bd9828e0652", + "fullUrl": "urn:uuid:eb84eba4-4718-5bb5-b808-07e09b09e7fa", "resource": { "resourceType": "Provenance", "meta": { @@ -257,7 +257,7 @@ ] }, "target": [ - { "reference": "urn:uuid:98b9c214-2168-51ee-ba2a-c7b5c818d61e" } + { "reference": "urn:uuid:8f4caaf7-2b81-5387-aecb-51048bd560df" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -284,7 +284,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:89b496ef-4b88-5d28-97b5-ab36d8b3136b" + "reference": "urn:uuid:b7c37e2b-ba62-562d-94e4-7c034814ef4e" } } ], @@ -294,7 +294,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:bMJIOFm8WX8UdsKhlujt9hPADkeh8DNMV-baupj_kKc", + "value": "v0:fixture-key:1:6JYnSRBq-28f00cb2qTI45a4PwviE2ZkDZ0CgfxOBu8", "type": { "coding": [ { @@ -318,7 +318,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:7978f9e9-eae2-547f-8313-10c3751a57c1" + "reference": "urn:uuid:bfa210b1-746d-5e96-82a7-0ca4ab299873" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json index 7ec2443e..a5682cc9 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:33" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:33" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:DhscoNlcMhvVGag5mNyUR5jZm81lFbCq5n4MYQw5oKY" + "value": "v0:fixture-key:1:8_DgUefPBkmNsGzXuKk6xRHRGSabA-vhJw_Ts1OaULo" } } ], - "fullUrl": "urn:uuid:a1ffb3e5-3084-50fc-bb53-0432be9bf875", + "fullUrl": "urn:uuid:13fc40fe-24a3-59f6-8182-8674de79f99f", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:3350h9qJT8w5YTMTSx7uhmcLA_4y5BtXnZFqtC6dufY", + "value": "v0:fixture-key:1:fd_6B30JQm1RurdhENC10i8LahBJt8rk4pjXN5m97HA", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:DhscoNlcMhvVGag5mNyUR5jZm81lFbCq5n4MYQw5oKY", + "value": "v0:fixture-key:1:8_DgUefPBkmNsGzXuKk6xRHRGSabA-vhJw_Ts1OaULo", "type": { "coding": [ { @@ -132,11 +132,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:PE3zgguOXvysbgnbG3pvgjmQmiab7WnkZYVAdiMhzGo" + "value": "v0:fixture-key:1:kNqCR8LM6Ge_iHvz9Zpqo0miCqPAoZpsUY6XQ5cToDw" } } ], - "fullUrl": "urn:uuid:1918f022-0cb1-5f71-b3c2-8b8409bffda4", + "fullUrl": "urn:uuid:d4919db0-78eb-549d-b6e6-3b49d2ec7e3b", "resource": { "resourceType": "Device", "meta": { @@ -147,7 +147,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:PE3zgguOXvysbgnbG3pvgjmQmiab7WnkZYVAdiMhzGo", + "value": "v0:fixture-key:1:kNqCR8LM6Ge_iHvz9Zpqo0miCqPAoZpsUY6XQ5cToDw", "type": { "coding": [ { @@ -181,11 +181,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:MFvvceR7vuUbC4RDz6rnnbMhpYMwvyFCVyoxlSHblLs" + "value": "v0:fixture-key:1:hgLwQrXtczhHU7Bcj5uX7S2DF4vJWnYiwrOzsDboju8" } } ], - "fullUrl": "urn:uuid:b1cda0b7-4158-538a-866f-4ec26427da1c", + "fullUrl": "urn:uuid:a0aaca97-70da-5ccb-9510-dcdb4d5f7088", "resource": { "resourceType": "Device", "meta": { @@ -196,7 +196,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:MFvvceR7vuUbC4RDz6rnnbMhpYMwvyFCVyoxlSHblLs", + "value": "v0:fixture-key:1:hgLwQrXtczhHU7Bcj5uX7S2DF4vJWnYiwrOzsDboju8", "type": { "coding": [ { @@ -244,11 +244,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:hItGY9DfpHn8mUw3L6ZmtEBO8RCzDHmJH2KNL6OmKrU" + "value": "n0:conversion-provenance:0:UwgSxMJ26fI73RjvXWouraGX9p0_Il45jxNbZK_aHaM" } } ], - "fullUrl": "urn:uuid:4bbeccf8-5a03-5054-a2dd-bb2094999b3a", + "fullUrl": "urn:uuid:669a50f2-373b-5d47-a5af-fc52ba9c11c9", "resource": { "resourceType": "Provenance", "meta": { @@ -257,7 +257,7 @@ ] }, "target": [ - { "reference": "urn:uuid:a1ffb3e5-3084-50fc-bb53-0432be9bf875" } + { "reference": "urn:uuid:13fc40fe-24a3-59f6-8182-8674de79f99f" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -284,7 +284,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:b1cda0b7-4158-538a-866f-4ec26427da1c" + "reference": "urn:uuid:a0aaca97-70da-5ccb-9510-dcdb4d5f7088" } } ], @@ -294,7 +294,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:3350h9qJT8w5YTMTSx7uhmcLA_4y5BtXnZFqtC6dufY", + "value": "v0:fixture-key:1:fd_6B30JQm1RurdhENC10i8LahBJt8rk4pjXN5m97HA", "type": { "coding": [ { @@ -318,7 +318,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:1918f022-0cb1-5f71-b3c2-8b8409bffda4" + "reference": "urn:uuid:d4919db0-78eb-549d-b6e6-3b49d2ec7e3b" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json b/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json index e727f2cc..2ac4a5f6 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json +++ b/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:54" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:53" }, "type": "collection", "timestamp": "2026-08-22T12:02:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:p7ISqEJUX3HlUFGXpiEAAVaVoTJJxy2Lv3b45PaxdSI" + "value": "v0:fixture-key:1:oEl7Uhouma2Mj5_8vjJxzY3wJXaIO8DnSJzEeOhy2o0" } } ], - "fullUrl": "urn:uuid:b9531c09-6f04-5de0-8675-11c561b531f2", + "fullUrl": "urn:uuid:8ad483fc-efd8-53ad-99e6-7dfbf668b1c1", "resource": { "resourceType": "Device", "meta": { @@ -49,7 +49,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:p7ISqEJUX3HlUFGXpiEAAVaVoTJJxy2Lv3b45PaxdSI", + "value": "v0:fixture-key:1:oEl7Uhouma2Mj5_8vjJxzY3wJXaIO8DnSJzEeOhy2o0", "type": { "coding": [ { @@ -97,11 +97,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:retraction-provenance:0:9L7-Yd2G07JqjIf8iCbf4uJUAYkiVssyT3BjYu9tI-U" + "value": "n0:retraction-provenance:0:gv9DL_lwJx61nEPjo2CzyXAPyDpheZfgahVh9IKQ5kk" } } ], - "fullUrl": "urn:uuid:971dab9f-d023-5cf5-b51f-d061f6b127a3", + "fullUrl": "urn:uuid:ada84fca-188c-5dd5-90ba-27584527ef2f", "resource": { "resourceType": "Provenance", "meta": { @@ -120,7 +120,7 @@ "type": "Device", "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:e2j1gdQw2SwNKr5tOCv1YnM8xF-NxUolN2oVdQ9vrMI", + "value": "v0:fixture-key:1:U1GXWdV08ZaO2_cubyu1oaGFDXhnzLx8h_JwZVf6hUU", "type": { "coding": [ { @@ -141,7 +141,7 @@ "type": "Observation", "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:hUNfkRjsHis9rWj4K6qNLC-jFLQKKWL2Uh6UYxYBy9I", + "value": "v0:fixture-key:1:LycACgbJZx-uRos8zb-0w0PEXMdM7LoW4Sx1IvbEOC0", "type": { "coding": [ { @@ -178,7 +178,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:b9531c09-6f04-5de0-8675-11c561b531f2" + "reference": "urn:uuid:8ad483fc-efd8-53ad-99e6-7dfbf668b1c1" } } ], @@ -188,7 +188,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:Vu0a2YcqoF0_Kmt53rvO840SVdAxu8nPfHv7sZnTABw", + "value": "v0:fixture-key:1:cBsbfJ2FTYUguk5OcwFuDMb03zuen7zrG5xlFAniFTo", "type": { "coding": [ { diff --git a/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json b/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json index f1163b70..91174ba9 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json +++ b/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:55" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:54" }, "type": "collection", "timestamp": "2026-08-22T13:02:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:33a-BZk3SZFIl0sZdByOUvq2dAR-nqSkxKy12Xfe6_4" + "value": "v0:fixture-key:1:nEuRqgji46KZl6Zd4aPUEjeIbRhR7mJ96dA0q9RmA4U" } } ], - "fullUrl": "urn:uuid:64fa1ccd-2ebb-5b5b-b195-ab88ed10b39d", + "fullUrl": "urn:uuid:f3659235-64ff-5681-9783-5e712a989f97", "resource": { "resourceType": "Device", "meta": { @@ -49,7 +49,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:33a-BZk3SZFIl0sZdByOUvq2dAR-nqSkxKy12Xfe6_4", + "value": "v0:fixture-key:1:nEuRqgji46KZl6Zd4aPUEjeIbRhR7mJ96dA0q9RmA4U", "type": { "coding": [ { @@ -97,11 +97,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:retraction-provenance:0:n96he5rGWnKTKdpNDUxomZ1sZBTWwtRTuCahy_gEVcI" + "value": "n0:retraction-provenance:0:uOqh_hD3XZxlouavDYtKr6PhYACOazZDgCcBLqQJjcI" } } ], - "fullUrl": "urn:uuid:dc3860eb-4aad-5cef-b6aa-abf719ff7f84", + "fullUrl": "urn:uuid:d7f49bdc-acce-543a-bf93-154117fe1854", "resource": { "resourceType": "Provenance", "meta": { @@ -120,7 +120,7 @@ "type": "Device", "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:WclJMCQ6CUAslia5K2NFCa4BEWetQW0V2edf9a-EN_w", + "value": "v0:fixture-key:1:uocYwddFVtlKSSu89sdrCnhx7B1Zj4lX7iNLzaZjvAA", "type": { "coding": [ { @@ -141,7 +141,7 @@ "type": "DocumentReference", "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:dQm59jLLiwD0Zs3uVioH3S-V4hOzA3izmLiS1S94se4", + "value": "v0:fixture-key:1:w7o_lcZdhv2y5mj96CWnefbS73I3tPoRvuSQG7Vaqao", "type": { "coding": [ { @@ -178,7 +178,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:64fa1ccd-2ebb-5b5b-b195-ab88ed10b39d" + "reference": "urn:uuid:f3659235-64ff-5681-9783-5e712a989f97" } } ], @@ -188,7 +188,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:RDmUrHr5N6bLAoEeTx0TRfsl2wf739glATitaeC3zlM", + "value": "v0:fixture-key:1:Qm28JqWMxMvl2dsco1gdTLvUyKdg3S8qfrEX_4Z19KY", "type": { "coding": [ { diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json index 0c439a33..12b0aa04 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:36" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:35" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:v-8pEBoDCpdxHB1EduRyPoyGXEc3pHIOPR2zGkc9oRM" + "value": "v0:fixture-key:1:g9D8uYiqpZq0BrDkaP2tElDxS1h4zOwQ3OHIBNKXobg" } } ], - "fullUrl": "urn:uuid:1ffd2014-0a78-5bff-b0f4-33b8c8fd6ef1", + "fullUrl": "urn:uuid:3f31daca-6852-511c-8961-38d2cfba8936", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:F3IWWxP6dTqYRs3P7KAs2vDdLPXma4GZxbyOuzxdgxE", + "value": "v0:fixture-key:1:6VQPvHniXH4sNDqhmZ0DqtG129Xl6pOZ353Kdqg0SwA", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:v-8pEBoDCpdxHB1EduRyPoyGXEc3pHIOPR2zGkc9oRM", + "value": "v0:fixture-key:1:g9D8uYiqpZq0BrDkaP2tElDxS1h4zOwQ3OHIBNKXobg", "type": { "coding": [ { @@ -124,11 +124,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:gpUAhUBpb1SAO0tUA43n9NAneCosysH1I1S1QmW4Q_M" + "value": "v0:fixture-key:1:cgk--UOImI_WU47l-R2BGI_H4AqhrIEgrHXPC7dlUYA" } } ], - "fullUrl": "urn:uuid:0f67d388-b785-528d-bc7c-613df1a0c060", + "fullUrl": "urn:uuid:0d8fb81e-efa0-583e-be2f-7d3fd70cf98b", "resource": { "resourceType": "Device", "meta": { @@ -139,7 +139,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:gpUAhUBpb1SAO0tUA43n9NAneCosysH1I1S1QmW4Q_M", + "value": "v0:fixture-key:1:cgk--UOImI_WU47l-R2BGI_H4AqhrIEgrHXPC7dlUYA", "type": { "coding": [ { @@ -173,11 +173,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:nJQ0h6JsVl1eewTp7U2SNJ7_orKFQSR_lY2vw9ePkPQ" + "value": "v0:fixture-key:1:0qUilPLzuJbQGUboPRBuLJQsLcWYkqCI7t59qy7DoGA" } } ], - "fullUrl": "urn:uuid:0ca89bd1-0baf-537a-b308-ffb39af276f2", + "fullUrl": "urn:uuid:16b24c42-c731-5ba7-8859-8c288834f0d4", "resource": { "resourceType": "Device", "meta": { @@ -188,7 +188,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:nJQ0h6JsVl1eewTp7U2SNJ7_orKFQSR_lY2vw9ePkPQ", + "value": "v0:fixture-key:1:0qUilPLzuJbQGUboPRBuLJQsLcWYkqCI7t59qy7DoGA", "type": { "coding": [ { @@ -236,11 +236,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:UqkZ9h9kZT40Q-aG_nMJqKWoClDOYfrg6e0d3F9q3xo" + "value": "n0:conversion-provenance:0:SO0lcltW7S-39s0UkVo_0Fu7iP7CJPFYIHdvHhS72do" } } ], - "fullUrl": "urn:uuid:d781494c-ffbe-5f2b-bf3f-2d577dd03bd1", + "fullUrl": "urn:uuid:fa17f5ee-019b-5f79-902f-1f8862771964", "resource": { "resourceType": "Provenance", "meta": { @@ -249,7 +249,7 @@ ] }, "target": [ - { "reference": "urn:uuid:1ffd2014-0a78-5bff-b0f4-33b8c8fd6ef1" } + { "reference": "urn:uuid:3f31daca-6852-511c-8961-38d2cfba8936" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -276,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:0ca89bd1-0baf-537a-b308-ffb39af276f2" + "reference": "urn:uuid:16b24c42-c731-5ba7-8859-8c288834f0d4" } } ], @@ -286,7 +286,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:F3IWWxP6dTqYRs3P7KAs2vDdLPXma4GZxbyOuzxdgxE", + "value": "v0:fixture-key:1:6VQPvHniXH4sNDqhmZ0DqtG129Xl6pOZ353Kdqg0SwA", "type": { "coding": [ { @@ -310,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:0f67d388-b785-528d-bc7c-613df1a0c060" + "reference": "urn:uuid:0d8fb81e-efa0-583e-be2f-7d3fd70cf98b" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json b/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json index bc114024..416fec15 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:43" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:42" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:Y_f5paLes1rrzJNq8zx_GrReUzpj2KYUPTrpXkL_bJE" + "value": "v0:fixture-key:1:up2yrUsCU_7Cld1H_MYyI3dIGA9tbFvZ-lMm4M4tXVM" } } ], - "fullUrl": "urn:uuid:fb5ea01a-3278-568f-8e56-b0f6aa8de5d6", + "fullUrl": "urn:uuid:f04531e4-5996-5afd-858d-ad251ceb62b5", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:2KGZsgh2IbY6WKZvPoL3p0IxBcRnbFe0PuhBvkxkjnw", + "value": "v0:fixture-key:1:m1wV9ADh9_iaYO3rZmHmqYvvPh96-Fd9keJWZlCvf8Y", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:Y_f5paLes1rrzJNq8zx_GrReUzpj2KYUPTrpXkL_bJE", + "value": "v0:fixture-key:1:up2yrUsCU_7Cld1H_MYyI3dIGA9tbFvZ-lMm4M4tXVM", "type": { "coding": [ { @@ -124,11 +124,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:Sdq2HZgzFAkzJWOp27GRuRf7f1_0rWpxT2W6lzRrt5g" + "value": "v0:fixture-key:1:t-vS893OvYOMIWpKbtoHlnxyBaAItckjwQUpOY_BSGM" } } ], - "fullUrl": "urn:uuid:71f8e6b9-b929-5789-90a0-b8a2c35fc39f", + "fullUrl": "urn:uuid:0ffe260f-79f2-5f9d-a443-ad1a32aba472", "resource": { "resourceType": "Device", "meta": { @@ -139,7 +139,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:Sdq2HZgzFAkzJWOp27GRuRf7f1_0rWpxT2W6lzRrt5g", + "value": "v0:fixture-key:1:t-vS893OvYOMIWpKbtoHlnxyBaAItckjwQUpOY_BSGM", "type": { "coding": [ { @@ -173,11 +173,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:GVSBZVw6mAYUk10Aby73QqtkVAuHIsdAWndd2Gi8Y-M" + "value": "v0:fixture-key:1:GjU_pI0bDFWbiQ0MOJBOCBXMM0COVm2SSbC4g8zawJ8" } } ], - "fullUrl": "urn:uuid:6cdc1d6f-381f-50b2-b020-411e2984f81d", + "fullUrl": "urn:uuid:f31053bd-69da-509c-a8d7-d787b331d2a4", "resource": { "resourceType": "Device", "meta": { @@ -188,7 +188,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:GVSBZVw6mAYUk10Aby73QqtkVAuHIsdAWndd2Gi8Y-M", + "value": "v0:fixture-key:1:GjU_pI0bDFWbiQ0MOJBOCBXMM0COVm2SSbC4g8zawJ8", "type": { "coding": [ { @@ -236,11 +236,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:wqnT_bJnHqJ3cIRz8F47Y6RFgvxjkmUC7Rde7rXxlQ4" + "value": "n0:conversion-provenance:0:PxaVHTEjWlHBji7_0S-rbJCMbGjxFbGCpfaYwTYKs_E" } } ], - "fullUrl": "urn:uuid:7125ec33-ab85-5760-9c15-7ad81d197b2f", + "fullUrl": "urn:uuid:a459d197-d6f7-58fc-a973-98d610090925", "resource": { "resourceType": "Provenance", "meta": { @@ -249,7 +249,7 @@ ] }, "target": [ - { "reference": "urn:uuid:fb5ea01a-3278-568f-8e56-b0f6aa8de5d6" } + { "reference": "urn:uuid:f04531e4-5996-5afd-858d-ad251ceb62b5" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -276,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:6cdc1d6f-381f-50b2-b020-411e2984f81d" + "reference": "urn:uuid:f31053bd-69da-509c-a8d7-d787b331d2a4" } } ], @@ -286,7 +286,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:2KGZsgh2IbY6WKZvPoL3p0IxBcRnbFe0PuhBvkxkjnw", + "value": "v0:fixture-key:1:m1wV9ADh9_iaYO3rZmHmqYvvPh96-Fd9keJWZlCvf8Y", "type": { "coding": [ { @@ -310,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:71f8e6b9-b929-5789-90a0-b8a2c35fc39f" + "reference": "urn:uuid:0ffe260f-79f2-5f9d-a443-ad1a32aba472" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json index a0748faa..a9ca410d 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:46" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:45" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:aBHmnn17uPQJ1FOYWLjRJMYAsi-UoPoWectNN5p4YeI" + "value": "v0:fixture-key:1:ldxbCxa9ISJLN7kax9FiE80vGiw0WHIUyXPQZd3Sk4E" } } ], - "fullUrl": "urn:uuid:cb17c0b1-7dc2-51b8-a405-8d638c9e1137", + "fullUrl": "urn:uuid:6c02980d-5df9-5a37-a030-a8599d758291", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:Vf23EVGsuE6LzFmG94yu4Sbl68oSJPaC7ns20tgOTbE", + "value": "v0:fixture-key:1:1HgX55BzSSTYkJN89C8SXc1c7-w6cI0mbCGaekNC1yw", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:aBHmnn17uPQJ1FOYWLjRJMYAsi-UoPoWectNN5p4YeI", + "value": "v0:fixture-key:1:ldxbCxa9ISJLN7kax9FiE80vGiw0WHIUyXPQZd3Sk4E", "type": { "coding": [ { @@ -129,11 +129,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:wB83fOKGefr0oW9mSLAS-E4hfeiKpocLDy2z3UyF-Pg" + "value": "v0:fixture-key:1:Sk7a5vUo5Ke0h-wCaq8pkWlwBEg_lpUqKi0tbxwRpAg" } } ], - "fullUrl": "urn:uuid:998938b2-c583-5020-842d-cd2ebcc2898a", + "fullUrl": "urn:uuid:f86775dd-e51c-58bc-8b40-54c3a4c2704e", "resource": { "resourceType": "Device", "meta": { @@ -144,7 +144,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:wB83fOKGefr0oW9mSLAS-E4hfeiKpocLDy2z3UyF-Pg", + "value": "v0:fixture-key:1:Sk7a5vUo5Ke0h-wCaq8pkWlwBEg_lpUqKi0tbxwRpAg", "type": { "coding": [ { @@ -178,11 +178,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:xnn3tYFITW6OjbABhqa6GMrWWG34Vo0qTouz2wRhfcg" + "value": "v0:fixture-key:1:XfFTZ3nhXBEndRIdGRL2pS4gmMMzLXKGUxFQz81nb8w" } } ], - "fullUrl": "urn:uuid:80f93679-3b19-5963-9df6-d83960ee26df", + "fullUrl": "urn:uuid:1fe1f299-5178-556e-b4d9-6eec0d8d3ac9", "resource": { "resourceType": "Device", "meta": { @@ -193,7 +193,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:xnn3tYFITW6OjbABhqa6GMrWWG34Vo0qTouz2wRhfcg", + "value": "v0:fixture-key:1:XfFTZ3nhXBEndRIdGRL2pS4gmMMzLXKGUxFQz81nb8w", "type": { "coding": [ { @@ -241,11 +241,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:R5jJi9xKjjoAPm0B6cXb9MRGAPCmuWq6oTu0dnclFTY" + "value": "n0:conversion-provenance:0:oG1YrkySMKuoFgaHhPsjFpaaadNzDN512YXklTAqRAs" } } ], - "fullUrl": "urn:uuid:12e55e29-54eb-52c2-af3d-83345c5a4742", + "fullUrl": "urn:uuid:c9daa5d4-de63-5373-9f0c-1dca919aef51", "resource": { "resourceType": "Provenance", "meta": { @@ -254,7 +254,7 @@ ] }, "target": [ - { "reference": "urn:uuid:cb17c0b1-7dc2-51b8-a405-8d638c9e1137" } + { "reference": "urn:uuid:6c02980d-5df9-5a37-a030-a8599d758291" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -281,7 +281,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:80f93679-3b19-5963-9df6-d83960ee26df" + "reference": "urn:uuid:1fe1f299-5178-556e-b4d9-6eec0d8d3ac9" } } ], @@ -291,7 +291,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:Vf23EVGsuE6LzFmG94yu4Sbl68oSJPaC7ns20tgOTbE", + "value": "v0:fixture-key:1:1HgX55BzSSTYkJN89C8SXc1c7-w6cI0mbCGaekNC1yw", "type": { "coding": [ { @@ -315,7 +315,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:998938b2-c583-5020-842d-cd2ebcc2898a" + "reference": "urn:uuid:f86775dd-e51c-58bc-8b40-54c3a4c2704e" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json index f090f52b..a5189179 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:47" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:46" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:qnp8sw7U_5k1jcdZ3Q5_WhNSk66zG6ResQPZGuIZNZM" + "value": "v0:fixture-key:1:AH-H6ncy4X1z2tEipFNSrXuHFuFP3-ud6YhWXKipRto" } } ], - "fullUrl": "urn:uuid:3999714a-bd10-5630-bff0-358ebb9066f0", + "fullUrl": "urn:uuid:1ba6631d-8d9c-500d-b5e7-77d93c149d3a", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:67j8aU_KeUiEsRGYcYxStm8w4S96HpxbH1T82wX4YA0", + "value": "v0:fixture-key:1:QPmTxHXDUmbQmn5vhLVtyS_SyfCzJCTm93c03OxH-2o", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:qnp8sw7U_5k1jcdZ3Q5_WhNSk66zG6ResQPZGuIZNZM", + "value": "v0:fixture-key:1:AH-H6ncy4X1z2tEipFNSrXuHFuFP3-ud6YhWXKipRto", "type": { "coding": [ { @@ -129,11 +129,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:nobmhD2bLkWt4vIo-3U2581ybvFqZNMALB-nryW7XW4" + "value": "v0:fixture-key:1:B2gFdvvpWQxTB70sqX13q51Ty66_l_GDk-P3YrdDiSs" } } ], - "fullUrl": "urn:uuid:b0c56bad-8848-5bc3-ad2e-0c8dbe17a38f", + "fullUrl": "urn:uuid:40a17b86-d18f-54cb-977c-d75351a7919d", "resource": { "resourceType": "Device", "meta": { @@ -144,7 +144,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:nobmhD2bLkWt4vIo-3U2581ybvFqZNMALB-nryW7XW4", + "value": "v0:fixture-key:1:B2gFdvvpWQxTB70sqX13q51Ty66_l_GDk-P3YrdDiSs", "type": { "coding": [ { @@ -178,11 +178,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:ialp3WiZS9pCIWlLwKySmhNIo2pN6D_IgaI_el6nYGU" + "value": "v0:fixture-key:1:gCGVDG_iVGF7B6ZmWIN4DO4GRXcpOG-nyuXGpSqWL7g" } } ], - "fullUrl": "urn:uuid:1638f3f5-1bd8-5781-a868-c9f0c1ee08d7", + "fullUrl": "urn:uuid:99db234c-97c6-5d64-91cf-41f4375d4bbc", "resource": { "resourceType": "Device", "meta": { @@ -193,7 +193,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:ialp3WiZS9pCIWlLwKySmhNIo2pN6D_IgaI_el6nYGU", + "value": "v0:fixture-key:1:gCGVDG_iVGF7B6ZmWIN4DO4GRXcpOG-nyuXGpSqWL7g", "type": { "coding": [ { @@ -241,11 +241,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:qW4VHoMZF4T4_JhTUzAsWRxiO4JHnvi8KVp2N8aNRxM" + "value": "n0:conversion-provenance:0:L5XECi811lvx0O_j7row191VRz0FfxBuzkQCjFHewos" } } ], - "fullUrl": "urn:uuid:99011c44-9a29-53f6-8b76-d08299fff7fc", + "fullUrl": "urn:uuid:6646ef38-4230-5ed4-aa3b-b6b45e77d61f", "resource": { "resourceType": "Provenance", "meta": { @@ -254,7 +254,7 @@ ] }, "target": [ - { "reference": "urn:uuid:3999714a-bd10-5630-bff0-358ebb9066f0" } + { "reference": "urn:uuid:1ba6631d-8d9c-500d-b5e7-77d93c149d3a" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -281,7 +281,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:1638f3f5-1bd8-5781-a868-c9f0c1ee08d7" + "reference": "urn:uuid:99db234c-97c6-5d64-91cf-41f4375d4bbc" } } ], @@ -291,7 +291,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:67j8aU_KeUiEsRGYcYxStm8w4S96HpxbH1T82wX4YA0", + "value": "v0:fixture-key:1:QPmTxHXDUmbQmn5vhLVtyS_SyfCzJCTm93c03OxH-2o", "type": { "coding": [ { @@ -315,7 +315,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:b0c56bad-8848-5bc3-ad2e-0c8dbe17a38f" + "reference": "urn:uuid:40a17b86-d18f-54cb-977c-d75351a7919d" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json index 573fc78c..edf66d09 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:37" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:36" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:-plJxc895a7YvoL-kDFuPK_cEK-XXsMl7gcOgxhXDXQ" + "value": "v0:fixture-key:1:dmuPKjctx8XrjSJddZ2tPQXXnDkWrfZLZLUkkvUTLKg" } } ], - "fullUrl": "urn:uuid:1a18d23d-d6c7-55ef-bc0f-3c510d32db32", + "fullUrl": "urn:uuid:30badcd5-097e-5e48-92d7-d24d96c1e1dd", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:vrz0pgc6vEQRz5vCFQ0zFUeVkbDKISXZ5jXSw2Sf2lI", + "value": "v0:fixture-key:1:Eh9FyyObehDndCz4NWKS92aPKvIf3zJvsPAdTGODtm0", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:-plJxc895a7YvoL-kDFuPK_cEK-XXsMl7gcOgxhXDXQ", + "value": "v0:fixture-key:1:dmuPKjctx8XrjSJddZ2tPQXXnDkWrfZLZLUkkvUTLKg", "type": { "coding": [ { @@ -124,11 +124,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:eg3cIAyec7MkCkm5Us4Na0GcK3jrnVQ4pMGAARybdrI" + "value": "v0:fixture-key:1:OVHkuQ0ProM33_M1f9lTxHrbw8xr3WsTWa9lwqluClA" } } ], - "fullUrl": "urn:uuid:0f8a9455-1b5e-525b-b08c-06a6125ac41a", + "fullUrl": "urn:uuid:f4e6c723-efec-5fe6-9159-ce76a2ae15dd", "resource": { "resourceType": "Device", "meta": { @@ -139,7 +139,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:eg3cIAyec7MkCkm5Us4Na0GcK3jrnVQ4pMGAARybdrI", + "value": "v0:fixture-key:1:OVHkuQ0ProM33_M1f9lTxHrbw8xr3WsTWa9lwqluClA", "type": { "coding": [ { @@ -173,11 +173,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:JMPo1bm4W-tUPoCj0950nF1Ctxy1mY-tk-xBhx7v7Mg" + "value": "v0:fixture-key:1:hsOMQ8T6sLQBqj0qrt0InL91Oy3nF8DyD-AmaOzX97Y" } } ], - "fullUrl": "urn:uuid:98f4eb28-c7b2-56a6-bcc3-72811b9be3c1", + "fullUrl": "urn:uuid:83c978cc-422e-5180-b430-2fdce3eb6604", "resource": { "resourceType": "Device", "meta": { @@ -188,7 +188,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:JMPo1bm4W-tUPoCj0950nF1Ctxy1mY-tk-xBhx7v7Mg", + "value": "v0:fixture-key:1:hsOMQ8T6sLQBqj0qrt0InL91Oy3nF8DyD-AmaOzX97Y", "type": { "coding": [ { @@ -236,11 +236,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:ZI1k1uS2uXaZEf535CiSb8EAqdYMmpsTqNoe6sg8IUA" + "value": "n0:conversion-provenance:0:oDUf94G4-TotGjhBmFnu7C5iCGVf05D_KytEY3OOxwg" } } ], - "fullUrl": "urn:uuid:5add0b87-845c-5acf-99e1-d473ac399eff", + "fullUrl": "urn:uuid:145c28e1-8e1c-558e-b7ba-ee0fe6f9534a", "resource": { "resourceType": "Provenance", "meta": { @@ -249,7 +249,7 @@ ] }, "target": [ - { "reference": "urn:uuid:1a18d23d-d6c7-55ef-bc0f-3c510d32db32" } + { "reference": "urn:uuid:30badcd5-097e-5e48-92d7-d24d96c1e1dd" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -276,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:98f4eb28-c7b2-56a6-bcc3-72811b9be3c1" + "reference": "urn:uuid:83c978cc-422e-5180-b430-2fdce3eb6604" } } ], @@ -286,7 +286,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:vrz0pgc6vEQRz5vCFQ0zFUeVkbDKISXZ5jXSw2Sf2lI", + "value": "v0:fixture-key:1:Eh9FyyObehDndCz4NWKS92aPKvIf3zJvsPAdTGODtm0", "type": { "coding": [ { @@ -310,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:0f8a9455-1b5e-525b-b08c-06a6125ac41a" + "reference": "urn:uuid:f4e6c723-efec-5fe6-9159-ce76a2ae15dd" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json b/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json index 307f8d67..4abc24f2 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:41" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:40" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:8HT96eYiLbWjtbtVIwWUDixBE8r-i6SuUXCqEWs1IRk" + "value": "v0:fixture-key:1:m3zt6QNnSRhBsJXMHQpVURB9vlHwD7YEAZ29DSSj6Nk" } } ], - "fullUrl": "urn:uuid:659fa6df-ec45-5897-8a85-b68df925ee29", + "fullUrl": "urn:uuid:849c5b45-d32b-59c4-b7ba-d61312d9ef27", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:OD56zfXSltzOVd2cSE1aOMqphMiPvMtEav-SEPxrxhk", + "value": "v0:fixture-key:1:gPh7mylmekrCE9UJitdYyUQi_Tj1QXwbb8wHwoB9GYI", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:8HT96eYiLbWjtbtVIwWUDixBE8r-i6SuUXCqEWs1IRk", + "value": "v0:fixture-key:1:m3zt6QNnSRhBsJXMHQpVURB9vlHwD7YEAZ29DSSj6Nk", "type": { "coding": [ { @@ -124,11 +124,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:Ba-kuQQnIk98NEVsqQQe7O-S5JbbkdSMJ8kE9yAl2Ho" + "value": "v0:fixture-key:1:__ZuL0w8sk5Yz3yTgI7SC4XK9w44B9HXUj91cm1FsWQ" } } ], - "fullUrl": "urn:uuid:5ba8549c-cc98-5c68-b5c7-85824ba0cc8b", + "fullUrl": "urn:uuid:6262253c-dd07-5d04-8579-92fbf81533a7", "resource": { "resourceType": "Device", "meta": { @@ -139,7 +139,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:Ba-kuQQnIk98NEVsqQQe7O-S5JbbkdSMJ8kE9yAl2Ho", + "value": "v0:fixture-key:1:__ZuL0w8sk5Yz3yTgI7SC4XK9w44B9HXUj91cm1FsWQ", "type": { "coding": [ { @@ -173,11 +173,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:f_GbpxT669C97lP8rd5ol_9HzDfbAqnhPoC-fU16FXQ" + "value": "v0:fixture-key:1:V8GTrHviGlTvyHN2OgpQSTynWLcnM-QC0_--aCfssRE" } } ], - "fullUrl": "urn:uuid:9a1660b3-378f-51e0-8b8b-6a4648072516", + "fullUrl": "urn:uuid:e21dbf5c-7238-597a-b201-f6d7f835d951", "resource": { "resourceType": "Device", "meta": { @@ -188,7 +188,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:f_GbpxT669C97lP8rd5ol_9HzDfbAqnhPoC-fU16FXQ", + "value": "v0:fixture-key:1:V8GTrHviGlTvyHN2OgpQSTynWLcnM-QC0_--aCfssRE", "type": { "coding": [ { @@ -236,11 +236,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:QwHAjAj49BSmxEwHifopjWWYBgp4fiyGLl1HYHljgrE" + "value": "n0:conversion-provenance:0:haG6QJYpWmGVbQuTrZ6uNIIfwXKNK61ZDcIfsiHbXo4" } } ], - "fullUrl": "urn:uuid:b65d3f35-883c-524a-a7cb-e48718b42409", + "fullUrl": "urn:uuid:f7d48e17-92fe-51ad-a989-96c372ef8e88", "resource": { "resourceType": "Provenance", "meta": { @@ -249,7 +249,7 @@ ] }, "target": [ - { "reference": "urn:uuid:659fa6df-ec45-5897-8a85-b68df925ee29" } + { "reference": "urn:uuid:849c5b45-d32b-59c4-b7ba-d61312d9ef27" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -276,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:9a1660b3-378f-51e0-8b8b-6a4648072516" + "reference": "urn:uuid:e21dbf5c-7238-597a-b201-f6d7f835d951" } } ], @@ -286,7 +286,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:OD56zfXSltzOVd2cSE1aOMqphMiPvMtEav-SEPxrxhk", + "value": "v0:fixture-key:1:gPh7mylmekrCE9UJitdYyUQi_Tj1QXwbb8wHwoB9GYI", "type": { "coding": [ { @@ -310,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:5ba8549c-cc98-5c68-b5c7-85824ba0cc8b" + "reference": "urn:uuid:6262253c-dd07-5d04-8579-92fbf81533a7" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json b/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json index 98f743d1..13561dc5 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:40" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:39" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:-VrehvMePEy4QTFL6eqYg1_o1JaTVnaj2whlu5_56Eo" + "value": "v0:fixture-key:1:YrLEi4lkIP19yRHhjL0N7US8Uho6xCZYLkZOqcq9CWY" } } ], - "fullUrl": "urn:uuid:ad5adfe1-83a9-5445-b973-a10e2ce6a637", + "fullUrl": "urn:uuid:d84ea885-e118-52f2-90b1-925f92afb702", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:XgJtiUCD0cuBpIIXA5R3z4CYwW4K2fdEKeZwMJq--bc", + "value": "v0:fixture-key:1:9BdypFvzThLDwoZaAkXJoWLWqDNqaNic5oAhD6zAb6w", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:-VrehvMePEy4QTFL6eqYg1_o1JaTVnaj2whlu5_56Eo", + "value": "v0:fixture-key:1:YrLEi4lkIP19yRHhjL0N7US8Uho6xCZYLkZOqcq9CWY", "type": { "coding": [ { @@ -124,11 +124,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:5OysBeno7l6iCOCst3Uw4XDyQBea8geSJCEZEBRXDaI" + "value": "v0:fixture-key:1:AtOWrf7z6ZVcyMJNO_vx-fIUBZUytKMW-PT_x0I2CiE" } } ], - "fullUrl": "urn:uuid:4e6093ee-1107-544d-8450-c4c51f156641", + "fullUrl": "urn:uuid:41ced2c7-02a2-57ec-aed8-d996f96f0345", "resource": { "resourceType": "Device", "meta": { @@ -139,7 +139,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:5OysBeno7l6iCOCst3Uw4XDyQBea8geSJCEZEBRXDaI", + "value": "v0:fixture-key:1:AtOWrf7z6ZVcyMJNO_vx-fIUBZUytKMW-PT_x0I2CiE", "type": { "coding": [ { @@ -173,11 +173,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:QLLqe7qyiVlalpsljgKO_UNSsrDYq5ZBDbJhkV2oNXo" + "value": "v0:fixture-key:1:lp2Cnde-8YMwJBqwNUzgr5t4k8z_7ybfORUJagYVpEE" } } ], - "fullUrl": "urn:uuid:51ad7b35-f4ab-57c5-a0ba-8a1e38717a98", + "fullUrl": "urn:uuid:a31e7d33-eec1-55a9-ba76-f7a2e115a4de", "resource": { "resourceType": "Device", "meta": { @@ -188,7 +188,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:QLLqe7qyiVlalpsljgKO_UNSsrDYq5ZBDbJhkV2oNXo", + "value": "v0:fixture-key:1:lp2Cnde-8YMwJBqwNUzgr5t4k8z_7ybfORUJagYVpEE", "type": { "coding": [ { @@ -236,11 +236,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:zRFH4nYMDVzX2kUwWWNZvgTf-jEM6KiMTPVgdV6qFh4" + "value": "n0:conversion-provenance:0:8ZRVTj4Udaeytn6DxmTcyAWuzCQGrpgFIJBmH4tDES4" } } ], - "fullUrl": "urn:uuid:d5201df4-07ca-5c11-b841-9d19f4f7333a", + "fullUrl": "urn:uuid:0b45d042-6862-55bd-a464-fc2679ff3020", "resource": { "resourceType": "Provenance", "meta": { @@ -249,7 +249,7 @@ ] }, "target": [ - { "reference": "urn:uuid:ad5adfe1-83a9-5445-b973-a10e2ce6a637" } + { "reference": "urn:uuid:d84ea885-e118-52f2-90b1-925f92afb702" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -276,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:51ad7b35-f4ab-57c5-a0ba-8a1e38717a98" + "reference": "urn:uuid:a31e7d33-eec1-55a9-ba76-f7a2e115a4de" } } ], @@ -286,7 +286,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:XgJtiUCD0cuBpIIXA5R3z4CYwW4K2fdEKeZwMJq--bc", + "value": "v0:fixture-key:1:9BdypFvzThLDwoZaAkXJoWLWqDNqaNic5oAhD6zAb6w", "type": { "coding": [ { @@ -310,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:4e6093ee-1107-544d-8450-c4c51f156641" + "reference": "urn:uuid:41ced2c7-02a2-57ec-aed8-d996f96f0345" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json b/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json index 4a1cebf7..71f39817 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:42" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:41" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:NgpKwjtbacLLLIALkH5ljPqgtMspwLQHN0ih-LQeYkY" + "value": "v0:fixture-key:1:AbiOF6Y3Hvecmdt0vcY9O-tpBBvhuvJVPLPWe2VzN2s" } } ], - "fullUrl": "urn:uuid:0c0f2d86-7809-529e-8f39-4af267e8ee85", + "fullUrl": "urn:uuid:2fbda5c3-1ad0-59b9-9331-388671178ead", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:zE-GrKPuCrUM4HFNERcyX-hoimDBHlhm0g5nEnz8338", + "value": "v0:fixture-key:1:SiSGZdCnIwML3G4TrUiYwjaz8Db2kjtvOJWFrQ7Y9v8", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:NgpKwjtbacLLLIALkH5ljPqgtMspwLQHN0ih-LQeYkY", + "value": "v0:fixture-key:1:AbiOF6Y3Hvecmdt0vcY9O-tpBBvhuvJVPLPWe2VzN2s", "type": { "coding": [ { @@ -124,11 +124,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:k-WKPQzpF_j3Hw_bWyYnbA185sdWTT_kl-9sTR0JZ2o" + "value": "v0:fixture-key:1:99bYfKq38HdU_3V1w8_trVXSCQzB2ywUCIZBRKBwL6M" } } ], - "fullUrl": "urn:uuid:23362b25-5dcd-5d21-abed-93d18aba8c2a", + "fullUrl": "urn:uuid:2138c578-bb7d-5e64-a620-5bcc51797536", "resource": { "resourceType": "Device", "meta": { @@ -139,7 +139,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:k-WKPQzpF_j3Hw_bWyYnbA185sdWTT_kl-9sTR0JZ2o", + "value": "v0:fixture-key:1:99bYfKq38HdU_3V1w8_trVXSCQzB2ywUCIZBRKBwL6M", "type": { "coding": [ { @@ -173,11 +173,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:oh1U7xktK50hOpwTsCdz_pWXcp1eaLsRrDTbjIdlgWs" + "value": "v0:fixture-key:1:luW2Yct587KQwti7mnsRdpVqPXZUeQXyyWRRwjH-Ogg" } } ], - "fullUrl": "urn:uuid:a78ce352-3518-5d01-bba4-514d95ab8260", + "fullUrl": "urn:uuid:d46c3738-eda6-5a17-91b3-03bf1806962f", "resource": { "resourceType": "Device", "meta": { @@ -188,7 +188,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:oh1U7xktK50hOpwTsCdz_pWXcp1eaLsRrDTbjIdlgWs", + "value": "v0:fixture-key:1:luW2Yct587KQwti7mnsRdpVqPXZUeQXyyWRRwjH-Ogg", "type": { "coding": [ { @@ -236,11 +236,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:_ZMjCrqh1oRUDYwUpdo6mFJNt7n9lKQ1ijluft3sv6Y" + "value": "n0:conversion-provenance:0:kyJnRRrDXnYDxIiriQDYk8XXJXNYwEwA6I9tMzf_AmQ" } } ], - "fullUrl": "urn:uuid:3b3c0a16-43e8-586b-80e9-e5f170de5224", + "fullUrl": "urn:uuid:e246c232-e0fe-54df-9292-f2487cf88fe7", "resource": { "resourceType": "Provenance", "meta": { @@ -249,7 +249,7 @@ ] }, "target": [ - { "reference": "urn:uuid:0c0f2d86-7809-529e-8f39-4af267e8ee85" } + { "reference": "urn:uuid:2fbda5c3-1ad0-59b9-9331-388671178ead" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -276,7 +276,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:a78ce352-3518-5d01-bba4-514d95ab8260" + "reference": "urn:uuid:d46c3738-eda6-5a17-91b3-03bf1806962f" } } ], @@ -286,7 +286,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:zE-GrKPuCrUM4HFNERcyX-hoimDBHlhm0g5nEnz8338", + "value": "v0:fixture-key:1:SiSGZdCnIwML3G4TrUiYwjaz8Db2kjtvOJWFrQ7Y9v8", "type": { "coding": [ { @@ -310,7 +310,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:23362b25-5dcd-5d21-abed-93d18aba8c2a" + "reference": "urn:uuid:2138c578-bb7d-5e64-a620-5bcc51797536" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json b/packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json deleted file mode 100644 index f8e2c4da..00000000 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json +++ /dev/null @@ -1,340 +0,0 @@ -{ - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, - "identifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "event" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:35" - }, - "type": "collection", - "timestamp": "2026-08-20T12:03:00Z", - "entry": [ - { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:wfMYZsydK0NY4MYx71uNA3EwZ7vfQ4qsTWrfEH0aXnU" - } - } - ], - "fullUrl": "urn:uuid:f2b1fd23-c157-528e-814e-3e89d8f80d78", - "resource": { - "resourceType": "Observation", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/withings/StructureDefinition/withings-sleeping-heart-rate-average", - "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" - ] - }, - "extension": [ - { - "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", - "valueCode": "withings" - }, - { - "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", - "valueCode": "withings/getsummary:hr_average" - }, - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", - "valueCoding": { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", - "code": "automatically-recorded" - } - } - ], - "identifier": [ - { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:CZ2cmfO6offumxbR-gXU9SJ2_8Rk3AazW-0xdsjKQKs", - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-record" - } - ] - } - }, - { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:wfMYZsydK0NY4MYx71uNA3EwZ7vfQ4qsTWrfEH0aXnU", - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - } - } - ], - "status": "final", - "code": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", - "code": "sleeping-heart-rate-average" - } - ] - }, - "subject": { - "type": "Patient", - "identifier": { - "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", - "value": "patient-example" - } - }, - "effectivePeriod": { - "start": "2026-08-20T00:00:00.000-07:00", - "end": "2026-08-21T00:00:00.000-07:00" - }, - "valueQuantity": { - "value": 55, - "unit": "beats/minute", - "system": "http://unitsofmeasure.org", - "code": "/min" - }, - "method": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-aggregation-method", - "code": "session-mean", - "display": "Session mean" - } - ], - "text": "Session mean" - } - } - }, - { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:-auiTPVO34UPW2ganrhpldZ8AZIo-OgZ2PX2ypqIBBk" - } - } - ], - "fullUrl": "urn:uuid:e013c037-4170-57d9-843e-9c4b51cbb6e8", - "resource": { - "resourceType": "Device", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" - ] - }, - "identifier": [ - { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:-auiTPVO34UPW2ganrhpldZ8AZIo-OgZ2PX2ypqIBBk", - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" - } - ] - } - } - ], - "status": "active", - "deviceName": [ - { - "name": "Synthetic connected provider", - "type": "user-friendly-name" - } - ] - } - }, - { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:GIryRZlH8NN1hVjxX6i0LR1Pp_kOBt0IbNQ7AYhCwnI" - } - } - ], - "fullUrl": "urn:uuid:d4fbec3e-a3d3-5c26-b821-0c8573aa7d92", - "resource": { - "resourceType": "Device", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" - ] - }, - "identifier": [ - { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:GIryRZlH8NN1hVjxX6i0LR1Pp_kOBt0IbNQ7AYhCwnI", - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" - } - ] - } - } - ], - "status": "active", - "deviceName": [ - { - "name": "Grove TypeScript conformance producer", - "type": "user-friendly-name" - } - ], - "version": [ - { - "type": { - "coding": [ - { - "system": "urn:iso:std:iso:11073:10101", - "code": "531975", - "display": "Software revision" - } - ] - }, - "value": "0.0.0" - } - ] - } - }, - { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:I1GTBoaSyqEh7xIu6Ymum1Lt4REZ0iMmTuT7i3x9UyE" - } - } - ], - "fullUrl": "urn:uuid:2625ae16-19f8-5ade-b459-c026e2137adc", - "resource": { - "resourceType": "Provenance", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" - ] - }, - "target": [ - { "reference": "urn:uuid:f2b1fd23-c157-528e-814e-3e89d8f80d78" } - ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", - "activity": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", - "code": "transform", - "display": "Transform/Translate Record Lifecycle Event" - } - ], - "text": "Transform/Translate Record Lifecycle Event" - }, - "agent": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", - "code": "assembler", - "display": "Assembler" - } - ], - "text": "Assembler" - }, - "who": { - "reference": "urn:uuid:d4fbec3e-a3d3-5c26-b821-0c8573aa7d92" - } - } - ], - "entity": [ - { - "role": "source", - "what": { - "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:CZ2cmfO6offumxbR-gXU9SJ2_8Rk3AazW-0xdsjKQKs", - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-record" - } - ] - } - } - }, - "agent": [ - { - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", - "code": "enterer", - "display": "Enterer" - } - ], - "text": "Enterer" - }, - "who": { - "reference": "urn:uuid:e013c037-4170-57d9-843e-9c4b51cbb6e8" - } - } - ] - } - ] - } - } - ] -} diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json.license b/packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json.license deleted file mode 100644 index fa4e8d92..00000000 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-sleeping-heart-rate-average.json.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) - -SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json index b1e60022..e9b0313c 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:39" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:38" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:emH-NR1xqg_vUxVL0hRTwagb5U_a19915i0i1-R6A2o" + "value": "v0:fixture-key:1:UIpn4q5L_fH_yipQV_n3rk8PKcagpVghtl1zhOUB53c" } } ], - "fullUrl": "urn:uuid:8833e73d-b370-5a45-a5bb-25a1dae8416f", + "fullUrl": "urn:uuid:14899158-9250-565e-bebb-5eb5633aa2e4", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:mILmtdrsmxCMZfQbvWplAFWwisalKkizN8iGpnpOhko", + "value": "v0:fixture-key:1:ZxvReRdSivoUK5JDcmAqUlMR3LUZJvTNscfXuIReUBY", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:emH-NR1xqg_vUxVL0hRTwagb5U_a19915i0i1-R6A2o", + "value": "v0:fixture-key:1:UIpn4q5L_fH_yipQV_n3rk8PKcagpVghtl1zhOUB53c", "type": { "coding": [ { @@ -131,11 +131,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:Il9XmM9hhHHcq2duFtsKSaGGjcHi93bXcxP7PHWQqR0" + "value": "v0:fixture-key:1:UClwZV7svmpSmN8FNXWl-VfiXDrzMfqjK8sxfS26pyQ" } } ], - "fullUrl": "urn:uuid:fe81dcbb-4115-5986-92e6-8cc9c77c8063", + "fullUrl": "urn:uuid:4f1da7df-5e4e-5c3b-b239-c7ede7493c89", "resource": { "resourceType": "Device", "meta": { @@ -146,7 +146,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:Il9XmM9hhHHcq2duFtsKSaGGjcHi93bXcxP7PHWQqR0", + "value": "v0:fixture-key:1:UClwZV7svmpSmN8FNXWl-VfiXDrzMfqjK8sxfS26pyQ", "type": { "coding": [ { @@ -180,11 +180,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:tUlqK6yLqPm92UDowyv74VPf3boNLpZlH1b4cdpcewg" + "value": "v0:fixture-key:1:O9Tw3fu-swdRGFWTe5R2t-hWL8JDHTm3UJGoSoxnn10" } } ], - "fullUrl": "urn:uuid:cf01f8b8-9d69-5d62-9e8f-2da6ea54ad0c", + "fullUrl": "urn:uuid:aabcc5f8-104d-53a7-a55f-91fabe26d699", "resource": { "resourceType": "Device", "meta": { @@ -195,7 +195,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:tUlqK6yLqPm92UDowyv74VPf3boNLpZlH1b4cdpcewg", + "value": "v0:fixture-key:1:O9Tw3fu-swdRGFWTe5R2t-hWL8JDHTm3UJGoSoxnn10", "type": { "coding": [ { @@ -243,11 +243,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:kXrlAZ8HIs0Hv-Ug_Za1nV5kPCTUMEQa_rbUDFAi3S0" + "value": "n0:conversion-provenance:0:FY6d5Pc23vpiqS2qy2e5rvA4T6UzWja_ZjZp_nT80rQ" } } ], - "fullUrl": "urn:uuid:d831037e-f4f8-5e2f-b1dd-78b7ea7bf940", + "fullUrl": "urn:uuid:bba7b490-1564-5e5b-9927-6b7e5b4cc63d", "resource": { "resourceType": "Provenance", "meta": { @@ -256,7 +256,7 @@ ] }, "target": [ - { "reference": "urn:uuid:8833e73d-b370-5a45-a5bb-25a1dae8416f" } + { "reference": "urn:uuid:14899158-9250-565e-bebb-5eb5633aa2e4" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -283,7 +283,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:cf01f8b8-9d69-5d62-9e8f-2da6ea54ad0c" + "reference": "urn:uuid:aabcc5f8-104d-53a7-a55f-91fabe26d699" } } ], @@ -293,7 +293,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:mILmtdrsmxCMZfQbvWplAFWwisalKkizN8iGpnpOhko", + "value": "v0:fixture-key:1:ZxvReRdSivoUK5JDcmAqUlMR3LUZJvTNscfXuIReUBY", "type": { "coding": [ { @@ -317,7 +317,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:fe81dcbb-4115-5986-92e6-8cc9c77c8063" + "reference": "urn:uuid:4f1da7df-5e4e-5c3b-b239-c7ede7493c89" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json index 54bd3f83..38911ac9 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:44" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:43" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:zMUpq6PKQ7KQzFV8uvbij_qpG9P5KuVYCsi2UtsSWLo" + "value": "v0:fixture-key:1:DhzbFybuD21ODCiEK2DB-fCJUfF3OOYQFk3UHpp7GSE" } } ], - "fullUrl": "urn:uuid:103b8762-3db0-5b9e-a96a-5c99e60c364a", + "fullUrl": "urn:uuid:5b1a9420-aa1a-55f3-ac61-2f721d0540d6", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:TyYVQi5gWz12InQQ1q-4jM-vE0zcxdOjPgZ96X9IToM", + "value": "v0:fixture-key:1:OUDYL-JBwPu9y4Z1U7Ql1-9C2TShwD4chzmFTmnrxNs", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:zMUpq6PKQ7KQzFV8uvbij_qpG9P5KuVYCsi2UtsSWLo", + "value": "v0:fixture-key:1:DhzbFybuD21ODCiEK2DB-fCJUfF3OOYQFk3UHpp7GSE", "type": { "coding": [ { @@ -131,11 +131,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:wPHa9mRDNZ2mxN1Gr98bVc1AJsa3MfGoSU9OZ2rdSMI" + "value": "v0:fixture-key:1:VOCwn-nw7Eu3UMotEURLN4inM8fMv7T9-_SeRWDjisQ" } } ], - "fullUrl": "urn:uuid:7386126f-fd74-5776-add6-742a2794a810", + "fullUrl": "urn:uuid:731ad91c-6e4e-56ad-b35b-cf13bcd2a5f9", "resource": { "resourceType": "Device", "meta": { @@ -146,7 +146,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:wPHa9mRDNZ2mxN1Gr98bVc1AJsa3MfGoSU9OZ2rdSMI", + "value": "v0:fixture-key:1:VOCwn-nw7Eu3UMotEURLN4inM8fMv7T9-_SeRWDjisQ", "type": { "coding": [ { @@ -180,11 +180,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:dICuwOuxbMBV5mOe-F_OWanjaUBgCr24EmZj5UFx_0U" + "value": "v0:fixture-key:1:vkwcQexNNJMwlm8JYiVoBTX1-5A5n6pyZS4MS9TugPQ" } } ], - "fullUrl": "urn:uuid:9e0b38fe-b155-5efb-8f7d-4be794b626ef", + "fullUrl": "urn:uuid:2af9b110-443b-5976-af32-24f06b43b61a", "resource": { "resourceType": "Device", "meta": { @@ -195,7 +195,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:dICuwOuxbMBV5mOe-F_OWanjaUBgCr24EmZj5UFx_0U", + "value": "v0:fixture-key:1:vkwcQexNNJMwlm8JYiVoBTX1-5A5n6pyZS4MS9TugPQ", "type": { "coding": [ { @@ -243,11 +243,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:UHm6_5bDGuYRkyIs7yvhQoa9Hck6OcZncYXbYRKhVIk" + "value": "n0:conversion-provenance:0:VVL8mOlyjsCfZVr7MZV0ahVYEH5geZ52CSvU8dwBu74" } } ], - "fullUrl": "urn:uuid:96053cec-a187-50c8-9865-3f450665fc15", + "fullUrl": "urn:uuid:d36943e1-6026-5e6c-bb2e-a1b029503b86", "resource": { "resourceType": "Provenance", "meta": { @@ -256,7 +256,7 @@ ] }, "target": [ - { "reference": "urn:uuid:103b8762-3db0-5b9e-a96a-5c99e60c364a" } + { "reference": "urn:uuid:5b1a9420-aa1a-55f3-ac61-2f721d0540d6" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -283,7 +283,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:9e0b38fe-b155-5efb-8f7d-4be794b626ef" + "reference": "urn:uuid:2af9b110-443b-5976-af32-24f06b43b61a" } } ], @@ -293,7 +293,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:TyYVQi5gWz12InQQ1q-4jM-vE0zcxdOjPgZ96X9IToM", + "value": "v0:fixture-key:1:OUDYL-JBwPu9y4Z1U7Ql1-9C2TShwD4chzmFTmnrxNs", "type": { "coding": [ { @@ -317,7 +317,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:7386126f-fd74-5776-add6-742a2794a810" + "reference": "urn:uuid:731ad91c-6e4e-56ad-b35b-cf13bcd2a5f9" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json index 376d50c3..ad0f3ffc 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:45" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:44" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:Nupjb6-6LRKMFNKdJZ_a8fpCGzOvIxiYPsXz1LCZx70" + "value": "v0:fixture-key:1:iKOlXQ-gWHTTGJe5x8oFaaYLnB2esy9gRXjQTIJvWXg" } } ], - "fullUrl": "urn:uuid:69e4617e-0a17-5728-b431-3e326ee00b8f", + "fullUrl": "urn:uuid:5ddc75c4-ce94-5330-941e-52c9470adc55", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:e1WLlbtbT-04i-Z-dr6wkLIwmNjjc8wmlFH6AqzrSSA", + "value": "v0:fixture-key:1:gknGkBm8LLih4l5bW2EJf0VMUE1-cJMRmVr4_eVDT0c", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:Nupjb6-6LRKMFNKdJZ_a8fpCGzOvIxiYPsXz1LCZx70", + "value": "v0:fixture-key:1:iKOlXQ-gWHTTGJe5x8oFaaYLnB2esy9gRXjQTIJvWXg", "type": { "coding": [ { @@ -129,11 +129,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:hLXwYN5X8Ktb5SnrmBBsZt817xz7mTyLealZ933VboI" + "value": "v0:fixture-key:1:N1m0pHHsgiv9tFv6kqHO7OVrcRcFzxQ11q3khjuDRj4" } } ], - "fullUrl": "urn:uuid:cb6bbeb6-adbb-562b-aadd-d369bf9bd355", + "fullUrl": "urn:uuid:d5cfaa1f-d8dc-5154-956b-79a2574fec38", "resource": { "resourceType": "Device", "meta": { @@ -144,7 +144,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:hLXwYN5X8Ktb5SnrmBBsZt817xz7mTyLealZ933VboI", + "value": "v0:fixture-key:1:N1m0pHHsgiv9tFv6kqHO7OVrcRcFzxQ11q3khjuDRj4", "type": { "coding": [ { @@ -178,11 +178,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:EDLAes5DVhwOatmQGgLPL6kCWu1Mi5dZYnxSIeZvyFw" + "value": "v0:fixture-key:1:Tt5TQF_t0Sa93jP9w3UcAfQBUfCyuTAWkm1zobHBye4" } } ], - "fullUrl": "urn:uuid:7711f83b-c805-5c21-a65b-1d9dfb63c36c", + "fullUrl": "urn:uuid:cf0c5dad-04d3-5a54-827e-c7e86fa32d28", "resource": { "resourceType": "Device", "meta": { @@ -193,7 +193,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:EDLAes5DVhwOatmQGgLPL6kCWu1Mi5dZYnxSIeZvyFw", + "value": "v0:fixture-key:1:Tt5TQF_t0Sa93jP9w3UcAfQBUfCyuTAWkm1zobHBye4", "type": { "coding": [ { @@ -241,11 +241,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:ulz23Bd4CnBDvPiT9TxdmtBhEg3PCfoPCQGyBPYZbDA" + "value": "n0:conversion-provenance:0:SAGhx-yLYVhtcX78WfYR-BVjcwjmLAu54qAOX_c7UvE" } } ], - "fullUrl": "urn:uuid:e4510a70-4e18-5b49-9114-77eb0615051c", + "fullUrl": "urn:uuid:a878c05a-740f-5bc0-911c-6235f9329476", "resource": { "resourceType": "Provenance", "meta": { @@ -254,7 +254,7 @@ ] }, "target": [ - { "reference": "urn:uuid:69e4617e-0a17-5728-b431-3e326ee00b8f" } + { "reference": "urn:uuid:5ddc75c4-ce94-5330-941e-52c9470adc55" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -281,7 +281,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:7711f83b-c805-5c21-a65b-1d9dfb63c36c" + "reference": "urn:uuid:cf0c5dad-04d3-5a54-827e-c7e86fa32d28" } } ], @@ -291,7 +291,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:e1WLlbtbT-04i-Z-dr6wkLIwmNjjc8wmlFH6AqzrSSA", + "value": "v0:fixture-key:1:gknGkBm8LLih4l5bW2EJf0VMUE1-cJMRmVr4_eVDT0c", "type": { "coding": [ { @@ -315,7 +315,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:cb6bbeb6-adbb-562b-aadd-d369bf9bd355" + "reference": "urn:uuid:d5cfaa1f-d8dc-5154-956b-79a2574fec38" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json index 01f0f51c..122c7ef9 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:38" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:37" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:FqQDl6lwjbaRTytFX2ebkYzxZVe6I2BvlaJ8l9WapRQ" + "value": "v0:fixture-key:1:ZV6dweE3ClIU0TX2L5u1nA_-QX2n8mNZSW6SzwjiG6o" } } ], - "fullUrl": "urn:uuid:3743f2a7-4635-512e-8a7f-85f613ce5c51", + "fullUrl": "urn:uuid:e502b0a8-d613-5be5-af9c-c536dcdf8965", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:8V-A1z7HIta0HdzglUhddFLCMuke0tj4Jqalih8E1x4", + "value": "v0:fixture-key:1:0CXIWS-tb3h677E99r2s0kCjmKRmjT5koIKz6SnEV-o", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:FqQDl6lwjbaRTytFX2ebkYzxZVe6I2BvlaJ8l9WapRQ", + "value": "v0:fixture-key:1:ZV6dweE3ClIU0TX2L5u1nA_-QX2n8mNZSW6SzwjiG6o", "type": { "coding": [ { @@ -129,11 +129,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:yYOwkwpAwZ1YIrHn2yudC_JTipVjnM09wA8xw08qJ60" + "value": "v0:fixture-key:1:VpM6a_fjm3X5jsTGMqAYfPQ0a91415LGHoo8U9hrLYM" } } ], - "fullUrl": "urn:uuid:4ad85f24-e1e9-5944-8651-5bcc971c8a31", + "fullUrl": "urn:uuid:b3a3e49e-8642-5af0-85a3-36df6da0d1df", "resource": { "resourceType": "Device", "meta": { @@ -144,7 +144,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:yYOwkwpAwZ1YIrHn2yudC_JTipVjnM09wA8xw08qJ60", + "value": "v0:fixture-key:1:VpM6a_fjm3X5jsTGMqAYfPQ0a91415LGHoo8U9hrLYM", "type": { "coding": [ { @@ -178,11 +178,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:9ObTGmgNyzr1T-rsn6TA3oVgR8ylKy35MLs-TWCHvXE" + "value": "v0:fixture-key:1:SAXtyoGCST2d9rSphOxwj7EZgdyex3Gh0NYcj69xHk8" } } ], - "fullUrl": "urn:uuid:a106321e-50be-5095-a4ec-12fe69677052", + "fullUrl": "urn:uuid:277dad0b-090f-52d4-9595-16d6e2ccaf73", "resource": { "resourceType": "Device", "meta": { @@ -193,7 +193,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:9ObTGmgNyzr1T-rsn6TA3oVgR8ylKy35MLs-TWCHvXE", + "value": "v0:fixture-key:1:SAXtyoGCST2d9rSphOxwj7EZgdyex3Gh0NYcj69xHk8", "type": { "coding": [ { @@ -241,11 +241,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:OfCIr0vxkZEoRDf5mnkqOVlQQbZnYsvJgj4lmxoIg4o" + "value": "n0:conversion-provenance:0:oX8Epkb4hJGt8W5eDD2pS103Nv9UchjAGGTebThAOWo" } } ], - "fullUrl": "urn:uuid:9cca95a8-a441-5a16-a7e6-524540e019d9", + "fullUrl": "urn:uuid:dc673541-6367-5b66-93b5-14b3019515e0", "resource": { "resourceType": "Provenance", "meta": { @@ -254,7 +254,7 @@ ] }, "target": [ - { "reference": "urn:uuid:3743f2a7-4635-512e-8a7f-85f613ce5c51" } + { "reference": "urn:uuid:e502b0a8-d613-5be5-af9c-c536dcdf8965" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -281,7 +281,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:a106321e-50be-5095-a4ec-12fe69677052" + "reference": "urn:uuid:277dad0b-090f-52d4-9595-16d6e2ccaf73" } } ], @@ -291,7 +291,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:8V-A1z7HIta0HdzglUhddFLCMuke0tj4Jqalih8E1x4", + "value": "v0:fixture-key:1:0CXIWS-tb3h677E99r2s0kCjmKRmjT5koIKz6SnEV-o", "type": { "coding": [ { @@ -315,7 +315,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:4ad85f24-e1e9-5944-8651-5bcc971c8a31" + "reference": "urn:uuid:b3a3e49e-8642-5af0-85a3-36df6da0d1df" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json index 409c62f8..48a8e255 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:34" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:34" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:2iMBjOsXnAQIT45pZU9H5ypPRzY153KkVupDOHIKLWY" + "value": "v0:fixture-key:1:sTZ6V-sSi52BgZy4Z8NRDLRLid7ssBNqXnkSwJpEh6o" } } ], - "fullUrl": "urn:uuid:90c159c5-309d-5400-99cf-4b1807c92576", + "fullUrl": "urn:uuid:c4c4f7e0-34ca-52bf-9166-237f9bd63c48", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:C7K-KSVzTkMWqHwgrTYgZZCYsd9iE2EikLLsGcS4vJM", + "value": "v0:fixture-key:1:_l2x6stLJ5Pmo2dPPDYp2B8RPmKnXslZiw1uKzFuNYY", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:2iMBjOsXnAQIT45pZU9H5ypPRzY153KkVupDOHIKLWY", + "value": "v0:fixture-key:1:sTZ6V-sSi52BgZy4Z8NRDLRLid7ssBNqXnkSwJpEh6o", "type": { "coding": [ { @@ -129,11 +129,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:VxnD6ESPP8cXKJKDtr5Q2bKTvbWorcFHs8-UJRtBgIQ" + "value": "v0:fixture-key:1:EKyKxUz76qvIHbKXieIfjmLdkpPQ-VhRxGVPmcSqBhQ" } } ], - "fullUrl": "urn:uuid:af293fce-c17c-51f6-a74f-3820ff9dee04", + "fullUrl": "urn:uuid:f99a4b98-b062-5dde-b8b0-840e160e42f0", "resource": { "resourceType": "Device", "meta": { @@ -144,7 +144,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:VxnD6ESPP8cXKJKDtr5Q2bKTvbWorcFHs8-UJRtBgIQ", + "value": "v0:fixture-key:1:EKyKxUz76qvIHbKXieIfjmLdkpPQ-VhRxGVPmcSqBhQ", "type": { "coding": [ { @@ -178,11 +178,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:SSVrBCM7t7d-X19tAz_m4mRTShckrfCJdM-MHPil3eo" + "value": "v0:fixture-key:1:H1u4xN5uRPTDRI9WWv339sBBp8j5rNtyVPYFAp5S5ys" } } ], - "fullUrl": "urn:uuid:e07b3c6b-6ee1-56e6-b378-0ab3b463b833", + "fullUrl": "urn:uuid:dde945e4-a798-5e0e-81a9-e386994f4672", "resource": { "resourceType": "Device", "meta": { @@ -193,7 +193,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:SSVrBCM7t7d-X19tAz_m4mRTShckrfCJdM-MHPil3eo", + "value": "v0:fixture-key:1:H1u4xN5uRPTDRI9WWv339sBBp8j5rNtyVPYFAp5S5ys", "type": { "coding": [ { @@ -241,11 +241,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:aB9HfOQ1q8VFIiwTHa2P4omQsDz3e2Hivq8hrsGsoII" + "value": "n0:conversion-provenance:0:NPl8ugskS80-ewIkyuzrLgotSslalz00ZyOtD8fm7aM" } } ], - "fullUrl": "urn:uuid:ce7089de-11a7-54aa-a2cb-d089f625c747", + "fullUrl": "urn:uuid:ab1f9434-7723-5575-a5ea-8833fb57b380", "resource": { "resourceType": "Provenance", "meta": { @@ -254,7 +254,7 @@ ] }, "target": [ - { "reference": "urn:uuid:90c159c5-309d-5400-99cf-4b1807c92576" } + { "reference": "urn:uuid:c4c4f7e0-34ca-52bf-9166-237f9bd63c48" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -281,7 +281,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e07b3c6b-6ee1-56e6-b378-0ab3b463b833" + "reference": "urn:uuid:dde945e4-a798-5e0e-81a9-e386994f4672" } } ], @@ -291,7 +291,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:C7K-KSVzTkMWqHwgrTYgZZCYsd9iE2EikLLsGcS4vJM", + "value": "v0:fixture-key:1:_l2x6stLJ5Pmo2dPPDYp2B8RPmKnXslZiw1uKzFuNYY", "type": { "coding": [ { @@ -315,7 +315,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:af293fce-c17c-51f6-a74f-3820ff9dee04" + "reference": "urn:uuid:f99a4b98-b062-5dde-b8b0-840e160e42f0" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json index 8990a1f5..a4968f54 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:48" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:47" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:tZvXJhhF-AF2LfpkVJT-2y113D1Uk4qI7gMYq8octas" + "value": "v0:fixture-key:1:mpcsNy93_GF0urGccYrlm7WcamEEB3JLTyUD3o5QNpg" } } ], - "fullUrl": "urn:uuid:c36c4917-50f3-5369-a417-824e74dbfc2a", + "fullUrl": "urn:uuid:dabd84be-fb8a-5569-ab07-d718f176ef9e", "resource": { "resourceType": "Observation", "meta": { @@ -67,7 +67,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:Mz7eIgHMyN-svW-aZcm0pfRUcHEGUeyhueiSGXYyToI", + "value": "v0:fixture-key:1:mXuLjG0QGS2dxig2AVgHnnRSwLPbJRAZI8vB-6ANrJY", "type": { "coding": [ { @@ -79,7 +79,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:tZvXJhhF-AF2LfpkVJT-2y113D1Uk4qI7gMYq8octas", + "value": "v0:fixture-key:1:mpcsNy93_GF0urGccYrlm7WcamEEB3JLTyUD3o5QNpg", "type": { "coding": [ { @@ -129,11 +129,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:grWuDgnsPw60xb8ySPI9U1FukEnmnHpQNMFkf0p0P_0" + "value": "v0:fixture-key:1:ZZ3OyjMSTgTPzidj8lQLnZAEVzNyoHZaWNixOJ08smw" } } ], - "fullUrl": "urn:uuid:1860727d-2576-55d5-95b1-c044cb1df722", + "fullUrl": "urn:uuid:fa327e8d-ba69-5548-8572-0a5651a0a973", "resource": { "resourceType": "Device", "meta": { @@ -144,7 +144,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:grWuDgnsPw60xb8ySPI9U1FukEnmnHpQNMFkf0p0P_0", + "value": "v0:fixture-key:1:ZZ3OyjMSTgTPzidj8lQLnZAEVzNyoHZaWNixOJ08smw", "type": { "coding": [ { @@ -178,11 +178,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:TFt-5upyqZDRPM32U7kvf6w4yiNxc1rsvPizF1L4_Ug" + "value": "v0:fixture-key:1:MF2JK1kM8eGepo4odzSLt4CNLjdJMGJHPjXnmiAi61Y" } } ], - "fullUrl": "urn:uuid:4d922b3a-5d55-5950-9be9-292492a0b071", + "fullUrl": "urn:uuid:0638e592-a3af-5d4c-b689-264c19f94ccd", "resource": { "resourceType": "Device", "meta": { @@ -193,7 +193,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:TFt-5upyqZDRPM32U7kvf6w4yiNxc1rsvPizF1L4_Ug", + "value": "v0:fixture-key:1:MF2JK1kM8eGepo4odzSLt4CNLjdJMGJHPjXnmiAi61Y", "type": { "coding": [ { @@ -241,11 +241,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:pYi0YCuMa86bbjk85SR0AMiqhh_YQiX0vi0up-bemtk" + "value": "n0:conversion-provenance:0:KiJhlB1_wGC3WEMuY4Y4o9J103fJ04dAZo5lV513j4g" } } ], - "fullUrl": "urn:uuid:a8db880a-9ee1-5e06-a7ac-f3881280b431", + "fullUrl": "urn:uuid:7f761f2c-dde0-5cf3-99c6-02795efed9f2", "resource": { "resourceType": "Provenance", "meta": { @@ -254,7 +254,7 @@ ] }, "target": [ - { "reference": "urn:uuid:c36c4917-50f3-5369-a417-824e74dbfc2a" } + { "reference": "urn:uuid:dabd84be-fb8a-5569-ab07-d718f176ef9e" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -281,7 +281,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:4d922b3a-5d55-5950-9be9-292492a0b071" + "reference": "urn:uuid:0638e592-a3af-5d4c-b689-264c19f94ccd" } } ], @@ -291,7 +291,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:Mz7eIgHMyN-svW-aZcm0pfRUcHEGUeyhueiSGXYyToI", + "value": "v0:fixture-key:1:mXuLjG0QGS2dxig2AVgHnnRSwLPbJRAZI8vB-6ANrJY", "type": { "coding": [ { @@ -315,7 +315,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:1860727d-2576-55d5-95b1-c044cb1df722" + "reference": "urn:uuid:fa327e8d-ba69-5548-8572-0a5651a0a973" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json b/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json index 1d4a19fe..85e85776 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json +++ b/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:50" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:49" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:dQm59jLLiwD0Zs3uVioH3S-V4hOzA3izmLiS1S94se4" + "value": "v0:fixture-key:1:w7o_lcZdhv2y5mj96CWnefbS73I3tPoRvuSQG7Vaqao" } } ], - "fullUrl": "urn:uuid:7a8fa1d7-37d4-5dbd-a1f1-3b63629b18e0", + "fullUrl": "urn:uuid:f39530e9-26b5-5078-9f30-5d61d3e4881a", "resource": { "resourceType": "DocumentReference", "meta": { @@ -60,7 +60,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:RDmUrHr5N6bLAoEeTx0TRfsl2wf739glATitaeC3zlM", + "value": "v0:fixture-key:1:Qm28JqWMxMvl2dsco1gdTLvUyKdg3S8qfrEX_4Z19KY", "type": { "coding": [ { @@ -72,7 +72,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:dQm59jLLiwD0Zs3uVioH3S-V4hOzA3izmLiS1S94se4", + "value": "v0:fixture-key:1:w7o_lcZdhv2y5mj96CWnefbS73I3tPoRvuSQG7Vaqao", "type": { "coding": [ { @@ -84,7 +84,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", - "value": "v2:fixture-key:1:F2IrGFyNF7wRiEXA_f_VCSiIVV_35FCfAj1wU3lnwXg", + "value": "v0:fixture-key:1:PHw-YmUdZHB6MdPZ3172jcWHpUdXb6Dh9IOm9pXrO4M", "type": { "coding": [ { @@ -106,12 +106,12 @@ }, "date": "2026-08-20T12:01:00Z", "author": [ - { "reference": "urn:uuid:f90fcdf9-1d5d-5f4d-8759-ce24ee11ecbf" } + { "reference": "urn:uuid:443842a6-4a5b-5ce2-b29e-134f07e39c58" } ], "content": [ { "attachment": { - "contentType": "application/vnd.grovealliance.provider+json", + "contentType": "application/json", "data": "eyJzeW50aGV0aWMiOnRydWV9", "size": 18, "hash": "SHXPL1bWXRn+jEpR9Aw2zmx15qs=", @@ -120,8 +120,7 @@ "format": { "system": "https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format", "code": "provider-recording", - "display": "Provider Recording", - "version": "0.6.0" + "display": "Provider Recording" } } ] @@ -141,11 +140,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:QlkY7bt_QZvWGB92pst3f3Bmlv0vPJ9p12NzL-WcOIw" + "value": "v0:fixture-key:1:oLMQbfc0NeyY4hR6JEtvp-FZuco9HARLJLu04VS1a94" } } ], - "fullUrl": "urn:uuid:7b317e95-cd66-5207-9263-1192c80dd69d", + "fullUrl": "urn:uuid:5cfe6e6b-d30e-5e8a-88cf-d4d922961187", "resource": { "resourceType": "Device", "meta": { @@ -156,7 +155,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:QlkY7bt_QZvWGB92pst3f3Bmlv0vPJ9p12NzL-WcOIw", + "value": "v0:fixture-key:1:oLMQbfc0NeyY4hR6JEtvp-FZuco9HARLJLu04VS1a94", "type": { "coding": [ { @@ -190,11 +189,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:WclJMCQ6CUAslia5K2NFCa4BEWetQW0V2edf9a-EN_w" + "value": "v0:fixture-key:1:uocYwddFVtlKSSu89sdrCnhx7B1Zj4lX7iNLzaZjvAA" } } ], - "fullUrl": "urn:uuid:f90fcdf9-1d5d-5f4d-8759-ce24ee11ecbf", + "fullUrl": "urn:uuid:443842a6-4a5b-5ce2-b29e-134f07e39c58", "resource": { "resourceType": "Device", "meta": { @@ -205,7 +204,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:WclJMCQ6CUAslia5K2NFCa4BEWetQW0V2edf9a-EN_w", + "value": "v0:fixture-key:1:uocYwddFVtlKSSu89sdrCnhx7B1Zj4lX7iNLzaZjvAA", "type": { "coding": [ { @@ -253,11 +252,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:tVYB3e_dIl_B-kam9oOpu0sMNeUG_mjlIHE00kv-jWk" + "value": "n0:conversion-provenance:0:cIH4Nzhma1gYzkQquANDEpkWbH1g8n6BZFu8SGZae8k" } } ], - "fullUrl": "urn:uuid:341ccbad-8942-5258-835a-a930be090079", + "fullUrl": "urn:uuid:9e5ec01e-0d7f-5b0b-a505-f65fd1ea665a", "resource": { "resourceType": "Provenance", "meta": { @@ -266,7 +265,7 @@ ] }, "target": [ - { "reference": "urn:uuid:7a8fa1d7-37d4-5dbd-a1f1-3b63629b18e0" } + { "reference": "urn:uuid:f39530e9-26b5-5078-9f30-5d61d3e4881a" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -293,7 +292,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:f90fcdf9-1d5d-5f4d-8759-ce24ee11ecbf" + "reference": "urn:uuid:443842a6-4a5b-5ce2-b29e-134f07e39c58" } } ], @@ -303,7 +302,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:RDmUrHr5N6bLAoEeTx0TRfsl2wf739glATitaeC3zlM", + "value": "v0:fixture-key:1:Qm28JqWMxMvl2dsco1gdTLvUyKdg3S8qfrEX_4Z19KY", "type": { "coding": [ { @@ -327,7 +326,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:7b317e95-cd66-5207-9263-1192c80dd69d" + "reference": "urn:uuid:5cfe6e6b-d30e-5e8a-88cf-d4d922961187" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json b/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json index 8c0b0321..fbb18fe3 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json +++ b/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:51" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:50" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:q_Tx9qoh1QsMf_QtcyEXDvej3MG40dckUnPrtKrXVJg" + "value": "v0:fixture-key:1:JbH3fA_A04xTUndXZKXKxlEF8b9as0R0q34_o1kBSW8" } } ], - "fullUrl": "urn:uuid:b27eb87d-b169-54f7-bf36-08a7bef04a65", + "fullUrl": "urn:uuid:9916390c-d9ec-5e86-ac4b-9545f65ccfd9", "resource": { "resourceType": "DocumentReference", "meta": { @@ -60,7 +60,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:ds91lAVuNGWeEI1UGr6VCMVH8PUszhnZV8nHUtrWeKs", + "value": "v0:fixture-key:1:fnfIzdy0ubGE-azcaxeBYJW6Yr9_ICD_fqPfjI39XfI", "type": { "coding": [ { @@ -72,7 +72,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:q_Tx9qoh1QsMf_QtcyEXDvej3MG40dckUnPrtKrXVJg", + "value": "v0:fixture-key:1:JbH3fA_A04xTUndXZKXKxlEF8b9as0R0q34_o1kBSW8", "type": { "coding": [ { @@ -84,7 +84,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", - "value": "v2:fixture-key:1:B2LqptJ5yQlG2j9eikA5NC0q7v_mnoEGTqlpOsRJS_4", + "value": "v0:fixture-key:1:4h_4O6TrHzwSL8GWzqd-_Ppl0AzDUP_M3zY0IvE4lTI", "type": { "coding": [ { @@ -106,12 +106,12 @@ }, "date": "2026-08-20T12:01:00Z", "author": [ - { "reference": "urn:uuid:3683e54f-a0b6-5883-aa38-fd65fea05471" } + { "reference": "urn:uuid:88a3cca3-4f36-52cd-aa6c-e86732e82c06" } ], "content": [ { "attachment": { - "contentType": "application/vnd.grovealliance.provider+json", + "contentType": "application/json", "data": "eyJzeW50aGV0aWMiOnRydWV9", "size": 18, "hash": "SHXPL1bWXRn+jEpR9Aw2zmx15qs=", @@ -120,8 +120,7 @@ "format": { "system": "https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format", "code": "provider-recording", - "display": "Provider Recording", - "version": "0.6.0" + "display": "Provider Recording" } } ] @@ -141,11 +140,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:9SAOPDcfMDEycjmgjHuzq3AwaXpGgcWTUfzvcka1clU" + "value": "v0:fixture-key:1:eKyVTfyrcOiy_bu_yX0GsJm0XT946MmjfoXJzsX_wKA" } } ], - "fullUrl": "urn:uuid:23210e76-49c2-5503-8a4e-f5a6ce47f389", + "fullUrl": "urn:uuid:aad76c82-3ffe-527e-a679-7ac0da57d307", "resource": { "resourceType": "Device", "meta": { @@ -156,7 +155,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:9SAOPDcfMDEycjmgjHuzq3AwaXpGgcWTUfzvcka1clU", + "value": "v0:fixture-key:1:eKyVTfyrcOiy_bu_yX0GsJm0XT946MmjfoXJzsX_wKA", "type": { "coding": [ { @@ -190,11 +189,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:VQ3pqzf0ae4VagpWXQAO6_pSrmE6qUIJIK5gRgKEiz4" + "value": "v0:fixture-key:1:jmlPta-p2yy0ez5EdOqwg6Z48EenmJ7OxaNRx7O22QM" } } ], - "fullUrl": "urn:uuid:3683e54f-a0b6-5883-aa38-fd65fea05471", + "fullUrl": "urn:uuid:88a3cca3-4f36-52cd-aa6c-e86732e82c06", "resource": { "resourceType": "Device", "meta": { @@ -205,7 +204,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:VQ3pqzf0ae4VagpWXQAO6_pSrmE6qUIJIK5gRgKEiz4", + "value": "v0:fixture-key:1:jmlPta-p2yy0ez5EdOqwg6Z48EenmJ7OxaNRx7O22QM", "type": { "coding": [ { @@ -253,11 +252,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:YSj0Ah1T_5LZPm78bAPuwtt5bd86Bm6dY9NL7gkw_QI" + "value": "n0:conversion-provenance:0:NvVaCec-6ykB4NgfVZEUFzS47_HJq6IwVJJhMurnKeg" } } ], - "fullUrl": "urn:uuid:f4706ee9-b5ed-577e-9035-9adaab428906", + "fullUrl": "urn:uuid:14d60b3d-587c-5830-9ed8-30f35e291bdb", "resource": { "resourceType": "Provenance", "meta": { @@ -266,7 +265,7 @@ ] }, "target": [ - { "reference": "urn:uuid:b27eb87d-b169-54f7-bf36-08a7bef04a65" } + { "reference": "urn:uuid:9916390c-d9ec-5e86-ac4b-9545f65ccfd9" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -293,7 +292,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:3683e54f-a0b6-5883-aa38-fd65fea05471" + "reference": "urn:uuid:88a3cca3-4f36-52cd-aa6c-e86732e82c06" } } ], @@ -303,7 +302,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:ds91lAVuNGWeEI1UGr6VCMVH8PUszhnZV8nHUtrWeKs", + "value": "v0:fixture-key:1:fnfIzdy0ubGE-azcaxeBYJW6Yr9_ICD_fqPfjI39XfI", "type": { "coding": [ { @@ -327,7 +326,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:23210e76-49c2-5503-8a4e-f5a6ce47f389" + "reference": "urn:uuid:aad76c82-3ffe-527e-a679-7ac0da57d307" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json b/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json index 69712a7a..f24a5474 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json +++ b/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:52" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:51" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:SIl4FZ2kZAPZ15QYM9VnCgBCfK3yg6bWqkwIQYNo9gw" + "value": "v0:fixture-key:1:B3PLQy1r2W61RW1kB3OKSWh3mMpcVj0paRHWyND2O2g" } } ], - "fullUrl": "urn:uuid:8c0d21d1-440a-573c-9d9c-b6e05601198c", + "fullUrl": "urn:uuid:accf8995-d7a1-57d6-b653-61a258ec2e1b", "resource": { "resourceType": "DocumentReference", "meta": { @@ -60,7 +60,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:4im449uv3-Tg03jydL0lwfnVFYQc_2LP-j7mXBCkGzI", + "value": "v0:fixture-key:1:lAjnPJ63IR1m-sgVedR7vawwKb7tDYLxzjcpGJFdH8M", "type": { "coding": [ { @@ -72,7 +72,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:SIl4FZ2kZAPZ15QYM9VnCgBCfK3yg6bWqkwIQYNo9gw", + "value": "v0:fixture-key:1:B3PLQy1r2W61RW1kB3OKSWh3mMpcVj0paRHWyND2O2g", "type": { "coding": [ { @@ -84,7 +84,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", - "value": "v2:fixture-key:1:KWEc4JvtiEpZ4-q2O09yqsca7DLECOcIObGtz8E1P-o", + "value": "v0:fixture-key:1:Ngl8REcCCf8c48ftm1b4wa0XNyWjSQa1i0UKiTHaV4s", "type": { "coding": [ { @@ -106,12 +106,12 @@ }, "date": "2026-08-20T12:01:00Z", "author": [ - { "reference": "urn:uuid:c832b2d5-de29-5fa4-90b4-00d14a429d35" } + { "reference": "urn:uuid:21a9b5a2-58d8-5dd2-b01b-838b8776909a" } ], "content": [ { "attachment": { - "contentType": "application/vnd.grovealliance.provider+json", + "contentType": "application/json", "data": "eyJzeW50aGV0aWMiOnRydWV9", "size": 18, "hash": "SHXPL1bWXRn+jEpR9Aw2zmx15qs=", @@ -120,8 +120,7 @@ "format": { "system": "https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format", "code": "provider-recording", - "display": "Provider Recording", - "version": "0.6.0" + "display": "Provider Recording" } } ] @@ -141,11 +140,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:eBOHrN0OUNB6VbrRGeaTzLMa24c22LK7ToMSYhVjGqM" + "value": "v0:fixture-key:1:yzDY-vpFTbwMkAWgPEJ4D7kUHMcsuPrWGrEXS0KMlSM" } } ], - "fullUrl": "urn:uuid:129d84e9-fbb5-5d6e-87b3-eba8cace7863", + "fullUrl": "urn:uuid:89ab20ae-a2f0-574a-8f41-4c690cebda0f", "resource": { "resourceType": "Device", "meta": { @@ -156,7 +155,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:eBOHrN0OUNB6VbrRGeaTzLMa24c22LK7ToMSYhVjGqM", + "value": "v0:fixture-key:1:yzDY-vpFTbwMkAWgPEJ4D7kUHMcsuPrWGrEXS0KMlSM", "type": { "coding": [ { @@ -190,11 +189,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:6OlNGWgUEqrs5upUH80PMNkuOiB3FN-Giy1WxsvxwPE" + "value": "v0:fixture-key:1:x-LlatjeBgMfWLnLO419O1s_lhhmbSk6YiYcXeVU7NA" } } ], - "fullUrl": "urn:uuid:c832b2d5-de29-5fa4-90b4-00d14a429d35", + "fullUrl": "urn:uuid:21a9b5a2-58d8-5dd2-b01b-838b8776909a", "resource": { "resourceType": "Device", "meta": { @@ -205,7 +204,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:6OlNGWgUEqrs5upUH80PMNkuOiB3FN-Giy1WxsvxwPE", + "value": "v0:fixture-key:1:x-LlatjeBgMfWLnLO419O1s_lhhmbSk6YiYcXeVU7NA", "type": { "coding": [ { @@ -253,11 +252,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:4jm6yHHA_sU8uDIbkGAE2w5gRxpDPdYsqZuivs-I9XE" + "value": "n0:conversion-provenance:0:gIphHW-fnGuULq9DANuncEdHEFazUS1Lr6XZYSsvakE" } } ], - "fullUrl": "urn:uuid:dc289d38-6058-5c14-92b2-da6f4a0bca22", + "fullUrl": "urn:uuid:b21403d9-6715-5be8-ab04-061e3a0e0e19", "resource": { "resourceType": "Provenance", "meta": { @@ -266,7 +265,7 @@ ] }, "target": [ - { "reference": "urn:uuid:8c0d21d1-440a-573c-9d9c-b6e05601198c" } + { "reference": "urn:uuid:accf8995-d7a1-57d6-b653-61a258ec2e1b" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -293,7 +292,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:c832b2d5-de29-5fa4-90b4-00d14a429d35" + "reference": "urn:uuid:21a9b5a2-58d8-5dd2-b01b-838b8776909a" } } ], @@ -303,7 +302,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:4im449uv3-Tg03jydL0lwfnVFYQc_2LP-j7mXBCkGzI", + "value": "v0:fixture-key:1:lAjnPJ63IR1m-sgVedR7vawwKb7tDYLxzjcpGJFdH8M", "type": { "coding": [ { @@ -327,7 +326,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:129d84e9-fbb5-5d6e-87b3-eba8cace7863" + "reference": "urn:uuid:89ab20ae-a2f0-574a-8f41-4c690cebda0f" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json b/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json index a3466122..88776dc8 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json +++ b/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json @@ -15,7 +15,7 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/event", - "value": "e2:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:53" + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:52" }, "type": "collection", "timestamp": "2026-08-20T12:03:00Z", @@ -34,11 +34,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:J-y_R7It9boHzh5qac_KBiUMKZuXf6PRmrIVhdaKZzg" + "value": "v0:fixture-key:1:Uh2ZvHcTyaxH3aO6RElukrQWXzQxwKED3FMteFYyvoo" } } ], - "fullUrl": "urn:uuid:61a3eeb8-40b8-5948-8d99-5113e83bb9d0", + "fullUrl": "urn:uuid:7cc82885-eaae-52a4-8954-cbf4e42d70fd", "resource": { "resourceType": "DocumentReference", "meta": { @@ -60,7 +60,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:i983RVmugbV7mBvlWBJ1bLXFa9dv7Dz8lOg8Yx9ovgQ", + "value": "v0:fixture-key:1:2X6rXvj9ynbIR3JXJhe8uk0JtT9fSD-OPA16Do4D8yQ", "type": { "coding": [ { @@ -72,7 +72,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v2:fixture-key:1:J-y_R7It9boHzh5qac_KBiUMKZuXf6PRmrIVhdaKZzg", + "value": "v0:fixture-key:1:Uh2ZvHcTyaxH3aO6RElukrQWXzQxwKED3FMteFYyvoo", "type": { "coding": [ { @@ -84,7 +84,7 @@ }, { "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", - "value": "v2:fixture-key:1:MAWqcZLaMAzYRUhy1NsjyKp5XoOBoFmZfwxUey9YJS4", + "value": "v0:fixture-key:1:AimTLWJeR6crk04-8_iSRxDRJmAKM7VKXhdYAUoT9e4", "type": { "coding": [ { @@ -106,12 +106,12 @@ }, "date": "2026-08-20T12:01:00Z", "author": [ - { "reference": "urn:uuid:a313be7b-70d3-5963-a1bb-690c7654d007" } + { "reference": "urn:uuid:bb28b03a-6b47-56ea-b849-487f307e6f4c" } ], "content": [ { "attachment": { - "contentType": "application/vnd.grovealliance.provider+json", + "contentType": "application/json", "data": "eyJzeW50aGV0aWMiOnRydWV9", "size": 18, "hash": "SHXPL1bWXRn+jEpR9Aw2zmx15qs=", @@ -120,8 +120,7 @@ "format": { "system": "https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format", "code": "provider-recording", - "display": "Provider Recording", - "version": "0.6.0" + "display": "Provider Recording" } } ] @@ -141,11 +140,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:Z0ADfd9fY__cR5QlT_hmNkEqiqOoD2UG1BreVu__DkU" + "value": "v0:fixture-key:1:bj-SsK_YMXFNS4md3NAMVI6wrSVd2KQIj-6ZMuY9a-E" } } ], - "fullUrl": "urn:uuid:c760430c-cd6f-53da-b631-f81c20646d74", + "fullUrl": "urn:uuid:b70165c6-669b-51d6-ab8c-837c42455bd4", "resource": { "resourceType": "Device", "meta": { @@ -156,7 +155,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:Z0ADfd9fY__cR5QlT_hmNkEqiqOoD2UG1BreVu__DkU", + "value": "v0:fixture-key:1:bj-SsK_YMXFNS4md3NAMVI6wrSVd2KQIj-6ZMuY9a-E", "type": { "coding": [ { @@ -190,11 +189,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:d3o65QGoz2ek2Z9w-JW7wMVlXk2Qu_xWsMM2-OQ62T0" + "value": "v0:fixture-key:1:Fk4ot8ZFQZFFmgsML91JFuas22bK-FhacPfljbciTgA" } } ], - "fullUrl": "urn:uuid:a313be7b-70d3-5963-a1bb-690c7654d007", + "fullUrl": "urn:uuid:bb28b03a-6b47-56ea-b849-487f307e6f4c", "resource": { "resourceType": "Device", "meta": { @@ -205,7 +204,7 @@ "identifier": [ { "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v2:fixture-key:1:d3o65QGoz2ek2Z9w-JW7wMVlXk2Qu_xWsMM2-OQ62T0", + "value": "v0:fixture-key:1:Fk4ot8ZFQZFFmgsML91JFuas22bK-FhacPfljbciTgA", "type": { "coding": [ { @@ -253,11 +252,11 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n2:conversion-provenance:0:XVxtHKA7DWFrwHFPUWzabP00tnU3q4o8_-qXaCPqJxc" + "value": "n0:conversion-provenance:0:sKOYrb7a0lyQiBSQYAsmbtmbU4tdWkVS3bQsLzWJWZ4" } } ], - "fullUrl": "urn:uuid:7a04c3c6-3267-5250-82c6-5d72a764d86f", + "fullUrl": "urn:uuid:555fb2c9-9210-56b8-a1ef-e6391fc23fcf", "resource": { "resourceType": "Provenance", "meta": { @@ -266,7 +265,7 @@ ] }, "target": [ - { "reference": "urn:uuid:61a3eeb8-40b8-5948-8d99-5113e83bb9d0" } + { "reference": "urn:uuid:7cc82885-eaae-52a4-8954-cbf4e42d70fd" } ], "occurredDateTime": "2026-08-20T12:00:00Z", "recorded": "2026-08-20T12:02:00Z", @@ -293,7 +292,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:a313be7b-70d3-5963-a1bb-690c7654d007" + "reference": "urn:uuid:bb28b03a-6b47-56ea-b849-487f307e6f4c" } } ], @@ -303,7 +302,7 @@ "what": { "identifier": { "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v2:fixture-key:1:i983RVmugbV7mBvlWBJ1bLXFa9dv7Dz8lOg8Yx9ovgQ", + "value": "v0:fixture-key:1:2X6rXvj9ynbIR3JXJhe8uk0JtT9fSD-OPA16Do4D8yQ", "type": { "coding": [ { @@ -327,7 +326,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:c760430c-cd6f-53da-b631-f81c20646d74" + "reference": "urn:uuid:b70165c6-669b-51d6-ab8c-837c42455bd4" } } ] diff --git a/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json b/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json index 25182562..29a31b94 100644 --- a/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json +++ b/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json @@ -18,7 +18,7 @@ }, "attachment": { "kind": "embedded", - "contentType": "application/vnd.grovealliance.provider+json", + "contentType": "application/json", "title": "Authorized minimized Google heart-rate recording", "format": "provider-recording", "payloadAssertion": "caller-authorized-opaque-payload", diff --git a/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json b/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json index abf1c89a..b14d0138 100644 --- a/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json +++ b/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json @@ -18,7 +18,7 @@ }, "attachment": { "kind": "embedded", - "contentType": "application/vnd.grovealliance.provider+json", + "contentType": "application/json", "title": "Sanitized Oura heart-rate recording", "format": "provider-recording", "payloadAssertion": "verified-sanitized-input", diff --git a/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json b/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json index 5478705b..59b98d5e 100644 --- a/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json +++ b/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json @@ -18,7 +18,7 @@ }, "attachment": { "kind": "embedded", - "contentType": "application/vnd.grovealliance.provider+json", + "contentType": "application/json", "title": "Authorized minimized Withings activity recording", "format": "provider-recording", "payloadAssertion": "caller-authorized-opaque-payload", diff --git a/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json b/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json index 6582c777..2c86102d 100644 --- a/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json +++ b/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json @@ -18,7 +18,7 @@ }, "attachment": { "kind": "embedded", - "contentType": "application/vnd.grovealliance.provider+json", + "contentType": "application/json", "title": "Authorized minimized Withings sleep recording", "format": "provider-recording", "payloadAssertion": "caller-authorized-opaque-payload", diff --git a/packages/fhir/grove-fhir.json b/packages/fhir/grove-fhir.json index 51df19fe..9bc075e6 100644 --- a/packages/fhir/grove-fhir.json +++ b/packages/fhir/grove-fhir.json @@ -1,5 +1,5 @@ { "repository": "https://github.com/SchmiedmayerLab/grove-fhir", - "ref": "31197e876161e9c6d207760d472343231da527e2", - "archiveSha256": "1397dbd3daf591697ead3c54db6a18bd076967bf8489955115176e95d5b6cd68" + "ref": "54d977149c038b7487614bbe30fbc8f8ebf541d0", + "archiveSha256": "7acd5d12c076b23c19ec4460b67962b5cf54bbbf2395c85201a6457ae6eefc78" } diff --git a/packages/fhir/scripts/browser-smoke.mjs b/packages/fhir/scripts/browser-smoke.mjs index 5e22cf52..b5ce408a 100644 --- a/packages/fhir/scripts/browser-smoke.mjs +++ b/packages/fhir/scripts/browser-smoke.mjs @@ -224,7 +224,7 @@ try { }, attachment: { kind: 'embedded', - contentType: 'application/vnd.grovealliance.provider+json', + contentType: 'application/json', title: 'Authorized minimized provider recording', format: 'provider-recording', payloadAssertion: 'caller-authorized-opaque-payload', @@ -325,7 +325,7 @@ try { result.recordingGraph !== true || result.retractionGraph !== true || result.rawSourceCount !== 4 || - result.scalarMeasurementCount !== 48 || + result.scalarMeasurementCount !== 47 || result.providerApiVisibleFromMobile || result.providerApiVisibleFromRoot || result.internalGraphVisible || diff --git a/packages/fhir/scripts/generate-conformance-fixtures.mjs b/packages/fhir/scripts/generate-conformance-fixtures.mjs index 73313e65..390f5ea8 100644 --- a/packages/fhir/scripts/generate-conformance-fixtures.mjs +++ b/packages/fhir/scripts/generate-conformance-fixtures.mjs @@ -7,7 +7,7 @@ // import { Buffer } from 'node:buffer' -import { mkdir, readFile, writeFile } from 'node:fs/promises' +import { mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises' import { dirname, resolve } from 'node:path' import { argv, stdout } from 'node:process' import { fileURLToPath } from 'node:url' @@ -501,9 +501,7 @@ const recordingBundle = ({ provider, sourceType }, index) => }, attachment: { kind: 'embedded', - contentType: unwrap( - parseMediaType('application/vnd.grovealliance.provider+json'), - ), + contentType: unwrap(parseMediaType('application/json')), title: 'Authorized minimized provider recording', format: 'provider-recording', payloadAssertion: 'caller-authorized-opaque-payload', @@ -776,7 +774,7 @@ for (const [path, value] of resources) { } const manifest = { - schemaVersion: 1, + schemaVersion: 0, fhirVersion: packageGraph.fhirVersion, producer: { name: 'Grove TypeScript', version: producerVersion }, packages: conformancePackages, @@ -870,6 +868,26 @@ if (invalid.length > 0) { await mkdir(resourceRoot, { recursive: true }) await mkdir(resolve(fixtureRoot, 'mobile-exchange'), { recursive: true }) +for (const directory of ['resources', 'mobile-exchange']) { + const expected = new Set( + [...serialized.keys()] + .filter((relative) => relative.startsWith(`${directory}/`)) + .map((relative) => relative.slice(directory.length + 1)), + ) + const stale = (await readdir(resolve(fixtureRoot, directory))).filter( + (name) => + (name.endsWith('.json') || name.endsWith('.json.license')) && + !expected.has(name), + ) + if (check && stale.length > 0) { + throw new Error( + `Unexpected conformance fixtures: ${stale.map((name) => `${directory}/${name}`).join(', ')}`, + ) + } + await Promise.all( + stale.map((name) => rm(resolve(fixtureRoot, directory, name))), + ) +} for (const [relative, value] of serialized) { const path = resolve(fixtureRoot, relative) if (check) { diff --git a/packages/fhir/scripts/generate-measurement-catalog.mjs b/packages/fhir/scripts/generate-measurement-catalog.mjs index 585a6b24..491be2a2 100644 --- a/packages/fhir/scripts/generate-measurement-catalog.mjs +++ b/packages/fhir/scripts/generate-measurement-catalog.mjs @@ -57,7 +57,7 @@ if (!Array.isArray(measurements) || measurements.length === 0) { const mobileMeasurements = measurements.filter(isMobileOwned) if ( - semanticCorpus.schemaVersion !== 1 || + semanticCorpus.schemaVersion !== 0 || semanticCorpus.fhirVersion !== '4.0.1' || semanticCorpus.version !== catalog.version || !Array.isArray(semanticCorpus.vectors) || @@ -1146,7 +1146,7 @@ const expectedProviderArtifactComponents = [ ] if ( providerIdentity?.contract !== 'catalog/exchange-protocol.json' || - providerIdentity.protocolVersion !== 2 || + providerIdentity.protocolVersion !== 0 || providerIdentity.adapterId !== 'providers' || providerIdentity.sourceRecord?.identityKind !== 'provider-record' || providerIdentity.sourceRecord?.identifierRole !== 'source-record' || @@ -1189,7 +1189,7 @@ if ( formatRegistry.formats?.['fhir-r4-resource']?.contentType !== 'application/fhir+json' || formatRegistry.formats?.['provider-recording']?.contentType !== - 'application/vnd.grovealliance.provider+json' + 'application/json' ) { throw new Error('Recording format registry is incomplete.') } @@ -1286,7 +1286,7 @@ const sourceContextVector = identityVectors?.find( ({ id }) => id === 'healthkit-medication-source-context', ) const opaqueIdentityValue = - /^v2:[A-Za-z0-9._-]+:[1-9][0-9]*:[A-Za-z0-9_-]{43}$/u + /^v0:[A-Za-z0-9._-]+:[1-9][0-9]*:[A-Za-z0-9_-]{43}$/u const retractionTargetRules = exchangeProtocol.lifecycle?.retraction?.targetRoles const retractionTargetEntries = @@ -1357,6 +1357,7 @@ const expectedReferencePaths = [ ['Observation', 'DocumentReference', 'QuestionnaireResponse'], ], ['DocumentReference', 'subject', ['Patient']], + ['QuestionnaireResponse', 'subject', ['Patient']], ['Specimen', 'subject', ['Patient']], ['MedicationAdministration', 'subject', ['Patient']], ['MedicationStatement', 'subject', ['Patient']], @@ -1396,9 +1397,9 @@ const exchangeRuleRows = exchangeCorpus.cases?.map( ({ expectedRule }) => expectedRule, ) if ( - exchangeProtocol.schemaVersion !== 1 || + exchangeProtocol.schemaVersion !== 0 || exchangeProtocol.version !== packageGraph.version || - exchangeProtocol.protocolVersion !== 2 || + exchangeProtocol.protocolVersion !== 0 || exchangeProtocol.releaseVersion !== packageGraph.version || exchangeProtocol.fhirVersion !== '4.0.1' || exchangeProtocol.profiles?.activeBundle !== @@ -1412,9 +1413,9 @@ if ( exchangeProtocol.opaqueIdentity?.algorithm !== 'HMAC-SHA-256' || exchangeProtocol.opaqueIdentity?.keyRequirements?.minimumBytes !== 32 || exchangeProtocol.opaqueIdentity?.domain !== - 'org.grovealliance.fhir.identity.v2' || + 'org.grovealliance.fhir.identity.v0' || exchangeProtocol.entryIdentity?.entryNode?.domain !== - 'org.grovealliance.fhir.entry-node.v2' || + 'org.grovealliance.fhir.entry-node.v0' || exchangeProtocol.entryIdentity?.fullUrl?.namespace !== '43df4575-bff7-5a57-9a80-2472cd2b0623' || !uniqueNonemptyStrings( @@ -1496,9 +1497,9 @@ if ( typeof exchangeProtocol.referencePolicy?.literalClosure !== 'string' || typeof exchangeProtocol.referencePolicy?.declaredType !== 'string' || typeof exchangeProtocol.referencePolicy?.governedShape !== 'string' || - exchangeCorpus.schemaVersion !== 1 || + exchangeCorpus.schemaVersion !== 0 || !Array.isArray(exchangeRuleRows) || - exchangeRuleRows.length !== 31 || + exchangeRuleRows.length !== 34 || Object.keys(exchangeRuleDiagnostics).length !== new Set(exchangeRuleRows.map(({ code }) => code)).size || exchangeRuleRows.some( @@ -1598,7 +1599,7 @@ if ( sourceOutputVector?.identityKind !== 'source-output' || sourceOutputVector.components?.length !== 7 || sourceOutputVector.value !== - 'v2:test-key:1:PQCWz9dZSrJm-KrbhbkckGeowkjhSSwWDRCVuF3VfXw' || + 'v0:test-key:1:MYPFjAMsSt0suOqpN29y_KjG__sagIpCbYKAfVKx6ck' || sourceContextVector?.identityKind !== 'source-context' ) { throw new Error('Exchange protocol catalog is incomplete or inconsistent.') diff --git a/packages/fhir/scripts/package-smoke.mjs b/packages/fhir/scripts/package-smoke.mjs index a67c9fc3..431cdd82 100644 --- a/packages/fhir/scripts/package-smoke.mjs +++ b/packages/fhir/scripts/package-smoke.mjs @@ -81,7 +81,7 @@ for (const name of internalGraphExports) { assert.equal(typeof mobile.canonicalizeMobileEffectiveInstant, 'function') assert.equal(root.groveFhirVersion, '4.0.1') assert.equal(root.groveFhirContractVersion, '0.6.0') -assert.equal(root.groveExchangeProtocol.protocolVersion, 2) +assert.equal(root.groveExchangeProtocol.protocolVersion, 0) assert.equal(root.groveMobileContract.version, '0.6.0') assert.equal( mobile.groveMobilePackageMetadata.packageId, diff --git a/packages/fhir/src/mobile/contract.ts b/packages/fhir/src/mobile/contract.ts index 0384b571..1ce94f7c 100644 --- a/packages/fhir/src/mobile/contract.ts +++ b/packages/fhir/src/mobile/contract.ts @@ -100,7 +100,7 @@ const groveMobileContractValue: GroveMobileProtocolContract = { recordingFormats: groveRecordingFormatRegistry, } -/** Runtime constants projected from the pinned, generated Grove 0.6 protocol catalog. */ +/** Runtime constants projected from the pinned Grove protocol catalog. */ export const groveMobileContract: GroveMobileProtocolContract = deepFreeze( groveMobileContractValue, ) diff --git a/packages/fhir/src/mobile/identity.ts b/packages/fhir/src/mobile/identity.ts index 0dbd5174..b68b2325 100644 --- a/packages/fhir/src/mobile/identity.ts +++ b/packages/fhir/src/mobile/identity.ts @@ -39,10 +39,10 @@ const UNSIGNED_DECIMAL = /^(?:0|[1-9]\d*)$/u const LOWERCASE_ROLE = /^[a-z][a-z\d-]*$/u const CANONICAL_PRODUCER_UUID = /^[\da-f]{8}-[\da-f]{4}-[1-5][\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}$/u -const OPAQUE_VALUE = /^v2:[A-Za-z\d._-]+:[1-9]\d*:[A-Za-z\d_-]{43}$/u +const OPAQUE_VALUE = /^v0:[A-Za-z\d._-]+:[1-9]\d*:[A-Za-z\d_-]{43}$/u const EVENT_VALUE = - /^e2:[\da-f]{8}-[\da-f]{4}-[1-5][\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}:[1-9]\d*$/u -const ENTRY_NODE_VALUE = /^n2:[a-z][a-z\d-]*:(?:0|[1-9]\d*):[A-Za-z\d_-]{43}$/u + /^e0:[\da-f]{8}-[\da-f]{4}-[1-5][\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}:[1-9]\d*$/u +const ENTRY_NODE_VALUE = /^n0:[a-z][a-z\d-]*:(?:0|[1-9]\d*):[A-Za-z\d_-]{43}$/u const BASE64_URL = /^[A-Za-z\d_-]+$/u const MINIMUM_HMAC_KEY_BYTES = 32 @@ -383,7 +383,7 @@ const deriveOpaqueIdentifierInternal = ( ) { return err( 'invalid-code', - 'Identity kind is not part of the closed Grove v2 identity contract.', + 'Identity kind is not part of the closed Grove v0 identity contract.', ['identityKind'], ) } @@ -458,12 +458,12 @@ const deriveOpaqueIdentifierInternal = ( const digest = hmac(sha256, secret, preimage.value) return ok({ system: validated.value.opaqueIdentifierSystems[identityKind], - value: `v2:${validated.value.keyId}:${validated.value.keyEpoch}:${base64UrlWithoutPadding(digest)}`, + value: `v0:${validated.value.keyId}:${validated.value.keyEpoch}:${base64UrlWithoutPadding(digest)}`, role: identifierRoleByKind[identityKind], }) } -/** Derives one deployment-owned, role-typed Grove v2 HMAC Identifier. */ +/** Derives one deployment-owned, role-typed Grove v0 HMAC Identifier. */ export const deriveOpaqueIdentifier = ( deployment: DeploymentIdentityInput, identityKind: Kind, @@ -520,7 +520,7 @@ export const deriveEventIdentifier = ( } return ok({ system: validated.value.eventIdentifierSystem, - value: `e2:${validated.value.producerInstance}:${sequence}`, + value: `e0:${validated.value.producerInstance}:${sequence}`, role: 'event', }) } @@ -567,7 +567,7 @@ export const deriveEntryNodeValue = ( ]) if (!preimage.ok) return preimage return ok( - `n2:${role}:${ordinal}:${base64UrlWithoutPadding(sha256(preimage.value))}`, + `n0:${role}:${ordinal}:${base64UrlWithoutPadding(sha256(preimage.value))}`, ) } @@ -599,7 +599,7 @@ export const deriveEntryNodeIdentifier = ( eventValue.role !== 'event' || typeof eventValue.value !== 'string' || !EVENT_VALUE.test(eventValue.value) || - !eventValue.value.startsWith(`e2:${validated.value.producerInstance}:`) + !eventValue.value.startsWith(`e0:${validated.value.producerInstance}:`) ) { return err( 'invalid-identifier', diff --git a/packages/fhir/src/mobile/measurement-catalog.generated.ts b/packages/fhir/src/mobile/measurement-catalog.generated.ts index 842d3592..98f79fd4 100644 --- a/packages/fhir/src/mobile/measurement-catalog.generated.ts +++ b/packages/fhir/src/mobile/measurement-catalog.generated.ts @@ -51,9 +51,9 @@ export const groveMobileProfileCanonicals: typeof groveMobileProfileCanonicalsVa const groveExchangeProtocolValue = { $schema: 'https://grovealliance.org/fhir/catalog/schemas/exchange-protocol.schema.json', - schemaVersion: 1, + schemaVersion: 0, version: '0.6.0', - protocolVersion: 2, + protocolVersion: 0, releaseVersion: '0.6.0', fhirVersion: '4.0.1', profiles: { @@ -126,7 +126,7 @@ const groveExchangeProtocolValue = { { code: 'mobile-exchange.event-identity', reason: - 'Bundle.identifier.value must be the canonical e2 producer UUID and positive sequence form.', + 'Bundle.identifier.value must be the canonical e0 producer UUID and positive sequence form.', }, { code: 'mobile-exchange.entry-node-digest', @@ -171,7 +171,7 @@ const groveExchangeProtocolValue = { { code: 'mobile-retraction.opaque-target', reason: - 'A retraction target must use the exact canonical v2 HMAC identity previously emitted.', + 'A retraction target must use the exact canonical v0 HMAC identity previously emitted.', }, { code: 'mobile-retraction.no-clinical-copy', @@ -196,12 +196,12 @@ const groveExchangeProtocolValue = { { code: 'mobile-output.quantity-value-domain', reason: - 'Every Quantity-valued catalog measurement stays within its reviewed representational minimum, maximum, and integer-only domain without inventing a physiologic range.', + 'Every Quantity-valued catalog measurement stays within its catalog-declared representational minimum, maximum, and integer-only domain without inventing a physiologic range.', }, { code: 'mobile-exchange.reference-target-type', reason: - 'An Observation subject resolves to a Patient entry, not merely to any existing fullUrl.', + 'Every governed Patient reference resolves to a Patient entry, not merely to any existing fullUrl.', }, { code: 'mobile-exchange.reference-declared-type', @@ -226,12 +226,12 @@ const groveExchangeProtocolValue = { { code: 'mobile-exchange.reference-shape', reason: - 'A governed Reference is exclusively resolving-literal or identifier-only logical, never both.', + 'Each governed path has its declared singular or repeating shape and contains valid Reference objects that are exclusively resolving-literal or identifier-only logical, never both.', }, { code: 'mobile-exchange.logical-patient-reference', reason: - 'An identifier-only logical Patient Reference carries the exact Patient type and one complete absolute-system pseudonym Identifier.', + 'An identifier-only logical Patient Reference carries the exact Patient type and one complete absolute-system pseudonym Identifier without a Grove role or protocol-reserved system.', }, { code: 'mobile-output.adapter-only-profile', @@ -276,7 +276,7 @@ const groveExchangeProtocolValue = { system: 'A deployment-owned absolute URI, stable for the producer instance and never reused for another key space.', valueForm: - 'e2::', + 'e0::', soleEventBusinessIdentifier: true, }, times: { @@ -294,11 +294,11 @@ const groveExchangeProtocolValue = { }, opaqueIdentity: { algorithm: 'HMAC-SHA-256', - domain: 'org.grovealliance.fhir.identity.v2', + domain: 'org.grovealliance.fhir.identity.v0', preimage: "The domain, one identity kind from the closed identityKinds list, and exactly that kind's typed components in declared order. Every typed component is a non-empty Unicode-scalar string; missing, empty, additional, reordered, or non-scalar components are errors. Every field is encoded as an unsigned 32-bit big-endian byte length followed by its exact UTF-8 bytes.", valueForm: - 'v2:::<43-character-base64url-digest-without-padding>', + 'v0:::<43-character-base64url-digest-without-padding>', identifierSystem: 'Deployment-owned and immutable for one identity kind, deployment scope, key id, and key epoch. A rotated key uses a new system and epoch.', keyRequirements: { @@ -664,7 +664,7 @@ const groveExchangeProtocolValue = { ], entryNode: { system: 'A deployment-owned absolute URI for event-scoped graph nodes.', - domain: 'org.grovealliance.fhir.entry-node.v2', + domain: 'org.grovealliance.fhir.entry-node.v0', components: [ 'event-system', 'event-value', @@ -672,7 +672,7 @@ const groveExchangeProtocolValue = { 'zero-based-ordinal', ], valueForm: - 'n2:::<43-character-base64url-sha256-without-padding>', + 'n0:::<43-character-base64url-sha256-without-padding>', digest: 'Unkeyed SHA-256 over the same unsigned 32-bit length-framed UTF-8 encoding. It is a deterministic graph key, not a privacy control.', }, @@ -775,36 +775,53 @@ const groveExchangeProtocolValue = { declaredType: 'When Reference.type is present on a literal reference, it is the exact target resourceType token.', governedShape: - 'Every reference at a governed path is either one resolving Bundle fullUrl with no logical identifier, or one identifier-only logical reference with no literal, an exact allowed Reference.type, and one complete absolute-system Identifier. Identifier-only Patient references carry a deployment-scoped pseudonym and are not fabricated Bundle nodes.', + 'Every reference at a governed path is either one resolving Bundle fullUrl with no logical identifier, or one identifier-only logical reference with no literal, an exact allowed Reference.type, and one complete absolute-system Identifier. Identifier-only Patient references carry the exact deployment-scoped pseudonym pair, use no Grove identifier role or protocol-reserved code-system URI, and are not fabricated Bundle nodes.', + identifierOnlyPatient: { + systemRule: + 'Identifier.system is an absolute pseudonym namespace and is not one of the protocol-reserved code-system URIs. Namespace ownership and deployment scope are producer/deployment obligations; this validator proves only absolute-URI shape and reserved-system exclusion.', + valueRule: + 'Identifier.value is the exact non-empty caller-supplied pseudonym token; producers do not normalize or replace it.', + reservedSystems: [ + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role', + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-lifecycle-event', + 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-retraction-target-role', + ], + }, paths: [ { resourceType: 'Observation', path: 'subject', + repeating: false, targetTypes: ['Patient'], }, { resourceType: 'Observation', path: 'device', + repeating: false, targetTypes: ['Device'], }, { resourceType: 'Observation', path: 'specimen', + repeating: false, targetTypes: ['Specimen'], }, { resourceType: 'Observation', path: 'focus', + repeating: true, targetTypes: ['Location'], }, { resourceType: 'Observation', path: 'hasMember', + repeating: true, targetTypes: ['Observation'], }, { resourceType: 'Observation', path: 'derivedFrom', + repeating: true, targetTypes: [ 'Observation', 'DocumentReference', @@ -814,46 +831,61 @@ const groveExchangeProtocolValue = { { resourceType: 'DocumentReference', path: 'subject', + repeating: false, + targetTypes: ['Patient'], + }, + { + resourceType: 'QuestionnaireResponse', + path: 'subject', + repeating: false, targetTypes: ['Patient'], }, { resourceType: 'Specimen', path: 'subject', + repeating: false, targetTypes: ['Patient'], }, { resourceType: 'MedicationAdministration', path: 'subject', + repeating: false, targetTypes: ['Patient'], }, { resourceType: 'MedicationStatement', path: 'subject', + repeating: false, targetTypes: ['Patient'], }, { resourceType: 'VisionPrescription', path: 'patient', + repeating: false, targetTypes: ['Patient'], }, { resourceType: 'ResearchSubject', path: 'individual', + repeating: false, targetTypes: ['Patient'], }, { resourceType: 'ResearchSubject', path: 'study', + repeating: false, targetTypes: ['ResearchStudy'], }, { resourceType: 'ResearchStudy', path: 'protocol', + repeating: true, targetTypes: ['PlanDefinition'], }, { resourceType: 'Device', path: 'parent', + repeating: false, targetTypes: ['Device'], }, ], @@ -871,8 +903,7 @@ const groveExchangeProtocolValue = { outputModes: [ { code: 'semantic-only', - meaning: - 'A reviewed FHIR semantic projection is the complete admitted output.', + meaning: 'A FHIR semantic projection is the complete admitted output.', }, { code: 'source-only', @@ -928,52 +959,52 @@ const groveExchangeProtocolValue = { { identityKind: 'source-record', system: - 'https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1', }, { identityKind: 'source-output', system: - 'https://study.example.org/fhir/NamingSystem/grove-source-output-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1', }, { identityKind: 'writer-record', system: - 'https://study.example.org/fhir/NamingSystem/grove-writer-record-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1', }, { identityKind: 'provider-record', system: - 'https://study.example.org/fhir/NamingSystem/grove-provider-record-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-provider-record-v0/test-key/1', }, { identityKind: 'provider-output', system: - 'https://study.example.org/fhir/NamingSystem/grove-provider-output-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-provider-output-v0/test-key/1', }, { identityKind: 'source-artifact', system: - 'https://study.example.org/fhir/NamingSystem/grove-source-artifact-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-source-artifact-v0/test-key/1', }, { identityKind: 'provider-artifact', system: - 'https://study.example.org/fhir/NamingSystem/grove-provider-artifact-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-provider-artifact-v0/test-key/1', }, { identityKind: 'source-context', system: - 'https://study.example.org/fhir/NamingSystem/grove-source-context-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-source-context-v0/test-key/1', }, { identityKind: 'recording-device', system: - 'https://study.example.org/fhir/NamingSystem/grove-recording-device-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-recording-device-v0/test-key/1', }, { identityKind: 'device-snapshot', system: - 'https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1', }, ], measurementExamples: { @@ -1015,7 +1046,7 @@ const groveExchangeProtocolValue = { 'default', 'record|東京', ], - value: 'v2:test-key:1:UKY2qgzSB8--SueGxEfOhpElzHTVJ6usIUWV_KUTD6o', + value: 'v0:test-key:1:BDCkwCFA2Wg4-fHVRsy4L0JWYuvQknZkCTXL4Ct01IQ', }, { id: 'multi-output-sample', @@ -1029,7 +1060,7 @@ const groveExchangeProtocolValue = { 'sample', '2026-08-19T10:30:00.000000000Z|0', ], - value: 'v2:test-key:1:PQCWz9dZSrJm-KrbhbkckGeowkjhSSwWDRCVuF3VfXw', + value: 'v0:test-key:1:MYPFjAMsSt0suOqpN29y_KjG__sagIpCbYKAfVKx6ck', }, { id: 'provider-account-complete-pair', @@ -1041,7 +1072,7 @@ const groveExchangeProtocolValue = { 'patient|α', '17348211', ], - value: 'v2:test-key:1:p3NFdQ-hmHon98JG7cmCbLncbAmNkjkBa5sYocSr6pw', + value: 'v0:test-key:1:qfmx1ajnVg_rNE8qiL_hIAWI8GtbVpRtf6T2RWREezM', }, { id: 'provider-output-domain-separated', @@ -1055,7 +1086,7 @@ const groveExchangeProtocolValue = { 'blood-pressure-panel', 'single', ], - value: 'v2:test-key:1:HjBwHRt0W3-CbhJbc7hGpWRp92zug70gt5m626T4Y2U', + value: 'v0:test-key:1:mum-FTSsu6Kv_QBFNSjcFuznTp5C6D3QnrQ6iwm23Ec', }, { id: 'provider-artifact-domain-separated', @@ -1069,7 +1100,7 @@ const groveExchangeProtocolValue = { 'provider-recording', '0', ], - value: 'v2:test-key:1:CxLpZ4NQee12xCyJCGimrxMLEKvbRt54Kl6RTh9UsrU', + value: 'v0:test-key:1:0LxvhsWmRngwxPPMqWk7--4qTN8Wa1j-oTbGuMCCgrA', }, { id: 'generic-source-artifact', @@ -1083,7 +1114,7 @@ const groveExchangeProtocolValue = { 'native-recording', '0', ], - value: 'v2:test-key:1:7QkJwJojYb7w6BlKMy3TyoX-sgiPzjGJHL1euY-4SKk', + value: 'v0:test-key:1:iN5W23PWv_0jfI3RuCiO__c--cElAPDllZ4Gl_3CUPM', }, { id: 'writer-record-complete-application-pair', @@ -1093,7 +1124,7 @@ const groveExchangeProtocolValue = { 'com.withings.wiscale2', 'logical-record-001', ], - value: 'v2:test-key:1:N4QSlWU6sNp9ahfyfSRTUO0K_VIZZoy-Lw3JTNrDzP4', + value: 'v0:test-key:1:DvVpDnGZfj28tqpozMhpZSHjT2J65wvN_bmTYu0x0Cw', }, { id: 'healthkit-medication-source-context', @@ -1105,7 +1136,7 @@ const groveExchangeProtocolValue = { 'default', '2f8a51c6-9d34-4e07-b2f1-63c8ad905e12', ], - value: 'v2:test-key:1:nq3ZogmXHSznC1LC1wNMm7KTQChgapPzmjmGeB9RHcw', + value: 'v0:test-key:1:YTeSyVMN8VDKaIp7rLq6h9HfDIypiKcwO-nFqCJA8Dk', }, { id: 'recording-device-per-unit', @@ -1116,18 +1147,18 @@ const groveExchangeProtocolValue = { 'participant-001', 'watch-unit-token-001', ], - value: 'v2:test-key:1:MWGV3Vfk0jfLIr0nowr_I7TAwoqGtpSkxUi1d8FxTnE', + value: 'v0:test-key:1:BZfymBSOLQlRaEwkvjBNFKbiTvUoNrz8CJEgYlztkus', }, { id: 'device-snapshot-per-event', identityKind: 'device-snapshot', components: [ - 'https://study.example.org/fhir/NamingSystem/grove-event-v2', - 'e2:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42', + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42', 'application', 'com.example.app|1.2.3', ], - value: 'v2:test-key:1:TUaZacIuSMAFl_WbhxvMBX4AgxIKcHw_KvbntQdXYxQ', + value: 'v0:test-key:1:Q2sME_dGFj94xyprI1HtMokER94GYsHINZi0ilnsnRY', }, ], invalidIdentities: [ @@ -1185,18 +1216,18 @@ const groveExchangeProtocolValue = { }, ], event: { - system: 'https://study.example.org/fhir/NamingSystem/grove-event-v2', + system: 'https://study.example.org/fhir/NamingSystem/grove-event-v0', producerInstance: '1f5c58aa-6ec6-4e79-a682-829a9debd3f5', sequence: '42', - value: 'e2:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42', + value: 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42', }, entryNode: { - system: 'https://study.example.org/fhir/NamingSystem/grove-entry-node-v2', + system: 'https://study.example.org/fhir/NamingSystem/grove-entry-node-v0', role: 'conversion-provenance', ordinal: '0', value: - 'n2:conversion-provenance:0:SwGD7C4DT5_9kgIOQ9h7W8I4UdwJPuEOnkh2TgQVwko', - fullUrl: 'urn:uuid:9908feb7-0370-5f06-a689-f8afa210eb41', + 'n0:conversion-provenance:0:8JmcQF7rmULm9uJBkHWruJwfMu3GJTxGWqXWn2DGqWk', + fullUrl: 'urn:uuid:71abc484-b9ee-511e-b22a-5b35d026d620', }, fullUrls: [ { @@ -1235,7 +1266,7 @@ const groveExchangeRuleDiagnosticsValue = { }, 'mobile-exchange.event-identity': { reason: - 'Bundle.identifier.value must be the canonical e2 producer UUID and positive sequence form.', + 'Bundle.identifier.value must be the canonical e0 producer UUID and positive sequence form.', severity: 'error', }, 'mobile-exchange.entry-node-digest': { @@ -1270,7 +1301,7 @@ const groveExchangeRuleDiagnosticsValue = { }, 'mobile-retraction.opaque-target': { reason: - 'A retraction target must use the exact canonical v2 HMAC identity previously emitted.', + 'A retraction target must use the exact canonical v0 HMAC identity previously emitted.', severity: 'error', }, 'mobile-retraction.no-clinical-copy': { @@ -1290,7 +1321,7 @@ const groveExchangeRuleDiagnosticsValue = { }, 'mobile-exchange.reference-target-type': { reason: - 'An Observation subject resolves to a Patient entry, not merely to any existing fullUrl.', + 'Every governed Patient reference resolves to a Patient entry, not merely to any existing fullUrl.', severity: 'error', }, 'mobile-exchange.reference-declared-type': { @@ -1315,12 +1346,12 @@ const groveExchangeRuleDiagnosticsValue = { }, 'mobile-exchange.reference-shape': { reason: - 'A governed Reference is exclusively resolving-literal or identifier-only logical, never both.', + 'Each governed path has its declared singular or repeating shape and contains valid Reference objects that are exclusively resolving-literal or identifier-only logical, never both.', severity: 'error', }, 'mobile-exchange.logical-patient-reference': { reason: - 'An identifier-only logical Patient Reference carries the exact Patient type and one complete absolute-system pseudonym Identifier.', + 'An identifier-only logical Patient Reference carries the exact Patient type and one complete absolute-system pseudonym Identifier without a Grove role or protocol-reserved system.', severity: 'error', }, 'mobile-exchange.entry-resource-type': { @@ -1366,7 +1397,7 @@ export const groveExchangeRuleDiagnostics: typeof groveExchangeRuleDiagnosticsVa const groveProfileClaimsValue = { $schema: 'https://grovealliance.org/fhir/catalog/schemas/catalog-contracts.schema.json', - schemaVersion: 1, + schemaVersion: 0, fhirVersion: '4.0.1', version: '0.6.0', observationAdapterClaim: { @@ -2303,17 +2334,17 @@ const groveRecordingFormatRegistryValue = { }, 'native-recording': { title: 'Native Recording', - contentType: 'application/vnd.grovealliance.native+json', + contentType: 'application/json', status: 'active', }, 'provider-recording': { title: 'Provider Recording', - contentType: 'application/vnd.grovealliance.provider+json', + contentType: 'application/json', status: 'active', }, 'photoplethysmogram-samples': { title: 'Photoplethysmogram Samples', - contentType: 'application/vnd.grovealliance.ppg', + contentType: 'application/octet-stream', status: 'active', }, }, @@ -2385,7 +2416,7 @@ const sharedMobileMeasurementCatalogValue = { standardProfile: null, title: 'Basal Energy Burned', description: - 'Resting (basal) energy expenditure accrued during an exact effective Period, normalized to UCUM kilocalories. It is disjoint from active-energy: the two measurements never overlap in meaning and are summed only by consumers.', + 'Resting (basal) energy expenditure accrued during an exact effective Period, normalized to UCUM kilocalories. It is disjoint from active-energy and may be combined with it only through an explicit derived-total calculation.', code: { system: 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', @@ -2786,7 +2817,7 @@ const sharedMobileMeasurementCatalogValue = { standardProfile: null, title: 'Deep Sleep Duration', description: - 'Total time classified as deep sleep within one sleep session or civil-day summary, normalized to UCUM minutes over the exact session Period. It is a stage-total aggregate without per-stage intervals, distinct from the interval sleep-stage Observation, and is implemented by the phase-2 aggregate design.', + 'Total time classified as deep sleep within one sleep session or civil-day summary, normalized to UCUM minutes over the exact session Period. It is a stage-total aggregate without per-stage intervals and is distinct from the interval sleep-stage Observation.', code: { system: 'http://loinc.org', code: '93831-6', @@ -4045,7 +4076,7 @@ const sharedMobileMeasurementCatalogValue = { standardProfile: null, title: 'Lean Body Mass', description: - 'A source-neutral lean body mass normalized to UCUM kilograms. Consumer bioimpedance sources report fat-free mass under this label; the lean-body-mass versus fat-free-mass distinction (essential fat) is not recoverable from any source, so Withings fat-free mass joins this measurement with that caveat rather than forming a separate concept.', + 'A source-neutral lean body mass normalized to UCUM kilograms. Consumer bioimpedance devices report fat-free mass under this label; the lean-body-mass versus fat-free-mass distinction (essential fat) is not recoverable from any source, so Withings fat-free mass joins this measurement with that caveat rather than forming a separate concept.', code: { system: 'http://loinc.org', code: '91557-9', @@ -4069,7 +4100,7 @@ const sharedMobileMeasurementCatalogValue = { standardProfile: null, title: 'Light Sleep Duration', description: - 'Total time classified as light sleep within one sleep session or civil-day summary, normalized to UCUM minutes over the exact session Period. It is a stage-total aggregate without per-stage intervals and is implemented by the phase-2 aggregate design.', + 'Total time classified as light sleep within one sleep session or civil-day summary, normalized to UCUM minutes over the exact session Period. It is a stage-total aggregate without per-stage intervals and is distinct from the interval sleep-stage Observation.', code: { system: 'http://loinc.org', code: '93830-8', @@ -4292,7 +4323,7 @@ const sharedMobileMeasurementCatalogValue = { standardProfile: null, title: 'Oxygen Saturation Daily Average', description: - 'The mean oxygen saturation over a civil-date effective Period, normalized to UCUM percent. It is distinct from the point-in-time shared oxygen-saturation measurement and is implemented by the phase-2 aggregate design.', + 'The mean oxygen saturation over a civil-date effective Period, normalized to UCUM percent. The exact aggregation window is carried by effectivePeriod, and the result is distinct from the point-in-time shared oxygen-saturation measurement.', code: { system: 'http://loinc.org', code: '103209-3', @@ -4358,7 +4389,7 @@ const sharedMobileMeasurementCatalogValue = { standardProfile: null, title: 'REM Sleep Duration', description: - 'Total time classified as REM sleep within one sleep session or civil-day summary, normalized to UCUM minutes over the exact session Period. It is a stage-total aggregate without per-stage intervals and is implemented by the phase-2 aggregate design.', + 'Total time classified as REM sleep within one sleep session or civil-day summary, normalized to UCUM minutes over the exact session Period. It is a stage-total aggregate without per-stage intervals and is distinct from the interval sleep-stage Observation.', code: { system: 'http://loinc.org', code: '93829-0', @@ -4414,7 +4445,7 @@ const sharedMobileMeasurementCatalogValue = { standardProfile: null, title: 'Respiratory Rate Average', description: - 'The mean respiratory rate over a windowed effective Period, normalized to UCUM breaths per minute; current source windows are a civil date (Google) and a sleep session (Oura, Withings), and the window is carried entirely by the effectivePeriod. It is distinct from the point-in-time shared respiratory-rate measurement and is implemented by the phase-2 aggregate design.', + 'The mean respiratory rate over a windowed effective Period, normalized to UCUM breaths per minute. Admitted source windows are a civil date (Google Health API) and a sleep session (Oura and Withings), and the exact window is carried by effectivePeriod. The result is distinct from the point-in-time shared respiratory-rate measurement.', code: { system: 'http://loinc.org', code: '103217-6', @@ -4541,7 +4572,7 @@ const sharedMobileMeasurementCatalogValue = { standardProfile: null, title: 'Awake Duration During Sleep', description: - 'Total time awake within one sleep session or civil-day sleep summary, normalized to UCUM minutes over the exact session Period. It is a stage-total aggregate without per-stage intervals and is implemented by the phase-2 aggregate design.', + 'Total time awake within one sleep session or civil-day sleep summary, normalized to UCUM minutes over the exact session Period. It is a stage-total aggregate without per-stage intervals and is distinct from the interval sleep-stage Observation.', code: { system: 'http://loinc.org', code: '93828-2', @@ -4593,7 +4624,7 @@ const sharedMobileMeasurementCatalogValue = { standardProfile: null, title: 'Sleep Heart Rate', description: - "A heart-rate statistic computed over one sleep session (Oura reports the session minimum, Withings the session mean), normalized to UCUM beats per minute over the exact session Period. The statistic is carried by the aggregate design's fixed method coding, never collapsed into the shared point heart-rate measurement.", + 'A heart-rate statistic computed over one sleep session (Oura reports the session minimum, Withings the session mean), normalized to UCUM beats per minute over the exact session Period. A required method coding distinguishes the statistic, which is never collapsed into the shared point heart-rate measurement.', code: { system: 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-mobile-measurement', diff --git a/packages/fhir/src/mobile/types.ts b/packages/fhir/src/mobile/types.ts index ee483b42..6ca3c98a 100644 --- a/packages/fhir/src/mobile/types.ts +++ b/packages/fhir/src/mobile/types.ts @@ -138,11 +138,11 @@ export interface SleepStageMeasurement { export interface CompleteIdentifierInput { readonly system: AbsoluteUri readonly value: string - /** Grove role carried in Identifier.type for deployment-owned v2 identities. */ + /** Grove role carried in Identifier.type for deployment-owned v0 identities. */ readonly role?: GroveIdentifierRole } -/** Closed roles used to distinguish deployment-owned v2 identifier key spaces. */ +/** Closed roles used to distinguish deployment-owned v0 identifier key spaces. */ export type GroveIdentifierRole = OpaqueIdentityDefinition['identifierRole'] | 'entry-node' | 'event' diff --git a/packages/fhir/src/providers/contract.generated.ts b/packages/fhir/src/providers/contract.generated.ts index 7b636bd2..4dbbc278 100644 --- a/packages/fhir/src/providers/contract.generated.ts +++ b/packages/fhir/src/providers/contract.generated.ts @@ -341,8 +341,6 @@ const groveProviderProfileCanonicalsValue = { 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-sleep-stage', 'healthkit-sleeping-breathing-disturbances': 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-sleeping-breathing-disturbances', - 'withings-sleeping-heart-rate-average': - 'https://grovealliance.org/fhir/withings/StructureDefinition/withings-sleeping-heart-rate-average', 'grove-mobile-speed': 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-speed', 'healthkit-stair-ascent-speed': @@ -521,7 +519,7 @@ export const groveProviderProfileCanonicals: typeof groveProviderProfileCanonica const providerAdapterCatalogValue = { $schema: 'https://grovealliance.org/fhir/catalog/schemas/catalog-contracts.schema.json', - schemaVersion: 1, + schemaVersion: 0, fhirVersion: '4.0.1', version: '0.6.0', packageId: 'org.grovealliance.fhir.providers', @@ -580,30 +578,30 @@ const providerAdapterCatalogValue = { ], statusDefinitions: { supported: - 'Version 0.6.0 defines an exact conversion to every semantic profile listed for the source element, whether the profile is shared or provider-owned.', + 'The Grove FHIR contracts define an exact conversion to every semantic profile listed for the source element, whether the profile is shared or provider-owned.', 'mapped-standard': - 'Version 0.6.0 admits only the listed source-neutral Sensor recording contract and does not assert a scalar clinical result.', + 'The Grove FHIR contracts admit only the listed source-neutral Sensor recording contract and does not assert a scalar clinical result.', 'platform-exclusive': - 'Version 0.6.0 defines the listed provider-scoped structured profile because no exact shared profile exists.', + 'The Grove FHIR contracts define the listed provider-scoped structured profile because no exact shared profile exists.', unmodeled: - 'The source element is inventoried, but no shared or provider-scoped profile models it in version 0.6.0 and no output is admitted.', + 'The source element is inventoried, but no shared or provider-scoped profile models it under the Grove FHIR contracts and no output is admitted.', deferred: - 'A plausible shared mapping is known, but the source evidence, time semantics, or result shape is insufficient for a conformant version 0.6.0 output.', + 'A plausible shared mapping is known, but the source evidence, time semantics, or result shape is insufficient for a conformant output under the Grove FHIR contracts.', 'intentionally-unsupported': - 'Version 0.6.0 deliberately refuses a normalized conversion because doing so would be misleading or diagnostic-adjacent.', + 'The Grove FHIR contracts deliberately refuses a normalized conversion because doing so would be misleading or diagnostic-adjacent.', }, observationIssued: { cardinality: '0..0', source: - 'only an exact provider field explicitly declared by its catalog row; no such field is declared in 0.6.0', - rule: 'Version 0.6.0 declares no authoritative provider row field for result availability or current-version modification, so every Provider Observation omits Observation.issued. Conversion, retrieval, assembly, and upload times are prohibited substitutes.', + 'only an exact provider field explicitly declared by its catalog row; no such field is declared under the Grove FHIR contracts', + rule: 'The Grove FHIR contracts declare no authoritative provider row field for result availability or current-version modification, so every Provider Observation omits Observation.issued. Conversion, retrieval, assembly, and upload times are prohibited substitutes.', }, sourceEvidence: { accessed: '2026-08-20', scope: 'Exact provider data-type tokens and consumed fields frozen in providers[].sourceTypes; conversion operates only on already-obtained payloads and defines no provider fetching.', tokenBinding: - 'Each providers[].sourceTypes[].token and elements[].path is part of the exact v0.6.0 consumed source surface.', + 'Each providers[].sourceTypes[].token and elements[].path is part of the exact consumed source surface under the Grove FHIR contracts.', providers: [ { id: 'google-health-api', @@ -633,7 +631,7 @@ const providerAdapterCatalogValue = { }, identity: { contract: 'catalog/exchange-protocol.json', - protocolVersion: 2, + protocolVersion: 0, adapterId: 'providers', sourceRecord: { identityKind: 'provider-record', @@ -814,10 +812,7 @@ const providerAdapterCatalogValue = { { path: 'heartRateVariability.standardDeviationMilliseconds', status: 'supported', - measurementIds: [ - 'heart-rate-variability-rmssd', - 'heart-rate-variability-sdnn', - ], + measurementIds: ['heart-rate-variability-sdnn'], }, ], }, @@ -1046,7 +1041,7 @@ const providerAdapterCatalogValue = { path: 'average_hrv', status: 'unmodeled', reason: - 'Oura reports this as an RMSSD average over the sleep session, which the shared heart-rate-variability-rmssd measurement represents. Unmodelled only until the session-window effective rule for it is settled.', + 'Oura reports this as an RMSSD average over the sleep session, but this contract does not define the exact session window to use as Observation.effectivePeriod. No FHIR output is admitted.', }, { path: 'average_breath', @@ -1106,7 +1101,7 @@ const providerAdapterCatalogValue = { effective: 'the source civil day represented as a complete day Period; midpoint substitution is forbidden', reason: - "Version 0.6.0 carries the Oura cardiovascular-age figure as the provider-scoped oura-cardiovascular-age profile, on an age scale in UCUM years over the civil-day Period. The profile names the vendor in its code and description, so the figure cannot be mistaken for a chronological age, for a clinical vascular assessment, or for Withings' vascular age, which stays a separate measurement because the two are undisclosed algorithms over different inputs. No comparability between vendors and no diagnosis is asserted.", + "The Grove FHIR contracts carry the Oura cardiovascular-age figure as the provider-scoped oura-cardiovascular-age profile, on an age scale in UCUM years over the civil-day Period. The profile names the vendor in its code and description, so the figure cannot be mistaken for a chronological age, for a clinical vascular assessment, or for Withings' vascular age, which stays a separate measurement because the two are undisclosed algorithms over different inputs. No comparability between vendors and no diagnosis is asserted.", }, ], }, @@ -1126,7 +1121,7 @@ const providerAdapterCatalogValue = { effective: 'the source civil day represented as a complete day Period; midpoint substitution is forbidden', reason: - "Version 0.6.0 carries the Oura readiness figure as the provider-scoped oura-readiness-score profile over the civil-day Period, on the dimensionless UCUM {score} annotation because the vendor publishes no physical unit for it. The code names the vendor and the profile description states the scale, so the figure cannot be read as an observable quantity. Nothing about the composite's inputs, weighting, or comparability across people is asserted, and Oura's daily sleep and activity scores remain outside the inventoried source surface.", + "The Grove FHIR contracts carry the Oura readiness figure as the provider-scoped oura-readiness-score profile over the civil-day Period, on the dimensionless UCUM {score} annotation because the vendor publishes no physical unit for it. The code names the vendor and the profile description states the scale, so the figure cannot be read as an observable quantity. Nothing about the composite's inputs, weighting, or comparability across people is asserted, and Oura's daily sleep and activity scores remain outside the inventoried source surface.", }, ], }, @@ -1310,7 +1305,7 @@ const providerAdapterCatalogValue = { unitConversion: 'provider SI value to UCUM a', effective: 'measure group date', reason: - "Version 0.6.0 carries the Withings vascular-age figure as the provider-scoped withings-vascular-age profile, on an age scale in UCUM years. The profile names the vendor in its code and description, so the figure cannot be mistaken for a chronological age, for a clinical vascular assessment, or for Oura's cardiovascular age, which stays a separate measurement because the two are undisclosed algorithms over different inputs. No comparability between vendors and no diagnosis is asserted.", + "The Grove FHIR contracts carry the Withings vascular-age figure as the provider-scoped withings-vascular-age profile, on an age scale in UCUM years. The profile names the vendor in its code and description, so the figure cannot be mistaken for a chronological age, for a clinical vascular assessment, or for Oura's cardiovascular age, which stays a separate measurement because the two are undisclosed algorithms over different inputs. No comparability between vendors and no diagnosis is asserted.", }, ], }, @@ -1407,10 +1402,8 @@ const providerAdapterCatalogValue = { { path: 'data.hr_average', status: 'supported', - measurementIds: [ - 'sleep-heart-rate', - 'sleeping-heart-rate-average', - ], + measurementIds: ['sleep-heart-rate'], + effective: 'exact sleep-session Period', aggregationMethod: { 'sleep-heart-rate': 'session-mean', }, @@ -1618,7 +1611,7 @@ const providerAdapterCatalogValue = { unitConversion: 'provider SI value to UCUM m/s', effective: 'measure group date', reason: - 'Version 0.6.0 carries the Withings aortic pulse-wave velocity estimate as the provider-scoped withings-pulse-wave-velocity profile in UCUM metres per second. The code names the vendor rather than a shared arterial-stiffness concept, because no second inventoried source evidences it and a scale-derived estimate is not interchangeable with a tonometric measurement taken in clinic. No arterial-stiffness finding and no diagnosis is asserted.', + 'The Grove FHIR contracts carry the Withings aortic pulse-wave velocity estimate as the provider-scoped withings-pulse-wave-velocity profile in UCUM metres per second. The code names the vendor rather than a shared arterial-stiffness concept, because no second inventoried source evidences it and a scale-derived estimate is not interchangeable with a tonometric measurement taken in clinic. No arterial-stiffness finding and no diagnosis is asserted.', }, ], }, @@ -1635,7 +1628,7 @@ const providerAdapterCatalogValue = { measurementIds: ['withings-atrial-fibrillation-notification-ecg'], effective: 'measure group date', reason: - "Version 0.6.0 carries the fact that Withings' electrocardiogram screening algorithm flagged signs of atrial fibrillation, as the provider-scoped withings-atrial-fibrillation-notification-ecg profile with a closed single-code result, on the same basis as the HealthKit irregular-heart-rhythm notification. An Observation is admitted only for the vendor's positive screening classification: Withings publishes no encoding for the numeric measure.value, so a negative or inconclusive classification produces no output. No rhythm finding, atrial-fibrillation burden, or diagnosis is asserted, and the code is taken from the providers code system so no receiver can read it as one.", + "The Grove FHIR contracts carry the fact that Withings' electrocardiogram screening algorithm flagged signs of atrial fibrillation, as the provider-scoped withings-atrial-fibrillation-notification-ecg profile with a closed single-code result, on the same basis as the HealthKit irregular-heart-rhythm notification. An Observation is admitted only for the vendor's positive screening classification: Withings publishes no encoding for the numeric measure.value, so a negative or inconclusive classification produces no output. No rhythm finding, atrial-fibrillation burden, or diagnosis is asserted, and the code is taken from the providers code system so it cannot be interpreted as one.", }, ], }, @@ -1653,7 +1646,7 @@ const providerAdapterCatalogValue = { unitConversion: 'provider SI value to UCUM ms', effective: 'measure group date', reason: - 'Version 0.6.0 carries the QRS duration the Withings algorithm measured from its own electrocardiogram, as the provider-scoped withings-qrs-duration profile under LOINC 8633-0. The profile is provider-scoped because Withings is the only inventoried source that reports the interval as a discrete measure. No rhythm interpretation, conduction finding, or diagnosis is asserted; the electrocardiogram recording remains the rhythm evidence.', + 'The Grove FHIR contracts carry the QRS duration the Withings algorithm measured from its own electrocardiogram, as the provider-scoped withings-qrs-duration profile under LOINC 8633-0. The profile is provider-scoped because Withings is the only inventoried source that reports the interval as a discrete measure. No rhythm interpretation, conduction finding, or diagnosis is asserted; the electrocardiogram recording remains the rhythm evidence.', }, ], }, @@ -1671,7 +1664,7 @@ const providerAdapterCatalogValue = { unitConversion: 'provider SI value to UCUM ms', effective: 'measure group date', reason: - 'Version 0.6.0 carries the PR interval the Withings algorithm measured from its own electrocardiogram, as the provider-scoped withings-pr-interval profile under LOINC 8625-6. The profile is provider-scoped because Withings is the only inventoried source that reports the interval as a discrete measure. No rhythm interpretation, conduction finding, or diagnosis is asserted; the electrocardiogram recording remains the rhythm evidence.', + 'The Grove FHIR contracts carry the PR interval the Withings algorithm measured from its own electrocardiogram, as the provider-scoped withings-pr-interval profile under LOINC 8625-6. The profile is provider-scoped because Withings is the only inventoried source that reports the interval as a discrete measure. No rhythm interpretation, conduction finding, or diagnosis is asserted; the electrocardiogram recording remains the rhythm evidence.', }, ], }, @@ -1689,7 +1682,7 @@ const providerAdapterCatalogValue = { unitConversion: 'provider SI value to UCUM ms', effective: 'measure group date', reason: - 'Version 0.6.0 carries the uncorrected QT interval the Withings algorithm measured from its own electrocardiogram, as the provider-scoped withings-qt-interval profile under LOINC 8634-8. The profile is provider-scoped because Withings is the only inventoried source that reports the interval as a discrete measure. No repolarization finding, rhythm interpretation, or diagnosis is asserted, and the value is never substituted for the rate-corrected interval.', + 'The Grove FHIR contracts carry the uncorrected QT interval the Withings algorithm measured from its own electrocardiogram, as the provider-scoped withings-qt-interval profile under LOINC 8634-8. The profile is provider-scoped because Withings is the only inventoried source that reports the interval as a discrete measure. No repolarization finding, rhythm interpretation, or diagnosis is asserted, and the value is never substituted for the rate-corrected interval.', }, ], }, @@ -1707,7 +1700,7 @@ const providerAdapterCatalogValue = { unitConversion: 'provider SI value to UCUM ms', effective: 'measure group date', reason: - 'Version 0.6.0 carries the rate-corrected QT interval Withings reports, as the provider-scoped withings-corrected-qt-interval profile under LOINC 8636-3. The profile is provider-scoped because Withings is the only inventoried source that reports the correction as a discrete measure and does not publish which correction formula it applied, so the value is deliberately not asserted to be interchangeable with a QTc read from a clinical twelve-lead electrocardiogram. No repolarization finding and no diagnosis is asserted.', + 'The Grove FHIR contracts carry the rate-corrected QT interval Withings reports, as the provider-scoped withings-corrected-qt-interval profile under LOINC 8636-3. The profile is provider-scoped because Withings is the only inventoried source that reports the correction as a discrete measure and does not publish which correction formula it applied, so the value is deliberately not asserted to be interchangeable with a QTc read from a clinical twelve-lead electrocardiogram. No repolarization finding and no diagnosis is asserted.', }, ], }, @@ -1724,7 +1717,7 @@ const providerAdapterCatalogValue = { measurementIds: ['withings-atrial-fibrillation-notification-ppg'], effective: 'measure group date', reason: - "Version 0.6.0 carries the fact that Withings' photoplethysmography screening algorithm flagged signs of atrial fibrillation, as the provider-scoped withings-atrial-fibrillation-notification-ppg profile with a closed single-code result. It is kept separate from the electrocardiogram notification because it screens a different signal. An Observation is admitted only for the vendor's positive screening classification: Withings publishes no encoding for the numeric measure.value, so a negative or inconclusive classification produces no output. No rhythm finding, atrial-fibrillation burden, or diagnosis is asserted.", + "The Grove FHIR contracts carry the fact that Withings' photoplethysmography screening algorithm flagged signs of atrial fibrillation, as the provider-scoped withings-atrial-fibrillation-notification-ppg profile with a closed single-code result. It is kept separate from the electrocardiogram notification because it screens a different signal. An Observation is admitted only for the vendor's positive screening classification: Withings publishes no encoding for the numeric measure.value, so a negative or inconclusive classification produces no output. No rhythm finding, atrial-fibrillation burden, or diagnosis is asserted.", }, ], }, @@ -1743,7 +1736,7 @@ const providerAdapterCatalogValue = { 'unitless provider value to the dimensionless UCUM {score} annotation', effective: 'measure group date', reason: - 'Version 0.6.0 carries the Withings nerve-health figure as the provider-scoped withings-nerve-health-score profile, on the dimensionless UCUM {score} annotation because the vendor publishes no physical unit for it. The code names the vendor and the profile description states the scale. Withings positions the figure as small-fiber-neuropathy screening, so the profile deliberately asserts no neuropathy finding, no nerve-conduction measurement, and no diagnosis.', + 'The Grove FHIR contracts carry the Withings nerve-health figure as the provider-scoped withings-nerve-health-score profile, on the dimensionless UCUM {score} annotation because the vendor publishes no physical unit for it. The code names the vendor and the profile description states the scale. Withings positions the figure as small-fiber-neuropathy screening, so the profile deliberately asserts no neuropathy finding, no nerve-conduction measurement, and no diagnosis.', }, ], }, @@ -1784,7 +1777,7 @@ const providerAdapterCatalogValue = { 'unitless provider value to the dimensionless UCUM {score} annotation', effective: 'measure group date', reason: - "Version 0.6.0 carries the Withings visceral-fat figure as the provider-scoped withings-visceral-fat-index profile, on the dimensionless UCUM {score} annotation because the API returns the value without a unit and it is a rating rather than a mass or an area. The code names the vendor, so the figure cannot be folded into a shared body-composition measurement, and no measurand, no mass, and no comparability with another vendor's visceral-fat figure is asserted.", + "The Grove FHIR contracts carry the Withings visceral-fat figure as the provider-scoped withings-visceral-fat-index profile, on the dimensionless UCUM {score} annotation because the API returns the value without a unit and it is a rating rather than a mass or an area. The code names the vendor, so the figure cannot be folded into a shared body-composition measurement, and no measurand, no mass, and no comparability with another vendor's visceral-fat figure is asserted.", }, ], }, @@ -1838,7 +1831,7 @@ const providerAdapterCatalogValue = { fallback: 'Omit Observation.device when no stable per-unit token exists. Manufacturer, model, payload group, account, and event identity do not prove a physical device instance.', snapshot: - 'When emitted, the Device carries both recording-device and event-scoped device-snapshot v2 identifiers.', + 'When emitted, the Device carries both recording-device and event-scoped device-snapshot v0 identifiers.', }, } as const @@ -1976,7 +1969,7 @@ const adapterMeasurementCatalogValue = { standardProfile: null, title: 'Atrial Fibrillation Burden', description: - 'The estimated percentage of analyzed time showing atrial fibrillation over a multi-day estimation window, normalized to UCUM percent. It is a windowed estimate implemented by the phase-2 aggregate design; the Withings AFib classification scalars remain intentionally unsupported and never join it.', + 'The estimated percentage of analyzed time showing atrial fibrillation over a multi-day estimation window, normalized to UCUM percent. It is a source-supplied windowed estimate whose exact analysis window is carried by effectivePeriod; the Withings AFib classification scalars remain intentionally unsupported and never join it.', code: { system: 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', @@ -2324,7 +2317,7 @@ const adapterMeasurementCatalogValue = { standardProfile: null, title: 'Grove HealthKit Biological Sex', description: - "Biological sex as a coded Observation. LOINC 46098-0 'Sex' is the context-free concept (verified ACTIVE, PROPERTY LP6886-8 Type, SCALE_TYP LP7750-5 Nom). 76689-9 'Sex assigned at birth' was rejected: the HealthKit characteristic states only a sex, never that it is the one assigned at birth, and adopting that code would fabricate a provenance the source does not carry. Value is a Grove-coded concept with the HKBiologicalSex token retained as secondary coding per the sleep-stage absorption pattern. HKBiologicalSex.notSet emits no Observation.", + "Biological sex as a coded Observation using LOINC 46098-0 'Sex'. LOINC 76689-9 'Sex assigned at birth' is not used because the HealthKit characteristic states only sex and does not assert birth-assignment provenance. The value uses a Grove code with the exact HKBiologicalSex token retained as secondary coding; HKBiologicalSex.notSet produces no Observation.", code: { system: 'http://loinc.org', code: '46098-0', @@ -2489,7 +2482,7 @@ const adapterMeasurementCatalogValue = { standardProfile: null, title: 'Blood Alcohol Content', description: - 'Blood alcohol content as the mass-percent concentration figure a breathalyzer or user reports, kept as the plain UCUM percent scalar per the decided contract. It is not converted into a laboratory ethanol mass concentration.', + 'Blood alcohol content as the mass-percent concentration figure a breathalyzer or user reports, represented as a UCUM percent scalar. It is not converted into a laboratory ethanol mass concentration.', code: { system: 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', @@ -2526,7 +2519,7 @@ const adapterMeasurementCatalogValue = { standardProfile: null, title: 'Grove HealthKit Blood Type', description: - "ABO and Rh group as a coded Observation. LOINC 882-1 'ABO and Rh group [Type] in Blood' is the exact concept (re-verified ACTIVE, PROPERTY LP6886-8 Type, SCALE_TYP LP7750-5 Nom). Value is a Grove-coded ABO/Rh concept with the HKBloodType token retained as secondary coding per the sleep-stage absorption pattern.", + "ABO and Rh group as a coded Observation using LOINC 882-1 'ABO and Rh group [Type] in Blood'. The value uses a Grove ABO/Rh code with the exact HKBloodType token retained as secondary coding.", code: { system: 'http://loinc.org', code: '882-1', @@ -2696,7 +2689,7 @@ const adapterMeasurementCatalogValue = { standardProfile: null, title: 'Grove HealthKit Date of Birth', description: - "Date of birth as an Observation. LOINC 21112-8 'Birth date' is the exact concept (verified ACTIVE, PROPERTY LP6882-7 TmStp, SCALE_TYP LP7753-9 Qn). A date of birth identifies a person across systems, so the adapter withholds it unless the deployment authorizes disclosure; a deployment that already knows its participant's demographics from enrollment should prefer that authoritative record over this assertion.", + "Date of birth as an Observation using LOINC 21112-8 'Birth date'. A date of birth identifies a person across systems, so the adapter withholds it unless the deployment authorizes disclosure; a deployment that already knows its participant's demographics from enrollment should prefer that authoritative record over this assertion.", code: { system: 'http://loinc.org', code: '21112-8', @@ -2807,7 +2800,7 @@ const adapterMeasurementCatalogValue = { standardProfile: null, title: 'Grove HealthKit Fitzpatrick Skin Type', description: - "Fitzpatrick sun-reactive skin type as a coded Observation. LOINC 66555-4 'Skin type [Fitzpatrick Classification Scale]' is the exact concept (verified ACTIVE, PROPERTY LP6886-8 Type, SCALE_TYP LP7750-5 Nom). The scale materially conditions UV-exposure interpretation, which is why it is modelled rather than left to the deployment. HKFitzpatrickSkinType.notSet emits no Observation.", + "Fitzpatrick sun-reactive skin type as a coded Observation using LOINC 66555-4 'Skin type [Fitzpatrick Classification Scale]'. The scale materially conditions UV-exposure interpretation, which is why it is modelled rather than left to the deployment. HKFitzpatrickSkinType.notSet produces no Observation.", code: { system: 'http://loinc.org', code: '66555-4', @@ -2940,7 +2933,7 @@ const adapterMeasurementCatalogValue = { standardProfile: null, title: 'Grove HealthKit GAD-7 Score', description: - "HKGAD7Assessment (iOS 18, HKScoredAssessment subclass): score 0-21, answers [HKGAD7Assessment.Answer], risk. Routing recommendation: platform-exclusive score Observation on exact LOINC 70274-6 'Generalized anxiety disorder 7 item (GAD-7) total score [Reported.PHQ]' (re-verified ACTIVE, PROPERTY LP185820-0 Score, SCALE_TYP LP7753-9 Qn). The seven answers are retained as components coded with the LOINC GAD-7 item codes under panel 69737-5 (re-verified), each valued with the standard LOINC answer codes (LA6568-5 'Not at all', LA6569-3 'Several days', LA6570-1 'More than half the days', LA6571-9 'Nearly every day' — all four displays re-verified against LOINC 2.82 via tx.fhir.org) absorbing Answer cases notAtAll, severalDays, moreThanHalfTheDays, nearlyEveryDay; Risk (noneToMinimal, mild, moderate, severe — re-verified) is redundant with the published score bands and is carried only as an Observation.interpretation-style Grove coding, not a value.", + "A HealthKit GAD-7 assessment represented by its 0-21 total score using LOINC 70274-6 'Generalized anxiety disorder 7 item (GAD-7) total score [Reported.PHQ]'. The seven item answers are retained as components using the standard LOINC GAD-7 item and answer codes under panel 69737-5. HealthKit's risk classification is redundant with the score bands and is retained as a Grove interpretation coding rather than a second result value.", code: { system: 'http://loinc.org', code: '70274-6', @@ -3243,7 +3236,7 @@ const adapterMeasurementCatalogValue = { standardProfile: null, title: 'Insulin Delivery', description: - 'The quantity of insulin delivered during the exact effective Period, normalized to UCUM international units, with a REQUIRED delivery-reason component distinguishing basal from bolus delivery per the decided contract. A sample without the HKMetadataKeyInsulinDeliveryReason metadata fails closed.', + 'The quantity of insulin delivered during the exact effective Period, normalized to UCUM international units. A required delivery-reason component distinguishes basal from bolus delivery; a sample without HKMetadataKeyInsulinDeliveryReason fails closed.', code: { system: 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', @@ -3648,7 +3641,7 @@ const adapterMeasurementCatalogValue = { standardProfile: null, title: 'Grove HealthKit PHQ-9 Score', description: - "HKPHQ9Assessment (iOS 18, HKScoredAssessment subclass): score 0-27, answers [HKPHQ9Assessment.Answer] including preferNotToAnswer, risk. Routing recommendation: platform-exclusive score Observation on exact LOINC 44261-6 'Patient Health Questionnaire 9 item (PHQ-9) total score [Reported]' (re-verified ACTIVE, Score, Qn); item answers retained as components under panel 44249-1 'PHQ-9 quick depression assessment panel [Reported.PHQ]' (re-verified) with the standard LA answer codes; preferNotToAnswer maps to a component with dataAbsentReason asked-declined, which is the semantically exact FHIR mechanism; Risk (noneToMinimal, mild, moderate, moderatelySevere, severe — re-verified) carried as interpretation-style Grove coding only.", + "A HealthKit PHQ-9 assessment represented by its 0-27 total score using LOINC 44261-6 'Patient Health Questionnaire 9 item (PHQ-9) total score [Reported]'. Item answers are retained as components under LOINC panel 44249-1 with the standard answer codes; preferNotToAnswer maps to dataAbsentReason asked-declined. HealthKit's risk classification is retained as a Grove interpretation coding rather than a second result value.", code: { system: 'http://loinc.org', code: '44261-6', @@ -3935,7 +3928,7 @@ const adapterMeasurementCatalogValue = { standardProfile: null, title: 'Six-Minute Walk Test Distance', description: - "The distance walked (or estimated walkable) in six minutes, using LOINC Six minute walk test and normalized to UCUM metres. Apple's samples are predominantly rolling-window mobility estimates rather than administered tests, so the phase-2 aggregate design owns the emission contract.", + "The distance walked (or estimated walkable) in six minutes, using LOINC Six minute walk test and normalized to UCUM metres. Apple's samples are predominantly rolling-window mobility estimates rather than administered tests, so effectivePeriod carries the exact measurement window.", code: { system: 'http://loinc.org', code: '64098-7', @@ -5619,7 +5612,7 @@ const adapterMeasurementCatalogValue = { standardProfile: null, title: 'Waist Circumference', description: - 'A HealthKit-exclusive waist circumference normalized to UCUM centimetres. The LOINC code asserts the umbilicus site and tape-measure method that HealthKit does not itself state; this is the established consumer-health interoperability mapping and the caveat is documented rather than inventing a Grove code.', + 'A HealthKit-exclusive waist circumference normalized to UCUM centimetres. The selected LOINC code asserts the umbilicus site and tape-measure method that HealthKit does not itself state; the profile documents that caveat rather than inventing a Grove code.', code: { system: 'http://loinc.org', code: '8280-0', @@ -5712,7 +5705,7 @@ const adapterMeasurementCatalogValue = { standardProfile: null, title: 'Walking Heart Rate Average', description: - 'The average heart rate during walking activity over a daily window, normalized to UCUM beats per minute. HealthKit-only evidence today; the phase-2 aggregate design implements the windowing.', + 'The average heart rate during walking activity over a daily window, normalized to UCUM beats per minute. This is a HealthKit-specific source-supplied aggregate whose exact daily window is carried by effectivePeriod.', code: { system: 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', @@ -5773,7 +5766,7 @@ const adapterMeasurementCatalogValue = { standardProfile: null, title: 'Walking Steadiness', description: - "Apple's windowed walking-steadiness score, a rolling multi-day percentage summarizing gait stability, normalized to UCUM percent over the exact aggregation Period. As a windowed aggregate it is implemented by the phase-2 aggregate design rather than as a point measurement.", + "Apple's windowed walking-steadiness score, a rolling multi-day percentage summarizing gait stability, normalized to UCUM percent over the exact aggregation Period. It is represented as a source-supplied aggregate with an exact effectivePeriod, not as a point measurement.", code: { system: 'https://grovealliance.org/fhir/healthkit/CodeSystem/healthkit-measurement', @@ -6372,7 +6365,7 @@ const adapterMeasurementCatalogValue = { standardProfile: null, title: 'Muscle Mass', description: - 'A providers-exclusive whole-body muscle mass calculated by bioimpedance analysis, normalized to UCUM kilograms. It is distinct from lean body mass (which additionally includes bone, water, and organ mass) and is not folded into that shared measurement.', + 'A Withings-specific whole-body muscle mass calculated by bioimpedance analysis, normalized to UCUM kilograms. It is distinct from lean body mass (which additionally includes bone, water, and organ mass) and is not folded into that shared measurement.', code: { system: 'http://loinc.org', code: '73964-9', @@ -6466,45 +6459,13 @@ const adapterMeasurementCatalogValue = { }, effective: 'dateTime', }, - 'sleeping-heart-rate-average': { - id: 'sleeping-heart-rate-average', - profile: 'withings-sleeping-heart-rate-average', - standardProfile: null, - title: 'Sleeping Heart Rate Average', - description: - 'The average heart rate across one sleep session, normalized to UCUM beats per minute. It is a session-windowed average, distinct from both the shared point heart-rate measurement and the daily resting-heart-rate estimate, and is implemented by the phase-2 aggregate design.', - code: { - system: - 'https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement', - code: 'sleeping-heart-rate-average', - display: 'Sleeping heart rate average', - definition: - 'The mean heart rate across the exact sleep-session Observation effective Period.', - }, - valueKind: 'quantity', - owner: 'withings', - quantity: { - system: 'http://unitsofmeasure.org', - code: '/min', - unit: 'beats/minute', - example: 55, - dimension: { - s: -1, - }, - }, - method: { - code: 'session-mean', - display: 'Session mean', - }, - effective: 'Period', - }, 'withings-atrial-fibrillation-notification-ecg': { id: 'withings-atrial-fibrillation-notification-ecg', profile: 'withings-atrial-fibrillation-notification-ecg', standardProfile: null, title: 'Withings Atrial Fibrillation Notification (ECG)', description: - "Withings meastype 130: a screening notification from the vendor's proprietary electrocardiogram algorithm. It is emitted as a notification and never as a rhythm finding, exactly as the HealthKit irregular-heart-rhythm notification is: the recording itself remains the rhythm evidence. The adapter admits an Observation only for the vendor's positive screening classification; Withings publishes no encoding for the numeric measure.value, so no negative result, no inconclusive result, and no diagnosis is asserted. The code is deliberately taken from the providers code system so no receiver can read it as an atrial-fibrillation finding.", + "Withings meastype 130: a screening notification from the vendor's proprietary electrocardiogram algorithm. It is emitted as a notification and never as a rhythm finding, exactly as the HealthKit irregular-heart-rhythm notification is: the recording itself remains the rhythm evidence. The adapter admits an Observation only for the vendor's positive screening classification; Withings publishes no encoding for the numeric measure.value, so no negative result, no inconclusive result, and no diagnosis is asserted. The code is deliberately taken from the providers code system so it cannot be interpreted as an atrial-fibrillation finding.", code: { system: 'https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement', @@ -6537,7 +6498,7 @@ const adapterMeasurementCatalogValue = { standardProfile: null, title: 'Withings Atrial Fibrillation Notification (PPG)', description: - "Withings meastype 139: a screening notification from the vendor's proprietary photoplethysmography algorithm, kept separate from the electrocardiogram notification because it screens a different signal. It is emitted as a notification and never as a rhythm finding. The adapter admits an Observation only for the vendor's positive screening classification; Withings publishes no encoding for the numeric measure.value, so no negative result, no inconclusive result, and no diagnosis is asserted. The code is deliberately taken from the providers code system so no receiver can read it as an atrial-fibrillation finding.", + "Withings meastype 139: a screening notification from the vendor's proprietary photoplethysmography algorithm, kept separate from the electrocardiogram notification because it screens a different signal. It is emitted as a notification and never as a rhythm finding. The adapter admits an Observation only for the vendor's positive screening classification; Withings publishes no encoding for the numeric measure.value, so no negative result, no inconclusive result, and no diagnosis is asserted. The code is deliberately taken from the providers code system so it cannot be interpreted as an atrial-fibrillation finding.", code: { system: 'https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement', @@ -6953,7 +6914,6 @@ const providerScalarOutputRolesValue = { }, 'getsummary:hr_average': { 'sleep-heart-rate': 'sleep-heart-rate', - 'sleeping-heart-rate-average': 'sleeping-heart-rate-average', }, 'getsummary:rr_average': { 'respiratory-rate-average': 'respiratory-rate-average', @@ -7164,7 +7124,6 @@ const providerScalarOutputDiscriminatorsValue = { }, 'getsummary:hr_average': { 'sleep-heart-rate': 'single', - 'sleeping-heart-rate-average': 'single', }, 'getsummary:rr_average': { 'respiratory-rate-average': 'single', diff --git a/packages/fhir/src/providers/identity.ts b/packages/fhir/src/providers/identity.ts index cf776c85..0b499ff2 100644 --- a/packages/fhir/src/providers/identity.ts +++ b/packages/fhir/src/providers/identity.ts @@ -96,7 +96,7 @@ export const deriveDeviceSnapshotEntryIdentity = ( eventRole !== 'event' || typeof eventValue !== 'string' || !isEventIdentityValue(eventValue) || - !eventValue.startsWith(`e2:${safeDeployment.value.producerInstance}:`) + !eventValue.startsWith(`e0:${safeDeployment.value.producerInstance}:`) ) { return err( 'invalid-identifier', diff --git a/packages/fhir/src/providers/recording.ts b/packages/fhir/src/providers/recording.ts index 90811a27..51d29a43 100644 --- a/packages/fhir/src/providers/recording.ts +++ b/packages/fhir/src/providers/recording.ts @@ -9,7 +9,6 @@ import { sha1 } from '@noble/hashes/legacy.js' import { z } from 'zod' import { - groveFhirContractVersion, groveRecordingFormatRegistry, providerAdapterCatalog, providerRawOutputRoles, @@ -657,17 +656,12 @@ export const buildProviderRecordingBundle = ( content: [ { attachment: attachmentFor(validated.attachment), - format: { - // The release travels in the instance: a receiver reads which registry generation - // defined this code, and the producer contract refuses a version that never did. - ...coding( - groveRecordingFormatRegistry.codeSystem, - validated.attachment.format, - groveRecordingFormatRegistry.formats[validated.attachment.format] - .title, - ), - version: groveFhirContractVersion, - }, + format: coding( + groveRecordingFormatRegistry.codeSystem, + validated.attachment.format, + groveRecordingFormatRegistry.formats[validated.attachment.format] + .title, + ), }, ], } diff --git a/packages/fhir/src/r4/exchange-envelope.ts b/packages/fhir/src/r4/exchange-envelope.ts index daece461..b557876f 100644 --- a/packages/fhir/src/r4/exchange-envelope.ts +++ b/packages/fhir/src/r4/exchange-envelope.ts @@ -215,7 +215,7 @@ const validateEntryKeyValue = ( context, 'mobile-exchange.opaque-entry-key', ['entry', index, 'extension'], - 'Business entry keys require a closed role and canonical v2 HMAC value.', + 'Business entry keys require a closed role and canonical v0 HMAC value.', ) } } @@ -313,7 +313,7 @@ const resourceIdentityRoleCounts = ( context, 'mobile-exchange.opaque-resource-identity', ['entry', index, 'resource', 'identifier'], - 'A typed resource identity must be one complete canonical v2 HMAC Identifier.', + 'A typed resource identity must be one complete canonical v0 HMAC Identifier.', ) } } @@ -556,7 +556,7 @@ export const validateExchangeEnvelope = ( context, 'mobile-exchange.event-identity', ['identifier'], - 'Bundle.identifier must be one complete typed canonical e2 event Identifier.', + 'Bundle.identifier must be one complete typed canonical e0 event Identifier.', ) return undefined } diff --git a/packages/fhir/src/r4/graph-schema-utils.ts b/packages/fhir/src/r4/graph-schema-utils.ts index c975f2b0..4ccdd440 100644 --- a/packages/fhir/src/r4/graph-schema-utils.ts +++ b/packages/fhir/src/r4/graph-schema-utils.ts @@ -37,7 +37,10 @@ export const completeIdentifier = ( const LOWERCASE_UUID_V5 = /^urn:uuid:[\da-f]{8}-[\da-f]{4}-5[\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}$/u -const ENTRY_NODE_PARTS = /^n2:([a-z][a-z\d-]*):(0|[1-9]\d*):[A-Za-z\d_-]{43}$/u +const ENTRY_NODE_PARTS = /^n0:([a-z][a-z\d-]*):(0|[1-9]\d*):[A-Za-z\d_-]{43}$/u +const LOGICAL_PATIENT_RESERVED_SYSTEMS = new Set( + groveMobileContract.referencePolicy.identifierOnlyPatient.reservedSystems, +) export const isLowercaseUuidV5 = (value: string): boolean => LOWERCASE_UUID_V5.test(value) @@ -266,6 +269,7 @@ const validateGovernedReferenceShape = ( parseAbsoluteUri(value.identifier.system).ok && typeof value.type === 'string' && allowedTypes.has(value.type) + const patientOnly = allowedTypes.size === 1 && allowedTypes.has('Patient') if (hasLiteral && hasIdentifier) { addIssue( context, @@ -277,7 +281,7 @@ const validateGovernedReferenceShape = ( } else if (!hasLiteral && hasIdentifier && !validLogical) { addIssue( context, - allowedTypes.size === 1 && allowedTypes.has('Patient') ? + patientOnly ? 'mobile-exchange.logical-patient-reference' : 'mobile-exchange.reference-shape', path, @@ -292,6 +296,37 @@ const validateGovernedReferenceShape = ( 'A governed Reference requires exactly one resolving literal or identifier-only logical shape.', location, ) + } else if (validLogical && patientOnly) { + const identifier = asRecord(value.identifier) + if ( + typeof identifier?.system === 'string' && + LOGICAL_PATIENT_RESERVED_SYSTEMS.has(identifier.system) + ) { + addIssue( + context, + 'mobile-exchange.logical-patient-reference', + [...path, 'identifier', 'system'], + 'A logical Patient identifier cannot use a protocol-reserved code-system URI.', + `${location}.identifier.system`, + ) + } + const type = asRecord(identifier?.type) + const coding = Array.isArray(type?.coding) ? type.coding : [] + if ( + coding.some( + (candidate) => + asRecord(candidate)?.system === + groveMobileContract.systems.identifierRole, + ) + ) { + addIssue( + context, + 'mobile-exchange.logical-patient-reference', + [...path, 'identifier', 'type'], + 'A logical Patient identifier cannot carry a Grove identifier role.', + `${location}.identifier.type`, + ) + } } } diff --git a/packages/fhir/src/r4/profile-semantics.ts b/packages/fhir/src/r4/profile-semantics.ts index 27b213e3..937e4905 100644 --- a/packages/fhir/src/r4/profile-semantics.ts +++ b/packages/fhir/src/r4/profile-semantics.ts @@ -363,7 +363,7 @@ const validateRecordingDocument = ( typeof format?.code === 'string' ? formats[format.code] : undefined if ( format?.system !== groveMobileContract.recordingFormats.codeSystem || - format.version !== groveMobileContract.recordingFormats.version || + format.version !== undefined || definition?.status !== 'active' || definition.contentType !== attachment?.contentType ) { @@ -371,7 +371,7 @@ const validateRecordingDocument = ( context, 'mobile-recording-document.format', [...path, 'content', 0, 'format'], - 'Recording format, release, and content type must match the active Grove registry entry.', + 'Recording format and content type must match the active Grove registry entry; release-coupled Coding.version is not carried in an instance.', ) } } diff --git a/packages/fhir/src/r4/retraction-graph-semantics.ts b/packages/fhir/src/r4/retraction-graph-semantics.ts index 7930889a..ac484709 100644 --- a/packages/fhir/src/r4/retraction-graph-semantics.ts +++ b/packages/fhir/src/r4/retraction-graph-semantics.ts @@ -169,7 +169,7 @@ const validateRetractionTargetIdentity = ( context, 'mobile-retraction.opaque-target', ['entry', 'resource', 'target', index, 'identifier'], - 'Each target requires the exact typed canonical v2 HMAC Identifier previously emitted.', + 'Each target requires the exact typed canonical v0 HMAC Identifier previously emitted.', ) } } diff --git a/packages/fhir/test/exchange-corpus.test.ts b/packages/fhir/test/exchange-corpus.test.ts index e0d5cf6d..c2415eb2 100644 --- a/packages/fhir/test/exchange-corpus.test.ts +++ b/packages/fhir/test/exchange-corpus.test.ts @@ -33,7 +33,7 @@ interface CorpusCase { } interface ExchangeCorpus { - readonly schemaVersion: 1 + readonly schemaVersion: 0 readonly cases: readonly CorpusCase[] } @@ -449,8 +449,8 @@ describe('shared Grove Mobile exchange corpus', () => { path: '/entry/1/resource/identifier/1', value: { system: - 'https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1', - value: 'v2:test-key:1:Fz7vAPqR5b-ZOS8DwWQn3q7MogUtBVdlb3vYhRZniGU', + 'https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1', + value: 'v0:test-key:1:Fz7vAPqR5b-ZOS8DwWQn3q7MogUtBVdlb3vYhRZniGU', type: { coding: [ { diff --git a/packages/fhir/test/exchange-protocol.test.ts b/packages/fhir/test/exchange-protocol.test.ts index d45a0579..e18a330b 100644 --- a/packages/fhir/test/exchange-protocol.test.ts +++ b/packages/fhir/test/exchange-protocol.test.ts @@ -44,34 +44,34 @@ const uri = (value: string): AbsoluteUri => unwrap(parseAbsoluteUri(value)) const conformanceDeployment = { opaqueIdentifierSystems: { 'source-record': uri( - 'https://study.example.org/fhir/NamingSystem/grove-source-record-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1', ), 'source-output': uri( - 'https://study.example.org/fhir/NamingSystem/grove-source-output-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1', ), 'writer-record': uri( - 'https://study.example.org/fhir/NamingSystem/grove-writer-record-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1', ), 'provider-record': uri( - 'https://study.example.org/fhir/NamingSystem/grove-provider-record-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-provider-record-v0/test-key/1', ), 'provider-output': uri( - 'https://study.example.org/fhir/NamingSystem/grove-provider-output-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-provider-output-v0/test-key/1', ), 'source-artifact': uri( - 'https://study.example.org/fhir/NamingSystem/grove-source-artifact-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-source-artifact-v0/test-key/1', ), 'provider-artifact': uri( - 'https://study.example.org/fhir/NamingSystem/grove-provider-artifact-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-provider-artifact-v0/test-key/1', ), 'source-context': uri( - 'https://study.example.org/fhir/NamingSystem/grove-source-context-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-source-context-v0/test-key/1', ), 'recording-device': uri( - 'https://study.example.org/fhir/NamingSystem/grove-recording-device-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-recording-device-v0/test-key/1', ), 'device-snapshot': uri( - 'https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v2/test-key/1', + 'https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1', ), }, eventIdentifierSystem: uri(groveExchangeProtocol.testVectors.event.system), @@ -106,7 +106,7 @@ const deriveVectorIdentity = ( components, ) -describe('Grove exchange protocol v2 identity', () => { +describe('Grove exchange protocol v0 identity', () => { it('fails closed without throwing at every untyped identity boundary', () => { const invalidValues = [null, undefined, 42, 'wrong-shape', Symbol('x')] for (const invalid of invalidValues) { @@ -468,7 +468,7 @@ describe('Grove exchange protocol v2 identity', () => { runtimeDeployment, { ...event, - value: 'e2:13ed2fea-25b0-4d0d-9a26-c6d2a7f057a2:1', + value: 'e0:13ed2fea-25b0-4d0d-9a26-c6d2a7f057a2:1', }, 'conversion-provenance', '0', @@ -505,15 +505,15 @@ describe('Grove exchange protocol v2 identity', () => { deriveEntryNodeIdentifier(runtimeDeployment, event, 'Uppercase', '0').ok, ).toBe(false) expect(isEventIdentityValue(42 as never)).toBe(false) - expect(isEventIdentityValue('e2:not-a-uuid:1')).toBe(false) + expect(isEventIdentityValue('e0:not-a-uuid:1')).toBe(false) expect(containsIsolatedSurrogate(42 as never)).toBe(true) }) it('rejects noncanonical base64url digest spellings', () => { const noncanonicalDigest = `${'A'.repeat(42)}B` - expect(isOpaqueIdentityValue(`v2:key:1:${noncanonicalDigest}`)).toBe(false) + expect(isOpaqueIdentityValue(`v0:key:1:${noncanonicalDigest}`)).toBe(false) expect( - isEntryNodeIdentityValue(`n2:resource:0:${noncanonicalDigest}`), + isEntryNodeIdentityValue(`n0:resource:0:${noncanonicalDigest}`), ).toBe(false) }) }) diff --git a/packages/fhir/test/exports.test.ts b/packages/fhir/test/exports.test.ts index 5efaf9b1..54cc66a6 100644 --- a/packages/fhir/test/exports.test.ts +++ b/packages/fhir/test/exports.test.ts @@ -74,7 +74,7 @@ describe('public entry-point boundaries', () => { it('exposes bounded generated version and package metadata', () => { expect(root.groveFhirVersion).toBe('4.0.1') expect(root.groveFhirContractVersion).toBe('0.6.0') - expect(root.groveExchangeProtocol.protocolVersion).toBe(2) + expect(root.groveExchangeProtocol.protocolVersion).toBe(0) expect(root.groveMobileContract.version).toBe('0.6.0') expect( root.groveMobileContract.identity.resourceIdentifierPriority, diff --git a/packages/fhir/test/mobile.test.ts b/packages/fhir/test/mobile.test.ts index ec4a5cf4..8048efed 100644 --- a/packages/fhir/test/mobile.test.ts +++ b/packages/fhir/test/mobile.test.ts @@ -46,7 +46,7 @@ describe('source-neutral Mobile contract', () => { 'org.grovealliance.fhir.mobile', ) expect(Object.isFrozen(groveMobilePackageMetadata)).toBe(true) - expect(mobile.groveExchangeProtocol.protocolVersion).toBe(2) + expect(mobile.groveExchangeProtocol.protocolVersion).toBe(0) expect(mobile.groveMobileContract.version).toBe('0.6.0') expect(Object.isFrozen(mobile.groveMobileContract)).toBe(true) expect('groveFhirExchangeIdentity' in mobile).toBe(false) diff --git a/packages/fhir/test/profile-semantics-boundaries.test.ts b/packages/fhir/test/profile-semantics-boundaries.test.ts index c5831fd6..16e5c5a2 100644 --- a/packages/fhir/test/profile-semantics-boundaries.test.ts +++ b/packages/fhir/test/profile-semantics-boundaries.test.ts @@ -259,7 +259,6 @@ describe('profile-specific resource semantic boundaries', () => { format: { system: 'https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format', - version: '0.6.0', code: 'sensor-recording', }, }, diff --git a/packages/fhir/test/provider-identity.test.ts b/packages/fhir/test/provider-identity.test.ts index 58e31d27..a60d530b 100644 --- a/packages/fhir/test/provider-identity.test.ts +++ b/packages/fhir/test/provider-identity.test.ts @@ -62,7 +62,7 @@ describe('Provider R4 graph builder', () => { )['provider-output'], ]) for (const identity of observation.identifier ?? []) { - expect(identity.value).toMatch(/^v2:test-key:1:[A-Za-z0-9_-]{43}$/u) + expect(identity.value).toMatch(/^v0:test-key:1:[A-Za-z0-9_-]{43}$/u) } expect(result.value.identifier.system).toBe( deploymentIdentity.eventIdentifierSystem, @@ -349,7 +349,7 @@ describe('Provider R4 graph builder', () => { expect(observation?.identifier).toHaveLength(2) expect(observation?.identifier?.map((entry) => entry.value)).toEqual( expect.arrayContaining([ - expect.stringMatching(/^v2:test-key:1:[A-Za-z0-9_-]{43}$/u), + expect.stringMatching(/^v0:test-key:1:[A-Za-z0-9_-]{43}$/u), ]), ) @@ -374,7 +374,7 @@ describe('Provider R4 graph builder', () => { (resource) => resource.resourceType === 'Observation', ) expect(unicodeObservation?.identifier?.[0]?.value).toMatch( - /^v2:test-key:1:[A-Za-z0-9_-]{43}$/u, + /^v0:test-key:1:[A-Za-z0-9_-]{43}$/u, ) expect(JSON.stringify(unicodeResult)).not.toContain('résumé-\u0001') }) diff --git a/packages/fhir/test/recording.test.ts b/packages/fhir/test/recording.test.ts index 61b29917..329de75d 100644 --- a/packages/fhir/test/recording.test.ts +++ b/packages/fhir/test/recording.test.ts @@ -118,9 +118,7 @@ const rawInput = ( }, attachment: { kind: 'embedded', - contentType: unwrap( - parseMediaType('application/vnd.grovealliance.provider+json'), - ), + contentType: unwrap(parseMediaType('application/json')), title: 'Authorized minimized provider recording', format: 'provider-recording', payloadAssertion: 'caller-authorized-opaque-payload', @@ -383,7 +381,6 @@ describe('Provider native recording graph', () => { format: { system: 'https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format', - version: '0.6.0', code: healthKitClinicalRecordAdmission.payloadFormat, }, attachment: { @@ -526,7 +523,7 @@ describe('Provider native recording graph', () => { expect(documentEntry.resource.content).toEqual([ { attachment: { - contentType: 'application/vnd.grovealliance.provider+json', + contentType: 'application/json', data: 'AQID', size: 3, hash: 'cDeAcZjCKn0rCAc3HXY3eahP388=', @@ -535,7 +532,6 @@ describe('Provider native recording graph', () => { format: { system: 'https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format', - version: '0.6.0', code: 'provider-recording', display: 'Provider Recording', }, @@ -601,7 +597,7 @@ describe('Provider native recording graph', () => { }, }, { - name: 'wrong recording registry version', + name: 'release-coupled recording registry version', mutate: (document: MutableJsonObject) => { const content = document.content if (!Array.isArray(content)) throw new Error('Expected content.') @@ -621,7 +617,7 @@ describe('Provider native recording graph', () => { mutableObject(content[0], 'content').attachment, 'content.attachment', ) - attachment.contentType = 'application/json' + attachment.contentType = 'text/plain' }, }, { @@ -692,7 +688,7 @@ describe('Provider native recording graph', () => { } expect(document.identifier).toHaveLength(3) for (const entry of document.identifier ?? []) { - expect(entry.value).toMatch(/^v2:test-key:1:[A-Za-z0-9_-]{43}$/u) + expect(entry.value).toMatch(/^v0:test-key:1:[A-Za-z0-9_-]{43}$/u) } }) @@ -808,9 +804,7 @@ describe('Provider native recording graph', () => { ...input, attachment: { kind: 'external', - contentType: unwrap( - parseMediaType('application/vnd.grovealliance.provider+json'), - ), + contentType: unwrap(parseMediaType('application/json')), title: 'Authorized minimized Oura recording', format: 'provider-recording', payloadAssertion: 'verified-sanitized-input', @@ -937,7 +931,7 @@ describe('Provider native recording graph', () => { const input = rawInput('oura', 'heartrate') const external = { kind: 'external', - contentType: 'application/vnd.grovealliance.provider+json', + contentType: 'application/json', title: 'Authorized recording', format: 'provider-recording', payloadAssertion: 'caller-authorized-opaque-payload', diff --git a/packages/fhir/test/retraction.test.ts b/packages/fhir/test/retraction.test.ts index b90b71d8..f285f1f8 100644 --- a/packages/fhir/test/retraction.test.ts +++ b/packages/fhir/test/retraction.test.ts @@ -275,9 +275,7 @@ describe('Provider source-record retraction', () => { }, attachment: { kind: 'embedded', - contentType: unwrap( - parseMediaType('application/vnd.grovealliance.provider+json'), - ), + contentType: unwrap(parseMediaType('application/json')), title: 'Authorized minimized provider recording', format: 'provider-recording', payloadAssertion: 'verified-sanitized-input', From 1ad183a94a292ccdbcd7ef71fc37ab1197164e08 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Sat, 29 Aug 2026 15:56:45 -0700 Subject: [PATCH 05/31] Remove release-specific protocol wording --- packages/fhir/src/mobile/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fhir/src/mobile/types.ts b/packages/fhir/src/mobile/types.ts index 6ca3c98a..392ad4f6 100644 --- a/packages/fhir/src/mobile/types.ts +++ b/packages/fhir/src/mobile/types.ts @@ -146,7 +146,7 @@ export interface CompleteIdentifierInput { export type GroveIdentifierRole = OpaqueIdentityDefinition['identifierRole'] | 'entry-node' | 'event' -/** Closed HMAC domains from the Grove 0.6 exchange protocol. */ +/** Closed HMAC domains from the Grove exchange protocol. */ export type GroveOpaqueIdentityKind = OpaqueIdentityDefinition['kind'] export type GroveOpaqueIdentitySystems = Readonly< From 9fccc11faa7948eed26241e1ff86cc5a9cf277dd Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Sat, 29 Aug 2026 16:07:21 -0700 Subject: [PATCH 06/31] Align recording boundary fixture with versionless coding --- packages/fhir/test/profile-semantics-boundaries.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/fhir/test/profile-semantics-boundaries.test.ts b/packages/fhir/test/profile-semantics-boundaries.test.ts index 16e5c5a2..5f5c21bf 100644 --- a/packages/fhir/test/profile-semantics-boundaries.test.ts +++ b/packages/fhir/test/profile-semantics-boundaries.test.ts @@ -281,7 +281,6 @@ describe('profile-specific resource semantic boundaries', () => { throw new Error('Missing recording content.') formatContent.format = { system: 'https://example.org/wrong', - version: 'future', code: 'unknown', } expectRule( From 8d0ca1f46791d416e17176feeeaeb8aec7233455 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Sat, 29 Aug 2026 16:21:08 -0700 Subject: [PATCH 07/31] Skip release-readiness aggregates for scoped drafts --- .github/workflows/build-and-test.yml | 11 +++++------ .github/workflows/fhir-conformance.yml | 10 ++-------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 9fdb2ab3..bbcb791f 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -299,7 +299,11 @@ jobs: done ci: name: CI — Full Release Readiness - if: always() + if: >- + always() && + !(github.event_name == 'pull_request' && + github.event.pull_request.draft && + startsWith(github.head_ref, 'feature/fhir-')) needs: - actionlint - dependency-review @@ -318,7 +322,6 @@ jobs: ACTIONLINT_RESULT: ${{ needs.actionlint.result }} DEPENDENCY_REVIEW_RESULT: ${{ needs.dependency-review.result }} DOCUMENTATION_RESULT: ${{ needs.documentation.result }} - IS_FHIR_DRAFT: ${{ github.event_name == 'pull_request' && github.event.pull_request.draft && startsWith(github.head_ref, 'feature/fhir-') }} MARKDOWN_LINKS_RESULT: ${{ needs.markdown-links.result }} NODE_CURRENT_RESULT: ${{ needs.node-current.result }} QUALITY_RESULT: ${{ needs.quality.result }} @@ -326,10 +329,6 @@ jobs: TESTS_RESULT: ${{ needs.tests.result }} TESTS_EXACT_HEAD_RESULT: ${{ needs.tests-exact-head.result }} run: | - if [[ "$IS_FHIR_DRAFT" == true ]]; then - echo "Draft-scoped checks do not establish full release readiness." >&2 - exit 1 - fi for result in \ "$ACTIONLINT_RESULT" \ "$DOCUMENTATION_RESULT" \ diff --git a/.github/workflows/fhir-conformance.yml b/.github/workflows/fhir-conformance.yml index 4193e8c5..60d07853 100644 --- a/.github/workflows/fhir-conformance.yml +++ b/.github/workflows/fhir-conformance.yml @@ -159,17 +159,11 @@ jobs: fhir-readiness: name: FHIR R4 — Full Release Readiness - if: always() + if: always() && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) needs: fhir-r4 runs-on: ubuntu-latest steps: - name: Require the full exact-head Validator lane env: FHIR_RESULT: ${{ needs.fhir-r4.result }} - IS_DRAFT: ${{ github.event_name == 'pull_request' && github.event.pull_request.draft }} - run: | - if [[ "$IS_DRAFT" == true ]]; then - echo "Draft structural checks do not establish FHIR release readiness." >&2 - exit 1 - fi - test "$FHIR_RESULT" = success + run: test "$FHIR_RESULT" = success From 901d57dcaf5539b99003e6cbfbdb44b6097609b3 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Sat, 29 Aug 2026 16:22:51 -0700 Subject: [PATCH 08/31] Recognize FHIR work across feature branch names --- .github/workflows/build-and-test.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index bbcb791f..256e15eb 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -40,7 +40,7 @@ jobs: if: >- github.event_name == 'pull_request' && github.event.pull_request.draft && - startsWith(github.head_ref, 'feature/fhir-') + contains(github.head_ref, 'fhir') runs-on: ubuntu-latest timeout-minutes: 20 steps: @@ -101,7 +101,7 @@ jobs: if: >- github.event_name != 'pull_request' || github.event.pull_request.draft == false || - !startsWith(github.head_ref, 'feature/fhir-') + !contains(github.head_ref, 'fhir') runs-on: ubuntu-latest timeout-minutes: 20 steps: @@ -142,7 +142,7 @@ jobs: if: >- github.event_name != 'pull_request' || github.event.pull_request.draft == false || - !startsWith(github.head_ref, 'feature/fhir-') + !contains(github.head_ref, 'fhir') runs-on: ubuntu-latest timeout-minutes: 20 steps: @@ -171,7 +171,7 @@ jobs: if: >- github.event_name != 'pull_request' || github.event.pull_request.draft == false || - !startsWith(github.head_ref, 'feature/fhir-') + !contains(github.head_ref, 'fhir') runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -207,7 +207,7 @@ jobs: if: >- github.event_name != 'pull_request' || github.event.pull_request.draft == false || - !startsWith(github.head_ref, 'feature/fhir-') + !contains(github.head_ref, 'fhir') uses: SchmiedmayerLab/.github/.github/workflows/npm-test-coverage.yml@v0.5 with: coverage-files: >- @@ -223,7 +223,7 @@ jobs: if: >- github.event_name != 'pull_request' || github.event.pull_request.draft == false || - !startsWith(github.head_ref, 'feature/fhir-') + !contains(github.head_ref, 'fhir') runs-on: ubuntu-latest timeout-minutes: 20 steps: @@ -270,7 +270,7 @@ jobs: always() && github.event_name == 'pull_request' && github.event.pull_request.draft && - startsWith(github.head_ref, 'feature/fhir-') + contains(github.head_ref, 'fhir') needs: - actionlint - dependency-review @@ -303,7 +303,7 @@ jobs: always() && !(github.event_name == 'pull_request' && github.event.pull_request.draft && - startsWith(github.head_ref, 'feature/fhir-')) + contains(github.head_ref, 'fhir')) needs: - actionlint - dependency-review From 9f1a190d80074bc7ed2915755803b019157f1814 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Sat, 29 Aug 2026 21:25:10 -0700 Subject: [PATCH 09/31] Refresh TypeScript FHIR Contracts from the Implementation Guides --- packages/fhir/grove-fhir.json | 4 +- .../scripts/generate-measurement-catalog.mjs | 58 +++++++++++------- .../mobile/measurement-catalog.generated.ts | 9 ++- .../fhir/src/providers/contract.generated.ts | 20 +++--- packages/fhir/src/r4/profile-semantics.ts | 61 ++++++++++--------- packages/fhir/test/exports.test.ts | 14 +++-- .../test/profile-semantics-boundaries.test.ts | 14 +++-- packages/fhir/test/recording.test.ts | 56 ++++++++--------- 8 files changed, 128 insertions(+), 108 deletions(-) diff --git a/packages/fhir/grove-fhir.json b/packages/fhir/grove-fhir.json index 9bc075e6..d4789f20 100644 --- a/packages/fhir/grove-fhir.json +++ b/packages/fhir/grove-fhir.json @@ -1,5 +1,5 @@ { "repository": "https://github.com/SchmiedmayerLab/grove-fhir", - "ref": "54d977149c038b7487614bbe30fbc8f8ebf541d0", - "archiveSha256": "7acd5d12c076b23c19ec4460b67962b5cf54bbbf2395c85201a6457ae6eefc78" + "ref": "90b23e647f1dd728035c64348abede5746375529", + "archiveSha256": "1ae9204acd104095cc12f6dff1226fd2cdba68fdea88552971b542ca0df344a0" } diff --git a/packages/fhir/scripts/generate-measurement-catalog.mjs b/packages/fhir/scripts/generate-measurement-catalog.mjs index 491be2a2..26b54c91 100644 --- a/packages/fhir/scripts/generate-measurement-catalog.mjs +++ b/packages/fhir/scripts/generate-measurement-catalog.mjs @@ -8,7 +8,6 @@ import { readFile, writeFile } from 'node:fs/promises' import { argv } from 'node:process' -import { URL } from 'node:url' import { format } from 'prettier' @@ -596,6 +595,12 @@ const healthKitClinicalRecordAdmission = healthKitAdapter.clinicalRecordAdmission const healthKitClinicalFhirRepresentation = healthKitClinicalRecordAdmission?.fhirRepresentation +const healthKitClinicalFhirReleases = + healthKitClinicalRecordAdmission?.admittedFHIRReleases +const healthKitClinicalContentTypes = + healthKitClinicalFhirRepresentation?.contentTypeByRelease +const healthKitClinicalFormat = + formatRegistry.formats?.[healthKitClinicalRecordAdmission?.payloadFormat] const healthKitApplicationDeviceIdentity = healthKitAdapter.applicationDeviceIdentity const healthKitBundleIdentifier = @@ -605,23 +610,20 @@ if ( healthKitClinicalRecordAdmission === null || Array.isArray(healthKitClinicalRecordAdmission) || Object.keys(healthKitClinicalRecordAdmission).sort().join(',') !== - 'admittedFHIRRelease,fhirRepresentation,payloadFormat,profile,rejectedFHIRReleases,rule,sourceFHIRReleaseField' || + 'admittedFHIRReleases,fhirRepresentation,payloadFormat,profile,rejectedFHIRReleases,rule,sourceFHIRReleaseField' || healthKitClinicalRecordAdmission.profile !== profiles['healthkit-clinical-record-document'] || typeof healthKitClinicalRecordAdmission.payloadFormat !== 'string' || - formatRegistry.formats?.[healthKitClinicalRecordAdmission.payloadFormat] - ?.status !== 'active' || - formatRegistry.formats[healthKitClinicalRecordAdmission.payloadFormat] - .contentType !== 'application/fhir+json' || + healthKitClinicalFormat?.status !== 'active' || typeof healthKitClinicalRecordAdmission.sourceFHIRReleaseField !== 'string' || healthKitClinicalRecordAdmission.sourceFHIRReleaseField.trim() === '' || - typeof healthKitClinicalRecordAdmission.admittedFHIRRelease !== 'string' || - healthKitClinicalRecordAdmission.admittedFHIRRelease.trim() === '' || + JSON.stringify(healthKitClinicalFhirReleases) !== + JSON.stringify(['dstu2', 'r4']) || !uniqueNonemptyStrings( healthKitClinicalRecordAdmission.rejectedFHIRReleases, ) || - healthKitClinicalRecordAdmission.rejectedFHIRReleases.includes( - healthKitClinicalRecordAdmission.admittedFHIRRelease, + healthKitClinicalRecordAdmission.rejectedFHIRReleases.some((release) => + healthKitClinicalFhirReleases.includes(release), ) || typeof healthKitClinicalRecordAdmission.rule !== 'string' || healthKitClinicalRecordAdmission.rule.trim() === '' || @@ -629,15 +631,21 @@ if ( healthKitClinicalFhirRepresentation === null || Array.isArray(healthKitClinicalFhirRepresentation) || Object.keys(healthKitClinicalFhirRepresentation).sort().join(',') !== - 'cardinality,extensionUrl,fixedValue,resourceType,valueElement' || + 'contentTypeByRelease,resourceType' || healthKitClinicalFhirRepresentation.resourceType !== 'DocumentReference' || - typeof healthKitClinicalFhirRepresentation.extensionUrl !== 'string' || - !URL.canParse(healthKitClinicalFhirRepresentation.extensionUrl) || - healthKitClinicalFhirRepresentation.valueElement !== 'valueCode' || - healthKitClinicalFhirRepresentation.cardinality?.min !== 1 || - healthKitClinicalFhirRepresentation.cardinality.max !== 1 || - healthKitClinicalFhirRepresentation.fixedValue !== - healthKitClinicalRecordAdmission.admittedFHIRRelease + typeof healthKitClinicalContentTypes !== 'object' || + healthKitClinicalContentTypes === null || + Array.isArray(healthKitClinicalContentTypes) || + JSON.stringify(Object.keys(healthKitClinicalContentTypes).sort()) !== + JSON.stringify([...healthKitClinicalFhirReleases].sort()) || + !uniqueNonemptyStrings(Object.values(healthKitClinicalContentTypes)) || + !uniqueNonemptyStrings(healthKitClinicalFormat.contentTypes) || + JSON.stringify(healthKitClinicalFormat.contentTypes) !== + JSON.stringify( + healthKitClinicalFhirReleases.map( + (release) => healthKitClinicalContentTypes[release], + ), + ) ) { throw new Error( 'HealthKit clinical-record FHIR admission must remain complete and closed.', @@ -1184,10 +1192,12 @@ if ( typeof formatRegistry.valueSet !== 'string' || Object.keys(formatRegistry.formats ?? {}).length === 0 || Object.hasOwn(formatRegistry.formats ?? {}, 'fhir-resource-array') || + Object.hasOwn(formatRegistry.formats ?? {}, 'fhir-r4-resource') || formatRegistry.formats?.['fhir-collection-bundle']?.contentType !== 'application/fhir+json' || - formatRegistry.formats?.['fhir-r4-resource']?.contentType !== - 'application/fhir+json' || + !uniqueNonemptyStrings( + formatRegistry.formats?.['fhir-resource']?.contentTypes, + ) || formatRegistry.formats?.['provider-recording']?.contentType !== 'application/json' ) { @@ -1201,9 +1211,11 @@ const recordingFormatRegistry = { version: formatRegistry.version, formats: Object.fromEntries( Object.entries(formatRegistry.formats).map(([code, entry]) => { + const hasContentType = typeof entry.contentType === 'string' + const hasContentTypes = uniqueNonemptyStrings(entry.contentTypes) if ( typeof entry.title !== 'string' || - typeof entry.contentType !== 'string' || + hasContentType === hasContentTypes || entry.status !== 'active' ) { throw new Error(`Recording format ${code} is incomplete.`) @@ -1212,7 +1224,9 @@ const recordingFormatRegistry = { code, { title: entry.title, - contentType: entry.contentType, + ...(hasContentType ? + { contentType: entry.contentType } + : { contentTypes: entry.contentTypes }), status: entry.status, }, ] diff --git a/packages/fhir/src/mobile/measurement-catalog.generated.ts b/packages/fhir/src/mobile/measurement-catalog.generated.ts index 98f79fd4..cf7d63a6 100644 --- a/packages/fhir/src/mobile/measurement-catalog.generated.ts +++ b/packages/fhir/src/mobile/measurement-catalog.generated.ts @@ -2322,9 +2322,12 @@ const groveRecordingFormatRegistryValue = { contentType: 'application/fhir+json', status: 'active', }, - 'fhir-r4-resource': { - title: 'FHIR R4 Resource', - contentType: 'application/fhir+json', + 'fhir-resource': { + title: 'FHIR Resource', + contentTypes: [ + 'application/fhir+json; fhirVersion=1.0', + 'application/fhir+json; fhirVersion=4.0', + ], status: 'active', }, 'clinical-document': { diff --git a/packages/fhir/src/providers/contract.generated.ts b/packages/fhir/src/providers/contract.generated.ts index 4dbbc278..3c986c0a 100644 --- a/packages/fhir/src/providers/contract.generated.ts +++ b/packages/fhir/src/providers/contract.generated.ts @@ -6754,22 +6754,18 @@ export const healthKitApplicationDeviceIdentity: typeof healthKitApplicationDevi const healthKitClinicalRecordAdmissionValue = { profile: 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-clinical-record-document', - payloadFormat: 'fhir-r4-resource', + payloadFormat: 'fhir-resource', sourceFHIRReleaseField: 'HKFHIRVersion.fhirRelease', - admittedFHIRRelease: 'r4', + admittedFHIRReleases: ['dstu2', 'r4'], fhirRepresentation: { resourceType: 'DocumentReference', - extensionUrl: - 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-clinical-fhir-release', - valueElement: 'valueCode', - cardinality: { - min: 1, - max: 1, - }, - fixedValue: 'r4', + contentTypeByRelease: { + dstu2: 'application/fhir+json; fhirVersion=1.0', + r4: 'application/fhir+json; fhirVersion=4.0', + }, }, - rejectedFHIRReleases: ['dstu2', 'unknown'], - rule: 'Admit a HealthKit clinical record only when HKFHIRVersion.fhirRelease is r4. Reject dstu2, unknown, a missing release, and every future release before emitting a DocumentReference; the payload is preserved byte-for-byte and never upgraded, downgraded, or inferred from its JSON shape.', + rejectedFHIRReleases: ['unknown'], + rule: 'Admit a HealthKit clinical record only when HKFHIRVersion.fhirRelease is exactly dstu2 or r4, then set Attachment.contentType to its matching versioned FHIR JSON media type. Reject an unknown, missing, or future release before emission; preserve the provider-issued payload bytes exactly and never convert, re-encode, or infer their release from JSON shape.', } as const export const healthKitClinicalRecordAdmission: typeof healthKitClinicalRecordAdmissionValue = diff --git a/packages/fhir/src/r4/profile-semantics.ts b/packages/fhir/src/r4/profile-semantics.ts index 937e4905..958211a7 100644 --- a/packages/fhir/src/r4/profile-semantics.ts +++ b/packages/fhir/src/r4/profile-semantics.ts @@ -248,33 +248,6 @@ const validateRecordingDocument = ( 'A source-preservation DocumentReference must declare exactly one catalog-admitted direct profile set.', ) } - if (claim === groveProfileClaims.healthKitClinicalRecordDocumentClaim) { - const definition = healthKitClinicalRecordAdmission.fhirRepresentation - const extensions = - Array.isArray(resource.extension) ? resource.extension : [] - const matching = extensions.filter( - (extension) => asRecord(extension)?.url === definition.extensionUrl, - ) - const representedRelease = asRecord(matching[0]) - const valueElements = Object.keys(representedRelease ?? {}).filter((key) => - /^value[A-Z]/u.test(key), - ) - if ( - resource.resourceType !== definition.resourceType || - matching.length < definition.cardinality.min || - matching.length > definition.cardinality.max || - representedRelease?.[definition.valueElement] !== definition.fixedValue || - valueElements.length !== 1 || - valueElements[0] !== definition.valueElement - ) { - addIssue( - context, - 'healthkit-clinical-record.fhir-release', - [...path, 'extension'], - 'A HealthKit clinical-record document must carry exactly the catalog-admitted FHIR release representation.', - ) - } - } const requiredRoles: ReadonlySet = new Set( claim?.requiredIdentifierRoles ?? [], ) @@ -357,15 +330,30 @@ const validateRecordingDocument = ( } } const formats = groveMobileContract.recordingFormats.formats as Readonly< - Record + Record< + string, + { + readonly contentType?: string + readonly contentTypes?: readonly string[] + readonly status: string + } + > > const definition = typeof format?.code === 'string' ? formats[format.code] : undefined + const admittedContentTypes = + definition?.contentTypes ?? + (definition?.contentType === undefined ? [] : [definition.contentType]) + const attachmentContentType = + typeof attachment?.contentType === 'string' ? + attachment.contentType + : undefined if ( format?.system !== groveMobileContract.recordingFormats.codeSystem || format.version !== undefined || definition?.status !== 'active' || - definition.contentType !== attachment?.contentType + attachmentContentType === undefined || + !admittedContentTypes.includes(attachmentContentType) ) { addIssue( context, @@ -374,6 +362,21 @@ const validateRecordingDocument = ( 'Recording format and content type must match the active Grove registry entry; release-coupled Coding.version is not carried in an instance.', ) } + if ( + claim === groveProfileClaims.healthKitClinicalRecordDocumentClaim && + (format?.code !== healthKitClinicalRecordAdmission.payloadFormat || + !Object.values( + healthKitClinicalRecordAdmission.fhirRepresentation + .contentTypeByRelease, + ).some((contentType) => contentType === attachmentContentType)) + ) { + addIssue( + context, + 'healthkit-clinical-record.fhir-release', + [...path, 'content', 0], + 'A HealthKit clinical-record document must use the FHIR resource format and one catalog-admitted versioned FHIR JSON media type.', + ) + } } const validateProfiledQuestionnaireResponse = ( diff --git a/packages/fhir/test/exports.test.ts b/packages/fhir/test/exports.test.ts index 54cc66a6..5ed0f666 100644 --- a/packages/fhir/test/exports.test.ts +++ b/packages/fhir/test/exports.test.ts @@ -128,16 +128,22 @@ describe('public entry-point boundaries', () => { ), ).toBe(true) const clinicalAdmission = provider.healthKitClinicalRecordAdmission - expect(clinicalAdmission.fhirRepresentation.fixedValue).toBe( - clinicalAdmission.admittedFHIRRelease, + expect(clinicalAdmission.admittedFHIRReleases).toEqual(['dstu2', 'r4']) + const contentTypes = Object.values( + clinicalAdmission.fhirRepresentation.contentTypeByRelease, ) expect( provider.groveRecordingFormatRegistry.formats[ clinicalAdmission.payloadFormat - ].contentType, - ).toBe('application/fhir+json') + ].contentTypes, + ).toEqual(contentTypes) expect(Object.isFrozen(clinicalAdmission)).toBe(true) expect(Object.isFrozen(clinicalAdmission.fhirRepresentation)).toBe(true) + expect( + Object.isFrozen( + clinicalAdmission.fhirRepresentation.contentTypeByRelease, + ), + ).toBe(true) }) it('deeply freezes every generated contract read by public builders', () => { diff --git a/packages/fhir/test/profile-semantics-boundaries.test.ts b/packages/fhir/test/profile-semantics-boundaries.test.ts index 5f5c21bf..dd73ae3b 100644 --- a/packages/fhir/test/profile-semantics-boundaries.test.ts +++ b/packages/fhir/test/profile-semantics-boundaries.test.ts @@ -363,17 +363,19 @@ describe('profile-specific resource semantic boundaries', () => { expectRule(neutral, 'mobile-exchange\\.adapter-source-marker') }) - it('validates the fixed FHIR release declaration on clinical documents', () => { + it('validates the versioned FHIR media type on clinical documents', () => { const claim = groveProfileClaims.healthKitClinicalRecordDocumentClaim - const definition = healthKitClinicalRecordAdmission.fhirRepresentation const document = { resourceType: 'DocumentReference', meta: { profile: claim.profiles }, - extension: [ + content: [ { - url: definition.extensionUrl, - [definition.valueElement]: definition.fixedValue, - valueString: definition.fixedValue, + format: { + system: + 'https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format', + code: healthKitClinicalRecordAdmission.payloadFormat, + }, + attachment: { contentType: 'application/fhir+json' }, }, ], } diff --git a/packages/fhir/test/recording.test.ts b/packages/fhir/test/recording.test.ts index 329de75d..e4c19475 100644 --- a/packages/fhir/test/recording.test.ts +++ b/packages/fhir/test/recording.test.ts @@ -358,10 +358,6 @@ describe('Provider native recording graph', () => { profile: [healthKitClinicalRecordAdmission.profile], } document.extension = [ - { - url: representation.extensionUrl, - [representation.valueElement]: representation.fixedValue, - }, { url: healthKitMarker.url, valueCode: 'HKClinicalTypeIdentifierAllergyRecord', @@ -384,7 +380,7 @@ describe('Provider native recording graph', () => { code: healthKitClinicalRecordAdmission.payloadFormat, }, attachment: { - contentType: 'application/fhir+json', + contentType: representation.contentTypeByRelease.r4, title: 'Provider-issued AllergyIntolerance', data: 'eyJyZXNvdXJjZVR5cGUiOiJBbGxlcmd5SW50b2xlcmFuY2UiLCJwYXRpZW50Ijp7ImlkZW50aWZpZXIiOnsic3lzdGVtIjoiaHR0cHM6Ly9leGFtcGxlLm9yZy9wYXRpZW50IiwidmFsdWUiOiJwc2V1ZG9ueW0ifX19', size: 123, @@ -458,43 +454,43 @@ describe('Provider native recording graph', () => { 'clinical DocumentReference', ) } + const clinicalContentIn = (candidate: unknown): Record => { + const content = clinicalDocumentIn(candidate).content + if (!Array.isArray(content) || content.length !== 1) { + throw new Error('Expected one clinical DocumentReference.content item.') + } + return mutableObject(content[0], 'clinical content') + } + const dstu2 = structuredClone(bundle) + mutableObject( + clinicalContentIn(dstu2).attachment, + 'clinical attachment', + ).contentType = representation.contentTypeByRelease.dstu2 + expect(parseGroveMobileExchangeBundle(dstu2).ok).toBe(true) + const invalidRepresentations = [ (candidate: Record) => { - Reflect.deleteProperty(candidate, 'extension') + mutableObject(candidate.attachment, 'clinical attachment').contentType = + 'application/fhir+json' }, (candidate: Record) => { - candidate.extension = [ - { - url: representation.extensionUrl, - [representation.valueElement]: representation.fixedValue, - }, - { - url: representation.extensionUrl, - [representation.valueElement]: representation.fixedValue, - }, - ] + mutableObject(candidate.attachment, 'clinical attachment').contentType = + 'application/fhir+json; fhirVersion=5.0' }, (candidate: Record) => { - candidate.extension = [ - { - url: representation.extensionUrl, - [representation.valueElement]: - healthKitClinicalRecordAdmission.rejectedFHIRReleases[0], - }, - ] + mutableObject(candidate.format, 'clinical format').code = + 'native-recording' }, (candidate: Record) => { - candidate.extension = [ - { - url: representation.extensionUrl, - valueString: representation.fixedValue, - }, - ] + Reflect.deleteProperty( + mutableObject(candidate.attachment, 'clinical attachment'), + 'contentType', + ) }, ] for (const mutate of invalidRepresentations) { const candidate = structuredClone(bundle) - mutate(clinicalDocumentIn(candidate)) + mutate(clinicalContentIn(candidate)) const result = parseGroveMobileExchangeBundle(candidate) expect(result.ok).toBe(false) if (result.ok) continue From 87f039d903caebfbd68b2f17cf9996485a592edd Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Mon, 31 Aug 2026 11:26:42 -0700 Subject: [PATCH 10/31] Move the Generated Contracts Into a Single Contract Module --- .prettierignore | 3 --- packages/fhir/jest.config.js | 13 +++++-------- packages/fhir/scripts/generate-zod-schemas.mjs | 4 ++-- .../fhir/scripts/measurement-catalog-inputs.mjs | 6 +++--- .../fhir/scripts/measurement-catalog-renderer.mjs | 2 +- .../measurement-catalog.generated.ts | 0 .../providers.generated.ts} | 2 +- .../questionnaire.generated.ts} | 0 packages/fhir/src/mobile/contract.ts | 2 +- packages/fhir/src/mobile/identity.ts | 4 ++-- packages/fhir/src/mobile/index.ts | 2 +- packages/fhir/src/mobile/time.ts | 2 +- packages/fhir/src/mobile/types.ts | 6 +++--- packages/fhir/src/providers/builder.ts | 2 +- packages/fhir/src/providers/identity.ts | 4 ++-- packages/fhir/src/providers/index.ts | 2 +- .../fhir/src/providers/measurement-definition.ts | 4 ++-- packages/fhir/src/providers/profiles.ts | 2 +- .../fhir/src/providers/provider-input-schemas.ts | 4 ++-- packages/fhir/src/providers/provider.ts | 4 ++-- packages/fhir/src/providers/recording.ts | 2 +- packages/fhir/src/providers/retraction.ts | 4 ++-- packages/fhir/src/providers/types.ts | 10 +++++----- packages/fhir/src/questionnaire/builder.ts | 2 +- packages/fhir/src/questionnaire/index.ts | 2 +- packages/fhir/src/questionnaire/parse.ts | 2 +- packages/fhir/src/r4/active-graph-semantics.ts | 2 +- .../fhir/src/r4/active-observation-semantics.ts | 4 ++-- packages/fhir/src/r4/diagnostics.ts | 2 +- packages/fhir/src/r4/graph-schema-utils.ts | 2 +- packages/fhir/src/r4/profile-semantics.ts | 4 ++-- packages/fhir/src/zod/r4/index.ts | 2 +- .../src/zod/r4/{schemas.ts => schemas.generated.ts} | 0 packages/fhir/src/zod/r4b/index.ts | 2 +- .../zod/r4b/{schemas.ts => schemas.generated.ts} | 0 packages/fhir/test/diagnostics.test.ts | 2 +- packages/fhir/test/mobile.test.ts | 2 +- packages/fhir/test/observation-semantics.test.ts | 4 ++-- .../fhir/test/profile-semantics-boundaries.test.ts | 4 ++-- packages/fhir/test/recording.test.ts | 2 +- 40 files changed, 58 insertions(+), 64 deletions(-) rename packages/fhir/src/{mobile => contract}/measurement-catalog.generated.ts (100%) rename packages/fhir/src/{providers/contract.generated.ts => contract/providers.generated.ts} (99%) rename packages/fhir/src/{questionnaire/contract.generated.ts => contract/questionnaire.generated.ts} (100%) rename packages/fhir/src/zod/r4/{schemas.ts => schemas.generated.ts} (100%) rename packages/fhir/src/zod/r4b/{schemas.ts => schemas.generated.ts} (100%) diff --git a/.prettierignore b/.prettierignore index d8de8ef8..05732ded 100644 --- a/.prettierignore +++ b/.prettierignore @@ -15,9 +15,6 @@ **/*.tsbuildinfo **/.docusaurus packages/design-system/docs/docs/api -packages/fhir/catalog/grove-fhir/*.json -!packages/fhir/catalog/grove-fhir/source-ref.json -packages/fhir/catalog/grove-fhir-conformance/*.json .parcel-cache node_modules diff --git a/packages/fhir/jest.config.js b/packages/fhir/jest.config.js index 681fa5f9..bcc1240a 100644 --- a/packages/fhir/jest.config.js +++ b/packages/fhir/jest.config.js @@ -24,14 +24,11 @@ const config = { '^(\\.{1,2}/.*)\\.js$': '$1', }, collectCoverage: false, - // The generated schemas are excluded: an uncovered branch there is a FHIR type no example - // happened to use, not untested logic. What guards them is regeneration parity (`check:zod`), - // the pinned generation invariants, and validation against both releases' published examples. - collectCoverageFrom: [ - 'src/**/*.ts', - '!src/zod/r4/schemas.ts', - '!src/zod/r4b/schemas.ts', - ], + // Generated files are excluded: an uncovered branch there is a FHIR type no example happened + // to use, not untested logic. What guards them is regeneration parity (`check:zod`, + // `check:catalog`), the pinned generation invariants, and validation against both releases' + // published examples. + collectCoverageFrom: ['src/**/*.ts', '!src/**/*.generated.ts'], coverageDirectory: 'coverage', coverageReporters: ['text', 'json-summary', 'lcov'], coverageThreshold: { diff --git a/packages/fhir/scripts/generate-zod-schemas.mjs b/packages/fhir/scripts/generate-zod-schemas.mjs index 0d37ccde..f976776d 100644 --- a/packages/fhir/scripts/generate-zod-schemas.mjs +++ b/packages/fhir/scripts/generate-zod-schemas.mjs @@ -1093,7 +1093,7 @@ const HEADER = `// ` const outputPathFor = (releaseKey) => - resolve(PACKAGE_ROOT, 'src/zod', releaseKey, 'schemas.ts') + resolve(PACKAGE_ROOT, 'src/zod', releaseKey, 'schemas.generated.ts') async function main(argv) { const releaseKey = argv[argv.indexOf('--release') + 1] @@ -1227,7 +1227,7 @@ ${body}${skippedExport}` parser: 'typescript', }) const outputDir = resolve(PACKAGE_ROOT, 'src/zod', releaseKey) - const outputPath = join(outputDir, 'schemas.ts') + const outputPath = join(outputDir, 'schemas.generated.ts') if (check) { const current = existsSync(outputPath) ? await readFile(outputPath, 'utf8') : '' diff --git a/packages/fhir/scripts/measurement-catalog-inputs.mjs b/packages/fhir/scripts/measurement-catalog-inputs.mjs index 23b68a36..c152e98c 100644 --- a/packages/fhir/scripts/measurement-catalog-inputs.mjs +++ b/packages/fhir/scripts/measurement-catalog-inputs.mjs @@ -86,12 +86,12 @@ export const loadMeasurementCatalogInputs = async (scriptUrl, arguments_) => { outputPaths: { mobile: resolve( packageRoot, - 'src/mobile/measurement-catalog.generated.ts', + 'src/contract/measurement-catalog.generated.ts', ), - provider: resolve(packageRoot, 'src/providers/contract.generated.ts'), + provider: resolve(packageRoot, 'src/contract/providers.generated.ts'), questionnaire: resolve( packageRoot, - 'src/questionnaire/contract.generated.ts', + 'src/contract/questionnaire.generated.ts', ), }, packageGraph, diff --git a/packages/fhir/scripts/measurement-catalog-renderer.mjs b/packages/fhir/scripts/measurement-catalog-renderer.mjs index e8bae567..9177bc5a 100644 --- a/packages/fhir/scripts/measurement-catalog-renderer.mjs +++ b/packages/fhir/scripts/measurement-catalog-renderer.mjs @@ -141,7 +141,7 @@ export const renderMeasurementCatalogSources = ({ groveRecordingFormatRegistry, type GroveRecordingFormat, type ProviderRecordingFormat, - } from '../mobile/measurement-catalog.generated.js' + } from './measurement-catalog.generated.js' export type ProviderScalarOutputRoles = typeof providerScalarOutputRoles diff --git a/packages/fhir/src/mobile/measurement-catalog.generated.ts b/packages/fhir/src/contract/measurement-catalog.generated.ts similarity index 100% rename from packages/fhir/src/mobile/measurement-catalog.generated.ts rename to packages/fhir/src/contract/measurement-catalog.generated.ts diff --git a/packages/fhir/src/providers/contract.generated.ts b/packages/fhir/src/contract/providers.generated.ts similarity index 99% rename from packages/fhir/src/providers/contract.generated.ts rename to packages/fhir/src/contract/providers.generated.ts index 3c986c0a..b78fdd0b 100644 --- a/packages/fhir/src/providers/contract.generated.ts +++ b/packages/fhir/src/contract/providers.generated.ts @@ -7274,7 +7274,7 @@ export { groveRecordingFormatRegistry, type GroveRecordingFormat, type ProviderRecordingFormat, -} from '../mobile/measurement-catalog.generated.js' +} from './measurement-catalog.generated.js' export type ProviderScalarOutputRoles = typeof providerScalarOutputRoles diff --git a/packages/fhir/src/questionnaire/contract.generated.ts b/packages/fhir/src/contract/questionnaire.generated.ts similarity index 100% rename from packages/fhir/src/questionnaire/contract.generated.ts rename to packages/fhir/src/contract/questionnaire.generated.ts diff --git a/packages/fhir/src/mobile/contract.ts b/packages/fhir/src/mobile/contract.ts index 1ce94f7c..a5dba5fe 100644 --- a/packages/fhir/src/mobile/contract.ts +++ b/packages/fhir/src/mobile/contract.ts @@ -11,7 +11,7 @@ import { groveMobilePackageMetadata, groveMobileProfileCanonicals, groveRecordingFormatRegistry, -} from './measurement-catalog.generated.js' +} from '../contract/measurement-catalog.generated.js' import { deepFreeze } from '../core/index.js' export interface GroveMobileProtocolContract { diff --git a/packages/fhir/src/mobile/identity.ts b/packages/fhir/src/mobile/identity.ts index b68b2325..95c94966 100644 --- a/packages/fhir/src/mobile/identity.ts +++ b/packages/fhir/src/mobile/identity.ts @@ -10,7 +10,7 @@ import { hmac } from '@noble/hashes/hmac.js' import { sha256 } from '@noble/hashes/sha2.js' import { v5 as uuidV5 } from 'uuid' import { groveMobileContract } from './contract.js' -import { groveExchangeProtocol } from './measurement-catalog.generated.js' +import { groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' import type { CompleteIdentifierInput, DeploymentIdentityInput, @@ -31,7 +31,7 @@ import { type Result, type UrnUuid, } from '../core/index.js' -import { providerAdapterCatalog } from '../providers/contract.generated.js' +import { providerAdapterCatalog } from '../contract/providers.generated.js' const ASCII_TOKEN = /^[A-Za-z\d._-]+$/u const POSITIVE_DECIMAL = /^[1-9]\d*$/u diff --git a/packages/fhir/src/mobile/index.ts b/packages/fhir/src/mobile/index.ts index 7be209b9..91773a25 100644 --- a/packages/fhir/src/mobile/index.ts +++ b/packages/fhir/src/mobile/index.ts @@ -36,7 +36,7 @@ export { type GroveRecordingFormat, type ProviderRecordingFormat, type SharedMobileMeasurementKind, -} from './measurement-catalog.generated.js' +} from '../contract/measurement-catalog.generated.js' export { canonicalizeMobileEffectiveInstant } from './time.js' export type { ApplicationDeviceInput, diff --git a/packages/fhir/src/mobile/time.ts b/packages/fhir/src/mobile/time.ts index cc0e791c..4cc39120 100644 --- a/packages/fhir/src/mobile/time.ts +++ b/packages/fhir/src/mobile/time.ts @@ -6,7 +6,7 @@ // SPDX-License-Identifier: MIT // -import { mobileEffectiveCanonicalization } from './measurement-catalog.generated.js' +import { mobileEffectiveCanonicalization } from '../contract/measurement-catalog.generated.js' import { err, parseFhirInstant, diff --git a/packages/fhir/src/mobile/types.ts b/packages/fhir/src/mobile/types.ts index 392ad4f6..84b626d0 100644 --- a/packages/fhir/src/mobile/types.ts +++ b/packages/fhir/src/mobile/types.ts @@ -6,7 +6,7 @@ // SPDX-License-Identifier: MIT // -import type { SharedMobileMeasurementKind } from './measurement-catalog.generated.js' +import type { SharedMobileMeasurementKind } from '../contract/measurement-catalog.generated.js' import type { AbsoluteUri, FhirId, @@ -15,10 +15,10 @@ import type { } from '../core/index.js' type SharedCatalog = - typeof import('./measurement-catalog.generated.js').sharedMobileMeasurementCatalog + typeof import('../contract/measurement-catalog.generated.js').sharedMobileMeasurementCatalog type OpaqueIdentityDefinition = - (typeof import('./measurement-catalog.generated.js').groveExchangeProtocol)['opaqueIdentity']['identityKinds'][number] + (typeof import('../contract/measurement-catalog.generated.js').groveExchangeProtocol)['opaqueIdentity']['identityKinds'][number] /** Kinds whose catalog definition declares the given value kind and effective. */ type MeasurementKindsWhere< diff --git a/packages/fhir/src/providers/builder.ts b/packages/fhir/src/providers/builder.ts index 69f04593..6dee579d 100644 --- a/packages/fhir/src/providers/builder.ts +++ b/packages/fhir/src/providers/builder.ts @@ -6,7 +6,7 @@ // SPDX-License-Identifier: MIT // -import { providerAdapterCatalog } from './contract.generated.js' +import { providerAdapterCatalog } from '../contract/providers.generated.js' import { assemblerAgent, coding, diff --git a/packages/fhir/src/providers/identity.ts b/packages/fhir/src/providers/identity.ts index 0b499ff2..cc33d5f7 100644 --- a/packages/fhir/src/providers/identity.ts +++ b/packages/fhir/src/providers/identity.ts @@ -12,7 +12,7 @@ import { providerRawOutputRoles, providerScalarOutputDiscriminators, providerScalarOutputRoles, -} from './contract.generated.js' +} from '../contract/providers.generated.js' import type { ConnectedProvider, ProviderScopeIdentifierInput, @@ -35,7 +35,7 @@ import { isEventIdentityValue, validateDeploymentIdentity, } from '../mobile/identity.js' -import { groveProfileClaims } from '../mobile/measurement-catalog.generated.js' +import { groveProfileClaims } from '../contract/measurement-catalog.generated.js' import type { ApplicationDeviceInput, CompleteIdentifierInput, diff --git a/packages/fhir/src/providers/index.ts b/packages/fhir/src/providers/index.ts index 4ba3e917..cad380f2 100644 --- a/packages/fhir/src/providers/index.ts +++ b/packages/fhir/src/providers/index.ts @@ -29,7 +29,7 @@ export { type ProviderRecordEffectiveRules, type ProviderScalarOutputDiscriminators, type ProviderScalarOutputRoles, -} from './contract.generated.js' +} from '../contract/providers.generated.js' export { parseProviderMeasurementBundleInput, parseNormalizedProviderRecord, diff --git a/packages/fhir/src/providers/measurement-definition.ts b/packages/fhir/src/providers/measurement-definition.ts index e2e9dca2..54318687 100644 --- a/packages/fhir/src/providers/measurement-definition.ts +++ b/packages/fhir/src/providers/measurement-definition.ts @@ -11,9 +11,9 @@ import { groveProviderProfileCanonicals, providerAdapterCatalog, providerScalarOutputRoles, -} from './contract.generated.js' +} from '../contract/providers.generated.js' import type { ConnectedProvider } from './types.js' -import { sharedMobileMeasurementCatalog } from '../mobile/measurement-catalog.generated.js' +import { sharedMobileMeasurementCatalog } from '../contract/measurement-catalog.generated.js' export interface ProviderMeasurementDefinition { readonly id: string diff --git a/packages/fhir/src/providers/profiles.ts b/packages/fhir/src/providers/profiles.ts index 31f5d762..5fde8de6 100644 --- a/packages/fhir/src/providers/profiles.ts +++ b/packages/fhir/src/providers/profiles.ts @@ -12,7 +12,7 @@ import { providerAdapterCatalog, groveProviderPackageCanonicals, groveProviderProfileCanonicals, -} from './contract.generated.js' +} from '../contract/providers.generated.js' import { groveMobileContract } from '../mobile/contract.js' export const PROVIDER_RECORDING_OUTPUT_ROLE: 'native-recording' = diff --git a/packages/fhir/src/providers/provider-input-schemas.ts b/packages/fhir/src/providers/provider-input-schemas.ts index a16e41b3..b402abb8 100644 --- a/packages/fhir/src/providers/provider-input-schemas.ts +++ b/packages/fhir/src/providers/provider-input-schemas.ts @@ -7,7 +7,7 @@ // import { z } from 'zod' -import { providerAdapterCatalog } from './contract.generated.js' +import { providerAdapterCatalog } from '../contract/providers.generated.js' import { SYSTEMS } from './profiles.js' import type { ProviderPatientReferenceInput, @@ -20,7 +20,7 @@ import { type Issue, } from '../core/index.js' import { containsIsolatedSurrogate } from '../mobile/identity.js' -import { groveExchangeProtocol } from '../mobile/measurement-catalog.generated.js' +import { groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' import { canonicalizeMobileEffectiveInstant } from '../mobile/time.js' import type { ApplicationDeviceInput, diff --git a/packages/fhir/src/providers/provider.ts b/packages/fhir/src/providers/provider.ts index ca90cbc7..0ec317a4 100644 --- a/packages/fhir/src/providers/provider.ts +++ b/packages/fhir/src/providers/provider.ts @@ -11,7 +11,7 @@ import { providerRecordEffectiveRules, providerScalarOutputDiscriminators, providerScalarOutputRoles, -} from './contract.generated.js' +} from '../contract/providers.generated.js' import { deriveProviderIdentities } from './identity.js' import { connectedProviderExclusiveDefinitions, @@ -55,7 +55,7 @@ import { import { sharedMobileMeasurementCatalog, type SharedMobileMeasurementKind, -} from '../mobile/measurement-catalog.generated.js' +} from '../contract/measurement-catalog.generated.js' import type { ChoiceQuantityMeasurementKind, InstantCodedMeasurementKind, diff --git a/packages/fhir/src/providers/recording.ts b/packages/fhir/src/providers/recording.ts index 51d29a43..3542b5b5 100644 --- a/packages/fhir/src/providers/recording.ts +++ b/packages/fhir/src/providers/recording.ts @@ -13,7 +13,7 @@ import { providerAdapterCatalog, providerRawOutputRoles, type ProviderRecordingFormat, -} from './contract.generated.js' +} from '../contract/providers.generated.js' import { assemblerAgent, coding, diff --git a/packages/fhir/src/providers/retraction.ts b/packages/fhir/src/providers/retraction.ts index 9a9722a3..be406afe 100644 --- a/packages/fhir/src/providers/retraction.ts +++ b/packages/fhir/src/providers/retraction.ts @@ -12,7 +12,7 @@ import { providerRawOutputRoles, providerScalarOutputDiscriminators, providerScalarOutputRoles, -} from './contract.generated.js' +} from '../contract/providers.generated.js' import { concept, deduplicateIdentifiedEntries, @@ -66,7 +66,7 @@ import { deriveOpaqueIdentifier, validateDeploymentIdentity, } from '../mobile/identity.js' -import { type groveExchangeProtocol } from '../mobile/measurement-catalog.generated.js' +import { type groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' import type { ApplicationDeviceInput, CompleteIdentifierInput, diff --git a/packages/fhir/src/providers/types.ts b/packages/fhir/src/providers/types.ts index 7b0670ee..c25ece03 100644 --- a/packages/fhir/src/providers/types.ts +++ b/packages/fhir/src/providers/types.ts @@ -10,9 +10,9 @@ import type { AdapterMeasurementCatalog, ProviderRawOutputRoles, ProviderScalarOutputRoles, -} from './contract.generated.js' +} from '../contract/providers.generated.js' import type { AbsoluteUri, FhirId, FhirInstant } from '../core/index.js' -import type { SharedMobileMeasurementKind } from '../mobile/measurement-catalog.generated.js' +import type { SharedMobileMeasurementKind } from '../contract/measurement-catalog.generated.js' import type { ApplicationDeviceInput, CompleteIdentifierInput, @@ -65,7 +65,7 @@ export type SupportedConnectedProviderMeasurementKind< > = keyof ScalarOutputRoles[Provider][SourceType] & string type ProviderContractRow = Extract< - (typeof import('./contract.generated.js').providerAdapterCatalog)['providers'][number], + (typeof import('../contract/providers.generated.js').providerAdapterCatalog)['providers'][number], { readonly id: Provider } > @@ -332,10 +332,10 @@ interface RecordingAttachmentBaseInput { } export type ProviderRecordingFormat = - import('./contract.generated.js').ProviderRecordingFormat + import('../contract/providers.generated.js').ProviderRecordingFormat export type RawPayloadAdmissionAssertion = - (typeof import('./contract.generated.js').providerAdapterCatalog)['rawPayloadAdmission']['allowedAssertions'][number] + (typeof import('../contract/providers.generated.js').providerAdapterCatalog)['rawPayloadAdmission']['allowedAssertions'][number] export interface EmbeddedRecordingAttachmentInput extends RecordingAttachmentBaseInput { readonly kind: 'embedded' diff --git a/packages/fhir/src/questionnaire/builder.ts b/packages/fhir/src/questionnaire/builder.ts index 9deece5e..16bdbb57 100644 --- a/packages/fhir/src/questionnaire/builder.ts +++ b/packages/fhir/src/questionnaire/builder.ts @@ -7,7 +7,7 @@ // import type { z } from 'zod' -import { groveQuestionnaireProfileCanonicals } from './contract.generated.js' +import { groveQuestionnaireProfileCanonicals } from '../contract/questionnaire.generated.js' import { validateQuestionnaireContract, validateQuestionnaireResponseItemContract, diff --git a/packages/fhir/src/questionnaire/index.ts b/packages/fhir/src/questionnaire/index.ts index 3e43098b..3a5b87e6 100644 --- a/packages/fhir/src/questionnaire/index.ts +++ b/packages/fhir/src/questionnaire/index.ts @@ -11,7 +11,7 @@ export { groveFhirContractVersion, groveFhirVersion, groveQuestionnairePackageMetadata, -} from './contract.generated.js' +} from '../contract/questionnaire.generated.js' export { parseQuestionnaire, parseQuestionnaireResponse } from './parse.js' export { preflightQuestionnairePair } from './preflight.js' export { diff --git a/packages/fhir/src/questionnaire/parse.ts b/packages/fhir/src/questionnaire/parse.ts index b444068c..d0a92168 100644 --- a/packages/fhir/src/questionnaire/parse.ts +++ b/packages/fhir/src/questionnaire/parse.ts @@ -7,7 +7,7 @@ // import { type z } from 'zod' -import { groveQuestionnaireProfileCanonicals } from './contract.generated.js' +import { groveQuestionnaireProfileCanonicals } from '../contract/questionnaire.generated.js' import { extensionValue, extensionsFor, diff --git a/packages/fhir/src/r4/active-graph-semantics.ts b/packages/fhir/src/r4/active-graph-semantics.ts index 6799afba..0955684c 100644 --- a/packages/fhir/src/r4/active-graph-semantics.ts +++ b/packages/fhir/src/r4/active-graph-semantics.ts @@ -37,7 +37,7 @@ import { isOpaqueIdentityValue } from '../mobile/identity.js' import { groveProfileClaims, healthConnectDataOriginApplication, -} from '../mobile/measurement-catalog.generated.js' +} from '../contract/measurement-catalog.generated.js' export { hasAdmittedMobileObservationProfile } from './active-observation-semantics.js' export { validateExchangeEnvelope, type ValidatedEnvelope } diff --git a/packages/fhir/src/r4/active-observation-semantics.ts b/packages/fhir/src/r4/active-observation-semantics.ts index fab9c121..e1d8d4e9 100644 --- a/packages/fhir/src/r4/active-observation-semantics.ts +++ b/packages/fhir/src/r4/active-observation-semantics.ts @@ -20,12 +20,12 @@ import { groveMobileContract } from '../mobile/contract.js' import { groveProfileClaims, sharedMobileMeasurementCatalog, -} from '../mobile/measurement-catalog.generated.js' +} from '../contract/measurement-catalog.generated.js' import { adapterMeasurementCatalog, groveProviderProfileCanonicals, providerAdapterCatalog, -} from '../providers/contract.generated.js' +} from '../contract/providers.generated.js' import { compareFhirDateTimes } from '../zod/support.js' interface MeasurementDefinition { diff --git a/packages/fhir/src/r4/diagnostics.ts b/packages/fhir/src/r4/diagnostics.ts index 9c46c532..1b84fbb8 100644 --- a/packages/fhir/src/r4/diagnostics.ts +++ b/packages/fhir/src/r4/diagnostics.ts @@ -7,7 +7,7 @@ // import type { Issue } from '../core/index.js' -import { groveExchangeRuleDiagnostics } from '../mobile/measurement-catalog.generated.js' +import { groveExchangeRuleDiagnostics } from '../contract/measurement-catalog.generated.js' const ENCODED_RULE_PREFIX = 'grove-producer-rule:' diff --git a/packages/fhir/src/r4/graph-schema-utils.ts b/packages/fhir/src/r4/graph-schema-utils.ts index 4ccdd440..f10a5c7d 100644 --- a/packages/fhir/src/r4/graph-schema-utils.ts +++ b/packages/fhir/src/r4/graph-schema-utils.ts @@ -14,7 +14,7 @@ import type { z } from 'zod' import { encodeGroveRuleDiagnostic } from './diagnostics.js' import { parseAbsoluteUri } from '../core/index.js' import { groveMobileContract } from '../mobile/contract.js' -import { healthKitApplicationDeviceIdentity } from '../providers/contract.generated.js' +import { healthKitApplicationDeviceIdentity } from '../contract/providers.generated.js' export type UnknownRecord = Readonly> diff --git a/packages/fhir/src/r4/profile-semantics.ts b/packages/fhir/src/r4/profile-semantics.ts index 958211a7..9b2d9542 100644 --- a/packages/fhir/src/r4/profile-semantics.ts +++ b/packages/fhir/src/r4/profile-semantics.ts @@ -22,11 +22,11 @@ import { groveMobileContract } from '../mobile/contract.js' import { adapterSourceMarkerClaims, groveProfileClaims, -} from '../mobile/measurement-catalog.generated.js' +} from '../contract/measurement-catalog.generated.js' import { healthKitApplicationDeviceIdentity, healthKitClinicalRecordAdmission, -} from '../providers/contract.generated.js' +} from '../contract/providers.generated.js' const SHA1_BASE64 = /^[A-Za-z\d+/]{26}[AEIMQUYcgkosw048]=$/u diff --git a/packages/fhir/src/zod/r4/index.ts b/packages/fhir/src/zod/r4/index.ts index d320f7a3..8dbb1662 100644 --- a/packages/fhir/src/zod/r4/index.ts +++ b/packages/fhir/src/zod/r4/index.ts @@ -6,4 +6,4 @@ // SPDX-License-Identifier: MIT // -export * from './schemas.js' +export * from './schemas.generated.js' diff --git a/packages/fhir/src/zod/r4/schemas.ts b/packages/fhir/src/zod/r4/schemas.generated.ts similarity index 100% rename from packages/fhir/src/zod/r4/schemas.ts rename to packages/fhir/src/zod/r4/schemas.generated.ts diff --git a/packages/fhir/src/zod/r4b/index.ts b/packages/fhir/src/zod/r4b/index.ts index d320f7a3..8dbb1662 100644 --- a/packages/fhir/src/zod/r4b/index.ts +++ b/packages/fhir/src/zod/r4b/index.ts @@ -6,4 +6,4 @@ // SPDX-License-Identifier: MIT // -export * from './schemas.js' +export * from './schemas.generated.js' diff --git a/packages/fhir/src/zod/r4b/schemas.ts b/packages/fhir/src/zod/r4b/schemas.generated.ts similarity index 100% rename from packages/fhir/src/zod/r4b/schemas.ts rename to packages/fhir/src/zod/r4b/schemas.generated.ts diff --git a/packages/fhir/test/diagnostics.test.ts b/packages/fhir/test/diagnostics.test.ts index 3474ba47..0b5b0587 100644 --- a/packages/fhir/test/diagnostics.test.ts +++ b/packages/fhir/test/diagnostics.test.ts @@ -6,7 +6,7 @@ // SPDX-License-Identifier: MIT // -import { groveExchangeRuleDiagnostics } from '../src/mobile/measurement-catalog.generated.js' +import { groveExchangeRuleDiagnostics } from '../src/contract/measurement-catalog.generated.js' import { decodeGroveRuleDiagnostic, encodeGroveRuleDiagnostic, diff --git a/packages/fhir/test/mobile.test.ts b/packages/fhir/test/mobile.test.ts index 8048efed..7c5c14af 100644 --- a/packages/fhir/test/mobile.test.ts +++ b/packages/fhir/test/mobile.test.ts @@ -28,7 +28,7 @@ import { sharedMobileMeasurementCatalog, type MobileMeasurement, } from '../src/mobile/index.js' -import * as mobileContract from '../src/mobile/measurement-catalog.generated.js' +import * as mobileContract from '../src/contract/measurement-catalog.generated.js' const unwrap = (result: Result): T => { if (!result.ok) throw new Error(result.issues[0]?.message) diff --git a/packages/fhir/test/observation-semantics.test.ts b/packages/fhir/test/observation-semantics.test.ts index d9911266..cf0c9a9d 100644 --- a/packages/fhir/test/observation-semantics.test.ts +++ b/packages/fhir/test/observation-semantics.test.ts @@ -12,11 +12,11 @@ import { z } from 'zod' import { groveProfileClaims, sharedMobileMeasurementCatalog, -} from '../src/mobile/measurement-catalog.generated.js' +} from '../src/contract/measurement-catalog.generated.js' import { adapterMeasurementCatalog, groveProviderProfileCanonicals, -} from '../src/providers/contract.generated.js' +} from '../src/contract/providers.generated.js' import { hasAdmittedMobileObservationProfile, validateMobileObservationSemantics, diff --git a/packages/fhir/test/profile-semantics-boundaries.test.ts b/packages/fhir/test/profile-semantics-boundaries.test.ts index dd73ae3b..afc4111c 100644 --- a/packages/fhir/test/profile-semantics-boundaries.test.ts +++ b/packages/fhir/test/profile-semantics-boundaries.test.ts @@ -10,11 +10,11 @@ import { z } from 'zod' import { adapterSourceMarkerClaims, groveProfileClaims, -} from '../src/mobile/measurement-catalog.generated.js' +} from '../src/contract/measurement-catalog.generated.js' import { healthKitApplicationDeviceIdentity, healthKitClinicalRecordAdmission, -} from '../src/providers/contract.generated.js' +} from '../src/contract/providers.generated.js' import { decodeGroveRuleDiagnostic } from '../src/r4/diagnostics.js' import { hasAdmittedActiveDeviceProfile, diff --git a/packages/fhir/test/recording.test.ts b/packages/fhir/test/recording.test.ts index e4c19475..66a7abad 100644 --- a/packages/fhir/test/recording.test.ts +++ b/packages/fhir/test/recording.test.ts @@ -18,7 +18,7 @@ import { type FhirInstant, type Result, } from '../src/index.js' -import { adapterSourceMarkerClaims } from '../src/mobile/measurement-catalog.generated.js' +import { adapterSourceMarkerClaims } from '../src/contract/measurement-catalog.generated.js' import { buildProviderRecordingBundle, healthKitClinicalRecordAdmission, From 3324085e9abd824ef36ac985bc578531fb101727 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Mon, 31 Aug 2026 11:26:47 -0700 Subject: [PATCH 11/31] Order FHIR dateTime Values Under One Contract Policy --- packages/fhir/src/core/primitives.ts | 92 ++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 18 deletions(-) diff --git a/packages/fhir/src/core/primitives.ts b/packages/fhir/src/core/primitives.ts index 539f9331..31c59f97 100644 --- a/packages/fhir/src/core/primitives.ts +++ b/packages/fhir/src/core/primitives.ts @@ -188,15 +188,70 @@ const parseInstantParts = (value: unknown): InstantParts | undefined => { } } -const compareFractions = (left: string, right: string): -1 | 0 | 1 => { - const digits = Math.max(left.length, right.length) - for (let index = 0; index < digits; index += 1) { - const leftDigit = left[index] ?? '0' - const rightDigit = right[index] ?? '0' - if (leftDigit < rightDigit) return -1 - if (leftDigit > rightDigit) return 1 +interface ComparableFhirDateTime { + readonly epochSecond: number + /** Leap seconds sort after the preceding second and before the next POSIX second. */ + readonly phase: 0 | 1 + readonly fraction: string +} + +const FHIR_TIMESTAMP = + /^(?\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:)(?[0-5]\d|60)(?:\.(?\d+))?(?Z|[+-]\d{2}:\d{2})$/u +const FHIR_PARTIAL_DATE = /^\d{4}(?:-\d{2}(?:-\d{2})?)?$/u + +const comparableFhirDateTime = ( + value: string, +): ComparableFhirDateTime | undefined => { + const timestamp = FHIR_TIMESTAMP.exec(value)?.groups + if (timestamp !== undefined) { + const leap = timestamp.second === '60' + const parsed = Date.parse( + `${timestamp.prefix}${leap ? '59' : timestamp.second}${timestamp.offset}`, + ) + if (!Number.isFinite(parsed)) return undefined + return { + epochSecond: parsed / 1000 + (leap ? 1 : 0), + phase: leap ? 0 : 1, + fraction: timestamp.fraction ?? '', + } } - return 0 + if (!FHIR_PARTIAL_DATE.test(value)) return undefined + let suffix = '' + if (value.length === 4) suffix = '-01-01' + else if (value.length === 7) suffix = '-01' + const parsed = Date.parse(`${value}${suffix}T00:00:00Z`) + return Number.isFinite(parsed) ? + { epochSecond: parsed / 1000, phase: 1, fraction: '' } + : undefined +} + +const compareFraction = (left: string, right: string): -1 | 0 | 1 => { + const width = Math.max(left.length, right.length) + const normalizedLeft = left.padEnd(width, '0') + const normalizedRight = right.padEnd(width, '0') + if (normalizedLeft === normalizedRight) return 0 + return normalizedLeft < normalizedRight ? -1 : 1 +} + +/** + * Compares FHIR dateTime values without JavaScript Date's millisecond truncation. + * + * Partial values resolve to the start of the period they name, matching Grove's contract-defined + * ordering policy. Leap seconds retain their distinct position before the following POSIX second. + * `undefined` means an endpoint is malformed; primitive validation reports that defect separately. + */ +export const compareFhirDateTimes = ( + left: string, + right: string, +): -1 | 0 | 1 | undefined => { + const first = comparableFhirDateTime(left) + const second = comparableFhirDateTime(right) + if (first === undefined || second === undefined) return undefined + if (first.epochSecond !== second.epochSecond) { + return first.epochSecond < second.epochSecond ? -1 : 1 + } + if (first.phase !== second.phase) return first.phase < second.phase ? -1 : 1 + return compareFraction(first.fraction, second.fraction) } export const parseAbsoluteUri = (value: unknown): Result => { @@ -250,19 +305,20 @@ export const parseFhirInstant = (value: unknown): Result => { return ok(value as FhirInstant) } -/** Compares two fully validated FHIR instants without losing sub-millisecond precision. */ +/** Compares two fully validated FHIR instants under the one contract ordering policy. */ export const compareFhirInstants = ( left: unknown, right: unknown, ): Result<-1 | 0 | 1> => { - const leftParts = parseInstantParts(left) - const rightParts = parseInstantParts(right) - if (leftParts === undefined || rightParts === undefined) { - return err('invalid-date-time', 'Expected two valid FHIR instants.') - } - if (leftParts.epochSecond < rightParts.epochSecond) return ok(-1) - if (leftParts.epochSecond > rightParts.epochSecond) return ok(1) - return ok(compareFractions(leftParts.fraction, rightParts.fraction)) + const leftInstant = parseFhirInstant(left) + const rightInstant = parseFhirInstant(right) + const ordering = + leftInstant.ok && rightInstant.ok ? + compareFhirDateTimes(leftInstant.value, rightInstant.value) + : undefined + return ordering === undefined ? + err('invalid-date-time', 'Expected two valid FHIR instants.') + : ok(ordering) } export const parsePatientReference = ( @@ -330,7 +386,7 @@ export const parseSemVer = (value: unknown): Result => { * The three primitives share one lexical space: a year, a year and month, a calendar day, or a * full timestamp with an offset. A value stated to lower precision resolves to the start of the * period it names, in UTC, because a FHIR `date` carries no offset to resolve it against — the - * same reading `fhirDateTimeToEpoch` uses for ordering. + * same reading `compareFhirDateTimes` uses for ordering. * * `Date` holds milliseconds, so a value stating more digits is truncated rather than rounded: a * truncated instant still falls inside the second it was recorded in. From 35d41cd6b4d87e3b231e5e769edde26c2cb8e230 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Mon, 31 Aug 2026 11:26:52 -0700 Subject: [PATCH 12/31] Lift Base64 Coding and Issue Construction Into Core --- packages/fhir/src/core/base64.ts | 68 ++++++++++++++++++ packages/fhir/src/core/index.ts | 8 +++ packages/fhir/src/core/result.ts | 107 ++++++++++------------------ packages/fhir/src/extract/encode.ts | 4 +- 4 files changed, 114 insertions(+), 73 deletions(-) create mode 100644 packages/fhir/src/core/base64.ts diff --git a/packages/fhir/src/core/base64.ts b/packages/fhir/src/core/base64.ts new file mode 100644 index 00000000..01d2abdb --- /dev/null +++ b/packages/fhir/src/core/base64.ts @@ -0,0 +1,68 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +const STANDARD_ALPHABET = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' +const URL_ALPHABET = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_' + +export interface Base64Options { + /** Use the URL and filename safe alphabet, which carries no `=` padding. */ + readonly urlSafe?: boolean +} + +export const encodeBase64 = ( + bytes: Uint8Array, + options: Base64Options = {}, +): string => { + const urlSafe = options.urlSafe === true + const alphabet = urlSafe ? URL_ALPHABET : STANDARD_ALPHABET + const padding = urlSafe ? '' : '=' + let output = '' + for (let index = 0; index < bytes.length; index += 3) { + const first = bytes[index] ?? 0 + const second = bytes[index + 1] + const third = bytes[index + 2] + const block = (first << 16) | ((second ?? 0) << 8) | (third ?? 0) + output += alphabet.charAt((block >>> 18) & 63) + output += alphabet.charAt((block >>> 12) & 63) + output += + second === undefined ? padding : alphabet.charAt((block >>> 6) & 63) + output += third === undefined ? padding : alphabet.charAt(block & 63) + } + return output +} + +/** + * The bytes `value` encodes, but only when it is their exact canonical encoding. + * + * Re-encoding is the whole check: it rejects a foreign alphabet, wrong padding, and + * the non-zero trailing bits a lenient decoder would silently discard. + */ +export const decodeCanonicalBase64 = ( + value: string, + options: Base64Options = {}, +): Uint8Array | undefined => { + const alphabet = options.urlSafe === true ? URL_ALPHABET : STANDARD_ALPHABET + const digits = value.replace(/={1,2}$/u, '') + const bytes: number[] = [] + let accumulator = 0 + let bits = 0 + for (const character of digits) { + const digit = alphabet.indexOf(character) + if (digit < 0) return undefined + accumulator = (accumulator << 6) | digit + bits += 6 + if (bits >= 8) { + bits -= 8 + bytes.push((accumulator >>> bits) & 0xff) + } + } + const decoded = Uint8Array.from(bytes) + return encodeBase64(decoded, options) === value ? decoded : undefined +} diff --git a/packages/fhir/src/core/index.ts b/packages/fhir/src/core/index.ts index c944d8f3..397b101c 100644 --- a/packages/fhir/src/core/index.ts +++ b/packages/fhir/src/core/index.ts @@ -6,6 +6,11 @@ // SPDX-License-Identifier: MIT // +export { + decodeCanonicalBase64, + encodeBase64, + type Base64Options, +} from './base64.js' export { deepFreeze, type ReadonlyDeep } from './freeze.js' export { cloneJsonValue, type JsonValue } from './json.js' export { @@ -38,9 +43,12 @@ export { export { collectResults, err, + issue, issues, mapResult, ok, + zodIssuePath, + zodIssueToIssue, type Issue, type IssueCode, type IssueSeverity, diff --git a/packages/fhir/src/core/result.ts b/packages/fhir/src/core/result.ts index e7492a5d..29716c51 100644 --- a/packages/fhir/src/core/result.ts +++ b/packages/fhir/src/core/result.ts @@ -6,8 +6,16 @@ // SPDX-License-Identifier: MIT // +import type { z } from 'zod' + export type IssueSeverity = 'error' | 'warning' +/** + * Namespaced producer-rule identifier. The registry that names them lives above core, + * so this states only the contract's `namespace.rule` grammar. + */ +type GroveProducerRuleCode = `${string}.${string}` + export type IssueCode = | 'duplicate-identifier' | 'external-validation-required' @@ -23,7 +31,7 @@ export type IssueCode = | 'schema-invalid' | 'unsupported-measurement' | 'value-mismatch' - | `mobile-${string}` + | GroveProducerRuleCode export interface Issue { readonly severity: IssueSeverity @@ -62,92 +70,49 @@ export const issues = (entries: readonly Issue[]): Result => ({ issues: entries, }) -type InspectedResult = - | { - readonly ok: true - readonly value: T - readonly warnings: readonly Issue[] - } - | { readonly ok: false; readonly issues: readonly Issue[] } +export const issue = ( + code: IssueCode, + path: Issue['path'], + message: string, +): Issue => ({ severity: 'error', code, path, message }) -const isIssue = (value: unknown): value is Issue => { - if (typeof value !== 'object' || value === null) return false - const candidate = value as Partial - return ( - ['error', 'warning'].includes(String(candidate.severity)) && - typeof candidate.code === 'string' && - Array.isArray(candidate.path) && - candidate.path.every( - (component) => - typeof component === 'string' || typeof component === 'number', - ) && - typeof candidate.message === 'string' && - (candidate.reason === undefined || typeof candidate.reason === 'string') && - (candidate.location === undefined || typeof candidate.location === 'string') +/** A zod path with symbol keys rendered as the text a caller can act on. */ +export const zodIssuePath = ( + entry: z.core.$ZodIssue, +): ReadonlyArray => + entry.path.map((component) => + typeof component === 'symbol' ? + (component.description ?? component.toString()) + : component, ) -} -const inspectResult = (value: unknown): InspectedResult | undefined => { - try { - if (typeof value !== 'object' || value === null) return undefined - const candidate = value as { - readonly ok?: unknown - readonly value?: T - readonly warnings?: unknown - readonly issues?: unknown - } - if (candidate.ok === true) { - const warnings = candidate.warnings ?? [] - return Array.isArray(warnings) && warnings.every(isIssue) ? - { ok: true, value: candidate.value as T, warnings } - : undefined - } - return ( - candidate.ok === false && - Array.isArray(candidate.issues) && - candidate.issues.every(isIssue) - ) ? - { ok: false, issues: candidate.issues } - : undefined - } catch { - return undefined - } -} - -const invalidResult = ( - path: ReadonlyArray = [], -): Result => - err('invalid-type', 'Expected a well-formed Grove Result value.', path) +/** A base schema failure as a Grove Issue; producer rules carry their own code. */ +export const zodIssueToIssue = (entry: z.core.$ZodIssue): Issue => ({ + severity: 'error', + code: 'schema-invalid', + path: zodIssuePath(entry), + message: entry.message, +}) export const mapResult = ( result: Result, transform: (value: T) => U, -): Result => { - const inspected = inspectResult(result) - if (inspected === undefined) return invalidResult() - return inspected.ok ? - ok(transform(inspected.value), inspected.warnings) - : inspected -} +): Result => + result.ok ? ok(transform(result.value), result.warnings ?? []) : result export const collectResults = ( results: ReadonlyArray>, ): Result => { - if (!Array.isArray(results)) return invalidResult() const values: T[] = [] const failures: Issue[] = [] const warnings: Issue[] = [] - for (const [index, result] of results.entries()) { - const inspected = inspectResult(result) - if (inspected === undefined) { - const invalid = invalidResult([index]) - if (!invalid.ok) failures.push(...invalid.issues) - } else if (inspected.ok) { - values.push(inspected.value) - warnings.push(...inspected.warnings) + for (const result of results) { + if (result.ok) { + values.push(result.value) + warnings.push(...(result.warnings ?? [])) } else { - failures.push(...inspected.issues) + failures.push(...result.issues) } } diff --git a/packages/fhir/src/extract/encode.ts b/packages/fhir/src/extract/encode.ts index 8c35d3e4..91fca277 100644 --- a/packages/fhir/src/extract/encode.ts +++ b/packages/fhir/src/extract/encode.ts @@ -95,8 +95,8 @@ export const toFhirReference = ( /** * Bytes as the FHIR `base64Binary` that carries them. * - * Unpadded and unwrapped: the lexical form admits whitespace, but a value written without it is - * what every producer emits and what `decodeBase64Binary` reads back unchanged. + * Unwrapped: the lexical form admits whitespace, but a value written without it is what every + * producer emits and what `decodeBase64Binary` reads back unchanged. */ export const encodeBase64Binary = (bytes: Uint8Array): string => { let binary = '' From 78523e4374585dde445d05b36ee80149d84a4422 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Mon, 31 Aug 2026 11:27:03 -0700 Subject: [PATCH 13/31] Derive Identity Prefixes From the Contract and Freeze the Deployment Handle --- packages/fhir/src/mobile/contract.ts | 36 ++- packages/fhir/src/mobile/identity.ts | 222 +++++++++-------- packages/fhir/src/mobile/index.ts | 1 - packages/fhir/src/mobile/types.ts | 38 +-- packages/fhir/src/providers/builder.ts | 231 +++++++----------- packages/fhir/src/providers/identity.ts | 87 ++++--- .../src/providers/provider-input-schemas.ts | 120 ++++----- 7 files changed, 377 insertions(+), 358 deletions(-) diff --git a/packages/fhir/src/mobile/contract.ts b/packages/fhir/src/mobile/contract.ts index a5dba5fe..04e647dd 100644 --- a/packages/fhir/src/mobile/contract.ts +++ b/packages/fhir/src/mobile/contract.ts @@ -16,7 +16,6 @@ import { deepFreeze } from '../core/index.js' export interface GroveMobileProtocolContract { readonly canonical: string - readonly version: string readonly fhirVersion: string readonly protocolVersion: number readonly profiles: Readonly<{ @@ -32,6 +31,7 @@ export interface GroveMobileProtocolContract { readonly extensions: Readonly<{ entryNodeKey: string retractionTargetRole: string + retractionTargetNativeIdentifier: string }> readonly systems: Readonly<{ identifierRole: string @@ -44,6 +44,11 @@ export interface GroveMobileProtocolContract { fullUrlNamespace: string opaqueIdentifierRoles: readonly string[] resourceIdentifierPriority: readonly string[] + valuePrefixes: Readonly<{ + opaque: string + event: string + entryNode: string + }> }> readonly lifecycle: Readonly<{ sourceRecordRetracted: string @@ -55,9 +60,20 @@ export interface GroveMobileProtocolContract { readonly recordingFormats: typeof groveRecordingFormatRegistry } +// Each identity value form opens with its revision token, so the contract owns every +// persisted prefix and a renumbered revision reaches this package by regeneration alone. +const valuePrefix = (valueForm: string): string => + `${valueForm.slice(0, valueForm.indexOf(':'))}:` + +// Canonicals the implementation guide owns that the pinned catalog release does not yet +// publish. The catalog spread overrides each one the moment a re-pin carries it. +const unpinnedExtensionCanonicals = { + retractionTargetNativeIdentifier: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-native-identifier', +} as const + const groveMobileContractValue: GroveMobileProtocolContract = { canonical: groveMobilePackageMetadata.canonical, - version: groveMobilePackageMetadata.version, fhirVersion: groveMobilePackageMetadata.fhirVersion, protocolVersion: groveExchangeProtocol.protocolVersion, profiles: { @@ -71,12 +87,24 @@ const groveMobileContractValue: GroveMobileProtocolContract = { retractionBundle: groveExchangeProtocol.profiles.retractionBundle, retractionProvenance: groveExchangeProtocol.profiles.retractionProvenance, }, - extensions: groveExchangeProtocol.extensions, + extensions: { + ...unpinnedExtensionCanonicals, + ...groveExchangeProtocol.extensions, + }, systems: groveExchangeProtocol.codeSystems, identity: { domain: groveExchangeProtocol.opaqueIdentity.domain, entryNodeDomain: groveExchangeProtocol.entryIdentity.entryNode.domain, fullUrlNamespace: groveExchangeProtocol.entryIdentity.fullUrl.namespace, + valuePrefixes: { + opaque: valuePrefix(groveExchangeProtocol.opaqueIdentity.valueForm), + event: valuePrefix( + groveExchangeProtocol.event.bundleIdentifier.valueForm, + ), + entryNode: valuePrefix( + groveExchangeProtocol.entryIdentity.entryNode.valueForm, + ), + }, opaqueIdentifierRoles: [ ...new Set( groveExchangeProtocol.opaqueIdentity.identityKinds.map( @@ -100,7 +128,7 @@ const groveMobileContractValue: GroveMobileProtocolContract = { recordingFormats: groveRecordingFormatRegistry, } -/** Runtime constants projected from the pinned Grove protocol catalog. */ +/** Runtime constants projected from the generated Grove FHIR protocol catalog. */ export const groveMobileContract: GroveMobileProtocolContract = deepFreeze( groveMobileContractValue, ) diff --git a/packages/fhir/src/mobile/identity.ts b/packages/fhir/src/mobile/identity.ts index 95c94966..21e30698 100644 --- a/packages/fhir/src/mobile/identity.ts +++ b/packages/fhir/src/mobile/identity.ts @@ -10,7 +10,6 @@ import { hmac } from '@noble/hashes/hmac.js' import { sha256 } from '@noble/hashes/sha2.js' import { v5 as uuidV5 } from 'uuid' import { groveMobileContract } from './contract.js' -import { groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' import type { CompleteIdentifierInput, DeploymentIdentityInput, @@ -18,10 +17,15 @@ import type { GroveOpaqueIdentityKind, IdentifiedEntryIdentityInput, } from './types.js' +import { groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' +import { providerAdapterCatalog } from '../contract/providers.generated.js' import { cloneJsonValue, + decodeCanonicalBase64, deepFreeze, + encodeBase64, err, + mapResult, ok, parseAbsoluteUri, parseFhirId, @@ -31,7 +35,6 @@ import { type Result, type UrnUuid, } from '../core/index.js' -import { providerAdapterCatalog } from '../contract/providers.generated.js' const ASCII_TOKEN = /^[A-Za-z\d._-]+$/u const POSITIVE_DECIMAL = /^[1-9]\d*$/u @@ -39,26 +42,47 @@ const UNSIGNED_DECIMAL = /^(?:0|[1-9]\d*)$/u const LOWERCASE_ROLE = /^[a-z][a-z\d-]*$/u const CANONICAL_PRODUCER_UUID = /^[\da-f]{8}-[\da-f]{4}-[1-5][\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}$/u -const OPAQUE_VALUE = /^v0:[A-Za-z\d._-]+:[1-9]\d*:[A-Za-z\d_-]{43}$/u -const EVENT_VALUE = - /^e0:[\da-f]{8}-[\da-f]{4}-[1-5][\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}:[1-9]\d*$/u -const ENTRY_NODE_VALUE = /^n0:[a-z][a-z\d-]*:(?:0|[1-9]\d*):[A-Za-z\d_-]{43}$/u -const BASE64_URL = /^[A-Za-z\d_-]+$/u +const { + opaque: OPAQUE_PREFIX, + event: EVENT_PREFIX, + entryNode: ENTRY_NODE_PREFIX, +} = groveMobileContract.identity.valuePrefixes +const OPAQUE_VALUE = new RegExp( + `^${OPAQUE_PREFIX}[A-Za-z\\d._-]+:[1-9]\\d*:[A-Za-z\\d_-]{43}$`, + 'u', +) +const EVENT_VALUE = new RegExp( + `^${EVENT_PREFIX}[\\da-f]{8}-[\\da-f]{4}-[1-5][\\da-f]{3}-[89ab][\\da-f]{3}-[\\da-f]{12}:[1-9]\\d*$`, + 'u', +) +const ENTRY_NODE_VALUE = new RegExp( + `^${ENTRY_NODE_PREFIX}[a-z][a-z\\d-]*:(?:0|[1-9]\\d*):[A-Za-z\\d_-]{43}$`, + 'u', +) const MINIMUM_HMAC_KEY_BYTES = 32 const PROVIDER_CODES: ReadonlySet = new Set( providerAdapterCatalog.providers.map(({ id }) => id), ) -const PROVIDER_IDENTITY_KINDS: ReadonlySet = new Set([ - 'provider-record', - 'provider-output', - 'provider-artifact', -]) -const GENERIC_SOURCE_IDENTITY_KINDS: ReadonlySet = new Set([ - 'source-record', - 'source-output', - 'source-artifact', -]) +// A kind's coordinate family is written in its components: the first names the adapter +// space, and only source-record coordinates carry a native record id. +const identityKindsCoordinatedBy = ( + adapterComponent: string, +): ReadonlySet => + new Set( + groveExchangeProtocol.opaqueIdentity.identityKinds + .filter( + ({ components }) => + components[0] === adapterComponent && + (components as readonly string[]).includes('native-record-id'), + ) + .map(({ kind }) => kind), + ) + +const PROVIDER_IDENTITY_KINDS: ReadonlySet = + identityKindsCoordinatedBy('provider-code') +const GENERIC_SOURCE_IDENTITY_KINDS: ReadonlySet = + identityKindsCoordinatedBy('adapter-id') const identityKinds = groveExchangeProtocol.opaqueIdentity.identityKinds.map( ({ kind }) => kind, @@ -93,7 +117,7 @@ type StringComponentTuple = Readonly<{ [Index in keyof Components]: string }> -/** Exact ordered component tuples projected from the pinned protocol catalog. */ +/** Exact ordered component tuples projected from the Grove FHIR protocol catalog. */ export type GroveOpaqueIdentityComponents = Readonly<{ [Kind in GroveOpaqueIdentityKind]: StringComponentTuple< Extract['components'] @@ -186,52 +210,11 @@ export const encodeLengthFramedUtf8 = ( } } -const base64UrlWithoutPadding = (bytes: Uint8Array): string => { - const alphabet = - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_' - let result = '' - for (let index = 0; index < bytes.length; index += 3) { - const first = bytes[index] ?? 0 - const second = bytes[index + 1] - const third = bytes[index + 2] - const block = (first << 16) | ((second ?? 0) << 8) | (third ?? 0) - result += alphabet.charAt((block >>> 18) & 63) - result += alphabet.charAt((block >>> 12) & 63) - if (second !== undefined) result += alphabet.charAt((block >>> 6) & 63) - if (third !== undefined) result += alphabet.charAt(block & 63) - } - return result -} +const base64UrlWithoutPadding = (bytes: Uint8Array): string => + encodeBase64(bytes, { urlSafe: true }) -const decodeBase64UrlWithoutPadding = ( - value: string, -): Uint8Array | undefined => { - if ( - typeof value !== 'string' || - !BASE64_URL.test(value) || - value.length % 4 === 1 - ) { - return undefined - } - const alphabet = - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_' - const bytes: number[] = [] - let accumulator = 0 - let bits = 0 - for (const character of value) { - const digit = alphabet.indexOf(character) - if (digit < 0) return undefined - accumulator = (accumulator << 6) | digit - bits += 6 - if (bits >= 8) { - bits -= 8 - bytes.push((accumulator >>> bits) & 0xff) - } - } - if (bits > 0 && (accumulator & ((1 << bits) - 1)) !== 0) return undefined - const decoded = Uint8Array.from(bytes) - return base64UrlWithoutPadding(decoded) === value ? decoded : undefined -} +const decodeBase64UrlWithoutPadding = (value: string): Uint8Array | undefined => + decodeCanonicalBase64(value, { urlSafe: true }) const isPublicConformanceKey = (key: Uint8Array): boolean => key.length === 32 && key.every((value, index) => value === index) @@ -246,10 +229,37 @@ const DEPLOYMENT_IDENTITY_KEYS = [ 'secretBase64Url', ] as const +/** Deployment material proven complete once; its decoded HMAC secret stays module-private. */ +export interface ValidatedDeploymentIdentity { + readonly identity: DeploymentIdentityInput +} + +/** Raw deployment configuration, or material `validateDeploymentIdentity` already proved. */ +export type DeploymentIdentitySource = + DeploymentIdentityInput | ValidatedDeploymentIdentity + +interface ResolvedDeployment { + readonly handle: ValidatedDeploymentIdentity + readonly identity: DeploymentIdentityInput + readonly secret: Uint8Array +} + +// Membership is the proof, and only frozen material is admitted: a handle a caller holds +// can be neither forged nor edited afterwards, so reuse cannot smuggle in new material. +const publiclyValidatedDeployments = new WeakSet() +const deploymentSecrets = new WeakMap() + +const isPubliclyValidated = ( + source: unknown, +): source is ValidatedDeploymentIdentity => + typeof source === 'object' && + source !== null && + publiclyValidatedDeployments.has(source as ValidatedDeploymentIdentity) + const validateDeploymentIdentityInternal = ( input: unknown, allowPublicConformanceKey: boolean, -): Result => { +): Result => { const snapshot = cloneJsonValue(input) if (!snapshot.ok) return snapshot if ( @@ -347,7 +357,10 @@ const validateDeploymentIdentityInternal = ( ['producerInstance'], ) } - const secret = decodeBase64UrlWithoutPadding(candidate.secretBase64Url) + const secret = + typeof candidate.secretBase64Url === 'string' ? + decodeBase64UrlWithoutPadding(candidate.secretBase64Url) + : undefined if (secret === undefined || secret.byteLength < MINIMUM_HMAC_KEY_BYTES) { return err( 'invalid-identifier', @@ -362,17 +375,35 @@ const validateDeploymentIdentityInternal = ( ['secretBase64Url'], ) } - return ok(candidate) + const identity = deepFreeze(candidate) as DeploymentIdentityInput + const handle: ValidatedDeploymentIdentity = Object.freeze({ identity }) + deploymentSecrets.set(handle, secret) + // Only publicly validated material is reusable; the conformance seam revalidates. + if (!allowPublicConformanceKey) publiclyValidatedDeployments.add(handle) + return ok({ handle, identity, secret }) +} + +const resolveDeployment = ( + source: unknown, + allowPublicConformanceKey: boolean, +): Result => { + if (isPubliclyValidated(source)) { + const secret = deploymentSecrets.get(source) + if (secret !== undefined) { + return ok({ handle: source, identity: source.identity, secret }) + } + } + return validateDeploymentIdentityInternal(source, allowPublicConformanceKey) } /** Validates deployment identity material for every public production/runtime operation. */ export const validateDeploymentIdentity = ( input: unknown, -): Result => - validateDeploymentIdentityInternal(input, false) +): Result => + mapResult(resolveDeployment(input, false), ({ handle }) => handle) const deriveOpaqueIdentifierInternal = ( - deployment: DeploymentIdentityInput, + deployment: DeploymentIdentitySource, identityKind: Kind, components: GroveOpaqueIdentityComponents[Kind], allowPublicConformanceKey: boolean, @@ -383,14 +414,11 @@ const deriveOpaqueIdentifierInternal = ( ) { return err( 'invalid-code', - 'Identity kind is not part of the closed Grove v0 identity contract.', + 'Identity kind is not part of the closed Grove identity contract.', ['identityKind'], ) } - const validated = validateDeploymentIdentityInternal( - deployment, - allowPublicConformanceKey, - ) + const validated = resolveDeployment(deployment, allowPublicConformanceKey) if (!validated.ok) return validated const componentSnapshot = cloneJsonValue(components) if (!componentSnapshot.ok) return componentSnapshot @@ -448,24 +476,18 @@ const deriveOpaqueIdentifierInternal = ( ...componentSnapshot.value, ]) if (!preimage.ok) return preimage - const secret = decodeBase64UrlWithoutPadding(validated.value.secretBase64Url) - if (secret === undefined) { - return err( - 'invalid-identifier', - 'Identity secret is not canonical base64url.', - ) - } + const { identity, secret } = validated.value const digest = hmac(sha256, secret, preimage.value) return ok({ - system: validated.value.opaqueIdentifierSystems[identityKind], - value: `v0:${validated.value.keyId}:${validated.value.keyEpoch}:${base64UrlWithoutPadding(digest)}`, + system: identity.opaqueIdentifierSystems[identityKind], + value: `${OPAQUE_PREFIX}${identity.keyId}:${identity.keyEpoch}:${base64UrlWithoutPadding(digest)}`, role: identifierRoleByKind[identityKind], }) } -/** Derives one deployment-owned, role-typed Grove v0 HMAC Identifier. */ +/** Derives one deployment-owned, role-typed Grove HMAC Identifier. */ export const deriveOpaqueIdentifier = ( - deployment: DeploymentIdentityInput, + deployment: DeploymentIdentitySource, identityKind: Kind, components: GroveOpaqueIdentityComponents[Kind], ): Result => @@ -506,10 +528,10 @@ export const deriveConformanceVectorOpaqueIdentifier = < /** Creates the sole event business Identifier for one immutable exchange assertion. */ export const deriveEventIdentifier = ( - deployment: DeploymentIdentityInput, + deployment: DeploymentIdentitySource, sequence: string, ): Result => { - const validated = validateDeploymentIdentity(deployment) + const validated = resolveDeployment(deployment, false) if (!validated.ok) return validated if (typeof sequence !== 'string' || !POSITIVE_DECIMAL.test(sequence)) { return err( @@ -519,13 +541,13 @@ export const deriveEventIdentifier = ( ) } return ok({ - system: validated.value.eventIdentifierSystem, - value: `e0:${validated.value.producerInstance}:${sequence}`, + system: validated.value.identity.eventIdentifierSystem, + value: `${EVENT_PREFIX}${validated.value.identity.producerInstance}:${sequence}`, role: 'event', }) } -/** Creates an unkeyed, event-scoped graph-node Identifier for a resource without business ID. */ +/** Derives the event-scoped node value a resource without a business Identifier is keyed by. */ export const deriveEntryNodeValue = ( eventSystem: string, eventValue: string, @@ -567,18 +589,18 @@ export const deriveEntryNodeValue = ( ]) if (!preimage.ok) return preimage return ok( - `n0:${role}:${ordinal}:${base64UrlWithoutPadding(sha256(preimage.value))}`, + `${ENTRY_NODE_PREFIX}${role}:${ordinal}:${base64UrlWithoutPadding(sha256(preimage.value))}`, ) } /** Creates an unkeyed, event-scoped graph-node Identifier for a resource without business ID. */ export const deriveEntryNodeIdentifier = ( - deployment: DeploymentIdentityInput, + deployment: DeploymentIdentitySource, event: CompleteIdentifierInput, role: string, ordinal: string, ): Result => { - const validated = validateDeploymentIdentity(deployment) + const validated = resolveDeployment(deployment, false) if (!validated.ok) return validated const eventSnapshot = cloneJsonValue(event) if ( @@ -595,11 +617,13 @@ export const deriveEntryNodeIdentifier = ( } const eventValue = eventSnapshot.value as unknown as CompleteIdentifierInput if ( - eventValue.system !== validated.value.eventIdentifierSystem || + eventValue.system !== validated.value.identity.eventIdentifierSystem || eventValue.role !== 'event' || typeof eventValue.value !== 'string' || !EVENT_VALUE.test(eventValue.value) || - !eventValue.value.startsWith(`e0:${validated.value.producerInstance}:`) + !eventValue.value.startsWith( + `${EVENT_PREFIX}${validated.value.identity.producerInstance}:`, + ) ) { return err( 'invalid-identifier', @@ -615,7 +639,7 @@ export const deriveEntryNodeIdentifier = ( ) if (!value.ok) return value return ok({ - system: validated.value.entryNodeIdentifierSystem, + system: validated.value.identity.entryNodeIdentifierSystem, value: value.value, role: 'entry-node', }) @@ -626,15 +650,15 @@ const hasCanonicalSha256DigestSuffix = (value: string): boolean => { return decodeBase64UrlWithoutPadding(digest)?.length === 32 } -export const isOpaqueIdentityValue = (value: string): boolean => +export const isOpaqueIdentityValue = (value: unknown): boolean => typeof value === 'string' && OPAQUE_VALUE.test(value) && hasCanonicalSha256DigestSuffix(value) -export const isEventIdentityValue = (value: string): boolean => +export const isEventIdentityValue = (value: unknown): boolean => typeof value === 'string' && EVENT_VALUE.test(value) -export const isEntryNodeIdentityValue = (value: string): boolean => +export const isEntryNodeIdentityValue = (value: unknown): boolean => typeof value === 'string' && ENTRY_NODE_VALUE.test(value) && hasCanonicalSha256DigestSuffix(value) diff --git a/packages/fhir/src/mobile/index.ts b/packages/fhir/src/mobile/index.ts index 91773a25..1ee068dc 100644 --- a/packages/fhir/src/mobile/index.ts +++ b/packages/fhir/src/mobile/index.ts @@ -25,7 +25,6 @@ export type { GroveMobileProtocolContract } from './contract.js' export { groveExchangeProtocol, groveProfileClaims, - groveFhirContractVersion, groveFhirVersion, groveMobilePackageMetadata, groveMobileProfileCanonicals, diff --git a/packages/fhir/src/mobile/types.ts b/packages/fhir/src/mobile/types.ts index 84b626d0..925c46e7 100644 --- a/packages/fhir/src/mobile/types.ts +++ b/packages/fhir/src/mobile/types.ts @@ -21,7 +21,7 @@ type OpaqueIdentityDefinition = (typeof import('../contract/measurement-catalog.generated.js').groveExchangeProtocol)['opaqueIdentity']['identityKinds'][number] /** Kinds whose catalog definition declares the given value kind and effective. */ -type MeasurementKindsWhere< +export type MeasurementKindsWhere< ValueKind extends string, Effective extends string, > = { @@ -123,14 +123,14 @@ export type SleepStage = SharedCatalog['sleep-stage']['allowedValues'][number] export interface SleepStageSourceCodingInput { readonly system: AbsoluteUri readonly code: string - readonly display?: string + readonly display?: string | undefined } export interface SleepStageMeasurement { readonly kind: 'sleep-stage' readonly stage: SleepStage /** Source-native stage retained when it is more precise than the shared stage. */ - readonly sourceStageCoding?: SleepStageSourceCodingInput + readonly sourceStageCoding?: SleepStageSourceCodingInput | undefined readonly effective: PeriodEffectiveTime } @@ -138,15 +138,15 @@ export interface SleepStageMeasurement { export interface CompleteIdentifierInput { readonly system: AbsoluteUri readonly value: string - /** Grove role carried in Identifier.type for deployment-owned v0 identities. */ + /** Grove role carried in Identifier.type for deployment-owned identities. */ readonly role?: GroveIdentifierRole } -/** Closed roles used to distinguish deployment-owned v0 identifier key spaces. */ +/** Closed roles used to distinguish deployment-owned identifier key spaces. */ export type GroveIdentifierRole = OpaqueIdentityDefinition['identifierRole'] | 'entry-node' | 'event' -/** Closed HMAC domains from the Grove exchange protocol. */ +/** Closed HMAC domains from the Grove FHIR exchange protocol. */ export type GroveOpaqueIdentityKind = OpaqueIdentityDefinition['kind'] export type GroveOpaqueIdentitySystems = Readonly< @@ -195,22 +195,22 @@ export interface IdentifiedEntryIdentityInput extends ResourceIdentityInput { export interface ApplicationDeviceInput { /** Deployment-governed token for this source application/build; never emitted in clear. */ readonly sourceDeviceToken: string - readonly id?: FhirId + readonly id?: FhirId | undefined readonly name: string - readonly version?: string - readonly build?: string - readonly manufacturer?: string + readonly version?: string | undefined + readonly build?: string | undefined + readonly manufacturer?: string | undefined /** Optional immutable snapshot of the hardware/OS hosting this application. */ - readonly host?: HostDeviceInput + readonly host?: HostDeviceInput | undefined } export interface HostDeviceInput { /** Event-time source token for this host snapshot; never emitted in clear. */ readonly sourceDeviceToken: string - readonly id?: FhirId - readonly name?: string - readonly manufacturer?: string - readonly modelNumber?: string + readonly id?: FhirId | undefined + readonly name?: string | undefined + readonly manufacturer?: string | undefined + readonly modelNumber?: string | undefined readonly operatingSystemVersion: string } @@ -219,10 +219,10 @@ interface RecordingDeviceBaseInput { readonly stableUnitToken: string /** Complete logical subject identity used only in the HMAC preimage. */ readonly subjectIdentifier: CompleteIdentifierInput - readonly id?: FhirId - readonly name?: string - readonly manufacturer?: string - readonly modelNumber?: string + readonly id?: FhirId | undefined + readonly name?: string | undefined + readonly manufacturer?: string | undefined + readonly modelNumber?: string | undefined } /** Recording-device identity must declare its privacy/disclosure scope. */ diff --git a/packages/fhir/src/providers/builder.ts b/packages/fhir/src/providers/builder.ts index 6dee579d..f7b0a92e 100644 --- a/packages/fhir/src/providers/builder.ts +++ b/packages/fhir/src/providers/builder.ts @@ -6,7 +6,6 @@ // SPDX-License-Identifier: MIT // -import { providerAdapterCatalog } from '../contract/providers.generated.js' import { assemblerAgent, coding, @@ -24,27 +23,35 @@ import { } from './graph.js' import { deriveApplicationEntryIdentity, - deriveDeviceSnapshotEntryIdentity, - deriveProviderIdentities, deriveRecordingDeviceEntryIdentity, deriveWriterRecordIdentifier, + resolveHostIdentity, + type ProviderIdentities, type RecordingDeviceGraphIdentity, } from './identity.js' import { providerMeasurementDefinition, providerMeasurementProfile, providerObservationProfile, - type ProviderMeasurementDefinition, + type MeasurementDefinition, } from './measurement-definition.js' import { EXTENSIONS, PROFILES, SYSTEMS } from './profiles.js' -import { - providerOutputCoordinates, - type ProviderOutputCoordinates, - parseProviderMeasurementBundleInput, -} from './provider.js' +import { parseProviderMeasurementBundle } from './provider.js' import type { ProviderMeasurementBundleInput } from './types.js' -import { issues, ok, type Result, type UrnUuid } from '../core/index.js' -import { createEntryIdentity } from '../mobile/identity.js' +import { providerAdapterCatalog } from '../contract/providers.generated.js' +import { + collectResults, + issues, + ok, + type Issue, + type Result, + type UrnUuid, +} from '../core/index.js' +import { + createEntryIdentity, + validateDeploymentIdentity, + type DeploymentIdentitySource, +} from '../mobile/identity.js' import type { CompleteIdentifierInput, IdentifiedEntryIdentityInput, @@ -94,7 +101,7 @@ type ProviderGraphResource = Parameters[1] const definitionFor = ( input: ProviderMeasurementBundleInput, kind: ConnectedMeasurement['kind'], -): ProviderMeasurementDefinition => { +): MeasurementDefinition => { const definition = providerMeasurementDefinition( input.source.adapter.provider, kind, @@ -107,7 +114,7 @@ const definitionFor = ( return definition } -const categoryFor = (definition: ProviderMeasurementDefinition) => { +const categoryFor = (definition: MeasurementDefinition) => { if (definition.category === undefined) return undefined return [ concept( @@ -383,7 +390,7 @@ const makeProvenance = ( const optionalRecordingDeviceIdentity = ( input: ProviderMeasurementBundleInput['source']['recordingDevice'], - deployment: ProviderMeasurementBundleInput['deploymentIdentity'], + deployment: DeploymentIdentitySource, event: CompleteIdentifierInput, adapterId: string, ): Result => @@ -396,25 +403,10 @@ type ResolvedGatewayIdentity = Pick< 'distinctGatewayApplication' | 'distinctGatewayHost' | 'gatewayReference' > -const resolveHostIdentity = ( - application: ProviderMeasurementBundleInput['application'], - deployment: ProviderMeasurementBundleInput['deploymentIdentity'], - event: CompleteIdentifierInput, -): Result => - application.host === undefined ? - ok(undefined) - : deriveDeviceSnapshotEntryIdentity( - deployment, - event, - application.host.sourceDeviceToken, - 'host', - application.host.id, - ) - const resolveGatewayIdentity = ( input: ProviderMeasurementBundleInput['gatewayApplication'], converter: IdentifiedEntryIdentityInput, - deployment: ProviderMeasurementBundleInput['deploymentIdentity'], + deployment: DeploymentIdentitySource, event: CompleteIdentifierInput, ): Result => { if (input === undefined) return ok({}) @@ -436,138 +428,116 @@ const resolveGatewayIdentity = ( }) } -const resolveProviderOutputCoordinates = ( +const missingAggregationIssues = ( input: ProviderMeasurementBundleInput, -): Result => { - for (const [index, measurement] of input.measurements.entries()) { - if (missingAggregationMethod(input, measurement.kind)) { - return issues([ +): readonly Issue[] => + input.measurements.flatMap((measurement, index) => + missingAggregationMethod(input, measurement.kind) ? + [ { - severity: 'error', - code: 'unsupported-measurement', + severity: 'error' as const, + code: 'unsupported-measurement' as const, path: ['measurements', index], message: `${input.source.adapter.provider}/${input.source.sourceType} declares no aggregation ` + `for ${measurement.kind}, so its Observation would omit the method its profile requires.`, }, - ]) - } - } - const outputCoordinates = input.measurements.map(({ kind }) => - providerOutputCoordinates( - input.source.adapter.provider, - input.source.sourceType, - kind, - ), + ] + : [], ) - if (outputCoordinates.some((coordinates) => coordinates === undefined)) { - return issues([ - { - severity: 'error', - code: 'unsupported-measurement', - path: ['measurements'], - message: 'No catalog-owned Provider output role exists.', - }, - ]) - } - return ok(outputCoordinates as readonly ProviderOutputCoordinates[]) + +// One output identity is derived per measurement, so a length mismatch is a builder defect +// rather than a caller error; it is still reported instead of thrown. +const misalignedIdentities = (path: ReadonlyArray) => + issues([ + { + severity: 'error' as const, + code: 'value-mismatch' as const, + path, + message: + 'Provider output identities do not align with the measurements they were derived from.', + }, + ]) + +const zipMeasurements = ( + input: ProviderMeasurementBundleInput, + values: readonly Value[], +): ReadonlyArray | undefined => { + const pairs = input.measurements.flatMap((measurement, index) => { + const value = values[index] + return value === undefined ? [] : [[measurement, value] as const] + }) + return pairs.length === values.length ? pairs : undefined } const resolveObservationIdentities = ( input: ProviderMeasurementBundleInput, outputs: readonly CompleteIdentifierInput[], ): Result => { - const observations: IdentifiedEntryIdentityInput[] = [] - for (const [index, output] of outputs.entries()) { - const measurement = input.measurements[index] - if (measurement === undefined) { - return issues([ - { - severity: 'error', - code: 'value-mismatch', - path: ['measurements'], - message: 'Provider output identities are incomplete.', - }, - ]) - } - const observation = createEntryIdentity( - output, - input.repositoryIds?.observations?.[measurement.kind], - ) - if (!observation.ok) return observation - observations.push(observation.value) - } - return ok(observations) + const pairs = zipMeasurements(input, outputs) + if (pairs === undefined) return misalignedIdentities(['measurements']) + return collectResults( + pairs.map(([measurement, output]) => + createEntryIdentity( + output, + input.repositoryIds?.observations?.[measurement.kind], + ), + ), + ) } const resolveGraphIdentities = ( input: ProviderMeasurementBundleInput, + connected: ProviderIdentities, ): Result => { - const outputCoordinates = resolveProviderOutputCoordinates(input) - if (!outputCoordinates.ok) return outputCoordinates - const connected = deriveProviderIdentities({ - provider: input.source.adapter.provider, - providerScopeIdentifier: input.source.providerScopeIdentifier, - sourceType: input.source.sourceType, - sourceNativeId: input.source.sourceNativeId, - outputs: outputCoordinates.value.map( - ({ outputRole, outputDiscriminator }) => ({ - kind: 'provider-output' as const, - outputRole, - outputDiscriminator, - }), - ), - eventSequence: input.eventSequence, - deployment: input.deploymentIdentity, - }) - if (!connected.ok) return connected + const aggregation = missingAggregationIssues(input) + if (aggregation.length > 0) return issues(aggregation) + const deployment = validateDeploymentIdentity(input.deploymentIdentity) + if (!deployment.ok) return deployment - const observations = resolveObservationIdentities( - input, - connected.value.outputs, - ) + const observations = resolveObservationIdentities(input, connected.outputs) if (!observations.ok) return observations const provenance = createEntryIdentity( - connected.value.provenanceNode, + connected.provenanceNode, input.repositoryIds?.provenance, ) if (!provenance.ok) return provenance const application = deriveApplicationEntryIdentity( - input.deploymentIdentity, - connected.value.event, + deployment.value, + connected.event, input.application, ) if (!application.ok) return application const applicationHost = resolveHostIdentity( input.application, - input.deploymentIdentity, - connected.value.event, + deployment.value, + connected.event, ) if (!applicationHost.ok) return applicationHost const gateway = resolveGatewayIdentity( input.gatewayApplication, application.value, - input.deploymentIdentity, - connected.value.event, + deployment.value, + connected.event, ) if (!gateway.ok) return gateway const dataOrigin = deriveApplicationEntryIdentity( - input.deploymentIdentity, - connected.value.event, + deployment.value, + connected.event, input.source.dataOrigin, ) if (!dataOrigin.ok) return dataOrigin const dataOriginHost = resolveHostIdentity( input.source.dataOrigin, - input.deploymentIdentity, - connected.value.event, + deployment.value, + connected.event, ) if (!dataOriginHost.ok) return dataOriginHost const recordingDevice = optionalRecordingDeviceIdentity( input.source.recordingDevice, - input.deploymentIdentity, - connected.value.event, + deployment.value, + connected.event, input.source.adapter.provider, ) if (!recordingDevice.ok) return recordingDevice @@ -576,15 +546,15 @@ const resolveGraphIdentities = ( input.source.writerRecord === undefined ? undefined : deriveWriterRecordIdentifier( - input.deploymentIdentity, + deployment.value, input.source.writerRecord.applicationIdentifier, input.source.writerRecord.nativeRecordId, ) if (writerRecord !== undefined && !writerRecord.ok) return writerRecord const resolved: ResolvedGraphIdentities = { - sourceRecord: connected.value.sourceRecord, - event: connected.value.event, + sourceRecord: connected.sourceRecord, + event: connected.event, observations: observations.value, provenance: provenance.value, application: application.value, @@ -608,20 +578,10 @@ const buildProviderObservationEntries = ( input: ProviderMeasurementBundleInput, identities: ResolvedGraphIdentities, ): Result => { - const entries: ProviderGraphEntry[] = [] - for (const [index, measurement] of input.measurements.entries()) { - const identity = identities.observations[index] - if (identity === undefined) { - return issues([ - { - severity: 'error', - code: 'value-mismatch', - path: ['measurements', index], - message: 'Provider output identities are incomplete.', - }, - ]) - } - entries.push( + const pairs = zipMeasurements(input, identities.observations) + if (pairs === undefined) return misalignedIdentities(['measurements']) + return ok( + pairs.map(([measurement, identity]) => identifiedEntry( identity, makeObservation( @@ -632,9 +592,8 @@ const buildProviderObservationEntries = ( aggregationMethodFor(input, measurement.kind), ), ), - ) - } - return ok(entries) + ), + ) } const optionalIdentifiedEntry = ( @@ -767,11 +726,11 @@ const buildProviderGraphEntries = ( export const buildProviderMeasurementBundle = ( input: ProviderMeasurementBundleInput, ): Result => { - const parsed = parseProviderMeasurementBundleInput(input) + const parsed = parseProviderMeasurementBundle(input) if (!parsed.ok) return parsed - const validatedInput = parsed.value + const { input: validatedInput, identities: connected } = parsed.value - const identities = resolveGraphIdentities(validatedInput) + const identities = resolveGraphIdentities(validatedInput, connected) if (!identities.ok) return identities const entry = buildProviderGraphEntries(validatedInput, identities.value) diff --git a/packages/fhir/src/providers/identity.ts b/packages/fhir/src/providers/identity.ts index cc33d5f7..6053e916 100644 --- a/packages/fhir/src/providers/identity.ts +++ b/packages/fhir/src/providers/identity.ts @@ -6,6 +6,11 @@ // SPDX-License-Identifier: MIT // +import type { + ConnectedProvider, + ProviderScopeIdentifierInput, +} from './types.js' +import { groveProfileClaims } from '../contract/measurement-catalog.generated.js' import { providerAdapterCatalog, providerRawOutputDiscriminators, @@ -13,10 +18,6 @@ import { providerScalarOutputDiscriminators, providerScalarOutputRoles, } from '../contract/providers.generated.js' -import type { - ConnectedProvider, - ProviderScopeIdentifierInput, -} from './types.js' import { cloneJsonValue, deepFreeze, @@ -27,6 +28,7 @@ import { type JsonValue, type Result, } from '../core/index.js' +import { groveMobileContract } from '../mobile/contract.js' import { createEntryIdentity, deriveEntryNodeIdentifier, @@ -34,11 +36,12 @@ import { deriveOpaqueIdentifier, isEventIdentityValue, validateDeploymentIdentity, + type DeploymentIdentitySource, } from '../mobile/identity.js' -import { groveProfileClaims } from '../contract/measurement-catalog.generated.js' import type { ApplicationDeviceInput, CompleteIdentifierInput, + HostDeviceInput, DeploymentIdentityInput, IdentifiedEntryIdentityInput, RecordingDeviceInput, @@ -75,7 +78,7 @@ const snapshotObject = (input: unknown): Result => { /** Derives an immutable event-scoped Device snapshot identity. */ export const deriveDeviceSnapshotEntryIdentity = ( - deployment: DeploymentIdentityInput, + deployment: DeploymentIdentitySource, event: CompleteIdentifierInput, sourceDeviceToken: string, role: DeviceSnapshotRole, @@ -92,11 +95,13 @@ export const deriveDeviceSnapshotEntryIdentity = ( typeof sourceDeviceToken !== 'string' || sourceDeviceToken.trim() === '' || !deviceSnapshotRoles.has(role) || - eventSystem !== safeDeployment.value.eventIdentifierSystem || + eventSystem !== safeDeployment.value.identity.eventIdentifierSystem || eventRole !== 'event' || typeof eventValue !== 'string' || !isEventIdentityValue(eventValue) || - !eventValue.startsWith(`e0:${safeDeployment.value.producerInstance}:`) + !eventValue.startsWith( + `${groveMobileContract.identity.valuePrefixes.event}${safeDeployment.value.identity.producerInstance}:`, + ) ) { return err( 'invalid-identifier', @@ -112,9 +117,25 @@ export const deriveDeviceSnapshotEntryIdentity = ( return createEntryIdentity(identifier.value, id) } +/** Derives the optional host snapshot identity an application declares, if any. */ +export const resolveHostIdentity = ( + application: { readonly host?: HostDeviceInput | undefined }, + deployment: DeploymentIdentitySource, + event: CompleteIdentifierInput, +): Result => + application.host === undefined ? + ok(undefined) + : deriveDeviceSnapshotEntryIdentity( + deployment, + event, + application.host.sourceDeviceToken, + 'host', + application.host.id, + ) + /** Derives an immutable event-scoped application Device snapshot identity. */ export const deriveApplicationEntryIdentity = ( - deployment: DeploymentIdentityInput, + deployment: DeploymentIdentitySource, event: CompleteIdentifierInput, input: ApplicationDeviceInput, ): Result => { @@ -148,7 +169,7 @@ export interface RecordingDeviceGraphIdentity { /** Derives a stable per-unit recording Device identity from admitted instance evidence. */ export const deriveRecordingDeviceEntryIdentity = ( - deployment: DeploymentIdentityInput, + deployment: DeploymentIdentitySource, event: CompleteIdentifierInput, adapterId: string, input: RecordingDeviceInput, @@ -206,7 +227,7 @@ export const deriveRecordingDeviceEntryIdentity = ( /** Optional writer-record lineage identity for a logical record assigned by an application. */ export const deriveWriterRecordIdentifier = ( - deployment: DeploymentIdentityInput, + deployment: DeploymentIdentitySource, writerApplication: CompleteIdentifierInput, writerRecordId: string, ): Result => { @@ -402,6 +423,7 @@ const providerSourceComponents = ( ] interface AdmittedProviderSelector { + readonly row: (typeof providerAdapterCatalog.providers)[number] readonly provider: ConnectedProvider readonly sourceType: string } @@ -409,15 +431,16 @@ interface AdmittedProviderSelector { const admittedProviderSelector = ( provider: JsonValue | undefined, sourceType: JsonValue | undefined, -): AdmittedProviderSelector | undefined => - ( - typeof provider === 'string' && - providerCodes.has(provider) && - typeof sourceType === 'string' && - admittedProviderSource(provider, sourceType) - ) ? - { provider: provider as ConnectedProvider, sourceType } - : undefined +): AdmittedProviderSelector | undefined => { + const row = providerAdapterCatalog.providers.find(({ id }) => id === provider) + return ( + row !== undefined && + typeof sourceType === 'string' && + admittedProviderSource(row.id, sourceType) + ) ? + { row, provider: row.id, sourceType } + : undefined +} const providerOutputsAreAdmitted = ( provider: ConnectedProvider, @@ -448,7 +471,7 @@ export const deriveProviderIdentities = ( ['outputs'], ) } - const { provider, sourceType } = selector + const { row: providerRow, provider, sourceType } = selector const parsedOutputs = rawOutputs.map(providerOutputIdentity) const provenanceNodeRole = value.provenanceNodeRole if ( @@ -475,16 +498,6 @@ export const deriveProviderIdentities = ( ['providerScopeIdentifier', 'system'], ) } - const providerRow = providerAdapterCatalog.providers.find( - ({ id }) => id === provider, - ) - if (providerRow === undefined) { - return err( - 'unsupported-measurement', - 'Provider identity selectors must match one exact pinned provider catalog row.', - ['provider'], - ) - } const expectedAssurance = providerRow.providerScopeMode if ( typeof providerScope?.value !== 'string' || @@ -493,6 +506,7 @@ export const deriveProviderIdentities = ( sourceType.trim() === '' || typeof value.sourceNativeId !== 'string' || value.sourceNativeId.trim() === '' || + typeof value.eventSequence !== 'string' || (provenanceNodeRole !== undefined && !isProvenanceNodeRole(provenanceNodeRole)) ) { @@ -501,8 +515,6 @@ export const deriveProviderIdentities = ( 'Provider identity inputs must be complete canonical values.', ) } - const normalizedProvenanceNodeRole = - isProvenanceNodeRole(provenanceNodeRole) ? provenanceNodeRole : undefined const outputKeys = typedOutputs.map((output) => JSON.stringify(output)) if (new Set(outputKeys).size !== outputKeys.length) { return err( @@ -524,12 +536,9 @@ export const deriveProviderIdentities = ( sourceType, sourceNativeId: value.sourceNativeId, outputs: typedOutputs, - eventSequence: - typeof value.eventSequence === 'string' ? value.eventSequence : '', - deployment: deployment.value, - ...(normalizedProvenanceNodeRole === undefined ? - {} - : { provenanceNodeRole: normalizedProvenanceNodeRole }), + eventSequence: value.eventSequence, + deployment: deployment.value.identity, + ...(provenanceNodeRole === undefined ? {} : { provenanceNodeRole }), } const source = providerSourceComponents(normalizedInput) const sourceRecord = deriveOpaqueIdentifier( diff --git a/packages/fhir/src/providers/provider-input-schemas.ts b/packages/fhir/src/providers/provider-input-schemas.ts index b402abb8..2cd0deef 100644 --- a/packages/fhir/src/providers/provider-input-schemas.ts +++ b/packages/fhir/src/providers/provider-input-schemas.ts @@ -7,26 +7,30 @@ // import { z } from 'zod' -import { providerAdapterCatalog } from '../contract/providers.generated.js' import { SYSTEMS } from './profiles.js' import type { ProviderPatientReferenceInput, ProviderResearchStudyReferenceInput, } from './types.js' +import { providerAdapterCatalog } from '../contract/providers.generated.js' import { parseAbsoluteUri, + parseFhirId, parseFhirInstant, + type AbsoluteUri, + type FhirId, type FhirInstant, type Issue, } from '../core/index.js' -import { containsIsolatedSurrogate } from '../mobile/identity.js' -import { groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' +import { + containsIsolatedSurrogate, + validateDeploymentIdentity, +} from '../mobile/identity.js' import { canonicalizeMobileEffectiveInstant } from '../mobile/time.js' import type { ApplicationDeviceInput, DeploymentIdentityInput, GatewayApplicationInput, - GroveOpaqueIdentityKind, RecordingDeviceInput, } from '../mobile/types.js' @@ -67,18 +71,18 @@ const fhirCodeSchema = nonBlankStringSchema.refine( 'Expected a FHIR code with no leading, trailing, consecutive, or control whitespace.', ) -export const absoluteUriSchema: z.ZodString = z - .string() - .refine((value) => parseAbsoluteUri(value).ok, { - message: 'Expected an absolute ASCII RFC 3986 URI.', - }) +// The parser is the proof, so the schema carries the brand the input contract promises. +export const absoluteUriSchema: z.ZodType = z.custom( + (value) => parseAbsoluteUri(value).ok, + { message: 'Expected an absolute ASCII RFC 3986 URI.' }, +) const identifierInputSchemaValue = z.strictObject({ system: absoluteUriSchema, value: nonBlankStringSchema, }) export const identifierInputSchema: z.ZodObject< - { system: z.ZodString; value: z.ZodString }, + { system: z.ZodType; value: z.ZodString }, z.core.$strict > = identifierInputSchemaValue @@ -86,7 +90,7 @@ export type ProviderScopeAssurance = 'deployment-scoped-account-pseudonym' | 'documented-global-key-space' export const providerScopeIdentifierSchema: z.ZodType<{ - readonly system: string + readonly system: AbsoluteUri readonly value: string readonly assurance: ProviderScopeAssurance }> = identifierInputSchema.extend({ @@ -227,40 +231,34 @@ const providerPatientReferenceSchemaValue = z.strictObject({ }), }) export const providerPatientReferenceSchema: z.ZodType = - providerPatientReferenceSchemaValue as unknown as z.ZodType + providerPatientReferenceSchemaValue const providerResearchStudyReferenceSchemaValue = z.strictObject({ type: z.literal('ResearchStudy'), identifier: identifierInputSchema, }) export const providerResearchStudyReferenceSchema: z.ZodType = - providerResearchStudyReferenceSchemaValue as unknown as z.ZodType + providerResearchStudyReferenceSchemaValue -const opaqueIdentifierSystemShape = Object.fromEntries( - groveExchangeProtocol.opaqueIdentity.identityKinds.map(({ kind }) => [ - kind, - absoluteUriSchema, - ]), -) as Record - -const deploymentIdentitySchemaValue = z.strictObject({ - opaqueIdentifierSystems: z.strictObject(opaqueIdentifierSystemShape), - eventIdentifierSystem: absoluteUriSchema, - entryNodeIdentifierSystem: absoluteUriSchema, - keyId: z.string().regex(/^[A-Za-z0-9._-]+$/u), - keyEpoch: z.string().regex(/^[1-9]\d*$/u), - secretBase64Url: z.string().regex(/^[A-Za-z0-9_-]+$/u), - producerInstance: z - .string() - .regex( - /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u, - ), -}) -export const deploymentIdentitySchema: z.ZodType = - deploymentIdentitySchemaValue as unknown as z.ZodType +// One validator owns the deployment contract; a second field-level copy only diverges. +export const deploymentIdentitySchema: z.ZodType = z + .custom() + .superRefine((value, context) => { + const validated = validateDeploymentIdentity(value) + if (validated.ok) return + for (const issue of validated.issues) { + context.addIssue({ + code: 'custom', + path: [...issue.path], + message: issue.message, + }) + } + }) -const fhirIdSchemaValue = z.string().regex(/^[A-Za-z0-9\-.]{1,64}$/u) -export const fhirIdSchema: z.ZodString = fhirIdSchemaValue +export const fhirIdSchema: z.ZodType = z.custom( + (value) => parseFhirId(value).ok, + { message: 'Expected a FHIR id of 1 to 64 unreserved characters.' }, +) const hostDeviceSchema = z.strictObject({ sourceDeviceToken: nonBlankStringSchema, @@ -281,7 +279,7 @@ const applicationDeviceSchemaValue = z.strictObject({ host: hostDeviceSchema.optional(), }) export const applicationDeviceSchema: z.ZodType = - applicationDeviceSchemaValue as unknown as z.ZodType + applicationDeviceSchemaValue const recordingDeviceBase = { stableUnitToken: nonBlankStringSchema, @@ -292,28 +290,30 @@ const recordingDeviceBase = { modelNumber: nonBlankStringSchema.optional(), } as const +const recordingDeviceSchemaValue = z.discriminatedUnion('identityScope', [ + z.strictObject({ + ...recordingDeviceBase, + identityScope: z.literal('deployment-scoped'), + }), + z.strictObject({ + ...recordingDeviceBase, + identityScope: z.literal('authorized-hardware'), + disclosureAuthorization: z.literal('authorized-for-exchange'), + }), +]) export const recordingDeviceSchema: z.ZodType = - z.discriminatedUnion('identityScope', [ - z.strictObject({ - ...recordingDeviceBase, - identityScope: z.literal('deployment-scoped'), - }), - z.strictObject({ - ...recordingDeviceBase, - identityScope: z.literal('authorized-hardware'), - disclosureAuthorization: z.literal('authorized-for-exchange'), - }), - ]) as unknown as z.ZodType + recordingDeviceSchemaValue +const gatewayApplicationSchemaValue = z.discriminatedUnion('kind', [ + z.strictObject({ + kind: z.literal('converter-application'), + roleAssurance: z.literal('mediated-or-routed-measurement'), + }), + z.strictObject({ + kind: z.literal('distinct-application'), + roleAssurance: z.literal('mediated-or-routed-measurement'), + application: applicationDeviceSchema, + }), +]) export const gatewayApplicationSchema: z.ZodType = - z.discriminatedUnion('kind', [ - z.strictObject({ - kind: z.literal('converter-application'), - roleAssurance: z.literal('mediated-or-routed-measurement'), - }), - z.strictObject({ - kind: z.literal('distinct-application'), - roleAssurance: z.literal('mediated-or-routed-measurement'), - application: applicationDeviceSchema, - }), - ]) + gatewayApplicationSchemaValue From 48a7a2a2d1ec62e96f6005e1a9f27bc009b1fc06 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Mon, 31 Aug 2026 11:27:07 -0700 Subject: [PATCH 14/31] Build Provider Resources From the Catalog Contract --- packages/fhir/package.json | 4 - packages/fhir/src/provenance/index.ts | 9 - packages/fhir/src/providers/index.ts | 2 +- .../src/providers/measurement-definition.ts | 163 +++++++----- packages/fhir/src/providers/profiles.ts | 3 + packages/fhir/src/providers/provider.ts | 231 +++++++----------- packages/fhir/src/providers/recording.ts | 190 +++++--------- packages/fhir/src/providers/retraction.ts | 163 +++++++----- packages/fhir/src/providers/types.ts | 2 +- 9 files changed, 357 insertions(+), 410 deletions(-) delete mode 100644 packages/fhir/src/provenance/index.ts diff --git a/packages/fhir/package.json b/packages/fhir/package.json index 5a747aed..3c58686c 100644 --- a/packages/fhir/package.json +++ b/packages/fhir/package.json @@ -27,10 +27,6 @@ "types": "./dist/questionnaire/index.d.ts", "default": "./dist/questionnaire/index.js" }, - "./provenance": { - "types": "./dist/provenance/index.d.ts", - "default": "./dist/provenance/index.js" - }, "./zod/r4": { "types": "./dist/zod/r4/index.d.ts", "default": "./dist/zod/r4/index.js" diff --git a/packages/fhir/src/provenance/index.ts b/packages/fhir/src/provenance/index.ts deleted file mode 100644 index dd789841..00000000 --- a/packages/fhir/src/provenance/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -export { parseProvenance, type Provenance } from '../r4/index.js' diff --git a/packages/fhir/src/providers/index.ts b/packages/fhir/src/providers/index.ts index cad380f2..ae052d58 100644 --- a/packages/fhir/src/providers/index.ts +++ b/packages/fhir/src/providers/index.ts @@ -19,7 +19,6 @@ export { providerScalarOutputDiscriminators, providerScalarOutputRoles, groveProviderPackageMetadata, - groveFhirContractVersion, groveFhirVersion, type AdapterMeasurementCatalog, type HealthKitApplicationDeviceIdentity, @@ -53,6 +52,7 @@ export { export type { ProviderRetractionInput, ProviderRetractionTargetInput, + ProviderRetractionTargetNativeIdentifierInput, RetractionTargetRole, } from './retraction.js' export type { DeviceSnapshotRole } from './identity.js' diff --git a/packages/fhir/src/providers/measurement-definition.ts b/packages/fhir/src/providers/measurement-definition.ts index 54318687..76fce67b 100644 --- a/packages/fhir/src/providers/measurement-definition.ts +++ b/packages/fhir/src/providers/measurement-definition.ts @@ -6,79 +6,111 @@ // SPDX-License-Identifier: MIT // +import type { ConnectedProvider } from './types.js' +import { sharedMobileMeasurementCatalog } from '../contract/measurement-catalog.generated.js' import { adapterMeasurementCatalog, groveProviderProfileCanonicals, providerAdapterCatalog, providerScalarOutputRoles, } from '../contract/providers.generated.js' -import type { ConnectedProvider } from './types.js' -import { sharedMobileMeasurementCatalog } from '../contract/measurement-catalog.generated.js' -export interface ProviderMeasurementDefinition { - readonly id: string - readonly profile: string - readonly owner?: string - readonly code: Readonly<{ - system: string - code: string - display?: string - }> - readonly requiredCodings?: ReadonlyArray< - Readonly<{ system: string; code: string }> - > - readonly category?: Readonly<{ - system: string - code: string - display?: string - }> - readonly effective: 'Period' | 'dateTime' | 'dateTime-or-Period' - readonly obeys?: readonly string[] - readonly method?: Readonly<{ code: string; display?: string }> - readonly methodChoice?: readonly string[] - readonly valueKind: - 'codeableConcept' | 'components' | 'dateTime' | 'grouping' | 'quantity' - readonly quantity?: Readonly<{ - system: string - code: string - unit?: string - valueDomain?: Readonly<{ - minimum: Readonly<{ value: number; inclusive: boolean }> - maximum?: Readonly<{ value: number; inclusive: boolean }> - integerOnly: boolean - }> - }> | null - readonly resultCodeSystem?: string - readonly allowedValues?: readonly string[] - readonly components?: ReadonlyArray< - Readonly<{ - id: string - system: string - code: string - quantity?: Readonly<{ system: string; code: string; unit?: string }> - }> - > -} +type SharedCatalog = typeof sharedMobileMeasurementCatalog +type AdapterCatalog = typeof adapterMeasurementCatalog -const SHARED_DEFINITIONS = - sharedMobileMeasurementCatalog as unknown as Readonly< - Record - > -const OWNER_DEFINITIONS = adapterMeasurementCatalog as unknown as Readonly< - Record>> -> -const PROFILE_CANONICALS = groveProviderProfileCanonicals as Readonly< - Record +type Values = Catalog extends Catalog ? Catalog[keyof Catalog] : never +type KeysOfUnion = Member extends Member ? keyof Member : never +type ValueOfUnion = + Member extends Member ? + Key extends keyof Member ? + Member[Key] + : never + : never + +type Scalar = string | number | boolean | null | undefined + +/** + * Collapses a union of catalog rows into the one shape they collectively describe: + * keys every row carries stay required, keys only some rows carry become optional, + * and literal element types widen through arrays and nested objects. Renaming a + * generated field breaks compilation here instead of silently producing `undefined`. + */ +type Merged = + Extract | MergedStructure> + +// `keyof Member & string` keeps the mapped types non-homomorphic, so a union of rows +// merges into one shape instead of distributing back into a union. +type SharedKeys = keyof Member & string +type MergedStructure = + [Member] extends [never] ? never + : [Member] extends [ReadonlyArray] ? + ReadonlyArray> + : { + readonly [Key in SharedKeys]: Merged> + } & { + readonly [ + Key in Exclude & string, SharedKeys> + ]?: Merged> + } + +export type MeasurementDefinition = Merged< + Values | Values> > -interface ProviderContractRow { - readonly id: ConnectedProvider - readonly measurementOwner: string - readonly observationProfile: string +/** One connected-provider row of the generated adapter catalog. */ +export type ProviderContractRow = + (typeof providerAdapterCatalog.providers)[number] + +export const PROVIDER_ROWS: readonly ProviderContractRow[] = + providerAdapterCatalog.providers + +export const providerObservationProfiles: ReadonlySet = new Set( + PROVIDER_ROWS.map(({ observationProfile }) => observationProfile), +) + +/** Resolves a catalog profile id to the canonical URL the IG publishes for it. */ +export const profileCanonical = (profile: string): string | undefined => + PROFILE_CANONICALS[profile] + +const PROFILE_CANONICALS: Readonly> = + groveProviderProfileCanonicals + +const SHARED_DEFINITIONS: Readonly> = + sharedMobileMeasurementCatalog +const OWNER_DEFINITIONS: Readonly< + Record>> +> = adapterMeasurementCatalog + +/** Every shared and owner-exclusive definition the catalogs publish. */ +export const ALL_MEASUREMENT_DEFINITIONS: readonly MeasurementDefinition[] = [ + ...Object.values(SHARED_DEFINITIONS), + ...Object.values(OWNER_DEFINITIONS).flatMap((owner) => Object.values(owner)), +] + +interface NumericBoundary { + readonly value: number + readonly inclusive: boolean } -const PROVIDER_ROWS = - providerAdapterCatalog.providers as unknown as readonly ProviderContractRow[] +const violatesMinimum = (value: number, boundary: NumericBoundary): boolean => + value < boundary.value || (!boundary.inclusive && value === boundary.value) + +const violatesMaximum = (value: number, boundary: NumericBoundary): boolean => + value > boundary.value || (!boundary.inclusive && value === boundary.value) + +/** Whether a numeric result falls outside its catalog-reviewed representational domain. */ +export const violatesQuantityDomain = ( + value: number, + definition: MeasurementDefinition, +): boolean => { + const domain = definition.quantity?.valueDomain + if (domain === undefined) return false + return ( + violatesMinimum(value, domain.minimum) || + (domain.maximum !== undefined && violatesMaximum(value, domain.maximum)) || + (domain.integerOnly && !Number.isInteger(value)) + ) +} const providerContractRow = ( provider: ConnectedProvider, @@ -89,12 +121,17 @@ const providerContractRow = ( export const providerMeasurementDefinition = ( provider: ConnectedProvider, kind: string, -): ProviderMeasurementDefinition | undefined => +): MeasurementDefinition | undefined => SHARED_DEFINITIONS[kind] ?? OWNER_DEFINITIONS[providerContractRow(provider)?.measurementOwner ?? '']?.[ kind ] +/** Resolves a shared definition by kind, independent of any provider. */ +export const sharedMeasurementDefinition = ( + kind: string, +): MeasurementDefinition | undefined => SHARED_DEFINITIONS[kind] + /** Returns the exact direct semantic canonical for this provider-owned mapping. */ export const providerMeasurementProfile = ( provider: ConnectedProvider, @@ -111,7 +148,7 @@ export const providerObservationProfile = ( /** Closed owner-exclusive definitions that the normalized Provider facade may parse. */ export const connectedProviderExclusiveDefinitions: Readonly< - Record + Record > = Object.freeze( Object.fromEntries( [...new Set(PROVIDER_ROWS.map(({ measurementOwner }) => measurementOwner))] diff --git a/packages/fhir/src/providers/profiles.ts b/packages/fhir/src/providers/profiles.ts index 5fde8de6..4b1722f5 100644 --- a/packages/fhir/src/providers/profiles.ts +++ b/packages/fhir/src/providers/profiles.ts @@ -74,6 +74,7 @@ type GroveExtensions = Readonly<{ providerSourceType: string entryNodeKey: string retractionTargetRole: string + retractionTargetNativeIdentifier: string researchStudy: string writerRecordVersion: string }> @@ -86,6 +87,8 @@ export const EXTENSIONS: GroveExtensions = { providerSourceType: `${groveProviderPackageCanonicals.providers}/StructureDefinition/provider-source-type`, entryNodeKey: groveMobileContract.extensions.entryNodeKey, retractionTargetRole: groveMobileContract.extensions.retractionTargetRole, + retractionTargetNativeIdentifier: + groveMobileContract.extensions.retractionTargetNativeIdentifier, researchStudy: 'http://hl7.org/fhir/StructureDefinition/workflow-researchStudy', writerRecordVersion: diff --git a/packages/fhir/src/providers/provider.ts b/packages/fhir/src/providers/provider.ts index 0ec317a4..8eea480b 100644 --- a/packages/fhir/src/providers/provider.ts +++ b/packages/fhir/src/providers/provider.ts @@ -8,14 +8,14 @@ import { z } from 'zod' import { - providerRecordEffectiveRules, - providerScalarOutputDiscriminators, - providerScalarOutputRoles, -} from '../contract/providers.generated.js' -import { deriveProviderIdentities } from './identity.js' + deriveProviderIdentities, + type ProviderIdentities, +} from './identity.js' import { connectedProviderExclusiveDefinitions, - type ProviderMeasurementDefinition, + sharedMeasurementDefinition, + violatesQuantityDomain, + type MeasurementDefinition, } from './measurement-definition.js' import { absoluteUriSchema, @@ -41,28 +41,29 @@ import type { ConnectedProvider, NormalizedProviderRecord, } from './types.js' +import { + sharedMobileMeasurementCatalog, + type SharedMobileMeasurementKind, +} from '../contract/measurement-catalog.generated.js' +import { + providerRecordEffectiveRules, + providerScalarOutputDiscriminators, + providerScalarOutputRoles, +} from '../contract/providers.generated.js' import { cloneJsonValue, compareFhirInstants, deepFreeze, - err, issues, + mapResult, ok, - parseAbsoluteUri, + zodIssueToIssue, type Issue, type Result, } from '../core/index.js' -import { - sharedMobileMeasurementCatalog, - type SharedMobileMeasurementKind, -} from '../contract/measurement-catalog.generated.js' import type { - ChoiceQuantityMeasurementKind, - InstantCodedMeasurementKind, - InstantQuantityMeasurementKind, + MeasurementKindsWhere, MobileMeasurement, - PeriodCodedMeasurementKind, - PeriodQuantityMeasurementKind, } from '../mobile/types.js' type ParsedProviderMeasurement = @@ -146,51 +147,56 @@ const periodEffectiveSchema = z { message: 'A measurement Period must not end before it starts.' }, ) -const measurementKindsWhere = ( - valueKind: 'codeableConcept' | 'quantity', - effective: 'Period' | 'dateTime' | 'dateTime-or-Period', - excluded?: SharedMobileMeasurementKind, -): [Kind, ...Kind[]] => - ( +// The value arguments decide the kinds, so a swapped pair cannot compile into a lie. +const measurementKindsWhere = < + ValueKind extends 'codeableConcept' | 'quantity', + Effective extends 'Period' | 'dateTime' | 'dateTime-or-Period', + Excluded extends SharedMobileMeasurementKind = never, +>( + valueKind: ValueKind, + effective: Effective, + excluded?: Excluded, +): [ + Exclude, Excluded>, + ...Array, Excluded>>, +] => { + const excludedKind: string | undefined = excluded + return ( Object.keys(sharedMobileMeasurementCatalog) as SharedMobileMeasurementKind[] ).filter((kind) => { const definition = sharedMobileMeasurementCatalog[kind] return ( definition.valueKind === valueKind && definition.effective === effective && - kind !== excluded + kind !== excludedKind ) - }) as [Kind, ...Kind[]] + }) as [ + Exclude, Excluded>, + ...Array, Excluded>>, + ] +} const instantQuantityMeasurementSchema = z.strictObject({ - kind: z.enum( - measurementKindsWhere( - 'quantity', - 'dateTime', - ), - ), + kind: z.enum(measurementKindsWhere('quantity', 'dateTime')), value: z.number(), effective: instantEffectiveSchema, }) const periodQuantityMeasurementSchema = z.strictObject({ - kind: z.enum( - measurementKindsWhere('quantity', 'Period'), - ), + kind: z.enum(measurementKindsWhere('quantity', 'Period')), value: z.number(), effective: periodEffectiveSchema, }) -const choiceQuantityMeasurementKinds = - measurementKindsWhere( - 'quantity', - 'dateTime-or-Period', - ) +const choiceQuantityMeasurementKinds = measurementKindsWhere( + 'quantity', + 'dateTime-or-Period', +) const choiceQuantityMeasurementKindSet: ReadonlySet = new Set( choiceQuantityMeasurementKinds, ) const choiceQuantityMeasurementSchema = z.strictObject({ - kind: z.custom( + kind: z.custom<(typeof choiceQuantityMeasurementKinds)[number]>( (value) => typeof value === 'string' && choiceQuantityMeasurementKindSet.has(value), { message: 'Expected a catalog measurement with choice effective[x].' }, @@ -200,23 +206,14 @@ const choiceQuantityMeasurementSchema = z.strictObject({ }) const instantCodedMeasurementSchema = z.strictObject({ - kind: z.enum( - measurementKindsWhere( - 'codeableConcept', - 'dateTime', - ), - ), + kind: z.enum(measurementKindsWhere('codeableConcept', 'dateTime')), value: nonBlankStringSchema, effective: instantEffectiveSchema, }) const periodCodedMeasurementSchema = z.strictObject({ kind: z.enum( - measurementKindsWhere( - 'codeableConcept', - 'Period', - 'sleep-stage', - ), + measurementKindsWhere('codeableConcept', 'Period', 'sleep-stage'), ), value: nonBlankStringSchema, effective: periodEffectiveSchema, @@ -272,21 +269,10 @@ const measurementSchema: z.ZodType = z.union([ sleepStageMeasurementSchema, exclusiveQuantityMeasurementSchema, exclusiveCodedMeasurementSchema, -]) as unknown as z.ZodType - -interface NumericBoundary { - readonly value: number - readonly inclusive: boolean -} - -const violatesMinimum = (value: number, boundary: NumericBoundary): boolean => - value < boundary.value || (!boundary.inclusive && value === boundary.value) - -const violatesMaximum = (value: number, boundary: NumericBoundary): boolean => - value > boundary.value || (!boundary.inclusive && value === boundary.value) +]) const effectiveKindMatches = ( - definition: ProviderMeasurementDefinition, + definition: MeasurementDefinition, effectiveKind: 'date-time' | 'period', ): boolean => definition.effective === 'dateTime-or-Period' || @@ -295,7 +281,7 @@ const effectiveKindMatches = ( const violatesRequiredPeriodOrdering = ( measurement: z.infer, - definition: ProviderMeasurementDefinition, + definition: MeasurementDefinition, ): boolean => { if ( measurement.effective.kind !== 'period' || @@ -310,30 +296,14 @@ const violatesRequiredPeriodOrdering = ( return !ordering.ok || ordering.value !== -1 } -const violatesQuantityDomain = ( - value: number, - definition: ProviderMeasurementDefinition, -): boolean => { - const domain = definition.quantity?.valueDomain - if (domain === undefined) return false - return ( - violatesMinimum(value, domain.minimum) || - (domain.maximum !== undefined && violatesMaximum(value, domain.maximum)) || - (domain.integerOnly && !Number.isInteger(value)) - ) -} - const refineMeasurement = ( measurement: z.infer, path: ReadonlyArray, context: z.core.$RefinementCtx, ) => { const measurementDefinition = - ( - sharedMobileMeasurementCatalog as unknown as Readonly< - Record - > - )[measurement.kind] ?? exclusiveDefinitions[measurement.kind] + sharedMeasurementDefinition(measurement.kind) ?? + exclusiveDefinitions[measurement.kind] if (measurementDefinition === undefined) { context.addIssue({ code: 'custom', @@ -358,10 +328,12 @@ const refineMeasurement = ( message: `The ${measurement.kind} Period must satisfy its catalog-owned nonzero-duration rule.`, }) } + const allowedValues: readonly string[] | undefined = + measurementDefinition.allowedValues if ( 'value' in measurement && typeof measurement.value === 'string' && - measurementDefinition.allowedValues?.includes(measurement.value) !== true + allowedValues?.includes(measurement.value) !== true ) { context.addIssue({ code: 'custom', @@ -444,17 +416,6 @@ const providerMeasurementBundleInputSchema = z }) .superRefine(refineMeasurements) -export const normalizeZodIssue = (entry: z.core.$ZodIssue): Issue => ({ - severity: 'error', - code: 'schema-invalid', - path: entry.path.map((component) => - typeof component === 'symbol' ? - (component.description ?? component.toString()) - : component, - ), - message: entry.message, -}) - const providerSourceMapping = ( provider: ConnectedProvider, sourceType: string, @@ -691,16 +652,8 @@ export const parseNormalizedProviderRecord = ( ): Result => { const snapshot = cloneJsonValue(input) if (!snapshot.ok) return snapshot - let result: ReturnType - try { - result = normalizedProviderRecordSchema.safeParse(snapshot.value) - } catch { - return err( - 'schema-invalid', - 'Provider record validation could not safely inspect the supplied value.', - ) - } - if (!result.success) return issues(result.error.issues.map(normalizeZodIssue)) + const result = normalizedProviderRecordSchema.safeParse(snapshot.value) + if (!result.success) return issues(result.error.issues.map(zodIssueToIssue)) const mappingIssues = recordMappingIssues(result.data) if (mappingIssues.length > 0) return issues(mappingIssues) return ok( @@ -708,22 +661,20 @@ export const parseNormalizedProviderRecord = ( ) } -/** Strict runtime boundary for the complete deterministic graph input. */ -export const parseProviderMeasurementBundleInput = ( +/** The validated graph input together with the identities its derivation proved. */ +export interface ParsedProviderMeasurementBundle { + readonly input: ProviderMeasurementBundleInput + readonly identities: ProviderIdentities +} + +/** Strict runtime boundary that also derives the graph identities exactly once. */ +export const parseProviderMeasurementBundle = ( input: unknown, -): Result => { +): Result => { const snapshot = cloneJsonValue(input) if (!snapshot.ok) return snapshot - let result: ReturnType - try { - result = providerMeasurementBundleInputSchema.safeParse(snapshot.value) - } catch { - return err( - 'schema-invalid', - 'Provider graph input validation could not safely inspect the supplied value.', - ) - } - if (!result.success) return issues(result.error.issues.map(normalizeZodIssue)) + const result = providerMeasurementBundleInputSchema.safeParse(snapshot.value) + if (!result.success) return issues(result.error.issues.map(zodIssueToIssue)) const disclosureIssues = governedSourceIdentifierIssues( result.data.nativeIdentifierDisclosure, result.data.source.sourceNativeId, @@ -766,10 +717,11 @@ export const parseProviderMeasurementBundleInput = ( }, ]) } - const outputCoordinates = result.data.measurements.map(({ kind }) => + const sorted = sortMeasurements(result.data) + const outputCoordinates = sorted.measurements.map(({ kind }) => providerOutputCoordinates( - result.data.source.adapter.provider, - result.data.source.sourceType, + sorted.source.adapter.provider, + sorted.source.sourceType, kind, ), ) @@ -788,19 +740,11 @@ export const parseProviderMeasurementBundleInput = ( }, ]) } - const providerScopeSystem = parseAbsoluteUri( - result.data.source.providerScopeIdentifier.system, - ) - if (!providerScopeSystem.ok) return providerScopeSystem - const identity = deriveProviderIdentities({ - provider: result.data.source.adapter.provider, - providerScopeIdentifier: { - system: providerScopeSystem.value, - value: result.data.source.providerScopeIdentifier.value, - assurance: result.data.source.providerScopeIdentifier.assurance, - }, - sourceType: result.data.source.sourceType, - sourceNativeId: result.data.source.sourceNativeId, + const identities = deriveProviderIdentities({ + provider: sorted.source.adapter.provider, + providerScopeIdentifier: sorted.source.providerScopeIdentifier, + sourceType: sorted.source.sourceType, + sourceNativeId: sorted.source.sourceNativeId, outputs: definedOutputCoordinates.map( ({ outputRole, outputDiscriminator }) => ({ kind: 'provider-output' as const, @@ -808,11 +752,18 @@ export const parseProviderMeasurementBundleInput = ( outputDiscriminator, }), ), - eventSequence: result.data.eventSequence, - deployment: result.data.deploymentIdentity, + eventSequence: sorted.eventSequence, + deployment: sorted.deploymentIdentity, + }) + if (!identities.ok) return identities + return ok({ + input: deepFreeze(sorted) as ProviderMeasurementBundleInput, + identities: identities.value, }) - if (!identity.ok) return identity - return ok( - deepFreeze(sortMeasurements(result.data)) as ProviderMeasurementBundleInput, - ) } + +/** Strict runtime boundary for the complete deterministic graph input. */ +export const parseProviderMeasurementBundleInput = ( + input: unknown, +): Result => + mapResult(parseProviderMeasurementBundle(input), ({ input: value }) => value) diff --git a/packages/fhir/src/providers/recording.ts b/packages/fhir/src/providers/recording.ts index 3542b5b5..9bd56fdc 100644 --- a/packages/fhir/src/providers/recording.ts +++ b/packages/fhir/src/providers/recording.ts @@ -8,12 +8,6 @@ import { sha1 } from '@noble/hashes/legacy.js' import { z } from 'zod' -import { - groveRecordingFormatRegistry, - providerAdapterCatalog, - providerRawOutputRoles, - type ProviderRecordingFormat, -} from '../contract/providers.generated.js' import { assemblerAgent, coding, @@ -29,9 +23,9 @@ import { } from './graph.js' import { deriveApplicationEntryIdentity, - deriveDeviceSnapshotEntryIdentity, deriveProviderIdentities, deriveWriterRecordIdentifier, + resolveHostIdentity, } from './identity.js' import { EXTENSIONS, @@ -47,7 +41,6 @@ import { governedSourceIdentifierIssues, governedSourceIdentifierSchema, nonBlankStringSchema, - normalizeZodIssue, primitiveInstantSchema, providerPatientReferenceSchema, providerScopeIdentifierIssues, @@ -61,14 +54,22 @@ import type { MediaType, Sha1Base64, } from './types.js' +import { + groveRecordingFormatRegistry, + providerAdapterCatalog, + providerRawOutputRoles, + type ProviderRecordingFormat, +} from '../contract/providers.generated.js' import { cloneJsonValue, deepFreeze, err, issues, ok, - parseAbsoluteUri, + decodeCanonicalBase64, + encodeBase64, parseFhirInstant, + zodIssueToIssue, type Issue, type Result, } from '../core/index.js' @@ -79,61 +80,18 @@ import { type GroveMobileExchangeBundle, } from '../r4/index.js' -const BASE64_ALPHABET = - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' const BASE64 = /^(?:[A-Za-z\d+/]{4})*(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?$/u const MEDIA_TYPE = /^[A-Za-z\d!#$&^_.+-]+\/[A-Za-z\d!#$&^_.+-]+$/u -const encodeBase64 = (bytes: Uint8Array): string => { - let output = '' - const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength) - for (let index = 0; index < bytes.length; index += 3) { - const first = view.getUint8(index) - const second = bytes[index + 1] - const third = bytes[index + 2] - const block = (first << 16) | ((second ?? 0) << 8) | (third ?? 0) - output += BASE64_ALPHABET.charAt((block >>> 18) & 63) - output += BASE64_ALPHABET.charAt((block >>> 12) & 63) - output += - second === undefined ? '=' : BASE64_ALPHABET.charAt((block >>> 6) & 63) - output += third === undefined ? '=' : BASE64_ALPHABET.charAt(block & 63) - } - return output -} - -const decodeBase64 = (value: string): Uint8Array => { - let padding = 0 - if (value.endsWith('==')) padding = 2 - else if (value.endsWith('=')) padding = 1 - const bytes = new Uint8Array((value.length / 4) * 3 - padding) - let outputIndex = 0 - for (let index = 0; index < value.length; index += 4) { - const first = BASE64_ALPHABET.indexOf(value.charAt(index)) - const second = BASE64_ALPHABET.indexOf(value.charAt(index + 1)) - const thirdCharacter = value.charAt(index + 2) - const fourthCharacter = value.charAt(index + 3) - const third = - thirdCharacter === '=' ? 0 : BASE64_ALPHABET.indexOf(thirdCharacter) - const fourth = - fourthCharacter === '=' ? 0 : BASE64_ALPHABET.indexOf(fourthCharacter) - const block = (first << 18) | (second << 12) | (third << 6) | fourth - bytes[outputIndex++] = block >>> 16 - if (thirdCharacter !== '=') bytes[outputIndex++] = block >>> 8 - if (fourthCharacter !== '=') bytes[outputIndex++] = block - } - return bytes -} - -const decodeCanonicalBase64 = (value: string): Uint8Array | undefined => { - if (value.length === 0 || !BASE64.test(value)) return undefined - const bytes = decodeBase64(value) - return encodeBase64(bytes) === value ? bytes : undefined -} +const decodeRecordingBase64 = (value: string): Uint8Array | undefined => + value.length === 0 || !BASE64.test(value) ? + undefined + : decodeCanonicalBase64(value) export const parseCanonicalBase64 = ( value: unknown, ): Result => { - if (typeof value !== 'string' || decodeCanonicalBase64(value) === undefined) { + if (typeof value !== 'string' || decodeRecordingBase64(value) === undefined) { return err( 'invalid-code', 'Expected non-empty, canonically padded RFC 4648 base64.', @@ -155,7 +113,7 @@ export const parseSha1Base64 = (value: unknown): Result => { if (typeof value !== 'string') { return err('invalid-code', 'Expected a base64-encoded SHA-1 digest.') } - const decoded = decodeCanonicalBase64(value) + const decoded = decodeRecordingBase64(value) if (decoded?.length !== 20) { return err( 'invalid-code', @@ -303,16 +261,8 @@ export const parseProviderRecordingBundleInput = ( ): Result => { const snapshot = cloneJsonValue(input) if (!snapshot.ok) return snapshot - let parsed: ReturnType - try { - parsed = recordingBundleInputSchema.safeParse(snapshot.value) - } catch { - return err( - 'schema-invalid', - 'Provider recording input validation could not safely inspect the supplied value.', - ) - } - if (!parsed.success) return issues(parsed.error.issues.map(normalizeZodIssue)) + const parsed = recordingBundleInputSchema.safeParse(snapshot.value) + if (!parsed.success) return issues(parsed.error.issues.map(zodIssueToIssue)) const findings: Issue[] = [] findings.push( @@ -382,50 +332,40 @@ export const parseProviderRecordingBundleInput = ( } } - const declaredFormat = + const declaredContentTypes: readonly string[] = groveRecordingFormatRegistry.formats[parsed.data.attachment.format] - if (parsed.data.attachment.contentType !== declaredFormat.contentType) { + .contentTypes + if (!declaredContentTypes.includes(parsed.data.attachment.contentType)) { findings.push({ severity: 'error', code: 'value-mismatch', path: ['attachment', 'contentType'], - message: `Recording contentType must be ${declaredFormat.contentType} for the declared ${parsed.data.attachment.format} registry format.`, + message: `Recording contentType must be one of ${declaredContentTypes.join(', ')} for the declared ${parsed.data.attachment.format} registry format.`, }) } if (findings.length === 0) { - const providerScopeSystem = parseAbsoluteUri( - parsed.data.source.providerScopeIdentifier.system, - ) - if (!providerScopeSystem.ok) { - findings.push(...providerScopeSystem.issues) - } else { - const identity = deriveProviderIdentities({ - provider: parsed.data.source.adapter.provider, - providerScopeIdentifier: { - system: providerScopeSystem.value, - value: parsed.data.source.providerScopeIdentifier.value, - assurance: parsed.data.source.providerScopeIdentifier.assurance, + const identity = deriveProviderIdentities({ + provider: parsed.data.source.adapter.provider, + providerScopeIdentifier: parsed.data.source.providerScopeIdentifier, + sourceType: parsed.data.source.sourceType, + sourceNativeId: parsed.data.source.sourceNativeId, + outputs: [ + { + kind: 'provider-output', + outputRole: PROVIDER_RECORDING_OUTPUT_ROLE, + outputDiscriminator: PROVIDER_RECORDING_OUTPUT_DISCRIMINATOR, }, - sourceType: parsed.data.source.sourceType, - sourceNativeId: parsed.data.source.sourceNativeId, - outputs: [ - { - kind: 'provider-output', - outputRole: PROVIDER_RECORDING_OUTPUT_ROLE, - outputDiscriminator: PROVIDER_RECORDING_OUTPUT_DISCRIMINATOR, - }, - { - kind: 'provider-artifact', - formatCode: parsed.data.attachment.format, - partIndex: '0', - }, - ], - eventSequence: parsed.data.eventSequence, - deployment: parsed.data.deploymentIdentity, - }) - if (!identity.ok) findings.push(...identity.issues) - } + { + kind: 'provider-artifact', + formatCode: parsed.data.attachment.format, + partIndex: '0', + }, + ], + eventSequence: parsed.data.eventSequence, + deployment: parsed.data.deploymentIdentity, + }) + if (!identity.ok) findings.push(...identity.issues) } if (findings.length > 0) return issues(findings) @@ -499,38 +439,24 @@ const resolveRecordingGraphIdentities = ( input.application, ) if (!application.ok) return application - const applicationHost = - input.application.host === undefined ? - undefined - : deriveDeviceSnapshotEntryIdentity( - input.deploymentIdentity, - connected.value.event, - input.application.host.sourceDeviceToken, - 'host', - input.application.host.id, - ) - if (applicationHost !== undefined && !applicationHost.ok) { - return applicationHost - } + const applicationHost = resolveHostIdentity( + input.application, + input.deploymentIdentity, + connected.value.event, + ) + if (!applicationHost.ok) return applicationHost const dataOrigin = deriveApplicationEntryIdentity( input.deploymentIdentity, connected.value.event, input.source.dataOrigin, ) if (!dataOrigin.ok) return dataOrigin - const dataOriginHost = - input.source.dataOrigin.host === undefined ? - undefined - : deriveDeviceSnapshotEntryIdentity( - input.deploymentIdentity, - connected.value.event, - input.source.dataOrigin.host.sourceDeviceToken, - 'host', - input.source.dataOrigin.host.id, - ) - if (dataOriginHost !== undefined && !dataOriginHost.ok) { - return dataOriginHost - } + const dataOriginHost = resolveHostIdentity( + input.source.dataOrigin, + input.deploymentIdentity, + connected.value.event, + ) + if (!dataOriginHost.ok) return dataOriginHost const writerRecord = input.source.writerRecord === undefined ? @@ -550,10 +476,10 @@ const resolveRecordingGraphIdentities = ( provenance: provenance.value, application: application.value, dataOrigin: dataOrigin.value, - ...(applicationHost === undefined ? + ...(applicationHost.value === undefined ? {} : { applicationHost: applicationHost.value }), - ...(dataOriginHost === undefined ? + ...(dataOriginHost.value === undefined ? {} : { dataOriginHost: dataOriginHost.value }), ...(writerRecord === undefined ? {} : { writerRecord: writerRecord.value }), @@ -577,7 +503,9 @@ const attachmentFor = (input: ProviderRecordingBundleInput['attachment']) => { ...(input.title === undefined ? {} : { title: input.title }), } } - const validatedBytes = decodeBase64(input.dataBase64) + // The parser already proved this is canonical base64, so decoding cannot fail. + const validatedBytes = + decodeCanonicalBase64(input.dataBase64) ?? new Uint8Array() return { contentType: input.contentType, data: input.dataBase64, diff --git a/packages/fhir/src/providers/retraction.ts b/packages/fhir/src/providers/retraction.ts index be406afe..7538606d 100644 --- a/packages/fhir/src/providers/retraction.ts +++ b/packages/fhir/src/providers/retraction.ts @@ -7,12 +7,6 @@ // import { z } from 'zod' -import { - providerAdapterCatalog, - providerRawOutputRoles, - providerScalarOutputDiscriminators, - providerScalarOutputRoles, -} from '../contract/providers.generated.js' import { concept, deduplicateIdentifiedEntries, @@ -35,12 +29,12 @@ import { PROVIDER_RECORDING_OUTPUT_ROLE, SYSTEMS, } from './profiles.js' +import { absoluteUriSchema } from './provider-input-schemas.js' import { applicationDeviceSchema, deploymentIdentitySchema, fhirIdSchema, nonBlankStringSchema, - normalizeZodIssue, primitiveInstantSchema, providerScopeIdentifierIssues, providerScopeIdentifierSchema, @@ -49,11 +43,19 @@ import type { ConnectedProvider, ProviderScopeIdentifierInput, } from './types.js' +import { type groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' +import { + providerAdapterCatalog, + providerRawOutputRoles, + providerScalarOutputDiscriminators, + providerScalarOutputRoles, +} from '../contract/providers.generated.js' import { cloneJsonValue, deepFreeze, - err, issues, + zodIssueToIssue, + type AbsoluteUri, type FhirId, type FhirInstant, type Issue, @@ -66,7 +68,6 @@ import { deriveOpaqueIdentifier, validateDeploymentIdentity, } from '../mobile/identity.js' -import { type groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' import type { ApplicationDeviceInput, CompleteIdentifierInput, @@ -80,28 +81,42 @@ import { export type RetractionTargetRole = keyof typeof groveExchangeProtocol.lifecycle.retraction.targetRoles +/** + * The adapter's own record key for one retracted target, carried verbatim. + * + * It never participates in Grove identity and carries no Grove identifier-role coding. + */ +export interface ProviderRetractionTargetNativeIdentifierInput { + readonly system: AbsoluteUri + readonly value: string +} + +interface ProviderRetractionTargetCommon { + readonly nativeIdentifier?: ProviderRetractionTargetNativeIdentifierInput +} + export type ProviderRetractionTargetInput = - | { + | (ProviderRetractionTargetCommon & { readonly role: 'primary-output' readonly resourceType: 'Observation' /** Exact closed output role used when the active Observation was emitted. */ readonly outputRole: string /** Exact closed discriminator paired with the output role in the catalog. */ readonly outputDiscriminator: string - } - | { + }) + | (ProviderRetractionTargetCommon & { readonly role: 'source-artifact' readonly resourceType: 'DocumentReference' readonly formatCode: 'provider-recording' readonly partIndex: '0' - } - | { + }) + | (ProviderRetractionTargetCommon & { readonly role: 'device-snapshot' readonly resourceType: 'Device' readonly priorEventSequence: string readonly deviceRole: DeviceSnapshotRole readonly sourceDeviceToken: string - } + }) export interface ProviderRetractionInput< Provider extends ConnectedProvider = ConnectedProvider, @@ -148,18 +163,27 @@ const comparePositiveDecimals = (left: string, right: string): -1 | 0 | 1 => { return left < right ? -1 : 1 } +const nativeIdentifierSchema = z + .strictObject({ + system: absoluteUriSchema, + value: nonBlankStringSchema, + }) + .optional() + const retractionTargetSchema = z.discriminatedUnion('role', [ z.strictObject({ role: z.literal('primary-output'), resourceType: z.literal('Observation'), outputRole: nonBlankStringSchema, outputDiscriminator: nonBlankStringSchema, + nativeIdentifier: nativeIdentifierSchema, }), z.strictObject({ role: z.literal('source-artifact'), resourceType: z.literal('DocumentReference'), formatCode: z.literal('provider-recording'), partIndex: z.literal('0'), + nativeIdentifier: nativeIdentifierSchema, }), z.strictObject({ role: z.literal('device-snapshot'), @@ -167,6 +191,7 @@ const retractionTargetSchema = z.discriminatedUnion('role', [ priorEventSequence: z.string().regex(POSITIVE_DECIMAL), deviceRole: z.enum(['application', 'host', 'recording-device']), sourceDeviceToken: nonBlankStringSchema, + nativeIdentifier: nativeIdentifierSchema, }), ]) @@ -304,16 +329,8 @@ export const parseProviderRetractionInput = ( ): Result => { const snapshot = cloneJsonValue(input) if (!snapshot.ok) return snapshot - let parsed: ReturnType - try { - parsed = retractionInputSchema.safeParse(snapshot.value) - } catch { - return err( - 'schema-invalid', - 'Provider retraction input validation could not safely inspect the supplied value.', - ) - } - if (!parsed.success) return issues(parsed.error.issues.map(normalizeZodIssue)) + const parsed = retractionInputSchema.safeParse(snapshot.value) + if (!parsed.success) return issues(parsed.error.issues.map(zodIssueToIssue)) const value = parsed.data as unknown as ProviderRetractionInput const findings = validateInput(value) if (findings.length > 0) return issues(findings) @@ -367,31 +384,53 @@ const compareCanonicalText = (left: string, right: string): -1 | 0 | 1 => { return left < right ? -1 : 1 } +interface TargetRoleExtension { + readonly url: string + readonly valueCode: RetractionTargetRole +} + +interface TargetNativeIdentifierExtension { + readonly url: string + readonly valueIdentifier: ProviderRetractionTargetNativeIdentifierInput +} + +interface TargetReference { + readonly extension: + | readonly [TargetRoleExtension] + | readonly [TargetRoleExtension, TargetNativeIdentifierExtension] + readonly identifier: ReturnType + readonly type: string +} + +const nativeIdentifierOf = ( + target: TargetReference, +): ProviderRetractionTargetNativeIdentifierInput | undefined => + target.extension[1]?.valueIdentifier + const compareTargetReferences = ( - left: { - readonly extension: readonly [{ readonly valueCode: RetractionTargetRole }] - readonly identifier: ReturnType - readonly type: string - }, - right: { - readonly extension: readonly [{ readonly valueCode: RetractionTargetRole }] - readonly identifier: ReturnType - readonly type: string - }, -): number => - compareCanonicalText( - left.extension[0].valueCode, - right.extension[0].valueCode, - ) || - compareCanonicalText(left.type, right.type) || - compareCanonicalText( - left.identifier.system ?? '', - right.identifier.system ?? '', - ) || - compareCanonicalText( - left.identifier.value ?? '', - right.identifier.value ?? '', + left: TargetReference, + right: TargetReference, +): number => { + const leftNative = nativeIdentifierOf(left) + const rightNative = nativeIdentifierOf(right) + return ( + compareCanonicalText( + left.extension[0].valueCode, + right.extension[0].valueCode, + ) || + compareCanonicalText(left.type, right.type) || + compareCanonicalText( + left.identifier.system ?? '', + right.identifier.system ?? '', + ) || + compareCanonicalText( + left.identifier.value ?? '', + right.identifier.value ?? '', + ) || + compareCanonicalText(leftNative?.system ?? '', rightNative?.system ?? '') || + compareCanonicalText(leftNative?.value ?? '', rightNative?.value ?? '') ) +} /** * Builds one append-only source-retraction assertion. @@ -440,23 +479,25 @@ export const buildProviderRetractionBundle = ( ) if (hostIdentity !== undefined && !hostIdentity.ok) return hostIdentity - const targetReferences: Array<{ - readonly extension: readonly [ - { readonly url: string; readonly valueCode: RetractionTargetRole }, - ] - readonly identifier: ReturnType - readonly type: string - }> = [] + const targetReferences: TargetReference[] = [] for (const target of validated.targets) { const targetIdentifier = deriveTargetIdentifier(validated, target) if (!targetIdentifier.ok) return targetIdentifier + const role: TargetRoleExtension = { + url: EXTENSIONS.retractionTargetRole, + valueCode: target.role, + } targetReferences.push({ - extension: [ - { - url: EXTENSIONS.retractionTargetRole, - valueCode: target.role, - }, - ], + extension: + target.nativeIdentifier === undefined ? + [role] + : [ + role, + { + url: EXTENSIONS.retractionTargetNativeIdentifier, + valueIdentifier: target.nativeIdentifier, + }, + ], type: target.resourceType, identifier: identifier(targetIdentifier.value), }) diff --git a/packages/fhir/src/providers/types.ts b/packages/fhir/src/providers/types.ts index c25ece03..878d03ca 100644 --- a/packages/fhir/src/providers/types.ts +++ b/packages/fhir/src/providers/types.ts @@ -6,13 +6,13 @@ // SPDX-License-Identifier: MIT // +import type { SharedMobileMeasurementKind } from '../contract/measurement-catalog.generated.js' import type { AdapterMeasurementCatalog, ProviderRawOutputRoles, ProviderScalarOutputRoles, } from '../contract/providers.generated.js' import type { AbsoluteUri, FhirId, FhirInstant } from '../core/index.js' -import type { SharedMobileMeasurementKind } from '../contract/measurement-catalog.generated.js' import type { ApplicationDeviceInput, CompleteIdentifierInput, From b32ff92ed658dec0e93658689ec54d9c57f58ff2 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Mon, 31 Aug 2026 11:27:12 -0700 Subject: [PATCH 15/31] Tighten the Questionnaire Contract Surface --- packages/fhir/src/questionnaire/builder.ts | 99 ++--------- packages/fhir/src/questionnaire/contract.ts | 1 + packages/fhir/src/questionnaire/index.ts | 1 - packages/fhir/src/questionnaire/parse.ts | 74 ++++---- .../questionnaire/preflight-diagnostics.ts | 8 +- packages/fhir/src/questionnaire/preflight.ts | 15 +- .../questionnaire/questionnaire-extensions.ts | 22 ++- .../src/questionnaire/r4-resource-types.ts | 162 +----------------- packages/fhir/src/questionnaire/references.ts | 5 +- packages/fhir/src/questionnaire/schemas.ts | 36 ++-- packages/fhir/src/questionnaire/types.ts | 9 +- 11 files changed, 112 insertions(+), 320 deletions(-) diff --git a/packages/fhir/src/questionnaire/builder.ts b/packages/fhir/src/questionnaire/builder.ts index 16bdbb57..a99b804f 100644 --- a/packages/fhir/src/questionnaire/builder.ts +++ b/packages/fhir/src/questionnaire/builder.ts @@ -7,18 +7,21 @@ // import type { z } from 'zod' -import { groveQuestionnaireProfileCanonicals } from '../contract/questionnaire.generated.js' import { validateQuestionnaireContract, validateQuestionnaireResponseItemContract, } from './contract.js' import { parseQuestionnaire, parseQuestionnaireResponse } from './parse.js' -import { isR4ResourceType } from './r4-resource-types.js' +import { + QUESTIONNAIRE_EXTENSIONS, + QUESTIONNAIRE_SYSTEMS, +} from './questionnaire-extensions.js' import { isExactQuestionnaireUrl, isQuestionnaireResponseBuilderReference, QUESTIONNAIRE_RESPONSE_AUTHOR_TYPES, QUESTIONNAIRE_RESPONSE_SOURCE_TYPES, + QUESTIONNAIRE_RESPONSE_SUBJECT_TYPES, } from './references.js' import { questionnaireBuilderInputSchema, @@ -30,59 +33,32 @@ import type { QuestionnaireInput, QuestionnaireResponseInput, } from './types.js' +import { groveQuestionnaireProfileCanonicals } from '../contract/questionnaire.generated.js' import { cloneJsonValue, - err, + issue, issues, parseAbsoluteUri, parseCanonical, parseFhirId, parseFhirInstant, parseSemVer, + zodIssueToIssue, type Issue, type Result, } from '../core/index.js' -/* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 canonicals are normative HTTP URIs. */ - -const VERSION_ALGORITHM = - 'http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm' -const VERSION_ALGORITHM_SYSTEM = 'http://hl7.org/fhir/version-algorithm' -const COMPLETION_MODE = - 'http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode' -const PARTICIPATION_MODE = - 'http://terminology.hl7.org/CodeSystem/v3-ParticipationMode' -const issue = ( - code: Issue['code'], - path: Issue['path'], - message: string, -): Issue => ({ severity: 'error', code, path, message }) - -const schemaIssue = (entry: z.core.$ZodIssue): Issue => ({ - severity: 'error', - code: 'schema-invalid', - path: entry.path.map((component) => - typeof component === 'symbol' ? - (component.description ?? component.toString()) - : component, - ), - message: entry.message, -}) - +const VERSION_ALGORITHM = QUESTIONNAIRE_EXTENSIONS.versionAlgorithm +const VERSION_ALGORITHM_SYSTEM = QUESTIONNAIRE_SYSTEMS.versionAlgorithm +const COMPLETION_MODE = QUESTIONNAIRE_EXTENSIONS.completionMode +const PARTICIPATION_MODE = QUESTIONNAIRE_SYSTEMS.participationMode const parseBuilderInput = (schema: z.ZodType, input: unknown): Result => { const snapshot = cloneJsonValue(input) if (!snapshot.ok) return snapshot - try { - const parsed = schema.safeParse(snapshot.value) - return parsed.success ? - ({ ok: true, value: parsed.data as T, warnings: [] } as const) - : issues(parsed.error.issues.map(schemaIssue)) - } catch { - return err( - 'schema-invalid', - 'Questionnaire builder input could not be safely inspected.', - ) - } + const parsed = schema.safeParse(snapshot.value) + return parsed.success ? + ({ ok: true, value: parsed.data as T, warnings: [] } as const) + : issues(parsed.error.issues.map(zodIssueToIssue)) } const extensionCount = ( @@ -122,28 +98,6 @@ const validateQuestionnaireInput = ( issue('invalid-date-time', ['date'], 'Questionnaire.date is invalid.'), ) } - if (input.subjectTypes?.some((entry) => entry.trim() === '') === true) { - failures.push( - issue( - 'missing-required', - ['subjectTypes'], - 'Questionnaire.subjectTypes cannot contain empty resource type codes.', - ), - ) - } - if ( - input.subjectTypes?.some( - (entry) => entry.trim() !== '' && !isR4ResourceType(entry), - ) === true - ) { - failures.push( - issue( - 'invalid-code', - ['subjectTypes'], - 'Questionnaire.subjectTypes contains an unknown R4 ResourceType code.', - ), - ) - } if (extensionCount(input.extensions, VERSION_ALGORITHM) > 0) { failures.push( issue( @@ -193,9 +147,7 @@ export const buildQuestionnaire = ( {} : { title: validatedInput.title }), status: validatedInput.status, - ...(validatedInput.subjectTypes === undefined ? - {} - : { subjectType: validatedInput.subjectTypes }), + subjectType: validatedInput.subjectTypes, ...(validatedInput.date === undefined ? {} : { date: validatedInput.date }), ...(validatedInput.description === undefined ? {} @@ -253,19 +205,8 @@ const validateResponseInput = ( issue('invalid-identifier', ['id'], 'Response.id is invalid.'), ) } - if ( - input.subject !== undefined && - !isQuestionnaireResponseBuilderReference(input.subject) - ) { - failures.push( - issue( - 'invalid-reference', - ['subject'], - 'Response.subject requires one typed literal or identifier-only logical R4 Reference.', - ), - ) - } for (const [field, reference, allowedTypes] of [ + ['subject', input.subject, QUESTIONNAIRE_RESPONSE_SUBJECT_TYPES], ['author', input.author, QUESTIONNAIRE_RESPONSE_AUTHOR_TYPES], ['source', input.source, QUESTIONNAIRE_RESPONSE_SOURCE_TYPES], ] as const) { @@ -330,9 +271,7 @@ export const buildQuestionnaireResponse = ( identifier: validatedInput.identifier, questionnaire: validatedInput.questionnaire, status: validatedInput.status, - ...(validatedInput.subject === undefined ? - {} - : { subject: validatedInput.subject }), + subject: validatedInput.subject, authored: validatedInput.authored, ...(validatedInput.author === undefined ? {} diff --git a/packages/fhir/src/questionnaire/contract.ts b/packages/fhir/src/questionnaire/contract.ts index 16145098..edf5b367 100644 --- a/packages/fhir/src/questionnaire/contract.ts +++ b/packages/fhir/src/questionnaire/contract.ts @@ -30,6 +30,7 @@ import type { Extension } from '../r4/index.js' export { QUESTIONNAIRE_EXTENSIONS, + QUESTIONNAIRE_SYSTEMS, boundIsOrdered, expectedQuestionnaireAnswerKeys, extensionValue, diff --git a/packages/fhir/src/questionnaire/index.ts b/packages/fhir/src/questionnaire/index.ts index 3a5b87e6..eb6df007 100644 --- a/packages/fhir/src/questionnaire/index.ts +++ b/packages/fhir/src/questionnaire/index.ts @@ -8,7 +8,6 @@ export { buildQuestionnaire, buildQuestionnaireResponse } from './builder.js' export { - groveFhirContractVersion, groveFhirVersion, groveQuestionnairePackageMetadata, } from '../contract/questionnaire.generated.js' diff --git a/packages/fhir/src/questionnaire/parse.ts b/packages/fhir/src/questionnaire/parse.ts index d0a92168..050e8152 100644 --- a/packages/fhir/src/questionnaire/parse.ts +++ b/packages/fhir/src/questionnaire/parse.ts @@ -7,18 +7,22 @@ // import { type z } from 'zod' -import { groveQuestionnaireProfileCanonicals } from '../contract/questionnaire.generated.js' import { extensionValue, extensionsFor, validateQuestionnaireContract, validateQuestionnaireResponseItemContract, } from './contract.js' +import { + QUESTIONNAIRE_EXTENSIONS, + QUESTIONNAIRE_SYSTEMS, +} from './questionnaire-extensions.js' import { isExactQuestionnaireUrl, isQuestionnaireResponseReference, QUESTIONNAIRE_RESPONSE_AUTHOR_TYPES, QUESTIONNAIRE_RESPONSE_SOURCE_TYPES, + QUESTIONNAIRE_RESPONSE_SUBJECT_TYPES, } from './references.js' import { questionnaireResponseSchema, questionnaireSchema } from './schemas.js' import type { @@ -27,29 +31,26 @@ import type { QuestionnaireItemInput, QuestionnaireResponseItemInput, } from './types.js' +import { groveQuestionnaireProfileCanonicals } from '../contract/questionnaire.generated.js' import { cloneJsonValue, deepFreeze, - err, + issue, issues, ok, parseAbsoluteUri, parseCanonical, parseSemVer, + zodIssueToIssue, type Issue, type Result, } from '../core/index.js' import type { Extension } from '../r4/index.js' -/* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 canonicals are normative HTTP URIs. */ - -const VERSION_ALGORITHM = - 'http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm' -const VERSION_ALGORITHM_SYSTEM = 'http://hl7.org/fhir/version-algorithm' -const COMPLETION_MODE = - 'http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode' -const PARTICIPATION_MODE = - 'http://terminology.hl7.org/CodeSystem/v3-ParticipationMode' +const VERSION_ALGORITHM = QUESTIONNAIRE_EXTENSIONS.versionAlgorithm +const VERSION_ALGORITHM_SYSTEM = QUESTIONNAIRE_SYSTEMS.versionAlgorithm +const COMPLETION_MODE = QUESTIONNAIRE_EXTENSIONS.completionMode +const PARTICIPATION_MODE = QUESTIONNAIRE_SYSTEMS.participationMode const extensionValueKeys = [ 'valueBoolean', @@ -73,12 +74,6 @@ const extensionValueKeys = [ 'valueUrl', ] as const -const issue = ( - code: Issue['code'], - path: Issue['path'], - message: string, -): Issue => ({ severity: 'error', code, path, message }) - const objectPart = (value: unknown, property: string): unknown => typeof value === 'object' && value !== null ? Reflect.get(value, property) @@ -98,17 +93,6 @@ export const isExactQuestionnaireCanonical = (value: unknown): boolean => { ) } -const normalizeIssue = (entry: z.core.$ZodIssue): Issue => ({ - severity: 'error', - code: 'schema-invalid', - path: entry.path.map((component) => - typeof component === 'symbol' ? - (component.description ?? component.toString()) - : component, - ), - message: entry.message, -}) - const exactProfileIssues = ( resource: { readonly meta?: @@ -218,6 +202,16 @@ const questionnaireContractIssues = ( ), ) } + const subjectTypes = questionnaire.subjectType ?? [] + if (subjectTypes.length !== 1 || subjectTypes[0] !== 'Patient') { + failures.push( + issue( + 'value-mismatch', + ['subjectType'], + 'Questionnaire.subjectType is fixed to exactly Patient.', + ), + ) + } const algorithms = extensionsFor( { extension: questionnaire.extension ?? [] }, VERSION_ALGORITHM, @@ -280,8 +274,17 @@ const responseContractIssues = ( ), ) } + if (response.subject === undefined) { + failures.push( + issue( + 'missing-required', + ['subject'], + 'QuestionnaireResponse.subject is mandatory and references exactly one Patient.', + ), + ) + } for (const [field, reference, allowedTypes] of [ - ['subject', response.subject, undefined], + ['subject', response.subject, QUESTIONNAIRE_RESPONSE_SUBJECT_TYPES], ['author', response.author, QUESTIONNAIRE_RESPONSE_AUTHOR_TYPES], ['source', response.source, QUESTIONNAIRE_RESPONSE_SOURCE_TYPES], ] as const) { @@ -339,16 +342,9 @@ const responseContractIssues = ( const parseWith = (schema: z.ZodType, input: unknown): Result => { const snapshot = cloneJsonValue(input) if (!snapshot.ok) return snapshot - try { - const result = schema.safeParse(snapshot.value) - if (!result.success) return issues(result.error.issues.map(normalizeIssue)) - return ok(deepFreeze(result.data) as T) - } catch { - return err( - 'schema-invalid', - 'Questionnaire JSON validation could not safely inspect the supplied value.', - ) - } + const result = schema.safeParse(snapshot.value) + if (!result.success) return issues(result.error.issues.map(zodIssueToIssue)) + return ok(deepFreeze(result.data) as T) } export const parseQuestionnaire = ( diff --git a/packages/fhir/src/questionnaire/preflight-diagnostics.ts b/packages/fhir/src/questionnaire/preflight-diagnostics.ts index 10defa8a..e8943cd8 100644 --- a/packages/fhir/src/questionnaire/preflight-diagnostics.ts +++ b/packages/fhir/src/questionnaire/preflight-diagnostics.ts @@ -6,13 +6,9 @@ // SPDX-License-Identifier: MIT // -import type { Issue } from '../core/index.js' +import { issue, type Issue } from '../core/index.js' -export const issue = ( - code: Issue['code'], - path: Issue['path'], - message: string, -): Issue => ({ severity: 'error', code, path, message }) +export { issue } export const warning = ( code: Issue['code'], diff --git a/packages/fhir/src/questionnaire/preflight.ts b/packages/fhir/src/questionnaire/preflight.ts index 5f32d6a5..0a2b3698 100644 --- a/packages/fhir/src/questionnaire/preflight.ts +++ b/packages/fhir/src/questionnaire/preflight.ts @@ -11,6 +11,7 @@ import { extensionsFor, firstExtensionValue, QUESTIONNAIRE_EXTENSIONS, + QUESTIONNAIRE_SYSTEMS, validateQuestionnaireContract, } from './contract.js' import { @@ -46,16 +47,11 @@ import { type Result, } from '../core/index.js' -/* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 canonicals are normative HTTP URIs. */ - const completedStatuses = new Set(['amended', 'completed']) -const VERSION_ALGORITHM = - 'http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm' -const VERSION_ALGORITHM_SYSTEM = 'http://hl7.org/fhir/version-algorithm' -const COMPLETION_MODE = - 'http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode' -const PARTICIPATION_MODE = - 'http://terminology.hl7.org/CodeSystem/v3-ParticipationMode' +const VERSION_ALGORITHM = QUESTIONNAIRE_EXTENSIONS.versionAlgorithm +const VERSION_ALGORITHM_SYSTEM = QUESTIONNAIRE_SYSTEMS.versionAlgorithm +const COMPLETION_MODE = QUESTIONNAIRE_EXTENSIONS.completionMode +const PARTICIPATION_MODE = QUESTIONNAIRE_SYSTEMS.participationMode const validateQuestionnaireEnvelope = ( questionnaire: QuestionnairePair['questionnaire'], @@ -423,7 +419,6 @@ export const preflightQuestionnairePair = ( : prefixed(questionnaire.issues, 'questionnaire')), ...(response.ok ? [] : prefixed(response.issues, 'response')), ] - if (parseFailures.length > 0) return issues(parseFailures) if (!questionnaire.ok || !response.ok) return issues(parseFailures) const failures: Issue[] = [ diff --git a/packages/fhir/src/questionnaire/questionnaire-extensions.ts b/packages/fhir/src/questionnaire/questionnaire-extensions.ts index 35aff805..4926c965 100644 --- a/packages/fhir/src/questionnaire/questionnaire-extensions.ts +++ b/packages/fhir/src/questionnaire/questionnaire-extensions.ts @@ -14,9 +14,13 @@ import { type R4TemporalKind, } from './temporal.js' import type { QuestionnaireItemInput } from './types.js' -import type { Issue } from '../core/index.js' +import { issue, type Issue } from '../core/index.js' import type { Extension } from '../r4/index.js' +/* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 canonicals are normative HTTP URIs. */ + +export { issue } + export const QUESTIONNAIRE_EXTENSIONS = { calculatedExpression: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression', @@ -50,6 +54,16 @@ export const QUESTIONNAIRE_EXTENSIONS = { unitValueSet: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitValueSet', variable: 'http://hl7.org/fhir/StructureDefinition/variable', + versionAlgorithm: + 'http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm', + completionMode: + 'http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode', +} as const + +export const QUESTIONNAIRE_SYSTEMS = { + versionAlgorithm: 'http://hl7.org/fhir/version-algorithm', + participationMode: + 'http://terminology.hl7.org/CodeSystem/v3-ParticipationMode', } as const export interface ExtensionElement { @@ -152,12 +166,6 @@ export const populatedQuestionnaireAnswerKey = ( (key) => Reflect.get(value, key) !== undefined, ) -export const issue = ( - code: Issue['code'], - path: Issue['path'], - message: string, -): Issue => ({ severity: 'error', code, path, message }) - export const extensionsFor = ( element: ExtensionElement, url: string, diff --git a/packages/fhir/src/questionnaire/r4-resource-types.ts b/packages/fhir/src/questionnaire/r4-resource-types.ts index 67eadac9..10cabf57 100644 --- a/packages/fhir/src/questionnaire/r4-resource-types.ts +++ b/packages/fhir/src/questionnaire/r4-resource-types.ts @@ -6,162 +6,18 @@ // SPDX-License-Identifier: MIT // -// Exact codes from the required R4 ResourceType value set (FHIR 4.0.1). -const R4_RESOURCE_TYPE_CODES = [ - 'Account', - 'ActivityDefinition', - 'AdverseEvent', - 'AllergyIntolerance', - 'Appointment', - 'AppointmentResponse', - 'AuditEvent', - 'Basic', - 'Binary', - 'BiologicallyDerivedProduct', - 'BodyStructure', - 'Bundle', - 'CapabilityStatement', - 'CarePlan', - 'CareTeam', - 'CatalogEntry', - 'ChargeItem', - 'ChargeItemDefinition', - 'Claim', - 'ClaimResponse', - 'ClinicalImpression', - 'CodeSystem', - 'Communication', - 'CommunicationRequest', - 'CompartmentDefinition', - 'Composition', - 'ConceptMap', - 'Condition', - 'Consent', - 'Contract', - 'Coverage', - 'CoverageEligibilityRequest', - 'CoverageEligibilityResponse', - 'DetectedIssue', - 'Device', - 'DeviceDefinition', - 'DeviceMetric', - 'DeviceRequest', - 'DeviceUseStatement', - 'DiagnosticReport', - 'DocumentManifest', - 'DocumentReference', - 'DomainResource', - 'EffectEvidenceSynthesis', - 'Encounter', - 'Endpoint', - 'EnrollmentRequest', - 'EnrollmentResponse', - 'EpisodeOfCare', - 'EventDefinition', - 'Evidence', - 'EvidenceVariable', - 'ExampleScenario', - 'ExplanationOfBenefit', - 'FamilyMemberHistory', - 'Flag', - 'Goal', - 'GraphDefinition', - 'Group', - 'GuidanceResponse', - 'HealthcareService', - 'ImagingStudy', - 'Immunization', - 'ImmunizationEvaluation', - 'ImmunizationRecommendation', - 'ImplementationGuide', - 'InsurancePlan', - 'Invoice', - 'Library', - 'Linkage', - 'List', - 'Location', - 'Measure', - 'MeasureReport', - 'Media', - 'Medication', - 'MedicationAdministration', - 'MedicationDispense', - 'MedicationKnowledge', - 'MedicationRequest', - 'MedicationStatement', - 'MedicinalProduct', - 'MedicinalProductAuthorization', - 'MedicinalProductContraindication', - 'MedicinalProductIndication', - 'MedicinalProductIngredient', - 'MedicinalProductInteraction', - 'MedicinalProductManufactured', - 'MedicinalProductPackaged', - 'MedicinalProductPharmaceutical', - 'MedicinalProductUndesirableEffect', - 'MessageDefinition', - 'MessageHeader', - 'MolecularSequence', - 'NamingSystem', - 'NutritionOrder', - 'Observation', - 'ObservationDefinition', - 'OperationDefinition', - 'OperationOutcome', - 'Organization', - 'OrganizationAffiliation', - 'Parameters', - 'Patient', - 'PaymentNotice', - 'PaymentReconciliation', - 'Person', - 'PlanDefinition', - 'Practitioner', - 'PractitionerRole', - 'Procedure', - 'Provenance', - 'Questionnaire', - 'QuestionnaireResponse', - 'RelatedPerson', - 'RequestGroup', - 'ResearchDefinition', - 'ResearchElementDefinition', - 'ResearchStudy', - 'ResearchSubject', - 'Resource', - 'RiskAssessment', - 'RiskEvidenceSynthesis', - 'Schedule', - 'SearchParameter', - 'ServiceRequest', - 'Slot', - 'Specimen', - 'SpecimenDefinition', - 'StructureDefinition', - 'StructureMap', - 'Subscription', - 'Substance', - 'SubstanceNucleicAcid', - 'SubstancePolymer', - 'SubstanceProtein', - 'SubstanceReferenceInformation', - 'SubstanceSourceMaterial', - 'SubstanceSpecification', - 'SupplyDelivery', - 'SupplyRequest', - 'Task', - 'TerminologyCapabilities', - 'TestReport', - 'TestScript', - 'ValueSet', - 'VerificationResult', - 'VisionPrescription', -] as const +import { resourceTypeCodes } from '../zod/r4/index.js' +// The release publishes every concrete type plus the abstract Resource; DomainResource +// is abstract over the rest and instantiates nothing, so no schema names it. /** Exact code union from the required FHIR R4 ResourceType value set. */ -export type R4ResourceType = (typeof R4_RESOURCE_TYPE_CODES)[number] +export type R4ResourceType = + (typeof resourceTypeCodes)[number] | 'DomainResource' -const R4_RESOURCE_TYPES: ReadonlySet = new Set(R4_RESOURCE_TYPE_CODES) +const R4_RESOURCE_TYPES: ReadonlySet = new Set([ + ...resourceTypeCodes, + 'DomainResource', +]) /** Whether a code belongs to the required FHIR R4 ResourceType value set. */ export const isR4ResourceType = (value: string): boolean => diff --git a/packages/fhir/src/questionnaire/references.ts b/packages/fhir/src/questionnaire/references.ts index 71fb7e79..196e3b3e 100644 --- a/packages/fhir/src/questionnaire/references.ts +++ b/packages/fhir/src/questionnaire/references.ts @@ -6,8 +6,6 @@ // SPDX-License-Identifier: MIT // -/** Closed QuestionnaireResponse reference targets and literal/logical shapes. */ - import { isR4ResourceType } from './r4-resource-types.js' import { parseAbsoluteUri } from '../core/index.js' @@ -22,6 +20,9 @@ const FHIR_ID = /^[A-Za-z\d\-.]{1,64}$/u const TYPED_LITERAL_REFERENCE = /(?:^|\/)([A-Z][A-Za-z\d]*)\/([A-Za-z\d\-.]{1,64})(?:\/_history\/[A-Za-z\d\-.]{1,64})?$/u +export const QUESTIONNAIRE_RESPONSE_SUBJECT_TYPES: ReadonlySet = + new Set(['Patient']) + export const QUESTIONNAIRE_RESPONSE_AUTHOR_TYPES: ReadonlySet = new Set( [ 'Device', diff --git a/packages/fhir/src/questionnaire/schemas.ts b/packages/fhir/src/questionnaire/schemas.ts index 200076f2..e4a80f7b 100644 --- a/packages/fhir/src/questionnaire/schemas.ts +++ b/packages/fhir/src/questionnaire/schemas.ts @@ -10,7 +10,6 @@ import type { QuestionnaireItem as R4QuestionnaireItem, QuestionnaireResponseItem as R4QuestionnaireResponseItem, QuestionnaireResponseItemAnswer as R4QuestionnaireResponseItemAnswer, - Resource as R4Resource, } from 'fhir/r4.js' import { z } from 'zod' import { isR4ResourceType } from './r4-resource-types.js' @@ -31,7 +30,7 @@ import { metaSchema, primitiveElementSchema, quantitySchema, - referenceSchema, + resolvableReferenceSchema, } from '../r4/schemas.js' const primitiveMetadata = primitiveElementSchema.optional() @@ -103,12 +102,13 @@ const answerQuantitySchema = quantitySchema.refine( 'Quantity answer values cannot be empty.', ) +// A contained resource keeps the issuer's own shape; only its type code and id are Grove's. const containedResourceSchema = z.looseObject({ resourceType: z .string() .refine(isR4ResourceType, 'Unknown contained R4 ResourceType code.'), id: z.string().regex(/^[A-Za-z\d\-.]{1,64}$/u), -}) as unknown as z.ZodType +}) const resourceFields = { id: z.string().optional(), @@ -411,8 +411,8 @@ const questionnaireResponseSchemaValue = z.strictObject({ resourceType: z.literal('QuestionnaireResponse'), ...resourceFields, identifier: identifierSchema, - basedOn: z.array(referenceSchema).optional(), - partOf: z.array(referenceSchema).optional(), + basedOn: z.array(resolvableReferenceSchema).optional(), + partOf: z.array(resolvableReferenceSchema).optional(), questionnaire: z.string().min(1), _questionnaire: primitiveMetadata, status: z.enum([ @@ -423,12 +423,12 @@ const questionnaireResponseSchemaValue = z.strictObject({ 'stopped', ]), _status: primitiveMetadata, - subject: referenceSchema.optional(), - encounter: referenceSchema.optional(), + subject: resolvableReferenceSchema.optional(), + encounter: resolvableReferenceSchema.optional(), authored: r4InstantSchema, _authored: primitiveMetadata, - author: referenceSchema.optional(), - source: referenceSchema.optional(), + author: resolvableReferenceSchema.optional(), + source: resolvableReferenceSchema.optional(), item: z.array(questionnaireResponseItemSchema).optional(), }) export const questionnaireResponseSchema: z.ZodType = @@ -541,7 +541,7 @@ export const questionnaireBuilderInputSchema: z.ZodType = z.strictObject({ name: z.string().optional(), title: z.string().optional(), status: z.enum(['draft', 'active', 'retired', 'unknown']), - subjectTypes: z.array(z.string()).optional(), + subjectTypes: z.tuple([z.literal('Patient')]), date: z.string().optional(), description: z.string().optional(), purpose: z.string().optional(), @@ -589,13 +589,13 @@ const builderReferenceSchema = (type: z.ZodType): z.ZodType => }), ]) -const subjectReferenceInputSchema = builderReferenceSchema( - z.string().refine(isR4ResourceType, 'Expected a FHIR R4 ResourceType code.'), -) +// The Grove profile fixes the response subject to Patient, so the shape boundary states it. +const subjectReferenceInputSchema = builderReferenceSchema(z.literal('Patient')) // The shape boundary admits every R4 type so semantic validation can report // the field-specific author/source target rule alongside other input issues. -const authorReferenceInputSchema = subjectReferenceInputSchema -const sourceReferenceInputSchema = subjectReferenceInputSchema +const actorReferenceInputSchema = builderReferenceSchema( + z.string().refine(isR4ResourceType, 'Expected a FHIR R4 ResourceType code.'), +) /** Strict runtime boundary for the public QuestionnaireResponse builder input. */ export const questionnaireResponseBuilderInputSchema: z.ZodType = @@ -610,10 +610,10 @@ export const questionnaireResponseBuilderInputSchema: z.ZodType = 'entered-in-error', 'stopped', ]), - subject: subjectReferenceInputSchema.optional(), + subject: subjectReferenceInputSchema, authored: z.string(), - author: authorReferenceInputSchema.optional(), - source: sourceReferenceInputSchema.optional(), + author: actorReferenceInputSchema.optional(), + source: actorReferenceInputSchema.optional(), extensions: z.array(extensionSchema).optional(), items: z.array(questionnaireResponseBuilderItemSchema).optional(), }) diff --git a/packages/fhir/src/questionnaire/types.ts b/packages/fhir/src/questionnaire/types.ts index 0bc135eb..5db5afab 100644 --- a/packages/fhir/src/questionnaire/types.ts +++ b/packages/fhir/src/questionnaire/types.ts @@ -153,7 +153,8 @@ export interface QuestionnaireInput { readonly name?: string readonly title?: string readonly status: Questionnaire['status'] - readonly subjectTypes?: readonly R4ResourceType[] + /** Fixed by the Grove Questionnaire profile to exactly `Patient`. */ + readonly subjectTypes: readonly ['Patient'] readonly date?: FhirInstant readonly description?: string readonly purpose?: string @@ -178,9 +179,9 @@ type TypedQuestionnaireResponseReference = identifier: QuestionnaireResponseIdentifierInput }> -/** Any typed literal or identifier-only logical R4 subject Reference. */ +/** Typed literal or identifier-only logical Patient Reference. */ export type QuestionnaireResponseSubjectInput = - TypedQuestionnaireResponseReference + TypedQuestionnaireResponseReference<'Patient'> export type QuestionnaireResponseAuthorInput = TypedQuestionnaireResponseReference< @@ -203,7 +204,7 @@ export interface QuestionnaireResponseInput { readonly questionnaire: Canonical readonly identifier: QuestionnaireResponseIdentifierInput readonly status: QuestionnaireResponse['status'] - readonly subject?: QuestionnaireResponseSubjectInput + readonly subject: QuestionnaireResponseSubjectInput readonly authored: FhirInstant readonly author?: QuestionnaireResponseAuthorInput readonly source?: QuestionnaireResponseSourceInput From cf97f38342f6128d4cab35c92ea765ab4ae24145 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Mon, 31 Aug 2026 11:27:23 -0700 Subject: [PATCH 16/31] Read the Date Ordering Policy From Core --- packages/fhir/src/zod/support.ts | 69 ++------------------------------ 1 file changed, 3 insertions(+), 66 deletions(-) diff --git a/packages/fhir/src/zod/support.ts b/packages/fhir/src/zod/support.ts index 778545ba..9a011755 100644 --- a/packages/fhir/src/zod/support.ts +++ b/packages/fhir/src/zod/support.ts @@ -6,71 +6,8 @@ // SPDX-License-Identifier: MIT // -interface ComparableFhirDateTime { - readonly epochSecond: number - /** Leap seconds sort after the preceding second and before the next POSIX second. */ - readonly phase: 0 | 1 - readonly fraction: string -} - -const FHIR_TIMESTAMP = - /^(?\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:)(?[0-5]\d|60)(?:\.(?\d+))?(?Z|[+-]\d{2}:\d{2})$/u -const FHIR_PARTIAL_DATE = /^\d{4}(?:-\d{2}(?:-\d{2})?)?$/u - -const comparableFhirDateTime = ( - value: string, -): ComparableFhirDateTime | undefined => { - const timestamp = FHIR_TIMESTAMP.exec(value)?.groups - if (timestamp !== undefined) { - const leap = timestamp.second === '60' - const parsed = Date.parse( - `${timestamp.prefix}${leap ? '59' : timestamp.second}${timestamp.offset}`, - ) - if (!Number.isFinite(parsed)) return undefined - return { - epochSecond: parsed / 1000 + (leap ? 1 : 0), - phase: leap ? 0 : 1, - fraction: timestamp.fraction ?? '', - } - } - if (!FHIR_PARTIAL_DATE.test(value)) return undefined - let suffix = '' - if (value.length === 4) suffix = '-01-01' - else if (value.length === 7) suffix = '-01' - const parsed = Date.parse(`${value}${suffix}T00:00:00Z`) - return Number.isFinite(parsed) ? - { epochSecond: parsed / 1000, phase: 1, fraction: '' } - : undefined -} - -const compareFraction = (left: string, right: string): -1 | 0 | 1 => { - const width = Math.max(left.length, right.length) - const normalizedLeft = left.padEnd(width, '0') - const normalizedRight = right.padEnd(width, '0') - if (normalizedLeft === normalizedRight) return 0 - return normalizedLeft < normalizedRight ? -1 : 1 -} - -/** - * Compares FHIR dateTime values without JavaScript Date's millisecond truncation. - * - * Partial values resolve to the start of the period they name, matching Grove's release-owned - * ordering policy. Leap seconds retain their distinct position before the following POSIX second. - * `undefined` means an endpoint is malformed; primitive validation reports that defect separately. - */ -export const compareFhirDateTimes = ( - left: string, - right: string, -): -1 | 0 | 1 | undefined => { - const first = comparableFhirDateTime(left) - const second = comparableFhirDateTime(right) - if (first === undefined || second === undefined) return undefined - if (first.epochSecond !== second.epochSecond) { - return first.epochSecond < second.epochSecond ? -1 : 1 - } - if (first.phase !== second.phase) return first.phase < second.phase ? -1 : 1 - return compareFraction(first.fraction, second.fraction) -} +// The generated release schemas read their ordering policy from here; core owns it. +export { compareFhirDateTimes } from '../core/primitives.js' const FULL_FHIR_DATE = /^(\d{4})-(\d{2})-(\d{2})(?:T|$)/u @@ -84,7 +21,7 @@ const daysInMonth = (year: number, month: number): number => { } /** - * Complements the release-owned primitive regex with Gregorian calendar validity. + * Complements the contract-owned primitive regex with Gregorian calendar validity. * Partial FHIR dates have no day to validate and remain valid at this layer. */ export const hasValidFhirCalendarDate = (value: string): boolean => { From 2ed9b41b9797be4782fdb14e6b2f50b36690f419 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Mon, 31 Aug 2026 11:27:23 -0700 Subject: [PATCH 17/31] Name Producer Rules and Their Locations From the Registry --- packages/fhir/eslint.config.mjs | 7 - .../fhir/src/r4/active-graph-semantics.ts | 8 +- .../src/r4/active-observation-semantics.ts | 163 ++------ packages/fhir/src/r4/base-schemas.ts | 2 - packages/fhir/src/r4/diagnostics.ts | 250 +++++------ packages/fhir/src/r4/exchange-envelope.ts | 61 ++- packages/fhir/src/r4/graph-schema-utils.ts | 126 +++--- packages/fhir/src/r4/index.ts | 1 - packages/fhir/src/r4/parse.ts | 393 ++++++------------ packages/fhir/src/r4/profile-semantics.ts | 88 ++-- .../fhir/src/r4/retraction-graph-semantics.ts | 37 +- packages/fhir/src/r4/schemas.ts | 36 +- packages/fhir/src/r4/types.ts | 43 +- 13 files changed, 557 insertions(+), 658 deletions(-) diff --git a/packages/fhir/eslint.config.mjs b/packages/fhir/eslint.config.mjs index 8a617957..74afe499 100644 --- a/packages/fhir/eslint.config.mjs +++ b/packages/fhir/eslint.config.mjs @@ -10,13 +10,6 @@ import { getEslintNodeConfig } from '@schmiedmayerlab/grove-configurations' export default [ ...getEslintNodeConfig({ tsconfigRootDir: import.meta.dirname }), - { - files: ['src/**/*.ts'], - rules: { - // FHIR's recursive schema graph intentionally mirrors the specification. - 'import-x/no-cycle': 'off', - }, - }, { files: ['src/extract/*.ts', 'test/extract*.test.ts'], rules: { diff --git a/packages/fhir/src/r4/active-graph-semantics.ts b/packages/fhir/src/r4/active-graph-semantics.ts index 0955684c..afb8151f 100644 --- a/packages/fhir/src/r4/active-graph-semantics.ts +++ b/packages/fhir/src/r4/active-graph-semantics.ts @@ -6,8 +6,6 @@ // SPDX-License-Identifier: MIT // -/** Active exchange-envelope and cross-resource measurement semantics. */ - import type { z } from 'zod' import { validateMobileObservationSemantics } from './active-observation-semantics.js' import { @@ -31,13 +29,13 @@ import { type UnknownRecord, } from './graph-schema-utils.js' import type { R4CollectionBundle } from './types.js' -import { parseAbsoluteUri } from '../core/index.js' -import { groveMobileContract } from '../mobile/contract.js' -import { isOpaqueIdentityValue } from '../mobile/identity.js' import { groveProfileClaims, healthConnectDataOriginApplication, } from '../contract/measurement-catalog.generated.js' +import { parseAbsoluteUri } from '../core/index.js' +import { groveMobileContract } from '../mobile/contract.js' +import { isOpaqueIdentityValue } from '../mobile/identity.js' export { hasAdmittedMobileObservationProfile } from './active-observation-semantics.js' export { validateExchangeEnvelope, type ValidatedEnvelope } diff --git a/packages/fhir/src/r4/active-observation-semantics.ts b/packages/fhir/src/r4/active-observation-semantics.ts index e1d8d4e9..d843bb1f 100644 --- a/packages/fhir/src/r4/active-observation-semantics.ts +++ b/packages/fhir/src/r4/active-observation-semantics.ts @@ -6,8 +6,6 @@ // SPDX-License-Identifier: MIT // -/** Catalog-driven Observation profile and result semantics. */ - import type { z } from 'zod' import { addIssue, @@ -16,80 +14,23 @@ import { codingCountForSystem, type UnknownRecord, } from './graph-schema-utils.js' +import { groveProfileClaims } from '../contract/measurement-catalog.generated.js' +import { providerAdapterCatalog } from '../contract/providers.generated.js' +import { compareFhirDateTimes } from '../core/primitives.js' import { groveMobileContract } from '../mobile/contract.js' import { - groveProfileClaims, - sharedMobileMeasurementCatalog, -} from '../contract/measurement-catalog.generated.js' -import { - adapterMeasurementCatalog, - groveProviderProfileCanonicals, - providerAdapterCatalog, -} from '../contract/providers.generated.js' -import { compareFhirDateTimes } from '../zod/support.js' - -interface MeasurementDefinition { - readonly id: string - readonly profile: string - readonly owner?: string - readonly code: Readonly<{ system: string; code: string }> - readonly requiredCodings?: ReadonlyArray< - Readonly<{ - system: string - code: string - }> - > - readonly category?: Readonly<{ system: string; code: string }> - readonly effective: 'Period' | 'dateTime' | 'dateTime-or-Period' - readonly method?: Readonly<{ code: string }> - readonly methodChoice?: readonly string[] - readonly valueKind: - 'codeableConcept' | 'components' | 'dateTime' | 'grouping' | 'quantity' - readonly quantity?: Readonly<{ - system: string - code: string - unit: string - valueDomain?: Readonly<{ - minimum: Readonly<{ value: number; inclusive: boolean }> - maximum?: Readonly<{ value: number; inclusive: boolean }> - integerOnly: boolean - }> - }> | null - readonly resultCodeSystem?: string - readonly allowedValues?: readonly string[] - readonly components?: ReadonlyArray< - Readonly<{ - id: string - system: string - code: string - cardinality?: string - quantity: Readonly<{ system: string; code: string; unit: string }> - }> - > -} - -const ADAPTER_MEASUREMENT_DEFINITIONS = - adapterMeasurementCatalog as unknown as Readonly< - Record>> - > - -const MEASUREMENT_DEFINITIONS: readonly MeasurementDefinition[] = [ - ...(Object.values( - sharedMobileMeasurementCatalog, - ) as readonly MeasurementDefinition[]), - ...Object.values(ADAPTER_MEASUREMENT_DEFINITIONS).flatMap((owner) => - Object.values(owner), - ), -] - -const PROFILE_CANONICALS = groveProviderProfileCanonicals as Readonly< - Record -> + ALL_MEASUREMENT_DEFINITIONS, + PROVIDER_ROWS, + profileCanonical, + providerObservationProfiles, + violatesQuantityDomain, + type MeasurementDefinition, +} from '../providers/measurement-definition.js' const MEASUREMENT_BY_PROFILE: ReadonlyMap = new Map( - MEASUREMENT_DEFINITIONS.flatMap((definition) => { - const canonical = PROFILE_CANONICALS[definition.profile] + ALL_MEASUREMENT_DEFINITIONS.flatMap((definition) => { + const canonical = profileCanonical(definition.profile) return canonical === undefined ? [] : [[canonical, definition] as const] }), ) @@ -114,18 +55,6 @@ const SENSORKIT_HYBRID_PROFILES: ReadonlySet = new Set( groveProfileClaims.sensorKitHybridObservationClaims.profiles, ) -interface ProviderContractRow { - readonly id: string - readonly measurementOwner: string - readonly observationProfile: string -} - -const PROVIDER_ROWS = - providerAdapterCatalog.providers as unknown as readonly ProviderContractRow[] -const PROVIDER_OBSERVATION_PROFILES: ReadonlySet = new Set( - PROVIDER_ROWS.map(({ observationProfile }) => observationProfile), -) - const hasExactProviderObservationClaim = ( observation: UnknownRecord, match: MeasurementDefinition | null, @@ -144,8 +73,7 @@ const hasExactProviderObservationClaim = ( directProfiles.includes(providerAdapterCatalog.adapterProfile) || directProfiles.some( (profile) => - typeof profile === 'string' && - PROVIDER_OBSERVATION_PROFILES.has(profile), + typeof profile === 'string' && providerObservationProfiles.has(profile), ) || (match?.owner !== undefined && PROVIDER_ROWS.some( @@ -158,7 +86,7 @@ const hasExactProviderObservationClaim = ( const providerCode = asRecord(providerMarkers[0])?.valueCode const provider = PROVIDER_ROWS.find(({ id }) => id === providerCode) - const semanticProfile = PROFILE_CANONICALS[match.profile] + const semanticProfile = profileCanonical(match.profile) if ( provider === undefined || semanticProfile === undefined || @@ -251,7 +179,7 @@ const validateObservationCodes = ( path: ReadonlyArray, ): void => { const requiredCodings = match.requiredCodings ?? [] - const admittedPrimaryCodes = new Set([ + const admittedPrimaryCodes: ReadonlySet = new Set([ match.code.code, ...requiredCodings .filter(({ system }) => system === match.code.system) @@ -398,7 +326,7 @@ const validateObservationMethod = ( ) } } else if (match.methodChoice !== undefined) { - const admittedMethods = match.methodChoice + const admittedMethods: readonly string[] = match.methodChoice const coding = asRecord(observation.method)?.coding const admitted = Array.isArray(coding) ? @@ -431,36 +359,25 @@ const validateQuantityValueDomain = ( context: z.core.$RefinementCtx, path: ReadonlyArray, ): void => { - const domain = match.quantity?.valueDomain - if (typeof value !== 'number' || domain === undefined) return - const belowMinimum = - value < domain.minimum.value || - (!domain.minimum.inclusive && value === domain.minimum.value) - const aboveMaximum = - domain.maximum !== undefined && - (value > domain.maximum.value || - (!domain.maximum.inclusive && value === domain.maximum.value)) - if ( - belowMinimum || - aboveMaximum || - (domain.integerOnly && !Number.isInteger(value)) - ) { - addIssue( - context, - `mobile-${match.id}.value-domain`, - [...path, 'valueQuantity', 'value'], - `A Grove Mobile ${match.id} result must satisfy its catalog-owned value domain.`, - ) - } + if (typeof value !== 'number' || !violatesQuantityDomain(value, match)) return + addIssue( + context, + 'mobile-output.quantity-value-domain', + [...path, 'valueQuantity', 'value'], + `A Grove Mobile ${match.id} result must satisfy its catalog-owned value domain.`, + ) } -const validateStepCountPeriod = ( +const POSITIVE_PERIOD_CONSTRAINT = 'grove-step-count-period-1' + +const validatePositivePeriod = ( observation: UnknownRecord, match: MeasurementDefinition, context: z.core.$RefinementCtx, path: ReadonlyArray, ): void => { - if (match.id !== 'step-count') return + const obeys: readonly string[] = match.obeys ?? [] + if (!obeys.includes(POSITIVE_PERIOD_CONSTRAINT)) return const period = asRecord(observation.effectivePeriod) if ( typeof period?.start === 'string' && @@ -469,9 +386,9 @@ const validateStepCountPeriod = ( ) { addIssue( context, - 'mobile-step-count.nonzero-period', + `mobile-${match.id}.nonzero-period`, [...path, 'effectivePeriod'], - 'A Grove Mobile step-count period must have nonzero duration.', + `A Grove Mobile ${match.id} period must have nonzero duration.`, ) } } @@ -500,7 +417,7 @@ const validateQuantityResult = ( ) } validateQuantityValueDomain(valueQuantity?.value, match, context, path) - validateStepCountPeriod(observation, match, context, path) + validatePositivePeriod(observation, match, context, path) } const validateCodeableConceptResult = ( @@ -511,7 +428,9 @@ const validateCodeableConceptResult = ( ): void => { const result = asRecord(observation.valueCodeableConcept) const codings = Array.isArray(result?.coding) ? result.coding : [] - const allowedValues = new Set(match.allowedValues ?? []) + const allowedValues: ReadonlySet = new Set( + match.allowedValues ?? [], + ) const admitted = codings.filter((candidate) => { const coding = asRecord(candidate) return ( @@ -600,6 +519,11 @@ const validateComponentResult = ( asRecord(components[matchedIndex]) ) const quantity = asRecord(matching?.valueQuantity) + const componentQuantity = definition.quantity + const unitClause = + componentQuantity === undefined ? '' : ( + ` in fixed UCUM ${componentQuantity.code}` + ) const optional = definition.cardinality === '0..1' if ( matchingIndexes.length > 1 || @@ -609,18 +533,19 @@ const validateComponentResult = ( (matching !== undefined && codingCountForSystem(matching.code, definition.system) !== 1) || (matching !== undefined && + componentQuantity !== undefined && (typeof quantity?.value !== 'number' || !Number.isFinite(quantity.value) || quantity.comparator !== undefined || - quantity.system !== definition.quantity.system || - quantity.code !== definition.quantity.code || - quantity.unit !== definition.quantity.unit)) + quantity.system !== componentQuantity.system || + quantity.code !== componentQuantity.code || + quantity.unit !== componentQuantity.unit)) ) { addIssue( context, `mobile-${match.id}.${definition.id}`, [...path, 'component'], - `A Grove Mobile ${match.id} panel admits at most one distinct finite ${definition.id} component in fixed UCUM ${definition.quantity.code}${optional ? '' : ' and requires it'}.`, + `A Grove Mobile ${match.id} panel admits at most one distinct finite ${definition.id} component${unitClause}${optional ? '' : ' and requires it'}.`, ) } if (matchedIndex !== undefined) matchedComponentIndexes.add(matchedIndex) diff --git a/packages/fhir/src/r4/base-schemas.ts b/packages/fhir/src/r4/base-schemas.ts index 74d27150..84ea61ea 100644 --- a/packages/fhir/src/r4/base-schemas.ts +++ b/packages/fhir/src/r4/base-schemas.ts @@ -93,8 +93,6 @@ export const resolvableReferenceSchema: z.ZodType = }, ) -export const referenceSchema: z.ZodType = resolvableReferenceSchema - /** The Element a primitive carries its id and extensions in. */ export const primitiveElementSchema: typeof elementSchema = elementSchema diff --git a/packages/fhir/src/r4/diagnostics.ts b/packages/fhir/src/r4/diagnostics.ts index 1b84fbb8..df6b6867 100644 --- a/packages/fhir/src/r4/diagnostics.ts +++ b/packages/fhir/src/r4/diagnostics.ts @@ -6,20 +6,11 @@ // SPDX-License-Identifier: MIT // -import type { Issue } from '../core/index.js' import { groveExchangeRuleDiagnostics } from '../contract/measurement-catalog.generated.js' - -const ENCODED_RULE_PREFIX = 'grove-producer-rule:' +import type { Issue } from '../core/index.js' export type GroveExchangeRuleCode = keyof typeof groveExchangeRuleDiagnostics -export interface GroveExchangeRuleDiagnostic { - readonly code: GroveExchangeRuleCode - readonly reason: string - readonly location: string - readonly severity: 'error' -} - const entryIndex = ( path: ReadonlyArray, ): number | undefined => { @@ -28,140 +19,153 @@ const entryIndex = ( return typeof candidate === 'number' ? candidate : undefined } +type LocationPath = ReadonlyArray + +const bundleEntry = (path: LocationPath): string => + `Bundle.entry[${entryIndex(path) ?? 0}]` +const provenanceTarget = ( + path: LocationPath, + ordinalOffset: number, +): string => { + const ordinal = path.at(ordinalOffset) + return `Provenance.target[${typeof ordinal === 'number' ? String(ordinal) : '0'}]` +} +const fixed = (location: string) => (): string => location + /** * Stable FHIR-facing locations for producer rules. Array ordinals remain dynamic, * while logical resource paths intentionally avoid leaking parser-internal paths. + * `groveRuleLocation` indexes this table by the registry, so a newly registered rule + * fails to compile until it names its location. A rule this package implements ahead + * of the pinned registry may be named here, so registering it costs no edit. */ +const groveRuleLocations = { + 'healthkit-clinical.fhir-representation': (path) => + `${bundleEntry(path)}.resource.content[0].attachment.contentType`, + 'healthkit-ecg.output-graph': (path) => `${bundleEntry(path)}.resource`, + 'mobile-device.recording-device-dual-identity': fixed('Device.identifier'), + 'mobile-exchange.collection-entry-operation': fixed('Bundle.entry'), + 'mobile-exchange.contained-resource-prohibited': (path) => + `${bundleEntry(path)}.resource.contained`, + 'mobile-exchange.deterministic-full-url': (path) => + `${bundleEntry(path)}.fullUrl`, + 'mobile-exchange.entry-node-digest': (path) => + `${bundleEntry(path)}.extension.valueIdentifier.value`, + 'mobile-exchange.entry-node-key': bundleEntry, + 'mobile-exchange.entry-node-ordinal': (path) => + `${bundleEntry(path)}.extension.valueIdentifier.value`, + 'mobile-exchange.entry-resource-type': (path) => + `${bundleEntry(path)}.resource.resourceType`, + 'mobile-exchange.event-identity': fixed('Bundle.identifier.value'), + 'mobile-exchange.identity-system-role': fixed('Bundle'), + 'mobile-exchange.lifecycle-coding': fixed('Provenance.activity.coding'), + 'mobile-exchange.logical-patient-reference': fixed('Observation.subject'), + 'mobile-exchange.logical-source-entity': fixed('Provenance.entity[0].what'), + 'mobile-exchange.provenance-profile': fixed('Provenance.meta.profile'), + 'mobile-exchange.reference-declared-type': fixed('Observation.subject.type'), + 'mobile-exchange.reference-shape': fixed('Observation.subject'), + 'mobile-exchange.reference-target-type': fixed( + 'Observation.subject.reference', + ), + 'mobile-exchange.resolved-reference': (path) => + `${bundleEntry(path)}.resource.subject.reference`, + 'mobile-exchange.single-source-entity': fixed('Provenance.entity'), + 'mobile-exchange.transform-provenance': fixed('Bundle.entry'), + 'mobile-exchange.unclassified': fixed('Bundle'), + 'mobile-output.adapter-only-profile': fixed('Specimen.meta.profile'), + 'mobile-output.document-profile': fixed('DocumentReference.meta.profile'), + 'mobile-output.fixed-quantity-unit': (path) => + `${bundleEntry(path)}.resource.valueQuantity.code`, + 'mobile-output.hybrid-companion': (path) => + `${bundleEntry(path)}.resource.meta.profile`, + 'mobile-output.quantity-value-domain': (path) => + `${bundleEntry(path)}.resource.valueQuantity.value`, + 'mobile-output.semantic-profile': fixed('Observation.meta.profile'), + 'mobile-output.source-output-required': (path) => + `${bundleEntry(path)}.resource.identifier`, + 'mobile-retraction.logical-target': (path) => provenanceTarget(path, -1), + 'mobile-retraction.native-record-identifier': (path) => + `${provenanceTarget(path, -2)}.extension.valueIdentifier.type`, + 'mobile-retraction.no-clinical-copy': (path) => + `${bundleEntry(path)}.resource`, + 'mobile-retraction.opaque-target': (path) => + `${provenanceTarget(path, -2)}.identifier.value`, + 'mobile-retraction.role-target-type': (path) => + `${provenanceTarget(path, -2)}.type`, + 'mobile-retraction.target-role': (path) => + `${provenanceTarget(path, -2)}.extension`, + 'mobile-support.connected': fixed('Bundle.entry'), + 'mobile-support.device-profile': fixed('Device.meta.profile'), + 'mobile-support.questionnaire-response-profile': fixed( + 'QuestionnaireResponse.meta.profile', + ), + 'sensor-recording-document.identity-and-content': fixed( + 'DocumentReference.identifier', + ), +} satisfies Readonly string>> + const groveRuleLocation = ( code: GroveExchangeRuleCode, - path: ReadonlyArray, -): string => { - const index = entryIndex(path) - const bundleEntry = `Bundle.entry[${index ?? 0}]` - switch (code) { - case 'mobile-exchange.entry-node-key': - return bundleEntry - case 'mobile-exchange.deterministic-full-url': - return `${bundleEntry}.fullUrl` - case 'mobile-exchange.resolved-reference': - return `${bundleEntry}.resource.subject.reference` - case 'mobile-output.fixed-quantity-unit': - return `${bundleEntry}.resource.valueQuantity.code` - case 'mobile-exchange.event-identity': - return 'Bundle.identifier.value' - case 'mobile-exchange.entry-node-digest': - return `${bundleEntry}.extension.valueIdentifier.value` - case 'mobile-output.source-output-required': - return `${bundleEntry}.resource.identifier` - case 'mobile-exchange.identity-system-role': - return 'Bundle' - case 'mobile-exchange.transform-provenance': - return 'Bundle.entry' - case 'mobile-retraction.logical-target': - return `Provenance.target[${typeof path.at(-1) === 'number' ? String(path.at(-1)) : '0'}]` - case 'mobile-retraction.target-role': - return `Provenance.target[${typeof path.at(-2) === 'number' ? String(path.at(-2)) : '0'}].extension` - case 'mobile-retraction.opaque-target': - return `Provenance.target[${typeof path.at(-2) === 'number' ? String(path.at(-2)) : '0'}].identifier.value` - case 'mobile-retraction.no-clinical-copy': - return `${bundleEntry}.resource` - case 'mobile-exchange.lifecycle-coding': - return 'Provenance.activity.coding' - case 'mobile-output.semantic-profile': - return 'Observation.meta.profile' - case 'mobile-exchange.reference-target-type': - return 'Observation.subject.reference' - case 'mobile-exchange.reference-declared-type': - return 'Observation.subject.type' - case 'mobile-exchange.logical-source-entity': - return 'Provenance.entity[0].what' - case 'mobile-retraction.role-target-type': - return `Provenance.target[${typeof path.at(-2) === 'number' ? String(path.at(-2)) : '0'}].type` - case 'mobile-exchange.single-source-entity': - return 'Provenance.entity' - case 'mobile-exchange.reference-shape': - case 'mobile-exchange.logical-patient-reference': - return 'Observation.subject' - case 'mobile-exchange.entry-resource-type': - return `${bundleEntry}.resource.resourceType` - case 'mobile-output.adapter-only-profile': - return 'Specimen.meta.profile' - case 'mobile-exchange.contained-resource-prohibited': - return `${bundleEntry}.resource.contained` - case 'mobile-output.document-profile': - return 'DocumentReference.meta.profile' - case 'mobile-support.device-profile': - return 'Device.meta.profile' - case 'mobile-exchange.provenance-profile': - return 'Provenance.meta.profile' - case 'mobile-support.connected': - return 'Bundle.entry' - } -} + path: LocationPath, +): string => groveRuleLocations[code](path) const isGroveExchangeRuleCode = (code: string): code is GroveExchangeRuleCode => Object.hasOwn(groveExchangeRuleDiagnostics, code) -const groveRuleDiagnostic = ( - code: GroveExchangeRuleCode, - location: string, -): GroveExchangeRuleDiagnostic => ({ - code, - reason: groveExchangeRuleDiagnostics[code].reason, - location, - severity: groveExchangeRuleDiagnostics[code].severity, -}) +// Producer rules are namespaced; anything else is a base schema failure, not a rule. +const isProducerRuleCode = (code: string): code is `${string}.${string}` => + code.includes('.') -export const encodeGroveRuleDiagnostic = ( +/** Registered normative reason, or undefined for a rule this package names locally. */ +export const groveRuleReason = (code: string): string | undefined => + isGroveExchangeRuleCode(code) ? + groveExchangeRuleDiagnostics[code].reason + : undefined + +/** Structured producer-rule payload a Grove refinement attaches to its zod issue. */ +export const groveRuleParameters = ( code: string, path: ReadonlyArray, - fallbackMessage: string, location?: string, -): string => +): Readonly> => isGroveExchangeRuleCode(code) ? - `${ENCODED_RULE_PREFIX}${JSON.stringify(groveRuleDiagnostic(code, location ?? groveRuleLocation(code, path)))}` - : `${code}: ${fallbackMessage}` - -export const decodeGroveRuleDiagnostic = ( - message: string, -): GroveExchangeRuleDiagnostic | undefined => { - if (!message.startsWith(ENCODED_RULE_PREFIX)) return undefined - try { - const candidate = JSON.parse(message.slice(ENCODED_RULE_PREFIX.length)) as { - readonly code?: unknown - readonly reason?: unknown - readonly location?: unknown - readonly severity?: unknown - } - if ( - typeof candidate.code !== 'string' || - !isGroveExchangeRuleCode(candidate.code) || - candidate.reason !== - groveExchangeRuleDiagnostics[candidate.code].reason || - candidate.severity !== - groveExchangeRuleDiagnostics[candidate.code].severity || - typeof candidate.location !== 'string' || - candidate.location.length === 0 - ) { - return undefined + { + groveRuleCode: code, + groveRuleLocation: location ?? groveRuleLocation(code, path), } - return candidate as GroveExchangeRuleDiagnostic - } catch { - return undefined - } -} + : { groveRuleCode: code } export const groveRuleIssue = ( code: GroveExchangeRuleCode, path: ReadonlyArray, -): Issue => { - const diagnostic = groveRuleDiagnostic(code, groveRuleLocation(code, path)) +): Issue => ({ + severity: groveExchangeRuleDiagnostics[code].severity, + code, + path, + message: groveExchangeRuleDiagnostics[code].reason, + reason: groveExchangeRuleDiagnostics[code].reason, + location: groveRuleLocation(code, path), +}) + +/** Rebuilds the producer-rule Issue a refinement recorded on its zod issue params. */ +export const groveRuleIssueFromParameters = ( + parameters: Readonly> | undefined, + path: ReadonlyArray, + message: string, +): Issue | undefined => { + const code = parameters?.groveRuleCode + if (typeof code !== 'string' || !isProducerRuleCode(code)) return undefined + if (!isGroveExchangeRuleCode(code)) { + return { severity: 'error', code, path, message } + } + const location = parameters?.groveRuleLocation return { - severity: diagnostic.severity, - code: diagnostic.code, + severity: groveExchangeRuleDiagnostics[code].severity, + code, path, - message: diagnostic.reason, - reason: diagnostic.reason, - location: diagnostic.location, + message: groveExchangeRuleDiagnostics[code].reason, + reason: groveExchangeRuleDiagnostics[code].reason, + location: + typeof location === 'string' ? location : groveRuleLocation(code, path), } } diff --git a/packages/fhir/src/r4/exchange-envelope.ts b/packages/fhir/src/r4/exchange-envelope.ts index b557876f..0ed0dc85 100644 --- a/packages/fhir/src/r4/exchange-envelope.ts +++ b/packages/fhir/src/r4/exchange-envelope.ts @@ -6,8 +6,6 @@ // SPDX-License-Identifier: MIT // -/** Deterministic Bundle envelope, entry identity, and reference validation. */ - import type { z } from 'zod' import { OPAQUE_IDENTIFIER_ROLES, @@ -24,7 +22,6 @@ import { locatedGroveIdentifiers, locatedReferences, parseEntryNodeParts, - referenceTypeMatches, selectedBusinessKey, type UnknownRecord, validateProfileReferenceTargets, @@ -55,6 +52,8 @@ interface EnvelopeState { readonly keyPairs: Set readonly internalLogicalReferences: Set readonly resourcesByFullUrl: Map + /** Ordinal each entry-node entry owes the graph, counted over Bundle order alone. */ + readonly entryNodeOrdinals: ReadonlyMap } interface ValidatedEntryKey { @@ -161,6 +160,45 @@ const validateEntryTransport = ( return entry.fullUrl } +// A self-consistent ordinal hides inside its own digest, so the graph counts the +// entries sharing each node role itself and never reads the ordinal it is checking. +const entryNodeOrdinals = ( + entries: readonly UnknownRecord[], +): ReadonlyMap => { + const counts = new Map() + const ordinals = new Map() + for (const [index, entry] of entries.entries()) { + const key = entryKey(entry) + if (!completeIdentifier(key) || identifierRole(key) !== 'entry-node') { + continue + } + const role = parseEntryNodeParts(key.value)?.[1] + if (role === undefined) continue + const ordinal = counts.get(role) ?? 0 + ordinals.set(index, String(ordinal)) + counts.set(role, ordinal + 1) + } + return ordinals +} + +const validateEntryNodeOrdinal = ( + key: CompleteIdentifier, + expected: string | undefined, + index: number, + context: z.core.$RefinementCtx, +): void => { + const written = parseEntryNodeParts(key.value)?.[2] + if (written === undefined || expected === undefined || written === expected) { + return + } + addIssue( + context, + 'mobile-exchange.entry-node-ordinal', + ['entry', index, 'extension'], + `This entry is number ${expected} among the entries sharing its node role, so its entry-node ordinal cannot be ${written}.`, + ) +} + const validateEntryNodeDigest = ( key: CompleteIdentifier, event: CompleteIdentifier, @@ -196,6 +234,7 @@ const validateEntryKeyValue = ( role: string | undefined, event: CompleteIdentifier, index: number, + state: EnvelopeState, context: z.core.$RefinementCtx, ): void => { if (role === undefined) { @@ -207,6 +246,12 @@ const validateEntryKeyValue = ( ) } else if (role === 'entry-node') { validateEntryNodeDigest(key, event, index, context) + validateEntryNodeOrdinal( + key, + state.entryNodeOrdinals.get(index), + index, + context, + ) } else if ( !OPAQUE_IDENTIFIER_ROLES.has(role) || !isOpaqueIdentityValue(key.value) @@ -215,7 +260,7 @@ const validateEntryKeyValue = ( context, 'mobile-exchange.opaque-entry-key', ['entry', index, 'extension'], - 'Business entry keys require a closed role and canonical v0 HMAC value.', + 'Business entry keys require a closed role and canonical Grove HMAC value.', ) } } @@ -248,7 +293,7 @@ const validateEntryKey = ( } state.keyPairs.add(pair) const role = identifierRole(key) - validateEntryKeyValue(key, role, event, index, context) + validateEntryKeyValue(key, role, event, index, state, context) return { identifier: key, role } } @@ -313,7 +358,7 @@ const resourceIdentityRoleCounts = ( context, 'mobile-exchange.opaque-resource-identity', ['entry', index, 'resource', 'identifier'], - 'A typed resource identity must be one complete canonical v0 HMAC Identifier.', + 'A typed resource identity must be one complete canonical Grove HMAC Identifier.', ) } } @@ -497,8 +542,7 @@ const validateLocatedReference = ( )?.resourceType if ( located.type !== undefined && - (typeof targetType !== 'string' || - !referenceTypeMatches(located.type, targetType)) + (typeof targetType !== 'string' || located.type !== targetType) ) { addIssue( context, @@ -585,6 +629,7 @@ export const validateExchangeEnvelope = ( keyPairs: new Set(), internalLogicalReferences: new Set(), resourcesByFullUrl: new Map(), + entryNodeOrdinals: entryNodeOrdinals(entries), } const active = requiredProfile === groveMobileContract.profiles.exchangeBundle for (const [index, entry] of entries.entries()) { diff --git a/packages/fhir/src/r4/graph-schema-utils.ts b/packages/fhir/src/r4/graph-schema-utils.ts index f10a5c7d..efb59d99 100644 --- a/packages/fhir/src/r4/graph-schema-utils.ts +++ b/packages/fhir/src/r4/graph-schema-utils.ts @@ -6,15 +6,13 @@ // SPDX-License-Identifier: MIT // -/** FHIR graph identity, reference, coding, and diagnostic utilities. */ - /* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 fixes lifecycle and participant canonicals to HTTP. */ import type { z } from 'zod' -import { encodeGroveRuleDiagnostic } from './diagnostics.js' +import { groveRuleParameters, groveRuleReason } from './diagnostics.js' +import { healthKitApplicationDeviceIdentity } from '../contract/providers.generated.js' import { parseAbsoluteUri } from '../core/index.js' import { groveMobileContract } from '../mobile/contract.js' -import { healthKitApplicationDeviceIdentity } from '../contract/providers.generated.js' export type UnknownRecord = Readonly> @@ -37,9 +35,9 @@ export const completeIdentifier = ( const LOWERCASE_UUID_V5 = /^urn:uuid:[\da-f]{8}-[\da-f]{4}-5[\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}$/u -const ENTRY_NODE_PARTS = /^n0:([a-z][a-z\d-]*):(0|[1-9]\d*):[A-Za-z\d_-]{43}$/u -const LOGICAL_PATIENT_RESERVED_SYSTEMS = new Set( - groveMobileContract.referencePolicy.identifierOnlyPatient.reservedSystems, +const ENTRY_NODE_PARTS = new RegExp( + `^${groveMobileContract.identity.valuePrefixes.entryNode}([a-z][a-z\\d-]*):(0|[1-9]\\d*):[A-Za-z\\d_-]{43}$`, + 'u', ) export const isLowercaseUuidV5 = (value: string): boolean => @@ -214,11 +212,6 @@ export const locatedReferences = ( ] } -export const referenceTypeMatches = ( - declared: unknown, - actual: string, -): boolean => declared === actual - const internalReferenceTarget = ( reference: unknown, resourcesByFullUrl: ReadonlyMap, @@ -263,13 +256,49 @@ const validateGovernedReferenceShape = ( const hasLiteral = typeof value?.reference === 'string' && value.reference.trim() !== '' const hasIdentifier = value?.identifier !== undefined + const identifier = asRecord(value?.identifier) + const patientOnly = allowedTypes.size === 1 && allowedTypes.has('Patient') + const reservedPatientSystems: ReadonlySet = new Set( + groveMobileContract.referencePolicy.identifierOnlyPatient.reservedSystems, + ) + const declaredType = value?.type + if ( + !hasLiteral && + hasIdentifier && + patientOnly && + typeof identifier?.system === 'string' && + reservedPatientSystems.has(identifier.system) + ) { + addIssue( + context, + 'mobile-exchange.logical-patient-reference', + [...path, 'identifier', 'system'], + 'An identifier-only Patient Reference cannot use a protocol-reserved system.', + `${location}.identifier.system`, + ) + return + } + if ( + !hasLiteral && + hasIdentifier && + patientOnly && + groveIdentifierRoles(identifier).length > 0 + ) { + addIssue( + context, + 'mobile-exchange.logical-patient-reference', + [...path, 'identifier', 'type'], + 'An identifier-only Patient Reference cannot carry a Grove identifier role.', + `${location}.identifier.type`, + ) + return + } const validLogical = !hasLiteral && - completeIdentifier(value?.identifier) && - parseAbsoluteUri(value.identifier.system).ok && - typeof value.type === 'string' && - allowedTypes.has(value.type) - const patientOnly = allowedTypes.size === 1 && allowedTypes.has('Patient') + completeIdentifier(identifier) && + parseAbsoluteUri(identifier.system).ok && + typeof declaredType === 'string' && + allowedTypes.has(declaredType) if (hasLiteral && hasIdentifier) { addIssue( context, @@ -296,40 +325,22 @@ const validateGovernedReferenceShape = ( 'A governed Reference requires exactly one resolving literal or identifier-only logical shape.', location, ) - } else if (validLogical && patientOnly) { - const identifier = asRecord(value.identifier) - if ( - typeof identifier?.system === 'string' && - LOGICAL_PATIENT_RESERVED_SYSTEMS.has(identifier.system) - ) { - addIssue( - context, - 'mobile-exchange.logical-patient-reference', - [...path, 'identifier', 'system'], - 'A logical Patient identifier cannot use a protocol-reserved code-system URI.', - `${location}.identifier.system`, - ) - } - const type = asRecord(identifier?.type) - const coding = Array.isArray(type?.coding) ? type.coding : [] - if ( - coding.some( - (candidate) => - asRecord(candidate)?.system === - groveMobileContract.systems.identifierRole, - ) - ) { - addIssue( - context, - 'mobile-exchange.logical-patient-reference', - [...path, 'identifier', 'type'], - 'A logical Patient identifier cannot carry a Grove identifier role.', - `${location}.identifier.type`, - ) - } } } +// The reference-policy catalog states admitted targets, not cardinality. Every Grove +// profile binding one of these paths to Patient also binds it 1..1, so an absent +// subject is a contract violation the governed-reference check must report. +const MANDATORY_REFERENCE_PATHS: ReadonlySet = new Set([ + 'DocumentReference.subject', + 'MedicationAdministration.subject', + 'MedicationStatement.subject', + 'Observation.subject', + 'QuestionnaireResponse.subject', + 'Specimen.subject', + 'VisionPrescription.patient', +]) + const validateProfilePathReferenceTargets = ( resource: UnknownRecord, resourcesByFullUrl: ReadonlyMap, @@ -340,6 +351,20 @@ const validateProfilePathReferenceTargets = ( if (rule.resourceType !== resource.resourceType) continue const references = resource[rule.path] const values = Array.isArray(references) ? references : [references] + const ruleLocation = `${rule.resourceType}.${rule.path}` + if ( + values.every((reference) => reference === undefined) && + MANDATORY_REFERENCE_PATHS.has(ruleLocation) + ) { + addIssue( + context, + 'mobile-exchange.reference-shape', + [...path, rule.path], + `${ruleLocation} is mandatory and requires one governed Reference.`, + ruleLocation, + ) + continue + } for (const [index, reference] of values.entries()) { if (reference === undefined) continue const targetTypes = new Set(rule.targetTypes) @@ -350,7 +375,7 @@ const validateProfilePathReferenceTargets = ( ] const referenceIndex = Array.isArray(references) ? `[${String(index)}]` : '' - const referenceLocation = `${String(resource.resourceType)}.${rule.path}${referenceIndex}` + const referenceLocation = `${ruleLocation}${referenceIndex}` validateGovernedReferenceShape( reference, targetTypes, @@ -466,6 +491,7 @@ export const addIssue = ( context.addIssue({ code: 'custom', path: [...path], - message: encodeGroveRuleDiagnostic(code, path, message, location), + message: groveRuleReason(code) ?? message, + params: groveRuleParameters(code, path, location), }) } diff --git a/packages/fhir/src/r4/index.ts b/packages/fhir/src/r4/index.ts index d759bcfb..98c4c76c 100644 --- a/packages/fhir/src/r4/index.ts +++ b/packages/fhir/src/r4/index.ts @@ -36,7 +36,6 @@ export { primitiveElementSchema, provenanceSchema, quantitySchema, - referenceSchema, resolvableReferenceSchema, r4CollectionBundleSchema, sampledDataSchema, diff --git a/packages/fhir/src/r4/parse.ts b/packages/fhir/src/r4/parse.ts index 4e57f167..ed3f7ffe 100644 --- a/packages/fhir/src/r4/parse.ts +++ b/packages/fhir/src/r4/parse.ts @@ -7,7 +7,12 @@ // import { type z } from 'zod' -import { decodeGroveRuleDiagnostic, groveRuleIssue } from './diagnostics.js' +import { + groveRuleIssue, + groveRuleIssueFromParameters, + type GroveExchangeRuleCode, +} from './diagnostics.js' +import { asRecord } from './graph-schema-utils.js' import { groveMobileExchangeBundleSchema, groveMobileRetractionBundleSchema, @@ -40,70 +45,30 @@ import type { import { cloneJsonValue, deepFreeze, - err, issues, - type IssueCode, ok, + zodIssuePath, + zodIssueToIssue, type Issue, type Result, } from '../core/index.js' -const LOCAL_MOBILE_RULE = /^(mobile-[a-z\d.-]+): (.+)$/su - -const issuePath = (entry: z.core.$ZodIssue): ReadonlyArray => - entry.path.map((component) => - typeof component === 'symbol' ? - (component.description ?? component.toString()) - : component, - ) - -const normalizeIssue = (entry: z.core.$ZodIssue): Issue => { - const path = issuePath(entry) - const diagnostic = decodeGroveRuleDiagnostic(entry.message) - if (diagnostic !== undefined) { - return { - severity: diagnostic.severity, - code: diagnostic.code, - path, - message: diagnostic.reason, - reason: diagnostic.reason, - location: diagnostic.location, - } - } - const localRule = LOCAL_MOBILE_RULE.exec(entry.message) - if (localRule !== null) { - return { - severity: 'error', - code: localRule[1] as IssueCode, - path, - message: entry.message, - } - } - return { - severity: 'error', - code: 'schema-invalid', - path, - message: entry.message, - } -} +const normalizeIssue = (entry: z.core.$ZodIssue): Issue => + groveRuleIssueFromParameters( + entry.code === 'custom' ? entry.params : undefined, + zodIssuePath(entry), + entry.message, + ) ?? zodIssueToIssue(entry) // The schema decides the parsed type; a caller-chosen T would make the cast below a lie. const parseSnapshotWith = ( schema: z.ZodType, snapshot: unknown, ): Result => { - try { - const result = schema.safeParse(snapshot) - if (!result.success) { - return issues(result.error.issues.map(normalizeIssue)) - } - return ok(deepFreeze(result.data) as T) - } catch { - return err( - 'schema-invalid', - 'FHIR JSON validation could not safely inspect the supplied value.', - ) - } + const result = schema.safeParse(snapshot) + return result.success ? + ok(deepFreeze(result.data) as T) + : issues(result.error.issues.map(normalizeIssue)) } const parseWith = (schema: z.ZodType, input: unknown): Result => { @@ -111,11 +76,6 @@ const parseWith = (schema: z.ZodType, input: unknown): Result => { return snapshot.ok ? parseSnapshotWith(schema, snapshot.value) : snapshot } -const entryResource = (entry: unknown): unknown => - typeof entry === 'object' && entry !== null && 'resource' in entry ? - (entry as { readonly resource?: unknown }).resource - : undefined - export const parseObservation = (input: unknown): Result => parseWith(observationSchema, input) @@ -136,215 +96,134 @@ export const parseR4CollectionBundle = ( input: unknown, ): Result => parseWith(r4CollectionBundleSchema, input) -export const parseGroveMobileExchangeBundle = ( - input: unknown, -): Result => { - const snapshot = cloneJsonValue(input) - if (!snapshot.ok) return snapshot - input = snapshot.value - const entries = - typeof input === 'object' && input !== null && 'entry' in input ? - (input as { readonly entry?: unknown }).entry - : undefined - if (Array.isArray(entries)) { - const invalidResourceTypeIndex = entries.findIndex( - (entry) => !isAdmittedActiveEntryResource(entryResource(entry)), - ) - if (invalidResourceTypeIndex !== -1) { - return issues([ - groveRuleIssue('mobile-exchange.entry-resource-type', [ - 'entry', - invalidResourceTypeIndex, - 'resource', - 'resourceType', - ]), - ]) - } - const containedIndex = entries.findIndex((entry) => - hasProhibitedContainedResource(entryResource(entry)), - ) - if (containedIndex !== -1) { - return issues([ - groveRuleIssue('mobile-exchange.contained-resource-prohibited', [ - 'entry', - containedIndex, - 'resource', - 'contained', - ]), - ]) - } - const invalidIndex = entries.findIndex((entry) => { - if ( - typeof entry !== 'object' || - entry === null || - !('resource' in entry) - ) { - return false - } - const resource = (entry as { readonly resource?: unknown }).resource - return ( - typeof resource === 'object' && - resource !== null && - 'resourceType' in resource && - (resource as { readonly resourceType?: unknown }).resourceType === - 'Observation' && - !hasAdmittedMobileObservationProfile(resource) - ) - }) - if (invalidIndex !== -1) { - return issues([ - groveRuleIssue('mobile-output.semantic-profile', [ - 'entry', - invalidIndex, - 'resource', - 'meta', - 'profile', - ]), - ]) - } - const invalidAdapterOnlyIndex = entries.findIndex((entry) => { - if ( - typeof entry !== 'object' || - entry === null || - !('resource' in entry) - ) { - return false - } - return !hasAdmittedAdapterOnlyOutputProfile( - (entry as { readonly resource?: unknown }).resource, - ) - }) - if (invalidAdapterOnlyIndex !== -1) { - return issues([ - groveRuleIssue('mobile-output.adapter-only-profile', [ - 'entry', - invalidAdapterOnlyIndex, - 'resource', - 'meta', - 'profile', - ]), - ]) - } - const invalidDocumentIndex = entries.findIndex((entry) => { - const resource = entryResource(entry) - return ( - typeof resource === 'object' && - resource !== null && - 'resourceType' in resource && - (resource as { readonly resourceType?: unknown }).resourceType === - 'DocumentReference' && - !hasAdmittedActiveDocumentReferenceProfile(resource) - ) - }) - if (invalidDocumentIndex !== -1) { - return issues([ - groveRuleIssue('mobile-output.document-profile', [ - 'entry', - invalidDocumentIndex, - 'resource', - 'meta', - 'profile', - ]), - ]) - } - const invalidDeviceIndex = entries.findIndex((entry) => { - const resource = entryResource(entry) - return ( - typeof resource === 'object' && - resource !== null && - 'resourceType' in resource && - (resource as { readonly resourceType?: unknown }).resourceType === - 'Device' && - !hasAdmittedActiveDeviceProfile(resource) - ) - }) - if (invalidDeviceIndex !== -1) { - return issues([ - groveRuleIssue('mobile-support.device-profile', [ - 'entry', - invalidDeviceIndex, - 'resource', - 'meta', - 'profile', - ]), - ]) - } - const invalidProvenanceIndex = entries.findIndex((entry) => { - const resource = entryResource(entry) +const entryResource = (entry: unknown): unknown => asRecord(entry)?.resource + +const ofType = + (resourceType: string, rejects: (resource: unknown) => boolean) => + (resource: unknown): boolean => + asRecord(resource)?.resourceType === resourceType && rejects(resource) + +interface EntryRulePrecheck { + readonly code: GroveExchangeRuleCode + readonly path: readonly string[] + readonly rejects: (resource: unknown) => boolean +} + +// The base R4 union rejects an unadmitted entry before the graph refinements run, so +// these rules report first to keep their exact corpus diagnostic. +const ACTIVE_ENTRY_PRECHECKS: readonly EntryRulePrecheck[] = [ + { + code: 'mobile-exchange.entry-resource-type', + path: ['resource', 'resourceType'], + rejects: (resource) => !isAdmittedActiveEntryResource(resource), + }, + { + code: 'mobile-exchange.contained-resource-prohibited', + path: ['resource', 'contained'], + rejects: hasProhibitedContainedResource, + }, + { + code: 'mobile-output.semantic-profile', + path: ['resource', 'meta', 'profile'], + rejects: ofType( + 'Observation', + (resource) => !hasAdmittedMobileObservationProfile(resource), + ), + }, + { + code: 'mobile-output.adapter-only-profile', + path: ['resource', 'meta', 'profile'], + rejects: (resource) => !hasAdmittedAdapterOnlyOutputProfile(resource), + }, + { + code: 'mobile-output.document-profile', + path: ['resource', 'meta', 'profile'], + rejects: ofType( + 'DocumentReference', + (resource) => !hasAdmittedActiveDocumentReferenceProfile(resource), + ), + }, + { + code: 'mobile-support.device-profile', + path: ['resource', 'meta', 'profile'], + rejects: ofType( + 'Device', + (resource) => !hasAdmittedActiveDeviceProfile(resource), + ), + }, + { + code: 'mobile-exchange.provenance-profile', + path: ['resource', 'meta', 'profile'], + rejects: ofType( + 'Provenance', + (resource) => !hasAdmittedActiveProvenanceProfile(resource), + ), + }, +] + +const RETRACTION_ENTRY_PRECHECKS: readonly EntryRulePrecheck[] = [ + { + code: 'mobile-retraction.no-clinical-copy', + path: ['resource'], + rejects: (resource) => { + const resourceType = asRecord(resource)?.resourceType return ( - typeof resource === 'object' && - resource !== null && - 'resourceType' in resource && - (resource as { readonly resourceType?: unknown }).resourceType === - 'Provenance' && - !hasAdmittedActiveProvenanceProfile(resource) + typeof resourceType === 'string' && + !['Device', 'Provenance'].includes(resourceType) ) - }) - if (invalidProvenanceIndex !== -1) { - return issues([ - groveRuleIssue('mobile-exchange.provenance-profile', [ - 'entry', - invalidProvenanceIndex, - 'resource', - 'meta', - 'profile', - ]), - ]) - } + }, + }, + { + code: 'mobile-exchange.contained-resource-prohibited', + path: ['resource', 'contained'], + rejects: hasProhibitedContainedResource, + }, +] + +const entryRuleIssue = ( + snapshot: unknown, + prechecks: readonly EntryRulePrecheck[], +): Issue | undefined => { + const entries = asRecord(snapshot)?.entry + if (!Array.isArray(entries)) return undefined + for (const { code, path, rejects } of prechecks) { + const index = entries.findIndex((entry) => rejects(entryResource(entry))) + if (index !== -1) return groveRuleIssue(code, ['entry', index, ...path]) } - return parseSnapshotWith(groveMobileExchangeBundleSchema, input) + return undefined } -export const parseGroveMobileRetractionBundle = ( +const parseGraphBundle = ( + schema: z.ZodType, + prechecks: readonly EntryRulePrecheck[], input: unknown, -): Result => { +): Result => { const snapshot = cloneJsonValue(input) if (!snapshot.ok) return snapshot - input = snapshot.value - const entries = - typeof input === 'object' && input !== null && 'entry' in input ? - (input as { readonly entry?: unknown }).entry - : undefined - if (Array.isArray(entries)) { - const invalidIndex = entries.findIndex((entry) => { - const resource = entryResource(entry) - return ( - typeof resource === 'object' && - resource !== null && - 'resourceType' in resource && - !['Device', 'Provenance'].includes( - String( - (resource as { readonly resourceType?: unknown }).resourceType, - ), - ) - ) - }) - if (invalidIndex !== -1) { - return issues([ - groveRuleIssue('mobile-retraction.no-clinical-copy', [ - 'entry', - invalidIndex, - 'resource', - ]), - ]) - } - const containedIndex = entries.findIndex((entry) => - hasProhibitedContainedResource(entryResource(entry)), - ) - if (containedIndex !== -1) { - return issues([ - groveRuleIssue('mobile-exchange.contained-resource-prohibited', [ - 'entry', - containedIndex, - 'resource', - 'contained', - ]), - ]) - } - } - return parseSnapshotWith(groveMobileRetractionBundleSchema, input) + const early = entryRuleIssue(snapshot.value, prechecks) + return early === undefined ? + parseSnapshotWith(schema, snapshot.value) + : issues([early]) } +export const parseGroveMobileExchangeBundle = ( + input: unknown, +): Result => + parseGraphBundle( + groveMobileExchangeBundleSchema, + ACTIVE_ENTRY_PRECHECKS, + input, + ) + +export const parseGroveMobileRetractionBundle = ( + input: unknown, +): Result => + parseGraphBundle( + groveMobileRetractionBundleSchema, + RETRACTION_ENTRY_PRECHECKS, + input, + ) + export const parseSupportedR4Resource = ( input: unknown, ): Result => parseWith(supportedR4ResourceSchema, input) diff --git a/packages/fhir/src/r4/profile-semantics.ts b/packages/fhir/src/r4/profile-semantics.ts index 9b2d9542..c489b243 100644 --- a/packages/fhir/src/r4/profile-semantics.ts +++ b/packages/fhir/src/r4/profile-semantics.ts @@ -6,8 +6,6 @@ // SPDX-License-Identifier: MIT // -/** Exact profile claims and adapter-specific resource semantics. */ - import { sha1 } from '@noble/hashes/legacy.js' import type { z } from 'zod' import { @@ -18,7 +16,6 @@ import { identifiersOf, type UnknownRecord, } from './graph-schema-utils.js' -import { groveMobileContract } from '../mobile/contract.js' import { adapterSourceMarkerClaims, groveProfileClaims, @@ -27,29 +24,35 @@ import { healthKitApplicationDeviceIdentity, healthKitClinicalRecordAdmission, } from '../contract/providers.generated.js' +import { decodeCanonicalBase64 } from '../core/index.js' +import { groveMobileContract } from '../mobile/contract.js' const SHA1_BASE64 = /^[A-Za-z\d+/]{26}[AEIMQUYcgkosw048]=$/u +// FHIR's base64Binary lexical space admits whitespace; the payload itself must still be canonical. const decodeFhirBase64 = (value: unknown): Uint8Array | undefined => { if (typeof value !== 'string') return undefined const packed = value.replaceAll(/[ \t\n\r]/gu, '') - if (packed.length === 0 || packed.length % 4 !== 0) return undefined - try { - const decoded = atob(packed) - const bytes = new Uint8Array(decoded.length) - for (let index = 0; index < decoded.length; index += 1) { - bytes[index] = decoded.charCodeAt(index) - } - return bytes - } catch { - return undefined - } + return packed.length === 0 ? undefined : decodeCanonicalBase64(packed) } const bytesEqual = (left: Uint8Array, right: Uint8Array): boolean => left.length === right.length && left.every((value, index) => value === right[index]) +type RecordingFormats = typeof groveMobileContract.recordingFormats.formats + +const isRecordingFormatCode = (code: unknown): code is keyof RecordingFormats => + typeof code === 'string' && + Object.hasOwn(groveMobileContract.recordingFormats.formats, code) + +const recordingFormat = ( + code: unknown, +): RecordingFormats[keyof RecordingFormats] | undefined => + isRecordingFormatCode(code) ? + groveMobileContract.recordingFormats.formats[code] + : undefined + interface ExactProfileClaim { readonly cardinality: number readonly profiles: readonly string[] @@ -329,53 +332,40 @@ const validateRecordingDocument = ( ) } } - const formats = groveMobileContract.recordingFormats.formats as Readonly< - Record< - string, - { - readonly contentType?: string - readonly contentTypes?: readonly string[] - readonly status: string - } - > - > - const definition = - typeof format?.code === 'string' ? formats[format.code] : undefined - const admittedContentTypes = - definition?.contentTypes ?? - (definition?.contentType === undefined ? [] : [definition.contentType]) - const attachmentContentType = - typeof attachment?.contentType === 'string' ? - attachment.contentType - : undefined + const definition = recordingFormat(format?.code) + const formatContentTypes: readonly string[] = definition?.contentTypes ?? [] if ( format?.system !== groveMobileContract.recordingFormats.codeSystem || format.version !== undefined || definition?.status !== 'active' || - attachmentContentType === undefined || - !admittedContentTypes.includes(attachmentContentType) + typeof attachment?.contentType !== 'string' || + !formatContentTypes.includes(attachment.contentType) ) { addIssue( context, 'mobile-recording-document.format', [...path, 'content', 0, 'format'], - 'Recording format and content type must match the active Grove registry entry; release-coupled Coding.version is not carried in an instance.', + 'Recording format and content type must match the active Grove registry entry; Coding.version is not permitted.', ) } - if ( - claim === groveProfileClaims.healthKitClinicalRecordDocumentClaim && - (format?.code !== healthKitClinicalRecordAdmission.payloadFormat || - !Object.values( - healthKitClinicalRecordAdmission.fhirRepresentation - .contentTypeByRelease, - ).some((contentType) => contentType === attachmentContentType)) - ) { - addIssue( - context, - 'healthkit-clinical-record.fhir-release', - [...path, 'content', 0], - 'A HealthKit clinical-record document must use the FHIR resource format and one catalog-admitted versioned FHIR JSON media type.', + if (claim === groveProfileClaims.healthKitClinicalRecordDocumentClaim) { + const representation = healthKitClinicalRecordAdmission.fhirRepresentation + const admittedContentTypes: readonly string[] = Object.values( + representation.contentTypeByRelease, ) + if ( + resource.resourceType !== representation.resourceType || + format?.code !== healthKitClinicalRecordAdmission.payloadFormat || + typeof attachment?.contentType !== 'string' || + !admittedContentTypes.includes(attachment.contentType) + ) { + addIssue( + context, + 'healthkit-clinical.fhir-representation', + [...path, 'content', 0, 'attachment', 'contentType'], + 'A HealthKit clinical-record document must declare the admitted payload format and the exact media type of its source FHIR release.', + ) + } } } diff --git a/packages/fhir/src/r4/retraction-graph-semantics.ts b/packages/fhir/src/r4/retraction-graph-semantics.ts index ac484709..efa16d37 100644 --- a/packages/fhir/src/r4/retraction-graph-semantics.ts +++ b/packages/fhir/src/r4/retraction-graph-semantics.ts @@ -6,8 +6,6 @@ // SPDX-License-Identifier: MIT // -/** Append-only retraction graph and logical-target semantics. */ - import type { z } from 'zod' import { validateAssemblerAgent, @@ -21,6 +19,7 @@ import { codingCountForSystem, codingExists, completeIdentifier, + groveIdentifierRoles, identifierRole, type UnknownRecord, } from './graph-schema-utils.js' @@ -169,7 +168,7 @@ const validateRetractionTargetIdentity = ( context, 'mobile-retraction.opaque-target', ['entry', 'resource', 'target', index, 'identifier'], - 'Each target requires the exact typed canonical v0 HMAC Identifier previously emitted.', + 'Each target requires the exact typed canonical Grove HMAC Identifier previously emitted.', ) } } @@ -193,6 +192,37 @@ const recordDistinctTarget = ( keys.add(pair) } +// The native key space belongs to the adapter, so Grove proves only that it is complete, +// absolute, singular, and never dressed up as one of Grove's own identity roles. +const validateRetractionTargetNativeIdentifier = ( + reference: UnknownRecord | undefined, + index: number, + context: z.core.$RefinementCtx, +): void => { + const extensions = + Array.isArray(reference?.extension) ? reference.extension : [] + const matches = extensions.filter( + (extension) => + asRecord(extension)?.url === + groveMobileContract.extensions.retractionTargetNativeIdentifier, + ) + if (matches.length === 0) return + const native = asRecord(matches[0])?.valueIdentifier + if ( + matches.length !== 1 || + !completeIdentifier(native) || + !parseAbsoluteUri(native.system).ok || + groveIdentifierRoles(native).length > 0 + ) { + addIssue( + context, + 'mobile-retraction.native-record-identifier', + ['entry', 'resource', 'target', index, 'extension'], + "A target carries at most one native record Identifier, complete in the adapter's own absolute key space and free of any Grove identifier-role coding.", + ) + } +} + const validateRetractionTargetType = ( reference: UnknownRecord | undefined, role: string | undefined, @@ -246,6 +276,7 @@ const validateRetractionTarget = ( ) recordDistinctTarget(identifier, index, keys, context) validateRetractionTargetType(reference, role, targetRule, index, context) + validateRetractionTargetNativeIdentifier(reference, index, context) } const validateRetractionTargets = ( diff --git a/packages/fhir/src/r4/schemas.ts b/packages/fhir/src/r4/schemas.ts index 79c27bb3..98f6c5c6 100644 --- a/packages/fhir/src/r4/schemas.ts +++ b/packages/fhir/src/r4/schemas.ts @@ -8,9 +8,13 @@ /** Stable public schema composition; domain refinements live in focused modules. */ -import type { z } from 'zod' +import { z } from 'zod' import { refineActiveBundle } from './active-graph-semantics.js' -import { r4CollectionBundleSchema } from './base-schemas.js' +import { + graphResourceSchema, + identifierSchema, + r4CollectionBundleSchema, +} from './base-schemas.js' import { refineRetractionBundle } from './retraction-graph-semantics.js' import type { GroveMobileExchangeBundle, @@ -30,14 +34,38 @@ export { isAdmittedActiveEntryResource, } from './active-graph-semantics.js' +// The envelope every Grove graph Bundle type promises. It runs after the graph +// refinements so a rule keeps reporting its own diagnostic, and it holds the +// refinements to what the exported types assert; only readonly depth stays a cast. +const groveGraphBundleSchema = z.looseObject({ + identifier: identifierSchema, + timestamp: z.string().min(1), + entry: z + .array( + z.looseObject({ + fullUrl: z.string().min(1), + resource: graphResourceSchema, + }), + ) + .nonempty(), +}) + +const groveGraphBundle = (refine: typeof refineActiveBundle) => + r4CollectionBundleSchema + .superRefine(refine) + // The graph rules run first and report their own diagnostics; piping their result + // through the envelope holds them to what the exported bundle types assert. + .transform((value): unknown => value) + .pipe(groveGraphBundleSchema) + /** Profile-aware parser for one immutable active Grove exchange event. */ export const groveMobileExchangeBundleSchema: z.ZodType = - r4CollectionBundleSchema.superRefine( + groveGraphBundle( refineActiveBundle, ) as unknown as z.ZodType /** Profile-aware parser for one append-only Grove source retraction assertion. */ export const groveMobileRetractionBundleSchema: z.ZodType = - r4CollectionBundleSchema.superRefine( + groveGraphBundle( refineRetractionBundle, ) as unknown as z.ZodType diff --git a/packages/fhir/src/r4/types.ts b/packages/fhir/src/r4/types.ts index e36fd893..71fe2ac6 100644 --- a/packages/fhir/src/r4/types.ts +++ b/packages/fhir/src/r4/types.ts @@ -90,44 +90,27 @@ export type R4CollectionBundle = ReadonlyDeep< } > -/** Grove-profiled event graph with complete event and entry identity. */ -export type GroveMobileExchangeBundle = ReadonlyDeep< +/** One Bundle entry whose deterministic fullUrl and admitted resource are both present. */ +type GraphEntry = Omit & { + readonly fullUrl: string + readonly resource: Resource +} + +type GraphBundle = ReadonlyDeep< Omit & { readonly identifier: Identifier readonly timestamp: string readonly entry: readonly [ - Omit & { - readonly fullUrl: string - readonly resource: GraphResource - }, - ...Array< - Omit & { - readonly fullUrl: string - readonly resource: GraphResource - } - >, + GraphEntry, + ...Array>, ] } > +/** Grove-profiled event graph with complete event and entry identity. */ +export type GroveMobileExchangeBundle = GraphBundle + /** Grove-profiled retraction assertion containing Provenance and optional Device agents only. */ -export type GroveMobileRetractionBundle = ReadonlyDeep< - Omit & { - readonly identifier: Identifier - readonly timestamp: string - readonly entry: readonly [ - Omit & { - readonly fullUrl: string - readonly resource: Device | Provenance - }, - ...Array< - Omit & { - readonly fullUrl: string - readonly resource: Device | Provenance - } - >, - ] - } -> +export type GroveMobileRetractionBundle = GraphBundle export type SupportedR4Resource = R4CollectionBundle | GraphResource From 7bffdef0ba42eefe1aaf356c147dc12d86f6d1b2 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Mon, 31 Aug 2026 11:27:31 -0700 Subject: [PATCH 18/31] Regenerate the Contracts and Fixtures From Grove FHIR 0.6.0 --- packages/fhir/README.md | 57 +++- .../catalog/measurement-capabilities.json | 32 +- .../conformance/mobile-exchange/corpus.json | 41 +++ .../mobile-exchange/retraction-bundle.json | 7 + .../resources/mobile-active-energy.json | 120 +++---- .../resources/mobile-basal-energy.json | 120 +++---- ...le-blood-glucose-unspecified-specimen.json | 120 +++---- .../resources/mobile-blood-pressure.json | 120 +++---- .../resources/mobile-body-fat-percentage.json | 120 +++---- .../resources/mobile-body-height.json | 120 +++---- .../resources/mobile-body-temperature.json | 120 +++---- .../resources/mobile-body-water-mass.json | 120 +++---- .../resources/mobile-body-weight.json | 120 +++---- .../resources/mobile-bone-mass.json | 120 +++---- .../resources/mobile-deep-sleep-duration.json | 120 +++---- .../resources/mobile-distance.json | 120 +++---- .../mobile-electrodermal-activity.json | 120 +++---- .../resources/mobile-flights-climbed.json | 120 +++---- .../mobile-heart-rate-variability-rmssd.json | 120 +++---- .../mobile-heart-rate-variability-sdnn.json | 120 +++---- .../resources/mobile-heart-rate.json | 120 +++---- .../resources/mobile-lean-body-mass.json | 120 +++---- .../mobile-light-sleep-duration.json | 120 +++---- ...obile-oxygen-saturation-daily-average.json | 120 +++---- .../resources/mobile-oxygen-saturation.json | 120 +++---- .../resources/mobile-rem-sleep-duration.json | 120 +++---- .../mobile-respiratory-rate-average.json | 120 +++---- .../resources/mobile-skin-temperature.json | 120 +++---- .../mobile-sleep-awake-duration.json | 120 +++---- .../resources/mobile-sleep-duration.json | 120 +++---- .../resources/mobile-sleep-heart-rate.json | 120 +++---- .../resources/mobile-step-count.json | 120 +++---- .../conformance/resources/mobile-vo2-max.json | 120 +++---- .../conformance/resources/mobile-workout.json | 120 +++---- ...-api-resting-heart-rate-daily-average.json | 120 +++---- .../provider-oura-daily-activity.json | 164 ++++----- ...provider-oura-oura-cardiovascular-age.json | 120 +++---- .../provider-oura-oura-readiness-score.json | 120 +++---- .../provider-primary-output-retraction.json | 90 ++--- .../provider-source-artifact-retraction.json | 90 ++--- .../provider-withings-body-fat-mass.json | 120 +++---- ...ovider-withings-corrected-qt-interval.json | 120 +++---- ...der-withings-extracellular-water-mass.json | 120 +++---- ...der-withings-intracellular-water-mass.json | 120 +++---- .../provider-withings-muscle-mass.json | 120 +++---- .../provider-withings-pr-interval.json | 120 +++---- .../provider-withings-qrs-duration.json | 120 +++---- .../provider-withings-qt-interval.json | 120 +++---- ...-atrial-fibrillation-notification-ecg.json | 120 +++---- ...-atrial-fibrillation-notification-ppg.json | 120 +++---- ...-withings-withings-nerve-health-score.json | 120 +++---- ...withings-withings-pulse-wave-velocity.json | 120 +++---- ...ovider-withings-withings-vascular-age.json | 120 +++---- ...-withings-withings-visceral-fat-index.json | 120 +++---- ...ecording-google-health-api-heart-rate.json | 126 +++---- .../resources/recording-oura-heartrate.json | 126 +++---- .../recording-withings-activityintraday.json | 126 +++---- .../recording-withings-sleepintraday.json | 126 +++---- packages/fhir/grove-fhir.json | 4 +- packages/fhir/scripts/browser-smoke.mjs | 4 +- .../scripts/generate-conformance-fixtures.mjs | 33 +- .../scripts/generate-measurement-catalog.mjs | 321 ++++++++++-------- .../fhir/scripts/generate-zod-schemas.mjs | 23 +- .../scripts/measurement-catalog-renderer.mjs | 4 +- packages/fhir/scripts/package-smoke.mjs | 107 +++--- packages/fhir/scripts/zod-generator-input.mjs | 4 +- .../contract/measurement-catalog.generated.ts | 321 +++++++++++++++--- .../fhir/src/contract/providers.generated.ts | 13 +- .../src/contract/questionnaire.generated.ts | 2 - packages/fhir/src/zod/r4/schemas.generated.ts | 151 ++++++++ .../fhir/src/zod/r4b/schemas.generated.ts | 146 ++++++++ 71 files changed, 4190 insertions(+), 3568 deletions(-) diff --git a/packages/fhir/README.md b/packages/fhir/README.md index d3812513..e825a20a 100644 --- a/packages/fhir/README.md +++ b/packages/fhir/README.md @@ -15,13 +15,29 @@ SPDX-License-Identifier: MIT The package targets FHIR R4 4.0.1 only. It has no Firebase dependency and does not fetch health data, authenticate with providers, persist resources, or define transport behavior. -The contract version exposed by generated metadata is the Grove FHIR implementation-guide version. -The monorepo keeps its `0.0.0` source placeholders until the lockstep release workflow injects the selected npm release version. +Generated package metadata retains the exact SemVer coordinates required by FHIR package manifests. Runtime compatibility is determined by the generated Grove FHIR contracts, not by an exported implementation-guide release number. +The monorepo keeps its `0.0.0` source placeholders until the lockstep npm release workflow injects the selected package version. + +### Migrating from 0.1.x + +This release replaces the entire hand-written 0.1.x surface. +Nothing is carried forward under its old name, and every published 0.1.x export is gone. + +Import the generated, contract-derived replacements from the entry point that owns them: the root for validated primitives, the bounded R4 closure, and Questionnaire support; `/r4` for the resource schemas and graph parsers; `/mobile` for the shared measurement contract and deterministic identity; `/providers` for the closed connected-provider builders; `/questionnaire` for the instrument pair; and `/zod/r4` or `/zod/r4b` for the generated base-release schemas. +Coverage only grew: every resource type the 0.1.x line validated has a generated schema here, alongside the rest of R4 and R4B. + +The 0.1.x line is superseded and should be deprecated on the registry once this release publishes: + +```sh +npm deprecate '@schmiedmayerlab/grove-fhir@<0.2.0' \ + 'Superseded by the generated Grove FHIR contracts; see the Migrating from 0.1.x section of the README.' +``` ## New to FHIR? FHIR is a standard for exchanging health data as JSON documents called resources. -This package builds four of them: an `Observation` for each measurement, a `Device` for the thing that recorded it, a `Provenance` saying where the record came from, and a `Bundle` holding the set together. +A measurement bundle holds four of them: an `Observation` for each measurement, a `Device` for the thing that recorded it, a `Provenance` saying where the record came from, and a `Bundle` holding the set together. +The package also builds `DocumentReference` graphs for preserved source recordings and `Questionnaire`/`QuestionnaireResponse` pairs. If those words are unfamiliar, read the _New to FHIR_ page of the Grove Mobile implementation guide (`https://grovealliance.org/fhir/mobile`) first. @@ -155,7 +171,7 @@ Four conventions are visible in that call and hold throughout the package: This is why the example parses its literals instead of passing raw strings. - **You supply governed identity inputs; the package derives the graph.** The deployment owns the key, key-epoch-specific Identifier systems, producer instance, monotonic event sequence, account pseudonym, source-native id, and all event times. - Source and output identifiers use the Grove v0 domain-separated HMAC contract; internal `urn:uuid` links use UUIDv5 over the selected complete Identifier pair. + Source and output identifiers use the Grove domain-separated HMAC contract; internal `urn:uuid` links use UUIDv5 over the selected complete Identifier pair. An exact retry reuses the event sequence, `occurred`, `recorded`, `assembled`, and payload. A changed source revision or payload is a new event with a new sequence. - **Results are frozen plain JSON.** Serialize them, store them, send them; there are no classes and no hidden state. @@ -409,6 +425,9 @@ contains one retraction Provenance plus its immutable assembler application and optional host snapshots. It never emits `entered-in-error`, a FHIR DELETE, or a receiver retention instruction. +A target may carry the adapter's own record key alongside its Grove identity through the optional `nativeIdentifier` field. +It is emitted verbatim as a target-carried native record Identifier, must use an absolute system, and never carries a Grove identifier-role coding. + Receivers resolve complete Identifier pairs and apply their own idempotent, atomic lifecycle policy. `parseProviderRetractionInput(unknown)` is the strict JavaScript boundary and rejects unknown fields, unsupported target/type pairs, @@ -432,6 +451,8 @@ import { The builders own the exact Grove profile declarations, Semantic Versioning algorithm extension, and electronic completion-mode extension. Answer values are an exactly-one `value[x]` union in TypeScript and in the runtime schema. +The Grove profiles fix both subjects to `Patient`, so `subjectTypes` is required and is exactly `['Patient']`, and a response requires one `Patient`-typed `subject`. +Both builders and both parsers enforce this, as does the exchange-graph validator for every mandatory governed subject. The pair preflight checks the exact `url|version`, global link identity, response nesting, answer types, inline options, repeats, response text, conditional enablement, and required enabled items for completed or amended @@ -459,6 +480,10 @@ code points to match the IG companion validator. This package does not extract Questionnaire answers into Observations. That is a separate, explicitly configured clinical mapping operation. +One Grove Questionnaire element is deliberately deferred. +`grove-questionnaire-writer-context` is `0..1 MS` on the Grove QuestionnaireResponse profile and states the capturing application and host, but the Grove FHIR catalogs do not publish it, so this package cannot project its canonical or its sub-extension contract without hand-copying the implementation guide. +Until the catalog carries it, a writer context supplied through the builder's `extensions` pass-through reaches consumers with only generic FHIR extension shape checking, and neither the builder nor the parser validates its sub-extensions. + ## Entry points The root entry point contains the source-neutral aggregate: validated @@ -500,11 +525,11 @@ import { Each generated package-metadata value is bounded to that entry point's applicable IG package and includes its exact package id, canonical, dependency -list, FHIR release, and IG contract version. The root exposes only the shared -`groveFhirVersion` and `groveFhirContractVersion`; it does not expose the full -internal adapter package graph. +list, FHIR release, and package SemVer coordinate. The root exposes the shared +`groveFhirVersion` and the Mobile package coordinates; it does not expose the +full internal adapter package graph or a release-number compatibility gate. -There is no generic Sensor producer entry point in this release. The admitted +There is no generic Sensor producer entry point. The admitted raw-recording facade owns exact Sensor + Provider profile claims and provider identity, so presenting it as source-neutral would weaken the closed adapter contract. A future Sensor producer must expose its own evidenced, @@ -661,11 +686,16 @@ Synchronized IG catalogs and the Mobile semantic corpus retain the exact upstrea ### Regenerating ```sh -npm run generate:zod # rewrite the generated schemas for both releases -npm run check:zod # fail if either is stale +npm run generate:zod # rewrite the generated schemas for both releases +npm run check:zod # fail if either is stale +npm run generate:catalog # reproject the Grove contracts a pin bump changed +npm run check:catalog # fail if the projected contracts are stale +npm run generate:fixtures # rebuild the conformance corpus from the public API +npm run check:fixtures # fail if any emitted resource has drifted ``` -`check:zod` runs as part of `typecheck`, so a stale schema fails the build the same way a stale catalog does. +`check:zod` and `check:catalog` run as part of `typecheck`, so a stale schema or +catalog fails the build the same way a stale fixture does. ## Development conformance @@ -697,8 +727,9 @@ corresponding GitHub archive consumed by generation. Fetching verifies that archive before extraction and binds both values in its cache marker; catalog, corpus, generated TypeScript, or emitted-resource drift fails CI. For any future contract update, refresh both the immutable commit and archive -digest, regenerate from a clean checkout, and run unit, package, browser, -structural, and official validator checks. The validator steps use failure-independent conditions so a +digest, rerun every generator in [Regenerating](#regenerating) from a clean +checkout, and run unit, package, browser, structural, and official validator +checks. The validator steps use failure-independent conditions so a browser failure cannot suppress their evidence. ## License diff --git a/packages/fhir/catalog/measurement-capabilities.json b/packages/fhir/catalog/measurement-capabilities.json index 5bf57edb..05524589 100644 --- a/packages/fhir/catalog/measurement-capabilities.json +++ b/packages/fhir/catalog/measurement-capabilities.json @@ -1,6 +1,5 @@ { "schemaVersion": 0, - "igVersion": "0.6.0", "canonicalBase": "https://grovealliance.org/fhir", "recordConversion": { "input": "One connected-provider source record with a non-empty unique subset of the catalog-admitted outputs actually present.", @@ -10,11 +9,11 @@ "fetchBoundary": "Provider clients, authentication, pagination, webhooks, and raw response models remain outside grove-fhir." }, "statuses": { - "implemented": "A closed public builder emits resources conforming to the listed v0.6.0 profile.", + "implemented": "A closed public builder emits resources conforming to the listed profile.", "implemented-recording": "The closed native-recording builder emits the exact Sensor + Provider DocumentReference graph for this catalogued provider source token.", "profiled-not-admitted": "The shared profile exists, but this adapter facade has no supported provider source.", - "adapter-profiled": "Version 0.6.0 defines an adapter-owned or standard-plus-adapter profile claim; it is intentionally not a shared Mobile profile and is outside this Provider facade.", - "planned-shared": "The concept is shared by multiple sources but has no admitted v0.6.0 Grove builder.", + "adapter-profiled": "The relevant Grove FHIR Implementation Guide defines an adapter-owned or standard-plus-adapter profile claim; it is intentionally not a shared Mobile profile and is outside this Provider facade.", + "planned-shared": "The concept is shared by multiple sources but has no admitted Grove builder.", "adapter-specific": "The semantics are source-specific and must not be collapsed into a shared concept.", "sensor-ig": "The concept belongs to the Sensor and Waveform contract.", "not-in-scope": "The source exposes the record, but it is not a mobile measurement admitted by this package.", @@ -58,7 +57,7 @@ }, "effective": "dateTime", "sources": ["healthkit", "health-connect"], - "reason": "None of the Google Health API, Oura, or Withings mappings admits this shared measurement in v0.6.0." + "reason": "None of the Google Health API, Oura, or Withings mappings admits this shared measurement." }, { "key": "basal-energy", @@ -1474,7 +1473,7 @@ }, "effective": "dateTime", "sources": ["healthkit"], - "reason": "FHIR R4 BMI plus the HealthKit adapter profile is the exact v0.6.0 claim; a one-source shared Mobile wrapper is prohibited." + "reason": "FHIR R4 BMI plus the HealthKit adapter profile is the exact claim; a one-source shared Mobile wrapper is prohibited." }, { "key": "blood-glucose", @@ -1797,7 +1796,7 @@ "status": "platform-exclusive", "profile": "withings-extracellular-water-mass", "code": { - "system": "https://grovealliance.org/fhir/providers/CodeSystem/provider-measurement", + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", "code": "extracellular-water-mass", "display": "Extracellular water mass" }, @@ -1814,7 +1813,7 @@ "status": "platform-exclusive", "profile": "withings-intracellular-water-mass", "code": { - "system": "https://grovealliance.org/fhir/providers/CodeSystem/provider-measurement", + "system": "https://grovealliance.org/fhir/withings/CodeSystem/withings-measurement", "code": "intracellular-water-mass", "display": "Intracellular water mass" }, @@ -1843,23 +1842,6 @@ "owner": "withings", "sources": ["withings"] }, - { - "key": "sleeping-heart-rate-average", - "status": "platform-exclusive", - "profile": "withings-sleeping-heart-rate-average", - "code": { - "system": "https://grovealliance.org/fhir/providers/CodeSystem/provider-measurement", - "code": "sleeping-heart-rate-average", - "display": "Sleeping heart rate average" - }, - "unit": { - "code": "/min", - "display": "beats/minute" - }, - "effective": "Period", - "owner": "withings", - "sources": ["withings"] - }, { "key": "corrected-qt-interval", "status": "platform-exclusive", diff --git a/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json b/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json index ec65a47d..de0cd149 100644 --- a/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json +++ b/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json @@ -102,6 +102,23 @@ "severity": "error" } }, + { + "id": "misnumbered-entry-node-ordinal", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/0/extension/0/valueIdentifier/value", + "value": "n0:patient:1:qN9xaTdoz-LfvONC7FgEUNMz06zyWhfzD0khPhtjVig" + } + ], + "expectedRule": { + "code": "mobile-exchange.entry-node-ordinal", + "reason": "An entry-node ordinal is the zero-based position of its entry among the entries sharing that node-role, in Bundle entry order.", + "location": "Bundle.entry[0].extension.valueIdentifier.value", + "severity": "error" + } + }, { "id": "missing-source-output-identity", "base": "mobile-exchange", @@ -655,6 +672,30 @@ "location": "Bundle.entry", "severity": "error" } + }, + { + "id": "retraction-native-identifier-uses-grove-role", + "base": "mobile-retraction", + "patch": [ + { + "op": "add", + "path": "/entry/0/resource/target/0/extension/1/valueIdentifier/type", + "value": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + } + } + ], + "expectedRule": { + "code": "mobile-retraction.native-record-identifier", + "reason": "An optional retraction native record identifier carries one complete Identifier in the adapter's own absolute native key space and never a Grove identifier-role coding.", + "location": "Provenance.target[0].extension.valueIdentifier.type", + "severity": "error" + } } ] } diff --git a/packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json b/packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json index a22b0179..138fdc94 100644 --- a/packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json +++ b/packages/fhir/fixtures/conformance/mobile-exchange/retraction-bundle.json @@ -113,6 +113,13 @@ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-role", "valueCode": "primary-output" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-native-identifier", + "valueIdentifier": { + "system": "https://study.example.org/fhir/NamingSystem/native-record/health-connect/1f5c58aa-6ec6-4e79-a682-829a9debd3f5", + "value": "record-heart-001" + } } ], "type": "Observation", diff --git a/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json b/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json index 371d7b2b..e8adbac2 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:1" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:LycACgbJZx-uRos8zb-0w0PEXMdM7LoW4Sx1IvbEOC0" - } - } - ], "fullUrl": "urn:uuid:fff33fbe-2314-590f-b6b1-26c530f6c4ec", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:cBsbfJ2FTYUguk5OcwFuDMb03zuen7zrG5xlFAniFTo", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:cBsbfJ2FTYUguk5OcwFuDMb03zuen7zrG5xlFAniFTo" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:LycACgbJZx-uRos8zb-0w0PEXMdM7LoW4Sx1IvbEOC0", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:LycACgbJZx-uRos8zb-0w0PEXMdM7LoW4Sx1IvbEOC0" } ], "status": "final", @@ -116,9 +92,7 @@ "system": "http://unitsofmeasure.org", "code": "kcal" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -127,15 +101,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:NDTrpLTq9kiSi5FYG4ZY37w64LhMOM9ADTH9-v65Ydg" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:LycACgbJZx-uRos8zb-0w0PEXMdM7LoW4Sx1IvbEOC0" } } - ], + ] + }, + { "fullUrl": "urn:uuid:43fca3bd-f88d-5c9f-aee2-c1823335ee58", "resource": { "resourceType": "Device", @@ -146,8 +122,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:NDTrpLTq9kiSi5FYG4ZY37w64LhMOM9ADTH9-v65Ydg", "type": { "coding": [ { @@ -155,7 +129,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:NDTrpLTq9kiSi5FYG4ZY37w64LhMOM9ADTH9-v65Ydg" } ], "status": "active", @@ -165,9 +141,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -181,10 +155,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:U1GXWdV08ZaO2_cubyu1oaGFDXhnzLx8h_JwZVf6hUU" + "value": "v0:fixture-key:1:NDTrpLTq9kiSi5FYG4ZY37w64LhMOM9ADTH9-v65Ydg" } } - ], + ] + }, + { "fullUrl": "urn:uuid:536d5716-4fb1-5959-baf9-3eac3a86d493", "resource": { "resourceType": "Device", @@ -195,8 +171,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:U1GXWdV08ZaO2_cubyu1oaGFDXhnzLx8h_JwZVf6hUU", "type": { "coding": [ { @@ -204,7 +178,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:U1GXWdV08ZaO2_cubyu1oaGFDXhnzLx8h_JwZVf6hUU" } ], "status": "active", @@ -228,9 +204,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -239,15 +213,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:JSuiGqhmUZK1zgym0F6SZEhogZ906ZSkQhTZqK76W5s" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:U1GXWdV08ZaO2_cubyu1oaGFDXhnzLx8h_JwZVf6hUU" } } - ], + ] + }, + { "fullUrl": "urn:uuid:f6732476-2d2b-5b8f-baf8-af76c3e56564", "resource": { "resourceType": "Provenance", @@ -293,8 +269,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:cBsbfJ2FTYUguk5OcwFuDMb03zuen7zrG5xlFAniFTo", "type": { "coding": [ { @@ -302,7 +276,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:cBsbfJ2FTYUguk5OcwFuDMb03zuen7zrG5xlFAniFTo" } }, "agent": [ @@ -324,7 +300,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:JSuiGqhmUZK1zgym0F6SZEhogZ906ZSkQhTZqK76W5s" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json b/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json index d526f2fd..ef0be341 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:2" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:bGqlV4JlMEN1EGtpQcfgYP75jVLT5SCtsBfvwHIq8Yw" - } - } - ], "fullUrl": "urn:uuid:5a6a734e-af3b-51a9-85c0-7823a9e40c9b", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:J_lqguXT9TuoBwwFhLXLFidICzFsaqScnK0TqMeAB-I", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:J_lqguXT9TuoBwwFhLXLFidICzFsaqScnK0TqMeAB-I" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:bGqlV4JlMEN1EGtpQcfgYP75jVLT5SCtsBfvwHIq8Yw", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:bGqlV4JlMEN1EGtpQcfgYP75jVLT5SCtsBfvwHIq8Yw" } ], "status": "final", @@ -116,9 +92,7 @@ "system": "http://unitsofmeasure.org", "code": "kcal" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -127,15 +101,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:W1F_GPOEQhXLa4op2E407dR0glREa1hcySXn8NQqIcc" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:bGqlV4JlMEN1EGtpQcfgYP75jVLT5SCtsBfvwHIq8Yw" } } - ], + ] + }, + { "fullUrl": "urn:uuid:e39f62eb-c867-532e-a955-db7eb7354ad9", "resource": { "resourceType": "Device", @@ -146,8 +122,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:W1F_GPOEQhXLa4op2E407dR0glREa1hcySXn8NQqIcc", "type": { "coding": [ { @@ -155,7 +129,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:W1F_GPOEQhXLa4op2E407dR0glREa1hcySXn8NQqIcc" } ], "status": "active", @@ -165,9 +141,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -181,10 +155,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:u5TL9xXam0QJkWk5kHlvukPvutOg61R5brwRjRupWoY" + "value": "v0:fixture-key:1:W1F_GPOEQhXLa4op2E407dR0glREa1hcySXn8NQqIcc" } } - ], + ] + }, + { "fullUrl": "urn:uuid:ca0593ae-cc72-5870-93e6-799df283ea91", "resource": { "resourceType": "Device", @@ -195,8 +171,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:u5TL9xXam0QJkWk5kHlvukPvutOg61R5brwRjRupWoY", "type": { "coding": [ { @@ -204,7 +178,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:u5TL9xXam0QJkWk5kHlvukPvutOg61R5brwRjRupWoY" } ], "status": "active", @@ -228,9 +204,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -239,15 +213,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:bPr9qu85WDJRHQ_RIPRZRurly-o77lEQL6RYw1bsNog" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:u5TL9xXam0QJkWk5kHlvukPvutOg61R5brwRjRupWoY" } } - ], + ] + }, + { "fullUrl": "urn:uuid:02d8d13a-725f-5a98-8adb-7bdbe1d2c8ac", "resource": { "resourceType": "Provenance", @@ -293,8 +269,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:J_lqguXT9TuoBwwFhLXLFidICzFsaqScnK0TqMeAB-I", "type": { "coding": [ { @@ -302,7 +276,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:J_lqguXT9TuoBwwFhLXLFidICzFsaqScnK0TqMeAB-I" } }, "agent": [ @@ -324,7 +300,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:bPr9qu85WDJRHQ_RIPRZRurly-o77lEQL6RYw1bsNog" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json b/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json index e88508d5..54833ff7 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:3" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:sIHAUnrc78LCCIylboGtuD4FkMHZ5IcR9_DscGIbDZA" - } - } - ], "fullUrl": "urn:uuid:26835628-7045-5579-8509-ced6196c4250", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:27EvW0bT37TNTuvwrW8l4xOSfYIZ4S1ve49v2O8NLmc", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:27EvW0bT37TNTuvwrW8l4xOSfYIZ4S1ve49v2O8NLmc" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:sIHAUnrc78LCCIylboGtuD4FkMHZ5IcR9_DscGIbDZA", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:sIHAUnrc78LCCIylboGtuD4FkMHZ5IcR9_DscGIbDZA" } ], "status": "final", @@ -108,9 +84,7 @@ "system": "http://unitsofmeasure.org", "code": "mg/dL" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -119,15 +93,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:xoJ9_x4-WlVdj2qnI_cuEu_h-kqCU_k3oJypbqVKAu0" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:sIHAUnrc78LCCIylboGtuD4FkMHZ5IcR9_DscGIbDZA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:75eef62c-12cb-597b-b50b-e7bfc7492aa9", "resource": { "resourceType": "Device", @@ -138,8 +114,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:xoJ9_x4-WlVdj2qnI_cuEu_h-kqCU_k3oJypbqVKAu0", "type": { "coding": [ { @@ -147,7 +121,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:xoJ9_x4-WlVdj2qnI_cuEu_h-kqCU_k3oJypbqVKAu0" } ], "status": "active", @@ -157,9 +133,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -173,10 +147,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uZjSlR8HhG3r4orDE1tamL-7ADdF63SWpfVP3gi9PFk" + "value": "v0:fixture-key:1:xoJ9_x4-WlVdj2qnI_cuEu_h-kqCU_k3oJypbqVKAu0" } } - ], + ] + }, + { "fullUrl": "urn:uuid:23ee24a9-6a50-50db-aa09-b10818b5c5d9", "resource": { "resourceType": "Device", @@ -187,8 +163,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uZjSlR8HhG3r4orDE1tamL-7ADdF63SWpfVP3gi9PFk", "type": { "coding": [ { @@ -196,7 +170,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:uZjSlR8HhG3r4orDE1tamL-7ADdF63SWpfVP3gi9PFk" } ], "status": "active", @@ -220,9 +196,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -231,15 +205,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:sHgw9yxq8eUbRbaTd3vKq88xsxm3beKvqRPkO5x4og8" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:uZjSlR8HhG3r4orDE1tamL-7ADdF63SWpfVP3gi9PFk" } } - ], + ] + }, + { "fullUrl": "urn:uuid:68e61fb6-a6d4-5a6e-9b53-e4805ae0cad0", "resource": { "resourceType": "Provenance", @@ -285,8 +261,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:27EvW0bT37TNTuvwrW8l4xOSfYIZ4S1ve49v2O8NLmc", "type": { "coding": [ { @@ -294,7 +268,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:27EvW0bT37TNTuvwrW8l4xOSfYIZ4S1ve49v2O8NLmc" } }, "agent": [ @@ -316,7 +292,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:sHgw9yxq8eUbRbaTd3vKq88xsxm3beKvqRPkO5x4og8" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json b/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json index 8045ab66..e012eeac 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:4" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:h_61-KWtwbgmorpjpV-hiBahMW9-fvRxBFWS04MHpZE" - } - } - ], "fullUrl": "urn:uuid:d0c0a2a4-8c81-5907-96ad-834c557418b0", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:WuyZHg4lYDvE4FhjSBqArtkRJOGxBtiM7vDOOT9R98o", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:WuyZHg4lYDvE4FhjSBqArtkRJOGxBtiM7vDOOT9R98o" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:h_61-KWtwbgmorpjpV-hiBahMW9-fvRxBFWS04MHpZE", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:h_61-KWtwbgmorpjpV-hiBahMW9-fvRxBFWS04MHpZE" } ], "status": "final", @@ -138,9 +114,7 @@ } } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -149,15 +123,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:7EV1uB106bpRYwJR9HZKAN1rb0oq6FZj9UsFj44FATU" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:h_61-KWtwbgmorpjpV-hiBahMW9-fvRxBFWS04MHpZE" } } - ], + ] + }, + { "fullUrl": "urn:uuid:5458fdad-053a-55ae-a659-ef8cba4cc18f", "resource": { "resourceType": "Device", @@ -168,8 +144,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:7EV1uB106bpRYwJR9HZKAN1rb0oq6FZj9UsFj44FATU", "type": { "coding": [ { @@ -177,7 +151,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:7EV1uB106bpRYwJR9HZKAN1rb0oq6FZj9UsFj44FATU" } ], "status": "active", @@ -187,9 +163,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -203,10 +177,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:StGlLYOvF2jMhvbe3CuauF8rp7vuRc6brEJfxxgE0Kg" + "value": "v0:fixture-key:1:7EV1uB106bpRYwJR9HZKAN1rb0oq6FZj9UsFj44FATU" } } - ], + ] + }, + { "fullUrl": "urn:uuid:6a8866f4-f3eb-5f62-b099-17c43cbe4d40", "resource": { "resourceType": "Device", @@ -217,8 +193,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:StGlLYOvF2jMhvbe3CuauF8rp7vuRc6brEJfxxgE0Kg", "type": { "coding": [ { @@ -226,7 +200,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:StGlLYOvF2jMhvbe3CuauF8rp7vuRc6brEJfxxgE0Kg" } ], "status": "active", @@ -250,9 +226,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -261,15 +235,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:HSbFZAF1J0Z2AiQllTCYg47pEddf3_bqHBCtM_kKHTc" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:StGlLYOvF2jMhvbe3CuauF8rp7vuRc6brEJfxxgE0Kg" } } - ], + ] + }, + { "fullUrl": "urn:uuid:e643c8be-4880-58e2-a645-64479de7bd05", "resource": { "resourceType": "Provenance", @@ -315,8 +291,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:WuyZHg4lYDvE4FhjSBqArtkRJOGxBtiM7vDOOT9R98o", "type": { "coding": [ { @@ -324,7 +298,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:WuyZHg4lYDvE4FhjSBqArtkRJOGxBtiM7vDOOT9R98o" } }, "agent": [ @@ -346,7 +322,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:HSbFZAF1J0Z2AiQllTCYg47pEddf3_bqHBCtM_kKHTc" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json b/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json index 0a14f389..41004b73 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:5" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:CKv-2R1W4otUFhAE3dZ_GJwX4LjRSCYU12bc8TFxhu0" - } - } - ], "fullUrl": "urn:uuid:2d4505ad-24d1-53b0-b8bc-ba6c0021bd20", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:WxoGom7ZNS31zsgBLsLtuMnFQ-Zb2PV_UbLkLzxlVwE", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:WxoGom7ZNS31zsgBLsLtuMnFQ-Zb2PV_UbLkLzxlVwE" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:CKv-2R1W4otUFhAE3dZ_GJwX4LjRSCYU12bc8TFxhu0", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:CKv-2R1W4otUFhAE3dZ_GJwX4LjRSCYU12bc8TFxhu0" } ], "status": "final", @@ -108,9 +84,7 @@ "system": "http://unitsofmeasure.org", "code": "%" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -119,15 +93,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:jFa4dFMQh0ReZZ1PAqwe-uI9UcTq5j2IlwWALGF5Vs8" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:CKv-2R1W4otUFhAE3dZ_GJwX4LjRSCYU12bc8TFxhu0" } } - ], + ] + }, + { "fullUrl": "urn:uuid:47afd496-5885-5729-816d-fcbce4f015bb", "resource": { "resourceType": "Device", @@ -138,8 +114,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:jFa4dFMQh0ReZZ1PAqwe-uI9UcTq5j2IlwWALGF5Vs8", "type": { "coding": [ { @@ -147,7 +121,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:jFa4dFMQh0ReZZ1PAqwe-uI9UcTq5j2IlwWALGF5Vs8" } ], "status": "active", @@ -157,9 +133,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -173,10 +147,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:dMnfWEQk2xChFyh1BU6bUz9-invN_CJx4a7K5VJwnoA" + "value": "v0:fixture-key:1:jFa4dFMQh0ReZZ1PAqwe-uI9UcTq5j2IlwWALGF5Vs8" } } - ], + ] + }, + { "fullUrl": "urn:uuid:7dca5c46-8cbe-5996-a7dc-e1d38cc12bf4", "resource": { "resourceType": "Device", @@ -187,8 +163,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:dMnfWEQk2xChFyh1BU6bUz9-invN_CJx4a7K5VJwnoA", "type": { "coding": [ { @@ -196,7 +170,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:dMnfWEQk2xChFyh1BU6bUz9-invN_CJx4a7K5VJwnoA" } ], "status": "active", @@ -220,9 +196,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -231,15 +205,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:JXf4d0INS6AhdsWU91DRhrSqJ2KDrn6Owd6DutdAz7s" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:dMnfWEQk2xChFyh1BU6bUz9-invN_CJx4a7K5VJwnoA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:8488a61c-011b-5ba8-8cf6-0efe011d95b5", "resource": { "resourceType": "Provenance", @@ -285,8 +261,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:WxoGom7ZNS31zsgBLsLtuMnFQ-Zb2PV_UbLkLzxlVwE", "type": { "coding": [ { @@ -294,7 +268,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:WxoGom7ZNS31zsgBLsLtuMnFQ-Zb2PV_UbLkLzxlVwE" } }, "agent": [ @@ -316,7 +292,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:JXf4d0INS6AhdsWU91DRhrSqJ2KDrn6Owd6DutdAz7s" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-height.json b/packages/fhir/fixtures/conformance/resources/mobile-body-height.json index 727107cc..12b95695 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-height.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-height.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:6" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:lS0QSYana6P-mPdj0GPjtR0-h3xqSULeep0f1ocR0Q4" - } - } - ], "fullUrl": "urn:uuid:9e59de7e-0518-5df2-ac5e-54685bd09a8a", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:i73_ClycuTs0StcJTYSAMPNYRgSA_kzfiagjX50PQMQ", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:i73_ClycuTs0StcJTYSAMPNYRgSA_kzfiagjX50PQMQ" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:lS0QSYana6P-mPdj0GPjtR0-h3xqSULeep0f1ocR0Q4", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:lS0QSYana6P-mPdj0GPjtR0-h3xqSULeep0f1ocR0Q4" } ], "status": "final", @@ -120,9 +96,7 @@ "system": "http://unitsofmeasure.org", "code": "cm" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -131,15 +105,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:nkrVMmK5Z25j6D3O4YFbpTz9gcI7nYUYvMMhJgWqk2I" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:lS0QSYana6P-mPdj0GPjtR0-h3xqSULeep0f1ocR0Q4" } } - ], + ] + }, + { "fullUrl": "urn:uuid:c7fdc3f1-5bba-524a-93df-19600a63219a", "resource": { "resourceType": "Device", @@ -150,8 +126,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:nkrVMmK5Z25j6D3O4YFbpTz9gcI7nYUYvMMhJgWqk2I", "type": { "coding": [ { @@ -159,7 +133,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:nkrVMmK5Z25j6D3O4YFbpTz9gcI7nYUYvMMhJgWqk2I" } ], "status": "active", @@ -169,9 +145,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -185,10 +159,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:JE_Aqlhn9MnxX9wx4t8kJfLES5YcXywYLTzTB97A-Ec" + "value": "v0:fixture-key:1:nkrVMmK5Z25j6D3O4YFbpTz9gcI7nYUYvMMhJgWqk2I" } } - ], + ] + }, + { "fullUrl": "urn:uuid:559b0b4e-5460-5ab7-9a53-c3267ecaa7af", "resource": { "resourceType": "Device", @@ -199,8 +175,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:JE_Aqlhn9MnxX9wx4t8kJfLES5YcXywYLTzTB97A-Ec", "type": { "coding": [ { @@ -208,7 +182,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:JE_Aqlhn9MnxX9wx4t8kJfLES5YcXywYLTzTB97A-Ec" } ], "status": "active", @@ -232,9 +208,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -243,15 +217,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:xznW5Dtyrp_tkOdtvAdJ4XrJLURFAb463IsGE_0Fz6o" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:JE_Aqlhn9MnxX9wx4t8kJfLES5YcXywYLTzTB97A-Ec" } } - ], + ] + }, + { "fullUrl": "urn:uuid:f9680bea-996f-5f05-aefa-758aaece4bf1", "resource": { "resourceType": "Provenance", @@ -297,8 +273,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:i73_ClycuTs0StcJTYSAMPNYRgSA_kzfiagjX50PQMQ", "type": { "coding": [ { @@ -306,7 +280,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:i73_ClycuTs0StcJTYSAMPNYRgSA_kzfiagjX50PQMQ" } }, "agent": [ @@ -328,7 +304,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:xznW5Dtyrp_tkOdtvAdJ4XrJLURFAb463IsGE_0Fz6o" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json b/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json index 5a7fc61c..5a0f53d5 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:7" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:0xwJuyM9Oj7kD9ci-wffqZeinUCEg_Aisu4THXZqzu0" - } - } - ], "fullUrl": "urn:uuid:991342d3-ad32-5672-a092-cea731aa446d", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:kglrsTbI-sisN9jpTRtdE0mf3W_iLOyNBWx6GQ54xQg", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:kglrsTbI-sisN9jpTRtdE0mf3W_iLOyNBWx6GQ54xQg" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:0xwJuyM9Oj7kD9ci-wffqZeinUCEg_Aisu4THXZqzu0", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:0xwJuyM9Oj7kD9ci-wffqZeinUCEg_Aisu4THXZqzu0" } ], "status": "final", @@ -120,9 +96,7 @@ "system": "http://unitsofmeasure.org", "code": "Cel" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -131,15 +105,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:H2r0u1o9Dno6Uy4_UDnq2S31gXiT9aTLQ6W9BR3RA9E" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:0xwJuyM9Oj7kD9ci-wffqZeinUCEg_Aisu4THXZqzu0" } } - ], + ] + }, + { "fullUrl": "urn:uuid:def97c74-f47d-54ab-a249-d81dd508ea0c", "resource": { "resourceType": "Device", @@ -150,8 +126,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:H2r0u1o9Dno6Uy4_UDnq2S31gXiT9aTLQ6W9BR3RA9E", "type": { "coding": [ { @@ -159,7 +133,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:H2r0u1o9Dno6Uy4_UDnq2S31gXiT9aTLQ6W9BR3RA9E" } ], "status": "active", @@ -169,9 +145,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -185,10 +159,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:FtVDXLqRlsdnWhRUEUCd9tzd5DUdLpPXw0X7QM0rBhI" + "value": "v0:fixture-key:1:H2r0u1o9Dno6Uy4_UDnq2S31gXiT9aTLQ6W9BR3RA9E" } } - ], + ] + }, + { "fullUrl": "urn:uuid:b4924f51-3fc2-590f-9659-21e430b4df06", "resource": { "resourceType": "Device", @@ -199,8 +175,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:FtVDXLqRlsdnWhRUEUCd9tzd5DUdLpPXw0X7QM0rBhI", "type": { "coding": [ { @@ -208,7 +182,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:FtVDXLqRlsdnWhRUEUCd9tzd5DUdLpPXw0X7QM0rBhI" } ], "status": "active", @@ -232,9 +208,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -243,15 +217,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:-rZfHFRZQvg1O-TQfGbqlkvRpL2apKTfsgrv3ezhgmU" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:FtVDXLqRlsdnWhRUEUCd9tzd5DUdLpPXw0X7QM0rBhI" } } - ], + ] + }, + { "fullUrl": "urn:uuid:076db598-eef8-518e-9b1c-d5cbdb574d39", "resource": { "resourceType": "Provenance", @@ -297,8 +273,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:kglrsTbI-sisN9jpTRtdE0mf3W_iLOyNBWx6GQ54xQg", "type": { "coding": [ { @@ -306,7 +280,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:kglrsTbI-sisN9jpTRtdE0mf3W_iLOyNBWx6GQ54xQg" } }, "agent": [ @@ -328,7 +304,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:-rZfHFRZQvg1O-TQfGbqlkvRpL2apKTfsgrv3ezhgmU" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json b/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json index c1e79035..7712d01d 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:8" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:KcKSTEKCMUmSLMXdnqW-kAJHPCGqCtRhmZ5hP5-F58k" - } - } - ], "fullUrl": "urn:uuid:011209c1-efd2-5615-b48d-7267fb8f587d", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:8BGOdrODZOHaIi5_vcptrhCvIp9pXLGBex92mcxbFWc", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:8BGOdrODZOHaIi5_vcptrhCvIp9pXLGBex92mcxbFWc" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:KcKSTEKCMUmSLMXdnqW-kAJHPCGqCtRhmZ5hP5-F58k", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:KcKSTEKCMUmSLMXdnqW-kAJHPCGqCtRhmZ5hP5-F58k" } ], "status": "final", @@ -108,9 +84,7 @@ "system": "http://unitsofmeasure.org", "code": "kg" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -119,15 +93,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:KBU_fiP09JGvryRvEZWtIf1i90yfqC_o6ewWCsBvTSc" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:KcKSTEKCMUmSLMXdnqW-kAJHPCGqCtRhmZ5hP5-F58k" } } - ], + ] + }, + { "fullUrl": "urn:uuid:a2b1a3b7-2aa7-539c-a900-f1e0a8345a13", "resource": { "resourceType": "Device", @@ -138,8 +114,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:KBU_fiP09JGvryRvEZWtIf1i90yfqC_o6ewWCsBvTSc", "type": { "coding": [ { @@ -147,7 +121,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:KBU_fiP09JGvryRvEZWtIf1i90yfqC_o6ewWCsBvTSc" } ], "status": "active", @@ -157,9 +133,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -173,10 +147,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:kwYq49ze78zEF1E8fXhHPgjMxt1ovZafXb6h2T0x-yk" + "value": "v0:fixture-key:1:KBU_fiP09JGvryRvEZWtIf1i90yfqC_o6ewWCsBvTSc" } } - ], + ] + }, + { "fullUrl": "urn:uuid:c0960bc7-f32d-5bc3-a7a4-09bb09504534", "resource": { "resourceType": "Device", @@ -187,8 +163,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:kwYq49ze78zEF1E8fXhHPgjMxt1ovZafXb6h2T0x-yk", "type": { "coding": [ { @@ -196,7 +170,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:kwYq49ze78zEF1E8fXhHPgjMxt1ovZafXb6h2T0x-yk" } ], "status": "active", @@ -220,9 +196,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -231,15 +205,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:ImvOlb_AJ5BnIOM2MfGr9duCtXlQo6JDZBERq3S1vD8" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:kwYq49ze78zEF1E8fXhHPgjMxt1ovZafXb6h2T0x-yk" } } - ], + ] + }, + { "fullUrl": "urn:uuid:b82881c7-a010-519e-af56-703c8f328f5e", "resource": { "resourceType": "Provenance", @@ -285,8 +261,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:8BGOdrODZOHaIi5_vcptrhCvIp9pXLGBex92mcxbFWc", "type": { "coding": [ { @@ -294,7 +268,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:8BGOdrODZOHaIi5_vcptrhCvIp9pXLGBex92mcxbFWc" } }, "agent": [ @@ -316,7 +292,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:ImvOlb_AJ5BnIOM2MfGr9duCtXlQo6JDZBERq3S1vD8" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json b/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json index bba28080..a9bc864c 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:9" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:ikY1sCJq7ZCjIGDZ-WA0UvDbDbFi8qfkM_PYHXagJbQ" - } - } - ], "fullUrl": "urn:uuid:21266e9b-0e9c-5b9f-b625-37d12c3fd1b7", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:IBIE7eiXHQUPEZS7lzzC8j4DhYMU186jXgbefxDYR40", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:IBIE7eiXHQUPEZS7lzzC8j4DhYMU186jXgbefxDYR40" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:ikY1sCJq7ZCjIGDZ-WA0UvDbDbFi8qfkM_PYHXagJbQ", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:ikY1sCJq7ZCjIGDZ-WA0UvDbDbFi8qfkM_PYHXagJbQ" } ], "status": "final", @@ -120,9 +96,7 @@ "system": "http://unitsofmeasure.org", "code": "kg" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -131,15 +105,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:4fwTGS-w5KNNRuvXElRWQ0k1wmPyVsywbcoR7vuHvUo" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:ikY1sCJq7ZCjIGDZ-WA0UvDbDbFi8qfkM_PYHXagJbQ" } } - ], + ] + }, + { "fullUrl": "urn:uuid:12813d7d-be61-5da2-af9e-5fba50ebe4ba", "resource": { "resourceType": "Device", @@ -150,8 +126,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:4fwTGS-w5KNNRuvXElRWQ0k1wmPyVsywbcoR7vuHvUo", "type": { "coding": [ { @@ -159,7 +133,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:4fwTGS-w5KNNRuvXElRWQ0k1wmPyVsywbcoR7vuHvUo" } ], "status": "active", @@ -169,9 +145,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -185,10 +159,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Kt2LgM4ys7hckDgWMZuwshW4x_uSmcPjB8XB5Howui0" + "value": "v0:fixture-key:1:4fwTGS-w5KNNRuvXElRWQ0k1wmPyVsywbcoR7vuHvUo" } } - ], + ] + }, + { "fullUrl": "urn:uuid:d1f4eef8-89b3-5952-866a-998a0a0e5bad", "resource": { "resourceType": "Device", @@ -199,8 +175,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Kt2LgM4ys7hckDgWMZuwshW4x_uSmcPjB8XB5Howui0", "type": { "coding": [ { @@ -208,7 +182,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:Kt2LgM4ys7hckDgWMZuwshW4x_uSmcPjB8XB5Howui0" } ], "status": "active", @@ -232,9 +208,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -243,15 +217,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:9Ik9SMiyAYvhRBM566jFJU2Z99kwPJO9hZ7gCOYA-_k" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:Kt2LgM4ys7hckDgWMZuwshW4x_uSmcPjB8XB5Howui0" } } - ], + ] + }, + { "fullUrl": "urn:uuid:a2c7aae9-1014-5b67-bc48-26b7a4cb65a0", "resource": { "resourceType": "Provenance", @@ -297,8 +273,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:IBIE7eiXHQUPEZS7lzzC8j4DhYMU186jXgbefxDYR40", "type": { "coding": [ { @@ -306,7 +280,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:IBIE7eiXHQUPEZS7lzzC8j4DhYMU186jXgbefxDYR40" } }, "agent": [ @@ -328,7 +304,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:9Ik9SMiyAYvhRBM566jFJU2Z99kwPJO9hZ7gCOYA-_k" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json b/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json index a4e6bc71..54cd618e 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:10" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:aBvPRliVxlOZKqhr6Q3Jyj9MM77CD8lEczzB0vw4PtA" - } - } - ], "fullUrl": "urn:uuid:6b5ea6d7-2c92-5510-ba1f-f995de70bd27", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:nviR_nQRpK91BIeM0wRmRPO1IKJpSZAqx980Bj4ZE50", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:nviR_nQRpK91BIeM0wRmRPO1IKJpSZAqx980Bj4ZE50" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:aBvPRliVxlOZKqhr6Q3Jyj9MM77CD8lEczzB0vw4PtA", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:aBvPRliVxlOZKqhr6Q3Jyj9MM77CD8lEczzB0vw4PtA" } ], "status": "final", @@ -108,9 +84,7 @@ "system": "http://unitsofmeasure.org", "code": "kg" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -119,15 +93,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:9-IltYX5VPvJ6JIKK6WfmZ-NRa4b5lONWt0QPWIAm48" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:aBvPRliVxlOZKqhr6Q3Jyj9MM77CD8lEczzB0vw4PtA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:4609626d-627d-54bb-8e80-a0694b386d9c", "resource": { "resourceType": "Device", @@ -138,8 +114,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:9-IltYX5VPvJ6JIKK6WfmZ-NRa4b5lONWt0QPWIAm48", "type": { "coding": [ { @@ -147,7 +121,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:9-IltYX5VPvJ6JIKK6WfmZ-NRa4b5lONWt0QPWIAm48" } ], "status": "active", @@ -157,9 +133,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -173,10 +147,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:HyentO5mz54bN1oU8dwpNpGIwjGaJlkj7Ujg1HvVy3I" + "value": "v0:fixture-key:1:9-IltYX5VPvJ6JIKK6WfmZ-NRa4b5lONWt0QPWIAm48" } } - ], + ] + }, + { "fullUrl": "urn:uuid:af05eabe-bdae-5fc1-8515-8632dc115687", "resource": { "resourceType": "Device", @@ -187,8 +163,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:HyentO5mz54bN1oU8dwpNpGIwjGaJlkj7Ujg1HvVy3I", "type": { "coding": [ { @@ -196,7 +170,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:HyentO5mz54bN1oU8dwpNpGIwjGaJlkj7Ujg1HvVy3I" } ], "status": "active", @@ -220,9 +196,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -231,15 +205,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:SSey1ttqPqMR4AflrsB5eKNcr-DhZ0z9Sdkg_Z5gvE0" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:HyentO5mz54bN1oU8dwpNpGIwjGaJlkj7Ujg1HvVy3I" } } - ], + ] + }, + { "fullUrl": "urn:uuid:55a5abc3-e3e8-5469-892c-8d7c91c09f05", "resource": { "resourceType": "Provenance", @@ -285,8 +261,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:nviR_nQRpK91BIeM0wRmRPO1IKJpSZAqx980Bj4ZE50", "type": { "coding": [ { @@ -294,7 +268,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:nviR_nQRpK91BIeM0wRmRPO1IKJpSZAqx980Bj4ZE50" } }, "agent": [ @@ -316,7 +292,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:SSey1ttqPqMR4AflrsB5eKNcr-DhZ0z9Sdkg_Z5gvE0" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json index 2e444e69..09736721 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:11" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:Xnf3G1rIoAI-q4QVXBBvPkehNiAdCYRR4mbLvZkF0eE" - } - } - ], "fullUrl": "urn:uuid:8f50510c-d9e1-55e0-968f-d468f650a2bf", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:TCynCJxssAt4DKR6-a8p8A25iLlU9LB_s1EPaePLqKU", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:TCynCJxssAt4DKR6-a8p8A25iLlU9LB_s1EPaePLqKU" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:Xnf3G1rIoAI-q4QVXBBvPkehNiAdCYRR4mbLvZkF0eE", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:Xnf3G1rIoAI-q4QVXBBvPkehNiAdCYRR4mbLvZkF0eE" } ], "status": "final", @@ -121,9 +97,7 @@ ], "text": "Session total" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -132,15 +106,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:zjMgtZF1_WNxGQz_vUBTVxdogh6EcWgF0fnJ1y7dVow" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:Xnf3G1rIoAI-q4QVXBBvPkehNiAdCYRR4mbLvZkF0eE" } } - ], + ] + }, + { "fullUrl": "urn:uuid:618ecd91-ad82-57b9-9c0f-b6ea3b69a76b", "resource": { "resourceType": "Device", @@ -151,8 +127,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:zjMgtZF1_WNxGQz_vUBTVxdogh6EcWgF0fnJ1y7dVow", "type": { "coding": [ { @@ -160,7 +134,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:zjMgtZF1_WNxGQz_vUBTVxdogh6EcWgF0fnJ1y7dVow" } ], "status": "active", @@ -170,9 +146,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -186,10 +160,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:B2dz6Hre6_NHZxYSJHMNsG-uDpdEhDapwMVcz1SR_BA" + "value": "v0:fixture-key:1:zjMgtZF1_WNxGQz_vUBTVxdogh6EcWgF0fnJ1y7dVow" } } - ], + ] + }, + { "fullUrl": "urn:uuid:64bfef0a-3d97-58cd-956e-8baa7c1f0217", "resource": { "resourceType": "Device", @@ -200,8 +176,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:B2dz6Hre6_NHZxYSJHMNsG-uDpdEhDapwMVcz1SR_BA", "type": { "coding": [ { @@ -209,7 +183,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:B2dz6Hre6_NHZxYSJHMNsG-uDpdEhDapwMVcz1SR_BA" } ], "status": "active", @@ -233,9 +209,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -244,15 +218,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:FfiBKfjUXMHGR5-SZO6c_SLhoLnifAG1ibSV9sW1K0U" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:B2dz6Hre6_NHZxYSJHMNsG-uDpdEhDapwMVcz1SR_BA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:72d90e29-c7e7-545c-90bd-c42199825277", "resource": { "resourceType": "Provenance", @@ -298,8 +274,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:TCynCJxssAt4DKR6-a8p8A25iLlU9LB_s1EPaePLqKU", "type": { "coding": [ { @@ -307,7 +281,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:TCynCJxssAt4DKR6-a8p8A25iLlU9LB_s1EPaePLqKU" } }, "agent": [ @@ -329,7 +305,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:FfiBKfjUXMHGR5-SZO6c_SLhoLnifAG1ibSV9sW1K0U" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-distance.json b/packages/fhir/fixtures/conformance/resources/mobile-distance.json index 3bca00ea..1ff0ce73 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-distance.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-distance.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:12" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:fHVg4ZAoSyzRoC4co_eCnbyEkz3bGc-MjiZDIfpCjRg" - } - } - ], "fullUrl": "urn:uuid:a316b980-80ef-574b-8c92-f9ea4098c4cf", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:TXoHUQohPJ8ryT_B_A4JXlEeOz9zuZOS0c4OxCDHycY", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:TXoHUQohPJ8ryT_B_A4JXlEeOz9zuZOS0c4OxCDHycY" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:fHVg4ZAoSyzRoC4co_eCnbyEkz3bGc-MjiZDIfpCjRg", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:fHVg4ZAoSyzRoC4co_eCnbyEkz3bGc-MjiZDIfpCjRg" } ], "status": "final", @@ -111,9 +87,7 @@ "system": "http://unitsofmeasure.org", "code": "m" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -122,15 +96,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:BsReyBbLVpNg7dz2pyPiH27BYmfz8sXY8mip5ohsDsI" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:fHVg4ZAoSyzRoC4co_eCnbyEkz3bGc-MjiZDIfpCjRg" } } - ], + ] + }, + { "fullUrl": "urn:uuid:5784f8ea-7c3c-53fb-91a6-d102609163b3", "resource": { "resourceType": "Device", @@ -141,8 +117,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:BsReyBbLVpNg7dz2pyPiH27BYmfz8sXY8mip5ohsDsI", "type": { "coding": [ { @@ -150,7 +124,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:BsReyBbLVpNg7dz2pyPiH27BYmfz8sXY8mip5ohsDsI" } ], "status": "active", @@ -160,9 +136,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -176,10 +150,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:gYmc80HvYh0cNa1N-DVgQFgVHF-31EMwtPjP-JBSj9U" + "value": "v0:fixture-key:1:BsReyBbLVpNg7dz2pyPiH27BYmfz8sXY8mip5ohsDsI" } } - ], + ] + }, + { "fullUrl": "urn:uuid:9dd72b24-695e-5058-b521-42985d983e88", "resource": { "resourceType": "Device", @@ -190,8 +166,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:gYmc80HvYh0cNa1N-DVgQFgVHF-31EMwtPjP-JBSj9U", "type": { "coding": [ { @@ -199,7 +173,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:gYmc80HvYh0cNa1N-DVgQFgVHF-31EMwtPjP-JBSj9U" } ], "status": "active", @@ -223,9 +199,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -234,15 +208,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:DLKzTcfXTY-hdSLv9GmCMjZpw3ouRC4CPcG1pf4tSvA" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:gYmc80HvYh0cNa1N-DVgQFgVHF-31EMwtPjP-JBSj9U" } } - ], + ] + }, + { "fullUrl": "urn:uuid:e101cbaf-9a6b-585e-913a-6636fba01c24", "resource": { "resourceType": "Provenance", @@ -288,8 +264,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:TXoHUQohPJ8ryT_B_A4JXlEeOz9zuZOS0c4OxCDHycY", "type": { "coding": [ { @@ -297,7 +271,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:TXoHUQohPJ8ryT_B_A4JXlEeOz9zuZOS0c4OxCDHycY" } }, "agent": [ @@ -319,7 +295,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:DLKzTcfXTY-hdSLv9GmCMjZpw3ouRC4CPcG1pf4tSvA" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json b/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json index 5cf67f58..44a93aa0 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:13" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:BlsXMWS8VeTKUY0-gRERFgFfSQX3KOWfQ9cWnbVJYDU" - } - } - ], "fullUrl": "urn:uuid:05e2b23b-2b07-5454-9dd2-378d69ee2150", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:4YedtTQr9n-nubQkvlBwC-1mOoLtiknhWp4RTtKkSlM", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:4YedtTQr9n-nubQkvlBwC-1mOoLtiknhWp4RTtKkSlM" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:BlsXMWS8VeTKUY0-gRERFgFfSQX3KOWfQ9cWnbVJYDU", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:BlsXMWS8VeTKUY0-gRERFgFfSQX3KOWfQ9cWnbVJYDU" } ], "status": "final", @@ -113,9 +89,7 @@ "system": "http://unitsofmeasure.org", "code": "uS" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -124,15 +98,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:u-1rXN1Mxg6FOcZeVAsxYBKbRr1DMHadZTocKEEUOx4" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:BlsXMWS8VeTKUY0-gRERFgFfSQX3KOWfQ9cWnbVJYDU" } } - ], + ] + }, + { "fullUrl": "urn:uuid:c74adc7d-e2cc-5fe2-8288-704a23bd131e", "resource": { "resourceType": "Device", @@ -143,8 +119,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:u-1rXN1Mxg6FOcZeVAsxYBKbRr1DMHadZTocKEEUOx4", "type": { "coding": [ { @@ -152,7 +126,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:u-1rXN1Mxg6FOcZeVAsxYBKbRr1DMHadZTocKEEUOx4" } ], "status": "active", @@ -162,9 +138,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -178,10 +152,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:-S0Gqyi7O_7YfEiA8qAsF_fbOCQKVviT4-XvrnxqtBE" + "value": "v0:fixture-key:1:u-1rXN1Mxg6FOcZeVAsxYBKbRr1DMHadZTocKEEUOx4" } } - ], + ] + }, + { "fullUrl": "urn:uuid:2127a0df-3555-5ec9-bf92-62b908a8691f", "resource": { "resourceType": "Device", @@ -192,8 +168,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:-S0Gqyi7O_7YfEiA8qAsF_fbOCQKVviT4-XvrnxqtBE", "type": { "coding": [ { @@ -201,7 +175,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:-S0Gqyi7O_7YfEiA8qAsF_fbOCQKVviT4-XvrnxqtBE" } ], "status": "active", @@ -225,9 +201,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -236,15 +210,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:o6HbMwcpACffZfDBRcw3uhTXFHQO8CAKI_kDG78YUHw" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:-S0Gqyi7O_7YfEiA8qAsF_fbOCQKVviT4-XvrnxqtBE" } } - ], + ] + }, + { "fullUrl": "urn:uuid:62cb671e-5836-5c0c-91bd-583d7486256a", "resource": { "resourceType": "Provenance", @@ -290,8 +266,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:4YedtTQr9n-nubQkvlBwC-1mOoLtiknhWp4RTtKkSlM", "type": { "coding": [ { @@ -299,7 +273,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:4YedtTQr9n-nubQkvlBwC-1mOoLtiknhWp4RTtKkSlM" } }, "agent": [ @@ -321,7 +297,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:o6HbMwcpACffZfDBRcw3uhTXFHQO8CAKI_kDG78YUHw" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json b/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json index 88b85c16..8bd6c41b 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:14" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:Jd_HuLq4yPD61i7SXwNGSVKzGVRlPQhpCRkuTKhjqOE" - } - } - ], "fullUrl": "urn:uuid:1b278f81-bafa-5e5a-aa91-820281798dc1", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:vgABOZcd_2aMgE45wa5IupFp8PcTzVbBph9MSC6nbzE", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:vgABOZcd_2aMgE45wa5IupFp8PcTzVbBph9MSC6nbzE" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:Jd_HuLq4yPD61i7SXwNGSVKzGVRlPQhpCRkuTKhjqOE", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:Jd_HuLq4yPD61i7SXwNGSVKzGVRlPQhpCRkuTKhjqOE" } ], "status": "final", @@ -111,9 +87,7 @@ "system": "http://unitsofmeasure.org", "code": "{flights}" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -122,15 +96,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:5JsSfWJISELeuXbZe-bRaI8TDU1PGIoSZtz60Z2X7HM" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:Jd_HuLq4yPD61i7SXwNGSVKzGVRlPQhpCRkuTKhjqOE" } } - ], + ] + }, + { "fullUrl": "urn:uuid:ca82e833-a64a-5080-8f55-e25ec09ffb3b", "resource": { "resourceType": "Device", @@ -141,8 +117,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:5JsSfWJISELeuXbZe-bRaI8TDU1PGIoSZtz60Z2X7HM", "type": { "coding": [ { @@ -150,7 +124,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:5JsSfWJISELeuXbZe-bRaI8TDU1PGIoSZtz60Z2X7HM" } ], "status": "active", @@ -160,9 +136,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -176,10 +150,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:svLLBnin4pxJFIYsSL1qNT0ARP9QqSEBxlOWDkkz9DQ" + "value": "v0:fixture-key:1:5JsSfWJISELeuXbZe-bRaI8TDU1PGIoSZtz60Z2X7HM" } } - ], + ] + }, + { "fullUrl": "urn:uuid:d2359082-1541-585e-8e66-8bae2a65011a", "resource": { "resourceType": "Device", @@ -190,8 +166,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:svLLBnin4pxJFIYsSL1qNT0ARP9QqSEBxlOWDkkz9DQ", "type": { "coding": [ { @@ -199,7 +173,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:svLLBnin4pxJFIYsSL1qNT0ARP9QqSEBxlOWDkkz9DQ" } ], "status": "active", @@ -223,9 +199,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -234,15 +208,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:e88H16uxr9KnpgkeHQZBgQ7JaHfHbJ0iV8PAKr8vop8" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:svLLBnin4pxJFIYsSL1qNT0ARP9QqSEBxlOWDkkz9DQ" } } - ], + ] + }, + { "fullUrl": "urn:uuid:0713c33b-6273-530c-9915-44dc21a60b55", "resource": { "resourceType": "Provenance", @@ -288,8 +264,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:vgABOZcd_2aMgE45wa5IupFp8PcTzVbBph9MSC6nbzE", "type": { "coding": [ { @@ -297,7 +271,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:vgABOZcd_2aMgE45wa5IupFp8PcTzVbBph9MSC6nbzE" } }, "agent": [ @@ -319,7 +295,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:e88H16uxr9KnpgkeHQZBgQ7JaHfHbJ0iV8PAKr8vop8" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json index 34094738..cc506070 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:16" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:S5vVITWsjX9mcvy8pBYaU_lPwGfNl8M6BijEtChtbCU" - } - } - ], "fullUrl": "urn:uuid:f6e3927c-3d0a-5a3c-8fde-eb54caeca7b7", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:AEZUZCTuy5drfmVoPO_ot7LunYUw68sNqiXSV5fSBko", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:AEZUZCTuy5drfmVoPO_ot7LunYUw68sNqiXSV5fSBko" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:S5vVITWsjX9mcvy8pBYaU_lPwGfNl8M6BijEtChtbCU", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:S5vVITWsjX9mcvy8pBYaU_lPwGfNl8M6BijEtChtbCU" } ], "status": "final", @@ -113,9 +89,7 @@ "system": "http://unitsofmeasure.org", "code": "ms" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -124,15 +98,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:1GzMX6TN8GutnhmfmYulPCxpQfkJgPHuDr46ZX8DhTY" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:S5vVITWsjX9mcvy8pBYaU_lPwGfNl8M6BijEtChtbCU" } } - ], + ] + }, + { "fullUrl": "urn:uuid:375bbe70-10e1-5992-b3f9-0e4eb5612ae8", "resource": { "resourceType": "Device", @@ -143,8 +119,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:1GzMX6TN8GutnhmfmYulPCxpQfkJgPHuDr46ZX8DhTY", "type": { "coding": [ { @@ -152,7 +126,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:1GzMX6TN8GutnhmfmYulPCxpQfkJgPHuDr46ZX8DhTY" } ], "status": "active", @@ -162,9 +138,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -178,10 +152,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OG_EL_fZjoyFtd7M2on8ei01lKoevG6MNr3bfeuT5ec" + "value": "v0:fixture-key:1:1GzMX6TN8GutnhmfmYulPCxpQfkJgPHuDr46ZX8DhTY" } } - ], + ] + }, + { "fullUrl": "urn:uuid:c2e33326-2fed-5e61-84af-db7558330215", "resource": { "resourceType": "Device", @@ -192,8 +168,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OG_EL_fZjoyFtd7M2on8ei01lKoevG6MNr3bfeuT5ec", "type": { "coding": [ { @@ -201,7 +175,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:OG_EL_fZjoyFtd7M2on8ei01lKoevG6MNr3bfeuT5ec" } ], "status": "active", @@ -225,9 +201,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -236,15 +210,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:4M2ckiaROaQsqFL7zobE316ingS1pOnTxc05MfhitdQ" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:OG_EL_fZjoyFtd7M2on8ei01lKoevG6MNr3bfeuT5ec" } } - ], + ] + }, + { "fullUrl": "urn:uuid:c989d2f9-98e7-5835-b736-95cda36e4f12", "resource": { "resourceType": "Provenance", @@ -290,8 +266,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:AEZUZCTuy5drfmVoPO_ot7LunYUw68sNqiXSV5fSBko", "type": { "coding": [ { @@ -299,7 +273,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:AEZUZCTuy5drfmVoPO_ot7LunYUw68sNqiXSV5fSBko" } }, "agent": [ @@ -321,7 +297,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:4M2ckiaROaQsqFL7zobE316ingS1pOnTxc05MfhitdQ" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json index dbece90f..322bd718 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:17" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:CZXatvohITV673olsXmQKz38AcBWcMpvz5he0Qm5VKI" - } - } - ], "fullUrl": "urn:uuid:8270c509-bfdf-5d38-a815-b7e2214c5058", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:tkdht7TFVXQyq6IHNvYVQ8tNFVyUPgbZXNMZPxXED6U", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:tkdht7TFVXQyq6IHNvYVQ8tNFVyUPgbZXNMZPxXED6U" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:CZXatvohITV673olsXmQKz38AcBWcMpvz5he0Qm5VKI", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:CZXatvohITV673olsXmQKz38AcBWcMpvz5he0Qm5VKI" } ], "status": "final", @@ -108,9 +84,7 @@ "system": "http://unitsofmeasure.org", "code": "ms" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -119,15 +93,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OVqOPjrQEoSJS--PZePeSkqILMB9dA0xef1_SpJ4viY" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:CZXatvohITV673olsXmQKz38AcBWcMpvz5he0Qm5VKI" } } - ], + ] + }, + { "fullUrl": "urn:uuid:1ac62dcf-7508-5504-8cf9-88aa67a19f05", "resource": { "resourceType": "Device", @@ -138,8 +114,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OVqOPjrQEoSJS--PZePeSkqILMB9dA0xef1_SpJ4viY", "type": { "coding": [ { @@ -147,7 +121,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:OVqOPjrQEoSJS--PZePeSkqILMB9dA0xef1_SpJ4viY" } ], "status": "active", @@ -157,9 +133,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -173,10 +147,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:RMkmXi5iDHPmp9alpqCtmszW5AIvyyRqxxRMY_nwChw" + "value": "v0:fixture-key:1:OVqOPjrQEoSJS--PZePeSkqILMB9dA0xef1_SpJ4viY" } } - ], + ] + }, + { "fullUrl": "urn:uuid:dc091ace-e49f-5779-bcad-c1b7724d6484", "resource": { "resourceType": "Device", @@ -187,8 +163,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:RMkmXi5iDHPmp9alpqCtmszW5AIvyyRqxxRMY_nwChw", "type": { "coding": [ { @@ -196,7 +170,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:RMkmXi5iDHPmp9alpqCtmszW5AIvyyRqxxRMY_nwChw" } ], "status": "active", @@ -220,9 +196,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -231,15 +205,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:qtQ7FdqRyP8fcGw_zIozpXqNB9VIxfITbTY4xqoSj3Y" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:RMkmXi5iDHPmp9alpqCtmszW5AIvyyRqxxRMY_nwChw" } } - ], + ] + }, + { "fullUrl": "urn:uuid:6cfc6400-6efa-5c4a-8345-f481d5f52388", "resource": { "resourceType": "Provenance", @@ -285,8 +261,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:tkdht7TFVXQyq6IHNvYVQ8tNFVyUPgbZXNMZPxXED6U", "type": { "coding": [ { @@ -294,7 +268,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:tkdht7TFVXQyq6IHNvYVQ8tNFVyUPgbZXNMZPxXED6U" } }, "agent": [ @@ -316,7 +292,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:qtQ7FdqRyP8fcGw_zIozpXqNB9VIxfITbTY4xqoSj3Y" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json index 96c8c9c1..ec961e1e 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:15" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:bmuym0sEonI5tHUX1mpn1Rm7zoBhUOUWrWJSXt-J4b8" - } - } - ], "fullUrl": "urn:uuid:d977eb59-e38c-5c58-a9e1-d6aaab8006c4", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:tJcKn-_0jxgFDvTYEiVfnjgPQy6bjsEgJ6whvNnqwjM", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:tJcKn-_0jxgFDvTYEiVfnjgPQy6bjsEgJ6whvNnqwjM" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:bmuym0sEonI5tHUX1mpn1Rm7zoBhUOUWrWJSXt-J4b8", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:bmuym0sEonI5tHUX1mpn1Rm7zoBhUOUWrWJSXt-J4b8" } ], "status": "final", @@ -120,9 +96,7 @@ "system": "http://unitsofmeasure.org", "code": "/min" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -131,15 +105,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:_jJrqQjnriMYhYAfnPCdEg3KSuVIV2CPuLJ5jQLUbqA" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:bmuym0sEonI5tHUX1mpn1Rm7zoBhUOUWrWJSXt-J4b8" } } - ], + ] + }, + { "fullUrl": "urn:uuid:b49b2e14-6bf2-572d-b927-ffc5dbb2de0e", "resource": { "resourceType": "Device", @@ -150,8 +126,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:_jJrqQjnriMYhYAfnPCdEg3KSuVIV2CPuLJ5jQLUbqA", "type": { "coding": [ { @@ -159,7 +133,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:_jJrqQjnriMYhYAfnPCdEg3KSuVIV2CPuLJ5jQLUbqA" } ], "status": "active", @@ -169,9 +145,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -185,10 +159,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:iu_gT-exxcFt4UuQNJUirFWc_H7mb3n2H2opZGP7JFc" + "value": "v0:fixture-key:1:_jJrqQjnriMYhYAfnPCdEg3KSuVIV2CPuLJ5jQLUbqA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:fa8f73d9-94a9-5603-a4d6-401a04ea1714", "resource": { "resourceType": "Device", @@ -199,8 +175,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:iu_gT-exxcFt4UuQNJUirFWc_H7mb3n2H2opZGP7JFc", "type": { "coding": [ { @@ -208,7 +182,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:iu_gT-exxcFt4UuQNJUirFWc_H7mb3n2H2opZGP7JFc" } ], "status": "active", @@ -232,9 +208,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -243,15 +217,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:KeJVdr0G0FQra4gqbd817-DGBzsTeQyRmXS4w9wqvx4" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:iu_gT-exxcFt4UuQNJUirFWc_H7mb3n2H2opZGP7JFc" } } - ], + ] + }, + { "fullUrl": "urn:uuid:1b3fd21e-a7b2-5ded-b129-7c9cf4cccaf0", "resource": { "resourceType": "Provenance", @@ -297,8 +273,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:tJcKn-_0jxgFDvTYEiVfnjgPQy6bjsEgJ6whvNnqwjM", "type": { "coding": [ { @@ -306,7 +280,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:tJcKn-_0jxgFDvTYEiVfnjgPQy6bjsEgJ6whvNnqwjM" } }, "agent": [ @@ -328,7 +304,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:KeJVdr0G0FQra4gqbd817-DGBzsTeQyRmXS4w9wqvx4" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json b/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json index 62688e09..61c62a32 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:18" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:TZXCBXOpPixQ2BNvn0NbH2dqlgzPFN7dffcFH7JRopU" - } - } - ], "fullUrl": "urn:uuid:bc7ae0a2-0fc8-5ace-89e5-45f4c40a5dcb", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:8l5WdJ-o5mNFdCSbs5y9t3UvOl60nYnd583KpnzMSKM", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:8l5WdJ-o5mNFdCSbs5y9t3UvOl60nYnd583KpnzMSKM" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:TZXCBXOpPixQ2BNvn0NbH2dqlgzPFN7dffcFH7JRopU", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:TZXCBXOpPixQ2BNvn0NbH2dqlgzPFN7dffcFH7JRopU" } ], "status": "final", @@ -108,9 +84,7 @@ "system": "http://unitsofmeasure.org", "code": "kg" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -119,15 +93,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:2yXt6DebFvXDTeSlzgzyFye9iuh92QGZMLIL4n0jN80" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:TZXCBXOpPixQ2BNvn0NbH2dqlgzPFN7dffcFH7JRopU" } } - ], + ] + }, + { "fullUrl": "urn:uuid:913d1293-e271-513f-b01f-ebb18d782d5f", "resource": { "resourceType": "Device", @@ -138,8 +114,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:2yXt6DebFvXDTeSlzgzyFye9iuh92QGZMLIL4n0jN80", "type": { "coding": [ { @@ -147,7 +121,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:2yXt6DebFvXDTeSlzgzyFye9iuh92QGZMLIL4n0jN80" } ], "status": "active", @@ -157,9 +133,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -173,10 +147,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:KJTEBWuDAWHHJ5xERnRW8CcL1X4cUMxv0uoTuNNohQ8" + "value": "v0:fixture-key:1:2yXt6DebFvXDTeSlzgzyFye9iuh92QGZMLIL4n0jN80" } } - ], + ] + }, + { "fullUrl": "urn:uuid:57d9a45a-44d5-5cf9-9545-4e386990fee6", "resource": { "resourceType": "Device", @@ -187,8 +163,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:KJTEBWuDAWHHJ5xERnRW8CcL1X4cUMxv0uoTuNNohQ8", "type": { "coding": [ { @@ -196,7 +170,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:KJTEBWuDAWHHJ5xERnRW8CcL1X4cUMxv0uoTuNNohQ8" } ], "status": "active", @@ -220,9 +196,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -231,15 +205,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:32bLLYB_SafBR6GtMo9KZHVV2SiFvcclBMoKe-Tki1Y" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:KJTEBWuDAWHHJ5xERnRW8CcL1X4cUMxv0uoTuNNohQ8" } } - ], + ] + }, + { "fullUrl": "urn:uuid:19b3591a-7abf-5469-a12a-f47f00d1e5c4", "resource": { "resourceType": "Provenance", @@ -285,8 +261,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:8l5WdJ-o5mNFdCSbs5y9t3UvOl60nYnd583KpnzMSKM", "type": { "coding": [ { @@ -294,7 +268,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:8l5WdJ-o5mNFdCSbs5y9t3UvOl60nYnd583KpnzMSKM" } }, "agent": [ @@ -316,7 +292,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:32bLLYB_SafBR6GtMo9KZHVV2SiFvcclBMoKe-Tki1Y" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json index 0591d372..21cd2e44 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:19" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:a8s1HpBI9_4qjgDcZG1wbl8Opf3E51HAkTK3f2QsUYg" - } - } - ], "fullUrl": "urn:uuid:4efc2828-d56e-533c-973a-822c1bfa4e23", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:uIT4xv9B-PBwKmJAw7VglCZ1hAYn6BSJF577LrFO2ZA", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:uIT4xv9B-PBwKmJAw7VglCZ1hAYn6BSJF577LrFO2ZA" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:a8s1HpBI9_4qjgDcZG1wbl8Opf3E51HAkTK3f2QsUYg", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:a8s1HpBI9_4qjgDcZG1wbl8Opf3E51HAkTK3f2QsUYg" } ], "status": "final", @@ -121,9 +97,7 @@ ], "text": "Session total" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -132,15 +106,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:By5ZV5MsYx3CBGT8S2VIwR4j7Rv6dE81YMIVOeBOtSo" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:a8s1HpBI9_4qjgDcZG1wbl8Opf3E51HAkTK3f2QsUYg" } } - ], + ] + }, + { "fullUrl": "urn:uuid:5d918d67-4fa9-55ee-b070-98dcf22a5fae", "resource": { "resourceType": "Device", @@ -151,8 +127,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:By5ZV5MsYx3CBGT8S2VIwR4j7Rv6dE81YMIVOeBOtSo", "type": { "coding": [ { @@ -160,7 +134,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:By5ZV5MsYx3CBGT8S2VIwR4j7Rv6dE81YMIVOeBOtSo" } ], "status": "active", @@ -170,9 +146,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -186,10 +160,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:gAk_xo_m3VIoAq8WwnPjv8FyKliF_r2JVORqOlVXzQk" + "value": "v0:fixture-key:1:By5ZV5MsYx3CBGT8S2VIwR4j7Rv6dE81YMIVOeBOtSo" } } - ], + ] + }, + { "fullUrl": "urn:uuid:4fe4116e-b120-5c76-b6b4-c45669831277", "resource": { "resourceType": "Device", @@ -200,8 +176,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:gAk_xo_m3VIoAq8WwnPjv8FyKliF_r2JVORqOlVXzQk", "type": { "coding": [ { @@ -209,7 +183,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:gAk_xo_m3VIoAq8WwnPjv8FyKliF_r2JVORqOlVXzQk" } ], "status": "active", @@ -233,9 +209,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -244,15 +218,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:iom5w6J85R7VsUmEG6UnuzPUqpuUXfUcEUI7pogG9IA" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:gAk_xo_m3VIoAq8WwnPjv8FyKliF_r2JVORqOlVXzQk" } } - ], + ] + }, + { "fullUrl": "urn:uuid:6e7dd3df-58b6-522c-a757-ebb39306085c", "resource": { "resourceType": "Provenance", @@ -298,8 +274,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:uIT4xv9B-PBwKmJAw7VglCZ1hAYn6BSJF577LrFO2ZA", "type": { "coding": [ { @@ -307,7 +281,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:uIT4xv9B-PBwKmJAw7VglCZ1hAYn6BSJF577LrFO2ZA" } }, "agent": [ @@ -329,7 +305,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:iom5w6J85R7VsUmEG6UnuzPUqpuUXfUcEUI7pogG9IA" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json index cd2dea4d..c9a7aa20 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:21" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:Foy5lK9amkmeg12XbXQYMrT-TUBizAOPdTfE7c8MsIE" - } - } - ], "fullUrl": "urn:uuid:43ad632f-c572-576f-8fef-5b703ca7cd84", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:osGy-Ko7LLVWyvk64uBV-JuE2OlQbjgV9yXagYzTplA", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:osGy-Ko7LLVWyvk64uBV-JuE2OlQbjgV9yXagYzTplA" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:Foy5lK9amkmeg12XbXQYMrT-TUBizAOPdTfE7c8MsIE", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:Foy5lK9amkmeg12XbXQYMrT-TUBizAOPdTfE7c8MsIE" } ], "status": "final", @@ -121,9 +97,7 @@ ], "text": "Daily mean" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -132,15 +106,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:JRw2RshEpj2tnoPVOkAi7BWBqTQu2B2oX5wgLXZiX3c" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:Foy5lK9amkmeg12XbXQYMrT-TUBizAOPdTfE7c8MsIE" } } - ], + ] + }, + { "fullUrl": "urn:uuid:f0661a79-cb88-5556-a043-744b7b78328c", "resource": { "resourceType": "Device", @@ -151,8 +127,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:JRw2RshEpj2tnoPVOkAi7BWBqTQu2B2oX5wgLXZiX3c", "type": { "coding": [ { @@ -160,7 +134,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:JRw2RshEpj2tnoPVOkAi7BWBqTQu2B2oX5wgLXZiX3c" } ], "status": "active", @@ -170,9 +146,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -186,10 +160,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uIYGTaUTHfKiwEm5jVkr_z9QOzRbCzr3NeyStHgnB7Y" + "value": "v0:fixture-key:1:JRw2RshEpj2tnoPVOkAi7BWBqTQu2B2oX5wgLXZiX3c" } } - ], + ] + }, + { "fullUrl": "urn:uuid:99e59cb2-5f78-5b22-96f2-6c3467d5b7d1", "resource": { "resourceType": "Device", @@ -200,8 +176,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uIYGTaUTHfKiwEm5jVkr_z9QOzRbCzr3NeyStHgnB7Y", "type": { "coding": [ { @@ -209,7 +183,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:uIYGTaUTHfKiwEm5jVkr_z9QOzRbCzr3NeyStHgnB7Y" } ], "status": "active", @@ -233,9 +209,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -244,15 +218,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:S4UXghjlk4gU1D48Oolvx5KhsOA-BiiIGLy8DuV88F0" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:uIYGTaUTHfKiwEm5jVkr_z9QOzRbCzr3NeyStHgnB7Y" } } - ], + ] + }, + { "fullUrl": "urn:uuid:21a750dd-da67-5b44-a03a-0cdd3cdd0cfc", "resource": { "resourceType": "Provenance", @@ -298,8 +274,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:osGy-Ko7LLVWyvk64uBV-JuE2OlQbjgV9yXagYzTplA", "type": { "coding": [ { @@ -307,7 +281,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:osGy-Ko7LLVWyvk64uBV-JuE2OlQbjgV9yXagYzTplA" } }, "agent": [ @@ -329,7 +305,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:S4UXghjlk4gU1D48Oolvx5KhsOA-BiiIGLy8DuV88F0" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json index 9e8b13ca..bf79d459 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:20" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:HPgVl-lF8_XjYKtNbZqZoeUvyJx6cwlNxaJ3AgW6mG8" - } - } - ], "fullUrl": "urn:uuid:ba01f42d-adfb-532e-8909-cf8ff6e1d183", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:sJSuge3Kkid_TsVY7p-oSU7CUFGvHXOZEVfyi6GRsvs", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:sJSuge3Kkid_TsVY7p-oSU7CUFGvHXOZEVfyi6GRsvs" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:HPgVl-lF8_XjYKtNbZqZoeUvyJx6cwlNxaJ3AgW6mG8", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:HPgVl-lF8_XjYKtNbZqZoeUvyJx6cwlNxaJ3AgW6mG8" } ], "status": "final", @@ -120,9 +96,7 @@ "system": "http://unitsofmeasure.org", "code": "%" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -131,15 +105,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:U37Z6ve6J62eVasgsTcFZNjogwaV5RSx0vOFIcZnowQ" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:HPgVl-lF8_XjYKtNbZqZoeUvyJx6cwlNxaJ3AgW6mG8" } } - ], + ] + }, + { "fullUrl": "urn:uuid:ef4c6dd5-5186-52bd-81ff-c35ceff514cd", "resource": { "resourceType": "Device", @@ -150,8 +126,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:U37Z6ve6J62eVasgsTcFZNjogwaV5RSx0vOFIcZnowQ", "type": { "coding": [ { @@ -159,7 +133,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:U37Z6ve6J62eVasgsTcFZNjogwaV5RSx0vOFIcZnowQ" } ], "status": "active", @@ -169,9 +145,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -185,10 +159,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:fs_lzSgI2UvEDgi6o7MZqbAjw2DzA9xn0PCEWBSfRjo" + "value": "v0:fixture-key:1:U37Z6ve6J62eVasgsTcFZNjogwaV5RSx0vOFIcZnowQ" } } - ], + ] + }, + { "fullUrl": "urn:uuid:a26ecd38-abeb-5d40-b1fb-7b6217720f23", "resource": { "resourceType": "Device", @@ -199,8 +175,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:fs_lzSgI2UvEDgi6o7MZqbAjw2DzA9xn0PCEWBSfRjo", "type": { "coding": [ { @@ -208,7 +182,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:fs_lzSgI2UvEDgi6o7MZqbAjw2DzA9xn0PCEWBSfRjo" } ], "status": "active", @@ -232,9 +208,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -243,15 +217,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:vCXndC-N8tGe3pKoAEF3lUnqguUQbAFW-Ntf2LkQHQg" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:fs_lzSgI2UvEDgi6o7MZqbAjw2DzA9xn0PCEWBSfRjo" } } - ], + ] + }, + { "fullUrl": "urn:uuid:464a25cc-b778-556b-98d6-637239e26837", "resource": { "resourceType": "Provenance", @@ -297,8 +273,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:sJSuge3Kkid_TsVY7p-oSU7CUFGvHXOZEVfyi6GRsvs", "type": { "coding": [ { @@ -306,7 +280,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:sJSuge3Kkid_TsVY7p-oSU7CUFGvHXOZEVfyi6GRsvs" } }, "agent": [ @@ -328,7 +304,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:vCXndC-N8tGe3pKoAEF3lUnqguUQbAFW-Ntf2LkQHQg" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json index aedf9d15..064bfe4e 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:22" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:ldt-blmNxjKdqbyUa4I71AKfMfwlCmOatocVyPgQyxs" - } - } - ], "fullUrl": "urn:uuid:3e90f50b-622a-53e2-9479-a27ada726090", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:RKGg7_SVjLJ-TW2IEyKdFAqEFIyLZ6mZjuKs4CBBC9U", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:RKGg7_SVjLJ-TW2IEyKdFAqEFIyLZ6mZjuKs4CBBC9U" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:ldt-blmNxjKdqbyUa4I71AKfMfwlCmOatocVyPgQyxs", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:ldt-blmNxjKdqbyUa4I71AKfMfwlCmOatocVyPgQyxs" } ], "status": "final", @@ -121,9 +97,7 @@ ], "text": "Session total" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -132,15 +106,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:idFKgbnmzoK3YLYXQ9N6KsqXS3CAPEPqADjwroI9Bsg" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:ldt-blmNxjKdqbyUa4I71AKfMfwlCmOatocVyPgQyxs" } } - ], + ] + }, + { "fullUrl": "urn:uuid:ca2701fb-c3c9-5019-9a86-da7b9aa683d2", "resource": { "resourceType": "Device", @@ -151,8 +127,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:idFKgbnmzoK3YLYXQ9N6KsqXS3CAPEPqADjwroI9Bsg", "type": { "coding": [ { @@ -160,7 +134,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:idFKgbnmzoK3YLYXQ9N6KsqXS3CAPEPqADjwroI9Bsg" } ], "status": "active", @@ -170,9 +146,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -186,10 +160,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:h1-IrXtzorIt0CuTA5q7b8m8r6AzfjVwt8gA8BpTkZA" + "value": "v0:fixture-key:1:idFKgbnmzoK3YLYXQ9N6KsqXS3CAPEPqADjwroI9Bsg" } } - ], + ] + }, + { "fullUrl": "urn:uuid:e575699a-b0df-5dc5-9d5a-e1ee8f319c1d", "resource": { "resourceType": "Device", @@ -200,8 +176,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:h1-IrXtzorIt0CuTA5q7b8m8r6AzfjVwt8gA8BpTkZA", "type": { "coding": [ { @@ -209,7 +183,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:h1-IrXtzorIt0CuTA5q7b8m8r6AzfjVwt8gA8BpTkZA" } ], "status": "active", @@ -233,9 +209,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -244,15 +218,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:G3eA3tIQdES8WikO3gyu5o7DnXbrq-Rc2xFFRMP9KVA" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:h1-IrXtzorIt0CuTA5q7b8m8r6AzfjVwt8gA8BpTkZA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:5b2461b4-1c63-5b4f-b75f-d9f0f8e78d0c", "resource": { "resourceType": "Provenance", @@ -298,8 +274,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:RKGg7_SVjLJ-TW2IEyKdFAqEFIyLZ6mZjuKs4CBBC9U", "type": { "coding": [ { @@ -307,7 +281,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:RKGg7_SVjLJ-TW2IEyKdFAqEFIyLZ6mZjuKs4CBBC9U" } }, "agent": [ @@ -329,7 +305,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:G3eA3tIQdES8WikO3gyu5o7DnXbrq-Rc2xFFRMP9KVA" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json b/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json index 72738188..325514b7 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:23" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:r10WUFXbDXvxtZpsCniA4V0TxQygNJFC5AEv23Q2oXU" - } - } - ], "fullUrl": "urn:uuid:a1134f0f-22c8-5c88-add6-5fb90cabf8c2", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:TrGlFtP5wdk-NzsSnMruKHD25VHWmapGR8rUdWfjLEk", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:TrGlFtP5wdk-NzsSnMruKHD25VHWmapGR8rUdWfjLEk" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:r10WUFXbDXvxtZpsCniA4V0TxQygNJFC5AEv23Q2oXU", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:r10WUFXbDXvxtZpsCniA4V0TxQygNJFC5AEv23Q2oXU" } ], "status": "final", @@ -119,9 +95,7 @@ } ] } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -130,15 +104,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Suyzh8Jk_ZVJrdhLmLMe8ydUYu_IJuKhg8MVfBTTsjw" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:r10WUFXbDXvxtZpsCniA4V0TxQygNJFC5AEv23Q2oXU" } } - ], + ] + }, + { "fullUrl": "urn:uuid:a4e868e2-8d17-5b8b-9a65-d26a146f5af0", "resource": { "resourceType": "Device", @@ -149,8 +125,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Suyzh8Jk_ZVJrdhLmLMe8ydUYu_IJuKhg8MVfBTTsjw", "type": { "coding": [ { @@ -158,7 +132,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:Suyzh8Jk_ZVJrdhLmLMe8ydUYu_IJuKhg8MVfBTTsjw" } ], "status": "active", @@ -168,9 +144,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -184,10 +158,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Z2HsC_lKlXKDL6bi7wIzi2ovDucUm2-W2e0TLsG-WI0" + "value": "v0:fixture-key:1:Suyzh8Jk_ZVJrdhLmLMe8ydUYu_IJuKhg8MVfBTTsjw" } } - ], + ] + }, + { "fullUrl": "urn:uuid:119259a9-5f5d-5b2b-b86d-f71052244ad1", "resource": { "resourceType": "Device", @@ -198,8 +174,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Z2HsC_lKlXKDL6bi7wIzi2ovDucUm2-W2e0TLsG-WI0", "type": { "coding": [ { @@ -207,7 +181,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:Z2HsC_lKlXKDL6bi7wIzi2ovDucUm2-W2e0TLsG-WI0" } ], "status": "active", @@ -231,9 +207,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -242,15 +216,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:2N9RBu-o6BhoV6evxl2ZIn8X-bRd76K7CyySv3zfNuE" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:Z2HsC_lKlXKDL6bi7wIzi2ovDucUm2-W2e0TLsG-WI0" } } - ], + ] + }, + { "fullUrl": "urn:uuid:5a2b779a-59f5-5c57-8ca8-410e6bebc4c0", "resource": { "resourceType": "Provenance", @@ -296,8 +272,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:TrGlFtP5wdk-NzsSnMruKHD25VHWmapGR8rUdWfjLEk", "type": { "coding": [ { @@ -305,7 +279,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:TrGlFtP5wdk-NzsSnMruKHD25VHWmapGR8rUdWfjLEk" } }, "agent": [ @@ -327,7 +303,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:2N9RBu-o6BhoV6evxl2ZIn8X-bRd76K7CyySv3zfNuE" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json b/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json index 6e5814b4..c7454a24 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:24" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:fdLtyB1vt86GnGXA2wJPWN5tMekIYkeKXWnBPMvtvsc" - } - } - ], "fullUrl": "urn:uuid:83c3b3f4-b249-5e84-a42f-680a9c7cb75c", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:jnbU2fWZpeemaU1jjrjbCQ6EkOKBh5itBxi1bLLujW0", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:jnbU2fWZpeemaU1jjrjbCQ6EkOKBh5itBxi1bLLujW0" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:fdLtyB1vt86GnGXA2wJPWN5tMekIYkeKXWnBPMvtvsc", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:fdLtyB1vt86GnGXA2wJPWN5tMekIYkeKXWnBPMvtvsc" } ], "status": "final", @@ -108,9 +84,7 @@ "system": "http://unitsofmeasure.org", "code": "Cel" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -119,15 +93,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:xBHRY4vXBxZ2fEDb8AicBvFVjYOStAf_p_DKiN7rnoo" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:fdLtyB1vt86GnGXA2wJPWN5tMekIYkeKXWnBPMvtvsc" } } - ], + ] + }, + { "fullUrl": "urn:uuid:64c16cac-7015-51b2-92e2-6bc3ac450a3a", "resource": { "resourceType": "Device", @@ -138,8 +114,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:xBHRY4vXBxZ2fEDb8AicBvFVjYOStAf_p_DKiN7rnoo", "type": { "coding": [ { @@ -147,7 +121,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:xBHRY4vXBxZ2fEDb8AicBvFVjYOStAf_p_DKiN7rnoo" } ], "status": "active", @@ -157,9 +133,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -173,10 +147,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uZEfoSsNnjS4K9JmUnCoUz_vO2fD_l8IPkRhi946zPo" + "value": "v0:fixture-key:1:xBHRY4vXBxZ2fEDb8AicBvFVjYOStAf_p_DKiN7rnoo" } } - ], + ] + }, + { "fullUrl": "urn:uuid:648c018e-0d55-5039-987a-08ad126aeac9", "resource": { "resourceType": "Device", @@ -187,8 +163,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uZEfoSsNnjS4K9JmUnCoUz_vO2fD_l8IPkRhi946zPo", "type": { "coding": [ { @@ -196,7 +170,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:uZEfoSsNnjS4K9JmUnCoUz_vO2fD_l8IPkRhi946zPo" } ], "status": "active", @@ -220,9 +196,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -231,15 +205,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:MtBt0Wkhm18_pyA_rsaK91FIZ0_QarfjD9ZKfe1L4U0" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:uZEfoSsNnjS4K9JmUnCoUz_vO2fD_l8IPkRhi946zPo" } } - ], + ] + }, + { "fullUrl": "urn:uuid:5097c006-bede-5794-b1b9-ccebfcba4748", "resource": { "resourceType": "Provenance", @@ -285,8 +261,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:jnbU2fWZpeemaU1jjrjbCQ6EkOKBh5itBxi1bLLujW0", "type": { "coding": [ { @@ -294,7 +268,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:jnbU2fWZpeemaU1jjrjbCQ6EkOKBh5itBxi1bLLujW0" } }, "agent": [ @@ -316,7 +292,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:MtBt0Wkhm18_pyA_rsaK91FIZ0_QarfjD9ZKfe1L4U0" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json index 46f4927c..70d174d6 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:25" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:RrQhI3fnS7enR0ln-yibj1hlouMBhA3GI8L9NVWkON4" - } - } - ], "fullUrl": "urn:uuid:e63294c3-551a-5e44-8555-92a844716e30", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:Ox1oQ-oDGcR1WotanmbfWhWK2iBcfBG6TEDfZmgHBm4", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:Ox1oQ-oDGcR1WotanmbfWhWK2iBcfBG6TEDfZmgHBm4" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:RrQhI3fnS7enR0ln-yibj1hlouMBhA3GI8L9NVWkON4", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:RrQhI3fnS7enR0ln-yibj1hlouMBhA3GI8L9NVWkON4" } ], "status": "final", @@ -121,9 +97,7 @@ ], "text": "Session total" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -132,15 +106,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:jxJJDBZztrLVbB7pyk4VKRDVrbOCbEIqcdIdqJmFsJw" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:RrQhI3fnS7enR0ln-yibj1hlouMBhA3GI8L9NVWkON4" } } - ], + ] + }, + { "fullUrl": "urn:uuid:3900cdcb-8c1a-55b9-b0dd-b02fb6023801", "resource": { "resourceType": "Device", @@ -151,8 +127,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:jxJJDBZztrLVbB7pyk4VKRDVrbOCbEIqcdIdqJmFsJw", "type": { "coding": [ { @@ -160,7 +134,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:jxJJDBZztrLVbB7pyk4VKRDVrbOCbEIqcdIdqJmFsJw" } ], "status": "active", @@ -170,9 +146,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -186,10 +160,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:N8JAO-8OSs3AhGY2IxA7UNiM9whJhJqGnzFplOsRHOo" + "value": "v0:fixture-key:1:jxJJDBZztrLVbB7pyk4VKRDVrbOCbEIqcdIdqJmFsJw" } } - ], + ] + }, + { "fullUrl": "urn:uuid:c57e5208-b0a4-5ac2-bef1-6c4eefa43bc8", "resource": { "resourceType": "Device", @@ -200,8 +176,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:N8JAO-8OSs3AhGY2IxA7UNiM9whJhJqGnzFplOsRHOo", "type": { "coding": [ { @@ -209,7 +183,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:N8JAO-8OSs3AhGY2IxA7UNiM9whJhJqGnzFplOsRHOo" } ], "status": "active", @@ -233,9 +209,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -244,15 +218,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:SiMEd3qZlkyTXV_qGX4ZkIvuHd8Fm1jNouSrwNjZJ-I" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:N8JAO-8OSs3AhGY2IxA7UNiM9whJhJqGnzFplOsRHOo" } } - ], + ] + }, + { "fullUrl": "urn:uuid:88a35473-0b28-556c-8673-32b1acf1ce4e", "resource": { "resourceType": "Provenance", @@ -298,8 +274,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:Ox1oQ-oDGcR1WotanmbfWhWK2iBcfBG6TEDfZmgHBm4", "type": { "coding": [ { @@ -307,7 +281,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:Ox1oQ-oDGcR1WotanmbfWhWK2iBcfBG6TEDfZmgHBm4" } }, "agent": [ @@ -329,7 +305,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:SiMEd3qZlkyTXV_qGX4ZkIvuHd8Fm1jNouSrwNjZJ-I" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json index 41dc50eb..f3353bd3 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:26" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:BnHqjF7z-fvb4wgudNxtBNc9e0Td37yuHakVXYwIpdA" - } - } - ], "fullUrl": "urn:uuid:c7d05f45-e906-5225-ac65-22dc09975c6a", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:tq2KjCGUlwWO9GMzdQoVswuS6YBF6h5Wpd1BnbXhhYc", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:tq2KjCGUlwWO9GMzdQoVswuS6YBF6h5Wpd1BnbXhhYc" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:BnHqjF7z-fvb4wgudNxtBNc9e0Td37yuHakVXYwIpdA", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:BnHqjF7z-fvb4wgudNxtBNc9e0Td37yuHakVXYwIpdA" } ], "status": "final", @@ -111,9 +87,7 @@ "system": "http://unitsofmeasure.org", "code": "h" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -122,15 +96,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:eAmZ5wOIQlO_MYAqnpwJRFjvli30bYvkdRtDO1wMfOQ" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:BnHqjF7z-fvb4wgudNxtBNc9e0Td37yuHakVXYwIpdA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:83d5b122-5954-54c7-b4b2-c0fa6e8ef9b6", "resource": { "resourceType": "Device", @@ -141,8 +117,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:eAmZ5wOIQlO_MYAqnpwJRFjvli30bYvkdRtDO1wMfOQ", "type": { "coding": [ { @@ -150,7 +124,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:eAmZ5wOIQlO_MYAqnpwJRFjvli30bYvkdRtDO1wMfOQ" } ], "status": "active", @@ -160,9 +136,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -176,10 +150,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:7MURadTgoXGjqOMEPmM18FYdUWAp08tL56ZbkpMj6HA" + "value": "v0:fixture-key:1:eAmZ5wOIQlO_MYAqnpwJRFjvli30bYvkdRtDO1wMfOQ" } } - ], + ] + }, + { "fullUrl": "urn:uuid:6eae3163-c8bf-5af0-a2aa-898f30fedbaa", "resource": { "resourceType": "Device", @@ -190,8 +166,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:7MURadTgoXGjqOMEPmM18FYdUWAp08tL56ZbkpMj6HA", "type": { "coding": [ { @@ -199,7 +173,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:7MURadTgoXGjqOMEPmM18FYdUWAp08tL56ZbkpMj6HA" } ], "status": "active", @@ -223,9 +199,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -234,15 +208,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:KOdHjEeddjWKfXuVEGFAN1GhoEkZv3s2YSypNw3sJ40" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:7MURadTgoXGjqOMEPmM18FYdUWAp08tL56ZbkpMj6HA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:5e0b87cc-e153-5c3e-a848-1732a6dd7702", "resource": { "resourceType": "Provenance", @@ -288,8 +264,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:tq2KjCGUlwWO9GMzdQoVswuS6YBF6h5Wpd1BnbXhhYc", "type": { "coding": [ { @@ -297,7 +271,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:tq2KjCGUlwWO9GMzdQoVswuS6YBF6h5Wpd1BnbXhhYc" } }, "agent": [ @@ -319,7 +295,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:KOdHjEeddjWKfXuVEGFAN1GhoEkZv3s2YSypNw3sJ40" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json b/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json index 47ae750a..349cc35c 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:27" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:v_p29kjZUa7EaT8C-ggI4-rA-62O_X1K0UchTFhf7YU" - } - } - ], "fullUrl": "urn:uuid:7f60454b-9cc2-58ee-bedd-defb9560013f", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:gOtALovyEyY_gqwlVxmn-BvfUaxigGpRasfr7UORnlM", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:gOtALovyEyY_gqwlVxmn-BvfUaxigGpRasfr7UORnlM" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:v_p29kjZUa7EaT8C-ggI4-rA-62O_X1K0UchTFhf7YU", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:v_p29kjZUa7EaT8C-ggI4-rA-62O_X1K0UchTFhf7YU" } ], "status": "final", @@ -124,9 +100,7 @@ } ] } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -135,15 +109,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:dNoKM-UEFi5MYBVp7UEtHG2Ao0--GXrzOSQuQxAxeRg" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:v_p29kjZUa7EaT8C-ggI4-rA-62O_X1K0UchTFhf7YU" } } - ], + ] + }, + { "fullUrl": "urn:uuid:5c611825-6542-516b-ba90-66684d349d39", "resource": { "resourceType": "Device", @@ -154,8 +130,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:dNoKM-UEFi5MYBVp7UEtHG2Ao0--GXrzOSQuQxAxeRg", "type": { "coding": [ { @@ -163,7 +137,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:dNoKM-UEFi5MYBVp7UEtHG2Ao0--GXrzOSQuQxAxeRg" } ], "status": "active", @@ -173,9 +149,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -189,10 +163,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:MDAetuhZk3l44pAAh5PaWGoW9IO9PGRm6X1ch4jI94M" + "value": "v0:fixture-key:1:dNoKM-UEFi5MYBVp7UEtHG2Ao0--GXrzOSQuQxAxeRg" } } - ], + ] + }, + { "fullUrl": "urn:uuid:cf7a92be-643f-504b-b60b-8f1c24c2aa5f", "resource": { "resourceType": "Device", @@ -203,8 +179,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:MDAetuhZk3l44pAAh5PaWGoW9IO9PGRm6X1ch4jI94M", "type": { "coding": [ { @@ -212,7 +186,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:MDAetuhZk3l44pAAh5PaWGoW9IO9PGRm6X1ch4jI94M" } ], "status": "active", @@ -236,9 +212,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -247,15 +221,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:DA75S6ZIPeJEPB6N1IgISQ79mrz3BS0sfaQ_CPg_4Bo" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:MDAetuhZk3l44pAAh5PaWGoW9IO9PGRm6X1ch4jI94M" } } - ], + ] + }, + { "fullUrl": "urn:uuid:4c4d3370-e8d3-5721-a321-9a6d8edc8abc", "resource": { "resourceType": "Provenance", @@ -301,8 +277,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:gOtALovyEyY_gqwlVxmn-BvfUaxigGpRasfr7UORnlM", "type": { "coding": [ { @@ -310,7 +284,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:gOtALovyEyY_gqwlVxmn-BvfUaxigGpRasfr7UORnlM" } }, "agent": [ @@ -332,7 +308,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:DA75S6ZIPeJEPB6N1IgISQ79mrz3BS0sfaQ_CPg_4Bo" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-step-count.json b/packages/fhir/fixtures/conformance/resources/mobile-step-count.json index 4c47a737..a62330da 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-step-count.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-step-count.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:28" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:8mwhbk99yf3qpijHJ2VWzxAsXnypD6VVECucMVPuirw" - } - } - ], "fullUrl": "urn:uuid:def6a4c8-a9f0-5d9b-a512-98d5dbfca87a", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:9zGYMkV1hVhzIUOw3odddQ_0JDsGiR_ZOl7_sV4ylaY", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:9zGYMkV1hVhzIUOw3odddQ_0JDsGiR_ZOl7_sV4ylaY" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:8mwhbk99yf3qpijHJ2VWzxAsXnypD6VVECucMVPuirw", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:8mwhbk99yf3qpijHJ2VWzxAsXnypD6VVECucMVPuirw" } ], "status": "final", @@ -116,9 +92,7 @@ "system": "http://unitsofmeasure.org", "code": "{steps}" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -127,15 +101,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:xaoyrihBfXy4uJJ28nRdUxKMsq08tNIl26Ucwjp7iQQ" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:8mwhbk99yf3qpijHJ2VWzxAsXnypD6VVECucMVPuirw" } } - ], + ] + }, + { "fullUrl": "urn:uuid:0a836d40-fd1f-5d14-a41b-a8525341c001", "resource": { "resourceType": "Device", @@ -146,8 +122,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:xaoyrihBfXy4uJJ28nRdUxKMsq08tNIl26Ucwjp7iQQ", "type": { "coding": [ { @@ -155,7 +129,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:xaoyrihBfXy4uJJ28nRdUxKMsq08tNIl26Ucwjp7iQQ" } ], "status": "active", @@ -165,9 +141,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -181,10 +155,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:-_UQxypI0SjyF6fEwjQ5odDh_ER349OWAWVuEytoLMM" + "value": "v0:fixture-key:1:xaoyrihBfXy4uJJ28nRdUxKMsq08tNIl26Ucwjp7iQQ" } } - ], + ] + }, + { "fullUrl": "urn:uuid:456ac9d1-6a74-5fd7-9ca9-282783f29731", "resource": { "resourceType": "Device", @@ -195,8 +171,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:-_UQxypI0SjyF6fEwjQ5odDh_ER349OWAWVuEytoLMM", "type": { "coding": [ { @@ -204,7 +178,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:-_UQxypI0SjyF6fEwjQ5odDh_ER349OWAWVuEytoLMM" } ], "status": "active", @@ -228,9 +204,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -239,15 +213,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:cD_VWhvlrj683Wch8scpXL5DtlefkyZA-WfO372_inM" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:-_UQxypI0SjyF6fEwjQ5odDh_ER349OWAWVuEytoLMM" } } - ], + ] + }, + { "fullUrl": "urn:uuid:19777a0c-5c43-567f-b887-e9ec799d80bb", "resource": { "resourceType": "Provenance", @@ -293,8 +269,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:9zGYMkV1hVhzIUOw3odddQ_0JDsGiR_ZOl7_sV4ylaY", "type": { "coding": [ { @@ -302,7 +276,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:9zGYMkV1hVhzIUOw3odddQ_0JDsGiR_ZOl7_sV4ylaY" } }, "agent": [ @@ -324,7 +300,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:cD_VWhvlrj683Wch8scpXL5DtlefkyZA-WfO372_inM" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json b/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json index a66888a5..7d549f24 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:29" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:byw_l-KxsVtgYYiKT-e5xUOjsnJOeELyiUftu7HeVCA" - } - } - ], "fullUrl": "urn:uuid:f0075728-43f1-5fe6-95d9-53613a722202", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:tfPeQl3pTiKfgjBUofMUoVbrLlwsNZn1cHtIxccehpY", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:tfPeQl3pTiKfgjBUofMUoVbrLlwsNZn1cHtIxccehpY" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:byw_l-KxsVtgYYiKT-e5xUOjsnJOeELyiUftu7HeVCA", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:byw_l-KxsVtgYYiKT-e5xUOjsnJOeELyiUftu7HeVCA" } ], "status": "final", @@ -113,9 +89,7 @@ "system": "http://unitsofmeasure.org", "code": "mL/kg/min" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -124,15 +98,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:mEOIRyxLXC_8bNJ1cmx0JcKaPGXE2oW4a0PL094BZWk" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:byw_l-KxsVtgYYiKT-e5xUOjsnJOeELyiUftu7HeVCA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:48c66d02-a1aa-540f-acf7-c768a7c96979", "resource": { "resourceType": "Device", @@ -143,8 +119,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:mEOIRyxLXC_8bNJ1cmx0JcKaPGXE2oW4a0PL094BZWk", "type": { "coding": [ { @@ -152,7 +126,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:mEOIRyxLXC_8bNJ1cmx0JcKaPGXE2oW4a0PL094BZWk" } ], "status": "active", @@ -162,9 +138,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -178,10 +152,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:JUAIgKxQnM_ad5eD24yDcecDG_XelEy6eEdzZKT7zi0" + "value": "v0:fixture-key:1:mEOIRyxLXC_8bNJ1cmx0JcKaPGXE2oW4a0PL094BZWk" } } - ], + ] + }, + { "fullUrl": "urn:uuid:d4b2afc0-724c-5834-aced-2254893693c5", "resource": { "resourceType": "Device", @@ -192,8 +168,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:JUAIgKxQnM_ad5eD24yDcecDG_XelEy6eEdzZKT7zi0", "type": { "coding": [ { @@ -201,7 +175,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:JUAIgKxQnM_ad5eD24yDcecDG_XelEy6eEdzZKT7zi0" } ], "status": "active", @@ -225,9 +201,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -236,15 +210,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:ri56YyNp3A9fJTTp-1IagZQJnuquHZ6s4vQx60awN9I" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:JUAIgKxQnM_ad5eD24yDcecDG_XelEy6eEdzZKT7zi0" } } - ], + ] + }, + { "fullUrl": "urn:uuid:1e759f0a-1feb-5565-b08e-4dbe3e9b53d2", "resource": { "resourceType": "Provenance", @@ -290,8 +266,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:tfPeQl3pTiKfgjBUofMUoVbrLlwsNZn1cHtIxccehpY", "type": { "coding": [ { @@ -299,7 +273,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:tfPeQl3pTiKfgjBUofMUoVbrLlwsNZn1cHtIxccehpY" } }, "agent": [ @@ -321,7 +297,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:ri56YyNp3A9fJTTp-1IagZQJnuquHZ6s4vQx60awN9I" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/mobile-workout.json b/packages/fhir/fixtures/conformance/resources/mobile-workout.json index c5854299..cd4096b8 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-workout.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-workout.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:30" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:7Xrr8q_a0KNfW6JdKupGPUOm4HTozWQ99TksbjKEvYc" - } - } - ], "fullUrl": "urn:uuid:39ed2a93-60f9-5643-a707-d4da21771ca3", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:LcqDQSNcMDxugFlPHpH5RFXlEN4jj61vsIebOIwHBus", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:LcqDQSNcMDxugFlPHpH5RFXlEN4jj61vsIebOIwHBus" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:7Xrr8q_a0KNfW6JdKupGPUOm4HTozWQ99TksbjKEvYc", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:7Xrr8q_a0KNfW6JdKupGPUOm4HTozWQ99TksbjKEvYc" } ], "status": "final", @@ -118,9 +94,7 @@ } ] } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -129,15 +103,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:6nMZv2pJPKEo82zRsPoCm8_Bzt1S1EZ_AyEFy1KGgtA" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:7Xrr8q_a0KNfW6JdKupGPUOm4HTozWQ99TksbjKEvYc" } } - ], + ] + }, + { "fullUrl": "urn:uuid:fda6fbb7-3989-5f51-8167-3a6114856c2a", "resource": { "resourceType": "Device", @@ -148,8 +124,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:6nMZv2pJPKEo82zRsPoCm8_Bzt1S1EZ_AyEFy1KGgtA", "type": { "coding": [ { @@ -157,7 +131,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:6nMZv2pJPKEo82zRsPoCm8_Bzt1S1EZ_AyEFy1KGgtA" } ], "status": "active", @@ -167,9 +143,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -183,10 +157,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:a7Dc9F2riqMjx8rq2NTJLmiF4GBdJSbO4Rtj9kp1njc" + "value": "v0:fixture-key:1:6nMZv2pJPKEo82zRsPoCm8_Bzt1S1EZ_AyEFy1KGgtA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:01837f45-5907-5a36-86c4-e518cc98d3de", "resource": { "resourceType": "Device", @@ -197,8 +173,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:a7Dc9F2riqMjx8rq2NTJLmiF4GBdJSbO4Rtj9kp1njc", "type": { "coding": [ { @@ -206,7 +180,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:a7Dc9F2riqMjx8rq2NTJLmiF4GBdJSbO4Rtj9kp1njc" } ], "status": "active", @@ -230,9 +206,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -241,15 +215,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:W9MmnB630oKz8k7NTcsS7eag8L4yDQnnpOFHDf-m1oo" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:a7Dc9F2riqMjx8rq2NTJLmiF4GBdJSbO4Rtj9kp1njc" } } - ], + ] + }, + { "fullUrl": "urn:uuid:5f6e4358-2bf6-5f07-b679-ac7a5bf91aac", "resource": { "resourceType": "Provenance", @@ -295,8 +271,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:LcqDQSNcMDxugFlPHpH5RFXlEN4jj61vsIebOIwHBus", "type": { "coding": [ { @@ -304,7 +278,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:LcqDQSNcMDxugFlPHpH5RFXlEN4jj61vsIebOIwHBus" } }, "agent": [ @@ -326,7 +302,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:W9MmnB630oKz8k7NTcsS7eag8L4yDQnnpOFHDf-m1oo" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json b/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json index b2122cf6..d761711c 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json +++ b/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:31" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:bOgI-QR3GvHruf1pWLzIRIPn3UomHEkWtri0pIvy0X0" - } - } - ], "fullUrl": "urn:uuid:0286e181-761e-5a07-931f-2e415f5ef041", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:40Iy1wmaK8u1RiYFs3w-vdPd5r_vC9_FOJcZm9L47Ug", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:40Iy1wmaK8u1RiYFs3w-vdPd5r_vC9_FOJcZm9L47Ug" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:bOgI-QR3GvHruf1pWLzIRIPn3UomHEkWtri0pIvy0X0", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:bOgI-QR3GvHruf1pWLzIRIPn3UomHEkWtri0pIvy0X0" } ], "status": "final", @@ -126,9 +102,7 @@ ], "text": "Daily mean" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -137,15 +111,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:TqpEGThlZqYZypD6eQEFZXoaz7En7wSNZ2Phvr0Kigg" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:bOgI-QR3GvHruf1pWLzIRIPn3UomHEkWtri0pIvy0X0" } } - ], + ] + }, + { "fullUrl": "urn:uuid:1c9af85c-9462-5fbb-b19b-619207d49ddc", "resource": { "resourceType": "Device", @@ -156,8 +132,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:TqpEGThlZqYZypD6eQEFZXoaz7En7wSNZ2Phvr0Kigg", "type": { "coding": [ { @@ -165,7 +139,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:TqpEGThlZqYZypD6eQEFZXoaz7En7wSNZ2Phvr0Kigg" } ], "status": "active", @@ -175,9 +151,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -191,10 +165,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:ZZTtK-U3HfYobIKdA52R7jROS-LXygWsb6GS_HKq55I" + "value": "v0:fixture-key:1:TqpEGThlZqYZypD6eQEFZXoaz7En7wSNZ2Phvr0Kigg" } } - ], + ] + }, + { "fullUrl": "urn:uuid:4f0e4237-13f6-5a5d-92af-aa70cea16d8d", "resource": { "resourceType": "Device", @@ -205,8 +181,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:ZZTtK-U3HfYobIKdA52R7jROS-LXygWsb6GS_HKq55I", "type": { "coding": [ { @@ -214,7 +188,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:ZZTtK-U3HfYobIKdA52R7jROS-LXygWsb6GS_HKq55I" } ], "status": "active", @@ -238,9 +214,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -249,15 +223,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:3WWEy8RBZ7hJLE3Mr8nXzyIpx6REeHiokAzs_WIkRP8" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:ZZTtK-U3HfYobIKdA52R7jROS-LXygWsb6GS_HKq55I" } } - ], + ] + }, + { "fullUrl": "urn:uuid:fe869ae8-658e-50c6-a4d5-a3ea19fed148", "resource": { "resourceType": "Provenance", @@ -303,8 +279,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:40Iy1wmaK8u1RiYFs3w-vdPd5r_vC9_FOJcZm9L47Ug", "type": { "coding": [ { @@ -312,7 +286,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:40Iy1wmaK8u1RiYFs3w-vdPd5r_vC9_FOJcZm9L47Ug" } }, "agent": [ @@ -334,7 +310,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:3WWEy8RBZ7hJLE3Mr8nXzyIpx6REeHiokAzs_WIkRP8" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json b/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json index f0c20c80..276b0443 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:48" }, - "type": "collection", "timestamp": "2026-08-21T07:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:ZW-l9DWDo6F8KQtTUw0c0jARFGJw7IVvv_uUABMaLL8" - } - } - ], "fullUrl": "urn:uuid:455c32a1-29f0-51c6-8fbc-99d9b1ffbcd1", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:ZW-l9DWDo6F8KQtTUw0c0jARFGJw7IVvv_uUABMaLL8", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:ZW-l9DWDo6F8KQtTUw0c0jARFGJw7IVvv_uUABMaLL8" } ], "status": "final", @@ -116,9 +92,7 @@ "system": "http://unitsofmeasure.org", "code": "{steps}" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -132,10 +106,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:jLcaQl4VlDZK-a6tYFi6RzphgjIdgSocnwNXiIKnNug" + "value": "v0:fixture-key:1:ZW-l9DWDo6F8KQtTUw0c0jARFGJw7IVvv_uUABMaLL8" } } - ], + ] + }, + { "fullUrl": "urn:uuid:5966d687-aa4f-5965-a4b2-cf84a9c6f248", "resource": { "resourceType": "Observation", @@ -164,8 +140,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc", "type": { "coding": [ { @@ -173,11 +147,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:jLcaQl4VlDZK-a6tYFi6RzphgjIdgSocnwNXiIKnNug", "type": { "coding": [ { @@ -185,7 +159,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:jLcaQl4VlDZK-a6tYFi6RzphgjIdgSocnwNXiIKnNug" } ], "status": "final", @@ -214,9 +190,7 @@ "system": "http://unitsofmeasure.org", "code": "kcal" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -230,10 +204,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:E45pGFqeE8kQDlMSBXc05YscVQdR0EFm3jgZWqrGP4k" + "value": "v0:fixture-key:1:jLcaQl4VlDZK-a6tYFi6RzphgjIdgSocnwNXiIKnNug" } } - ], + ] + }, + { "fullUrl": "urn:uuid:88799d8b-82b2-5ba0-94b5-96c13acd9f8a", "resource": { "resourceType": "Observation", @@ -262,8 +238,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc", "type": { "coding": [ { @@ -271,11 +245,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:E45pGFqeE8kQDlMSBXc05YscVQdR0EFm3jgZWqrGP4k", "type": { "coding": [ { @@ -283,7 +257,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:E45pGFqeE8kQDlMSBXc05YscVQdR0EFm3jgZWqrGP4k" } ], "status": "final", @@ -307,9 +283,7 @@ "system": "http://unitsofmeasure.org", "code": "m" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -318,15 +292,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:P0jj07qi0PyO4vEU1lcP36UlMqKaO-mxMgv1ZKNKBYI" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:E45pGFqeE8kQDlMSBXc05YscVQdR0EFm3jgZWqrGP4k" } } - ], + ] + }, + { "fullUrl": "urn:uuid:d32c76f1-7a31-5342-b7c8-cd9f20064de3", "resource": { "resourceType": "Device", @@ -337,8 +313,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:P0jj07qi0PyO4vEU1lcP36UlMqKaO-mxMgv1ZKNKBYI", "type": { "coding": [ { @@ -346,7 +320,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:P0jj07qi0PyO4vEU1lcP36UlMqKaO-mxMgv1ZKNKBYI" } ], "status": "active", @@ -356,9 +332,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -372,10 +346,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:ERW6WdlhAGDHJ1V_q9pZsma1U6S_NzLxrFsIf4CE-SQ" + "value": "v0:fixture-key:1:P0jj07qi0PyO4vEU1lcP36UlMqKaO-mxMgv1ZKNKBYI" } } - ], + ] + }, + { "fullUrl": "urn:uuid:70d10730-ee46-5c11-b27b-274a19a1c9b8", "resource": { "resourceType": "Device", @@ -386,8 +362,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:ERW6WdlhAGDHJ1V_q9pZsma1U6S_NzLxrFsIf4CE-SQ", "type": { "coding": [ { @@ -395,7 +369,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:ERW6WdlhAGDHJ1V_q9pZsma1U6S_NzLxrFsIf4CE-SQ" } ], "status": "active", @@ -419,9 +395,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -430,15 +404,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:BsM8PuZ48RNmemYKpVpQEZCPDNRPRIzR5GqIBLy8p6g" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:ERW6WdlhAGDHJ1V_q9pZsma1U6S_NzLxrFsIf4CE-SQ" } } - ], + ] + }, + { "fullUrl": "urn:uuid:b4b754f4-c374-5391-8a3e-edf1ab5e7dd5", "resource": { "resourceType": "Provenance", @@ -486,8 +462,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc", "type": { "coding": [ { @@ -495,7 +469,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc" } }, "agent": [ @@ -517,7 +493,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:BsM8PuZ48RNmemYKpVpQEZCPDNRPRIzR5GqIBLy8p6g" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json index 4f46ed3b..77f91540 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:32" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:VkvVjJoPr21ib2OR1ukyFv4PEwmfnfIADgZkrdI0OAY" - } - } - ], "fullUrl": "urn:uuid:8f4caaf7-2b81-5387-aecb-51048bd560df", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:6JYnSRBq-28f00cb2qTI45a4PwviE2ZkDZ0CgfxOBu8", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:6JYnSRBq-28f00cb2qTI45a4PwviE2ZkDZ0CgfxOBu8" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:VkvVjJoPr21ib2OR1ukyFv4PEwmfnfIADgZkrdI0OAY", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:VkvVjJoPr21ib2OR1ukyFv4PEwmfnfIADgZkrdI0OAY" } ], "status": "final", @@ -116,9 +92,7 @@ "system": "http://unitsofmeasure.org", "code": "a" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -127,15 +101,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:h5Rb3MFdNKfsjqqCI-RcuHTVXXud55bMyIae_ByOKKk" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:VkvVjJoPr21ib2OR1ukyFv4PEwmfnfIADgZkrdI0OAY" } } - ], + ] + }, + { "fullUrl": "urn:uuid:bfa210b1-746d-5e96-82a7-0ca4ab299873", "resource": { "resourceType": "Device", @@ -146,8 +122,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:h5Rb3MFdNKfsjqqCI-RcuHTVXXud55bMyIae_ByOKKk", "type": { "coding": [ { @@ -155,7 +129,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:h5Rb3MFdNKfsjqqCI-RcuHTVXXud55bMyIae_ByOKKk" } ], "status": "active", @@ -165,9 +141,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -181,10 +155,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OHeAUXdlghyg_Gho66VlpfMOcbKOwauPDAaCiOlaaDU" + "value": "v0:fixture-key:1:h5Rb3MFdNKfsjqqCI-RcuHTVXXud55bMyIae_ByOKKk" } } - ], + ] + }, + { "fullUrl": "urn:uuid:b7c37e2b-ba62-562d-94e4-7c034814ef4e", "resource": { "resourceType": "Device", @@ -195,8 +171,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OHeAUXdlghyg_Gho66VlpfMOcbKOwauPDAaCiOlaaDU", "type": { "coding": [ { @@ -204,7 +178,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:OHeAUXdlghyg_Gho66VlpfMOcbKOwauPDAaCiOlaaDU" } ], "status": "active", @@ -228,9 +204,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -239,15 +213,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:c_XcuO0rRflq1E3Kny_pHsLUXqP-JCMIfXlxfwArKoo" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:OHeAUXdlghyg_Gho66VlpfMOcbKOwauPDAaCiOlaaDU" } } - ], + ] + }, + { "fullUrl": "urn:uuid:eb84eba4-4718-5bb5-b808-07e09b09e7fa", "resource": { "resourceType": "Provenance", @@ -293,8 +269,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:6JYnSRBq-28f00cb2qTI45a4PwviE2ZkDZ0CgfxOBu8", "type": { "coding": [ { @@ -302,7 +276,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:6JYnSRBq-28f00cb2qTI45a4PwviE2ZkDZ0CgfxOBu8" } }, "agent": [ @@ -324,7 +300,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:c_XcuO0rRflq1E3Kny_pHsLUXqP-JCMIfXlxfwArKoo" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json index a5682cc9..3a30b281 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:33" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:8_DgUefPBkmNsGzXuKk6xRHRGSabA-vhJw_Ts1OaULo" - } - } - ], "fullUrl": "urn:uuid:13fc40fe-24a3-59f6-8182-8674de79f99f", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:fd_6B30JQm1RurdhENC10i8LahBJt8rk4pjXN5m97HA", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:fd_6B30JQm1RurdhENC10i8LahBJt8rk4pjXN5m97HA" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:8_DgUefPBkmNsGzXuKk6xRHRGSabA-vhJw_Ts1OaULo", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:8_DgUefPBkmNsGzXuKk6xRHRGSabA-vhJw_Ts1OaULo" } ], "status": "final", @@ -116,9 +92,7 @@ "system": "http://unitsofmeasure.org", "code": "{score}" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -127,15 +101,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:kNqCR8LM6Ge_iHvz9Zpqo0miCqPAoZpsUY6XQ5cToDw" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:8_DgUefPBkmNsGzXuKk6xRHRGSabA-vhJw_Ts1OaULo" } } - ], + ] + }, + { "fullUrl": "urn:uuid:d4919db0-78eb-549d-b6e6-3b49d2ec7e3b", "resource": { "resourceType": "Device", @@ -146,8 +122,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:kNqCR8LM6Ge_iHvz9Zpqo0miCqPAoZpsUY6XQ5cToDw", "type": { "coding": [ { @@ -155,7 +129,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:kNqCR8LM6Ge_iHvz9Zpqo0miCqPAoZpsUY6XQ5cToDw" } ], "status": "active", @@ -165,9 +141,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -181,10 +155,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:hgLwQrXtczhHU7Bcj5uX7S2DF4vJWnYiwrOzsDboju8" + "value": "v0:fixture-key:1:kNqCR8LM6Ge_iHvz9Zpqo0miCqPAoZpsUY6XQ5cToDw" } } - ], + ] + }, + { "fullUrl": "urn:uuid:a0aaca97-70da-5ccb-9510-dcdb4d5f7088", "resource": { "resourceType": "Device", @@ -195,8 +171,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:hgLwQrXtczhHU7Bcj5uX7S2DF4vJWnYiwrOzsDboju8", "type": { "coding": [ { @@ -204,7 +178,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:hgLwQrXtczhHU7Bcj5uX7S2DF4vJWnYiwrOzsDboju8" } ], "status": "active", @@ -228,9 +204,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -239,15 +213,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:UwgSxMJ26fI73RjvXWouraGX9p0_Il45jxNbZK_aHaM" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:hgLwQrXtczhHU7Bcj5uX7S2DF4vJWnYiwrOzsDboju8" } } - ], + ] + }, + { "fullUrl": "urn:uuid:669a50f2-373b-5d47-a5af-fc52ba9c11c9", "resource": { "resourceType": "Provenance", @@ -293,8 +269,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:fd_6B30JQm1RurdhENC10i8LahBJt8rk4pjXN5m97HA", "type": { "coding": [ { @@ -302,7 +276,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:fd_6B30JQm1RurdhENC10i8LahBJt8rk4pjXN5m97HA" } }, "agent": [ @@ -324,7 +300,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:UwgSxMJ26fI73RjvXWouraGX9p0_Il45jxNbZK_aHaM" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json b/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json index 2ac4a5f6..e19aded1 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json +++ b/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:53" }, - "type": "collection", "timestamp": "2026-08-22T12:02:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:oEl7Uhouma2Mj5_8vjJxzY3wJXaIO8DnSJzEeOhy2o0" - } - } - ], "fullUrl": "urn:uuid:8ad483fc-efd8-53ad-99e6-7dfbf668b1c1", "resource": { "resourceType": "Device", @@ -48,8 +24,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:oEl7Uhouma2Mj5_8vjJxzY3wJXaIO8DnSJzEeOhy2o0", "type": { "coding": [ { @@ -57,7 +31,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:oEl7Uhouma2Mj5_8vjJxzY3wJXaIO8DnSJzEeOhy2o0" } ], "status": "active", @@ -81,9 +57,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -92,15 +66,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:retraction-provenance:0:gv9DL_lwJx61nEPjo2CzyXAPyDpheZfgahVh9IKQ5kk" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:oEl7Uhouma2Mj5_8vjJxzY3wJXaIO8DnSJzEeOhy2o0" } } - ], + ] + }, + { "fullUrl": "urn:uuid:ada84fca-188c-5dd5-90ba-27584527ef2f", "resource": { "resourceType": "Provenance", @@ -119,8 +95,6 @@ ], "type": "Device", "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:U1GXWdV08ZaO2_cubyu1oaGFDXhnzLx8h_JwZVf6hUU", "type": { "coding": [ { @@ -128,7 +102,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:U1GXWdV08ZaO2_cubyu1oaGFDXhnzLx8h_JwZVf6hUU" } }, { @@ -140,8 +116,6 @@ ], "type": "Observation", "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:LycACgbJZx-uRos8zb-0w0PEXMdM7LoW4Sx1IvbEOC0", "type": { "coding": [ { @@ -149,7 +123,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:LycACgbJZx-uRos8zb-0w0PEXMdM7LoW4Sx1IvbEOC0" } } ], @@ -187,8 +163,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:cBsbfJ2FTYUguk5OcwFuDMb03zuen7zrG5xlFAniFTo", "type": { "coding": [ { @@ -196,12 +170,38 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:cBsbfJ2FTYUguk5OcwFuDMb03zuen7zrG5xlFAniFTo" } } } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:retraction-provenance:0:gv9DL_lwJx61nEPjo2CzyXAPyDpheZfgahVh9IKQ5kk" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json b/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json index 91174ba9..7f4bbf41 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json +++ b/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:54" }, - "type": "collection", "timestamp": "2026-08-22T13:02:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:nEuRqgji46KZl6Zd4aPUEjeIbRhR7mJ96dA0q9RmA4U" - } - } - ], "fullUrl": "urn:uuid:f3659235-64ff-5681-9783-5e712a989f97", "resource": { "resourceType": "Device", @@ -48,8 +24,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:nEuRqgji46KZl6Zd4aPUEjeIbRhR7mJ96dA0q9RmA4U", "type": { "coding": [ { @@ -57,7 +31,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:nEuRqgji46KZl6Zd4aPUEjeIbRhR7mJ96dA0q9RmA4U" } ], "status": "active", @@ -81,9 +57,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -92,15 +66,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:retraction-provenance:0:uOqh_hD3XZxlouavDYtKr6PhYACOazZDgCcBLqQJjcI" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:nEuRqgji46KZl6Zd4aPUEjeIbRhR7mJ96dA0q9RmA4U" } } - ], + ] + }, + { "fullUrl": "urn:uuid:d7f49bdc-acce-543a-bf93-154117fe1854", "resource": { "resourceType": "Provenance", @@ -119,8 +95,6 @@ ], "type": "Device", "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uocYwddFVtlKSSu89sdrCnhx7B1Zj4lX7iNLzaZjvAA", "type": { "coding": [ { @@ -128,7 +102,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:uocYwddFVtlKSSu89sdrCnhx7B1Zj4lX7iNLzaZjvAA" } }, { @@ -140,8 +116,6 @@ ], "type": "DocumentReference", "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:w7o_lcZdhv2y5mj96CWnefbS73I3tPoRvuSQG7Vaqao", "type": { "coding": [ { @@ -149,7 +123,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:w7o_lcZdhv2y5mj96CWnefbS73I3tPoRvuSQG7Vaqao" } } ], @@ -187,8 +163,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:Qm28JqWMxMvl2dsco1gdTLvUyKdg3S8qfrEX_4Z19KY", "type": { "coding": [ { @@ -196,12 +170,38 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:Qm28JqWMxMvl2dsco1gdTLvUyKdg3S8qfrEX_4Z19KY" } } } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:retraction-provenance:0:uOqh_hD3XZxlouavDYtKr6PhYACOazZDgCcBLqQJjcI" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json index 12b0aa04..c3391ff1 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:35" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:g9D8uYiqpZq0BrDkaP2tElDxS1h4zOwQ3OHIBNKXobg" - } - } - ], "fullUrl": "urn:uuid:3f31daca-6852-511c-8961-38d2cfba8936", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:6VQPvHniXH4sNDqhmZ0DqtG129Xl6pOZ353Kdqg0SwA", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:6VQPvHniXH4sNDqhmZ0DqtG129Xl6pOZ353Kdqg0SwA" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:g9D8uYiqpZq0BrDkaP2tElDxS1h4zOwQ3OHIBNKXobg", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:g9D8uYiqpZq0BrDkaP2tElDxS1h4zOwQ3OHIBNKXobg" } ], "status": "final", @@ -108,9 +84,7 @@ "system": "http://unitsofmeasure.org", "code": "kg" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -119,15 +93,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:cgk--UOImI_WU47l-R2BGI_H4AqhrIEgrHXPC7dlUYA" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:g9D8uYiqpZq0BrDkaP2tElDxS1h4zOwQ3OHIBNKXobg" } } - ], + ] + }, + { "fullUrl": "urn:uuid:0d8fb81e-efa0-583e-be2f-7d3fd70cf98b", "resource": { "resourceType": "Device", @@ -138,8 +114,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:cgk--UOImI_WU47l-R2BGI_H4AqhrIEgrHXPC7dlUYA", "type": { "coding": [ { @@ -147,7 +121,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:cgk--UOImI_WU47l-R2BGI_H4AqhrIEgrHXPC7dlUYA" } ], "status": "active", @@ -157,9 +133,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -173,10 +147,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:0qUilPLzuJbQGUboPRBuLJQsLcWYkqCI7t59qy7DoGA" + "value": "v0:fixture-key:1:cgk--UOImI_WU47l-R2BGI_H4AqhrIEgrHXPC7dlUYA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:16b24c42-c731-5ba7-8859-8c288834f0d4", "resource": { "resourceType": "Device", @@ -187,8 +163,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:0qUilPLzuJbQGUboPRBuLJQsLcWYkqCI7t59qy7DoGA", "type": { "coding": [ { @@ -196,7 +170,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:0qUilPLzuJbQGUboPRBuLJQsLcWYkqCI7t59qy7DoGA" } ], "status": "active", @@ -220,9 +196,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -231,15 +205,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:SO0lcltW7S-39s0UkVo_0Fu7iP7CJPFYIHdvHhS72do" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:0qUilPLzuJbQGUboPRBuLJQsLcWYkqCI7t59qy7DoGA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:fa17f5ee-019b-5f79-902f-1f8862771964", "resource": { "resourceType": "Provenance", @@ -285,8 +261,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:6VQPvHniXH4sNDqhmZ0DqtG129Xl6pOZ353Kdqg0SwA", "type": { "coding": [ { @@ -294,7 +268,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:6VQPvHniXH4sNDqhmZ0DqtG129Xl6pOZ353Kdqg0SwA" } }, "agent": [ @@ -316,7 +292,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:SO0lcltW7S-39s0UkVo_0Fu7iP7CJPFYIHdvHhS72do" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json b/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json index 416fec15..d7b69af8 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:42" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:up2yrUsCU_7Cld1H_MYyI3dIGA9tbFvZ-lMm4M4tXVM" - } - } - ], "fullUrl": "urn:uuid:f04531e4-5996-5afd-858d-ad251ceb62b5", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:m1wV9ADh9_iaYO3rZmHmqYvvPh96-Fd9keJWZlCvf8Y", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:m1wV9ADh9_iaYO3rZmHmqYvvPh96-Fd9keJWZlCvf8Y" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:up2yrUsCU_7Cld1H_MYyI3dIGA9tbFvZ-lMm4M4tXVM", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:up2yrUsCU_7Cld1H_MYyI3dIGA9tbFvZ-lMm4M4tXVM" } ], "status": "final", @@ -108,9 +84,7 @@ "system": "http://unitsofmeasure.org", "code": "ms" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -119,15 +93,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:t-vS893OvYOMIWpKbtoHlnxyBaAItckjwQUpOY_BSGM" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:up2yrUsCU_7Cld1H_MYyI3dIGA9tbFvZ-lMm4M4tXVM" } } - ], + ] + }, + { "fullUrl": "urn:uuid:0ffe260f-79f2-5f9d-a443-ad1a32aba472", "resource": { "resourceType": "Device", @@ -138,8 +114,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:t-vS893OvYOMIWpKbtoHlnxyBaAItckjwQUpOY_BSGM", "type": { "coding": [ { @@ -147,7 +121,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:t-vS893OvYOMIWpKbtoHlnxyBaAItckjwQUpOY_BSGM" } ], "status": "active", @@ -157,9 +133,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -173,10 +147,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:GjU_pI0bDFWbiQ0MOJBOCBXMM0COVm2SSbC4g8zawJ8" + "value": "v0:fixture-key:1:t-vS893OvYOMIWpKbtoHlnxyBaAItckjwQUpOY_BSGM" } } - ], + ] + }, + { "fullUrl": "urn:uuid:f31053bd-69da-509c-a8d7-d787b331d2a4", "resource": { "resourceType": "Device", @@ -187,8 +163,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:GjU_pI0bDFWbiQ0MOJBOCBXMM0COVm2SSbC4g8zawJ8", "type": { "coding": [ { @@ -196,7 +170,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:GjU_pI0bDFWbiQ0MOJBOCBXMM0COVm2SSbC4g8zawJ8" } ], "status": "active", @@ -220,9 +196,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -231,15 +205,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:PxaVHTEjWlHBji7_0S-rbJCMbGjxFbGCpfaYwTYKs_E" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:GjU_pI0bDFWbiQ0MOJBOCBXMM0COVm2SSbC4g8zawJ8" } } - ], + ] + }, + { "fullUrl": "urn:uuid:a459d197-d6f7-58fc-a973-98d610090925", "resource": { "resourceType": "Provenance", @@ -285,8 +261,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:m1wV9ADh9_iaYO3rZmHmqYvvPh96-Fd9keJWZlCvf8Y", "type": { "coding": [ { @@ -294,7 +268,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:m1wV9ADh9_iaYO3rZmHmqYvvPh96-Fd9keJWZlCvf8Y" } }, "agent": [ @@ -316,7 +292,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:PxaVHTEjWlHBji7_0S-rbJCMbGjxFbGCpfaYwTYKs_E" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json index a9ca410d..2d9c9cd0 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:45" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:ldxbCxa9ISJLN7kax9FiE80vGiw0WHIUyXPQZd3Sk4E" - } - } - ], "fullUrl": "urn:uuid:6c02980d-5df9-5a37-a030-a8599d758291", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:1HgX55BzSSTYkJN89C8SXc1c7-w6cI0mbCGaekNC1yw", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:1HgX55BzSSTYkJN89C8SXc1c7-w6cI0mbCGaekNC1yw" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:ldxbCxa9ISJLN7kax9FiE80vGiw0WHIUyXPQZd3Sk4E", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:ldxbCxa9ISJLN7kax9FiE80vGiw0WHIUyXPQZd3Sk4E" } ], "status": "final", @@ -113,9 +89,7 @@ "system": "http://unitsofmeasure.org", "code": "kg" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -124,15 +98,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Sk7a5vUo5Ke0h-wCaq8pkWlwBEg_lpUqKi0tbxwRpAg" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:ldxbCxa9ISJLN7kax9FiE80vGiw0WHIUyXPQZd3Sk4E" } } - ], + ] + }, + { "fullUrl": "urn:uuid:f86775dd-e51c-58bc-8b40-54c3a4c2704e", "resource": { "resourceType": "Device", @@ -143,8 +119,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Sk7a5vUo5Ke0h-wCaq8pkWlwBEg_lpUqKi0tbxwRpAg", "type": { "coding": [ { @@ -152,7 +126,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:Sk7a5vUo5Ke0h-wCaq8pkWlwBEg_lpUqKi0tbxwRpAg" } ], "status": "active", @@ -162,9 +138,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -178,10 +152,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:XfFTZ3nhXBEndRIdGRL2pS4gmMMzLXKGUxFQz81nb8w" + "value": "v0:fixture-key:1:Sk7a5vUo5Ke0h-wCaq8pkWlwBEg_lpUqKi0tbxwRpAg" } } - ], + ] + }, + { "fullUrl": "urn:uuid:1fe1f299-5178-556e-b4d9-6eec0d8d3ac9", "resource": { "resourceType": "Device", @@ -192,8 +168,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:XfFTZ3nhXBEndRIdGRL2pS4gmMMzLXKGUxFQz81nb8w", "type": { "coding": [ { @@ -201,7 +175,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:XfFTZ3nhXBEndRIdGRL2pS4gmMMzLXKGUxFQz81nb8w" } ], "status": "active", @@ -225,9 +201,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -236,15 +210,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:oG1YrkySMKuoFgaHhPsjFpaaadNzDN512YXklTAqRAs" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:XfFTZ3nhXBEndRIdGRL2pS4gmMMzLXKGUxFQz81nb8w" } } - ], + ] + }, + { "fullUrl": "urn:uuid:c9daa5d4-de63-5373-9f0c-1dca919aef51", "resource": { "resourceType": "Provenance", @@ -290,8 +266,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:1HgX55BzSSTYkJN89C8SXc1c7-w6cI0mbCGaekNC1yw", "type": { "coding": [ { @@ -299,7 +273,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:1HgX55BzSSTYkJN89C8SXc1c7-w6cI0mbCGaekNC1yw" } }, "agent": [ @@ -321,7 +297,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:oG1YrkySMKuoFgaHhPsjFpaaadNzDN512YXklTAqRAs" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json index a5189179..c89f50ac 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:46" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:AH-H6ncy4X1z2tEipFNSrXuHFuFP3-ud6YhWXKipRto" - } - } - ], "fullUrl": "urn:uuid:1ba6631d-8d9c-500d-b5e7-77d93c149d3a", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:QPmTxHXDUmbQmn5vhLVtyS_SyfCzJCTm93c03OxH-2o", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:QPmTxHXDUmbQmn5vhLVtyS_SyfCzJCTm93c03OxH-2o" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:AH-H6ncy4X1z2tEipFNSrXuHFuFP3-ud6YhWXKipRto", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:AH-H6ncy4X1z2tEipFNSrXuHFuFP3-ud6YhWXKipRto" } ], "status": "final", @@ -113,9 +89,7 @@ "system": "http://unitsofmeasure.org", "code": "kg" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -124,15 +98,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:B2gFdvvpWQxTB70sqX13q51Ty66_l_GDk-P3YrdDiSs" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:AH-H6ncy4X1z2tEipFNSrXuHFuFP3-ud6YhWXKipRto" } } - ], + ] + }, + { "fullUrl": "urn:uuid:40a17b86-d18f-54cb-977c-d75351a7919d", "resource": { "resourceType": "Device", @@ -143,8 +119,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:B2gFdvvpWQxTB70sqX13q51Ty66_l_GDk-P3YrdDiSs", "type": { "coding": [ { @@ -152,7 +126,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:B2gFdvvpWQxTB70sqX13q51Ty66_l_GDk-P3YrdDiSs" } ], "status": "active", @@ -162,9 +138,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -178,10 +152,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:gCGVDG_iVGF7B6ZmWIN4DO4GRXcpOG-nyuXGpSqWL7g" + "value": "v0:fixture-key:1:B2gFdvvpWQxTB70sqX13q51Ty66_l_GDk-P3YrdDiSs" } } - ], + ] + }, + { "fullUrl": "urn:uuid:99db234c-97c6-5d64-91cf-41f4375d4bbc", "resource": { "resourceType": "Device", @@ -192,8 +168,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:gCGVDG_iVGF7B6ZmWIN4DO4GRXcpOG-nyuXGpSqWL7g", "type": { "coding": [ { @@ -201,7 +175,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:gCGVDG_iVGF7B6ZmWIN4DO4GRXcpOG-nyuXGpSqWL7g" } ], "status": "active", @@ -225,9 +201,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -236,15 +210,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:L5XECi811lvx0O_j7row191VRz0FfxBuzkQCjFHewos" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:gCGVDG_iVGF7B6ZmWIN4DO4GRXcpOG-nyuXGpSqWL7g" } } - ], + ] + }, + { "fullUrl": "urn:uuid:6646ef38-4230-5ed4-aa3b-b6b45e77d61f", "resource": { "resourceType": "Provenance", @@ -290,8 +266,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:QPmTxHXDUmbQmn5vhLVtyS_SyfCzJCTm93c03OxH-2o", "type": { "coding": [ { @@ -299,7 +273,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:QPmTxHXDUmbQmn5vhLVtyS_SyfCzJCTm93c03OxH-2o" } }, "agent": [ @@ -321,7 +297,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:L5XECi811lvx0O_j7row191VRz0FfxBuzkQCjFHewos" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json index edf66d09..5cf50822 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:36" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:dmuPKjctx8XrjSJddZ2tPQXXnDkWrfZLZLUkkvUTLKg" - } - } - ], "fullUrl": "urn:uuid:30badcd5-097e-5e48-92d7-d24d96c1e1dd", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:Eh9FyyObehDndCz4NWKS92aPKvIf3zJvsPAdTGODtm0", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:Eh9FyyObehDndCz4NWKS92aPKvIf3zJvsPAdTGODtm0" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:dmuPKjctx8XrjSJddZ2tPQXXnDkWrfZLZLUkkvUTLKg", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:dmuPKjctx8XrjSJddZ2tPQXXnDkWrfZLZLUkkvUTLKg" } ], "status": "final", @@ -108,9 +84,7 @@ "system": "http://unitsofmeasure.org", "code": "kg" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -119,15 +93,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OVHkuQ0ProM33_M1f9lTxHrbw8xr3WsTWa9lwqluClA" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:dmuPKjctx8XrjSJddZ2tPQXXnDkWrfZLZLUkkvUTLKg" } } - ], + ] + }, + { "fullUrl": "urn:uuid:f4e6c723-efec-5fe6-9159-ce76a2ae15dd", "resource": { "resourceType": "Device", @@ -138,8 +114,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OVHkuQ0ProM33_M1f9lTxHrbw8xr3WsTWa9lwqluClA", "type": { "coding": [ { @@ -147,7 +121,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:OVHkuQ0ProM33_M1f9lTxHrbw8xr3WsTWa9lwqluClA" } ], "status": "active", @@ -157,9 +133,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -173,10 +147,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:hsOMQ8T6sLQBqj0qrt0InL91Oy3nF8DyD-AmaOzX97Y" + "value": "v0:fixture-key:1:OVHkuQ0ProM33_M1f9lTxHrbw8xr3WsTWa9lwqluClA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:83c978cc-422e-5180-b430-2fdce3eb6604", "resource": { "resourceType": "Device", @@ -187,8 +163,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:hsOMQ8T6sLQBqj0qrt0InL91Oy3nF8DyD-AmaOzX97Y", "type": { "coding": [ { @@ -196,7 +170,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:hsOMQ8T6sLQBqj0qrt0InL91Oy3nF8DyD-AmaOzX97Y" } ], "status": "active", @@ -220,9 +196,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -231,15 +205,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:oDUf94G4-TotGjhBmFnu7C5iCGVf05D_KytEY3OOxwg" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:hsOMQ8T6sLQBqj0qrt0InL91Oy3nF8DyD-AmaOzX97Y" } } - ], + ] + }, + { "fullUrl": "urn:uuid:145c28e1-8e1c-558e-b7ba-ee0fe6f9534a", "resource": { "resourceType": "Provenance", @@ -285,8 +261,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:Eh9FyyObehDndCz4NWKS92aPKvIf3zJvsPAdTGODtm0", "type": { "coding": [ { @@ -294,7 +268,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:Eh9FyyObehDndCz4NWKS92aPKvIf3zJvsPAdTGODtm0" } }, "agent": [ @@ -316,7 +292,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:oDUf94G4-TotGjhBmFnu7C5iCGVf05D_KytEY3OOxwg" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json b/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json index 4abc24f2..4cca811f 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:40" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:m3zt6QNnSRhBsJXMHQpVURB9vlHwD7YEAZ29DSSj6Nk" - } - } - ], "fullUrl": "urn:uuid:849c5b45-d32b-59c4-b7ba-d61312d9ef27", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:gPh7mylmekrCE9UJitdYyUQi_Tj1QXwbb8wHwoB9GYI", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:gPh7mylmekrCE9UJitdYyUQi_Tj1QXwbb8wHwoB9GYI" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:m3zt6QNnSRhBsJXMHQpVURB9vlHwD7YEAZ29DSSj6Nk", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:m3zt6QNnSRhBsJXMHQpVURB9vlHwD7YEAZ29DSSj6Nk" } ], "status": "final", @@ -108,9 +84,7 @@ "system": "http://unitsofmeasure.org", "code": "ms" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -119,15 +93,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:__ZuL0w8sk5Yz3yTgI7SC4XK9w44B9HXUj91cm1FsWQ" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:m3zt6QNnSRhBsJXMHQpVURB9vlHwD7YEAZ29DSSj6Nk" } } - ], + ] + }, + { "fullUrl": "urn:uuid:6262253c-dd07-5d04-8579-92fbf81533a7", "resource": { "resourceType": "Device", @@ -138,8 +114,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:__ZuL0w8sk5Yz3yTgI7SC4XK9w44B9HXUj91cm1FsWQ", "type": { "coding": [ { @@ -147,7 +121,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:__ZuL0w8sk5Yz3yTgI7SC4XK9w44B9HXUj91cm1FsWQ" } ], "status": "active", @@ -157,9 +133,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -173,10 +147,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:V8GTrHviGlTvyHN2OgpQSTynWLcnM-QC0_--aCfssRE" + "value": "v0:fixture-key:1:__ZuL0w8sk5Yz3yTgI7SC4XK9w44B9HXUj91cm1FsWQ" } } - ], + ] + }, + { "fullUrl": "urn:uuid:e21dbf5c-7238-597a-b201-f6d7f835d951", "resource": { "resourceType": "Device", @@ -187,8 +163,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:V8GTrHviGlTvyHN2OgpQSTynWLcnM-QC0_--aCfssRE", "type": { "coding": [ { @@ -196,7 +170,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:V8GTrHviGlTvyHN2OgpQSTynWLcnM-QC0_--aCfssRE" } ], "status": "active", @@ -220,9 +196,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -231,15 +205,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:haG6QJYpWmGVbQuTrZ6uNIIfwXKNK61ZDcIfsiHbXo4" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:V8GTrHviGlTvyHN2OgpQSTynWLcnM-QC0_--aCfssRE" } } - ], + ] + }, + { "fullUrl": "urn:uuid:f7d48e17-92fe-51ad-a989-96c372ef8e88", "resource": { "resourceType": "Provenance", @@ -285,8 +261,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:gPh7mylmekrCE9UJitdYyUQi_Tj1QXwbb8wHwoB9GYI", "type": { "coding": [ { @@ -294,7 +268,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:gPh7mylmekrCE9UJitdYyUQi_Tj1QXwbb8wHwoB9GYI" } }, "agent": [ @@ -316,7 +292,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:haG6QJYpWmGVbQuTrZ6uNIIfwXKNK61ZDcIfsiHbXo4" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json b/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json index 13561dc5..163bc646 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:39" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:YrLEi4lkIP19yRHhjL0N7US8Uho6xCZYLkZOqcq9CWY" - } - } - ], "fullUrl": "urn:uuid:d84ea885-e118-52f2-90b1-925f92afb702", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:9BdypFvzThLDwoZaAkXJoWLWqDNqaNic5oAhD6zAb6w", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:9BdypFvzThLDwoZaAkXJoWLWqDNqaNic5oAhD6zAb6w" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:YrLEi4lkIP19yRHhjL0N7US8Uho6xCZYLkZOqcq9CWY", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:YrLEi4lkIP19yRHhjL0N7US8Uho6xCZYLkZOqcq9CWY" } ], "status": "final", @@ -108,9 +84,7 @@ "system": "http://unitsofmeasure.org", "code": "ms" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -119,15 +93,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:AtOWrf7z6ZVcyMJNO_vx-fIUBZUytKMW-PT_x0I2CiE" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:YrLEi4lkIP19yRHhjL0N7US8Uho6xCZYLkZOqcq9CWY" } } - ], + ] + }, + { "fullUrl": "urn:uuid:41ced2c7-02a2-57ec-aed8-d996f96f0345", "resource": { "resourceType": "Device", @@ -138,8 +114,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:AtOWrf7z6ZVcyMJNO_vx-fIUBZUytKMW-PT_x0I2CiE", "type": { "coding": [ { @@ -147,7 +121,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:AtOWrf7z6ZVcyMJNO_vx-fIUBZUytKMW-PT_x0I2CiE" } ], "status": "active", @@ -157,9 +133,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -173,10 +147,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:lp2Cnde-8YMwJBqwNUzgr5t4k8z_7ybfORUJagYVpEE" + "value": "v0:fixture-key:1:AtOWrf7z6ZVcyMJNO_vx-fIUBZUytKMW-PT_x0I2CiE" } } - ], + ] + }, + { "fullUrl": "urn:uuid:a31e7d33-eec1-55a9-ba76-f7a2e115a4de", "resource": { "resourceType": "Device", @@ -187,8 +163,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:lp2Cnde-8YMwJBqwNUzgr5t4k8z_7ybfORUJagYVpEE", "type": { "coding": [ { @@ -196,7 +170,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:lp2Cnde-8YMwJBqwNUzgr5t4k8z_7ybfORUJagYVpEE" } ], "status": "active", @@ -220,9 +196,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -231,15 +205,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:8ZRVTj4Udaeytn6DxmTcyAWuzCQGrpgFIJBmH4tDES4" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:lp2Cnde-8YMwJBqwNUzgr5t4k8z_7ybfORUJagYVpEE" } } - ], + ] + }, + { "fullUrl": "urn:uuid:0b45d042-6862-55bd-a464-fc2679ff3020", "resource": { "resourceType": "Provenance", @@ -285,8 +261,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:9BdypFvzThLDwoZaAkXJoWLWqDNqaNic5oAhD6zAb6w", "type": { "coding": [ { @@ -294,7 +268,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:9BdypFvzThLDwoZaAkXJoWLWqDNqaNic5oAhD6zAb6w" } }, "agent": [ @@ -316,7 +292,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:8ZRVTj4Udaeytn6DxmTcyAWuzCQGrpgFIJBmH4tDES4" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json b/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json index 71f39817..adba6f93 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:41" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:AbiOF6Y3Hvecmdt0vcY9O-tpBBvhuvJVPLPWe2VzN2s" - } - } - ], "fullUrl": "urn:uuid:2fbda5c3-1ad0-59b9-9331-388671178ead", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:SiSGZdCnIwML3G4TrUiYwjaz8Db2kjtvOJWFrQ7Y9v8", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:SiSGZdCnIwML3G4TrUiYwjaz8Db2kjtvOJWFrQ7Y9v8" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:AbiOF6Y3Hvecmdt0vcY9O-tpBBvhuvJVPLPWe2VzN2s", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:AbiOF6Y3Hvecmdt0vcY9O-tpBBvhuvJVPLPWe2VzN2s" } ], "status": "final", @@ -108,9 +84,7 @@ "system": "http://unitsofmeasure.org", "code": "ms" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -119,15 +93,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:99bYfKq38HdU_3V1w8_trVXSCQzB2ywUCIZBRKBwL6M" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:AbiOF6Y3Hvecmdt0vcY9O-tpBBvhuvJVPLPWe2VzN2s" } } - ], + ] + }, + { "fullUrl": "urn:uuid:2138c578-bb7d-5e64-a620-5bcc51797536", "resource": { "resourceType": "Device", @@ -138,8 +114,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:99bYfKq38HdU_3V1w8_trVXSCQzB2ywUCIZBRKBwL6M", "type": { "coding": [ { @@ -147,7 +121,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:99bYfKq38HdU_3V1w8_trVXSCQzB2ywUCIZBRKBwL6M" } ], "status": "active", @@ -157,9 +133,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -173,10 +147,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:luW2Yct587KQwti7mnsRdpVqPXZUeQXyyWRRwjH-Ogg" + "value": "v0:fixture-key:1:99bYfKq38HdU_3V1w8_trVXSCQzB2ywUCIZBRKBwL6M" } } - ], + ] + }, + { "fullUrl": "urn:uuid:d46c3738-eda6-5a17-91b3-03bf1806962f", "resource": { "resourceType": "Device", @@ -187,8 +163,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:luW2Yct587KQwti7mnsRdpVqPXZUeQXyyWRRwjH-Ogg", "type": { "coding": [ { @@ -196,7 +170,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:luW2Yct587KQwti7mnsRdpVqPXZUeQXyyWRRwjH-Ogg" } ], "status": "active", @@ -220,9 +196,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -231,15 +205,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:kyJnRRrDXnYDxIiriQDYk8XXJXNYwEwA6I9tMzf_AmQ" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:luW2Yct587KQwti7mnsRdpVqPXZUeQXyyWRRwjH-Ogg" } } - ], + ] + }, + { "fullUrl": "urn:uuid:e246c232-e0fe-54df-9292-f2487cf88fe7", "resource": { "resourceType": "Provenance", @@ -285,8 +261,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:SiSGZdCnIwML3G4TrUiYwjaz8Db2kjtvOJWFrQ7Y9v8", "type": { "coding": [ { @@ -294,7 +268,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:SiSGZdCnIwML3G4TrUiYwjaz8Db2kjtvOJWFrQ7Y9v8" } }, "agent": [ @@ -316,7 +292,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:kyJnRRrDXnYDxIiriQDYk8XXJXNYwEwA6I9tMzf_AmQ" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json index e9b0313c..8df04984 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:38" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:UIpn4q5L_fH_yipQV_n3rk8PKcagpVghtl1zhOUB53c" - } - } - ], "fullUrl": "urn:uuid:14899158-9250-565e-bebb-5eb5633aa2e4", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:ZxvReRdSivoUK5JDcmAqUlMR3LUZJvTNscfXuIReUBY", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:ZxvReRdSivoUK5JDcmAqUlMR3LUZJvTNscfXuIReUBY" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:UIpn4q5L_fH_yipQV_n3rk8PKcagpVghtl1zhOUB53c", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:UIpn4q5L_fH_yipQV_n3rk8PKcagpVghtl1zhOUB53c" } ], "status": "final", @@ -115,9 +91,7 @@ } ] } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -126,15 +100,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:UClwZV7svmpSmN8FNXWl-VfiXDrzMfqjK8sxfS26pyQ" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:UIpn4q5L_fH_yipQV_n3rk8PKcagpVghtl1zhOUB53c" } } - ], + ] + }, + { "fullUrl": "urn:uuid:4f1da7df-5e4e-5c3b-b239-c7ede7493c89", "resource": { "resourceType": "Device", @@ -145,8 +121,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:UClwZV7svmpSmN8FNXWl-VfiXDrzMfqjK8sxfS26pyQ", "type": { "coding": [ { @@ -154,7 +128,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:UClwZV7svmpSmN8FNXWl-VfiXDrzMfqjK8sxfS26pyQ" } ], "status": "active", @@ -164,9 +140,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -180,10 +154,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:O9Tw3fu-swdRGFWTe5R2t-hWL8JDHTm3UJGoSoxnn10" + "value": "v0:fixture-key:1:UClwZV7svmpSmN8FNXWl-VfiXDrzMfqjK8sxfS26pyQ" } } - ], + ] + }, + { "fullUrl": "urn:uuid:aabcc5f8-104d-53a7-a55f-91fabe26d699", "resource": { "resourceType": "Device", @@ -194,8 +170,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:O9Tw3fu-swdRGFWTe5R2t-hWL8JDHTm3UJGoSoxnn10", "type": { "coding": [ { @@ -203,7 +177,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:O9Tw3fu-swdRGFWTe5R2t-hWL8JDHTm3UJGoSoxnn10" } ], "status": "active", @@ -227,9 +203,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -238,15 +212,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:FY6d5Pc23vpiqS2qy2e5rvA4T6UzWja_ZjZp_nT80rQ" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:O9Tw3fu-swdRGFWTe5R2t-hWL8JDHTm3UJGoSoxnn10" } } - ], + ] + }, + { "fullUrl": "urn:uuid:bba7b490-1564-5e5b-9927-6b7e5b4cc63d", "resource": { "resourceType": "Provenance", @@ -292,8 +268,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:ZxvReRdSivoUK5JDcmAqUlMR3LUZJvTNscfXuIReUBY", "type": { "coding": [ { @@ -301,7 +275,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:ZxvReRdSivoUK5JDcmAqUlMR3LUZJvTNscfXuIReUBY" } }, "agent": [ @@ -323,7 +299,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:FY6d5Pc23vpiqS2qy2e5rvA4T6UzWja_ZjZp_nT80rQ" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json index 38911ac9..595cb9f7 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:43" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:DhzbFybuD21ODCiEK2DB-fCJUfF3OOYQFk3UHpp7GSE" - } - } - ], "fullUrl": "urn:uuid:5b1a9420-aa1a-55f3-ac61-2f721d0540d6", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:OUDYL-JBwPu9y4Z1U7Ql1-9C2TShwD4chzmFTmnrxNs", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:OUDYL-JBwPu9y4Z1U7Ql1-9C2TShwD4chzmFTmnrxNs" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:DhzbFybuD21ODCiEK2DB-fCJUfF3OOYQFk3UHpp7GSE", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:DhzbFybuD21ODCiEK2DB-fCJUfF3OOYQFk3UHpp7GSE" } ], "status": "final", @@ -115,9 +91,7 @@ } ] } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -126,15 +100,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:VOCwn-nw7Eu3UMotEURLN4inM8fMv7T9-_SeRWDjisQ" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:DhzbFybuD21ODCiEK2DB-fCJUfF3OOYQFk3UHpp7GSE" } } - ], + ] + }, + { "fullUrl": "urn:uuid:731ad91c-6e4e-56ad-b35b-cf13bcd2a5f9", "resource": { "resourceType": "Device", @@ -145,8 +121,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:VOCwn-nw7Eu3UMotEURLN4inM8fMv7T9-_SeRWDjisQ", "type": { "coding": [ { @@ -154,7 +128,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:VOCwn-nw7Eu3UMotEURLN4inM8fMv7T9-_SeRWDjisQ" } ], "status": "active", @@ -164,9 +140,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -180,10 +154,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:vkwcQexNNJMwlm8JYiVoBTX1-5A5n6pyZS4MS9TugPQ" + "value": "v0:fixture-key:1:VOCwn-nw7Eu3UMotEURLN4inM8fMv7T9-_SeRWDjisQ" } } - ], + ] + }, + { "fullUrl": "urn:uuid:2af9b110-443b-5976-af32-24f06b43b61a", "resource": { "resourceType": "Device", @@ -194,8 +170,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:vkwcQexNNJMwlm8JYiVoBTX1-5A5n6pyZS4MS9TugPQ", "type": { "coding": [ { @@ -203,7 +177,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:vkwcQexNNJMwlm8JYiVoBTX1-5A5n6pyZS4MS9TugPQ" } ], "status": "active", @@ -227,9 +203,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -238,15 +212,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:VVL8mOlyjsCfZVr7MZV0ahVYEH5geZ52CSvU8dwBu74" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:vkwcQexNNJMwlm8JYiVoBTX1-5A5n6pyZS4MS9TugPQ" } } - ], + ] + }, + { "fullUrl": "urn:uuid:d36943e1-6026-5e6c-bb2e-a1b029503b86", "resource": { "resourceType": "Provenance", @@ -292,8 +268,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:OUDYL-JBwPu9y4Z1U7Ql1-9C2TShwD4chzmFTmnrxNs", "type": { "coding": [ { @@ -301,7 +275,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:OUDYL-JBwPu9y4Z1U7Ql1-9C2TShwD4chzmFTmnrxNs" } }, "agent": [ @@ -323,7 +299,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:VVL8mOlyjsCfZVr7MZV0ahVYEH5geZ52CSvU8dwBu74" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json index ad0f3ffc..499076dc 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:44" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:iKOlXQ-gWHTTGJe5x8oFaaYLnB2esy9gRXjQTIJvWXg" - } - } - ], "fullUrl": "urn:uuid:5ddc75c4-ce94-5330-941e-52c9470adc55", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:gknGkBm8LLih4l5bW2EJf0VMUE1-cJMRmVr4_eVDT0c", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:gknGkBm8LLih4l5bW2EJf0VMUE1-cJMRmVr4_eVDT0c" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:iKOlXQ-gWHTTGJe5x8oFaaYLnB2esy9gRXjQTIJvWXg", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:iKOlXQ-gWHTTGJe5x8oFaaYLnB2esy9gRXjQTIJvWXg" } ], "status": "final", @@ -113,9 +89,7 @@ "system": "http://unitsofmeasure.org", "code": "{score}" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -124,15 +98,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:N1m0pHHsgiv9tFv6kqHO7OVrcRcFzxQ11q3khjuDRj4" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:iKOlXQ-gWHTTGJe5x8oFaaYLnB2esy9gRXjQTIJvWXg" } } - ], + ] + }, + { "fullUrl": "urn:uuid:d5cfaa1f-d8dc-5154-956b-79a2574fec38", "resource": { "resourceType": "Device", @@ -143,8 +119,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:N1m0pHHsgiv9tFv6kqHO7OVrcRcFzxQ11q3khjuDRj4", "type": { "coding": [ { @@ -152,7 +126,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:N1m0pHHsgiv9tFv6kqHO7OVrcRcFzxQ11q3khjuDRj4" } ], "status": "active", @@ -162,9 +138,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -178,10 +152,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Tt5TQF_t0Sa93jP9w3UcAfQBUfCyuTAWkm1zobHBye4" + "value": "v0:fixture-key:1:N1m0pHHsgiv9tFv6kqHO7OVrcRcFzxQ11q3khjuDRj4" } } - ], + ] + }, + { "fullUrl": "urn:uuid:cf0c5dad-04d3-5a54-827e-c7e86fa32d28", "resource": { "resourceType": "Device", @@ -192,8 +168,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Tt5TQF_t0Sa93jP9w3UcAfQBUfCyuTAWkm1zobHBye4", "type": { "coding": [ { @@ -201,7 +175,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:Tt5TQF_t0Sa93jP9w3UcAfQBUfCyuTAWkm1zobHBye4" } ], "status": "active", @@ -225,9 +201,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -236,15 +210,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:SAGhx-yLYVhtcX78WfYR-BVjcwjmLAu54qAOX_c7UvE" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:Tt5TQF_t0Sa93jP9w3UcAfQBUfCyuTAWkm1zobHBye4" } } - ], + ] + }, + { "fullUrl": "urn:uuid:a878c05a-740f-5bc0-911c-6235f9329476", "resource": { "resourceType": "Provenance", @@ -290,8 +266,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:gknGkBm8LLih4l5bW2EJf0VMUE1-cJMRmVr4_eVDT0c", "type": { "coding": [ { @@ -299,7 +273,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:gknGkBm8LLih4l5bW2EJf0VMUE1-cJMRmVr4_eVDT0c" } }, "agent": [ @@ -321,7 +297,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:SAGhx-yLYVhtcX78WfYR-BVjcwjmLAu54qAOX_c7UvE" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json index 122c7ef9..15ccb43a 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:37" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:ZV6dweE3ClIU0TX2L5u1nA_-QX2n8mNZSW6SzwjiG6o" - } - } - ], "fullUrl": "urn:uuid:e502b0a8-d613-5be5-af9c-c536dcdf8965", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:0CXIWS-tb3h677E99r2s0kCjmKRmjT5koIKz6SnEV-o", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:0CXIWS-tb3h677E99r2s0kCjmKRmjT5koIKz6SnEV-o" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:ZV6dweE3ClIU0TX2L5u1nA_-QX2n8mNZSW6SzwjiG6o", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:ZV6dweE3ClIU0TX2L5u1nA_-QX2n8mNZSW6SzwjiG6o" } ], "status": "final", @@ -113,9 +89,7 @@ "system": "http://unitsofmeasure.org", "code": "m/s" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -124,15 +98,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:VpM6a_fjm3X5jsTGMqAYfPQ0a91415LGHoo8U9hrLYM" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:ZV6dweE3ClIU0TX2L5u1nA_-QX2n8mNZSW6SzwjiG6o" } } - ], + ] + }, + { "fullUrl": "urn:uuid:b3a3e49e-8642-5af0-85a3-36df6da0d1df", "resource": { "resourceType": "Device", @@ -143,8 +119,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:VpM6a_fjm3X5jsTGMqAYfPQ0a91415LGHoo8U9hrLYM", "type": { "coding": [ { @@ -152,7 +126,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:VpM6a_fjm3X5jsTGMqAYfPQ0a91415LGHoo8U9hrLYM" } ], "status": "active", @@ -162,9 +138,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -178,10 +152,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:SAXtyoGCST2d9rSphOxwj7EZgdyex3Gh0NYcj69xHk8" + "value": "v0:fixture-key:1:VpM6a_fjm3X5jsTGMqAYfPQ0a91415LGHoo8U9hrLYM" } } - ], + ] + }, + { "fullUrl": "urn:uuid:277dad0b-090f-52d4-9595-16d6e2ccaf73", "resource": { "resourceType": "Device", @@ -192,8 +168,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:SAXtyoGCST2d9rSphOxwj7EZgdyex3Gh0NYcj69xHk8", "type": { "coding": [ { @@ -201,7 +175,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:SAXtyoGCST2d9rSphOxwj7EZgdyex3Gh0NYcj69xHk8" } ], "status": "active", @@ -225,9 +201,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -236,15 +210,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:oX8Epkb4hJGt8W5eDD2pS103Nv9UchjAGGTebThAOWo" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:SAXtyoGCST2d9rSphOxwj7EZgdyex3Gh0NYcj69xHk8" } } - ], + ] + }, + { "fullUrl": "urn:uuid:dc673541-6367-5b66-93b5-14b3019515e0", "resource": { "resourceType": "Provenance", @@ -290,8 +266,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:0CXIWS-tb3h677E99r2s0kCjmKRmjT5koIKz6SnEV-o", "type": { "coding": [ { @@ -299,7 +273,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:0CXIWS-tb3h677E99r2s0kCjmKRmjT5koIKz6SnEV-o" } }, "agent": [ @@ -321,7 +297,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:oX8Epkb4hJGt8W5eDD2pS103Nv9UchjAGGTebThAOWo" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json index 48a8e255..2bb5899b 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:34" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:sTZ6V-sSi52BgZy4Z8NRDLRLid7ssBNqXnkSwJpEh6o" - } - } - ], "fullUrl": "urn:uuid:c4c4f7e0-34ca-52bf-9166-237f9bd63c48", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:_l2x6stLJ5Pmo2dPPDYp2B8RPmKnXslZiw1uKzFuNYY", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:_l2x6stLJ5Pmo2dPPDYp2B8RPmKnXslZiw1uKzFuNYY" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:sTZ6V-sSi52BgZy4Z8NRDLRLid7ssBNqXnkSwJpEh6o", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:sTZ6V-sSi52BgZy4Z8NRDLRLid7ssBNqXnkSwJpEh6o" } ], "status": "final", @@ -113,9 +89,7 @@ "system": "http://unitsofmeasure.org", "code": "a" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -124,15 +98,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:EKyKxUz76qvIHbKXieIfjmLdkpPQ-VhRxGVPmcSqBhQ" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:sTZ6V-sSi52BgZy4Z8NRDLRLid7ssBNqXnkSwJpEh6o" } } - ], + ] + }, + { "fullUrl": "urn:uuid:f99a4b98-b062-5dde-b8b0-840e160e42f0", "resource": { "resourceType": "Device", @@ -143,8 +119,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:EKyKxUz76qvIHbKXieIfjmLdkpPQ-VhRxGVPmcSqBhQ", "type": { "coding": [ { @@ -152,7 +126,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:EKyKxUz76qvIHbKXieIfjmLdkpPQ-VhRxGVPmcSqBhQ" } ], "status": "active", @@ -162,9 +138,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -178,10 +152,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:H1u4xN5uRPTDRI9WWv339sBBp8j5rNtyVPYFAp5S5ys" + "value": "v0:fixture-key:1:EKyKxUz76qvIHbKXieIfjmLdkpPQ-VhRxGVPmcSqBhQ" } } - ], + ] + }, + { "fullUrl": "urn:uuid:dde945e4-a798-5e0e-81a9-e386994f4672", "resource": { "resourceType": "Device", @@ -192,8 +168,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:H1u4xN5uRPTDRI9WWv339sBBp8j5rNtyVPYFAp5S5ys", "type": { "coding": [ { @@ -201,7 +175,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:H1u4xN5uRPTDRI9WWv339sBBp8j5rNtyVPYFAp5S5ys" } ], "status": "active", @@ -225,9 +201,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -236,15 +210,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:NPl8ugskS80-ewIkyuzrLgotSslalz00ZyOtD8fm7aM" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:H1u4xN5uRPTDRI9WWv339sBBp8j5rNtyVPYFAp5S5ys" } } - ], + ] + }, + { "fullUrl": "urn:uuid:ab1f9434-7723-5575-a5ea-8833fb57b380", "resource": { "resourceType": "Provenance", @@ -290,8 +266,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:_l2x6stLJ5Pmo2dPPDYp2B8RPmKnXslZiw1uKzFuNYY", "type": { "coding": [ { @@ -299,7 +273,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:_l2x6stLJ5Pmo2dPPDYp2B8RPmKnXslZiw1uKzFuNYY" } }, "agent": [ @@ -321,7 +297,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:NPl8ugskS80-ewIkyuzrLgotSslalz00ZyOtD8fm7aM" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json index a4968f54..38749948 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:47" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:mpcsNy93_GF0urGccYrlm7WcamEEB3JLTyUD3o5QNpg" - } - } - ], "fullUrl": "urn:uuid:dabd84be-fb8a-5569-ab07-d718f176ef9e", "resource": { "resourceType": "Observation", @@ -66,8 +42,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:mXuLjG0QGS2dxig2AVgHnnRSwLPbJRAZI8vB-6ANrJY", "type": { "coding": [ { @@ -75,11 +49,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:mXuLjG0QGS2dxig2AVgHnnRSwLPbJRAZI8vB-6ANrJY" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:mpcsNy93_GF0urGccYrlm7WcamEEB3JLTyUD3o5QNpg", "type": { "coding": [ { @@ -87,7 +61,9 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:mpcsNy93_GF0urGccYrlm7WcamEEB3JLTyUD3o5QNpg" } ], "status": "final", @@ -113,9 +89,7 @@ "system": "http://unitsofmeasure.org", "code": "{score}" } - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -124,15 +98,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:ZZ3OyjMSTgTPzidj8lQLnZAEVzNyoHZaWNixOJ08smw" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:mpcsNy93_GF0urGccYrlm7WcamEEB3JLTyUD3o5QNpg" } } - ], + ] + }, + { "fullUrl": "urn:uuid:fa327e8d-ba69-5548-8572-0a5651a0a973", "resource": { "resourceType": "Device", @@ -143,8 +119,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:ZZ3OyjMSTgTPzidj8lQLnZAEVzNyoHZaWNixOJ08smw", "type": { "coding": [ { @@ -152,7 +126,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:ZZ3OyjMSTgTPzidj8lQLnZAEVzNyoHZaWNixOJ08smw" } ], "status": "active", @@ -162,9 +138,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -178,10 +152,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:MF2JK1kM8eGepo4odzSLt4CNLjdJMGJHPjXnmiAi61Y" + "value": "v0:fixture-key:1:ZZ3OyjMSTgTPzidj8lQLnZAEVzNyoHZaWNixOJ08smw" } } - ], + ] + }, + { "fullUrl": "urn:uuid:0638e592-a3af-5d4c-b689-264c19f94ccd", "resource": { "resourceType": "Device", @@ -192,8 +168,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:MF2JK1kM8eGepo4odzSLt4CNLjdJMGJHPjXnmiAi61Y", "type": { "coding": [ { @@ -201,7 +175,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:MF2JK1kM8eGepo4odzSLt4CNLjdJMGJHPjXnmiAi61Y" } ], "status": "active", @@ -225,9 +201,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -236,15 +210,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:KiJhlB1_wGC3WEMuY4Y4o9J103fJ04dAZo5lV513j4g" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:MF2JK1kM8eGepo4odzSLt4CNLjdJMGJHPjXnmiAi61Y" } } - ], + ] + }, + { "fullUrl": "urn:uuid:7f761f2c-dde0-5cf3-99c6-02795efed9f2", "resource": { "resourceType": "Provenance", @@ -290,8 +266,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:mXuLjG0QGS2dxig2AVgHnnRSwLPbJRAZI8vB-6ANrJY", "type": { "coding": [ { @@ -299,7 +273,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:mXuLjG0QGS2dxig2AVgHnnRSwLPbJRAZI8vB-6ANrJY" } }, "agent": [ @@ -321,7 +297,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:KiJhlB1_wGC3WEMuY4Y4o9J103fJ04dAZo5lV513j4g" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json b/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json index 85e85776..d0acd3ce 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json +++ b/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:49" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:w7o_lcZdhv2y5mj96CWnefbS73I3tPoRvuSQG7Vaqao" - } - } - ], "fullUrl": "urn:uuid:f39530e9-26b5-5078-9f30-5d61d3e4881a", "resource": { "resourceType": "DocumentReference", @@ -59,8 +35,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:Qm28JqWMxMvl2dsco1gdTLvUyKdg3S8qfrEX_4Z19KY", "type": { "coding": [ { @@ -68,11 +42,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:Qm28JqWMxMvl2dsco1gdTLvUyKdg3S8qfrEX_4Z19KY" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:w7o_lcZdhv2y5mj96CWnefbS73I3tPoRvuSQG7Vaqao", "type": { "coding": [ { @@ -80,11 +54,11 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:w7o_lcZdhv2y5mj96CWnefbS73I3tPoRvuSQG7Vaqao" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", - "value": "v0:fixture-key:1:PHw-YmUdZHB6MdPZ3172jcWHpUdXb6Dh9IOm9pXrO4M", "type": { "coding": [ { @@ -92,7 +66,9 @@ "code": "source-artifact" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", + "value": "v0:fixture-key:1:PHw-YmUdZHB6MdPZ3172jcWHpUdXb6Dh9IOm9pXrO4M" } ], "status": "current", @@ -124,9 +100,7 @@ } } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -135,15 +109,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:oLMQbfc0NeyY4hR6JEtvp-FZuco9HARLJLu04VS1a94" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:w7o_lcZdhv2y5mj96CWnefbS73I3tPoRvuSQG7Vaqao" } } - ], + ] + }, + { "fullUrl": "urn:uuid:5cfe6e6b-d30e-5e8a-88cf-d4d922961187", "resource": { "resourceType": "Device", @@ -154,8 +130,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:oLMQbfc0NeyY4hR6JEtvp-FZuco9HARLJLu04VS1a94", "type": { "coding": [ { @@ -163,7 +137,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:oLMQbfc0NeyY4hR6JEtvp-FZuco9HARLJLu04VS1a94" } ], "status": "active", @@ -173,9 +149,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -189,10 +163,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uocYwddFVtlKSSu89sdrCnhx7B1Zj4lX7iNLzaZjvAA" + "value": "v0:fixture-key:1:oLMQbfc0NeyY4hR6JEtvp-FZuco9HARLJLu04VS1a94" } } - ], + ] + }, + { "fullUrl": "urn:uuid:443842a6-4a5b-5ce2-b29e-134f07e39c58", "resource": { "resourceType": "Device", @@ -203,8 +179,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uocYwddFVtlKSSu89sdrCnhx7B1Zj4lX7iNLzaZjvAA", "type": { "coding": [ { @@ -212,7 +186,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:uocYwddFVtlKSSu89sdrCnhx7B1Zj4lX7iNLzaZjvAA" } ], "status": "active", @@ -236,9 +212,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -247,15 +221,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:cIH4Nzhma1gYzkQquANDEpkWbH1g8n6BZFu8SGZae8k" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:uocYwddFVtlKSSu89sdrCnhx7B1Zj4lX7iNLzaZjvAA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:9e5ec01e-0d7f-5b0b-a505-f65fd1ea665a", "resource": { "resourceType": "Provenance", @@ -301,8 +277,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:Qm28JqWMxMvl2dsco1gdTLvUyKdg3S8qfrEX_4Z19KY", "type": { "coding": [ { @@ -310,7 +284,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:Qm28JqWMxMvl2dsco1gdTLvUyKdg3S8qfrEX_4Z19KY" } }, "agent": [ @@ -332,7 +308,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:cIH4Nzhma1gYzkQquANDEpkWbH1g8n6BZFu8SGZae8k" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json b/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json index fbb18fe3..4521f845 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json +++ b/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:50" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:JbH3fA_A04xTUndXZKXKxlEF8b9as0R0q34_o1kBSW8" - } - } - ], "fullUrl": "urn:uuid:9916390c-d9ec-5e86-ac4b-9545f65ccfd9", "resource": { "resourceType": "DocumentReference", @@ -59,8 +35,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:fnfIzdy0ubGE-azcaxeBYJW6Yr9_ICD_fqPfjI39XfI", "type": { "coding": [ { @@ -68,11 +42,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:fnfIzdy0ubGE-azcaxeBYJW6Yr9_ICD_fqPfjI39XfI" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:JbH3fA_A04xTUndXZKXKxlEF8b9as0R0q34_o1kBSW8", "type": { "coding": [ { @@ -80,11 +54,11 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:JbH3fA_A04xTUndXZKXKxlEF8b9as0R0q34_o1kBSW8" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", - "value": "v0:fixture-key:1:4h_4O6TrHzwSL8GWzqd-_Ppl0AzDUP_M3zY0IvE4lTI", "type": { "coding": [ { @@ -92,7 +66,9 @@ "code": "source-artifact" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", + "value": "v0:fixture-key:1:4h_4O6TrHzwSL8GWzqd-_Ppl0AzDUP_M3zY0IvE4lTI" } ], "status": "current", @@ -124,9 +100,7 @@ } } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -135,15 +109,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:eKyVTfyrcOiy_bu_yX0GsJm0XT946MmjfoXJzsX_wKA" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:JbH3fA_A04xTUndXZKXKxlEF8b9as0R0q34_o1kBSW8" } } - ], + ] + }, + { "fullUrl": "urn:uuid:aad76c82-3ffe-527e-a679-7ac0da57d307", "resource": { "resourceType": "Device", @@ -154,8 +130,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:eKyVTfyrcOiy_bu_yX0GsJm0XT946MmjfoXJzsX_wKA", "type": { "coding": [ { @@ -163,7 +137,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:eKyVTfyrcOiy_bu_yX0GsJm0XT946MmjfoXJzsX_wKA" } ], "status": "active", @@ -173,9 +149,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -189,10 +163,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:jmlPta-p2yy0ez5EdOqwg6Z48EenmJ7OxaNRx7O22QM" + "value": "v0:fixture-key:1:eKyVTfyrcOiy_bu_yX0GsJm0XT946MmjfoXJzsX_wKA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:88a3cca3-4f36-52cd-aa6c-e86732e82c06", "resource": { "resourceType": "Device", @@ -203,8 +179,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:jmlPta-p2yy0ez5EdOqwg6Z48EenmJ7OxaNRx7O22QM", "type": { "coding": [ { @@ -212,7 +186,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:jmlPta-p2yy0ez5EdOqwg6Z48EenmJ7OxaNRx7O22QM" } ], "status": "active", @@ -236,9 +212,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -247,15 +221,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:NvVaCec-6ykB4NgfVZEUFzS47_HJq6IwVJJhMurnKeg" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:jmlPta-p2yy0ez5EdOqwg6Z48EenmJ7OxaNRx7O22QM" } } - ], + ] + }, + { "fullUrl": "urn:uuid:14d60b3d-587c-5830-9ed8-30f35e291bdb", "resource": { "resourceType": "Provenance", @@ -301,8 +277,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:fnfIzdy0ubGE-azcaxeBYJW6Yr9_ICD_fqPfjI39XfI", "type": { "coding": [ { @@ -310,7 +284,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:fnfIzdy0ubGE-azcaxeBYJW6Yr9_ICD_fqPfjI39XfI" } }, "agent": [ @@ -332,7 +308,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:NvVaCec-6ykB4NgfVZEUFzS47_HJq6IwVJJhMurnKeg" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json b/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json index f24a5474..7885be35 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json +++ b/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:51" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:B3PLQy1r2W61RW1kB3OKSWh3mMpcVj0paRHWyND2O2g" - } - } - ], "fullUrl": "urn:uuid:accf8995-d7a1-57d6-b653-61a258ec2e1b", "resource": { "resourceType": "DocumentReference", @@ -59,8 +35,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:lAjnPJ63IR1m-sgVedR7vawwKb7tDYLxzjcpGJFdH8M", "type": { "coding": [ { @@ -68,11 +42,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:lAjnPJ63IR1m-sgVedR7vawwKb7tDYLxzjcpGJFdH8M" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:B3PLQy1r2W61RW1kB3OKSWh3mMpcVj0paRHWyND2O2g", "type": { "coding": [ { @@ -80,11 +54,11 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:B3PLQy1r2W61RW1kB3OKSWh3mMpcVj0paRHWyND2O2g" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", - "value": "v0:fixture-key:1:Ngl8REcCCf8c48ftm1b4wa0XNyWjSQa1i0UKiTHaV4s", "type": { "coding": [ { @@ -92,7 +66,9 @@ "code": "source-artifact" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", + "value": "v0:fixture-key:1:Ngl8REcCCf8c48ftm1b4wa0XNyWjSQa1i0UKiTHaV4s" } ], "status": "current", @@ -124,9 +100,7 @@ } } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -135,15 +109,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:yzDY-vpFTbwMkAWgPEJ4D7kUHMcsuPrWGrEXS0KMlSM" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:B3PLQy1r2W61RW1kB3OKSWh3mMpcVj0paRHWyND2O2g" } } - ], + ] + }, + { "fullUrl": "urn:uuid:89ab20ae-a2f0-574a-8f41-4c690cebda0f", "resource": { "resourceType": "Device", @@ -154,8 +130,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:yzDY-vpFTbwMkAWgPEJ4D7kUHMcsuPrWGrEXS0KMlSM", "type": { "coding": [ { @@ -163,7 +137,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:yzDY-vpFTbwMkAWgPEJ4D7kUHMcsuPrWGrEXS0KMlSM" } ], "status": "active", @@ -173,9 +149,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -189,10 +163,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:x-LlatjeBgMfWLnLO419O1s_lhhmbSk6YiYcXeVU7NA" + "value": "v0:fixture-key:1:yzDY-vpFTbwMkAWgPEJ4D7kUHMcsuPrWGrEXS0KMlSM" } } - ], + ] + }, + { "fullUrl": "urn:uuid:21a9b5a2-58d8-5dd2-b01b-838b8776909a", "resource": { "resourceType": "Device", @@ -203,8 +179,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:x-LlatjeBgMfWLnLO419O1s_lhhmbSk6YiYcXeVU7NA", "type": { "coding": [ { @@ -212,7 +186,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:x-LlatjeBgMfWLnLO419O1s_lhhmbSk6YiYcXeVU7NA" } ], "status": "active", @@ -236,9 +212,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -247,15 +221,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:gIphHW-fnGuULq9DANuncEdHEFazUS1Lr6XZYSsvakE" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:x-LlatjeBgMfWLnLO419O1s_lhhmbSk6YiYcXeVU7NA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:b21403d9-6715-5be8-ab04-061e3a0e0e19", "resource": { "resourceType": "Provenance", @@ -301,8 +277,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:lAjnPJ63IR1m-sgVedR7vawwKb7tDYLxzjcpGJFdH8M", "type": { "coding": [ { @@ -310,7 +284,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:lAjnPJ63IR1m-sgVedR7vawwKb7tDYLxzjcpGJFdH8M" } }, "agent": [ @@ -332,7 +308,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:gIphHW-fnGuULq9DANuncEdHEFazUS1Lr6XZYSsvakE" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json b/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json index 88776dc8..d9b91df2 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json +++ b/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json @@ -1,10 +1,4 @@ { - "resourceType": "Bundle", - "meta": { - "profile": [ - "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" - ] - }, "identifier": { "type": { "coding": [ @@ -17,27 +11,9 @@ "system": "https://grovealliance.org/fhir/testing/identity/event", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:52" }, - "type": "collection", "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "extension": [ - { - "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", - "valueIdentifier": { - "type": { - "coding": [ - { - "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "source-output" - } - ] - }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:Uh2ZvHcTyaxH3aO6RElukrQWXzQxwKED3FMteFYyvoo" - } - } - ], "fullUrl": "urn:uuid:7cc82885-eaae-52a4-8954-cbf4e42d70fd", "resource": { "resourceType": "DocumentReference", @@ -59,8 +35,6 @@ ], "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:2X6rXvj9ynbIR3JXJhe8uk0JtT9fSD-OPA16Do4D8yQ", "type": { "coding": [ { @@ -68,11 +42,11 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:2X6rXvj9ynbIR3JXJhe8uk0JtT9fSD-OPA16Do4D8yQ" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", - "value": "v0:fixture-key:1:Uh2ZvHcTyaxH3aO6RElukrQWXzQxwKED3FMteFYyvoo", "type": { "coding": [ { @@ -80,11 +54,11 @@ "code": "source-output" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:Uh2ZvHcTyaxH3aO6RElukrQWXzQxwKED3FMteFYyvoo" }, { - "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", - "value": "v0:fixture-key:1:AimTLWJeR6crk04-8_iSRxDRJmAKM7VKXhdYAUoT9e4", "type": { "coding": [ { @@ -92,7 +66,9 @@ "code": "source-artifact" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", + "value": "v0:fixture-key:1:AimTLWJeR6crk04-8_iSRxDRJmAKM7VKXhdYAUoT9e4" } ], "status": "current", @@ -124,9 +100,7 @@ } } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -135,15 +109,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "device-snapshot" + "code": "source-output" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:bj-SsK_YMXFNS4md3NAMVI6wrSVd2KQIj-6ZMuY9a-E" + "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "value": "v0:fixture-key:1:Uh2ZvHcTyaxH3aO6RElukrQWXzQxwKED3FMteFYyvoo" } } - ], + ] + }, + { "fullUrl": "urn:uuid:b70165c6-669b-51d6-ab8c-837c42455bd4", "resource": { "resourceType": "Device", @@ -154,8 +130,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:bj-SsK_YMXFNS4md3NAMVI6wrSVd2KQIj-6ZMuY9a-E", "type": { "coding": [ { @@ -163,7 +137,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:bj-SsK_YMXFNS4md3NAMVI6wrSVd2KQIj-6ZMuY9a-E" } ], "status": "active", @@ -173,9 +149,7 @@ "type": "user-friendly-name" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -189,10 +163,12 @@ ] }, "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Fk4ot8ZFQZFFmgsML91JFuas22bK-FhacPfljbciTgA" + "value": "v0:fixture-key:1:bj-SsK_YMXFNS4md3NAMVI6wrSVd2KQIj-6ZMuY9a-E" } } - ], + ] + }, + { "fullUrl": "urn:uuid:bb28b03a-6b47-56ea-b849-487f307e6f4c", "resource": { "resourceType": "Device", @@ -203,8 +179,6 @@ }, "identifier": [ { - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Fk4ot8ZFQZFFmgsML91JFuas22bK-FhacPfljbciTgA", "type": { "coding": [ { @@ -212,7 +186,9 @@ "code": "device-snapshot" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:Fk4ot8ZFQZFFmgsML91JFuas22bK-FhacPfljbciTgA" } ], "status": "active", @@ -236,9 +212,7 @@ "value": "0.0.0" } ] - } - }, - { + }, "extension": [ { "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", @@ -247,15 +221,17 @@ "coding": [ { "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", - "code": "entry-node" + "code": "device-snapshot" } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:sKOYrb7a0lyQiBSQYAsmbtmbU4tdWkVS3bQsLzWJWZ4" + "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", + "value": "v0:fixture-key:1:Fk4ot8ZFQZFFmgsML91JFuas22bK-FhacPfljbciTgA" } } - ], + ] + }, + { "fullUrl": "urn:uuid:555fb2c9-9210-56b8-a1ef-e6391fc23fcf", "resource": { "resourceType": "Provenance", @@ -301,8 +277,6 @@ "role": "source", "what": { "identifier": { - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", - "value": "v0:fixture-key:1:2X6rXvj9ynbIR3JXJhe8uk0JtT9fSD-OPA16Do4D8yQ", "type": { "coding": [ { @@ -310,7 +284,9 @@ "code": "source-record" } ] - } + }, + "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "value": "v0:fixture-key:1:2X6rXvj9ynbIR3JXJhe8uk0JtT9fSD-OPA16Do4D8yQ" } }, "agent": [ @@ -332,7 +308,31 @@ ] } ] - } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/identity/entry-node", + "value": "n0:conversion-provenance:0:sKOYrb7a0lyQiBSQYAsmbtmbU4tdWkVS3bQsLzWJWZ4" + } + } + ] } - ] + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" } diff --git a/packages/fhir/grove-fhir.json b/packages/fhir/grove-fhir.json index d4789f20..2d3bfa06 100644 --- a/packages/fhir/grove-fhir.json +++ b/packages/fhir/grove-fhir.json @@ -1,5 +1,5 @@ { "repository": "https://github.com/SchmiedmayerLab/grove-fhir", - "ref": "90b23e647f1dd728035c64348abede5746375529", - "archiveSha256": "1ae9204acd104095cc12f6dff1226fd2cdba68fdea88552971b542ca0df344a0" + "ref": "444e06fb25680c3af29e5d3ccbebeb0249be7318", + "archiveSha256": "e436be0be250a335a01db1a2580c06349bbc35b15ccb5a0397c023ba54a37ffe" } diff --git a/packages/fhir/scripts/browser-smoke.mjs b/packages/fhir/scripts/browser-smoke.mjs index b5ce408a..caeda094 100644 --- a/packages/fhir/scripts/browser-smoke.mjs +++ b/packages/fhir/scripts/browser-smoke.mjs @@ -224,7 +224,9 @@ try { }, attachment: { kind: 'embedded', - contentType: 'application/json', + contentType: + provider.groveRecordingFormatRegistry.formats['provider-recording'] + .contentTypes[0], title: 'Authorized minimized provider recording', format: 'provider-recording', payloadAssertion: 'caller-authorized-opaque-payload', diff --git a/packages/fhir/scripts/generate-conformance-fixtures.mjs b/packages/fhir/scripts/generate-conformance-fixtures.mjs index 390f5ea8..5b274bfb 100644 --- a/packages/fhir/scripts/generate-conformance-fixtures.mjs +++ b/packages/fhir/scripts/generate-conformance-fixtures.mjs @@ -7,7 +7,7 @@ // import { Buffer } from 'node:buffer' -import { mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises' +import { mkdir, readFile, readdir, unlink, writeFile } from 'node:fs/promises' import { dirname, resolve } from 'node:path' import { argv, stdout } from 'node:process' import { fileURLToPath } from 'node:url' @@ -22,7 +22,8 @@ const { buildQuestionnaire, buildQuestionnaireResponse, groveExchangeProtocol, - groveFhirContractVersion, + groveMobilePackageMetadata, + groveRecordingFormatRegistry, parseAbsoluteUri, parseCanonical, parseFhirInstant, @@ -101,11 +102,12 @@ const packageBySource = new Map( packageGraph.packages.map((entry) => [entry.source, entry]), ) if ( + packageGraph.schemaVersion !== 0 || packageGraph.fhirVersion !== '4.0.1' || - packageGraph.version !== groveFhirContractVersion + packageGraph.version !== groveMobilePackageMetadata.version ) { throw new Error( - 'The synchronized package graph must match the generated R4 contract version.', + 'The synchronized package graph must use the supported schema, target FHIR R4, and match the generated package coordinates.', ) } const conformancePackages = conformancePackageSources.map((source) => { @@ -501,7 +503,12 @@ const recordingBundle = ({ provider, sourceType }, index) => }, attachment: { kind: 'embedded', - contentType: unwrap(parseMediaType('application/json')), + contentType: unwrap( + parseMediaType( + groveRecordingFormatRegistry.formats['provider-recording'] + .contentTypes[0], + ), + ), title: 'Authorized minimized provider recording', format: 'provider-recording', payloadAssertion: 'caller-authorized-opaque-payload', @@ -868,6 +875,7 @@ if (invalid.length > 0) { await mkdir(resourceRoot, { recursive: true }) await mkdir(resolve(fixtureRoot, 'mobile-exchange'), { recursive: true }) +const staleFixtureFiles = [] for (const directory of ['resources', 'mobile-exchange']) { const expected = new Set( [...serialized.keys()] @@ -879,13 +887,16 @@ for (const directory of ['resources', 'mobile-exchange']) { (name.endsWith('.json') || name.endsWith('.json.license')) && !expected.has(name), ) - if (check && stale.length > 0) { - throw new Error( - `Unexpected conformance fixtures: ${stale.map((name) => `${directory}/${name}`).join(', ')}`, - ) - } + staleFixtureFiles.push(...stale.map((name) => `${directory}/${name}`)) +} +if (check && staleFixtureFiles.length > 0) { + throw new Error( + `Conformance fixture directory contains stale generated files: ${staleFixtureFiles.join(', ')}`, + ) +} +if (!check) { await Promise.all( - stale.map((name) => rm(resolve(fixtureRoot, directory, name))), + staleFixtureFiles.map((relative) => unlink(resolve(fixtureRoot, relative))), ) } for (const [relative, value] of serialized) { diff --git a/packages/fhir/scripts/generate-measurement-catalog.mjs b/packages/fhir/scripts/generate-measurement-catalog.mjs index 26b54c91..b85744e0 100644 --- a/packages/fhir/scripts/generate-measurement-catalog.mjs +++ b/packages/fhir/scripts/generate-measurement-catalog.mjs @@ -46,6 +46,30 @@ const uniqueNonemptyStrings = (values) => values.length > 0 && values.every((value) => typeof value === 'string' && value.length > 0) && new Set(values).size === values.length +const exactSet = (left, right) => + Array.isArray(left) && + Array.isArray(right) && + left.length === right.length && + new Set(left).size === left.length && + left.every((value) => right.includes(value)) +// A registry format declares either one media type or a closed release-keyed set of them. +const recordingFormatContentTypes = (code) => { + const entry = formatRegistry.formats?.[code] + const declared = + entry?.contentTypes ?? + (typeof entry?.contentType === 'string' ? [entry.contentType] : undefined) + if ( + typeof entry?.title !== 'string' || + entry.status !== 'active' || + !uniqueNonemptyStrings(declared) || + declared.some( + (mediaType) => !Object.hasOwn(formatRegistry.mediaTypes ?? {}, mediaType), + ) + ) { + throw new Error(`Recording format ${code} is incomplete.`) + } + return declared +} const isMobileOwned = (measurement) => measurement.owner === undefined || measurement.owner === 'mobile' @@ -58,7 +82,6 @@ const mobileMeasurements = measurements.filter(isMobileOwned) if ( semanticCorpus.schemaVersion !== 0 || semanticCorpus.fhirVersion !== '4.0.1' || - semanticCorpus.version !== catalog.version || !Array.isArray(semanticCorpus.vectors) || semanticCorpus.vectors.length !== mobileMeasurements.length ) { @@ -282,7 +305,14 @@ if ( throw new Error('Sleep-stage terminology contract is incomplete.') } -if (packageGraph.fhirVersion !== '4.0.1' || catalog.fhirVersion !== '4.0.1') { +if ( + packageGraph.schemaVersion !== 0 || + typeof packageGraph.version !== 'string' || + packageGraph.version.length === 0 || + packageGraph.fhirVersion !== '4.0.1' || + catalog.schemaVersion !== 0 || + catalog.fhirVersion !== '4.0.1' +) { throw new Error( 'The TypeScript package consumes FHIR R4 (4.0.1) catalogs only.', ) @@ -295,11 +325,11 @@ for (const consumedCatalog of [ sensorKitAdapter, ]) { if ( - consumedCatalog.fhirVersion !== '4.0.1' || - consumedCatalog.version !== packageGraph.version + consumedCatalog.schemaVersion !== 0 || + consumedCatalog.fhirVersion !== '4.0.1' ) { throw new Error( - 'Every consumed IG catalog must target the same FHIR R4 release.', + 'Every consumed IG catalog must use the supported schema and target FHIR R4.', ) } } @@ -427,6 +457,7 @@ const providerPackageCanonicals = selectEntries(packageCanonicals, [ ]) if ( + profileClaims.schemaVersion !== 0 || profileClaims.fhirVersion !== '4.0.1' || profileClaims.observationAdapterClaim?.cardinality !== 2 || profileClaims.observationAdapterClaim?.inheritedProfilesAreNotDeclared !== @@ -595,12 +626,6 @@ const healthKitClinicalRecordAdmission = healthKitAdapter.clinicalRecordAdmission const healthKitClinicalFhirRepresentation = healthKitClinicalRecordAdmission?.fhirRepresentation -const healthKitClinicalFhirReleases = - healthKitClinicalRecordAdmission?.admittedFHIRReleases -const healthKitClinicalContentTypes = - healthKitClinicalFhirRepresentation?.contentTypeByRelease -const healthKitClinicalFormat = - formatRegistry.formats?.[healthKitClinicalRecordAdmission?.payloadFormat] const healthKitApplicationDeviceIdentity = healthKitAdapter.applicationDeviceIdentity const healthKitBundleIdentifier = @@ -614,16 +639,16 @@ if ( healthKitClinicalRecordAdmission.profile !== profiles['healthkit-clinical-record-document'] || typeof healthKitClinicalRecordAdmission.payloadFormat !== 'string' || - healthKitClinicalFormat?.status !== 'active' || typeof healthKitClinicalRecordAdmission.sourceFHIRReleaseField !== 'string' || healthKitClinicalRecordAdmission.sourceFHIRReleaseField.trim() === '' || - JSON.stringify(healthKitClinicalFhirReleases) !== - JSON.stringify(['dstu2', 'r4']) || + !uniqueNonemptyStrings( + healthKitClinicalRecordAdmission.admittedFHIRReleases, + ) || !uniqueNonemptyStrings( healthKitClinicalRecordAdmission.rejectedFHIRReleases, ) || healthKitClinicalRecordAdmission.rejectedFHIRReleases.some((release) => - healthKitClinicalFhirReleases.includes(release), + healthKitClinicalRecordAdmission.admittedFHIRReleases.includes(release), ) || typeof healthKitClinicalRecordAdmission.rule !== 'string' || healthKitClinicalRecordAdmission.rule.trim() === '' || @@ -633,19 +658,16 @@ if ( Object.keys(healthKitClinicalFhirRepresentation).sort().join(',') !== 'contentTypeByRelease,resourceType' || healthKitClinicalFhirRepresentation.resourceType !== 'DocumentReference' || - typeof healthKitClinicalContentTypes !== 'object' || - healthKitClinicalContentTypes === null || - Array.isArray(healthKitClinicalContentTypes) || - JSON.stringify(Object.keys(healthKitClinicalContentTypes).sort()) !== - JSON.stringify([...healthKitClinicalFhirReleases].sort()) || - !uniqueNonemptyStrings(Object.values(healthKitClinicalContentTypes)) || - !uniqueNonemptyStrings(healthKitClinicalFormat.contentTypes) || - JSON.stringify(healthKitClinicalFormat.contentTypes) !== - JSON.stringify( - healthKitClinicalFhirReleases.map( - (release) => healthKitClinicalContentTypes[release], - ), - ) + !exactSet( + Object.keys(healthKitClinicalFhirRepresentation.contentTypeByRelease ?? {}), + healthKitClinicalRecordAdmission.admittedFHIRReleases, + ) || + !exactSet( + Object.values( + healthKitClinicalFhirRepresentation.contentTypeByRelease ?? {}, + ), + recordingFormatContentTypes(healthKitClinicalRecordAdmission.payloadFormat), + ) ) { throw new Error( 'HealthKit clinical-record FHIR admission must remain complete and closed.', @@ -726,8 +748,8 @@ if ( const expectedConnectedProviders = ['google-health-api', 'oura', 'withings'] if ( + providerAdapter.schemaVersion !== 0 || providerAdapter.fhirVersion !== '4.0.1' || - providerAdapter.version !== packageGraph.version || providerAdapter.packageId !== 'org.grovealliance.fhir.providers' || providerAdapter.canonical !== packageCanonicals['providers'] || providerAdapter.adapterProfile !== profiles['providers-observation'] || @@ -1186,20 +1208,22 @@ if ( } if ( + formatRegistry.schemaVersion !== 0 || formatRegistry.fhirVersion !== '4.0.1' || - formatRegistry.version !== packageGraph.version || typeof formatRegistry.codeSystem !== 'string' || typeof formatRegistry.valueSet !== 'string' || Object.keys(formatRegistry.formats ?? {}).length === 0 || Object.hasOwn(formatRegistry.formats ?? {}, 'fhir-resource-array') || - Object.hasOwn(formatRegistry.formats ?? {}, 'fhir-r4-resource') || - formatRegistry.formats?.['fhir-collection-bundle']?.contentType !== - 'application/fhir+json' || - !uniqueNonemptyStrings( - formatRegistry.formats?.['fhir-resource']?.contentTypes, - ) || - formatRegistry.formats?.['provider-recording']?.contentType !== - 'application/json' + !exactSet(recordingFormatContentTypes('fhir-collection-bundle'), [ + 'application/fhir+json', + ]) || + !exactSet(recordingFormatContentTypes('fhir-resource'), [ + 'application/fhir+json; fhirVersion=1.0', + 'application/fhir+json; fhirVersion=4.0', + ]) || + !exactSet(recordingFormatContentTypes('provider-recording'), [ + 'application/json', + ]) ) { throw new Error('Recording format registry is incomplete.') } @@ -1208,29 +1232,15 @@ if ( const recordingFormatRegistry = { codeSystem: formatRegistry.codeSystem, valueSet: formatRegistry.valueSet, - version: formatRegistry.version, formats: Object.fromEntries( - Object.entries(formatRegistry.formats).map(([code, entry]) => { - const hasContentType = typeof entry.contentType === 'string' - const hasContentTypes = uniqueNonemptyStrings(entry.contentTypes) - if ( - typeof entry.title !== 'string' || - hasContentType === hasContentTypes || - entry.status !== 'active' - ) { - throw new Error(`Recording format ${code} is incomplete.`) - } - return [ - code, - { - title: entry.title, - ...(hasContentType ? - { contentType: entry.contentType } - : { contentTypes: entry.contentTypes }), - status: entry.status, - }, - ] - }), + Object.entries(formatRegistry.formats).map(([code, entry]) => [ + code, + { + title: entry.title, + contentTypes: recordingFormatContentTypes(code), + status: entry.status, + }, + ]), ), } @@ -1283,24 +1293,47 @@ const protocolIdentifierRoles = new Set( const identityVectors = exchangeProtocol.testVectors?.identities const invalidIdentityVectors = exchangeProtocol.testVectors?.invalidIdentities const providerCodes = new Set(providerAdapter.providers.map(({ id }) => id)) -const providerIdentityKinds = new Set([ - 'provider-record', - 'provider-output', - 'provider-artifact', -]) -const genericSourceIdentityKinds = new Set([ - 'source-record', - 'source-output', - 'source-artifact', -]) +// A kind's coordinate family is written in its components: the first names the adapter +// space, and only source-record coordinates carry a native record id. +const identityKindsCoordinatedBy = (adapterComponent) => + new Set( + (protocolIdentityKinds ?? []) + .filter( + ({ components }) => + Array.isArray(components) && + components[0] === adapterComponent && + components.includes('native-record-id'), + ) + .map(({ kind }) => kind), + ) +const providerIdentityKinds = identityKindsCoordinatedBy('provider-code') +const genericSourceIdentityKinds = identityKindsCoordinatedBy('adapter-id') const sourceOutputVector = identityVectors?.find( ({ id }) => id === 'multi-output-sample', ) const sourceContextVector = identityVectors?.find( ({ id }) => id === 'healthkit-medication-source-context', ) -const opaqueIdentityValue = - /^v0:[A-Za-z0-9._-]+:[1-9][0-9]*:[A-Za-z0-9_-]{43}$/u +// Every persisted identity prefix is the contract's own revision token, never a literal here. +const identityValuePrefix = (valueForm, marker) => { + const expected = `${marker}${String(exchangeProtocol.protocolVersion)}:` + if (typeof valueForm !== 'string' || !valueForm.startsWith(expected)) { + throw new Error( + `Identity value form must open with ${expected} at protocol version ${String(exchangeProtocol.protocolVersion)}.`, + ) + } + return expected +} +const opaqueValuePrefix = identityValuePrefix( + exchangeProtocol.opaqueIdentity?.valueForm, + 'v', +) +identityValuePrefix(exchangeProtocol.event?.bundleIdentifier?.valueForm, 'e') +identityValuePrefix(exchangeProtocol.entryIdentity?.entryNode?.valueForm, 'n') +const opaqueIdentityValue = new RegExp( + `^${opaqueValuePrefix}[A-Za-z0-9._-]+:[1-9][0-9]*:[A-Za-z0-9_-]{43}$`, + 'u', +) const retractionTargetRules = exchangeProtocol.lifecycle?.retraction?.targetRoles const retractionTargetEntries = @@ -1334,12 +1367,6 @@ const documentProfileClaimRows = [ profileClaims.providerRecordingDocumentClaim, profileClaims.sensorKitRecordingDocumentClaim, ] -const exactSet = (left, right) => - Array.isArray(left) && - Array.isArray(right) && - left.length === right.length && - new Set(left).size === left.length && - left.every((value) => right.includes(value)) const activeOutputResourceTypes = [ 'Observation', 'DocumentReference', @@ -1360,26 +1387,27 @@ const activeDeviceClaims = profileClaims.activeDeviceClaims const activeQuestionnaireResponseClaim = profileClaims.activeQuestionnaireResponseClaim const expectedReferencePaths = [ - ['Observation', 'subject', ['Patient']], - ['Observation', 'device', ['Device']], - ['Observation', 'specimen', ['Specimen']], - ['Observation', 'focus', ['Location']], - ['Observation', 'hasMember', ['Observation']], + ['Observation', 'subject', false, ['Patient']], + ['Observation', 'device', false, ['Device']], + ['Observation', 'specimen', false, ['Specimen']], + ['Observation', 'focus', true, ['Location']], + ['Observation', 'hasMember', true, ['Observation']], [ 'Observation', 'derivedFrom', + true, ['Observation', 'DocumentReference', 'QuestionnaireResponse'], ], - ['DocumentReference', 'subject', ['Patient']], - ['QuestionnaireResponse', 'subject', ['Patient']], - ['Specimen', 'subject', ['Patient']], - ['MedicationAdministration', 'subject', ['Patient']], - ['MedicationStatement', 'subject', ['Patient']], - ['VisionPrescription', 'patient', ['Patient']], - ['ResearchSubject', 'individual', ['Patient']], - ['ResearchSubject', 'study', ['ResearchStudy']], - ['ResearchStudy', 'protocol', ['PlanDefinition']], - ['Device', 'parent', ['Device']], + ['DocumentReference', 'subject', false, ['Patient']], + ['QuestionnaireResponse', 'subject', false, ['Patient']], + ['Specimen', 'subject', false, ['Patient']], + ['MedicationAdministration', 'subject', false, ['Patient']], + ['MedicationStatement', 'subject', false, ['Patient']], + ['VisionPrescription', 'patient', false, ['Patient']], + ['ResearchSubject', 'individual', false, ['Patient']], + ['ResearchSubject', 'study', false, ['ResearchStudy']], + ['ResearchStudy', 'protocol', true, ['PlanDefinition']], + ['Device', 'parent', false, ['Device']], ] const expectedExtensionTargets = [ [ @@ -1392,19 +1420,25 @@ const expectedExtensionTargets = [ ], ] const referencePaths = exchangeProtocol.referencePolicy?.paths?.map( - ({ resourceType, path, targetTypes }) => [resourceType, path, targetTypes], + ({ resourceType, path, repeating, targetTypes }) => [ + resourceType, + path, + repeating, + targetTypes, + ], ) const extensionTargets = exchangeProtocol.referencePolicy?.extensionTargets?.map( ({ url, targetTypes }) => [url, targetTypes], ) +const expectedPatientReservedSystems = Object.values( + exchangeProtocol.codeSystems ?? {}, +) +// The protocol's registry is the closed rule surface; the corpus only exercises part of it. const exchangeRuleDiagnostics = Object.fromEntries( - (exchangeCorpus.cases ?? []).map(({ expectedRule }) => [ - expectedRule?.code, - { - reason: expectedRule?.reason, - severity: expectedRule?.severity, - }, + (exchangeProtocol.producerDiagnostics ?? []).map(({ code, reason }) => [ + code, + { reason, severity: 'error' }, ]), ) const exchangeRuleRows = exchangeCorpus.cases?.map( @@ -1412,9 +1446,7 @@ const exchangeRuleRows = exchangeCorpus.cases?.map( ) if ( exchangeProtocol.schemaVersion !== 0 || - exchangeProtocol.version !== packageGraph.version || exchangeProtocol.protocolVersion !== 0 || - exchangeProtocol.releaseVersion !== packageGraph.version || exchangeProtocol.fhirVersion !== '4.0.1' || exchangeProtocol.profiles?.activeBundle !== profiles['grove-mobile-exchange-bundle'] || @@ -1506,25 +1538,31 @@ if ( activeQuestionnaireResponseClaim.profiles?.length !== 1 || !uniqueNonemptyStrings(activeQuestionnaireResponseClaim.profiles) || JSON.stringify(referencePaths) !== JSON.stringify(expectedReferencePaths) || + !exactSet( + exchangeProtocol.referencePolicy?.identifierOnlyPatient?.reservedSystems, + expectedPatientReservedSystems, + ) || + typeof exchangeProtocol.referencePolicy?.identifierOnlyPatient?.systemRule !== + 'string' || + typeof exchangeProtocol.referencePolicy?.identifierOnlyPatient?.valueRule !== + 'string' || JSON.stringify(extensionTargets) !== JSON.stringify(expectedExtensionTargets) || typeof exchangeProtocol.referencePolicy?.literalClosure !== 'string' || typeof exchangeProtocol.referencePolicy?.declaredType !== 'string' || typeof exchangeProtocol.referencePolicy?.governedShape !== 'string' || exchangeCorpus.schemaVersion !== 0 || + !uniqueNonemptyStrings(Object.keys(exchangeRuleDiagnostics)) || + !Object.hasOwn(exchangeRuleDiagnostics, 'mobile-exchange.unclassified') || + Object.values(exchangeRuleDiagnostics).some( + ({ reason }) => typeof reason !== 'string' || reason.length === 0, + ) || !Array.isArray(exchangeRuleRows) || - exchangeRuleRows.length !== 34 || - Object.keys(exchangeRuleDiagnostics).length !== - new Set(exchangeRuleRows.map(({ code }) => code)).size || + exchangeRuleRows.length === 0 || exchangeRuleRows.some( (rule) => - typeof rule?.code !== 'string' || - !rule.code.startsWith('mobile-') || - typeof rule.reason !== 'string' || - rule.reason.length === 0 || - typeof rule.location !== 'string' || + typeof rule?.location !== 'string' || rule.location.length === 0 || - rule.severity !== 'error' || exchangeRuleDiagnostics[rule.code]?.reason !== rule.reason || exchangeRuleDiagnostics[rule.code]?.severity !== rule.severity, ) || @@ -1630,12 +1668,8 @@ const adapterProfiledCapabilities = capabilities.measurements?.filter( ) const capabilityKeys = capabilities.measurements?.map((entry) => entry.key) const capabilityStatuses = new Set(Object.keys(capabilities.statuses ?? {})) -if (capabilities.igVersion !== catalog.version) { - throw new Error( - `Capability matrix declares IG version ${String(capabilities.igVersion)} against catalog ${String(catalog.version)}.`, - ) -} if ( + capabilities.schemaVersion !== 0 || !Array.isArray(capabilityKeys) || new Set(capabilityKeys).size !== capabilityKeys.length || capabilities.measurements.some( @@ -1718,37 +1752,43 @@ if ( 'Capability matrix adapter-profiled rows must match the IG profile claims.', ) } +const catalogMeasurementIds = new Set(measurements.map(({ id }) => id)) +const catalogBackedStatuses = ['implemented', 'platform-exclusive'] +const strandedCapabilities = capabilities.measurements.filter( + (entry) => + catalogBackedStatuses.includes(entry.status) && + !catalogMeasurementIds.has(entry.key), +) +if (strandedCapabilities.length > 0) { + throw new Error( + `Capability matrix keeps rows the IG catalog no longer defines: ${strandedCapabilities + .map(({ key }) => key) + .join(', ')}.`, + ) +} for (const normative of measurements) { const capability = capabilities.measurements.find( (entry) => entry.key === normative.id, ) - if (!isMobileOwned(normative)) { - if ( - capability !== undefined && - (capability.status !== 'platform-exclusive' || - capability.profile !== normative.profile) - ) { - throw new Error( - `Owner-exclusive capability ${normative.id} must retain its exact platform-exclusive contract.`, - ) - } - continue - } - const admitted = sharedAdmittedMeasurements.has(normative.id) + const ownerExclusive = !isMobileOwned(normative) + // Owner-exclusive measurements need not be inventoried; a row that exists must be exact. + if (capability === undefined && ownerExclusive) continue + const expectedStatus = + ownerExclusive ? 'platform-exclusive' + : sharedAdmittedMeasurements.has(normative.id) ? 'implemented' + : 'profiled-not-admitted' if ( - capability === undefined || - capability.status !== - (admitted ? 'implemented' : 'profiled-not-admitted') || + capability?.status !== expectedStatus || capability.profile !== normative.profile || capability.effective !== normative.effective || capability.code?.system !== normative.code.system || capability.code?.code !== normative.code.code || - (normative.quantity !== null && + (normative.quantity != null && capability.unit?.code !== normative.quantity.code) || !Array.isArray(capability.sources) ) { throw new Error( - `Implemented capability ${String(capability?.key ?? normative.id)} does not match the shared IG catalog.`, + `Capability ${String(capability?.key ?? normative.id)} does not match the IG catalog contract.`, ) } } @@ -1814,12 +1854,19 @@ for (const value of Object.values(generated)) { } } +const withoutReleaseMetadata = (value) => + Object.fromEntries( + Object.entries(value).filter( + ([key]) => key !== 'version' && key !== 'releaseVersion', + ), + ) + const unformattedOutputs = renderMeasurementCatalogSources({ adapterMeasurementCatalog, adapterSourceMarkerClaims, effectiveCanonicalization, effectiveCanonicalizationVectors: semanticEffectiveCanonicalization.vectors, - exchangeProtocol, + exchangeProtocol: withoutReleaseMetadata(exchangeProtocol), exchangeRuleDiagnostics, healthConnectDataOriginApplication, healthKitApplicationDeviceIdentity, @@ -1827,8 +1874,8 @@ const unformattedOutputs = renderMeasurementCatalogSources({ mobilePackageMetadata, mobileProfiles, packageGraph, - profileClaims, - providerAdapter, + profileClaims: withoutReleaseMetadata(profileClaims), + providerAdapter: withoutReleaseMetadata(providerAdapter), providerScalarOutputDiscriminators, providerPackageCanonicals, providerPackageMetadata, diff --git a/packages/fhir/scripts/generate-zod-schemas.mjs b/packages/fhir/scripts/generate-zod-schemas.mjs index f976776d..8297df5f 100644 --- a/packages/fhir/scripts/generate-zod-schemas.mjs +++ b/packages/fhir/scripts/generate-zod-schemas.mjs @@ -675,17 +675,6 @@ const newObjectRefinements = () => ({ primitiveScalars: [], }) -/** - * The constraints that apply to an object, as the release states applicability. - * - * A constraint is declared once on the element that owns it, and every element it governs names - * it in `condition`. Reading both means a rule stated at the resource root still reaches the - * backbone it actually constrains — Observation.component.dataAbsentReason names obs-6 that way. - * - * This is sound only for a constraint whose expression is relative to the object it lands on. - * Every entry in IMPLEMENTED_CONSTRAINTS satisfies that; a constraint that navigates from an - * ancestor must not be added here without restricting it to its declaring path. - */ const SKIPPED_CONSTRAINTS = new Map() /** Generic constraints every element carries; not a gap worth reporting. */ @@ -1178,7 +1167,19 @@ async function main(argv) { const skipped = [...SKIPPED_CONSTRAINTS.entries()] .map(([path, keys]) => [path, [...keys].sort()]) .sort(([a], [b]) => a.localeCompare(b)) + const resourceTypeCodes = [ + ...new Set([ + ...ordered.filter((name) => structures.get(name)?.kind === 'resource'), + ...context.abstractResources, + ]), + ].sort() + const resourceTypeExport = + `\n/** Every resourceType code the release publishes, abstract types included. */\n` + + `export const resourceTypeCodes = [\n` + + resourceTypeCodes.map((name) => ` ${JSON.stringify(name)},`).join('\n') + + `\n] as const\n` const skippedExport = + resourceTypeExport + `\n/** Exact pinned release material and generated closure behind this module. */\n` + `export const STRUCTURAL_SCHEMA_SOURCE = {\n` + ` packageId: ${JSON.stringify(release.packageId)},\n` + diff --git a/packages/fhir/scripts/measurement-catalog-renderer.mjs b/packages/fhir/scripts/measurement-catalog-renderer.mjs index 9177bc5a..0092a01a 100644 --- a/packages/fhir/scripts/measurement-catalog-renderer.mjs +++ b/packages/fhir/scripts/measurement-catalog-renderer.mjs @@ -56,9 +56,7 @@ export const renderMeasurementCatalogSources = ({ import { deepFreeze } from '../core/index.js' ` - const versions = `export const groveFhirContractVersion = ${JSON.stringify(packageGraph.version)} as const - - export const groveFhirVersion = ${JSON.stringify(packageGraph.fhirVersion)} as const + const versions = `export const groveFhirVersion = ${JSON.stringify(packageGraph.fhirVersion)} as const ` const mobileUnformattedOutput = `${header(true)}${versions} diff --git a/packages/fhir/scripts/package-smoke.mjs b/packages/fhir/scripts/package-smoke.mjs index 431cdd82..5d0cb1be 100644 --- a/packages/fhir/scripts/package-smoke.mjs +++ b/packages/fhir/scripts/package-smoke.mjs @@ -7,86 +7,62 @@ // import assert from 'node:assert/strict' +import { readFile } from 'node:fs/promises' +import { dirname, resolve } from 'node:path' import { stdout } from 'node:process' +import { fileURLToPath } from 'node:url' const packageName = '@schmiedmayerlab/grove-fhir' -const [root, mobile, provider, provenance, questionnaire, r4] = - await Promise.all([ - import(packageName), - import(`${packageName}/mobile`), - import(`${packageName}/providers`), - import(`${packageName}/provenance`), - import(`${packageName}/questionnaire`), - import(`${packageName}/r4`), - ]) +const [root, mobile, provider, questionnaire, r4] = await Promise.all([ + import(packageName), + import(`${packageName}/mobile`), + import(`${packageName}/providers`), + import(`${packageName}/questionnaire`), + import(`${packageName}/r4`), +]) -const providerExports = [ - 'buildProviderMeasurementBundle', - 'buildProviderRecordingBundle', - 'buildProviderRetractionBundle', - 'healthKitApplicationDeviceIdentity', - 'providerRawOutputDiscriminators', - 'providerRawOutputRoles', - 'providerScalarOutputDiscriminators', - 'providerScalarOutputRoles', -] -const internalGraphExports = [ - 'deriveConformanceVectorOpaqueIdentifier', - 'groveFhirPackageCanonicals', - 'groveFhirPackageGraph', - 'groveFhirProfileCanonicals', - 'groveFhirProfileClaims', - 'groveFhirSourceRef', -] +// The packed tarball must expose exactly the surface the source test locks. +const exportSurface = JSON.parse( + await readFile( + resolve( + dirname(fileURLToPath(import.meta.url)), + '../test/export-surface.json', + ), + 'utf8', + ), +) -for (const name of providerExports) { - assert.equal( - name in root, - false, - `${name} leaked through the root entry point`, - ) - assert.equal( - name in mobile, - false, - `${name} leaked through the source-neutral Mobile entry point`, - ) - assert.equal( - name in provenance, - false, - `${name} leaked through the source-neutral Provenance entry point`, - ) - assert.equal( - name in provider, - true, - `${name} is missing from the Provider entry point`, +for (const [name, entryPoint] of [ + ['.', root], + ['./r4', r4], + ['./mobile', mobile], + ['./providers', provider], + ['./questionnaire', questionnaire], +]) { + assert.deepEqual( + Object.keys(entryPoint).sort((left, right) => left.localeCompare(right)), + exportSurface[name], + `The packed ${name} entry point does not expose the recorded surface`, ) } -for (const name of internalGraphExports) { - for (const [label, entryPoint] of [ - ['root', root], - ['Mobile', mobile], - ['Provider', provider], - ['Provenance', provenance], - ['Questionnaire', questionnaire], - ]) { - assert.equal( - name in entryPoint, - false, - `${name} leaked through the ${label} entry point`, - ) - } -} - assert.equal(typeof mobile.canonicalizeMobileEffectiveInstant, 'function') assert.equal(root.groveFhirVersion, '4.0.1') -assert.equal(root.groveFhirContractVersion, '0.6.0') +assert.equal('groveFhirContractVersion' in root, false) +assert.equal(root.groveExchangeProtocol.schemaVersion, 0) assert.equal(root.groveExchangeProtocol.protocolVersion, 0) -assert.equal(root.groveMobileContract.version, '0.6.0') +assert.equal('version' in root.groveExchangeProtocol, false) +assert.equal('releaseVersion' in root.groveExchangeProtocol, false) +assert.equal('version' in root.groveMobileContract, false) +assert.equal('version' in root.groveRecordingFormatRegistry, false) assert.equal( mobile.groveMobilePackageMetadata.packageId, 'org.grovealliance.fhir.mobile', ) +assert.equal( + root.parseSemVer(mobile.groveMobilePackageMetadata.version).ok, + true, +) assert.equal( provider.groveProviderPackageMetadata.packageId, 'org.grovealliance.fhir.providers', @@ -102,7 +78,6 @@ assert.equal(typeof r4.parseGroveMobileRetractionBundle, 'function') assert.equal(typeof provider.parseProviderRetractionInput, 'function') assert.equal(typeof provider.providerOutputCoordinates, 'function') assert.equal(typeof provider.providerOutputRole, 'function') -assert.equal(typeof provenance.parseProvenance, 'function') assert.equal(typeof questionnaire.buildQuestionnaire, 'function') stdout.write('Verified all Grove FHIR package entry points.\n') diff --git a/packages/fhir/scripts/zod-generator-input.mjs b/packages/fhir/scripts/zod-generator-input.mjs index c86fa009..b0eea00d 100644 --- a/packages/fhir/scripts/zod-generator-input.mjs +++ b/packages/fhir/scripts/zod-generator-input.mjs @@ -76,7 +76,7 @@ const fail = (label, value, expectation) => { throw new Error(`${label} ${JSON.stringify(value)} ${expectation}.`) } -/** A release-owned FHIR type name that is safe to derive TypeScript identifiers from. */ +/** A contract-owned FHIR type name that is safe to derive TypeScript identifiers from. */ export function fhirTypeName(value, label = 'FHIR type name') { if ( typeof value !== 'string' || @@ -363,7 +363,7 @@ const validateStructure = ( } /** - * Rejects a malformed or code-generating package AST before any release-owned string reaches a + * Rejects a malformed or code-generating package AST before any contract-owned string reaches a * TypeScript template. Rendering still quotes property/table keys; this gate protects the few * positions that must be identifiers and catches generator-name collisions deterministically. */ diff --git a/packages/fhir/src/contract/measurement-catalog.generated.ts b/packages/fhir/src/contract/measurement-catalog.generated.ts index cf7d63a6..3aab2932 100644 --- a/packages/fhir/src/contract/measurement-catalog.generated.ts +++ b/packages/fhir/src/contract/measurement-catalog.generated.ts @@ -11,8 +11,6 @@ import { deepFreeze } from '../core/index.js' -export const groveFhirContractVersion = '0.6.0' as const - export const groveFhirVersion = '4.0.1' as const const groveMobilePackageMetadataValue = { @@ -52,9 +50,7 @@ const groveExchangeProtocolValue = { $schema: 'https://grovealliance.org/fhir/catalog/schemas/exchange-protocol.schema.json', schemaVersion: 0, - version: '0.6.0', protocolVersion: 0, - releaseVersion: '0.6.0', fhirVersion: '4.0.1', profiles: { activeBundle: @@ -71,6 +67,8 @@ const groveExchangeProtocolValue = { 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key', retractionTargetRole: 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-role', + retractionTargetNativeIdentifier: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-native-identifier', writerRecordVersion: 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version', }, @@ -107,166 +105,241 @@ const groveExchangeProtocolValue = { code: 'mobile-exchange.unclassified', reason: 'A producer-contract failure reached the exchange diagnostic boundary without a more specific registered rule; validation fails closed and the conformance kit must classify the gap.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.entry-node-key', reason: 'Every Bundle entry must carry exactly one complete Grove exchange entry node key.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.deterministic-full-url', reason: 'Bundle.entry.fullUrl must be the UUID version 5 value derived from its complete entry identifier.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.resolved-reference', reason: 'Every internal UUID URN reference must resolve to a Bundle entry fullUrl.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.event-identity', reason: 'Bundle.identifier.value must be the canonical e0 producer UUID and positive sequence form.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.entry-node-digest', reason: 'An entry-node digest must be derived from the enclosing event identifier, role, and ordinal.', + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-exchange.entry-node-ordinal', + reason: + 'An entry-node ordinal is the zero-based position of its entry among the entries sharing that node-role, in Bundle entry order.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.identity-system-role', reason: 'Within one event graph, each Grove Identifier.system names exactly one Grove identifier role; one namespace cannot change meaning between nodes.', + emittedBy: 'conformance-kit', }, { code: 'mobile-output.source-output-required', reason: 'Every active clinical output must carry its exact typed source-output identity in addition to source-record identity.', + emittedBy: 'conformance-kit', }, { code: 'mobile-output.hybrid-companion', reason: 'A hybrid-required output and its exact source-preservation companion must form the catalog-declared closed, same-source, bidirectionally linked graph.', + emittedBy: 'conformance-kit', }, { code: 'healthkit-ecg.output-graph', reason: 'A HealthKit ECG event must satisfy the catalog-owned waveform, optional average-heart-rate, symptom-member, relationship-direction, identity, and effective-period graph contract.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.transform-provenance', reason: 'An active event must contain exactly one transform Provenance and no retraction Provenance.', + emittedBy: 'conformance-kit', }, { code: 'mobile-retraction.logical-target', reason: 'A retraction target must be a typed logical Reference without a literal reference.', + emittedBy: 'conformance-kit', }, { code: 'mobile-retraction.target-role', reason: 'Every retraction target must carry exactly one closed Grove target-role code.', + emittedBy: 'conformance-kit', }, { code: 'mobile-retraction.opaque-target', reason: 'A retraction target must use the exact canonical v0 HMAC identity previously emitted.', + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-retraction.native-record-identifier', + reason: + "An optional retraction native record identifier carries one complete Identifier in the adapter's own absolute native key space and never a Grove identifier-role coding.", + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-exchange.collection-entry-operation', + reason: + 'A Mobile exchange event is a collection Bundle; an entry may carry neither a request nor a response, because an event is an assertion and never a transaction instruction.', + emittedBy: 'client', + }, + { + code: 'mobile-support.questionnaire-response-profile', + reason: + 'Every supporting QuestionnaireResponse must directly claim exactly the Grove Questionnaire Response profile.', + emittedBy: 'client', + }, + { + code: 'mobile-device.recording-device-dual-identity', + reason: + 'A recording Device carries both its durable recording-device identity and its event-scoped device-snapshot identity; neither may stand alone.', + emittedBy: 'client', + }, + { + code: 'healthkit-clinical.fhir-representation', + reason: + 'A HealthKit clinical-record envelope must carry the exact admitted FHIR release and preserve the source representation its adapter contract declares.', + emittedBy: 'client', + }, + { + code: 'sensor-recording-document.identity-and-content', + reason: + 'Every Sensor Recording Document carries its source-record, source-output, and source-artifact identities together with exactly one registered attachment payload.', + emittedBy: 'client', }, { code: 'mobile-retraction.no-clinical-copy', reason: 'A retraction event contains its lifecycle Provenance and optional Device agents, never a copied or mutilated clinical resource.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.lifecycle-coding', reason: 'A lifecycle Provenance must carry exactly one coding across the ISO transform and Grove retraction lifecycle systems; translations from other systems remain open.', + emittedBy: 'conformance-kit', }, { code: 'mobile-output.semantic-profile', reason: 'Every active Observation must directly claim one admitted Grove semantic profile shape; an empty claim cannot bypass semantic validation.', + emittedBy: 'conformance-kit', }, { code: 'mobile-output.fixed-quantity-unit', reason: 'Every Quantity-valued catalog measurement uses the exact fixed system and code declared by its semantic profile contract.', + emittedBy: 'conformance-kit', }, { code: 'mobile-output.quantity-value-domain', reason: 'Every Quantity-valued catalog measurement stays within its catalog-declared representational minimum, maximum, and integer-only domain without inventing a physiologic range.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.reference-target-type', reason: 'Every governed Patient reference resolves to a Patient entry, not merely to any existing fullUrl.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.reference-declared-type', reason: "When Reference.type is present it must equal the referenced entry's actual resourceType token.", + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.logical-source-entity', reason: 'Lifecycle Provenance carries exactly one logical source-record Identifier entity and never a literal source Reference.', + emittedBy: 'conformance-kit', }, { code: 'mobile-retraction.role-target-type', reason: 'Every retraction target role fixes its admitted resource type and Identifier role.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.single-source-entity', reason: 'A lifecycle Provenance identifies exactly one source-record entity.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.reference-shape', reason: 'Each governed path has its declared singular or repeating shape and contains valid Reference objects that are exclusively resolving-literal or identifier-only logical, never both.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.logical-patient-reference', reason: 'An identifier-only logical Patient Reference carries the exact Patient type and one complete absolute-system pseudonym Identifier without a Grove role or protocol-reserved system.', + emittedBy: 'conformance-kit', }, { code: 'mobile-output.adapter-only-profile', reason: 'An adapter-only active output type must directly claim exactly its one admitted adapter profile.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.entry-resource-type', reason: 'An active event admits only its closed output, supporting, and lifecycle resource type set.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.contained-resource-prohibited', reason: 'Mobile exchange events prohibit contained resources; every graph node must be an addressable Bundle entry.', + emittedBy: 'conformance-kit', }, { code: 'mobile-output.document-profile', reason: 'Every active DocumentReference must directly claim exactly one admitted recording or clinical-document profile mode.', + emittedBy: 'conformance-kit', }, { code: 'mobile-support.device-profile', reason: 'Every active Device must directly claim exactly one admitted Grove Device profile mode.', + emittedBy: 'conformance-kit', }, { code: 'mobile-support.connected', reason: 'Every supporting resource must be connected to an output or the lifecycle Provenance.', + emittedBy: 'conformance-kit', }, { code: 'mobile-exchange.provenance-profile', reason: 'The sole active lifecycle Provenance must directly claim exactly one admitted Mobile or adapter conversion profile.', + emittedBy: 'conformance-kit', }, ], event: { @@ -483,7 +556,7 @@ const groveExchangeProtocolValue = { rowKey: 'sourceTypeIdentifier', sourceMarker: { kind: 'extension', - url: 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-source-type-extension', + url: 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-source-type', valueElement: 'valueCode', }, strategy: 'unique-output-intersecting-source-row-profiles', @@ -671,6 +744,8 @@ const groveExchangeProtocolValue = { 'node-role', 'zero-based-ordinal', ], + ordinal: + 'Zero-based among the entries that share the same node-role within the one event, counted in Bundle.entry order over the entries that carry an entry-node key. A role represented once is always 0; a second entry of that role is 1. Roles are numbered independently of one another, and an entry whose resource has a typed business Identifier takes no ordinal because it carries no entry-node key.', valueForm: 'n0:::<43-character-base64url-sha256-without-padding>', digest: @@ -738,7 +813,22 @@ const groveExchangeProtocolValue = { meaning: 'The producer records that its source no longer exposes the record and identifies the exact previously emitted graph nodes. It neither asserts clinical error nor instructs a FHIR server to delete anything.', target: - 'Each Provenance.target is a typed logical Reference with exactly one complete Identifier and one Grove target-role extension. Literal references and copied active clinical resources are prohibited.', + 'Each Provenance.target is a typed logical Reference with exactly one complete Identifier, one Grove target-role extension, and at most one Grove native-record-identifier extension. Literal references and copied active clinical resources are prohibited.', + nativeRecordIdentifier: { + optionality: 'optional', + extension: + 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-native-identifier', + valueType: 'Identifier', + maxPerTarget: 1, + systemAuthority: 'per-adapter', + system: + 'The adapter-owned absolute URI naming the exact native key space, scoped to the governed repository, account, or store exactly as a governed source identifier is on the addition path. An underspecified vendor-wide system is nonconformant.', + value: + "The exact platform-native record identifier of the retracted source record in the adapter's own canonical form, so a consumer can delete the exact native records.", + disclosurePolicy: 'governedSourceIdentifier', + prohibitions: + 'It never addresses the target: the Grove source-output or device-snapshot identity remains the retraction address. It carries no Grove identifier-role coding, and omitting it remains conformant.', + }, targetRoles: { 'primary-output': { identifierRole: 'source-output', @@ -1160,6 +1250,105 @@ const groveExchangeProtocolValue = { ], value: 'v0:test-key:1:Q2sME_dGFj94xyprI1HtMokER94GYsHINZi0ilnsnRY', }, + { + id: 'normative-corpus-heart-rate-source-record', + identityKind: 'source-record', + components: [ + 'health-connect', + 'HeartRateRecord', + 'urn:uuid:1f5c58aa-6ec6-4e79-a682-829a9debd3f5', + 'default', + 'record-heart-001', + ], + value: 'v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY', + }, + { + id: 'normative-corpus-heart-rate-source-output', + identityKind: 'source-output', + components: [ + 'health-connect', + 'HeartRateRecord', + 'urn:uuid:1f5c58aa-6ec6-4e79-a682-829a9debd3f5', + 'default', + 'record-heart-001', + 'sample', + '2026-08-20T15:30:00.251000000Z|0', + ], + value: 'v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8', + }, + { + id: 'normative-corpus-retraction-application-snapshot', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:43', + 'application', + 'com.example.app|1.2.3', + ], + value: 'v0:test-key:1:JUZooa4NZX-rlMn9-NC8W078xzEuUO9X3Xo1mgk0Vac', + }, + { + id: 'questionnaire-extraction-source-record', + identityKind: 'source-record', + components: [ + 'questionnaire', + 'QuestionnaireResponse', + 'https://study.example.org/fhir/NamingSystem/questionnaire-response', + 'default', + 'home-vitals-2026-08-28', + ], + value: 'v0:test-key:1:7_7dmqTFxPH01QGTycmp8nIPty7WwphH2X0ZaV-p3wk', + }, + { + id: 'questionnaire-extraction-body-weight-output', + identityKind: 'source-output', + components: [ + 'questionnaire', + 'QuestionnaireResponse', + 'https://study.example.org/fhir/NamingSystem/questionnaire-response', + 'default', + 'home-vitals-2026-08-28', + 'body-weight', + 'single', + ], + value: 'v0:test-key:1:oY5ARo6F_wNj5wVdtFqORMdUwZ__B9kQ6GW1RSaEz10', + }, + { + id: 'questionnaire-extraction-blood-pressure-output', + identityKind: 'source-output', + components: [ + 'questionnaire', + 'QuestionnaireResponse', + 'https://study.example.org/fhir/NamingSystem/questionnaire-response', + 'default', + 'home-vitals-2026-08-28', + 'blood-pressure', + 'single', + ], + value: 'v0:test-key:1:ljzpyfYa15oOpj_A37-cd-_8JXqQwwfomR9iT0gct6g', + }, + { + id: 'questionnaire-extraction-application-snapshot', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:6f9d1c4a-2b7e-4f18-9c33-5a1d0e7b2c48:1', + 'application', + 'org.grovealliance.example.client|1.4.0|1402', + ], + value: 'v0:test-key:1:W8r_v8DzqOippQF5DUZiyhr_iwyRQk3NTa_sQLF8aM0', + }, + { + id: 'questionnaire-extraction-host-snapshot', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:6f9d1c4a-2b7e-4f18-9c33-5a1d0e7b2c48:1', + 'host', + 'iPhone17,1|26.0', + ], + value: 'v0:test-key:1:Bjx_NBNO2-QhII9c1M5mcLUUOJRB0YSTXbg68kkTDdc', + }, ], invalidIdentities: [ { @@ -1244,6 +1433,11 @@ export const groveExchangeProtocol: typeof groveExchangeProtocolValue = deepFreeze(groveExchangeProtocolValue) const groveExchangeRuleDiagnosticsValue = { + 'mobile-exchange.unclassified': { + reason: + 'A producer-contract failure reached the exchange diagnostic boundary without a more specific registered rule; validation fails closed and the conformance kit must classify the gap.', + severity: 'error', + }, 'mobile-exchange.entry-node-key': { reason: 'Every Bundle entry must carry exactly one complete Grove exchange entry node key.', @@ -1259,11 +1453,6 @@ const groveExchangeRuleDiagnosticsValue = { 'Every internal UUID URN reference must resolve to a Bundle entry fullUrl.', severity: 'error', }, - 'mobile-output.fixed-quantity-unit': { - reason: - 'Every Quantity-valued catalog measurement uses the exact fixed system and code declared by its semantic profile contract.', - severity: 'error', - }, 'mobile-exchange.event-identity': { reason: 'Bundle.identifier.value must be the canonical e0 producer UUID and positive sequence form.', @@ -1274,9 +1463,9 @@ const groveExchangeRuleDiagnosticsValue = { 'An entry-node digest must be derived from the enclosing event identifier, role, and ordinal.', severity: 'error', }, - 'mobile-output.source-output-required': { + 'mobile-exchange.entry-node-ordinal': { reason: - 'Every active clinical output must carry its exact typed source-output identity in addition to source-record identity.', + 'An entry-node ordinal is the zero-based position of its entry among the entries sharing that node-role, in Bundle entry order.', severity: 'error', }, 'mobile-exchange.identity-system-role': { @@ -1284,6 +1473,21 @@ const groveExchangeRuleDiagnosticsValue = { 'Within one event graph, each Grove Identifier.system names exactly one Grove identifier role; one namespace cannot change meaning between nodes.', severity: 'error', }, + 'mobile-output.source-output-required': { + reason: + 'Every active clinical output must carry its exact typed source-output identity in addition to source-record identity.', + severity: 'error', + }, + 'mobile-output.hybrid-companion': { + reason: + 'A hybrid-required output and its exact source-preservation companion must form the catalog-declared closed, same-source, bidirectionally linked graph.', + severity: 'error', + }, + 'healthkit-ecg.output-graph': { + reason: + 'A HealthKit ECG event must satisfy the catalog-owned waveform, optional average-heart-rate, symptom-member, relationship-direction, identity, and effective-period graph contract.', + severity: 'error', + }, 'mobile-exchange.transform-provenance': { reason: 'An active event must contain exactly one transform Provenance and no retraction Provenance.', @@ -1304,6 +1508,36 @@ const groveExchangeRuleDiagnosticsValue = { 'A retraction target must use the exact canonical v0 HMAC identity previously emitted.', severity: 'error', }, + 'mobile-retraction.native-record-identifier': { + reason: + "An optional retraction native record identifier carries one complete Identifier in the adapter's own absolute native key space and never a Grove identifier-role coding.", + severity: 'error', + }, + 'mobile-exchange.collection-entry-operation': { + reason: + 'A Mobile exchange event is a collection Bundle; an entry may carry neither a request nor a response, because an event is an assertion and never a transaction instruction.', + severity: 'error', + }, + 'mobile-support.questionnaire-response-profile': { + reason: + 'Every supporting QuestionnaireResponse must directly claim exactly the Grove Questionnaire Response profile.', + severity: 'error', + }, + 'mobile-device.recording-device-dual-identity': { + reason: + 'A recording Device carries both its durable recording-device identity and its event-scoped device-snapshot identity; neither may stand alone.', + severity: 'error', + }, + 'healthkit-clinical.fhir-representation': { + reason: + 'A HealthKit clinical-record envelope must carry the exact admitted FHIR release and preserve the source representation its adapter contract declares.', + severity: 'error', + }, + 'sensor-recording-document.identity-and-content': { + reason: + 'Every Sensor Recording Document carries its source-record, source-output, and source-artifact identities together with exactly one registered attachment payload.', + severity: 'error', + }, 'mobile-retraction.no-clinical-copy': { reason: 'A retraction event contains its lifecycle Provenance and optional Device agents, never a copied or mutilated clinical resource.', @@ -1319,6 +1553,16 @@ const groveExchangeRuleDiagnosticsValue = { 'Every active Observation must directly claim one admitted Grove semantic profile shape; an empty claim cannot bypass semantic validation.', severity: 'error', }, + 'mobile-output.fixed-quantity-unit': { + reason: + 'Every Quantity-valued catalog measurement uses the exact fixed system and code declared by its semantic profile contract.', + severity: 'error', + }, + 'mobile-output.quantity-value-domain': { + reason: + 'Every Quantity-valued catalog measurement stays within its catalog-declared representational minimum, maximum, and integer-only domain without inventing a physiologic range.', + severity: 'error', + }, 'mobile-exchange.reference-target-type': { reason: 'Every governed Patient reference resolves to a Patient entry, not merely to any existing fullUrl.', @@ -1354,14 +1598,14 @@ const groveExchangeRuleDiagnosticsValue = { 'An identifier-only logical Patient Reference carries the exact Patient type and one complete absolute-system pseudonym Identifier without a Grove role or protocol-reserved system.', severity: 'error', }, - 'mobile-exchange.entry-resource-type': { + 'mobile-output.adapter-only-profile': { reason: - 'An active event admits only its closed output, supporting, and lifecycle resource type set.', + 'An adapter-only active output type must directly claim exactly its one admitted adapter profile.', severity: 'error', }, - 'mobile-output.adapter-only-profile': { + 'mobile-exchange.entry-resource-type': { reason: - 'An adapter-only active output type must directly claim exactly its one admitted adapter profile.', + 'An active event admits only its closed output, supporting, and lifecycle resource type set.', severity: 'error', }, 'mobile-exchange.contained-resource-prohibited': { @@ -1379,14 +1623,14 @@ const groveExchangeRuleDiagnosticsValue = { 'Every active Device must directly claim exactly one admitted Grove Device profile mode.', severity: 'error', }, - 'mobile-exchange.provenance-profile': { + 'mobile-support.connected': { reason: - 'The sole active lifecycle Provenance must directly claim exactly one admitted Mobile or adapter conversion profile.', + 'Every supporting resource must be connected to an output or the lifecycle Provenance.', severity: 'error', }, - 'mobile-support.connected': { + 'mobile-exchange.provenance-profile': { reason: - 'Every supporting resource must be connected to an output or the lifecycle Provenance.', + 'The sole active lifecycle Provenance must directly claim exactly one admitted Mobile or adapter conversion profile.', severity: 'error', }, } as const @@ -1399,7 +1643,6 @@ const groveProfileClaimsValue = { 'https://grovealliance.org/fhir/catalog/schemas/catalog-contracts.schema.json', schemaVersion: 0, fhirVersion: '4.0.1', - version: '0.6.0', observationAdapterClaim: { cardinality: 2, members: [ @@ -2163,7 +2406,7 @@ const adapterSourceMarkerClaimsValue = [ markers: [ { kind: 'extension-url', - url: 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-source-type-extension', + url: 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-source-type', resourceTypes: [ 'Observation', 'DocumentReference', @@ -2265,61 +2508,60 @@ const groveRecordingFormatRegistryValue = { 'https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format', valueSet: 'https://grovealliance.org/fhir/sensor/ValueSet/grove-recording-format', - version: '0.6.0', formats: { 'heart-rate-samples': { title: 'Heart Rate Samples', - contentType: 'text/csv', + contentTypes: ['text/csv'], status: 'active', }, 'triaxial-acceleration-samples': { title: 'Triaxial Acceleration Samples', - contentType: 'text/csv', + contentTypes: ['text/csv'], status: 'active', }, 'ambient-light-samples': { title: 'Ambient Light Samples', - contentType: 'text/csv', + contentTypes: ['text/csv'], status: 'active', }, 'ambient-pressure-samples': { title: 'Ambient Pressure Samples', - contentType: 'text/csv', + contentTypes: ['text/csv'], status: 'active', }, 'pedometer-samples': { title: 'Pedometer Samples', - contentType: 'text/csv', + contentTypes: ['text/csv'], status: 'active', }, 'wrist-temperature-samples': { title: 'Wrist Temperature Samples', - contentType: 'text/csv', + contentTypes: ['text/csv'], status: 'active', }, 'triaxial-rotation-samples': { title: 'Triaxial Rotation Samples', - contentType: 'text/csv', + contentTypes: ['text/csv'], status: 'active', }, 'odometer-samples': { title: 'Odometer Samples', - contentType: 'text/csv', + contentTypes: ['text/csv'], status: 'active', }, 'beat-interval-series': { title: 'Beat Interval Series', - contentType: 'text/csv', + contentTypes: ['text/csv'], status: 'active', }, 'location-track-samples': { title: 'Location Track Samples', - contentType: 'text/csv', + contentTypes: ['text/csv'], status: 'active', }, 'fhir-collection-bundle': { title: 'FHIR R4 Collection Bundle', - contentType: 'application/fhir+json', + contentTypes: ['application/fhir+json'], status: 'active', }, 'fhir-resource': { @@ -2332,22 +2574,22 @@ const groveRecordingFormatRegistryValue = { }, 'clinical-document': { title: 'Clinical Document', - contentType: 'application/hl7-cda+xml', + contentTypes: ['application/hl7-cda+xml'], status: 'active', }, 'native-recording': { title: 'Native Recording', - contentType: 'application/json', + contentTypes: ['application/json'], status: 'active', }, 'provider-recording': { title: 'Provider Recording', - contentType: 'application/json', + contentTypes: ['application/json'], status: 'active', }, 'photoplethysmogram-samples': { title: 'Photoplethysmogram Samples', - contentType: 'application/octet-stream', + contentTypes: ['application/octet-stream'], status: 'active', }, }, @@ -4801,6 +5043,7 @@ const sharedMobileMeasurementCatalogValue = { }, }, effective: 'Period', + obeys: ['grove-step-count-period-1'], }, workout: { id: 'workout', diff --git a/packages/fhir/src/contract/providers.generated.ts b/packages/fhir/src/contract/providers.generated.ts index b78fdd0b..5a1ca469 100644 --- a/packages/fhir/src/contract/providers.generated.ts +++ b/packages/fhir/src/contract/providers.generated.ts @@ -11,8 +11,6 @@ import { deepFreeze } from '../core/index.js' -export const groveFhirContractVersion = '0.6.0' as const - export const groveFhirVersion = '4.0.1' as const const groveProviderPackageMetadataValue = { @@ -521,7 +519,6 @@ const providerAdapterCatalogValue = { 'https://grovealliance.org/fhir/catalog/schemas/catalog-contracts.schema.json', schemaVersion: 0, fhirVersion: '4.0.1', - version: '0.6.0', packageId: 'org.grovealliance.fhir.providers', canonical: 'https://grovealliance.org/fhir/providers', adapterProfile: @@ -568,15 +565,7 @@ const providerAdapterCatalogValue = { }, conversionProvenanceProfile: 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance', - statusVocabulary: [ - 'supported', - 'mapped-standard', - 'platform-exclusive', - 'unmodeled', - 'deferred', - 'intentionally-unsupported', - ], - statusDefinitions: { + statusVocabulary: { supported: 'The Grove FHIR contracts define an exact conversion to every semantic profile listed for the source element, whether the profile is shared or provider-owned.', 'mapped-standard': diff --git a/packages/fhir/src/contract/questionnaire.generated.ts b/packages/fhir/src/contract/questionnaire.generated.ts index a60597cc..1040d0d4 100644 --- a/packages/fhir/src/contract/questionnaire.generated.ts +++ b/packages/fhir/src/contract/questionnaire.generated.ts @@ -9,8 +9,6 @@ import { deepFreeze } from '../core/index.js' -export const groveFhirContractVersion = '0.6.0' as const - export const groveFhirVersion = '4.0.1' as const const groveQuestionnairePackageMetadataValue = { diff --git a/packages/fhir/src/zod/r4/schemas.generated.ts b/packages/fhir/src/zod/r4/schemas.generated.ts index b3dd8478..61bdc2af 100644 --- a/packages/fhir/src/zod/r4/schemas.generated.ts +++ b/packages/fhir/src/zod/r4/schemas.generated.ts @@ -49009,6 +49009,157 @@ export const resourceSchema: z.ZodType> = z } }) +/** Every resourceType code the release publishes, abstract types included. */ +export const resourceTypeCodes = [ + 'Account', + 'ActivityDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'EffectEvidenceSynthesis', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProduct', + 'MedicinalProductAuthorization', + 'MedicinalProductContraindication', + 'MedicinalProductIndication', + 'MedicinalProductIngredient', + 'MedicinalProductInteraction', + 'MedicinalProductManufactured', + 'MedicinalProductPackaged', + 'MedicinalProductPharmaceutical', + 'MedicinalProductUndesirableEffect', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'RiskEvidenceSynthesis', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'Substance', + 'SubstanceNucleicAcid', + 'SubstancePolymer', + 'SubstanceProtein', + 'SubstanceReferenceInformation', + 'SubstanceSourceMaterial', + 'SubstanceSpecification', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', +] as const + /** Exact pinned release material and generated closure behind this module. */ export const STRUCTURAL_SCHEMA_SOURCE = { packageId: 'hl7.fhir.r4.core', diff --git a/packages/fhir/src/zod/r4b/schemas.generated.ts b/packages/fhir/src/zod/r4b/schemas.generated.ts index 7f294f0d..385ad52f 100644 --- a/packages/fhir/src/zod/r4b/schemas.generated.ts +++ b/packages/fhir/src/zod/r4b/schemas.generated.ts @@ -50190,6 +50190,152 @@ export const resourceSchema: z.ZodType> = z } }) +/** Every resourceType code the release publishes, abstract types included. */ +export const resourceTypeCodes = [ + 'Account', + 'ActivityDefinition', + 'AdministrableProductDefinition', + 'AdverseEvent', + 'AllergyIntolerance', + 'Appointment', + 'AppointmentResponse', + 'AuditEvent', + 'Basic', + 'Binary', + 'BiologicallyDerivedProduct', + 'BodyStructure', + 'Bundle', + 'CapabilityStatement', + 'CarePlan', + 'CareTeam', + 'CatalogEntry', + 'ChargeItem', + 'ChargeItemDefinition', + 'Citation', + 'Claim', + 'ClaimResponse', + 'ClinicalImpression', + 'ClinicalUseDefinition', + 'CodeSystem', + 'Communication', + 'CommunicationRequest', + 'CompartmentDefinition', + 'Composition', + 'ConceptMap', + 'Condition', + 'Consent', + 'Contract', + 'Coverage', + 'CoverageEligibilityRequest', + 'CoverageEligibilityResponse', + 'DetectedIssue', + 'Device', + 'DeviceDefinition', + 'DeviceMetric', + 'DeviceRequest', + 'DeviceUseStatement', + 'DiagnosticReport', + 'DocumentManifest', + 'DocumentReference', + 'Encounter', + 'Endpoint', + 'EnrollmentRequest', + 'EnrollmentResponse', + 'EpisodeOfCare', + 'EventDefinition', + 'Evidence', + 'EvidenceReport', + 'EvidenceVariable', + 'ExampleScenario', + 'ExplanationOfBenefit', + 'FamilyMemberHistory', + 'Flag', + 'Goal', + 'GraphDefinition', + 'Group', + 'GuidanceResponse', + 'HealthcareService', + 'ImagingStudy', + 'Immunization', + 'ImmunizationEvaluation', + 'ImmunizationRecommendation', + 'ImplementationGuide', + 'Ingredient', + 'InsurancePlan', + 'Invoice', + 'Library', + 'Linkage', + 'List', + 'Location', + 'ManufacturedItemDefinition', + 'Measure', + 'MeasureReport', + 'Media', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationKnowledge', + 'MedicationRequest', + 'MedicationStatement', + 'MedicinalProductDefinition', + 'MessageDefinition', + 'MessageHeader', + 'MolecularSequence', + 'NamingSystem', + 'NutritionOrder', + 'NutritionProduct', + 'Observation', + 'ObservationDefinition', + 'OperationDefinition', + 'OperationOutcome', + 'Organization', + 'OrganizationAffiliation', + 'PackagedProductDefinition', + 'Parameters', + 'Patient', + 'PaymentNotice', + 'PaymentReconciliation', + 'Person', + 'PlanDefinition', + 'Practitioner', + 'PractitionerRole', + 'Procedure', + 'Provenance', + 'Questionnaire', + 'QuestionnaireResponse', + 'RegulatedAuthorization', + 'RelatedPerson', + 'RequestGroup', + 'ResearchDefinition', + 'ResearchElementDefinition', + 'ResearchStudy', + 'ResearchSubject', + 'Resource', + 'RiskAssessment', + 'Schedule', + 'SearchParameter', + 'ServiceRequest', + 'Slot', + 'Specimen', + 'SpecimenDefinition', + 'StructureDefinition', + 'StructureMap', + 'Subscription', + 'SubscriptionStatus', + 'SubscriptionTopic', + 'Substance', + 'SubstanceDefinition', + 'SupplyDelivery', + 'SupplyRequest', + 'Task', + 'TerminologyCapabilities', + 'TestReport', + 'TestScript', + 'ValueSet', + 'VerificationResult', + 'VisionPrescription', +] as const + /** Exact pinned release material and generated closure behind this module. */ export const STRUCTURAL_SCHEMA_SOURCE = { packageId: 'hl7.fhir.r4b.core', From 1f8b74c1cf880b8f39947abbb1bded9665c3bf88 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Mon, 31 Aug 2026 11:27:39 -0700 Subject: [PATCH 19/31] Pin the Public Export Surface and Cover the Regenerated Contract --- packages/fhir/src/index.ts | 95 ++++++- packages/fhir/test/diagnostics.test.ts | 91 ++++--- packages/fhir/test/exchange-corpus.test.ts | 131 +++++++--- packages/fhir/test/exchange-protocol.test.ts | 52 +++- packages/fhir/test/export-surface.json | 246 ++++++++++++++++++ .../fhir/test/export-surface.json.license | 3 + packages/fhir/test/exports.test.ts | 80 +++--- .../fhir/test/foundation-boundaries.test.ts | 7 +- packages/fhir/test/foundation.test.ts | 62 ----- packages/fhir/test/mobile.test.ts | 13 +- .../fhir/test/observation-semantics.test.ts | 158 +++++------ .../test/profile-semantics-boundaries.test.ts | 121 ++++----- packages/fhir/test/provider-contract.test.ts | 8 +- packages/fhir/test/provider-identity.test.ts | 17 +- .../fhir/test/questionnaire-builder.test.ts | 46 +++- .../test/questionnaire-input-surface.test.ts | 1 - ...uestionnaire-preflight-expressions.test.ts | 16 ++ .../fhir/test/questionnaire-preflight.test.ts | 11 +- packages/fhir/test/recording.test.ts | 149 ++++------- packages/fhir/test/retraction.test.ts | 92 ++++++- packages/fhir/test/rule-test-support.ts | 50 ++++ packages/fhir/test/tsconfig.json | 3 +- packages/fhir/test/zod-fixtures.test.ts | 4 +- .../test/zod-generation-invariants.test.ts | 5 +- packages/fhir/test/zod-schemas.test.ts | 33 ++- 25 files changed, 1025 insertions(+), 469 deletions(-) create mode 100644 packages/fhir/test/export-surface.json create mode 100644 packages/fhir/test/export-surface.json.license create mode 100644 packages/fhir/test/rule-test-support.ts diff --git a/packages/fhir/src/index.ts b/packages/fhir/src/index.ts index bc1e045f..21d14bf2 100644 --- a/packages/fhir/src/index.ts +++ b/packages/fhir/src/index.ts @@ -42,7 +42,99 @@ export { type UrnUuid, } from './core/index.js' -export * from './extract/index.js' +export { + annotationTexts, + appointmentServiceTypeDisplay, + boundsToFhirPeriod, + carePlanCategoryDisplays, + carePlanCreatedDate, + carePlanNoteTexts, + carePlanPeriodEnd, + carePlanPeriodIsActive, + carePlanPeriodStart, + codeableConceptDisplay, + codeableConceptDisplays, + codesBySystem, + codingToCodeableConcept, + conditionAbatementDate, + conditionCategoryDisplays, + conditionClinicalStatus, + conditionCodeDisplay, + conditionDuration, + conditionIsActive, + conditionIsConfirmed, + conditionOnsetDate, + conditionRecordedDate, + contactPointBySystem, + contactPointsBySystem, + containedResource, + containedResourcesByType, + containsCoding, + dateToFhirDate, + dateToFhirDateTime, + dateToFhirInstant, + decodeBase64Binary, + encodeBase64Binary, + extensionByUrl, + extensionsByUrl, + fhirPeriodToBounds, + formatHumanName, + humanNameParts, + identifierBySystem, + identifierByType, + identifiersBySystem, + identifiersByType, + immunizationExpirationDate, + immunizationIsExpired, + immunizationNoteTexts, + immunizationOccurrenceDate, + immunizationReactionDisplays, + immunizationRecordedDate, + immunizationRouteDisplay, + immunizationSiteDisplay, + immunizationVaccineDisplay, + isReferenceToType, + observationBooleanValue, + observationCodeDisplay, + observationEffectiveDate, + observationEffectiveEnd, + observationEffectiveOverlaps, + observationHasCategory, + observationInterpretationDisplays, + observationIssuedDate, + observationNoteTexts, + observationNumericValue, + observationQuantity, + observationStringValue, + observationUnit, + observationValueDisplay, + patientAgeInYears, + patientBirthDate, + patientDeceasedDate, + patientEmailAddresses, + patientIsDeceased, + patientMaritalStatusDisplay, + patientName, + patientPhoneNumbers, + periodDuration, + periodIsActive, + periodOverlaps, + referenceType, + scheduledCovers, + scheduledDuration, + scheduledEnd, + scheduledIsPast, + scheduledIsUpcoming, + scheduledStart, + slotSpecialtyDisplays, + toFhirReference, + toReference, + valueToFhirQuantity, + type CodingParts, + type HumanNameOptions, + type IdentifierParts, + type PeriodBounds, +} from './extract/index.js' export { parseGroveMobileExchangeBundle, @@ -76,7 +168,6 @@ export { deriveEventIdentifier, deriveOpaqueIdentifier, encodeLengthFramedUtf8, - groveFhirContractVersion, groveExchangeProtocol, groveProfileClaims, groveFhirVersion, diff --git a/packages/fhir/test/diagnostics.test.ts b/packages/fhir/test/diagnostics.test.ts index 0b5b0587..ccceb98d 100644 --- a/packages/fhir/test/diagnostics.test.ts +++ b/packages/fhir/test/diagnostics.test.ts @@ -6,32 +6,51 @@ // SPDX-License-Identifier: MIT // -import { groveExchangeRuleDiagnostics } from '../src/contract/measurement-catalog.generated.js' import { - decodeGroveRuleDiagnostic, - encodeGroveRuleDiagnostic, + groveExchangeProtocol, + groveExchangeRuleDiagnostics, +} from '../src/contract/measurement-catalog.generated.js' +import { groveRuleIssue, + groveRuleIssueFromParameters, + groveRuleParameters, + groveRuleReason, type GroveExchangeRuleCode, } from '../src/r4/diagnostics.js' +const byText = (left: string, right: string): number => + left.localeCompare(right) + const ruleCodes = Object.keys( groveExchangeRuleDiagnostics, ) as GroveExchangeRuleCode[] describe('stable Grove rule diagnostics', () => { + it('registers exactly the protocol producer diagnostics', () => { + expect([...ruleCodes].sort(byText)).toEqual( + groveExchangeProtocol.producerDiagnostics + .map(({ code }) => code) + .sort(byText), + ) + }) + it.each(ruleCodes)( 'round-trips %s with its public reason and location', (code) => { const path = ['entry', 3, 'resource', 'target', 2, 'extension', 0] const issue = groveRuleIssue(code, path) - const encoded = encodeGroveRuleDiagnostic(code, path, 'fallback') - expect(decodeGroveRuleDiagnostic(encoded)).toEqual({ - code, - reason: groveExchangeRuleDiagnostics[code].reason, - location: issue.location, - severity: groveExchangeRuleDiagnostics[code].severity, - }) + expect( + groveRuleIssueFromParameters( + groveRuleParameters(code, path), + path, + 'fallback', + ), + ).toEqual(issue) + expect(issue.reason).toBe(groveExchangeRuleDiagnostics[code].reason) + expect(groveRuleReason(code)).toBe( + groveExchangeRuleDiagnostics[code].reason, + ) }, ) @@ -41,30 +60,36 @@ describe('stable Grove rule diagnostics', () => { ).toBe('Provenance.target[0]') }) - it('rejects malformed or forged encoded diagnostics', () => { - const code = ruleCodes[0] - if (code === undefined) throw new Error('The rule catalog is empty.') - const valid = encodeGroveRuleDiagnostic(code, [], 'fallback') - const prefix = valid.slice(0, valid.indexOf('{')) - const reason = groveExchangeRuleDiagnostics[code].reason - const cases = [ - 'not encoded', - `${prefix}{`, - `${prefix}${JSON.stringify({ code: 42, reason, location: 'Bundle', severity: 'error' })}`, - `${prefix}${JSON.stringify({ code: 'unknown', reason, location: 'Bundle', severity: 'error' })}`, - `${prefix}${JSON.stringify({ code, reason: 'forged', location: 'Bundle', severity: 'error' })}`, - `${prefix}${JSON.stringify({ code, reason, location: '', severity: 'error' })}`, - `${prefix}${JSON.stringify({ code, reason, location: 42, severity: 'error' })}`, - `${prefix}${JSON.stringify({ code, reason, location: 'Bundle', severity: 'warning' })}`, - ] + it('carries a locally named rule without a registered reason or location', () => { + const parameters = groveRuleParameters('mobile-step-count.nonzero-period', [ + 'entry', + 0, + ]) - for (const candidate of cases) { - expect(decodeGroveRuleDiagnostic(candidate)).toBeUndefined() - } + expect(groveRuleReason('mobile-step-count.nonzero-period')).toBeUndefined() + expect(parameters).toEqual({ + groveRuleCode: 'mobile-step-count.nonzero-period', + }) expect( - decodeGroveRuleDiagnostic( - encodeGroveRuleDiagnostic('unknown', [], 'fallback'), - ), - ).toBeUndefined() + groveRuleIssueFromParameters(parameters, ['entry', 0], 'local message'), + ).toEqual({ + severity: 'error', + code: 'mobile-step-count.nonzero-period', + path: ['entry', 0], + message: 'local message', + }) + }) + + it('ignores issue parameters that name no producer rule', () => { + for (const parameters of [ + undefined, + {}, + { groveRuleCode: 42 }, + { groveRuleCode: 'undotted' }, + ]) { + expect( + groveRuleIssueFromParameters(parameters, [], 'message'), + ).toBeUndefined() + } }) }) diff --git a/packages/fhir/test/exchange-corpus.test.ts b/packages/fhir/test/exchange-corpus.test.ts index c2415eb2..a0d6e1fc 100644 --- a/packages/fhir/test/exchange-corpus.test.ts +++ b/packages/fhir/test/exchange-corpus.test.ts @@ -9,6 +9,7 @@ /* eslint-disable sonarjs/no-clear-text-protocols -- The FHIR R4 lifecycle and terminology canonicals are normative HTTP URIs. */ import { readFileSync } from 'node:fs' +import { adapterSourceMarkerClaims } from '../src/contract/measurement-catalog.generated.js' import { parseGroveMobileExchangeBundle, parseGroveMobileRetractionBundle, @@ -54,6 +55,13 @@ const bases = { } as const const corpus = fixture('corpus.json') as ExchangeCorpus +const healthKitSourceMarkerUrl = adapterSourceMarkerClaims.find( + ({ adapter }) => adapter === 'healthkit', +)?.markers[0]?.url +if (healthKitSourceMarkerUrl === undefined) { + throw new Error('Missing HealthKit source marker.') +} + const pointerComponents = (pointer: string): string[] => pointer .slice(1) @@ -152,6 +160,50 @@ describe('shared Grove Mobile exchange corpus', () => { expect(parseGroveMobileExchangeBundle(input).ok).toBe(true) }) + it.each([ + { entry: 2, resourceType: 'Observation' }, + { entry: 4, resourceType: 'QuestionnaireResponse' }, + ])('rejects a $resourceType with no subject at all', ({ entry }) => { + const input = patched(bases['mobile-exchange'], [ + { op: 'remove', path: `/entry/${String(entry)}/resource/subject` }, + ]) + const result = parseGroveMobileExchangeBundle(input) + expect(result.ok).toBe(false) + if (result.ok) return + expect( + result.issues.find( + ({ code }) => code === 'mobile-exchange.reference-shape', + )?.location, + ).toBe( + `${String((bases['mobile-exchange'] as { entry: Array<{ resource: { resourceType: string } }> }).entry[entry]?.resource.resourceType)}.subject`, + ) + }) + + it('counts entry-node ordinals over the Bundle, never over the value itself', () => { + const key = ( + bases['mobile-exchange'] as { + entry: Array<{ + extension: Array<{ valueIdentifier: { value: string } }> + }> + } + ).entry[0]?.extension[0]?.valueIdentifier.value + expect(key).toMatch(/^n0:patient:0:/u) + // A self-consistent misnumbering: role and ordinal still agree with the digest. + const misnumbered = patched(bases['mobile-exchange'], [ + { + op: 'replace', + path: '/entry/0/extension/0/valueIdentifier/value', + value: 'n0:patient:1:qN9xaTdoz-LfvONC7FgEUNMz06zyWhfzD0khPhtjVig', + }, + ]) + const result = parseGroveMobileExchangeBundle(misnumbered) + expect(result.ok).toBe(false) + if (result.ok) return + const codes = result.issues.map(({ code }) => code) + expect(codes).toContain('mobile-exchange.entry-node-ordinal') + expect(codes).not.toContain('mobile-exchange.entry-node-digest') + }) + it('reports the actual unresolved reference path instead of assuming subject', () => { const input = patched(bases['mobile-exchange'], [ { @@ -220,7 +272,7 @@ describe('shared Grove Mobile exchange corpus', () => { op: 'add', path: '/entry/2/resource/extension/1', value: { - url: 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-source-type-extension', + url: healthKitSourceMarkerUrl, valueCode: 'HKQuantityTypeIdentifierHeartRate', }, }, @@ -228,11 +280,9 @@ describe('shared Grove Mobile exchange corpus', () => { const result = parseGroveMobileExchangeBundle(input) expect(result.ok).toBe(false) if (!result.ok) { - expect( - result.issues.some(({ message }) => - message.includes('mobile-exchange.adapter-source-marker'), - ), - ).toBe(true) + expect(result.issues.map(({ code }) => code)).toContain( + 'mobile-exchange.adapter-source-marker', + ) } }) @@ -254,11 +304,9 @@ describe('shared Grove Mobile exchange corpus', () => { const result = parseGroveMobileExchangeBundle(input) expect(result.ok).toBe(false) if (!result.ok) { - expect( - result.issues.some(({ message }) => - message.includes('mobile-exchange.adapter-source-marker'), - ), - ).toBe(true) + expect(result.issues.map(({ code }) => code)).toContain( + 'mobile-exchange.adapter-source-marker', + ) } }) @@ -322,11 +370,9 @@ describe('shared Grove Mobile exchange corpus', () => { const result = parseGroveMobileExchangeBundle(literalDataOrigin) expect(result.ok).toBe(false) if (!result.ok) { - expect( - result.issues.some(({ message }) => - message.includes('health-connect.data-origin-application'), - ), - ).toBe(true) + expect(result.issues.map(({ code }) => code)).toContain( + 'health-connect.data-origin-application', + ) } }) @@ -360,6 +406,7 @@ describe('shared Grove Mobile exchange corpus', () => { it.each([ { name: 'missing quantity value', + expectedCode: 'mobile-output.fixed-quantity-unit', patch: [ { op: 'remove' as const, @@ -369,6 +416,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'impossible effective calendar date', + expectedCode: 'schema-invalid', patch: [ { op: 'replace' as const, @@ -379,6 +427,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'duplicated primary clinical coding', + expectedCode: 'mobile-heart-rate.code', patch: [ { op: 'add' as const, @@ -389,6 +438,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'duplicated category coding', + expectedCode: 'mobile-heart-rate.category', patch: [ { op: 'add' as const, @@ -403,6 +453,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'unprofiled Device snapshot', + expectedCode: 'mobile-support.device-profile', patch: [ { op: 'remove' as const, @@ -412,6 +463,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'Device snapshot with an additional arbitrary profile', + expectedCode: 'mobile-support.device-profile', patch: [ { op: 'add' as const, @@ -422,6 +474,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'empty contained array in an active graph', + expectedCode: 'mobile-exchange.contained-resource-prohibited', patch: [ { op: 'add' as const, @@ -432,6 +485,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'HealthKit application Device without its product identifier', + expectedCode: 'healthkit-application-device.bundle-identifier', patch: [ { op: 'replace' as const, @@ -443,6 +497,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'application Device with an extra identity role', + expectedCode: 'mobile-support.device-profile', patch: [ { op: 'add' as const, @@ -466,6 +521,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'relative reference to another Bundle entry', + expectedCode: 'mobile-exchange.resolved-reference', patch: [ { op: 'replace' as const, @@ -476,6 +532,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'duplicated active exchange profile claim', + expectedCode: 'mobile-exchange.profile', patch: [ { op: 'add' as const, @@ -487,6 +544,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'simultaneous active and retraction profile claims', + expectedCode: 'mobile-exchange.profile', patch: [ { op: 'add' as const, @@ -498,6 +556,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'duplicated semantic Observation profile claim', + expectedCode: 'mobile-output.semantic-profile', patch: [ { op: 'add' as const, @@ -509,6 +568,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'adapter output profile under source-neutral Provenance', + expectedCode: 'mobile-output.semantic-profile', patch: [ { op: 'add' as const, @@ -520,6 +580,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'adapter Provenance targeting a source-neutral output', + expectedCode: 'mobile-exchange.adapter-provenance-graph', patch: [ { op: 'replace' as const, @@ -531,6 +592,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'duplicated transform lifecycle coding', + expectedCode: 'mobile-exchange.lifecycle-coding', patch: [ { op: 'add' as const, @@ -544,6 +606,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'different ISO lifecycle coding beside transform', + expectedCode: 'mobile-exchange.lifecycle-coding', patch: [ { op: 'add' as const, @@ -557,6 +620,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'different Grove lifecycle coding in active event', + expectedCode: 'mobile-exchange.lifecycle-coding', patch: [ { op: 'add' as const, @@ -569,16 +633,19 @@ describe('shared Grove Mobile exchange corpus', () => { }, ], }, - ])('rejects adversarial $name input', ({ patch }) => { - expect( - parseGroveMobileExchangeBundle(patched(bases['mobile-exchange'], patch)) - .ok, - ).toBe(false) + ])('rejects adversarial $name input', ({ patch, expectedCode }) => { + const result = parseGroveMobileExchangeBundle( + patched(bases['mobile-exchange'], patch), + ) + expect(result.ok).toBe(false) + if (result.ok) return + expect(result.issues.map(({ code }) => code)).toContain(expectedCode) }) it.each([ { name: 'different Grove lifecycle coding beside retraction', + expectedCode: 'mobile-exchange.lifecycle-coding', value: { system: 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-lifecycle-event', @@ -587,23 +654,21 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'ISO lifecycle coding in retraction event', + expectedCode: 'mobile-exchange.lifecycle-coding', value: { system: 'http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle', code: 'originate', }, }, - ])('rejects adversarial retraction $name', ({ value }) => { - expect( - parseGroveMobileRetractionBundle( - patched(bases['mobile-retraction'], [ - { - op: 'add', - path: '/entry/0/resource/activity/coding/1', - value, - }, - ]), - ).ok, - ).toBe(false) + ])('rejects adversarial retraction $name', ({ value, expectedCode }) => { + const result = parseGroveMobileRetractionBundle( + patched(bases['mobile-retraction'], [ + { op: 'add', path: '/entry/0/resource/activity/coding/1', value }, + ]), + ) + expect(result.ok).toBe(false) + if (result.ok) return + expect(result.issues.map(({ code }) => code)).toContain(expectedCode) }) it('rejects even an empty contained array in a retraction graph', () => { diff --git a/packages/fhir/test/exchange-protocol.test.ts b/packages/fhir/test/exchange-protocol.test.ts index e18a330b..365ff58d 100644 --- a/packages/fhir/test/exchange-protocol.test.ts +++ b/packages/fhir/test/exchange-protocol.test.ts @@ -106,7 +106,7 @@ const deriveVectorIdentity = ( components, ) -describe('Grove exchange protocol v0 identity', () => { +describe('Grove exchange protocol identity', () => { it('fails closed without throwing at every untyped identity boundary', () => { const invalidValues = [null, undefined, 42, 'wrong-shape', Symbol('x')] for (const invalid of invalidValues) { @@ -150,6 +150,54 @@ describe('Grove exchange protocol v0 identity', () => { expect(validateDeploymentIdentity(cyclic as never).ok).toBe(false) }) + it('keeps a reused deployment handle immune to mutation', () => { + const validated = unwrap(validateDeploymentIdentity(runtimeDeployment)) + const components: GroveOpaqueIdentityComponents['source-record'] = [ + 'healthkit', + 'HKQuantityTypeIdentifierHeartRate', + 'https://study.example.org/fhir/NamingSystem/participant', + 'participant-1', + 'native-1', + ] + const before = unwrap( + deriveOpaqueIdentifier(validated, 'source-record', components), + ) + const event = unwrap(deriveEventIdentifier(validated, '1')) + + const mutable = validated as { + identity: { keyId: string } + secret?: Uint8Array + } + expect(mutable.secret).toBeUndefined() + for (const mutate of [ + () => { + mutable.identity.keyId = 'TAMPERED' + }, + () => { + mutable.identity = { keyId: 'TAMPERED' } + }, + () => { + mutable.secret = new Uint8Array(32) + }, + ]) { + try { + mutate() + } catch { + // A frozen handle rejects the write; a sloppy-mode caller silently keeps the original. + } + } + + expect(validated.identity.keyId).toBe(runtimeDeployment.keyId) + expect( + deriveOpaqueIdentifier(validated, 'source-record', components), + ).toEqual({ ok: true, value: before }) + expect(deriveEventIdentifier(validated, '1')).toEqual({ + ok: true, + value: event, + }) + expect(validateDeploymentIdentity(validated.identity).ok).toBe(true) + }) + it.each(identityVectors)( 'matches the shared $identityKind HMAC vector byte for byte', (vector) => { @@ -504,7 +552,7 @@ describe('Grove exchange protocol v0 identity', () => { expect( deriveEntryNodeIdentifier(runtimeDeployment, event, 'Uppercase', '0').ok, ).toBe(false) - expect(isEventIdentityValue(42 as never)).toBe(false) + expect(isEventIdentityValue(42)).toBe(false) expect(isEventIdentityValue('e0:not-a-uuid:1')).toBe(false) expect(containsIsolatedSurrogate(42 as never)).toBe(true) }) diff --git a/packages/fhir/test/export-surface.json b/packages/fhir/test/export-surface.json new file mode 100644 index 00000000..e6bea024 --- /dev/null +++ b/packages/fhir/test/export-surface.json @@ -0,0 +1,246 @@ +{ + ".": [ + "annotationTexts", + "appointmentServiceTypeDisplay", + "boundsToFhirPeriod", + "buildQuestionnaire", + "buildQuestionnaireResponse", + "canonicalizeMobileEffectiveInstant", + "carePlanCategoryDisplays", + "carePlanCreatedDate", + "carePlanNoteTexts", + "carePlanPeriodEnd", + "carePlanPeriodIsActive", + "carePlanPeriodStart", + "codeableConceptDisplay", + "codeableConceptDisplays", + "codesBySystem", + "codingToCodeableConcept", + "collectResults", + "conditionAbatementDate", + "conditionCategoryDisplays", + "conditionClinicalStatus", + "conditionCodeDisplay", + "conditionDuration", + "conditionIsActive", + "conditionIsConfirmed", + "conditionOnsetDate", + "conditionRecordedDate", + "contactPointBySystem", + "contactPointsBySystem", + "containedResource", + "containedResourcesByType", + "containsCoding", + "createEntryIdentity", + "dateToFhirDate", + "dateToFhirDateTime", + "dateToFhirInstant", + "decodeBase64Binary", + "deepFreeze", + "deriveEntryFullUrl", + "deriveEntryNodeIdentifier", + "deriveEntryNodeValue", + "deriveEventIdentifier", + "deriveOpaqueIdentifier", + "encodeBase64Binary", + "encodeLengthFramedUtf8", + "entryIdentifierName", + "err", + "extensionByUrl", + "extensionsByUrl", + "fhirDateTimeToDate", + "fhirPeriodToBounds", + "fhirQuantityToValue", + "formatHumanName", + "groveExchangeProtocol", + "groveFhirVersion", + "groveMobileContract", + "groveMobilePackageMetadata", + "groveMobileProfileCanonicals", + "groveProfileClaims", + "groveRecordingFormatRegistry", + "humanNameParts", + "identifierBySystem", + "identifierByType", + "identifiersBySystem", + "identifiersByType", + "immunizationExpirationDate", + "immunizationIsExpired", + "immunizationNoteTexts", + "immunizationOccurrenceDate", + "immunizationReactionDisplays", + "immunizationRecordedDate", + "immunizationRouteDisplay", + "immunizationSiteDisplay", + "immunizationVaccineDisplay", + "isEntryNodeIdentityValue", + "isEventIdentityValue", + "isOpaqueIdentityValue", + "isReferenceToType", + "issues", + "mapResult", + "mobileEffectiveCanonicalization", + "mobileEffectiveCanonicalizationVectors", + "observationBooleanValue", + "observationCodeDisplay", + "observationEffectiveDate", + "observationEffectiveEnd", + "observationEffectiveOverlaps", + "observationHasCategory", + "observationInterpretationDisplays", + "observationIssuedDate", + "observationNoteTexts", + "observationNumericValue", + "observationQuantity", + "observationStringValue", + "observationUnit", + "observationValueDisplay", + "ok", + "parseAbsoluteUri", + "parseCanonical", + "parseDevice", + "parseDocumentReference", + "parseFhirId", + "parseFhirInstant", + "parseGroveMobileExchangeBundle", + "parseGroveMobileRetractionBundle", + "parseObservation", + "parsePatientReference", + "parsePositiveInteger", + "parseProvenance", + "parseQuestionnaire", + "parseQuestionnaireResponse", + "parseR4CollectionBundle", + "parseResearchStudyReference", + "parseSemVer", + "parseSpecimen", + "parseSupportedR4Resource", + "parseUrnUuid", + "patientAgeInYears", + "patientBirthDate", + "patientDeceasedDate", + "patientEmailAddresses", + "patientIsDeceased", + "patientMaritalStatusDisplay", + "patientName", + "patientPhoneNumbers", + "periodDuration", + "periodIsActive", + "periodOverlaps", + "preflightQuestionnairePair", + "referenceType", + "scheduledCovers", + "scheduledDuration", + "scheduledEnd", + "scheduledIsPast", + "scheduledIsUpcoming", + "scheduledStart", + "sharedMobileMeasurementCatalog", + "slotSpecialtyDisplays", + "toFhirReference", + "toReference", + "validateDeploymentIdentity", + "valueToFhirQuantity" + ], + "./r4": [ + "attachmentSchema", + "codeableConceptSchema", + "codingSchema", + "deviceSchema", + "documentReferenceSchema", + "expressionSchema", + "extensionSchema", + "graphResourceSchema", + "groveMobileExchangeBundleSchema", + "groveMobileRetractionBundleSchema", + "identifierSchema", + "metaSchema", + "observationComponentSchema", + "observationSchema", + "parseDevice", + "parseDocumentReference", + "parseGroveMobileExchangeBundle", + "parseGroveMobileRetractionBundle", + "parseObservation", + "parseProvenance", + "parseR4CollectionBundle", + "parseSpecimen", + "parseSupportedR4Resource", + "periodSchema", + "primitiveElementSchema", + "provenanceSchema", + "quantitySchema", + "r4CollectionBundleSchema", + "resolvableReferenceSchema", + "sampledDataSchema", + "specimenSchema", + "supportedR4ResourceSchema" + ], + "./mobile": [ + "canonicalizeMobileEffectiveInstant", + "createEntryIdentity", + "deriveEntryFullUrl", + "deriveEntryNodeIdentifier", + "deriveEntryNodeValue", + "deriveEventIdentifier", + "deriveOpaqueIdentifier", + "encodeLengthFramedUtf8", + "entryIdentifierName", + "groveExchangeProtocol", + "groveFhirVersion", + "groveMobileContract", + "groveMobilePackageMetadata", + "groveMobileProfileCanonicals", + "groveProfileClaims", + "groveRecordingFormatRegistry", + "isEntryNodeIdentityValue", + "isEventIdentityValue", + "isOpaqueIdentityValue", + "mobileEffectiveCanonicalization", + "mobileEffectiveCanonicalizationVectors", + "sharedMobileMeasurementCatalog", + "validateDeploymentIdentity" + ], + "./providers": [ + "adapterMeasurementCatalog", + "buildProviderMeasurementBundle", + "buildProviderRecordingBundle", + "buildProviderRetractionBundle", + "encodeRecordingBytes", + "groveFhirVersion", + "groveProviderPackageMetadata", + "groveRecordingFormatRegistry", + "healthKitApplicationDeviceIdentity", + "healthKitClinicalRecordAdmission", + "parseCanonicalBase64", + "parseImmutableRecordingUrl", + "parseMediaType", + "parseNormalizedProviderRecord", + "parseProviderMeasurementBundleInput", + "parseProviderRecordingBundleInput", + "parseProviderRetractionInput", + "parseSha1Base64", + "providerAdapterCatalog", + "providerOutputCoordinates", + "providerOutputRole", + "providerRawOutputDiscriminators", + "providerRawOutputRoles", + "providerRecordEffectiveRules", + "providerScalarOutputDiscriminators", + "providerScalarOutputRoles" + ], + "./questionnaire": [ + "buildQuestionnaire", + "buildQuestionnaireResponse", + "groveFhirVersion", + "groveQuestionnairePackageMetadata", + "parseQuestionnaire", + "parseQuestionnaireResponse", + "preflightQuestionnairePair", + "questionnaireItemSchema", + "questionnaireResponseAnswerSchema", + "questionnaireResponseItemSchema", + "questionnaireResponseSchema", + "questionnaireSchema" + ] +} diff --git a/packages/fhir/test/export-surface.json.license b/packages/fhir/test/export-surface.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/test/export-surface.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/test/exports.test.ts b/packages/fhir/test/exports.test.ts index 5ed0f666..ced75ad7 100644 --- a/packages/fhir/test/exports.test.ts +++ b/packages/fhir/test/exports.test.ts @@ -7,10 +7,12 @@ // import { expectTypeOf } from 'expect-type' +import exportSurface from './export-surface.json' with { type: 'json' } import * as root from '../src/index.js' import * as mobile from '../src/mobile/index.js' -import * as provenance from '../src/provenance/index.js' import * as provider from '../src/providers/index.js' +import * as questionnaire from '../src/questionnaire/index.js' +import * as r4 from '../src/r4/index.js' type HasMeasurementBuilder = 'buildProviderMeasurementBundle' extends keyof T ? true : false @@ -20,22 +22,28 @@ type HasRetractionBuilder = 'buildProviderRetractionBundle' extends keyof T ? true : false type HasInternalPackageGraph = 'groveFhirPackageGraph' extends keyof T ? true : false +type HasContractVersion = + 'groveFhirContractVersion' extends keyof T ? true : false +type HasRuntimeVersion = 'version' extends keyof T ? true : false + +const byText = (left: string, right: string): number => + left.localeCompare(right) describe('public entry-point boundaries', () => { + it.each([ + ['.', root], + ['./r4', r4], + ['./mobile', mobile], + ['./providers', provider], + ['./questionnaire', questionnaire], + ] as const)('exports exactly the recorded %s surface', (name, entryPoint) => { + expect(Object.keys(entryPoint).sort(byText)).toEqual(exportSurface[name]) + }) + it('keeps provider-specific APIs out of the source-neutral entry points', () => { - expect('buildProviderMeasurementBundle' in root).toBe(false) - expect('buildProviderRecordingBundle' in root).toBe(false) - expect('buildProviderRetractionBundle' in root).toBe(false) - expect('buildProviderMeasurementBundle' in mobile).toBe(false) - expect('buildProviderRecordingBundle' in mobile).toBe(false) - expect('buildProviderRetractionBundle' in mobile).toBe(false) expect(JSON.stringify(mobile.sharedMobileMeasurementCatalog)).not.toMatch( /healthkit|health-connect|sensorkit|google-health-api|oura|withings|sourceTokens/u, ) - expect('adapterMeasurementCatalog' in root).toBe(false) - expect('adapterMeasurementCatalog' in mobile).toBe(false) - expect('PROFILES' in provenance).toBe(false) - expect('SYSTEMS' in provenance).toBe(false) expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() @@ -43,22 +51,10 @@ describe('public entry-point boundaries', () => { expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() - expectTypeOf< - HasMeasurementBuilder - >().toEqualTypeOf() - expectTypeOf< - HasRecordingBuilder - >().toEqualTypeOf() - expectTypeOf< - HasRetractionBuilder - >().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() expectTypeOf< HasInternalPackageGraph >().toEqualTypeOf() - expectTypeOf< - HasInternalPackageGraph - >().toEqualTypeOf() }) it('exposes the closed provider facade only from Provider', () => { @@ -71,11 +67,16 @@ describe('public entry-point boundaries', () => { expectTypeOf>().toEqualTypeOf() }) - it('exposes bounded generated version and package metadata', () => { + it('exposes FHIR and package metadata without a runtime release gate', () => { expect(root.groveFhirVersion).toBe('4.0.1') - expect(root.groveFhirContractVersion).toBe('0.6.0') + expect(root.groveExchangeProtocol.schemaVersion).toBe(0) expect(root.groveExchangeProtocol.protocolVersion).toBe(0) - expect(root.groveMobileContract.version).toBe('0.6.0') + expect(root.groveExchangeProtocol).not.toHaveProperty('version') + expect(root.groveExchangeProtocol).not.toHaveProperty('releaseVersion') + expect(root.groveMobileContract).not.toHaveProperty('version') + expect(root.groveRecordingFormatRegistry).not.toHaveProperty('version') + expect(root.groveProfileClaims).not.toHaveProperty('version') + expect(provider.providerAdapterCatalog).not.toHaveProperty('version') expect( root.groveMobileContract.identity.resourceIdentifierPriority, ).toEqual( @@ -84,12 +85,18 @@ describe('public entry-point boundaries', () => { expect(mobile.groveMobilePackageMetadata.packageId).toBe( 'org.grovealliance.fhir.mobile', ) + expect(root.parseSemVer(mobile.groveMobilePackageMetadata.version).ok).toBe( + true, + ) expect(provider.groveProviderPackageMetadata.packageId).toBe( 'org.grovealliance.fhir.providers', ) expectTypeOf(root.groveFhirVersion).toEqualTypeOf<'4.0.1'>() - expectTypeOf(root.groveFhirContractVersion).toEqualTypeOf<'0.6.0'>() + expectTypeOf>().toEqualTypeOf() + expectTypeOf< + HasRuntimeVersion + >().toEqualTypeOf() }) it('exposes owner-exclusive measurements only from the Provider contract', () => { @@ -128,15 +135,20 @@ describe('public entry-point boundaries', () => { ), ).toBe(true) const clinicalAdmission = provider.healthKitClinicalRecordAdmission - expect(clinicalAdmission.admittedFHIRReleases).toEqual(['dstu2', 'r4']) - const contentTypes = Object.values( - clinicalAdmission.fhirRepresentation.contentTypeByRelease, + const contentTypeByRelease = + clinicalAdmission.fhirRepresentation.contentTypeByRelease + const byText = (left: string, right: string): number => + left.localeCompare(right) + expect(Object.keys(contentTypeByRelease).sort(byText)).toEqual( + [...clinicalAdmission.admittedFHIRReleases].sort(byText), ) expect( - provider.groveRecordingFormatRegistry.formats[ - clinicalAdmission.payloadFormat - ].contentTypes, - ).toEqual(contentTypes) + [ + ...provider.groveRecordingFormatRegistry.formats[ + clinicalAdmission.payloadFormat + ].contentTypes, + ].sort(byText), + ).toEqual(Object.values(contentTypeByRelease).sort(byText)) expect(Object.isFrozen(clinicalAdmission)).toBe(true) expect(Object.isFrozen(clinicalAdmission.fhirRepresentation)).toBe(true) expect( diff --git a/packages/fhir/test/foundation-boundaries.test.ts b/packages/fhir/test/foundation-boundaries.test.ts index 56e950de..39fbfbea 100644 --- a/packages/fhir/test/foundation-boundaries.test.ts +++ b/packages/fhir/test/foundation-boundaries.test.ts @@ -23,7 +23,7 @@ import { observationSchema, periodSchema, provenanceSchema, - referenceSchema, + resolvableReferenceSchema, sampledDataSchema, } from '../src/r4/index.js' @@ -358,7 +358,7 @@ describe('bounded R4 schema invariants', () => { }).success, ).toBe(true) expect( - referenceSchema.safeParse({ + resolvableReferenceSchema.safeParse({ // FHIR R4 Reference.type is a URI naming the resource definition. type: 'http://hl7.org/fhir/StructureDefinition/Patient', identifier: { @@ -368,7 +368,8 @@ describe('bounded R4 schema invariants', () => { }).success, ).toBe(true) expect( - referenceSchema.safeParse({ display: 'missing identity' }).success, + resolvableReferenceSchema.safeParse({ display: 'missing identity' }) + .success, ).toBe(false) const provenance = { diff --git a/packages/fhir/test/foundation.test.ts b/packages/fhir/test/foundation.test.ts index 5181f6f2..2e549af8 100644 --- a/packages/fhir/test/foundation.test.ts +++ b/packages/fhir/test/foundation.test.ts @@ -374,68 +374,6 @@ describe('Result composition', () => { warnings: [warning], }) }) - - it('reports malformed JavaScript Result inputs without throwing', () => { - const cyclic: Record = {} - cyclic.self = cyclic - const hostile = new Proxy( - {}, - { - get: () => { - throw new Error('hostile result') - }, - }, - ) - for (const malformed of [null, undefined, 42, 'invalid', cyclic, hostile]) { - const map = () => - mapResult(malformed as Result, (value) => value * 2) - const collect = () => - collectResults(malformed as ReadonlyArray>) - expect(map).not.toThrow() - expect(map().ok).toBe(false) - expect(collect).not.toThrow() - expect(collect().ok).toBe(false) - } - const collected = collectResults([ - ok(1), - { ok: false, issues: null } as unknown as Result, - ]) - expect(collected.ok).toBe(false) - if (!collected.ok) { - expect(collected.issues).toContainEqual( - expect.objectContaining({ code: 'invalid-type', path: [1] }), - ) - } - - const validIssue = { - severity: 'error', - code: 'invalid-type', - path: ['value'], - message: 'Invalid value.', - } as const - const malformedIssues = [ - null, - { ...validIssue, severity: 'fatal' }, - { ...validIssue, code: 42 }, - { ...validIssue, path: 'value' }, - { ...validIssue, path: [true] }, - { ...validIssue, message: 42 }, - { ...validIssue, reason: 42 }, - { ...validIssue, location: 42 }, - ] - for (const malformedIssue of malformedIssues) { - expect( - mapResult( - { - ok: true, - value: 1, - warnings: [malformedIssue], - } as unknown as Result, - (value) => value, - ).ok, - ).toBe(false) - } - }) }) describe('hostile JSON snapshots', () => { diff --git a/packages/fhir/test/mobile.test.ts b/packages/fhir/test/mobile.test.ts index 7c5c14af..45d18ce0 100644 --- a/packages/fhir/test/mobile.test.ts +++ b/packages/fhir/test/mobile.test.ts @@ -7,10 +7,12 @@ // import { expectTypeOf } from 'expect-type' +import * as mobileContract from '../src/contract/measurement-catalog.generated.js' import { parseAbsoluteUri, parseFhirId, parseFhirInstant, + parseSemVer, type FhirInstant, type Result, } from '../src/core/index.js' @@ -21,14 +23,12 @@ import { createEntryIdentity, deriveEntryFullUrl, encodeLengthFramedUtf8, - groveFhirContractVersion, groveFhirVersion, groveMobilePackageMetadata, mobileEffectiveCanonicalizationVectors, sharedMobileMeasurementCatalog, type MobileMeasurement, } from '../src/mobile/index.js' -import * as mobileContract from '../src/contract/measurement-catalog.generated.js' const unwrap = (result: Result): T => { if (!result.ok) throw new Error(result.issues[0]?.message) @@ -41,14 +41,19 @@ const instant = (value: string): FhirInstant => unwrap(parseFhirInstant(value)) describe('source-neutral Mobile contract', () => { it('exports only source-neutral data, types, identity, and time contracts', () => { expect(groveFhirVersion).toBe('4.0.1') - expect(groveFhirContractVersion).toBe('0.6.0') expect(groveMobilePackageMetadata.packageId).toBe( 'org.grovealliance.fhir.mobile', ) + expect(parseSemVer(groveMobilePackageMetadata.version).ok).toBe(true) expect(Object.isFrozen(groveMobilePackageMetadata)).toBe(true) + expect(mobile.groveExchangeProtocol.schemaVersion).toBe(0) expect(mobile.groveExchangeProtocol.protocolVersion).toBe(0) - expect(mobile.groveMobileContract.version).toBe('0.6.0') + expect(mobile.groveExchangeProtocol).not.toHaveProperty('version') + expect(mobile.groveExchangeProtocol).not.toHaveProperty('releaseVersion') + expect(mobile.groveMobileContract).not.toHaveProperty('version') + expect(mobile.groveRecordingFormatRegistry).not.toHaveProperty('version') expect(Object.isFrozen(mobile.groveMobileContract)).toBe(true) + expect('groveFhirContractVersion' in mobile).toBe(false) expect('groveFhirExchangeIdentity' in mobile).toBe(false) expect('buildProviderMeasurementBundle' in mobile).toBe(false) expect('providerAdapterCatalog' in mobile).toBe(false) diff --git a/packages/fhir/test/observation-semantics.test.ts b/packages/fhir/test/observation-semantics.test.ts index cf0c9a9d..17c0b78d 100644 --- a/packages/fhir/test/observation-semantics.test.ts +++ b/packages/fhir/test/observation-semantics.test.ts @@ -8,7 +8,11 @@ /* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 fixes the LOINC, UCUM, and Observation category systems to HTTP URIs. */ -import { z } from 'zod' +import { type z } from 'zod' +import { + expectRule as expectRuleIssue, + refinementIssues, +} from './rule-test-support.js' import { groveProfileClaims, sharedMobileMeasurementCatalog, @@ -17,51 +21,18 @@ import { adapterMeasurementCatalog, groveProviderProfileCanonicals, } from '../src/contract/providers.generated.js' +import type { MeasurementDefinition } from '../src/providers/measurement-definition.js' import { hasAdmittedMobileObservationProfile, validateMobileObservationSemantics, } from '../src/r4/active-observation-semantics.js' -import { decodeGroveRuleDiagnostic } from '../src/r4/diagnostics.js' - -interface MeasurementDefinition { - readonly id: string - readonly profile: string - readonly code: Readonly<{ system: string; code: string }> - readonly requiredCodings?: ReadonlyArray< - Readonly<{ system: string; code: string }> - > - readonly category?: Readonly<{ system: string; code: string }> - readonly effective: 'Period' | 'dateTime' | 'dateTime-or-Period' - readonly method?: Readonly<{ code: string }> - readonly methodChoice?: readonly string[] - readonly valueKind: - 'codeableConcept' | 'components' | 'dateTime' | 'grouping' | 'quantity' - readonly quantity?: Readonly<{ - system: string - code: string - unit: string - }> | null - readonly resultCodeSystem?: string - readonly allowedValues?: readonly string[] - readonly components?: ReadonlyArray< - Readonly<{ - id: string - system: string - code: string - cardinality?: string - quantity: Readonly<{ system: string; code: string; unit: string }> - }> - > -} -const sharedDefinitions = sharedMobileMeasurementCatalog as unknown as Readonly< - Record -> -const healthKitDefinitions = adapterMeasurementCatalog.healthkit as unknown as - Readonly> | undefined -const profileCanonicals = groveProviderProfileCanonicals as Readonly< - Record -> +const sharedDefinitions: Readonly> = + sharedMobileMeasurementCatalog +const healthKitDefinitions: Readonly> = + adapterMeasurementCatalog.healthkit +const profileCanonicals: Readonly> = + groveProviderProfileCanonicals const aggregationSystem = 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-aggregation-method' @@ -70,7 +41,7 @@ const definition = ( owner: 'healthkit' | 'shared' = 'shared', ): MeasurementDefinition => { const value = - owner === 'shared' ? sharedDefinitions[kind] : healthKitDefinitions?.[kind] + owner === 'shared' ? sharedDefinitions[kind] : healthKitDefinitions[kind] if (value === undefined) throw new Error(`Missing ${owner} ${kind} catalog row.`) return value @@ -144,9 +115,9 @@ const observationFor = ( }, valueQuantity: { value: 1, - system: component.quantity.system, - code: component.quantity.code, - unit: component.quantity.unit, + system: component.quantity?.system, + code: component.quantity?.code, + unit: component.quantity?.unit, }, })) break @@ -154,29 +125,18 @@ const observationFor = ( return observation } -const semanticMessages = (resource: unknown): readonly string[] => { - const result = z - .unknown() - .superRefine((value, context) => { - validateMobileObservationSemantics(value, context, ['resource']) - }) - .safeParse(resource) - return result.success ? [] : result.error.issues.map(({ message }) => message) +const refineSemantics = ( + value: unknown, + context: z.core.$RefinementCtx, +): void => { + validateMobileObservationSemantics(value, context, ['resource']) } +const semanticMessages = (resource: unknown): readonly string[] => + refinementIssues(refineSemantics, resource).map(({ message }) => message) + const expectRule = (resource: unknown, code: string): void => { - const expectedCode = code.replaceAll('\\', '') - expect( - semanticMessages(resource).some((message) => { - const fallbackSeparator = message.indexOf(': ') - const actualCode = - decodeGroveRuleDiagnostic(message)?.code ?? - (fallbackSeparator === -1 ? message : ( - message.slice(0, fallbackSeparator) - )) - return actualCode === expectedCode - }), - ).toBe(true) + expectRuleIssue(refineSemantics, resource, code) } describe('catalog-driven Observation semantic boundaries', () => { @@ -253,7 +213,7 @@ describe('catalog-driven Observation semantic boundaries', () => { ) unknownProfile.meta = { profile: ['https://example.org/unknown'] } - expectRule(unknownProfile, 'mobile-output\\.semantic-profile') + expectRule(unknownProfile, 'mobile-output.semantic-profile') expect(semanticMessages({ resourceType: 'Patient' })).toEqual([]) // An owner-specific connected-provider profile is incomplete without its @@ -261,7 +221,7 @@ describe('catalog-driven Observation semantic boundaries', () => { withingsExclusive.meta = { profile: [profileCanonicals[withingsExclusiveDefinition.profile]], } - expectRule(withingsExclusive, 'mobile-output\\.semantic-profile') + expectRule(withingsExclusive, 'mobile-output.semantic-profile') }) it.each([ @@ -270,14 +230,14 @@ describe('catalog-driven Observation semantic boundaries', () => { mutate: (observation: Record) => { observation.status = 'preliminary' }, - code: 'mobile-heart-rate\\.status', + code: 'mobile-heart-rate.status', }, { name: 'missing coding list', mutate: (observation: Record) => { observation.code = {} }, - code: 'mobile-heart-rate\\.code', + code: 'mobile-heart-rate.code', }, { name: 'duplicate primary code', @@ -285,7 +245,7 @@ describe('catalog-driven Observation semantic boundaries', () => { const code = observation.code as { coding: unknown[] } code.coding.push(structuredClone(code.coding[0])) }, - code: 'mobile-heart-rate\\.code', + code: 'mobile-heart-rate.code', }, { name: 'wrong primary code', @@ -295,14 +255,14 @@ describe('catalog-driven Observation semantic boundaries', () => { } code.coding[0] = { system: 'http://loinc.org', code: 'not-admitted' } }, - code: 'mobile-heart-rate\\.code', + code: 'mobile-heart-rate.code', }, { name: 'missing category', mutate: (observation: Record) => { Reflect.deleteProperty(observation, 'category') }, - code: 'mobile-heart-rate\\.category', + code: 'mobile-heart-rate.category', }, { name: 'additional same-system category', @@ -315,14 +275,14 @@ describe('catalog-driven Observation semantic boundaries', () => { code: 'laboratory', }) }, - code: 'mobile-heart-rate\\.category', + code: 'mobile-heart-rate.category', }, { name: 'neither flexible effective choice', mutate: (observation: Record) => { Reflect.deleteProperty(observation, 'effectiveDateTime') }, - code: 'mobile-heart-rate\\.effective', + code: 'mobile-heart-rate.effective', }, { name: 'both flexible effective choices', @@ -332,7 +292,7 @@ describe('catalog-driven Observation semantic boundaries', () => { end: '2026-08-20T01:00:00Z', } }, - code: 'mobile-heart-rate\\.effective', + code: 'mobile-heart-rate.effective', }, { name: 'missing quantity value', @@ -340,7 +300,7 @@ describe('catalog-driven Observation semantic boundaries', () => { const quantity = observation.valueQuantity as Record Reflect.deleteProperty(quantity, 'value') }, - code: 'mobile-output\\.fixed-quantity-unit', + code: 'mobile-output.fixed-quantity-unit', }, ...['system', 'code', 'unit'].map((field) => ({ name: `invalid quantity ${field}`, @@ -348,7 +308,7 @@ describe('catalog-driven Observation semantic boundaries', () => { const quantity = observation.valueQuantity as Record quantity[field] = 'not-admitted' }, - code: 'mobile-output\\.fixed-quantity-unit', + code: 'mobile-output.fixed-quantity-unit', })), { name: 'quantity comparator', @@ -356,7 +316,7 @@ describe('catalog-driven Observation semantic boundaries', () => { const quantity = observation.valueQuantity as Record quantity.comparator = '<' }, - code: 'mobile-output\\.fixed-quantity-unit', + code: 'mobile-output.fixed-quantity-unit', }, ])('rejects a heart-rate Observation with $name', ({ mutate, code }) => { const observation = observationFor(definition('heart-rate')) @@ -369,62 +329,62 @@ describe('catalog-driven Observation semantic boundaries', () => { expect(semanticMessages(resting)).toEqual([]) const restingCode = resting.code as { coding: unknown[] } restingCode.coding.pop() - expectRule(resting, 'mobile-resting-heart-rate\\.required-coding') + expectRule(resting, 'mobile-resting-heart-rate.required-coding') const sleep = observationFor(definition('sleep-duration')) sleep.category = [ { coding: [{ system: 'http://loinc.org', code: 'sleep' }] }, ] - expectRule(sleep, 'mobile-sleep-duration\\.category') + expectRule(sleep, 'mobile-sleep-duration.category') }) it('enforces fixed and choice aggregation methods', () => { const fixed = observationFor(definition('deep-sleep-duration')) fixed.method = undefined - expectRule(fixed, 'mobile-deep-sleep-duration\\.method') + expectRule(fixed, 'mobile-deep-sleep-duration.method') const choice = observationFor(definition('respiratory-rate-average')) choice.method = {} - expectRule(choice, 'mobile-respiratory-rate-average\\.method') + expectRule(choice, 'mobile-respiratory-rate-average.method') choice.method = { coding: [ { system: aggregationSystem, code: 'not-admitted' }, { system: aggregationSystem, code: 'also-not-admitted' }, ], } - expectRule(choice, 'mobile-respiratory-rate-average\\.method') + expectRule(choice, 'mobile-respiratory-rate-average.method') }) it('enforces bounded effective periods and quantity value domains', () => { const weight = observationFor(definition('body-weight')) Reflect.deleteProperty(weight, 'effectiveDateTime') - expectRule(weight, 'mobile-body-weight\\.effective') + expectRule(weight, 'mobile-body-weight.effective') const steps = observationFor(definition('step-count')) steps.effectivePeriod = { start: '2026-08-20T00:00:00Z' } - expectRule(steps, 'mobile-step-count\\.effective') + expectRule(steps, 'mobile-step-count.effective') steps.effectivePeriod = { start: '2026-08-20T00:00:00Z', end: '2026-08-20T00:00:00Z', } - expectRule(steps, 'mobile-step-count\\.nonzero-period') + expectRule(steps, 'mobile-step-count.nonzero-period') ;(steps.valueQuantity as Record).value = -1 - expectRule(steps, 'mobile-step-count\\.value-domain') + expectRule(steps, 'mobile-output.quantity-value-domain') ;(steps.valueQuantity as Record).value = 0.5 - expectRule(steps, 'mobile-step-count\\.value-domain') + expectRule(steps, 'mobile-output.quantity-value-domain') const saturation = observationFor(definition('oxygen-saturation')) ;(saturation.valueQuantity as Record).value = 100 expect(semanticMessages(saturation)).toEqual([]) ;(saturation.valueQuantity as Record).value = 101 - expectRule(saturation, 'mobile-oxygen-saturation\\.value-domain') + expectRule(saturation, 'mobile-output.quantity-value-domain') }) it('enforces closed coded, date-time, and grouping result shapes', () => { const coded = observationFor(definition('intermenstrual-bleeding')) expect(semanticMessages(coded)).toEqual([]) coded.valueCodeableConcept = {} - expectRule(coded, 'mobile-intermenstrual-bleeding\\.coded-result') + expectRule(coded, 'mobile-intermenstrual-bleeding.coded-result') coded.valueCodeableConcept = { coding: [ { system: 'https://example.org/other', code: 'present' }, @@ -435,31 +395,31 @@ describe('catalog-driven Observation semantic boundaries', () => { }, ], } - expectRule(coded, 'mobile-intermenstrual-bleeding\\.coded-result') + expectRule(coded, 'mobile-intermenstrual-bleeding.coded-result') const dateOfBirth = observationFor(definition('date-of-birth', 'healthkit')) expect(semanticMessages(dateOfBirth)).toEqual([]) Reflect.deleteProperty(dateOfBirth, 'valueDateTime') - expectRule(dateOfBirth, 'mobile-date-of-birth\\.date-time-result') + expectRule(dateOfBirth, 'mobile-date-of-birth.date-time-result') const food = observationFor(definition('food-correlation', 'healthkit')) expect(semanticMessages(food)).toEqual([]) food.hasMember = 'not-a-reference-list' - expectRule(food, 'mobile-food-correlation\\.members') + expectRule(food, 'mobile-food-correlation.members') food.hasMember = [] - expectRule(food, 'mobile-food-correlation\\.members') + expectRule(food, 'mobile-food-correlation.members') }) it('enforces distinct required blood-pressure components and their units', () => { const row = definition('blood-pressure') const missing = observationFor(row) missing.component = [] - expectRule(missing, 'mobile-blood-pressure\\.systolic') + expectRule(missing, 'mobile-blood-pressure.systolic') const duplicate = observationFor(row) const components = duplicate.component as unknown[] components.push(structuredClone(components[0])) - expectRule(duplicate, 'mobile-blood-pressure\\.systolic') + expectRule(duplicate, 'mobile-blood-pressure.systolic') const shared = observationFor(row) const sharedComponents = shared.component as Array> @@ -474,7 +434,7 @@ describe('catalog-driven Observation semantic boundaries', () => { ], } shared.component = [first] - expectRule(shared, 'mobile-blood-pressure\\.diastolic') + expectRule(shared, 'mobile-blood-pressure.diastolic') const malformed = observationFor(row) const malformedComponent = ( @@ -493,7 +453,7 @@ describe('catalog-driven Observation semantic boundaries', () => { quantity.system = 'https://example.org/not-ucum' quantity.code = 'not-admitted' quantity.unit = 'not-admitted' - expectRule(malformed, 'mobile-blood-pressure\\.systolic') + expectRule(malformed, 'mobile-blood-pressure.systolic') const audiogram = observationFor(definition('audiogram-panel', 'healthkit')) const optionalComponent = definition('audiogram-panel', 'healthkit') @@ -510,6 +470,6 @@ describe('catalog-driven Observation semantic boundaries', () => { valueQuantity: {}, }, ] - expectRule(audiogram, `mobile-audiogram-panel\\.${optionalComponent.id}`) + expectRule(audiogram, `mobile-audiogram-panel.${optionalComponent.id}`) }) }) diff --git a/packages/fhir/test/profile-semantics-boundaries.test.ts b/packages/fhir/test/profile-semantics-boundaries.test.ts index afc4111c..006c6a02 100644 --- a/packages/fhir/test/profile-semantics-boundaries.test.ts +++ b/packages/fhir/test/profile-semantics-boundaries.test.ts @@ -6,7 +6,11 @@ // SPDX-License-Identifier: MIT // -import { z } from 'zod' +import { type z } from 'zod' +import { + expectRule as expectRuleIssue, + refinementIssues, +} from './rule-test-support.js' import { adapterSourceMarkerClaims, groveProfileClaims, @@ -15,7 +19,7 @@ import { healthKitApplicationDeviceIdentity, healthKitClinicalRecordAdmission, } from '../src/contract/providers.generated.js' -import { decodeGroveRuleDiagnostic } from '../src/r4/diagnostics.js' +import { groveMobileContract } from '../src/mobile/contract.js' import { hasAdmittedActiveDeviceProfile, hasAdmittedActiveDocumentReferenceProfile, @@ -23,36 +27,32 @@ import { validateProfiledResource, } from '../src/r4/profile-semantics.js' +const refineProfiles = + (roles: ReadonlyMap = new Map()) => + (value: unknown, context: z.core.$RefinementCtx): void => { + validateProfiledResource(value, roles, context, ['resource']) + } + const messagesFor = ( resource: unknown, roles: ReadonlyMap = new Map(), -): readonly string[] => { - const result = z - .unknown() - .superRefine((value, context) => { - validateProfiledResource(value, roles, context, ['resource']) - }) - .safeParse(resource) - return result.success ? [] : result.error.issues.map(({ message }) => message) -} +): readonly string[] => + refinementIssues(refineProfiles(roles), resource).map( + ({ message }) => message, + ) + +const codesFor = ( + resource: unknown, + roles?: ReadonlyMap, +): readonly string[] => + refinementIssues(refineProfiles(roles), resource).map(({ code }) => code) const expectRule = ( resource: unknown, code: string, roles?: ReadonlyMap, ): void => { - const expectedCode = code.replaceAll('\\', '') - expect( - messagesFor(resource, roles).some((message) => { - const fallbackSeparator = message.indexOf(': ') - const actualCode = - decodeGroveRuleDiagnostic(message)?.code ?? - (fallbackSeparator === -1 ? message : ( - message.slice(0, fallbackSeparator) - )) - return actualCode === expectedCode - }), - ).toBe(true) + expectRuleIssue(refineProfiles(roles), resource, code) } const roles = (...values: readonly string[]): ReadonlyMap => @@ -85,7 +85,7 @@ describe('profile-specific resource semantic boundaries', () => { ]) { const device = { resourceType: 'Device', meta: { profile } } expect(hasAdmittedActiveDeviceProfile(device)).toBe(false) - expectRule(device, 'mobile-support\\.device-profile') + expectRule(device, 'mobile-support.device-profile') } expect( hasAdmittedActiveDocumentReferenceProfile({ resourceType: 'Patient' }), @@ -110,12 +110,12 @@ describe('profile-specific resource semantic boundaries', () => { } expectRule( recording, - 'mobile-support\\.device-profile', + 'mobile-support.device-profile', roles('recording-device'), ) expectRule( recording, - 'mobile-recording-device\\.identities', + 'mobile-recording-device.identities', roles('recording-device', 'device-snapshot'), ) @@ -125,7 +125,7 @@ describe('profile-specific resource semantic boundaries', () => { meta: { profile: [hostProfile] }, identifier: [], }, - 'mobile-host-device\\.identity', + 'mobile-host-device.identity', roles('device-snapshot'), ) }) @@ -184,7 +184,7 @@ describe('profile-specific resource semantic boundaries', () => { mutate(candidate) expectRule( candidate, - 'healthkit-application-device\\.bundle-identifier', + 'healthkit-application-device.bundle-identifier', roles('device-snapshot'), ) } @@ -204,17 +204,17 @@ describe('profile-specific resource semantic boundaries', () => { expectRule( { ...document, meta: { profile: ['https://example.org/unknown'] } }, - 'mobile-output\\.document-profile', + 'mobile-output.document-profile', ) - expectRule(document, 'mobile-recording-document\\.identities') + expectRule(document, 'mobile-recording-document.identities') expectRule( document, - 'mobile-recording-document\\.identity-roles', + 'mobile-recording-document.identity-roles', roles('source-record', 'source-output', 'source-artifact', 'unexpected'), ) expectRule( document, - 'mobile-recording-document\\.identity-roles', + 'mobile-recording-document.identity-roles', new Map([ ['source-record', 1], ['source-output', 1], @@ -224,15 +224,11 @@ describe('profile-specific resource semantic boundaries', () => { ) expectRule( document, - 'mobile-recording-document\\.required-metadata', - requiredRoles, - ) - expectRule( - document, - 'mobile-recording-document\\.attachment', + 'mobile-recording-document.required-metadata', requiredRoles, ) - expectRule(document, 'mobile-recording-document\\.format', requiredRoles) + expectRule(document, 'mobile-recording-document.attachment', requiredRoles) + expectRule(document, 'mobile-recording-document.format', requiredRoles) }) it('fails closed for malformed embedded recording data and registry metadata', () => { @@ -271,7 +267,7 @@ describe('profile-specific resource semantic boundaries', () => { content.attachment.data = data expectRule( candidate, - 'mobile-recording-document\\.embedded-integrity', + 'mobile-recording-document.embedded-integrity', requiredRoles, ) } @@ -283,11 +279,7 @@ describe('profile-specific resource semantic boundaries', () => { system: 'https://example.org/wrong', code: 'unknown', } - expectRule( - invalidFormat, - 'mobile-recording-document\\.format', - requiredRoles, - ) + expectRule(invalidFormat, 'mobile-recording-document.format', requiredRoles) }) it('requires the exact QuestionnaireResponse profile', () => { @@ -301,7 +293,7 @@ describe('profile-specific resource semantic boundaries', () => { ).toEqual([]) expectRule( { resourceType: 'QuestionnaireResponse', meta: { profile: [] } }, - 'mobile-support\\.questionnaire-response-profile', + 'mobile-support.questionnaire-response-profile', ) }) @@ -320,16 +312,16 @@ describe('profile-specific resource semantic boundaries', () => { expect(hasAdmittedAdapterOnlyOutputProfile({})).toBe(true) expect(messagesFor({})).toEqual([]) expect(hasAdmittedAdapterOnlyOutputProfile(specimen)).toBe(true) - expectRule(specimen, 'mobile-output\\.adapter-only-profile', requiredRoles) + expectRule(specimen, 'mobile-output.adapter-only-profile', requiredRoles) expectRule( { ...specimen, meta: { profile: ['https://example.org/unknown'] } }, - 'mobile-output\\.adapter-only-profile', + 'mobile-output.adapter-only-profile', requiredRoles, ) - expectRule(specimen, 'mobile-output\\.adapter-only-profile', new Map()) + expectRule(specimen, 'mobile-output.adapter-only-profile', new Map()) expectRule( specimen, - 'mobile-output\\.adapter-only-profile', + 'mobile-output.adapter-only-profile', roles(...claim.requiredIdentifierRoles, 'unexpected'), ) @@ -351,7 +343,7 @@ describe('profile-specific resource semantic boundaries', () => { } expectRule( healthKitResource, - 'mobile-exchange\\.adapter-source-marker', + 'mobile-exchange.adapter-source-marker', roles(...healthKitClaim.requiredIdentifierRoles), ) @@ -360,29 +352,38 @@ describe('profile-specific resource semantic boundaries', () => { meta: { profile: ['https://example.org/neutral'] }, extension: [{ url: marker.url, valueCode: 'owned-marker' }], } - expectRule(neutral, 'mobile-exchange\\.adapter-source-marker') + expectRule(neutral, 'mobile-exchange.adapter-source-marker') }) - it('validates the versioned FHIR media type on clinical documents', () => { + it('admits only the release-versioned media types on clinical documents', () => { const claim = groveProfileClaims.healthKitClinicalRecordDocumentClaim - const document = { + const representation = healthKitClinicalRecordAdmission.fhirRepresentation + const clinicalDocument = (contentType: string) => ({ resourceType: 'DocumentReference', meta: { profile: claim.profiles }, content: [ { + attachment: { contentType }, format: { - system: - 'https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format', + system: groveMobileContract.recordingFormats.codeSystem, code: healthKitClinicalRecordAdmission.payloadFormat, }, - attachment: { contentType: 'application/fhir+json' }, }, ], + }) + const clinicalRoles = roles(...claim.requiredIdentifierRoles) + + for (const contentType of Object.values( + representation.contentTypeByRelease, + )) { + expect( + codesFor(clinicalDocument(contentType), clinicalRoles), + ).not.toContain('healthkit-clinical.fhir-representation') } expectRule( - document, - 'healthkit-clinical-record\\.fhir-release', - roles(...claim.requiredIdentifierRoles), + clinicalDocument('application/fhir+json'), + 'healthkit-clinical.fhir-representation', + clinicalRoles, ) }) }) diff --git a/packages/fhir/test/provider-contract.test.ts b/packages/fhir/test/provider-contract.test.ts index f3db997c..a2b047b0 100644 --- a/packages/fhir/test/provider-contract.test.ts +++ b/packages/fhir/test/provider-contract.test.ts @@ -100,11 +100,9 @@ describe('Exchange entry identity', () => { const parsed = parseGroveMobileExchangeBundle(invalidBundle) expect(parsed.ok).toBe(false) if (!parsed.ok) { - expect( - parsed.issues.some(({ message }) => - message.includes('mobile-body-fat-percentage.value-domain'), - ), - ).toBe(true) + expect(parsed.issues.map(({ code }) => code)).toContain( + 'mobile-output.quantity-value-domain', + ) } }) diff --git a/packages/fhir/test/provider-identity.test.ts b/packages/fhir/test/provider-identity.test.ts index a60d530b..96da4ccf 100644 --- a/packages/fhir/test/provider-identity.test.ts +++ b/packages/fhir/test/provider-identity.test.ts @@ -20,7 +20,11 @@ import { study, uri, } from './provider-test-support.js' -import { sharedMobileMeasurementCatalog } from '../src/mobile/index.js' +import { parseUrnUuid } from '../src/core/index.js' +import { + groveMobileContract, + sharedMobileMeasurementCatalog, +} from '../src/mobile/index.js' import { buildProviderMeasurementBundle, parseProviderMeasurementBundleInput, @@ -502,7 +506,7 @@ describe('Provider R4 graph builder', () => { const observation = resources(result).find( (resource) => resource.resourceType === 'Observation', ) - expect(observation?.device?.reference).toMatch(/^urn:uuid:/u) + expect(parseUrnUuid(observation?.device?.reference).ok).toBe(true) expect( observation?.extension?.some( ({ url }) => @@ -514,9 +518,7 @@ describe('Provider R4 graph builder', () => { ({ resource }) => resource.resourceType === 'Device' && resource.meta?.profile?.some( - (profile) => - profile === - 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device', + (profile) => profile === groveMobileContract.profiles.hostDevice, ), ) expect(hostEntries).toHaveLength(3) @@ -525,13 +527,12 @@ describe('Provider R4 graph builder', () => { applicationDevice.resourceType !== 'Device' || !applicationDevice.meta?.profile?.some( (profile) => - profile === - 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device', + profile === groveMobileContract.profiles.applicationDevice, ) ) { continue } - expect(applicationDevice.parent?.reference).toMatch(/^urn:uuid:/u) + expect(parseUrnUuid(applicationDevice.parent?.reference).ok).toBe(true) expect( hostEntries.some( ({ fullUrl }) => fullUrl === applicationDevice.parent?.reference, diff --git a/packages/fhir/test/questionnaire-builder.test.ts b/packages/fhir/test/questionnaire-builder.test.ts index 63eb3d3f..14997481 100644 --- a/packages/fhir/test/questionnaire-builder.test.ts +++ b/packages/fhir/test/questionnaire-builder.test.ts @@ -28,6 +28,12 @@ import { } from '../src/index.js' /* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 canonicals are normative HTTP URIs. */ +const withoutSubject = (value: object): unknown => { + const copy = { ...value } + Reflect.deleteProperty(copy, 'subject') + return copy +} + describe('Questionnaire R4 builders', () => { it('stamps the exact Grove profiles and owned extensions', () => { expect(questionnaire.meta?.profile).toEqual([ @@ -579,20 +585,42 @@ describe('Questionnaire R4 builders', () => { ).toBe(false) }) - it('enforces R4 ResourceType subject codes and signed integer bounds', () => { + it('requires the profile-fixed Patient subjectType and response subject', () => { + for (const subjectTypes of [ + undefined, + [], + ['Banana'], + ['Group'], + ['Patient', 'Group'], + ]) { + expect( + buildQuestionnaire({ + ...questionnaireInput, + subjectTypes, + } as unknown as QuestionnaireInput).ok, + ).toBe(false) + } + for (const subjectType of [undefined, ['Banana'], ['Group'], []]) { + expect(parseQuestionnaire({ ...questionnaire, subjectType }).ok).toBe( + false, + ) + } + expect( - buildQuestionnaire({ - ...questionnaireInput, - subjectTypes: ['Banana'], - } as unknown as QuestionnaireInput).ok, + buildQuestionnaireResponse( + withoutSubject(responseInput()) as QuestionnaireResponseInput, + ).ok, ).toBe(false) expect( - parseQuestionnaire({ - ...questionnaire, - subjectType: ['Banana'], - }).ok, + buildQuestionnaireResponse({ + ...responseInput(), + subject: { type: 'Group', reference: 'Group/example' }, + } as unknown as QuestionnaireResponseInput).ok, ).toBe(false) + expect(parseQuestionnaireResponse(withoutSubject(response)).ok).toBe(false) + }) + it('enforces signed integer bounds', () => { for (const valueInteger of [2_147_483_648, -2_147_483_649]) { expect( preflightItems( diff --git a/packages/fhir/test/questionnaire-input-surface.test.ts b/packages/fhir/test/questionnaire-input-surface.test.ts index 4e63436b..c08f7b70 100644 --- a/packages/fhir/test/questionnaire-input-surface.test.ts +++ b/packages/fhir/test/questionnaire-input-surface.test.ts @@ -30,7 +30,6 @@ describe('Questionnaire R4 builders', () => { version: '01.0.0', id: 'invalid/id', date: 'not-an-instant', - subjectTypes: ['Patient', ''], extensions: [ { url: 'http://hl7.org/fhir/StructureDefinition/rendering-styleSensitive', diff --git a/packages/fhir/test/questionnaire-preflight-expressions.test.ts b/packages/fhir/test/questionnaire-preflight-expressions.test.ts index df852aad..9ca4e270 100644 --- a/packages/fhir/test/questionnaire-preflight-expressions.test.ts +++ b/packages/fhir/test/questionnaire-preflight-expressions.test.ts @@ -21,7 +21,9 @@ import { buildQuestionnaireResponse, parseCanonical, preflightQuestionnairePair, + type QuestionnaireItemInput, } from '../src/index.js' +import { evaluateEnableWhen } from '../src/questionnaire/preflight-answer-validation.js' describe('Questionnaire pair preflight', () => { it('returns non-blocking warnings for warning target constraints', () => { @@ -210,6 +212,20 @@ describe('Questionnaire pair preflight', () => { expect(result.ok).toBe(true) }) + it('matches any repeated answer for != and treats no answers as false', () => { + const dependent = { + linkId: 'dependent', + text: 'Dependent', + type: 'string', + enableWhen: [{ question: 'source', operator: '!=', answerInteger: 5 }], + } as const satisfies QuestionnaireItemInput + + expect(evaluateEnableWhen(dependent, new Map([['source', [5, 6]]]))).toBe( + true, + ) + expect(evaluateEnableWhen(dependent, new Map())).toBe(false) + }) + it('compares Coding and Quantity enableWhen values semantically', () => { const quantity = { value: 3, diff --git a/packages/fhir/test/questionnaire-preflight.test.ts b/packages/fhir/test/questionnaire-preflight.test.ts index 6241f803..e277880e 100644 --- a/packages/fhir/test/questionnaire-preflight.test.ts +++ b/packages/fhir/test/questionnaire-preflight.test.ts @@ -81,19 +81,16 @@ describe('Questionnaire pair preflight', () => { }) it('requires the typed response subject to be admitted by Questionnaire.subjectType', () => { - const mismatched = buildQuestionnaireResponse({ - ...responseInput(), + const result = preflightQuestionnairePair(questionnaire, { + ...response, subject: { type: 'Observation', reference: 'Observation/example' }, }) - expect(mismatched.ok).toBe(true) - if (!mismatched.ok) return - const result = preflightQuestionnairePair(questionnaire, mismatched.value) expect(result.ok).toBe(false) if (!result.ok) { expect(result.issues).toContainEqual( expect.objectContaining({ - code: 'value-mismatch', - path: ['response', 'subject', 'type'], + code: 'invalid-reference', + path: ['response', 'subject'], }), ) } diff --git a/packages/fhir/test/recording.test.ts b/packages/fhir/test/recording.test.ts index 66a7abad..9bfddddb 100644 --- a/packages/fhir/test/recording.test.ts +++ b/packages/fhir/test/recording.test.ts @@ -10,17 +10,22 @@ import { readFileSync } from 'node:fs' import { expectTypeOf } from 'expect-type' import { assert, property, uint8Array } from 'fast-check' import { - parseAbsoluteUri, + deploymentIdentity, + instant, + patient, + unwrap, + uri, +} from './provider-test-support.js' +import { adapterSourceMarkerClaims } from '../src/contract/measurement-catalog.generated.js' +import { parseFhirId, - parseFhirInstant, parseGroveMobileExchangeBundle, parsePositiveInteger, - type FhirInstant, type Result, } from '../src/index.js' -import { adapterSourceMarkerClaims } from '../src/contract/measurement-catalog.generated.js' import { buildProviderRecordingBundle, + groveRecordingFormatRegistry, healthKitClinicalRecordAdmission, providerRawOutputRoles, encodeRecordingBytes, @@ -34,61 +39,11 @@ import { type ConnectedRawProvider, } from '../src/providers/index.js' -const unwrap = (result: Result): T => { - if (!result.ok) { - throw new Error(result.issues.map((issue) => issue.message).join('\n')) - } - return result.value -} - -const uri = (value: string) => unwrap(parseAbsoluteUri(value)) -const instant = (value: string): FhirInstant => unwrap(parseFhirInstant(value)) const converter: ApplicationDeviceInput = { sourceDeviceToken: 'converter-instance-7f3a', name: 'Grove converter', version: '0.0.0', } -const patient = { - type: 'Patient', - identifier: { - system: uri('https://example.org/deployments/patient-pseudonyms'), - value: 'patient-example', - assurance: 'deployment-scoped-pseudonym', - }, -} as const - -const deploymentIdentity = { - opaqueIdentifierSystems: { - 'source-record': uri('https://example.org/identity/source-record/test/1'), - 'source-output': uri('https://example.org/identity/source-output/test/1'), - 'writer-record': uri('https://example.org/identity/writer-record/test/1'), - 'provider-record': uri( - 'https://example.org/identity/provider-record/test/1', - ), - 'provider-output': uri( - 'https://example.org/identity/provider-output/test/1', - ), - 'source-artifact': uri( - 'https://example.org/identity/source-artifact/test/1', - ), - 'provider-artifact': uri( - 'https://example.org/identity/provider-artifact/test/1', - ), - 'source-context': uri('https://example.org/identity/source-context/test/1'), - 'recording-device': uri( - 'https://example.org/identity/recording-device/test/1', - ), - 'device-snapshot': uri( - 'https://example.org/identity/device-snapshot/test/1', - ), - }, - eventIdentifierSystem: uri('https://example.org/identity/event'), - entryNodeIdentifierSystem: uri('https://example.org/identity/entry-node'), - keyId: 'test-key', - keyEpoch: '1', - secretBase64Url: 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', - producerInstance: '1f5c58aa-6ec6-4e79-a682-829a9debd3f5', -} as const const rawInput = ( provider: ConnectedRawProvider, @@ -118,7 +73,12 @@ const rawInput = ( }, attachment: { kind: 'embedded', - contentType: unwrap(parseMediaType('application/json')), + contentType: unwrap( + parseMediaType( + groveRecordingFormatRegistry.formats['provider-recording'] + .contentTypes[0], + ), + ), title: 'Authorized minimized provider recording', format: 'provider-recording', payloadAssertion: 'caller-authorized-opaque-payload', @@ -454,51 +414,41 @@ describe('Provider native recording graph', () => { 'clinical DocumentReference', ) } - const clinicalContentIn = (candidate: unknown): Record => { - const content = clinicalDocumentIn(candidate).content - if (!Array.isArray(content) || content.length !== 1) { - throw new Error('Expected one clinical DocumentReference.content item.') - } - return mutableObject(content[0], 'clinical content') - } - const dstu2 = structuredClone(bundle) - mutableObject( - clinicalContentIn(dstu2).attachment, - 'clinical attachment', - ).contentType = representation.contentTypeByRelease.dstu2 - expect(parseGroveMobileExchangeBundle(dstu2).ok).toBe(true) - + const contentOf = (candidate: Record) => + mutableObject( + Array.isArray(candidate.content) ? candidate.content[0] : undefined, + 'clinical content', + ) const invalidRepresentations = [ (candidate: Record) => { - mutableObject(candidate.attachment, 'clinical attachment').contentType = - 'application/fhir+json' - }, - (candidate: Record) => { - mutableObject(candidate.attachment, 'clinical attachment').contentType = - 'application/fhir+json; fhirVersion=5.0' - }, - (candidate: Record) => { - mutableObject(candidate.format, 'clinical format').code = - 'native-recording' + mutableObject( + contentOf(candidate).attachment, + 'attachment', + ).contentType = 'application/fhir+json' }, (candidate: Record) => { - Reflect.deleteProperty( - mutableObject(candidate.attachment, 'clinical attachment'), - 'contentType', - ) + mutableObject(contentOf(candidate).format, 'format').code = + 'fhir-collection-bundle' }, ] for (const mutate of invalidRepresentations) { const candidate = structuredClone(bundle) - mutate(clinicalContentIn(candidate)) + mutate(clinicalDocumentIn(candidate)) const result = parseGroveMobileExchangeBundle(candidate) expect(result.ok).toBe(false) if (result.ok) continue - expect( - result.issues.some((issue) => - issue.message.includes('healthkit-clinical-record.fhir-release'), - ), - ).toBe(true) + expect(result.issues.map(({ code }) => code)).toContain( + 'healthkit-clinical.fhir-representation', + ) + } + + for (const release of healthKitClinicalRecordAdmission.admittedFHIRReleases) { + const candidate = structuredClone(bundle) + mutableObject( + contentOf(clinicalDocumentIn(candidate)).attachment, + 'attachment', + ).contentType = representation.contentTypeByRelease[release] + expect(parseGroveMobileExchangeBundle(candidate).ok).toBe(true) } }) @@ -519,7 +469,9 @@ describe('Provider native recording graph', () => { expect(documentEntry.resource.content).toEqual([ { attachment: { - contentType: 'application/json', + contentType: + groveRecordingFormatRegistry.formats['provider-recording'] + .contentTypes[0], data: 'AQID', size: 3, hash: 'cDeAcZjCKn0rCAc3HXY3eahP388=', @@ -593,7 +545,7 @@ describe('Provider native recording graph', () => { }, }, { - name: 'release-coupled recording registry version', + name: 'unexpected recording registry version', mutate: (document: MutableJsonObject) => { const content = document.content if (!Array.isArray(content)) throw new Error('Expected content.') @@ -601,7 +553,7 @@ describe('Provider native recording graph', () => { mutableObject(content[0], 'content').format, 'content.format', ) - format.version = '0.5.0' + format.version = '1.2.3' }, }, { @@ -613,7 +565,7 @@ describe('Provider native recording graph', () => { mutableObject(content[0], 'content').attachment, 'content.attachment', ) - attachment.contentType = 'text/plain' + attachment.contentType = 'application/octet-stream' }, }, { @@ -800,7 +752,12 @@ describe('Provider native recording graph', () => { ...input, attachment: { kind: 'external', - contentType: unwrap(parseMediaType('application/json')), + contentType: unwrap( + parseMediaType( + groveRecordingFormatRegistry.formats['provider-recording'] + .contentTypes[0], + ), + ), title: 'Authorized minimized Oura recording', format: 'provider-recording', payloadAssertion: 'verified-sanitized-input', @@ -927,7 +884,9 @@ describe('Provider native recording graph', () => { const input = rawInput('oura', 'heartrate') const external = { kind: 'external', - contentType: 'application/json', + contentType: + groveRecordingFormatRegistry.formats['provider-recording'] + .contentTypes[0], title: 'Authorized recording', format: 'provider-recording', payloadAssertion: 'caller-authorized-opaque-payload', diff --git a/packages/fhir/test/retraction.test.ts b/packages/fhir/test/retraction.test.ts index f285f1f8..50f75eb0 100644 --- a/packages/fhir/test/retraction.test.ts +++ b/packages/fhir/test/retraction.test.ts @@ -12,12 +12,16 @@ import { type FhirInstant, type Result, } from '../src/core/index.js' -import type { DeploymentIdentityInput } from '../src/mobile/index.js' +import { + groveMobileContract, + type DeploymentIdentityInput, +} from '../src/mobile/index.js' import { buildProviderMeasurementBundle, buildProviderRecordingBundle, buildProviderRetractionBundle, encodeRecordingBytes, + groveRecordingFormatRegistry, parseMediaType, parseProviderRetractionInput, providerOutputCoordinates, @@ -110,7 +114,7 @@ const activeInput = { application: { sourceDeviceToken: 'converter-build-42', name: 'Grove converter', - version: '0.6.0', + version: '1.2.3', build: '42', }, eventSequence: '100', @@ -275,7 +279,12 @@ describe('Provider source-record retraction', () => { }, attachment: { kind: 'embedded', - contentType: unwrap(parseMediaType('application/json')), + contentType: unwrap( + parseMediaType( + groveRecordingFormatRegistry.formats['provider-recording'] + .contentTypes[0], + ), + ), title: 'Authorized minimized provider recording', format: 'provider-recording', payloadAssertion: 'verified-sanitized-input', @@ -365,9 +374,7 @@ describe('Provider source-record retraction', () => { ({ resource }) => resource.resourceType === 'Device' && resource.meta?.profile?.some( - (profile) => - profile === - 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-device', + (profile) => profile === groveMobileContract.profiles.recordingDevice, ), )?.resource if (device?.resourceType !== 'Device') { @@ -439,6 +446,79 @@ describe('Provider source-record retraction', () => { ).toBe(false) }) + it('carries the adapter native record identifier on its target', () => { + const nativeIdentifier = { + system: uri('https://study.example.org/fhir/NamingSystem/native-record'), + value: 'record-heart-001', + } + const retraction = unwrap( + buildProviderRetractionBundle({ + ...retractionInput, + targets: [{ ...retractionInput.targets[0], nativeIdentifier }], + }), + ) + const provenance = retraction.entry.find( + ({ resource }) => resource.resourceType === 'Provenance', + )?.resource + if (provenance?.resourceType !== 'Provenance') { + throw new Error('The retraction graph did not contain Provenance.') + } + expect(provenance.target[0]?.extension).toEqual([ + { + url: groveMobileContract.extensions.retractionTargetRole, + valueCode: 'primary-output', + }, + { + url: groveMobileContract.extensions.retractionTargetNativeIdentifier, + valueIdentifier: nativeIdentifier, + }, + ]) + + const groveRoleCoding = { + ...retraction, + entry: retraction.entry.map((entry) => + entry.resource.resourceType === 'Provenance' ? + { + ...entry, + resource: { + ...entry.resource, + target: [ + { + ...entry.resource.target[0], + extension: [ + entry.resource.target[0]?.extension?.[0], + { + url: groveMobileContract.extensions + .retractionTargetNativeIdentifier, + valueIdentifier: { + ...nativeIdentifier, + type: { + coding: [ + { + system: + groveMobileContract.systems.identifierRole, + code: 'source-record', + }, + ], + }, + }, + }, + ], + }, + ], + }, + } + : entry, + ), + } + const result = parseGroveMobileRetractionBundle(groveRoleCoding) + expect(result.ok).toBe(false) + if (result.ok) return + expect(result.issues.map(({ code }) => code)).toContain( + 'mobile-retraction.native-record-identifier', + ) + }) + it('emits retraction targets in a deterministic canonical order', () => { const deviceTarget = { role: 'device-snapshot', diff --git a/packages/fhir/test/rule-test-support.ts b/packages/fhir/test/rule-test-support.ts new file mode 100644 index 00000000..7e9bd2d3 --- /dev/null +++ b/packages/fhir/test/rule-test-support.ts @@ -0,0 +1,50 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { z } from 'zod' +import type { Issue } from '../src/core/index.js' +import { groveRuleIssueFromParameters } from '../src/r4/diagnostics.js' + +type Refinement = (value: unknown, context: z.core.$RefinementCtx) => void + +/** Producer-rule issues one Grove refinement records for one resource. */ +export const refinementIssues = ( + refine: Refinement, + resource: unknown, +): readonly Issue[] => { + const result = z.unknown().superRefine(refine).safeParse(resource) + if (result.success) return [] + return result.error.issues.flatMap((issue) => { + const path = issue.path.map((component) => String(component)) + const rule = groveRuleIssueFromParameters( + issue.code === 'custom' ? issue.params : undefined, + path, + issue.message, + ) + return rule === undefined ? + [ + { + severity: 'error', + code: 'schema-invalid', + path, + message: issue.message, + } satisfies Issue, + ] + : [rule] + }) +} + +export const expectRule = ( + refine: Refinement, + resource: unknown, + code: string, +): void => { + expect( + refinementIssues(refine, resource).map(({ code: actual }) => actual), + ).toContain(code) +} diff --git a/packages/fhir/test/tsconfig.json b/packages/fhir/test/tsconfig.json index 446ee81c..bfc586c5 100644 --- a/packages/fhir/test/tsconfig.json +++ b/packages/fhir/test/tsconfig.json @@ -5,7 +5,8 @@ "moduleResolution": "NodeNext", "noEmit": true, "types": ["jest", "node"], - "rootDir": "../" + "rootDir": "../", + "resolveJsonModule": true }, "include": ["./**/*.ts", "../src/**/*.ts"], "exclude": [] diff --git a/packages/fhir/test/zod-fixtures.test.ts b/packages/fhir/test/zod-fixtures.test.ts index d9bc60a2..f78a68e4 100644 --- a/packages/fhir/test/zod-fixtures.test.ts +++ b/packages/fhir/test/zod-fixtures.test.ts @@ -46,8 +46,8 @@ describe('generated schemas against the published conformance fixtures', () => { const schema = schemaFor( (resource as { resourceType: string }).resourceType, ) - if (!schema) continue - expect(() => schema.parse(resource)).not.toThrow() + expect(schema).toBeDefined() + expect(() => schema?.parse(resource)).not.toThrow() } }) }) diff --git a/packages/fhir/test/zod-generation-invariants.test.ts b/packages/fhir/test/zod-generation-invariants.test.ts index ce1999ea..87a71d90 100644 --- a/packages/fhir/test/zod-generation-invariants.test.ts +++ b/packages/fhir/test/zod-generation-invariants.test.ts @@ -472,7 +472,10 @@ const UNCHECKED_KEYS: Readonly> = { } const source = (release: string): string => - readFileSync(join(process.cwd(), 'src/zod', release, 'schemas.ts'), 'utf8') + readFileSync( + join(process.cwd(), 'src/zod', release, 'schemas.generated.ts'), + 'utf8', + ) describe.each(RELEASES)('generated %s schemas', (release) => { const text = source(release) diff --git a/packages/fhir/test/zod-schemas.test.ts b/packages/fhir/test/zod-schemas.test.ts index 6e02e9ae..ed0cbafb 100644 --- a/packages/fhir/test/zod-schemas.test.ts +++ b/packages/fhir/test/zod-schemas.test.ts @@ -20,6 +20,7 @@ import { patientSchema, positiveIntSchema, instantSchema, + questionnaireSchema, resourceSchema, unsignedIntSchema, visionPrescriptionSchema, @@ -142,8 +143,36 @@ describe('generated Zod schemas', () => { }) it('validates a recursive backbone to its full depth', () => { - // Questionnaire.item.item is a contentReference back to Questionnaire.item; a nested item - // with a bad linkId must still be rejected rather than passing as an open record. + // Questionnaire.item.item is a contentReference back to Questionnaire.item, so a + // depth-2 item must obey the same rules as a depth-1 one rather than pass as an + // open record. + const nested = (depthTwo: Record) => ({ + resourceType: 'Questionnaire', + status: 'active', + item: [ + { + linkId: 'outer', + type: 'group', + item: [depthTwo], + }, + ], + }) + + expect( + questionnaireSchema.parse(nested({ linkId: 'inner', type: 'string' })), + ).toBeTruthy() + expect(() => + questionnaireSchema.parse(nested({ type: 'string' })), + ).toThrow() + expect(() => + questionnaireSchema.parse(nested({ linkId: 42, type: 'string' })), + ).toThrow() + expect(() => + questionnaireSchema.parse(nested({ linkId: 'inner', type: 'unknown' })), + ).toThrow() + }) + + it('rejects a DocumentReference status outside the required binding', () => { const document = { resourceType: 'DocumentReference', status: 'current', From 4606284be6785953f12044dde74692f33841b634 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Mon, 31 Aug 2026 12:05:43 -0700 Subject: [PATCH 20/31] Give the Browser Smoke Contract Its Required Questionnaire Subject --- packages/fhir/scripts/browser-smoke.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/fhir/scripts/browser-smoke.mjs b/packages/fhir/scripts/browser-smoke.mjs index caeda094..d2151deb 100644 --- a/packages/fhir/scripts/browser-smoke.mjs +++ b/packages/fhir/scripts/browser-smoke.mjs @@ -248,6 +248,7 @@ try { url: 'https://example.org/Questionnaire/browser', version: '1.0.0', status: 'active', + subjectTypes: ['Patient'], items: [ { linkId: 'ready', @@ -264,6 +265,7 @@ try { value: 'browser-1', }, status: 'completed', + subject: { type: 'Patient', reference: 'Patient/browser' }, authored: '2026-08-20T12:00:00Z', items: [ { From 7f01a3e4e0a90ce35c253679fc05d5a8ea385868 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Mon, 7 Sep 2026 11:40:28 -0700 Subject: [PATCH 21/31] Pin the Published 0.6.0 Grove FHIR Revision --- packages/fhir/grove-fhir.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/fhir/grove-fhir.json b/packages/fhir/grove-fhir.json index 2d3bfa06..6d852bf5 100644 --- a/packages/fhir/grove-fhir.json +++ b/packages/fhir/grove-fhir.json @@ -1,5 +1,5 @@ { "repository": "https://github.com/SchmiedmayerLab/grove-fhir", - "ref": "444e06fb25680c3af29e5d3ccbebeb0249be7318", - "archiveSha256": "e436be0be250a335a01db1a2580c06349bbc35b15ccb5a0397c023ba54a37ffe" + "ref": "83668d2d33325d2c1c9d42638fdc7557314f3d88", + "archiveSha256": "7320dedac84b2c1a5bfe78cd5641ee5709038506c339715ed8d295ebc8518418" } From eb50e9989519160060d3573af2008cc6fdd12cca Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Mon, 7 Sep 2026 12:56:34 -0700 Subject: [PATCH 22/31] Pin Grove FHIR to the Verified Tool Assets --- packages/fhir/grove-fhir.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/fhir/grove-fhir.json b/packages/fhir/grove-fhir.json index 6d852bf5..2c65d127 100644 --- a/packages/fhir/grove-fhir.json +++ b/packages/fhir/grove-fhir.json @@ -1,5 +1,5 @@ { "repository": "https://github.com/SchmiedmayerLab/grove-fhir", - "ref": "83668d2d33325d2c1c9d42638fdc7557314f3d88", - "archiveSha256": "7320dedac84b2c1a5bfe78cd5641ee5709038506c339715ed8d295ebc8518418" + "ref": "459e016610a94de776953601cc7b65cc445eb3c6", + "archiveSha256": "04c5f42ae083d8efa9b3cc651f8b1f170b948ef411684632d697a0b6a93b3cac" } From f3505068471ca72eee91dddb8fa47daa6766231d Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Mon, 21 Sep 2026 23:07:07 -0700 Subject: [PATCH 23/31] Align the TypeScript Producer Surface With the Implementation Guides --- .prettierignore | 3 + packages/fhir/README.md | 983 ++-- .../fhir/fixtures/conformance/manifest.json | 6 + .../conformance/mobile-exchange/corpus.json | 635 ++- .../mobile-exchange/exchange-bundle.json | 17 +- .../receiver-lifecycle/events.json | 318 ++ .../receiver-lifecycle/events.json.license | 3 + .../resources/altered-retry.json | 389 ++ .../resources/altered-retry.json.license | 3 + .../resources/changed-source-record.json | 383 ++ .../changed-source-record.json.license | 3 + .../resources/corrected.json | 389 ++ .../resources/corrected.json.license | 3 + .../resources/equal-version-conflict.json | 389 ++ .../equal-version-conflict.json.license | 3 + .../resources/equal-version-reassembly.json | 389 ++ .../equal-version-reassembly.json.license | 3 + .../resources/lexeme-retry.json | 389 ++ .../resources/lexeme-retry.json.license | 3 + .../resources/missing-order.json | 373 ++ .../resources/missing-order.json.license | 3 + .../resources/multi-v1.json | 518 ++ .../resources/multi-v1.json.license | 3 + .../resources/multi-v2.json | 389 ++ .../resources/multi-v2.json.license | 3 + .../resources/multi-v3.json | 518 ++ .../resources/multi-v3.json.license | 3 + .../resources/original.json | 389 ++ .../resources/original.json.license | 3 + .../resources/other-writer.json | 389 ++ .../resources/other-writer.json.license | 3 + .../receiver-lifecycle/resources/pending.json | 300 ++ .../resources/pending.json.license | 3 + .../resources/reformatted-retry.json | 389 ++ .../resources/reformatted-retry.json.license | 3 + .../resources/retraction.json | 147 + .../resources/retraction.json.license | 3 + .../receiver-lifecycle/resources/target.json | 367 ++ .../resources/target.json.license | 3 + .../resources/unordered-a.json | 373 ++ .../resources/unordered-a.json.license | 3 + .../resources/unordered-b.json | 373 ++ .../resources/unordered-b.json.license | 3 + .../revision-sequences.json | 4481 +++++++++++++++++ .../revision-sequences.json.license | 3 + .../receiver-lifecycle/sequences.json | 709 +++ .../receiver-lifecycle/sequences.json.license | 3 + .../resources/mobile-active-energy.json | 114 +- .../resources/mobile-basal-energy.json | 114 +- ...le-blood-glucose-unspecified-specimen.json | 111 +- .../resources/mobile-blood-pressure.json | 111 +- .../resources/mobile-body-fat-percentage.json | 111 +- .../resources/mobile-body-height.json | 111 +- .../resources/mobile-body-temperature.json | 111 +- .../resources/mobile-body-water-mass.json | 111 +- .../resources/mobile-body-weight.json | 111 +- .../resources/mobile-bone-mass.json | 111 +- .../resources/mobile-deep-sleep-duration.json | 114 +- .../resources/mobile-distance.json | 114 +- .../mobile-electrodermal-activity.json | 111 +- .../resources/mobile-flights-climbed.json | 114 +- .../mobile-heart-rate-variability-rmssd.json | 111 +- .../mobile-heart-rate-variability-sdnn.json | 111 +- .../resources/mobile-heart-rate.json | 111 +- .../resources/mobile-lean-body-mass.json | 111 +- .../mobile-light-sleep-duration.json | 114 +- ...obile-oxygen-saturation-daily-average.json | 114 +- .../resources/mobile-oxygen-saturation.json | 111 +- .../resources/mobile-rem-sleep-duration.json | 114 +- .../mobile-respiratory-rate-average.json | 114 +- .../resources/mobile-skin-temperature.json | 111 +- .../mobile-sleep-awake-duration.json | 114 +- .../resources/mobile-sleep-duration.json | 114 +- .../resources/mobile-sleep-heart-rate.json | 114 +- .../resources/mobile-step-count.json | 114 +- .../conformance/resources/mobile-vo2-max.json | 111 +- .../conformance/resources/mobile-workout.json | 114 +- ...-api-resting-heart-rate-daily-average.json | 114 +- .../provider-oura-daily-activity.json | 134 +- ...provider-oura-oura-cardiovascular-age.json | 114 +- .../provider-oura-oura-readiness-score.json | 114 +- .../provider-primary-output-retraction.json | 93 +- .../provider-source-artifact-retraction.json | 93 +- .../resources/provider-study-context.json | 527 ++ .../provider-study-context.json.license | 3 + .../provider-withings-body-fat-mass.json | 111 +- ...ovider-withings-corrected-qt-interval.json | 111 +- ...der-withings-extracellular-water-mass.json | 111 +- ...der-withings-intracellular-water-mass.json | 111 +- .../provider-withings-muscle-mass.json | 111 +- .../provider-withings-pr-interval.json | 111 +- .../provider-withings-qrs-duration.json | 111 +- .../provider-withings-qt-interval.json | 111 +- ...-atrial-fibrillation-notification-ecg.json | 111 +- ...-atrial-fibrillation-notification-ppg.json | 111 +- ...-withings-withings-nerve-health-score.json | 111 +- ...withings-withings-pulse-wave-velocity.json | 111 +- ...ovider-withings-withings-vascular-age.json | 111 +- ...-withings-withings-visceral-fat-index.json | 111 +- ...ecording-google-health-api-heart-rate.json | 120 +- .../resources/recording-oura-heartrate.json | 120 +- .../recording-withings-activityintraday.json | 120 +- .../recording-withings-sleepintraday.json | 120 +- .../context-two-studies.json | 585 +++ .../context-two-studies.json.license | 3 + .../study-attribution/negative-cases.json | 34 + .../negative-cases.json.license | 3 + .../study-attribution/source-event.json | 477 ++ .../source-event.json.license | 3 + .../google-health-active-energy.json | 14 +- .../google-health-heart-rate-recording.json | 12 +- .../normalized/oura-daily-activity.json | 14 +- .../normalized/oura-heart-rate-recording.json | 12 +- .../normalized/oura-sleep-duration.json | 14 +- .../withings-activity-intraday-recording.json | 12 +- .../normalized/withings-body-weight.json | 14 +- .../withings-sleep-intraday-recording.json | 12 +- packages/fhir/package.json | 11 +- packages/fhir/scripts/browser-smoke.mjs | 213 +- packages/fhir/scripts/fetch-grove-fhir.mjs | 125 +- .../scripts/generate-conformance-fixtures.mjs | 521 +- .../scripts/generate-measurement-catalog.mjs | 75 +- .../scripts/measurement-catalog-renderer.mjs | 10 +- packages/fhir/scripts/package-smoke.mjs | 17 +- packages/fhir/scripts/type-surface.mjs | 103 + .../fhir/scripts/validate-conformance.mjs | 22 +- .../contract/measurement-catalog.generated.ts | 919 +++- .../fhir/src/contract/providers.generated.ts | 24 +- .../src/contract/questionnaire.generated.ts | 4 +- packages/fhir/src/core/freeze.ts | 4 +- packages/fhir/src/core/index.ts | 8 + packages/fhir/src/core/json.ts | 7 +- packages/fhir/src/core/primitives.ts | 44 +- packages/fhir/src/core/result.ts | 14 +- packages/fhir/src/index.ts | 78 +- packages/fhir/src/mobile/context.ts | 479 ++ packages/fhir/src/mobile/contract.ts | 134 - packages/fhir/src/mobile/identity.ts | 781 +-- packages/fhir/src/mobile/index.ts | 50 +- packages/fhir/src/mobile/types.ts | 232 +- packages/fhir/src/providers/builder.ts | 879 ++-- packages/fhir/src/providers/context-graph.ts | 398 ++ packages/fhir/src/providers/graph.ts | 144 +- packages/fhir/src/providers/identity.ts | 752 ++- packages/fhir/src/providers/index.ts | 73 +- packages/fhir/src/providers/profiles.ts | 46 +- .../src/providers/provider-input-schemas.ts | 434 +- packages/fhir/src/providers/provider.ts | 667 +-- packages/fhir/src/providers/recording.ts | 735 ++- packages/fhir/src/providers/retraction.ts | 782 ++- packages/fhir/src/providers/types.ts | 243 +- .../fhir/src/r4/active-graph-semantics.ts | 153 +- .../src/r4/active-observation-semantics.ts | 158 +- packages/fhir/src/r4/diagnostics.ts | 200 +- packages/fhir/src/r4/exchange-envelope.ts | 350 +- packages/fhir/src/r4/graph-schema-utils.ts | 95 +- packages/fhir/src/r4/index.ts | 23 +- packages/fhir/src/r4/parse.ts | 75 +- packages/fhir/src/r4/profile-semantics.ts | 179 +- .../fhir/src/r4/retraction-graph-semantics.ts | 166 +- packages/fhir/src/r4/retraction-targets.ts | 174 + packages/fhir/src/r4/schemas.ts | 16 +- packages/fhir/src/r4/semantic-equality.ts | 227 + .../fhir/src/r4/study-context-semantics.ts | 165 + packages/fhir/src/r4/types.ts | 8 +- packages/fhir/test/diagnostics.test.ts | 133 +- packages/fhir/test/exchange-corpus.test.ts | 116 +- packages/fhir/test/exchange-protocol.test.ts | 408 +- packages/fhir/test/export-surface.json | 46 +- packages/fhir/test/exports.test.ts | 21 +- packages/fhir/test/identity-systems.test.ts | 111 + packages/fhir/test/mobile.test.ts | 192 +- .../fhir/test/observation-semantics.test.ts | 52 +- .../test/profile-semantics-boundaries.test.ts | 34 +- packages/fhir/test/provider-contract.test.ts | 373 +- packages/fhir/test/provider-identity.test.ts | 803 ++- packages/fhir/test/provider-mapping.test.ts | 374 +- packages/fhir/test/provider-semantics.test.ts | 838 ++- packages/fhir/test/provider-test-support.ts | 217 +- packages/fhir/test/readme-walkthrough.test.ts | 470 ++ packages/fhir/test/receiver-lifecycle.test.ts | 219 + packages/fhir/test/recording.test.ts | 889 ++-- packages/fhir/test/retraction.test.ts | 856 ++-- packages/fhir/test/rule-test-support.ts | 10 +- packages/fhir/test/study-context.test.ts | 380 ++ packages/fhir/test/type-surface.json | 513 ++ packages/fhir/test/type-surface.json.license | 3 + 187 files changed, 30796 insertions(+), 9154 deletions(-) create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/events.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/events.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/altered-retry.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/altered-retry.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/changed-source-record.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/changed-source-record.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/corrected.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/corrected.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-conflict.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-conflict.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-reassembly.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-reassembly.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/lexeme-retry.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/lexeme-retry.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/missing-order.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/missing-order.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v1.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v1.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v2.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v2.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v3.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v3.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/original.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/original.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/other-writer.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/other-writer.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/pending.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/pending.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/reformatted-retry.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/reformatted-retry.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/retraction.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/retraction.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/target.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/target.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-a.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-a.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-b.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-b.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/revision-sequences.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/revision-sequences.json.license create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/sequences.json create mode 100644 packages/fhir/fixtures/conformance/receiver-lifecycle/sequences.json.license create mode 100644 packages/fhir/fixtures/conformance/resources/provider-study-context.json create mode 100644 packages/fhir/fixtures/conformance/resources/provider-study-context.json.license create mode 100644 packages/fhir/fixtures/conformance/study-attribution/context-two-studies.json create mode 100644 packages/fhir/fixtures/conformance/study-attribution/context-two-studies.json.license create mode 100644 packages/fhir/fixtures/conformance/study-attribution/negative-cases.json create mode 100644 packages/fhir/fixtures/conformance/study-attribution/negative-cases.json.license create mode 100644 packages/fhir/fixtures/conformance/study-attribution/source-event.json create mode 100644 packages/fhir/fixtures/conformance/study-attribution/source-event.json.license create mode 100644 packages/fhir/scripts/type-surface.mjs create mode 100644 packages/fhir/src/mobile/context.ts delete mode 100644 packages/fhir/src/mobile/contract.ts create mode 100644 packages/fhir/src/providers/context-graph.ts create mode 100644 packages/fhir/src/r4/retraction-targets.ts create mode 100644 packages/fhir/src/r4/semantic-equality.ts create mode 100644 packages/fhir/src/r4/study-context-semantics.ts create mode 100644 packages/fhir/test/identity-systems.test.ts create mode 100644 packages/fhir/test/readme-walkthrough.test.ts create mode 100644 packages/fhir/test/receiver-lifecycle.test.ts create mode 100644 packages/fhir/test/study-context.test.ts create mode 100644 packages/fhir/test/type-surface.json create mode 100644 packages/fhir/test/type-surface.json.license diff --git a/.prettierignore b/.prettierignore index 05732ded..43ff2220 100644 --- a/.prettierignore +++ b/.prettierignore @@ -19,3 +19,6 @@ packages/design-system/docs/docs/api node_modules packages/fhir/.fhir-cache/ +packages/fhir/fixtures/conformance/mobile-exchange/ +packages/fhir/fixtures/conformance/receiver-lifecycle/ +packages/fhir/fixtures/conformance/study-attribution/ diff --git a/packages/fhir/README.md b/packages/fhir/README.md index e825a20a..360df32a 100644 --- a/packages/fhir/README.md +++ b/packages/fhir/README.md @@ -10,431 +10,626 @@ SPDX-License-Identifier: MIT # Grove FHIR for TypeScript -`@schmiedmayerlab/grove-fhir` provides strict FHIR R4 contracts and Grove resource builders for TypeScript applications. +`@schmiedmayerlab/grove-fhir` turns a connected-provider record into a Grove exchange graph: one FHIR R4 4.0.1 Bundle a receiver can trust. +It fetches nothing, authenticates with no one and stores nothing; those stay in your application. -The package targets FHIR R4 4.0.1 only. -It has no Firebase dependency and does not fetch health data, authenticate with providers, persist resources, or define transport behavior. +If you already know the pieces, jump to [Beyond the minimum](#beyond-the-minimum) or the [Reference](#reference). -Generated package metadata retains the exact SemVer coordinates required by FHIR package manifests. Runtime compatibility is determined by the generated Grove FHIR contracts, not by an exported implementation-guide release number. -The monorepo keeps its `0.0.0` source placeholders until the lockstep npm release workflow injects the selected package version. +## Why this exists -### Migrating from 0.1.x +A source record, such as a Withings measure or an Oura daily summary, becomes one immutable, self-describing FHIR Bundle called the exchange graph. +FHIR is the standard for exchanging health data as JSON documents called resources, and a Bundle is the document that carries several of them together. +Any receiver can deduplicate, correct and retract that graph without knowing which platform it came from. +Grove adds what plain FHIR lacks: stable identities that never leak the provider's own record ids, provenance that says which application assembled the graph on which host, and optional study context. +The receiver gets a record it can store, match to a retry, replace with a later revision and retract, all through identifiers it can verify and none it can reverse. -This release replaces the entire hand-written 0.1.x surface. -Nothing is carried forward under its old name, and every published 0.1.x export is gone. +If FHIR is new to you, the [New to FHIR](https://grovealliance.org/fhir/mobile/fhir-basics.html) page of the Grove Mobile guide explains the resources this package emits in about ten minutes. -Import the generated, contract-derived replacements from the entry point that owns them: the root for validated primitives, the bounded R4 closure, and Questionnaire support; `/r4` for the resource schemas and graph parsers; `/mobile` for the shared measurement contract and deterministic identity; `/providers` for the closed connected-provider builders; `/questionnaire` for the instrument pair; and `/zod/r4` or `/zod/r4b` for the generated base-release schemas. -Coverage only grew: every resource type the 0.1.x line validated has a generated schema here, alongside the rest of R4 and R4B. +## What you need and why -The 0.1.x line is superseded and should be deprecated on the registry once this release publishes: +Five inputs are yours to supply, in the order the graph is assembled. +Everything else has a default. -```sh -npm deprecate '@schmiedmayerlab/grove-fhir@<0.2.0' \ - 'Superseded by the generated Grove FHIR contracts; see the Migrating from 0.1.x section of the README.' -``` +### The subject pseudonym + +The subject is the participant, named by a pseudonym your deployment issued: an identifier system you own plus the pseudonym value, as a `Subject` of kind `logical`. +Every output says who the measurement is about, and a receiver matches records by that pair, so it must be stable and it must not be a name or a provider account. +It comes from your enrollment records. +Persist the system and the pseudonym with the participant; changing either makes later records look like another person. + +### The identity scope -## New to FHIR? +The identity scope is the material that mints every identifier: the twelve identifier systems derived from your deployment root, an HMAC key with its key id and epoch, and the id of this producer instance, validated once into an `OpaqueIdentityScope`. +HMAC is what makes an identifier both stable and opaque: the same source record exported twice yields the same identifier, so a receiver can deduplicate, and nobody without the key can recover the provider's id from it. +The root is a base URL your deployment owns, the key comes from your secret store, and the producer instance id is a UUID you generate once per installation. +Persist the key id, the epoch and the producer instance id beside the key; a rotation moves to a new epoch and therefore new systems, never a new key under an old epoch. -FHIR is a standard for exchanging health data as JSON documents called resources. -A measurement bundle holds four of them: an `Observation` for each measurement, a `Device` for the thing that recorded it, a `Provenance` saying where the record came from, and a `Bundle` holding the set together. -The package also builds `DocumentReference` graphs for preserved source recordings and `Questionnaire`/`QuestionnaireResponse` pairs. +### The event identifier -If those words are unfamiliar, read the _New to FHIR_ page of the Grove Mobile implementation guide -(`https://grovealliance.org/fhir/mobile`) first. -It explains the resources this package emits, the difference between a server-assigned `id` and a business `identifier`, and why a measurement carries two devices — in about ten minutes, without assuming any FHIR background. +The event identifier names one export: an immutable event with a monotonic sequence per producer instance, minted by `deriveEventIdentifier`. +A retry resends the same bytes under the same identifier, and a new revision of the record gets a new sequence, which is how a receiver tells a repeat from a change. +The sequence comes from your own storage, reserved in the same transaction that records the export. +Persist the last reserved sequence per producer instance. -The guide is the single place those concepts are explained; this README covers only what is specific to this package. +### The repository scope -## Your first bundle +The repository scope is the store the record came from, as a `BusinessIdentifier`: the linked provider account under a system you own, again as a pseudonym. +A provider's record ids are unique only within one account, so two accounts can hold the same id; the scope keeps them apart inside every derived identity. +It comes from your provider-account records. +Persist the account pseudonym with the linked account. -One measurement, turned into a complete Grove exchange graph: +### The application + +The application is the software assembling the graph: a stable token for this build, a name and a version, as an `ApplicationDevice`. +Provenance names the assembler, so a receiver knows which code produced a graph and can act on a defect in one version. +It is a constant of your build; nothing to persist beyond your release history. + +> **Note:** `host` is required on TypeScript, unlike the Swift and Kotlin producers: a phone reads its own facts from the operating system, a server states them, so you pass a stable token for the machine and its operating system version. +> The conversion instant defaults to the moment the builder runs; pass `conversionInstant` when you replay a stored export or when one batch must share one instant. +> `converterRole` defaults to the assembler, `studies` to none and `repositoryIds` to none. + +## Assemble it + +Once per installation: derive the identifier systems, validate the scope and describe the application. ```typescript import { + deriveOpaqueIdentitySystems, parseAbsoluteUri, - parseFhirInstant, - type DeploymentIdentityInput, + parseKeyEpoch, + validateOpaqueIdentityScope, + type ApplicationDevice, type Result, } from '@schmiedmayerlab/grove-fhir' -import { buildProviderMeasurementBundle } from '@schmiedmayerlab/grove-fhir/providers' -// Every parse returns a Result; a real application reports the issues instead of throwing. +// Every parser returns a Result; report the issues where a real application handles them. const unwrap = (result: Result): Value => { - if (!result.ok) throw new Error(result.issues[0]?.message) + if (!result.ok) { + throw new Error(result.issues.map(({ message }) => message).join('\n')) + } return result.value } +const uri = (value: string) => unwrap(parseAbsoluteUri(value)) -const measuredAt = unwrap(parseFhirInstant('2026-08-19T17:30:00Z')) - +// From your secret store: the HMAC key as base64url, and the UUID generated for this installation. const identitySecret = process.env.GROVE_IDENTITY_SECRET_BASE64URL -if (identitySecret === undefined) { - throw new Error('Load the Grove HMAC key from the deployment secret store.') +const producerInstance = process.env.GROVE_PRODUCER_INSTANCE +if (identitySecret === undefined || producerInstance === undefined) { + throw new Error('The identity key and the producer instance id are missing.') } -const identitySystem = (kind: string) => - unwrap( - parseAbsoluteUri( - `https://mystudy.example.org/fhir/identity/${kind}/study-key/1`, +const keyEpoch = unwrap(parseKeyEpoch('1')) +const identityScope = unwrap( + validateOpaqueIdentityScope({ + systems: unwrap( + deriveOpaqueIdentitySystems( + uri('https://mystudy.example.org/fhir'), + 'study-key', + keyEpoch, + ), ), - ) - -const deploymentIdentity: DeploymentIdentityInput = { - opaqueIdentifierSystems: { - 'source-record': identitySystem('source-record'), - 'source-output': identitySystem('source-output'), - 'writer-record': identitySystem('writer-record'), - 'provider-record': identitySystem('provider-record'), - 'provider-output': identitySystem('provider-output'), - 'source-artifact': identitySystem('source-artifact'), - 'provider-artifact': identitySystem('provider-artifact'), - 'source-context': identitySystem('source-context'), - 'recording-device': identitySystem('recording-device'), - 'device-snapshot': identitySystem('device-snapshot'), - }, - eventIdentifierSystem: identitySystem('event'), - entryNodeIdentifierSystem: identitySystem('entry-node'), - keyId: 'study-key', - keyEpoch: '1', - secretBase64Url: identitySecret, - producerInstance: 'd59d7bda-7c5a-48f9-a5ac-0eb87ec04cb0', + keyId: 'study-key', + keyEpoch, + secretBase64Url: identitySecret, + producerInstance, + }), +) + +const application: ApplicationDevice = { + sourceDeviceToken: 'org.example.mystudy-server/2.1.0', + name: 'MyStudy server', + version: '2.1.0', } +``` -const result = buildProviderMeasurementBundle({ +Per export: reserve the next event sequence and create the context, using every default. + +```typescript +import { hostname, release, type } from 'node:os' +import { + deriveEventIdentifier, + parseEventSequence, + type ExchangeEventContext, +} from '@schmiedmayerlab/grove-fhir' + +// Reserve the sequence in the same transaction that records this export; it is never reused. +const sequence = unwrap(parseEventSequence(reserveNextEventSequence())) + +const context: ExchangeEventContext = { subject: { - type: 'Patient', + kind: 'logical', identifier: { - system: unwrap( - parseAbsoluteUri( - 'https://mystudy.example.org/fhir/identifiers/patient-pseudonyms', - ), - ), - value: 'pseudonym-participant-01', - assurance: 'deployment-scoped-pseudonym', + system: uri('https://mystudy.example.org/fhir/identifiers/participants'), + value: participant.pseudonym, }, }, + event: unwrap(deriveEventIdentifier(identityScope, sequence)), + identityScope, + repositoryScope: { + system: uri( + 'https://mystudy.example.org/fhir/identifiers/withings-accounts', + ), + value: participant.withingsAccountPseudonym, + }, + application, + host: { + sourceDeviceToken: hostname(), + operatingSystemVersion: `${type()} ${release()}`, + }, +} +``` + +Convert one record and hand the Bundle to your uploader. + +```typescript +import { parseFhirInstant } from '@schmiedmayerlab/grove-fhir' +import { + buildProviderExchangeGraph, + type NormalizedProviderRecord, +} from '@schmiedmayerlab/grove-fhir/providers' + +// One row of the Withings measure response, already fetched and decoded by your adapter. +const record: NormalizedProviderRecord = { + source: { + adapter: { kind: 'providers', provider: 'withings' }, + sourceType: 'getmeas:11', + sourceNativeId: measure.grpid, + writer: { sourceDeviceToken: 'withings-api', name: 'Withings API' }, + }, measurements: [ { - kind: 'body-weight', - value: 72.5, - effective: { kind: 'date-time', value: measuredAt }, + kind: 'heart-rate', + value: measure.value, + effective: { + kind: 'date-time', + value: unwrap(parseFhirInstant(measure.date)), + }, }, ], - source: { - adapter: { kind: 'providers', provider: 'google-health-api' }, - providerScopeIdentifier: { - system: unwrap( - parseAbsoluteUri( - 'https://mystudy.example.org/fhir/identifiers/provider-accounts', - ), - ), - value: 'provider-account-pseudonym-7f3a', - assurance: 'deployment-scoped-account-pseudonym', - }, - sourceType: 'weight', - sourceNativeId: 'ghapi-weight-20260819', - recordingMethod: 'automatically-recorded', - dataOrigin: { - sourceDeviceToken: 'google-health-api-2026-08', - name: 'Google Health API', +} + +const result = buildProviderExchangeGraph(record, context) +if (result.ok) { + upload(result.value.graph) +} else { + for (const issue of result.issues) { + console.error(issue.code, issue.path.join('.'), issue.message) + } +} +``` + +What to persist, and why: + +| Value | Why | +| ---------------------------------------------- | -------------------------------------------------------------------------------------------- | +| The last event sequence, per producer instance | A reused sequence under different bytes is two events claiming one identity. | +| The key id and the epoch, beside the key | They are part of every identifier system; the wrong pair mints identifiers nobody can match. | +| The producer instance id | It is inside every event identifier; a new one starts a new sequence space. | + +> **Important:** Never reuse a sequence for different content. +> A retry is the same bytes under the same identifier; anything else is a new event with a new sequence. + +> **Important:** Never change the key or the epoch without changing the systems. +> `deriveOpaqueIdentitySystems` derives the systems from the key id and the epoch, so a rotation is a new epoch with new systems, and the old epoch stays readable for the identifiers minted under it. + +## Beyond the minimum + +### Study enrollment + +When you know the participant's enrollment, name the study, its exact protocol revision and the enrollment; the graph bundles them as ResearchStudy, PlanDefinition and ResearchSubject entries and references the study from every output. +A bundled subject adds your own Patient entry. +Reference: [The exchange event context](#the-exchange-event-context) and the guide's [Study context](https://grovealliance.org/fhir/mobile/study.html). + +```typescript +const enrolled: ExchangeEventContext = { + ...context, + subject: { + kind: 'bundled', + identifier: context.subject.identifier, + patient: { + resourceType: 'Patient', + identifier: [context.subject.identifier], }, }, - application: { - sourceDeviceToken: 'org.example.mystudy/2.1.0/42', - name: 'MyStudy', - version: '2.1.0', - build: '42', + studies: [ + { + study: { + system: uri('https://mystudy.example.org/fhir/identifiers/studies'), + value: 'heart-2026', + }, + protocol: { + url: uri('https://mystudy.example.org/fhir/PlanDefinition/heart-2026'), + version: '3', + }, + enrollment: { + system: uri('https://mystudy.example.org/fhir/identifiers/enrollments'), + value: 'enrollment-7f3a', + }, + }, + ], +} +``` + +### Disclosure policies + +By default the provider's record id is HMAC input only; nothing in the graph reveals it. +A deployment that needs round-trip traceability discloses it on the primary output under a key space it governs. +Reference: [Governed source identifiers](#governed-source-identifiers). + +```typescript +const disclosed = buildProviderExchangeGraph(record, context, { + nativeIdentifierDisclosure: { + kind: 'authorized', + system: uri( + 'https://mystudy.example.org/fhir/identifiers/withings-measure-groups', + ), + type: { text: 'Withings measure group id' }, }, - deploymentIdentity, - eventSequence: '1042', - occurred: measuredAt, - recorded: unwrap(parseFhirInstant('2026-08-20T12:02:00Z')), - assembled: unwrap(parseFhirInstant('2026-08-20T12:03:00Z')), }) +``` -if (!result.ok) { - for (const issue of result.issues) { - console.error(issue.path.join('.'), issue.code, issue.message) +### Repository ids + +A graph carries no `Resource.id` unless your repository has already assigned one to a node. +Reference: [The exchange event context](#the-exchange-event-context). + +```typescript +import { parseFhirId } from '@schmiedmayerlab/grove-fhir' + +const stored: ExchangeEventContext = { + ...context, + repositoryIds: { + bundle: unwrap(parseFhirId('bundle-1042')), + 'primary-output': unwrap(parseFhirId('observation-1042')), + }, +} +``` + +### A distinct gateway application + +When another application relayed the measurement, name it as the gateway; it appears as a second application snapshot referenced from every output. +Reference: [Devices and writers](#devices-and-writers). + +```typescript +const relayed: ExchangeEventContext = { + ...context, + converterRole: { + kind: 'gateway-application', + application: { + sourceDeviceToken: 'org.example.mystudy-app/4.2.0', + name: 'MyStudy app', + version: '4.2.0', + }, + }, +} +``` + +### Warnings + +An accepted record can still lose something, and `warnings` says what, with a registered `mobile-omission.*` code: `recording-device` when the source names a device without a stable per-unit token, so no Device is emitted; `source-offset` when an instant came without a UTC offset, so it is serialized in UTC; `unmodeled-metadata` when the source carried fields outside the adapter's allowlist, so they were withheld. +Log them with the event so a receiver's question has an answer. +Reference: [Diagnostics](#diagnostics). + +```typescript +if (result.ok) { + for (const warning of result.value.warnings) { + console.warn(warning.code, warning.reason) } } ``` -A success gives you a frozen `collection` Bundle that has passed the package's profile-aware producer preflight, holding the Observation, application snapshots, and Provenance. Release conformance still uses the official validator as described below. +### Batch conversion + +Convert many records under one call; the callback reserves a fresh event for each one, and refusals sit beside conversions instead of aborting the batch. +Reference: [Provider measurements](#provider-measurements). + +```typescript +import { buildProviderExchangeGraphs } from '@schmiedmayerlab/grove-fhir/providers' + +const batch = buildProviderExchangeGraphs(records, () => ({ + ...context, + event: unwrap( + deriveEventIdentifier( + identityScope, + unwrap(parseEventSequence(reserveNextEventSequence())), + ), + ), +})) +for (const conversion of batch.conversions) upload(conversion.graph) +for (const failure of batch.failures) { + console.error( + failure.record.source.sourceNativeId, + failure.issues.map(({ code }) => code), + ) +} +``` + +### Retries + +A receiver decides whether a replay is the exact retry over JSON tokens, never bytes: member order, whitespace and escaping do not matter, decimal lexemes do. +Reference: [Replay and equality](#replay-and-equality). + +```typescript +import { isSemanticallyEqual } from '@schmiedmayerlab/grove-fhir' + +const sameEvent = unwrap( + isSemanticallyEqual(storedGraphText, JSON.stringify(graph)), +) +``` + +### Retraction events + +A source removal is a new event that names what it retracts by identity, never a mutation of the earlier graph. +`conversion` is the successful result of `buildProviderExchangeGraph`. +Reference: [Retractions](#retractions). + +```typescript +import { retractionTargets } from '@schmiedmayerlab/grove-fhir' +import { buildProviderRetractionEvent } from '@schmiedmayerlab/grove-fhir/providers' + +const retraction = buildProviderRetractionEvent( + unwrap(retractionTargets(graph)), + { + ...context, + event: unwrap( + deriveEventIdentifier( + identityScope, + unwrap(parseEventSequence(reserveNextEventSequence())), + ), + ), + }, + conversion.identifiers.sourceRecord, + unwrap(parseFhirInstant('2026-09-01T08:00:00Z')), +) +``` + +### Writing back + +Connected providers are read-only sources, so there is no reverse projection from FHIR to a provider record here. +Reference: [No reverse projection](#no-reverse-projection). + +### The conformance lane + +Prove your output against the guide's own corpus and the official validator before you ship. +Reference: [Development conformance](#development-conformance). + +```sh +npm run conformance:structural -- --ig /path/to/grove-fhir +npm run conformance -- --ig /path/to/grove-fhir +``` + +## Glossary + +The guide's vocabulary and the TypeScript type that carries it. + +| Term in the guide | Here | +| ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | +| Exchange event | `ExchangeEventIdentifier`, minted by `deriveEventIdentifier` | +| Exchange graph | `ExchangeGraph`, built by `buildProviderExchangeGraph` and checked by `parseExchangeGraph` | +| Business identifier | `BusinessIdentifier` | +| Identifier role | `GroveIdentifierRole`, carried by a `RoledIdentifier` | +| Opaque identity | minted by an `OpaqueIdentityScope` under `DeploymentIdentifierSystems` | +| Entry-node key | `EntryNodeKey`, rendered as an `EntryNodeIdentifier` | +| Subject | `Subject` | +| Study enrollment | `StudyEnrollment` | +| Application device, host device, recording device | `ApplicationDevice`, `HostDevice`, `RecordingDevice` | +| Writer | `writer` on the source record, an `ApplicationDevice`; `WriterRecord` when the platform assigns a record identity | +| Retraction event and retraction target | `RetractionEvent` and `RetractionTarget`, derived by `retractionTargets` | +| Governed source identifier | `GovernedSourceIdentifierDisclosurePolicy` | +| Producer diagnostic | `ProducerDiagnosticCode`, described by `groveProducerDiagnostics`, reported as an `Issue` | + +## Reference + +### Migrating from 0.1.x + +This release replaces the entire hand-written 0.1.x surface. +Nothing is carried forward under its old name, and every published 0.1.x export is gone. + +Import the generated, contract-derived replacements from the entry point that owns them: the root for validated primitives, the bounded R4 closure, and Questionnaire support; `/r4` for the resource schemas and graph parsers; `/mobile` for the shared measurement contract and deterministic identity; `/providers` for the closed connected-provider builders; `/questionnaire` for the instrument pair; and `/zod/r4` or `/zod/r4b` for the generated base-release schemas. +Coverage only grew: every resource type the 0.1.x line validated has a generated schema here, alongside the rest of R4 and R4B. -Four conventions are visible in that call and hold throughout the package: +The 0.1.x line is superseded and should be deprecated on the registry once this release publishes: -- **Nothing throws for bad input.** - Every parser and builder returns a `Result`: either `{ ok: true, value }` or `{ ok: false, issues }`, where each issue carries a stable `code` and a `path` into your input. - Unknown JavaScript input first crosses a strict JSON-domain snapshot boundary, so cycles, accessors, sparse arrays, special objects, and non-JSON values are reported rather than reaching schema code or becoming shared mutable state. - Reserve `try`/`catch` for genuine programming errors. -- **Validated strings are branded types.** - An `AbsoluteUri` or a `FhirInstant` can only be produced by its parser, so a malformed timestamp is caught where it enters rather than at the receiving server. - This is why the example parses its literals instead of passing raw strings. -- **You supply governed identity inputs; the package derives the graph.** - The deployment owns the key, key-epoch-specific Identifier systems, producer instance, monotonic event sequence, account pseudonym, source-native id, and all event times. - Source and output identifiers use the Grove domain-separated HMAC contract; internal `urn:uuid` links use UUIDv5 over the selected complete Identifier pair. - An exact retry reuses the event sequence, `occurred`, `recorded`, `assembled`, and payload. A changed source revision or payload is a new event with a new sequence. -- **Results are frozen plain JSON.** - Serialize them, store them, send them; there are no classes and no hidden state. +```sh +npm deprecate '@schmiedmayerlab/grove-fhir@<0.2.0' \ + 'Superseded by the generated Grove FHIR contracts; see the Migrating from 0.1.x section of the README.' +``` -## Requirements +### Requirements - Node.js 24 or newer - npm 12 for repository development - TypeScript 6 for repository development +- `zod` 4 as a peer dependency, installed by the consuming application The published JavaScript uses portable ES2022 and Web Platform APIs. -## Design +### Design - FHIR resources are immutable plain JSON values. - Runtime schemas are strict and reject unknown properties instead of silently removing them. - The supported R4 closure is intentionally bounded to the resources Grove constructs and validates. - Expected input failures use a discriminated `Result` with stable issue codes and paths; successful results may carry non-blocking warnings. -- Validated strings use branded types for instants, canonical URLs, FHIR ids, Patient references, and `urn:uuid` full URLs. -- Provider scope, native source id, device snapshot token, durable producer-wide event sequence, key lifecycle, and all times are caller-owned inputs. Account-scoped providers require a deployment pseudonym; a provider with authoritative global record keys instead requires one documented global key-space pair, so identity cannot fragment across accounts. +- Validated strings use branded types for instants, canonical URLs, FHIR ids, Patient references, `urn:uuid` full URLs, key epochs, event sequences, and entry-node ordinals. +- The identity scope, event identifier, repository scope, device snapshots, conversion instant, and studies are caller-owned context; the source record is the adapter's normalized handoff. - Source/output/artifact/writer/device identifiers use HMAC-SHA-256 over unsigned-32-bit length-framed UTF-8 fields with an explicit protocol domain, key id, and positive key epoch. - `Bundle.identifier` is the sole business identifier for one immutable source-record revision event; Provenance uses a typed event-scoped node key because R4 Provenance has no business-identifier element. - Every Bundle `fullUrl` is lowercase UUIDv5 over the length-framed selected Identifier `(system, value)` pair. UUIDv5 formats an internal link; it is not a privacy mechanism. - Every emitted output carries matching typed `source-record` and `source-output` identifiers. Source-preservation documents additionally carry one typed `source-artifact` identifier. -- `Resource.id` is omitted unless the caller supplies a repository-assigned id. - -## Provider measurements - -The generated Grove Mobile catalog defines the measurements whose semantics are -shared by at least two evidenced sources; `sharedMobileMeasurementCatalog` exposes them -with their value kind, coded-result vocabulary, method, and member contracts. -Owner-exclusive measurements (HealthKit, Health Connect, and -connected-provider platform exclusives) are deliberately absent from the -shared catalog and are published separately as `adapterMeasurementCatalog`, -keyed by owning adapter, from the `providers` entry point. -This Provider facade exposes a closed provider/source-token/measurement union -for the shared scalar and coded measurements admitted by the frozen Google -Health API, Oura, and Withings inventories. It includes point and period -quantities, closed coded results such as workout classification, and composite -blood pressure. Shared profiles without a semantically exact provider source -remain profiled but are not constructible through this facade, and -owner-exclusive scalar mappings are exposed only through their exact owner profile and provider source row. BMI reuses the -standard R4 profile with a HealthKit adapter claim; the four specimen-specific -glucose profiles belong to Health Connect. Neither is incorrectly presented as -a shared Mobile profile. Google glucose without specimen evidence maps only to -the specimen-neutral shared profile, daily vital summaries are admitted only -as their distinct daily-average measurements, and provider stage-duration -summaries map to the per-stage sleep-duration measurements rather than -sleep-stage intervals. There is intentionally no unprofiled fallback builder. +- `Resource.id` is omitted unless the context supplies a repository-assigned id for that graph node. +- Every public concept is named after the Mobile guide's vocabulary: exchange graph, business identifier, identifier role, opaque identity, entry-node key, subject, study enrollment, application device, host device, recording device, retraction event, retraction target, governed source identifier, and producer diagnostic. + +### Deployment identity + +`deriveOpaqueIdentitySystems(root, keyId, epoch)` names all twelve deployment-owned identifier systems by the catalog's recommended form: `/NamingSystem/grove--v0//` for the ten opaque identity kinds, `/NamingSystem/grove-event-v0` for events, and `/NamingSystem/grove-entry-node-v0` for entry-node keys. +The normative test vectors are derived from `https://study.example.org/fhir`, so a deployment that adopts the form names its identities exactly as every other Grove producer does. +A deployment that already governs its own namespaces supplies them explicitly instead. + +`validateOpaqueIdentityScope(input)` checks the systems, key id, epoch, secret, and producer instance once and returns an `OpaqueIdentityScope`. +The handle prints its systems, key id, epoch, and producer instance; the decoded key stays private to the module, and every minting function accepts only a handle it validated. +A rotated key uses a new epoch and therefore new systems, and a deployment retains old epochs while identifiers minted under them can still be replayed or retracted. + +`deriveOpaqueIdentifier(scope, kind, components)`, `deriveEventIdentifier(scope, sequence)`, and `deriveEntryNodeIdentifier(scope, key)` mint the three identifier families from a scope; `createEntryIdentity` and `deriveEntryFullUrl` derive the deterministic Bundle `fullUrl` for any complete identifier pair. + +### The exchange event context + +Every builder takes an `ExchangeEventContext` beside the source record. +It names the subject, the event identifier the scope minted for this event, the identity scope, the repository scope the source keys are unique in, the converter application and its host. +`host` stays required on TypeScript because a server supplies its own facts, where the Swift and Kotlin producers read them from the device. +`conversionInstant` defaults to the instant the builder runs, `converterRole` to the assembler, `studies` to none and `repositoryIds` to none; `parseExchangeEventContext(input)` is the strict boundary that applies them and returns a `ResolvedExchangeEventContext`, and the builders run it themselves. +`gateway` states that the converter itself mediated the measurement and `gateway-application` names a distinct application that did, emitted as a second application snapshot referenced by `observation-gatewayDevice`. +A bundled study context is held to `mobile-support.study-context` by `parseExchangeGraph`: one ResearchStudy whose `protocol` names its PlanDefinition, that PlanDefinition with `url` and `version`, and one ResearchSubject linking the graph's subject to the study, each keyed as an entry node with its study-context role. + +The subject is logical by default: the identifier-only deployment pseudonym reference the guide admits, with no fabricated Patient entry. +A bundled subject adds the deployment's own Patient resource as an entry keyed by the `patient` node role, and every output references that entry. + +A `StudyEnrollment` names one ResearchStudy by business identifier, the exact protocol revision as a PlanDefinition `url` and `version`, and one ResearchSubject enrollment identifier. +For each enrollment the graph bundles those three entries under the catalog's `research-study`, `plan-definition`, and `research-subject` node roles, references the study from every output through `workflow-researchStudy`, and references the protocol from `ResearchStudy.protocol`. +`workflow-instantiatesCanonical` is not an admitted extension and is never emitted. +A native recording document cannot carry the study extension, so a context that names studies is a fault for the recording builder rather than a silent omission. + +Context faults are deployment configuration, never producer rules: they carry the package's schema codes, because no source record can cause them. + +### Provider measurements + +The generated Grove Mobile catalog defines the measurements whose semantics are shared by at least two evidenced sources; `sharedMobileMeasurementCatalog` exposes them with their value kind, coded-result vocabulary, method, and member contracts. +Owner-exclusive measurements (HealthKit, Health Connect, and connected-provider platform exclusives) are deliberately absent from the shared catalog and are published separately as `adapterMeasurementCatalog`, keyed by owning adapter, from the `providers` entry point. +This Provider facade exposes a closed provider/source-token/measurement union for the shared scalar and coded measurements admitted by the frozen Google Health API, Oura, and Withings inventories. +It includes point and period quantities, closed coded results such as workout classification, and composite blood pressure. +Shared profiles without a semantically exact provider source remain profiled but are not constructible through this facade, and owner-exclusive scalar mappings are exposed only through their exact owner profile and provider source row. +BMI reuses the standard R4 profile with a HealthKit adapter claim; the four specimen-specific glucose profiles belong to Health Connect. +Neither is incorrectly presented as a shared Mobile profile. +Google glucose without specimen evidence maps only to the specimen-neutral shared profile, daily vital summaries are admitted only as their distinct daily-average measurements, and provider stage-duration summaries map to the per-stage sleep-duration measurements rather than sleep-stage intervals. +There is intentionally no unprofiled fallback builder. ```typescript import { - buildProviderMeasurementBundle, - parseProviderMeasurementBundleInput, + buildProviderExchangeGraph, + buildProviderExchangeGraphs, parseNormalizedProviderRecord, - type ProviderMeasurementBundleInput, + type NormalizedProviderRecord, } from '@schmiedmayerlab/grove-fhir/providers' ``` -`parseNormalizedProviderRecord(unknown)` is the strict boundary for one source -record that an application has already mapped from Google Health API, Oura, or -Withings. Its `measurements` tuple is a non-empty, unique subset of the outputs -present in that record and admitted for its exact provider/source token. The -builder emits all supplied outputs atomically with one Provenance that targets -every resulting Observation; a malformed member fails the entire conversion. -Catalog order makes serialization deterministic but carries no FHIR semantic -meaning. For example, one Oura `daily_activity` record may carry any present -subset of step count, active energy, and distance; those outputs remain one -auditable conversion event and share the source's complete midnight-to-midnight -civil-day Period. The same complete-day rule applies to Withings daily activity -rows and permits offset changes across daylight-saving transitions. The strict -schema rejects unknown fields and raw provider responses. -This package contains no provider client, authentication, pagination, webhook, -or fetching behavior. - -`parseProviderMeasurementBundleInput(unknown)` applies the same fail-closed behavior to the -complete graph input. `buildProviderMeasurementBundle` also runs that strict parser itself, -so JavaScript callers cannot bypass the boundary or add fields that would be -silently ignored. Identity strings are validated without trimming or otherwise -changing their HMAC preimage. - -Every scalar `effectiveDateTime` and aggregate `effectivePeriod` endpoint is -canonicalized with the generated Mobile IG contract: exact offset-bearing -instants are rounded to the nearest millisecond with ties to even, including -across the Unix epoch, and serialized with the supplied numeric offset. The -source offset is never replaced or invented. `canonicalizeMobileEffectiveInstant` -exposes the same `Result`-typed primitive operation; Sensor and ECG -`SampledData` timing intentionally remains outside this policy. - -The public producer is deliberately closed to the Provider adapter -profile. A future HealthKit, Health Connect, or other adapter must add its own -closed facade and profile claims; callers cannot inject an arbitrary adapter -canonical into this API. - -The four provider-native series that the IG marks `mapped-standard` use the -separate closed `buildProviderRecordingBundle` facade: +`parseNormalizedProviderRecord(unknown)` is the strict boundary for one source record that an application has already mapped from Google Health API, Oura, or Withings. +Its `measurements` tuple is a non-empty, unique subset of the outputs present in that record and admitted for its exact provider/source token, and its `source` names the adapter, source type, native id, the application that entered the record at the provider (`writer`), and optionally the recording method, the recording device, and the writer record. +The builder emits all supplied outputs atomically with one Provenance that targets every resulting Observation; a malformed member refuses the entire record. +Catalog order makes serialization deterministic but carries no FHIR semantic meaning. +For example, one Oura `daily_activity` record may carry any present subset of step count, active energy, and distance; those outputs remain one auditable conversion event and share the source's complete midnight-to-midnight civil-day Period. +The same complete-day rule applies to Withings daily activity rows and permits offset changes across daylight-saving transitions. +The strict schema refuses unknown fields and raw provider responses. +This package contains no provider client, authentication, pagination, webhook, or fetching behavior. + +`buildProviderExchangeGraph(record, context, options?)` runs that strict parser itself, so JavaScript callers cannot bypass the boundary or add fields that would be silently ignored. +Identity strings are validated without trimming or otherwise changing their HMAC preimage. +`buildProviderExchangeGraphs(records, context, options?)` converts several records, calling the context function once per record so each reservation of an event sequence stays the application's own decision, and returns a `ConversionBatch` whose `conversions` and `failures` sit side by side; an error the context function throws propagates, because a failed reservation is the caller's exception and never a refusal. + +Provenance.occurred[x] spans the effective times of the record's outputs, Provenance.recorded and Bundle.timestamp are the conversion instant, and every scalar `effectiveDateTime` and aggregate `effectivePeriod` endpoint is canonicalized with the generated Mobile IG contract: exact offset-bearing instants are rounded to the nearest millisecond with ties to even, including across the Unix epoch, and serialized with the supplied numeric offset. +The source offset is never replaced or invented. +`canonicalizeMobileEffectiveInstant` exposes the same `Result`-typed primitive operation; Sensor and ECG `SampledData` timing intentionally remains outside this policy. + +The public producer is deliberately closed to the Provider adapter profile. +A future HealthKit, Health Connect, or other adapter must add its own closed facade and profile claims; callers cannot inject an arbitrary adapter canonical into this API. + +#### Native recordings + +The four provider-native series that the IG marks `mapped-standard` use the separate closed `buildProviderRecordingGraph(source, attachment, context, options?)` facade: ```typescript import { - buildProviderRecordingBundle, + buildProviderRecordingGraph, encodeRecordingBytes, parseMediaType, } from '@schmiedmayerlab/grove-fhir/providers' ``` -The admitted source tokens are Google Health API `heart-rate`, Oura -`heartrate`, and Withings `activityIntraday` and `sleepIntraday`. The caller -passes an explicitly authorized and minimized encoding of bytes already in its -possession, or an immutable version-specific HTTP(S) URL with required size and -SHA-1/base64 integrity metadata. The builder emits a Sensor + Provider -`DocumentReference`, converter and data-origin application Devices, conversion -Provenance targeting the document, and the enclosing Mobile collection Bundle. -It never fetches the URL or parses provider-native content. Embedded attachment -size and hash are computed from the exact decoded bytes. - -Every attachment declares exactly one payload format from the closed -`groveRecordingFormatRegistry` (for provider API responses, `provider-recording`), -and its `contentType` must match that registry entry; the format is emitted as -the `DocumentReference.content.format` coding. Every attachment also requires -exactly one of the two catalog-derived producer assertions: -`caller-authorized-opaque-payload` or `verified-sanitized-input`. An absent, -ambiguous, or unsupported format or assertion fails closed. The assertion is producer preflight only, is never emitted as -FHIR authorization or consent, and is separate from recording-hardware identity -authorization. - -For an admitted HealthKit clinical-record document, the parser consumes the -HealthKit catalog's `clinicalRecordAdmission.fhirRepresentation` object. It -requires exactly one extension at the published URL, exactly the published -FHIR value element, and the fixed admitted release. Missing, duplicate, DSTU2, -unknown, or differently shaped release metadata fails closed; Grove preserves -the provider-issued R4 payload bytes without asserting issuer conformance. - -`sourceNativeId` remains HMAC-input-only by default for raw recordings too. -An application that deliberately needs round-trip traceability may opt in with -`nativeIdentifierDisclosure`; its absolute `system` must name the governed -repository/account/store key space and its `nativeId` must exactly equal -`source.sourceNativeId`. The builder then adds that Identifier only to the sole -primary `DocumentReference`. It never copies it to the companion artifact, -Devices, Provenance, entry keys, titles, URLs, or payload metadata. Opaque -attachment bytes are not inspected or sanitized by Grove; neither an HMAC nor -an Attachment hash grants authorization to disclose or retain them. - -The high-level builder accepts a validated `ProviderMeasurementBundleInput` and returns a -`Result`. The collection contains every output, -immutable converter and data-origin application snapshots, optional host and -recording-device snapshots, an optional distinct gateway application, and one -conversion Provenance. Every internal edge uses a derived `urn:uuid` Bundle -full URL. Each entry carries the selected complete Identifier pair from which -that URL was derived. - -FHIR conversion does not make the converter a gateway. The gateway extension -is absent by default and appears only when `GatewayApplicationInput` carries the -explicit `mediated-or-routed-measurement` role assurance. The gateway may be the -converter in that additional role or a separately identified application. -Recording-device identity likewise declares either a deployment scope or an -explicitly authorized hardware scope. It requires a governed stable per-unit -token and emits both a stable `recording-device` identity and an event-scoped -`device-snapshot` identity; without that evidence the Device is omitted. Serial -numbers are not accepted by the producer API. Application and host snapshots -are separate Devices linked through `Device.parent`. A single snapshot can -fill multiple Provenance or gateway roles and is emitted only once. - -The source record input contains a complete `providerScopeIdentifier`, an exact -catalog source token, and the provider-native id. Google Health API and Withings -require a deployment-scoped account pseudonym; Oura's authoritative globally -unique document ids use one documented global key-space pair rather than an -account coordinate. The native id and provider scope are HMAC inputs only unless -the caller uses the explicit native-Identifier disclosure above; the provider -scope is never emitted. A scalar record may disclose the native Identifier only when its -generated Provider source row designates one unique one-to-one Observation: +The admitted source tokens are Google Health API `heart-rate`, Oura `heartrate`, and Withings `activityIntraday` and `sleepIntraday`. +The source names the span the recording covers as its `effective` time, and the caller passes an explicitly authorized and minimized encoding of bytes already in its possession, or an immutable version-specific HTTP(S) URL with required size and SHA-1/base64 integrity metadata. +The builder emits a Sensor + Provider `DocumentReference`, the converter application, its host and the writer as Device snapshots, conversion Provenance targeting the document, and the enclosing Mobile collection Bundle. +It never fetches the URL or parses provider-native content. +Embedded attachment size and hash are computed from the exact decoded bytes. -```typescript -import { parseAbsoluteUri } from '@schmiedmayerlab/grove-fhir' -import type { GovernedSourceIdentifierInput } from '@schmiedmayerlab/grove-fhir/providers' +Every attachment declares exactly one payload format from the closed `groveRecordingFormatRegistry` (for provider API responses, `provider-recording`), and its `contentType` must match that registry entry; the format is emitted as the `DocumentReference.content.format` coding. +Every attachment also requires exactly one of the two catalog-derived producer assertions: `caller-authorized-opaque-payload` or `verified-sanitized-input`. +An absent, ambiguous, or unsupported format or assertion refuses the record. +The assertion is producer preflight only, is never emitted as FHIR authorization or consent, and is separate from recording-hardware identity authorization. -const nativeIdentifierDisclosure: GovernedSourceIdentifierInput = { - system: unwrap( - parseAbsoluteUri( - 'https://example.org/repositories/withings-account-7/heart-rate-records', - ), - ), - nativeId: 'withings-heart-rate-2026-08-20', // exactly source.sourceNativeId - type: { text: 'Withings account-scoped heart-rate id' }, -} -``` +For an admitted HealthKit clinical-record document, the parser consumes the HealthKit catalog's `clinicalRecordAdmission.fhirRepresentation` object. +It requires exactly one extension at the published URL, exactly the published FHIR value element, and the fixed admitted release. +Missing, duplicate, DSTU2, unknown, or differently shaped release metadata fails closed; Grove preserves the provider-issued R4 payload bytes without asserting issuer conformance. -Provider disclosure is admitted only when the generated source row has one -unique supported measurement and the event contains that sole Observation. -Grouped rows such as Oura `daily_activity` have no unambiguous primary and must -omit it. The system cannot be a Grove graph-role, event, entry, or opaque identity -namespace, and `Identifier.type` cannot use the Grove identifier-role code -system. Omission remains conformant. The output role and discriminator are -selected by the generated catalog—callers cannot provide arbitrary text. -Scalar outputs use their exact catalog output role with discriminator `single`; -native provider recordings use role `native-recording` with discriminator -`single`. The positive decimal -event sequence changes the event and its Device snapshots without changing -logical source or output identity. The builder never generates keys, times, -event sequences, or repository ids. - -An optional `writerRecord` always carries its separately derived -`writer-record` Identifier. Its canonical non-negative `version` remains -optional because some sources expose a stable writer record without a revision; -when absent, Grove emits no writer-version extension and makes no supersession -ordering claim. A writer-version extension is therefore never emitted without -the identity it qualifies. - -`createEntryIdentity(identifier, id?)` remains available for deriving the -normative UUID-v5 full URL for caller-owned Device or Specimen business -identifiers. The high-level builder performs that derivation automatically. - -Provider source tokens are lineage and identity inputs; they are never appended -to `Observation.code` as if they were clinically equivalent concepts. A future -adapter that admits sleep-stage intervals may retain a catalog-reviewed, -more-specific source state as the second -`Observation.valueCodeableConcept.coding`; the required shared Grove coding -remains first. - -Study context uses branded `ResearchStudyReference` values. The parser accepts -only `ResearchStudy/{FHIR-id}` or an absolute HTTP(S) resource URL ending in -`/ResearchStudy/{FHIR-id}`. Queries, fragments, history URLs, malformed paths, -and duplicate literal references fail closed. - -The synchronized normative catalog and its resolved development reference live -under `.grove-fhir`. `catalog/measurement-capabilities.json` distinguishes the -constructible Provider scalar/coded measurements and native-recording sources -from shared profiles not admitted by this facade, adapter-profiled outputs, -platform-exclusive measurements, and reviewed future or out-of-scope candidates. - -## Retractions - -A source removal is a new append-only Provenance event, never a mutation of the -previous clinical resource: +#### Governed source identifiers -```typescript -import { - buildProviderRetractionBundle, - parseProviderRetractionInput, -} from '@schmiedmayerlab/grove-fhir/providers' -``` +`sourceNativeId` is HMAC input only by default, for measurements and recordings alike. +An application that deliberately needs round-trip traceability opts in through the conversion options, as the [disclosure fragment](#disclosure-policies) shows. + +The policy defaults to `{ kind: 'omit' }` on every platform. +When authorized, the absolute `system` must name the governed repository/account/store key space and the builder adds the source-native id only to the designated primary output: the sole Observation of a source row that designates exactly one, or the sole recording `DocumentReference`. +It never copies it to the companion artifact, Devices, Provenance, entry keys, titles, URLs, or payload metadata. +Grouped rows such as Oura `daily_activity` have no unambiguous primary and must omit it. +The system cannot be a Grove graph-role, event, entry, or opaque identity namespace, and `Identifier.type` cannot use the Grove identifier-role code system. +Omission remains conformant. -`buildProviderRetractionBundle` accepts only logical targets the Provider facade can itself emit: primary Observations, native-recording documents, and Device snapshots. -Each target has a closed role and a typed Identifier; it never carries a literal -reference or a copied prior resource. Primary and source-artifact targets use -the exact prior Provider `provider-output` identity (carried with the FHIR -`source-output` role). A recording document additionally retains its distinct -`provider-artifact` identity with the `source-artifact` role, while the selected -entry and retraction key remain the Provider output identity. Device retirement -targets use the prior event's `device-snapshot` identity. The resulting Bundle -contains one retraction Provenance plus its immutable assembler application and -optional host snapshots. It never emits `entered-in-error`, a FHIR DELETE, or a -receiver retention instruction. - -A target may carry the adapter's own record key alongside its Grove identity through the optional `nativeIdentifier` field. -It is emitted verbatim as a target-carried native record Identifier, must use an absolute system, and never carries a Grove identifier-role coding. - -Receivers resolve complete Identifier pairs and apply their own idempotent, -atomic lifecycle policy. `parseProviderRetractionInput(unknown)` is the strict -JavaScript boundary and rejects unknown fields, unsupported target/type pairs, -malformed key material, and a device target that names the retraction event -itself. - -## Questionnaires +#### Devices and writers + +The converter application and its host always appear as separate Device snapshots linked through `Device.parent`, and the writer appears as its own application snapshot. +A single snapshot can fill multiple Provenance or gateway roles and is emitted only once; two different devices supplying the same source device token are an identity collision and refuse the conversion. + +Recording-device identity requires a governed stable per-unit token on the record's `recordingDevice` and emits both a stable `recording-device` identity, derived with the subject, and an event-scoped `device-snapshot` identity; without that evidence the record names no device. +Serial numbers are not accepted by the producer API. + +An optional `writerRecord` always carries its separately derived `writer-record` Identifier. +Its canonical non-negative `version` remains optional because some sources expose a stable writer record without a revision; when absent, Grove emits no writer-version extension and makes no supersession ordering claim. + +`createEntryIdentity(identifier, id?)` remains available for deriving the normative UUID-v5 full URL for caller-owned Device or Specimen business identifiers. +The builders perform that derivation automatically. + +Provider source tokens are lineage and identity inputs; they are never appended to `Observation.code` as if they were clinically equivalent concepts. + +The synchronized normative catalog and its resolved development reference live under `.grove-fhir`. +`catalog/measurement-capabilities.json` distinguishes the constructible Provider scalar/coded measurements and native-recording sources from shared profiles not admitted by this facade, adapter-profiled outputs, platform-exclusive measurements, and reviewed future or out-of-scope candidates. + +### Diagnostics + +Every issue code is closed over the exchange protocol's producer-diagnostic registry plus the package's own schema codes; `groveProducerDiagnostics` publishes each registered code with its normative `reason`, its `severity`, and the side that emits it. +A refused source record carries exactly one `mobile-input.*` code per countable reason: an unsupported source type, an unsupported source value, a value outside its catalog domain, a malformed value shape, missing required metadata, an invalid effective period, text with an unpaired surrogate, an invalid native identifier, an oversized recording payload, or an empty recording series. +`Issue.reason` is the registry text and `Issue.message` the typed detail of that particular refusal. +A graph rule reported by `parseExchangeGraph` or `parseRetractionEvent` additionally carries the stable FHIR-facing `Issue.location` the shared corpus pins; a rule about a refused or trimmed record has no location, because no FHIR element exists yet. +Deployment faults, such as an invalid identity scope, an event another producer minted, or a disclosure policy naming a Grove system, use the package's undotted schema codes, because no record can cause them. + +`Result.warnings` and `ProviderConversion.warnings` carry the non-blocking findings of an accepted record under the registry's `mobile-omission.*` rows, each of severity `warning`; the graph stays valid, and an omission a disclosure policy chose is never reported. +The provider facade currently emits none, because its normalized input cannot describe a device without a token, an instant without an offset, or unmodeled metadata. + +### Retractions + +A source removal is a new append-only Provenance event, never a mutation of the previous clinical resource; the [retraction fragment](#retraction-events) shows the call. + +`retractionTargets(graph)` derives every node of an accepted graph a retraction must name, typed by the catalog's closed target roles: `primary-output`, `child-output`, `source-artifact`, `specimen`, and `device-snapshot`, each with its admitted resource type and the exact opaque identity previously emitted. +An Observation another Observation lists in `hasMember` is a child output; every other output is primary. +A governed source identifier disclosed on the primary output travels along as the target's `nativeIdentifier`, and an application may also supply the adapter's own record key there itself. +It is emitted verbatim as a target-carried native record Identifier, must use an absolute system outside every Grove namespace, and never carries a Grove identifier-role coding. + +`buildProviderRetractionEvent(targets, context, sourceRecord, retractedAt)` accepts only targets and a source record the context's own scope minted. +Each target has a closed role and a typed Identifier; it never carries a literal reference or a copied prior resource. +`retractedAt` is the time the producer learned the source no longer exposes the record and becomes Provenance.occurred[x]; the conversion instant becomes its recorded time and the Bundle timestamp. +The resulting event contains one retraction Provenance plus its immutable assembler application and host snapshots. +It never emits `entered-in-error`, a FHIR DELETE, or a receiver retention instruction. +Receivers resolve complete Identifier pairs and apply their own idempotent, atomic lifecycle policy. + +### Replay and equality + +An exact retry reuses the event identifier, all three times, entry keys, and payload; a receiver decides whether a replay is that exact retry over lossless JSON tokens, never bytes. +`isSemanticallyEqual(left, right)` implements that comparison on serialized graphs, as text or UTF-8 bytes, without any dependency: member order, whitespace, and string escaping are irrelevant, decimal lexemes are compared as text, so `72.0` is different content from `72`, and malformed JSON is an issue rather than `false`. +The receiver-lifecycle corpus pins it: `reformatted-retry` equals `original` and `lexeme-retry` does not. + +The [retry fragment](#retries) shows the comparison; parse a stored graph again with `parseExchangeGraph` before you act on it. + +### No reverse projection + +Connected providers are read-only sources with no native store to write back to, so this package has no reverse projection from FHIR to a provider record and no durable export or outbox. +The `extract/*` accessors on the root entry point are the read-back API for FHIR values, and the application owns storage, transport, retry, and receiver policy. + +### Questionnaires The `questionnaire` entry point builds strict, versioned R4 `Questionnaire`/`QuestionnaireResponse` resources and preflights a pair before @@ -484,7 +679,7 @@ One Grove Questionnaire element is deliberately deferred. `grove-questionnaire-writer-context` is `0..1 MS` on the Grove QuestionnaireResponse profile and states the capturing application and host, but the Grove FHIR catalogs do not publish it, so this package cannot project its canonical or its sub-extension contract without hand-copying the implementation guide. Until the catalog carries it, a writer context supplied through the builder's `extensions` pass-through reaches consumers with only generic FHIR extension shape checking, and neither the builder nor the parser validates its sub-extensions. -## Entry points +### Entry points The root entry point contains the source-neutral aggregate: validated primitives, the bounded R4 closure, the shared Mobile contract, and @@ -498,27 +693,30 @@ import { } from '@schmiedmayerlab/grove-fhir' ``` -Use the Mobile entry point for source-neutral shared semantics, deterministic -entry identity, and effective-time canonicalization: +Use the Mobile entry point for source-neutral shared semantics, deployment identity, the exchange event context, and effective-time canonicalization; it never imports the provider catalog, so an identity-only consumer does not ship it: ```typescript import { canonicalizeMobileEffectiveInstant, + deriveOpaqueIdentitySystems, groveExchangeProtocol, - groveMobilePackageMetadata, + groveProducerDiagnostics, + parseExchangeEventContext, sharedMobileMeasurementCatalog, + validateOpaqueIdentityScope, + type ExchangeEventContext, type MobileMeasurement, } from '@schmiedmayerlab/grove-fhir/mobile' ``` -Use the Provider entry point for the closed Google Health API, Oura, -and Withings mapping and graph builders: +Use the Provider entry point for the closed Google Health API, Oura, and Withings mapping and graph builders: ```typescript import { - buildProviderMeasurementBundle, - buildProviderRecordingBundle, - buildProviderRetractionBundle, + buildProviderExchangeGraph, + buildProviderExchangeGraphs, + buildProviderRecordingGraph, + buildProviderRetractionEvent, groveProviderPackageMetadata, } from '@schmiedmayerlab/grove-fhir/providers' ``` @@ -539,46 +737,38 @@ Use the R4 entry point for the bounded resource schemas and types: ```typescript import { - parseGroveMobileExchangeBundle, - parseGroveMobileRetractionBundle, + parseExchangeGraph, + parseRetractionEvent, parseR4CollectionBundle, - type GroveMobileExchangeBundle, + type ExchangeGraph, + type RetractionEvent, } from '@schmiedmayerlab/grove-fhir/r4' ``` -`parseR4CollectionBundle` checks a bounded base-R4 collection Bundle and makes -no Grove profile claim. `parseGroveMobileExchangeBundle` and -`parseGroveMobileRetractionBundle` additionally enforce event identity, -entry-key selection and digest, deterministic UUIDv5 full URLs, reference -closure, output identities, exact Provenance coverage, event times, and the -active-versus-retraction graph shape. Active events admit only the catalog's -closed output/support/lifecycle resource set, prohibit contained resources, -require exact direct profile modes for outputs, Device, -QuestionnaireResponse, and Provenance, and reject disconnected supporting -nodes. Health Connect `DataOrigin.packageName` remains an identifier-only -logical Device agent on Provenance; it is never synthesized as an unprofiled -Bundle Device. The deliberately explicit parser names prevent callers from -mistaking base-R4 parsing for Grove profile validation. - -For a shared producer-rule failure, `Issue.code` is the exact normative rule -identifier and `Issue.reason` plus `Issue.location` reproduce the stable -corpus diagnostic. `Issue.path` remains the parser-facing array path useful for -programmatic input handling; non-protocol schema failures continue to use -`schema-invalid` without inventing a Grove rule. +`parseR4CollectionBundle` checks a bounded base-R4 collection Bundle and makes no Grove profile claim. +`parseExchangeGraph` and `parseRetractionEvent` additionally enforce event identity, entry-key selection and digest, deterministic UUIDv5 full URLs, reference closure, output identities, exact Provenance coverage, event times, the study-context entry roles, and the active-versus-retraction graph shape. +Active events admit only the catalog's closed output/support/lifecycle resource set, prohibit contained resources, require exact direct profile modes for outputs, Device, QuestionnaireResponse, and Provenance, and reject disconnected supporting nodes. +Health Connect `DataOrigin.packageName` remains an identifier-only logical Device agent on Provenance; it is never synthesized as an unprofiled Bundle Device. +The deliberately explicit parser names prevent callers from mistaking base-R4 parsing for Grove profile validation. + +For a producer-rule failure, `Issue.code` is the exact registry identifier and `Issue.reason` plus `Issue.location` reproduce the stable corpus diagnostic; a graph rule is reported even when the base R4 schema would already reject the Bundle. +`Issue.path` remains the parser-facing array path useful for programmatic input handling; non-protocol schema failures continue to use `schema-invalid` without inventing a Grove rule. Parsing never returns a partially accepted resource: ```typescript -const result = parseObservation(input) +import { parseObservation } from '@schmiedmayerlab/grove-fhir' -if (!result.ok) { - for (const issue of result.issues) { +const parsed = parseObservation(input) + +if (!parsed.ok) { + for (const issue of parsed.issues) { console.error(issue.path, issue.code, issue.message) } } ``` -### Reading values back out +#### Reading values back out FHIR states time as a string and a measurement as a `Quantity`, neither of which a consumer can compute with directly. Two accessors convert them, and both return a `Result` so a malformed value is reported rather than silently becoming `Invalid Date` or `NaN`: @@ -596,7 +786,7 @@ const measured = fhirQuantityToValue(observation.valueQuantity) `fhirDateTimeToDate` accepts a `date`, `dateTime`, or `instant`; a value stated to lower precision resolves to the start of the period it names, in UTC, because a FHIR `date` carries no offset to resolve it against. `fhirQuantityToValue` returns the number with the unit's code where the Quantity states one — only the code is comparable, `unit` alone being a label the source chose — along with the code's system and any comparator, since dropping a comparator would report a bound as a measurement. -### Extracting values +#### Extracting values FHIR states a name across five fields, a code across three, and a period as two strings. Accessors read each as the JavaScript value a consumer works with, and take `unknown` so they can be @@ -607,8 +797,6 @@ import { codeableConceptDisplay, observationEffectiveDate, observationNumericValue, - patientAgeInYears, - periodIsActive, } from '@schmiedmayerlab/grove-fhir' const when = observationEffectiveDate(observation) @@ -626,7 +814,7 @@ Dates are computed in UTC throughout. A FHIR `date` names a calendar day with no offset, so comparing it against the host's local calendar moves the day for anyone west of UTC — which is a wrong age for one day each year. -### Validating another release +#### Validating another release The root entry point validates R4, which is the release every Grove guide targets. Consumers working in R4B import the same shapes from the R4B entry point; both are produced by one generator from their own release's definitions, so neither is a hand-maintained copy of the other. @@ -639,7 +827,7 @@ import { observationSchema as r4bObservationSchema } from '@schmiedmayerlab/grov Use these when you need a schema the root entry point does not narrow, or when you are validating a release other than R4. Prefer `parseObservation` and its siblings for R4: they return a `Result` with typed issues instead of throwing. -## R4 boundary +### R4 boundary The runtime schemas are generated from a FHIR release's own `StructureDefinition` resources, not written by hand. Cardinality, choice types, offline-enumerable required bindings, and primitive @@ -658,13 +846,10 @@ Required singular and repeated primitives may likewise be represented by their Primitive-only choice extensions count as the corresponding `value[x]` alternative, and empty emitted string primitives are rejected. -These schemas validate already-materialized JavaScript values. JavaScript -`number` cannot retain a FHIR decimal's original lexical scale or arbitrary -precision after ordinary `JSON.parse`, so this package does not promise -lossless decimal wire round-tripping. Preserve/tokenize wire JSON with a -lossless decimal representation when lexical precision matters, and use the -official validator at that boundary. Grove builders emit only finite numeric -values inside JavaScript's supported producer scope. +These schemas validate already-materialized JavaScript values. +JavaScript `number` cannot retain a FHIR decimal's original lexical scale or arbitrary precision after ordinary `JSON.parse`, so this package does not promise lossless decimal wire round-tripping through its parsers. +Where lexical precision decides an outcome, compare the serialized text with `isSemanticallyEqual`, which tokenizes it losslessly, and use the official validator at that boundary. +Grove builders emit only finite numeric values inside JavaScript's supported producer scope. A slot that may hold _any_ resource — `contained`, `Bundle.entry.resource`, and `Bundle.entry.response.outcome` — discriminates on `resourceType` and validates the resource against the schema for that type. A malformed Observation nested in a bundle entry is rejected exactly where a standalone one would be, and the reported path names the element at fault rather than the entry that carries it. @@ -683,7 +868,7 @@ Passing a runtime schema is therefore an application preflight, not a declaratio Normative conformance is established by validating generated fixtures with the official HL7 FHIR Validator and the pinned Grove implementation-guide packages. Synchronized IG catalogs and the Mobile semantic corpus retain the exact upstream bytes; local generated TypeScript is derived from those immutable inputs. -### Regenerating +#### Regenerating ```sh npm run generate:zod # rewrite the generated schemas for both releases @@ -692,12 +877,14 @@ npm run generate:catalog # reproject the Grove contracts a pin bump changed npm run check:catalog # fail if the projected contracts are stale npm run generate:fixtures # rebuild the conformance corpus from the public API npm run check:fixtures # fail if any emitted resource has drifted +npm run generate:surface # snapshot the declared public surface from the built .d.ts files +npm run check:surface # fail if a public type or signature changed without review ``` -`check:zod` and `check:catalog` run as part of `typecheck`, so a stale schema or -catalog fails the build the same way a stale fixture does. +`check:zod` and `check:catalog` run as part of `typecheck`, and `check:surface` as part of `test:package`, so a stale schema, catalog, or surface fails the build the same way a stale fixture does. +The runtime export snapshot in `test/export-surface.json` and the declared surface snapshot in `test/type-surface.json` sit side by side; both are reviewed diffs, never regenerated silently. -## Development conformance +### Development conformance `npm run check:fixtures` regenerates the committed conformance corpus from the public API and fails if any resource has drifted. With a local Grove FHIR @@ -707,15 +894,13 @@ checkout, the fast fail-closed checks run as follows: npm run conformance:structural -- --ig /path/to/grove-fhir ``` -`npm run conformance` additionally requires the checksum-pinned official FHIR -Validator and the Mobile, Sensor, Provider, Questionnaire, Google Health, -Oura, and Withings packages built in that checkout. It validates the generated Mobile measurement -graphs, every admitted native-recording graph, the shared active/retraction -exchange corpus, and the Questionnaire/Response pair. Every admitted scalar fixture is generated from -the IG's versioned Mobile semantic corpus and bound through an exact RFC 6901 -resource pointer, so the generic producer kit compares the emitted profile, -clinical code, effective time, and result projection—not only the resource -shape. +`npm run conformance` additionally requires the checksum-pinned official FHIR Validator and the Mobile, Sensor, Provider, Questionnaire, Google Health, Oura, and Withings packages built in that checkout. +It validates the generated Mobile measurement graphs, the study-context graph, every admitted native-recording graph, both retraction events, the shared active/retraction exchange corpus, and the Questionnaire/Response pair. +Every admitted scalar fixture is generated from the IG's versioned Mobile semantic corpus and bound through an exact RFC 6901 resource pointer, so the generic producer kit compares the emitted profile, clinical code, effective time, and result projection, not only the resource shape. +The unit tests bind the same contract from the committed fixtures: the protocol vectors and the derived identifier systems, every negative case of the mobile-exchange corpus with its exact code and location, every equality decision of the receiver-lifecycle sequences, the study-attribution events, and the registry's severities. + +While a contract change is still unmerged upstream, `GROVE_FHIR_CHECKOUT=/path/to/grove-fhir npm run check:catalog` (or `node scripts/fetch-grove-fhir.mjs --local /path/to/grove-fhir`) vendors the consumed paths from that working tree instead of the pin; the validator then reports that the pin is not verified. +The pin moves to the final commit before the change ships. These committed demonstration fixtures deliberately use `example.org` deployment systems. The official-validator runner enables the root validator's diff --git a/packages/fhir/fixtures/conformance/manifest.json b/packages/fhir/fixtures/conformance/manifest.json index 02ac6044..c3655aa0 100644 --- a/packages/fhir/fixtures/conformance/manifest.json +++ b/packages/fhir/fixtures/conformance/manifest.json @@ -364,6 +364,12 @@ "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle" ] }, + { + "path": "resources/provider-study-context.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, { "path": "mobile-exchange/exchange-bundle.json", "requiredProfiles": [ diff --git a/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json b/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json index de0cd149..e0f8106b 100644 --- a/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json +++ b/packages/fhir/fixtures/conformance/mobile-exchange/corpus.json @@ -2,14 +2,25 @@ "schemaVersion": 0, "version": "0.6.0", "bases": [ - { "id": "mobile-exchange", "path": "exchange-bundle.json" }, - { "id": "mobile-retraction", "path": "retraction-bundle.json" } + { + "id": "mobile-exchange", + "path": "exchange-bundle.json" + }, + { + "id": "mobile-retraction", + "path": "retraction-bundle.json" + } ], "cases": [ { "id": "missing-entry-node-key", "base": "mobile-exchange", - "patch": [{ "op": "remove", "path": "/entry/0/extension" }], + "patch": [ + { + "op": "remove", + "path": "/entry/0/extension" + } + ], "expectedRule": { "code": "mobile-exchange.entry-node-key", "reason": "Every Bundle entry must carry exactly one complete Grove exchange entry node key.", @@ -122,7 +133,12 @@ { "id": "missing-source-output-identity", "base": "mobile-exchange", - "patch": [{ "op": "remove", "path": "/entry/2/resource/identifier/1" }], + "patch": [ + { + "op": "remove", + "path": "/entry/2/resource/identifier/1" + } + ], "expectedRule": { "code": "mobile-output.source-output-required", "reason": "Every active clinical output must carry its exact typed source-output identity in addition to source-record identity.", @@ -150,7 +166,12 @@ { "id": "missing-transform-provenance", "base": "mobile-exchange", - "patch": [{ "op": "remove", "path": "/entry/3" }], + "patch": [ + { + "op": "remove", + "path": "/entry/3" + } + ], "expectedRule": { "code": "mobile-exchange.transform-provenance", "reason": "An active event must contain exactly one transform Provenance and no retraction Provenance.", @@ -269,7 +290,12 @@ { "id": "unprofiled-active-observation", "base": "mobile-exchange", - "patch": [{ "op": "remove", "path": "/entry/2/resource/meta/profile" }], + "patch": [ + { + "op": "remove", + "path": "/entry/2/resource/meta/profile" + } + ], "expectedRule": { "code": "mobile-output.semantic-profile", "reason": "Every active Observation must directly claim one admitted Grove semantic profile shape; an empty claim cannot bypass semantic validation.", @@ -461,7 +487,12 @@ { "op": "add", "path": "/entry/2/resource/contained", - "value": [{ "resourceType": "Patient", "id": "hidden-patient" }] + "value": [ + { + "resourceType": "Patient", + "id": "hidden-patient" + } + ] } ], "expectedRule": { @@ -549,7 +580,12 @@ { "id": "unprofiled-active-device", "base": "mobile-exchange", - "patch": [{ "op": "remove", "path": "/entry/1/resource/meta/profile" }], + "patch": [ + { + "op": "remove", + "path": "/entry/1/resource/meta/profile" + } + ], "expectedRule": { "code": "mobile-support.device-profile", "reason": "Every active Device must directly claim exactly one admitted Grove Device profile mode.", @@ -560,7 +596,12 @@ { "id": "unprofiled-active-provenance", "base": "mobile-exchange", - "patch": [{ "op": "remove", "path": "/entry/3/resource/meta/profile" }], + "patch": [ + { + "op": "remove", + "path": "/entry/3/resource/meta/profile" + } + ], "expectedRule": { "code": "mobile-exchange.provenance-profile", "reason": "The sole active lifecycle Provenance must directly claim exactly one admitted Mobile or adapter conversion profile.", @@ -696,6 +737,582 @@ "location": "Provenance.target[0].extension.valueIdentifier.type", "severity": "error" } + }, + { + "id": "both-exchange-profiles-claimed", + "base": "mobile-exchange", + "patch": [ + { + "op": "add", + "path": "/meta/profile/-", + "value": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle" + } + ], + "expectedRule": { + "code": "mobile-exchange.bundle-profile", + "reason": "An exchange Bundle directly claims exactly the Grove Mobile exchange or retraction Bundle profile for its event kind.", + "location": "Bundle.meta.profile", + "severity": "error" + } + }, + { + "id": "empty-entry-list", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry", + "value": [] + } + ], + "expectedRule": { + "code": "mobile-exchange.entry-required", + "reason": "An exchange event carries at least one entry; an empty Bundle is not an event.", + "location": "Bundle.entry", + "severity": "error" + } + }, + { + "id": "repeated-entry-key", + "base": "mobile-exchange", + "patch": [ + { + "op": "add", + "path": "/entry/-", + "value": { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:Q2sME_dGFj94xyprI1HtMokER94GYsHINZi0ilnsnRY" + } + } + ], + "fullUrl": "urn:uuid:aef62dba-db7e-5a99-a854-3b5ec312312f", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:Q2sME_dGFj94xyprI1HtMokER94GYsHINZi0ilnsnRY" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + } + } + ], + "expectedRule": { + "code": "mobile-exchange.distinct-entry-key", + "reason": "No two entries of one event carry the same entry key system and value pair.", + "location": "Bundle.entry[5].extension.valueIdentifier", + "severity": "error" + } + }, + { + "id": "lower-priority-entry-key", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/2/extension/0/valueIdentifier", + "value": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + ], + "expectedRule": { + "code": "mobile-exchange.entry-key-selection", + "reason": "An entry key is the entry resource's highest-priority typed business identifier, or its entry-node key when the resource carries none.", + "location": "Bundle.entry[2].extension.valueIdentifier", + "severity": "error" + } + }, + { + "id": "non-canonical-entry-node-key", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/0/extension/0/valueIdentifier/value", + "value": "patient-node-0" + } + ], + "expectedRule": { + "code": "mobile-exchange.entry-key-selection", + "reason": "An entry key is the entry resource's highest-priority typed business identifier, or its entry-node key when the resource carries none.", + "location": "Bundle.entry[0].extension.valueIdentifier", + "severity": "error" + } + }, + { + "id": "event-identifier-with-two-roles", + "base": "mobile-exchange", + "patch": [ + { + "op": "add", + "path": "/identifier/type/coding/-", + "value": { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + } + ], + "expectedRule": { + "code": "mobile-exchange.identifier-role", + "reason": "A Grove-typed resource Identifier carries exactly one closed Grove identifier-role coding.", + "location": "Bundle.identifier", + "severity": "error" + } + }, + { + "id": "repeated-identifier-role-coding", + "base": "mobile-exchange", + "patch": [ + { + "op": "add", + "path": "/entry/2/resource/identifier/0/type/coding/-", + "value": { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + } + ], + "expectedRule": { + "code": "mobile-exchange.identifier-role", + "reason": "A Grove-typed resource Identifier carries exactly one closed Grove identifier-role coding.", + "location": "Observation.identifier[0]", + "severity": "error" + } + }, + { + "id": "clear-source-record-identity", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/2/resource/identifier/0/value", + "value": "record-heart-001" + } + ], + "expectedRule": { + "code": "mobile-exchange.opaque-resource-identity", + "reason": "A resource Identifier typed with an opaque Grove identifier role is one complete pair whose system is absolute and whose value is a canonical v0 HMAC identity.", + "location": "Observation.identifier[0]", + "severity": "error" + } + }, + { + "id": "repeated-source-record-identifier", + "base": "mobile-exchange", + "patch": [ + { + "op": "add", + "path": "/entry/2/resource/identifier/-", + "value": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + ], + "expectedRule": { + "code": "mobile-exchange.distinct-resource-identity-role", + "reason": "A resource carries at most one Identifier for each Grove identifier role.", + "location": "Observation.identifier[2]", + "severity": "error" + } + }, + { + "id": "active-event-without-output", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/2/resource/resourceType", + "value": "ResearchStudy" + } + ], + "expectedRule": { + "code": "mobile-exchange.output-required", + "reason": "An active event carries at least one clinical or source-artifact output.", + "location": "Bundle.entry", + "severity": "error" + } + }, + { + "id": "transform-provenance-without-target", + "base": "mobile-exchange", + "patch": [ + { + "op": "remove", + "path": "/entry/3/resource/target/0" + } + ], + "expectedRule": { + "code": "mobile-exchange.provenance-targets", + "reason": "A conversion Provenance targets every active clinical output of its event and nothing else.", + "location": "Provenance.target", + "severity": "error" + } + }, + { + "id": "adapter-provenance-without-adapter-output", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/3/resource/meta/profile/0", + "value": "https://grovealliance.org/fhir/sensorkit/StructureDefinition/sensorkit-conversion-provenance" + } + ], + "expectedRule": { + "code": "mobile-exchange.adapter-provenance-graph", + "reason": "Every adapter output of an event claims the adapter profile its conversion Provenance governs, and a source-neutral event claims no adapter output.", + "location": "Provenance.entity", + "severity": "error" + } + }, + { + "id": "source-marker-on-source-neutral-output", + "base": "mobile-exchange", + "patch": [ + { + "op": "add", + "path": "/entry/2/resource/extension/-", + "value": { + "url": "https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-source-type", + "valueCode": "HKQuantityTypeIdentifierHeartRate" + } + } + ], + "expectedRule": { + "code": "mobile-output.adapter-source-marker", + "reason": "An adapter's source marker appears exactly once on each catalog-owned output of that adapter and never on a source-neutral or different-adapter output.", + "location": "Observation.extension", + "severity": "error" + } + }, + { + "id": "health-connect-provenance-without-data-origin", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/3/resource/meta/profile/0", + "value": "https://grovealliance.org/fhir/health-connect/StructureDefinition/health-connect-conversion-provenance" + } + ], + "expectedRule": { + "code": "health-connect-provenance.data-origin-agent", + "reason": "A Health Connect data origin is one enterer agent whose typed identifier-only logical Device Reference names the Android package in the package-name namespace.", + "location": "Provenance.entity[0].agent", + "severity": "error" + } + }, + { + "id": "tampered-embedded-recording-hash", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/2/resource", + "value": { + "resourceType": "DocumentReference", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + }, + { + "type": { + "coding": [ + { + "code": "source-artifact", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-artifact-v0/test-key/1", + "value": "v0:test-key:1:ZWmx94XtgArrE8XO6W6LQ7QYbkiWCIn-K1JVw8hxwrI" + } + ], + "status": "current", + "subject": { + "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" + }, + "date": "2026-08-20T17:30:02Z", + "context": { + "related": [ + { + "reference": "urn:uuid:e516dd51-04d0-502d-abcf-b5c870915c17" + } + ] + }, + "content": [ + { + "attachment": { + "contentType": "text/csv", + "data": "dGltZXN0YW1wLHZhbHVlLGNvbmZpZGVuY2UsZGV2aWNlCjE3NTU3MDc0MDAuMjUxLDcyLDIsV2F0Y2g3Cg==", + "size": 61, + "hash": "k9bJPZp20n7DRi57V95d8etFvHs=" + }, + "format": { + "system": "https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format", + "code": "heart-rate-samples" + } + } + ] + } + } + ], + "expectedRule": { + "code": "sensor-recording-document.embedded-integrity", + "reason": "An embedded recording attachment's size and SHA-1 hash match its decoded bytes.", + "location": "DocumentReference.content[0].attachment.hash", + "severity": "error" + } + }, + { + "id": "unregistered-recording-format", + "base": "mobile-exchange", + "patch": [ + { + "op": "replace", + "path": "/entry/2/resource", + "value": { + "resourceType": "DocumentReference", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + }, + { + "type": { + "coding": [ + { + "code": "source-artifact", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-artifact-v0/test-key/1", + "value": "v0:test-key:1:ZWmx94XtgArrE8XO6W6LQ7QYbkiWCIn-K1JVw8hxwrI" + } + ], + "status": "current", + "subject": { + "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" + }, + "date": "2026-08-20T17:30:02Z", + "context": { + "related": [ + { + "reference": "urn:uuid:e516dd51-04d0-502d-abcf-b5c870915c17" + } + ] + }, + "content": [ + { + "attachment": { + "contentType": "text/csv", + "data": "dGltZXN0YW1wLHZhbHVlLGNvbmZpZGVuY2UsZGV2aWNlCjE3NTU3MDc0MDAuMjUxLDcyLDIsV2F0Y2g3Cg==", + "size": 61, + "hash": "z3GFc1fkRzogDOGJOm9gm9H0RrE=" + }, + "format": { + "system": "https://grovealliance.org/fhir/sensor/CodeSystem/grove-recording-format", + "code": "heart-rate-waveform" + } + } + ] + } + } + ], + "expectedRule": { + "code": "sensor-recording-document.format", + "reason": "A recording attachment's format coding and content type match one active entry of the Grove recording-format registry, with no Coding.version.", + "location": "DocumentReference.content[0].format.code", + "severity": "error" + } + }, + { + "id": "retraction-with-transform-activity", + "base": "mobile-retraction", + "patch": [ + { + "op": "replace", + "path": "/entry/0/resource/activity/coding/0", + "value": { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle" + } + } + ], + "expectedRule": { + "code": "mobile-retraction.provenance", + "reason": "A retraction event carries exactly one retraction Provenance and no transform Provenance.", + "location": "Bundle.entry", + "severity": "error" + } + }, + { + "id": "retraction-without-target", + "base": "mobile-retraction", + "patch": [ + { + "op": "replace", + "path": "/entry/0/resource/target", + "value": [] + } + ], + "expectedRule": { + "code": "mobile-retraction.target-required", + "reason": "A retraction Provenance names at least one logical target.", + "location": "Provenance.target", + "severity": "error" + } + }, + { + "id": "repeated-retraction-target", + "base": "mobile-retraction", + "patch": [ + { + "op": "add", + "path": "/entry/0/resource/target/-", + "value": { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-role", + "valueCode": "primary-output" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-native-identifier", + "valueIdentifier": { + "system": "https://study.example.org/fhir/NamingSystem/native-record/health-connect/1f5c58aa-6ec6-4e79-a682-829a9debd3f5", + "value": "record-heart-001" + } + } + ], + "type": "Observation", + "identifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source output" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + } + ], + "expectedRule": { + "code": "mobile-retraction.distinct-target", + "reason": "No two retraction targets carry the same Identifier system and value pair.", + "location": "Provenance.target[1].identifier", + "severity": "error" + } } ] } diff --git a/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json b/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json index a7028e75..81468d06 100644 --- a/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json +++ b/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json @@ -95,7 +95,10 @@ } ], "deviceName": [ - { "type": "user-friendly-name", "name": "Mobile Study" } + { + "type": "user-friendly-name", + "name": "Mobile Study" + } ], "status": "active" } @@ -187,10 +190,14 @@ "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" }, "performer": [ - { "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" } + { + "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" + } ], "derivedFrom": [ - { "reference": "urn:uuid:e516dd51-04d0-502d-abcf-b5c870915c17" } + { + "reference": "urn:uuid:e516dd51-04d0-502d-abcf-b5c870915c17" + } ], "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", "issued": "2026-08-20T17:30:02Z", @@ -276,7 +283,9 @@ } ], "target": [ - { "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" } + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } ], "occurredDateTime": "2026-08-20T10:30:02-07:00", "recorded": "2026-08-20T17:30:02Z" diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/events.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/events.json new file mode 100644 index 00000000..732bab21 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/events.json @@ -0,0 +1,318 @@ +{ + "schemaVersion": 0, + "contractLock": { + "baseSourceRevision": "444e06fb25680c3af29e5d3ccbebeb0249be7318", + "baseExchangeProtocolSHA256": "bbc9e980c2c674d5ef6f9900958d29c5602031ead8a3b6325cff508799106bb5", + "releaseVersion": "0.6.0", + "fhirVersion": "4.0.1", + "exchangeProtocolSHA256": "04ad91c9ed49804f89bbf9a86486b001ed67ed5d98d2754fc48c6f4007481b69" + }, + "events": { + "original": { + "path": "resources/original.json", + "sha256": "ff6c49864a4cf56068022ea019de6d6efb424f0c01eb51278578a98a33f47e6d", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42" + } + }, + "corrected": { + "path": "resources/corrected.json", + "sha256": "dc4684173f3173538c61b164fa5654a5c8692d173b7d95f3b4109141f5ca584f", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:41" + } + }, + "altered-retry": { + "path": "resources/altered-retry.json", + "sha256": "9a57d2b9697b6250966de2e6e134fe3f5855bfc8a4830386ac2ff698654cebd7", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42" + } + }, + "reformatted-retry": { + "path": "resources/reformatted-retry.json", + "sha256": "093a6dfd0cebc17b78073f1a64b4aae349fa37724903cb4e5b0c74da77368c9c", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42" + } + }, + "lexeme-retry": { + "path": "resources/lexeme-retry.json", + "sha256": "7518697d093c4646c0a2b48aac74306f5c9ed195bb85617130379f1334c1803d", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42" + } + }, + "unordered-a": { + "path": "resources/unordered-a.json", + "sha256": "77edbb69c72b65cc9e0f4fec4f09e482cc6cd961f507c6f352da6ee13fdbd420", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:44" + } + }, + "unordered-b": { + "path": "resources/unordered-b.json", + "sha256": "f0d1de544dae87d08909d0738bdb6acc979c8db023c5409e86f3d042a572eafc", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:45" + } + }, + "pending": { + "path": "resources/pending.json", + "sha256": "7ae1c22cbfd01d55ab84ce8aeae07500899f717a5c0753983ef01dea53db6bda", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:46" + } + }, + "target": { + "path": "resources/target.json", + "sha256": "d47ca9fe090282e1c84924f29088db4ffaa8ca3813f56a936c1bf02e96a8b5e2", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:47" + } + }, + "retraction": { + "path": "resources/retraction.json", + "sha256": "54085f51d0d7629bc5a556cb9ea2813b7c7d6defadb8f2d3340270bc88f5d226", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:43" + } + }, + "multi-v1": { + "path": "resources/multi-v1.json", + "sha256": "6460bf578d4239d06efbbbe6aefe2ef1fa4d4e8a343a9d1f704867e504fd7524", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:62" + } + }, + "multi-v2": { + "path": "resources/multi-v2.json", + "sha256": "93f39fce303e95ab51253822b2192b67614a834977c7ad3b627c8f4245f7992e", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:61" + } + }, + "multi-v3": { + "path": "resources/multi-v3.json", + "sha256": "0655373c22f80324ae0406cb26bc791f53d3e183102976a8b9652cd347ba1035", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:60" + } + }, + "equal-version-conflict": { + "path": "resources/equal-version-conflict.json", + "sha256": "18432910374b9739e05eb3119d911625e3c59a56b8a8b9ac8319d805342078b3", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:63" + } + }, + "equal-version-reassembly": { + "path": "resources/equal-version-reassembly.json", + "sha256": "8d430701506d6e790f648c023b676a8c6dbaa1f65cd88e0819c13d76cf76e5c7", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:64" + } + }, + "other-writer": { + "path": "resources/other-writer.json", + "sha256": "ddcd18be3734b9d7b40620f985fca2f88135ca93f14c2fe2f2d17d244e44ebf1", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:65" + } + }, + "missing-order": { + "path": "resources/missing-order.json", + "sha256": "f1b91c21429b9c0a900c35b92b0f6e05e10390ce735fe8d31676372b4d9e03f6", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:66" + } + }, + "changed-source-record": { + "path": "resources/changed-source-record.json", + "sha256": "a67904123787cc274e68b6fc6b23a927e0260e7deb4b4ade232be3b508c6c003", + "eventIdentity": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:67" + } + } + } +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/events.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/events.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/events.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/altered-retry.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/altered-retry.json new file mode 100644 index 00000000..78a6c8d5 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/altered-retry.json @@ -0,0 +1,389 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:BMLA8cDb0x8fjNhv70m6l8dDSHIk2zWfOScLokevBQQ" + } + } + ], + "fullUrl": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:FhFF4pknUiHfdg3v3p6KQYzE8ZI_U3YKj-IdsYcRpLU" + } + } + ], + "fullUrl": "urn:uuid:088c04b2-2b63-5a60-8f61-467f795cf5f0", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:FhFF4pknUiHfdg3v3p6KQYzE8ZI_U3YKj-IdsYcRpLU" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "writer-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1", + "value": "v0:test-key:1:TN39cF6jcCICy4SlWBjpAulk4eMlcb-KM3MwESD9cvw" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:088c04b2-2b63-5a60-8f61-467f795cf5f0" + } + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version", + "valueString": "9007199254740992" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" + }, + "performer": [ + { + "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:e516dd51-04d0-502d-abcf-b5c870915c17" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "note": [ + { + "text": "Different content under an already accepted event identity." + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:8JmcQF7rmULm9uJBkHWruJwfMu3GJTxGWqXWn2DGqWk" + } + } + ], + "fullUrl": "urn:uuid:71abc484-b9ee-511e-b22a-5b35d026d620", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:088c04b2-2b63-5a60-8f61-467f795cf5f0" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:kkYuCTNdLr58lk-fG2HJJQyMfv6quSSBFKSNI331b3k" + } + } + ], + "fullUrl": "urn:uuid:e516dd51-04d0-502d-abcf-b5c870915c17", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/altered-retry.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/altered-retry.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/altered-retry.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/changed-source-record.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/changed-source-record.json new file mode 100644 index 00000000..9dcf66f7 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/changed-source-record.json @@ -0,0 +1,383 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:67" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:ikzPKaukoIJ-2hW08F5ucIZuAAqSZp3jBIsPsPDsDKw" + } + } + ], + "fullUrl": "urn:uuid:263fde3b-c42a-583a-939b-4be8902f24d5", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:2f3FbY5IkXSZQs9xgEChYLIWANCcdFhDmauCsjHgA7A" + } + } + ], + "fullUrl": "urn:uuid:e74f8749-7016-514a-b496-270fcc99a8ae", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:2f3FbY5IkXSZQs9xgEChYLIWANCcdFhDmauCsjHgA7A" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:WssrtI7oYzcgG0mFxlitvSznCx4esKSDEbK-bOUQpTk" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "writer-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1", + "value": "v0:test-key:1:TN39cF6jcCICy4SlWBjpAulk4eMlcb-KM3MwESD9cvw" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:e74f8749-7016-514a-b496-270fcc99a8ae" + } + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version", + "valueString": "9007199254740994" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:263fde3b-c42a-583a-939b-4be8902f24d5" + }, + "performer": [ + { + "reference": "urn:uuid:263fde3b-c42a-583a-939b-4be8902f24d5" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:56f9d09e-27dd-5f75-a54d-bd6128728dd3" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:i9ZBc6c8VwZ4AvUmql7O1tRcj8owgFlvDJQDkJi4Mhs" + } + } + ], + "fullUrl": "urn:uuid:be22805a-907c-5e13-879b-00c15f0bf6c1", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:e74f8749-7016-514a-b496-270fcc99a8ae" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:WssrtI7oYzcgG0mFxlitvSznCx4esKSDEbK-bOUQpTk" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:NvPcHRCZiCGjhfePDKSPi4_lO2rpWGdU0JnJam5izrs" + } + } + ], + "fullUrl": "urn:uuid:56f9d09e-27dd-5f75-a54d-bd6128728dd3", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/changed-source-record.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/changed-source-record.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/changed-source-record.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/corrected.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/corrected.json new file mode 100644 index 00000000..5e00a8d9 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/corrected.json @@ -0,0 +1,389 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:41" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:0kbWzGHyKqxwf1KSbi--Rr0NrGTbZRWSHVm1czKkpeA" + } + } + ], + "fullUrl": "urn:uuid:f9ee2eb6-66a5-5bb5-9459-0ba0d68e823a", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:s7LpE9vScdo_lguSPQ-KetFYzpcgwAdbUFlTLS8ECPY" + } + } + ], + "fullUrl": "urn:uuid:3cd67024-eab5-5604-900b-e287501b5b60", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:s7LpE9vScdo_lguSPQ-KetFYzpcgwAdbUFlTLS8ECPY" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "writer-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1", + "value": "v0:test-key:1:TN39cF6jcCICy4SlWBjpAulk4eMlcb-KM3MwESD9cvw" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:3cd67024-eab5-5604-900b-e287501b5b60" + } + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version", + "valueString": "9007199254740993" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:f9ee2eb6-66a5-5bb5-9459-0ba0d68e823a" + }, + "performer": [ + { + "reference": "urn:uuid:f9ee2eb6-66a5-5bb5-9459-0ba0d68e823a" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:cf931bb7-b891-515b-8ca9-5645f6beb6e3" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-19T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "note": [ + { + "text": "Corrected source wording." + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:Y1RmRiTOXPRb6OXJeFxLdQ6m7RBkcALGfSNLPSt4DSk" + } + } + ], + "fullUrl": "urn:uuid:436cd99f-9396-56df-879d-ae00bbd53bbc", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:3cd67024-eab5-5604-900b-e287501b5b60" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:KO1AL4I7gqyRTVAeL7C06PosoLwPNYPnmXDsmyYyIIA" + } + } + ], + "fullUrl": "urn:uuid:cf931bb7-b891-515b-8ca9-5645f6beb6e3", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/corrected.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/corrected.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/corrected.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-conflict.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-conflict.json new file mode 100644 index 00000000..3c5556fb --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-conflict.json @@ -0,0 +1,389 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:63" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:ZFAuxSAIbtJj52-57l1UzIHTHMn1qDR8tPeJ6paVc7Y" + } + } + ], + "fullUrl": "urn:uuid:425786a2-5b80-53ce-a40e-2cc2cfa58faf", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:-mUWvC9wBT8cKHPAO4t6fvsJW9ele_WgaEO0swH-tQc" + } + } + ], + "fullUrl": "urn:uuid:119f148f-bf01-51f8-b59b-95579cbb11e0", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:-mUWvC9wBT8cKHPAO4t6fvsJW9ele_WgaEO0swH-tQc" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "writer-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1", + "value": "v0:test-key:1:TN39cF6jcCICy4SlWBjpAulk4eMlcb-KM3MwESD9cvw" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:119f148f-bf01-51f8-b59b-95579cbb11e0" + } + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version", + "valueString": "9007199254740993" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:425786a2-5b80-53ce-a40e-2cc2cfa58faf" + }, + "performer": [ + { + "reference": "urn:uuid:425786a2-5b80-53ce-a40e-2cc2cfa58faf" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:bc141551-c4f0-5a5d-8b1b-108c0e911293" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "note": [ + { + "text": "Conflicting source content claiming the same writer version." + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:XofqEZhQjw-0jRoWNCAysrwO1YbyqlMtmI_CfuuGkZM" + } + } + ], + "fullUrl": "urn:uuid:1a7c4848-ef93-5540-9e2c-9eee8d852fc1", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:119f148f-bf01-51f8-b59b-95579cbb11e0" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:Epv0o4T8eZFdz2N5_YRE-_cnVW0YjxtQPEDk6j6528o" + } + } + ], + "fullUrl": "urn:uuid:bc141551-c4f0-5a5d-8b1b-108c0e911293", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-conflict.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-conflict.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-conflict.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-reassembly.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-reassembly.json new file mode 100644 index 00000000..a8b26d87 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-reassembly.json @@ -0,0 +1,389 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:64" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:c3oXK_O4H_U-bPi8ojCQ7evxWEoDUZ1DuRQawgfZO9A" + } + } + ], + "fullUrl": "urn:uuid:7797382f-269c-563e-9103-03f7a5f8b96f", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:EYUasFsZmO8n8-2AWt5w5VyYh-6Yz5-5vSybbwLlMb8" + } + } + ], + "fullUrl": "urn:uuid:0232612a-a1ea-5122-92d8-90f8a485dac5", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:EYUasFsZmO8n8-2AWt5w5VyYh-6Yz5-5vSybbwLlMb8" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "writer-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1", + "value": "v0:test-key:1:TN39cF6jcCICy4SlWBjpAulk4eMlcb-KM3MwESD9cvw" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:0232612a-a1ea-5122-92d8-90f8a485dac5" + } + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version", + "valueString": "9007199254740993" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:7797382f-269c-563e-9103-03f7a5f8b96f" + }, + "performer": [ + { + "reference": "urn:uuid:7797382f-269c-563e-9103-03f7a5f8b96f" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:20043975-ca5b-5ae5-9684-c782ebc5bc33" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "note": [ + { + "text": "Primary reading from the synthetic source record." + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:4TLJ2QSOG-UwZ0JJez8C1EQwy2YA5bdG4CK5hsmO15w" + } + } + ], + "fullUrl": "urn:uuid:f24e4958-e688-5b5c-953f-9d2da7e03683", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:0232612a-a1ea-5122-92d8-90f8a485dac5" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:uajXfnMJHTMNEvY41IEBi0nlH8FUTkr0onITiAo3d50" + } + } + ], + "fullUrl": "urn:uuid:20043975-ca5b-5ae5-9684-c782ebc5bc33", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-reassembly.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-reassembly.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-reassembly.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/lexeme-retry.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/lexeme-retry.json new file mode 100644 index 00000000..cf88c043 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/lexeme-retry.json @@ -0,0 +1,389 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:BMLA8cDb0x8fjNhv70m6l8dDSHIk2zWfOScLokevBQQ" + } + } + ], + "fullUrl": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:FhFF4pknUiHfdg3v3p6KQYzE8ZI_U3YKj-IdsYcRpLU" + } + } + ], + "fullUrl": "urn:uuid:088c04b2-2b63-5a60-8f61-467f795cf5f0", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:FhFF4pknUiHfdg3v3p6KQYzE8ZI_U3YKj-IdsYcRpLU" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "writer-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1", + "value": "v0:test-key:1:TN39cF6jcCICy4SlWBjpAulk4eMlcb-KM3MwESD9cvw" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:088c04b2-2b63-5a60-8f61-467f795cf5f0" + } + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version", + "valueString": "9007199254740992" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" + }, + "performer": [ + { + "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:e516dd51-04d0-502d-abcf-b5c870915c17" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72.0, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "note": [ + { + "text": "Original source wording." + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:8JmcQF7rmULm9uJBkHWruJwfMu3GJTxGWqXWn2DGqWk" + } + } + ], + "fullUrl": "urn:uuid:71abc484-b9ee-511e-b22a-5b35d026d620", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:088c04b2-2b63-5a60-8f61-467f795cf5f0" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:kkYuCTNdLr58lk-fG2HJJQyMfv6quSSBFKSNI331b3k" + } + } + ], + "fullUrl": "urn:uuid:e516dd51-04d0-502d-abcf-b5c870915c17", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/lexeme-retry.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/lexeme-retry.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/lexeme-retry.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/missing-order.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/missing-order.json new file mode 100644 index 00000000..f3339a6a --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/missing-order.json @@ -0,0 +1,373 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:66" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:Ye5h3eMjiLrvRG3jWqnnvHXW5bvRFvRpZKyt20tB0xI" + } + } + ], + "fullUrl": "urn:uuid:e7327dcb-541c-525b-9d18-a0973dafd1af", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:Tj726etFKNnHaooVq2wP2K9I5odIL8ZDIp4yvqJJtS4" + } + } + ], + "fullUrl": "urn:uuid:685f3974-642a-5fc3-bd29-92d6dca680b1", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:Tj726etFKNnHaooVq2wP2K9I5odIL8ZDIp4yvqJJtS4" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:685f3974-642a-5fc3-bd29-92d6dca680b1" + } + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:e7327dcb-541c-525b-9d18-a0973dafd1af" + }, + "performer": [ + { + "reference": "urn:uuid:e7327dcb-541c-525b-9d18-a0973dafd1af" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:5ce1b928-7a73-5e27-ab07-89f200bd5aab" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "note": [ + { + "text": "Primary reading from the synthetic source record." + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:zUwy9k4SDIcbtrGu6OkcrFlLM6s3Kkqfj8V-V_CMgXY" + } + } + ], + "fullUrl": "urn:uuid:b576db96-3f74-59de-81e5-a1ec49f61b6a", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:685f3974-642a-5fc3-bd29-92d6dca680b1" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:6QR2mYCSSsDoCFCT1LY_EebWYMfKGppkrv7M7IlrmXU" + } + } + ], + "fullUrl": "urn:uuid:5ce1b928-7a73-5e27-ab07-89f200bd5aab", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/missing-order.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/missing-order.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/missing-order.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v1.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v1.json new file mode 100644 index 00000000..3f21c9af --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v1.json @@ -0,0 +1,518 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:62" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:kScHZcPxAFJVCyH6r5h7Z7IWFy5X-TGiTr1xUsJa1ks" + } + } + ], + "fullUrl": "urn:uuid:cd66f121-2723-5e47-b262-c5887dd4037e", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:tfnsLulBQxFAOUV6gNjzORPIjCXbXgrqMMZ3bvNa1ik" + } + } + ], + "fullUrl": "urn:uuid:0e18ddb5-c122-5cfa-b694-c68f1c1482f1", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:tfnsLulBQxFAOUV6gNjzORPIjCXbXgrqMMZ3bvNa1ik" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "writer-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1", + "value": "v0:test-key:1:TN39cF6jcCICy4SlWBjpAulk4eMlcb-KM3MwESD9cvw" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:0e18ddb5-c122-5cfa-b694-c68f1c1482f1" + } + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version", + "valueString": "9007199254740992" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:cd66f121-2723-5e47-b262-c5887dd4037e" + }, + "performer": [ + { + "reference": "urn:uuid:cd66f121-2723-5e47-b262-c5887dd4037e" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:5e75658c-daf2-5e4c-a732-d2f326a0739a" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "note": [ + { + "text": "Primary reading from the synthetic source record." + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:74t1kX69zhka2HnLSE6M2COW4qtNU9RNWRE9Izvwwxg" + } + } + ], + "fullUrl": "urn:uuid:f60492f9-024c-5288-9bbd-44f79124fce5", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:0e18ddb5-c122-5cfa-b694-c68f1c1482f1" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + }, + { + "reference": "urn:uuid:96934596-b8b3-5623-b026-cb381d6beede" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:6cn4OgNKsU3R-JGHiu2PUgvcNH72WFoei3_5UpaYtqs" + } + } + ], + "fullUrl": "urn:uuid:5e75658c-daf2-5e4c-a732-d2f326a0739a", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:BYrV6N3A1WrjO1saOCgLww1gdAQqFQ5jLoFoTKwec9c" + } + } + ], + "fullUrl": "urn:uuid:96934596-b8b3-5623-b026-cb381d6beede", + "resource": { + "resourceType": "Observation", + "id": "ReceiverSecondaryHeartRate", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:BYrV6N3A1WrjO1saOCgLww1gdAQqFQ5jLoFoTKwec9c" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "writer-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1", + "value": "v0:test-key:1:TN39cF6jcCICy4SlWBjpAulk4eMlcb-KM3MwESD9cvw" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:0e18ddb5-c122-5cfa-b694-c68f1c1482f1" + } + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version", + "valueString": "9007199254740992" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:cd66f121-2723-5e47-b262-c5887dd4037e" + }, + "performer": [ + { + "reference": "urn:uuid:cd66f121-2723-5e47-b262-c5887dd4037e" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:5e75658c-daf2-5e4c-a732-d2f326a0739a" + } + ], + "effectiveDateTime": "2026-08-20T08:30:01.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "note": [ + { + "text": "Secondary reading from the same source record." + } + ] + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v1.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v1.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v1.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v2.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v2.json new file mode 100644 index 00000000..01e249d1 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v2.json @@ -0,0 +1,389 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:61" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:KWfQvY949uVMxr5bM3rJ0tRFXf8U3LIW-xkHm0ly_Dk" + } + } + ], + "fullUrl": "urn:uuid:b0e08f59-4f6e-5060-a1b3-90ad07a35ee1", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:G0shWKztBFVa7S2QFoeDhU71zLfIEkS5BkE_3jFpiNk" + } + } + ], + "fullUrl": "urn:uuid:c0100562-c587-53c1-84db-4c7a00dfdbcb", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:G0shWKztBFVa7S2QFoeDhU71zLfIEkS5BkE_3jFpiNk" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "writer-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1", + "value": "v0:test-key:1:TN39cF6jcCICy4SlWBjpAulk4eMlcb-KM3MwESD9cvw" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:c0100562-c587-53c1-84db-4c7a00dfdbcb" + } + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version", + "valueString": "9007199254740993" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:b0e08f59-4f6e-5060-a1b3-90ad07a35ee1" + }, + "performer": [ + { + "reference": "urn:uuid:b0e08f59-4f6e-5060-a1b3-90ad07a35ee1" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:e8dbccec-2e95-5ec1-8aa9-2e470acf3808" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "note": [ + { + "text": "Primary reading from the synthetic source record." + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:NIfIte3o6LQOat5YwymSLL8HN25gOvelPRNrSePoS7M" + } + } + ], + "fullUrl": "urn:uuid:97d48257-16a0-5232-8fc8-2b98f2c408c7", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:c0100562-c587-53c1-84db-4c7a00dfdbcb" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:wTnKa7yifrZtBMgVuDPLEwG4eqzpI0s8UoXSEsnLVfg" + } + } + ], + "fullUrl": "urn:uuid:e8dbccec-2e95-5ec1-8aa9-2e470acf3808", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v2.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v2.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v2.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v3.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v3.json new file mode 100644 index 00000000..c826ab45 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v3.json @@ -0,0 +1,518 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:60" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:C8vUY0t3SqEzXVtbuuS8h59u-HwS1gf-PKaghMxX9_g" + } + } + ], + "fullUrl": "urn:uuid:7fe33eb6-ecc8-5b66-b793-92ba52f56f37", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:dFdgpZqSrZI83hNxyslxoI1qXUlq0fa2bAo4tnhv2CE" + } + } + ], + "fullUrl": "urn:uuid:c4fc6731-fa00-594a-8b16-84bb4a2f41e5", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:dFdgpZqSrZI83hNxyslxoI1qXUlq0fa2bAo4tnhv2CE" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "writer-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1", + "value": "v0:test-key:1:TN39cF6jcCICy4SlWBjpAulk4eMlcb-KM3MwESD9cvw" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:c4fc6731-fa00-594a-8b16-84bb4a2f41e5" + } + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version", + "valueString": "9007199254740994" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:7fe33eb6-ecc8-5b66-b793-92ba52f56f37" + }, + "performer": [ + { + "reference": "urn:uuid:7fe33eb6-ecc8-5b66-b793-92ba52f56f37" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:42d323e4-ca65-5f7c-b515-acc0bd159671" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "note": [ + { + "text": "Primary reading from the synthetic source record." + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:yPn6eVRWgBlqK-r7hoJBa7wealeJ6N9nlUlT33_2ldA" + } + } + ], + "fullUrl": "urn:uuid:f55cbd9d-3ac8-57fc-aec8-41429891a210", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:c4fc6731-fa00-594a-8b16-84bb4a2f41e5" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + }, + { + "reference": "urn:uuid:96934596-b8b3-5623-b026-cb381d6beede" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:rt1TlWxc2csoWBEUEhxKcgvFCGjDt6gN9jQUpgIA8d8" + } + } + ], + "fullUrl": "urn:uuid:42d323e4-ca65-5f7c-b515-acc0bd159671", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:BYrV6N3A1WrjO1saOCgLww1gdAQqFQ5jLoFoTKwec9c" + } + } + ], + "fullUrl": "urn:uuid:96934596-b8b3-5623-b026-cb381d6beede", + "resource": { + "resourceType": "Observation", + "id": "ReceiverSecondaryHeartRate", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:BYrV6N3A1WrjO1saOCgLww1gdAQqFQ5jLoFoTKwec9c" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "writer-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1", + "value": "v0:test-key:1:TN39cF6jcCICy4SlWBjpAulk4eMlcb-KM3MwESD9cvw" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:c4fc6731-fa00-594a-8b16-84bb4a2f41e5" + } + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version", + "valueString": "9007199254740994" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:7fe33eb6-ecc8-5b66-b793-92ba52f56f37" + }, + "performer": [ + { + "reference": "urn:uuid:7fe33eb6-ecc8-5b66-b793-92ba52f56f37" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:42d323e4-ca65-5f7c-b515-acc0bd159671" + } + ], + "effectiveDateTime": "2026-08-20T08:30:01.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "note": [ + { + "text": "Secondary reading from the same source record." + } + ] + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v3.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v3.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v3.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/original.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/original.json new file mode 100644 index 00000000..25fdc9f5 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/original.json @@ -0,0 +1,389 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:BMLA8cDb0x8fjNhv70m6l8dDSHIk2zWfOScLokevBQQ" + } + } + ], + "fullUrl": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:FhFF4pknUiHfdg3v3p6KQYzE8ZI_U3YKj-IdsYcRpLU" + } + } + ], + "fullUrl": "urn:uuid:088c04b2-2b63-5a60-8f61-467f795cf5f0", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:FhFF4pknUiHfdg3v3p6KQYzE8ZI_U3YKj-IdsYcRpLU" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "writer-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1", + "value": "v0:test-key:1:TN39cF6jcCICy4SlWBjpAulk4eMlcb-KM3MwESD9cvw" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:088c04b2-2b63-5a60-8f61-467f795cf5f0" + } + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version", + "valueString": "9007199254740992" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" + }, + "performer": [ + { + "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:e516dd51-04d0-502d-abcf-b5c870915c17" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "note": [ + { + "text": "Original source wording." + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:8JmcQF7rmULm9uJBkHWruJwfMu3GJTxGWqXWn2DGqWk" + } + } + ], + "fullUrl": "urn:uuid:71abc484-b9ee-511e-b22a-5b35d026d620", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:088c04b2-2b63-5a60-8f61-467f795cf5f0" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:kkYuCTNdLr58lk-fG2HJJQyMfv6quSSBFKSNI331b3k" + } + } + ], + "fullUrl": "urn:uuid:e516dd51-04d0-502d-abcf-b5c870915c17", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/original.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/original.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/original.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/other-writer.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/other-writer.json new file mode 100644 index 00000000..6f2bc5c2 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/other-writer.json @@ -0,0 +1,389 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:65" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:J9dt0_AMPafZdgbwyVAApWTz87LWYzqOOn832otZbhQ" + } + } + ], + "fullUrl": "urn:uuid:5e0074e2-9ab3-502c-afbf-b75af2e302e1", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:oPK4uLWJLUJo8JL5r7lWjaQL8dAiSAsfVOtqsk6ruQ8" + } + } + ], + "fullUrl": "urn:uuid:b632ac75-3014-58df-8a54-c330d28176e7", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:oPK4uLWJLUJo8JL5r7lWjaQL8dAiSAsfVOtqsk6ruQ8" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "writer-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1", + "value": "v0:test-key:1:5mIZKnS2ox49bXbK-YuTn_SPDVdslEyQIsXF7oV2h3k" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:b632ac75-3014-58df-8a54-c330d28176e7" + } + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version", + "valueString": "9007199254740994" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:5e0074e2-9ab3-502c-afbf-b75af2e302e1" + }, + "performer": [ + { + "reference": "urn:uuid:5e0074e2-9ab3-502c-afbf-b75af2e302e1" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:2325adb5-e4f3-5648-a64f-39d430ebc7cb" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "note": [ + { + "text": "Primary reading from the synthetic source record." + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:n5t4I7TyDcqchxhZy3chq9oB5kNT5CP1iPqzUhRsMW0" + } + } + ], + "fullUrl": "urn:uuid:7dbfd4d4-8a46-5ed8-87e4-84f594036b7c", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:b632ac75-3014-58df-8a54-c330d28176e7" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:M7RIjz9LK373S1usqIEvjP0xXfmSt1DntahIvzgTkIo" + } + } + ], + "fullUrl": "urn:uuid:2325adb5-e4f3-5648-a64f-39d430ebc7cb", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/other-writer.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/other-writer.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/other-writer.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/pending.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/pending.json new file mode 100644 index 00000000..dedaa5d9 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/pending.json @@ -0,0 +1,300 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:46" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:EfZGRcBw2f_2Hwl7KKuisBo_sxx3BosfF59VQqS90i0" + } + } + ], + "fullUrl": "urn:uuid:38245485-4bc3-59c4-876d-aa568b5dadeb", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:P-CZcBXDylBtujPUTLBrMVc24OEl_83sh_WrgLhEGfk" + } + } + ], + "fullUrl": "urn:uuid:c8e5f2c1-b0da-5956-809a-b5104b9dfb05", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:P-CZcBXDylBtujPUTLBrMVc24OEl_83sh_WrgLhEGfk" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:c8e5f2c1-b0da-5956-809a-b5104b9dfb05" + } + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:38245485-4bc3-59c4-876d-aa568b5dadeb" + }, + "performer": [ + { + "reference": "urn:uuid:38245485-4bc3-59c4-876d-aa568b5dadeb" + } + ], + "derivedFrom": [ + { + "type": "QuestionnaireResponse", + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + } + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:0sgvrej69pIC5QY_KxQ2nQVzrpaaKUkbnyM-ZDTyZZs" + } + } + ], + "fullUrl": "urn:uuid:9cc7e000-88f1-5ffc-b7a8-fb8b70a791ca", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:c8e5f2c1-b0da-5956-809a-b5104b9dfb05" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/pending.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/pending.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/pending.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/reformatted-retry.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/reformatted-retry.json new file mode 100644 index 00000000..2afc41d8 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/reformatted-retry.json @@ -0,0 +1,389 @@ +{ + "timestamp": "2026-08-20T17:30:02Z", + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:BMLA8cDb0x8fjNhv70m6l8dDSHIk2zWfOScLokevBQQ" + } + } + ], + "fullUrl": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:FhFF4pknUiHfdg3v3p6KQYzE8ZI_U3YKj-IdsYcRpLU" + } + } + ], + "fullUrl": "urn:uuid:088c04b2-2b63-5a60-8f61-467f795cf5f0", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:FhFF4pknUiHfdg3v3p6KQYzE8ZI_U3YKj-IdsYcRpLU" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "note": [ + { + "text": "Original source wording." + } + ], + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "issued": "2026-08-20T17:30:02Z", + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "derivedFrom": [ + { + "reference": "urn:uuid:e516dd51-04d0-502d-abcf-b5c870915c17" + } + ], + "performer": [ + { + "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" + } + ], + "subject": { + "reference": "urn:uuid:d5137e30-79b1-5110-a09c-bd2528e25085" + }, + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "status": "final", + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:088c04b2-2b63-5a60-8f61-467f795cf5f0" + } + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version", + "valueString": "9007199254740992" + } + ], + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "writer-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1", + "value": "v0:test-key:1:TN39cF6jcCICy4SlWBjpAulk4eMlcb-KM3MwESD9cvw" + } + ], + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "id": "GroveMobileExchangeHeartRateExample", + "resourceType": "Observation" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:8JmcQF7rmULm9uJBkHWruJwfMu3GJTxGWqXWn2DGqWk" + } + } + ], + "fullUrl": "urn:uuid:71abc484-b9ee-511e-b22a-5b35d026d620", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:088c04b2-2b63-5a60-8f61-467f795cf5f0" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:kkYuCTNdLr58lk-fG2HJJQyMfv6quSSBFKSNI331b3k" + } + } + ], + "fullUrl": "urn:uuid:e516dd51-04d0-502d-abcf-b5c870915c17", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + } + ], + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42" + }, + "type": "collection", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "id": "GroveMobileExchangeBundleExample", + "resourceType": "Bundle" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/reformatted-retry.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/reformatted-retry.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/reformatted-retry.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/retraction.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/retraction.json new file mode 100644 index 00000000..138fdc94 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/retraction.json @@ -0,0 +1,147 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileRetractionBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:43" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:retraction-provenance:0:o0Qrv-QfsfhozEJ5jeXVloNGbKrhYM9Rk6eziKoPmV4" + } + } + ], + "fullUrl": "urn:uuid:f99194b8-aaf8-5fdb-91fa-19309bdd6716", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileRetractionProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "source-record-retracted", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-lifecycle-event", + "display": "Source record retracted" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "type": "Device", + "identifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:JUZooa4NZX-rlMn9-NC8W078xzEuUO9X3Xo1mgk0Vac" + } + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-role", + "valueCode": "primary-output" + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-native-identifier", + "valueIdentifier": { + "system": "https://study.example.org/fhir/NamingSystem/native-record/health-connect/1f5c58aa-6ec6-4e79-a682-829a9debd3f5", + "value": "record-heart-001" + } + } + ], + "type": "Observation", + "identifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source output" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "occurredDateTime": "2026-08-21T08:00:00Z", + "recorded": "2026-08-21T08:00:01Z" + } + } + ], + "timestamp": "2026-08-21T08:00:01Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/retraction.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/retraction.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/retraction.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/target.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/target.json new file mode 100644 index 00000000..aba18920 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/target.json @@ -0,0 +1,367 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:47" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:ZSPD5PHzutPagk6qSayyq9GH64UGhUFCYRT361KDygk" + } + } + ], + "fullUrl": "urn:uuid:6cfd6eec-c9a2-58e1-9c1c-7e0099f2ddff", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:7CSIYKL1G_jH0947A2Wrz5arDAmPJ__NU5syHdbiLFk" + } + } + ], + "fullUrl": "urn:uuid:059b32db-025e-5bce-9d3e-451f14a2bf7a", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:7CSIYKL1G_jH0947A2Wrz5arDAmPJ__NU5syHdbiLFk" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:M6afafiHPF1Pqyg4OZ5IN0R_8s97oM3WWy904IjTwy8" + } + } + ], + "fullUrl": "urn:uuid:a0b3b2a7-a112-5a26-9090-e0b1ee94dcab", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:TE47APXmta6xbmGufkf8LBv8q3aPR4NDn9bltP7vEn4" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:M6afafiHPF1Pqyg4OZ5IN0R_8s97oM3WWy904IjTwy8" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:059b32db-025e-5bce-9d3e-451f14a2bf7a" + } + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:6cfd6eec-c9a2-58e1-9c1c-7e0099f2ddff" + }, + "performer": [ + { + "reference": "urn:uuid:6cfd6eec-c9a2-58e1-9c1c-7e0099f2ddff" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:67ffa41f-8ddb-54db-b3bb-7f2702db250c" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:myQoJ_RMebZA__rH0KS_EAMTRcGBx2PSCbRFcmBaEuc" + } + } + ], + "fullUrl": "urn:uuid:4fac4f5b-e17f-5493-b601-1e7dfdf8c11f", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:059b32db-025e-5bce-9d3e-451f14a2bf7a" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:TE47APXmta6xbmGufkf8LBv8q3aPR4NDn9bltP7vEn4" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a0b3b2a7-a112-5a26-9090-e0b1ee94dcab" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:CvzBJHl1HWW5BPp_L9EHpwMGXCh1LupLAZKXrP7sGkk" + } + } + ], + "fullUrl": "urn:uuid:67ffa41f-8ddb-54db-b3bb-7f2702db250c", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/target.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/target.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/target.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-a.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-a.json new file mode 100644 index 00000000..e7c22000 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-a.json @@ -0,0 +1,373 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:44" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:ajCbf1s7BC33Ki56HSjyZ4yhQGxiONfjunh5ZyUzHHY" + } + } + ], + "fullUrl": "urn:uuid:b30696f6-abab-5df7-bddf-296e6e059651", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:6I8uWw-e6duXMHPcuf6Zcjv5YKphw1S0hZgfq1N9EMM" + } + } + ], + "fullUrl": "urn:uuid:c9fa6cd2-06f4-5b70-8b87-12507504a909", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:6I8uWw-e6duXMHPcuf6Zcjv5YKphw1S0hZgfq1N9EMM" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:c9fa6cd2-06f4-5b70-8b87-12507504a909" + } + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:b30696f6-abab-5df7-bddf-296e6e059651" + }, + "performer": [ + { + "reference": "urn:uuid:b30696f6-abab-5df7-bddf-296e6e059651" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:3be7197b-d8df-56fe-b17c-db6b11ac9428" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "note": [ + { + "text": "First source wording without writer ordering evidence." + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:lLJjnS60Cge7fzpLw-U4Xe9PuWYJknCrTHgPZjaLr1s" + } + } + ], + "fullUrl": "urn:uuid:6d4a0448-073a-58e0-aa3e-06c8d3d60f73", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:c9fa6cd2-06f4-5b70-8b87-12507504a909" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:RsFtGtiPnUhr-6xmNnjrWe_R4TlOLa9mXqyQkuFkXwE" + } + } + ], + "fullUrl": "urn:uuid:3be7197b-d8df-56fe-b17c-db6b11ac9428", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-a.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-a.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-a.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-b.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-b.json new file mode 100644 index 00000000..a77465cd --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-b.json @@ -0,0 +1,373 @@ +{ + "resourceType": "Bundle", + "id": "GroveMobileExchangeBundleExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:45" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:9Qzw5knMv64roHEDXLjPlLjvUzAvg8NsAGkQnoDTdYA" + } + } + ], + "fullUrl": "urn:uuid:c39aa097-4b56-530b-8c90-0f39e3e1b7bd", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:v4OvFw1CH0dYyiwUrWICl6MPUpp9BrBDva51303xv4M" + } + } + ], + "fullUrl": "urn:uuid:92c223ec-5f2c-5289-b265-7db1cdc89b44", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:v4OvFw1CH0dYyiwUrWICl6MPUpp9BrBDva51303xv4M" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:92c223ec-5f2c-5289-b265-7db1cdc89b44" + } + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:c39aa097-4b56-530b-8c90-0f39e3e1b7bd" + }, + "performer": [ + { + "reference": "urn:uuid:c39aa097-4b56-530b-8c90-0f39e3e1b7bd" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:815d427c-7274-5000-b8dc-79be45a377a1" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + }, + "note": [ + { + "text": "Different wording without comparable writer ordering evidence." + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:tz4vZwGZDX_N-opMGzxexwYDD_XMLQ022amZb4VOrfE" + } + } + ], + "fullUrl": "urn:uuid:7c43ee9a-f52b-588d-bb88-433be674eec6", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:92c223ec-5f2c-5289-b265-7db1cdc89b44" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:tC3-O76VMjOGSume8ui2ZfLdy5B1wQP_yvebjGkojmw" + } + } + ], + "fullUrl": "urn:uuid:815d427c-7274-5000-b8dc-79be45a377a1", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-b.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-b.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-b.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/revision-sequences.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/revision-sequences.json new file mode 100644 index 00000000..9623d557 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/revision-sequences.json @@ -0,0 +1,4481 @@ +{ + "schemaVersion": 0, + "events": "events.json", + "outputs": { + "multi-v1.primary": { + "event": "multi-v1", + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + }, + "multi-v1.secondary": { + "event": "multi-v1", + "fullUrl": "urn:uuid:96934596-b8b3-5623-b026-cb381d6beede" + }, + "multi-v2.primary": { + "event": "multi-v2", + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + }, + "multi-v3.primary": { + "event": "multi-v3", + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + }, + "multi-v3.secondary": { + "event": "multi-v3", + "fullUrl": "urn:uuid:96934596-b8b3-5623-b026-cb381d6beede" + }, + "equal-version-conflict.primary": { + "event": "equal-version-conflict", + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + }, + "equal-version-reassembly.primary": { + "event": "equal-version-reassembly", + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + }, + "other-writer.primary": { + "event": "other-writer", + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + }, + "missing-order.primary": { + "event": "missing-order", + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + }, + "policy": { + "scope": "One verified participant/source repository and the same complete source-record identity.", + "membership": "Comparable writer versions select one complete source-record revision, including its output membership, not a maximum independently for each output.", + "equivalence": "Exact event replay is proven equality. This receiver policy does not infer cross-event source-content equality by stripping graph fields. Distinct events with equal writer versions remain unresolved, even when displayed measurements agree.", + "ordering": "Different writer identities or absent comparable writer evidence cannot order candidates. A missing output in a later complete revision is not a retraction tombstone.", + "supersession": "A unique higher complete revision under the same writer identity can become current while equal-version conflicts among older revisions remain in history. A higher version cannot resolve missing or different writer evidence. Conflict evidence is not erased when current selection advances.", + "authorization": "Only the listed exact output versions are authorized. Current selection never carries a previous version's grant forward." + }, + "authorizedOutputs": { + "purpose-a": [ + "equal-version-conflict.primary", + "equal-version-reassembly.primary", + "missing-order.primary", + "multi-v1.primary", + "multi-v1.secondary", + "multi-v2.primary", + "multi-v3.primary", + "other-writer.primary" + ], + "purpose-b": [] + }, + "sequences": [ + { + "id": "removed-child-does-not-remain-current", + "steps": [ + { + "event": "multi-v1", + "expect": { + "history": [ + "multi-v1" + ], + "versions": [ + "multi-v1.primary", + "multi-v1.secondary" + ], + "current": [ + "multi-v1.primary", + "multi-v1.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v1.primary", + "multi-v1.secondary" + ], + "newlyVisible": [ + "multi-v1.primary", + "multi-v1.secondary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v1", + "multi-v2" + ], + "versions": [ + "multi-v1.primary", + "multi-v1.secondary", + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [ + "multi-v2.primary" + ] + } + }, + { + "event": "multi-v1", + "expect": { + "history": [ + "multi-v1", + "multi-v2" + ], + "versions": [ + "multi-v1.primary", + "multi-v1.secondary", + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [] + } + } + ] + }, + { + "id": "late-old-revision-does-not-add-a-child", + "steps": [ + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2" + ], + "versions": [ + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [ + "multi-v2.primary" + ] + } + }, + { + "event": "multi-v1", + "expect": { + "history": [ + "multi-v2", + "multi-v1" + ], + "versions": [ + "multi-v1.primary", + "multi-v1.secondary", + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [] + } + } + ] + }, + { + "id": "membership-removal-is-not-a-tombstone-or-a-grant", + "steps": [ + { + "event": "multi-v1", + "expect": { + "history": [ + "multi-v1" + ], + "versions": [ + "multi-v1.primary", + "multi-v1.secondary" + ], + "current": [ + "multi-v1.primary", + "multi-v1.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v1.primary", + "multi-v1.secondary" + ], + "newlyVisible": [ + "multi-v1.primary", + "multi-v1.secondary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v1", + "multi-v2" + ], + "versions": [ + "multi-v1.primary", + "multi-v1.secondary", + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [ + "multi-v2.primary" + ] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v1", + "multi-v2", + "multi-v3" + ], + "versions": [ + "multi-v1.primary", + "multi-v1.secondary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + } + ] + }, + { + "id": "equal-version-conflict-requires-resolution", + "steps": [ + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2" + ], + "versions": [ + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [ + "multi-v2.primary" + ] + } + }, + { + "event": "equal-version-conflict", + "expect": { + "history": [ + "multi-v2", + "equal-version-conflict" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2", + "equal-version-conflict" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "equal-version-conflict-arrives-first", + "steps": [ + { + "event": "equal-version-conflict", + "expect": { + "history": [ + "equal-version-conflict" + ], + "versions": [ + "equal-version-conflict.primary" + ], + "current": [ + "equal-version-conflict.primary" + ], + "conflicts": [], + "visible": [ + "equal-version-conflict.primary" + ], + "newlyVisible": [ + "equal-version-conflict.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "equal-version-conflict", + "multi-v2" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "equal-version-conflict", + "multi-v2" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "equal-version-conflict", + "expect": { + "history": [ + "equal-version-conflict", + "multi-v2" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "equal-version-conflict", + "multi-v2" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "equal-version-conflict-with-higher-revision-older-competing-newer", + "steps": [ + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2" + ], + "versions": [ + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [ + "multi-v2.primary" + ] + } + }, + { + "event": "equal-version-conflict", + "expect": { + "history": [ + "multi-v2", + "equal-version-conflict" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "equal-version-conflict", + "multi-v3" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2", + "equal-version-conflict", + "multi-v3" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "equal-version-conflict", + "expect": { + "history": [ + "multi-v2", + "equal-version-conflict", + "multi-v3" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "equal-version-conflict", + "multi-v3" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + } + ] + }, + { + "id": "equal-version-conflict-with-higher-revision-older-newer-competing", + "steps": [ + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2" + ], + "versions": [ + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [ + "multi-v2.primary" + ] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "multi-v3" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "equal-version-conflict", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "equal-version-conflict" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "equal-version-conflict" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "equal-version-conflict" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "equal-version-conflict", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "equal-version-conflict" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + } + ] + }, + { + "id": "equal-version-conflict-with-higher-revision-competing-older-newer", + "steps": [ + { + "event": "equal-version-conflict", + "expect": { + "history": [ + "equal-version-conflict" + ], + "versions": [ + "equal-version-conflict.primary" + ], + "current": [ + "equal-version-conflict.primary" + ], + "conflicts": [], + "visible": [ + "equal-version-conflict.primary" + ], + "newlyVisible": [ + "equal-version-conflict.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "equal-version-conflict", + "multi-v2" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "equal-version-conflict", + "multi-v2", + "multi-v3" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "equal-version-conflict", + "expect": { + "history": [ + "equal-version-conflict", + "multi-v2", + "multi-v3" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "equal-version-conflict", + "multi-v2", + "multi-v3" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "equal-version-conflict", + "multi-v2", + "multi-v3" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + } + ] + }, + { + "id": "equal-version-conflict-with-higher-revision-competing-newer-older", + "steps": [ + { + "event": "equal-version-conflict", + "expect": { + "history": [ + "equal-version-conflict" + ], + "versions": [ + "equal-version-conflict.primary" + ], + "current": [ + "equal-version-conflict.primary" + ], + "conflicts": [], + "visible": [ + "equal-version-conflict.primary" + ], + "newlyVisible": [ + "equal-version-conflict.primary" + ] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "equal-version-conflict", + "multi-v3" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "equal-version-conflict", + "multi-v3", + "multi-v2" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "equal-version-conflict", + "expect": { + "history": [ + "equal-version-conflict", + "multi-v3", + "multi-v2" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "equal-version-conflict", + "multi-v3", + "multi-v2" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "equal-version-conflict", + "multi-v3", + "multi-v2" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + } + ] + }, + { + "id": "equal-version-conflict-with-higher-revision-newer-older-competing", + "steps": [ + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3" + ], + "versions": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "multi-v2" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "equal-version-conflict", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "equal-version-conflict" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "equal-version-conflict" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "equal-version-conflict" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "equal-version-conflict", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "equal-version-conflict" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + } + ] + }, + { + "id": "equal-version-conflict-with-higher-revision-newer-competing-older", + "steps": [ + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3" + ], + "versions": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "equal-version-conflict", + "expect": { + "history": [ + "multi-v3", + "equal-version-conflict" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "equal-version-conflict", + "multi-v2" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3", + "equal-version-conflict", + "multi-v2" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "equal-version-conflict", + "expect": { + "history": [ + "multi-v3", + "equal-version-conflict", + "multi-v2" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "equal-version-conflict", + "multi-v2" + ], + "versions": [ + "equal-version-conflict.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-conflict" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + } + ] + }, + { + "id": "equal-version-reassembly-requires-resolution", + "steps": [ + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2" + ], + "versions": [ + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [ + "multi-v2.primary" + ] + } + }, + { + "event": "equal-version-reassembly", + "expect": { + "history": [ + "multi-v2", + "equal-version-reassembly" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2", + "equal-version-reassembly" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "equal-version-reassembly-arrives-first", + "steps": [ + { + "event": "equal-version-reassembly", + "expect": { + "history": [ + "equal-version-reassembly" + ], + "versions": [ + "equal-version-reassembly.primary" + ], + "current": [ + "equal-version-reassembly.primary" + ], + "conflicts": [], + "visible": [ + "equal-version-reassembly.primary" + ], + "newlyVisible": [ + "equal-version-reassembly.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "equal-version-reassembly", + "multi-v2" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "equal-version-reassembly", + "multi-v2" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "equal-version-reassembly", + "expect": { + "history": [ + "equal-version-reassembly", + "multi-v2" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "equal-version-reassembly", + "multi-v2" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "equal-version-reassembly-with-higher-revision-older-competing-newer", + "steps": [ + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2" + ], + "versions": [ + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [ + "multi-v2.primary" + ] + } + }, + { + "event": "equal-version-reassembly", + "expect": { + "history": [ + "multi-v2", + "equal-version-reassembly" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "equal-version-reassembly", + "multi-v3" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2", + "equal-version-reassembly", + "multi-v3" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "equal-version-reassembly", + "expect": { + "history": [ + "multi-v2", + "equal-version-reassembly", + "multi-v3" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "equal-version-reassembly", + "multi-v3" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + } + ] + }, + { + "id": "equal-version-reassembly-with-higher-revision-older-newer-competing", + "steps": [ + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2" + ], + "versions": [ + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [ + "multi-v2.primary" + ] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "multi-v3" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "equal-version-reassembly", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "equal-version-reassembly" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "equal-version-reassembly" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "equal-version-reassembly" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "equal-version-reassembly", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "equal-version-reassembly" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + } + ] + }, + { + "id": "equal-version-reassembly-with-higher-revision-competing-older-newer", + "steps": [ + { + "event": "equal-version-reassembly", + "expect": { + "history": [ + "equal-version-reassembly" + ], + "versions": [ + "equal-version-reassembly.primary" + ], + "current": [ + "equal-version-reassembly.primary" + ], + "conflicts": [], + "visible": [ + "equal-version-reassembly.primary" + ], + "newlyVisible": [ + "equal-version-reassembly.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "equal-version-reassembly", + "multi-v2" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "equal-version-reassembly", + "multi-v2", + "multi-v3" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "equal-version-reassembly", + "expect": { + "history": [ + "equal-version-reassembly", + "multi-v2", + "multi-v3" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "equal-version-reassembly", + "multi-v2", + "multi-v3" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "equal-version-reassembly", + "multi-v2", + "multi-v3" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + } + ] + }, + { + "id": "equal-version-reassembly-with-higher-revision-competing-newer-older", + "steps": [ + { + "event": "equal-version-reassembly", + "expect": { + "history": [ + "equal-version-reassembly" + ], + "versions": [ + "equal-version-reassembly.primary" + ], + "current": [ + "equal-version-reassembly.primary" + ], + "conflicts": [], + "visible": [ + "equal-version-reassembly.primary" + ], + "newlyVisible": [ + "equal-version-reassembly.primary" + ] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "equal-version-reassembly", + "multi-v3" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "equal-version-reassembly", + "multi-v3", + "multi-v2" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "equal-version-reassembly", + "expect": { + "history": [ + "equal-version-reassembly", + "multi-v3", + "multi-v2" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "equal-version-reassembly", + "multi-v3", + "multi-v2" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "equal-version-reassembly", + "multi-v3", + "multi-v2" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + } + ] + }, + { + "id": "equal-version-reassembly-with-higher-revision-newer-older-competing", + "steps": [ + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3" + ], + "versions": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "multi-v2" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "equal-version-reassembly", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "equal-version-reassembly" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "equal-version-reassembly" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "equal-version-reassembly" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "equal-version-reassembly", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "equal-version-reassembly" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + } + ] + }, + { + "id": "equal-version-reassembly-with-higher-revision-newer-competing-older", + "steps": [ + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3" + ], + "versions": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "equal-version-reassembly", + "expect": { + "history": [ + "multi-v3", + "equal-version-reassembly" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "equal-version-reassembly", + "multi-v2" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3", + "equal-version-reassembly", + "multi-v2" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "equal-version-reassembly", + "expect": { + "history": [ + "multi-v3", + "equal-version-reassembly", + "multi-v2" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "equal-version-reassembly", + "multi-v2" + ], + "versions": [ + "equal-version-reassembly.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [ + "multi-v2", + "equal-version-reassembly" + ], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + } + ] + }, + { + "id": "other-writer-requires-resolution", + "steps": [ + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2" + ], + "versions": [ + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [ + "multi-v2.primary" + ] + } + }, + { + "event": "other-writer", + "expect": { + "history": [ + "multi-v2", + "other-writer" + ], + "versions": [ + "multi-v2.primary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2", + "other-writer" + ], + "versions": [ + "multi-v2.primary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "other-writer-arrives-first", + "steps": [ + { + "event": "other-writer", + "expect": { + "history": [ + "other-writer" + ], + "versions": [ + "other-writer.primary" + ], + "current": [ + "other-writer.primary" + ], + "conflicts": [], + "visible": [ + "other-writer.primary" + ], + "newlyVisible": [ + "other-writer.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "other-writer", + "multi-v2" + ], + "versions": [ + "multi-v2.primary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "other-writer", + "multi-v2" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "other-writer", + "expect": { + "history": [ + "other-writer", + "multi-v2" + ], + "versions": [ + "multi-v2.primary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "other-writer", + "multi-v2" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "other-writer-with-higher-revision-older-competing-newer", + "steps": [ + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2" + ], + "versions": [ + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [ + "multi-v2.primary" + ] + } + }, + { + "event": "other-writer", + "expect": { + "history": [ + "multi-v2", + "other-writer" + ], + "versions": [ + "multi-v2.primary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "other-writer", + "expect": { + "history": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "other-writer-with-higher-revision-older-newer-competing", + "steps": [ + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2" + ], + "versions": [ + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [ + "multi-v2.primary" + ] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "multi-v3" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "other-writer", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "other-writer" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "other-writer" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "other-writer" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "other-writer", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "other-writer" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "other-writer-with-higher-revision-competing-older-newer", + "steps": [ + { + "event": "other-writer", + "expect": { + "history": [ + "other-writer" + ], + "versions": [ + "other-writer.primary" + ], + "current": [ + "other-writer.primary" + ], + "conflicts": [], + "visible": [ + "other-writer.primary" + ], + "newlyVisible": [ + "other-writer.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "other-writer", + "multi-v2" + ], + "versions": [ + "multi-v2.primary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "other-writer", + "multi-v2", + "multi-v3" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "other-writer", + "expect": { + "history": [ + "other-writer", + "multi-v2", + "multi-v3" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "other-writer", + "multi-v2", + "multi-v3" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "other-writer", + "multi-v2", + "multi-v3" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "other-writer-with-higher-revision-competing-newer-older", + "steps": [ + { + "event": "other-writer", + "expect": { + "history": [ + "other-writer" + ], + "versions": [ + "other-writer.primary" + ], + "current": [ + "other-writer.primary" + ], + "conflicts": [], + "visible": [ + "other-writer.primary" + ], + "newlyVisible": [ + "other-writer.primary" + ] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "other-writer", + "multi-v3" + ], + "versions": [ + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "other-writer", + "multi-v3", + "multi-v2" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "other-writer", + "expect": { + "history": [ + "other-writer", + "multi-v3", + "multi-v2" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "other-writer", + "multi-v3", + "multi-v2" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "other-writer", + "multi-v3", + "multi-v2" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "other-writer-with-higher-revision-newer-older-competing", + "steps": [ + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3" + ], + "versions": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "multi-v2" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "other-writer", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "other-writer" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "other-writer" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "other-writer" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "other-writer", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "other-writer" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "other-writer-with-higher-revision-newer-competing-older", + "steps": [ + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3" + ], + "versions": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "other-writer", + "expect": { + "history": [ + "multi-v3", + "other-writer" + ], + "versions": [ + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "other-writer", + "multi-v2" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3", + "other-writer", + "multi-v2" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "other-writer", + "expect": { + "history": [ + "multi-v3", + "other-writer", + "multi-v2" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "other-writer", + "multi-v2" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary", + "other-writer.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "other-writer", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "missing-order-requires-resolution", + "steps": [ + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2" + ], + "versions": [ + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [ + "multi-v2.primary" + ] + } + }, + { + "event": "missing-order", + "expect": { + "history": [ + "multi-v2", + "missing-order" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2", + "missing-order" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "missing-order-arrives-first", + "steps": [ + { + "event": "missing-order", + "expect": { + "history": [ + "missing-order" + ], + "versions": [ + "missing-order.primary" + ], + "current": [ + "missing-order.primary" + ], + "conflicts": [], + "visible": [ + "missing-order.primary" + ], + "newlyVisible": [ + "missing-order.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "missing-order", + "multi-v2" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "missing-order", + "multi-v2" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "missing-order", + "expect": { + "history": [ + "missing-order", + "multi-v2" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "missing-order", + "multi-v2" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "missing-order-with-higher-revision-older-competing-newer", + "steps": [ + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2" + ], + "versions": [ + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [ + "multi-v2.primary" + ] + } + }, + { + "event": "missing-order", + "expect": { + "history": [ + "multi-v2", + "missing-order" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "missing-order", + "expect": { + "history": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "missing-order-with-higher-revision-older-newer-competing", + "steps": [ + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2" + ], + "versions": [ + "multi-v2.primary" + ], + "current": [ + "multi-v2.primary" + ], + "conflicts": [], + "visible": [ + "multi-v2.primary" + ], + "newlyVisible": [ + "multi-v2.primary" + ] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "multi-v3" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "missing-order", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "missing-order" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "missing-order" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "missing-order" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "missing-order", + "expect": { + "history": [ + "multi-v2", + "multi-v3", + "missing-order" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "missing-order-with-higher-revision-competing-older-newer", + "steps": [ + { + "event": "missing-order", + "expect": { + "history": [ + "missing-order" + ], + "versions": [ + "missing-order.primary" + ], + "current": [ + "missing-order.primary" + ], + "conflicts": [], + "visible": [ + "missing-order.primary" + ], + "newlyVisible": [ + "missing-order.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "missing-order", + "multi-v2" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "missing-order", + "multi-v2", + "multi-v3" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "missing-order", + "expect": { + "history": [ + "missing-order", + "multi-v2", + "multi-v3" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "missing-order", + "multi-v2", + "multi-v3" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "missing-order", + "multi-v2", + "multi-v3" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "missing-order-with-higher-revision-competing-newer-older", + "steps": [ + { + "event": "missing-order", + "expect": { + "history": [ + "missing-order" + ], + "versions": [ + "missing-order.primary" + ], + "current": [ + "missing-order.primary" + ], + "conflicts": [], + "visible": [ + "missing-order.primary" + ], + "newlyVisible": [ + "missing-order.primary" + ] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "missing-order", + "multi-v3" + ], + "versions": [ + "missing-order.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "missing-order", + "multi-v3", + "multi-v2" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "missing-order", + "expect": { + "history": [ + "missing-order", + "multi-v3", + "multi-v2" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "missing-order", + "multi-v3", + "multi-v2" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "missing-order", + "multi-v3", + "multi-v2" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "missing-order-with-higher-revision-newer-older-competing", + "steps": [ + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3" + ], + "versions": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "multi-v2" + ], + "versions": [ + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [] + } + }, + { + "event": "missing-order", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "missing-order" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "missing-order" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "missing-order" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "missing-order", + "expect": { + "history": [ + "multi-v3", + "multi-v2", + "missing-order" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + }, + { + "id": "missing-order-with-higher-revision-newer-competing-older", + "steps": [ + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3" + ], + "versions": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [ + "multi-v3.primary", + "multi-v3.secondary" + ], + "conflicts": [], + "visible": [ + "multi-v3.primary" + ], + "newlyVisible": [ + "multi-v3.primary" + ] + } + }, + { + "event": "missing-order", + "expect": { + "history": [ + "multi-v3", + "missing-order" + ], + "versions": [ + "missing-order.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "missing-order", + "multi-v2" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v3", + "expect": { + "history": [ + "multi-v3", + "missing-order", + "multi-v2" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "missing-order", + "expect": { + "history": [ + "multi-v3", + "missing-order", + "multi-v2" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + }, + { + "event": "multi-v2", + "expect": { + "history": [ + "multi-v3", + "missing-order", + "multi-v2" + ], + "versions": [ + "missing-order.primary", + "multi-v2.primary", + "multi-v3.primary", + "multi-v3.secondary" + ], + "current": [], + "conflicts": [ + "multi-v2", + "missing-order", + "multi-v3" + ], + "visible": [], + "newlyVisible": [] + } + } + ] + } + ], + "replayWholeSequence": { + "repeat": 1, + "restartBeforeReplay": true, + "preserveFinalState": true, + "newlyVisible": [] + } +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/revision-sequences.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/revision-sequences.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/revision-sequences.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/sequences.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/sequences.json new file mode 100644 index 00000000..c3fe134e --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/sequences.json @@ -0,0 +1,709 @@ +{ + "schemaVersion": 0, + "events": "events.json", + "policy": { + "visiblePurpose": "purpose-a", + "deniedPurposes": [ + "purpose-b" + ], + "scope": "One verified participant and source repository; transport restoration preserves that scope.", + "order": "Compare canonical decimal writer versions only for the same complete writer-record Identifier pair. Neither arrival, event sequence nor issued establishes source revision order.", + "conflict": "Retain incomparable different versions; disclose neither as the current output.", + "retraction": "Retain the logical target tombstone before or after its target arrives. Replay never removes it. Reappearance requires a separate receiver policy and is outside this corpus.", + "references": "An unresolved logical clinical reference keeps the referring output pending and undisclosed. A later target must match its complete Identifier and verified subject/source scope.", + "authorization": "The harness independently authorizes purpose-a for the named output versions only; purpose-b has no authorization. Profile validity and graph readiness never create authorization.", + "arrays": "State arrays are sets of event aliases. Versions/current/visible aliases select the Observation in the named event. A retracted alias identifies its complete source-output Identifier and suppresses every version with that identity, not just that alias. history includes retractions; versions does not. newlyVisible is the access newly made available at this step, not an HTTP receipt code." + }, + "sequences": [ + { + "id": "exact-and-conflicting-replay", + "steps": [ + { + "event": "original", + "expect": { + "history": [ + "original" + ], + "versions": [ + "original" + ], + "current": [ + "original" + ], + "pending": [], + "retracted": [], + "conflicts": [], + "visible": [ + "original" + ], + "newlyVisible": [ + "original" + ], + "references": {} + } + }, + { + "event": "original", + "expect": { + "history": [ + "original" + ], + "versions": [ + "original" + ], + "current": [ + "original" + ], + "pending": [], + "retracted": [], + "conflicts": [], + "visible": [ + "original" + ], + "newlyVisible": [], + "references": {} + } + }, + { + "event": "altered-retry", + "expect": { + "history": [ + "original" + ], + "versions": [ + "original" + ], + "current": [ + "original" + ], + "pending": [], + "retracted": [], + "conflicts": [], + "visible": [ + "original" + ], + "newlyVisible": [], + "references": {} + } + } + ] + }, + { + "id": "reformatted-replay", + "steps": [ + { + "event": "original", + "expect": { + "history": [ + "original" + ], + "versions": [ + "original" + ], + "current": [ + "original" + ], + "pending": [], + "retracted": [], + "conflicts": [], + "visible": [ + "original" + ], + "newlyVisible": [ + "original" + ], + "references": {} + } + }, + { + "event": "reformatted-retry", + "expect": { + "history": [ + "original" + ], + "versions": [ + "original" + ], + "current": [ + "original" + ], + "pending": [], + "retracted": [], + "conflicts": [], + "visible": [ + "original" + ], + "newlyVisible": [], + "references": {} + } + } + ] + }, + { + "id": "lexeme-replay", + "steps": [ + { + "event": "original", + "expect": { + "history": [ + "original" + ], + "versions": [ + "original" + ], + "current": [ + "original" + ], + "pending": [], + "retracted": [], + "conflicts": [], + "visible": [ + "original" + ], + "newlyVisible": [ + "original" + ], + "references": {} + } + }, + { + "event": "lexeme-retry", + "expect": { + "history": [ + "original" + ], + "versions": [ + "original" + ], + "current": [ + "original" + ], + "pending": [], + "retracted": [], + "conflicts": [], + "visible": [ + "original" + ], + "newlyVisible": [], + "references": {} + } + } + ] + }, + { + "id": "correction-before-original", + "steps": [ + { + "event": "corrected", + "expect": { + "history": [ + "corrected" + ], + "versions": [ + "corrected" + ], + "current": [ + "corrected" + ], + "pending": [], + "retracted": [], + "conflicts": [], + "visible": [ + "corrected" + ], + "newlyVisible": [ + "corrected" + ], + "references": {} + } + }, + { + "event": "original", + "expect": { + "history": [ + "corrected", + "original" + ], + "versions": [ + "corrected", + "original" + ], + "current": [ + "corrected" + ], + "pending": [], + "retracted": [], + "conflicts": [], + "visible": [ + "corrected" + ], + "newlyVisible": [], + "references": {} + } + }, + { + "event": "corrected", + "expect": { + "history": [ + "corrected", + "original" + ], + "versions": [ + "corrected", + "original" + ], + "current": [ + "corrected" + ], + "pending": [], + "retracted": [], + "conflicts": [], + "visible": [ + "corrected" + ], + "newlyVisible": [], + "references": {} + } + } + ] + }, + { + "id": "original-before-correction", + "steps": [ + { + "event": "original", + "expect": { + "history": [ + "original" + ], + "versions": [ + "original" + ], + "current": [ + "original" + ], + "pending": [], + "retracted": [], + "conflicts": [], + "visible": [ + "original" + ], + "newlyVisible": [ + "original" + ], + "references": {} + } + }, + { + "event": "corrected", + "expect": { + "history": [ + "original", + "corrected" + ], + "versions": [ + "original", + "corrected" + ], + "current": [ + "corrected" + ], + "pending": [], + "retracted": [], + "conflicts": [], + "visible": [ + "corrected" + ], + "newlyVisible": [ + "corrected" + ], + "references": {} + } + } + ] + }, + { + "id": "unordered-conflict", + "steps": [ + { + "event": "unordered-a", + "expect": { + "history": [ + "unordered-a" + ], + "versions": [ + "unordered-a" + ], + "current": [ + "unordered-a" + ], + "pending": [], + "retracted": [], + "conflicts": [], + "visible": [ + "unordered-a" + ], + "newlyVisible": [ + "unordered-a" + ], + "references": {} + } + }, + { + "event": "unordered-b", + "expect": { + "history": [ + "unordered-a", + "unordered-b" + ], + "versions": [ + "unordered-a", + "unordered-b" + ], + "current": [], + "pending": [], + "retracted": [], + "conflicts": [ + "unordered-a", + "unordered-b" + ], + "visible": [], + "newlyVisible": [], + "references": {} + } + }, + { + "event": "unordered-a", + "expect": { + "history": [ + "unordered-a", + "unordered-b" + ], + "versions": [ + "unordered-a", + "unordered-b" + ], + "current": [], + "pending": [], + "retracted": [], + "conflicts": [ + "unordered-a", + "unordered-b" + ], + "visible": [], + "newlyVisible": [], + "references": {} + } + } + ] + }, + { + "id": "pending-reference-resolution", + "steps": [ + { + "event": "pending", + "expect": { + "history": [ + "pending" + ], + "versions": [ + "pending" + ], + "current": [ + "pending" + ], + "pending": [ + "pending" + ], + "retracted": [], + "conflicts": [], + "visible": [], + "newlyVisible": [], + "references": { + "questionnaire": "pending" + } + } + }, + { + "event": "target", + "expect": { + "history": [ + "pending", + "target" + ], + "versions": [ + "pending", + "target" + ], + "current": [ + "pending", + "target" + ], + "pending": [], + "retracted": [], + "conflicts": [], + "visible": [ + "pending", + "target" + ], + "newlyVisible": [ + "pending", + "target" + ], + "references": { + "questionnaire": "resolved" + } + } + } + ] + }, + { + "id": "retraction-before-target", + "steps": [ + { + "event": "retraction", + "expect": { + "history": [ + "retraction" + ], + "versions": [], + "current": [], + "pending": [], + "retracted": [ + "original" + ], + "conflicts": [], + "visible": [], + "newlyVisible": [], + "references": {} + } + }, + { + "event": "original", + "expect": { + "history": [ + "retraction", + "original" + ], + "versions": [ + "original" + ], + "current": [], + "pending": [], + "retracted": [ + "original" + ], + "conflicts": [], + "visible": [], + "newlyVisible": [], + "references": {} + } + }, + { + "event": "retraction", + "expect": { + "history": [ + "retraction", + "original" + ], + "versions": [ + "original" + ], + "current": [], + "pending": [], + "retracted": [ + "original" + ], + "conflicts": [], + "visible": [], + "newlyVisible": [], + "references": {} + } + }, + { + "event": "original", + "transport": "restored-producer", + "expect": { + "history": [ + "retraction", + "original" + ], + "versions": [ + "original" + ], + "current": [], + "pending": [], + "retracted": [ + "original" + ], + "conflicts": [], + "visible": [], + "newlyVisible": [], + "references": {} + } + } + ] + }, + { + "id": "retraction-after-target", + "steps": [ + { + "event": "original", + "expect": { + "history": [ + "original" + ], + "versions": [ + "original" + ], + "current": [ + "original" + ], + "pending": [], + "retracted": [], + "conflicts": [], + "visible": [ + "original" + ], + "newlyVisible": [ + "original" + ], + "references": {} + } + }, + { + "event": "retraction", + "expect": { + "history": [ + "original", + "retraction" + ], + "versions": [ + "original" + ], + "current": [], + "pending": [], + "retracted": [ + "original" + ], + "conflicts": [], + "visible": [], + "newlyVisible": [], + "references": {} + } + }, + { + "event": "original", + "expect": { + "history": [ + "original", + "retraction" + ], + "versions": [ + "original" + ], + "current": [], + "pending": [], + "retracted": [ + "original" + ], + "conflicts": [], + "visible": [], + "newlyVisible": [], + "references": {} + } + } + ] + }, + { + "id": "correction-before-retraction", + "steps": [ + { + "event": "corrected", + "expect": { + "history": ["corrected"], "versions": ["corrected"], "current": ["corrected"], + "pending": [], "retracted": [], "conflicts": [], "visible": ["corrected"], + "newlyVisible": ["corrected"], "references": {} + } + }, + { + "event": "retraction", + "expect": { + "history": ["corrected", "retraction"], "versions": ["corrected"], "current": [], + "pending": [], "retracted": ["original"], "conflicts": [], "visible": [], + "newlyVisible": [], "references": {} + } + }, + { + "event": "original", + "expect": { + "history": ["corrected", "retraction", "original"], "versions": ["corrected", "original"], "current": [], + "pending": [], "retracted": ["original"], "conflicts": [], "visible": [], + "newlyVisible": [], "references": {} + } + }, + { + "event": "corrected", + "transport": "restored-producer", + "expect": { + "history": ["corrected", "retraction", "original"], "versions": ["corrected", "original"], "current": [], + "pending": [], "retracted": ["original"], "conflicts": [], "visible": [], + "newlyVisible": [], "references": {} + } + } + ] + }, + { + "id": "retraction-before-correction", + "steps": [ + { + "event": "retraction", + "expect": { + "history": ["retraction"], "versions": [], "current": [], + "pending": [], "retracted": ["original"], "conflicts": [], "visible": [], + "newlyVisible": [], "references": {} + } + }, + { + "event": "corrected", + "expect": { + "history": ["retraction", "corrected"], "versions": ["corrected"], "current": [], + "pending": [], "retracted": ["original"], "conflicts": [], "visible": [], + "newlyVisible": [], "references": {} + } + }, + { + "event": "original", + "expect": { + "history": ["retraction", "corrected", "original"], "versions": ["corrected", "original"], "current": [], + "pending": [], "retracted": ["original"], "conflicts": [], "visible": [], + "newlyVisible": [], "references": {} + } + }, + { + "event": "corrected", + "transport": "restored-producer", + "expect": { + "history": ["retraction", "corrected", "original"], "versions": ["corrected", "original"], "current": [], + "pending": [], "retracted": ["original"], "conflicts": [], "visible": [], + "newlyVisible": [], "references": {} + } + } + ] + } + ], + "replayWholeSequence": { + "repeat": 1, + "restartBeforeReplay": true, + "preserveFinalState": true, + "newlyVisible": [] + }, + "logicalReferences": { + "questionnaire": { + "sourceEvent": "pending", + "referencePointer": "/entry/2/resource/derivedFrom/0", + "targetEvent": "target", + "targetPointer": "/entry/4/resource" + } + } +} diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/sequences.json.license b/packages/fhir/fixtures/conformance/receiver-lifecycle/sequences.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/sequences.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json b/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json index e8adbac2..ae989d88 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-active-energy.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:1" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:fff33fbe-2314-590f-b6b1-26c530f6c4ec", + "fullUrl": "urn:uuid:c2ab84a3-0737-5112-85fc-c5cae3ec7e2b", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:cBsbfJ2FTYUguk5OcwFuDMb03zuen7zrG5xlFAniFTo" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:LycACgbJZx-uRos8zb-0w0PEXMdM7LoW4Sx1IvbEOC0" } ], @@ -105,14 +105,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:LycACgbJZx-uRos8zb-0w0PEXMdM7LoW4Sx1IvbEOC0" } } ] }, { - "fullUrl": "urn:uuid:43fca3bd-f88d-5c9f-aee2-c1823335ee58", + "fullUrl": "urn:uuid:92f9e083-c4ee-595f-8646-c75150895f82", "resource": { "resourceType": "Device", "meta": { @@ -130,8 +130,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:NDTrpLTq9kiSi5FYG4ZY37w64LhMOM9ADTH9-v65Ydg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:rsu2RnJwIIxov4D6EdPCfnXsS4Mu1imJ1JsuteDQXnQ" } ], "status": "active", @@ -154,14 +154,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:NDTrpLTq9kiSi5FYG4ZY37w64LhMOM9ADTH9-v65Ydg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:rsu2RnJwIIxov4D6EdPCfnXsS4Mu1imJ1JsuteDQXnQ" } } ] }, { - "fullUrl": "urn:uuid:536d5716-4fb1-5959-baf9-3eac3a86d493", + "fullUrl": "urn:uuid:2b5cbd60-d7a6-56ec-8953-284da5e387f9", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:7IKim6gIJILRiKXQslDAZAYZvUNZ4FjzF038gujYIwg" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:7IKim6gIJILRiKXQslDAZAYZvUNZ4FjzF038gujYIwg" + } + } + ] + }, + { + "fullUrl": "urn:uuid:0d9e6679-bc2d-5d18-866f-e9b162aa7ace", "resource": { "resourceType": "Device", "meta": { @@ -179,8 +237,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:U1GXWdV08ZaO2_cubyu1oaGFDXhnzLx8h_JwZVf6hUU" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:6wGOnwAruJbWSwuz3E9oztleNvGIc5M-tC85oOe6kmY" } ], "status": "active", @@ -203,7 +261,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:2b5cbd60-d7a6-56ec-8953-284da5e387f9" + } }, "extension": [ { @@ -217,14 +278,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:U1GXWdV08ZaO2_cubyu1oaGFDXhnzLx8h_JwZVf6hUU" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:6wGOnwAruJbWSwuz3E9oztleNvGIc5M-tC85oOe6kmY" } } ] }, { - "fullUrl": "urn:uuid:f6732476-2d2b-5b8f-baf8-af76c3e56564", + "fullUrl": "urn:uuid:aa3e7e65-3dc3-5bec-a0ed-1cad0cd018f5", "resource": { "resourceType": "Provenance", "meta": { @@ -233,10 +294,13 @@ ] }, "target": [ - { "reference": "urn:uuid:fff33fbe-2314-590f-b6b1-26c530f6c4ec" } + { "reference": "urn:uuid:c2ab84a3-0737-5112-85fc-c5cae3ec7e2b" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-20T08:00:00.000-07:00", + "end": "2026-08-20T09:00:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -260,7 +324,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:536d5716-4fb1-5959-baf9-3eac3a86d493" + "reference": "urn:uuid:0d9e6679-bc2d-5d18-866f-e9b162aa7ace" } } ], @@ -277,7 +341,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:cBsbfJ2FTYUguk5OcwFuDMb03zuen7zrG5xlFAniFTo" } }, @@ -294,7 +358,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:43fca3bd-f88d-5c9f-aee2-c1823335ee58" + "reference": "urn:uuid:92f9e083-c4ee-595f-8646-c75150895f82" } } ] @@ -313,8 +377,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:JSuiGqhmUZK1zgym0F6SZEhogZ906ZSkQhTZqK76W5s" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:uG-9PimJ24IsJ54PSzd9L-Cxb_1i_PotjCPJQRB5FhM" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json b/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json index ef0be341..4b486585 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-basal-energy.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:2" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:5a6a734e-af3b-51a9-85c0-7823a9e40c9b", + "fullUrl": "urn:uuid:a1c96f25-5d48-54cb-8395-1e26c5c114de", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:J_lqguXT9TuoBwwFhLXLFidICzFsaqScnK0TqMeAB-I" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:bGqlV4JlMEN1EGtpQcfgYP75jVLT5SCtsBfvwHIq8Yw" } ], @@ -105,14 +105,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:bGqlV4JlMEN1EGtpQcfgYP75jVLT5SCtsBfvwHIq8Yw" } } ] }, { - "fullUrl": "urn:uuid:e39f62eb-c867-532e-a955-db7eb7354ad9", + "fullUrl": "urn:uuid:a9e89ad4-0e65-50ea-88db-363086f9b028", "resource": { "resourceType": "Device", "meta": { @@ -130,8 +130,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:W1F_GPOEQhXLa4op2E407dR0glREa1hcySXn8NQqIcc" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:UBwn6_NS47BhpMO3aHonRRJF4ga0P_Kw9GsyzsT27EQ" } ], "status": "active", @@ -154,14 +154,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:W1F_GPOEQhXLa4op2E407dR0glREa1hcySXn8NQqIcc" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:UBwn6_NS47BhpMO3aHonRRJF4ga0P_Kw9GsyzsT27EQ" } } ] }, { - "fullUrl": "urn:uuid:ca0593ae-cc72-5870-93e6-799df283ea91", + "fullUrl": "urn:uuid:1bb8a529-6e5b-5c6d-a053-6116182f6414", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:0gyYYYoMtelFyIUw_bXqwlS11ZMjyNObbDKWIxg_nFA" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:0gyYYYoMtelFyIUw_bXqwlS11ZMjyNObbDKWIxg_nFA" + } + } + ] + }, + { + "fullUrl": "urn:uuid:53d78417-5837-5f83-b43c-ffe75eae8e30", "resource": { "resourceType": "Device", "meta": { @@ -179,8 +237,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:u5TL9xXam0QJkWk5kHlvukPvutOg61R5brwRjRupWoY" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:jhGOKGZJ21o9pmIrE7wd_uPI6ou5eEcDidoIfpblykQ" } ], "status": "active", @@ -203,7 +261,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:1bb8a529-6e5b-5c6d-a053-6116182f6414" + } }, "extension": [ { @@ -217,14 +278,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:u5TL9xXam0QJkWk5kHlvukPvutOg61R5brwRjRupWoY" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:jhGOKGZJ21o9pmIrE7wd_uPI6ou5eEcDidoIfpblykQ" } } ] }, { - "fullUrl": "urn:uuid:02d8d13a-725f-5a98-8adb-7bdbe1d2c8ac", + "fullUrl": "urn:uuid:ad4446b2-f8a8-54a9-ae4e-a3d609139466", "resource": { "resourceType": "Provenance", "meta": { @@ -233,10 +294,13 @@ ] }, "target": [ - { "reference": "urn:uuid:5a6a734e-af3b-51a9-85c0-7823a9e40c9b" } + { "reference": "urn:uuid:a1c96f25-5d48-54cb-8395-1e26c5c114de" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-20T08:00:00.000-07:00", + "end": "2026-08-20T09:00:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -260,7 +324,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:ca0593ae-cc72-5870-93e6-799df283ea91" + "reference": "urn:uuid:53d78417-5837-5f83-b43c-ffe75eae8e30" } } ], @@ -277,7 +341,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:J_lqguXT9TuoBwwFhLXLFidICzFsaqScnK0TqMeAB-I" } }, @@ -294,7 +358,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:e39f62eb-c867-532e-a955-db7eb7354ad9" + "reference": "urn:uuid:a9e89ad4-0e65-50ea-88db-363086f9b028" } } ] @@ -313,8 +377,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:bPr9qu85WDJRHQ_RIPRZRurly-o77lEQL6RYw1bsNog" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:nm7r_5-i5xrnxXHAkGCqkdAOHuyUK0kcKSfPtCZ6cn4" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json b/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json index 54833ff7..97bb5f7e 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-blood-glucose-unspecified-specimen.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:3" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:26835628-7045-5579-8509-ced6196c4250", + "fullUrl": "urn:uuid:1333db20-94dd-59a0-81d3-45df4b89eb59", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:27EvW0bT37TNTuvwrW8l4xOSfYIZ4S1ve49v2O8NLmc" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:sIHAUnrc78LCCIylboGtuD4FkMHZ5IcR9_DscGIbDZA" } ], @@ -97,14 +97,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:sIHAUnrc78LCCIylboGtuD4FkMHZ5IcR9_DscGIbDZA" } } ] }, { - "fullUrl": "urn:uuid:75eef62c-12cb-597b-b50b-e7bfc7492aa9", + "fullUrl": "urn:uuid:891cd8a8-8533-50dd-9997-47d221650402", "resource": { "resourceType": "Device", "meta": { @@ -122,8 +122,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:xoJ9_x4-WlVdj2qnI_cuEu_h-kqCU_k3oJypbqVKAu0" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:reEZGwhugn9QBgi81CEzFu1rw4Em_Jh_CBt0k3pQTts" } ], "status": "active", @@ -146,14 +146,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:xoJ9_x4-WlVdj2qnI_cuEu_h-kqCU_k3oJypbqVKAu0" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:reEZGwhugn9QBgi81CEzFu1rw4Em_Jh_CBt0k3pQTts" } } ] }, { - "fullUrl": "urn:uuid:23ee24a9-6a50-50db-aa09-b10818b5c5d9", + "fullUrl": "urn:uuid:602995cf-af75-579c-824f-fd4996b52ed9", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:o-URuOWfOzVNPYugPK7SDhAFmoy1kj3iATgDnvHr4Zw" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:o-URuOWfOzVNPYugPK7SDhAFmoy1kj3iATgDnvHr4Zw" + } + } + ] + }, + { + "fullUrl": "urn:uuid:574690f1-b11f-558b-9ec1-f9b36c471116", "resource": { "resourceType": "Device", "meta": { @@ -171,8 +229,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uZjSlR8HhG3r4orDE1tamL-7ADdF63SWpfVP3gi9PFk" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:os90HUPA9Fzq-jI5TI4XRfGFK6XrcwsG975Wzz3_0CI" } ], "status": "active", @@ -195,7 +253,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:602995cf-af75-579c-824f-fd4996b52ed9" + } }, "extension": [ { @@ -209,14 +270,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uZjSlR8HhG3r4orDE1tamL-7ADdF63SWpfVP3gi9PFk" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:os90HUPA9Fzq-jI5TI4XRfGFK6XrcwsG975Wzz3_0CI" } } ] }, { - "fullUrl": "urn:uuid:68e61fb6-a6d4-5a6e-9b53-e4805ae0cad0", + "fullUrl": "urn:uuid:ef0de0bc-9948-5886-be97-12a9c5cc50e3", "resource": { "resourceType": "Provenance", "meta": { @@ -225,10 +286,10 @@ ] }, "target": [ - { "reference": "urn:uuid:26835628-7045-5579-8509-ced6196c4250" } + { "reference": "urn:uuid:1333db20-94dd-59a0-81d3-45df4b89eb59" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T07:45:00.120-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -252,7 +313,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:23ee24a9-6a50-50db-aa09-b10818b5c5d9" + "reference": "urn:uuid:574690f1-b11f-558b-9ec1-f9b36c471116" } } ], @@ -269,7 +330,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:27EvW0bT37TNTuvwrW8l4xOSfYIZ4S1ve49v2O8NLmc" } }, @@ -286,7 +347,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:75eef62c-12cb-597b-b50b-e7bfc7492aa9" + "reference": "urn:uuid:891cd8a8-8533-50dd-9997-47d221650402" } } ] @@ -305,8 +366,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:sHgw9yxq8eUbRbaTd3vKq88xsxm3beKvqRPkO5x4og8" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:u4Kh_4H3fM-5Ho59AAG5JyoiupRF8_VL6sNLDFgv6Os" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json b/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json index e012eeac..3951c654 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-blood-pressure.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:4" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:d0c0a2a4-8c81-5907-96ad-834c557418b0", + "fullUrl": "urn:uuid:8f9f27cf-6b81-525d-ac58-8e60cffb597d", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:WuyZHg4lYDvE4FhjSBqArtkRJOGxBtiM7vDOOT9R98o" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:h_61-KWtwbgmorpjpV-hiBahMW9-fvRxBFWS04MHpZE" } ], @@ -127,14 +127,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:h_61-KWtwbgmorpjpV-hiBahMW9-fvRxBFWS04MHpZE" } } ] }, { - "fullUrl": "urn:uuid:5458fdad-053a-55ae-a659-ef8cba4cc18f", + "fullUrl": "urn:uuid:16d464a3-b38b-5dbe-ae37-86b885d34e1b", "resource": { "resourceType": "Device", "meta": { @@ -152,8 +152,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:7EV1uB106bpRYwJR9HZKAN1rb0oq6FZj9UsFj44FATU" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:tuA4jMIvZV9Q5IvoLVfVjyzPHZK-Rku57MszgethJfo" } ], "status": "active", @@ -176,14 +176,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:7EV1uB106bpRYwJR9HZKAN1rb0oq6FZj9UsFj44FATU" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:tuA4jMIvZV9Q5IvoLVfVjyzPHZK-Rku57MszgethJfo" } } ] }, { - "fullUrl": "urn:uuid:6a8866f4-f3eb-5f62-b099-17c43cbe4d40", + "fullUrl": "urn:uuid:00c370ca-1cb2-58ce-a661-443db070fac3", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ynCIE1EIz0OCKp2cm-uPwhS-kHnmJMqE9KJ-QC4Ygk4" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ynCIE1EIz0OCKp2cm-uPwhS-kHnmJMqE9KJ-QC4Ygk4" + } + } + ] + }, + { + "fullUrl": "urn:uuid:e8098a60-1468-5e0d-a13a-b6983521c54f", "resource": { "resourceType": "Device", "meta": { @@ -201,8 +259,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:StGlLYOvF2jMhvbe3CuauF8rp7vuRc6brEJfxxgE0Kg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:4ifzUtd1rmPdPxiwSpOQTQCRbojJf6I1Td6QR1FTvN8" } ], "status": "active", @@ -225,7 +283,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:00c370ca-1cb2-58ce-a661-443db070fac3" + } }, "extension": [ { @@ -239,14 +300,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:StGlLYOvF2jMhvbe3CuauF8rp7vuRc6brEJfxxgE0Kg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:4ifzUtd1rmPdPxiwSpOQTQCRbojJf6I1Td6QR1FTvN8" } } ] }, { - "fullUrl": "urn:uuid:e643c8be-4880-58e2-a645-64479de7bd05", + "fullUrl": "urn:uuid:acabc31e-7fec-53a7-9056-fad1389e51d8", "resource": { "resourceType": "Provenance", "meta": { @@ -255,10 +316,10 @@ ] }, "target": [ - { "reference": "urn:uuid:d0c0a2a4-8c81-5907-96ad-834c557418b0" } + { "reference": "urn:uuid:8f9f27cf-6b81-525d-ac58-8e60cffb597d" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T08:10:00.000-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -282,7 +343,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:6a8866f4-f3eb-5f62-b099-17c43cbe4d40" + "reference": "urn:uuid:e8098a60-1468-5e0d-a13a-b6983521c54f" } } ], @@ -299,7 +360,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:WuyZHg4lYDvE4FhjSBqArtkRJOGxBtiM7vDOOT9R98o" } }, @@ -316,7 +377,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:5458fdad-053a-55ae-a659-ef8cba4cc18f" + "reference": "urn:uuid:16d464a3-b38b-5dbe-ae37-86b885d34e1b" } } ] @@ -335,8 +396,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:HSbFZAF1J0Z2AiQllTCYg47pEddf3_bqHBCtM_kKHTc" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:J7oBxjP7xkUfbmy5QaUKMH801KLipy4WEyBdMdpCJdc" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json b/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json index 41004b73..ca658861 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-fat-percentage.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:5" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:2d4505ad-24d1-53b0-b8bc-ba6c0021bd20", + "fullUrl": "urn:uuid:f2f6bbae-e3e9-58ef-a223-bbd0dd1ea119", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:WxoGom7ZNS31zsgBLsLtuMnFQ-Zb2PV_UbLkLzxlVwE" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:CKv-2R1W4otUFhAE3dZ_GJwX4LjRSCYU12bc8TFxhu0" } ], @@ -97,14 +97,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:CKv-2R1W4otUFhAE3dZ_GJwX4LjRSCYU12bc8TFxhu0" } } ] }, { - "fullUrl": "urn:uuid:47afd496-5885-5729-816d-fcbce4f015bb", + "fullUrl": "urn:uuid:70f1f35b-1ecc-5aa9-a7ee-5246329f2f49", "resource": { "resourceType": "Device", "meta": { @@ -122,8 +122,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:jFa4dFMQh0ReZZ1PAqwe-uI9UcTq5j2IlwWALGF5Vs8" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:qFMz-l4HLJNV_SAVXHS9GXPNzqgCc_ypTA-wWloWPAo" } ], "status": "active", @@ -146,14 +146,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:jFa4dFMQh0ReZZ1PAqwe-uI9UcTq5j2IlwWALGF5Vs8" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:qFMz-l4HLJNV_SAVXHS9GXPNzqgCc_ypTA-wWloWPAo" } } ] }, { - "fullUrl": "urn:uuid:7dca5c46-8cbe-5996-a7dc-e1d38cc12bf4", + "fullUrl": "urn:uuid:29c42d91-ddcc-5306-b5f5-7af85f1b71ea", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:e-G4Y1LO_Q_hdvR0xDKVBoAPRs9aCRwJWzC5Vvudu54" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:e-G4Y1LO_Q_hdvR0xDKVBoAPRs9aCRwJWzC5Vvudu54" + } + } + ] + }, + { + "fullUrl": "urn:uuid:75ae61f0-6f16-52ce-87f5-27f7a69d23d8", "resource": { "resourceType": "Device", "meta": { @@ -171,8 +229,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:dMnfWEQk2xChFyh1BU6bUz9-invN_CJx4a7K5VJwnoA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:a7bNAQcF4fyoAfJVyeEm5fFCurOzDAzmOsU1B4rI_DY" } ], "status": "active", @@ -195,7 +253,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:29c42d91-ddcc-5306-b5f5-7af85f1b71ea" + } }, "extension": [ { @@ -209,14 +270,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:dMnfWEQk2xChFyh1BU6bUz9-invN_CJx4a7K5VJwnoA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:a7bNAQcF4fyoAfJVyeEm5fFCurOzDAzmOsU1B4rI_DY" } } ] }, { - "fullUrl": "urn:uuid:8488a61c-011b-5ba8-8cf6-0efe011d95b5", + "fullUrl": "urn:uuid:1cc24aaf-88b1-508b-8695-31ce695bb7f2", "resource": { "resourceType": "Provenance", "meta": { @@ -225,10 +286,10 @@ ] }, "target": [ - { "reference": "urn:uuid:2d4505ad-24d1-53b0-b8bc-ba6c0021bd20" } + { "reference": "urn:uuid:f2f6bbae-e3e9-58ef-a223-bbd0dd1ea119" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-21T07:12:04.000-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -252,7 +313,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:7dca5c46-8cbe-5996-a7dc-e1d38cc12bf4" + "reference": "urn:uuid:75ae61f0-6f16-52ce-87f5-27f7a69d23d8" } } ], @@ -269,7 +330,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:WxoGom7ZNS31zsgBLsLtuMnFQ-Zb2PV_UbLkLzxlVwE" } }, @@ -286,7 +347,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:47afd496-5885-5729-816d-fcbce4f015bb" + "reference": "urn:uuid:70f1f35b-1ecc-5aa9-a7ee-5246329f2f49" } } ] @@ -305,8 +366,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:JXf4d0INS6AhdsWU91DRhrSqJ2KDrn6Owd6DutdAz7s" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:2PXoXhyS1bO1Z7HiKhfDLEXWyAND_K1LDDCGHItfDU8" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-height.json b/packages/fhir/fixtures/conformance/resources/mobile-body-height.json index 12b95695..66db9cd0 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-height.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-height.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:6" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:9e59de7e-0518-5df2-ac5e-54685bd09a8a", + "fullUrl": "urn:uuid:bbb0254b-5832-5605-9105-a65fb13baa9b", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:i73_ClycuTs0StcJTYSAMPNYRgSA_kzfiagjX50PQMQ" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:lS0QSYana6P-mPdj0GPjtR0-h3xqSULeep0f1ocR0Q4" } ], @@ -109,14 +109,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:lS0QSYana6P-mPdj0GPjtR0-h3xqSULeep0f1ocR0Q4" } } ] }, { - "fullUrl": "urn:uuid:c7fdc3f1-5bba-524a-93df-19600a63219a", + "fullUrl": "urn:uuid:9599e893-ec4f-5ad9-b3f4-535248b5162f", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +134,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:nkrVMmK5Z25j6D3O4YFbpTz9gcI7nYUYvMMhJgWqk2I" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:F6s07DDmog9Eq61XnLxGLVvh0xjSlQAweKuV1QNgrho" } ], "status": "active", @@ -158,14 +158,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:nkrVMmK5Z25j6D3O4YFbpTz9gcI7nYUYvMMhJgWqk2I" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:F6s07DDmog9Eq61XnLxGLVvh0xjSlQAweKuV1QNgrho" } } ] }, { - "fullUrl": "urn:uuid:559b0b4e-5460-5ab7-9a53-c3267ecaa7af", + "fullUrl": "urn:uuid:d3f8e9c1-4bdc-5ad3-a846-4095126a8ba5", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:5OMRr-Tg9Wl4JFqWYg1S8uZEvquz19fV6yK0cA3CM_o" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:5OMRr-Tg9Wl4JFqWYg1S8uZEvquz19fV6yK0cA3CM_o" + } + } + ] + }, + { + "fullUrl": "urn:uuid:15c27eb0-e927-5097-bc3e-67ea1dae02c3", "resource": { "resourceType": "Device", "meta": { @@ -183,8 +241,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:JE_Aqlhn9MnxX9wx4t8kJfLES5YcXywYLTzTB97A-Ec" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:JaFn-38ti6ZhrX9XuN_9KZMhJMo0IyECr17G68s_qFw" } ], "status": "active", @@ -207,7 +265,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:d3f8e9c1-4bdc-5ad3-a846-4095126a8ba5" + } }, "extension": [ { @@ -221,14 +282,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:JE_Aqlhn9MnxX9wx4t8kJfLES5YcXywYLTzTB97A-Ec" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:JaFn-38ti6ZhrX9XuN_9KZMhJMo0IyECr17G68s_qFw" } } ] }, { - "fullUrl": "urn:uuid:f9680bea-996f-5f05-aefa-758aaece4bf1", + "fullUrl": "urn:uuid:0d17e52a-bd11-51bc-a0b9-3c3775325c8f", "resource": { "resourceType": "Provenance", "meta": { @@ -237,10 +298,10 @@ ] }, "target": [ - { "reference": "urn:uuid:9e59de7e-0518-5df2-ac5e-54685bd09a8a" } + { "reference": "urn:uuid:bbb0254b-5832-5605-9105-a65fb13baa9b" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T08:15:00.000-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -264,7 +325,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:559b0b4e-5460-5ab7-9a53-c3267ecaa7af" + "reference": "urn:uuid:15c27eb0-e927-5097-bc3e-67ea1dae02c3" } } ], @@ -281,7 +342,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:i73_ClycuTs0StcJTYSAMPNYRgSA_kzfiagjX50PQMQ" } }, @@ -298,7 +359,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:c7fdc3f1-5bba-524a-93df-19600a63219a" + "reference": "urn:uuid:9599e893-ec4f-5ad9-b3f4-535248b5162f" } } ] @@ -317,8 +378,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:xznW5Dtyrp_tkOdtvAdJ4XrJLURFAb463IsGE_0Fz6o" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:2ejph8IUiQk-duhs2dIJiDoONW98qnySFkZbelA_JA8" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json b/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json index 5a0f53d5..b60e0cf8 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-temperature.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:7" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:991342d3-ad32-5672-a092-cea731aa446d", + "fullUrl": "urn:uuid:b170c4c7-e2b2-5a85-8920-22cc6032541a", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:kglrsTbI-sisN9jpTRtdE0mf3W_iLOyNBWx6GQ54xQg" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:0xwJuyM9Oj7kD9ci-wffqZeinUCEg_Aisu4THXZqzu0" } ], @@ -109,14 +109,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:0xwJuyM9Oj7kD9ci-wffqZeinUCEg_Aisu4THXZqzu0" } } ] }, { - "fullUrl": "urn:uuid:def97c74-f47d-54ab-a249-d81dd508ea0c", + "fullUrl": "urn:uuid:1f6cb6be-2e78-54cb-9bb2-e2335777bf26", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +134,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:H2r0u1o9Dno6Uy4_UDnq2S31gXiT9aTLQ6W9BR3RA9E" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:58--UzfRZeBfO3blNRzXZHJGJ_MEs78Tg9v8hBWI-NM" } ], "status": "active", @@ -158,14 +158,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:H2r0u1o9Dno6Uy4_UDnq2S31gXiT9aTLQ6W9BR3RA9E" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:58--UzfRZeBfO3blNRzXZHJGJ_MEs78Tg9v8hBWI-NM" } } ] }, { - "fullUrl": "urn:uuid:b4924f51-3fc2-590f-9659-21e430b4df06", + "fullUrl": "urn:uuid:8898345d-2dd7-5a7b-8411-14e277781a4f", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:hzt_MSrnaLKlRnLNmiwsegwKdGHNeTzeKHtrGHDqzYk" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:hzt_MSrnaLKlRnLNmiwsegwKdGHNeTzeKHtrGHDqzYk" + } + } + ] + }, + { + "fullUrl": "urn:uuid:1ffc2866-e769-5f05-b9d0-9c7dd814d713", "resource": { "resourceType": "Device", "meta": { @@ -183,8 +241,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:FtVDXLqRlsdnWhRUEUCd9tzd5DUdLpPXw0X7QM0rBhI" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:vALRM3D06u3-t687cnPW6_PLCf_mQ01BsRDH9HbgZF8" } ], "status": "active", @@ -207,7 +265,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:8898345d-2dd7-5a7b-8411-14e277781a4f" + } }, "extension": [ { @@ -221,14 +282,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:FtVDXLqRlsdnWhRUEUCd9tzd5DUdLpPXw0X7QM0rBhI" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:vALRM3D06u3-t687cnPW6_PLCf_mQ01BsRDH9HbgZF8" } } ] }, { - "fullUrl": "urn:uuid:076db598-eef8-518e-9b1c-d5cbdb574d39", + "fullUrl": "urn:uuid:a0e16521-0cd2-50ce-b82b-49650c416231", "resource": { "resourceType": "Provenance", "meta": { @@ -237,10 +298,10 @@ ] }, "target": [ - { "reference": "urn:uuid:991342d3-ad32-5672-a092-cea731aa446d" } + { "reference": "urn:uuid:b170c4c7-e2b2-5a85-8920-22cc6032541a" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T08:20:00.000-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -264,7 +325,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:b4924f51-3fc2-590f-9659-21e430b4df06" + "reference": "urn:uuid:1ffc2866-e769-5f05-b9d0-9c7dd814d713" } } ], @@ -281,7 +342,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:kglrsTbI-sisN9jpTRtdE0mf3W_iLOyNBWx6GQ54xQg" } }, @@ -298,7 +359,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:def97c74-f47d-54ab-a249-d81dd508ea0c" + "reference": "urn:uuid:1f6cb6be-2e78-54cb-9bb2-e2335777bf26" } } ] @@ -317,8 +378,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:-rZfHFRZQvg1O-TQfGbqlkvRpL2apKTfsgrv3ezhgmU" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:35K3-tct8nxTt5ETA8frMH5BeNEwubGr2CcxtCK87QE" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json b/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json index 7712d01d..190ac942 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-water-mass.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:8" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:011209c1-efd2-5615-b48d-7267fb8f587d", + "fullUrl": "urn:uuid:70deb71c-63be-5fdc-b014-7db23ed31c7f", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:8BGOdrODZOHaIi5_vcptrhCvIp9pXLGBex92mcxbFWc" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:KcKSTEKCMUmSLMXdnqW-kAJHPCGqCtRhmZ5hP5-F58k" } ], @@ -97,14 +97,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:KcKSTEKCMUmSLMXdnqW-kAJHPCGqCtRhmZ5hP5-F58k" } } ] }, { - "fullUrl": "urn:uuid:a2b1a3b7-2aa7-539c-a900-f1e0a8345a13", + "fullUrl": "urn:uuid:742c122f-4419-5bcd-88e9-cdf2332a751d", "resource": { "resourceType": "Device", "meta": { @@ -122,8 +122,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:KBU_fiP09JGvryRvEZWtIf1i90yfqC_o6ewWCsBvTSc" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:_N6DrRezMErGOLvKnRW_w8VNL73GvVI7HNWUJtSoGOg" } ], "status": "active", @@ -146,14 +146,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:KBU_fiP09JGvryRvEZWtIf1i90yfqC_o6ewWCsBvTSc" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:_N6DrRezMErGOLvKnRW_w8VNL73GvVI7HNWUJtSoGOg" } } ] }, { - "fullUrl": "urn:uuid:c0960bc7-f32d-5bc3-a7a4-09bb09504534", + "fullUrl": "urn:uuid:a39e6184-7d52-5308-8148-0e7355635e54", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:gCWuz_h9htvXzzpTYi6-quVDSGrB7vWRTIdznnGnCaQ" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:gCWuz_h9htvXzzpTYi6-quVDSGrB7vWRTIdznnGnCaQ" + } + } + ] + }, + { + "fullUrl": "urn:uuid:06366803-ef63-58e5-bf74-cb5043c2bf50", "resource": { "resourceType": "Device", "meta": { @@ -171,8 +229,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:kwYq49ze78zEF1E8fXhHPgjMxt1ovZafXb6h2T0x-yk" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ki54XNJR1ZDDag_jNuEm83nNFTsIE8ies_xWjPNFHmY" } ], "status": "active", @@ -195,7 +253,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:a39e6184-7d52-5308-8148-0e7355635e54" + } }, "extension": [ { @@ -209,14 +270,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:kwYq49ze78zEF1E8fXhHPgjMxt1ovZafXb6h2T0x-yk" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ki54XNJR1ZDDag_jNuEm83nNFTsIE8ies_xWjPNFHmY" } } ] }, { - "fullUrl": "urn:uuid:b82881c7-a010-519e-af56-703c8f328f5e", + "fullUrl": "urn:uuid:64da9ad3-7b45-5cba-a663-c7212e610af8", "resource": { "resourceType": "Provenance", "meta": { @@ -225,10 +286,10 @@ ] }, "target": [ - { "reference": "urn:uuid:011209c1-efd2-5615-b48d-7267fb8f587d" } + { "reference": "urn:uuid:70deb71c-63be-5fdc-b014-7db23ed31c7f" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-21T07:12:04.000-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -252,7 +313,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:c0960bc7-f32d-5bc3-a7a4-09bb09504534" + "reference": "urn:uuid:06366803-ef63-58e5-bf74-cb5043c2bf50" } } ], @@ -269,7 +330,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:8BGOdrODZOHaIi5_vcptrhCvIp9pXLGBex92mcxbFWc" } }, @@ -286,7 +347,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:a2b1a3b7-2aa7-539c-a900-f1e0a8345a13" + "reference": "urn:uuid:742c122f-4419-5bcd-88e9-cdf2332a751d" } } ] @@ -305,8 +366,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:ImvOlb_AJ5BnIOM2MfGr9duCtXlQo6JDZBERq3S1vD8" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:oQIeOCe4Mqwn7AZaYMlfsFCP5j0_CNPjAnUeKUz8aEY" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json b/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json index a9bc864c..3ad4b587 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-body-weight.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:9" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:21266e9b-0e9c-5b9f-b625-37d12c3fd1b7", + "fullUrl": "urn:uuid:893d4d97-265f-5e44-ad94-f4320c8e957b", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:IBIE7eiXHQUPEZS7lzzC8j4DhYMU186jXgbefxDYR40" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:ikY1sCJq7ZCjIGDZ-WA0UvDbDbFi8qfkM_PYHXagJbQ" } ], @@ -109,14 +109,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:ikY1sCJq7ZCjIGDZ-WA0UvDbDbFi8qfkM_PYHXagJbQ" } } ] }, { - "fullUrl": "urn:uuid:12813d7d-be61-5da2-af9e-5fba50ebe4ba", + "fullUrl": "urn:uuid:7abdfe1a-2bf9-5f62-a003-1c789d8edc1d", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +134,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:4fwTGS-w5KNNRuvXElRWQ0k1wmPyVsywbcoR7vuHvUo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:IPWrn43KEXerh0GjvSPO5UJvB__pHAeuQrKyI6ntt-k" } ], "status": "active", @@ -158,14 +158,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:4fwTGS-w5KNNRuvXElRWQ0k1wmPyVsywbcoR7vuHvUo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:IPWrn43KEXerh0GjvSPO5UJvB__pHAeuQrKyI6ntt-k" } } ] }, { - "fullUrl": "urn:uuid:d1f4eef8-89b3-5952-866a-998a0a0e5bad", + "fullUrl": "urn:uuid:b4f26780-fe93-5261-b7d9-bc2701cdd722", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:zxUMlK-ga_jlmD_60EKKDFVN6CmvxfQjywUme_7DJrE" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:zxUMlK-ga_jlmD_60EKKDFVN6CmvxfQjywUme_7DJrE" + } + } + ] + }, + { + "fullUrl": "urn:uuid:e3791049-9145-5346-93e1-e52fe931a3d5", "resource": { "resourceType": "Device", "meta": { @@ -183,8 +241,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Kt2LgM4ys7hckDgWMZuwshW4x_uSmcPjB8XB5Howui0" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:3gmDr0fAY8oAxeciAqsjksxlmXNKcsod7UzWoPYItLY" } ], "status": "active", @@ -207,7 +265,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:b4f26780-fe93-5261-b7d9-bc2701cdd722" + } }, "extension": [ { @@ -221,14 +282,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Kt2LgM4ys7hckDgWMZuwshW4x_uSmcPjB8XB5Howui0" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:3gmDr0fAY8oAxeciAqsjksxlmXNKcsod7UzWoPYItLY" } } ] }, { - "fullUrl": "urn:uuid:a2c7aae9-1014-5b67-bc48-26b7a4cb65a0", + "fullUrl": "urn:uuid:56e5ebd2-3352-5e01-b4b2-c671af2adc38", "resource": { "resourceType": "Provenance", "meta": { @@ -237,10 +298,10 @@ ] }, "target": [ - { "reference": "urn:uuid:21266e9b-0e9c-5b9f-b625-37d12c3fd1b7" } + { "reference": "urn:uuid:893d4d97-265f-5e44-ad94-f4320c8e957b" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T08:25:00.000-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -264,7 +325,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:d1f4eef8-89b3-5952-866a-998a0a0e5bad" + "reference": "urn:uuid:e3791049-9145-5346-93e1-e52fe931a3d5" } } ], @@ -281,7 +342,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:IBIE7eiXHQUPEZS7lzzC8j4DhYMU186jXgbefxDYR40" } }, @@ -298,7 +359,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:12813d7d-be61-5da2-af9e-5fba50ebe4ba" + "reference": "urn:uuid:7abdfe1a-2bf9-5f62-a003-1c789d8edc1d" } } ] @@ -317,8 +378,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:9Ik9SMiyAYvhRBM566jFJU2Z99kwPJO9hZ7gCOYA-_k" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:eDJRnTvataHp0DVJgE77kN0GEWWHKii_7iHyACHbds8" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json b/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json index 54cd618e..eee2b498 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-bone-mass.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:10" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:6b5ea6d7-2c92-5510-ba1f-f995de70bd27", + "fullUrl": "urn:uuid:c8dd3322-43ef-53d7-bdee-d50d02699b95", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:nviR_nQRpK91BIeM0wRmRPO1IKJpSZAqx980Bj4ZE50" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:aBvPRliVxlOZKqhr6Q3Jyj9MM77CD8lEczzB0vw4PtA" } ], @@ -97,14 +97,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:aBvPRliVxlOZKqhr6Q3Jyj9MM77CD8lEczzB0vw4PtA" } } ] }, { - "fullUrl": "urn:uuid:4609626d-627d-54bb-8e80-a0694b386d9c", + "fullUrl": "urn:uuid:2ee5e5e6-8544-5f68-8b89-10a2d474aec4", "resource": { "resourceType": "Device", "meta": { @@ -122,8 +122,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:9-IltYX5VPvJ6JIKK6WfmZ-NRa4b5lONWt0QPWIAm48" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:UN6UPgOucyqbCN5BK1BH1ouTaVsdrUnxQ5A9n9a-FYA" } ], "status": "active", @@ -146,14 +146,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:9-IltYX5VPvJ6JIKK6WfmZ-NRa4b5lONWt0QPWIAm48" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:UN6UPgOucyqbCN5BK1BH1ouTaVsdrUnxQ5A9n9a-FYA" } } ] }, { - "fullUrl": "urn:uuid:af05eabe-bdae-5fc1-8515-8632dc115687", + "fullUrl": "urn:uuid:ace0bd7d-3bd0-5d0d-9884-1aaef23f3742", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:s5SUCrx8txI6tgDzYM9GUC3h0RKAOu3ZAc6QsSJWQcM" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:s5SUCrx8txI6tgDzYM9GUC3h0RKAOu3ZAc6QsSJWQcM" + } + } + ] + }, + { + "fullUrl": "urn:uuid:62016ce6-7204-5b33-b829-b5505d44bb33", "resource": { "resourceType": "Device", "meta": { @@ -171,8 +229,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:HyentO5mz54bN1oU8dwpNpGIwjGaJlkj7Ujg1HvVy3I" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:gcidk_zQ3KxYfQAW1dGUOY4jyAc1K1MlYEpldXzUKTY" } ], "status": "active", @@ -195,7 +253,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:ace0bd7d-3bd0-5d0d-9884-1aaef23f3742" + } }, "extension": [ { @@ -209,14 +270,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:HyentO5mz54bN1oU8dwpNpGIwjGaJlkj7Ujg1HvVy3I" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:gcidk_zQ3KxYfQAW1dGUOY4jyAc1K1MlYEpldXzUKTY" } } ] }, { - "fullUrl": "urn:uuid:55a5abc3-e3e8-5469-892c-8d7c91c09f05", + "fullUrl": "urn:uuid:e77906b5-6879-5d21-9f80-77eeebc5797c", "resource": { "resourceType": "Provenance", "meta": { @@ -225,10 +286,10 @@ ] }, "target": [ - { "reference": "urn:uuid:6b5ea6d7-2c92-5510-ba1f-f995de70bd27" } + { "reference": "urn:uuid:c8dd3322-43ef-53d7-bdee-d50d02699b95" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-21T07:12:04.000-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -252,7 +313,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:af05eabe-bdae-5fc1-8515-8632dc115687" + "reference": "urn:uuid:62016ce6-7204-5b33-b829-b5505d44bb33" } } ], @@ -269,7 +330,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:nviR_nQRpK91BIeM0wRmRPO1IKJpSZAqx980Bj4ZE50" } }, @@ -286,7 +347,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:4609626d-627d-54bb-8e80-a0694b386d9c" + "reference": "urn:uuid:2ee5e5e6-8544-5f68-8b89-10a2d474aec4" } } ] @@ -305,8 +366,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:SSey1ttqPqMR4AflrsB5eKNcr-DhZ0z9Sdkg_Z5gvE0" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:6_DPjFvrJPaD9hYLvOoYYV6hsFpMzlZ7Fp0ALg3WC_g" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json index 09736721..fff21e09 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-deep-sleep-duration.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:11" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:8f50510c-d9e1-55e0-968f-d468f650a2bf", + "fullUrl": "urn:uuid:abf190b6-2af4-53dd-89e0-a450ad0d3234", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:TCynCJxssAt4DKR6-a8p8A25iLlU9LB_s1EPaePLqKU" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:Xnf3G1rIoAI-q4QVXBBvPkehNiAdCYRR4mbLvZkF0eE" } ], @@ -110,14 +110,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:Xnf3G1rIoAI-q4QVXBBvPkehNiAdCYRR4mbLvZkF0eE" } } ] }, { - "fullUrl": "urn:uuid:618ecd91-ad82-57b9-9c0f-b6ea3b69a76b", + "fullUrl": "urn:uuid:4c5c7f12-b643-5f12-90e7-7925d4b21153", "resource": { "resourceType": "Device", "meta": { @@ -135,8 +135,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:zjMgtZF1_WNxGQz_vUBTVxdogh6EcWgF0fnJ1y7dVow" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:0zVUOSnD6e3ttwp3GIlUO042VnTyy0ivSfVvo4n0xrw" } ], "status": "active", @@ -159,14 +159,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:zjMgtZF1_WNxGQz_vUBTVxdogh6EcWgF0fnJ1y7dVow" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:0zVUOSnD6e3ttwp3GIlUO042VnTyy0ivSfVvo4n0xrw" } } ] }, { - "fullUrl": "urn:uuid:64bfef0a-3d97-58cd-956e-8baa7c1f0217", + "fullUrl": "urn:uuid:13c87b85-58cf-50cf-9e41-14665e6a4e0a", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:FrjickYUosrdFiPoQMMF851YJ9vOAhn2heQR6VDhx1E" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:FrjickYUosrdFiPoQMMF851YJ9vOAhn2heQR6VDhx1E" + } + } + ] + }, + { + "fullUrl": "urn:uuid:9e51ea90-811f-5400-90a6-7bb160134b7a", "resource": { "resourceType": "Device", "meta": { @@ -184,8 +242,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:B2dz6Hre6_NHZxYSJHMNsG-uDpdEhDapwMVcz1SR_BA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Ys7j_D43PfVsDqh6PcyBQKBO_X70fu3klZcK4XanRg0" } ], "status": "active", @@ -208,7 +266,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:13c87b85-58cf-50cf-9e41-14665e6a4e0a" + } }, "extension": [ { @@ -222,14 +283,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:B2dz6Hre6_NHZxYSJHMNsG-uDpdEhDapwMVcz1SR_BA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Ys7j_D43PfVsDqh6PcyBQKBO_X70fu3klZcK4XanRg0" } } ] }, { - "fullUrl": "urn:uuid:72d90e29-c7e7-545c-90bd-c42199825277", + "fullUrl": "urn:uuid:a2998010-3bf9-5ef8-861a-fa1a5e354466", "resource": { "resourceType": "Provenance", "meta": { @@ -238,10 +299,13 @@ ] }, "target": [ - { "reference": "urn:uuid:8f50510c-d9e1-55e0-968f-d468f650a2bf" } + { "reference": "urn:uuid:abf190b6-2af4-53dd-89e0-a450ad0d3234" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-19T22:40:00.000-07:00", + "end": "2026-08-20T06:55:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -265,7 +329,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:64bfef0a-3d97-58cd-956e-8baa7c1f0217" + "reference": "urn:uuid:9e51ea90-811f-5400-90a6-7bb160134b7a" } } ], @@ -282,7 +346,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:TCynCJxssAt4DKR6-a8p8A25iLlU9LB_s1EPaePLqKU" } }, @@ -299,7 +363,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:618ecd91-ad82-57b9-9c0f-b6ea3b69a76b" + "reference": "urn:uuid:4c5c7f12-b643-5f12-90e7-7925d4b21153" } } ] @@ -318,8 +382,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:FfiBKfjUXMHGR5-SZO6c_SLhoLnifAG1ibSV9sW1K0U" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:J2gdJR_BxrNsouwRn6W8YGk7g2xQHx3-XGzeUAVomZU" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-distance.json b/packages/fhir/fixtures/conformance/resources/mobile-distance.json index 1ff0ce73..7c864df8 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-distance.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-distance.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:12" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:a316b980-80ef-574b-8c92-f9ea4098c4cf", + "fullUrl": "urn:uuid:d842d954-089e-5304-afce-2ee6b8e1495d", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:TXoHUQohPJ8ryT_B_A4JXlEeOz9zuZOS0c4OxCDHycY" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:fHVg4ZAoSyzRoC4co_eCnbyEkz3bGc-MjiZDIfpCjRg" } ], @@ -100,14 +100,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:fHVg4ZAoSyzRoC4co_eCnbyEkz3bGc-MjiZDIfpCjRg" } } ] }, { - "fullUrl": "urn:uuid:5784f8ea-7c3c-53fb-91a6-d102609163b3", + "fullUrl": "urn:uuid:28c39c09-48f0-52c4-a471-a3484fe0869b", "resource": { "resourceType": "Device", "meta": { @@ -125,8 +125,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:BsReyBbLVpNg7dz2pyPiH27BYmfz8sXY8mip5ohsDsI" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:zL3gAobNbY2RKRd527d7ES_GIDcC4n6skqquODWnWMY" } ], "status": "active", @@ -149,14 +149,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:BsReyBbLVpNg7dz2pyPiH27BYmfz8sXY8mip5ohsDsI" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:zL3gAobNbY2RKRd527d7ES_GIDcC4n6skqquODWnWMY" } } ] }, { - "fullUrl": "urn:uuid:9dd72b24-695e-5058-b521-42985d983e88", + "fullUrl": "urn:uuid:db4fdab3-9262-5972-adc0-cd200aff2408", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:afxAObbQxKAM3-CyUkHCHhyABNf-EG17-iFFbZollsc" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:afxAObbQxKAM3-CyUkHCHhyABNf-EG17-iFFbZollsc" + } + } + ] + }, + { + "fullUrl": "urn:uuid:699ee36e-ad05-5dca-b54b-4c10f449ee49", "resource": { "resourceType": "Device", "meta": { @@ -174,8 +232,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:gYmc80HvYh0cNa1N-DVgQFgVHF-31EMwtPjP-JBSj9U" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:mM5MBrdFJcuEYRfM7htiQDBTcIuCnbXi6LGdSreWHD0" } ], "status": "active", @@ -198,7 +256,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:db4fdab3-9262-5972-adc0-cd200aff2408" + } }, "extension": [ { @@ -212,14 +273,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:gYmc80HvYh0cNa1N-DVgQFgVHF-31EMwtPjP-JBSj9U" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:mM5MBrdFJcuEYRfM7htiQDBTcIuCnbXi6LGdSreWHD0" } } ] }, { - "fullUrl": "urn:uuid:e101cbaf-9a6b-585e-913a-6636fba01c24", + "fullUrl": "urn:uuid:873efaec-f4e5-5055-a0c9-8b7093b9fd75", "resource": { "resourceType": "Provenance", "meta": { @@ -228,10 +289,13 @@ ] }, "target": [ - { "reference": "urn:uuid:a316b980-80ef-574b-8c92-f9ea4098c4cf" } + { "reference": "urn:uuid:d842d954-089e-5304-afce-2ee6b8e1495d" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-20T07:00:00.000-07:00", + "end": "2026-08-20T07:30:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -255,7 +319,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:9dd72b24-695e-5058-b521-42985d983e88" + "reference": "urn:uuid:699ee36e-ad05-5dca-b54b-4c10f449ee49" } } ], @@ -272,7 +336,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:TXoHUQohPJ8ryT_B_A4JXlEeOz9zuZOS0c4OxCDHycY" } }, @@ -289,7 +353,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:5784f8ea-7c3c-53fb-91a6-d102609163b3" + "reference": "urn:uuid:28c39c09-48f0-52c4-a471-a3484fe0869b" } } ] @@ -308,8 +372,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:DLKzTcfXTY-hdSLv9GmCMjZpw3ouRC4CPcG1pf4tSvA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:5ipRUXob_FsDVwFf6K5F0DfYTeHKO1LF3AJEIMld6dM" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json b/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json index 44a93aa0..6146a13f 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-electrodermal-activity.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:13" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:05e2b23b-2b07-5454-9dd2-378d69ee2150", + "fullUrl": "urn:uuid:09e432b3-a4ca-55b8-b403-6213fa383669", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:4YedtTQr9n-nubQkvlBwC-1mOoLtiknhWp4RTtKkSlM" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:BlsXMWS8VeTKUY0-gRERFgFfSQX3KOWfQ9cWnbVJYDU" } ], @@ -102,14 +102,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:BlsXMWS8VeTKUY0-gRERFgFfSQX3KOWfQ9cWnbVJYDU" } } ] }, { - "fullUrl": "urn:uuid:c74adc7d-e2cc-5fe2-8288-704a23bd131e", + "fullUrl": "urn:uuid:44a13ce3-9da9-5449-8238-29d2871c64aa", "resource": { "resourceType": "Device", "meta": { @@ -127,8 +127,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:u-1rXN1Mxg6FOcZeVAsxYBKbRr1DMHadZTocKEEUOx4" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:AlhEDtegyqmpUaM3IEJXzZhjFPQvOUsDFLbwYzmQBbY" } ], "status": "active", @@ -151,14 +151,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:u-1rXN1Mxg6FOcZeVAsxYBKbRr1DMHadZTocKEEUOx4" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:AlhEDtegyqmpUaM3IEJXzZhjFPQvOUsDFLbwYzmQBbY" } } ] }, { - "fullUrl": "urn:uuid:2127a0df-3555-5ec9-bf92-62b908a8691f", + "fullUrl": "urn:uuid:410085e9-bf31-5a20-ba9e-307879a9e8e5", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:-UiVDu-rMxpnyFxV0RxGB2CZNkkGwR9t7_zIKv8Nwvo" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:-UiVDu-rMxpnyFxV0RxGB2CZNkkGwR9t7_zIKv8Nwvo" + } + } + ] + }, + { + "fullUrl": "urn:uuid:826fa641-12c7-589d-9ad6-bdd18ccb6aea", "resource": { "resourceType": "Device", "meta": { @@ -176,8 +234,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:-S0Gqyi7O_7YfEiA8qAsF_fbOCQKVviT4-XvrnxqtBE" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:nMXgffALMys5H0_GIFDZAN42NRmngn-8bBpGkGzuFhc" } ], "status": "active", @@ -200,7 +258,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:410085e9-bf31-5a20-ba9e-307879a9e8e5" + } }, "extension": [ { @@ -214,14 +275,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:-S0Gqyi7O_7YfEiA8qAsF_fbOCQKVviT4-XvrnxqtBE" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:nMXgffALMys5H0_GIFDZAN42NRmngn-8bBpGkGzuFhc" } } ] }, { - "fullUrl": "urn:uuid:62cb671e-5836-5c0c-91bd-583d7486256a", + "fullUrl": "urn:uuid:b5efb086-e50b-5f03-b4b1-bde8bfe2cf40", "resource": { "resourceType": "Provenance", "meta": { @@ -230,10 +291,10 @@ ] }, "target": [ - { "reference": "urn:uuid:05e2b23b-2b07-5454-9dd2-378d69ee2150" } + { "reference": "urn:uuid:09e432b3-a4ca-55b8-b403-6213fa383669" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T10:02:30.000-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -257,7 +318,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:2127a0df-3555-5ec9-bf92-62b908a8691f" + "reference": "urn:uuid:826fa641-12c7-589d-9ad6-bdd18ccb6aea" } } ], @@ -274,7 +335,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:4YedtTQr9n-nubQkvlBwC-1mOoLtiknhWp4RTtKkSlM" } }, @@ -291,7 +352,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:c74adc7d-e2cc-5fe2-8288-704a23bd131e" + "reference": "urn:uuid:44a13ce3-9da9-5449-8238-29d2871c64aa" } } ] @@ -310,8 +371,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:o6HbMwcpACffZfDBRcw3uhTXFHQO8CAKI_kDG78YUHw" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:c5w9uYJgMzBWBMQt43HG-Pek05imwiShaRkMe7k2Gs0" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json b/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json index 8bd6c41b..7a0d64f6 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-flights-climbed.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:14" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:1b278f81-bafa-5e5a-aa91-820281798dc1", + "fullUrl": "urn:uuid:c5910edf-99fa-55db-b386-1abf770c4f41", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:vgABOZcd_2aMgE45wa5IupFp8PcTzVbBph9MSC6nbzE" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:Jd_HuLq4yPD61i7SXwNGSVKzGVRlPQhpCRkuTKhjqOE" } ], @@ -100,14 +100,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:Jd_HuLq4yPD61i7SXwNGSVKzGVRlPQhpCRkuTKhjqOE" } } ] }, { - "fullUrl": "urn:uuid:ca82e833-a64a-5080-8f55-e25ec09ffb3b", + "fullUrl": "urn:uuid:bf6a7458-6f64-57d9-b82d-f74d80cedd91", "resource": { "resourceType": "Device", "meta": { @@ -125,8 +125,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:5JsSfWJISELeuXbZe-bRaI8TDU1PGIoSZtz60Z2X7HM" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:uonlHNQQU__yRTa2hC52o8sm8wYfijmByTcSCwiZiTg" } ], "status": "active", @@ -149,14 +149,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:5JsSfWJISELeuXbZe-bRaI8TDU1PGIoSZtz60Z2X7HM" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:uonlHNQQU__yRTa2hC52o8sm8wYfijmByTcSCwiZiTg" } } ] }, { - "fullUrl": "urn:uuid:d2359082-1541-585e-8e66-8bae2a65011a", + "fullUrl": "urn:uuid:0065d48b-0d8a-599b-919d-fb1d3d6eba1b", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Z8MakmwZHNS_3z4vw0RNYlHIxscgBJoZaR8yToD1UAU" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Z8MakmwZHNS_3z4vw0RNYlHIxscgBJoZaR8yToD1UAU" + } + } + ] + }, + { + "fullUrl": "urn:uuid:fc25ee81-89a1-58a3-ab47-5ff12c511bd8", "resource": { "resourceType": "Device", "meta": { @@ -174,8 +232,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:svLLBnin4pxJFIYsSL1qNT0ARP9QqSEBxlOWDkkz9DQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:pzhPJls0tkEWNqC3w9VbKlqyIJY1ttLmju2b7PAXJ6o" } ], "status": "active", @@ -198,7 +256,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:0065d48b-0d8a-599b-919d-fb1d3d6eba1b" + } }, "extension": [ { @@ -212,14 +273,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:svLLBnin4pxJFIYsSL1qNT0ARP9QqSEBxlOWDkkz9DQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:pzhPJls0tkEWNqC3w9VbKlqyIJY1ttLmju2b7PAXJ6o" } } ] }, { - "fullUrl": "urn:uuid:0713c33b-6273-530c-9915-44dc21a60b55", + "fullUrl": "urn:uuid:e2232c5e-9613-59eb-a8f6-63bac9d20b7e", "resource": { "resourceType": "Provenance", "meta": { @@ -228,10 +289,13 @@ ] }, "target": [ - { "reference": "urn:uuid:1b278f81-bafa-5e5a-aa91-820281798dc1" } + { "reference": "urn:uuid:c5910edf-99fa-55db-b386-1abf770c4f41" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-20T08:00:00.000-07:00", + "end": "2026-08-20T09:00:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -255,7 +319,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:d2359082-1541-585e-8e66-8bae2a65011a" + "reference": "urn:uuid:fc25ee81-89a1-58a3-ab47-5ff12c511bd8" } } ], @@ -272,7 +336,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:vgABOZcd_2aMgE45wa5IupFp8PcTzVbBph9MSC6nbzE" } }, @@ -289,7 +353,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:ca82e833-a64a-5080-8f55-e25ec09ffb3b" + "reference": "urn:uuid:bf6a7458-6f64-57d9-b82d-f74d80cedd91" } } ] @@ -308,8 +372,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:e88H16uxr9KnpgkeHQZBgQ7JaHfHbJ0iV8PAKr8vop8" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:qxLO3qqpjuIZaITyVSZrsmtZiPY3rHohH-i0btADnLA" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json index cc506070..ab935b23 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-rmssd.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:16" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:f6e3927c-3d0a-5a3c-8fde-eb54caeca7b7", + "fullUrl": "urn:uuid:0fea07c7-ef13-57d9-a240-a4143bc1c668", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:AEZUZCTuy5drfmVoPO_ot7LunYUw68sNqiXSV5fSBko" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:S5vVITWsjX9mcvy8pBYaU_lPwGfNl8M6BijEtChtbCU" } ], @@ -102,14 +102,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:S5vVITWsjX9mcvy8pBYaU_lPwGfNl8M6BijEtChtbCU" } } ] }, { - "fullUrl": "urn:uuid:375bbe70-10e1-5992-b3f9-0e4eb5612ae8", + "fullUrl": "urn:uuid:74a729bf-6f36-5964-9592-5f563c373a10", "resource": { "resourceType": "Device", "meta": { @@ -127,8 +127,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:1GzMX6TN8GutnhmfmYulPCxpQfkJgPHuDr46ZX8DhTY" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Rk0SWTvzmcx_Qy-KMlx1rSx4HfqRGVvxSq0Z1tUDo64" } ], "status": "active", @@ -151,14 +151,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:1GzMX6TN8GutnhmfmYulPCxpQfkJgPHuDr46ZX8DhTY" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Rk0SWTvzmcx_Qy-KMlx1rSx4HfqRGVvxSq0Z1tUDo64" } } ] }, { - "fullUrl": "urn:uuid:c2e33326-2fed-5e61-84af-db7558330215", + "fullUrl": "urn:uuid:4cb00b5a-440c-5b40-bab7-af24b54f73b5", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:hvr1Wex4yL-7KqPB4wBatnY_ZUz9cYu_xBB9JdiOWB8" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:hvr1Wex4yL-7KqPB4wBatnY_ZUz9cYu_xBB9JdiOWB8" + } + } + ] + }, + { + "fullUrl": "urn:uuid:76b0bce5-0ae3-532e-a36d-7b61fbbad73f", "resource": { "resourceType": "Device", "meta": { @@ -176,8 +234,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OG_EL_fZjoyFtd7M2on8ei01lKoevG6MNr3bfeuT5ec" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:p-F-0mtOg83d4ZM8UDljRwEuDyeSTYtG5Lvdc9hKwd8" } ], "status": "active", @@ -200,7 +258,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:4cb00b5a-440c-5b40-bab7-af24b54f73b5" + } }, "extension": [ { @@ -214,14 +275,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OG_EL_fZjoyFtd7M2on8ei01lKoevG6MNr3bfeuT5ec" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:p-F-0mtOg83d4ZM8UDljRwEuDyeSTYtG5Lvdc9hKwd8" } } ] }, { - "fullUrl": "urn:uuid:c989d2f9-98e7-5835-b736-95cda36e4f12", + "fullUrl": "urn:uuid:b73684d7-a73c-5664-b65d-a7be50198699", "resource": { "resourceType": "Provenance", "meta": { @@ -230,10 +291,10 @@ ] }, "target": [ - { "reference": "urn:uuid:f6e3927c-3d0a-5a3c-8fde-eb54caeca7b7" } + { "reference": "urn:uuid:0fea07c7-ef13-57d9-a240-a4143bc1c668" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T06:58:12.500-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -257,7 +318,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:c2e33326-2fed-5e61-84af-db7558330215" + "reference": "urn:uuid:76b0bce5-0ae3-532e-a36d-7b61fbbad73f" } } ], @@ -274,7 +335,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:AEZUZCTuy5drfmVoPO_ot7LunYUw68sNqiXSV5fSBko" } }, @@ -291,7 +352,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:375bbe70-10e1-5992-b3f9-0e4eb5612ae8" + "reference": "urn:uuid:74a729bf-6f36-5964-9592-5f563c373a10" } } ] @@ -310,8 +371,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:4M2ckiaROaQsqFL7zobE316ingS1pOnTxc05MfhitdQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:9CdNqu2Y6Q1oGw-k0Nf-vSA4aLHHEiMsf_HMJs5VEys" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json index 322bd718..3f46ea58 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate-variability-sdnn.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:17" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:8270c509-bfdf-5d38-a815-b7e2214c5058", + "fullUrl": "urn:uuid:0d55a1ff-fa33-561c-8769-73ff23d64636", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:tkdht7TFVXQyq6IHNvYVQ8tNFVyUPgbZXNMZPxXED6U" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:CZXatvohITV673olsXmQKz38AcBWcMpvz5he0Qm5VKI" } ], @@ -97,14 +97,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:CZXatvohITV673olsXmQKz38AcBWcMpvz5he0Qm5VKI" } } ] }, { - "fullUrl": "urn:uuid:1ac62dcf-7508-5504-8cf9-88aa67a19f05", + "fullUrl": "urn:uuid:a9357667-5cb8-5511-a4c3-2983b6a0cf5f", "resource": { "resourceType": "Device", "meta": { @@ -122,8 +122,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OVqOPjrQEoSJS--PZePeSkqILMB9dA0xef1_SpJ4viY" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Q3trM9h0isUdrdGPhBfGVz8al2Y7gm3Wx2Cbm2tDHzQ" } ], "status": "active", @@ -146,14 +146,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OVqOPjrQEoSJS--PZePeSkqILMB9dA0xef1_SpJ4viY" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Q3trM9h0isUdrdGPhBfGVz8al2Y7gm3Wx2Cbm2tDHzQ" } } ] }, { - "fullUrl": "urn:uuid:dc091ace-e49f-5779-bcad-c1b7724d6484", + "fullUrl": "urn:uuid:cf5e9ab4-f647-56d8-b8ab-d34fd23adbe9", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Eow3J_S-rMmlDXv6IvNreCStzZ-FnzsjrYw5bbdyNH4" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Eow3J_S-rMmlDXv6IvNreCStzZ-FnzsjrYw5bbdyNH4" + } + } + ] + }, + { + "fullUrl": "urn:uuid:91f3ca4f-67e7-59d0-b4c8-5469c2344d9e", "resource": { "resourceType": "Device", "meta": { @@ -171,8 +229,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:RMkmXi5iDHPmp9alpqCtmszW5AIvyyRqxxRMY_nwChw" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:23qKX5ZxtWf_SThibl8QTPwVkVlMgVvUS5Ufcfutz1s" } ], "status": "active", @@ -195,7 +253,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:cf5e9ab4-f647-56d8-b8ab-d34fd23adbe9" + } }, "extension": [ { @@ -209,14 +270,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:RMkmXi5iDHPmp9alpqCtmszW5AIvyyRqxxRMY_nwChw" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:23qKX5ZxtWf_SThibl8QTPwVkVlMgVvUS5Ufcfutz1s" } } ] }, { - "fullUrl": "urn:uuid:6cfc6400-6efa-5c4a-8345-f481d5f52388", + "fullUrl": "urn:uuid:82cbf18b-fa0a-5065-9ae1-82ec71d3ca55", "resource": { "resourceType": "Provenance", "meta": { @@ -225,10 +286,10 @@ ] }, "target": [ - { "reference": "urn:uuid:8270c509-bfdf-5d38-a815-b7e2214c5058" } + { "reference": "urn:uuid:0d55a1ff-fa33-561c-8769-73ff23d64636" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T03:12:44.000-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -252,7 +313,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:dc091ace-e49f-5779-bcad-c1b7724d6484" + "reference": "urn:uuid:91f3ca4f-67e7-59d0-b4c8-5469c2344d9e" } } ], @@ -269,7 +330,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:tkdht7TFVXQyq6IHNvYVQ8tNFVyUPgbZXNMZPxXED6U" } }, @@ -286,7 +347,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:1ac62dcf-7508-5504-8cf9-88aa67a19f05" + "reference": "urn:uuid:a9357667-5cb8-5511-a4c3-2983b6a0cf5f" } } ] @@ -305,8 +366,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:qtQ7FdqRyP8fcGw_zIozpXqNB9VIxfITbTY4xqoSj3Y" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:2dkVdrYYZwu6_m-uiJPhpLfNSqTVDYM55staryj2i_g" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json index ec961e1e..fa3d1292 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-heart-rate.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:15" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:d977eb59-e38c-5c58-a9e1-d6aaab8006c4", + "fullUrl": "urn:uuid:f179a73b-b524-5e68-a262-c1c427c808b6", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:tJcKn-_0jxgFDvTYEiVfnjgPQy6bjsEgJ6whvNnqwjM" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:bmuym0sEonI5tHUX1mpn1Rm7zoBhUOUWrWJSXt-J4b8" } ], @@ -109,14 +109,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:bmuym0sEonI5tHUX1mpn1Rm7zoBhUOUWrWJSXt-J4b8" } } ] }, { - "fullUrl": "urn:uuid:b49b2e14-6bf2-572d-b927-ffc5dbb2de0e", + "fullUrl": "urn:uuid:29d20a5f-60df-5ce5-b90d-a1ae36d674e8", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +134,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:_jJrqQjnriMYhYAfnPCdEg3KSuVIV2CPuLJ5jQLUbqA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:54Zw_lCyg7ofaCm-ZEvdzRaTAwVWz2LUlGlXGVdoxqY" } ], "status": "active", @@ -158,14 +158,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:_jJrqQjnriMYhYAfnPCdEg3KSuVIV2CPuLJ5jQLUbqA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:54Zw_lCyg7ofaCm-ZEvdzRaTAwVWz2LUlGlXGVdoxqY" } } ] }, { - "fullUrl": "urn:uuid:fa8f73d9-94a9-5603-a4d6-401a04ea1714", + "fullUrl": "urn:uuid:d76205cb-852e-52e2-be2d-c0488ffd2644", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:hiT-Wt-SJrKwL64JoYjin7H8O1N9g44V0MiNhUg5bFA" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:hiT-Wt-SJrKwL64JoYjin7H8O1N9g44V0MiNhUg5bFA" + } + } + ] + }, + { + "fullUrl": "urn:uuid:0aa5e708-b94e-5e92-9039-9059caba6a1d", "resource": { "resourceType": "Device", "meta": { @@ -183,8 +241,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:iu_gT-exxcFt4UuQNJUirFWc_H7mb3n2H2opZGP7JFc" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:8ZwCFZpjaNKJcoTMc0X7HdgXEmRoGhTaTZsZx--Jj5g" } ], "status": "active", @@ -207,7 +265,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:d76205cb-852e-52e2-be2d-c0488ffd2644" + } }, "extension": [ { @@ -221,14 +282,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:iu_gT-exxcFt4UuQNJUirFWc_H7mb3n2H2opZGP7JFc" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:8ZwCFZpjaNKJcoTMc0X7HdgXEmRoGhTaTZsZx--Jj5g" } } ] }, { - "fullUrl": "urn:uuid:1b3fd21e-a7b2-5ded-b129-7c9cf4cccaf0", + "fullUrl": "urn:uuid:c5c52e2e-52f0-5fe3-a9ca-480c0bb0b7e2", "resource": { "resourceType": "Provenance", "meta": { @@ -237,10 +298,10 @@ ] }, "target": [ - { "reference": "urn:uuid:d977eb59-e38c-5c58-a9e1-d6aaab8006c4" } + { "reference": "urn:uuid:f179a73b-b524-5e68-a262-c1c427c808b6" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T08:30:00.251-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -264,7 +325,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:fa8f73d9-94a9-5603-a4d6-401a04ea1714" + "reference": "urn:uuid:0aa5e708-b94e-5e92-9039-9059caba6a1d" } } ], @@ -281,7 +342,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:tJcKn-_0jxgFDvTYEiVfnjgPQy6bjsEgJ6whvNnqwjM" } }, @@ -298,7 +359,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:b49b2e14-6bf2-572d-b927-ffc5dbb2de0e" + "reference": "urn:uuid:29d20a5f-60df-5ce5-b90d-a1ae36d674e8" } } ] @@ -317,8 +378,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:KeJVdr0G0FQra4gqbd817-DGBzsTeQyRmXS4w9wqvx4" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:uMH0mAZ7mIzItwMmlgCaf7gewfrpYg_wrlDQ1uo1HMw" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json b/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json index 61c62a32..89596e3a 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-lean-body-mass.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:18" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:bc7ae0a2-0fc8-5ace-89e5-45f4c40a5dcb", + "fullUrl": "urn:uuid:0a07a7a4-2fd4-5614-bf27-ce1cf1c93174", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:8l5WdJ-o5mNFdCSbs5y9t3UvOl60nYnd583KpnzMSKM" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:TZXCBXOpPixQ2BNvn0NbH2dqlgzPFN7dffcFH7JRopU" } ], @@ -97,14 +97,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:TZXCBXOpPixQ2BNvn0NbH2dqlgzPFN7dffcFH7JRopU" } } ] }, { - "fullUrl": "urn:uuid:913d1293-e271-513f-b01f-ebb18d782d5f", + "fullUrl": "urn:uuid:727fade5-8c2b-5273-907d-ee4cfbc6faff", "resource": { "resourceType": "Device", "meta": { @@ -122,8 +122,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:2yXt6DebFvXDTeSlzgzyFye9iuh92QGZMLIL4n0jN80" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:t4rpiYiOd9MSFrhPcS4BuVWHd0fMbTaZinAHJdQu2gs" } ], "status": "active", @@ -146,14 +146,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:2yXt6DebFvXDTeSlzgzyFye9iuh92QGZMLIL4n0jN80" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:t4rpiYiOd9MSFrhPcS4BuVWHd0fMbTaZinAHJdQu2gs" } } ] }, { - "fullUrl": "urn:uuid:57d9a45a-44d5-5cf9-9545-4e386990fee6", + "fullUrl": "urn:uuid:a4547e0e-a550-546e-8cf0-59ac5845f4aa", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:unmZZj-2VdCE2BcL4ALV9YxdkAPjSe3iS2gkIXj9ess" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:unmZZj-2VdCE2BcL4ALV9YxdkAPjSe3iS2gkIXj9ess" + } + } + ] + }, + { + "fullUrl": "urn:uuid:2c734349-caec-5361-ae66-4728b2b2583e", "resource": { "resourceType": "Device", "meta": { @@ -171,8 +229,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:KJTEBWuDAWHHJ5xERnRW8CcL1X4cUMxv0uoTuNNohQ8" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:MghWeYb6BiQYowsE6MmOjoOYVkrQBEAAQB2CN_lLPNY" } ], "status": "active", @@ -195,7 +253,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:a4547e0e-a550-546e-8cf0-59ac5845f4aa" + } }, "extension": [ { @@ -209,14 +270,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:KJTEBWuDAWHHJ5xERnRW8CcL1X4cUMxv0uoTuNNohQ8" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:MghWeYb6BiQYowsE6MmOjoOYVkrQBEAAQB2CN_lLPNY" } } ] }, { - "fullUrl": "urn:uuid:19b3591a-7abf-5469-a12a-f47f00d1e5c4", + "fullUrl": "urn:uuid:ac863ca7-a816-51a6-a9d0-4c735afcea32", "resource": { "resourceType": "Provenance", "meta": { @@ -225,10 +286,10 @@ ] }, "target": [ - { "reference": "urn:uuid:bc7ae0a2-0fc8-5ace-89e5-45f4c40a5dcb" } + { "reference": "urn:uuid:0a07a7a4-2fd4-5614-bf27-ce1cf1c93174" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-21T07:12:04.000-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -252,7 +313,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:57d9a45a-44d5-5cf9-9545-4e386990fee6" + "reference": "urn:uuid:2c734349-caec-5361-ae66-4728b2b2583e" } } ], @@ -269,7 +330,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:8l5WdJ-o5mNFdCSbs5y9t3UvOl60nYnd583KpnzMSKM" } }, @@ -286,7 +347,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:913d1293-e271-513f-b01f-ebb18d782d5f" + "reference": "urn:uuid:727fade5-8c2b-5273-907d-ee4cfbc6faff" } } ] @@ -305,8 +366,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:32bLLYB_SafBR6GtMo9KZHVV2SiFvcclBMoKe-Tki1Y" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:mNl9dWMlsNcfMLcdwIUBOyJyKW4EDgRRxyR8M_m8tqY" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json index 21cd2e44..e41637f9 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-light-sleep-duration.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:19" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:4efc2828-d56e-533c-973a-822c1bfa4e23", + "fullUrl": "urn:uuid:d455671e-a6b6-530b-88c1-6f98d9e6bdf1", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:uIT4xv9B-PBwKmJAw7VglCZ1hAYn6BSJF577LrFO2ZA" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:a8s1HpBI9_4qjgDcZG1wbl8Opf3E51HAkTK3f2QsUYg" } ], @@ -110,14 +110,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:a8s1HpBI9_4qjgDcZG1wbl8Opf3E51HAkTK3f2QsUYg" } } ] }, { - "fullUrl": "urn:uuid:5d918d67-4fa9-55ee-b070-98dcf22a5fae", + "fullUrl": "urn:uuid:61e179bb-1d2d-55cc-a4db-6e2bd34741ef", "resource": { "resourceType": "Device", "meta": { @@ -135,8 +135,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:By5ZV5MsYx3CBGT8S2VIwR4j7Rv6dE81YMIVOeBOtSo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:SVAmv5YteIDOsjJ_ns19NUtKnQsARYq5i2QDxUZw9ek" } ], "status": "active", @@ -159,14 +159,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:By5ZV5MsYx3CBGT8S2VIwR4j7Rv6dE81YMIVOeBOtSo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:SVAmv5YteIDOsjJ_ns19NUtKnQsARYq5i2QDxUZw9ek" } } ] }, { - "fullUrl": "urn:uuid:4fe4116e-b120-5c76-b6b4-c45669831277", + "fullUrl": "urn:uuid:f54d731d-0b8c-5a24-98b8-01e97434ce11", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:0CKEb23dzGzzxCyvpv_F1jKwM0-kSmftSbyYXA3NtlM" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:0CKEb23dzGzzxCyvpv_F1jKwM0-kSmftSbyYXA3NtlM" + } + } + ] + }, + { + "fullUrl": "urn:uuid:7e1517e7-a326-5928-9d7e-ac8d72ca0239", "resource": { "resourceType": "Device", "meta": { @@ -184,8 +242,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:gAk_xo_m3VIoAq8WwnPjv8FyKliF_r2JVORqOlVXzQk" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ipoT4x7V0YPwFPekfMIA21rjAG9Kcb_PZGylA7AijBc" } ], "status": "active", @@ -208,7 +266,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:f54d731d-0b8c-5a24-98b8-01e97434ce11" + } }, "extension": [ { @@ -222,14 +283,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:gAk_xo_m3VIoAq8WwnPjv8FyKliF_r2JVORqOlVXzQk" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ipoT4x7V0YPwFPekfMIA21rjAG9Kcb_PZGylA7AijBc" } } ] }, { - "fullUrl": "urn:uuid:6e7dd3df-58b6-522c-a757-ebb39306085c", + "fullUrl": "urn:uuid:4d8c0f9f-df7f-5614-88e1-d0aa17167a72", "resource": { "resourceType": "Provenance", "meta": { @@ -238,10 +299,13 @@ ] }, "target": [ - { "reference": "urn:uuid:4efc2828-d56e-533c-973a-822c1bfa4e23" } + { "reference": "urn:uuid:d455671e-a6b6-530b-88c1-6f98d9e6bdf1" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-19T22:40:00.000-07:00", + "end": "2026-08-20T06:55:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -265,7 +329,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:4fe4116e-b120-5c76-b6b4-c45669831277" + "reference": "urn:uuid:7e1517e7-a326-5928-9d7e-ac8d72ca0239" } } ], @@ -282,7 +346,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:uIT4xv9B-PBwKmJAw7VglCZ1hAYn6BSJF577LrFO2ZA" } }, @@ -299,7 +363,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:5d918d67-4fa9-55ee-b070-98dcf22a5fae" + "reference": "urn:uuid:61e179bb-1d2d-55cc-a4db-6e2bd34741ef" } } ] @@ -318,8 +382,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:iom5w6J85R7VsUmEG6UnuzPUqpuUXfUcEUI7pogG9IA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:yfts9uPMW70OlGzcQhWM1Jh10LItasgwsXVSgEaXss4" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json index c9a7aa20..3ac76651 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation-daily-average.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:21" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:43ad632f-c572-576f-8fef-5b703ca7cd84", + "fullUrl": "urn:uuid:287a6d9b-671e-5d78-ae64-b1affc4a5706", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:osGy-Ko7LLVWyvk64uBV-JuE2OlQbjgV9yXagYzTplA" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:Foy5lK9amkmeg12XbXQYMrT-TUBizAOPdTfE7c8MsIE" } ], @@ -110,14 +110,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:Foy5lK9amkmeg12XbXQYMrT-TUBizAOPdTfE7c8MsIE" } } ] }, { - "fullUrl": "urn:uuid:f0661a79-cb88-5556-a043-744b7b78328c", + "fullUrl": "urn:uuid:4e3dc65b-92b4-568d-a11f-6df6af10b9c3", "resource": { "resourceType": "Device", "meta": { @@ -135,8 +135,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:JRw2RshEpj2tnoPVOkAi7BWBqTQu2B2oX5wgLXZiX3c" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:2Q_J82DSQIMkIl3m77WHe7igTQGz68Yr9mxvjzzqw50" } ], "status": "active", @@ -159,14 +159,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:JRw2RshEpj2tnoPVOkAi7BWBqTQu2B2oX5wgLXZiX3c" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:2Q_J82DSQIMkIl3m77WHe7igTQGz68Yr9mxvjzzqw50" } } ] }, { - "fullUrl": "urn:uuid:99e59cb2-5f78-5b22-96f2-6c3467d5b7d1", + "fullUrl": "urn:uuid:d2eed63a-b31c-5db2-9523-c7a2ee2204ba", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:RPQjaL1hdDqkC5XNE20gmnY6je0bzs1vlglPfO5CzAw" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:RPQjaL1hdDqkC5XNE20gmnY6je0bzs1vlglPfO5CzAw" + } + } + ] + }, + { + "fullUrl": "urn:uuid:354d297c-4af7-5b6e-bbc1-5920c80eee15", "resource": { "resourceType": "Device", "meta": { @@ -184,8 +242,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uIYGTaUTHfKiwEm5jVkr_z9QOzRbCzr3NeyStHgnB7Y" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:eBnqbrBYG2G5Jt-i2t3i1eKPeZP5O_wDySlD0QowYRU" } ], "status": "active", @@ -208,7 +266,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:d2eed63a-b31c-5db2-9523-c7a2ee2204ba" + } }, "extension": [ { @@ -222,14 +283,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uIYGTaUTHfKiwEm5jVkr_z9QOzRbCzr3NeyStHgnB7Y" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:eBnqbrBYG2G5Jt-i2t3i1eKPeZP5O_wDySlD0QowYRU" } } ] }, { - "fullUrl": "urn:uuid:21a750dd-da67-5b44-a03a-0cdd3cdd0cfc", + "fullUrl": "urn:uuid:8a0f3a8f-a49a-5fd6-be89-0ca6898dad75", "resource": { "resourceType": "Provenance", "meta": { @@ -238,10 +299,13 @@ ] }, "target": [ - { "reference": "urn:uuid:43ad632f-c572-576f-8fef-5b703ca7cd84" } + { "reference": "urn:uuid:287a6d9b-671e-5d78-ae64-b1affc4a5706" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-19T22:40:00.000-07:00", + "end": "2026-08-20T06:55:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -265,7 +329,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:99e59cb2-5f78-5b22-96f2-6c3467d5b7d1" + "reference": "urn:uuid:354d297c-4af7-5b6e-bbc1-5920c80eee15" } } ], @@ -282,7 +346,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:osGy-Ko7LLVWyvk64uBV-JuE2OlQbjgV9yXagYzTplA" } }, @@ -299,7 +363,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:f0661a79-cb88-5556-a043-744b7b78328c" + "reference": "urn:uuid:4e3dc65b-92b4-568d-a11f-6df6af10b9c3" } } ] @@ -318,8 +382,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:S4UXghjlk4gU1D48Oolvx5KhsOA-BiiIGLy8DuV88F0" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:uvTZoQyZv4sfPl40dbIofAslEwevHmWxdgaoFWqTGA0" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json index bf79d459..4882b9b8 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-oxygen-saturation.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:20" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:ba01f42d-adfb-532e-8909-cf8ff6e1d183", + "fullUrl": "urn:uuid:644046b4-ec57-5bf1-a5f5-8f2d1aa8c462", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:sJSuge3Kkid_TsVY7p-oSU7CUFGvHXOZEVfyi6GRsvs" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:HPgVl-lF8_XjYKtNbZqZoeUvyJx6cwlNxaJ3AgW6mG8" } ], @@ -109,14 +109,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:HPgVl-lF8_XjYKtNbZqZoeUvyJx6cwlNxaJ3AgW6mG8" } } ] }, { - "fullUrl": "urn:uuid:ef4c6dd5-5186-52bd-81ff-c35ceff514cd", + "fullUrl": "urn:uuid:40e4cb19-ae74-5be9-9ae7-eb6f01731558", "resource": { "resourceType": "Device", "meta": { @@ -134,8 +134,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:U37Z6ve6J62eVasgsTcFZNjogwaV5RSx0vOFIcZnowQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:cGty9td4dgRFDM1__amOEZmHB3bi_TaWBqG2PXU4Os0" } ], "status": "active", @@ -158,14 +158,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:U37Z6ve6J62eVasgsTcFZNjogwaV5RSx0vOFIcZnowQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:cGty9td4dgRFDM1__amOEZmHB3bi_TaWBqG2PXU4Os0" } } ] }, { - "fullUrl": "urn:uuid:a26ecd38-abeb-5d40-b1fb-7b6217720f23", + "fullUrl": "urn:uuid:49d132ec-3b9a-549a-8359-e2711d4cb695", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:1OS2FwxCq80doRXlJ8BAvcrlu02W_bi78eOL0m7siCw" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:1OS2FwxCq80doRXlJ8BAvcrlu02W_bi78eOL0m7siCw" + } + } + ] + }, + { + "fullUrl": "urn:uuid:e9c891eb-5c4d-581f-9827-aac2dbe57c3d", "resource": { "resourceType": "Device", "meta": { @@ -183,8 +241,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:fs_lzSgI2UvEDgi6o7MZqbAjw2DzA9xn0PCEWBSfRjo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:0x532DNRIcmwPL6mIJkx_KHOm_Wl7yIzJrQk957dD3M" } ], "status": "active", @@ -207,7 +265,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:49d132ec-3b9a-549a-8359-e2711d4cb695" + } }, "extension": [ { @@ -221,14 +282,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:fs_lzSgI2UvEDgi6o7MZqbAjw2DzA9xn0PCEWBSfRjo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:0x532DNRIcmwPL6mIJkx_KHOm_Wl7yIzJrQk957dD3M" } } ] }, { - "fullUrl": "urn:uuid:464a25cc-b778-556b-98d6-637239e26837", + "fullUrl": "urn:uuid:145f608a-fb4a-57d8-a35a-2b803e59caeb", "resource": { "resourceType": "Provenance", "meta": { @@ -237,10 +298,10 @@ ] }, "target": [ - { "reference": "urn:uuid:ba01f42d-adfb-532e-8909-cf8ff6e1d183" } + { "reference": "urn:uuid:644046b4-ec57-5bf1-a5f5-8f2d1aa8c462" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T08:35:00.000-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -264,7 +325,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:a26ecd38-abeb-5d40-b1fb-7b6217720f23" + "reference": "urn:uuid:e9c891eb-5c4d-581f-9827-aac2dbe57c3d" } } ], @@ -281,7 +342,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:sJSuge3Kkid_TsVY7p-oSU7CUFGvHXOZEVfyi6GRsvs" } }, @@ -298,7 +359,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:ef4c6dd5-5186-52bd-81ff-c35ceff514cd" + "reference": "urn:uuid:40e4cb19-ae74-5be9-9ae7-eb6f01731558" } } ] @@ -317,8 +378,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:vCXndC-N8tGe3pKoAEF3lUnqguUQbAFW-Ntf2LkQHQg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:ZJT7zn6G3wugYy86odjEJZba7hXAoI4OHvYepsEXYaI" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json index 064bfe4e..2cbb8d03 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-rem-sleep-duration.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:22" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:3e90f50b-622a-53e2-9479-a27ada726090", + "fullUrl": "urn:uuid:0b62f555-261a-5479-9ac2-0dadbd6f7e8e", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:RKGg7_SVjLJ-TW2IEyKdFAqEFIyLZ6mZjuKs4CBBC9U" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:ldt-blmNxjKdqbyUa4I71AKfMfwlCmOatocVyPgQyxs" } ], @@ -110,14 +110,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:ldt-blmNxjKdqbyUa4I71AKfMfwlCmOatocVyPgQyxs" } } ] }, { - "fullUrl": "urn:uuid:ca2701fb-c3c9-5019-9a86-da7b9aa683d2", + "fullUrl": "urn:uuid:2925eb1d-36f7-5b5e-9e6d-2f600daff950", "resource": { "resourceType": "Device", "meta": { @@ -135,8 +135,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:idFKgbnmzoK3YLYXQ9N6KsqXS3CAPEPqADjwroI9Bsg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:0qqVCoVQg_92SDP3koIzjLxODN3f42NJaa7wx7vb9Q4" } ], "status": "active", @@ -159,14 +159,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:idFKgbnmzoK3YLYXQ9N6KsqXS3CAPEPqADjwroI9Bsg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:0qqVCoVQg_92SDP3koIzjLxODN3f42NJaa7wx7vb9Q4" } } ] }, { - "fullUrl": "urn:uuid:e575699a-b0df-5dc5-9d5a-e1ee8f319c1d", + "fullUrl": "urn:uuid:13fb75c5-992d-5742-89eb-0473aeb37479", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:vIL4mfzxbq5wcjTj3tvgzWrqVK5jZnKhgiUtkDSIXQ8" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:vIL4mfzxbq5wcjTj3tvgzWrqVK5jZnKhgiUtkDSIXQ8" + } + } + ] + }, + { + "fullUrl": "urn:uuid:68e28913-839c-5382-9fb8-1ea55562017c", "resource": { "resourceType": "Device", "meta": { @@ -184,8 +242,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:h1-IrXtzorIt0CuTA5q7b8m8r6AzfjVwt8gA8BpTkZA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:I4bDxRtPdghxI4H8re68yRIphlJs6z6BuIA1aLKovT8" } ], "status": "active", @@ -208,7 +266,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:13fb75c5-992d-5742-89eb-0473aeb37479" + } }, "extension": [ { @@ -222,14 +283,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:h1-IrXtzorIt0CuTA5q7b8m8r6AzfjVwt8gA8BpTkZA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:I4bDxRtPdghxI4H8re68yRIphlJs6z6BuIA1aLKovT8" } } ] }, { - "fullUrl": "urn:uuid:5b2461b4-1c63-5b4f-b75f-d9f0f8e78d0c", + "fullUrl": "urn:uuid:8e816e78-fd5c-5b4e-a484-d8857f500ee9", "resource": { "resourceType": "Provenance", "meta": { @@ -238,10 +299,13 @@ ] }, "target": [ - { "reference": "urn:uuid:3e90f50b-622a-53e2-9479-a27ada726090" } + { "reference": "urn:uuid:0b62f555-261a-5479-9ac2-0dadbd6f7e8e" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-19T22:40:00.000-07:00", + "end": "2026-08-20T06:55:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -265,7 +329,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e575699a-b0df-5dc5-9d5a-e1ee8f319c1d" + "reference": "urn:uuid:68e28913-839c-5382-9fb8-1ea55562017c" } } ], @@ -282,7 +346,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:RKGg7_SVjLJ-TW2IEyKdFAqEFIyLZ6mZjuKs4CBBC9U" } }, @@ -299,7 +363,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:ca2701fb-c3c9-5019-9a86-da7b9aa683d2" + "reference": "urn:uuid:2925eb1d-36f7-5b5e-9e6d-2f600daff950" } } ] @@ -318,8 +382,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:G3eA3tIQdES8WikO3gyu5o7DnXbrq-Rc2xFFRMP9KVA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:lBoe86yaz5D_sun17Xsa0iEZJbrKau_ETHD3FZR_rcE" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json b/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json index 325514b7..cfd32aaf 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-respiratory-rate-average.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:23" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:a1134f0f-22c8-5c88-add6-5fb90cabf8c2", + "fullUrl": "urn:uuid:b166548f-a20b-5b7c-8c50-7c246007d01b", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:TrGlFtP5wdk-NzsSnMruKHD25VHWmapGR8rUdWfjLEk" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:r10WUFXbDXvxtZpsCniA4V0TxQygNJFC5AEv23Q2oXU" } ], @@ -108,14 +108,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:r10WUFXbDXvxtZpsCniA4V0TxQygNJFC5AEv23Q2oXU" } } ] }, { - "fullUrl": "urn:uuid:a4e868e2-8d17-5b8b-9a65-d26a146f5af0", + "fullUrl": "urn:uuid:6f0f7080-cbd9-5a41-b36a-e6f27a13b9d6", "resource": { "resourceType": "Device", "meta": { @@ -133,8 +133,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Suyzh8Jk_ZVJrdhLmLMe8ydUYu_IJuKhg8MVfBTTsjw" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:M20NHgSieB4ZIRw3Ip4FDjKAblP-vLRvsin2hHbmYj8" } ], "status": "active", @@ -157,14 +157,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Suyzh8Jk_ZVJrdhLmLMe8ydUYu_IJuKhg8MVfBTTsjw" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:M20NHgSieB4ZIRw3Ip4FDjKAblP-vLRvsin2hHbmYj8" } } ] }, { - "fullUrl": "urn:uuid:119259a9-5f5d-5b2b-b86d-f71052244ad1", + "fullUrl": "urn:uuid:e3ab7d73-ee27-52b6-bd3b-8561f2df566b", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:D6wj_60lXG13kYKKNIjklsHlQqygyqtSkcPGiwmRxj0" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:D6wj_60lXG13kYKKNIjklsHlQqygyqtSkcPGiwmRxj0" + } + } + ] + }, + { + "fullUrl": "urn:uuid:d17174f9-e63a-559e-9a78-adc5fe3db0dc", "resource": { "resourceType": "Device", "meta": { @@ -182,8 +240,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Z2HsC_lKlXKDL6bi7wIzi2ovDucUm2-W2e0TLsG-WI0" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:KXb0ksB2ShjuofJtvZJELVPTyi4J0X6CH4jKXCa9G4I" } ], "status": "active", @@ -206,7 +264,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:e3ab7d73-ee27-52b6-bd3b-8561f2df566b" + } }, "extension": [ { @@ -220,14 +281,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Z2HsC_lKlXKDL6bi7wIzi2ovDucUm2-W2e0TLsG-WI0" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:KXb0ksB2ShjuofJtvZJELVPTyi4J0X6CH4jKXCa9G4I" } } ] }, { - "fullUrl": "urn:uuid:5a2b779a-59f5-5c57-8ca8-410e6bebc4c0", + "fullUrl": "urn:uuid:a24e8ca2-193e-5455-92c4-2b5a33175178", "resource": { "resourceType": "Provenance", "meta": { @@ -236,10 +297,13 @@ ] }, "target": [ - { "reference": "urn:uuid:a1134f0f-22c8-5c88-add6-5fb90cabf8c2" } + { "reference": "urn:uuid:b166548f-a20b-5b7c-8c50-7c246007d01b" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-19T22:40:00.000-07:00", + "end": "2026-08-20T06:55:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -263,7 +327,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:119259a9-5f5d-5b2b-b86d-f71052244ad1" + "reference": "urn:uuid:d17174f9-e63a-559e-9a78-adc5fe3db0dc" } } ], @@ -280,7 +344,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:TrGlFtP5wdk-NzsSnMruKHD25VHWmapGR8rUdWfjLEk" } }, @@ -297,7 +361,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:a4e868e2-8d17-5b8b-9a65-d26a146f5af0" + "reference": "urn:uuid:6f0f7080-cbd9-5a41-b36a-e6f27a13b9d6" } } ] @@ -316,8 +380,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:2N9RBu-o6BhoV6evxl2ZIn8X-bRd76K7CyySv3zfNuE" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:3qxW-q490ZPen87O0m39onEgaAtUuQRE4qEXDvjUd5I" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json b/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json index c7454a24..4a499cee 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-skin-temperature.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:24" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:83c3b3f4-b249-5e84-a42f-680a9c7cb75c", + "fullUrl": "urn:uuid:ef8f79ea-db97-5d7d-8bcb-ad1b3f4391e2", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:jnbU2fWZpeemaU1jjrjbCQ6EkOKBh5itBxi1bLLujW0" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:fdLtyB1vt86GnGXA2wJPWN5tMekIYkeKXWnBPMvtvsc" } ], @@ -97,14 +97,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:fdLtyB1vt86GnGXA2wJPWN5tMekIYkeKXWnBPMvtvsc" } } ] }, { - "fullUrl": "urn:uuid:64c16cac-7015-51b2-92e2-6bc3ac450a3a", + "fullUrl": "urn:uuid:18e87396-e6f2-5591-af68-4a768d579d1a", "resource": { "resourceType": "Device", "meta": { @@ -122,8 +122,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:xBHRY4vXBxZ2fEDb8AicBvFVjYOStAf_p_DKiN7rnoo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:xAxYV-HxyTA_5RKSu1SSCI7w52RgrFdFgX3Jue6F9Ao" } ], "status": "active", @@ -146,14 +146,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:xBHRY4vXBxZ2fEDb8AicBvFVjYOStAf_p_DKiN7rnoo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:xAxYV-HxyTA_5RKSu1SSCI7w52RgrFdFgX3Jue6F9Ao" } } ] }, { - "fullUrl": "urn:uuid:648c018e-0d55-5039-987a-08ad126aeac9", + "fullUrl": "urn:uuid:56c60a20-1411-5289-9d58-60b5cdbc83ca", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:vFxgs6NUEiFjU67XeOt768YfmI6khzxleZLPpdKVFpo" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:vFxgs6NUEiFjU67XeOt768YfmI6khzxleZLPpdKVFpo" + } + } + ] + }, + { + "fullUrl": "urn:uuid:694e02be-1055-5081-a613-4a816ca59e78", "resource": { "resourceType": "Device", "meta": { @@ -171,8 +229,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uZEfoSsNnjS4K9JmUnCoUz_vO2fD_l8IPkRhi946zPo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Sh7BEyRgoT8EupYgA5h5h61sfs1JvqpWC2r_6FvtWpE" } ], "status": "active", @@ -195,7 +253,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:56c60a20-1411-5289-9d58-60b5cdbc83ca" + } }, "extension": [ { @@ -209,14 +270,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uZEfoSsNnjS4K9JmUnCoUz_vO2fD_l8IPkRhi946zPo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Sh7BEyRgoT8EupYgA5h5h61sfs1JvqpWC2r_6FvtWpE" } } ] }, { - "fullUrl": "urn:uuid:5097c006-bede-5794-b1b9-ccebfcba4748", + "fullUrl": "urn:uuid:c2548847-938a-5d40-b12f-4065048240b6", "resource": { "resourceType": "Provenance", "meta": { @@ -225,10 +286,10 @@ ] }, "target": [ - { "reference": "urn:uuid:83c3b3f4-b249-5e84-a42f-680a9c7cb75c" } + { "reference": "urn:uuid:ef8f79ea-db97-5d7d-8bcb-ad1b3f4391e2" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-21T03:41:12.000-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -252,7 +313,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:648c018e-0d55-5039-987a-08ad126aeac9" + "reference": "urn:uuid:694e02be-1055-5081-a613-4a816ca59e78" } } ], @@ -269,7 +330,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:jnbU2fWZpeemaU1jjrjbCQ6EkOKBh5itBxi1bLLujW0" } }, @@ -286,7 +347,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:64c16cac-7015-51b2-92e2-6bc3ac450a3a" + "reference": "urn:uuid:18e87396-e6f2-5591-af68-4a768d579d1a" } } ] @@ -305,8 +366,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:MtBt0Wkhm18_pyA_rsaK91FIZ0_QarfjD9ZKfe1L4U0" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:qSMCEPnY3fyxmXsOywoJnc5UK6WFTvymrLG3WcwBJVE" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json index 70d174d6..d1bf15b2 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-awake-duration.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:25" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:e63294c3-551a-5e44-8555-92a844716e30", + "fullUrl": "urn:uuid:35e5973a-f411-5cbe-9d85-0a189b1a5fe4", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:Ox1oQ-oDGcR1WotanmbfWhWK2iBcfBG6TEDfZmgHBm4" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:RrQhI3fnS7enR0ln-yibj1hlouMBhA3GI8L9NVWkON4" } ], @@ -110,14 +110,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:RrQhI3fnS7enR0ln-yibj1hlouMBhA3GI8L9NVWkON4" } } ] }, { - "fullUrl": "urn:uuid:3900cdcb-8c1a-55b9-b0dd-b02fb6023801", + "fullUrl": "urn:uuid:599a890d-7b0b-59d2-b634-f484acf2a77b", "resource": { "resourceType": "Device", "meta": { @@ -135,8 +135,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:jxJJDBZztrLVbB7pyk4VKRDVrbOCbEIqcdIdqJmFsJw" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:atd4W_IFLbVWWuplc1BQrOQD12ov3CimIYS1kcfBNss" } ], "status": "active", @@ -159,14 +159,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:jxJJDBZztrLVbB7pyk4VKRDVrbOCbEIqcdIdqJmFsJw" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:atd4W_IFLbVWWuplc1BQrOQD12ov3CimIYS1kcfBNss" } } ] }, { - "fullUrl": "urn:uuid:c57e5208-b0a4-5ac2-bef1-6c4eefa43bc8", + "fullUrl": "urn:uuid:762ec914-2203-512e-8a6e-b8b01140c974", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:-jDXp5AsVnsLYbyjlgL_Fuxq0lARoJtoppZsMRgmoxg" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:-jDXp5AsVnsLYbyjlgL_Fuxq0lARoJtoppZsMRgmoxg" + } + } + ] + }, + { + "fullUrl": "urn:uuid:b213a210-3fe3-50c8-af92-b38ae3804f0d", "resource": { "resourceType": "Device", "meta": { @@ -184,8 +242,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:N8JAO-8OSs3AhGY2IxA7UNiM9whJhJqGnzFplOsRHOo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:sTREkM9-0Wt1mKcowX1U-yE24Vs02MJVrrOTys2Mi1w" } ], "status": "active", @@ -208,7 +266,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:762ec914-2203-512e-8a6e-b8b01140c974" + } }, "extension": [ { @@ -222,14 +283,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:N8JAO-8OSs3AhGY2IxA7UNiM9whJhJqGnzFplOsRHOo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:sTREkM9-0Wt1mKcowX1U-yE24Vs02MJVrrOTys2Mi1w" } } ] }, { - "fullUrl": "urn:uuid:88a35473-0b28-556c-8673-32b1acf1ce4e", + "fullUrl": "urn:uuid:c65cab7a-d6fc-5f59-a0b6-20fd3e410bf9", "resource": { "resourceType": "Provenance", "meta": { @@ -238,10 +299,13 @@ ] }, "target": [ - { "reference": "urn:uuid:e63294c3-551a-5e44-8555-92a844716e30" } + { "reference": "urn:uuid:35e5973a-f411-5cbe-9d85-0a189b1a5fe4" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-19T22:40:00.000-07:00", + "end": "2026-08-20T06:55:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -265,7 +329,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:c57e5208-b0a4-5ac2-bef1-6c4eefa43bc8" + "reference": "urn:uuid:b213a210-3fe3-50c8-af92-b38ae3804f0d" } } ], @@ -282,7 +346,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:Ox1oQ-oDGcR1WotanmbfWhWK2iBcfBG6TEDfZmgHBm4" } }, @@ -299,7 +363,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:3900cdcb-8c1a-55b9-b0dd-b02fb6023801" + "reference": "urn:uuid:599a890d-7b0b-59d2-b634-f484acf2a77b" } } ] @@ -318,8 +382,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:SiMEd3qZlkyTXV_qGX4ZkIvuHd8Fm1jNouSrwNjZJ-I" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:2xmyj4zuA90-VJbRT61t_Y76upFZaJASSmWBkuJvTCo" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json b/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json index f3353bd3..c94983a2 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-duration.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:26" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:c7d05f45-e906-5225-ac65-22dc09975c6a", + "fullUrl": "urn:uuid:9e6e590a-8211-582a-a243-fb0ce715433b", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:tq2KjCGUlwWO9GMzdQoVswuS6YBF6h5Wpd1BnbXhhYc" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:BnHqjF7z-fvb4wgudNxtBNc9e0Td37yuHakVXYwIpdA" } ], @@ -100,14 +100,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:BnHqjF7z-fvb4wgudNxtBNc9e0Td37yuHakVXYwIpdA" } } ] }, { - "fullUrl": "urn:uuid:83d5b122-5954-54c7-b4b2-c0fa6e8ef9b6", + "fullUrl": "urn:uuid:c3d6482c-12a5-587c-ad9e-1635fdb43977", "resource": { "resourceType": "Device", "meta": { @@ -125,8 +125,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:eAmZ5wOIQlO_MYAqnpwJRFjvli30bYvkdRtDO1wMfOQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:zMNJk4ULKQZIp_Sd-IQOrorCyc3ep8yZ5ph1N1C1VIg" } ], "status": "active", @@ -149,14 +149,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:eAmZ5wOIQlO_MYAqnpwJRFjvli30bYvkdRtDO1wMfOQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:zMNJk4ULKQZIp_Sd-IQOrorCyc3ep8yZ5ph1N1C1VIg" } } ] }, { - "fullUrl": "urn:uuid:6eae3163-c8bf-5af0-a2aa-898f30fedbaa", + "fullUrl": "urn:uuid:5a80e4cd-1a9b-5723-9810-11a91054e164", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:TBlB1KHK-CKZ3GykZ-fTtDI5aj-DfIrzNwptAcGGNCw" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:TBlB1KHK-CKZ3GykZ-fTtDI5aj-DfIrzNwptAcGGNCw" + } + } + ] + }, + { + "fullUrl": "urn:uuid:341db27c-9cb4-53e9-8251-cbd7ee617ba7", "resource": { "resourceType": "Device", "meta": { @@ -174,8 +232,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:7MURadTgoXGjqOMEPmM18FYdUWAp08tL56ZbkpMj6HA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:DiNAjmfplt5hzokmdaIqaQVb0Ai-Kk_jat8g3XYGwiI" } ], "status": "active", @@ -198,7 +256,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:5a80e4cd-1a9b-5723-9810-11a91054e164" + } }, "extension": [ { @@ -212,14 +273,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:7MURadTgoXGjqOMEPmM18FYdUWAp08tL56ZbkpMj6HA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:DiNAjmfplt5hzokmdaIqaQVb0Ai-Kk_jat8g3XYGwiI" } } ] }, { - "fullUrl": "urn:uuid:5e0b87cc-e153-5c3e-a848-1732a6dd7702", + "fullUrl": "urn:uuid:e8b37081-a900-5561-9ca5-85aeaf675f68", "resource": { "resourceType": "Provenance", "meta": { @@ -228,10 +289,13 @@ ] }, "target": [ - { "reference": "urn:uuid:c7d05f45-e906-5225-ac65-22dc09975c6a" } + { "reference": "urn:uuid:9e6e590a-8211-582a-a243-fb0ce715433b" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-19T23:00:00.000-07:00", + "end": "2026-08-20T06:30:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -255,7 +319,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:6eae3163-c8bf-5af0-a2aa-898f30fedbaa" + "reference": "urn:uuid:341db27c-9cb4-53e9-8251-cbd7ee617ba7" } } ], @@ -272,7 +336,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:tq2KjCGUlwWO9GMzdQoVswuS6YBF6h5Wpd1BnbXhhYc" } }, @@ -289,7 +353,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:83d5b122-5954-54c7-b4b2-c0fa6e8ef9b6" + "reference": "urn:uuid:c3d6482c-12a5-587c-ad9e-1635fdb43977" } } ] @@ -308,8 +372,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:KOdHjEeddjWKfXuVEGFAN1GhoEkZv3s2YSypNw3sJ40" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:gkjEjs6VAbtBLTwymJGqFQ1lUBjGOc7RPblGdw0vcVE" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json b/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json index 349cc35c..f1195d84 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-sleep-heart-rate.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:27" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:7f60454b-9cc2-58ee-bedd-defb9560013f", + "fullUrl": "urn:uuid:825d4e11-3fb3-5092-841e-8f5f50569b6d", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:gOtALovyEyY_gqwlVxmn-BvfUaxigGpRasfr7UORnlM" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:v_p29kjZUa7EaT8C-ggI4-rA-62O_X1K0UchTFhf7YU" } ], @@ -113,14 +113,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:v_p29kjZUa7EaT8C-ggI4-rA-62O_X1K0UchTFhf7YU" } } ] }, { - "fullUrl": "urn:uuid:5c611825-6542-516b-ba90-66684d349d39", + "fullUrl": "urn:uuid:953fcc70-882e-5233-b170-71c597b14634", "resource": { "resourceType": "Device", "meta": { @@ -138,8 +138,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:dNoKM-UEFi5MYBVp7UEtHG2Ao0--GXrzOSQuQxAxeRg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:cn-WuDOwh62NCJg-8v3jqHBqncmn22JgG72bvzFNtAQ" } ], "status": "active", @@ -162,14 +162,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:dNoKM-UEFi5MYBVp7UEtHG2Ao0--GXrzOSQuQxAxeRg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:cn-WuDOwh62NCJg-8v3jqHBqncmn22JgG72bvzFNtAQ" } } ] }, { - "fullUrl": "urn:uuid:cf7a92be-643f-504b-b60b-8f1c24c2aa5f", + "fullUrl": "urn:uuid:4400ca02-2eb6-5014-8839-89c297804544", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:zXYFvGhXl2QDtwUoaATRcGiZVSGs_VcboGNeDqCNr9k" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:zXYFvGhXl2QDtwUoaATRcGiZVSGs_VcboGNeDqCNr9k" + } + } + ] + }, + { + "fullUrl": "urn:uuid:27864751-63f1-50b5-9a06-9179557412c9", "resource": { "resourceType": "Device", "meta": { @@ -187,8 +245,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:MDAetuhZk3l44pAAh5PaWGoW9IO9PGRm6X1ch4jI94M" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:-z3S9bytnnafRWafU9nt6TArtwKhJepfR01posmjKZk" } ], "status": "active", @@ -211,7 +269,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:4400ca02-2eb6-5014-8839-89c297804544" + } }, "extension": [ { @@ -225,14 +286,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:MDAetuhZk3l44pAAh5PaWGoW9IO9PGRm6X1ch4jI94M" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:-z3S9bytnnafRWafU9nt6TArtwKhJepfR01posmjKZk" } } ] }, { - "fullUrl": "urn:uuid:4c4d3370-e8d3-5721-a321-9a6d8edc8abc", + "fullUrl": "urn:uuid:71aad173-6ccb-57cf-bcc3-ad477796f631", "resource": { "resourceType": "Provenance", "meta": { @@ -241,10 +302,13 @@ ] }, "target": [ - { "reference": "urn:uuid:7f60454b-9cc2-58ee-bedd-defb9560013f" } + { "reference": "urn:uuid:825d4e11-3fb3-5092-841e-8f5f50569b6d" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-19T22:40:00.000-07:00", + "end": "2026-08-20T06:55:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -268,7 +332,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:cf7a92be-643f-504b-b60b-8f1c24c2aa5f" + "reference": "urn:uuid:27864751-63f1-50b5-9a06-9179557412c9" } } ], @@ -285,7 +349,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:gOtALovyEyY_gqwlVxmn-BvfUaxigGpRasfr7UORnlM" } }, @@ -302,7 +366,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:5c611825-6542-516b-ba90-66684d349d39" + "reference": "urn:uuid:953fcc70-882e-5233-b170-71c597b14634" } } ] @@ -321,8 +385,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:DA75S6ZIPeJEPB6N1IgISQ79mrz3BS0sfaQ_CPg_4Bo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:29avwAtoBfsg-GCIBMDj3LWmJUjb-iXHf05ilqaFBkE" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-step-count.json b/packages/fhir/fixtures/conformance/resources/mobile-step-count.json index a62330da..e373cb5d 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-step-count.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-step-count.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:28" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:def6a4c8-a9f0-5d9b-a512-98d5dbfca87a", + "fullUrl": "urn:uuid:a6b4120d-20ea-5ef5-a3fa-ee43e3844535", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:9zGYMkV1hVhzIUOw3odddQ_0JDsGiR_ZOl7_sV4ylaY" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:8mwhbk99yf3qpijHJ2VWzxAsXnypD6VVECucMVPuirw" } ], @@ -105,14 +105,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:8mwhbk99yf3qpijHJ2VWzxAsXnypD6VVECucMVPuirw" } } ] }, { - "fullUrl": "urn:uuid:0a836d40-fd1f-5d14-a41b-a8525341c001", + "fullUrl": "urn:uuid:849f7d2b-1362-5f5a-8b5a-e2685045b99f", "resource": { "resourceType": "Device", "meta": { @@ -130,8 +130,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:xaoyrihBfXy4uJJ28nRdUxKMsq08tNIl26Ucwjp7iQQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:QOCzfW47Chhd-Wz885dcd_9rUEJJ0nhik5MtTk4uU6E" } ], "status": "active", @@ -154,14 +154,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:xaoyrihBfXy4uJJ28nRdUxKMsq08tNIl26Ucwjp7iQQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:QOCzfW47Chhd-Wz885dcd_9rUEJJ0nhik5MtTk4uU6E" } } ] }, { - "fullUrl": "urn:uuid:456ac9d1-6a74-5fd7-9ca9-282783f29731", + "fullUrl": "urn:uuid:70896cca-f0aa-5b2f-bb51-f743c4374705", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ZUExz5u7YSKWGCmAPTfXk9-c5kpIQuoZiMJQ6kua90k" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ZUExz5u7YSKWGCmAPTfXk9-c5kpIQuoZiMJQ6kua90k" + } + } + ] + }, + { + "fullUrl": "urn:uuid:87a31c96-667a-5e93-a672-dff124ccf971", "resource": { "resourceType": "Device", "meta": { @@ -179,8 +237,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:-_UQxypI0SjyF6fEwjQ5odDh_ER349OWAWVuEytoLMM" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:HoxXZ8nHNEQCBUBWkHKANpOQpTTDtUmiQDOi7D2NUT8" } ], "status": "active", @@ -203,7 +261,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:70896cca-f0aa-5b2f-bb51-f743c4374705" + } }, "extension": [ { @@ -217,14 +278,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:-_UQxypI0SjyF6fEwjQ5odDh_ER349OWAWVuEytoLMM" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:HoxXZ8nHNEQCBUBWkHKANpOQpTTDtUmiQDOi7D2NUT8" } } ] }, { - "fullUrl": "urn:uuid:19777a0c-5c43-567f-b887-e9ec799d80bb", + "fullUrl": "urn:uuid:ee218fab-03d9-5ccd-b22f-70665b9b5a1b", "resource": { "resourceType": "Provenance", "meta": { @@ -233,10 +294,13 @@ ] }, "target": [ - { "reference": "urn:uuid:def6a4c8-a9f0-5d9b-a512-98d5dbfca87a" } + { "reference": "urn:uuid:a6b4120d-20ea-5ef5-a3fa-ee43e3844535" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-20T08:00:00.000-07:00", + "end": "2026-08-20T09:00:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -260,7 +324,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:456ac9d1-6a74-5fd7-9ca9-282783f29731" + "reference": "urn:uuid:87a31c96-667a-5e93-a672-dff124ccf971" } } ], @@ -277,7 +341,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:9zGYMkV1hVhzIUOw3odddQ_0JDsGiR_ZOl7_sV4ylaY" } }, @@ -294,7 +358,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:0a836d40-fd1f-5d14-a41b-a8525341c001" + "reference": "urn:uuid:849f7d2b-1362-5f5a-8b5a-e2685045b99f" } } ] @@ -313,8 +377,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:cD_VWhvlrj683Wch8scpXL5DtlefkyZA-WfO372_inM" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:Ubk_sqdckkDysqDlYX6QMPSOZyT_C0HUrMZgpgerG3E" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json b/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json index 7d549f24..390927d6 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-vo2-max.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:29" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:f0075728-43f1-5fe6-95d9-53613a722202", + "fullUrl": "urn:uuid:e6989d07-90a6-5209-ad39-ac7a2479e58d", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:tfPeQl3pTiKfgjBUofMUoVbrLlwsNZn1cHtIxccehpY" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:byw_l-KxsVtgYYiKT-e5xUOjsnJOeELyiUftu7HeVCA" } ], @@ -102,14 +102,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:byw_l-KxsVtgYYiKT-e5xUOjsnJOeELyiUftu7HeVCA" } } ] }, { - "fullUrl": "urn:uuid:48c66d02-a1aa-540f-acf7-c768a7c96979", + "fullUrl": "urn:uuid:60c51de9-91ed-5906-acb5-b4340e25d327", "resource": { "resourceType": "Device", "meta": { @@ -127,8 +127,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:mEOIRyxLXC_8bNJ1cmx0JcKaPGXE2oW4a0PL094BZWk" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Qivhk_8IOzYVL5_3cw7ZIbteHkzmRen6ziAcSIWLM8A" } ], "status": "active", @@ -151,14 +151,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:mEOIRyxLXC_8bNJ1cmx0JcKaPGXE2oW4a0PL094BZWk" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Qivhk_8IOzYVL5_3cw7ZIbteHkzmRen6ziAcSIWLM8A" } } ] }, { - "fullUrl": "urn:uuid:d4b2afc0-724c-5834-aced-2254893693c5", + "fullUrl": "urn:uuid:1e3bb60c-e8d1-50cd-b568-5aacb1f576b9", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:cn4HBabSITYE1mdyHZTmApp5rPYXCDNkP23PHk26S68" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:cn4HBabSITYE1mdyHZTmApp5rPYXCDNkP23PHk26S68" + } + } + ] + }, + { + "fullUrl": "urn:uuid:1a6ed802-3a4f-5f1c-9512-7768decfa8a0", "resource": { "resourceType": "Device", "meta": { @@ -176,8 +234,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:JUAIgKxQnM_ad5eD24yDcecDG_XelEy6eEdzZKT7zi0" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:RansLzkRb12oEZ9mZnu99LQLWmQerBCCOPUNu0Tgq-U" } ], "status": "active", @@ -200,7 +258,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:1e3bb60c-e8d1-50cd-b568-5aacb1f576b9" + } }, "extension": [ { @@ -214,14 +275,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:JUAIgKxQnM_ad5eD24yDcecDG_XelEy6eEdzZKT7zi0" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:RansLzkRb12oEZ9mZnu99LQLWmQerBCCOPUNu0Tgq-U" } } ] }, { - "fullUrl": "urn:uuid:1e759f0a-1feb-5565-b08e-4dbe3e9b53d2", + "fullUrl": "urn:uuid:b8a4224b-39ce-5c89-a178-ef41ba6be5dc", "resource": { "resourceType": "Provenance", "meta": { @@ -230,10 +291,10 @@ ] }, "target": [ - { "reference": "urn:uuid:f0075728-43f1-5fe6-95d9-53613a722202" } + { "reference": "urn:uuid:e6989d07-90a6-5209-ad39-ac7a2479e58d" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T09:15:00.000-07:00", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -257,7 +318,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:d4b2afc0-724c-5834-aced-2254893693c5" + "reference": "urn:uuid:1a6ed802-3a4f-5f1c-9512-7768decfa8a0" } } ], @@ -274,7 +335,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:tfPeQl3pTiKfgjBUofMUoVbrLlwsNZn1cHtIxccehpY" } }, @@ -291,7 +352,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:48c66d02-a1aa-540f-acf7-c768a7c96979" + "reference": "urn:uuid:60c51de9-91ed-5906-acb5-b4340e25d327" } } ] @@ -310,8 +371,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:ri56YyNp3A9fJTTp-1IagZQJnuquHZ6s4vQx60awN9I" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:mBJPR64Mq4hEqOFBMwL4zVTZqzvkXbzb4fv6bPOvtYs" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/mobile-workout.json b/packages/fhir/fixtures/conformance/resources/mobile-workout.json index cd4096b8..0d037b44 100644 --- a/packages/fhir/fixtures/conformance/resources/mobile-workout.json +++ b/packages/fhir/fixtures/conformance/resources/mobile-workout.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:30" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:39ed2a93-60f9-5643-a707-d4da21771ca3", + "fullUrl": "urn:uuid:42ec2b24-87d7-5357-91d8-d113f0ad05e9", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:LcqDQSNcMDxugFlPHpH5RFXlEN4jj61vsIebOIwHBus" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:7Xrr8q_a0KNfW6JdKupGPUOm4HTozWQ99TksbjKEvYc" } ], @@ -107,14 +107,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:7Xrr8q_a0KNfW6JdKupGPUOm4HTozWQ99TksbjKEvYc" } } ] }, { - "fullUrl": "urn:uuid:fda6fbb7-3989-5f51-8167-3a6114856c2a", + "fullUrl": "urn:uuid:187f1fcd-acd2-5188-a29a-1e476e811aa9", "resource": { "resourceType": "Device", "meta": { @@ -132,8 +132,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:6nMZv2pJPKEo82zRsPoCm8_Bzt1S1EZ_AyEFy1KGgtA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:MFjVEosI351-L9VgoxThfIlc8W-QCKtTZiJKoGW2CIg" } ], "status": "active", @@ -156,14 +156,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:6nMZv2pJPKEo82zRsPoCm8_Bzt1S1EZ_AyEFy1KGgtA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:MFjVEosI351-L9VgoxThfIlc8W-QCKtTZiJKoGW2CIg" } } ] }, { - "fullUrl": "urn:uuid:01837f45-5907-5a36-86c4-e518cc98d3de", + "fullUrl": "urn:uuid:b3b5b292-2fea-5693-86b0-183d793e582f", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:FM158mUrdn4NsnocaS1ImbhxnBjeSv_dDNWw7XOwokw" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:FM158mUrdn4NsnocaS1ImbhxnBjeSv_dDNWw7XOwokw" + } + } + ] + }, + { + "fullUrl": "urn:uuid:301af109-3dc5-5680-86f4-f758c5614d2d", "resource": { "resourceType": "Device", "meta": { @@ -181,8 +239,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:a7Dc9F2riqMjx8rq2NTJLmiF4GBdJSbO4Rtj9kp1njc" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:CQKMYCUrpKPPUi8XNH2datHlWyH5dYkiD4QJx-Jw8Bc" } ], "status": "active", @@ -205,7 +263,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:b3b5b292-2fea-5693-86b0-183d793e582f" + } }, "extension": [ { @@ -219,14 +280,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:a7Dc9F2riqMjx8rq2NTJLmiF4GBdJSbO4Rtj9kp1njc" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:CQKMYCUrpKPPUi8XNH2datHlWyH5dYkiD4QJx-Jw8Bc" } } ] }, { - "fullUrl": "urn:uuid:5f6e4358-2bf6-5f07-b679-ac7a5bf91aac", + "fullUrl": "urn:uuid:a33e212d-c202-55be-8072-0ca4de340ac0", "resource": { "resourceType": "Provenance", "meta": { @@ -235,10 +296,13 @@ ] }, "target": [ - { "reference": "urn:uuid:39ed2a93-60f9-5643-a707-d4da21771ca3" } + { "reference": "urn:uuid:42ec2b24-87d7-5357-91d8-d113f0ad05e9" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-20T17:00:00.000-07:00", + "end": "2026-08-20T17:45:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -262,7 +326,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:01837f45-5907-5a36-86c4-e518cc98d3de" + "reference": "urn:uuid:301af109-3dc5-5680-86f4-f758c5614d2d" } } ], @@ -279,7 +343,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:LcqDQSNcMDxugFlPHpH5RFXlEN4jj61vsIebOIwHBus" } }, @@ -296,7 +360,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:fda6fbb7-3989-5f51-8167-3a6114856c2a" + "reference": "urn:uuid:187f1fcd-acd2-5188-a29a-1e476e811aa9" } } ] @@ -315,8 +379,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:W9MmnB630oKz8k7NTcsS7eag8L4yDQnnpOFHDf-m1oo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:hsczulWLzmKZHpRPZoRPNEagCVCrXtiXKM0qowbrZTE" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json b/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json index d761711c..e47b366e 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json +++ b/packages/fhir/fixtures/conformance/resources/provider-google-health-api-resting-heart-rate-daily-average.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:31" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:0286e181-761e-5a07-931f-2e415f5ef041", + "fullUrl": "urn:uuid:d327ccfe-8706-5bea-ab77-98b1805afd19", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:40Iy1wmaK8u1RiYFs3w-vdPd5r_vC9_FOJcZm9L47Ug" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:bOgI-QR3GvHruf1pWLzIRIPn3UomHEkWtri0pIvy0X0" } ], @@ -115,14 +115,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:bOgI-QR3GvHruf1pWLzIRIPn3UomHEkWtri0pIvy0X0" } } ] }, { - "fullUrl": "urn:uuid:1c9af85c-9462-5fbb-b19b-619207d49ddc", + "fullUrl": "urn:uuid:b2726e8a-ca71-5b9f-9d3b-2ff1aef319cd", "resource": { "resourceType": "Device", "meta": { @@ -140,8 +140,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:TqpEGThlZqYZypD6eQEFZXoaz7En7wSNZ2Phvr0Kigg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:odaDKWEhYyJJoSVgglUvW2le0zlMttgyUvLgxcx6oRE" } ], "status": "active", @@ -164,14 +164,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:TqpEGThlZqYZypD6eQEFZXoaz7En7wSNZ2Phvr0Kigg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:odaDKWEhYyJJoSVgglUvW2le0zlMttgyUvLgxcx6oRE" } } ] }, { - "fullUrl": "urn:uuid:4f0e4237-13f6-5a5d-92af-aa70cea16d8d", + "fullUrl": "urn:uuid:6964fe64-fbb6-51c3-bc01-207535445775", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:K0lpbKvuoaueavRiLWMOeFH7vHzyYCPLUDm9adntiJ8" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:K0lpbKvuoaueavRiLWMOeFH7vHzyYCPLUDm9adntiJ8" + } + } + ] + }, + { + "fullUrl": "urn:uuid:825273ce-47ea-5481-a5ae-85801971af72", "resource": { "resourceType": "Device", "meta": { @@ -189,8 +247,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:ZZTtK-U3HfYobIKdA52R7jROS-LXygWsb6GS_HKq55I" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:nQ19EVWvtBn0DJMYRcKvaCnCLfK303rgHWuz2tADusk" } ], "status": "active", @@ -213,7 +271,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:6964fe64-fbb6-51c3-bc01-207535445775" + } }, "extension": [ { @@ -227,14 +288,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:ZZTtK-U3HfYobIKdA52R7jROS-LXygWsb6GS_HKq55I" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:nQ19EVWvtBn0DJMYRcKvaCnCLfK303rgHWuz2tADusk" } } ] }, { - "fullUrl": "urn:uuid:fe869ae8-658e-50c6-a4d5-a3ea19fed148", + "fullUrl": "urn:uuid:48db3921-a921-55cc-b7ac-244bd0eb76a0", "resource": { "resourceType": "Provenance", "meta": { @@ -243,10 +304,13 @@ ] }, "target": [ - { "reference": "urn:uuid:0286e181-761e-5a07-931f-2e415f5ef041" } + { "reference": "urn:uuid:d327ccfe-8706-5bea-ab77-98b1805afd19" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-20T00:00:00.000-07:00", + "end": "2026-08-21T00:00:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -270,7 +334,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:4f0e4237-13f6-5a5d-92af-aa70cea16d8d" + "reference": "urn:uuid:825273ce-47ea-5481-a5ae-85801971af72" } } ], @@ -287,7 +351,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:40Iy1wmaK8u1RiYFs3w-vdPd5r_vC9_FOJcZm9L47Ug" } }, @@ -304,7 +368,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:1c9af85c-9462-5fbb-b19b-619207d49ddc" + "reference": "urn:uuid:b2726e8a-ca71-5b9f-9d3b-2ff1aef319cd" } } ] @@ -323,8 +387,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:3WWEy8RBZ7hJLE3Mr8nXzyIpx6REeHiokAzs_WIkRP8" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:9FneScq8CwYTLNEL-Wc5Zmj6ppE1ynJLH_SVfrF1TJI" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json b/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json index 276b0443..ca79e4ef 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-daily-activity.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:48" }, "timestamp": "2026-08-21T07:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:455c32a1-29f0-51c6-8fbc-99d9b1ffbcd1", + "fullUrl": "urn:uuid:1b2092de-65d2-557c-bb09-d0428afb9efd", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:ZW-l9DWDo6F8KQtTUw0c0jARFGJw7IVvv_uUABMaLL8" } ], @@ -105,14 +105,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:ZW-l9DWDo6F8KQtTUw0c0jARFGJw7IVvv_uUABMaLL8" } } ] }, { - "fullUrl": "urn:uuid:5966d687-aa4f-5965-a4b2-cf84a9c6f248", + "fullUrl": "urn:uuid:4d357fea-15c3-502a-94c1-c75d3b07cc2b", "resource": { "resourceType": "Observation", "meta": { @@ -148,7 +148,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc" }, { @@ -160,7 +160,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:jLcaQl4VlDZK-a6tYFi6RzphgjIdgSocnwNXiIKnNug" } ], @@ -203,14 +203,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:jLcaQl4VlDZK-a6tYFi6RzphgjIdgSocnwNXiIKnNug" } } ] }, { - "fullUrl": "urn:uuid:88799d8b-82b2-5ba0-94b5-96c13acd9f8a", + "fullUrl": "urn:uuid:98892fbc-00f7-5197-b19b-1febc944fd7a", "resource": { "resourceType": "Observation", "meta": { @@ -246,7 +246,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc" }, { @@ -258,7 +258,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:E45pGFqeE8kQDlMSBXc05YscVQdR0EFm3jgZWqrGP4k" } ], @@ -296,14 +296,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:E45pGFqeE8kQDlMSBXc05YscVQdR0EFm3jgZWqrGP4k" } } ] }, { - "fullUrl": "urn:uuid:d32c76f1-7a31-5342-b7c8-cd9f20064de3", + "fullUrl": "urn:uuid:597e56ad-431c-5c9a-8a89-421230bbc5e5", "resource": { "resourceType": "Device", "meta": { @@ -321,8 +321,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:P0jj07qi0PyO4vEU1lcP36UlMqKaO-mxMgv1ZKNKBYI" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:tjrRKdhZ2_2ieOq-hi1KIJJAeoAgxfpXwYXujZAmuVk" } ], "status": "active", @@ -345,14 +345,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:P0jj07qi0PyO4vEU1lcP36UlMqKaO-mxMgv1ZKNKBYI" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:tjrRKdhZ2_2ieOq-hi1KIJJAeoAgxfpXwYXujZAmuVk" } } ] }, { - "fullUrl": "urn:uuid:70d10730-ee46-5c11-b27b-274a19a1c9b8", + "fullUrl": "urn:uuid:d1549615-c0f7-5bfc-bf03-964558dbe7a9", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:4_kMBzJ1ka3CLB4O63MOMFcNlgu3hDxASdTDIUmq1OA" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:4_kMBzJ1ka3CLB4O63MOMFcNlgu3hDxASdTDIUmq1OA" + } + } + ] + }, + { + "fullUrl": "urn:uuid:4434a1df-f25e-5f07-8358-8d2b77648691", "resource": { "resourceType": "Device", "meta": { @@ -370,8 +428,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:ERW6WdlhAGDHJ1V_q9pZsma1U6S_NzLxrFsIf4CE-SQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Yf8BP1-0zkIxSahkKxYAoE5LeeHRBkjJs7unIPIGWA8" } ], "status": "active", @@ -394,7 +452,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:d1549615-c0f7-5bfc-bf03-964558dbe7a9" + } }, "extension": [ { @@ -408,14 +469,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:ERW6WdlhAGDHJ1V_q9pZsma1U6S_NzLxrFsIf4CE-SQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Yf8BP1-0zkIxSahkKxYAoE5LeeHRBkjJs7unIPIGWA8" } } ] }, { - "fullUrl": "urn:uuid:b4b754f4-c374-5391-8a3e-edf1ab5e7dd5", + "fullUrl": "urn:uuid:8b8cdbcf-9364-5617-8f09-633eec7c9a50", "resource": { "resourceType": "Provenance", "meta": { @@ -424,12 +485,15 @@ ] }, "target": [ - { "reference": "urn:uuid:455c32a1-29f0-51c6-8fbc-99d9b1ffbcd1" }, - { "reference": "urn:uuid:5966d687-aa4f-5965-a4b2-cf84a9c6f248" }, - { "reference": "urn:uuid:88799d8b-82b2-5ba0-94b5-96c13acd9f8a" } + { "reference": "urn:uuid:1b2092de-65d2-557c-bb09-d0428afb9efd" }, + { "reference": "urn:uuid:4d357fea-15c3-502a-94c1-c75d3b07cc2b" }, + { "reference": "urn:uuid:98892fbc-00f7-5197-b19b-1febc944fd7a" } ], - "occurredDateTime": "2026-08-21T07:00:00Z", - "recorded": "2026-08-21T07:02:00Z", + "occurredPeriod": { + "start": "2026-08-20T00:00:00.000-07:00", + "end": "2026-08-21T00:00:00.000-07:00" + }, + "recorded": "2026-08-21T07:03:00Z", "activity": { "coding": [ { @@ -453,7 +517,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:70d10730-ee46-5c11-b27b-274a19a1c9b8" + "reference": "urn:uuid:4434a1df-f25e-5f07-8358-8d2b77648691" } } ], @@ -470,7 +534,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:drLDP7VU7oTYBfQqbZ0zAUtfRFO-RWfgtlR6Fys8Yvc" } }, @@ -487,7 +551,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:d32c76f1-7a31-5342-b7c8-cd9f20064de3" + "reference": "urn:uuid:597e56ad-431c-5c9a-8a89-421230bbc5e5" } } ] @@ -506,8 +570,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:BsM8PuZ48RNmemYKpVpQEZCPDNRPRIzR5GqIBLy8p6g" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:VeVOX9fPyIsMvHegg2WZacU-ewTLJHm6g5uN7LyB8qg" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json index 77f91540..e392951c 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-cardiovascular-age.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:32" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:8f4caaf7-2b81-5387-aecb-51048bd560df", + "fullUrl": "urn:uuid:6634dc4f-1c2e-51ba-8659-8ae4aabed2b3", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:6JYnSRBq-28f00cb2qTI45a4PwviE2ZkDZ0CgfxOBu8" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:VkvVjJoPr21ib2OR1ukyFv4PEwmfnfIADgZkrdI0OAY" } ], @@ -105,14 +105,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:VkvVjJoPr21ib2OR1ukyFv4PEwmfnfIADgZkrdI0OAY" } } ] }, { - "fullUrl": "urn:uuid:bfa210b1-746d-5e96-82a7-0ca4ab299873", + "fullUrl": "urn:uuid:cfc572dd-2630-595b-bca8-dc7da7a699d5", "resource": { "resourceType": "Device", "meta": { @@ -130,8 +130,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:h5Rb3MFdNKfsjqqCI-RcuHTVXXud55bMyIae_ByOKKk" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:38kYlMRiVmn8mv76WEzjzFEEGvtzahCM3jppquO0tFo" } ], "status": "active", @@ -154,14 +154,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:h5Rb3MFdNKfsjqqCI-RcuHTVXXud55bMyIae_ByOKKk" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:38kYlMRiVmn8mv76WEzjzFEEGvtzahCM3jppquO0tFo" } } ] }, { - "fullUrl": "urn:uuid:b7c37e2b-ba62-562d-94e4-7c034814ef4e", + "fullUrl": "urn:uuid:cdcba7bb-7e45-5701-9a79-ea3d9857f077", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ED6S3LwFU9ztRqanYssI51SqhQoAbPMdIOwAzlhQxXE" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ED6S3LwFU9ztRqanYssI51SqhQoAbPMdIOwAzlhQxXE" + } + } + ] + }, + { + "fullUrl": "urn:uuid:85bb9026-2bbd-5e79-a3af-11ad2e744e9b", "resource": { "resourceType": "Device", "meta": { @@ -179,8 +237,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OHeAUXdlghyg_Gho66VlpfMOcbKOwauPDAaCiOlaaDU" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Lh_lvoWhr66-7YM4mBQ5Mnzd_k42cM202OsB2ZQGT3c" } ], "status": "active", @@ -203,7 +261,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:cdcba7bb-7e45-5701-9a79-ea3d9857f077" + } }, "extension": [ { @@ -217,14 +278,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OHeAUXdlghyg_Gho66VlpfMOcbKOwauPDAaCiOlaaDU" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Lh_lvoWhr66-7YM4mBQ5Mnzd_k42cM202OsB2ZQGT3c" } } ] }, { - "fullUrl": "urn:uuid:eb84eba4-4718-5bb5-b808-07e09b09e7fa", + "fullUrl": "urn:uuid:4b21402a-50a6-547d-a490-6a04429b9364", "resource": { "resourceType": "Provenance", "meta": { @@ -233,10 +294,13 @@ ] }, "target": [ - { "reference": "urn:uuid:8f4caaf7-2b81-5387-aecb-51048bd560df" } + { "reference": "urn:uuid:6634dc4f-1c2e-51ba-8659-8ae4aabed2b3" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-20T00:00:00.000-07:00", + "end": "2026-08-21T00:00:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -260,7 +324,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:b7c37e2b-ba62-562d-94e4-7c034814ef4e" + "reference": "urn:uuid:85bb9026-2bbd-5e79-a3af-11ad2e744e9b" } } ], @@ -277,7 +341,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:6JYnSRBq-28f00cb2qTI45a4PwviE2ZkDZ0CgfxOBu8" } }, @@ -294,7 +358,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:bfa210b1-746d-5e96-82a7-0ca4ab299873" + "reference": "urn:uuid:cfc572dd-2630-595b-bca8-dc7da7a699d5" } } ] @@ -313,8 +377,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:c_XcuO0rRflq1E3Kny_pHsLUXqP-JCMIfXlxfwArKoo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:a1zEzTYimeUx7832dfccIN2NAM9mW_wy9QrprYCyGpw" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json index 3a30b281..4bc94a31 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json +++ b/packages/fhir/fixtures/conformance/resources/provider-oura-oura-readiness-score.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:33" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:13fc40fe-24a3-59f6-8182-8674de79f99f", + "fullUrl": "urn:uuid:d38b95d8-7d5b-5ae4-9bae-495304e3b61f", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:fd_6B30JQm1RurdhENC10i8LahBJt8rk4pjXN5m97HA" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:8_DgUefPBkmNsGzXuKk6xRHRGSabA-vhJw_Ts1OaULo" } ], @@ -105,14 +105,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:8_DgUefPBkmNsGzXuKk6xRHRGSabA-vhJw_Ts1OaULo" } } ] }, { - "fullUrl": "urn:uuid:d4919db0-78eb-549d-b6e6-3b49d2ec7e3b", + "fullUrl": "urn:uuid:63b736c1-5459-5590-b346-ccc86a0c95b3", "resource": { "resourceType": "Device", "meta": { @@ -130,8 +130,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:kNqCR8LM6Ge_iHvz9Zpqo0miCqPAoZpsUY6XQ5cToDw" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:n9wGrhBkJcfGPrjTqEA6FwoAj3dBPia2Xn9FNW1decI" } ], "status": "active", @@ -154,14 +154,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:kNqCR8LM6Ge_iHvz9Zpqo0miCqPAoZpsUY6XQ5cToDw" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:n9wGrhBkJcfGPrjTqEA6FwoAj3dBPia2Xn9FNW1decI" } } ] }, { - "fullUrl": "urn:uuid:a0aaca97-70da-5ccb-9510-dcdb4d5f7088", + "fullUrl": "urn:uuid:f9facabc-b1c6-500f-901f-11d5be249fcd", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:LBNa3KDjxDlKYmsEiUYcBfSYamKTETx4hr5K0FN-vrc" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:LBNa3KDjxDlKYmsEiUYcBfSYamKTETx4hr5K0FN-vrc" + } + } + ] + }, + { + "fullUrl": "urn:uuid:abb15ac1-1f99-57fb-a10f-fa422139adbe", "resource": { "resourceType": "Device", "meta": { @@ -179,8 +237,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:hgLwQrXtczhHU7Bcj5uX7S2DF4vJWnYiwrOzsDboju8" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:oZYyW4nSnQC11xb4akXSYFyveK99Z5JybDhYQ1nsTac" } ], "status": "active", @@ -203,7 +261,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:f9facabc-b1c6-500f-901f-11d5be249fcd" + } }, "extension": [ { @@ -217,14 +278,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:hgLwQrXtczhHU7Bcj5uX7S2DF4vJWnYiwrOzsDboju8" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:oZYyW4nSnQC11xb4akXSYFyveK99Z5JybDhYQ1nsTac" } } ] }, { - "fullUrl": "urn:uuid:669a50f2-373b-5d47-a5af-fc52ba9c11c9", + "fullUrl": "urn:uuid:228b8a89-25f2-5ee4-88b4-45cf5158300e", "resource": { "resourceType": "Provenance", "meta": { @@ -233,10 +294,13 @@ ] }, "target": [ - { "reference": "urn:uuid:13fc40fe-24a3-59f6-8182-8674de79f99f" } + { "reference": "urn:uuid:d38b95d8-7d5b-5ae4-9bae-495304e3b61f" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-20T00:00:00.000-07:00", + "end": "2026-08-21T00:00:00.000-07:00" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -260,7 +324,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:a0aaca97-70da-5ccb-9510-dcdb4d5f7088" + "reference": "urn:uuid:abb15ac1-1f99-57fb-a10f-fa422139adbe" } } ], @@ -277,7 +341,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:fd_6B30JQm1RurdhENC10i8LahBJt8rk4pjXN5m97HA" } }, @@ -294,7 +358,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:d4919db0-78eb-549d-b6e6-3b49d2ec7e3b" + "reference": "urn:uuid:63b736c1-5459-5590-b346-ccc86a0c95b3" } } ] @@ -313,8 +377,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:UwgSxMJ26fI73RjvXWouraGX9p0_Il45jxNbZK_aHaM" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:dLuhmsEQVodJqWOa2DK24AURjHEamqPWX71jJD6u66Q" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json b/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json index e19aded1..217fe2d6 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json +++ b/packages/fhir/fixtures/conformance/resources/provider-primary-output-retraction.json @@ -8,13 +8,71 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:53" }, "timestamp": "2026-08-22T12:02:00Z", "entry": [ { - "fullUrl": "urn:uuid:8ad483fc-efd8-53ad-99e6-7dfbf668b1c1", + "fullUrl": "urn:uuid:5d39290f-047d-587c-8776-87e8f3b33fc7", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:-ZCUXlHZ82R1s31S08OQgt6X2p0cPMdtjvuSakkQ3yU" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:-ZCUXlHZ82R1s31S08OQgt6X2p0cPMdtjvuSakkQ3yU" + } + } + ] + }, + { + "fullUrl": "urn:uuid:d04d135e-1ff8-5a32-b17d-f4b31d13dac5", "resource": { "resourceType": "Device", "meta": { @@ -32,8 +90,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:oEl7Uhouma2Mj5_8vjJxzY3wJXaIO8DnSJzEeOhy2o0" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:TsNjU0t3tTVovHc78aZeCZ2v7EUq7n-C3erO2OTfrFo" } ], "status": "active", @@ -56,7 +114,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:5d39290f-047d-587c-8776-87e8f3b33fc7" + } }, "extension": [ { @@ -70,14 +131,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:oEl7Uhouma2Mj5_8vjJxzY3wJXaIO8DnSJzEeOhy2o0" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:TsNjU0t3tTVovHc78aZeCZ2v7EUq7n-C3erO2OTfrFo" } } ] }, { - "fullUrl": "urn:uuid:ada84fca-188c-5dd5-90ba-27584527ef2f", + "fullUrl": "urn:uuid:b44a09f2-3620-5c60-91e3-fd1c34987bca", "resource": { "resourceType": "Provenance", "meta": { @@ -103,8 +164,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:U1GXWdV08ZaO2_cubyu1oaGFDXhnzLx8h_JwZVf6hUU" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:6wGOnwAruJbWSwuz3E9oztleNvGIc5M-tC85oOe6kmY" } }, { @@ -124,13 +185,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:LycACgbJZx-uRos8zb-0w0PEXMdM7LoW4Sx1IvbEOC0" } } ], "occurredDateTime": "2026-08-22T12:00:00Z", - "recorded": "2026-08-22T12:01:00Z", + "recorded": "2026-08-22T12:02:00Z", "activity": { "coding": [ { @@ -154,7 +215,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:8ad483fc-efd8-53ad-99e6-7dfbf668b1c1" + "reference": "urn:uuid:d04d135e-1ff8-5a32-b17d-f4b31d13dac5" } } ], @@ -171,7 +232,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:cBsbfJ2FTYUguk5OcwFuDMb03zuen7zrG5xlFAniFTo" } } @@ -190,8 +251,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:retraction-provenance:0:gv9DL_lwJx61nEPjo2CzyXAPyDpheZfgahVh9IKQ5kk" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:retraction-provenance:0:_Qzm7gg3unXeGEKQ5wRnlsaZveSOKRfL1t8jEeG_988" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json b/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json index 7f4bbf41..9c95838b 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json +++ b/packages/fhir/fixtures/conformance/resources/provider-source-artifact-retraction.json @@ -8,13 +8,71 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:54" }, "timestamp": "2026-08-22T13:02:00Z", "entry": [ { - "fullUrl": "urn:uuid:f3659235-64ff-5681-9783-5e712a989f97", + "fullUrl": "urn:uuid:0a5f2b94-7285-532d-9dad-7d2f6c4148c5", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:IWM_7Ri2kGnMYYFE105QlsHkpGUeDwxsZxBcJH1Dwvg" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:IWM_7Ri2kGnMYYFE105QlsHkpGUeDwxsZxBcJH1Dwvg" + } + } + ] + }, + { + "fullUrl": "urn:uuid:e1006fcb-4135-5c12-b816-1f98f936d767", "resource": { "resourceType": "Device", "meta": { @@ -32,8 +90,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:nEuRqgji46KZl6Zd4aPUEjeIbRhR7mJ96dA0q9RmA4U" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:4ezlfQvW81mflp9rZEF0-kmnoPkWJWe9rbgEBoBqEaM" } ], "status": "active", @@ -56,7 +114,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:0a5f2b94-7285-532d-9dad-7d2f6c4148c5" + } }, "extension": [ { @@ -70,14 +131,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:nEuRqgji46KZl6Zd4aPUEjeIbRhR7mJ96dA0q9RmA4U" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:4ezlfQvW81mflp9rZEF0-kmnoPkWJWe9rbgEBoBqEaM" } } ] }, { - "fullUrl": "urn:uuid:d7f49bdc-acce-543a-bf93-154117fe1854", + "fullUrl": "urn:uuid:c15d7585-b9d1-5470-a61a-6aee83d291d5", "resource": { "resourceType": "Provenance", "meta": { @@ -103,8 +164,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uocYwddFVtlKSSu89sdrCnhx7B1Zj4lX7iNLzaZjvAA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:7uqIqkrxFcJYzuEfaZFQOu6I9p2xh_uokJvuzqysQvU" } }, { @@ -124,13 +185,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:w7o_lcZdhv2y5mj96CWnefbS73I3tPoRvuSQG7Vaqao" } } ], "occurredDateTime": "2026-08-22T13:00:00Z", - "recorded": "2026-08-22T13:01:00Z", + "recorded": "2026-08-22T13:02:00Z", "activity": { "coding": [ { @@ -154,7 +215,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:f3659235-64ff-5681-9783-5e712a989f97" + "reference": "urn:uuid:e1006fcb-4135-5c12-b816-1f98f936d767" } } ], @@ -171,7 +232,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:Qm28JqWMxMvl2dsco1gdTLvUyKdg3S8qfrEX_4Z19KY" } } @@ -190,8 +251,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:retraction-provenance:0:uOqh_hD3XZxlouavDYtKr6PhYACOazZDgCcBLqQJjcI" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:retraction-provenance:0:ciQVEANtM5xn6PCjwF7jjVkhQ5kQSfzJ3Xs14vv-xaY" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-study-context.json b/packages/fhir/fixtures/conformance/resources/provider-study-context.json new file mode 100644 index 00000000..ee8cc252 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-study-context.json @@ -0,0 +1,527 @@ +{ + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "event" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", + "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:55" + }, + "timestamp": "2026-08-20T12:03:00Z", + "entry": [ + { + "fullUrl": "urn:uuid:fe562fcc-464b-5e05-9b19-19da3d0d9f5a", + "resource": { + "resourceType": "Patient", + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/patient-pseudonyms", + "value": "patient-example" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:maWuav_5Us--QBpuNU162BMa81sYHHXFxn8qXf_qrb8" + } + } + ] + }, + { + "fullUrl": "urn:uuid:7787bd54-c27f-5388-bab3-ff3c81fb0b42", + "resource": { + "resourceType": "Observation", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate", + "https://grovealliance.org/fhir/withings/StructureDefinition/withings-observation" + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider", + "valueCode": "withings" + }, + { + "url": "https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type", + "valueCode": "withings/getmeas:11" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:da4e343e-e99f-5126-bd98-0877fadbe8e3" + } + }, + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-recording-method", + "valueCoding": { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-recording-method", + "code": "automatically-recorded" + } + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/workflow-researchStudy", + "valueReference": { + "reference": "urn:uuid:d92f8de6-b2e3-56e2-8466-b9b3f1385b1a" + } + } + ], + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", + "value": "v0:fixture-key:1:rYuWQbuEPkUgtFs-LIcRlhkGQt2twvPb_OgucigeG8o" + }, + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", + "value": "v0:fixture-key:1:3DcJcezfh32lo4sCQGTxtEhNtFHFdGrWRdTDv78oKs0" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "vital-signs", + "display": "Vital Signs" + } + ], + "text": "Vital Signs" + } + ], + "code": { + "coding": [{ "system": "http://loinc.org", "code": "8867-4" }] + }, + "subject": { + "reference": "urn:uuid:fe562fcc-464b-5e05-9b19-19da3d0d9f5a" + }, + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "valueQuantity": { + "value": 72, + "unit": "beats/minute", + "system": "http://unitsofmeasure.org", + "code": "/min" + } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-output" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", + "value": "v0:fixture-key:1:3DcJcezfh32lo4sCQGTxtEhNtFHFdGrWRdTDv78oKs0" + } + } + ] + }, + { + "fullUrl": "urn:uuid:d92f8de6-b2e3-56e2-8466-b9b3f1385b1a", + "resource": { + "resourceType": "ResearchStudy", + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/studies", + "value": "grove-ts-conformance-study" + } + ], + "protocol": [ + { "reference": "urn:uuid:b036828c-adba-5a3b-a90a-3b65e3019615" } + ], + "status": "active" + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:research-study:0:b4M7vIJwP8o5N47bDT_3WvqhDoN7uhAgeto7OJ12BRg" + } + } + ] + }, + { + "fullUrl": "urn:uuid:b036828c-adba-5a3b-a90a-3b65e3019615", + "resource": { + "resourceType": "PlanDefinition", + "url": "https://grovealliance.org/fhir/testing/PlanDefinition/grove-ts-conformance", + "version": "1", + "status": "active" + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:plan-definition:0:5w7oTEeRviVEPkPOykmKWakxtrcEvShdpDuJIInpKZs" + } + } + ] + }, + { + "fullUrl": "urn:uuid:f6b82500-4480-514e-974f-e5f61f1083a6", + "resource": { + "resourceType": "ResearchSubject", + "identifier": [ + { + "system": "https://grovealliance.org/fhir/testing/enrollments", + "value": "grove-ts-conformance-enrollment" + } + ], + "status": "on-study", + "study": { + "reference": "urn:uuid:d92f8de6-b2e3-56e2-8466-b9b3f1385b1a" + }, + "individual": { + "reference": "urn:uuid:fe562fcc-464b-5e05-9b19-19da3d0d9f5a" + } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:research-subject:0:jtHoCJ89sJEng8CL-dWBaRRy2_YtYTdeyUBm9rIG-1I" + } + } + ] + }, + { + "fullUrl": "urn:uuid:47e0c6ad-063f-5cc7-a0d5-1fcf3e708cd4", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:lw9kSP3Jf0o7hQB55ikI9KztkmEVIdR7rontwAkFxtI" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Synthetic connected provider", + "type": "user-friendly-name" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:lw9kSP3Jf0o7hQB55ikI9KztkmEVIdR7rontwAkFxtI" + } + } + ] + }, + { + "fullUrl": "urn:uuid:c4c45c16-db9e-5c97-8603-ec8a7d3fb723", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:tdbQsEBGRgGtOfxA-wOgVF-TpenSFXGpOoWj3x_jfLs" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:tdbQsEBGRgGtOfxA-wOgVF-TpenSFXGpOoWj3x_jfLs" + } + } + ] + }, + { + "fullUrl": "urn:uuid:da4e343e-e99f-5126-bd98-0877fadbe8e3", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ntSwqSdG2gGqCG8swX5HBIW2EByIDEp0nbat2PPmmN4" + } + ], + "status": "active", + "deviceName": [ + { + "name": "Grove TypeScript conformance producer", + "type": "user-friendly-name" + } + ], + "version": [ + { + "type": { + "coding": [ + { + "system": "urn:iso:std:iso:11073:10101", + "code": "531975", + "display": "Software revision" + } + ] + }, + "value": "0.0.0" + } + ], + "parent": { + "reference": "urn:uuid:c4c45c16-db9e-5c97-8603-ec8a7d3fb723" + } + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ntSwqSdG2gGqCG8swX5HBIW2EByIDEp0nbat2PPmmN4" + } + } + ] + }, + { + "fullUrl": "urn:uuid:9d037ed7-da26-5801-89d2-e46baf500338", + "resource": { + "resourceType": "Provenance", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance" + ] + }, + "target": [ + { "reference": "urn:uuid:7787bd54-c27f-5388-bab3-ff3c81fb0b42" } + ], + "occurredDateTime": "2026-08-20T08:30:00.251-07:00", + "recorded": "2026-08-20T12:03:00Z", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "code": "transform", + "display": "Transform/Translate Record Lifecycle Event" + } + ], + "text": "Transform/Translate Record Lifecycle Event" + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "assembler", + "display": "Assembler" + } + ], + "text": "Assembler" + }, + "who": { + "reference": "urn:uuid:da4e343e-e99f-5126-bd98-0877fadbe8e3" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "source-record" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", + "value": "v0:fixture-key:1:rYuWQbuEPkUgtFs-LIcRlhkGQt2twvPb_OgucigeG8o" + } + }, + "agent": [ + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "code": "enterer", + "display": "Enterer" + } + ], + "text": "Enterer" + }, + "who": { + "reference": "urn:uuid:47e0c6ad-063f-5cc7-a0d5-1fcf3e708cd4" + } + } + ] + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:vw15dU50zvQGoVJACtDyH9hlQeydGlXFwj6W-gx5z8o" + } + } + ] + } + ], + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection" +} diff --git a/packages/fhir/fixtures/conformance/resources/provider-study-context.json.license b/packages/fhir/fixtures/conformance/resources/provider-study-context.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/provider-study-context.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json index c3391ff1..49bf6c95 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-body-fat-mass.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:35" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:3f31daca-6852-511c-8961-38d2cfba8936", + "fullUrl": "urn:uuid:3d14704e-dba8-508c-862a-03c256bec54c", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:6VQPvHniXH4sNDqhmZ0DqtG129Xl6pOZ353Kdqg0SwA" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:g9D8uYiqpZq0BrDkaP2tElDxS1h4zOwQ3OHIBNKXobg" } ], @@ -97,14 +97,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:g9D8uYiqpZq0BrDkaP2tElDxS1h4zOwQ3OHIBNKXobg" } } ] }, { - "fullUrl": "urn:uuid:0d8fb81e-efa0-583e-be2f-7d3fd70cf98b", + "fullUrl": "urn:uuid:cc08a473-bf8d-52cb-b29a-f30bc499d36e", "resource": { "resourceType": "Device", "meta": { @@ -122,8 +122,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:cgk--UOImI_WU47l-R2BGI_H4AqhrIEgrHXPC7dlUYA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:hD839h0NRXwcBZ7OJymHO71fMBc14tQsemEGZ0qXoas" } ], "status": "active", @@ -146,14 +146,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:cgk--UOImI_WU47l-R2BGI_H4AqhrIEgrHXPC7dlUYA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:hD839h0NRXwcBZ7OJymHO71fMBc14tQsemEGZ0qXoas" } } ] }, { - "fullUrl": "urn:uuid:16b24c42-c731-5ba7-8859-8c288834f0d4", + "fullUrl": "urn:uuid:8416ccb7-83ef-5b1a-b9be-59c8056ac926", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:fGq0UIuQ6bYgmoISIKWjUlC8HD4iew1FKtuZxnAQIjc" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:fGq0UIuQ6bYgmoISIKWjUlC8HD4iew1FKtuZxnAQIjc" + } + } + ] + }, + { + "fullUrl": "urn:uuid:971ee5b6-82ed-56eb-b59f-bb67f359c97e", "resource": { "resourceType": "Device", "meta": { @@ -171,8 +229,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:0qUilPLzuJbQGUboPRBuLJQsLcWYkqCI7t59qy7DoGA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:900_AoX7kmZQ8C_sOeiaNZSWI_0oDkS0Y4P97mzxlm0" } ], "status": "active", @@ -195,7 +253,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:8416ccb7-83ef-5b1a-b9be-59c8056ac926" + } }, "extension": [ { @@ -209,14 +270,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:0qUilPLzuJbQGUboPRBuLJQsLcWYkqCI7t59qy7DoGA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:900_AoX7kmZQ8C_sOeiaNZSWI_0oDkS0Y4P97mzxlm0" } } ] }, { - "fullUrl": "urn:uuid:fa17f5ee-019b-5f79-902f-1f8862771964", + "fullUrl": "urn:uuid:12da2a39-5830-5022-8c7f-3b75e3ccf65c", "resource": { "resourceType": "Provenance", "meta": { @@ -225,10 +286,10 @@ ] }, "target": [ - { "reference": "urn:uuid:3f31daca-6852-511c-8961-38d2cfba8936" } + { "reference": "urn:uuid:3d14704e-dba8-508c-862a-03c256bec54c" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T12:00:00.000Z", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -252,7 +313,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:16b24c42-c731-5ba7-8859-8c288834f0d4" + "reference": "urn:uuid:971ee5b6-82ed-56eb-b59f-bb67f359c97e" } } ], @@ -269,7 +330,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:6VQPvHniXH4sNDqhmZ0DqtG129Xl6pOZ353Kdqg0SwA" } }, @@ -286,7 +347,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:0d8fb81e-efa0-583e-be2f-7d3fd70cf98b" + "reference": "urn:uuid:cc08a473-bf8d-52cb-b29a-f30bc499d36e" } } ] @@ -305,8 +366,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:SO0lcltW7S-39s0UkVo_0Fu7iP7CJPFYIHdvHhS72do" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:eNBKIlKcmrv0M8WyFaxUVCftawT25mWPyrE411YpPWQ" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json b/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json index d7b69af8..5a80b348 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-corrected-qt-interval.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:42" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:f04531e4-5996-5afd-858d-ad251ceb62b5", + "fullUrl": "urn:uuid:26c9d43b-4f88-5763-b3e4-e18671290b9c", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:m1wV9ADh9_iaYO3rZmHmqYvvPh96-Fd9keJWZlCvf8Y" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:up2yrUsCU_7Cld1H_MYyI3dIGA9tbFvZ-lMm4M4tXVM" } ], @@ -97,14 +97,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:up2yrUsCU_7Cld1H_MYyI3dIGA9tbFvZ-lMm4M4tXVM" } } ] }, { - "fullUrl": "urn:uuid:0ffe260f-79f2-5f9d-a443-ad1a32aba472", + "fullUrl": "urn:uuid:b048ea1f-8fb9-5a67-8420-8e3c167c7a76", "resource": { "resourceType": "Device", "meta": { @@ -122,8 +122,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:t-vS893OvYOMIWpKbtoHlnxyBaAItckjwQUpOY_BSGM" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:KAc2tABiJva9I6ifz1LI7gmYT4bmdGetI-b__EomKG0" } ], "status": "active", @@ -146,14 +146,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:t-vS893OvYOMIWpKbtoHlnxyBaAItckjwQUpOY_BSGM" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:KAc2tABiJva9I6ifz1LI7gmYT4bmdGetI-b__EomKG0" } } ] }, { - "fullUrl": "urn:uuid:f31053bd-69da-509c-a8d7-d787b331d2a4", + "fullUrl": "urn:uuid:29791140-957b-551a-ae9e-930c34f334bc", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:T_5n7ojL_gPRapgvTpmJiGFL9CDT1WQr-i-gf9AFBWg" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:T_5n7ojL_gPRapgvTpmJiGFL9CDT1WQr-i-gf9AFBWg" + } + } + ] + }, + { + "fullUrl": "urn:uuid:d675cf1c-4b7f-5f32-b896-a0ed828a5e8e", "resource": { "resourceType": "Device", "meta": { @@ -171,8 +229,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:GjU_pI0bDFWbiQ0MOJBOCBXMM0COVm2SSbC4g8zawJ8" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:fhVOHsX6o_mhaxrCvk-B0pyotRNIvZAysomsJhFHOko" } ], "status": "active", @@ -195,7 +253,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:29791140-957b-551a-ae9e-930c34f334bc" + } }, "extension": [ { @@ -209,14 +270,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:GjU_pI0bDFWbiQ0MOJBOCBXMM0COVm2SSbC4g8zawJ8" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:fhVOHsX6o_mhaxrCvk-B0pyotRNIvZAysomsJhFHOko" } } ] }, { - "fullUrl": "urn:uuid:a459d197-d6f7-58fc-a973-98d610090925", + "fullUrl": "urn:uuid:021f8890-edb3-588a-9a83-f56b95a617c3", "resource": { "resourceType": "Provenance", "meta": { @@ -225,10 +286,10 @@ ] }, "target": [ - { "reference": "urn:uuid:f04531e4-5996-5afd-858d-ad251ceb62b5" } + { "reference": "urn:uuid:26c9d43b-4f88-5763-b3e4-e18671290b9c" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T12:00:00.000Z", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -252,7 +313,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:f31053bd-69da-509c-a8d7-d787b331d2a4" + "reference": "urn:uuid:d675cf1c-4b7f-5f32-b896-a0ed828a5e8e" } } ], @@ -269,7 +330,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:m1wV9ADh9_iaYO3rZmHmqYvvPh96-Fd9keJWZlCvf8Y" } }, @@ -286,7 +347,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:0ffe260f-79f2-5f9d-a443-ad1a32aba472" + "reference": "urn:uuid:b048ea1f-8fb9-5a67-8420-8e3c167c7a76" } } ] @@ -305,8 +366,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:PxaVHTEjWlHBji7_0S-rbJCMbGjxFbGCpfaYwTYKs_E" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:GAWzeZoXLBJD4DDc6h-NHbJlh_DsrWAHuuVzJLycuGg" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json index 2d9c9cd0..697a0d83 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-extracellular-water-mass.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:45" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:6c02980d-5df9-5a37-a030-a8599d758291", + "fullUrl": "urn:uuid:3dca6ee8-820b-5fe5-935f-69cd9b538f36", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:1HgX55BzSSTYkJN89C8SXc1c7-w6cI0mbCGaekNC1yw" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:ldxbCxa9ISJLN7kax9FiE80vGiw0WHIUyXPQZd3Sk4E" } ], @@ -102,14 +102,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:ldxbCxa9ISJLN7kax9FiE80vGiw0WHIUyXPQZd3Sk4E" } } ] }, { - "fullUrl": "urn:uuid:f86775dd-e51c-58bc-8b40-54c3a4c2704e", + "fullUrl": "urn:uuid:7533641f-97ce-5e20-917d-568dbfa54f95", "resource": { "resourceType": "Device", "meta": { @@ -127,8 +127,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Sk7a5vUo5Ke0h-wCaq8pkWlwBEg_lpUqKi0tbxwRpAg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Rfetc79XFC5YPHr5pajnWYiqySmvWb7inpcxqvAIJW4" } ], "status": "active", @@ -151,14 +151,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Sk7a5vUo5Ke0h-wCaq8pkWlwBEg_lpUqKi0tbxwRpAg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Rfetc79XFC5YPHr5pajnWYiqySmvWb7inpcxqvAIJW4" } } ] }, { - "fullUrl": "urn:uuid:1fe1f299-5178-556e-b4d9-6eec0d8d3ac9", + "fullUrl": "urn:uuid:edf7adfe-e0f8-574a-bf4f-30a3296244e0", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:4bdRZZdj7TjnRP8w6ZTZvGSYWiP2vXTtjMMa-G7iFTc" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:4bdRZZdj7TjnRP8w6ZTZvGSYWiP2vXTtjMMa-G7iFTc" + } + } + ] + }, + { + "fullUrl": "urn:uuid:4f7479b1-baa8-59a7-bb28-dabc184d87ce", "resource": { "resourceType": "Device", "meta": { @@ -176,8 +234,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:XfFTZ3nhXBEndRIdGRL2pS4gmMMzLXKGUxFQz81nb8w" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:YG3jJTKi1dxeFTLhm4PTngwXS-cb3k7boyFQj_fQnhc" } ], "status": "active", @@ -200,7 +258,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:edf7adfe-e0f8-574a-bf4f-30a3296244e0" + } }, "extension": [ { @@ -214,14 +275,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:XfFTZ3nhXBEndRIdGRL2pS4gmMMzLXKGUxFQz81nb8w" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:YG3jJTKi1dxeFTLhm4PTngwXS-cb3k7boyFQj_fQnhc" } } ] }, { - "fullUrl": "urn:uuid:c9daa5d4-de63-5373-9f0c-1dca919aef51", + "fullUrl": "urn:uuid:957a0eac-0626-5a6d-bf27-3ba329a95266", "resource": { "resourceType": "Provenance", "meta": { @@ -230,10 +291,10 @@ ] }, "target": [ - { "reference": "urn:uuid:6c02980d-5df9-5a37-a030-a8599d758291" } + { "reference": "urn:uuid:3dca6ee8-820b-5fe5-935f-69cd9b538f36" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T12:00:00.000Z", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -257,7 +318,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:1fe1f299-5178-556e-b4d9-6eec0d8d3ac9" + "reference": "urn:uuid:4f7479b1-baa8-59a7-bb28-dabc184d87ce" } } ], @@ -274,7 +335,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:1HgX55BzSSTYkJN89C8SXc1c7-w6cI0mbCGaekNC1yw" } }, @@ -291,7 +352,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:f86775dd-e51c-58bc-8b40-54c3a4c2704e" + "reference": "urn:uuid:7533641f-97ce-5e20-917d-568dbfa54f95" } } ] @@ -310,8 +371,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:oG1YrkySMKuoFgaHhPsjFpaaadNzDN512YXklTAqRAs" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:ZaXfICOnyDWPjtIRSWGZFSCnQzx262VLMADWaKDRKsU" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json index c89f50ac..6ca1b404 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-intracellular-water-mass.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:46" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:1ba6631d-8d9c-500d-b5e7-77d93c149d3a", + "fullUrl": "urn:uuid:db17b4c3-d8a9-5cb8-be7f-088d816480ee", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:QPmTxHXDUmbQmn5vhLVtyS_SyfCzJCTm93c03OxH-2o" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:AH-H6ncy4X1z2tEipFNSrXuHFuFP3-ud6YhWXKipRto" } ], @@ -102,14 +102,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:AH-H6ncy4X1z2tEipFNSrXuHFuFP3-ud6YhWXKipRto" } } ] }, { - "fullUrl": "urn:uuid:40a17b86-d18f-54cb-977c-d75351a7919d", + "fullUrl": "urn:uuid:b8fb7b23-0acc-595b-b886-994e9f42e76f", "resource": { "resourceType": "Device", "meta": { @@ -127,8 +127,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:B2gFdvvpWQxTB70sqX13q51Ty66_l_GDk-P3YrdDiSs" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:WQzDi5WjJ-0OJmcfO0Rq6I1thqXRjcj7jDToUVhez-E" } ], "status": "active", @@ -151,14 +151,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:B2gFdvvpWQxTB70sqX13q51Ty66_l_GDk-P3YrdDiSs" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:WQzDi5WjJ-0OJmcfO0Rq6I1thqXRjcj7jDToUVhez-E" } } ] }, { - "fullUrl": "urn:uuid:99db234c-97c6-5d64-91cf-41f4375d4bbc", + "fullUrl": "urn:uuid:af612534-1d45-5c5d-9b0c-ad48557f5f11", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ojG94Q1HAHmlOol5eKljFSxOc8qwPteY2VOWJvUw7MA" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ojG94Q1HAHmlOol5eKljFSxOc8qwPteY2VOWJvUw7MA" + } + } + ] + }, + { + "fullUrl": "urn:uuid:59738e30-0d68-5f9d-a1aa-d51089dcba08", "resource": { "resourceType": "Device", "meta": { @@ -176,8 +234,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:gCGVDG_iVGF7B6ZmWIN4DO4GRXcpOG-nyuXGpSqWL7g" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:lfeavgDBQS1sch0o8I_kbu3bNaYh1l9HIDzPGi6xygY" } ], "status": "active", @@ -200,7 +258,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:af612534-1d45-5c5d-9b0c-ad48557f5f11" + } }, "extension": [ { @@ -214,14 +275,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:gCGVDG_iVGF7B6ZmWIN4DO4GRXcpOG-nyuXGpSqWL7g" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:lfeavgDBQS1sch0o8I_kbu3bNaYh1l9HIDzPGi6xygY" } } ] }, { - "fullUrl": "urn:uuid:6646ef38-4230-5ed4-aa3b-b6b45e77d61f", + "fullUrl": "urn:uuid:e54002de-9c5e-5b3c-ad17-c095bc42f39d", "resource": { "resourceType": "Provenance", "meta": { @@ -230,10 +291,10 @@ ] }, "target": [ - { "reference": "urn:uuid:1ba6631d-8d9c-500d-b5e7-77d93c149d3a" } + { "reference": "urn:uuid:db17b4c3-d8a9-5cb8-be7f-088d816480ee" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T12:00:00.000Z", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -257,7 +318,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:99db234c-97c6-5d64-91cf-41f4375d4bbc" + "reference": "urn:uuid:59738e30-0d68-5f9d-a1aa-d51089dcba08" } } ], @@ -274,7 +335,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:QPmTxHXDUmbQmn5vhLVtyS_SyfCzJCTm93c03OxH-2o" } }, @@ -291,7 +352,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:40a17b86-d18f-54cb-977c-d75351a7919d" + "reference": "urn:uuid:b8fb7b23-0acc-595b-b886-994e9f42e76f" } } ] @@ -310,8 +371,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:L5XECi811lvx0O_j7row191VRz0FfxBuzkQCjFHewos" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:cDmpwuJv8KWMxjQRZLlfGr5OL3zfxcGqjRZwdL20RjM" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json b/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json index 5cf50822..01b6fb20 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-muscle-mass.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:36" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:30badcd5-097e-5e48-92d7-d24d96c1e1dd", + "fullUrl": "urn:uuid:0cb0156b-fccd-512e-910d-3c810c60f45e", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:Eh9FyyObehDndCz4NWKS92aPKvIf3zJvsPAdTGODtm0" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:dmuPKjctx8XrjSJddZ2tPQXXnDkWrfZLZLUkkvUTLKg" } ], @@ -97,14 +97,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:dmuPKjctx8XrjSJddZ2tPQXXnDkWrfZLZLUkkvUTLKg" } } ] }, { - "fullUrl": "urn:uuid:f4e6c723-efec-5fe6-9159-ce76a2ae15dd", + "fullUrl": "urn:uuid:1fef7d1f-a370-5932-9049-2aeadafe5c26", "resource": { "resourceType": "Device", "meta": { @@ -122,8 +122,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OVHkuQ0ProM33_M1f9lTxHrbw8xr3WsTWa9lwqluClA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:tO_ru09AFStgC_dK5TnQ2-ICObcLLoc4-hCkdF6MX_M" } ], "status": "active", @@ -146,14 +146,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:OVHkuQ0ProM33_M1f9lTxHrbw8xr3WsTWa9lwqluClA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:tO_ru09AFStgC_dK5TnQ2-ICObcLLoc4-hCkdF6MX_M" } } ] }, { - "fullUrl": "urn:uuid:83c978cc-422e-5180-b430-2fdce3eb6604", + "fullUrl": "urn:uuid:67fa8225-a0cf-5222-8aed-ef561f3aff68", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:utJ8JMrtKcd9Ab-8_pvqcLkfGBYcWl7ij2tKs8tv6Co" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:utJ8JMrtKcd9Ab-8_pvqcLkfGBYcWl7ij2tKs8tv6Co" + } + } + ] + }, + { + "fullUrl": "urn:uuid:d7709444-434a-57c7-ae67-eae03826777a", "resource": { "resourceType": "Device", "meta": { @@ -171,8 +229,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:hsOMQ8T6sLQBqj0qrt0InL91Oy3nF8DyD-AmaOzX97Y" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:aQcMxoTJopIaum-J8QTadEqdPvNMVteVW4pzR9OQLO0" } ], "status": "active", @@ -195,7 +253,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:67fa8225-a0cf-5222-8aed-ef561f3aff68" + } }, "extension": [ { @@ -209,14 +270,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:hsOMQ8T6sLQBqj0qrt0InL91Oy3nF8DyD-AmaOzX97Y" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:aQcMxoTJopIaum-J8QTadEqdPvNMVteVW4pzR9OQLO0" } } ] }, { - "fullUrl": "urn:uuid:145c28e1-8e1c-558e-b7ba-ee0fe6f9534a", + "fullUrl": "urn:uuid:5b379881-4a0a-5063-a5a3-c46d663d3811", "resource": { "resourceType": "Provenance", "meta": { @@ -225,10 +286,10 @@ ] }, "target": [ - { "reference": "urn:uuid:30badcd5-097e-5e48-92d7-d24d96c1e1dd" } + { "reference": "urn:uuid:0cb0156b-fccd-512e-910d-3c810c60f45e" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T12:00:00.000Z", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -252,7 +313,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:83c978cc-422e-5180-b430-2fdce3eb6604" + "reference": "urn:uuid:d7709444-434a-57c7-ae67-eae03826777a" } } ], @@ -269,7 +330,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:Eh9FyyObehDndCz4NWKS92aPKvIf3zJvsPAdTGODtm0" } }, @@ -286,7 +347,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:f4e6c723-efec-5fe6-9159-ce76a2ae15dd" + "reference": "urn:uuid:1fef7d1f-a370-5932-9049-2aeadafe5c26" } } ] @@ -305,8 +366,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:oDUf94G4-TotGjhBmFnu7C5iCGVf05D_KytEY3OOxwg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:gvegZwiJJjovFLjGkpGReTDNgxwYTezII6ETSeL8ZfI" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json b/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json index 4cca811f..1dcbea5c 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-pr-interval.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:40" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:849c5b45-d32b-59c4-b7ba-d61312d9ef27", + "fullUrl": "urn:uuid:c7fa6f70-a720-5a67-8ee4-ec224921960e", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:gPh7mylmekrCE9UJitdYyUQi_Tj1QXwbb8wHwoB9GYI" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:m3zt6QNnSRhBsJXMHQpVURB9vlHwD7YEAZ29DSSj6Nk" } ], @@ -97,14 +97,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:m3zt6QNnSRhBsJXMHQpVURB9vlHwD7YEAZ29DSSj6Nk" } } ] }, { - "fullUrl": "urn:uuid:6262253c-dd07-5d04-8579-92fbf81533a7", + "fullUrl": "urn:uuid:d9882464-d9d3-5366-8645-fcd617b065e0", "resource": { "resourceType": "Device", "meta": { @@ -122,8 +122,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:__ZuL0w8sk5Yz3yTgI7SC4XK9w44B9HXUj91cm1FsWQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:SXsIv8vUoYgTnJc6kE5Km-RSDyMelTzM-3Z698CfCYk" } ], "status": "active", @@ -146,14 +146,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:__ZuL0w8sk5Yz3yTgI7SC4XK9w44B9HXUj91cm1FsWQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:SXsIv8vUoYgTnJc6kE5Km-RSDyMelTzM-3Z698CfCYk" } } ] }, { - "fullUrl": "urn:uuid:e21dbf5c-7238-597a-b201-f6d7f835d951", + "fullUrl": "urn:uuid:d832bfba-4c8d-55bc-8410-7174a1939ccf", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:7MGRCsV7JWTUQFKi3otEqV0doU8mDUfxJvj9ROb3D34" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:7MGRCsV7JWTUQFKi3otEqV0doU8mDUfxJvj9ROb3D34" + } + } + ] + }, + { + "fullUrl": "urn:uuid:39a68b3a-8a59-56b7-b834-b147733534dc", "resource": { "resourceType": "Device", "meta": { @@ -171,8 +229,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:V8GTrHviGlTvyHN2OgpQSTynWLcnM-QC0_--aCfssRE" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:iHic532_V8PrWbFCIXhi6h0CqXJ3VYafmi60uzrgihU" } ], "status": "active", @@ -195,7 +253,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:d832bfba-4c8d-55bc-8410-7174a1939ccf" + } }, "extension": [ { @@ -209,14 +270,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:V8GTrHviGlTvyHN2OgpQSTynWLcnM-QC0_--aCfssRE" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:iHic532_V8PrWbFCIXhi6h0CqXJ3VYafmi60uzrgihU" } } ] }, { - "fullUrl": "urn:uuid:f7d48e17-92fe-51ad-a989-96c372ef8e88", + "fullUrl": "urn:uuid:9c30bade-78e8-54ab-877a-e6cd2b7cfd40", "resource": { "resourceType": "Provenance", "meta": { @@ -225,10 +286,10 @@ ] }, "target": [ - { "reference": "urn:uuid:849c5b45-d32b-59c4-b7ba-d61312d9ef27" } + { "reference": "urn:uuid:c7fa6f70-a720-5a67-8ee4-ec224921960e" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T12:00:00.000Z", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -252,7 +313,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:e21dbf5c-7238-597a-b201-f6d7f835d951" + "reference": "urn:uuid:39a68b3a-8a59-56b7-b834-b147733534dc" } } ], @@ -269,7 +330,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:gPh7mylmekrCE9UJitdYyUQi_Tj1QXwbb8wHwoB9GYI" } }, @@ -286,7 +347,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:6262253c-dd07-5d04-8579-92fbf81533a7" + "reference": "urn:uuid:d9882464-d9d3-5366-8645-fcd617b065e0" } } ] @@ -305,8 +366,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:haG6QJYpWmGVbQuTrZ6uNIIfwXKNK61ZDcIfsiHbXo4" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:Xnc8UjqF2c7_fJSoMJicNLDR5UWX0kX72pP3TTBLtXw" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json b/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json index 163bc646..3b2338e6 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-qrs-duration.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:39" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:d84ea885-e118-52f2-90b1-925f92afb702", + "fullUrl": "urn:uuid:ceed1c5c-acf1-5500-836f-7e8111665880", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:9BdypFvzThLDwoZaAkXJoWLWqDNqaNic5oAhD6zAb6w" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:YrLEi4lkIP19yRHhjL0N7US8Uho6xCZYLkZOqcq9CWY" } ], @@ -97,14 +97,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:YrLEi4lkIP19yRHhjL0N7US8Uho6xCZYLkZOqcq9CWY" } } ] }, { - "fullUrl": "urn:uuid:41ced2c7-02a2-57ec-aed8-d996f96f0345", + "fullUrl": "urn:uuid:883c97ed-922e-5414-ae85-72750379c8f2", "resource": { "resourceType": "Device", "meta": { @@ -122,8 +122,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:AtOWrf7z6ZVcyMJNO_vx-fIUBZUytKMW-PT_x0I2CiE" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:vCSSiUTtJvzBxYYeeVdoUWk7KXNo-dB9fCntdIWG7wk" } ], "status": "active", @@ -146,14 +146,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:AtOWrf7z6ZVcyMJNO_vx-fIUBZUytKMW-PT_x0I2CiE" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:vCSSiUTtJvzBxYYeeVdoUWk7KXNo-dB9fCntdIWG7wk" } } ] }, { - "fullUrl": "urn:uuid:a31e7d33-eec1-55a9-ba76-f7a2e115a4de", + "fullUrl": "urn:uuid:a0287a21-fdd7-522b-ba52-f7bdd6dce4e2", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:TvVOUUNYTSfNBKmf7aw6UI7TamWzGiOPmOOLS4rH1ps" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:TvVOUUNYTSfNBKmf7aw6UI7TamWzGiOPmOOLS4rH1ps" + } + } + ] + }, + { + "fullUrl": "urn:uuid:b7f4fe96-41be-5879-afb4-e3a4b9b69c56", "resource": { "resourceType": "Device", "meta": { @@ -171,8 +229,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:lp2Cnde-8YMwJBqwNUzgr5t4k8z_7ybfORUJagYVpEE" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:bcmhoEwRba_0u-hT05VFDEz6gvmKHwp-dfJ5LHSkFT0" } ], "status": "active", @@ -195,7 +253,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:a0287a21-fdd7-522b-ba52-f7bdd6dce4e2" + } }, "extension": [ { @@ -209,14 +270,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:lp2Cnde-8YMwJBqwNUzgr5t4k8z_7ybfORUJagYVpEE" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:bcmhoEwRba_0u-hT05VFDEz6gvmKHwp-dfJ5LHSkFT0" } } ] }, { - "fullUrl": "urn:uuid:0b45d042-6862-55bd-a464-fc2679ff3020", + "fullUrl": "urn:uuid:1339cbba-1a86-58ce-a5a4-34461560b76e", "resource": { "resourceType": "Provenance", "meta": { @@ -225,10 +286,10 @@ ] }, "target": [ - { "reference": "urn:uuid:d84ea885-e118-52f2-90b1-925f92afb702" } + { "reference": "urn:uuid:ceed1c5c-acf1-5500-836f-7e8111665880" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T12:00:00.000Z", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -252,7 +313,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:a31e7d33-eec1-55a9-ba76-f7a2e115a4de" + "reference": "urn:uuid:b7f4fe96-41be-5879-afb4-e3a4b9b69c56" } } ], @@ -269,7 +330,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:9BdypFvzThLDwoZaAkXJoWLWqDNqaNic5oAhD6zAb6w" } }, @@ -286,7 +347,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:41ced2c7-02a2-57ec-aed8-d996f96f0345" + "reference": "urn:uuid:883c97ed-922e-5414-ae85-72750379c8f2" } } ] @@ -305,8 +366,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:8ZRVTj4Udaeytn6DxmTcyAWuzCQGrpgFIJBmH4tDES4" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:u2ISMX_GZz3_FQItnukbu5Umaj_kV0AG1BLV5S_DV3E" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json b/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json index adba6f93..9485a3ae 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-qt-interval.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:41" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:2fbda5c3-1ad0-59b9-9331-388671178ead", + "fullUrl": "urn:uuid:139bdc56-265c-58f0-8d0f-bd98f7f6f3d2", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:SiSGZdCnIwML3G4TrUiYwjaz8Db2kjtvOJWFrQ7Y9v8" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:AbiOF6Y3Hvecmdt0vcY9O-tpBBvhuvJVPLPWe2VzN2s" } ], @@ -97,14 +97,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:AbiOF6Y3Hvecmdt0vcY9O-tpBBvhuvJVPLPWe2VzN2s" } } ] }, { - "fullUrl": "urn:uuid:2138c578-bb7d-5e64-a620-5bcc51797536", + "fullUrl": "urn:uuid:47786b25-8a07-56ed-bc99-2290cedc8e38", "resource": { "resourceType": "Device", "meta": { @@ -122,8 +122,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:99bYfKq38HdU_3V1w8_trVXSCQzB2ywUCIZBRKBwL6M" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:IKnDln62xP_twfUyKo4_jXQRN2LyCp0hBwjSkCEBa6Y" } ], "status": "active", @@ -146,14 +146,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:99bYfKq38HdU_3V1w8_trVXSCQzB2ywUCIZBRKBwL6M" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:IKnDln62xP_twfUyKo4_jXQRN2LyCp0hBwjSkCEBa6Y" } } ] }, { - "fullUrl": "urn:uuid:d46c3738-eda6-5a17-91b3-03bf1806962f", + "fullUrl": "urn:uuid:4bab1056-cbf0-55c7-9297-da06c3b90d2a", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:gSPrODw4MXZJttepC9ayav0tjBgqGyy_54KYTEHkOO0" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:gSPrODw4MXZJttepC9ayav0tjBgqGyy_54KYTEHkOO0" + } + } + ] + }, + { + "fullUrl": "urn:uuid:596b514c-a075-5a03-b6ad-8a9b5958e454", "resource": { "resourceType": "Device", "meta": { @@ -171,8 +229,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:luW2Yct587KQwti7mnsRdpVqPXZUeQXyyWRRwjH-Ogg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:g_Ow-3vAMxP9WWP5noZhg6iNouefpE3J5N66bfYMbQw" } ], "status": "active", @@ -195,7 +253,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:4bab1056-cbf0-55c7-9297-da06c3b90d2a" + } }, "extension": [ { @@ -209,14 +270,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:luW2Yct587KQwti7mnsRdpVqPXZUeQXyyWRRwjH-Ogg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:g_Ow-3vAMxP9WWP5noZhg6iNouefpE3J5N66bfYMbQw" } } ] }, { - "fullUrl": "urn:uuid:e246c232-e0fe-54df-9292-f2487cf88fe7", + "fullUrl": "urn:uuid:ca1f01d2-6afd-5604-b084-ccdde8c1f16d", "resource": { "resourceType": "Provenance", "meta": { @@ -225,10 +286,10 @@ ] }, "target": [ - { "reference": "urn:uuid:2fbda5c3-1ad0-59b9-9331-388671178ead" } + { "reference": "urn:uuid:139bdc56-265c-58f0-8d0f-bd98f7f6f3d2" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T12:00:00.000Z", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -252,7 +313,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:d46c3738-eda6-5a17-91b3-03bf1806962f" + "reference": "urn:uuid:596b514c-a075-5a03-b6ad-8a9b5958e454" } } ], @@ -269,7 +330,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:SiSGZdCnIwML3G4TrUiYwjaz8Db2kjtvOJWFrQ7Y9v8" } }, @@ -286,7 +347,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:2138c578-bb7d-5e64-a620-5bcc51797536" + "reference": "urn:uuid:47786b25-8a07-56ed-bc99-2290cedc8e38" } } ] @@ -305,8 +366,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:kyJnRRrDXnYDxIiriQDYk8XXJXNYwEwA6I9tMzf_AmQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:NBx1au8okHb9CW99R3lw0JmkSX7uSO4faOojgUzPFC0" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json index 8df04984..e41f02e7 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ecg.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:38" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:14899158-9250-565e-bebb-5eb5633aa2e4", + "fullUrl": "urn:uuid:c4a90665-8089-5d93-8652-be8f672b1488", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:ZxvReRdSivoUK5JDcmAqUlMR3LUZJvTNscfXuIReUBY" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:UIpn4q5L_fH_yipQV_n3rk8PKcagpVghtl1zhOUB53c" } ], @@ -104,14 +104,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:UIpn4q5L_fH_yipQV_n3rk8PKcagpVghtl1zhOUB53c" } } ] }, { - "fullUrl": "urn:uuid:4f1da7df-5e4e-5c3b-b239-c7ede7493c89", + "fullUrl": "urn:uuid:ef294ad9-d0f1-5a02-b5ae-041019364ae2", "resource": { "resourceType": "Device", "meta": { @@ -129,8 +129,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:UClwZV7svmpSmN8FNXWl-VfiXDrzMfqjK8sxfS26pyQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:J1aQLL5FVp2uA1S4DZQKGr2nHEBM4630fJQbgXTBLmw" } ], "status": "active", @@ -153,14 +153,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:UClwZV7svmpSmN8FNXWl-VfiXDrzMfqjK8sxfS26pyQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:J1aQLL5FVp2uA1S4DZQKGr2nHEBM4630fJQbgXTBLmw" } } ] }, { - "fullUrl": "urn:uuid:aabcc5f8-104d-53a7-a55f-91fabe26d699", + "fullUrl": "urn:uuid:582a8099-80ce-5543-8da8-bbc7970859ee", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:0ZrPg_eI9nEpRVAlpfClnMN0aCBxr0GzA_mD3YmWod0" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:0ZrPg_eI9nEpRVAlpfClnMN0aCBxr0GzA_mD3YmWod0" + } + } + ] + }, + { + "fullUrl": "urn:uuid:9102975b-ec7d-5b41-a870-ec8604f0d8d5", "resource": { "resourceType": "Device", "meta": { @@ -178,8 +236,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:O9Tw3fu-swdRGFWTe5R2t-hWL8JDHTm3UJGoSoxnn10" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:3GdJ1XjG7b5xgt3BOJASnpSMReyMmldc5We2z7PPyOY" } ], "status": "active", @@ -202,7 +260,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:582a8099-80ce-5543-8da8-bbc7970859ee" + } }, "extension": [ { @@ -216,14 +277,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:O9Tw3fu-swdRGFWTe5R2t-hWL8JDHTm3UJGoSoxnn10" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:3GdJ1XjG7b5xgt3BOJASnpSMReyMmldc5We2z7PPyOY" } } ] }, { - "fullUrl": "urn:uuid:bba7b490-1564-5e5b-9927-6b7e5b4cc63d", + "fullUrl": "urn:uuid:84db659c-984e-52ca-ad9f-73991c6b468e", "resource": { "resourceType": "Provenance", "meta": { @@ -232,10 +293,10 @@ ] }, "target": [ - { "reference": "urn:uuid:14899158-9250-565e-bebb-5eb5633aa2e4" } + { "reference": "urn:uuid:c4a90665-8089-5d93-8652-be8f672b1488" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T12:00:00.000Z", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -259,7 +320,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:aabcc5f8-104d-53a7-a55f-91fabe26d699" + "reference": "urn:uuid:9102975b-ec7d-5b41-a870-ec8604f0d8d5" } } ], @@ -276,7 +337,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:ZxvReRdSivoUK5JDcmAqUlMR3LUZJvTNscfXuIReUBY" } }, @@ -293,7 +354,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:4f1da7df-5e4e-5c3b-b239-c7ede7493c89" + "reference": "urn:uuid:ef294ad9-d0f1-5a02-b5ae-041019364ae2" } } ] @@ -312,8 +373,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:FY6d5Pc23vpiqS2qy2e5rvA4T6UzWja_ZjZp_nT80rQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:oUtLYhQm7cucX1YWu0qp67KwjKmKZtw8_kgPcCc6fCE" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json index 595cb9f7..7a1cb7fb 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-atrial-fibrillation-notification-ppg.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:43" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:5b1a9420-aa1a-55f3-ac61-2f721d0540d6", + "fullUrl": "urn:uuid:1fe4dded-74d3-5793-8a8e-acc3ae2a38c4", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:OUDYL-JBwPu9y4Z1U7Ql1-9C2TShwD4chzmFTmnrxNs" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:DhzbFybuD21ODCiEK2DB-fCJUfF3OOYQFk3UHpp7GSE" } ], @@ -104,14 +104,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:DhzbFybuD21ODCiEK2DB-fCJUfF3OOYQFk3UHpp7GSE" } } ] }, { - "fullUrl": "urn:uuid:731ad91c-6e4e-56ad-b35b-cf13bcd2a5f9", + "fullUrl": "urn:uuid:df459076-e570-5a0e-a010-89445e592ef1", "resource": { "resourceType": "Device", "meta": { @@ -129,8 +129,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:VOCwn-nw7Eu3UMotEURLN4inM8fMv7T9-_SeRWDjisQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:v8FS9eXzzGh-34f5VmXOTY6pE0vmBz4BQ_togI_ZU2Q" } ], "status": "active", @@ -153,14 +153,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:VOCwn-nw7Eu3UMotEURLN4inM8fMv7T9-_SeRWDjisQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:v8FS9eXzzGh-34f5VmXOTY6pE0vmBz4BQ_togI_ZU2Q" } } ] }, { - "fullUrl": "urn:uuid:2af9b110-443b-5976-af32-24f06b43b61a", + "fullUrl": "urn:uuid:d379aa5f-3a35-52d7-9a9e-5a8db74ed76a", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:YLfgTZAtL-HPQPSfSzS5-Fh-qwv6uCwipdaJKlPiNM8" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:YLfgTZAtL-HPQPSfSzS5-Fh-qwv6uCwipdaJKlPiNM8" + } + } + ] + }, + { + "fullUrl": "urn:uuid:082049ab-b8ae-5166-a44e-045134fedbf2", "resource": { "resourceType": "Device", "meta": { @@ -178,8 +236,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:vkwcQexNNJMwlm8JYiVoBTX1-5A5n6pyZS4MS9TugPQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:H4_Ey3WTIGweAeAN53YdI1mgABB1sBmQ1DsPvR8pcS8" } ], "status": "active", @@ -202,7 +260,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:d379aa5f-3a35-52d7-9a9e-5a8db74ed76a" + } }, "extension": [ { @@ -216,14 +277,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:vkwcQexNNJMwlm8JYiVoBTX1-5A5n6pyZS4MS9TugPQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:H4_Ey3WTIGweAeAN53YdI1mgABB1sBmQ1DsPvR8pcS8" } } ] }, { - "fullUrl": "urn:uuid:d36943e1-6026-5e6c-bb2e-a1b029503b86", + "fullUrl": "urn:uuid:46ae01c1-deab-5ed3-9929-adea6e484065", "resource": { "resourceType": "Provenance", "meta": { @@ -232,10 +293,10 @@ ] }, "target": [ - { "reference": "urn:uuid:5b1a9420-aa1a-55f3-ac61-2f721d0540d6" } + { "reference": "urn:uuid:1fe4dded-74d3-5793-8a8e-acc3ae2a38c4" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T12:00:00.000Z", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -259,7 +320,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:2af9b110-443b-5976-af32-24f06b43b61a" + "reference": "urn:uuid:082049ab-b8ae-5166-a44e-045134fedbf2" } } ], @@ -276,7 +337,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:OUDYL-JBwPu9y4Z1U7Ql1-9C2TShwD4chzmFTmnrxNs" } }, @@ -293,7 +354,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:731ad91c-6e4e-56ad-b35b-cf13bcd2a5f9" + "reference": "urn:uuid:df459076-e570-5a0e-a010-89445e592ef1" } } ] @@ -312,8 +373,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:VVL8mOlyjsCfZVr7MZV0ahVYEH5geZ52CSvU8dwBu74" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:1VYDcoqb8fvUUDUxc9vEyF2WIIVAGLKVeV97uu8e3rw" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json index 499076dc..76e12d87 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-nerve-health-score.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:44" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:5ddc75c4-ce94-5330-941e-52c9470adc55", + "fullUrl": "urn:uuid:bdd7c839-a088-5b25-af9b-194539baa74f", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:gknGkBm8LLih4l5bW2EJf0VMUE1-cJMRmVr4_eVDT0c" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:iKOlXQ-gWHTTGJe5x8oFaaYLnB2esy9gRXjQTIJvWXg" } ], @@ -102,14 +102,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:iKOlXQ-gWHTTGJe5x8oFaaYLnB2esy9gRXjQTIJvWXg" } } ] }, { - "fullUrl": "urn:uuid:d5cfaa1f-d8dc-5154-956b-79a2574fec38", + "fullUrl": "urn:uuid:e1db0a4a-35a4-52ae-8c0a-41d7cbc4b0b9", "resource": { "resourceType": "Device", "meta": { @@ -127,8 +127,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:N1m0pHHsgiv9tFv6kqHO7OVrcRcFzxQ11q3khjuDRj4" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:DsF63adNGr9vCSCi9YMzKocNmdN6hcqKcSNJmAiXqD8" } ], "status": "active", @@ -151,14 +151,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:N1m0pHHsgiv9tFv6kqHO7OVrcRcFzxQ11q3khjuDRj4" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:DsF63adNGr9vCSCi9YMzKocNmdN6hcqKcSNJmAiXqD8" } } ] }, { - "fullUrl": "urn:uuid:cf0c5dad-04d3-5a54-827e-c7e86fa32d28", + "fullUrl": "urn:uuid:8607ddd2-c9c6-5008-b71d-17d48386e636", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:dWPcEXOgj24PYjaZ32rZs9E_RJsWg6xIR0dsgtdfl9w" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:dWPcEXOgj24PYjaZ32rZs9E_RJsWg6xIR0dsgtdfl9w" + } + } + ] + }, + { + "fullUrl": "urn:uuid:871eac06-1646-5b1e-9261-2534f0ea1f59", "resource": { "resourceType": "Device", "meta": { @@ -176,8 +234,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Tt5TQF_t0Sa93jP9w3UcAfQBUfCyuTAWkm1zobHBye4" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:tCyzlrpA3lssmrc116D_knwbMJch4fICDH3AhpFY7-Y" } ], "status": "active", @@ -200,7 +258,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:8607ddd2-c9c6-5008-b71d-17d48386e636" + } }, "extension": [ { @@ -214,14 +275,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Tt5TQF_t0Sa93jP9w3UcAfQBUfCyuTAWkm1zobHBye4" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:tCyzlrpA3lssmrc116D_knwbMJch4fICDH3AhpFY7-Y" } } ] }, { - "fullUrl": "urn:uuid:a878c05a-740f-5bc0-911c-6235f9329476", + "fullUrl": "urn:uuid:79e83a0a-830e-580f-b8e1-cade11e09d23", "resource": { "resourceType": "Provenance", "meta": { @@ -230,10 +291,10 @@ ] }, "target": [ - { "reference": "urn:uuid:5ddc75c4-ce94-5330-941e-52c9470adc55" } + { "reference": "urn:uuid:bdd7c839-a088-5b25-af9b-194539baa74f" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T12:00:00.000Z", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -257,7 +318,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:cf0c5dad-04d3-5a54-827e-c7e86fa32d28" + "reference": "urn:uuid:871eac06-1646-5b1e-9261-2534f0ea1f59" } } ], @@ -274,7 +335,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:gknGkBm8LLih4l5bW2EJf0VMUE1-cJMRmVr4_eVDT0c" } }, @@ -291,7 +352,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:d5cfaa1f-d8dc-5154-956b-79a2574fec38" + "reference": "urn:uuid:e1db0a4a-35a4-52ae-8c0a-41d7cbc4b0b9" } } ] @@ -310,8 +371,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:SAGhx-yLYVhtcX78WfYR-BVjcwjmLAu54qAOX_c7UvE" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:s8k2R2TdF7vO9z0HKqohVHhu7iZxP9oI7fRWiXVVTWg" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json index 15ccb43a..0a2888f2 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-pulse-wave-velocity.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:37" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:e502b0a8-d613-5be5-af9c-c536dcdf8965", + "fullUrl": "urn:uuid:03aa3ded-75e4-5b3f-82db-56cd2c02a473", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:0CXIWS-tb3h677E99r2s0kCjmKRmjT5koIKz6SnEV-o" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:ZV6dweE3ClIU0TX2L5u1nA_-QX2n8mNZSW6SzwjiG6o" } ], @@ -102,14 +102,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:ZV6dweE3ClIU0TX2L5u1nA_-QX2n8mNZSW6SzwjiG6o" } } ] }, { - "fullUrl": "urn:uuid:b3a3e49e-8642-5af0-85a3-36df6da0d1df", + "fullUrl": "urn:uuid:7291eeb6-9310-5568-a28b-d5f7c7362c08", "resource": { "resourceType": "Device", "meta": { @@ -127,8 +127,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:VpM6a_fjm3X5jsTGMqAYfPQ0a91415LGHoo8U9hrLYM" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:018UCH3MmWxqTGwAR9-SlPqfAbo4e3b5PQZdQWITFs0" } ], "status": "active", @@ -151,14 +151,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:VpM6a_fjm3X5jsTGMqAYfPQ0a91415LGHoo8U9hrLYM" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:018UCH3MmWxqTGwAR9-SlPqfAbo4e3b5PQZdQWITFs0" } } ] }, { - "fullUrl": "urn:uuid:277dad0b-090f-52d4-9595-16d6e2ccaf73", + "fullUrl": "urn:uuid:8cfadbe7-ad57-583e-a003-fb5fdae6ea4d", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:uSdAJTxfWCh8EdKT5XgXFqPuLcOamTcw3auiZOmd-yc" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:uSdAJTxfWCh8EdKT5XgXFqPuLcOamTcw3auiZOmd-yc" + } + } + ] + }, + { + "fullUrl": "urn:uuid:d310bdab-a852-5110-b2a6-d8cc409478b0", "resource": { "resourceType": "Device", "meta": { @@ -176,8 +234,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:SAXtyoGCST2d9rSphOxwj7EZgdyex3Gh0NYcj69xHk8" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:qc0or-fS_1GJLJ7642jYRWujzDONlLw2BCH8Q6zFo3k" } ], "status": "active", @@ -200,7 +258,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:8cfadbe7-ad57-583e-a003-fb5fdae6ea4d" + } }, "extension": [ { @@ -214,14 +275,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:SAXtyoGCST2d9rSphOxwj7EZgdyex3Gh0NYcj69xHk8" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:qc0or-fS_1GJLJ7642jYRWujzDONlLw2BCH8Q6zFo3k" } } ] }, { - "fullUrl": "urn:uuid:dc673541-6367-5b66-93b5-14b3019515e0", + "fullUrl": "urn:uuid:88ea3d47-6ded-5fc5-9c67-611c9779ecd8", "resource": { "resourceType": "Provenance", "meta": { @@ -230,10 +291,10 @@ ] }, "target": [ - { "reference": "urn:uuid:e502b0a8-d613-5be5-af9c-c536dcdf8965" } + { "reference": "urn:uuid:03aa3ded-75e4-5b3f-82db-56cd2c02a473" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T12:00:00.000Z", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -257,7 +318,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:277dad0b-090f-52d4-9595-16d6e2ccaf73" + "reference": "urn:uuid:d310bdab-a852-5110-b2a6-d8cc409478b0" } } ], @@ -274,7 +335,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:0CXIWS-tb3h677E99r2s0kCjmKRmjT5koIKz6SnEV-o" } }, @@ -291,7 +352,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:b3a3e49e-8642-5af0-85a3-36df6da0d1df" + "reference": "urn:uuid:7291eeb6-9310-5568-a28b-d5f7c7362c08" } } ] @@ -310,8 +371,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:oX8Epkb4hJGt8W5eDD2pS103Nv9UchjAGGTebThAOWo" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:Pnr7NzG-1d7DWYPrmU9HYTmn08GQHH0vO39AEGYfXIQ" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json index 2bb5899b..d8e17d57 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-vascular-age.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:34" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:c4c4f7e0-34ca-52bf-9166-237f9bd63c48", + "fullUrl": "urn:uuid:aba86c35-e994-5a5f-995a-ba31f41cac20", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:_l2x6stLJ5Pmo2dPPDYp2B8RPmKnXslZiw1uKzFuNYY" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:sTZ6V-sSi52BgZy4Z8NRDLRLid7ssBNqXnkSwJpEh6o" } ], @@ -102,14 +102,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:sTZ6V-sSi52BgZy4Z8NRDLRLid7ssBNqXnkSwJpEh6o" } } ] }, { - "fullUrl": "urn:uuid:f99a4b98-b062-5dde-b8b0-840e160e42f0", + "fullUrl": "urn:uuid:75f60713-c2db-5d08-8af6-a5fed6e3fa58", "resource": { "resourceType": "Device", "meta": { @@ -127,8 +127,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:EKyKxUz76qvIHbKXieIfjmLdkpPQ-VhRxGVPmcSqBhQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:GkkzA8jfDlAVAGLmOOWVzqcmq6Iyf8i6VCBYiQPT3Xg" } ], "status": "active", @@ -151,14 +151,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:EKyKxUz76qvIHbKXieIfjmLdkpPQ-VhRxGVPmcSqBhQ" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:GkkzA8jfDlAVAGLmOOWVzqcmq6Iyf8i6VCBYiQPT3Xg" } } ] }, { - "fullUrl": "urn:uuid:dde945e4-a798-5e0e-81a9-e386994f4672", + "fullUrl": "urn:uuid:607d4793-01e4-5794-8ba1-639613e0f9d9", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ZXqgd14DMlgvztqGe43sp0auCW_7oXLHhRLC4eMn_Yc" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:ZXqgd14DMlgvztqGe43sp0auCW_7oXLHhRLC4eMn_Yc" + } + } + ] + }, + { + "fullUrl": "urn:uuid:707061d1-1dd9-5b1c-817c-c6cb51f11fa4", "resource": { "resourceType": "Device", "meta": { @@ -176,8 +234,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:H1u4xN5uRPTDRI9WWv339sBBp8j5rNtyVPYFAp5S5ys" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:pNM4vodWuwHVZ_PUYBUuTJgbf5P9cCLWolqVyEKxYSM" } ], "status": "active", @@ -200,7 +258,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:607d4793-01e4-5794-8ba1-639613e0f9d9" + } }, "extension": [ { @@ -214,14 +275,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:H1u4xN5uRPTDRI9WWv339sBBp8j5rNtyVPYFAp5S5ys" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:pNM4vodWuwHVZ_PUYBUuTJgbf5P9cCLWolqVyEKxYSM" } } ] }, { - "fullUrl": "urn:uuid:ab1f9434-7723-5575-a5ea-8833fb57b380", + "fullUrl": "urn:uuid:dac157e7-5df0-59b2-b249-1c59828b5a65", "resource": { "resourceType": "Provenance", "meta": { @@ -230,10 +291,10 @@ ] }, "target": [ - { "reference": "urn:uuid:c4c4f7e0-34ca-52bf-9166-237f9bd63c48" } + { "reference": "urn:uuid:aba86c35-e994-5a5f-995a-ba31f41cac20" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T12:00:00.000Z", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -257,7 +318,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:dde945e4-a798-5e0e-81a9-e386994f4672" + "reference": "urn:uuid:707061d1-1dd9-5b1c-817c-c6cb51f11fa4" } } ], @@ -274,7 +335,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:_l2x6stLJ5Pmo2dPPDYp2B8RPmKnXslZiw1uKzFuNYY" } }, @@ -291,7 +352,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:f99a4b98-b062-5dde-b8b0-840e160e42f0" + "reference": "urn:uuid:75f60713-c2db-5d08-8af6-a5fed6e3fa58" } } ] @@ -310,8 +371,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:NPl8ugskS80-ewIkyuzrLgotSslalz00ZyOtD8fm7aM" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:sfbOIu7kYqFZh2UEmWHZ8I-qFXnZQIW3hZCucLd--yI" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json index 38749948..e1d9a7db 100644 --- a/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json +++ b/packages/fhir/fixtures/conformance/resources/provider-withings-withings-visceral-fat-index.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:47" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:dabd84be-fb8a-5569-ab07-d718f176ef9e", + "fullUrl": "urn:uuid:bae64bf2-f4a2-57c6-8f91-ef05a266ec7e", "resource": { "resourceType": "Observation", "meta": { @@ -50,7 +50,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:mXuLjG0QGS2dxig2AVgHnnRSwLPbJRAZI8vB-6ANrJY" }, { @@ -62,7 +62,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:mpcsNy93_GF0urGccYrlm7WcamEEB3JLTyUD3o5QNpg" } ], @@ -102,14 +102,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:mpcsNy93_GF0urGccYrlm7WcamEEB3JLTyUD3o5QNpg" } } ] }, { - "fullUrl": "urn:uuid:fa327e8d-ba69-5548-8572-0a5651a0a973", + "fullUrl": "urn:uuid:9e0902f6-8d53-5a19-be4e-fe308b03aff9", "resource": { "resourceType": "Device", "meta": { @@ -127,8 +127,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:ZZ3OyjMSTgTPzidj8lQLnZAEVzNyoHZaWNixOJ08smw" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:o57Rqu78N5RQlE7v5l8NHg2Af6lIs50UYHHE6NwxOWs" } ], "status": "active", @@ -151,14 +151,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:ZZ3OyjMSTgTPzidj8lQLnZAEVzNyoHZaWNixOJ08smw" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:o57Rqu78N5RQlE7v5l8NHg2Af6lIs50UYHHE6NwxOWs" } } ] }, { - "fullUrl": "urn:uuid:0638e592-a3af-5d4c-b689-264c19f94ccd", + "fullUrl": "urn:uuid:427c50e5-6aed-5327-8e44-9c00ae15d28d", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:t9cgRa1-f-d_OAwGpADZrj4rWDucC2jwPr1cD3MgM7M" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:t9cgRa1-f-d_OAwGpADZrj4rWDucC2jwPr1cD3MgM7M" + } + } + ] + }, + { + "fullUrl": "urn:uuid:dbc2b474-384a-5ed2-a872-22598335f453", "resource": { "resourceType": "Device", "meta": { @@ -176,8 +234,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:MF2JK1kM8eGepo4odzSLt4CNLjdJMGJHPjXnmiAi61Y" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:C1tj1cdLGaOEycyDzSzCi_WO4R6TbRQMoeIs8FzHj4E" } ], "status": "active", @@ -200,7 +258,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:427c50e5-6aed-5327-8e44-9c00ae15d28d" + } }, "extension": [ { @@ -214,14 +275,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:MF2JK1kM8eGepo4odzSLt4CNLjdJMGJHPjXnmiAi61Y" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:C1tj1cdLGaOEycyDzSzCi_WO4R6TbRQMoeIs8FzHj4E" } } ] }, { - "fullUrl": "urn:uuid:7f761f2c-dde0-5cf3-99c6-02795efed9f2", + "fullUrl": "urn:uuid:687dc91b-ab22-5f5c-b709-e3396c9b079b", "resource": { "resourceType": "Provenance", "meta": { @@ -230,10 +291,10 @@ ] }, "target": [ - { "reference": "urn:uuid:dabd84be-fb8a-5569-ab07-d718f176ef9e" } + { "reference": "urn:uuid:bae64bf2-f4a2-57c6-8f91-ef05a266ec7e" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredDateTime": "2026-08-20T12:00:00.000Z", + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -257,7 +318,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:0638e592-a3af-5d4c-b689-264c19f94ccd" + "reference": "urn:uuid:dbc2b474-384a-5ed2-a872-22598335f453" } } ], @@ -274,7 +335,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:mXuLjG0QGS2dxig2AVgHnnRSwLPbJRAZI8vB-6ANrJY" } }, @@ -291,7 +352,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:fa327e8d-ba69-5548-8572-0a5651a0a973" + "reference": "urn:uuid:9e0902f6-8d53-5a19-be4e-fe308b03aff9" } } ] @@ -310,8 +371,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:KiJhlB1_wGC3WEMuY4Y4o9J103fJ04dAZo5lV513j4g" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:gorO9KKYqtxONXjPQagHE0nQEeHuXlTRq3VbGvHT6zc" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json b/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json index d0acd3ce..82b24f82 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json +++ b/packages/fhir/fixtures/conformance/resources/recording-google-health-api-heart-rate.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:49" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:f39530e9-26b5-5078-9f30-5d61d3e4881a", + "fullUrl": "urn:uuid:b303cba7-0e4f-558e-a8d9-18382c442f82", "resource": { "resourceType": "DocumentReference", "meta": { @@ -43,7 +43,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:Qm28JqWMxMvl2dsco1gdTLvUyKdg3S8qfrEX_4Z19KY" }, { @@ -55,7 +55,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:w7o_lcZdhv2y5mj96CWnefbS73I3tPoRvuSQG7Vaqao" }, { @@ -67,7 +67,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-artifact-v0/fixture-key/1", "value": "v0:fixture-key:1:PHw-YmUdZHB6MdPZ3172jcWHpUdXb6Dh9IOm9pXrO4M" } ], @@ -80,9 +80,9 @@ "value": "patient-example" } }, - "date": "2026-08-20T12:01:00Z", + "date": "2026-08-20T12:03:00Z", "author": [ - { "reference": "urn:uuid:443842a6-4a5b-5ce2-b29e-134f07e39c58" } + { "reference": "urn:uuid:0965e9ad-1db3-54fe-8f8c-5687c5936783" } ], "content": [ { @@ -113,14 +113,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:w7o_lcZdhv2y5mj96CWnefbS73I3tPoRvuSQG7Vaqao" } } ] }, { - "fullUrl": "urn:uuid:5cfe6e6b-d30e-5e8a-88cf-d4d922961187", + "fullUrl": "urn:uuid:fedfcaf1-e6d2-5979-a378-674d22707d5b", "resource": { "resourceType": "Device", "meta": { @@ -138,8 +138,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:oLMQbfc0NeyY4hR6JEtvp-FZuco9HARLJLu04VS1a94" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:se0T3wOLIUnNBSpPUMcr9OXwB2Sc2RiLBI5LoQV3lc8" } ], "status": "active", @@ -162,14 +162,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:oLMQbfc0NeyY4hR6JEtvp-FZuco9HARLJLu04VS1a94" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:se0T3wOLIUnNBSpPUMcr9OXwB2Sc2RiLBI5LoQV3lc8" } } ] }, { - "fullUrl": "urn:uuid:443842a6-4a5b-5ce2-b29e-134f07e39c58", + "fullUrl": "urn:uuid:099a0aa6-5ab1-5bb6-8930-cb1bc27cf2c3", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:UZCiHMuTIPrwUAnOXPpbCi9uEHF4MLi0g37bTWgIb5I" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:UZCiHMuTIPrwUAnOXPpbCi9uEHF4MLi0g37bTWgIb5I" + } + } + ] + }, + { + "fullUrl": "urn:uuid:0965e9ad-1db3-54fe-8f8c-5687c5936783", "resource": { "resourceType": "Device", "meta": { @@ -187,8 +245,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uocYwddFVtlKSSu89sdrCnhx7B1Zj4lX7iNLzaZjvAA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:7uqIqkrxFcJYzuEfaZFQOu6I9p2xh_uokJvuzqysQvU" } ], "status": "active", @@ -211,7 +269,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:099a0aa6-5ab1-5bb6-8930-cb1bc27cf2c3" + } }, "extension": [ { @@ -225,14 +286,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:uocYwddFVtlKSSu89sdrCnhx7B1Zj4lX7iNLzaZjvAA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:7uqIqkrxFcJYzuEfaZFQOu6I9p2xh_uokJvuzqysQvU" } } ] }, { - "fullUrl": "urn:uuid:9e5ec01e-0d7f-5b0b-a505-f65fd1ea665a", + "fullUrl": "urn:uuid:6479e8bf-0d3f-5bea-968f-c222afefadd4", "resource": { "resourceType": "Provenance", "meta": { @@ -241,10 +302,13 @@ ] }, "target": [ - { "reference": "urn:uuid:f39530e9-26b5-5078-9f30-5d61d3e4881a" } + { "reference": "urn:uuid:b303cba7-0e4f-558e-a8d9-18382c442f82" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-20T00:00:00.000Z", + "end": "2026-08-20T12:00:00.000Z" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -268,7 +332,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:443842a6-4a5b-5ce2-b29e-134f07e39c58" + "reference": "urn:uuid:0965e9ad-1db3-54fe-8f8c-5687c5936783" } } ], @@ -285,7 +349,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:Qm28JqWMxMvl2dsco1gdTLvUyKdg3S8qfrEX_4Z19KY" } }, @@ -302,7 +366,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:5cfe6e6b-d30e-5e8a-88cf-d4d922961187" + "reference": "urn:uuid:fedfcaf1-e6d2-5979-a378-674d22707d5b" } } ] @@ -321,8 +385,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:cIH4Nzhma1gYzkQquANDEpkWbH1g8n6BZFu8SGZae8k" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:ST9_96TlFS-cC9B5m4PZeZPtMV96g2MRwbSPzQNjlrU" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json b/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json index 4521f845..21c5f868 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json +++ b/packages/fhir/fixtures/conformance/resources/recording-oura-heartrate.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:50" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:9916390c-d9ec-5e86-ac4b-9545f65ccfd9", + "fullUrl": "urn:uuid:9cc0adbf-9651-54c4-861f-3d7ac23f4ebc", "resource": { "resourceType": "DocumentReference", "meta": { @@ -43,7 +43,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:fnfIzdy0ubGE-azcaxeBYJW6Yr9_ICD_fqPfjI39XfI" }, { @@ -55,7 +55,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:JbH3fA_A04xTUndXZKXKxlEF8b9as0R0q34_o1kBSW8" }, { @@ -67,7 +67,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-artifact-v0/fixture-key/1", "value": "v0:fixture-key:1:4h_4O6TrHzwSL8GWzqd-_Ppl0AzDUP_M3zY0IvE4lTI" } ], @@ -80,9 +80,9 @@ "value": "patient-example" } }, - "date": "2026-08-20T12:01:00Z", + "date": "2026-08-20T12:03:00Z", "author": [ - { "reference": "urn:uuid:88a3cca3-4f36-52cd-aa6c-e86732e82c06" } + { "reference": "urn:uuid:95125300-dfcf-50ab-9f44-20132db1b855" } ], "content": [ { @@ -113,14 +113,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:JbH3fA_A04xTUndXZKXKxlEF8b9as0R0q34_o1kBSW8" } } ] }, { - "fullUrl": "urn:uuid:aad76c82-3ffe-527e-a679-7ac0da57d307", + "fullUrl": "urn:uuid:19c1881f-c6b0-5389-ad4f-d8a7209eff43", "resource": { "resourceType": "Device", "meta": { @@ -138,8 +138,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:eKyVTfyrcOiy_bu_yX0GsJm0XT946MmjfoXJzsX_wKA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:YsUs60fIiLSRngNuETgtG7omRZcs6b2sqvyE0NGO8zQ" } ], "status": "active", @@ -162,14 +162,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:eKyVTfyrcOiy_bu_yX0GsJm0XT946MmjfoXJzsX_wKA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:YsUs60fIiLSRngNuETgtG7omRZcs6b2sqvyE0NGO8zQ" } } ] }, { - "fullUrl": "urn:uuid:88a3cca3-4f36-52cd-aa6c-e86732e82c06", + "fullUrl": "urn:uuid:0e993e78-5366-54d5-8b15-9efd5c89b8f1", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Kj8Nf_jZNXwPxluEWPx4erTPVfXg9wAkG8bdEPPRblQ" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Kj8Nf_jZNXwPxluEWPx4erTPVfXg9wAkG8bdEPPRblQ" + } + } + ] + }, + { + "fullUrl": "urn:uuid:95125300-dfcf-50ab-9f44-20132db1b855", "resource": { "resourceType": "Device", "meta": { @@ -187,8 +245,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:jmlPta-p2yy0ez5EdOqwg6Z48EenmJ7OxaNRx7O22QM" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:MmYvCcA0NWAnGf0RBWWhotydZt8_hJroKt_CzBnIlG0" } ], "status": "active", @@ -211,7 +269,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:0e993e78-5366-54d5-8b15-9efd5c89b8f1" + } }, "extension": [ { @@ -225,14 +286,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:jmlPta-p2yy0ez5EdOqwg6Z48EenmJ7OxaNRx7O22QM" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:MmYvCcA0NWAnGf0RBWWhotydZt8_hJroKt_CzBnIlG0" } } ] }, { - "fullUrl": "urn:uuid:14d60b3d-587c-5830-9ed8-30f35e291bdb", + "fullUrl": "urn:uuid:7c94706d-3679-5ef8-a0ad-1fe906f7b9c4", "resource": { "resourceType": "Provenance", "meta": { @@ -241,10 +302,13 @@ ] }, "target": [ - { "reference": "urn:uuid:9916390c-d9ec-5e86-ac4b-9545f65ccfd9" } + { "reference": "urn:uuid:9cc0adbf-9651-54c4-861f-3d7ac23f4ebc" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-20T00:00:00.000Z", + "end": "2026-08-20T12:00:00.000Z" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -268,7 +332,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:88a3cca3-4f36-52cd-aa6c-e86732e82c06" + "reference": "urn:uuid:95125300-dfcf-50ab-9f44-20132db1b855" } } ], @@ -285,7 +349,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:fnfIzdy0ubGE-azcaxeBYJW6Yr9_ICD_fqPfjI39XfI" } }, @@ -302,7 +366,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:aad76c82-3ffe-527e-a679-7ac0da57d307" + "reference": "urn:uuid:19c1881f-c6b0-5389-ad4f-d8a7209eff43" } } ] @@ -321,8 +385,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:NvVaCec-6ykB4NgfVZEUFzS47_HJq6IwVJJhMurnKeg" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:WWCLunD9fRkAxxStoK7z1cjUwtLEQb9mZdooFOY_j2w" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json b/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json index 7885be35..c30ed986 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json +++ b/packages/fhir/fixtures/conformance/resources/recording-withings-activityintraday.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:51" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:accf8995-d7a1-57d6-b653-61a258ec2e1b", + "fullUrl": "urn:uuid:8dd8aff6-e8dc-5cc2-ac9d-48477a90d210", "resource": { "resourceType": "DocumentReference", "meta": { @@ -43,7 +43,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:lAjnPJ63IR1m-sgVedR7vawwKb7tDYLxzjcpGJFdH8M" }, { @@ -55,7 +55,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:B3PLQy1r2W61RW1kB3OKSWh3mMpcVj0paRHWyND2O2g" }, { @@ -67,7 +67,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-artifact-v0/fixture-key/1", "value": "v0:fixture-key:1:Ngl8REcCCf8c48ftm1b4wa0XNyWjSQa1i0UKiTHaV4s" } ], @@ -80,9 +80,9 @@ "value": "patient-example" } }, - "date": "2026-08-20T12:01:00Z", + "date": "2026-08-20T12:03:00Z", "author": [ - { "reference": "urn:uuid:21a9b5a2-58d8-5dd2-b01b-838b8776909a" } + { "reference": "urn:uuid:e11405ff-c11e-5826-8815-213198302e30" } ], "content": [ { @@ -113,14 +113,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:B3PLQy1r2W61RW1kB3OKSWh3mMpcVj0paRHWyND2O2g" } } ] }, { - "fullUrl": "urn:uuid:89ab20ae-a2f0-574a-8f41-4c690cebda0f", + "fullUrl": "urn:uuid:a9937317-1f01-5267-b1e6-b75fef30cfd1", "resource": { "resourceType": "Device", "meta": { @@ -138,8 +138,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:yzDY-vpFTbwMkAWgPEJ4D7kUHMcsuPrWGrEXS0KMlSM" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:yV55os942KAyvweBqCVutfrlcYqzW6zbSreBSsRnmNA" } ], "status": "active", @@ -162,14 +162,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:yzDY-vpFTbwMkAWgPEJ4D7kUHMcsuPrWGrEXS0KMlSM" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:yV55os942KAyvweBqCVutfrlcYqzW6zbSreBSsRnmNA" } } ] }, { - "fullUrl": "urn:uuid:21a9b5a2-58d8-5dd2-b01b-838b8776909a", + "fullUrl": "urn:uuid:e46dd336-d27d-5f1f-b503-6dc15248f2cd", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:22TcPMVCPB7YW2Y2B7Sf3G8FF05975JFAuBXN6PKg6A" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:22TcPMVCPB7YW2Y2B7Sf3G8FF05975JFAuBXN6PKg6A" + } + } + ] + }, + { + "fullUrl": "urn:uuid:e11405ff-c11e-5826-8815-213198302e30", "resource": { "resourceType": "Device", "meta": { @@ -187,8 +245,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:x-LlatjeBgMfWLnLO419O1s_lhhmbSk6YiYcXeVU7NA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:WGRlv3thqjbE5v1Ck7A9qeP_mfD8htB5rBsm1c0hjEM" } ], "status": "active", @@ -211,7 +269,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:e46dd336-d27d-5f1f-b503-6dc15248f2cd" + } }, "extension": [ { @@ -225,14 +286,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:x-LlatjeBgMfWLnLO419O1s_lhhmbSk6YiYcXeVU7NA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:WGRlv3thqjbE5v1Ck7A9qeP_mfD8htB5rBsm1c0hjEM" } } ] }, { - "fullUrl": "urn:uuid:b21403d9-6715-5be8-ab04-061e3a0e0e19", + "fullUrl": "urn:uuid:7d043a41-3509-52e3-8325-b88652fcb19f", "resource": { "resourceType": "Provenance", "meta": { @@ -241,10 +302,13 @@ ] }, "target": [ - { "reference": "urn:uuid:accf8995-d7a1-57d6-b653-61a258ec2e1b" } + { "reference": "urn:uuid:8dd8aff6-e8dc-5cc2-ac9d-48477a90d210" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-20T00:00:00.000Z", + "end": "2026-08-20T12:00:00.000Z" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -268,7 +332,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:21a9b5a2-58d8-5dd2-b01b-838b8776909a" + "reference": "urn:uuid:e11405ff-c11e-5826-8815-213198302e30" } } ], @@ -285,7 +349,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:lAjnPJ63IR1m-sgVedR7vawwKb7tDYLxzjcpGJFdH8M" } }, @@ -302,7 +366,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:89ab20ae-a2f0-574a-8f41-4c690cebda0f" + "reference": "urn:uuid:a9937317-1f01-5267-b1e6-b75fef30cfd1" } } ] @@ -321,8 +385,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:gIphHW-fnGuULq9DANuncEdHEFazUS1Lr6XZYSsvakE" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:670lMXo4cPJANk5hQ3QXrdE-SVvjUJBL_GJJeHVJTHw" } } ] diff --git a/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json b/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json index d9b91df2..b13f6148 100644 --- a/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json +++ b/packages/fhir/fixtures/conformance/resources/recording-withings-sleepintraday.json @@ -8,13 +8,13 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/event", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-event-v0", "value": "e0:33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc:52" }, "timestamp": "2026-08-20T12:03:00Z", "entry": [ { - "fullUrl": "urn:uuid:7cc82885-eaae-52a4-8954-cbf4e42d70fd", + "fullUrl": "urn:uuid:97fb1e84-41a5-5154-b3f6-b2dce114aefd", "resource": { "resourceType": "DocumentReference", "meta": { @@ -43,7 +43,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:2X6rXvj9ynbIR3JXJhe8uk0JtT9fSD-OPA16Do4D8yQ" }, { @@ -55,7 +55,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:Uh2ZvHcTyaxH3aO6RElukrQWXzQxwKED3FMteFYyvoo" }, { @@ -67,7 +67,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-artifact/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-artifact-v0/fixture-key/1", "value": "v0:fixture-key:1:AimTLWJeR6crk04-8_iSRxDRJmAKM7VKXhdYAUoT9e4" } ], @@ -80,9 +80,9 @@ "value": "patient-example" } }, - "date": "2026-08-20T12:01:00Z", + "date": "2026-08-20T12:03:00Z", "author": [ - { "reference": "urn:uuid:bb28b03a-6b47-56ea-b849-487f307e6f4c" } + { "reference": "urn:uuid:e2be518f-ccea-520e-a0a8-1676461c55b1" } ], "content": [ { @@ -113,14 +113,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-output/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-output-v0/fixture-key/1", "value": "v0:fixture-key:1:Uh2ZvHcTyaxH3aO6RElukrQWXzQxwKED3FMteFYyvoo" } } ] }, { - "fullUrl": "urn:uuid:b70165c6-669b-51d6-ab8c-837c42455bd4", + "fullUrl": "urn:uuid:5a237f06-187d-5cff-9f91-64ef607eac02", "resource": { "resourceType": "Device", "meta": { @@ -138,8 +138,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:bj-SsK_YMXFNS4md3NAMVI6wrSVd2KQIj-6ZMuY9a-E" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Bj4HERlku-jeSeZvaI0kcRru-AXQXVIc6KNA1FrzoK0" } ], "status": "active", @@ -162,14 +162,72 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:bj-SsK_YMXFNS4md3NAMVI6wrSVd2KQIj-6ZMuY9a-E" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:Bj4HERlku-jeSeZvaI0kcRru-AXQXVIc6KNA1FrzoK0" } } ] }, { - "fullUrl": "urn:uuid:bb28b03a-6b47-56ea-b849-487f307e6f4c", + "fullUrl": "urn:uuid:aab5ae5b-7515-5eb8-aae0-6cdea373b1db", + "resource": { + "resourceType": "Device", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-host-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:IFDWzHVvxKSi67ONtN5Xx0Fu7SfhvHqku_XXTgtn5ig" + } + ], + "status": "active", + "version": [ + { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-application-version-type", + "code": "os-version", + "display": "Operating system version" + } + ], + "text": "Operating system version" + }, + "value": "Node.js 24" + } + ] + }, + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "device-snapshot" + } + ] + }, + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:IFDWzHVvxKSi67ONtN5Xx0Fu7SfhvHqku_XXTgtn5ig" + } + } + ] + }, + { + "fullUrl": "urn:uuid:e2be518f-ccea-520e-a0a8-1676461c55b1", "resource": { "resourceType": "Device", "meta": { @@ -187,8 +245,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Fk4ot8ZFQZFFmgsML91JFuas22bK-FhacPfljbciTgA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:LwEWSFCF87VzSPTc2URuMN-5lVAAOJIRTcRNYzSpYxY" } ], "status": "active", @@ -211,7 +269,10 @@ }, "value": "0.0.0" } - ] + ], + "parent": { + "reference": "urn:uuid:aab5ae5b-7515-5eb8-aae0-6cdea373b1db" + } }, "extension": [ { @@ -225,14 +286,14 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/device-snapshot/fixture-key/1", - "value": "v0:fixture-key:1:Fk4ot8ZFQZFFmgsML91JFuas22bK-FhacPfljbciTgA" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-device-snapshot-v0/fixture-key/1", + "value": "v0:fixture-key:1:LwEWSFCF87VzSPTc2URuMN-5lVAAOJIRTcRNYzSpYxY" } } ] }, { - "fullUrl": "urn:uuid:555fb2c9-9210-56b8-a1ef-e6391fc23fcf", + "fullUrl": "urn:uuid:117d7889-b7c9-5618-98d9-c7eebb5e3303", "resource": { "resourceType": "Provenance", "meta": { @@ -241,10 +302,13 @@ ] }, "target": [ - { "reference": "urn:uuid:7cc82885-eaae-52a4-8954-cbf4e42d70fd" } + { "reference": "urn:uuid:97fb1e84-41a5-5154-b3f6-b2dce114aefd" } ], - "occurredDateTime": "2026-08-20T12:00:00Z", - "recorded": "2026-08-20T12:02:00Z", + "occurredPeriod": { + "start": "2026-08-20T00:00:00.000Z", + "end": "2026-08-20T12:00:00.000Z" + }, + "recorded": "2026-08-20T12:03:00Z", "activity": { "coding": [ { @@ -268,7 +332,7 @@ "text": "Assembler" }, "who": { - "reference": "urn:uuid:bb28b03a-6b47-56ea-b849-487f307e6f4c" + "reference": "urn:uuid:e2be518f-ccea-520e-a0a8-1676461c55b1" } } ], @@ -285,7 +349,7 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/provider-record/fixture-key/1", + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-provider-record-v0/fixture-key/1", "value": "v0:fixture-key:1:2X6rXvj9ynbIR3JXJhe8uk0JtT9fSD-OPA16Do4D8yQ" } }, @@ -302,7 +366,7 @@ "text": "Enterer" }, "who": { - "reference": "urn:uuid:b70165c6-669b-51d6-ab8c-837c42455bd4" + "reference": "urn:uuid:5a237f06-187d-5cff-9f91-64ef607eac02" } } ] @@ -321,8 +385,8 @@ } ] }, - "system": "https://grovealliance.org/fhir/testing/identity/entry-node", - "value": "n0:conversion-provenance:0:sKOYrb7a0lyQiBSQYAsmbtmbU4tdWkVS3bQsLzWJWZ4" + "system": "https://grovealliance.org/fhir/testing/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:tKeCnKbjSKLWOMO9iHIi4RC9icFmRJsQArs4M1RBT90" } } ] diff --git a/packages/fhir/fixtures/conformance/study-attribution/context-two-studies.json b/packages/fhir/fixtures/conformance/study-attribution/context-two-studies.json new file mode 100644 index 00000000..d60e642e --- /dev/null +++ b/packages/fhir/fixtures/conformance/study-attribution/context-two-studies.json @@ -0,0 +1,585 @@ +{ + "resourceType": "Bundle", + "id": "GroveMultiStudyEventExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:51" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:j4Wu4W_g8dOYlTI7mr1NrLIoCxB3GmWfE3-hnKeu0L0" + } + } + ], + "fullUrl": "urn:uuid:2e4f4181-d1b7-5def-9131-5423f63dd827", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:1BRlmWamqCymL8adxO0bJCs1rV4L6ZGkPUGzrOfixnw" + } + } + ], + "fullUrl": "urn:uuid:1aeae376-cca7-5326-aa72-b9275b2ce7ed", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:1BRlmWamqCymL8adxO0bJCs1rV4L6ZGkPUGzrOfixnw" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source output" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:1aeae376-cca7-5326-aa72-b9275b2ce7ed" + } + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/workflow-researchStudy", + "valueReference": { + "reference": "urn:uuid:4c04a548-5132-5ae4-8acf-6840e4cef3a9" + } + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/workflow-researchStudy", + "valueReference": { + "reference": "urn:uuid:8d683bcd-f8cf-56e6-81db-360026281e84" + } + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:2e4f4181-d1b7-5def-9131-5423f63dd827" + }, + "performer": [ + { + "reference": "urn:uuid:2e4f4181-d1b7-5def-9131-5423f63dd827" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:5621ff79-2dc2-532a-85fa-de5cec9cc832" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:03OFWh5gXqquk8r3oDaaSVrJeH5UVuw0L6ntc42oHAw" + } + } + ], + "fullUrl": "urn:uuid:c018acd1-6c7f-5f61-b1fc-6bc09115a46f", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:1aeae376-cca7-5326-aa72-b9275b2ce7ed" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:JlkUoGLdfCZAD9o18ueii5dGvQ2zv11gmIZVxQX7hhk" + } + } + ], + "fullUrl": "urn:uuid:5621ff79-2dc2-532a-85fa-de5cec9cc832", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:plan-definition:0:NTKNcz4Eh5w2p3QOTxosbamt8q_cmBzjTOWTC-GK0pk" + } + } + ], + "fullUrl": "urn:uuid:e75ca567-0189-53d2-a424-cfc9b7cbf29b", + "resource": { + "resourceType": "PlanDefinition", + "id": "plan-a-r1", + "url": "https://study.example.org/PlanDefinition/a", + "version": "1", + "status": "active", + "title": "Study A Protocol" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:research-study:0:bPiZvSW2VKHzXRcQovNMbCvTVjxoWImNpo5xvHX6ihA" + } + } + ], + "fullUrl": "urn:uuid:4c04a548-5132-5ae4-8acf-6840e4cef3a9", + "resource": { + "resourceType": "ResearchStudy", + "id": "study-a-r1", + "identifier": [ + { + "system": "https://study.example.org/studies", + "value": "a" + } + ], + "status": "active", + "title": "Study A", + "protocol": [ + { + "reference": "urn:uuid:e75ca567-0189-53d2-a424-cfc9b7cbf29b" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:research-subject:0:Jsrkj8DEVAdqsFtBb8AA_JritBL14v3_BAjEfV805QI" + } + } + ], + "fullUrl": "urn:uuid:2b0f9ea6-43cb-5595-bf99-8b57dcaedd33", + "resource": { + "resourceType": "ResearchSubject", + "id": "enrollment-a", + "identifier": [ + { + "system": "https://study.example.org/enrollments", + "value": "enrollment-a" + } + ], + "status": "on-study", + "study": { + "reference": "urn:uuid:4c04a548-5132-5ae4-8acf-6840e4cef3a9" + }, + "individual": { + "reference": "urn:uuid:2e4f4181-d1b7-5def-9131-5423f63dd827" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:plan-definition:1:dK16uwDrYK0dl-gGGdWGrPPNQD-cqjYVXNSymsiCV8Y" + } + } + ], + "fullUrl": "urn:uuid:0bef4d0e-fd21-540d-a9ff-513811bfdffb", + "resource": { + "resourceType": "PlanDefinition", + "id": "plan-b-r3", + "url": "https://study.example.org/PlanDefinition/b", + "version": "3", + "status": "active", + "title": "Study B Protocol" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:research-study:1:YTNwmlmzj-CQJfEDTuVZGjLyslK8JflsH5jus1eBx4E" + } + } + ], + "fullUrl": "urn:uuid:8d683bcd-f8cf-56e6-81db-360026281e84", + "resource": { + "resourceType": "ResearchStudy", + "id": "study-b-r3", + "identifier": [ + { + "system": "https://study.example.org/studies", + "value": "b" + } + ], + "status": "active", + "title": "Study B", + "protocol": [ + { + "reference": "urn:uuid:0bef4d0e-fd21-540d-a9ff-513811bfdffb" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:research-subject:1:vRZY-dNV-TO5t5RcfxVWoGcSbRji38yHIJXzOJ5MY1g" + } + } + ], + "fullUrl": "urn:uuid:8a119054-cdbb-58e5-97b7-47023c687a77", + "resource": { + "resourceType": "ResearchSubject", + "id": "enrollment-b", + "identifier": [ + { + "system": "https://study.example.org/enrollments", + "value": "enrollment-b" + } + ], + "status": "on-study", + "study": { + "reference": "urn:uuid:8d683bcd-f8cf-56e6-81db-360026281e84" + }, + "individual": { + "reference": "urn:uuid:2e4f4181-d1b7-5def-9131-5423f63dd827" + } + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/study-attribution/context-two-studies.json.license b/packages/fhir/fixtures/conformance/study-attribution/context-two-studies.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/study-attribution/context-two-studies.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/study-attribution/negative-cases.json b/packages/fhir/fixtures/conformance/study-attribution/negative-cases.json new file mode 100644 index 00000000..a3a2fe0b --- /dev/null +++ b/packages/fhir/fixtures/conformance/study-attribution/negative-cases.json @@ -0,0 +1,34 @@ +{ + "schemaVersion": 0, + "version": "0.6.0", + "bases": [ + {"id": "source-event", "path": "source-event.json"}, + {"id": "export-a", "path": "export-a.json"} + ], + "cases": [ + { + "id": "subject-mismatch", + "base": "source-event", + "patch": [{"op": "replace", "path": "/entry/7/resource/individual", "value": {"type": "Patient", "identifier": {"system": "https://study.example.org/fhir/identifiers/participant", "value": "participant-002"}}}], + "expectedRule": {"code": "receiver-study.subject-mismatch", "reason": "Study enrollment subject differs from the verified source subject."} + }, + { + "id": "protocol-is-not-a-reference", + "base": "source-event", + "patch": [{"op": "replace", "path": "/entry/6/resource/protocol/0", "value": "https://study.example.org/PlanDefinition/a|1"}], + "expectedRule": {"code": "mobile-exchange.reference-shape", "reason": "ResearchStudy.protocol requires a Reference, not a canonical string."} + }, + { + "id": "lost-historical-revision", + "base": "export-a", + "patch": [{"op": "replace", "path": "/entry/4/resource/version", "value": "2"}], + "expectedRule": {"code": "receiver-study.historical-revision", "reason": "The export's protocol differs from the immutable revision authorized by its grant."} + }, + { + "id": "derived-export-as-event-retry", + "base": "export-a", + "patch": [{"op": "replace", "path": "/identifier", "value": {"type": {"coding": [{"code": "event", "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", "display": "Event"}]}, "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:50"}}], + "expectedRule": {"code": "receiver-study.not-exact-replay", "reason": "A filtered export cannot reuse the immutable producer event identity for different bytes."} + } + ] +} diff --git a/packages/fhir/fixtures/conformance/study-attribution/negative-cases.json.license b/packages/fhir/fixtures/conformance/study-attribution/negative-cases.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/study-attribution/negative-cases.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/study-attribution/source-event.json b/packages/fhir/fixtures/conformance/study-attribution/source-event.json new file mode 100644 index 00000000..6dc89372 --- /dev/null +++ b/packages/fhir/fixtures/conformance/study-attribution/source-event.json @@ -0,0 +1,477 @@ +{ + "resourceType": "Bundle", + "id": "GroveMultiStudyEventExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-exchange-bundle" + ] + }, + "type": "collection", + "identifier": { + "type": { + "coding": [ + { + "code": "event", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Event" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-event-v0", + "value": "e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:50" + }, + "entry": [ + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:patient:0:lKm5ncLaS9mP7iTY1_DhwLlbPqXiKW5fCRGKd0Xe4f0" + } + } + ], + "fullUrl": "urn:uuid:8c91dca5-9545-52e3-97ef-12d9b2e1e509", + "resource": { + "resourceType": "Patient", + "id": "GroveMobileExchangePatientExample", + "identifier": [ + { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:IPo3xVSPUNGwqi_4L3JZCTpztid-H4rQEvEt3YZ2TcU" + } + } + ], + "fullUrl": "urn:uuid:6bd9d0ad-1c35-5db9-86d1-089037b64138", + "resource": { + "resourceType": "Device", + "id": "GroveMobileExchangeApplicationExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-application-device" + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "device-snapshot", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Device snapshot" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1", + "value": "v0:test-key:1:IPo3xVSPUNGwqi_4L3JZCTpztid-H4rQEvEt3YZ2TcU" + } + ], + "deviceName": [ + { + "type": "user-friendly-name", + "name": "Mobile Study" + } + ], + "status": "active" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source output" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + } + ], + "fullUrl": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5", + "resource": { + "resourceType": "Observation", + "id": "GroveMobileExchangeHeartRateExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-heart-rate" + ] + }, + "code": { + "coding": [ + { + "code": "8867-4", + "system": "http://loinc.org", + "display": "Heart rate" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + }, + { + "type": { + "coding": [ + { + "code": "source-output", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1", + "value": "v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8" + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice", + "valueReference": { + "reference": "urn:uuid:6bd9d0ad-1c35-5db9-86d1-089037b64138" + } + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/workflow-researchStudy", + "valueReference": { + "reference": "urn:uuid:177ddab0-cb9a-56fa-8e38-ebe8b7b3cdf8" + } + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "code": "vital-signs", + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "display": "Vital Signs" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:8c91dca5-9545-52e3-97ef-12d9b2e1e509" + }, + "performer": [ + { + "reference": "urn:uuid:8c91dca5-9545-52e3-97ef-12d9b2e1e509" + } + ], + "derivedFrom": [ + { + "reference": "urn:uuid:dd4166bd-a23d-5162-850c-bf91c1a0d422" + } + ], + "effectiveDateTime": "2026-08-20T08:30:00.251-07:00", + "issued": "2026-08-20T17:30:02Z", + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:conversion-provenance:0:f7w1E2t_CQYic_ptgAdQ3NeDCAPJx91-g21qjDMRWio" + } + } + ], + "fullUrl": "urn:uuid:363ce159-0aa3-5b75-97c6-54c74e9270fe", + "resource": { + "resourceType": "Provenance", + "id": "GroveMobileExchangeProvenanceExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-conversion-provenance" + ] + }, + "activity": { + "coding": [ + { + "code": "transform", + "system": "http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle", + "display": "Transform/Translate Record Lifecycle Event" + } + ] + }, + "agent": [ + { + "type": { + "coding": [ + { + "code": "assembler", + "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", + "display": "Assembler" + } + ] + }, + "who": { + "reference": "urn:uuid:6bd9d0ad-1c35-5db9-86d1-089037b64138" + } + } + ], + "entity": [ + { + "role": "source", + "what": { + "identifier": { + "type": { + "coding": [ + { + "code": "source-record", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Source record" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1", + "value": "v0:test-key:1:ptHr751zWyYfaR2WIvrP1TfnVEK4bInC__ibP_AYfVY" + } + } + } + ], + "target": [ + { + "reference": "urn:uuid:a9b76bbc-f523-5ef4-9919-813ec70553e5" + } + ], + "occurredDateTime": "2026-08-20T10:30:02-07:00", + "recorded": "2026-08-20T17:30:02Z" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "code": "entry-node", + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "display": "Entry node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:questionnaire-response:0:4dxhFeIfq9x51l-hv_Zx9rpVEAwcHXrLrRvxf4pC3qk" + } + } + ], + "fullUrl": "urn:uuid:dd4166bd-a23d-5162-850c-bf91c1a0d422", + "resource": { + "resourceType": "QuestionnaireResponse", + "id": "GroveMobileExchangeQuestionnaireResponseExample", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://study.example.org/fhir/NamingSystem/questionnaire-response", + "value": "response-1" + }, + "questionnaire": "https://study.example.org/fhir/Questionnaire/heart-rate-check|1.0.0", + "status": "completed", + "subject": { + "type": "Patient", + "identifier": { + "system": "https://study.example.org/fhir/identifiers/participant", + "value": "participant-001" + } + }, + "authored": "2026-08-20T08:30:00-07:00", + "item": [ + { + "linkId": "heart-rate", + "answer": [ + { + "valueQuantity": { + "value": 72, + "code": "/min", + "system": "http://unitsofmeasure.org", + "unit": "beats/minute" + } + } + ] + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:plan-definition:0:S9PRk3VLkEoeFWoOObPjkQxBJKnQNymnbfa6NrNrNl0" + } + } + ], + "fullUrl": "urn:uuid:1332c904-e41d-5d43-b6cb-88e58b60f880", + "resource": { + "resourceType": "PlanDefinition", + "id": "plan-a-r1", + "url": "https://study.example.org/PlanDefinition/a", + "version": "1", + "status": "active", + "title": "Study A Protocol" + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:research-study:0:SG_YUp7NF57g7Ih5PyVtv0_ULEQKVfMBM3Hp0VoMWJo" + } + } + ], + "fullUrl": "urn:uuid:177ddab0-cb9a-56fa-8e38-ebe8b7b3cdf8", + "resource": { + "resourceType": "ResearchStudy", + "id": "study-a-r1", + "identifier": [ + { + "system": "https://study.example.org/studies", + "value": "a" + } + ], + "status": "active", + "title": "Study A", + "protocol": [ + { + "reference": "urn:uuid:1332c904-e41d-5d43-b6cb-88e58b60f880" + } + ] + } + }, + { + "extension": [ + { + "url": "https://grovealliance.org/fhir/mobile/StructureDefinition/grove-exchange-entry-node-key", + "valueIdentifier": { + "type": { + "coding": [ + { + "system": "https://grovealliance.org/fhir/mobile/CodeSystem/grove-identifier-role", + "code": "entry-node" + } + ] + }, + "system": "https://study.example.org/fhir/NamingSystem/grove-entry-node-v0", + "value": "n0:research-subject:0:VtknMSSv1DToowBDQNHXn5SkWqfMTg5_3b_nOt8GamI" + } + } + ], + "fullUrl": "urn:uuid:cf650112-4437-531e-9cad-d35099572eb5", + "resource": { + "resourceType": "ResearchSubject", + "id": "enrollment-a", + "identifier": [ + { + "system": "https://study.example.org/enrollments", + "value": "enrollment-a" + } + ], + "status": "on-study", + "study": { + "reference": "urn:uuid:177ddab0-cb9a-56fa-8e38-ebe8b7b3cdf8" + }, + "individual": { + "reference": "urn:uuid:8c91dca5-9545-52e3-97ef-12d9b2e1e509" + } + } + } + ], + "timestamp": "2026-08-20T17:30:02Z" +} diff --git a/packages/fhir/fixtures/conformance/study-attribution/source-event.json.license b/packages/fhir/fixtures/conformance/study-attribution/source-event.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/study-attribution/source-event.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/normalized/google-health-active-energy.json b/packages/fhir/fixtures/normalized/google-health-active-energy.json index 5b32b94f..122580e7 100644 --- a/packages/fhir/fixtures/normalized/google-health-active-energy.json +++ b/packages/fhir/fixtures/normalized/google-health-active-energy.json @@ -1,18 +1,16 @@ { "source": { - "adapter": { "kind": "providers", "provider": "google-health-api" }, - "providerScopeIdentifier": { - "system": "https://example.org/deployments/provider-accounts", - "value": "pseudonym-google-001", - "assurance": "deployment-scoped-account-pseudonym" + "adapter": { + "kind": "providers", + "provider": "google-health-api" }, "sourceType": "active-energy-burned", "sourceNativeId": "synthetic-active-energy-2026-08-20", - "recordingMethod": "automatically-recorded", - "dataOrigin": { + "writer": { "sourceDeviceToken": "origin-instance-google-001", "name": "Google Health" - } + }, + "recordingMethod": "automatically-recorded" }, "measurements": [ { diff --git a/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json b/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json index 29a31b94..1deb2385 100644 --- a/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json +++ b/packages/fhir/fixtures/normalized/google-health-heart-rate-recording.json @@ -4,14 +4,14 @@ "kind": "providers", "provider": "google-health-api" }, - "providerScopeIdentifier": { - "system": "https://example.org/deployments/provider-accounts", - "value": "pseudonym-google-001", - "assurance": "deployment-scoped-account-pseudonym" - }, "sourceType": "heart-rate", "sourceNativeId": "synthetic-google-heart-rate-series-001", - "dataOrigin": { + "effective": { + "kind": "period", + "start": "2026-08-19T00:00:00Z", + "end": "2026-08-20T00:00:00Z" + }, + "writer": { "sourceDeviceToken": "origin-instance-google-001", "name": "Google Health API" } diff --git a/packages/fhir/fixtures/normalized/oura-daily-activity.json b/packages/fhir/fixtures/normalized/oura-daily-activity.json index 3b03df5a..00f890b2 100644 --- a/packages/fhir/fixtures/normalized/oura-daily-activity.json +++ b/packages/fhir/fixtures/normalized/oura-daily-activity.json @@ -1,18 +1,16 @@ { "source": { - "adapter": { "kind": "providers", "provider": "oura" }, - "providerScopeIdentifier": { - "system": "https://example.org/provider-key-spaces", - "value": "oura-document-uuid-global", - "assurance": "documented-global-key-space" + "adapter": { + "kind": "providers", + "provider": "oura" }, "sourceType": "daily_activity", "sourceNativeId": "synthetic-daily-activity-2026-08-20", - "recordingMethod": "automatically-recorded", - "dataOrigin": { + "writer": { "sourceDeviceToken": "origin-instance-oura-001", "name": "Oura" - } + }, + "recordingMethod": "automatically-recorded" }, "measurements": [ { diff --git a/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json b/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json index b14d0138..f2fed1b6 100644 --- a/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json +++ b/packages/fhir/fixtures/normalized/oura-heart-rate-recording.json @@ -4,14 +4,14 @@ "kind": "providers", "provider": "oura" }, - "providerScopeIdentifier": { - "system": "https://example.org/provider-key-spaces", - "value": "oura-document-uuid-global", - "assurance": "documented-global-key-space" - }, "sourceType": "heartrate", "sourceNativeId": "synthetic-oura-heart-rate-series-001", - "dataOrigin": { + "effective": { + "kind": "period", + "start": "2026-08-19T00:00:00Z", + "end": "2026-08-20T00:00:00Z" + }, + "writer": { "sourceDeviceToken": "origin-instance-oura-001", "name": "Oura" } diff --git a/packages/fhir/fixtures/normalized/oura-sleep-duration.json b/packages/fhir/fixtures/normalized/oura-sleep-duration.json index bbd0470d..1dad8d11 100644 --- a/packages/fhir/fixtures/normalized/oura-sleep-duration.json +++ b/packages/fhir/fixtures/normalized/oura-sleep-duration.json @@ -1,18 +1,16 @@ { "source": { - "adapter": { "kind": "providers", "provider": "oura" }, - "providerScopeIdentifier": { - "system": "https://example.org/provider-key-spaces", - "value": "oura-document-uuid-global", - "assurance": "documented-global-key-space" + "adapter": { + "kind": "providers", + "provider": "oura" }, "sourceType": "sleep", "sourceNativeId": "synthetic-sleep-2026-08-19", - "recordingMethod": "automatically-recorded", - "dataOrigin": { + "writer": { "sourceDeviceToken": "origin-instance-oura-001", "name": "Oura" - } + }, + "recordingMethod": "automatically-recorded" }, "measurements": [ { diff --git a/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json b/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json index 59b98d5e..cb84988d 100644 --- a/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json +++ b/packages/fhir/fixtures/normalized/withings-activity-intraday-recording.json @@ -4,14 +4,14 @@ "kind": "providers", "provider": "withings" }, - "providerScopeIdentifier": { - "system": "https://example.org/deployments/provider-accounts", - "value": "pseudonym-withings-001", - "assurance": "deployment-scoped-account-pseudonym" - }, "sourceType": "activityIntraday", "sourceNativeId": "synthetic-withings-activity-series-001", - "dataOrigin": { + "effective": { + "kind": "period", + "start": "2026-08-19T00:00:00Z", + "end": "2026-08-20T00:00:00Z" + }, + "writer": { "sourceDeviceToken": "origin-instance-withings-001", "name": "Withings" } diff --git a/packages/fhir/fixtures/normalized/withings-body-weight.json b/packages/fhir/fixtures/normalized/withings-body-weight.json index b9078412..3efc70c4 100644 --- a/packages/fhir/fixtures/normalized/withings-body-weight.json +++ b/packages/fhir/fixtures/normalized/withings-body-weight.json @@ -1,18 +1,16 @@ { "source": { - "adapter": { "kind": "providers", "provider": "withings" }, - "providerScopeIdentifier": { - "system": "https://example.org/deployments/provider-accounts", - "value": "pseudonym-withings-001", - "assurance": "deployment-scoped-account-pseudonym" + "adapter": { + "kind": "providers", + "provider": "withings" }, "sourceType": "getmeas:1", "sourceNativeId": "synthetic-weight-2026-08-20", - "recordingMethod": "automatically-recorded", - "dataOrigin": { + "writer": { "sourceDeviceToken": "origin-instance-withings-001", "name": "Withings" - } + }, + "recordingMethod": "automatically-recorded" }, "measurements": [ { diff --git a/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json b/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json index 2c86102d..40fa36d4 100644 --- a/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json +++ b/packages/fhir/fixtures/normalized/withings-sleep-intraday-recording.json @@ -4,14 +4,14 @@ "kind": "providers", "provider": "withings" }, - "providerScopeIdentifier": { - "system": "https://example.org/deployments/provider-accounts", - "value": "pseudonym-withings-001", - "assurance": "deployment-scoped-account-pseudonym" - }, "sourceType": "sleepIntraday", "sourceNativeId": "synthetic-withings-sleep-series-001", - "dataOrigin": { + "effective": { + "kind": "period", + "start": "2026-08-19T00:00:00Z", + "end": "2026-08-20T00:00:00Z" + }, + "writer": { "sourceDeviceToken": "origin-instance-withings-001", "name": "Withings" } diff --git a/packages/fhir/package.json b/packages/fhir/package.json index 3c58686c..fc815a9b 100644 --- a/packages/fhir/package.json +++ b/packages/fhir/package.json @@ -53,7 +53,9 @@ "test": "npm run test:unit && npm run test:package", "test:browser": "npm run build && node scripts/browser-smoke.mjs", "test:coverage": "NODE_OPTIONS=--experimental-vm-modules jest --coverage", - "test:package": "npm run build && node scripts/package-smoke.mjs", + "test:package": "npm run build && node scripts/package-smoke.mjs && npm run check:surface", + "generate:surface": "npm run build && node scripts/type-surface.mjs", + "check:surface": "node scripts/type-surface.mjs --check", "test:unit": "NODE_OPTIONS=--experimental-vm-modules jest", "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch", "typecheck": "npm run check:catalog && npm run check:zod && tsc --noEmit && tsc -p test/tsconfig.json", @@ -92,7 +94,9 @@ "dependencies": { "@noble/hashes": "2.3.0", "@types/fhir": "^0.0.44", - "uuid": "^14.0.2", + "uuid": "^14.0.2" + }, + "peerDependencies": { "zod": "^4.4.3" }, "devDependencies": { @@ -108,7 +112,8 @@ "prettier": "^3.6.2", "rimraf": "^6.1.2", "ts-jest": "^29.4.12", - "typescript": "^6.0.3" + "typescript": "^6.0.3", + "zod": "^4.4.3" }, "engines": { "node": ">=24" diff --git a/packages/fhir/scripts/browser-smoke.mjs b/packages/fhir/scripts/browser-smoke.mjs index d2151deb..ae916536 100644 --- a/packages/fhir/scripts/browser-smoke.mjs +++ b/packages/fhir/scripts/browser-smoke.mjs @@ -104,125 +104,96 @@ try { system: 'https://study.example.org/fhir/identifiers/mobile-observation', value: 'heart-rate-20260820-001', }) - const deploymentIdentity = { - opaqueIdentifierSystems: Object.fromEntries( - [ - 'source-record', - 'source-output', - 'writer-record', - 'provider-record', - 'provider-output', - 'source-artifact', - 'provider-artifact', - 'source-context', - 'recording-device', - 'device-snapshot', - ].map((kind) => [ - kind, - `https://example.org/browser/identity/${kind}/browser-key/1`, - ]), - ), - eventIdentifierSystem: 'https://example.org/browser/identity/event', - entryNodeIdentifierSystem: - 'https://example.org/browser/identity/entry-node', - keyId: 'browser-key', - keyEpoch: '1', - secretBase64Url: 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', - producerInstance: '9738c1a2-4258-4ba7-a9f7-e7f56e2996d8', - } - const providerSubject = { - type: 'Patient', - identifier: { - system: 'https://example.org/browser/patient-pseudonyms', - value: 'patient-browser', - assurance: 'deployment-scoped-pseudonym', - }, + const unwrap = (result) => { + if (!result.ok) throw new Error(JSON.stringify(result.issues)) + return result.value } - const measurementGraph = provider.buildProviderMeasurementBundle({ - subject: providerSubject, - measurements: [ - { - kind: 'heart-rate', - value: 64, - effective: { kind: 'date-time', value: '2026-08-20T12:00:00Z' }, - }, - ], - source: { - adapter: { kind: 'providers', provider: 'withings' }, - providerScopeIdentifier: { - system: 'https://example.org/provider-account-pseudonyms', - value: 'browser-account', - assurance: 'deployment-scoped-account-pseudonym', - }, - sourceType: 'getmeas:11', - sourceNativeId: 'browser-heart-rate', - dataOrigin: { - sourceDeviceToken: 'browser-withings-origin', - name: 'Withings', + const identityScope = unwrap( + mobile.validateOpaqueIdentityScope({ + systems: unwrap( + mobile.deriveOpaqueIdentitySystems( + 'https://example.org/browser', + 'browser-key', + '1', + ), + ), + keyId: 'browser-key', + keyEpoch: '1', + secretBase64Url: 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', + producerInstance: '9738c1a2-4258-4ba7-a9f7-e7f56e2996d8', + }), + ) + const context = (sequence, repositoryScope) => ({ + subject: { + kind: 'logical', + identifier: { + system: 'https://example.org/browser/patient-pseudonyms', + value: 'patient-browser', }, }, + event: unwrap(mobile.deriveEventIdentifier(identityScope, sequence)), + identityScope, + repositoryScope, application: { sourceDeviceToken: 'browser-converter', name: 'Browser converter', }, - eventSequence: '1', - deploymentIdentity, - occurred: '2026-08-20T12:00:00Z', - recorded: '2026-08-20T12:02:00Z', - assembled: '2026-08-20T12:03:00Z', + host: { + sourceDeviceToken: 'browser-host', + operatingSystemVersion: 'Browser 1', + }, + conversionInstant: '2026-08-20T12:03:00Z', }) - const outputCoordinates = provider.providerOutputCoordinates( - 'withings', - 'getmeas:11', - 'heart-rate', - ) - const retraction = - outputCoordinates === undefined ? - { ok: false } - : provider.buildProviderRetractionBundle({ - source: { - provider: 'withings', - providerScopeIdentifier: { - system: 'https://example.org/provider-account-pseudonyms', - value: 'browser-account', - assurance: 'deployment-scoped-account-pseudonym', - }, - sourceType: 'getmeas:11', - sourceNativeId: 'browser-heart-rate', + const accountScope = { + system: 'https://example.org/provider-account-pseudonyms', + value: 'browser-account', + } + const measurementGraph = provider.buildProviderExchangeGraph( + { + source: { + adapter: { kind: 'providers', provider: 'withings' }, + sourceType: 'getmeas:11', + sourceNativeId: 'browser-heart-rate', + writer: { + sourceDeviceToken: 'browser-withings-origin', + name: 'Withings', }, - targets: [ - { - role: 'primary-output', - resourceType: 'Observation', - ...outputCoordinates, - }, - ], - application: { - sourceDeviceToken: 'browser-converter', - name: 'Browser converter', + }, + measurements: [ + { + kind: 'heart-rate', + value: 64, + effective: { kind: 'date-time', value: '2026-08-20T12:00:00Z' }, }, - eventSequence: '3', - deploymentIdentity, - occurred: '2026-08-21T12:00:00Z', - recorded: '2026-08-21T12:01:00Z', - assembled: '2026-08-21T12:02:00Z', - }) - const recording = provider.buildProviderRecordingBundle({ - source: { + ], + }, + context('1', accountScope), + ) + const retraction = + measurementGraph.ok ? + provider.buildProviderRetractionEvent( + unwrap(grove.retractionTargets(measurementGraph.value.graph)), + context('3', accountScope), + measurementGraph.value.identifiers.sourceRecord, + '2026-08-21T12:00:00Z', + ) + : { ok: false } + const recording = provider.buildProviderRecordingGraph( + { adapter: { kind: 'providers', provider: 'google-health-api' }, - providerScopeIdentifier: { - system: 'https://example.org/provider-account-pseudonyms', - value: 'browser-raw-account', - assurance: 'deployment-scoped-account-pseudonym', - }, sourceType: 'heart-rate', sourceNativeId: 'browser-native-recording-42', - dataOrigin: { + writer: { sourceDeviceToken: 'browser-google-origin', name: 'Google Health API', }, + effective: { + kind: 'period', + start: '2026-08-20T00:00:00Z', + end: '2026-08-20T12:00:00Z', + }, }, - attachment: { + { kind: 'embedded', contentType: provider.groveRecordingFormatRegistry.formats['provider-recording'] @@ -232,18 +203,18 @@ try { payloadAssertion: 'caller-authorized-opaque-payload', dataBase64: 'AQID', }, - subject: providerSubject, - application: { - sourceDeviceToken: 'browser-raw-converter', - name: 'Browser raw converter', - }, - eventSequence: '2', - deploymentIdentity, - documentDate: '2026-08-20T12:01:00Z', - occurred: '2026-08-20T12:00:00Z', - recorded: '2026-08-20T12:02:00Z', - assembled: '2026-08-20T12:03:00Z', - }) + context('2', { + system: 'https://example.org/provider-account-pseudonyms', + value: 'browser-raw-account', + }), + ) + const replayed = + measurementGraph.ok ? + grove.isSemanticallyEqual( + JSON.stringify(measurementGraph.value.graph), + JSON.stringify(measurementGraph.value.graph, null, 2), + ) + : { ok: false } const instrument = questionnaire.buildQuestionnaire({ url: 'https://example.org/Questionnaire/browser', version: '1.0.0', @@ -304,14 +275,15 @@ try { ), ), ).size, + replayed: replayed.ok && replayed.value, providerApiVisibleFromMobile: - 'buildProviderMeasurementBundle' in mobile || - 'buildProviderRecordingBundle' in mobile || - 'buildProviderRetractionBundle' in mobile, + 'buildProviderExchangeGraph' in mobile || + 'buildProviderRecordingGraph' in mobile || + 'buildProviderRetractionEvent' in mobile, providerApiVisibleFromRoot: - 'buildProviderMeasurementBundle' in grove || - 'buildProviderRecordingBundle' in grove || - 'buildProviderRetractionBundle' in grove, + 'buildProviderExchangeGraph' in grove || + 'buildProviderRecordingGraph' in grove || + 'buildProviderRetractionEvent' in grove, internalGraphVisible: 'groveFhirPackageGraph' in grove || 'groveFhirProfileClaims' in grove || @@ -328,6 +300,7 @@ try { result.measurementGraph !== true || result.recordingGraph !== true || result.retractionGraph !== true || + result.replayed !== true || result.rawSourceCount !== 4 || result.scalarMeasurementCount !== 47 || result.providerApiVisibleFromMobile || diff --git a/packages/fhir/scripts/fetch-grove-fhir.mjs b/packages/fhir/scripts/fetch-grove-fhir.mjs index ab1b4fe1..626e0fcf 100644 --- a/packages/fhir/scripts/fetch-grove-fhir.mjs +++ b/packages/fhir/scripts/fetch-grove-fhir.mjs @@ -9,13 +9,24 @@ // Fetch the Grove FHIR catalogs this package generates from. The implementation guide // owns them, so they are read at the pinned commit rather than copied into this // repository, where a second copy could drift from the contract it claims to follow. +// A local checkout can stand in for the pin while a contract change is still unmerged: +// `--local ` or GROVE_FHIR_CHECKOUT vendors the same paths from that working tree. import { Buffer } from 'node:buffer' import { execFile } from 'node:child_process' import { createHash } from 'node:crypto' -import { mkdtemp, readFile, rename, rm, writeFile } from 'node:fs/promises' +import { + cp, + mkdtemp, + readFile, + realpath, + rename, + rm, + stat, + writeFile, +} from 'node:fs/promises' import { dirname, resolve } from 'node:path' -import { stdout } from 'node:process' +import { argv, env, stdout } from 'node:process' import { fileURLToPath } from 'node:url' import { promisify } from 'node:util' @@ -25,6 +36,14 @@ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..') const destination = resolve(packageRoot, '.grove-fhir') const marker = resolve(destination, '.ref') const repository = 'https://github.com/SchmiedmayerLab/grove-fhir' +// The paths this package reads; the rest of the guide stays upstream. +const consumedPaths = [ + 'catalog', + 'Conformance/corpora/mobile-exchange', + 'Conformance/corpora/mobile-semantics', + 'Conformance/corpora/receiver-lifecycle', + 'Conformance/corpora/study-attribution', +] const pin = JSON.parse( await readFile(resolve(packageRoot, 'grove-fhir.json'), 'utf8'), @@ -43,45 +62,14 @@ if ( ) } -const markerValue = `${JSON.stringify({ ref, archiveSha256 })}\n` -const present = await readFile(marker, 'utf8').catch(() => undefined) -// Every generator and checker depends on this, so repeated runs are the common case. -if (present !== markerValue) { - const archive = `${repository}/archive/${ref}.tar.gz` - const response = await fetch(archive) - if (!response.ok) { - throw new Error(`Could not fetch ${archive}: ${response.status}`) - } - - const bytes = Buffer.from(await response.arrayBuffer()) - const actualSha256 = createHash('sha256').update(bytes).digest('hex') - if (actualSha256 !== archiveSha256) { - throw new Error( - `Grove FHIR archive digest mismatch.\n expected ${archiveSha256}\n actual ${actualSha256}`, - ) - } +const localArgument = argv.indexOf('--local') +const localCheckout = + localArgument === -1 ? env.GROVE_FHIR_CHECKOUT : argv[localArgument + 1] +const replaceDestination = async (stage) => { const staging = await mkdtemp(resolve(packageRoot, '.grove-fhir-fetch-')) try { - const tarball = resolve(staging, 'grove-fhir.tar.gz') - await writeFile(tarball, bytes) - - // The archive root is named for the commit, so strip it and keep only what is read. - await run('tar', [ - '--extract', - '--file', - tarball, - '--directory', - staging, - '--strip-components', - '1', - '--no-same-owner', - '--no-same-permissions', - `grove-fhir-${ref}/catalog`, - `grove-fhir-${ref}/Conformance/corpora/mobile-semantics`, - `grove-fhir-${ref}/Conformance/corpora/mobile-exchange`, - ]) - await rm(tarball) + const markerValue = await stage(staging) await writeFile(resolve(staging, '.ref'), markerValue) await rm(destination, { recursive: true, force: true }) await rename(staging, destination) @@ -89,5 +77,64 @@ if (present !== markerValue) { await rm(staging, { recursive: true, force: true }) throw error } - stdout.write(`Fetched Grove FHIR catalogs at ${ref}\n`) +} + +if (localCheckout !== undefined) { + const checkout = await realpath(resolve(localCheckout)) + for (const path of consumedPaths) { + const status = await stat(resolve(checkout, path)).catch(() => undefined) + if (status?.isDirectory() !== true) { + throw new Error(`Grove FHIR checkout ${checkout} has no ${path}.`) + } + } + // A working tree changes between runs, so it is copied every time. + await replaceDestination(async (staging) => { + for (const path of consumedPaths) { + await cp(resolve(checkout, path), resolve(staging, path), { + recursive: true, + }) + } + return `${JSON.stringify({ local: checkout })}\n` + }) + stdout.write(`Vendored the Grove FHIR contract from ${checkout}\n`) +} else { + const markerValue = `${JSON.stringify({ ref, archiveSha256 })}\n` + const present = await readFile(marker, 'utf8').catch(() => undefined) + // Every generator and checker depends on this, so repeated runs are the common case. + if (present !== markerValue) { + const archive = `${repository}/archive/${ref}.tar.gz` + const response = await fetch(archive) + if (!response.ok) { + throw new Error(`Could not fetch ${archive}: ${response.status}`) + } + + const bytes = Buffer.from(await response.arrayBuffer()) + const actualSha256 = createHash('sha256').update(bytes).digest('hex') + if (actualSha256 !== archiveSha256) { + throw new Error( + `Grove FHIR archive digest mismatch.\n expected ${archiveSha256}\n actual ${actualSha256}`, + ) + } + + await replaceDestination(async (staging) => { + const tarball = resolve(staging, 'grove-fhir.tar.gz') + await writeFile(tarball, bytes) + // The archive root is named for the commit, so strip it and keep only what is read. + await run('tar', [ + '--extract', + '--file', + tarball, + '--directory', + staging, + '--strip-components', + '1', + '--no-same-owner', + '--no-same-permissions', + ...consumedPaths.map((path) => `grove-fhir-${ref}/${path}`), + ]) + await rm(tarball) + return markerValue + }) + stdout.write(`Fetched Grove FHIR catalogs at ${ref}\n`) + } } diff --git a/packages/fhir/scripts/generate-conformance-fixtures.mjs b/packages/fhir/scripts/generate-conformance-fixtures.mjs index 5b274bfb..e04bb4a6 100644 --- a/packages/fhir/scripts/generate-conformance-fixtures.mjs +++ b/packages/fhir/scripts/generate-conformance-fixtures.mjs @@ -21,24 +21,28 @@ const providerApi = await import(`${distRoot}/providers/index.js`) const { buildQuestionnaire, buildQuestionnaireResponse, - groveExchangeProtocol, + deriveEventIdentifier, + deriveOpaqueIdentitySystems, groveMobilePackageMetadata, groveRecordingFormatRegistry, parseAbsoluteUri, parseCanonical, + parseEventSequence, parseFhirInstant, + parseKeyEpoch, parseSemVer, + retractionTargets, sharedMobileMeasurementCatalog, + validateOpaqueIdentityScope, } = rootApi const { adapterMeasurementCatalog, - buildProviderRecordingBundle, - buildProviderMeasurementBundle, - buildProviderRetractionBundle, + buildProviderExchangeGraph, + buildProviderRecordingGraph, + buildProviderRetractionEvent, providerAdapterCatalog, providerRawOutputRoles, providerRecordEffectiveRules, - providerScalarOutputDiscriminators, providerScalarOutputRoles, encodeRecordingBytes, parseMediaType, @@ -51,7 +55,6 @@ const upstreamRoot = resolve(root, '.grove-fhir') : resolve(argv[localIgIndex + 1] ?? '') const fixtureRoot = resolve(root, 'fixtures/conformance') -const resourceRoot = resolve(fixtureRoot, 'resources') const check = argv.includes('--check') const packageMetadata = JSON.parse( await readFile(resolve(root, 'package.json'), 'utf8'), @@ -62,21 +65,37 @@ const semanticCorpus = JSON.parse( 'utf8', ), ) -const mobileExchangeCorpusRoot = resolve( - upstreamRoot, - 'Conformance/corpora/mobile-exchange', -) const sharedMobileExchangeFiles = [ 'exchange-bundle.json', 'retraction-bundle.json', 'corpus.json', ] -const sharedMobileExchange = new Map( +// The corpora this package binds in its own tests travel with the fixtures, so the +// tests read committed bytes rather than the fetched contract. +const sharedCorpusFiles = [ + ...sharedMobileExchangeFiles.map((name) => `mobile-exchange/${name}`), + 'receiver-lifecycle/events.json', + 'receiver-lifecycle/sequences.json', + 'receiver-lifecycle/revision-sequences.json', + ...( + await readdir( + resolve(upstreamRoot, 'Conformance/corpora/receiver-lifecycle/resources'), + ) + ) + .filter((name) => name.endsWith('.json')) + .sort() + .map((name) => `receiver-lifecycle/resources/${name}`), + 'study-attribution/source-event.json', + 'study-attribution/context-two-studies.json', + 'study-attribution/negative-cases.json', +] +const sharedCorpora = new Map( await Promise.all( - sharedMobileExchangeFiles.map(async (name) => [ - `mobile-exchange/${name}`, - JSON.parse( - await readFile(resolve(mobileExchangeCorpusRoot, name), 'utf8'), + sharedCorpusFiles.map(async (name) => [ + name, + await readFile( + resolve(upstreamRoot, 'Conformance/corpora', name), + 'utf8', ), ]), ), @@ -154,12 +173,12 @@ const unwrap = (result) => { const uri = (value) => unwrap(parseAbsoluteUri(value)) const instant = (value) => unwrap(parseFhirInstant(value)) -const providerSubject = { - type: 'Patient', +const sequence = (value) => unwrap(parseEventSequence(value)) +const subject = { + kind: 'logical', identifier: { system: uri('https://grovealliance.org/fhir/testing/patient-pseudonyms'), value: 'patient-example', - assurance: 'deployment-scoped-pseudonym', }, } @@ -168,11 +187,15 @@ const application = { name: 'Grove TypeScript conformance producer', version: producerVersion, } -const dataOrigin = { +const host = { + sourceDeviceToken: 'grove-ts-conformance-host', + operatingSystemVersion: 'Node.js 24', +} +const writer = { sourceDeviceToken: 'synthetic-connected-provider', name: 'Synthetic connected provider', } -const providerScopeIdentifier = (provider) => { +const repositoryScope = (provider) => { const scope = providerAdapterCatalog.providers.find( ({ id }) => id === provider, )?.identifierScope @@ -182,38 +205,42 @@ const providerScopeIdentifier = (provider) => { 'https://example.org/deployments/provider-account-pseudonyms', ), value: `account-${provider}`, - assurance: 'deployment-scoped-account-pseudonym', } } if (scope === 'global' || scope === 'none') { return { system: uri('https://example.org/provider-key-spaces'), value: `${provider}-document-id-global`, - assurance: 'documented-global-key-space', } } throw new Error(`Provider ${provider} has no closed identifier scope.`) } -const deploymentIdentity = { - opaqueIdentifierSystems: Object.fromEntries( - groveExchangeProtocol.opaqueIdentity.identityKinds.map(({ kind }) => [ - kind, - uri( - `https://grovealliance.org/fhir/testing/identity/${kind}/fixture-key/1`, +// The deployment names its systems by the catalog's recommended form (D1). +const identityScope = unwrap( + validateOpaqueIdentityScope({ + systems: unwrap( + deriveOpaqueIdentitySystems( + uri('https://grovealliance.org/fhir/testing'), + 'fixture-key', + unwrap(parseKeyEpoch('1')), ), - ]), - ), - eventIdentifierSystem: uri( - 'https://grovealliance.org/fhir/testing/identity/event', - ), - entryNodeIdentifierSystem: uri( - 'https://grovealliance.org/fhir/testing/identity/entry-node', - ), - keyId: 'fixture-key', - keyEpoch: '1', - secretBase64Url: 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', - producerInstance: '33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc', -} + ), + keyId: 'fixture-key', + keyEpoch: unwrap(parseKeyEpoch('1')), + secretBase64Url: 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', + producerInstance: '33fc1b64-6b4c-4bb8-994a-82a8d72eb5dc', + }), +) +const context = (provider, eventSequence, conversionInstant, extra = {}) => ({ + subject, + event: unwrap(deriveEventIdentifier(identityScope, sequence(eventSequence))), + identityScope, + repositoryScope: repositoryScope(provider), + application, + host, + conversionInstant: instant(conversionInstant), + ...extra, +}) // The shared semantic corpus covers Mobile semantics; owner-exclusive Provider // measurements are generated separately from their exact catalog definitions below. const providerAdmittedMeasurementIds = new Set( @@ -386,57 +413,56 @@ const providerFor = (measurement, index) => { throw new Error(`No Provider provider admits ${measurement.kind}.`) } -const providerMeasurementBundle = (measurement, index) => { +const measurementRecord = (provider, sourceType, sourceNativeId, kinds) => ({ + source: { + adapter: { kind: 'providers', provider }, + sourceType, + sourceNativeId, + recordingMethod: 'automatically-recorded', + writer, + }, + measurements: kinds, +}) + +const conversionInstant = '2026-08-20T12:03:00Z' +const conversions = new Map() +const measurementGraph = (measurement, index) => { const provider = providerFor(measurement, index) - return unwrap( - buildProviderMeasurementBundle({ - subject: providerSubject, - measurements: [measurement], - source: { - adapter: { - kind: 'providers', - provider: provider.provider, - }, - providerScopeIdentifier: providerScopeIdentifier(provider.provider), - sourceType: provider.sourceType, - sourceNativeId: `source-${measurement.kind}`, - recordingMethod: 'automatically-recorded', - dataOrigin, - }, - application, - eventSequence: measurementEventSequences[index], - deploymentIdentity, - occurred: instant('2026-08-20T12:00:00Z'), - recorded: instant('2026-08-20T12:02:00Z'), - assembled: instant('2026-08-20T12:03:00Z'), - }), + const conversion = unwrap( + buildProviderExchangeGraph( + measurementRecord( + provider.provider, + provider.sourceType, + `source-${measurement.kind}`, + [measurement], + ), + context( + provider.provider, + measurementEventSequences[index], + conversionInstant, + ), + ), ) + conversions.set(`resources/mobile-${measurement.kind}.json`, conversion) + return conversion.graph } -const exclusiveProviderMeasurementBundle = ( +const exclusiveMeasurementGraph = ( { provider, sourceType, measurement }, index, ) => unwrap( - buildProviderMeasurementBundle({ - subject: providerSubject, - measurements: [measurement], - source: { - adapter: { kind: 'providers', provider }, - providerScopeIdentifier: providerScopeIdentifier(provider), - sourceType, - sourceNativeId: `source-${measurement.kind}`, - recordingMethod: 'automatically-recorded', - dataOrigin, - }, - application, - eventSequence: exclusiveMeasurementEventSequences[index], - deploymentIdentity, - occurred: instant('2026-08-20T12:00:00Z'), - recorded: instant('2026-08-20T12:02:00Z'), - assembled: instant('2026-08-20T12:03:00Z'), - }), - ) + buildProviderExchangeGraph( + measurementRecord(provider, sourceType, `source-${measurement.kind}`, [ + measurement, + ]), + context( + provider, + exclusiveMeasurementEventSequences[index], + conversionInstant, + ), + ), + ).graph const measurementByKind = new Map( measurements.map((measurement) => [measurement.kind, measurement]), @@ -457,26 +483,17 @@ const ouraDailyActivityMeasurements = [ } return { ...measurement, effective: ouraDailyActivityEffective } }) -const ouraDailyActivityBundle = unwrap( - buildProviderMeasurementBundle({ - subject: providerSubject, - measurements: ouraDailyActivityMeasurements, - source: { - adapter: { kind: 'providers', provider: 'oura' }, - providerScopeIdentifier: providerScopeIdentifier('oura'), - sourceType: 'daily_activity', - sourceNativeId: 'source-oura-daily-activity', - recordingMethod: 'automatically-recorded', - dataOrigin, - }, - application, - eventSequence: ouraDailyActivityEventSequence, - deploymentIdentity, - occurred: instant('2026-08-21T07:00:00Z'), - recorded: instant('2026-08-21T07:02:00Z'), - assembled: instant('2026-08-21T07:03:00Z'), - }), -) +const ouraDailyActivityGraph = unwrap( + buildProviderExchangeGraph( + measurementRecord( + 'oura', + 'daily_activity', + 'source-oura-daily-activity', + ouraDailyActivityMeasurements, + ), + context('oura', ouraDailyActivityEventSequence, '2026-08-21T07:03:00Z'), + ), +).graph const recordingSources = Object.entries(providerRawOutputRoles).flatMap( ([provider, sources]) => @@ -491,17 +508,22 @@ const recordingEventSequences = recordingSources.map((_, index) => const recordingPath = ({ provider, sourceType }) => `resources/recording-${provider}-${sourceType.replaceAll(/[^A-Za-z0-9]+/gu, '-').toLowerCase()}.json` -const recordingBundle = ({ provider, sourceType }, index) => - unwrap( - buildProviderRecordingBundle({ - source: { +const recordingConversions = new Map() +const recordingGraph = ({ provider, sourceType }, index) => { + const conversion = unwrap( + buildProviderRecordingGraph( + { adapter: { kind: 'providers', provider }, - providerScopeIdentifier: providerScopeIdentifier(provider), sourceType, sourceNativeId: `raw-source-${provider}-${String(index + 1)}`, - dataOrigin, + writer, + effective: { + kind: 'period', + start: instant('2026-08-20T00:00:00Z'), + end: instant('2026-08-20T12:00:00Z'), + }, }, - attachment: { + { kind: 'embedded', contentType: unwrap( parseMediaType( @@ -516,15 +538,24 @@ const recordingBundle = ({ provider, sourceType }, index) => encodeRecordingBytes(Buffer.from('{"synthetic":true}', 'utf8')), ), }, - subject: providerSubject, - application, - eventSequence: recordingEventSequences[index], - deploymentIdentity, - documentDate: instant('2026-08-20T12:01:00Z'), - occurred: instant('2026-08-20T12:00:00Z'), - recorded: instant('2026-08-20T12:02:00Z'), - assembled: instant('2026-08-20T12:03:00Z'), - }), + context(provider, recordingEventSequences[index], conversionInstant), + ), + ) + recordingConversions.set(recordingPath({ provider, sourceType }), conversion) + return conversion.graph +} + +// The retraction names what the accepted graph itself says is retractable: the one +// primary output and the converter's own application snapshot. +const targetsOf = (graph) => + unwrap(retractionTargets(graph)).filter( + (target) => + target.role !== 'device-snapshot' || + graph.entry.find(({ resource }) => + resource.identifier?.some( + ({ value }) => value === target.identifier.value, + ), + )?.resource.deviceName?.[0]?.name === application.name, ) const primaryRetractionMeasurement = measurements[0] @@ -533,107 +564,23 @@ if (primaryRetractionMeasurement === undefined) { 'A primary-output retraction fixture requires one measurement.', ) } -const primaryRetractionProvider = providerFor(primaryRetractionMeasurement, 0) -const primaryRetractionOutputRole = - providerScalarOutputRoles[primaryRetractionProvider.provider]?.[ - primaryRetractionProvider.sourceType - ]?.[primaryRetractionMeasurement.kind] -if (primaryRetractionOutputRole === undefined) { - throw new Error('The primary-output retraction role is not catalog-owned.') -} -const primaryRetractionOutputDiscriminator = - providerScalarOutputDiscriminators[primaryRetractionProvider.provider]?.[ - primaryRetractionProvider.sourceType - ]?.[primaryRetractionMeasurement.kind] -if (primaryRetractionOutputDiscriminator === undefined) { - throw new Error( - 'The primary-output retraction discriminator is not catalog-owned.', - ) -} const primaryRetractionEventSequence = eventSequenceAt( measurements.length + exclusiveMeasurementCases.length + 1 + recordingSources.length, ) -const primaryRetractionBundle = unwrap( - buildProviderRetractionBundle({ - source: { - provider: primaryRetractionProvider.provider, - providerScopeIdentifier: providerScopeIdentifier( - primaryRetractionProvider.provider, - ), - sourceType: primaryRetractionProvider.sourceType, - sourceNativeId: `source-${primaryRetractionMeasurement.kind}`, - }, - targets: [ - { - role: 'primary-output', - resourceType: 'Observation', - outputRole: primaryRetractionOutputRole, - outputDiscriminator: primaryRetractionOutputDiscriminator, - }, - { - role: 'device-snapshot', - resourceType: 'Device', - priorEventSequence: measurementEventSequences[0], - deviceRole: 'application', - sourceDeviceToken: application.sourceDeviceToken, - }, - ], - application, - eventSequence: primaryRetractionEventSequence, - deploymentIdentity, - occurred: instant('2026-08-22T12:00:00Z'), - recorded: instant('2026-08-22T12:01:00Z'), - assembled: instant('2026-08-22T12:02:00Z'), - }), -) - -const artifactRetractionSource = recordingSources[0] -if (artifactRetractionSource === undefined) { - throw new Error( - 'A source-artifact retraction fixture requires one recording.', - ) -} const artifactRetractionEventSequence = eventSequenceAt( measurements.length + exclusiveMeasurementCases.length + 2 + recordingSources.length, ) -const artifactRetractionBundle = unwrap( - buildProviderRetractionBundle({ - source: { - provider: artifactRetractionSource.provider, - providerScopeIdentifier: providerScopeIdentifier( - artifactRetractionSource.provider, - ), - sourceType: artifactRetractionSource.sourceType, - sourceNativeId: `raw-source-${artifactRetractionSource.provider}-1`, - }, - targets: [ - { - role: 'source-artifact', - resourceType: 'DocumentReference', - formatCode: 'provider-recording', - partIndex: '0', - }, - { - role: 'device-snapshot', - resourceType: 'Device', - priorEventSequence: recordingEventSequences[0], - deviceRole: 'application', - sourceDeviceToken: application.sourceDeviceToken, - }, - ], - application, - eventSequence: artifactRetractionEventSequence, - deploymentIdentity, - occurred: instant('2026-08-22T13:00:00Z'), - recorded: instant('2026-08-22T13:01:00Z'), - assembled: instant('2026-08-22T13:02:00Z'), - }), +const studyContextEventSequence = eventSequenceAt( + measurements.length + + exclusiveMeasurementCases.length + + 3 + + recordingSources.length, ) const questionnaireUrl = uri( @@ -715,7 +662,7 @@ const questionnaireResponse = unwrap( const resources = new Map( measurements.map((measurement, index) => [ `resources/mobile-${measurement.kind}.json`, - providerMeasurementBundle(measurement, index), + measurementGraph(measurement, index), ]), ) const exclusiveMeasurementPath = ({ provider, measurement }) => @@ -723,25 +670,123 @@ const exclusiveMeasurementPath = ({ provider, measurement }) => for (const [index, entry] of exclusiveMeasurementCases.entries()) { resources.set( exclusiveMeasurementPath(entry), - exclusiveProviderMeasurementBundle(entry, index), + exclusiveMeasurementGraph(entry, index), ) } const ouraDailyActivityPath = 'resources/provider-oura-daily-activity.json' -resources.set(ouraDailyActivityPath, ouraDailyActivityBundle) +resources.set(ouraDailyActivityPath, ouraDailyActivityGraph) for (const [index, source] of recordingSources.entries()) { - resources.set(recordingPath(source), recordingBundle(source, index)) + resources.set(recordingPath(source), recordingGraph(source, index)) } + +const primaryConversion = conversions.get( + `resources/mobile-${primaryRetractionMeasurement.kind}.json`, +) +const primaryRetractionProvider = providerFor(primaryRetractionMeasurement, 0) resources.set( 'resources/provider-primary-output-retraction.json', - primaryRetractionBundle, + unwrap( + buildProviderRetractionEvent( + targetsOf(primaryConversion.graph), + context( + primaryRetractionProvider.provider, + primaryRetractionEventSequence, + '2026-08-22T12:02:00Z', + ), + primaryConversion.identifiers.sourceRecord, + instant('2026-08-22T12:00:00Z'), + ), + ), +) +const artifactRetractionSource = recordingSources[0] +if (artifactRetractionSource === undefined) { + throw new Error( + 'A source-artifact retraction fixture requires one recording.', + ) +} +const artifactConversion = recordingConversions.get( + recordingPath(artifactRetractionSource), ) resources.set( 'resources/provider-source-artifact-retraction.json', - artifactRetractionBundle, + unwrap( + buildProviderRetractionEvent( + targetsOf(artifactConversion.graph), + context( + artifactRetractionSource.provider, + artifactRetractionEventSequence, + '2026-08-22T13:02:00Z', + ), + artifactConversion.identifiers.sourceRecord, + instant('2026-08-22T13:00:00Z'), + ), + ), +) + +// One event under the recommended study shape: a bundled Patient and one enrollment. +const studyContextPath = 'resources/provider-study-context.json' +const studyContextMeasurement = measurementByKind.get('heart-rate') +if (studyContextMeasurement === undefined) { + throw new Error('The study-context fixture requires the heart-rate vector.') +} +const studyContextProvider = providerFor(studyContextMeasurement, 0) +resources.set( + studyContextPath, + unwrap( + buildProviderExchangeGraph( + measurementRecord( + studyContextProvider.provider, + studyContextProvider.sourceType, + 'source-study-context-heart-rate', + [studyContextMeasurement], + ), + context( + studyContextProvider.provider, + studyContextEventSequence, + conversionInstant, + { + subject: { + kind: 'bundled', + identifier: subject.identifier, + patient: { + resourceType: 'Patient', + identifier: [ + { + system: subject.identifier.system, + value: subject.identifier.value, + }, + ], + }, + }, + converterRole: { kind: 'gateway' }, + studies: [ + { + study: { + system: uri('https://grovealliance.org/fhir/testing/studies'), + value: 'grove-ts-conformance-study', + }, + protocol: { + url: uri( + 'https://grovealliance.org/fhir/testing/PlanDefinition/grove-ts-conformance', + ), + version: '1', + }, + enrollment: { + system: uri( + 'https://grovealliance.org/fhir/testing/enrollments', + ), + value: 'grove-ts-conformance-enrollment', + }, + }, + ], + }, + ), + ), + ).graph, ) resources.set('resources/questionnaire.json', questionnaire) resources.set('resources/questionnaire-response.json', questionnaireResponse) -for (const [path, value] of sharedMobileExchange) resources.set(path, value) +for (const [path, text] of sharedCorpora) resources.set(path, JSON.parse(text)) const abstractProviderObservationProfile = providerAdapterCatalog.adapterProfile for (const [path, value] of resources) { @@ -762,10 +807,18 @@ for (const [path, value] of resources) { } } +// The copied receiver corpus replays identifiers by design; only this package's own +// emissions and the normative bases must be distinct. const eventIdentifiers = new Map() for (const [path, value] of resources) { - if (value?.resourceType !== 'Bundle' || value.identifier === undefined) + if ( + value?.resourceType !== 'Bundle' || + value.identifier === undefined || + path.startsWith('receiver-lifecycle/') || + path.startsWith('study-attribution/') + ) { continue + } const { system, value: identifierValue } = value.identifier if (typeof system !== 'string' || typeof identifierValue !== 'string') { throw new Error(`${path} has an incomplete event identifier.`) @@ -810,6 +863,10 @@ const manifest = { path: 'resources/provider-source-artifact-retraction.json', requiredProfiles: [retractionBundleProfile], }, + { + path: studyContextPath, + requiredProfiles: [exchangeBundleProfile], + }, { path: 'mobile-exchange/exchange-bundle.json', requiredProfiles: [exchangeBundleProfile], @@ -838,7 +895,8 @@ const serialized = new Map( await Promise.all( [...resources, ['manifest.json', manifest]].map(async ([path, value]) => [ path, - await format(JSON.stringify(value), { parser: 'json' }), + sharedCorpora.get(path) ?? + (await format(JSON.stringify(value), { parser: 'json' })), ]), ), ) @@ -854,7 +912,15 @@ for (const relative of [...serialized.keys()]) { // The official Validator checks these later in CI; catching a malformed emission here names the // resource and the element rather than surfacing as a validator report a build later. const { resourceSchema } = await import('@schmiedmayerlab/grove-fhir/zod/r4') -const nonResourceArtifacts = new Set(['mobile-exchange/corpus.json']) +const nonResourceArtifacts = new Set( + [...sharedCorpora.keys()].filter( + (path) => + !path.includes('/resources/') && + !path.endsWith('-bundle.json') && + !path.endsWith('source-event.json') && + !path.endsWith('context-two-studies.json'), + ), +) const invalid = [] for (const [relative, resource] of resources) { if (nonResourceArtifacts.has(relative)) continue @@ -873,14 +939,23 @@ if (invalid.length > 0) { ) } -await mkdir(resourceRoot, { recursive: true }) -await mkdir(resolve(fixtureRoot, 'mobile-exchange'), { recursive: true }) +const fixtureDirectories = [ + 'resources', + 'mobile-exchange', + 'receiver-lifecycle', + 'receiver-lifecycle/resources', + 'study-attribution', +] +for (const directory of fixtureDirectories) { + await mkdir(resolve(fixtureRoot, directory), { recursive: true }) +} const staleFixtureFiles = [] -for (const directory of ['resources', 'mobile-exchange']) { +for (const directory of fixtureDirectories) { const expected = new Set( [...serialized.keys()] .filter((relative) => relative.startsWith(`${directory}/`)) - .map((relative) => relative.slice(directory.length + 1)), + .map((relative) => relative.slice(directory.length + 1)) + .filter((relative) => !relative.includes('/')), ) const stale = (await readdir(resolve(fixtureRoot, directory))).filter( (name) => diff --git a/packages/fhir/scripts/generate-measurement-catalog.mjs b/packages/fhir/scripts/generate-measurement-catalog.mjs index b85744e0..501586b5 100644 --- a/packages/fhir/scripts/generate-measurement-catalog.mjs +++ b/packages/fhir/scripts/generate-measurement-catalog.mjs @@ -1435,12 +1435,22 @@ const expectedPatientReservedSystems = Object.values( exchangeProtocol.codeSystems ?? {}, ) // The protocol's registry is the closed rule surface; the corpus only exercises part of it. -const exchangeRuleDiagnostics = Object.fromEntries( - (exchangeProtocol.producerDiagnostics ?? []).map(({ code, reason }) => [ - code, - { reason, severity: 'error' }, - ]), +// A row without severity is an error; every row names the side that emits it. +const producerDiagnostics = Object.fromEntries( + (exchangeProtocol.producerDiagnostics ?? []).map( + ({ code, reason, emittedBy, severity }) => [ + code, + { reason, emittedBy, severity: severity ?? 'error' }, + ], + ), ) +const diagnosticEmitters = ['conformance-kit', 'client'] +const diagnosticSeverities = ['error', 'warning'] +// A deployment substitutes these placeholders when it names its identifier systems. +const systemFormPlaceholders = (form) => + typeof form === 'string' ? (form.match(/<[a-z-]+>/gu) ?? []) : undefined +const studyContext = exchangeProtocol.lifecycle?.active?.studyContext +const payloadEquality = exchangeProtocol.payload?.equality const exchangeRuleRows = exchangeCorpus.cases?.map( ({ expectedRule }) => expectedRule, ) @@ -1552,19 +1562,58 @@ if ( typeof exchangeProtocol.referencePolicy?.declaredType !== 'string' || typeof exchangeProtocol.referencePolicy?.governedShape !== 'string' || exchangeCorpus.schemaVersion !== 0 || - !uniqueNonemptyStrings(Object.keys(exchangeRuleDiagnostics)) || - !Object.hasOwn(exchangeRuleDiagnostics, 'mobile-exchange.unclassified') || - Object.values(exchangeRuleDiagnostics).some( - ({ reason }) => typeof reason !== 'string' || reason.length === 0, + !uniqueNonemptyStrings(Object.keys(producerDiagnostics)) || + !Object.hasOwn(producerDiagnostics, 'mobile-exchange.unclassified') || + !Object.hasOwn(producerDiagnostics, 'mobile-input.unclassified') || + Object.values(producerDiagnostics).some( + ({ reason, emittedBy, severity }) => + typeof reason !== 'string' || + reason.length === 0 || + !diagnosticEmitters.includes(emittedBy) || + !diagnosticSeverities.includes(severity), + ) || + Object.entries(producerDiagnostics).some( + ([code, { severity }]) => + (severity === 'warning') !== code.startsWith('mobile-omission.'), + ) || + !exactSet( + systemFormPlaceholders( + exchangeProtocol.opaqueIdentity?.recommendedSystemForm, + ), + ['', '', '', ''], ) || + !exactSet( + systemFormPlaceholders( + exchangeProtocol.event?.bundleIdentifier?.recommendedSystemForm, + ), + [''], + ) || + !exactSet( + systemFormPlaceholders( + exchangeProtocol.entryIdentity?.entryNode?.recommendedSystemForm, + ), + [''], + ) || + !exactSet(studyContext?.entryNodeRoles, [ + 'patient', + 'research-study', + 'research-subject', + 'plan-definition', + ]) || + typeof studyContext?.recommendedShape !== 'string' || + typeof studyContext?.subject !== 'string' || + typeof studyContext?.protocol !== 'string' || + typeof payloadEquality?.formatting !== 'string' || + typeof payloadEquality?.decimalLexeme !== 'string' || + typeof payloadEquality?.vectors !== 'string' || !Array.isArray(exchangeRuleRows) || exchangeRuleRows.length === 0 || exchangeRuleRows.some( (rule) => typeof rule?.location !== 'string' || rule.location.length === 0 || - exchangeRuleDiagnostics[rule.code]?.reason !== rule.reason || - exchangeRuleDiagnostics[rule.code]?.severity !== rule.severity, + producerDiagnostics[rule.code]?.reason !== rule.reason || + producerDiagnostics[rule.code]?.severity !== rule.severity, ) || typeof exchangeProtocol.recordingDevice?.instanceRule !== 'string' || typeof exchangeProtocol.recordingDevice?.unknownInstance !== 'string' || @@ -1823,7 +1872,7 @@ const generated = { questionnairePackageMetadata, providerPackageMetadata, exchangeProtocol, - exchangeRuleDiagnostics, + producerDiagnostics, profileClaims, packageCanonicals, profiles, @@ -1867,7 +1916,7 @@ const unformattedOutputs = renderMeasurementCatalogSources({ effectiveCanonicalization, effectiveCanonicalizationVectors: semanticEffectiveCanonicalization.vectors, exchangeProtocol: withoutReleaseMetadata(exchangeProtocol), - exchangeRuleDiagnostics, + producerDiagnostics, healthConnectDataOriginApplication, healthKitApplicationDeviceIdentity, healthKitClinicalRecordAdmission, diff --git a/packages/fhir/scripts/measurement-catalog-renderer.mjs b/packages/fhir/scripts/measurement-catalog-renderer.mjs index 0092a01a..57278cfa 100644 --- a/packages/fhir/scripts/measurement-catalog-renderer.mjs +++ b/packages/fhir/scripts/measurement-catalog-renderer.mjs @@ -13,7 +13,7 @@ export const renderMeasurementCatalogSources = ({ effectiveCanonicalization, effectiveCanonicalizationVectors, exchangeProtocol, - exchangeRuleDiagnostics, + producerDiagnostics, healthConnectDataOriginApplication, healthKitApplicationDeviceIdentity, healthKitClinicalRecordAdmission, @@ -39,7 +39,9 @@ export const renderMeasurementCatalogSources = ({ const valueName = `${name}Value` return `const ${valueName} = ${JSON.stringify(value, null, 2)} as const - export const ${name}: typeof ${valueName} = deepFreeze(${valueName})` + export const ${name}: typeof ${valueName} = /*#__PURE__*/ deepFreeze( + ${valueName}, + )` } const header = (disableClearText) => `// @@ -67,7 +69,7 @@ export const renderMeasurementCatalogSources = ({ ${frozenExport('groveExchangeProtocol', exchangeProtocol)} - ${frozenExport('groveExchangeRuleDiagnostics', exchangeRuleDiagnostics)} + ${frozenExport('groveProducerDiagnostics', producerDiagnostics)} ${frozenExport('groveProfileClaims', profileClaims)} @@ -85,6 +87,8 @@ export const renderMeasurementCatalogSources = ({ export type SharedMobileMeasurementKind = keyof typeof sharedMobileMeasurementCatalog + export type ProducerDiagnosticCode = keyof typeof groveProducerDiagnostics + export type GroveRecordingFormat = keyof typeof groveRecordingFormatRegistry.formats diff --git a/packages/fhir/scripts/package-smoke.mjs b/packages/fhir/scripts/package-smoke.mjs index 5d0cb1be..a2297afe 100644 --- a/packages/fhir/scripts/package-smoke.mjs +++ b/packages/fhir/scripts/package-smoke.mjs @@ -47,14 +47,22 @@ for (const [name, entryPoint] of [ } assert.equal(typeof mobile.canonicalizeMobileEffectiveInstant, 'function') +assert.equal(typeof mobile.validateOpaqueIdentityScope, 'function') +assert.equal(typeof mobile.deriveOpaqueIdentitySystems, 'function') +assert.equal(typeof root.isSemanticallyEqual, 'function') +assert.equal(typeof root.retractionTargets, 'function') assert.equal(root.groveFhirVersion, '4.0.1') assert.equal('groveFhirContractVersion' in root, false) +assert.equal('groveMobileContract' in root, false) assert.equal(root.groveExchangeProtocol.schemaVersion, 0) assert.equal(root.groveExchangeProtocol.protocolVersion, 0) assert.equal('version' in root.groveExchangeProtocol, false) assert.equal('releaseVersion' in root.groveExchangeProtocol, false) -assert.equal('version' in root.groveMobileContract, false) assert.equal('version' in root.groveRecordingFormatRegistry, false) +assert.equal( + root.groveProducerDiagnostics['mobile-omission.source-offset'].severity, + 'warning', +) assert.equal( mobile.groveMobilePackageMetadata.packageId, 'org.grovealliance.fhir.mobile', @@ -73,9 +81,10 @@ assert.equal( ) assert.equal(typeof root.parseFhirInstant, 'function') assert.equal(typeof r4.parseR4CollectionBundle, 'function') -assert.equal(typeof r4.parseGroveMobileExchangeBundle, 'function') -assert.equal(typeof r4.parseGroveMobileRetractionBundle, 'function') -assert.equal(typeof provider.parseProviderRetractionInput, 'function') +assert.equal(typeof r4.parseExchangeGraph, 'function') +assert.equal(typeof r4.parseRetractionEvent, 'function') +assert.equal(typeof provider.buildProviderRetractionEvent, 'function') +assert.equal(typeof provider.buildProviderExchangeGraphs, 'function') assert.equal(typeof provider.providerOutputCoordinates, 'function') assert.equal(typeof provider.providerOutputRole, 'function') assert.equal(typeof questionnaire.buildQuestionnaire, 'function') diff --git a/packages/fhir/scripts/type-surface.mjs b/packages/fhir/scripts/type-surface.mjs new file mode 100644 index 00000000..24105df0 --- /dev/null +++ b/packages/fhir/scripts/type-surface.mjs @@ -0,0 +1,103 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +// Snapshots the declared public surface of every entry point from the built .d.ts files, +// beside the runtime export snapshot: a renamed type or a changed signature shows up as a +// reviewable diff instead of reaching a consumer first. + +import { readFile, writeFile } from 'node:fs/promises' +import { dirname, resolve } from 'node:path' +import { argv, stdout } from 'node:process' +import { fileURLToPath } from 'node:url' + +import { + createProgram, + isFunctionDeclaration, + isVariableDeclaration, + ModuleKind, + ModuleResolutionKind, + ScriptTarget, + SymbolFlags, +} from 'typescript' + +const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..') +const snapshotPath = resolve(packageRoot, 'test/type-surface.json') +const check = argv.includes('--check') +const entryPoints = { + '.': 'dist/index.d.ts', + './r4': 'dist/r4/index.d.ts', + './mobile': 'dist/mobile/index.d.ts', + './providers': 'dist/providers/index.d.ts', + './questionnaire': 'dist/questionnaire/index.d.ts', +} + +const program = createProgram( + Object.values(entryPoints).map((path) => resolve(packageRoot, path)), + { + module: ModuleKind.NodeNext, + moduleResolution: ModuleResolutionKind.NodeNext, + target: ScriptTarget.ES2022, + exactOptionalPropertyTypes: true, + strict: true, + skipLibCheck: true, + noEmit: true, + }, +) +const checker = program.getTypeChecker() + +const declarationText = (declaration) => { + const text = declaration.getText() + if (isVariableDeclaration(declaration)) return `const ${text}` + if (isFunctionDeclaration(declaration)) return text + return text.replace(/^export\s+(?:declare\s+)?/u, '') +} + +const surface = {} +for (const [name, path] of Object.entries(entryPoints)) { + const sourceFile = program.getSourceFile(resolve(packageRoot, path)) + if (sourceFile === undefined) { + throw new Error(`Missing declaration file ${path}; run the build first.`) + } + const moduleSymbol = checker.getSymbolAtLocation(sourceFile) + if (moduleSymbol === undefined) { + throw new Error(`${path} declares no module.`) + } + const members = {} + for (const symbol of checker.getExportsOfModule(moduleSymbol)) { + const resolved = + symbol.flags & SymbolFlags.Alias ? + checker.getAliasedSymbol(symbol) + : symbol + const declarations = resolved.declarations ?? [] + if (declarations.length === 0) { + throw new Error(`${name} export ${symbol.name} has no declaration.`) + } + members[symbol.name] = declarations.map(declarationText).join('\n') + } + surface[name] = Object.fromEntries( + Object.entries(members).sort(([left], [right]) => + left.localeCompare(right), + ), + ) +} + +const rendered = `${JSON.stringify(surface, null, 2)}\n` +if (check) { + const existing = await readFile(snapshotPath, 'utf8').catch(() => undefined) + if (existing !== rendered) { + throw new Error( + 'The declared public surface changed. Review the diff and run npm run generate:surface.', + ) + } + stdout.write('The declared public surface matches its snapshot.\n') +} else { + await writeFile(snapshotPath, rendered) + stdout.write( + `Snapshotted ${String(Object.keys(surface).length)} entry points.\n`, + ) +} diff --git a/packages/fhir/scripts/validate-conformance.mjs b/packages/fhir/scripts/validate-conformance.mjs index f0d703be..6b110b93 100644 --- a/packages/fhir/scripts/validate-conformance.mjs +++ b/packages/fhir/scripts/validate-conformance.mjs @@ -104,6 +104,19 @@ for (const name of [ `Conformance/corpora/mobile-exchange/${name}`, ) } +const receiverCorpus = 'Conformance/corpora/receiver-lifecycle' +const receiverFiles = ( + await readdir(resolve(igRoot, receiverCorpus), { recursive: true }) +) + .filter((name) => name.endsWith('.json')) + .sort() +for (const name of receiverFiles) { + await requireByteIdentical( + resolve(packageRoot, '.grove-fhir', receiverCorpus, name), + resolve(igRoot, receiverCorpus, name), + `${receiverCorpus}/${name}`, + ) +} const pin = await readJson(resolve(packageRoot, 'grove-fhir.json')) if ( @@ -125,10 +138,17 @@ if (revision.status !== 0) { } const resolvedSha = revision.stdout.trim() const pinnedRef = pin.ref +const vendored = await readJson(resolve(packageRoot, '.grove-fhir/.ref')) stdout.write(`Grove FHIR pinned ref: ${pinnedRef}\n`) stdout.write(`Grove FHIR checkout SHA: ${resolvedSha}\n`) // The guide checkout and the catalogs this package generated from must be one commit. -if (!structuralOnly && resolvedSha !== pinnedRef) { +// A copy vendored from a local checkout proves that by the byte comparison above instead, +// so the pin is verified only once the contract is fetched from it. +if (typeof vendored.local === 'string') { + stdout.write( + `Grove FHIR contract vendored from ${vendored.local}; the pin is not verified.\n`, + ) +} else if (!structuralOnly && resolvedSha !== pinnedRef) { throw new Error( `The Grove FHIR checkout is ${resolvedSha}, but this package is pinned to ${pinnedRef}.`, ) diff --git a/packages/fhir/src/contract/measurement-catalog.generated.ts b/packages/fhir/src/contract/measurement-catalog.generated.ts index 3aab2932..57b66cbd 100644 --- a/packages/fhir/src/contract/measurement-catalog.generated.ts +++ b/packages/fhir/src/contract/measurement-catalog.generated.ts @@ -22,7 +22,7 @@ const groveMobilePackageMetadataValue = { } as const export const groveMobilePackageMetadata: typeof groveMobilePackageMetadataValue = - deepFreeze(groveMobilePackageMetadataValue) + /*#__PURE__*/ deepFreeze(groveMobilePackageMetadataValue) const groveMobileProfileCanonicalsValue = { 'grove-application-device': @@ -44,7 +44,7 @@ const groveMobileProfileCanonicalsValue = { } as const export const groveMobileProfileCanonicals: typeof groveMobileProfileCanonicalsValue = - deepFreeze(groveMobileProfileCanonicalsValue) + /*#__PURE__*/ deepFreeze(groveMobileProfileCanonicalsValue) const groveExchangeProtocolValue = { $schema: @@ -224,7 +224,7 @@ const groveExchangeProtocolValue = { { code: 'sensor-recording-document.identity-and-content', reason: - 'Every Sensor Recording Document carries its source-record, source-output, and source-artifact identities together with exactly one registered attachment payload.', + 'Every Sensor Recording Document carries its source-record, source-output, and source-artifact identities together with exactly one attachment payload.', emittedBy: 'client', }, { @@ -341,6 +341,284 @@ const groveExchangeProtocolValue = { 'The sole active lifecycle Provenance must directly claim exactly one admitted Mobile or adapter conversion profile.', emittedBy: 'conformance-kit', }, + { + code: 'mobile-input.unclassified', + reason: + 'A producer refused a source record without a more specific registered input rule; the refusal is reported, never silently dropped.', + emittedBy: 'client', + }, + { + code: 'mobile-input.unsupported-source-type', + reason: + 'The source type has no admitted Grove profile in the adapter catalog, so the producer emits nothing for it.', + emittedBy: 'client', + }, + { + code: 'mobile-input.intentionally-unsupported-source-type', + reason: + 'The adapter catalog deliberately refuses the source type for its stated reason; the producer emits no best-effort representation.', + emittedBy: 'client', + }, + { + code: 'mobile-input.not-yet-convertible', + reason: + 'The adapter catalog admits the source type, but this producer version does not yet emit its graph.', + emittedBy: 'client', + }, + { + code: 'mobile-input.platform-exclusive-source-type', + reason: + 'The source type is admitted only as a platform-exclusive recording document, which this conversion path does not emit.', + emittedBy: 'client', + }, + { + code: 'mobile-input.value-shape-invalid', + reason: + 'The source value does not have the shape its selected published mapping requires.', + emittedBy: 'client', + }, + { + code: 'mobile-input.value-outside-domain', + reason: + "A numeric source value is nonfinite, outside the measurement's inclusive value domain, or fractional where the contract admits only integers.", + emittedBy: 'client', + }, + { + code: 'mobile-input.unsupported-source-value', + reason: + 'A source enumeration, category, or typed metadata value has no published mapping in the selected contract.', + emittedBy: 'client', + }, + { + code: 'mobile-input.required-metadata-missing', + reason: + 'A source field the selected contract requires is absent from the record.', + emittedBy: 'client', + }, + { + code: 'mobile-input.required-component-missing', + reason: + 'A panel source record does not carry one of its required result components.', + emittedBy: 'client', + }, + { + code: 'mobile-input.effective-period-invalid', + reason: + "The source record's effective instant or period is not a valid FHIR date or time, starts after it ends, or places a sample outside its own interval.", + emittedBy: 'client', + }, + { + code: 'mobile-input.conversion-instant-precedes-source-version', + reason: + "The conversion instant precedes the source record's own last-modified time, so the event would misstate its order.", + emittedBy: 'client', + }, + { + code: 'mobile-input.text-not-unicode-scalar', + reason: + 'A source text field contains an unpaired UTF-16 surrogate and cannot enter a FHIR string or an identity preimage.', + emittedBy: 'client', + }, + { + code: 'mobile-input.native-identifier-invalid', + reason: + "The source's native record identifier or writer record version is absent, blank, or negative where the contract requires a value.", + emittedBy: 'client', + }, + { + code: 'mobile-input.recording-payload-too-large', + reason: + 'A recording payload exceeds the byte count FHIR Attachment.size can state, and no segmented manifest profile exists yet.', + emittedBy: 'client', + }, + { + code: 'mobile-input.empty-recording-series', + reason: + 'A series carried as a recording document was supplied without samples, so an empty series cannot be told from a failed enumeration.', + emittedBy: 'client', + }, + { + code: 'healthkit-input.ecg-evidence', + reason: + 'Caller-supplied electrocardiogram evidence is missing, incomplete, internally inconsistent, or cannot be represented losslessly by the adapter contract; the typed failure names the failing check.', + emittedBy: 'client', + }, + { + code: 'healthkit-input.clinical-release-unsupported', + reason: + 'A clinical record declares a FHIR release other than DSTU2 or R4, so its bytes cannot be carried under a versioned FHIR JSON media type.', + emittedBy: 'client', + }, + { + code: 'healthkit-input.clinical-record-empty', + reason: + 'A clinical record or CDA sample carries no resource or document bytes, which a query that excludes document data returns.', + emittedBy: 'client', + }, + { + code: 'healthkit-input.component-requires-correlation', + reason: + 'A blood pressure component sample converts only inside its admitting correlation, never on its own.', + emittedBy: 'client', + }, + { + code: 'healthkit-input.source-application-invalid', + reason: + 'A source revision the caller classified as an application carries no valid Apple bundle identifier.', + emittedBy: 'client', + }, + { + code: 'mobile-exchange.bundle-profile', + reason: + 'An exchange Bundle directly claims exactly the Grove Mobile exchange or retraction Bundle profile for its event kind.', + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-exchange.entry-required', + reason: + 'An exchange event carries at least one entry; an empty Bundle is not an event.', + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-exchange.event-times', + reason: + 'An event states its Bundle timestamp, and its lifecycle Provenance states occurred[x] and recorded.', + emittedBy: 'client', + }, + { + code: 'mobile-exchange.distinct-entry-key', + reason: + 'No two entries of one event carry the same entry key system and value pair.', + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-exchange.entry-key-selection', + reason: + "An entry key is the entry resource's highest-priority typed business identifier, or its entry-node key when the resource carries none.", + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-exchange.identifier-role', + reason: + 'A Grove-typed resource Identifier carries exactly one closed Grove identifier-role coding.', + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-exchange.opaque-resource-identity', + reason: + 'A resource Identifier typed with an opaque Grove identifier role is one complete pair whose system is absolute and whose value is a canonical v0 HMAC identity.', + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-exchange.distinct-resource-identity-role', + reason: + 'A resource carries at most one Identifier for each Grove identifier role.', + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-exchange.output-required', + reason: + 'An active event carries at least one clinical or source-artifact output.', + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-exchange.provenance-assembler', + reason: + "A conversion Provenance names exactly one assembler agent, and that agent is the event's application Device snapshot.", + emittedBy: 'client', + }, + { + code: 'mobile-exchange.provenance-targets', + reason: + 'A conversion Provenance targets every active clinical output of its event and nothing else.', + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-exchange.adapter-provenance-graph', + reason: + 'Every adapter output of an event claims the adapter profile its conversion Provenance governs, and a source-neutral event claims no adapter output.', + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-output.adapter-source-marker', + reason: + "An adapter's source marker appears exactly once on each catalog-owned output of that adapter and never on a source-neutral or different-adapter output.", + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-device.host-device-identity', + reason: + 'A host Device carries exactly one event-scoped device-snapshot identity.', + emittedBy: 'client', + }, + { + code: 'mobile-support.study-context', + reason: + 'A bundled study context carries one ResearchStudy, its exact-revision PlanDefinition with canonical url and version, and one ResearchSubject linking the subject to the study, each keyed as an entry node with its study-context role.', + emittedBy: 'client', + }, + { + code: 'healthkit-device.application-bundle-identifier', + reason: + 'A HealthKit application Device carries exactly one typed Apple bundle identifier.', + emittedBy: 'client', + }, + { + code: 'health-connect-provenance.data-origin-agent', + reason: + 'A Health Connect data origin is one enterer agent whose typed identifier-only logical Device Reference names the Android package in the package-name namespace.', + emittedBy: 'conformance-kit', + }, + { + code: 'sensor-recording-document.embedded-integrity', + reason: + "An embedded recording attachment's size and SHA-1 hash match its decoded bytes.", + emittedBy: 'conformance-kit', + }, + { + code: 'sensor-recording-document.format', + reason: + "A recording attachment's format coding and content type match one active entry of the Grove recording-format registry, with no Coding.version.", + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-retraction.provenance', + reason: + 'A retraction event carries exactly one retraction Provenance and no transform Provenance.', + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-retraction.target-required', + reason: 'A retraction Provenance names at least one logical target.', + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-retraction.distinct-target', + reason: + 'No two retraction targets carry the same Identifier system and value pair.', + emittedBy: 'conformance-kit', + }, + { + code: 'mobile-omission.recording-device', + reason: + 'The source names a recording device without a stable per-unit token, so the producer emits no recording Device and the output carries no device reference.', + emittedBy: 'client', + severity: 'warning', + }, + { + code: 'mobile-omission.source-offset', + reason: + 'The source supplies an effective instant without a UTC offset or time-zone name, so the producer serializes it in UTC and attaches no timezone extension.', + emittedBy: 'client', + severity: 'warning', + }, + { + code: 'mobile-omission.unmodeled-metadata', + reason: + "The source record carries metadata outside the adapter's typed allowlist, so the producer withholds those fields.", + emittedBy: 'client', + severity: 'warning', + }, ], event: { semanticUnit: @@ -348,6 +626,7 @@ const groveExchangeProtocolValue = { bundleIdentifier: { system: 'A deployment-owned absolute URI, stable for the producer instance and never reused for another key space.', + recommendedSystemForm: '/NamingSystem/grove-event-v0', valueForm: 'e0::', soleEventBusinessIdentifier: true, @@ -373,7 +652,9 @@ const groveExchangeProtocolValue = { valueForm: 'v0:::<43-character-base64url-digest-without-padding>', identifierSystem: - 'Deployment-owned and immutable for one identity kind, deployment scope, key id, and key epoch. A rotated key uses a new system and epoch.', + 'Deployment-owned and immutable for one identity kind, deployment scope, key id, and key epoch. A rotated key uses a new system and epoch. A deployment should name it with recommendedSystemForm; a deployment that already governs its own namespaces remains conformant.', + recommendedSystemForm: + '/NamingSystem/grove--v0//', keyRequirements: { minimumBytes: 32, managedSecret: true, @@ -737,6 +1018,8 @@ const groveExchangeProtocolValue = { ], entryNode: { system: 'A deployment-owned absolute URI for event-scoped graph nodes.', + recommendedSystemForm: + '/NamingSystem/grove-entry-node-v0', domain: 'org.grovealliance.fhir.entry-node.v0', components: [ 'event-system', @@ -769,6 +1052,20 @@ const groveExchangeProtocolValue = { 'Exactly one coding from the ISO lifecycle system and zero codings from the Grove lifecycle system; translations from unrelated systems remain open.', shape: 'One active Bundle contains every output derived from exactly one source record revision and exactly one conversion Provenance targeting every output.', + studyContext: { + recommendedShape: + "A producer that knows the participant's enrollment should bundle one ResearchStudy, its exact-revision PlanDefinition, and one ResearchSubject as event-scoped entry-node entries, reference the ResearchStudy from each output through workflow-researchStudy, and reference the PlanDefinition from ResearchStudy.protocol. An identifier-only logical ResearchStudy reference remains conformant but carries no protocol revision.", + subject: + 'Observation.subject is the identifier-only deployment pseudonym unless the deployment supplies a Patient entry; ResearchSubject.individual references the same subject.', + protocol: + 'ResearchStudy.protocol references a PlanDefinition entry whose url and version are exact; workflow-instantiatesCanonical is not an admitted extension.', + entryNodeRoles: [ + 'patient', + 'research-study', + 'research-subject', + 'plan-definition', + ], + }, entryResourcePolicy: { outputResourceTypes: [ 'Observation', @@ -1038,6 +1335,14 @@ const groveExchangeProtocolValue = { 'FHIR graphs compare semantically through lossless JSON tokens that preserve decimal lexemes. Whole-Bundle byte equality is not required.', byteComparison: 'Exact bytes are required for identity preimages and registered opaque, CSV, or binary payloads.', + equality: { + formatting: + 'A replay that differs from the accepted event only in JSON member order, whitespace, or string escaping carries the same tokens and is the exact retry.', + decimalLexeme: + 'A replay whose decimal lexeme differs, such as 72.0 for 72, is different content under a reused event identifier; the receiver retains the accepted event and disregards the replay exactly as any other altered retry.', + vectors: + 'Conformance/corpora/receiver-lifecycle: reformatted-retry and lexeme-retry', + }, }, testVectors: { keyHex: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', @@ -1276,6 +1581,20 @@ const groveExchangeProtocolValue = { ], value: 'v0:test-key:1:PPULnf0LKpASjIj8mU5TKafPKig_oqWND3_dHFShGd8', }, + { + id: 'normative-corpus-heart-rate-source-artifact', + identityKind: 'source-artifact', + components: [ + 'health-connect', + 'HeartRateRecord', + 'urn:uuid:1f5c58aa-6ec6-4e79-a682-829a9debd3f5', + 'default', + 'record-heart-001', + 'heart-rate-samples', + '0', + ], + value: 'v0:test-key:1:ZWmx94XtgArrE8XO6W6LQ7QYbkiWCIn-K1JVw8hxwrI', + }, { id: 'normative-corpus-retraction-application-snapshot', identityKind: 'device-snapshot', @@ -1338,6 +1657,254 @@ const groveExchangeProtocolValue = { ], value: 'v0:test-key:1:W8r_v8DzqOippQF5DUZiyhr_iwyRQk3NTa_sQLF8aM0', }, + { + id: 'receiver-secondary-output', + identityKind: 'source-output', + components: [ + 'health-connect', + 'HeartRateRecord', + 'urn:uuid:1f5c58aa-6ec6-4e79-a682-829a9debd3f5', + 'default', + 'record-heart-001', + 'sample', + '2026-08-20T15:30:01.251000000Z|0', + ], + value: 'v0:test-key:1:BYrV6N3A1WrjO1saOCgLww1gdAQqFQ5jLoFoTKwec9c', + }, + { + id: 'receiver-changed-source-record', + identityKind: 'source-record', + components: [ + 'health-connect', + 'HeartRateRecord', + 'urn:uuid:1f5c58aa-6ec6-4e79-a682-829a9debd3f5', + 'default', + 'another-record', + ], + value: 'v0:test-key:1:WssrtI7oYzcgG0mFxlitvSznCx4esKSDEbK-bOUQpTk', + }, + { + id: 'receiver-application-snapshot-67', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:67', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:2f3FbY5IkXSZQs9xgEChYLIWANCcdFhDmauCsjHgA7A', + }, + { + id: 'receiver-other-writer', + identityKind: 'writer-record', + components: [ + 'https://study.example.org/apps', + 'another-writer', + 'heart-rate-1', + ], + value: 'v0:test-key:1:5mIZKnS2ox49bXbK-YuTn_SPDVdslEyQIsXF7oV2h3k', + }, + { + id: 'receiver-application-snapshot-60', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:60', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:dFdgpZqSrZI83hNxyslxoI1qXUlq0fa2bAo4tnhv2CE', + }, + { + id: 'receiver-application-snapshot-61', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:61', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:G0shWKztBFVa7S2QFoeDhU71zLfIEkS5BkE_3jFpiNk', + }, + { + id: 'receiver-application-snapshot-62', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:62', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:tfnsLulBQxFAOUV6gNjzORPIjCXbXgrqMMZ3bvNa1ik', + }, + { + id: 'receiver-application-snapshot-63', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:63', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:-mUWvC9wBT8cKHPAO4t6fvsJW9ele_WgaEO0swH-tQc', + }, + { + id: 'receiver-application-snapshot-64', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:64', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:EYUasFsZmO8n8-2AWt5w5VyYh-6Yz5-5vSybbwLlMb8', + }, + { + id: 'receiver-application-snapshot-65', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:65', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:oPK4uLWJLUJo8JL5r7lWjaQL8dAiSAsfVOtqsk6ruQ8', + }, + { + id: 'receiver-application-snapshot-66', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:66', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:Tj726etFKNnHaooVq2wP2K9I5odIL8ZDIp4yvqJJtS4', + }, + { + id: 'receiver-writer', + identityKind: 'writer-record', + components: [ + 'https://study.example.org/apps', + 'fixture-writer', + 'heart-rate-1', + ], + value: 'v0:test-key:1:TN39cF6jcCICy4SlWBjpAulk4eMlcb-KM3MwESD9cvw', + }, + { + id: 'receiver-target-record', + identityKind: 'source-record', + components: [ + 'questionnaire', + 'heart-rate', + 'https://study.example.org/repositories', + 'fixture', + 'record-2', + ], + value: 'v0:test-key:1:TE47APXmta6xbmGufkf8LBv8q3aPR4NDn9bltP7vEn4', + }, + { + id: 'receiver-target-output', + identityKind: 'source-output', + components: [ + 'questionnaire', + 'heart-rate', + 'https://study.example.org/repositories', + 'fixture', + 'record-2', + 'primary', + '0', + ], + value: 'v0:test-key:1:M6afafiHPF1Pqyg4OZ5IN0R_8s97oM3WWy904IjTwy8', + }, + { + id: 'receiver-application-snapshot-41', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:41', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:s7LpE9vScdo_lguSPQ-KetFYzpcgwAdbUFlTLS8ECPY', + }, + { + id: 'receiver-application-snapshot-42', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:42', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:FhFF4pknUiHfdg3v3p6KQYzE8ZI_U3YKj-IdsYcRpLU', + }, + { + id: 'receiver-application-snapshot-44', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:44', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:6I8uWw-e6duXMHPcuf6Zcjv5YKphw1S0hZgfq1N9EMM', + }, + { + id: 'receiver-application-snapshot-45', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:45', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:v4OvFw1CH0dYyiwUrWICl6MPUpp9BrBDva51303xv4M', + }, + { + id: 'receiver-application-snapshot-46', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:46', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:P-CZcBXDylBtujPUTLBrMVc24OEl_83sh_WrgLhEGfk', + }, + { + id: 'receiver-application-snapshot-47', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:47', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:7CSIYKL1G_jH0947A2Wrz5arDAmPJ__NU5syHdbiLFk', + }, + { + id: 'multi-study-application-snapshot-50', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:50', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:IPo3xVSPUNGwqi_4L3JZCTpztid-H4rQEvEt3YZ2TcU', + }, + { + id: 'multi-study-application-snapshot-51', + identityKind: 'device-snapshot', + components: [ + 'https://study.example.org/fhir/NamingSystem/grove-event-v0', + 'e0:1f5c58aa-6ec6-4e79-a682-829a9debd3f5:51', + 'application', + 'fixture-app', + ], + value: 'v0:test-key:1:1BRlmWamqCymL8adxO0bJCs1rV4L6ZGkPUGzrOfixnw', + }, { id: 'questionnaire-extraction-host-snapshot', identityKind: 'device-snapshot', @@ -1430,213 +1997,528 @@ const groveExchangeProtocolValue = { } as const export const groveExchangeProtocol: typeof groveExchangeProtocolValue = - deepFreeze(groveExchangeProtocolValue) + /*#__PURE__*/ deepFreeze(groveExchangeProtocolValue) -const groveExchangeRuleDiagnosticsValue = { +const groveProducerDiagnosticsValue = { 'mobile-exchange.unclassified': { reason: 'A producer-contract failure reached the exchange diagnostic boundary without a more specific registered rule; validation fails closed and the conformance kit must classify the gap.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.entry-node-key': { reason: 'Every Bundle entry must carry exactly one complete Grove exchange entry node key.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.deterministic-full-url': { reason: 'Bundle.entry.fullUrl must be the UUID version 5 value derived from its complete entry identifier.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.resolved-reference': { reason: 'Every internal UUID URN reference must resolve to a Bundle entry fullUrl.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.event-identity': { reason: 'Bundle.identifier.value must be the canonical e0 producer UUID and positive sequence form.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.entry-node-digest': { reason: 'An entry-node digest must be derived from the enclosing event identifier, role, and ordinal.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.entry-node-ordinal': { reason: 'An entry-node ordinal is the zero-based position of its entry among the entries sharing that node-role, in Bundle entry order.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.identity-system-role': { reason: 'Within one event graph, each Grove Identifier.system names exactly one Grove identifier role; one namespace cannot change meaning between nodes.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-output.source-output-required': { reason: 'Every active clinical output must carry its exact typed source-output identity in addition to source-record identity.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-output.hybrid-companion': { reason: 'A hybrid-required output and its exact source-preservation companion must form the catalog-declared closed, same-source, bidirectionally linked graph.', + emittedBy: 'conformance-kit', severity: 'error', }, 'healthkit-ecg.output-graph': { reason: 'A HealthKit ECG event must satisfy the catalog-owned waveform, optional average-heart-rate, symptom-member, relationship-direction, identity, and effective-period graph contract.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.transform-provenance': { reason: 'An active event must contain exactly one transform Provenance and no retraction Provenance.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-retraction.logical-target': { reason: 'A retraction target must be a typed logical Reference without a literal reference.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-retraction.target-role': { reason: 'Every retraction target must carry exactly one closed Grove target-role code.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-retraction.opaque-target': { reason: 'A retraction target must use the exact canonical v0 HMAC identity previously emitted.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-retraction.native-record-identifier': { reason: "An optional retraction native record identifier carries one complete Identifier in the adapter's own absolute native key space and never a Grove identifier-role coding.", + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.collection-entry-operation': { reason: 'A Mobile exchange event is a collection Bundle; an entry may carry neither a request nor a response, because an event is an assertion and never a transaction instruction.', + emittedBy: 'client', severity: 'error', }, 'mobile-support.questionnaire-response-profile': { reason: 'Every supporting QuestionnaireResponse must directly claim exactly the Grove Questionnaire Response profile.', + emittedBy: 'client', severity: 'error', }, 'mobile-device.recording-device-dual-identity': { reason: 'A recording Device carries both its durable recording-device identity and its event-scoped device-snapshot identity; neither may stand alone.', + emittedBy: 'client', severity: 'error', }, 'healthkit-clinical.fhir-representation': { reason: 'A HealthKit clinical-record envelope must carry the exact admitted FHIR release and preserve the source representation its adapter contract declares.', + emittedBy: 'client', severity: 'error', }, 'sensor-recording-document.identity-and-content': { reason: - 'Every Sensor Recording Document carries its source-record, source-output, and source-artifact identities together with exactly one registered attachment payload.', + 'Every Sensor Recording Document carries its source-record, source-output, and source-artifact identities together with exactly one attachment payload.', + emittedBy: 'client', severity: 'error', }, 'mobile-retraction.no-clinical-copy': { reason: 'A retraction event contains its lifecycle Provenance and optional Device agents, never a copied or mutilated clinical resource.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.lifecycle-coding': { reason: 'A lifecycle Provenance must carry exactly one coding across the ISO transform and Grove retraction lifecycle systems; translations from other systems remain open.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-output.semantic-profile': { reason: 'Every active Observation must directly claim one admitted Grove semantic profile shape; an empty claim cannot bypass semantic validation.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-output.fixed-quantity-unit': { reason: 'Every Quantity-valued catalog measurement uses the exact fixed system and code declared by its semantic profile contract.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-output.quantity-value-domain': { reason: 'Every Quantity-valued catalog measurement stays within its catalog-declared representational minimum, maximum, and integer-only domain without inventing a physiologic range.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.reference-target-type': { reason: 'Every governed Patient reference resolves to a Patient entry, not merely to any existing fullUrl.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.reference-declared-type': { reason: "When Reference.type is present it must equal the referenced entry's actual resourceType token.", + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.logical-source-entity': { reason: 'Lifecycle Provenance carries exactly one logical source-record Identifier entity and never a literal source Reference.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-retraction.role-target-type': { reason: 'Every retraction target role fixes its admitted resource type and Identifier role.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.single-source-entity': { reason: 'A lifecycle Provenance identifies exactly one source-record entity.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.reference-shape': { reason: 'Each governed path has its declared singular or repeating shape and contains valid Reference objects that are exclusively resolving-literal or identifier-only logical, never both.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.logical-patient-reference': { reason: 'An identifier-only logical Patient Reference carries the exact Patient type and one complete absolute-system pseudonym Identifier without a Grove role or protocol-reserved system.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-output.adapter-only-profile': { reason: 'An adapter-only active output type must directly claim exactly its one admitted adapter profile.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.entry-resource-type': { reason: 'An active event admits only its closed output, supporting, and lifecycle resource type set.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.contained-resource-prohibited': { reason: 'Mobile exchange events prohibit contained resources; every graph node must be an addressable Bundle entry.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-output.document-profile': { reason: 'Every active DocumentReference must directly claim exactly one admitted recording or clinical-document profile mode.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-support.device-profile': { reason: 'Every active Device must directly claim exactly one admitted Grove Device profile mode.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-support.connected': { reason: 'Every supporting resource must be connected to an output or the lifecycle Provenance.', + emittedBy: 'conformance-kit', severity: 'error', }, 'mobile-exchange.provenance-profile': { reason: 'The sole active lifecycle Provenance must directly claim exactly one admitted Mobile or adapter conversion profile.', + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-input.unclassified': { + reason: + 'A producer refused a source record without a more specific registered input rule; the refusal is reported, never silently dropped.', + emittedBy: 'client', + severity: 'error', + }, + 'mobile-input.unsupported-source-type': { + reason: + 'The source type has no admitted Grove profile in the adapter catalog, so the producer emits nothing for it.', + emittedBy: 'client', severity: 'error', }, + 'mobile-input.intentionally-unsupported-source-type': { + reason: + 'The adapter catalog deliberately refuses the source type for its stated reason; the producer emits no best-effort representation.', + emittedBy: 'client', + severity: 'error', + }, + 'mobile-input.not-yet-convertible': { + reason: + 'The adapter catalog admits the source type, but this producer version does not yet emit its graph.', + emittedBy: 'client', + severity: 'error', + }, + 'mobile-input.platform-exclusive-source-type': { + reason: + 'The source type is admitted only as a platform-exclusive recording document, which this conversion path does not emit.', + emittedBy: 'client', + severity: 'error', + }, + 'mobile-input.value-shape-invalid': { + reason: + 'The source value does not have the shape its selected published mapping requires.', + emittedBy: 'client', + severity: 'error', + }, + 'mobile-input.value-outside-domain': { + reason: + "A numeric source value is nonfinite, outside the measurement's inclusive value domain, or fractional where the contract admits only integers.", + emittedBy: 'client', + severity: 'error', + }, + 'mobile-input.unsupported-source-value': { + reason: + 'A source enumeration, category, or typed metadata value has no published mapping in the selected contract.', + emittedBy: 'client', + severity: 'error', + }, + 'mobile-input.required-metadata-missing': { + reason: + 'A source field the selected contract requires is absent from the record.', + emittedBy: 'client', + severity: 'error', + }, + 'mobile-input.required-component-missing': { + reason: + 'A panel source record does not carry one of its required result components.', + emittedBy: 'client', + severity: 'error', + }, + 'mobile-input.effective-period-invalid': { + reason: + "The source record's effective instant or period is not a valid FHIR date or time, starts after it ends, or places a sample outside its own interval.", + emittedBy: 'client', + severity: 'error', + }, + 'mobile-input.conversion-instant-precedes-source-version': { + reason: + "The conversion instant precedes the source record's own last-modified time, so the event would misstate its order.", + emittedBy: 'client', + severity: 'error', + }, + 'mobile-input.text-not-unicode-scalar': { + reason: + 'A source text field contains an unpaired UTF-16 surrogate and cannot enter a FHIR string or an identity preimage.', + emittedBy: 'client', + severity: 'error', + }, + 'mobile-input.native-identifier-invalid': { + reason: + "The source's native record identifier or writer record version is absent, blank, or negative where the contract requires a value.", + emittedBy: 'client', + severity: 'error', + }, + 'mobile-input.recording-payload-too-large': { + reason: + 'A recording payload exceeds the byte count FHIR Attachment.size can state, and no segmented manifest profile exists yet.', + emittedBy: 'client', + severity: 'error', + }, + 'mobile-input.empty-recording-series': { + reason: + 'A series carried as a recording document was supplied without samples, so an empty series cannot be told from a failed enumeration.', + emittedBy: 'client', + severity: 'error', + }, + 'healthkit-input.ecg-evidence': { + reason: + 'Caller-supplied electrocardiogram evidence is missing, incomplete, internally inconsistent, or cannot be represented losslessly by the adapter contract; the typed failure names the failing check.', + emittedBy: 'client', + severity: 'error', + }, + 'healthkit-input.clinical-release-unsupported': { + reason: + 'A clinical record declares a FHIR release other than DSTU2 or R4, so its bytes cannot be carried under a versioned FHIR JSON media type.', + emittedBy: 'client', + severity: 'error', + }, + 'healthkit-input.clinical-record-empty': { + reason: + 'A clinical record or CDA sample carries no resource or document bytes, which a query that excludes document data returns.', + emittedBy: 'client', + severity: 'error', + }, + 'healthkit-input.component-requires-correlation': { + reason: + 'A blood pressure component sample converts only inside its admitting correlation, never on its own.', + emittedBy: 'client', + severity: 'error', + }, + 'healthkit-input.source-application-invalid': { + reason: + 'A source revision the caller classified as an application carries no valid Apple bundle identifier.', + emittedBy: 'client', + severity: 'error', + }, + 'mobile-exchange.bundle-profile': { + reason: + 'An exchange Bundle directly claims exactly the Grove Mobile exchange or retraction Bundle profile for its event kind.', + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-exchange.entry-required': { + reason: + 'An exchange event carries at least one entry; an empty Bundle is not an event.', + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-exchange.event-times': { + reason: + 'An event states its Bundle timestamp, and its lifecycle Provenance states occurred[x] and recorded.', + emittedBy: 'client', + severity: 'error', + }, + 'mobile-exchange.distinct-entry-key': { + reason: + 'No two entries of one event carry the same entry key system and value pair.', + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-exchange.entry-key-selection': { + reason: + "An entry key is the entry resource's highest-priority typed business identifier, or its entry-node key when the resource carries none.", + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-exchange.identifier-role': { + reason: + 'A Grove-typed resource Identifier carries exactly one closed Grove identifier-role coding.', + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-exchange.opaque-resource-identity': { + reason: + 'A resource Identifier typed with an opaque Grove identifier role is one complete pair whose system is absolute and whose value is a canonical v0 HMAC identity.', + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-exchange.distinct-resource-identity-role': { + reason: + 'A resource carries at most one Identifier for each Grove identifier role.', + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-exchange.output-required': { + reason: + 'An active event carries at least one clinical or source-artifact output.', + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-exchange.provenance-assembler': { + reason: + "A conversion Provenance names exactly one assembler agent, and that agent is the event's application Device snapshot.", + emittedBy: 'client', + severity: 'error', + }, + 'mobile-exchange.provenance-targets': { + reason: + 'A conversion Provenance targets every active clinical output of its event and nothing else.', + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-exchange.adapter-provenance-graph': { + reason: + 'Every adapter output of an event claims the adapter profile its conversion Provenance governs, and a source-neutral event claims no adapter output.', + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-output.adapter-source-marker': { + reason: + "An adapter's source marker appears exactly once on each catalog-owned output of that adapter and never on a source-neutral or different-adapter output.", + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-device.host-device-identity': { + reason: + 'A host Device carries exactly one event-scoped device-snapshot identity.', + emittedBy: 'client', + severity: 'error', + }, + 'mobile-support.study-context': { + reason: + 'A bundled study context carries one ResearchStudy, its exact-revision PlanDefinition with canonical url and version, and one ResearchSubject linking the subject to the study, each keyed as an entry node with its study-context role.', + emittedBy: 'client', + severity: 'error', + }, + 'healthkit-device.application-bundle-identifier': { + reason: + 'A HealthKit application Device carries exactly one typed Apple bundle identifier.', + emittedBy: 'client', + severity: 'error', + }, + 'health-connect-provenance.data-origin-agent': { + reason: + 'A Health Connect data origin is one enterer agent whose typed identifier-only logical Device Reference names the Android package in the package-name namespace.', + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'sensor-recording-document.embedded-integrity': { + reason: + "An embedded recording attachment's size and SHA-1 hash match its decoded bytes.", + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'sensor-recording-document.format': { + reason: + "A recording attachment's format coding and content type match one active entry of the Grove recording-format registry, with no Coding.version.", + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-retraction.provenance': { + reason: + 'A retraction event carries exactly one retraction Provenance and no transform Provenance.', + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-retraction.target-required': { + reason: 'A retraction Provenance names at least one logical target.', + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-retraction.distinct-target': { + reason: + 'No two retraction targets carry the same Identifier system and value pair.', + emittedBy: 'conformance-kit', + severity: 'error', + }, + 'mobile-omission.recording-device': { + reason: + 'The source names a recording device without a stable per-unit token, so the producer emits no recording Device and the output carries no device reference.', + emittedBy: 'client', + severity: 'warning', + }, + 'mobile-omission.source-offset': { + reason: + 'The source supplies an effective instant without a UTC offset or time-zone name, so the producer serializes it in UTC and attaches no timezone extension.', + emittedBy: 'client', + severity: 'warning', + }, + 'mobile-omission.unmodeled-metadata': { + reason: + "The source record carries metadata outside the adapter's typed allowlist, so the producer withholds those fields.", + emittedBy: 'client', + severity: 'warning', + }, } as const -export const groveExchangeRuleDiagnostics: typeof groveExchangeRuleDiagnosticsValue = - deepFreeze(groveExchangeRuleDiagnosticsValue) +export const groveProducerDiagnostics: typeof groveProducerDiagnosticsValue = + /*#__PURE__*/ deepFreeze(groveProducerDiagnosticsValue) const groveProfileClaimsValue = { $schema: @@ -2275,9 +3157,8 @@ const groveProfileClaimsValue = { }, } as const -export const groveProfileClaims: typeof groveProfileClaimsValue = deepFreeze( - groveProfileClaimsValue, -) +export const groveProfileClaims: typeof groveProfileClaimsValue = + /*#__PURE__*/ deepFreeze(groveProfileClaimsValue) const adapterSourceMarkerClaimsValue = [ { @@ -2484,7 +3365,7 @@ const adapterSourceMarkerClaimsValue = [ ] as const export const adapterSourceMarkerClaims: typeof adapterSourceMarkerClaimsValue = - deepFreeze(adapterSourceMarkerClaimsValue) + /*#__PURE__*/ deepFreeze(adapterSourceMarkerClaimsValue) const healthConnectDataOriginApplicationValue = { sourceField: 'Metadata.dataOrigin.packageName', @@ -2501,7 +3382,7 @@ const healthConnectDataOriginApplicationValue = { } as const export const healthConnectDataOriginApplication: typeof healthConnectDataOriginApplicationValue = - deepFreeze(healthConnectDataOriginApplicationValue) + /*#__PURE__*/ deepFreeze(healthConnectDataOriginApplicationValue) const groveRecordingFormatRegistryValue = { codeSystem: @@ -2596,7 +3477,7 @@ const groveRecordingFormatRegistryValue = { } as const export const groveRecordingFormatRegistry: typeof groveRecordingFormatRegistryValue = - deepFreeze(groveRecordingFormatRegistryValue) + /*#__PURE__*/ deepFreeze(groveRecordingFormatRegistryValue) const sharedMobileMeasurementCatalogValue = { 'active-energy': { @@ -5402,7 +6283,7 @@ const sharedMobileMeasurementCatalogValue = { } as const export const sharedMobileMeasurementCatalog: typeof sharedMobileMeasurementCatalogValue = - deepFreeze(sharedMobileMeasurementCatalogValue) + /*#__PURE__*/ deepFreeze(sharedMobileMeasurementCatalogValue) const mobileEffectiveCanonicalizationValue = { scope: @@ -5415,7 +6296,7 @@ const mobileEffectiveCanonicalizationValue = { } as const export const mobileEffectiveCanonicalization: typeof mobileEffectiveCanonicalizationValue = - deepFreeze(mobileEffectiveCanonicalizationValue) + /*#__PURE__*/ deepFreeze(mobileEffectiveCanonicalizationValue) const mobileEffectiveCanonicalizationVectorsValue = [ { @@ -5451,11 +6332,13 @@ const mobileEffectiveCanonicalizationVectorsValue = [ ] as const export const mobileEffectiveCanonicalizationVectors: typeof mobileEffectiveCanonicalizationVectorsValue = - deepFreeze(mobileEffectiveCanonicalizationVectorsValue) + /*#__PURE__*/ deepFreeze(mobileEffectiveCanonicalizationVectorsValue) export type SharedMobileMeasurementKind = keyof typeof sharedMobileMeasurementCatalog +export type ProducerDiagnosticCode = keyof typeof groveProducerDiagnostics + export type GroveRecordingFormat = keyof typeof groveRecordingFormatRegistry.formats diff --git a/packages/fhir/src/contract/providers.generated.ts b/packages/fhir/src/contract/providers.generated.ts index 5a1ca469..4edb783d 100644 --- a/packages/fhir/src/contract/providers.generated.ts +++ b/packages/fhir/src/contract/providers.generated.ts @@ -27,7 +27,7 @@ const groveProviderPackageMetadataValue = { } as const export const groveProviderPackageMetadata: typeof groveProviderPackageMetadataValue = - deepFreeze(groveProviderPackageMetadataValue) + /*#__PURE__*/ deepFreeze(groveProviderPackageMetadataValue) const groveProviderPackageCanonicalsValue = { mobile: 'https://grovealliance.org/fhir/mobile', @@ -36,7 +36,7 @@ const groveProviderPackageCanonicalsValue = { } as const export const groveProviderPackageCanonicals: typeof groveProviderPackageCanonicalsValue = - deepFreeze(groveProviderPackageCanonicalsValue) + /*#__PURE__*/ deepFreeze(groveProviderPackageCanonicalsValue) const groveProviderProfileCanonicalsValue = { 'grove-mobile-active-energy': @@ -512,7 +512,7 @@ const groveProviderProfileCanonicalsValue = { } as const export const groveProviderProfileCanonicals: typeof groveProviderProfileCanonicalsValue = - deepFreeze(groveProviderProfileCanonicalsValue) + /*#__PURE__*/ deepFreeze(groveProviderProfileCanonicalsValue) const providerAdapterCatalogValue = { $schema: @@ -1825,7 +1825,7 @@ const providerAdapterCatalogValue = { } as const export const providerAdapterCatalog: typeof providerAdapterCatalogValue = - deepFreeze(providerAdapterCatalogValue) + /*#__PURE__*/ deepFreeze(providerAdapterCatalogValue) const adapterMeasurementCatalogValue = { healthkit: { @@ -6717,7 +6717,7 @@ const adapterMeasurementCatalogValue = { } as const export const adapterMeasurementCatalog: typeof adapterMeasurementCatalogValue = - deepFreeze(adapterMeasurementCatalogValue) + /*#__PURE__*/ deepFreeze(adapterMeasurementCatalogValue) const healthKitApplicationDeviceIdentityValue = { profile: @@ -6738,7 +6738,7 @@ const healthKitApplicationDeviceIdentityValue = { } as const export const healthKitApplicationDeviceIdentity: typeof healthKitApplicationDeviceIdentityValue = - deepFreeze(healthKitApplicationDeviceIdentityValue) + /*#__PURE__*/ deepFreeze(healthKitApplicationDeviceIdentityValue) const healthKitClinicalRecordAdmissionValue = { profile: @@ -6758,7 +6758,7 @@ const healthKitClinicalRecordAdmissionValue = { } as const export const healthKitClinicalRecordAdmission: typeof healthKitClinicalRecordAdmissionValue = - deepFreeze(healthKitClinicalRecordAdmissionValue) + /*#__PURE__*/ deepFreeze(healthKitClinicalRecordAdmissionValue) const providerScalarOutputRolesValue = { 'google-health-api': { @@ -6969,7 +6969,7 @@ const providerScalarOutputRolesValue = { } as const export const providerScalarOutputRoles: typeof providerScalarOutputRolesValue = - deepFreeze(providerScalarOutputRolesValue) + /*#__PURE__*/ deepFreeze(providerScalarOutputRolesValue) const providerScalarOutputDiscriminatorsValue = { 'google-health-api': { @@ -7177,7 +7177,7 @@ const providerScalarOutputDiscriminatorsValue = { } as const export const providerScalarOutputDiscriminators: typeof providerScalarOutputDiscriminatorsValue = - deepFreeze(providerScalarOutputDiscriminatorsValue) + /*#__PURE__*/ deepFreeze(providerScalarOutputDiscriminatorsValue) const providerRecordEffectiveRulesValue = { oura: { @@ -7217,7 +7217,7 @@ const providerRecordEffectiveRulesValue = { } as const export const providerRecordEffectiveRules: typeof providerRecordEffectiveRulesValue = - deepFreeze(providerRecordEffectiveRulesValue) + /*#__PURE__*/ deepFreeze(providerRecordEffectiveRulesValue) const providerRawOutputRolesValue = { 'google-health-api': { @@ -7233,7 +7233,7 @@ const providerRawOutputRolesValue = { } as const export const providerRawOutputRoles: typeof providerRawOutputRolesValue = - deepFreeze(providerRawOutputRolesValue) + /*#__PURE__*/ deepFreeze(providerRawOutputRolesValue) const providerRawOutputDiscriminatorsValue = { 'google-health-api': { @@ -7249,7 +7249,7 @@ const providerRawOutputDiscriminatorsValue = { } as const export const providerRawOutputDiscriminators: typeof providerRawOutputDiscriminatorsValue = - deepFreeze(providerRawOutputDiscriminatorsValue) + /*#__PURE__*/ deepFreeze(providerRawOutputDiscriminatorsValue) export type AdapterMeasurementCatalog = typeof adapterMeasurementCatalog diff --git a/packages/fhir/src/contract/questionnaire.generated.ts b/packages/fhir/src/contract/questionnaire.generated.ts index 1040d0d4..c2a3ac69 100644 --- a/packages/fhir/src/contract/questionnaire.generated.ts +++ b/packages/fhir/src/contract/questionnaire.generated.ts @@ -20,7 +20,7 @@ const groveQuestionnairePackageMetadataValue = { } as const export const groveQuestionnairePackageMetadata: typeof groveQuestionnairePackageMetadataValue = - deepFreeze(groveQuestionnairePackageMetadataValue) + /*#__PURE__*/ deepFreeze(groveQuestionnairePackageMetadataValue) const groveQuestionnaireProfileCanonicalsValue = { 'grove-questionnaire': @@ -30,4 +30,4 @@ const groveQuestionnaireProfileCanonicalsValue = { } as const export const groveQuestionnaireProfileCanonicals: typeof groveQuestionnaireProfileCanonicalsValue = - deepFreeze(groveQuestionnaireProfileCanonicalsValue) + /*#__PURE__*/ deepFreeze(groveQuestionnaireProfileCanonicalsValue) diff --git a/packages/fhir/src/core/freeze.ts b/packages/fhir/src/core/freeze.ts index 5292df7f..e1d3bc2b 100644 --- a/packages/fhir/src/core/freeze.ts +++ b/packages/fhir/src/core/freeze.ts @@ -6,8 +6,10 @@ // SPDX-License-Identifier: MIT // +// A branded primitive is an intersection with an object type; it stays a primitive here. export type ReadonlyDeep = - T extends (...arguments_: never[]) => unknown ? T + T extends string | number | bigint | boolean | symbol | null | undefined ? T + : T extends (...arguments_: never[]) => unknown ? T : T extends readonly unknown[] ? Readonly<{ [Index in keyof T]: ReadonlyDeep }> : T extends object ? { readonly [Key in keyof T]: ReadonlyDeep } diff --git a/packages/fhir/src/core/index.ts b/packages/fhir/src/core/index.ts index 397b101c..dfbf866a 100644 --- a/packages/fhir/src/core/index.ts +++ b/packages/fhir/src/core/index.ts @@ -18,17 +18,24 @@ export { fhirDateTimeToDate, parseAbsoluteUri, parseCanonical, + parseEntryNodeOrdinal, + parseEventSequence, parseFhirId, parseFhirInstant, + parseKeyEpoch, parsePatientReference, parsePositiveInteger, parseResearchStudyReference, parseSemVer, parseUrnUuid, type AbsoluteUri, + type Branded, type Canonical, + type EntryNodeOrdinal, + type EventSequence, type FhirId, type FhirInstant, + type KeyEpoch, type PatientReference, type PositiveInteger, type ResearchStudyReference, @@ -53,4 +60,5 @@ export { type IssueCode, type IssueSeverity, type Result, + type SchemaIssueCode, } from './result.js' diff --git a/packages/fhir/src/core/json.ts b/packages/fhir/src/core/json.ts index 575491cb..72e6cde7 100644 --- a/packages/fhir/src/core/json.ts +++ b/packages/fhir/src/core/json.ts @@ -81,6 +81,8 @@ const cloneJsonObject = ( if (typeof key !== 'string') return invalidJson(path) const property = enumerableDataProperty(value, key, [...path, key]) if (!property.ok) return property + // An undefined member is absent, exactly as JSON serialization treats it. + if (property.value === undefined) continue const nested = cloneJsonNode(property.value, ancestors, [...path, key]) if (!nested.ok) return nested Object.defineProperty(clone, key, { @@ -140,8 +142,9 @@ const cloneJsonNode = ( * Takes an isolated snapshot of caller-owned JSON before validation. * * Accessors, proxies that throw, cycles, sparse arrays, special object instances, and values - * JSON cannot represent are rejected. Shared acyclic subgraphs are copied independently, just - * as they are after a JSON round trip. The function itself never throws for hostile input. + * JSON cannot represent are rejected; an undefined member is dropped. Shared acyclic subgraphs + * are copied independently, just as they are after a JSON round trip. The function itself + * never throws for hostile input. */ export const cloneJsonValue = (value: unknown): Result => { try { diff --git a/packages/fhir/src/core/primitives.ts b/packages/fhir/src/core/primitives.ts index 31c59f97..9834887b 100644 --- a/packages/fhir/src/core/primitives.ts +++ b/packages/fhir/src/core/primitives.ts @@ -11,10 +11,18 @@ import { err, ok, type Result } from './result.js' declare const brand: unique symbol declare const numericBrand: unique symbol -type Branded = string & { readonly [brand]: Name } +export type Branded = string & { + readonly [brand]: Name +} export type AbsoluteUri = Branded<'AbsoluteUri'> export type Canonical = Branded<'Canonical'> +/** Canonical positive decimal naming one key epoch of a deployment's identity scope. */ +export type KeyEpoch = Branded<'KeyEpoch'> +/** Canonical positive decimal ordering one producer instance's exchange events. */ +export type EventSequence = Branded<'EventSequence'> +/** Canonical unsigned decimal position of an entry among those sharing its node role. */ +export type EntryNodeOrdinal = Branded<'EntryNodeOrdinal'> export type FhirId = Branded<'FhirId'> export type FhirInstant = Branded<'FhirInstant'> export type PatientReference = Branded<'PatientReference'> @@ -34,6 +42,8 @@ const UUID = '[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}' const URN_UUID = new RegExp(`^urn:uuid:${UUID}$`, 'u') const DECIMAL_IDENTIFIER = /^\d+$/u +const POSITIVE_DECIMAL = /^[1-9]\d*$/u +const UNSIGNED_DECIMAL = /^(?:0|[1-9]\d*)$/u const SEMVER_IDENTIFIER = /^[\dA-Za-z-]+$/u const isNumericIdentifier = (value: string): boolean => @@ -366,6 +376,38 @@ export const parsePositiveInteger = ( return ok(value as PositiveInteger) } +export const parseKeyEpoch = (value: unknown): Result => { + if (typeof value !== 'string' || !POSITIVE_DECIMAL.test(value)) { + return err( + 'invalid-identifier', + 'Expected a canonical positive decimal key epoch.', + ) + } + return ok(value as KeyEpoch) +} + +export const parseEventSequence = (value: unknown): Result => { + if (typeof value !== 'string' || !POSITIVE_DECIMAL.test(value)) { + return err( + 'invalid-identifier', + 'Expected a canonical positive decimal event sequence.', + ) + } + return ok(value as EventSequence) +} + +export const parseEntryNodeOrdinal = ( + value: unknown, +): Result => { + if (typeof value !== 'string' || !UNSIGNED_DECIMAL.test(value)) { + return err( + 'invalid-identifier', + 'Expected a canonical unsigned decimal entry-node ordinal.', + ) + } + return ok(value as EntryNodeOrdinal) +} + export const parseUrnUuid = (value: unknown): Result => { if (typeof value !== 'string' || !URN_UUID.test(value)) { return err('invalid-uri', 'Expected a lowercase RFC 4122 urn:uuid fullUrl.') diff --git a/packages/fhir/src/core/result.ts b/packages/fhir/src/core/result.ts index 29716c51..72445177 100644 --- a/packages/fhir/src/core/result.ts +++ b/packages/fhir/src/core/result.ts @@ -7,16 +7,12 @@ // import type { z } from 'zod' +import type { ProducerDiagnosticCode } from '../contract/measurement-catalog.generated.js' export type IssueSeverity = 'error' | 'warning' -/** - * Namespaced producer-rule identifier. The registry that names them lives above core, - * so this states only the contract's `namespace.rule` grammar. - */ -type GroveProducerRuleCode = `${string}.${string}` - -export type IssueCode = +/** A failure of this package's own input contract, outside the producer registry. */ +export type SchemaIssueCode = | 'duplicate-identifier' | 'external-validation-required' | 'invalid-choice' @@ -29,9 +25,9 @@ export type IssueCode = | 'missing-required' | 'out-of-range' | 'schema-invalid' - | 'unsupported-measurement' | 'value-mismatch' - | GroveProducerRuleCode + +export type IssueCode = SchemaIssueCode | ProducerDiagnosticCode export interface Issue { readonly severity: IssueSeverity diff --git a/packages/fhir/src/index.ts b/packages/fhir/src/index.ts index 21d14bf2..7bd1f5b4 100644 --- a/packages/fhir/src/index.ts +++ b/packages/fhir/src/index.ts @@ -17,8 +17,11 @@ export { ok, parseAbsoluteUri, parseCanonical, + parseEntryNodeOrdinal, + parseEventSequence, parseFhirId, parseFhirInstant, + parseKeyEpoch, parsePatientReference, parsePositiveInteger, parseResearchStudyReference, @@ -26,11 +29,14 @@ export { parseUrnUuid, type AbsoluteUri, type Canonical, + type EntryNodeOrdinal, + type EventSequence, type FhirId, type FhirInstant, type Issue, type IssueCode, type IssueSeverity, + type KeyEpoch, type PatientReference, type PositiveInteger, type QuantityComparator, @@ -38,6 +44,7 @@ export { type ResearchStudyReference, type ReadonlyDeep, type Result, + type SchemaIssueCode, type SemVer, type UrnUuid, } from './core/index.js' @@ -137,8 +144,9 @@ export { } from './extract/index.js' export { - parseGroveMobileExchangeBundle, - parseGroveMobileRetractionBundle, + isSemanticallyEqual, + parseExchangeGraph, + parseRetractionEvent, parseR4CollectionBundle, parseDevice, parseDocumentReference, @@ -146,75 +154,101 @@ export { parseProvenance, parseSpecimen, parseSupportedR4Resource, + retractionTargets, type R4CollectionBundle, type Device, type DocumentReference, + type ExchangeGraph, type FhirJson, - type GroveMobileExchangeBundle, - type GroveMobileRetractionBundle, type Observation, + type Patient, + type PlanDefinition, type Provenance, + type ResearchStudy, + type ResearchSubject, + type RetractionEvent, + type RetractionTarget, + type RetractionTargetResourceType, + type RetractionTargetRole, type Specimen, type SupportedR4Resource, } from './r4/index.js' export { canonicalizeMobileEffectiveInstant, - entryIdentifierName, createEntryIdentity, deriveEntryFullUrl, deriveEntryNodeIdentifier, deriveEntryNodeValue, deriveEventIdentifier, deriveOpaqueIdentifier, + deriveOpaqueIdentitySystems, encodeLengthFramedUtf8, + entryIdentifierName, groveExchangeProtocol, - groveProfileClaims, groveFhirVersion, - groveMobileContract, - mobileEffectiveCanonicalization, - mobileEffectiveCanonicalizationVectors, groveMobilePackageMetadata, groveMobileProfileCanonicals, + groveProducerDiagnostics, + groveProfileClaims, groveRecordingFormatRegistry, isEntryNodeIdentityValue, isEventIdentityValue, + isOpaqueIdentityScope, isOpaqueIdentityValue, + mobileEffectiveCanonicalization, + mobileEffectiveCanonicalizationVectors, + parseExchangeEventContext, sharedMobileMeasurementCatalog, - validateDeploymentIdentity, - type ApplicationDeviceInput, + validateOpaqueIdentityScope, + type ApplicationDevice, type BloodPressureMeasurement, + type BusinessIdentifier, type ChoiceQuantityMeasurement, type ChoiceQuantityMeasurementKind, - type CompleteIdentifierInput, - type DeploymentIdentityInput, - type GatewayApplicationInput, + type ConversionBatch, + type ConverterRole, + type DeploymentIdentifierSystems, + type EntryIdentity, + type EntryNodeIdentifier, + type EntryNodeKey, + type ExchangeEventContext, + type ResolvedExchangeEventContext, + type ExchangeEventIdentifier, + type ExchangeGraphIdentifiers, + type ExchangeGraphNode, + type GovernedSourceIdentifierDisclosurePolicy, + type GovernedSourceIdentifierType, + type GovernedSourceIdentifierTypeCoding, type GroveIdentifierRole, - type GroveOpaqueIdentityComponents, - type GroveOpaqueIdentityKind, type GroveRecordingFormat, - type GroveOpaqueIdentitySystems, - type GroveMobileProtocolContract, - type HostDeviceInput, - type IdentifiedEntryIdentityInput, + type HostDevice, type InstantCodedMeasurement, type InstantCodedMeasurementKind, type InstantEffectiveTime, type InstantQuantityMeasurement, type InstantQuantityMeasurementKind, type MobileMeasurement, + type OpaqueIdentityComponents, + type OpaqueIdentityKind, + type OpaqueIdentityScope, + type OpaqueIdentityScopeInput, + type OpaqueIdentitySystems, type PeriodEffectiveTime, type PeriodCodedMeasurement, type PeriodCodedMeasurementKind, type PeriodQuantityMeasurement, type PeriodQuantityMeasurementKind, - type RecordingDeviceInput, + type ProducerDiagnosticCode, + type RecordingDevice, type RecordingMethod, - type ResourceIdentityInput, + type RoledIdentifier, type SharedMobileMeasurementKind, type SleepStage, type SleepStageMeasurement, type SleepStageSourceCodingInput, + type StudyEnrollment, + type Subject, } from './mobile/index.js' export { diff --git a/packages/fhir/src/mobile/context.ts b/packages/fhir/src/mobile/context.ts new file mode 100644 index 00000000..e9c4b057 --- /dev/null +++ b/packages/fhir/src/mobile/context.ts @@ -0,0 +1,479 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + containsIsolatedSurrogate, + isEventOfScope, + isOpaqueIdentityScope, + type BusinessIdentifier, + type ExchangeEventIdentifier, + type OpaqueIdentityScope, +} from './identity.js' +import type { + ApplicationDevice, + ConverterRole, + ResolvedExchangeEventContext, + ExchangeGraphNode, + HostDevice, + StudyEnrollment, + Subject, +} from './types.js' +import { + cloneJsonValue, + deepFreeze, + issues, + ok, + parseAbsoluteUri, + parseFhirId, + parseFhirInstant, + type FhirId, + type FhirInstant, + type Issue, + type JsonValue, + type Result, + type SchemaIssueCode, +} from '../core/index.js' +import type { Patient } from '../r4/types.js' + +type Path = ReadonlyArray +type UnknownRecord = Readonly> + +const EXCHANGE_GRAPH_NODES: ReadonlySet = new Set([ + 'bundle', + 'primary-output', + 'source-artifact', + 'recording-device', + 'application-device', + 'host-device', + 'writer', + 'provenance', +]) + +const fault = (code: SchemaIssueCode, path: Path, message: string): Issue => ({ + severity: 'error', + code, + path, + message, +}) + +const asRecord = (value: unknown): UnknownRecord | undefined => + typeof value === 'object' && value !== null && !Array.isArray(value) ? + (value as UnknownRecord) + : undefined + +const isNonBlank = (value: unknown): value is string => + typeof value === 'string' && + value.trim() !== '' && + !containsIsolatedSurrogate(value) + +// An optional field set to undefined is absent; every other key must be declared. +const shapeIssues = ( + record: UnknownRecord, + required: readonly string[], + optional: readonly string[], + path: Path, +): readonly Issue[] => [ + ...Object.keys(record) + .filter( + (key) => + record[key] !== undefined && + !required.includes(key) && + !optional.includes(key), + ) + .map((key) => + fault('schema-invalid', [...path, key], 'Unknown context field.'), + ), + ...required + .filter((key) => record[key] === undefined) + .map((key) => + fault('missing-required', [...path, key], 'Required context field.'), + ), +] + +const identifierIssues = (value: unknown, path: Path): readonly Issue[] => { + const record = asRecord(value) + if (record === undefined) { + return [ + fault('invalid-identifier', path, 'Expected a complete Identifier.'), + ] + } + return [ + ...shapeIssues(record, ['system', 'value'], [], path), + ...(parseAbsoluteUri(record.system).ok ? + [] + : [ + fault( + 'invalid-uri', + [...path, 'system'], + 'Identifier.system must be an absolute URI.', + ), + ]), + ...(( + typeof record.value === 'string' && + record.value !== '' && + !containsIsolatedSurrogate(record.value) + ) ? + [] + : [ + fault( + 'invalid-identifier', + [...path, 'value'], + 'Identifier.value must be a nonempty Unicode-scalar string.', + ), + ]), + ] +} + +const identifierOf = (value: unknown): BusinessIdentifier => { + const record = asRecord(value) ?? {} + return { system: record.system, value: record.value } as BusinessIdentifier +} + +const patientCarriesIdentifier = ( + patient: JsonValue, + identifier: BusinessIdentifier, +): boolean => { + const identifiers = asRecord(patient)?.identifier + return ( + Array.isArray(identifiers) && + identifiers.some((candidate) => { + const record = asRecord(candidate) + return ( + record?.system === identifier.system && + record.value === identifier.value + ) + }) + ) +} + +const subjectIssues = (value: unknown, path: Path): readonly Issue[] => { + const record = asRecord(value) + if ( + record === undefined || + !['bundled', 'logical'].includes(String(record.kind)) + ) { + return [fault('invalid-choice', path, 'A subject is logical or bundled.')] + } + const bundled = record.kind === 'bundled' + const findings = [ + ...shapeIssues( + record, + bundled ? ['kind', 'identifier', 'patient'] : ['kind', 'identifier'], + [], + path, + ), + ...identifierIssues(record.identifier, [...path, 'identifier']), + ] + if (!bundled) return findings + const patient = cloneJsonValue(record.patient) + if ( + !patient.ok || + asRecord(patient.value)?.resourceType !== 'Patient' || + !patientCarriesIdentifier(patient.value, identifierOf(record.identifier)) + ) { + findings.push( + fault( + 'value-mismatch', + [...path, 'patient'], + 'A bundled subject is a Patient resource carrying the subject identifier.', + ), + ) + } + return findings +} + +const deviceIssues = ( + value: unknown, + required: readonly string[], + optional: readonly string[], + path: Path, +): readonly Issue[] => { + const record = asRecord(value) + if (record === undefined) { + return [fault('invalid-type', path, 'Expected a device snapshot object.')] + } + return [ + ...shapeIssues(record, required, optional, path), + ...[...required, ...optional] + .filter((key) => record[key] !== undefined && !isNonBlank(record[key])) + .map((key) => + fault( + 'invalid-identifier', + [...path, key], + 'Expected a non-blank Unicode-scalar string.', + ), + ), + ] +} + +const applicationIssues = (value: unknown, path: Path): readonly Issue[] => + deviceIssues(value, ['sourceDeviceToken', 'name'], ['version', 'build'], path) + +const hostIssues = (value: unknown, path: Path): readonly Issue[] => + deviceIssues( + value, + ['sourceDeviceToken', 'operatingSystemVersion'], + ['name', 'manufacturer', 'modelNumber'], + path, + ) + +const converterRoleIssues = (value: unknown, path: Path): readonly Issue[] => { + if (value === undefined) return [] + const record = asRecord(value) + if (record?.kind === 'assembler' || record?.kind === 'gateway') { + return shapeIssues(record, ['kind'], [], path) + } + if (record?.kind === 'gateway-application') { + return [ + ...shapeIssues(record, ['kind', 'application'], [], path), + ...applicationIssues(record.application, [...path, 'application']), + ] + } + return [ + fault( + 'invalid-choice', + path, + 'A converter role is assembler, gateway, or gateway-application.', + ), + ] +} + +const pairKey = (identifier: BusinessIdentifier): string => + `${identifier.system.length}:${identifier.system}${identifier.value.length}:${identifier.value}` + +const studyIssues = (value: unknown, path: Path): readonly Issue[] => { + if (value === undefined) return [] + if (!Array.isArray(value)) { + return [ + fault('invalid-type', path, 'Expected a list of study enrollments.'), + ] + } + const findings: Issue[] = [] + const studies = new Set() + const enrollments = new Set() + for (const [index, entry] of value.entries()) { + const record = asRecord(entry) + const entryPath = [...path, index] + if (record === undefined) { + findings.push( + fault('invalid-type', entryPath, 'Expected a study enrollment.'), + ) + continue + } + findings.push( + ...shapeIssues( + record, + ['study', 'protocol', 'enrollment'], + [], + entryPath, + ), + ...identifierIssues(record.study, [...entryPath, 'study']), + ...identifierIssues(record.enrollment, [...entryPath, 'enrollment']), + ) + const protocol = asRecord(record.protocol) + if ( + protocol === undefined || + !parseAbsoluteUri(protocol.url).ok || + !isNonBlank(protocol.version) + ) { + findings.push( + fault( + 'invalid-uri', + [...entryPath, 'protocol'], + 'A protocol names its PlanDefinition by absolute url and non-blank version.', + ), + ) + } else { + findings.push( + ...shapeIssues( + protocol, + ['url', 'version'], + [], + [...entryPath, 'protocol'], + ), + ) + } + for (const [field, seen] of [ + ['study', studies], + ['enrollment', enrollments], + ] as const) { + const key = pairKey(identifierOf(record[field])) + if (seen.has(key)) { + findings.push( + fault( + 'duplicate-identifier', + [...entryPath, field], + `Each ${field} identifier appears once.`, + ), + ) + } + seen.add(key) + } + } + return findings +} + +const repositoryIdIssues = (value: unknown, path: Path): readonly Issue[] => { + if (value === undefined) return [] + const record = asRecord(value) + if (record === undefined) { + return [ + fault( + 'invalid-type', + path, + 'Expected repository ids keyed by graph node.', + ), + ] + } + return Object.entries(record).flatMap(([node, id]) => { + if (id === undefined) return [] + if (!EXCHANGE_GRAPH_NODES.has(node)) { + return [ + fault( + 'schema-invalid', + [...path, node], + 'Unknown exchange graph node.', + ), + ] + } + return parseFhirId(id).ok ? + [] + : [fault('invalid-identifier', [...path, node], 'Expected a FHIR id.')] + }) +} + +const eventIssues = ( + value: unknown, + scope: unknown, + path: Path, +): readonly Issue[] => + isOpaqueIdentityScope(scope) && isEventOfScope(scope, value) ? + shapeIssues(asRecord(value) ?? {}, ['system', 'value', 'role'], [], path) + : [ + fault( + 'invalid-identifier', + path, + "The event identifier must be one this context's identity scope minted.", + ), + ] + +const CONTEXT_REQUIRED = [ + 'subject', + 'event', + 'identityScope', + 'repositoryScope', + 'application', + 'host', +] +const CONTEXT_OPTIONAL = [ + 'conversionInstant', + 'converterRole', + 'studies', + 'repositoryIds', +] + +const conversionInstantOf = (value: unknown): Result => + value === undefined ? + parseFhirInstant(new Date().toISOString()) + : parseFhirInstant(value) + +const frozenRepositoryIds = ( + value: unknown, +): Readonly>> | undefined => { + const record = asRecord(value) + if (record === undefined) return undefined + const entries = Object.entries(record).filter(([, id]) => id !== undefined) + return entries.length === 0 ? undefined : Object.fromEntries(entries) +} + +/** + * Validates one exchange event context and returns its frozen copy with every default + * applied: the conversion instant is now, the converter role the assembler, the studies none. + * + * Context faults are deployment configuration, never producer rules: the issues carry the + * package's schema codes, not registry codes, because no source record can cause them. + */ +export const parseExchangeEventContext = ( + input: unknown, +): Result => { + const record = asRecord(input) + if (record === undefined) { + return issues([ + fault('invalid-type', [], 'Expected an exchange event context object.'), + ]) + } + const conversionInstant = conversionInstantOf(record.conversionInstant) + const findings = [ + ...shapeIssues(record, CONTEXT_REQUIRED, CONTEXT_OPTIONAL, []), + ...subjectIssues(record.subject, ['subject']), + ...(isOpaqueIdentityScope(record.identityScope) ? + [] + : [ + fault( + 'invalid-identifier', + ['identityScope'], + 'Expected the handle validateOpaqueIdentityScope returned.', + ), + ]), + ...eventIssues(record.event, record.identityScope, ['event']), + ...identifierIssues(record.repositoryScope, ['repositoryScope']), + ...applicationIssues(record.application, ['application']), + ...hostIssues(record.host, ['host']), + ...(conversionInstant.ok ? + [] + : [ + fault( + 'invalid-date-time', + ['conversionInstant'], + 'Expected an RFC 3339 instant with seconds and an explicit UTC offset.', + ), + ]), + ...converterRoleIssues(record.converterRole, ['converterRole']), + ...studyIssues(record.studies, ['studies']), + ...repositoryIdIssues(record.repositoryIds, ['repositoryIds']), + ] + if (findings.length > 0 || !conversionInstant.ok) return issues(findings) + + // Validated above; the scope handle is kept by reference because it proves itself by identity. + const subject = cloneJsonValue(record.subject) + const converterRole = cloneJsonValue( + record.converterRole ?? { kind: 'assembler' }, + ) + const studies = cloneJsonValue(record.studies ?? []) + const repositoryIds = frozenRepositoryIds(record.repositoryIds) + if (!subject.ok || !converterRole.ok || !studies.ok) { + return issues([ + fault('invalid-type', [], 'Expected an acyclic plain JSON context.'), + ]) + } + const application = cloneJsonValue(record.application) + const host = cloneJsonValue(record.host) + const repositoryScope = cloneJsonValue(record.repositoryScope) + const event = cloneJsonValue(record.event) + if (!application.ok || !host.ok || !repositoryScope.ok || !event.ok) { + return issues([ + fault('invalid-type', [], 'Expected an acyclic plain JSON context.'), + ]) + } + return ok( + deepFreeze({ + subject: subject.value as unknown as Subject, + event: event.value as unknown as ExchangeEventIdentifier, + identityScope: record.identityScope as OpaqueIdentityScope, + repositoryScope: repositoryScope.value as unknown as BusinessIdentifier, + application: application.value as unknown as ApplicationDevice, + host: host.value as unknown as HostDevice, + conversionInstant: conversionInstant.value, + converterRole: converterRole.value as unknown as ConverterRole, + studies: studies.value as unknown as readonly StudyEnrollment[], + ...(repositoryIds === undefined ? {} : { repositoryIds }), + }), + ) +} + +export type { Patient } diff --git a/packages/fhir/src/mobile/contract.ts b/packages/fhir/src/mobile/contract.ts deleted file mode 100644 index 04e647dd..00000000 --- a/packages/fhir/src/mobile/contract.ts +++ /dev/null @@ -1,134 +0,0 @@ -// -// This source file is part of the Grove open-source project -// -// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import { - groveExchangeProtocol, - groveMobilePackageMetadata, - groveMobileProfileCanonicals, - groveRecordingFormatRegistry, -} from '../contract/measurement-catalog.generated.js' -import { deepFreeze } from '../core/index.js' - -export interface GroveMobileProtocolContract { - readonly canonical: string - readonly fhirVersion: string - readonly protocolVersion: number - readonly profiles: Readonly<{ - exchangeBundle: string - conversionProvenance: string - recordingDevice: string - applicationDevice: string - hostDevice: string - sensorRecordingDocument: string - retractionBundle: string - retractionProvenance: string - }> - readonly extensions: Readonly<{ - entryNodeKey: string - retractionTargetRole: string - retractionTargetNativeIdentifier: string - }> - readonly systems: Readonly<{ - identifierRole: string - lifecycleEvent: string - retractionTargetRole: string - }> - readonly identity: Readonly<{ - domain: string - entryNodeDomain: string - fullUrlNamespace: string - opaqueIdentifierRoles: readonly string[] - resourceIdentifierPriority: readonly string[] - valuePrefixes: Readonly<{ - opaque: string - event: string - entryNode: string - }> - }> - readonly lifecycle: Readonly<{ - sourceRecordRetracted: string - retractionTargets: typeof groveExchangeProtocol.lifecycle.retraction.targetRoles - adapterOnlyOutputProfileClaims: typeof groveExchangeProtocol.lifecycle.active.adapterOnlyOutputProfileClaims - activeEntryResourcePolicy: typeof groveExchangeProtocol.lifecycle.active.entryResourcePolicy - }> - readonly referencePolicy: typeof groveExchangeProtocol.referencePolicy - readonly recordingFormats: typeof groveRecordingFormatRegistry -} - -// Each identity value form opens with its revision token, so the contract owns every -// persisted prefix and a renumbered revision reaches this package by regeneration alone. -const valuePrefix = (valueForm: string): string => - `${valueForm.slice(0, valueForm.indexOf(':'))}:` - -// Canonicals the implementation guide owns that the pinned catalog release does not yet -// publish. The catalog spread overrides each one the moment a re-pin carries it. -const unpinnedExtensionCanonicals = { - retractionTargetNativeIdentifier: - 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-native-identifier', -} as const - -const groveMobileContractValue: GroveMobileProtocolContract = { - canonical: groveMobilePackageMetadata.canonical, - fhirVersion: groveMobilePackageMetadata.fhirVersion, - protocolVersion: groveExchangeProtocol.protocolVersion, - profiles: { - exchangeBundle: groveExchangeProtocol.profiles.activeBundle, - conversionProvenance: groveExchangeProtocol.profiles.conversionProvenance, - recordingDevice: groveMobileProfileCanonicals['grove-recording-device'], - applicationDevice: groveMobileProfileCanonicals['grove-application-device'], - hostDevice: groveMobileProfileCanonicals['grove-host-device'], - sensorRecordingDocument: - groveMobileProfileCanonicals['grove-sensor-recording-document'], - retractionBundle: groveExchangeProtocol.profiles.retractionBundle, - retractionProvenance: groveExchangeProtocol.profiles.retractionProvenance, - }, - extensions: { - ...unpinnedExtensionCanonicals, - ...groveExchangeProtocol.extensions, - }, - systems: groveExchangeProtocol.codeSystems, - identity: { - domain: groveExchangeProtocol.opaqueIdentity.domain, - entryNodeDomain: groveExchangeProtocol.entryIdentity.entryNode.domain, - fullUrlNamespace: groveExchangeProtocol.entryIdentity.fullUrl.namespace, - valuePrefixes: { - opaque: valuePrefix(groveExchangeProtocol.opaqueIdentity.valueForm), - event: valuePrefix( - groveExchangeProtocol.event.bundleIdentifier.valueForm, - ), - entryNode: valuePrefix( - groveExchangeProtocol.entryIdentity.entryNode.valueForm, - ), - }, - opaqueIdentifierRoles: [ - ...new Set( - groveExchangeProtocol.opaqueIdentity.identityKinds.map( - ({ identifierRole }) => identifierRole, - ), - ), - ], - resourceIdentifierPriority: - groveExchangeProtocol.entryIdentity.resourceIdentifierPriority, - }, - lifecycle: { - activeEntryResourcePolicy: - groveExchangeProtocol.lifecycle.active.entryResourcePolicy, - adapterOnlyOutputProfileClaims: - groveExchangeProtocol.lifecycle.active.adapterOnlyOutputProfileClaims, - sourceRecordRetracted: - groveExchangeProtocol.lifecycle.retraction.activityCode, - retractionTargets: groveExchangeProtocol.lifecycle.retraction.targetRoles, - }, - referencePolicy: groveExchangeProtocol.referencePolicy, - recordingFormats: groveRecordingFormatRegistry, -} - -/** Runtime constants projected from the generated Grove FHIR protocol catalog. */ -export const groveMobileContract: GroveMobileProtocolContract = deepFreeze( - groveMobileContractValue, -) diff --git a/packages/fhir/src/mobile/identity.ts b/packages/fhir/src/mobile/identity.ts index 21e30698..415c0979 100644 --- a/packages/fhir/src/mobile/identity.ts +++ b/packages/fhir/src/mobile/identity.ts @@ -9,44 +9,149 @@ import { hmac } from '@noble/hashes/hmac.js' import { sha256 } from '@noble/hashes/sha2.js' import { v5 as uuidV5 } from 'uuid' -import { groveMobileContract } from './contract.js' -import type { - CompleteIdentifierInput, - DeploymentIdentityInput, - GroveIdentifierRole, - GroveOpaqueIdentityKind, - IdentifiedEntryIdentityInput, -} from './types.js' import { groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' -import { providerAdapterCatalog } from '../contract/providers.generated.js' import { cloneJsonValue, decodeCanonicalBase64, deepFreeze, encodeBase64, err, - mapResult, ok, parseAbsoluteUri, + parseEntryNodeOrdinal, + parseEventSequence, parseFhirId, + parseKeyEpoch, parseUrnUuid, + type AbsoluteUri, + type EntryNodeOrdinal, + type EventSequence, type FhirId, type JsonValue, + type KeyEpoch, type Result, type UrnUuid, } from '../core/index.js' +type OpaqueIdentityDefinition = + (typeof groveExchangeProtocol)['opaqueIdentity']['identityKinds'][number] + +/** Closed HMAC identity kinds of the Grove exchange protocol. */ +export type OpaqueIdentityKind = OpaqueIdentityDefinition['kind'] + +/** Closed roles a deployment-owned Identifier carries in Identifier.type. */ +export type GroveIdentifierRole = + OpaqueIdentityDefinition['identifierRole'] | 'entry-node' | 'event' + +/** A complete Identifier.system and Identifier.value pair; never a repository id. */ +export interface BusinessIdentifier { + readonly system: AbsoluteUri + readonly value: string +} + +export interface RoledIdentifier extends BusinessIdentifier { + readonly role: GroveIdentifierRole +} + +/** The sole business identifier of one exchange event, in the `e0:` form. */ +export type ExchangeEventIdentifier = RoledIdentifier & { + readonly role: 'event' +} + +export type EntryNodeIdentifier = RoledIdentifier & { + readonly role: 'entry-node' +} + +/** The coordinates of an entry whose resource carries no business identifier. */ +export interface EntryNodeKey { + readonly event: ExchangeEventIdentifier + readonly role: string + readonly ordinal: EntryNodeOrdinal +} + +/** One deployment-owned Identifier.system for each opaque identity kind. */ +export type OpaqueIdentitySystems = Readonly< + Record +> + +/** All twelve deployment-owned identifier systems of one key id and epoch. */ +export interface DeploymentIdentifierSystems { + readonly opaque: OpaqueIdentitySystems + readonly event: AbsoluteUri + readonly entryNode: AbsoluteUri +} + +/** + * Deployment identity material before validation. + * + * The secret is read once by `validateOpaqueIdentityScope` and never retained in, or + * emitted with, FHIR. + */ +export interface OpaqueIdentityScopeInput { + readonly systems: DeploymentIdentifierSystems + readonly keyId: string + readonly keyEpoch: KeyEpoch + /** Canonical unpadded base64url key material containing at least 32 bytes. */ + readonly secretBase64Url: string + /** Canonical lowercase RFC 4122 UUID (versions 1 through 5). */ + readonly producerInstance: string +} + +/** The validated handle that mints opaque identities; its key stays module-private. */ +export interface OpaqueIdentityScope { + readonly systems: DeploymentIdentifierSystems + readonly keyId: string + readonly keyEpoch: KeyEpoch + readonly producerInstance: string +} + +/** An entry key paired with the deterministic fullUrl derived from it. */ +export interface EntryIdentity { + readonly identifier: RoledIdentifier + readonly fullUrl: UrnUuid + readonly id?: FhirId +} + +type StringComponentTuple = Readonly<{ + [Index in keyof Components]: string +}> + +/** Exact ordered component tuples projected from the Grove FHIR protocol catalog. */ +export type OpaqueIdentityComponents = Readonly<{ + [Kind in OpaqueIdentityKind]: StringComponentTuple< + Extract['components'] + > +}> + const ASCII_TOKEN = /^[A-Za-z\d._-]+$/u -const POSITIVE_DECIMAL = /^[1-9]\d*$/u -const UNSIGNED_DECIMAL = /^(?:0|[1-9]\d*)$/u const LOWERCASE_ROLE = /^[a-z][a-z\d-]*$/u const CANONICAL_PRODUCER_UUID = /^[\da-f]{8}-[\da-f]{4}-[1-5][\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}$/u -const { +const MINIMUM_HMAC_KEY_BYTES = 32 + +// Each identity value form opens with its revision token, so the contract owns every +// persisted prefix and a renumbered revision reaches this package by regeneration alone. +const valuePrefix = (valueForm: string): string => + valueForm.slice(0, valueForm.indexOf(':') + 1) +const OPAQUE_PREFIX = valuePrefix( + groveExchangeProtocol.opaqueIdentity.valueForm, +) +const EVENT_PREFIX = valuePrefix( + groveExchangeProtocol.event.bundleIdentifier.valueForm, +) +const ENTRY_NODE_PREFIX = valuePrefix( + groveExchangeProtocol.entryIdentity.entryNode.valueForm, +) +/** The revision tokens every persisted identity value opens with. */ +export const identityValuePrefixes: Readonly<{ + opaque: string + event: string + entryNode: string +}> = Object.freeze({ opaque: OPAQUE_PREFIX, event: EVENT_PREFIX, entryNode: ENTRY_NODE_PREFIX, -} = groveMobileContract.identity.valuePrefixes +}) const OPAQUE_VALUE = new RegExp( `^${OPAQUE_PREFIX}[A-Za-z\\d._-]+:[1-9]\\d*:[A-Za-z\\d_-]{43}$`, 'u', @@ -59,70 +164,35 @@ const ENTRY_NODE_VALUE = new RegExp( `^${ENTRY_NODE_PREFIX}[a-z][a-z\\d-]*:(?:0|[1-9]\\d*):[A-Za-z\\d_-]{43}$`, 'u', ) -const MINIMUM_HMAC_KEY_BYTES = 32 - -const PROVIDER_CODES: ReadonlySet = new Set( - providerAdapterCatalog.providers.map(({ id }) => id), -) -// A kind's coordinate family is written in its components: the first names the adapter -// space, and only source-record coordinates carry a native record id. -const identityKindsCoordinatedBy = ( - adapterComponent: string, -): ReadonlySet => - new Set( - groveExchangeProtocol.opaqueIdentity.identityKinds - .filter( - ({ components }) => - components[0] === adapterComponent && - (components as readonly string[]).includes('native-record-id'), - ) - .map(({ kind }) => kind), - ) - -const PROVIDER_IDENTITY_KINDS: ReadonlySet = - identityKindsCoordinatedBy('provider-code') -const GENERIC_SOURCE_IDENTITY_KINDS: ReadonlySet = - identityKindsCoordinatedBy('adapter-id') const identityKinds = groveExchangeProtocol.opaqueIdentity.identityKinds.map( ({ kind }) => kind, -) as readonly GroveOpaqueIdentityKind[] +) as readonly OpaqueIdentityKind[] const identityComponentCounts = Object.fromEntries( groveExchangeProtocol.opaqueIdentity.identityKinds.map( ({ kind, components }) => [kind, components.length], ), -) as Readonly> +) as Readonly> -const identifierRoleByKind: Readonly< - Record< - GroveOpaqueIdentityKind, - Exclude - > -> = Object.fromEntries( +const identifierRoleByKind = Object.fromEntries( groveExchangeProtocol.opaqueIdentity.identityKinds.map( ({ kind, identifierRole }) => [kind, identifierRole], ), ) as Readonly< - Record< - GroveOpaqueIdentityKind, - Exclude - > + Record > -type OpaqueIdentityDefinition = - (typeof groveExchangeProtocol)['opaqueIdentity']['identityKinds'][number] - -type StringComponentTuple = Readonly<{ - [Index in keyof Components]: string -}> +const IDENTIFIER_ROLES: ReadonlySet = new Set([ + ...Object.values(identifierRoleByKind), + 'entry-node', + 'event', +]) -/** Exact ordered component tuples projected from the Grove FHIR protocol catalog. */ -export type GroveOpaqueIdentityComponents = Readonly<{ - [Kind in GroveOpaqueIdentityKind]: StringComponentTuple< - Extract['components'] - > -}> +export const isGroveIdentifierRole = ( + value: unknown, +): value is GroveIdentifierRole => + typeof value === 'string' && IDENTIFIER_ROLES.has(value) export const containsIsolatedSurrogate = (value: string): boolean => { if (typeof value !== 'string') return true @@ -219,114 +289,188 @@ const decodeBase64UrlWithoutPadding = (value: string): Uint8Array | undefined => const isPublicConformanceKey = (key: Uint8Array): boolean => key.length === 32 && key.every((value, index) => value === index) -const DEPLOYMENT_IDENTITY_KEYS = [ - 'entryNodeIdentifierSystem', - 'eventIdentifierSystem', - 'keyEpoch', - 'keyId', - 'opaqueIdentifierSystems', - 'producerInstance', - 'secretBase64Url', -] as const +const SYSTEM_FORM_PLACEHOLDER = /<([a-z-]+)>/gu -/** Deployment material proven complete once; its decoded HMAC secret stays module-private. */ -export interface ValidatedDeploymentIdentity { - readonly identity: DeploymentIdentityInput -} +const renderSystemForm = ( + form: string, + values: Readonly>, +): string => + form.replaceAll( + SYSTEM_FORM_PLACEHOLDER, + (placeholder: string, name: string) => values[name] ?? placeholder, + ) -/** Raw deployment configuration, or material `validateDeploymentIdentity` already proved. */ -export type DeploymentIdentitySource = - DeploymentIdentityInput | ValidatedDeploymentIdentity +/** + * Names all twelve deployment identifier systems by the catalog's recommended form. + * + * The root is the deployment's own absolute URI; a deployment that already governs its + * own namespaces supplies them to `validateOpaqueIdentityScope` directly instead. + */ +export const deriveOpaqueIdentitySystems = ( + root: AbsoluteUri, + keyId: string, + epoch: KeyEpoch, +): Result => { + const parsedRoot = parseAbsoluteUri(root) + if (!parsedRoot.ok) return parsedRoot + if (/[#?]/u.test(root) || root.endsWith('/')) { + return err( + 'invalid-uri', + 'A deployment root names a path without a trailing slash, query, or fragment.', + ['root'], + ) + } + if (typeof keyId !== 'string' || !ASCII_TOKEN.test(keyId)) { + return err( + 'invalid-identifier', + 'Identity keyId must be a nonempty ASCII token using A-Z, a-z, 0-9, dot, underscore, or hyphen.', + ['keyId'], + ) + } + const parsedEpoch = parseKeyEpoch(epoch) + if (!parsedEpoch.ok) return parsedEpoch + + const opaque: Partial> = {} + for (const kind of identityKinds) { + const system = parseAbsoluteUri( + renderSystemForm( + groveExchangeProtocol.opaqueIdentity.recommendedSystemForm, + { + 'deployment-root': root, + 'identity-kind': kind, + 'key-id': keyId, + epoch, + }, + ), + ) + if (!system.ok) return system + opaque[kind] = system.value + } + const event = parseAbsoluteUri( + renderSystemForm( + groveExchangeProtocol.event.bundleIdentifier.recommendedSystemForm, + { 'deployment-root': root }, + ), + ) + if (!event.ok) return event + const entryNode = parseAbsoluteUri( + renderSystemForm( + groveExchangeProtocol.entryIdentity.entryNode.recommendedSystemForm, + { 'deployment-root': root }, + ), + ) + if (!entryNode.ok) return entryNode + return ok( + deepFreeze({ + opaque: opaque as OpaqueIdentitySystems, + event: event.value, + entryNode: entryNode.value, + }), + ) +} -interface ResolvedDeployment { - readonly handle: ValidatedDeploymentIdentity - readonly identity: DeploymentIdentityInput +interface ResolvedScope { + readonly scope: OpaqueIdentityScope readonly secret: Uint8Array } // Membership is the proof, and only frozen material is admitted: a handle a caller holds // can be neither forged nor edited afterwards, so reuse cannot smuggle in new material. -const publiclyValidatedDeployments = new WeakSet() -const deploymentSecrets = new WeakMap() +const validatedScopes = new WeakSet() +const scopeSecrets = new WeakMap() -const isPubliclyValidated = ( - source: unknown, -): source is ValidatedDeploymentIdentity => - typeof source === 'object' && - source !== null && - publiclyValidatedDeployments.has(source as ValidatedDeploymentIdentity) +export const isOpaqueIdentityScope = ( + value: unknown, +): value is OpaqueIdentityScope => + typeof value === 'object' && + value !== null && + validatedScopes.has(value as OpaqueIdentityScope) -const validateDeploymentIdentityInternal = ( - input: unknown, - allowPublicConformanceKey: boolean, -): Result => { - const snapshot = cloneJsonValue(input) - if (!snapshot.ok) return snapshot - if ( - typeof snapshot.value !== 'object' || - snapshot.value === null || - Array.isArray(snapshot.value) - ) { - return err( - 'missing-required', - 'Deployment identity configuration must be a complete object.', - ) - } - const rawCandidate = snapshot.value as Readonly> - const deploymentKeys = Object.keys(rawCandidate) - if ( - deploymentKeys.length !== DEPLOYMENT_IDENTITY_KEYS.length || - deploymentKeys.some( - (key) => !(DEPLOYMENT_IDENTITY_KEYS as readonly string[]).includes(key), - ) - ) { - return err( - 'schema-invalid', - 'Deployment identity configuration contains missing or unknown fields.', - ) - } - const rawSystems = rawCandidate.opaqueIdentifierSystems +const SCOPE_INPUT_KEYS = [ + 'keyEpoch', + 'keyId', + 'producerInstance', + 'secretBase64Url', + 'systems', +] as const +const SYSTEM_KEYS = ['entryNode', 'event', 'opaque'] as const + +const hasExactKeys = ( + record: Readonly>, + keys: readonly string[], +): boolean => { + const present = Object.keys(record) + return ( + present.length === keys.length && present.every((key) => keys.includes(key)) + ) +} + +const asJsonObject = ( + value: JsonValue | undefined, +): Readonly> | undefined => + typeof value === 'object' && value !== null && !Array.isArray(value) ? + (value as Readonly>) + : undefined + +const validateSystems = ( + value: JsonValue | undefined, +): Result => { + const systems = asJsonObject(value) + const opaque = asJsonObject(systems?.opaque) if ( - typeof rawSystems !== 'object' || - rawSystems === null || - Array.isArray(rawSystems) + systems === undefined || + opaque === undefined || + !hasExactKeys(systems, SYSTEM_KEYS) ) { return err( 'missing-required', - 'Deployment identity configuration must include its Identifier systems.', - ['opaqueIdentifierSystems'], + 'Deployment identifier systems name the opaque, event, and entry-node systems and nothing else.', + ['systems'], ) } - const candidate = rawCandidate as unknown as DeploymentIdentityInput - const systemEntries = Object.entries(rawSystems) + const opaqueEntries = Object.entries(opaque) if ( - systemEntries.length !== identityKinds.length || - identityKinds.some((kind) => !Object.hasOwn(rawSystems, kind)) || - systemEntries.some( - ([kind]) => !identityKinds.includes(kind as GroveOpaqueIdentityKind), - ) || - systemEntries.some(([, system]) => !parseAbsoluteUri(system).ok) || - !parseAbsoluteUri(candidate.eventIdentifierSystem).ok || - !parseAbsoluteUri(candidate.entryNodeIdentifierSystem).ok + !hasExactKeys(opaque, identityKinds) || + opaqueEntries.some(([, system]) => !parseAbsoluteUri(system).ok) || + !parseAbsoluteUri(systems.event).ok || + !parseAbsoluteUri(systems.entryNode).ok ) { return err( 'invalid-uri', - 'Every deployment identity system must be an absolute URI.', - ['opaqueIdentifierSystems'], + 'Every deployment identifier system must be an absolute URI, one for each identity kind.', + ['systems'], ) } - const systems = [ - ...systemEntries.map(([, system]) => system), - candidate.eventIdentifierSystem, - candidate.entryNodeIdentifierSystem, + const all = [ + ...opaqueEntries.map(([, system]) => system), + systems.event, + systems.entryNode, ] - if (new Set(systems).size !== systems.length) { + if (new Set(all).size !== all.length) { return err( 'duplicate-identifier', 'Each identity kind, event, and entry-node key space requires its own Identifier.system.', - ['opaqueIdentifierSystems'], + ['systems'], + ) + } + return ok(systems as unknown as DeploymentIdentifierSystems) +} + +const validateScopeInternal = ( + input: unknown, + allowPublicConformanceKey: boolean, +): Result => { + const snapshot = cloneJsonValue(input) + if (!snapshot.ok) return snapshot + const candidate = asJsonObject(snapshot.value) + if (candidate === undefined || !hasExactKeys(candidate, SCOPE_INPUT_KEYS)) { + return err( + 'schema-invalid', + 'Identity scope input contains missing or unknown fields.', ) } + const systems = validateSystems(candidate.systems) + if (!systems.ok) return systems if ( typeof candidate.keyId !== 'string' || !ASCII_TOKEN.test(candidate.keyId) @@ -337,16 +481,8 @@ const validateDeploymentIdentityInternal = ( ['keyId'], ) } - if ( - typeof candidate.keyEpoch !== 'string' || - !POSITIVE_DECIMAL.test(candidate.keyEpoch) - ) { - return err( - 'invalid-identifier', - 'Identity keyEpoch must be a canonical positive decimal string.', - ['keyEpoch'], - ) - } + const keyEpoch = parseKeyEpoch(candidate.keyEpoch) + if (!keyEpoch.ok) return keyEpoch if ( typeof candidate.producerInstance !== 'string' || !CANONICAL_PRODUCER_UUID.test(candidate.producerInstance) @@ -375,39 +511,61 @@ const validateDeploymentIdentityInternal = ( ['secretBase64Url'], ) } - const identity = deepFreeze(candidate) as DeploymentIdentityInput - const handle: ValidatedDeploymentIdentity = Object.freeze({ identity }) - deploymentSecrets.set(handle, secret) + const scope: OpaqueIdentityScope = deepFreeze({ + systems: systems.value, + keyId: candidate.keyId, + keyEpoch: keyEpoch.value, + producerInstance: candidate.producerInstance, + }) + scopeSecrets.set(scope, secret) // Only publicly validated material is reusable; the conformance seam revalidates. - if (!allowPublicConformanceKey) publiclyValidatedDeployments.add(handle) - return ok({ handle, identity, secret }) + if (!allowPublicConformanceKey) validatedScopes.add(scope) + return ok({ scope, secret }) } -const resolveDeployment = ( - source: unknown, - allowPublicConformanceKey: boolean, -): Result => { - if (isPubliclyValidated(source)) { - const secret = deploymentSecrets.get(source) - if (secret !== undefined) { - return ok({ handle: source, identity: source.identity, secret }) - } +/** Validates deployment identity material once; every minting method takes the handle. */ +export const validateOpaqueIdentityScope = ( + input: OpaqueIdentityScopeInput, +): Result => { + const resolved = validateScopeInternal(input, false) + return resolved.ok ? ok(resolved.value.scope) : resolved +} + +const resolveScope = (scope: unknown): Result => { + const secret = + isOpaqueIdentityScope(scope) ? scopeSecrets.get(scope) : undefined + if (!isOpaqueIdentityScope(scope) || secret === undefined) { + return err( + 'invalid-identifier', + 'Expected the handle validateOpaqueIdentityScope returned.', + ['scope'], + ) } - return validateDeploymentIdentityInternal(source, allowPublicConformanceKey) + return ok({ scope, secret }) } -/** Validates deployment identity material for every public production/runtime operation. */ -export const validateDeploymentIdentity = ( - input: unknown, -): Result => - mapResult(resolveDeployment(input, false), ({ handle }) => handle) +/** Whether an event identifier was minted by this scope's producer instance. */ +export const isEventOfScope = ( + scope: OpaqueIdentityScope, + event: unknown, +): event is ExchangeEventIdentifier => { + const candidate = asJsonObject( + typeof event === 'object' && event !== null ? (event as JsonValue) : null, + ) + return ( + candidate?.role === 'event' && + candidate.system === scope.systems.event && + typeof candidate.value === 'string' && + EVENT_VALUE.test(candidate.value) && + candidate.value.startsWith(`${EVENT_PREFIX}${scope.producerInstance}:`) + ) +} -const deriveOpaqueIdentifierInternal = ( - deployment: DeploymentIdentitySource, +const deriveOpaqueIdentifierWith = ( + resolved: ResolvedScope, identityKind: Kind, - components: GroveOpaqueIdentityComponents[Kind], - allowPublicConformanceKey: boolean, -): Result => { + components: OpaqueIdentityComponents[Kind], +): Result => { if ( typeof identityKind !== 'string' || !Object.hasOwn(identityComponentCounts, identityKind) @@ -418,8 +576,6 @@ const deriveOpaqueIdentifierInternal = ( ['identityKind'], ) } - const validated = resolveDeployment(deployment, allowPublicConformanceKey) - if (!validated.ok) return validated const componentSnapshot = cloneJsonValue(components) if (!componentSnapshot.ok) return componentSnapshot if ( @@ -454,44 +610,33 @@ const deriveOpaqueIdentifierInternal = ( ['components', invalidComponentIndex], ) } - const firstComponent = componentSnapshot.value[0] - if ( - typeof firstComponent !== 'string' || - (PROVIDER_IDENTITY_KINDS.has(identityKind) && - !PROVIDER_CODES.has(firstComponent)) || - (GENERIC_SOURCE_IDENTITY_KINDS.has(identityKind) && - PROVIDER_CODES.has(firstComponent)) - ) { - return err( - 'invalid-code', - PROVIDER_IDENTITY_KINDS.has(identityKind) ? - 'A Provider identity kind requires one exact catalog provider code as its first component.' - : 'Provider coordinates require the matching provider-record, provider-output, or provider-artifact identity kind.', - ['components', 0], - ) - } const preimage = encodeLengthFramedUtf8([ - groveMobileContract.identity.domain, + groveExchangeProtocol.opaqueIdentity.domain, identityKind, ...componentSnapshot.value, ]) if (!preimage.ok) return preimage - const { identity, secret } = validated.value + const { scope, secret } = resolved const digest = hmac(sha256, secret, preimage.value) - return ok({ - system: identity.opaqueIdentifierSystems[identityKind], - value: `${OPAQUE_PREFIX}${identity.keyId}:${identity.keyEpoch}:${base64UrlWithoutPadding(digest)}`, - role: identifierRoleByKind[identityKind], - }) + return ok( + deepFreeze({ + system: scope.systems.opaque[identityKind], + value: `${OPAQUE_PREFIX}${scope.keyId}:${scope.keyEpoch}:${base64UrlWithoutPadding(digest)}`, + role: identifierRoleByKind[identityKind], + }), + ) } -/** Derives one deployment-owned, role-typed Grove HMAC Identifier. */ -export const deriveOpaqueIdentifier = ( - deployment: DeploymentIdentitySource, +/** Mints one deployment-owned, role-typed opaque identifier. */ +export const deriveOpaqueIdentifier = ( + scope: OpaqueIdentityScope, identityKind: Kind, - components: GroveOpaqueIdentityComponents[Kind], -): Result => - deriveOpaqueIdentifierInternal(deployment, identityKind, components, false) + components: OpaqueIdentityComponents[Kind], +): Result => { + const resolved = resolveScope(scope) + if (!resolved.ok) return resolved + return deriveOpaqueIdentifierWith(resolved.value, identityKind, components) +} /** * Internal test seam for the exact published normative vector key. @@ -499,18 +644,18 @@ export const deriveOpaqueIdentifier = ( * Deliberately omitted from every package entry point; application code cannot opt into it. */ export const deriveConformanceVectorOpaqueIdentifier = < - Kind extends GroveOpaqueIdentityKind, + Kind extends OpaqueIdentityKind, >( - deployment: DeploymentIdentityInput, + input: OpaqueIdentityScopeInput, identityKind: Kind, - components: GroveOpaqueIdentityComponents[Kind], -): Result => { - const secret = decodeBase64UrlWithoutPadding(deployment.secretBase64Url) + components: OpaqueIdentityComponents[Kind], +): Result => { + const secret = decodeBase64UrlWithoutPadding(input.secretBase64Url) if ( secret === undefined || !isPublicConformanceKey(secret) || - deployment.keyId !== groveExchangeProtocol.testVectors.keyId || - deployment.keyEpoch !== groveExchangeProtocol.testVectors.epoch + input.keyId !== groveExchangeProtocol.testVectors.keyId || + input.keyEpoch !== groveExchangeProtocol.testVectors.epoch ) { return err( 'invalid-identifier', @@ -518,47 +663,40 @@ export const deriveConformanceVectorOpaqueIdentifier = < ['secretBase64Url'], ) } - return deriveOpaqueIdentifierInternal( - deployment, - identityKind, - components, - true, - ) + const resolved = validateScopeInternal(input, true) + if (!resolved.ok) return resolved + return deriveOpaqueIdentifierWith(resolved.value, identityKind, components) } -/** Creates the sole event business Identifier for one immutable exchange assertion. */ +/** Mints the sole event business identifier for one immutable exchange assertion. */ export const deriveEventIdentifier = ( - deployment: DeploymentIdentitySource, - sequence: string, -): Result => { - const validated = resolveDeployment(deployment, false) - if (!validated.ok) return validated - if (typeof sequence !== 'string' || !POSITIVE_DECIMAL.test(sequence)) { - return err( - 'invalid-identifier', - 'Event sequence must be a canonical positive decimal string.', - ['sequence'], - ) - } - return ok({ - system: validated.value.identity.eventIdentifierSystem, - value: `${EVENT_PREFIX}${validated.value.identity.producerInstance}:${sequence}`, - role: 'event', - }) + scope: OpaqueIdentityScope, + sequence: EventSequence, +): Result => { + const resolved = resolveScope(scope) + if (!resolved.ok) return resolved + const parsed = parseEventSequence(sequence) + if (!parsed.ok) return parsed + return ok( + deepFreeze({ + system: resolved.value.scope.systems.event, + value: `${EVENT_PREFIX}${resolved.value.scope.producerInstance}:${parsed.value}`, + role: 'event' as const, + }), + ) } -/** Derives the event-scoped node value a resource without a business Identifier is keyed by. */ -export const deriveEntryNodeValue = ( - eventSystem: string, - eventValue: string, - role: string, - ordinal: string, -): Result => { +const validateEntryNodeKey = (key: unknown): Result => { + const snapshot = cloneJsonValue(key) + if (!snapshot.ok) return snapshot + const candidate = asJsonObject(snapshot.value) + const event = asJsonObject(candidate?.event) if ( - typeof eventSystem !== 'string' || - typeof eventValue !== 'string' || - !parseAbsoluteUri(eventSystem).ok || - !EVENT_VALUE.test(eventValue) + candidate === undefined || + event?.role !== 'event' || + !parseAbsoluteUri(event.system).ok || + typeof event.value !== 'string' || + !EVENT_VALUE.test(event.value) ) { return err( 'invalid-identifier', @@ -566,24 +704,34 @@ export const deriveEntryNodeValue = ( ['event'], ) } - if (typeof role !== 'string' || !LOWERCASE_ROLE.test(role)) { + if ( + typeof candidate.role !== 'string' || + !LOWERCASE_ROLE.test(candidate.role) + ) { return err( 'invalid-code', 'Entry-node role must be a lowercase code token.', ['role'], ) } - if (typeof ordinal !== 'string' || !UNSIGNED_DECIMAL.test(ordinal)) { - return err( - 'invalid-identifier', - 'Entry-node ordinal must be a canonical unsigned decimal string.', - ['ordinal'], - ) - } + const ordinal = parseEntryNodeOrdinal(candidate.ordinal) + if (!ordinal.ok) return ordinal + return ok({ + event: event as unknown as ExchangeEventIdentifier, + role: candidate.role, + ordinal: ordinal.value, + }) +} + +/** Derives the event-scoped node value a resource without a business identifier is keyed by. */ +export const deriveEntryNodeValue = (key: EntryNodeKey): Result => { + const validated = validateEntryNodeKey(key) + if (!validated.ok) return validated + const { event, role, ordinal } = validated.value const preimage = encodeLengthFramedUtf8([ - groveMobileContract.identity.entryNodeDomain, - eventSystem, - eventValue, + groveExchangeProtocol.entryIdentity.entryNode.domain, + event.system, + event.value, role, ordinal, ]) @@ -593,56 +741,31 @@ export const deriveEntryNodeValue = ( ) } -/** Creates an unkeyed, event-scoped graph-node Identifier for a resource without business ID. */ +/** Mints the entry-node identifier of one of this producer's own events. */ export const deriveEntryNodeIdentifier = ( - deployment: DeploymentIdentitySource, - event: CompleteIdentifierInput, - role: string, - ordinal: string, -): Result => { - const validated = resolveDeployment(deployment, false) + scope: OpaqueIdentityScope, + key: EntryNodeKey, +): Result => { + const resolved = resolveScope(scope) + if (!resolved.ok) return resolved + const validated = validateEntryNodeKey(key) if (!validated.ok) return validated - const eventSnapshot = cloneJsonValue(event) - if ( - !eventSnapshot.ok || - typeof eventSnapshot.value !== 'object' || - eventSnapshot.value === null || - Array.isArray(eventSnapshot.value) - ) { - return err( - 'invalid-identifier', - "Entry-node derivation requires this producer's complete typed event Identifier.", - ['event'], - ) - } - const eventValue = eventSnapshot.value as unknown as CompleteIdentifierInput - if ( - eventValue.system !== validated.value.identity.eventIdentifierSystem || - eventValue.role !== 'event' || - typeof eventValue.value !== 'string' || - !EVENT_VALUE.test(eventValue.value) || - !eventValue.value.startsWith( - `${EVENT_PREFIX}${validated.value.identity.producerInstance}:`, - ) - ) { + if (!isEventOfScope(resolved.value.scope, validated.value.event)) { return err( 'invalid-identifier', "Entry-node derivation requires this producer's complete typed event Identifier.", ['event'], ) } - const value = deriveEntryNodeValue( - eventValue.system, - eventValue.value, - role, - ordinal, - ) + const value = deriveEntryNodeValue(validated.value) if (!value.ok) return value - return ok({ - system: validated.value.identity.entryNodeIdentifierSystem, - value: value.value, - role: 'entry-node', - }) + return ok( + deepFreeze({ + system: resolved.value.scope.systems.entryNode, + value: value.value, + role: 'entry-node' as const, + }), + ) } const hasCanonicalSha256DigestSuffix = (value: string): boolean => { @@ -665,18 +788,14 @@ export const isEntryNodeIdentityValue = (value: unknown): boolean => /** The UUID-v5 byte name for one identifier: length-framed UTF-8 `[system, value]`. */ export const entryIdentifierName = ( - input: CompleteIdentifierInput, + input: BusinessIdentifier, ): Result => { const snapshot = cloneJsonValue(input) - if ( - !snapshot.ok || - typeof snapshot.value !== 'object' || - snapshot.value === null || - Array.isArray(snapshot.value) - ) { + if (!snapshot.ok) return snapshot + const identifier = asJsonObject(snapshot.value) + if (identifier === undefined) { return err('invalid-type', 'Expected one complete Identifier.') } - const identifier = snapshot.value as unknown as CompleteIdentifierInput if (!parseAbsoluteUri(identifier.system).ok) { return err('invalid-uri', 'Identifier.system must be an absolute URI.', [ 'system', @@ -687,54 +806,48 @@ export const entryIdentifierName = ( 'value', ]) } - return encodeLengthFramedUtf8([identifier.system, identifier.value]) + return encodeLengthFramedUtf8([identifier.system as string, identifier.value]) } /** Derives the protocol-mandated lowercase UUID-v5 Bundle entry fullUrl. */ export const deriveEntryFullUrl = ( - input: CompleteIdentifierInput, + input: BusinessIdentifier, ): Result => { const name = entryIdentifierName(input) if (!name.ok) return name return parseUrnUuid( - `urn:uuid:${uuidV5(name.value, groveMobileContract.identity.fullUrlNamespace)}`, + `urn:uuid:${uuidV5(name.value, groveExchangeProtocol.entryIdentity.fullUrl.namespace)}`, ) } /** Pairs a complete entry key with its deterministic exchange UUID URN. */ export const createEntryIdentity = ( - identifier: CompleteIdentifierInput, + identifier: RoledIdentifier, id?: FhirId, -): Result => { +): Result => { if (id !== undefined && !parseFhirId(id).ok) { return err('invalid-identifier', 'Resource.id is not a valid FHIR id.', [ 'id', ]) } - const identifierSnapshot = cloneJsonValue(identifier) - if ( - !identifierSnapshot.ok || - typeof identifierSnapshot.value !== 'object' || - identifierSnapshot.value === null || - Array.isArray(identifierSnapshot.value) - ) { - return err('invalid-type', 'Expected one complete entry Identifier.') + const snapshot = cloneJsonValue(identifier) + if (!snapshot.ok) return snapshot + const candidate = asJsonObject(snapshot.value) + if (candidate === undefined || !isGroveIdentifierRole(candidate.role)) { + return err( + 'invalid-identifier', + 'An entry key requires one complete Identifier with a Grove identifier role.', + ['role'], + ) } - const safeIdentifier = - identifierSnapshot.value as unknown as CompleteIdentifierInput - const fullUrl = deriveEntryFullUrl(safeIdentifier) + const key = candidate as unknown as RoledIdentifier + const fullUrl = deriveEntryFullUrl(key) if (!fullUrl.ok) return fullUrl return ok( deepFreeze({ fullUrl: fullUrl.value, - identifier: { - system: safeIdentifier.system, - value: safeIdentifier.value, - ...(safeIdentifier.role === undefined ? - {} - : { role: safeIdentifier.role }), - }, + identifier: { system: key.system, value: key.value, role: key.role }, ...(id === undefined ? {} : { id }), - }) as unknown as IdentifiedEntryIdentityInput, + }), ) } diff --git a/packages/fhir/src/mobile/index.ts b/packages/fhir/src/mobile/index.ts index 1ee068dc..a7777d13 100644 --- a/packages/fhir/src/mobile/index.ts +++ b/packages/fhir/src/mobile/index.ts @@ -7,23 +7,38 @@ // export { - entryIdentifierName, createEntryIdentity, deriveEntryFullUrl, deriveEntryNodeIdentifier, deriveEntryNodeValue, deriveEventIdentifier, deriveOpaqueIdentifier, + deriveOpaqueIdentitySystems, encodeLengthFramedUtf8, + entryIdentifierName, isEntryNodeIdentityValue, isEventIdentityValue, + isOpaqueIdentityScope, isOpaqueIdentityValue, - validateDeploymentIdentity, + validateOpaqueIdentityScope, + type BusinessIdentifier, + type DeploymentIdentifierSystems, + type EntryIdentity, + type EntryNodeIdentifier, + type EntryNodeKey, + type ExchangeEventIdentifier, + type GroveIdentifierRole, + type OpaqueIdentityComponents, + type OpaqueIdentityKind, + type OpaqueIdentityScope, + type OpaqueIdentityScopeInput, + type OpaqueIdentitySystems, + type RoledIdentifier, } from './identity.js' -export { groveMobileContract } from './contract.js' -export type { GroveMobileProtocolContract } from './contract.js' +export { parseExchangeEventContext } from './context.js' export { groveExchangeProtocol, + groveProducerDiagnostics, groveProfileClaims, groveFhirVersion, groveMobilePackageMetadata, @@ -33,23 +48,26 @@ export { mobileEffectiveCanonicalizationVectors, sharedMobileMeasurementCatalog, type GroveRecordingFormat, + type ProducerDiagnosticCode, type ProviderRecordingFormat, type SharedMobileMeasurementKind, } from '../contract/measurement-catalog.generated.js' export { canonicalizeMobileEffectiveInstant } from './time.js' export type { - ApplicationDeviceInput, + ApplicationDevice, BloodPressureMeasurement, ChoiceQuantityMeasurement, ChoiceQuantityMeasurementKind, - CompleteIdentifierInput, - DeploymentIdentityInput, - GroveIdentifierRole, - GroveOpaqueIdentityKind, - GroveOpaqueIdentitySystems, - GatewayApplicationInput, - HostDeviceInput, - IdentifiedEntryIdentityInput, + ConversionBatch, + ConverterRole, + ExchangeEventContext, + ResolvedExchangeEventContext, + ExchangeGraphIdentifiers, + ExchangeGraphNode, + GovernedSourceIdentifierDisclosurePolicy, + GovernedSourceIdentifierType, + GovernedSourceIdentifierTypeCoding, + HostDevice, InstantCodedMeasurement, InstantCodedMeasurementKind, InstantEffectiveTime, @@ -61,11 +79,11 @@ export type { PeriodEffectiveTime, PeriodQuantityMeasurement, PeriodQuantityMeasurementKind, - RecordingDeviceInput, + RecordingDevice, RecordingMethod, - ResourceIdentityInput, SleepStage, SleepStageMeasurement, SleepStageSourceCodingInput, + StudyEnrollment, + Subject, } from './types.js' -export type { GroveOpaqueIdentityComponents } from './identity.js' diff --git a/packages/fhir/src/mobile/types.ts b/packages/fhir/src/mobile/types.ts index 925c46e7..79a50103 100644 --- a/packages/fhir/src/mobile/types.ts +++ b/packages/fhir/src/mobile/types.ts @@ -6,20 +6,19 @@ // SPDX-License-Identifier: MIT // -import type { SharedMobileMeasurementKind } from '../contract/measurement-catalog.generated.js' import type { - AbsoluteUri, - FhirId, - FhirInstant, - UrnUuid, -} from '../core/index.js' + BusinessIdentifier, + ExchangeEventIdentifier, + OpaqueIdentityScope, + RoledIdentifier, +} from './identity.js' +import type { SharedMobileMeasurementKind } from '../contract/measurement-catalog.generated.js' +import type { AbsoluteUri, FhirId, FhirInstant } from '../core/index.js' +import type { Patient } from '../r4/types.js' type SharedCatalog = typeof import('../contract/measurement-catalog.generated.js').sharedMobileMeasurementCatalog -type OpaqueIdentityDefinition = - (typeof import('../contract/measurement-catalog.generated.js').groveExchangeProtocol)['opaqueIdentity']['identityKinds'][number] - /** Kinds whose catalog definition declares the given value kind and effective. */ export type MeasurementKindsWhere< ValueKind extends string, @@ -134,50 +133,6 @@ export interface SleepStageMeasurement { readonly effective: PeriodEffectiveTime } -/** Complete FHIR business Identifier pair; neither member may be omitted. */ -export interface CompleteIdentifierInput { - readonly system: AbsoluteUri - readonly value: string - /** Grove role carried in Identifier.type for deployment-owned identities. */ - readonly role?: GroveIdentifierRole -} - -/** Closed roles used to distinguish deployment-owned identifier key spaces. */ -export type GroveIdentifierRole = - OpaqueIdentityDefinition['identifierRole'] | 'entry-node' | 'event' - -/** Closed HMAC domains from the Grove FHIR exchange protocol. */ -export type GroveOpaqueIdentityKind = OpaqueIdentityDefinition['kind'] - -export type GroveOpaqueIdentitySystems = Readonly< - Record -> - -/** - * Deployment-owned identity material. - * - * Every opaque system names exactly one deployment, identity kind, key id, and key epoch. The - * secret is used only while a graph is built and is never retained in, or emitted with, FHIR. - */ -export interface DeploymentIdentityInput { - readonly opaqueIdentifierSystems: GroveOpaqueIdentitySystems - readonly eventIdentifierSystem: AbsoluteUri - readonly entryNodeIdentifierSystem: AbsoluteUri - readonly keyId: string - /** Canonical positive decimal string; represented lexically to avoid JavaScript integer loss. */ - readonly keyEpoch: string - /** Canonical unpadded base64url key material containing at least 32 bytes. */ - readonly secretBase64Url: string - /** Canonical lowercase RFC 4122 UUID (versions 1 through 5). */ - readonly producerInstance: string -} - -/** Caller-owned business identity with an optional repository-assigned Resource.id. */ -export interface ResourceIdentityInput { - readonly identifier: CompleteIdentifierInput - readonly id?: FhirId -} - /** Closed union of normalized measurements defined by the shared Mobile IG. */ export type MobileMeasurement = | BloodPressureMeasurement @@ -187,65 +142,162 @@ export type MobileMeasurement = | PeriodQuantityMeasurement | SleepStageMeasurement -/** Derived exchange identity returned by identity helpers, never required as input. */ -export interface IdentifiedEntryIdentityInput extends ResourceIdentityInput { - readonly fullUrl: UrnUuid +export type RecordingMethod = + 'actively-recorded' | 'automatically-recorded' | 'manual-entry' + +/** + * The participant an output describes. + * + * A logical subject is the identifier-only pseudonym reference and needs no Patient entry; + * a bundled subject adds the deployment's own Patient entry, which must carry the same + * identifier, and outputs reference that entry. + */ +export type Subject = + | { readonly kind: 'logical'; readonly identifier: BusinessIdentifier } + | { + readonly kind: 'bundled' + readonly identifier: BusinessIdentifier + readonly patient: Patient + } + +/** One ResearchStudy, its exact-revision PlanDefinition and one ResearchSubject. */ +export interface StudyEnrollment { + readonly study: BusinessIdentifier + readonly protocol: { + readonly url: AbsoluteUri + readonly version: string + } + readonly enrollment: BusinessIdentifier } -export interface ApplicationDeviceInput { - /** Deployment-governed token for this source application/build; never emitted in clear. */ +/** The immutable snapshot of one application build; the token never appears in clear. */ +export interface ApplicationDevice { readonly sourceDeviceToken: string - readonly id?: FhirId | undefined readonly name: string readonly version?: string | undefined readonly build?: string | undefined - readonly manufacturer?: string | undefined - /** Optional immutable snapshot of the hardware/OS hosting this application. */ - readonly host?: HostDeviceInput | undefined } -export interface HostDeviceInput { - /** Event-time source token for this host snapshot; never emitted in clear. */ +/** The immutable snapshot of the hardware and OS hosting the converter. */ +export interface HostDevice { readonly sourceDeviceToken: string - readonly id?: FhirId | undefined + readonly operatingSystemVersion: string readonly name?: string | undefined readonly manufacturer?: string | undefined readonly modelNumber?: string | undefined - readonly operatingSystemVersion: string } -interface RecordingDeviceBaseInput { - /** Stable token for one physical unit; model, manufacturer, or subject are not substitutes. */ +/** One physical recording unit; model, manufacturer, or subject are not substitutes for the token. */ +export interface RecordingDevice { readonly stableUnitToken: string - /** Complete logical subject identity used only in the HMAC preimage. */ - readonly subjectIdentifier: CompleteIdentifierInput - readonly id?: FhirId | undefined readonly name?: string | undefined readonly manufacturer?: string | undefined readonly modelNumber?: string | undefined } -/** Recording-device identity must declare its privacy/disclosure scope. */ -export type RecordingDeviceInput = RecordingDeviceBaseInput & - ( - | { readonly identityScope: 'deployment-scoped' } - | { - readonly identityScope: 'authorized-hardware' - readonly disclosureAuthorization: 'authorized-for-exchange' - } - ) - -/** Explicit evidence for an optional application gateway role. */ -export type GatewayApplicationInput = +/** + * How the converter application relates to the measurement. + * + * Conversion alone makes it the assembler; a gateway mediated or routed the measurement, + * either the converter itself or a distinct application emitted as a second snapshot. + */ +export type ConverterRole = + | { readonly kind: 'assembler' } + | { readonly kind: 'gateway' } | { - readonly kind: 'converter-application' - readonly roleAssurance: 'mediated-or-routed-measurement' + readonly kind: 'gateway-application' + readonly application: ApplicationDevice } + +/** The graph nodes a repository may have assigned a Resource.id to. */ +export type ExchangeGraphNode = + | 'bundle' + | 'primary-output' + | 'source-artifact' + | 'recording-device' + | 'application-device' + | 'host-device' + | 'writer' + | 'provenance' + +/** + * Everything one exchange event needs beyond the source record. + * + * `converterRole` defaults to the assembler, `studies` to none, and `repositoryIds` to + * none; Provenance.recorded and Bundle.timestamp are the conversion instant. + */ +export interface ExchangeEventContext { + readonly subject: Subject + readonly event: ExchangeEventIdentifier + readonly identityScope: OpaqueIdentityScope + readonly repositoryScope: BusinessIdentifier + readonly application: ApplicationDevice + /** Required here: a server states its own facts, where a phone reads them from the OS. */ + readonly host: HostDevice + /** Defaults to the instant the builder runs. */ + readonly conversionInstant?: FhirInstant | undefined + readonly converterRole?: ConverterRole | undefined + readonly studies?: readonly StudyEnrollment[] | undefined + readonly repositoryIds?: + Readonly>> | undefined +} + +/** A context after parsing, with every default applied. */ +export interface ResolvedExchangeEventContext extends ExchangeEventContext { + readonly conversionInstant: FhirInstant + readonly converterRole: ConverterRole + readonly studies: readonly StudyEnrollment[] +} + +export interface GovernedSourceIdentifierTypeCoding { + readonly system: AbsoluteUri + readonly code: string + readonly display?: string | undefined +} + +/** Narrow CodeableConcept surface for a disclosed source-native Identifier.type. */ +export interface GovernedSourceIdentifierType { + readonly coding?: + | readonly [ + GovernedSourceIdentifierTypeCoding, + ...GovernedSourceIdentifierTypeCoding[], + ] + | undefined + readonly text?: string | undefined +} + +/** + * Whether the designated primary output discloses the source-native record identifier in + * clear under a caller-governed key-space URI; it supplements Grove identities and never + * becomes an entry or retraction key. + */ +export type GovernedSourceIdentifierDisclosurePolicy = + | { readonly kind: 'omit' } | { - readonly kind: 'distinct-application' - readonly roleAssurance: 'mediated-or-routed-measurement' - readonly application: ApplicationDeviceInput + readonly kind: 'authorized' + readonly system: AbsoluteUri + readonly type?: GovernedSourceIdentifierType | undefined } -export type RecordingMethod = - 'actively-recorded' | 'automatically-recorded' | 'manual-entry' +/** Every identifier one exchange graph minted, in the roles the graph carries them. */ +export interface ExchangeGraphIdentifiers { + readonly event: ExchangeEventIdentifier + readonly sourceRecord: RoledIdentifier + /** Every output identity in Bundle order; a provider record has no child outputs. */ + readonly outputs: readonly [RoledIdentifier, ...RoledIdentifier[]] + readonly provenance: RoledIdentifier + readonly applicationSnapshot: RoledIdentifier + readonly hostSnapshot: RoledIdentifier + readonly writerSnapshot: RoledIdentifier + readonly sourceArtifact?: RoledIdentifier + readonly recordingDevice?: RoledIdentifier + readonly recordingDeviceSnapshot?: RoledIdentifier + readonly gatewayApplicationSnapshot?: RoledIdentifier + readonly writerRecord?: RoledIdentifier +} + +/** The outcome of converting several records: what converted and what was refused. */ +export interface ConversionBatch { + readonly conversions: readonly Conversion[] + readonly failures: readonly Failure[] +} diff --git a/packages/fhir/src/providers/builder.ts b/packages/fhir/src/providers/builder.ts index f7b0a92e..14f50e48 100644 --- a/packages/fhir/src/providers/builder.ts +++ b/packages/fhir/src/providers/builder.ts @@ -7,25 +7,26 @@ // import { - assemblerAgent, + contextFault, + makeConversionProvenance, + occurredFor, + resolveContextGraph, + type ContextGraph, +} from './context-graph.js' +import { coding, concept, deduplicateIdentifiedEntries, governedSourceIdentifier, identifiedEntry, identifier, - makeApplicationDevice, - makeHostDevice, makeRecordingDevice, - provenanceActivity, resourceId, - sourceEntityAgent, } from './graph.js' import { - deriveApplicationEntryIdentity, + deriveProviderIdentities, deriveRecordingDeviceEntryIdentity, deriveWriterRecordIdentifier, - resolveHostIdentity, type ProviderIdentities, type RecordingDeviceGraphIdentity, } from './identity.js' @@ -36,34 +37,41 @@ import { type MeasurementDefinition, } from './measurement-definition.js' import { EXTENSIONS, PROFILES, SYSTEMS } from './profiles.js' -import { parseProviderMeasurementBundle } from './provider.js' -import type { ProviderMeasurementBundleInput } from './types.js' -import { providerAdapterCatalog } from '../contract/providers.generated.js' +import { governedSourceIdentifierIssues } from './provider-input-schemas.js' import { - collectResults, - issues, - ok, - type Issue, - type Result, - type UrnUuid, -} from '../core/index.js' + parseNormalizedProviderRecord, + parseProviderConversionOptions, + providerOutputCoordinates, + providerSourceKinds, + type ProviderOutputCoordinates, +} from './provider.js' +import type { + NormalizedProviderRecord, + ProviderConversion, + ProviderConversionFailure, + ProviderConversionOptions, +} from './types.js' +import { providerAdapterCatalog } from '../contract/providers.generated.js' +import { collectResults, issues, ok, type Result } from '../core/index.js' import { createEntryIdentity, - validateDeploymentIdentity, - type DeploymentIdentitySource, + type EntryIdentity, + type RoledIdentifier, } from '../mobile/identity.js' import type { - CompleteIdentifierInput, - IdentifiedEntryIdentityInput, + ConversionBatch, + ExchangeEventContext, + ExchangeGraphIdentifiers, + GovernedSourceIdentifierDisclosurePolicy, } from '../mobile/types.js' import { - parseGroveMobileExchangeBundle, + parseExchangeGraph, type CodeableConcept, - type GroveMobileExchangeBundle, + type ExchangeGraph, + type Observation, } from '../r4/index.js' -type ConnectedMeasurement = - ProviderMeasurementBundleInput['measurements'][number] +type ConnectedMeasurement = NormalizedProviderRecord['measurements'][number] const quantity = ( value: number, @@ -80,70 +88,57 @@ const quantity = ( }) interface ResolvedGraphIdentities { - readonly sourceRecord: CompleteIdentifierInput - readonly event: CompleteIdentifierInput - readonly observations: readonly IdentifiedEntryIdentityInput[] - readonly provenance: IdentifiedEntryIdentityInput - readonly application: IdentifiedEntryIdentityInput - readonly applicationHost?: IdentifiedEntryIdentityInput - readonly dataOrigin: IdentifiedEntryIdentityInput - readonly dataOriginHost?: IdentifiedEntryIdentityInput - readonly gatewayReference?: UrnUuid - readonly distinctGatewayApplication?: IdentifiedEntryIdentityInput - readonly distinctGatewayHost?: IdentifiedEntryIdentityInput + readonly connected: ProviderIdentities + readonly observations: readonly [EntryIdentity, ...EntryIdentity[]] + readonly provenance: EntryIdentity readonly recordingDevice?: RecordingDeviceGraphIdentity - readonly writerRecord?: CompleteIdentifierInput + readonly writerRecord?: RoledIdentifier } -type ProviderGraphEntry = GroveMobileExchangeBundle['entry'][number] -type ProviderGraphResource = Parameters[1] +type ProviderGraphEntry = ExchangeGraph['entry'][number] +// A validated record names catalog rows the generator proved exist; a miss is a defect. const definitionFor = ( - input: ProviderMeasurementBundleInput, + record: NormalizedProviderRecord, kind: ConnectedMeasurement['kind'], ): MeasurementDefinition => { const definition = providerMeasurementDefinition( - input.source.adapter.provider, + record.source.adapter.provider, kind, ) if (definition === undefined) { throw new Error( - `Validated Provider input has no definition for ${input.source.adapter.provider}/${kind}.`, + `Validated Provider record has no definition for ${record.source.adapter.provider}/${kind}.`, ) } return definition } -const categoryFor = (definition: MeasurementDefinition) => { - if (definition.category === undefined) return undefined - return [ - concept( - definition.category.system, - definition.category.code, - definition.category.display, - ), - ] -} +const categoryFor = (definition: MeasurementDefinition) => + definition.category === undefined ? + undefined + : [ + concept( + definition.category.system, + definition.category.code, + definition.category.display, + ), + ] const profileFor = ( - input: ProviderMeasurementBundleInput, + record: NormalizedProviderRecord, kind: ConnectedMeasurement['kind'], ): readonly string[] => { const semanticProfile = providerMeasurementProfile( - input.source.adapter.provider, + record.source.adapter.provider, kind, ) - if (semanticProfile === undefined) { - throw new Error( - `Validated Provider input has no semantic profile for ${input.source.adapter.provider}/${kind}.`, - ) - } const adapterProfile = providerObservationProfile( - input.source.adapter.provider, + record.source.adapter.provider, ) - if (adapterProfile === undefined) { + if (semanticProfile === undefined || adapterProfile === undefined) { throw new Error( - `Validated Provider input has no adapter profile for ${input.source.adapter.provider}.`, + `Validated Provider record has no profile for ${record.source.adapter.provider}/${kind}.`, ) } return [semanticProfile, adapterProfile] @@ -171,10 +166,10 @@ for (const provider of providerAdapterCatalog.providers) { /** The aggregation a measurement carries, or `undefined` when the catalog declares none. */ const aggregationMethodFor = ( - input: ProviderMeasurementBundleInput, + record: NormalizedProviderRecord, kind: ConnectedMeasurement['kind'], ): CodeableConcept | undefined => { - const definition = definitionFor(input, kind) + const definition = definitionFor(record, kind) if (definition.method !== undefined) { return concept( SYSTEMS.groveAggregationMethod, @@ -186,24 +181,22 @@ const aggregationMethodFor = ( return undefined } const declared = declaredAggregationMethods.get( - `${input.source.adapter.provider}|${input.source.sourceType}|${kind}`, + `${record.source.adapter.provider}|${record.source.sourceType}|${kind}`, ) return declared === undefined ? undefined : ( concept(SYSTEMS.groveAggregationMethod, declared) ) } -/** - * A measurement whose catalog entry admits more than one aggregation must have one declared for - * this exact provider and source type, otherwise the emitted Observation would silently omit the - * method its profile requires. - */ +// A measurement whose catalog entry admits more than one aggregation must have one declared +// for this exact provider and source type, otherwise the emitted Observation would omit the +// method its profile requires. const missingAggregationMethod = ( - input: ProviderMeasurementBundleInput, + record: NormalizedProviderRecord, kind: ConnectedMeasurement['kind'], ): boolean => - definitionFor(input, kind).methodChoice !== undefined && - aggregationMethodFor(input, kind) === undefined + definitionFor(record, kind).methodChoice !== undefined && + aggregationMethodFor(record, kind) === undefined const effectiveFor = (measurement: ConnectedMeasurement) => measurement.effective.kind === 'date-time' ? @@ -216,11 +209,11 @@ const effectiveFor = (measurement: ConnectedMeasurement) => } const resultFor = ( - input: ProviderMeasurementBundleInput, + record: NormalizedProviderRecord, measurement: ConnectedMeasurement, ) => { if (measurement.kind === 'blood-pressure') { - const definition = definitionFor(input, 'blood-pressure') + const definition = definitionFor(record, 'blood-pressure') const systolic = definition.components?.[0] const diastolic = definition.components?.[1] if (systolic?.quantity === undefined || diastolic?.quantity === undefined) { @@ -242,7 +235,7 @@ const resultFor = ( } } - const definition = definitionFor(input, measurement.kind) + const definition = definitionFor(record, measurement.kind) if (typeof measurement.value === 'string') { const { resultCodeSystem } = definition as { @@ -264,487 +257,397 @@ const resultFor = ( } } -const makeObservation = ( - input: ProviderMeasurementBundleInput, - measurement: ConnectedMeasurement, - observationIdentity: IdentifiedEntryIdentityInput, - identities: ResolvedGraphIdentities, - method: CodeableConcept | undefined, -) => { - const definition = definitionFor(input, measurement.kind) - const category = categoryFor(definition) - const extensions = [ - { - url: EXTENSIONS.provider, - valueCode: input.source.adapter.provider, - }, - { - url: EXTENSIONS.providerSourceType, - valueCode: `${input.source.adapter.provider}/${input.source.sourceType}`, - }, - ...(identities.gatewayReference === undefined ? - [] - : [ - { - url: EXTENSIONS.gatewayDevice, - valueReference: { reference: identities.gatewayReference }, - }, - ]), - ...(input.source.recordingMethod === undefined ? - [] - : [ - { - url: EXTENSIONS.recordingMethod, - valueCoding: coding( - SYSTEMS.groveRecordingMethod, - input.source.recordingMethod, - ), - }, - ]), - ...(input.researchStudyReferences ?? []).map((reference) => ({ - url: EXTENSIONS.researchStudy, - valueReference: { - type: reference.type, - identifier: { - system: reference.identifier.system, - value: reference.identifier.value, - }, - }, - })), - ] +interface ObservationInput { + readonly record: NormalizedProviderRecord + readonly measurement: ConnectedMeasurement + readonly identity: EntryIdentity + readonly identities: ResolvedGraphIdentities + readonly graph: ContextGraph + readonly disclosure: GovernedSourceIdentifierDisclosurePolicy +} +const makeObservation = (input: ObservationInput): Observation => { + const { record, measurement, identity, identities, graph } = input + const definition = definitionFor(record, measurement.kind) + const category = categoryFor(definition) + const method = aggregationMethodFor(record, measurement.kind) return { resourceType: 'Observation' as const, - ...resourceId(observationIdentity), - meta: { profile: profileFor(input, measurement.kind) }, + ...resourceId(identity), + meta: { profile: profileFor(record, measurement.kind) }, extension: [ - ...extensions, - ...(input.source.writerRecord?.version === undefined ? + { + url: EXTENSIONS.provider, + valueCode: record.source.adapter.provider, + }, + { + url: EXTENSIONS.providerSourceType, + valueCode: `${record.source.adapter.provider}/${record.source.sourceType}`, + }, + ...(graph.gatewayReference === undefined ? + [] + : [ + { + url: EXTENSIONS.gatewayDevice, + valueReference: { reference: graph.gatewayReference }, + }, + ]), + ...(record.source.recordingMethod === undefined ? + [] + : [ + { + url: EXTENSIONS.recordingMethod, + valueCoding: coding( + SYSTEMS.groveRecordingMethod, + record.source.recordingMethod, + ), + }, + ]), + ...graph.researchStudyExtensions, + ...(record.source.writerRecord?.version === undefined ? [] : [ { url: EXTENSIONS.writerRecordVersion, - valueString: input.source.writerRecord.version, + valueString: record.source.writerRecord.version, }, ]), ], identifier: [ - identifier(identities.sourceRecord), - identifier(observationIdentity.identifier), + identifier(identities.connected.sourceRecord), + identifier(identity.identifier), ...(identities.writerRecord === undefined ? [] : [identifier(identities.writerRecord)]), - ...(input.nativeIdentifierDisclosure === undefined ? + ...(input.disclosure.kind === 'omit' ? [] - : [governedSourceIdentifier(input.nativeIdentifierDisclosure)]), + : [ + governedSourceIdentifier( + input.disclosure, + record.source.sourceNativeId, + ), + ]), ], status: 'final' as const, ...(category === undefined ? {} : { category }), code: { coding: [coding(definition.code.system, definition.code.code)], }, - subject: { - type: input.subject.type, - identifier: { - system: input.subject.identifier.system, - value: input.subject.identifier.value, - }, - }, + subject: graph.subject, ...effectiveFor(measurement), - ...resultFor(input, measurement), + ...resultFor(record, measurement), ...(method === undefined ? {} : { method }), - ...(input.source.recordingDevice === undefined ? + ...(identities.recordingDevice === undefined ? {} - : { device: { reference: identities.recordingDevice?.snapshot.fullUrl } }), - } -} - -const makeProvenance = ( - input: ProviderMeasurementBundleInput, - identities: ResolvedGraphIdentities, -) => { - return { - resourceType: 'Provenance' as const, - ...resourceId(identities.provenance), - meta: { - profile: [PROFILES.providerConversionProvenance], - }, - target: identities.observations.map(({ fullUrl }) => ({ - reference: fullUrl, - })), - occurredDateTime: input.occurred, - recorded: input.recorded, - activity: provenanceActivity(), - agent: [assemblerAgent(identities.application.fullUrl)], - entity: [ - { - role: 'source' as const, - what: { - identifier: identifier(identities.sourceRecord), - }, - agent: [sourceEntityAgent(identities.dataOrigin.fullUrl)], - }, - ], + : { device: { reference: identities.recordingDevice.snapshot.fullUrl } }), } } -const optionalRecordingDeviceIdentity = ( - input: ProviderMeasurementBundleInput['source']['recordingDevice'], - deployment: DeploymentIdentitySource, - event: CompleteIdentifierInput, - adapterId: string, -): Result => - input === undefined ? - ok(undefined) - : deriveRecordingDeviceEntryIdentity(deployment, event, adapterId, input) - -type ResolvedGatewayIdentity = Pick< - ResolvedGraphIdentities, - 'distinctGatewayApplication' | 'distinctGatewayHost' | 'gatewayReference' -> - -const resolveGatewayIdentity = ( - input: ProviderMeasurementBundleInput['gatewayApplication'], - converter: IdentifiedEntryIdentityInput, - deployment: DeploymentIdentitySource, - event: CompleteIdentifierInput, -): Result => { - if (input === undefined) return ok({}) - if (input.kind === 'converter-application') { - return ok({ gatewayReference: converter.fullUrl }) - } - const application = deriveApplicationEntryIdentity( - deployment, - event, - input.application, +const outputCoordinates = ( + record: NormalizedProviderRecord, +): Result< + readonly [ProviderOutputCoordinates, ...ProviderOutputCoordinates[]] +> => { + const coordinates = record.measurements.map(({ kind }) => + providerOutputCoordinates( + record.source.adapter.provider, + record.source.sourceType, + kind, + ), ) - if (!application.ok) return application - const host = resolveHostIdentity(input.application, deployment, event) - if (!host.ok) return host - return ok({ - gatewayReference: application.value.fullUrl, - distinctGatewayApplication: application.value, - ...(host.value === undefined ? {} : { distinctGatewayHost: host.value }), - }) + const [first, ...rest] = coordinates + if (first === undefined || coordinates.some((entry) => entry === undefined)) { + throw new Error('Validated Provider measurements name no catalog output.') + } + return ok([first, ...(rest as ProviderOutputCoordinates[])]) } -const missingAggregationIssues = ( - input: ProviderMeasurementBundleInput, -): readonly Issue[] => - input.measurements.flatMap((measurement, index) => - missingAggregationMethod(input, measurement.kind) ? - [ - { - severity: 'error' as const, - code: 'unsupported-measurement' as const, - path: ['measurements', index], - message: - `${input.source.adapter.provider}/${input.source.sourceType} declares no aggregation ` + - `for ${measurement.kind}, so its Observation would omit the method its profile requires.`, - }, - ] - : [], +// A disclosure is admitted only where the catalog row designates one one-to-one output. +const disclosureIssues = ( + record: NormalizedProviderRecord, + disclosure: GovernedSourceIdentifierDisclosurePolicy, + graph: ContextGraph, +): Result => { + if (disclosure.kind === 'omit') return ok(undefined) + const scopeIssues = governedSourceIdentifierIssues( + disclosure, + graph.context.identityScope, ) - -// One output identity is derived per measurement, so a length mismatch is a builder defect -// rather than a caller error; it is still reported instead of thrown. -const misalignedIdentities = (path: ReadonlyArray) => - issues([ - { - severity: 'error' as const, - code: 'value-mismatch' as const, - path, - message: - 'Provider output identities do not align with the measurements they were derived from.', - }, - ]) - -const zipMeasurements = ( - input: ProviderMeasurementBundleInput, - values: readonly Value[], -): ReadonlyArray | undefined => { - const pairs = input.measurements.flatMap((measurement, index) => { - const value = values[index] - return value === undefined ? [] : [[measurement, value] as const] - }) - return pairs.length === values.length ? pairs : undefined -} - -const resolveObservationIdentities = ( - input: ProviderMeasurementBundleInput, - outputs: readonly CompleteIdentifierInput[], -): Result => { - const pairs = zipMeasurements(input, outputs) - if (pairs === undefined) return misalignedIdentities(['measurements']) - return collectResults( - pairs.map(([measurement, output]) => - createEntryIdentity( - output, - input.repositoryIds?.observations?.[measurement.kind], - ), - ), + if (scopeIssues.length > 0) return issues(scopeIssues) + const rowKinds = providerSourceKinds( + record.source.adapter.provider, + record.source.sourceType, ) + return rowKinds.length === 1 && record.measurements.length === 1 ? + ok(undefined) + : contextFault( + ['nativeIdentifierDisclosure'], + 'The Provider catalog must designate one unique one-to-one Observation before a governed source Identifier may be disclosed; ambiguous multi-output records must omit it.', + ) } const resolveGraphIdentities = ( - input: ProviderMeasurementBundleInput, - connected: ProviderIdentities, + record: NormalizedProviderRecord, + graph: ContextGraph, ): Result => { - const aggregation = missingAggregationIssues(input) - if (aggregation.length > 0) return issues(aggregation) - const deployment = validateDeploymentIdentity(input.deploymentIdentity) - if (!deployment.ok) return deployment - - const observations = resolveObservationIdentities(input, connected.outputs) + const { context } = graph + const repositoryIds = context.repositoryIds ?? {} + const coordinates = outputCoordinates(record) + if (!coordinates.ok) return coordinates + const connected = deriveProviderIdentities({ + provider: record.source.adapter.provider, + repositoryScope: context.repositoryScope, + sourceType: record.source.sourceType, + sourceNativeId: record.source.sourceNativeId, + outputs: coordinates.value.map((output) => ({ + kind: 'provider-output' as const, + ...output, + })), + event: context.event, + scope: context.identityScope, + }) + if (!connected.ok) return connected + if ( + repositoryIds['primary-output'] !== undefined && + connected.value.outputs.length !== 1 + ) { + return contextFault( + ['repositoryIds', 'primary-output'], + 'A primary-output repository id names the sole output of a one-output record.', + ) + } + const observations = collectResults( + connected.value.outputs.map((output) => + createEntryIdentity(output, repositoryIds['primary-output']), + ), + ) if (!observations.ok) return observations + const [firstObservation, ...otherObservations] = observations.value + if (firstObservation === undefined) { + throw new Error('Validated Provider record derived no output identity.') + } const provenance = createEntryIdentity( - connected.provenanceNode, - input.repositoryIds?.provenance, + connected.value.provenanceNode, + repositoryIds.provenance, ) if (!provenance.ok) return provenance - const application = deriveApplicationEntryIdentity( - deployment.value, - connected.event, - input.application, - ) - if (!application.ok) return application - const applicationHost = resolveHostIdentity( - input.application, - deployment.value, - connected.event, - ) - if (!applicationHost.ok) return applicationHost - const gateway = resolveGatewayIdentity( - input.gatewayApplication, - application.value, - deployment.value, - connected.event, - ) - if (!gateway.ok) return gateway - const dataOrigin = deriveApplicationEntryIdentity( - deployment.value, - connected.event, - input.source.dataOrigin, - ) - if (!dataOrigin.ok) return dataOrigin - const dataOriginHost = resolveHostIdentity( - input.source.dataOrigin, - deployment.value, - connected.event, - ) - if (!dataOriginHost.ok) return dataOriginHost - - const recordingDevice = optionalRecordingDeviceIdentity( - input.source.recordingDevice, - deployment.value, - connected.event, - input.source.adapter.provider, - ) + const recordingDevice = + record.source.recordingDevice === undefined ? + ok(undefined) + : deriveRecordingDeviceEntryIdentity( + context.identityScope, + context.event, + record.source.adapter.provider, + context.subject.identifier, + record.source.recordingDevice, + repositoryIds['recording-device'], + ) if (!recordingDevice.ok) return recordingDevice - const writerRecord = - input.source.writerRecord === undefined ? - undefined + record.source.writerRecord === undefined ? + ok(undefined) : deriveWriterRecordIdentifier( - deployment.value, - input.source.writerRecord.applicationIdentifier, - input.source.writerRecord.nativeRecordId, + context.identityScope, + record.source.writerRecord, ) - if (writerRecord !== undefined && !writerRecord.ok) return writerRecord - - const resolved: ResolvedGraphIdentities = { - sourceRecord: connected.sourceRecord, - event: connected.event, - observations: observations.value, + if (!writerRecord.ok) return writerRecord + return ok({ + connected: connected.value, + observations: [firstObservation, ...otherObservations], provenance: provenance.value, - application: application.value, - dataOrigin: dataOrigin.value, - ...(applicationHost.value === undefined ? - {} - : { applicationHost: applicationHost.value }), - ...(dataOriginHost.value === undefined ? - {} - : { dataOriginHost: dataOriginHost.value }), - ...gateway.value, - ...(writerRecord === undefined ? {} : { writerRecord: writerRecord.value }), ...(recordingDevice.value === undefined ? {} : { recordingDevice: recordingDevice.value }), - } - return { ok: true, value: resolved } + ...(writerRecord.value === undefined ? + {} + : { writerRecord: writerRecord.value }), + }) } -const buildProviderObservationEntries = ( - input: ProviderMeasurementBundleInput, +const graphIdentifiers = ( identities: ResolvedGraphIdentities, -): Result => { - const pairs = zipMeasurements(input, identities.observations) - if (pairs === undefined) return misalignedIdentities(['measurements']) - return ok( - pairs.map(([measurement, identity]) => - identifiedEntry( - identity, - makeObservation( - input, - measurement, - identity, - identities, - aggregationMethodFor(input, measurement.kind), - ), - ), - ), - ) + graph: ContextGraph, +): ExchangeGraphIdentifiers => { + const [first, ...rest] = identities.connected.outputs + if (first === undefined) { + throw new Error('Validated Provider record derived no output identity.') + } + return { + event: identities.connected.event, + sourceRecord: identities.connected.sourceRecord, + outputs: [first, ...rest], + provenance: identities.provenance.identifier, + applicationSnapshot: graph.application.identifier, + hostSnapshot: graph.host.identifier, + writerSnapshot: graph.writer.identifier, + ...(identities.recordingDevice === undefined ? + {} + : { + recordingDevice: identities.recordingDevice.stableIdentifier, + recordingDeviceSnapshot: identities.recordingDevice.snapshot.identifier, + }), + ...(graph.gatewayApplication === undefined ? + {} + : { gatewayApplicationSnapshot: graph.gatewayApplication.identifier }), + ...(identities.writerRecord === undefined ? + {} + : { writerRecord: identities.writerRecord }), + } } -const optionalIdentifiedEntry = ( - identity: IdentifiedEntryIdentityInput | undefined, - resource: ProviderGraphResource | undefined, -): readonly ProviderGraphEntry[] => - identity === undefined || resource === undefined ? - [] - : [identifiedEntry(identity, resource)] - -const buildProviderSupportingEntries = ( - input: ProviderMeasurementBundleInput, +const buildEntries = ( + record: NormalizedProviderRecord, identities: ResolvedGraphIdentities, -): readonly ProviderGraphEntry[] => { - const applicationHostInput = input.application.host - const applicationHost = - ( - applicationHostInput === undefined || - identities.applicationHost === undefined - ) ? - undefined - : makeHostDevice({ - ...applicationHostInput, - identity: identities.applicationHost, - }) - const application = makeApplicationDevice({ - ...input.application, - identity: identities.application, - ...(identities.applicationHost === undefined ? - {} - : { parentReference: identities.applicationHost.fullUrl }), + graph: ContextGraph, + disclosure: GovernedSourceIdentifierDisclosurePolicy, +): Result => { + const observations = record.measurements.map((measurement, index) => { + const identity = identities.observations[index] + if (identity === undefined) { + throw new Error( + 'Provider output identities do not align with the measurements they were derived from.', + ) + } + return identifiedEntry( + identity, + makeObservation({ + record, + measurement, + identity, + identities, + graph, + disclosure, + }), + ) + }) + const [firstMeasurement, ...otherMeasurements] = record.measurements + const provenance = makeConversionProvenance({ + identity: identities.provenance, + profile: PROFILES.providerConversionProvenance, + targets: identities.observations.map(({ fullUrl }) => fullUrl), + occurred: occurredFor([ + firstMeasurement.effective, + ...otherMeasurements.map(({ effective }) => effective), + ]), + recorded: graph.context.conversionInstant, + sourceRecord: identities.connected.sourceRecord, + graph, }) - const gatewayApplicationInput = - input.gatewayApplication?.kind === 'distinct-application' ? - input.gatewayApplication.application - : undefined - const gatewayApplication = - ( - gatewayApplicationInput === undefined || - identities.distinctGatewayApplication === undefined - ) ? - undefined - : makeApplicationDevice({ - ...gatewayApplicationInput, - identity: identities.distinctGatewayApplication, - ...(identities.distinctGatewayHost === undefined ? - {} - : { parentReference: identities.distinctGatewayHost.fullUrl }), - }) - const gatewayHostInput = gatewayApplicationInput?.host - const gatewayHost = - ( - gatewayHostInput === undefined || - identities.distinctGatewayHost === undefined - ) ? - undefined - : makeHostDevice({ - ...gatewayHostInput, - identity: identities.distinctGatewayHost, - }) - const recordingDeviceInput = input.source.recordingDevice const recordingDevice = ( - recordingDeviceInput === undefined || + record.source.recordingDevice === undefined || identities.recordingDevice === undefined ) ? - undefined - : makeRecordingDevice({ - ...recordingDeviceInput, - identity: identities.recordingDevice.snapshot, - stableIdentifier: identities.recordingDevice.stableIdentifier, - }) - const dataOriginHostInput = input.source.dataOrigin.host - const dataOriginHost = - ( - dataOriginHostInput === undefined || - identities.dataOriginHost === undefined - ) ? - undefined - : makeHostDevice({ - ...dataOriginHostInput, - identity: identities.dataOriginHost, - }) - const dataOrigin = makeApplicationDevice({ - ...input.source.dataOrigin, - identity: identities.dataOrigin, - ...(identities.dataOriginHost === undefined ? - {} - : { parentReference: identities.dataOriginHost.fullUrl }), - }) - const provenance = makeProvenance(input, identities) - return [ - ...optionalIdentifiedEntry( - identities.recordingDevice?.snapshot, - recordingDevice, - ), - ...optionalIdentifiedEntry(identities.dataOriginHost, dataOriginHost), - identifiedEntry(identities.dataOrigin, dataOrigin), - ...optionalIdentifiedEntry(identities.distinctGatewayHost, gatewayHost), - ...optionalIdentifiedEntry( - identities.distinctGatewayApplication, - gatewayApplication, - ), - ...optionalIdentifiedEntry(identities.applicationHost, applicationHost), - identifiedEntry(identities.application, application), - identifiedEntry(identities.provenance, provenance), - ] -} - -const buildProviderGraphEntries = ( - input: ProviderMeasurementBundleInput, - identities: ResolvedGraphIdentities, -): Result => { - const observations = buildProviderObservationEntries(input, identities) - if (!observations.ok) return observations + [] + : [ + identifiedEntry( + identities.recordingDevice.snapshot, + makeRecordingDevice({ + ...record.source.recordingDevice, + identity: identities.recordingDevice.snapshot, + stableIdentifier: identities.recordingDevice.stableIdentifier, + }), + ), + ] return deduplicateIdentifiedEntries([ - ...observations.value, - ...buildProviderSupportingEntries(input, identities), + ...graph.leadingEntries, + ...observations, + ...recordingDevice, + ...graph.supportingEntries, + identifiedEntry(identities.provenance, provenance), ]) } /** - * Builds one deterministic, profile-stamped R4 resource graph for the non-empty - * subset of catalog-admitted outputs present in a connected-provider source - * record. Every output shares one conversion Provenance. Native ids are digest - * input by default and may appear only through the explicit governed disclosure - * policy. Times, repository ids, and the durable event sequence remain - * caller-owned; malformed inputs return structured issues and no partial graph. + * Converts one normalized provider record into its exchange graph. + * + * The context needs the subject, the event this scope minted, the identity scope, the + * repository scope, the application and the host; the conversion instant defaults to now, + * the converter role to the assembler, and the studies to none. A refused record returns + * `mobile-input.*` issues; a context or option fault returns the package's schema codes. + * Every output shares one conversion Provenance whose occurred time spans the outputs' + * effective times and whose recorded time is the conversion instant. A catalog + * inconsistency the generator should have caught throws, because no input can cause it. */ -export const buildProviderMeasurementBundle = ( - input: ProviderMeasurementBundleInput, -): Result => { - const parsed = parseProviderMeasurementBundle(input) - if (!parsed.ok) return parsed - const { input: validatedInput, identities: connected } = parsed.value - - const identities = resolveGraphIdentities(validatedInput, connected) +export const buildProviderExchangeGraph = ( + record: NormalizedProviderRecord, + context: ExchangeEventContext, + options: ProviderConversionOptions = {}, +): Result => { + const parsedRecord = parseNormalizedProviderRecord(record) + if (!parsedRecord.ok) return parsedRecord + const parsedOptions = parseProviderConversionOptions(options) + if (!parsedOptions.ok) return parsedOptions + const graph = resolveContextGraph( + context, + parsedRecord.value.source.writer, + true, + ) + if (!graph.ok) return graph + const disclosure = parsedOptions.value.nativeIdentifierDisclosure ?? { + kind: 'omit', + } + const admitted = disclosureIssues(parsedRecord.value, disclosure, graph.value) + if (!admitted.ok) return admitted + const aggregation = parsedRecord.value.measurements.flatMap( + (measurement, index) => + missingAggregationMethod(parsedRecord.value, measurement.kind) ? + [ + { + severity: 'error' as const, + code: 'mobile-input.unsupported-source-value' as const, + path: ['measurements', index], + message: `${parsedRecord.value.source.adapter.provider}/${parsedRecord.value.source.sourceType} declares no aggregation for ${measurement.kind}, so its Observation would omit the method its profile requires.`, + }, + ] + : [], + ) + if (aggregation.length > 0) return issues(aggregation) + const identities = resolveGraphIdentities(parsedRecord.value, graph.value) if (!identities.ok) return identities - - const entry = buildProviderGraphEntries(validatedInput, identities.value) - if (!entry.ok) return entry - - return parseGroveMobileExchangeBundle({ + const entries = buildEntries( + parsedRecord.value, + identities.value, + graph.value, + disclosure, + ) + if (!entries.ok) return entries + const { repositoryIds = {} } = graph.value.context + const parsed = parseExchangeGraph({ resourceType: 'Bundle', - ...(validatedInput.repositoryIds?.bundle === undefined ? - {} - : { id: validatedInput.repositoryIds.bundle }), + ...(repositoryIds.bundle === undefined ? {} : { id: repositoryIds.bundle }), meta: { profile: [PROFILES.mobileBundle] }, - identifier: identifier(identities.value.event), + identifier: identifier(identities.value.connected.event), type: 'collection', - timestamp: validatedInput.assembled, - entry: entry.value, + timestamp: graph.value.context.conversionInstant, + entry: entries.value, + }) + if (!parsed.ok) return parsed + return ok({ + source: parsedRecord.value.source, + identifiers: graphIdentifiers(identities.value, graph.value), + graph: parsed.value, + warnings: [], }) } + +/** + * Converts several records, each under the context its callback reserves for it. + * + * A refused record becomes a failure with its registry codes; the callback's own errors + * propagate, because a failed reservation is the caller's exception and never a refusal. + */ +export const buildProviderExchangeGraphs = ( + records: readonly NormalizedProviderRecord[], + context: (record: NormalizedProviderRecord) => ExchangeEventContext, + options: ProviderConversionOptions = {}, +): ConversionBatch => { + const conversions: ProviderConversion[] = [] + const failures: ProviderConversionFailure[] = [] + for (const record of records) { + const result = buildProviderExchangeGraph(record, context(record), options) + if (result.ok) conversions.push(result.value) + else failures.push({ record, issues: result.issues }) + } + return { conversions, failures } +} diff --git a/packages/fhir/src/providers/context-graph.ts b/packages/fhir/src/providers/context-graph.ts new file mode 100644 index 00000000..8f9b44c8 --- /dev/null +++ b/packages/fhir/src/providers/context-graph.ts @@ -0,0 +1,398 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + assemblerAgent, + identifiedEntry, + identifier, + makeApplicationDevice, + makeHostDevice, + makePlanDefinition, + makeResearchStudy, + makeResearchSubject, + provenanceActivity, + researchStudyExtension, + resourceId, + sourceEntityAgent, + subjectReference, + type StudyEntries, +} from './graph.js' +import { + deriveApplicationEntryIdentity, + deriveEntryNodeEntryIdentity, + deriveHostEntryIdentity, +} from './identity.js' +import { + err, + issues, + ok, + zodIssuePath, + type FhirInstant, + type Issue, + type Result, +} from '../core/index.js' +import { compareFhirDateTimes } from '../core/primitives.js' +import { parseExchangeEventContext } from '../mobile/context.js' +import type { + EntryIdentity, + ExchangeEventIdentifier, + RoledIdentifier, +} from '../mobile/identity.js' +import type { + ApplicationDevice, + ExchangeEventContext, + InstantEffectiveTime, + PeriodEffectiveTime, + ResolvedExchangeEventContext, +} from '../mobile/types.js' +import type { + ExchangeGraph, + Extension, + Provenance, + Reference, +} from '../r4/index.js' +import { patientSchema } from '../zod/r4/index.js' + +type GraphEntry = ExchangeGraph['entry'][number] + +/** The context-owned part of one graph: subject, studies and the converter's Device snapshots. */ +export interface ContextGraph { + readonly context: ResolvedExchangeEventContext + readonly subject: Reference + readonly studies: readonly StudyEntries[] + readonly researchStudyExtensions: readonly Extension[] + readonly application: EntryIdentity + readonly host: EntryIdentity + readonly writer: EntryIdentity + readonly gatewayApplication: EntryIdentity | undefined + readonly gatewayReference: string | undefined + /** The bundled Patient, first in Bundle order. */ + readonly leadingEntries: readonly GraphEntry[] + /** Studies, writer, gateway, host and application, after the outputs. */ + readonly supportingEntries: readonly GraphEntry[] +} + +const bundledPatientIssues = ( + context: ExchangeEventContext, +): readonly Issue[] => { + if (context.subject.kind !== 'bundled') return [] + const parsed = patientSchema.safeParse(context.subject.patient) + return parsed.success ? + [] + : parsed.error.issues.map((issue) => ({ + severity: 'error' as const, + code: 'schema-invalid' as const, + path: ['subject', 'patient', ...zodIssuePath(issue)], + message: issue.message, + })) +} + +const studyEntries = ( + context: ExchangeEventContext, + event: ExchangeEventIdentifier, +): Result => { + const entries: StudyEntries[] = [] + for (const [index] of (context.studies ?? []).entries()) { + const study = deriveEntryNodeEntryIdentity( + context.identityScope, + event, + 'research-study', + index, + ) + if (!study.ok) return study + const protocol = deriveEntryNodeEntryIdentity( + context.identityScope, + event, + 'plan-definition', + index, + ) + if (!protocol.ok) return protocol + const enrollment = deriveEntryNodeEntryIdentity( + context.identityScope, + event, + 'research-subject', + index, + ) + if (!enrollment.ok) return enrollment + entries.push({ + study: study.value, + protocol: protocol.value, + enrollment: enrollment.value, + }) + } + return ok(entries) +} + +const sameApplication = ( + left: ApplicationDevice, + right: ApplicationDevice, +): boolean => + left.sourceDeviceToken === right.sourceDeviceToken && + left.name === right.name && + left.version === right.version && + left.build === right.build + +// The writer may be the converter itself; the same token then names one snapshot, +// and differing facts under one token are a collision, not a second Device. +const writerEntries = ( + writer: ApplicationDevice, + author: EntryIdentity, + converters: ReadonlyArray, +): Result => { + const shared = converters.find( + ([, identity]) => identity.fullUrl === author.fullUrl, + ) + if (shared === undefined) { + return ok([ + identifiedEntry( + author, + makeApplicationDevice({ ...writer, identity: author }), + ), + ]) + } + return sameApplication(shared[0], writer) ? + ok([]) + : err( + 'duplicate-identifier', + 'The writer reuses a converter device token with different facts.', + ['source', 'writer'], + ) +} + +const gatewayIdentity = ( + context: ExchangeEventContext, + application: EntryIdentity, +): Result<{ + readonly gatewayApplication: EntryIdentity | undefined + readonly gatewayReference: string | undefined +}> => { + const role = context.converterRole ?? { kind: 'assembler' } + if (role.kind === 'assembler') { + return ok({ gatewayApplication: undefined, gatewayReference: undefined }) + } + if (role.kind === 'gateway') { + return ok({ + gatewayApplication: undefined, + gatewayReference: application.fullUrl, + }) + } + const distinct = deriveApplicationEntryIdentity( + context.identityScope, + context.event, + role.application, + ) + if (!distinct.ok) return distinct + return ok({ + gatewayApplication: distinct.value, + gatewayReference: distinct.value.fullUrl, + }) +} + +/** + * Validates the context and derives everything the graph takes from it. + * + * `admitsStudies` is false for a graph whose output cannot carry the research-study + * extension; such a context must not name studies, because dropping them would silently + * lose the attribution the deployment asked for. + */ +export const resolveContextGraph = ( + context: ExchangeEventContext, + writer: ApplicationDevice, + admitsStudies: boolean, +): Result => { + const parsed = parseExchangeEventContext(context) + if (!parsed.ok) return parsed + const validated = parsed.value + const findings = [...bundledPatientIssues(validated)] + if (!admitsStudies && validated.studies.length > 0) { + findings.push({ + severity: 'error', + code: 'value-mismatch', + path: ['studies'], + message: + 'This graph has no output that carries the research-study extension, so its context names no studies.', + }) + } + if (findings.length > 0) return issues(findings) + + const { identityScope: scope, event, repositoryIds = {} } = validated + const patient = + validated.subject.kind === 'bundled' ? + deriveEntryNodeEntryIdentity(scope, event, 'patient', 0) + : ok(undefined) + if (!patient.ok) return patient + const studies = studyEntries(validated, event) + if (!studies.ok) return studies + const application = deriveApplicationEntryIdentity( + scope, + event, + validated.application, + repositoryIds['application-device'], + ) + if (!application.ok) return application + const host = deriveHostEntryIdentity( + scope, + event, + validated.host, + repositoryIds['host-device'], + ) + if (!host.ok) return host + const author = deriveApplicationEntryIdentity( + scope, + event, + writer, + repositoryIds.writer, + ) + if (!author.ok) return author + const gateway = gatewayIdentity(validated, application.value) + if (!gateway.ok) return gateway + + const subject = subjectReference(validated.subject, patient.value) + const gatewayRole = validated.converterRole + const authorEntries = writerEntries(writer, author.value, [ + [validated.application, application.value], + ...(( + gatewayRole.kind === 'gateway-application' && + gateway.value.gatewayApplication !== undefined + ) ? + [[gatewayRole.application, gateway.value.gatewayApplication] as const] + : []), + ]) + if (!authorEntries.ok) return authorEntries + const supportingEntries: GraphEntry[] = [ + ...validated.studies.flatMap((enrollment, index) => { + const entries = studies.value[index] + if (entries === undefined) return [] + return [ + identifiedEntry(entries.study, makeResearchStudy(enrollment, entries)), + identifiedEntry( + entries.protocol, + makePlanDefinition(enrollment, entries.protocol), + ), + identifiedEntry( + entries.enrollment, + makeResearchSubject(enrollment, entries, subject), + ), + ] + }), + ...authorEntries.value, + ...(( + gatewayRole.kind === 'gateway-application' && + gateway.value.gatewayApplication !== undefined + ) ? + [ + identifiedEntry( + gateway.value.gatewayApplication, + makeApplicationDevice({ + ...gatewayRole.application, + identity: gateway.value.gatewayApplication, + }), + ), + ] + : []), + identifiedEntry( + host.value, + makeHostDevice({ ...validated.host, identity: host.value }), + ), + identifiedEntry( + application.value, + makeApplicationDevice({ + ...validated.application, + identity: application.value, + parentReference: host.value.fullUrl, + }), + ), + ] + return ok({ + context: validated, + subject, + studies: studies.value, + researchStudyExtensions: studies.value.map(researchStudyExtension), + application: application.value, + host: host.value, + writer: author.value, + gatewayApplication: gateway.value.gatewayApplication, + gatewayReference: gateway.value.gatewayReference, + leadingEntries: + validated.subject.kind === 'bundled' && patient.value !== undefined ? + [identifiedEntry(patient.value, validated.subject.patient)] + : [], + supportingEntries, + }) +} + +type EffectiveTime = InstantEffectiveTime | PeriodEffectiveTime + +const boundsOf = ( + effective: EffectiveTime, +): readonly [FhirInstant, FhirInstant] => + effective.kind === 'date-time' ? + [effective.value, effective.value] + : [effective.start, effective.end] + +const earlier = (left: FhirInstant, right: FhirInstant): FhirInstant => + compareFhirDateTimes(left, right) === 1 ? right : left +const later = (left: FhirInstant, right: FhirInstant): FhirInstant => + compareFhirDateTimes(left, right) === -1 ? right : left + +/** Provenance.occurred[x] as the span of the source activity the outputs describe. */ +export const occurredFor = ( + effectives: readonly [EffectiveTime, ...EffectiveTime[]], +): Pick => { + const [first, ...rest] = effectives + if (rest.length === 0 && first.kind === 'date-time') { + return { occurredDateTime: first.value } + } + const [start, end] = rest + .map(boundsOf) + .reduce( + ([earliest, latest], [nextStart, nextEnd]) => [ + earlier(earliest, nextStart), + later(latest, nextEnd), + ], + boundsOf(first), + ) + return start === end ? + { occurredDateTime: start } + : { occurredPeriod: { start, end } } +} + +export interface ConversionProvenanceInput { + readonly identity: EntryIdentity + readonly profile: string + readonly targets: readonly string[] + readonly occurred: Pick + readonly recorded: FhirInstant + readonly sourceRecord: RoledIdentifier + readonly graph: ContextGraph +} + +export const makeConversionProvenance = ( + input: ConversionProvenanceInput, +): Provenance => ({ + resourceType: 'Provenance' as const, + ...resourceId(input.identity), + meta: { profile: [input.profile] }, + target: input.targets.map((reference) => ({ reference })), + ...input.occurred, + recorded: input.recorded, + activity: provenanceActivity(), + agent: [assemblerAgent(input.graph.application.fullUrl)], + entity: [ + { + role: 'source' as const, + what: { identifier: identifier(input.sourceRecord) }, + agent: [sourceEntityAgent(input.graph.writer.fullUrl)], + }, + ], +}) + +export const contextFault = ( + path: ReadonlyArray, + message: string, +): Result => err('value-mismatch', message, path) diff --git a/packages/fhir/src/providers/graph.ts b/packages/fhir/src/providers/graph.ts index 2dc8b752..2a58b65a 100644 --- a/packages/fhir/src/providers/graph.ts +++ b/packages/fhir/src/providers/graph.ts @@ -7,42 +7,50 @@ // import { EXTENSIONS, PROFILES, SYSTEMS } from './profiles.js' -import type { GovernedSourceIdentifierInput } from './types.js' import { err, ok, type Result } from '../core/index.js' import type { - ApplicationDeviceInput, - CompleteIdentifierInput, - HostDeviceInput, - IdentifiedEntryIdentityInput, - RecordingDeviceInput, - ResourceIdentityInput, + BusinessIdentifier, + EntryIdentity, + RoledIdentifier, +} from '../mobile/identity.js' +import type { + ApplicationDevice, + GovernedSourceIdentifierDisclosurePolicy, + HostDevice, + RecordingDevice, + StudyEnrollment, + Subject, } from '../mobile/types.js' import type { CodeableConcept, Coding, Device, + ExchangeGraph, GraphResource, - GroveMobileExchangeBundle, Identifier, + PlanDefinition, Provenance, + Reference, + ResearchStudy, + ResearchSubject, } from '../r4/index.js' -type GraphEntry = GroveMobileExchangeBundle['entry'][number] +type GraphEntry = ExchangeGraph['entry'][number] type ProvenanceAgent = Provenance['agent'][number] export const resourceId = ( - identity: ResourceIdentityInput, + identity: EntryIdentity, ): { readonly id?: string } => identity.id === undefined ? {} : { id: identity.id } -export const identifier = (input: CompleteIdentifierInput): Identifier => ({ +export const identifier = ( + input: BusinessIdentifier | RoledIdentifier, +): Identifier => ({ system: input.system, value: input.value, - ...(input.role === undefined ? - {} - : { - type: concept(SYSTEMS.groveIdentifierRole, input.role), - }), + ...('role' in input ? + { type: concept(SYSTEMS.groveIdentifierRole, input.role) } + : {}), }) /** @@ -50,24 +58,28 @@ export const identifier = (input: CompleteIdentifierInput): Identifier => ({ * It deliberately has no Grove graph-role coding and never participates in identity. */ export const governedSourceIdentifier = ( - input: GovernedSourceIdentifierInput, + policy: Extract< + GovernedSourceIdentifierDisclosurePolicy, + { kind: 'authorized' } + >, + nativeId: string, ): Identifier => ({ - system: input.system, - value: input.nativeId, - ...(input.type === undefined ? + system: policy.system, + value: nativeId, + ...(policy.type === undefined ? {} : { type: { - ...(input.type.coding === undefined ? + ...(policy.type.coding === undefined ? {} : { - coding: input.type.coding.map(({ system, code, display }) => ({ + coding: policy.type.coding.map(({ system, code, display }) => ({ system, code, ...(display === undefined ? {} : { display }), })), }), - ...(input.type.text === undefined ? {} : { text: input.type.text }), + ...(policy.type.text === undefined ? {} : { text: policy.type.text }), }, }), }) @@ -91,9 +103,24 @@ export const concept = ( ...(display === undefined ? {} : { text: display }), }) +/** The literal reference to a bundled Patient entry, or the identifier-only logical one. */ +export const subjectReference = ( + subject: Subject, + patient: EntryIdentity | undefined, +): Reference => + patient === undefined ? + { + type: 'Patient', + identifier: { + system: subject.identifier.system, + value: subject.identifier.value, + }, + } + : { reference: patient.fullUrl } + export const makeApplicationDevice = ( - input: Omit & { - readonly identity: IdentifiedEntryIdentityInput + input: ApplicationDevice & { + readonly identity: EntryIdentity readonly parentReference?: string }, ): Device => { @@ -137,9 +164,6 @@ export const makeApplicationDevice = ( meta: { profile: [PROFILES.applicationDevice] }, identifier: [identifier(input.identity.identifier)], status: 'active' as const, - ...(input.manufacturer === undefined ? - {} - : { manufacturer: input.manufacturer }), deviceName: [{ name: input.name, type: 'user-friendly-name' as const }], ...(versions.length === 0 ? {} : { version: versions }), ...(input.parentReference === undefined ? @@ -149,9 +173,7 @@ export const makeApplicationDevice = ( } export const makeHostDevice = ( - input: Omit & { - readonly identity: IdentifiedEntryIdentityInput - }, + input: HostDevice & { readonly identity: EntryIdentity }, ): Device => ({ resourceType: 'Device' as const, ...resourceId(input.identity), @@ -182,12 +204,9 @@ export const makeHostDevice = ( }) export const makeRecordingDevice = ( - input: Omit< - RecordingDeviceInput, - 'id' | 'stableUnitToken' | 'subjectIdentifier' - > & { - readonly identity: IdentifiedEntryIdentityInput - readonly stableIdentifier: CompleteIdentifierInput + input: RecordingDevice & { + readonly identity: EntryIdentity + readonly stableIdentifier: RoledIdentifier }, ): Device => ({ resourceType: 'Device' as const, @@ -211,8 +230,57 @@ export const makeRecordingDevice = ( : { modelNumber: input.modelNumber }), }) +/** The three entries one study enrollment adds, keyed by the catalog's node roles. */ +export interface StudyEntries { + readonly study: EntryIdentity + readonly protocol: EntryIdentity + readonly enrollment: EntryIdentity +} + +export const makePlanDefinition = ( + enrollment: StudyEnrollment, + identity: EntryIdentity, +): PlanDefinition => ({ + resourceType: 'PlanDefinition' as const, + ...resourceId(identity), + url: enrollment.protocol.url, + version: enrollment.protocol.version, + status: 'active' as const, +}) + +export const makeResearchStudy = ( + enrollment: StudyEnrollment, + entries: StudyEntries, +): ResearchStudy => ({ + resourceType: 'ResearchStudy' as const, + ...resourceId(entries.study), + identifier: [identifier(enrollment.study)], + status: 'active' as const, + protocol: [{ reference: entries.protocol.fullUrl }], +}) + +export const makeResearchSubject = ( + enrollment: StudyEnrollment, + entries: StudyEntries, + individual: Reference, +): ResearchSubject => ({ + resourceType: 'ResearchSubject' as const, + ...resourceId(entries.enrollment), + identifier: [identifier(enrollment.enrollment)], + status: 'on-study' as const, + study: { reference: entries.study.fullUrl }, + individual, +}) + +export const researchStudyExtension = ( + entries: StudyEntries, +): { readonly url: string; readonly valueReference: Reference } => ({ + url: EXTENSIONS.researchStudy, + valueReference: { reference: entries.study.fullUrl }, +}) + export const identifiedEntry = ( - identity: IdentifiedEntryIdentityInput, + identity: EntryIdentity, resource: GraphResource, ): GraphEntry => ({ fullUrl: identity.fullUrl, diff --git a/packages/fhir/src/providers/identity.ts b/packages/fhir/src/providers/identity.ts index 6053e916..bf1902f9 100644 --- a/packages/fhir/src/providers/identity.ts +++ b/packages/fhir/src/providers/identity.ts @@ -6,11 +6,11 @@ // SPDX-License-Identifier: MIT // -import type { - ConnectedProvider, - ProviderScopeIdentifierInput, -} from './types.js' -import { groveProfileClaims } from '../contract/measurement-catalog.generated.js' +import type { ConnectedProvider, WriterRecord } from './types.js' +import { + groveExchangeProtocol, + groveProfileClaims, +} from '../contract/measurement-catalog.generated.js' import { providerAdapterCatalog, providerRawOutputDiscriminators, @@ -19,204 +19,225 @@ import { providerScalarOutputRoles, } from '../contract/providers.generated.js' import { - cloneJsonValue, deepFreeze, err, + issues, ok, parseAbsoluteUri, + type EntryNodeOrdinal, type FhirId, - type JsonValue, + type Issue, type Result, } from '../core/index.js' -import { groveMobileContract } from '../mobile/contract.js' import { + containsIsolatedSurrogate, createEntryIdentity, deriveEntryNodeIdentifier, - deriveEventIdentifier, deriveOpaqueIdentifier, - isEventIdentityValue, - validateDeploymentIdentity, - type DeploymentIdentitySource, + isEventOfScope, + isOpaqueIdentityScope, + type BusinessIdentifier, + type EntryIdentity, + type EntryNodeIdentifier, + type ExchangeEventIdentifier, + type OpaqueIdentityComponents, + type OpaqueIdentityKind, + type OpaqueIdentityScope, + type RoledIdentifier, } from '../mobile/identity.js' import type { - ApplicationDeviceInput, - CompleteIdentifierInput, - HostDeviceInput, - DeploymentIdentityInput, - IdentifiedEntryIdentityInput, - RecordingDeviceInput, + ApplicationDevice, + HostDevice, + RecordingDevice, } from '../mobile/types.js' +import { groveRuleIssue } from '../r4/diagnostics.js' /** Resource kind only; graph participation roles never partition Device identity. */ export type DeviceSnapshotRole = 'application' | 'host' | 'recording-device' -const deviceSnapshotRoles: ReadonlySet = new Set([ - 'application', - 'host', - 'recording-device', +const PROVIDER_CODES: ReadonlySet = new Set( + providerAdapterCatalog.providers.map(({ id }) => id), +) +// A kind's coordinate family is written in its components: the first names the adapter +// space, and only source-record coordinates carry a native record id. +const identityKindsCoordinatedBy = ( + adapterComponent: string, +): ReadonlySet => + new Set( + groveExchangeProtocol.opaqueIdentity.identityKinds + .filter( + ({ components }) => + components[0] === adapterComponent && + (components as readonly string[]).includes('native-record-id'), + ) + .map(({ kind }) => kind), + ) +const PROVIDER_IDENTITY_KINDS = identityKindsCoordinatedBy('provider-code') +const GENERIC_SOURCE_IDENTITY_KINDS = identityKindsCoordinatedBy('adapter-id') + +const recordingDeviceAdapters: ReadonlySet = new Set([ + ...groveProfileClaims.adapterConversionProvenanceClaims.map( + ({ adapter }) => adapter, + ), + ...PROVIDER_CODES, ]) -type JsonObject = Readonly> +const isNonBlank = (value: unknown): value is string => + typeof value === 'string' && + value.trim() !== '' && + !containsIsolatedSurrogate(value) -const asJsonObject = (value: JsonValue | undefined): JsonObject | undefined => - typeof value === 'object' && value !== null && !Array.isArray(value) ? - (value as JsonObject) - : undefined +const ordinal = (index: number): EntryNodeOrdinal => + String(index) as EntryNodeOrdinal -const snapshotObject = (input: unknown): Result => { - const snapshot = cloneJsonValue(input) - if (!snapshot.ok) return snapshot +/** + * The provider coordinate guard: a provider identity kind opens with one catalog provider + * code, and a provider code never enters a generic source identity kind. + */ +export const providerCoordinateIssue = ( + identityKind: OpaqueIdentityKind, + components: readonly string[], +): Issue | undefined => { + const first = components[0] if ( - typeof snapshot.value !== 'object' || - snapshot.value === null || - Array.isArray(snapshot.value) + PROVIDER_IDENTITY_KINDS.has(identityKind) && + !PROVIDER_CODES.has(first ?? '') ) { - return err('invalid-type', 'Expected a complete identity input object.') + return { + severity: 'error', + code: 'invalid-code', + path: ['components', 0], + message: + 'A Provider identity kind requires one exact catalog provider code as its first component.', + } } - return ok(snapshot.value as JsonObject) + if ( + GENERIC_SOURCE_IDENTITY_KINDS.has(identityKind) && + PROVIDER_CODES.has(first ?? '') + ) { + return { + severity: 'error', + code: 'invalid-code', + path: ['components', 0], + message: + 'Provider coordinates require the matching provider-record, provider-output, or provider-artifact identity kind.', + } + } + return undefined +} + +/** Mints one opaque identifier after the provider coordinate guard admits its components. */ +export const deriveProviderOpaqueIdentifier = ( + scope: OpaqueIdentityScope, + identityKind: Kind, + components: OpaqueIdentityComponents[Kind], +): Result => { + const guard = + Array.isArray(components) ? + providerCoordinateIssue(identityKind, components as readonly string[]) + : undefined + return guard === undefined ? + deriveOpaqueIdentifier(scope, identityKind, components) + : issues([guard]) } /** Derives an immutable event-scoped Device snapshot identity. */ -export const deriveDeviceSnapshotEntryIdentity = ( - deployment: DeploymentIdentitySource, - event: CompleteIdentifierInput, +const deriveDeviceSnapshotEntryIdentity = ( + scope: OpaqueIdentityScope, + event: ExchangeEventIdentifier, sourceDeviceToken: string, role: DeviceSnapshotRole, - id?: import('../core/index.js').FhirId, -): Result => { - const safeDeployment = validateDeploymentIdentity(deployment) - if (!safeDeployment.ok) return safeDeployment - const safeEvent = snapshotObject(event) - if (!safeEvent.ok) return safeEvent - const eventSystem = safeEvent.value.system - const eventValue = safeEvent.value.value - const eventRole = safeEvent.value.role - if ( - typeof sourceDeviceToken !== 'string' || - sourceDeviceToken.trim() === '' || - !deviceSnapshotRoles.has(role) || - eventSystem !== safeDeployment.value.identity.eventIdentifierSystem || - eventRole !== 'event' || - typeof eventValue !== 'string' || - !isEventIdentityValue(eventValue) || - !eventValue.startsWith( - `${groveMobileContract.identity.valuePrefixes.event}${safeDeployment.value.identity.producerInstance}:`, + id?: FhirId, +): Result => { + if (!isNonBlank(sourceDeviceToken)) { + return err( + 'invalid-identifier', + 'Device snapshot identity requires a non-blank source device token.', ) - ) { + } + if (!isOpaqueIdentityScope(scope) || !isEventOfScope(scope, event)) { return err( 'invalid-identifier', - "Device snapshot identity requires a nonblank token and this producer's complete typed event Identifier.", + "Device snapshot identity requires this producer's complete typed event Identifier.", ) } - const identifier = deriveOpaqueIdentifier( - safeDeployment.value, - 'device-snapshot', - [eventSystem, eventValue, role, sourceDeviceToken], - ) + const identifier = deriveOpaqueIdentifier(scope, 'device-snapshot', [ + event.system, + event.value, + role, + sourceDeviceToken, + ]) if (!identifier.ok) return identifier return createEntryIdentity(identifier.value, id) } -/** Derives the optional host snapshot identity an application declares, if any. */ -export const resolveHostIdentity = ( - application: { readonly host?: HostDeviceInput | undefined }, - deployment: DeploymentIdentitySource, - event: CompleteIdentifierInput, -): Result => - application.host === undefined ? - ok(undefined) - : deriveDeviceSnapshotEntryIdentity( - deployment, - event, - application.host.sourceDeviceToken, - 'host', - application.host.id, - ) - -/** Derives an immutable event-scoped application Device snapshot identity. */ export const deriveApplicationEntryIdentity = ( - deployment: DeploymentIdentitySource, - event: CompleteIdentifierInput, - input: ApplicationDeviceInput, -): Result => { - const safeInput = snapshotObject(input) - if (!safeInput.ok) return safeInput - const sourceDeviceToken = safeInput.value.sourceDeviceToken - const id = safeInput.value.id - if ( - typeof sourceDeviceToken !== 'string' || - (id !== undefined && typeof id !== 'string') - ) { - return err( - 'invalid-identifier', - 'Application identity requires a source Device token and optional valid FHIR id.', - ) - } - return deriveDeviceSnapshotEntryIdentity( - deployment, + scope: OpaqueIdentityScope, + event: ExchangeEventIdentifier, + application: ApplicationDevice, + id?: FhirId, +): Result => + deriveDeviceSnapshotEntryIdentity( + scope, event, - sourceDeviceToken, + application.sourceDeviceToken, 'application', - id as FhirId | undefined, + id, + ) + +export const deriveHostEntryIdentity = ( + scope: OpaqueIdentityScope, + event: ExchangeEventIdentifier, + host: HostDevice, + id?: FhirId, +): Result => + deriveDeviceSnapshotEntryIdentity( + scope, + event, + host.sourceDeviceToken, + 'host', + id, ) -} export interface RecordingDeviceGraphIdentity { - readonly stableIdentifier: CompleteIdentifierInput + readonly stableIdentifier: RoledIdentifier /** Selected Bundle entry key for this immutable event-time Device snapshot. */ - readonly snapshot: IdentifiedEntryIdentityInput + readonly snapshot: EntryIdentity } -/** Derives a stable per-unit recording Device identity from admitted instance evidence. */ +/** Derives a stable per-unit recording Device identity and its event snapshot. */ export const deriveRecordingDeviceEntryIdentity = ( - deployment: DeploymentIdentitySource, - event: CompleteIdentifierInput, + scope: OpaqueIdentityScope, + event: ExchangeEventIdentifier, adapterId: string, - input: RecordingDeviceInput, + subject: BusinessIdentifier, + device: RecordingDevice, + id?: FhirId, ): Result => { - const safeInput = snapshotObject(input) - if (!safeInput.ok) return safeInput - const stableUnitToken = safeInput.value.stableUnitToken - const subjectIdentifier = asJsonObject(safeInput.value.subjectIdentifier) - const subjectSystem = subjectIdentifier?.system - const subjectValue = subjectIdentifier?.value - const id = safeInput.value.id - const identityScope = safeInput.value.identityScope - const disclosureAuthorization = safeInput.value.disclosureAuthorization - const validIdentityScope = - identityScope === 'deployment-scoped' || - (identityScope === 'authorized-hardware' && - disclosureAuthorization === 'authorized-for-exchange') if ( - typeof adapterId !== 'string' || - adapterId.trim() === '' || !recordingDeviceAdapters.has(adapterId) || - typeof stableUnitToken !== 'string' || - stableUnitToken.trim() === '' || - typeof subjectSystem !== 'string' || - typeof subjectValue !== 'string' || - subjectValue.trim() === '' || - !parseAbsoluteUri(subjectSystem).ok || - (id !== undefined && typeof id !== 'string') || - !validIdentityScope + !isNonBlank(device.stableUnitToken) || + !parseAbsoluteUri(subject.system).ok || + !isNonBlank(subject.value) ) { return err( 'invalid-identifier', 'Recording Device identity requires an adapter, a complete subject Identifier, and stable per-unit token.', ) } - const stableIdentifier = deriveOpaqueIdentifier( - deployment, - 'recording-device', - [adapterId, subjectSystem, subjectValue, stableUnitToken], - ) + const stableIdentifier = deriveOpaqueIdentifier(scope, 'recording-device', [ + adapterId, + subject.system, + subject.value, + device.stableUnitToken, + ]) if (!stableIdentifier.ok) return stableIdentifier const snapshot = deriveDeviceSnapshotEntryIdentity( - deployment, + scope, event, - stableUnitToken, + device.stableUnitToken, 'recording-device', - id as FhirId | undefined, + id, ) if (!snapshot.ok) return snapshot return ok({ @@ -227,33 +248,14 @@ export const deriveRecordingDeviceEntryIdentity = ( /** Optional writer-record lineage identity for a logical record assigned by an application. */ export const deriveWriterRecordIdentifier = ( - deployment: DeploymentIdentitySource, - writerApplication: CompleteIdentifierInput, - writerRecordId: string, -): Result => { - const safeWriter = snapshotObject(writerApplication) - if (!safeWriter.ok) return safeWriter - const writerSystem = safeWriter.value.system - const writerValue = safeWriter.value.value - const parsedWriterSystem = parseAbsoluteUri(writerSystem) - if ( - !parsedWriterSystem.ok || - typeof writerValue !== 'string' || - writerValue.trim() === '' || - typeof writerRecordId !== 'string' || - writerRecordId.trim() === '' - ) { - return err( - 'invalid-identifier', - 'Writer identity requires a complete application Identifier and writer record id.', - ) - } - return deriveOpaqueIdentifier(deployment, 'writer-record', [ - parsedWriterSystem.value, - writerValue, - writerRecordId, + scope: OpaqueIdentityScope, + writer: WriterRecord, +): Result => + deriveOpaqueIdentifier(scope, 'writer-record', [ + writer.applicationIdentifier.system, + writer.applicationIdentifier.value, + writer.nativeRecordId, ]) -} export type ProviderOutputIdentityInput = | { @@ -271,104 +273,62 @@ export interface ProviderIdentityInput< Provider extends ConnectedProvider = ConnectedProvider, > { readonly provider: Provider - readonly providerScopeIdentifier: ProviderScopeIdentifierInput + readonly repositoryScope: BusinessIdentifier readonly sourceType: string readonly sourceNativeId: string /** Closed catalog selectors; callers do not supply arbitrary values. */ readonly outputs: readonly ProviderOutputIdentityInput[] - readonly eventSequence: string - readonly deployment: DeploymentIdentityInput + readonly event: ExchangeEventIdentifier + readonly scope: OpaqueIdentityScope readonly provenanceNodeRole?: - 'conversion-provenance' | 'retraction-provenance' + 'conversion-provenance' | 'retraction-provenance' | undefined } export interface ProviderIdentities { - readonly sourceRecord: CompleteIdentifierInput - readonly outputs: readonly CompleteIdentifierInput[] + readonly sourceRecord: RoledIdentifier + readonly outputs: readonly RoledIdentifier[] /** Sole business identifier for the exchange event and its Bundle. */ - readonly event: CompleteIdentifierInput + readonly event: ExchangeEventIdentifier /** Typed event-scoped node key for Provenance; not a business identifier. */ - readonly provenanceNode: CompleteIdentifierInput + readonly provenanceNode: EntryNodeIdentifier } -const providerCodes: ReadonlySet = new Set( - providerAdapterCatalog.providers.map(({ id }) => id), -) +type StringTable = Readonly< + Record> | undefined> +> +type NestedStringTable = Readonly> -const recordingDeviceAdapters: ReadonlySet = new Set([ - ...groveProfileClaims.adapterConversionProvenanceClaims.map( - ({ adapter }) => adapter, - ), - ...providerCodes, -]) +const scalarRoles = providerScalarOutputRoles as NestedStringTable +const scalarDiscriminators = + providerScalarOutputDiscriminators as NestedStringTable +const rawRoles = providerRawOutputRoles as StringTable +const rawDiscriminators = providerRawOutputDiscriminators as StringTable -const providerOutputIdentity = ( - value: JsonValue, -): ProviderOutputIdentityInput | undefined => { - const output = asJsonObject(value) - if (output === undefined) return undefined - const keys = Object.keys(output) - .sort((left, right) => left.localeCompare(right)) - .join(',') - if ( - output.kind === 'provider-output' && - keys === 'kind,outputDiscriminator,outputRole' && - typeof output.outputRole === 'string' && - output.outputRole.trim() !== '' && - typeof output.outputDiscriminator === 'string' && - output.outputDiscriminator.trim() !== '' - ) { - return { - kind: output.kind, - outputRole: output.outputRole, - outputDiscriminator: output.outputDiscriminator, - } - } - if ( - output.kind === 'provider-artifact' && - keys === 'formatCode,kind,partIndex' && - typeof output.formatCode === 'string' && - output.formatCode.trim() !== '' && - typeof output.partIndex === 'string' && - /^(?:0|[1-9]\d*)$/u.test(output.partIndex) - ) { - return { - kind: output.kind, - formatCode: output.formatCode, - partIndex: output.partIndex, - } - } - return undefined +const isRecord = (value: unknown): value is Record => + typeof value === 'object' && value !== null + +const OUTPUT_SELECTOR_KEYS: Readonly> = { + 'provider-artifact': ['kind', 'formatCode', 'partIndex'], + 'provider-output': ['kind', 'outputRole', 'outputDiscriminator'], } -const isProvenanceNodeRole = ( - value: JsonValue | undefined, -): value is 'conversion-provenance' | 'retraction-provenance' => - value === 'conversion-provenance' || value === 'retraction-provenance' +const isClosedSelector = (candidate: Record): boolean => { + const keys = OUTPUT_SELECTOR_KEYS[String(candidate.kind)] + return ( + keys !== undefined && + Object.keys(candidate).every((key) => keys.includes(key)) && + keys.every((key) => typeof candidate[key] === 'string') + ) +} const admittedProviderOutput = ( provider: string, sourceType: string, output: ProviderOutputIdentityInput, ): boolean => { - const scalarMapping = ( - providerScalarOutputRoles as Readonly< - Record< - string, - Readonly> | undefined>> - > - > - )[provider]?.[sourceType] - const rawRole = ( - providerRawOutputRoles as Readonly< - Record> | undefined> - > - )[provider]?.[sourceType] - const rawDiscriminator = ( - providerRawOutputDiscriminators as Readonly< - Record> | undefined> - > - )[provider]?.[sourceType] + const candidate: unknown = output + if (!isRecord(candidate) || !isClosedSelector(candidate)) return false + const rawRole = rawRoles[provider]?.[sourceType] if (output.kind === 'provider-artifact') { return ( rawRole !== undefined && @@ -376,24 +336,21 @@ const admittedProviderOutput = ( output.partIndex === '0' ) } - const scalarDiscriminators = ( - providerScalarOutputDiscriminators as Readonly< - Record< - string, - Readonly> | undefined>> - > - > - )[provider]?.[sourceType] if (rawRole === output.outputRole) { - return rawDiscriminator === output.outputDiscriminator - } - if (scalarMapping === undefined || scalarDiscriminators === undefined) { - return false + return ( + rawDiscriminators[provider]?.[sourceType] === output.outputDiscriminator + ) } - return Object.keys(scalarMapping).some( - (measurementId) => - scalarMapping[measurementId] === output.outputRole && - scalarDiscriminators[measurementId] === output.outputDiscriminator, + const mapping = scalarRoles[provider]?.[sourceType] + const discriminators = scalarDiscriminators[provider]?.[sourceType] + return ( + mapping !== undefined && + discriminators !== undefined && + Object.keys(mapping).some( + (measurementId) => + mapping[measurementId] === output.outputRole && + discriminators[measurementId] === output.outputDiscriminator, + ) ) } @@ -401,190 +358,185 @@ const admittedProviderSource = ( provider: string, sourceType: string, ): boolean => - ( - providerScalarOutputRoles as Readonly< - Record> | undefined> - > - )[provider]?.[sourceType] !== undefined || - ( - providerRawOutputRoles as Readonly< - Record> | undefined> - > - )[provider]?.[sourceType] !== undefined - -const providerSourceComponents = ( - input: ProviderIdentityInput, -): readonly [string, string, string, string, string] => [ - input.provider, - input.sourceType, - input.providerScopeIdentifier.system, - input.providerScopeIdentifier.value, - input.sourceNativeId, -] + scalarRoles[provider]?.[sourceType] !== undefined || + rawRoles[provider]?.[sourceType] !== undefined -interface AdmittedProviderSelector { - readonly row: (typeof providerAdapterCatalog.providers)[number] - readonly provider: ConnectedProvider - readonly sourceType: string -} - -const admittedProviderSelector = ( - provider: JsonValue | undefined, - sourceType: JsonValue | undefined, -): AdmittedProviderSelector | undefined => { - const row = providerAdapterCatalog.providers.find(({ id }) => id === provider) - return ( - row !== undefined && - typeof sourceType === 'string' && - admittedProviderSource(row.id, sourceType) - ) ? - { row, provider: row.id, sourceType } - : undefined +const selectorIssues = (input: ProviderIdentityInput): readonly Issue[] => { + if ( + !PROVIDER_CODES.has(input.provider) || + !admittedProviderSource(input.provider, input.sourceType) + ) { + return [ + groveRuleIssue( + 'mobile-input.unsupported-source-type', + ['source', 'sourceType'], + { + message: `${input.provider}/${input.sourceType} is not a source the Provider catalog admits.`, + }, + ), + ] + } + const findings: Issue[] = [] + const nodeRole: unknown = input.provenanceNodeRole + if ( + nodeRole !== undefined && + nodeRole !== 'conversion-provenance' && + nodeRole !== 'retraction-provenance' + ) { + return [ + groveRuleIssue( + 'mobile-input.value-shape-invalid', + ['provenanceNodeRole'], + { message: 'The Provenance node role is one of the two closed roles.' }, + ), + ] + } + const outputs: unknown = input.outputs + if ( + !Array.isArray(outputs) || + (outputs.length === 0 && + input.provenanceNodeRole !== 'retraction-provenance') + ) { + findings.push( + groveRuleIssue('mobile-input.unsupported-source-value', ['outputs'], { + message: + 'An active event derives at least one catalog-admitted output.', + }), + ) + return findings + } + for (const [index, output] of input.outputs.entries()) { + if (!admittedProviderOutput(input.provider, input.sourceType, output)) { + findings.push( + groveRuleIssue( + 'mobile-input.unsupported-source-value', + ['outputs', index], + { + message: `${input.provider}/${input.sourceType} does not admit the selected output.`, + }, + ), + ) + } + } + const keys = input.outputs.map((output) => JSON.stringify(output)) + if (new Set(keys).size !== keys.length) { + findings.push( + groveRuleIssue('mobile-input.value-shape-invalid', ['outputs'], { + message: 'Provider output identity selectors must be unique.', + }), + ) + } + if (!isNonBlank(input.sourceNativeId)) { + findings.push( + groveRuleIssue( + 'mobile-input.native-identifier-invalid', + ['source', 'sourceNativeId'], + { + message: 'The source-native record identifier is blank.', + }, + ), + ) + } + return findings } -const providerOutputsAreAdmitted = ( - provider: ConnectedProvider, - sourceType: string, - outputs: ReadonlyArray, - provenanceNodeRole: JsonValue | undefined, -): outputs is readonly ProviderOutputIdentityInput[] => - (outputs.length > 0 || provenanceNodeRole === 'retraction-provenance') && - outputs.every( - (output) => - output !== undefined && - admittedProviderOutput(provider, sourceType, output), - ) - /** Internal closed-facade derivation of Provider business and graph-node identifiers. */ export const deriveProviderIdentities = ( input: ProviderIdentityInput, ): Result => { - const safeInput = snapshotObject(input) - if (!safeInput.ok) return safeInput - const value = safeInput.value - const selector = admittedProviderSelector(value.provider, value.sourceType) - const rawOutputs = value.outputs - if (selector === undefined || !Array.isArray(rawOutputs)) { - return err( - 'unsupported-measurement', - 'Provider identity selectors must match one exact pinned provider catalog row.', - ['outputs'], - ) - } - const { row: providerRow, provider, sourceType } = selector - const parsedOutputs = rawOutputs.map(providerOutputIdentity) - const provenanceNodeRole = value.provenanceNodeRole + const candidate: unknown = input if ( - !providerOutputsAreAdmitted( - provider, - sourceType, - parsedOutputs, - provenanceNodeRole, - ) + !isRecord(candidate) || + !isRecord(candidate.repositoryScope) || + !isRecord(candidate.event) ) { return err( - 'unsupported-measurement', - 'Provider identity selectors must match one exact pinned provider catalog row.', - ['outputs'], + 'invalid-type', + 'Provider identity derivation takes one complete input object.', ) } - const typedOutputs = parsedOutputs - const providerScope = asJsonObject(value.providerScopeIdentifier) - const providerScopeSystem = parseAbsoluteUri(providerScope?.system) - if (!providerScopeSystem.ok) { - return err( - 'invalid-uri', - 'Provider scope Identifier.system must be an absolute URI.', - ['providerScopeIdentifier', 'system'], - ) - } - const expectedAssurance = providerRow.providerScopeMode + const findings = selectorIssues(input) + if (findings.length > 0) return issues(findings) if ( - typeof providerScope?.value !== 'string' || - providerScope.value.trim() === '' || - providerScope.assurance !== expectedAssurance || - sourceType.trim() === '' || - typeof value.sourceNativeId !== 'string' || - value.sourceNativeId.trim() === '' || - typeof value.eventSequence !== 'string' || - (provenanceNodeRole !== undefined && - !isProvenanceNodeRole(provenanceNodeRole)) + !parseAbsoluteUri(input.repositoryScope.system).ok || + !isNonBlank(input.repositoryScope.value) ) { return err( 'invalid-identifier', - 'Provider identity inputs must be complete canonical values.', + 'The repository scope must be one complete absolute-system Identifier.', + ['repositoryScope'], ) } - const outputKeys = typedOutputs.map((output) => JSON.stringify(output)) - if (new Set(outputKeys).size !== outputKeys.length) { + if ( + !isOpaqueIdentityScope(input.scope) || + !isEventOfScope(input.scope, input.event) + ) { return err( - 'duplicate-identifier', - 'Provider output identity selectors must be unique.', - ['outputs'], + 'invalid-identifier', + "The event identifier must be one this context's identity scope minted.", + ['event'], ) } - const deployment = validateDeploymentIdentity(value.deployment) - if (!deployment.ok) return deployment - - const normalizedInput: ProviderIdentityInput = { - provider: provider, - providerScopeIdentifier: { - system: providerScopeSystem.value, - value: providerScope.value, - assurance: expectedAssurance, - }, - sourceType, - sourceNativeId: value.sourceNativeId, - outputs: typedOutputs, - eventSequence: value.eventSequence, - deployment: deployment.value.identity, - ...(provenanceNodeRole === undefined ? {} : { provenanceNodeRole }), - } - const source = providerSourceComponents(normalizedInput) - const sourceRecord = deriveOpaqueIdentifier( - deployment.value, + const source = [ + input.provider, + input.sourceType, + input.repositoryScope.system, + input.repositoryScope.value, + input.sourceNativeId, + ] as const + const sourceRecord = deriveProviderOpaqueIdentifier( + input.scope, 'provider-record', source, ) if (!sourceRecord.ok) return sourceRecord - const derivedOutputs: CompleteIdentifierInput[] = [] - for (const output of typedOutputs) { + const outputs: RoledIdentifier[] = [] + for (const output of input.outputs) { const derived = output.kind === 'provider-output' ? - deriveOpaqueIdentifier(deployment.value, 'provider-output', [ + deriveProviderOpaqueIdentifier(input.scope, 'provider-output', [ ...source, output.outputRole, output.outputDiscriminator, ]) - : deriveOpaqueIdentifier(deployment.value, 'provider-artifact', [ + : deriveProviderOpaqueIdentifier(input.scope, 'provider-artifact', [ ...source, output.formatCode, output.partIndex, ]) if (!derived.ok) return derived - derivedOutputs.push(derived.value) + outputs.push(derived.value) } - - const event = deriveEventIdentifier( - deployment.value, - normalizedInput.eventSequence, - ) - if (!event.ok) return event - const provenanceNode = deriveEntryNodeIdentifier( - deployment.value, - event.value, - normalizedInput.provenanceNodeRole ?? 'conversion-provenance', - '0', - ) + const provenanceNode = deriveEntryNodeIdentifier(input.scope, { + event: input.event, + role: input.provenanceNodeRole ?? 'conversion-provenance', + ordinal: ordinal(0), + }) if (!provenanceNode.ok) return provenanceNode - return ok( deepFreeze({ sourceRecord: sourceRecord.value, - outputs: derivedOutputs, - event: event.value, + outputs, + event: input.event, provenanceNode: provenanceNode.value, - }) as unknown as ProviderIdentities, + }), ) } + +/** The entry-node identity of the ordinal-th entry keyed by one node role. */ +export const deriveEntryNodeEntryIdentity = ( + scope: OpaqueIdentityScope, + event: ExchangeEventIdentifier, + role: string, + index: number, + id?: FhirId, +): Result => { + const identifier = deriveEntryNodeIdentifier(scope, { + event, + role, + ordinal: ordinal(index), + }) + if (!identifier.ok) return identifier + return createEntryIdentity(identifier.value, id) +} diff --git a/packages/fhir/src/providers/index.ts b/packages/fhir/src/providers/index.ts index ae052d58..e4299470 100644 --- a/packages/fhir/src/providers/index.ts +++ b/packages/fhir/src/providers/index.ts @@ -6,7 +6,10 @@ // SPDX-License-Identifier: MIT // -export { buildProviderMeasurementBundle } from './builder.js' +export { + buildProviderExchangeGraph, + buildProviderExchangeGraphs, +} from './builder.js' export { adapterMeasurementCatalog, groveRecordingFormatRegistry, @@ -30,41 +33,26 @@ export { type ProviderScalarOutputRoles, } from '../contract/providers.generated.js' export { - parseProviderMeasurementBundleInput, parseNormalizedProviderRecord, + parseProviderConversionOptions, providerOutputCoordinates, providerOutputRole, + type ProviderOutputCoordinates, } from './provider.js' -export type { ProviderOutputCoordinates } from './provider.js' export { - buildProviderRecordingBundle, + buildProviderRecordingGraph, encodeRecordingBytes, parseCanonicalBase64, - parseProviderRecordingBundleInput, parseImmutableRecordingUrl, parseMediaType, + parseProviderRecordingAttachment, + parseProviderRecordingSource, parseSha1Base64, } from './recording.js' -export { - buildProviderRetractionBundle, - parseProviderRetractionInput, -} from './retraction.js' -export type { - ProviderRetractionInput, - ProviderRetractionTargetInput, - ProviderRetractionTargetNativeIdentifierInput, - RetractionTargetRole, -} from './retraction.js' +export { buildProviderRetractionEvent } from './retraction.js' export type { DeviceSnapshotRole } from './identity.js' export type { CanonicalBase64, - ProviderAdapter, - ProviderMeasurementBundleInput, - ProviderRecordingAttachmentInput, - ProviderRecordingBundleInput, - ProviderRecordingSource, - ProviderRecordingSourceRecord, - ProviderSourceRecord, ConnectedProvider, ConnectedProviderMeasurement, ConnectedProviderMeasurementKind, @@ -73,37 +61,40 @@ export type { ConnectedRawProvider, ConnectedRawSourceType, ConnectedSourceType, - EmbeddedRecordingAttachmentInput, - ExternalRecordingAttachmentInput, - GovernedSourceIdentifierInput, - GovernedSourceIdentifierTypeCodingInput, - GovernedSourceIdentifierTypeInput, + EmbeddedRecordingAttachment, + ExternalRecordingAttachment, ImmutableRecordingUrl, - MeasurementRepositoryAssignedResourceIds, MediaType, NormalizedProviderRecord, NormalizedSourceRecord, - ProviderAccountScopeIdentifierInput, - ProviderGlobalScopeIdentifierInput, - ProviderScopeIdentifierInput, + ProviderAdapter, + ProviderConversion, + ProviderConversionFailure, + ProviderConversionOptions, ProviderExclusiveMeasurement, ProviderExclusiveMeasurementKind, - ProviderPatientReferenceInput, - ProviderResearchStudyReferenceInput, + ProviderRecordingAttachment, + ProviderRecordingConversion, ProviderRecordingFormat, + ProviderRecordingSource, + ProviderRecordingSourceRecord, + ProviderSourceRecord, RawPayloadAdmissionAssertion, - RecordingRepositoryAssignedResourceIds, Sha1Base64, SupportedConnectedProviderMeasurementKind, - WriterRecordInput, + WriterRecord, } from './types.js' export type { - ApplicationDeviceInput, - DeploymentIdentityInput, - GatewayApplicationInput, - HostDeviceInput, + ApplicationDevice, + ConversionBatch, + ConverterRole, + ExchangeEventContext, + ExchangeGraphIdentifiers, + GovernedSourceIdentifierDisclosurePolicy, + HostDevice, MobileMeasurement, - RecordingDeviceInput, + RecordingDevice, RecordingMethod, - ResourceIdentityInput, + StudyEnrollment, + Subject, } from '../mobile/types.js' diff --git a/packages/fhir/src/providers/profiles.ts b/packages/fhir/src/providers/profiles.ts index 4b1722f5..f03b8d8c 100644 --- a/packages/fhir/src/providers/profiles.ts +++ b/packages/fhir/src/providers/profiles.ts @@ -8,12 +8,15 @@ /* eslint-disable sonarjs/no-clear-text-protocols -- FHIR fixes these canonical URIs to HTTP. */ +import { + groveExchangeProtocol, + groveMobileProfileCanonicals, +} from '../contract/measurement-catalog.generated.js' import { providerAdapterCatalog, groveProviderPackageCanonicals, groveProviderProfileCanonicals, } from '../contract/providers.generated.js' -import { groveMobileContract } from '../mobile/contract.js' export const PROVIDER_RECORDING_OUTPUT_ROLE: 'native-recording' = providerAdapterCatalog.recordingDocument.outputRole @@ -30,10 +33,10 @@ type GroveSystems = Readonly<{ export const SYSTEMS: GroveSystems = { groveAggregationMethod: `${groveProviderPackageCanonicals.mobile}/CodeSystem/grove-aggregation-method`, - groveIdentifierRole: groveMobileContract.systems.identifierRole, - groveLifecycleEvent: groveMobileContract.systems.lifecycleEvent, + groveIdentifierRole: groveExchangeProtocol.codeSystems.identifierRole, + groveLifecycleEvent: groveExchangeProtocol.codeSystems.lifecycleEvent, groveRecordingMethod: `${groveProviderPackageCanonicals.mobile}/CodeSystem/grove-recording-method`, - isoLifecycle: 'http://terminology.hl7.org/CodeSystem/iso-21089-lifecycle', + isoLifecycle: groveExchangeProtocol.lifecycle.active.activitySystem, provenanceParticipant: 'http://terminology.hl7.org/CodeSystem/provenance-participant-type', } as const @@ -55,7 +58,7 @@ export const PROFILES: GroveProfiles = { mobileBundle: groveProviderProfileCanonicals['grove-mobile-exchange-bundle'], recordingDevice: groveProviderProfileCanonicals['grove-recording-device'], applicationDevice: groveProviderProfileCanonicals['grove-application-device'], - hostDevice: groveMobileContract.profiles.hostDevice, + hostDevice: groveMobileProfileCanonicals['grove-host-device'], providerObservation: groveProviderProfileCanonicals['providers-observation'], sensorRecordingDocument: groveProviderProfileCanonicals['grove-sensor-recording-document'], @@ -63,8 +66,8 @@ export const PROFILES: GroveProfiles = { groveProviderProfileCanonicals['providers-recording-document'], providerConversionProvenance: groveProviderProfileCanonicals['providers-conversion-provenance'], - retractionBundle: groveMobileContract.profiles.retractionBundle, - retractionProvenance: groveMobileContract.profiles.retractionProvenance, + retractionBundle: groveExchangeProtocol.profiles.retractionBundle, + retractionProvenance: groveExchangeProtocol.profiles.retractionProvenance, } as const type GroveExtensions = Readonly<{ @@ -79,18 +82,25 @@ type GroveExtensions = Readonly<{ writerRecordVersion: string }> +const extensionTarget = (name: string): string => { + const rule = groveExchangeProtocol.referencePolicy.extensionTargets.find( + ({ url }) => url.endsWith(`/${name}`), + ) + if (rule === undefined) { + throw new Error(`The exchange protocol names no ${name} extension target.`) + } + return rule.url +} + export const EXTENSIONS: GroveExtensions = { - gatewayDevice: - 'http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice', + gatewayDevice: extensionTarget('observation-gatewayDevice'), recordingMethod: `${groveProviderPackageCanonicals.mobile}/StructureDefinition/grove-recording-method`, - provider: `${groveProviderPackageCanonicals.providers}/StructureDefinition/provider`, - providerSourceType: `${groveProviderPackageCanonicals.providers}/StructureDefinition/provider-source-type`, - entryNodeKey: groveMobileContract.extensions.entryNodeKey, - retractionTargetRole: groveMobileContract.extensions.retractionTargetRole, + provider: providerAdapterCatalog.providerExtension.url, + providerSourceType: providerAdapterCatalog.sourceTypeExtension.url, + entryNodeKey: groveExchangeProtocol.extensions.entryNodeKey, + retractionTargetRole: groveExchangeProtocol.extensions.retractionTargetRole, retractionTargetNativeIdentifier: - groveMobileContract.extensions.retractionTargetNativeIdentifier, - researchStudy: - 'http://hl7.org/fhir/StructureDefinition/workflow-researchStudy', - writerRecordVersion: - 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version', + groveExchangeProtocol.extensions.retractionTargetNativeIdentifier, + researchStudy: extensionTarget('workflow-researchStudy'), + writerRecordVersion: groveExchangeProtocol.extensions.writerRecordVersion, } as const diff --git a/packages/fhir/src/providers/provider-input-schemas.ts b/packages/fhir/src/providers/provider-input-schemas.ts index 2cd0deef..aa2fc2e1 100644 --- a/packages/fhir/src/providers/provider-input-schemas.ts +++ b/packages/fhir/src/providers/provider-input-schemas.ts @@ -8,200 +8,240 @@ import { z } from 'zod' import { SYSTEMS } from './profiles.js' -import type { - ProviderPatientReferenceInput, - ProviderResearchStudyReferenceInput, -} from './types.js' -import { providerAdapterCatalog } from '../contract/providers.generated.js' +import type { ProviderConversionOptions, WriterRecord } from './types.js' import { parseAbsoluteUri, - parseFhirId, parseFhirInstant, type AbsoluteUri, - type FhirId, - type FhirInstant, type Issue, } from '../core/index.js' import { containsIsolatedSurrogate, - validateDeploymentIdentity, + type OpaqueIdentityScope, } from '../mobile/identity.js' import { canonicalizeMobileEffectiveInstant } from '../mobile/time.js' import type { - ApplicationDeviceInput, - DeploymentIdentityInput, - GatewayApplicationInput, - RecordingDeviceInput, + ApplicationDevice, + GovernedSourceIdentifierDisclosurePolicy, + InstantEffectiveTime, + PeriodEffectiveTime, + RecordingDevice, } from '../mobile/types.js' +import { + groveRuleParameters, + type ClientRecordRule, +} from '../r4/diagnostics.js' -const primitiveInstantSchemaValue = z - .string() - .refine((value) => parseFhirInstant(value).ok, { - message: - 'Expected an RFC 3339 instant with seconds and an explicit UTC offset.', - }) -export const primitiveInstantSchema: z.ZodString = primitiveInstantSchemaValue - -const mobileEffectiveInstantSchemaValue = z - .string() - .transform((value, context) => { - const canonical = canonicalizeMobileEffectiveInstant(value) - if (!canonical.ok) { - for (const issue of canonical.issues) { - context.addIssue({ code: 'custom', message: issue.message }) - } - return z.NEVER - } - return canonical.value - }) -export const mobileEffectiveInstantSchema: z.ZodType = - mobileEffectiveInstantSchemaValue +// A refinement that refuses the record under one registry code; the parser rebuilds the +// Issue from these params, so the local text stays the detail and the reason stays the registry's. +export const refusal = ( + code: ClientRecordRule, + message: string, +): { + readonly message: string + readonly params: Readonly> +} => ({ + message, + params: groveRuleParameters(code, [], { message }), +}) -const nonBlankStringSchemaValue = z +const unicodeScalarText = z .string() .refine( - (value) => value.trim() !== '' && !containsIsolatedSurrogate(value), - 'Expected a non-blank Unicode-scalar string.', + (value) => !containsIsolatedSurrogate(value), + refusal( + 'mobile-input.text-not-unicode-scalar', + 'A source text field contains an unpaired UTF-16 surrogate.', + ), ) -export const nonBlankStringSchema: z.ZodString = nonBlankStringSchemaValue -const FHIR_CODE = /^[^\s\p{Cc}]+(?: [^\s\p{Cc}]+)*$/u -const fhirCodeSchema = nonBlankStringSchema.refine( - (value) => FHIR_CODE.test(value), - 'Expected a FHIR code with no leading, trailing, consecutive, or control whitespace.', +const nonBlankTextValue = unicodeScalarText.refine( + (value) => value.trim() !== '', + refusal('mobile-input.value-shape-invalid', 'Expected a non-blank string.'), +) +export const nonBlankText: z.ZodString = nonBlankTextValue + +const nativeIdentifierTextValue = unicodeScalarText.refine( + (value) => value.trim() !== '', + refusal( + 'mobile-input.native-identifier-invalid', + 'The source-native record identifier is blank.', + ), +) +export const nativeIdentifierText: z.ZodString = nativeIdentifierTextValue + +/** An absolute URI inside a source record; a malformed one is a refused record. */ +export const sourceAbsoluteUri: z.ZodType = z.custom( + (value) => parseAbsoluteUri(value).ok, + refusal( + 'mobile-input.value-shape-invalid', + 'Expected an absolute ASCII RFC 3986 URI.', + ), ) -// The parser is the proof, so the schema carries the brand the input contract promises. -export const absoluteUriSchema: z.ZodType = z.custom( +/** An absolute URI inside deployment configuration; a malformed one is a fault. */ +const absoluteUriSchema = z.custom( (value) => parseAbsoluteUri(value).ok, { message: 'Expected an absolute ASCII RFC 3986 URI.' }, ) -const identifierInputSchemaValue = z.strictObject({ - system: absoluteUriSchema, - value: nonBlankStringSchema, +const sourceIdentifierSchema = z.strictObject({ + system: sourceAbsoluteUri, + value: unicodeScalarText.refine( + (value) => value !== '', + refusal( + 'mobile-input.value-shape-invalid', + 'Identifier.value must not be empty.', + ), + ), }) -export const identifierInputSchema: z.ZodObject< - { system: z.ZodType; value: z.ZodString }, - z.core.$strict -> = identifierInputSchemaValue - -export type ProviderScopeAssurance = - 'deployment-scoped-account-pseudonym' | 'documented-global-key-space' -export const providerScopeIdentifierSchema: z.ZodType<{ - readonly system: AbsoluteUri - readonly value: string - readonly assurance: ProviderScopeAssurance -}> = identifierInputSchema.extend({ - assurance: z.enum([ - 'deployment-scoped-account-pseudonym', - 'documented-global-key-space', - ]), +const mobileEffectiveInstantSchema = z.string().transform((value, context) => { + const canonical = canonicalizeMobileEffectiveInstant(value) + if (!canonical.ok) { + for (const issue of canonical.issues) { + context.addIssue({ + code: 'custom', + ...refusal('mobile-input.effective-period-invalid', issue.message), + }) + } + return z.NEVER + } + return canonical.value }) -const providerScopeAssuranceByProvider: ReadonlyMap< - string, - ProviderScopeAssurance -> = new Map( - providerAdapterCatalog.providers.map((row) => [ - row.id, - row.providerScopeMode, - ]), -) +const instantEffectiveSchemaValue = z.strictObject({ + kind: z.literal('date-time'), + value: mobileEffectiveInstantSchema, +}) -/** Validates the provider-dependent scope assertion projected from the pinned catalog. */ -export const providerScopeIdentifierIssues = ( - provider: string, - identifier: { readonly assurance: ProviderScopeAssurance }, - path: ReadonlyArray = [ - 'source', - 'providerScopeIdentifier', - 'assurance', - ], -): readonly Issue[] => { - const expected = providerScopeAssuranceByProvider.get(provider) - if (expected === undefined || identifier.assurance === expected) return [] - return [ - { - severity: 'error', - code: 'value-mismatch', - path, - message: - expected === 'deployment-scoped-account-pseudonym' ? - 'This provider has account-scoped native keys and requires a deployment-scoped account pseudonym.' - : 'This provider has globally unique native keys and requires its documented global key-space pair.', +const periodEffectiveSchemaValue = z + .strictObject({ + kind: z.literal('period'), + start: mobileEffectiveInstantSchema, + end: mobileEffectiveInstantSchema, + }) + .refine( + (value) => { + const start = parseFhirInstant(value.start) + const end = parseFhirInstant(value.end) + return ( + start.ok && end.ok && Date.parse(start.value) <= Date.parse(end.value) + ) }, - ] -} + refusal( + 'mobile-input.effective-period-invalid', + 'A measurement Period must not end before it starts.', + ), + ) +export const instantEffectiveSchema: z.ZodType = + instantEffectiveSchemaValue +export const periodEffectiveSchema: z.ZodType = + periodEffectiveSchemaValue +export const effectiveTimeSchema: z.ZodType< + InstantEffectiveTime | PeriodEffectiveTime +> = z.discriminatedUnion('kind', [ + instantEffectiveSchemaValue, + periodEffectiveSchemaValue, +]) + +const applicationDeviceSchemaValue = z.strictObject({ + sourceDeviceToken: nonBlankText, + name: nonBlankText, + version: nonBlankText.optional(), + build: nonBlankText.optional(), +}) +export const applicationDeviceSchema: z.ZodType = + applicationDeviceSchemaValue + +const recordingDeviceSchemaValue = z.strictObject({ + stableUnitToken: nonBlankText, + name: nonBlankText.optional(), + manufacturer: nonBlankText.optional(), + modelNumber: nonBlankText.optional(), +}) +export const recordingDeviceSchema: z.ZodType = + recordingDeviceSchemaValue + +const writerRecordSchemaValue = z.strictObject({ + applicationIdentifier: sourceIdentifierSchema, + nativeRecordId: nativeIdentifierText, + version: z + .string() + .regex( + /^(?:0|[1-9]\d*)$/u, + refusal( + 'mobile-input.native-identifier-invalid', + 'A writer record version is a canonical non-negative decimal.', + ), + ) + .optional(), +}) +export const writerRecordSchema: z.ZodType = + writerRecordSchemaValue + +const FHIR_CODE = /^[^\s\p{Cc}]+(?: [^\s\p{Cc}]+)*$/u +const fhirCodeSchema = z + .string() + .refine( + (value) => FHIR_CODE.test(value), + 'Expected a FHIR code with no leading, trailing, consecutive, or control whitespace.', + ) +const configurationText = z + .string() + .refine( + (value) => value.trim() !== '' && !containsIsolatedSurrogate(value), + 'Expected a non-blank Unicode-scalar string.', + ) -const governedSourceIdentifierTypeCodingSchema = z.strictObject({ +const disclosureCodingSchema = z.strictObject({ system: absoluteUriSchema, code: fhirCodeSchema, - display: nonBlankStringSchema.optional(), + display: configurationText.optional(), }) -const governedSourceIdentifierTypeSchema = z - .strictObject({ - coding: z - .array(governedSourceIdentifierTypeCodingSchema) - .nonempty() +const disclosurePolicySchema = z.discriminatedUnion('kind', [ + z.strictObject({ kind: z.literal('omit') }), + z.strictObject({ + kind: z.literal('authorized'), + system: absoluteUriSchema, + type: z + .strictObject({ + coding: z + .tuple([disclosureCodingSchema], disclosureCodingSchema) + .optional(), + text: configurationText.optional(), + }) + .refine( + (value) => value.coding !== undefined || value.text !== undefined, + { + message: 'Identifier.type requires a non-empty coding or text.', + }, + ) .optional(), - text: nonBlankStringSchema.optional(), - }) - .refine((value) => value.coding !== undefined || value.text !== undefined, { - message: 'Identifier.type requires a non-empty coding or text.', - }) - -interface GovernedSourceIdentifierRuntime { - readonly system: string - readonly nativeId: string - readonly type?: - | { - readonly coding?: - | ReadonlyArray<{ - readonly system: string - readonly code: string - readonly display?: string | undefined - }> - | undefined - readonly text?: string | undefined - } - | undefined -} + }), +]) -const governedSourceIdentifierSchemaValue = z.strictObject({ - system: absoluteUriSchema, - nativeId: nonBlankStringSchema, - type: governedSourceIdentifierTypeSchema.optional(), +const conversionOptionsSchemaValue = z.strictObject({ + nativeIdentifierDisclosure: disclosurePolicySchema.optional(), }) -export const governedSourceIdentifierSchema: z.ZodType = - governedSourceIdentifierSchemaValue +export const conversionOptionsSchema: z.ZodType = + conversionOptionsSchemaValue /** Deployment-aware checks the structural disclosure schema cannot decide on its own. */ export const governedSourceIdentifierIssues = ( - disclosure: GovernedSourceIdentifierRuntime | undefined, - sourceNativeId: string, - deployment: DeploymentIdentityInput, + policy: GovernedSourceIdentifierDisclosurePolicy, + scope: OpaqueIdentityScope, ): readonly Issue[] => { - if (disclosure === undefined) return [] + if (policy.kind === 'omit') return [] const findings: Issue[] = [] - if (disclosure.nativeId !== sourceNativeId) { - findings.push({ - severity: 'error', - code: 'value-mismatch', - path: ['nativeIdentifierDisclosure', 'nativeId'], - message: - 'The governed source Identifier value must exactly equal the source-native id used for Grove reconciliation.', - }) - } - const groveIdentitySystems = new Set([ + const groveIdentitySystems = new Set([ SYSTEMS.groveIdentifierRole, - deployment.eventIdentifierSystem, - deployment.entryNodeIdentifierSystem, - ...Object.values(deployment.opaqueIdentifierSystems), + scope.systems.event, + scope.systems.entryNode, + ...Object.values(scope.systems.opaque), ]) - if (groveIdentitySystems.has(disclosure.system)) { + if (groveIdentitySystems.has(policy.system)) { findings.push({ severity: 'error', code: 'value-mismatch', @@ -210,7 +250,7 @@ export const governedSourceIdentifierIssues = ( 'A source-native Identifier requires its own governed provider/store namespace, never a Grove graph-role or identity system.', }) } - for (const [index, coding] of (disclosure.type?.coding ?? []).entries()) { + for (const [index, coding] of (policy.type?.coding ?? []).entries()) { if (coding.system === SYSTEMS.groveIdentifierRole) { findings.push({ severity: 'error', @@ -223,97 +263,3 @@ export const governedSourceIdentifierIssues = ( } return findings } - -const providerPatientReferenceSchemaValue = z.strictObject({ - type: z.literal('Patient'), - identifier: identifierInputSchema.extend({ - assurance: z.literal('deployment-scoped-pseudonym'), - }), -}) -export const providerPatientReferenceSchema: z.ZodType = - providerPatientReferenceSchemaValue - -const providerResearchStudyReferenceSchemaValue = z.strictObject({ - type: z.literal('ResearchStudy'), - identifier: identifierInputSchema, -}) -export const providerResearchStudyReferenceSchema: z.ZodType = - providerResearchStudyReferenceSchemaValue - -// One validator owns the deployment contract; a second field-level copy only diverges. -export const deploymentIdentitySchema: z.ZodType = z - .custom() - .superRefine((value, context) => { - const validated = validateDeploymentIdentity(value) - if (validated.ok) return - for (const issue of validated.issues) { - context.addIssue({ - code: 'custom', - path: [...issue.path], - message: issue.message, - }) - } - }) - -export const fhirIdSchema: z.ZodType = z.custom( - (value) => parseFhirId(value).ok, - { message: 'Expected a FHIR id of 1 to 64 unreserved characters.' }, -) - -const hostDeviceSchema = z.strictObject({ - sourceDeviceToken: nonBlankStringSchema, - id: fhirIdSchema.optional(), - name: nonBlankStringSchema.optional(), - manufacturer: nonBlankStringSchema.optional(), - modelNumber: nonBlankStringSchema.optional(), - operatingSystemVersion: nonBlankStringSchema, -}) - -const applicationDeviceSchemaValue = z.strictObject({ - sourceDeviceToken: nonBlankStringSchema, - id: fhirIdSchema.optional(), - name: nonBlankStringSchema, - version: nonBlankStringSchema.optional(), - build: nonBlankStringSchema.optional(), - manufacturer: nonBlankStringSchema.optional(), - host: hostDeviceSchema.optional(), -}) -export const applicationDeviceSchema: z.ZodType = - applicationDeviceSchemaValue - -const recordingDeviceBase = { - stableUnitToken: nonBlankStringSchema, - subjectIdentifier: identifierInputSchema, - id: fhirIdSchema.optional(), - name: nonBlankStringSchema.optional(), - manufacturer: nonBlankStringSchema.optional(), - modelNumber: nonBlankStringSchema.optional(), -} as const - -const recordingDeviceSchemaValue = z.discriminatedUnion('identityScope', [ - z.strictObject({ - ...recordingDeviceBase, - identityScope: z.literal('deployment-scoped'), - }), - z.strictObject({ - ...recordingDeviceBase, - identityScope: z.literal('authorized-hardware'), - disclosureAuthorization: z.literal('authorized-for-exchange'), - }), -]) -export const recordingDeviceSchema: z.ZodType = - recordingDeviceSchemaValue - -const gatewayApplicationSchemaValue = z.discriminatedUnion('kind', [ - z.strictObject({ - kind: z.literal('converter-application'), - roleAssurance: z.literal('mediated-or-routed-measurement'), - }), - z.strictObject({ - kind: z.literal('distinct-application'), - roleAssurance: z.literal('mediated-or-routed-measurement'), - application: applicationDeviceSchema, - }), -]) -export const gatewayApplicationSchema: z.ZodType = - gatewayApplicationSchemaValue diff --git a/packages/fhir/src/providers/provider.ts b/packages/fhir/src/providers/provider.ts index 8eea480b..405ea896 100644 --- a/packages/fhir/src/providers/provider.ts +++ b/packages/fhir/src/providers/provider.ts @@ -7,10 +7,6 @@ // import { z } from 'zod' -import { - deriveProviderIdentities, - type ProviderIdentities, -} from './identity.js' import { connectedProviderExclusiveDefinitions, sharedMeasurementDefinition, @@ -18,28 +14,22 @@ import { type MeasurementDefinition, } from './measurement-definition.js' import { - absoluteUriSchema, applicationDeviceSchema, - deploymentIdentitySchema, - fhirIdSchema, - gatewayApplicationSchema, - governedSourceIdentifierIssues, - governedSourceIdentifierSchema, - identifierInputSchema, - mobileEffectiveInstantSchema, - nonBlankStringSchema, - primitiveInstantSchema, - providerPatientReferenceSchema, - providerScopeIdentifierIssues, - providerScopeIdentifierSchema, - providerResearchStudyReferenceSchema, + conversionOptionsSchema, + effectiveTimeSchema, + instantEffectiveSchema, + nativeIdentifierText, + nonBlankText, + periodEffectiveSchema, recordingDeviceSchema, + refusal, + sourceAbsoluteUri, + writerRecordSchema, } from './provider-input-schemas.js' import type { - ProviderMeasurementBundleInput, - ConnectedProviderMeasurementKind, ConnectedProvider, NormalizedProviderRecord, + ProviderConversionOptions, } from './types.js' import { sharedMobileMeasurementCatalog, @@ -55,16 +45,18 @@ import { compareFhirInstants, deepFreeze, issues, - mapResult, ok, + zodIssuePath, zodIssueToIssue, type Issue, type Result, } from '../core/index.js' -import type { - MeasurementKindsWhere, - MobileMeasurement, -} from '../mobile/types.js' +import type { MobileMeasurement } from '../mobile/types.js' +import { + groveRuleIssue, + groveRuleIssueFromParameters, + type ClientRecordRule, +} from '../r4/diagnostics.js' type ParsedProviderMeasurement = | MobileMeasurement @@ -80,41 +72,75 @@ type ParsedProviderMeasurement = } } -export { - applicationDeviceSchema, - deploymentIdentitySchema, - fhirIdSchema, - governedSourceIdentifierIssues, - governedSourceIdentifierSchema, - identifierInputSchema, - nonBlankStringSchema, - primitiveInstantSchema, - providerPatientReferenceSchema, - providerScopeIdentifierIssues, - providerScopeIdentifierSchema, -} from './provider-input-schemas.js' +const refuse = ( + code: ClientRecordRule, + path: ReadonlyArray, + message: string, +): Issue => groveRuleIssue(code, path, { message }) + +// A zod failure inside a source record is a refusal: the registry code says why, the zod +// text is the detail. Refinements name their own code; the built-in failures map here. +const refusalCodeFor = (issue: z.core.$ZodIssue): ClientRecordRule => { + if (issue.code === 'invalid_type' && issue.input === undefined) { + return 'mobile-input.required-metadata-missing' + } + if (issue.code === 'invalid_value' || issue.code === 'invalid_union') { + return 'mobile-input.unsupported-source-value' + } + return 'mobile-input.value-shape-invalid' +} -const sourceBase = { - recordingMethod: z - .enum(['actively-recorded', 'automatically-recorded', 'manual-entry']) - .optional(), - recordingDevice: recordingDeviceSchema.optional(), - writerRecord: z - .strictObject({ - applicationIdentifier: identifierInputSchema, - nativeRecordId: nonBlankStringSchema, - version: z - .string() - .regex(/^(?:0|[1-9]\d*)$/u) - .optional(), - }) - .optional(), -} as const +const refusalIssue = (issue: z.core.$ZodIssue): readonly Issue[] => { + const path = zodIssuePath(issue) + if (issue.code === 'unrecognized_keys') { + return issue.keys.map((key) => + refuse('mobile-input.value-shape-invalid', [...path, key], issue.message), + ) + } + const registered = groveRuleIssueFromParameters( + issue.code === 'custom' ? issue.params : undefined, + path, + ) + return [registered ?? refuse(refusalCodeFor(issue), path, issue.message)] +} + +/** Every refusal of one source record, from its zod failures. */ +export const refusalIssues = (error: z.ZodError): readonly Issue[] => + error.issues.flatMap(refusalIssue) + +const valueAt = ( + value: unknown, + path: ReadonlyArray, +): unknown => + path.reduce( + (current, segment) => + typeof current === 'object' && current !== null ? + (current as Record)[segment] + : undefined, + value, + ) + +// The JSON snapshot refuses what zod never sees; a non-finite number is still a value. +const snapshotRefusal = (input: unknown, issue: Issue): Issue => { + const offending = valueAt(input, issue.path) + return typeof offending === 'number' && !Number.isFinite(offending) ? + refuse( + 'mobile-input.value-outside-domain', + issue.path, + 'A source value must be a finite number.', + ) + : refuse('mobile-input.value-shape-invalid', issue.path, issue.message) +} const sourceSchema = z.strictObject({ - ...sourceBase, adapter: z.strictObject({ - kind: z.literal('providers'), + kind: z.custom<'providers'>( + (value) => value === 'providers', + refusal( + 'mobile-input.value-shape-invalid', + 'A provider record names the providers adapter.', + ), + ), provider: z.enum( Object.keys(providerScalarOutputRoles) as [ ConnectedProvider, @@ -122,101 +148,40 @@ const sourceSchema = z.strictObject({ ], ), }), - providerScopeIdentifier: providerScopeIdentifierSchema, - sourceType: nonBlankStringSchema, - sourceNativeId: nonBlankStringSchema, - dataOrigin: applicationDeviceSchema, -}) - -const instantEffectiveSchema = z.strictObject({ - kind: z.literal('date-time'), - value: mobileEffectiveInstantSchema, + sourceType: nonBlankText, + sourceNativeId: nativeIdentifierText, + writer: applicationDeviceSchema, + recordingMethod: z + .enum(['actively-recorded', 'automatically-recorded', 'manual-entry']) + .optional(), + recordingDevice: recordingDeviceSchema.optional(), + writerRecord: writerRecordSchema.optional(), }) -const periodEffectiveSchema = z - .strictObject({ - kind: z.literal('period'), - start: mobileEffectiveInstantSchema, - end: mobileEffectiveInstantSchema, - }) - .refine( - (value) => { - const ordering = compareFhirInstants(value.start, value.end) - return ordering.ok && ordering.value !== 1 - }, - { message: 'A measurement Period must not end before it starts.' }, - ) - -// The value arguments decide the kinds, so a swapped pair cannot compile into a lie. -const measurementKindsWhere = < - ValueKind extends 'codeableConcept' | 'quantity', - Effective extends 'Period' | 'dateTime' | 'dateTime-or-Period', - Excluded extends SharedMobileMeasurementKind = never, ->( - valueKind: ValueKind, - effective: Effective, - excluded?: Excluded, -): [ - Exclude, Excluded>, - ...Array, Excluded>>, -] => { - const excludedKind: string | undefined = excluded - return ( +const sharedKindsWithValue = ( + valueKind: 'codeableConcept' | 'quantity', + ...excluded: SharedMobileMeasurementKind[] +): [SharedMobileMeasurementKind, ...SharedMobileMeasurementKind[]] => + ( Object.keys(sharedMobileMeasurementCatalog) as SharedMobileMeasurementKind[] - ).filter((kind) => { - const definition = sharedMobileMeasurementCatalog[kind] - return ( - definition.valueKind === valueKind && - definition.effective === effective && - kind !== excludedKind - ) - }) as [ - Exclude, Excluded>, - ...Array, Excluded>>, - ] -} - -const instantQuantityMeasurementSchema = z.strictObject({ - kind: z.enum(measurementKindsWhere('quantity', 'dateTime')), + ).filter( + (kind) => + sharedMobileMeasurementCatalog[kind].valueKind === valueKind && + !excluded.includes(kind), + ) as [SharedMobileMeasurementKind, ...SharedMobileMeasurementKind[]] + +// The catalog's effective[x] rule is enforced by refinement so that a mismatch is reported +// as the effective-period refusal rather than as a shape the union could not place. +const quantityMeasurementSchema = z.strictObject({ + kind: z.enum(sharedKindsWithValue('quantity')), value: z.number(), - effective: instantEffectiveSchema, + effective: effectiveTimeSchema, }) -const periodQuantityMeasurementSchema = z.strictObject({ - kind: z.enum(measurementKindsWhere('quantity', 'Period')), - value: z.number(), - effective: periodEffectiveSchema, -}) - -const choiceQuantityMeasurementKinds = measurementKindsWhere( - 'quantity', - 'dateTime-or-Period', -) -const choiceQuantityMeasurementKindSet: ReadonlySet = new Set( - choiceQuantityMeasurementKinds, -) -const choiceQuantityMeasurementSchema = z.strictObject({ - kind: z.custom<(typeof choiceQuantityMeasurementKinds)[number]>( - (value) => - typeof value === 'string' && choiceQuantityMeasurementKindSet.has(value), - { message: 'Expected a catalog measurement with choice effective[x].' }, - ), - value: z.number(), - effective: z.union([instantEffectiveSchema, periodEffectiveSchema]), -}) - -const instantCodedMeasurementSchema = z.strictObject({ - kind: z.enum(measurementKindsWhere('codeableConcept', 'dateTime')), - value: nonBlankStringSchema, - effective: instantEffectiveSchema, -}) - -const periodCodedMeasurementSchema = z.strictObject({ - kind: z.enum( - measurementKindsWhere('codeableConcept', 'Period', 'sleep-stage'), - ), - value: nonBlankStringSchema, - effective: periodEffectiveSchema, +const codedMeasurementSchema = z.strictObject({ + kind: z.enum(sharedKindsWithValue('codeableConcept', 'sleep-stage')), + value: nonBlankText, + effective: effectiveTimeSchema, }) const bloodPressureMeasurementSchema = z.strictObject({ @@ -231,9 +196,9 @@ const sleepStageMeasurementSchema = z.strictObject({ stage: z.enum(sharedMobileMeasurementCatalog['sleep-stage'].allowedValues), sourceStageCoding: z .strictObject({ - system: absoluteUriSchema, - code: nonBlankStringSchema, - display: nonBlankStringSchema.optional(), + system: sourceAbsoluteUri, + code: nonBlankText, + display: nonBlankText.optional(), }) .optional(), effective: periodEffectiveSchema, @@ -250,26 +215,24 @@ const exclusiveCodedKinds = Object.values(exclusiveDefinitions) const exclusiveQuantityMeasurementSchema = z.strictObject({ kind: z.enum(exclusiveQuantityKinds), value: z.number(), - effective: z.union([instantEffectiveSchema, periodEffectiveSchema]), + effective: effectiveTimeSchema, }) const exclusiveCodedMeasurementSchema = z.strictObject({ kind: z.enum(exclusiveCodedKinds), - value: nonBlankStringSchema, - effective: z.union([instantEffectiveSchema, periodEffectiveSchema]), + value: nonBlankText, + effective: effectiveTimeSchema, }) -const measurementSchema: z.ZodType = z.union([ - instantQuantityMeasurementSchema, - periodQuantityMeasurementSchema, - choiceQuantityMeasurementSchema, - instantCodedMeasurementSchema, - periodCodedMeasurementSchema, - bloodPressureMeasurementSchema, - sleepStageMeasurementSchema, - exclusiveQuantityMeasurementSchema, - exclusiveCodedMeasurementSchema, -]) +const measurementSchema: z.ZodType = + z.discriminatedUnion('kind', [ + quantityMeasurementSchema, + codedMeasurementSchema, + bloodPressureMeasurementSchema, + sleepStageMeasurementSchema, + exclusiveQuantityMeasurementSchema, + exclusiveCodedMeasurementSchema, + ]) const effectiveKindMatches = ( definition: MeasurementDefinition, @@ -296,6 +259,19 @@ const violatesRequiredPeriodOrdering = ( return !ordering.ok || ordering.value !== -1 } +const addRefusal = ( + context: z.core.$RefinementCtx, + code: ClientRecordRule, + path: ReadonlyArray, + message: string, +): void => { + context.addIssue({ + code: 'custom', + path: [...path], + ...refusal(code, message), + }) +} + const refineMeasurement = ( measurement: z.infer, path: ReadonlyArray, @@ -305,28 +281,31 @@ const refineMeasurement = ( sharedMeasurementDefinition(measurement.kind) ?? exclusiveDefinitions[measurement.kind] if (measurementDefinition === undefined) { - context.addIssue({ - code: 'custom', - path: [...path, 'kind'], - message: `No closed Provider measurement definition exists for ${measurement.kind}.`, - }) + addRefusal( + context, + 'mobile-input.unsupported-source-value', + [...path, 'kind'], + `No closed Provider measurement definition exists for ${measurement.kind}.`, + ) return } if ( !effectiveKindMatches(measurementDefinition, measurement.effective.kind) ) { - context.addIssue({ - code: 'custom', - path: [...path, 'effective'], - message: `${measurement.kind} requires catalog effective[x] ${measurementDefinition.effective}.`, - }) + addRefusal( + context, + 'mobile-input.effective-period-invalid', + [...path, 'effective'], + `${measurement.kind} requires catalog effective[x] ${measurementDefinition.effective}.`, + ) } if (violatesRequiredPeriodOrdering(measurement, measurementDefinition)) { - context.addIssue({ - code: 'custom', - path: [...path, 'effective'], - message: `The ${measurement.kind} Period must satisfy its catalog-owned nonzero-duration rule.`, - }) + addRefusal( + context, + 'mobile-input.effective-period-invalid', + [...path, 'effective'], + `The ${measurement.kind} Period must satisfy its catalog-owned nonzero-duration rule.`, + ) } const allowedValues: readonly string[] | undefined = measurementDefinition.allowedValues @@ -335,30 +314,27 @@ const refineMeasurement = ( typeof measurement.value === 'string' && allowedValues?.includes(measurement.value) !== true ) { - context.addIssue({ - code: 'custom', - path: [...path, 'value'], - message: `Expected a catalog-allowed coded result for ${measurement.kind}.`, - }) + addRefusal( + context, + 'mobile-input.unsupported-source-value', + [...path, 'value'], + `Expected a catalog-allowed coded result for ${measurement.kind}.`, + ) } if ( 'value' in measurement && typeof measurement.value === 'number' && violatesQuantityDomain(measurement.value, measurementDefinition) ) { - context.addIssue({ - code: 'custom', - path: [...path, 'value'], - message: `The ${measurement.kind} value is outside its catalog-owned value domain.`, - }) + addRefusal( + context, + 'mobile-input.value-outside-domain', + [...path, 'value'], + `The ${measurement.kind} value is outside its catalog-owned value domain.`, + ) } } -const normalizedProviderRecordShape = { - source: sourceSchema, - measurements: z.array(measurementSchema).nonempty(), -} as const - const refineMeasurements = ( value: { readonly measurements: ReadonlyArray> @@ -371,51 +347,14 @@ const refineMeasurements = ( } const normalizedProviderRecordSchema = z - .strictObject(normalizedProviderRecordShape) - .superRefine(refineMeasurements) - -const scalarOutputRoles = providerScalarOutputRoles as Readonly< - Record>>>> -> - -const connectedProviderMeasurementKinds = [ - ...new Set( - Object.values(scalarOutputRoles).flatMap((sources) => - Object.values(sources).flatMap((mapping) => Object.keys(mapping)), - ), - ), -] as [ConnectedProviderMeasurementKind, ...ConnectedProviderMeasurementKind[]] - -const providerMeasurementBundleInputSchema = z .strictObject({ - ...normalizedProviderRecordShape, - subject: providerPatientReferenceSchema, - application: applicationDeviceSchema, - gatewayApplication: gatewayApplicationSchema.optional(), - eventSequence: z.string().regex(/^[1-9]\d*$/u), - deploymentIdentity: deploymentIdentitySchema, - nativeIdentifierDisclosure: governedSourceIdentifierSchema.optional(), - occurred: primitiveInstantSchema, - recorded: primitiveInstantSchema, - assembled: primitiveInstantSchema, - repositoryIds: z - .strictObject({ - bundle: fhirIdSchema.optional(), - observations: z - .partialRecord( - z.enum(connectedProviderMeasurementKinds), - fhirIdSchema, - ) - .optional(), - provenance: fhirIdSchema.optional(), - }) - .optional(), - researchStudyReferences: z - .array(providerResearchStudyReferenceSchema) - .optional(), + source: sourceSchema, + measurements: z.array(measurementSchema).nonempty(), }) .superRefine(refineMeasurements) +type ParsedRecord = z.infer + const providerSourceMapping = ( provider: ConnectedProvider, sourceType: string, @@ -485,41 +424,34 @@ const isCompleteCivilDay = (start: string, end: string): boolean => { ) } -const recordEffectiveIssues = (input: { - readonly source: z.infer - readonly measurements: ReadonlyArray> -}): readonly Issue[] => { +const recordEffectiveIssues = (record: ParsedRecord): readonly Issue[] => { const rule = providerRecordEffectiveRule( - input.source.adapter.provider, - input.source.sourceType, + record.source.adapter.provider, + record.source.sourceType, ) if (rule === undefined) return [] - const first = input.measurements[0]?.effective + const first = record.measurements[0]?.effective if (first?.kind !== 'period' || !isCompleteCivilDay(first.start, first.end)) { return [ - { - severity: 'error', - code: 'value-mismatch', - path: ['measurements', 0, 'effective'], - message: - 'This source record requires the complete source civil day as a midnight-to-midnight Period.', - }, + refuse( + 'mobile-input.effective-period-invalid', + ['measurements', 0, 'effective'], + 'This source record requires the complete source civil day as a midnight-to-midnight Period.', + ), ] } - return input.measurements.flatMap((measurement, index) => + return record.measurements.flatMap((measurement, index) => ( measurement.effective.kind !== 'period' || measurement.effective.start !== first.start || measurement.effective.end !== first.end ) ? [ - { - severity: 'error' as const, - code: 'value-mismatch' as const, - path: ['measurements', index, 'effective'], - message: - 'Every output from this source record must share the same complete civil-day Period.', - }, + refuse( + 'mobile-input.effective-period-invalid', + ['measurements', index, 'effective'], + 'Every output from this source record must share the same complete civil-day Period.', + ), ] : [], ) @@ -529,9 +461,14 @@ export const providerOutputRole = ( provider: ConnectedProvider, sourceType: string, kind: string, -): string | undefined => { - return providerSourceMapping(provider, sourceType)?.[kind] -} +): string | undefined => providerSourceMapping(provider, sourceType)?.[kind] + +/** The measurement kinds one catalog source row admits, in catalog order. */ +export const providerSourceKinds = ( + provider: ConnectedProvider, + sourceType: string, +): readonly string[] => + Object.keys(providerSourceMapping(provider, sourceType) ?? {}) /** Exact catalog-owned HMAC coordinates for one Provider Observation output. */ export const providerOutputCoordinates = ( @@ -549,93 +486,58 @@ export const providerOutputCoordinates = ( : { outputRole, outputDiscriminator } } -const recordMappingIssues = (input: { - readonly source: z.infer - readonly measurements: ReadonlyArray> - readonly repositoryIds?: - | { - readonly observations?: - | Readonly>> - | undefined - } - | undefined -}): readonly Issue[] => { +const recordMappingIssues = (record: ParsedRecord): readonly Issue[] => { const mapping = providerSourceMapping( - input.source.adapter.provider, - input.source.sourceType, + record.source.adapter.provider, + record.source.sourceType, ) if (mapping === undefined) { return [ - { - severity: 'error', - code: 'unsupported-measurement', - path: ['source', 'sourceType'], - message: `${input.source.adapter.provider}/${input.source.sourceType} does not have a supported scalar mapping.`, - }, + refuse( + 'mobile-input.unsupported-source-type', + ['source', 'sourceType'], + `${record.source.adapter.provider}/${record.source.sourceType} does not have a supported scalar mapping.`, + ), ] } - const findings: Issue[] = [] - findings.push( - ...providerScopeIdentifierIssues( - input.source.adapter.provider, - input.source.providerScopeIdentifier, - ), - ) - const kinds = input.measurements.map(({ kind }) => kind) + const kinds = record.measurements.map(({ kind }) => kind) for (const [index, kind] of kinds.entries()) { if (!Object.hasOwn(mapping, kind)) { - findings.push({ - severity: 'error', - code: 'unsupported-measurement', - path: ['measurements', index, 'kind'], - message: `${input.source.adapter.provider}/${input.source.sourceType} does not have a supported scalar mapping for ${kind}.`, - }) + findings.push( + refuse( + 'mobile-input.unsupported-source-value', + ['measurements', index, 'kind'], + `${record.source.adapter.provider}/${record.source.sourceType} does not have a supported scalar mapping for ${kind}.`, + ), + ) } } if (new Set(kinds).size !== kinds.length) { - findings.push({ - severity: 'error', - code: 'duplicate-identifier', - path: ['measurements'], - message: + findings.push( + refuse( + 'mobile-input.value-shape-invalid', + ['measurements'], 'A source record may emit each admitted measurement kind at most once.', - }) - } - for (const kind of Object.keys(input.repositoryIds?.observations ?? {})) { - if (!kinds.includes(kind)) { - findings.push({ - severity: 'error', - code: 'invalid-reference', - path: ['repositoryIds', 'observations', kind], - message: - 'A repository Observation id requires a matching emitted measurement.', - }) - } + ), + ) } - findings.push(...recordEffectiveIssues(input)) - return findings + // A refused kind has no admitted effective time to hold to the record rule. + return findings.length > 0 ? findings : recordEffectiveIssues(record) } -const sortMeasurements = < - Value extends { - readonly source: z.infer - readonly measurements: ReadonlyArray> - }, ->( - value: Value, -): Value => { +const sortMeasurements = (record: ParsedRecord): ParsedRecord => { const mapping = providerSourceMapping( - value.source.adapter.provider, - value.source.sourceType, + record.source.adapter.provider, + record.source.sourceType, ) - if (mapping === undefined) return value + if (mapping === undefined) return record const order = new Map( Object.keys(mapping).map((kind, index) => [kind, index] as const), ) return { - ...value, - measurements: [...value.measurements].sort( + ...record, + measurements: [...record.measurements].sort( (left, right) => (order.get(left.kind) ?? Number.MAX_SAFE_INTEGER) - (order.get(right.kind) ?? Number.MAX_SAFE_INTEGER), @@ -645,15 +547,20 @@ const sortMeasurements = < /** * Parses the provider-neutral handoff produced by an external provider adapter. - * Raw provider payload fields are rejected rather than retained or stripped. + * Raw provider payload fields are refused rather than retained or stripped, and every + * refusal carries one `mobile-input.*` registry code. */ export const parseNormalizedProviderRecord = ( input: unknown, ): Result => { const snapshot = cloneJsonValue(input) - if (!snapshot.ok) return snapshot - const result = normalizedProviderRecordSchema.safeParse(snapshot.value) - if (!result.success) return issues(result.error.issues.map(zodIssueToIssue)) + if (!snapshot.ok) { + return issues(snapshot.issues.map((issue) => snapshotRefusal(input, issue))) + } + const result = normalizedProviderRecordSchema.safeParse(snapshot.value, { + reportInput: true, + }) + if (!result.success) return issues(refusalIssues(result.error)) const mappingIssues = recordMappingIssues(result.data) if (mappingIssues.length > 0) return issues(mappingIssues) return ok( @@ -661,109 +568,13 @@ export const parseNormalizedProviderRecord = ( ) } -/** The validated graph input together with the identities its derivation proved. */ -export interface ParsedProviderMeasurementBundle { - readonly input: ProviderMeasurementBundleInput - readonly identities: ProviderIdentities -} - -/** Strict runtime boundary that also derives the graph identities exactly once. */ -export const parseProviderMeasurementBundle = ( +/** Strict boundary for the deployment policy of one conversion; faults, not refusals. */ +export const parseProviderConversionOptions = ( input: unknown, -): Result => { +): Result => { const snapshot = cloneJsonValue(input) if (!snapshot.ok) return snapshot - const result = providerMeasurementBundleInputSchema.safeParse(snapshot.value) + const result = conversionOptionsSchema.safeParse(snapshot.value) if (!result.success) return issues(result.error.issues.map(zodIssueToIssue)) - const disclosureIssues = governedSourceIdentifierIssues( - result.data.nativeIdentifierDisclosure, - result.data.source.sourceNativeId, - result.data.deploymentIdentity, - ) - if (disclosureIssues.length > 0) return issues(disclosureIssues) - const researchStudyIssues: Issue[] = [] - const researchStudyKeys = (result.data.researchStudyReferences ?? []).map( - ({ identifier }) => - `${identifier.system.length}:${identifier.system}${identifier.value.length}:${identifier.value}`, - ) - if (new Set(researchStudyKeys).size !== researchStudyKeys.length) { - researchStudyIssues.push({ - severity: 'error', - code: 'duplicate-identifier', - path: ['researchStudyReferences'], - message: 'ResearchStudy references must be unique.', - }) - } - if (researchStudyIssues.length > 0) return issues(researchStudyIssues) - const mappingIssues = recordMappingIssues(result.data) - if (mappingIssues.length > 0) return issues(mappingIssues) - const sourceMapping = providerSourceMapping( - result.data.source.adapter.provider, - result.data.source.sourceType, - ) - if ( - result.data.nativeIdentifierDisclosure !== undefined && - (sourceMapping === undefined || - Object.keys(sourceMapping).length !== 1 || - result.data.measurements.length !== 1) - ) { - return issues([ - { - severity: 'error', - code: 'value-mismatch', - path: ['nativeIdentifierDisclosure'], - message: - 'The Provider catalog must designate one unique one-to-one Observation before a governed source Identifier may be disclosed; ambiguous multi-output records must omit it.', - }, - ]) - } - const sorted = sortMeasurements(result.data) - const outputCoordinates = sorted.measurements.map(({ kind }) => - providerOutputCoordinates( - sorted.source.adapter.provider, - sorted.source.sourceType, - kind, - ), - ) - const definedOutputCoordinates = outputCoordinates.filter( - (coordinates): coordinates is ProviderOutputCoordinates => - coordinates !== undefined, - ) - if (definedOutputCoordinates.length !== outputCoordinates.length) { - return issues([ - { - severity: 'error', - code: 'unsupported-measurement', - path: ['measurements'], - message: - 'Every emitted measurement requires a catalog-owned Provider output role.', - }, - ]) - } - const identities = deriveProviderIdentities({ - provider: sorted.source.adapter.provider, - providerScopeIdentifier: sorted.source.providerScopeIdentifier, - sourceType: sorted.source.sourceType, - sourceNativeId: sorted.source.sourceNativeId, - outputs: definedOutputCoordinates.map( - ({ outputRole, outputDiscriminator }) => ({ - kind: 'provider-output' as const, - outputRole, - outputDiscriminator, - }), - ), - eventSequence: sorted.eventSequence, - deployment: sorted.deploymentIdentity, - }) - if (!identities.ok) return identities - return ok({ - input: deepFreeze(sorted) as ProviderMeasurementBundleInput, - identities: identities.value, - }) + return ok(deepFreeze(result.data)) } - -/** Strict runtime boundary for the complete deterministic graph input. */ -export const parseProviderMeasurementBundleInput = ( - input: unknown, -): Result => - mapResult(parseProviderMeasurementBundle(input), ({ input: value }) => value) diff --git a/packages/fhir/src/providers/recording.ts b/packages/fhir/src/providers/recording.ts index 9bd56fdc..d1511eda 100644 --- a/packages/fhir/src/providers/recording.ts +++ b/packages/fhir/src/providers/recording.ts @@ -9,23 +9,23 @@ import { sha1 } from '@noble/hashes/legacy.js' import { z } from 'zod' import { - assemblerAgent, + makeConversionProvenance, + occurredFor, + resolveContextGraph, + type ContextGraph, +} from './context-graph.js' +import { coding, deduplicateIdentifiedEntries, governedSourceIdentifier, identifiedEntry, identifier, - makeApplicationDevice, - makeHostDevice, - provenanceActivity, resourceId, - sourceEntityAgent, } from './graph.js' import { - deriveApplicationEntryIdentity, deriveProviderIdentities, deriveWriterRecordIdentifier, - resolveHostIdentity, + type ProviderIdentities, } from './identity.js' import { EXTENSIONS, @@ -35,23 +35,23 @@ import { } from './profiles.js' import { applicationDeviceSchema, - deploymentIdentitySchema, - fhirIdSchema, - identifierInputSchema, governedSourceIdentifierIssues, - governedSourceIdentifierSchema, - nonBlankStringSchema, - primitiveInstantSchema, - providerPatientReferenceSchema, - providerScopeIdentifierIssues, - providerScopeIdentifierSchema, -} from './provider.js' + effectiveTimeSchema, + nativeIdentifierText, + nonBlankText, + refusal, + writerRecordSchema, +} from './provider-input-schemas.js' +import { parseProviderConversionOptions, refusalIssues } from './provider.js' import type { CanonicalBase64, - ProviderRecordingBundleInput, ConnectedRawProvider, ImmutableRecordingUrl, MediaType, + ProviderConversionOptions, + ProviderRecordingAttachment, + ProviderRecordingConversion, + ProviderRecordingSource, Sha1Base64, } from './types.js' import { @@ -68,20 +68,25 @@ import { ok, decodeCanonicalBase64, encodeBase64, - parseFhirInstant, - zodIssueToIssue, type Issue, type Result, } from '../core/index.js' -import { createEntryIdentity } from '../mobile/identity.js' -import type { IdentifiedEntryIdentityInput } from '../mobile/types.js' import { - parseGroveMobileExchangeBundle, - type GroveMobileExchangeBundle, -} from '../r4/index.js' + createEntryIdentity, + type EntryIdentity, + type RoledIdentifier, +} from '../mobile/identity.js' +import type { + ExchangeEventContext, + ExchangeGraphIdentifiers, + GovernedSourceIdentifierDisclosurePolicy, +} from '../mobile/types.js' +import { groveRuleIssue, type ClientRecordRule } from '../r4/diagnostics.js' +import { parseExchangeGraph, type DocumentReference } from '../r4/index.js' const BASE64 = /^(?:[A-Za-z\d+/]{4})*(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?$/u const MEDIA_TYPE = /^[A-Za-z\d!#$&^_.+-]+\/[A-Za-z\d!#$&^_.+-]+$/u +const MAX_ATTACHMENT_SIZE = 2_147_483_647 const decodeRecordingBase64 = (value: string): Uint8Array | undefined => value.length === 0 || !BASE64.test(value) ? @@ -133,21 +138,6 @@ export const parseMediaType = (value: unknown): Result => { return ok(value as MediaType) } -const parseAttachmentSize = (value: unknown): Result => { - if ( - typeof value !== 'number' || - !Number.isInteger(value) || - value < 0 || - value > 2_147_483_647 - ) { - return err( - 'out-of-range', - 'Attachment.size must be an integer in the FHIR R4 unsignedInt range 0...2147483647.', - ) - } - return ok(value) -} - export const parseImmutableRecordingUrl = ( value: unknown, ): Result => { @@ -176,6 +166,22 @@ export const parseImmutableRecordingUrl = ( return ok(value as ImmutableRecordingUrl) } +// A primitive parser's verdict, restated as the refusal the registry names for it. +const refusing = + (parse: (value: unknown) => Result, code: ClientRecordRule) => + (value: unknown, context: z.core.$RefinementCtx): void => { + const parsed = parse(value) + if (!parsed.ok) { + context.addIssue({ + code: 'custom', + ...refusal( + code, + parsed.issues[0]?.message ?? 'Invalid attachment field.', + ), + }) + } + } + const providerValues = Object.keys(providerRawOutputRoles) as [ ConnectedRawProvider, ...ConnectedRawProvider[], @@ -186,30 +192,31 @@ const recordingSourceSchema = z.strictObject({ kind: z.literal('providers'), provider: z.enum(providerValues), }), - providerScopeIdentifier: providerScopeIdentifierSchema, - sourceType: nonBlankStringSchema, - sourceNativeId: nonBlankStringSchema, - dataOrigin: applicationDeviceSchema, - writerRecord: z - .strictObject({ - applicationIdentifier: identifierInputSchema, - nativeRecordId: nonBlankStringSchema, - version: z - .string() - .regex(/^(?:0|[1-9]\d*)$/u) - .optional(), - }) - .optional(), + sourceType: nonBlankText, + sourceNativeId: nativeIdentifierText, + writer: applicationDeviceSchema, + effective: effectiveTimeSchema, + writerRecord: writerRecordSchema.optional(), }) const recordingAttachmentBase = { - contentType: z.string().min(1), - title: nonBlankStringSchema.optional(), + contentType: z + .string() + .superRefine(refusing(parseMediaType, 'mobile-input.value-shape-invalid')), + title: nonBlankText.optional(), format: z.literal( 'provider-recording', + refusal( + 'mobile-input.unsupported-source-value', + 'Expected a registered provider recording format.', + ), ) satisfies z.ZodType, payloadAssertion: z.enum( providerAdapterCatalog.rawPayloadAdmission.allowedAssertions, + refusal( + 'mobile-input.value-shape-invalid', + 'Expected exactly one catalog-derived payload admission assertion.', + ), ), } as const @@ -217,188 +224,136 @@ const recordingAttachmentSchema = z.discriminatedUnion('kind', [ z.strictObject({ ...recordingAttachmentBase, kind: z.literal('embedded'), - dataBase64: z.string().min(1), + dataBase64: z + .string() + .superRefine( + refusing(parseCanonicalBase64, 'mobile-input.empty-recording-series'), + ), }), z.strictObject({ ...recordingAttachmentBase, kind: z.literal('external'), - url: z.string().min(1), - size: z.number(), - hash: z.string().min(1), + url: z + .string() + .superRefine( + refusing( + parseImmutableRecordingUrl, + 'mobile-input.value-shape-invalid', + ), + ), + size: z + .number() + .int() + .min(0) + .refine( + (value) => value <= MAX_ATTACHMENT_SIZE, + refusal( + 'mobile-input.recording-payload-too-large', + 'Attachment.size exceeds the FHIR R4 unsignedInt range.', + ), + ), + hash: z + .string() + .superRefine( + refusing(parseSha1Base64, 'mobile-input.value-shape-invalid'), + ), immutabilityAssurance: z.literal('immutable-version-specific'), }), ]) -const recordingBundleInputSchema = z.strictObject({ - source: recordingSourceSchema, - attachment: recordingAttachmentSchema, - subject: providerPatientReferenceSchema, - application: applicationDeviceSchema, - eventSequence: z.string().regex(/^[1-9]\d*$/u), - deploymentIdentity: deploymentIdentitySchema, - nativeIdentifierDisclosure: governedSourceIdentifierSchema.optional(), - documentDate: primitiveInstantSchema, - occurred: primitiveInstantSchema, - recorded: primitiveInstantSchema, - assembled: primitiveInstantSchema, - repositoryIds: z - .strictObject({ - bundle: fhirIdSchema.optional(), - document: fhirIdSchema.optional(), - provenance: fhirIdSchema.optional(), - }) - .optional(), -}) - const hasRawMapping = ( provider: ConnectedRawProvider, sourceType: string, ): boolean => Object.hasOwn(providerRawOutputRoles[provider], sourceType) -/** Strict parser for the closed Provider mapped-standard facade. */ -export const parseProviderRecordingBundleInput = ( +const refuse = ( + code: ClientRecordRule, + path: ReadonlyArray, + message: string, +): Issue => groveRuleIssue(code, path, { message }) + +/** Strict boundary for one already-obtained provider recording's source record. */ +export const parseProviderRecordingSource = ( input: unknown, -): Result => { +): Result => { const snapshot = cloneJsonValue(input) - if (!snapshot.ok) return snapshot - const parsed = recordingBundleInputSchema.safeParse(snapshot.value) - if (!parsed.success) return issues(parsed.error.issues.map(zodIssueToIssue)) - - const findings: Issue[] = [] - findings.push( - ...providerScopeIdentifierIssues( - parsed.data.source.adapter.provider, - parsed.data.source.providerScopeIdentifier, - ), - ) - findings.push( - ...governedSourceIdentifierIssues( - parsed.data.nativeIdentifierDisclosure, - parsed.data.source.sourceNativeId, - parsed.data.deploymentIdentity, - ), - ) - if ( - !hasRawMapping( - parsed.data.source.adapter.provider, - parsed.data.source.sourceType, - ) - ) { - findings.push({ - severity: 'error', - code: 'unsupported-measurement', - path: ['source', 'sourceType'], - message: `${parsed.data.source.adapter.provider}/${parsed.data.source.sourceType} is not admitted as a Provider native recording.`, - }) + if (!snapshot.ok) { + return issues([ + refuse( + 'mobile-input.value-shape-invalid', + [], + snapshot.issues[0]?.message ?? '', + ), + ]) } - const checks: ReadonlyArray< - readonly [Result, ReadonlyArray] - > = [ - [parseFhirInstant(parsed.data.documentDate), ['documentDate']], - [parseFhirInstant(parsed.data.occurred), ['occurred']], - [parseFhirInstant(parsed.data.recorded), ['recorded']], - [parseFhirInstant(parsed.data.assembled), ['assembled']], - [ - parseMediaType(parsed.data.attachment.contentType), - ['attachment', 'contentType'], - ], - ...(parsed.data.attachment.kind === 'embedded' ? - [ - [ - parseCanonicalBase64(parsed.data.attachment.dataBase64), - ['attachment', 'dataBase64'], - ] as const, - ] - : [ - [ - parseImmutableRecordingUrl(parsed.data.attachment.url), - ['attachment', 'url'], - ] as const, - [ - parseAttachmentSize(parsed.data.attachment.size), - ['attachment', 'size'], - ] as const, - [ - parseSha1Base64(parsed.data.attachment.hash), - ['attachment', 'hash'], - ] as const, - ]), - ] - for (const [check, path] of checks) { - if (!check.ok) { - findings.push( - ...check.issues.map((finding) => ({ ...finding, path: [...path] })), - ) - } + const parsed = recordingSourceSchema.safeParse(snapshot.value, { + reportInput: true, + }) + if (!parsed.success) return issues(refusalIssues(parsed.error)) + if (!hasRawMapping(parsed.data.adapter.provider, parsed.data.sourceType)) { + return issues([ + refuse( + 'mobile-input.unsupported-source-type', + ['sourceType'], + `${parsed.data.adapter.provider}/${parsed.data.sourceType} is not admitted as a Provider native recording.`, + ), + ]) } + return ok(deepFreeze(parsed.data) as unknown as ProviderRecordingSource) +} - const declaredContentTypes: readonly string[] = - groveRecordingFormatRegistry.formats[parsed.data.attachment.format] - .contentTypes - if (!declaredContentTypes.includes(parsed.data.attachment.contentType)) { - findings.push({ - severity: 'error', - code: 'value-mismatch', - path: ['attachment', 'contentType'], - message: `Recording contentType must be one of ${declaredContentTypes.join(', ')} for the declared ${parsed.data.attachment.format} registry format.`, - }) +/** Strict boundary for the caller-supplied recording payload and its admission assertion. */ +export const parseProviderRecordingAttachment = ( + input: unknown, +): Result => { + const snapshot = cloneJsonValue(input) + if (!snapshot.ok) { + return issues([ + refuse( + 'mobile-input.value-shape-invalid', + [], + snapshot.issues[0]?.message ?? '', + ), + ]) } - - if (findings.length === 0) { - const identity = deriveProviderIdentities({ - provider: parsed.data.source.adapter.provider, - providerScopeIdentifier: parsed.data.source.providerScopeIdentifier, - sourceType: parsed.data.source.sourceType, - sourceNativeId: parsed.data.source.sourceNativeId, - outputs: [ - { - kind: 'provider-output', - outputRole: PROVIDER_RECORDING_OUTPUT_ROLE, - outputDiscriminator: PROVIDER_RECORDING_OUTPUT_DISCRIMINATOR, - }, - { - kind: 'provider-artifact', - formatCode: parsed.data.attachment.format, - partIndex: '0', - }, - ], - eventSequence: parsed.data.eventSequence, - deployment: parsed.data.deploymentIdentity, - }) - if (!identity.ok) findings.push(...identity.issues) + const parsed = recordingAttachmentSchema.safeParse(snapshot.value, { + reportInput: true, + }) + if (!parsed.success) return issues(refusalIssues(parsed.error)) + const declaredContentTypes: readonly string[] = + groveRecordingFormatRegistry.formats[parsed.data.format].contentTypes + if (!declaredContentTypes.includes(parsed.data.contentType)) { + return issues([ + refuse( + 'mobile-input.unsupported-source-value', + ['contentType'], + `Recording contentType must be one of ${declaredContentTypes.join(', ')} for the declared ${parsed.data.format} registry format.`, + ), + ]) } - - if (findings.length > 0) return issues(findings) - return ok(deepFreeze(parsed.data) as unknown as ProviderRecordingBundleInput) + return ok(deepFreeze(parsed.data) as unknown as ProviderRecordingAttachment) } interface RecordingGraphIdentities { - readonly sourceRecord: { - readonly system: import('../core/index.js').AbsoluteUri - readonly value: string - } - readonly event: { - readonly system: import('../core/index.js').AbsoluteUri - readonly value: string - } - readonly document: IdentifiedEntryIdentityInput - readonly sourceArtifact: import('../mobile/types.js').CompleteIdentifierInput - readonly provenance: IdentifiedEntryIdentityInput - readonly application: IdentifiedEntryIdentityInput - readonly applicationHost?: IdentifiedEntryIdentityInput - readonly dataOrigin: IdentifiedEntryIdentityInput - readonly dataOriginHost?: IdentifiedEntryIdentityInput - readonly writerRecord?: import('../mobile/types.js').CompleteIdentifierInput + readonly connected: ProviderIdentities + readonly document: EntryIdentity + readonly sourceArtifact: RoledIdentifier + readonly provenance: EntryIdentity + readonly writerRecord?: RoledIdentifier } -const resolveRecordingGraphIdentities = ( - input: ProviderRecordingBundleInput, +const resolveRecordingIdentities = ( + source: ProviderRecordingSource, + attachment: ProviderRecordingAttachment, + graph: ContextGraph, ): Result => { + const { context } = graph + const repositoryIds = context.repositoryIds ?? {} const connected = deriveProviderIdentities({ - provider: input.source.adapter.provider, - providerScopeIdentifier: input.source.providerScopeIdentifier, - sourceType: input.source.sourceType, - sourceNativeId: input.source.sourceNativeId, + provider: source.adapter.provider, + repositoryScope: context.repositoryScope, + sourceType: source.sourceType, + sourceNativeId: source.sourceNativeId, outputs: [ { kind: 'provider-output', @@ -407,82 +362,43 @@ const resolveRecordingGraphIdentities = ( }, { kind: 'provider-artifact', - formatCode: input.attachment.format, + formatCode: attachment.format, partIndex: '0', }, ], - eventSequence: input.eventSequence, - deployment: input.deploymentIdentity, + event: context.event, + scope: context.identityScope, }) if (!connected.ok) return connected - const documentIdentifier = connected.value.outputs[0] - const sourceArtifact = connected.value.outputs[1] + const [documentIdentifier, sourceArtifact] = connected.value.outputs if (documentIdentifier === undefined || sourceArtifact === undefined) { - return err( - 'missing-required', + throw new Error( 'The recording output and source artifact identities are required.', ) } const document = createEntryIdentity( documentIdentifier, - input.repositoryIds?.document, + repositoryIds['primary-output'], ) if (!document.ok) return document const provenance = createEntryIdentity( connected.value.provenanceNode, - input.repositoryIds?.provenance, + repositoryIds.provenance, ) if (!provenance.ok) return provenance - const application = deriveApplicationEntryIdentity( - input.deploymentIdentity, - connected.value.event, - input.application, - ) - if (!application.ok) return application - const applicationHost = resolveHostIdentity( - input.application, - input.deploymentIdentity, - connected.value.event, - ) - if (!applicationHost.ok) return applicationHost - const dataOrigin = deriveApplicationEntryIdentity( - input.deploymentIdentity, - connected.value.event, - input.source.dataOrigin, - ) - if (!dataOrigin.ok) return dataOrigin - const dataOriginHost = resolveHostIdentity( - input.source.dataOrigin, - input.deploymentIdentity, - connected.value.event, - ) - if (!dataOriginHost.ok) return dataOriginHost - const writerRecord = - input.source.writerRecord === undefined ? - undefined - : deriveWriterRecordIdentifier( - input.deploymentIdentity, - input.source.writerRecord.applicationIdentifier, - input.source.writerRecord.nativeRecordId, - ) - if (writerRecord !== undefined && !writerRecord.ok) return writerRecord - + source.writerRecord === undefined ? + ok(undefined) + : deriveWriterRecordIdentifier(context.identityScope, source.writerRecord) + if (!writerRecord.ok) return writerRecord return ok({ - sourceRecord: connected.value.sourceRecord, - event: connected.value.event, + connected: connected.value, document: document.value, sourceArtifact, provenance: provenance.value, - application: application.value, - dataOrigin: dataOrigin.value, - ...(applicationHost.value === undefined ? - {} - : { applicationHost: applicationHost.value }), - ...(dataOriginHost.value === undefined ? + ...(writerRecord.value === undefined ? {} - : { dataOriginHost: dataOriginHost.value }), - ...(writerRecord === undefined ? {} : { writerRecord: writerRecord.value }), + : { writerRecord: writerRecord.value }), }) } @@ -490,10 +406,7 @@ const PROVIDER_TITLES = Object.fromEntries( providerAdapterCatalog.providers.map(({ id, title }) => [id, title]), ) as Readonly> -const providerTitle = (provider: ConnectedRawProvider): string => - PROVIDER_TITLES[provider] - -const attachmentFor = (input: ProviderRecordingBundleInput['attachment']) => { +const attachmentFor = (input: ProviderRecordingAttachment) => { if (input.kind === 'external') { return { contentType: input.contentType, @@ -515,167 +428,157 @@ const attachmentFor = (input: ProviderRecordingBundleInput['attachment']) => { } } -/** - * Builds the complete deterministic R4 graph for one already-obtained native - * provider recording. This pure facade performs no fetching, authentication, - * webhook handling, vendor parsing, or credential management. - */ -export const buildProviderRecordingBundle = ( - input: ProviderRecordingBundleInput, -): Result => { - const parsed = parseProviderRecordingBundleInput(input) - if (!parsed.ok) return parsed - const validated = parsed.value - const identities = resolveRecordingGraphIdentities(validated) - if (!identities.ok) return identities - - const sourceCode = `${validated.source.adapter.provider}/${validated.source.sourceType}` - const document = { - resourceType: 'DocumentReference' as const, - ...resourceId(identities.value.document), - meta: { - profile: [ - PROFILES.sensorRecordingDocument, - PROFILES.providerRecordingDocument, - ], - }, - extension: [ - { - url: EXTENSIONS.provider, - valueCode: validated.source.adapter.provider, - }, - { - url: EXTENSIONS.providerSourceType, - valueCode: sourceCode, - }, - ...(validated.source.writerRecord?.version === undefined ? - [] - : [ - { - url: EXTENSIONS.writerRecordVersion, - valueString: validated.source.writerRecord.version, - }, - ]), +const makeDocument = ( + source: ProviderRecordingSource, + attachment: ProviderRecordingAttachment, + identities: RecordingGraphIdentities, + graph: ContextGraph, + disclosure: GovernedSourceIdentifierDisclosurePolicy, +): DocumentReference => ({ + resourceType: 'DocumentReference' as const, + ...resourceId(identities.document), + meta: { + profile: [ + PROFILES.sensorRecordingDocument, + PROFILES.providerRecordingDocument, ], - identifier: [ - identifier(identities.value.sourceRecord), - identifier(identities.value.document.identifier), - identifier(identities.value.sourceArtifact), - ...(identities.value.writerRecord === undefined ? - [] - : [identifier(identities.value.writerRecord)]), - ...(validated.nativeIdentifierDisclosure === undefined ? - [] - : [governedSourceIdentifier(validated.nativeIdentifierDisclosure)]), - ], - status: 'current' as const, - type: { - text: `${providerTitle(validated.source.adapter.provider)} ${validated.source.sourceType} archive`, + }, + extension: [ + { url: EXTENSIONS.provider, valueCode: source.adapter.provider }, + { + url: EXTENSIONS.providerSourceType, + valueCode: `${source.adapter.provider}/${source.sourceType}`, }, - subject: { - type: validated.subject.type, - identifier: { - system: validated.subject.identifier.system, - value: validated.subject.identifier.value, - }, + ...(source.writerRecord?.version === undefined ? + [] + : [ + { + url: EXTENSIONS.writerRecordVersion, + valueString: source.writerRecord.version, + }, + ]), + ], + identifier: [ + identifier(identities.connected.sourceRecord), + identifier(identities.document.identifier), + identifier(identities.sourceArtifact), + ...(identities.writerRecord === undefined ? + [] + : [identifier(identities.writerRecord)]), + ...(disclosure.kind === 'omit' ? + [] + : [governedSourceIdentifier(disclosure, source.sourceNativeId)]), + ], + status: 'current' as const, + type: { + text: `${PROVIDER_TITLES[source.adapter.provider]} ${source.sourceType} archive`, + }, + subject: graph.subject, + date: graph.context.conversionInstant, + author: [{ reference: graph.application.fullUrl }], + content: [ + { + attachment: attachmentFor(attachment), + format: coding( + groveRecordingFormatRegistry.codeSystem, + attachment.format, + groveRecordingFormatRegistry.formats[attachment.format].title, + ), }, - date: validated.documentDate, - author: [{ reference: identities.value.application.fullUrl }], - content: [ - { - attachment: attachmentFor(validated.attachment), - format: coding( - groveRecordingFormatRegistry.codeSystem, - validated.attachment.format, - groveRecordingFormatRegistry.formats[validated.attachment.format] - .title, - ), - }, - ], + ], +}) + +/** + * Builds the exchange graph for one already-obtained native provider recording. + * + * This pure facade performs no fetching, authentication, webhook handling, vendor parsing, + * or credential management. Its DocumentReference cannot carry the research-study + * extension, so a context naming studies is a fault rather than a silent omission. + */ +export const buildProviderRecordingGraph = ( + source: ProviderRecordingSource, + attachment: ProviderRecordingAttachment, + context: ExchangeEventContext, + options: ProviderConversionOptions = {}, +): Result => { + const parsedSource = parseProviderRecordingSource(source) + if (!parsedSource.ok) return parsedSource + const parsedAttachment = parseProviderRecordingAttachment(attachment) + if (!parsedAttachment.ok) return parsedAttachment + const parsedOptions = parseProviderConversionOptions(options) + if (!parsedOptions.ok) return parsedOptions + const graph = resolveContextGraph(context, parsedSource.value.writer, false) + if (!graph.ok) return graph + const disclosure = parsedOptions.value.nativeIdentifierDisclosure ?? { + kind: 'omit', } - const application = makeApplicationDevice({ - ...validated.application, - identity: identities.value.application, - ...(identities.value.applicationHost === undefined ? - {} - : { parentReference: identities.value.applicationHost.fullUrl }), - }) - const applicationHost = - ( - validated.application.host === undefined || - identities.value.applicationHost === undefined - ) ? - undefined - : makeHostDevice({ - ...validated.application.host, - identity: identities.value.applicationHost, - }) - const dataOrigin = makeApplicationDevice({ - ...validated.source.dataOrigin, - identity: identities.value.dataOrigin, - ...(identities.value.dataOriginHost === undefined ? - {} - : { parentReference: identities.value.dataOriginHost.fullUrl }), + const disclosureIssues = governedSourceIdentifierIssues( + disclosure, + graph.value.context.identityScope, + ) + if (disclosureIssues.length > 0) return issues(disclosureIssues) + const identities = resolveRecordingIdentities( + parsedSource.value, + parsedAttachment.value, + graph.value, + ) + if (!identities.ok) return identities + const document = makeDocument( + parsedSource.value, + parsedAttachment.value, + identities.value, + graph.value, + disclosure, + ) + const provenance = makeConversionProvenance({ + identity: identities.value.provenance, + profile: PROFILES.providerConversionProvenance, + targets: [identities.value.document.fullUrl], + occurred: occurredFor([parsedSource.value.effective]), + recorded: graph.value.context.conversionInstant, + sourceRecord: identities.value.connected.sourceRecord, + graph: graph.value, }) - const dataOriginHost = - ( - validated.source.dataOrigin.host === undefined || - identities.value.dataOriginHost === undefined - ) ? - undefined - : makeHostDevice({ - ...validated.source.dataOrigin.host, - identity: identities.value.dataOriginHost, - }) - const provenance = { - resourceType: 'Provenance' as const, - ...resourceId(identities.value.provenance), - meta: { - profile: [PROFILES.providerConversionProvenance], - }, - target: [{ reference: identities.value.document.fullUrl }], - occurredDateTime: validated.occurred, - recorded: validated.recorded, - activity: provenanceActivity(), - agent: [assemblerAgent(identities.value.application.fullUrl)], - entity: [ - { - role: 'source' as const, - what: { identifier: identifier(identities.value.sourceRecord) }, - agent: [sourceEntityAgent(identities.value.dataOrigin.fullUrl)], - }, - ], - } - const entries = deduplicateIdentifiedEntries([ + ...graph.value.leadingEntries, identifiedEntry(identities.value.document, document), - ...(( - dataOriginHost === undefined || - identities.value.dataOriginHost === undefined - ) ? - [] - : [identifiedEntry(identities.value.dataOriginHost, dataOriginHost)]), - identifiedEntry(identities.value.dataOrigin, dataOrigin), - ...(( - applicationHost === undefined || - identities.value.applicationHost === undefined - ) ? - [] - : [identifiedEntry(identities.value.applicationHost, applicationHost)]), - identifiedEntry(identities.value.application, application), + ...graph.value.supportingEntries, identifiedEntry(identities.value.provenance, provenance), ]) if (!entries.ok) return entries - - return parseGroveMobileExchangeBundle({ + const { repositoryIds = {} } = graph.value.context + const parsed = parseExchangeGraph({ resourceType: 'Bundle', - ...(validated.repositoryIds?.bundle === undefined ? - {} - : { id: validated.repositoryIds.bundle }), + ...(repositoryIds.bundle === undefined ? {} : { id: repositoryIds.bundle }), meta: { profile: [PROFILES.mobileBundle] }, - identifier: identifier(identities.value.event), + identifier: identifier(identities.value.connected.event), type: 'collection', - timestamp: validated.assembled, + timestamp: graph.value.context.conversionInstant, entry: entries.value, }) + if (!parsed.ok) return parsed + const identifiers: ExchangeGraphIdentifiers = { + event: identities.value.connected.event, + sourceRecord: identities.value.connected.sourceRecord, + outputs: [identities.value.document.identifier], + provenance: identities.value.provenance.identifier, + applicationSnapshot: graph.value.application.identifier, + hostSnapshot: graph.value.host.identifier, + writerSnapshot: graph.value.writer.identifier, + sourceArtifact: identities.value.sourceArtifact, + ...(graph.value.gatewayApplication === undefined ? + {} + : { + gatewayApplicationSnapshot: graph.value.gatewayApplication.identifier, + }), + ...(identities.value.writerRecord === undefined ? + {} + : { writerRecord: identities.value.writerRecord }), + } + return ok({ + source: parsedSource.value, + identifiers, + graph: parsed.value, + warnings: [], + }) } diff --git a/packages/fhir/src/providers/retraction.ts b/packages/fhir/src/providers/retraction.ts index 7538606d..1d90b838 100644 --- a/packages/fhir/src/providers/retraction.ts +++ b/packages/fhir/src/providers/retraction.ts @@ -6,7 +6,6 @@ // SPDX-License-Identifier: MIT // -import { z } from 'zod' import { concept, deduplicateIdentifiedEntries, @@ -18,553 +17,394 @@ import { } from './graph.js' import { deriveApplicationEntryIdentity, - deriveDeviceSnapshotEntryIdentity, - deriveProviderIdentities, - type DeviceSnapshotRole, + deriveEntryNodeEntryIdentity, + deriveHostEntryIdentity, } from './identity.js' +import { EXTENSIONS, PROFILES, SYSTEMS } from './profiles.js' +import { groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' import { - EXTENSIONS, - PROFILES, - PROVIDER_RECORDING_OUTPUT_DISCRIMINATOR, - PROVIDER_RECORDING_OUTPUT_ROLE, - SYSTEMS, -} from './profiles.js' -import { absoluteUriSchema } from './provider-input-schemas.js' -import { - applicationDeviceSchema, - deploymentIdentitySchema, - fhirIdSchema, - nonBlankStringSchema, - primitiveInstantSchema, - providerScopeIdentifierIssues, - providerScopeIdentifierSchema, -} from './provider.js' -import type { - ConnectedProvider, - ProviderScopeIdentifierInput, -} from './types.js' -import { type groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' -import { - providerAdapterCatalog, - providerRawOutputRoles, - providerScalarOutputDiscriminators, - providerScalarOutputRoles, -} from '../contract/providers.generated.js' -import { - cloneJsonValue, - deepFreeze, issues, - zodIssueToIssue, - type AbsoluteUri, - type FhirId, + parseAbsoluteUri, + parseFhirInstant, type FhirInstant, type Issue, type Result, + type SchemaIssueCode, } from '../core/index.js' -import { groveMobileContract } from '../mobile/contract.js' +import { parseExchangeEventContext } from '../mobile/context.js' import { - createEntryIdentity, - deriveEventIdentifier, - deriveOpaqueIdentifier, - validateDeploymentIdentity, + containsIsolatedSurrogate, + isOpaqueIdentityValue, + type BusinessIdentifier, + type OpaqueIdentityScope, + type RoledIdentifier, } from '../mobile/identity.js' -import type { - ApplicationDeviceInput, - CompleteIdentifierInput, - DeploymentIdentityInput, -} from '../mobile/types.js' +import type { ExchangeEventContext } from '../mobile/types.js' import { - parseGroveMobileRetractionBundle, - type GroveMobileRetractionBundle, + parseRetractionEvent, + type Identifier, + type RetractionEvent, + type RetractionTarget, + type RetractionTargetRole, } from '../r4/index.js' -export type RetractionTargetRole = - keyof typeof groveExchangeProtocol.lifecycle.retraction.targetRoles +const TARGET_ROLES = groveExchangeProtocol.lifecycle.retraction.targetRoles +type Path = ReadonlyArray -/** - * The adapter's own record key for one retracted target, carried verbatim. - * - * It never participates in Grove identity and carries no Grove identifier-role coding. - */ -export interface ProviderRetractionTargetNativeIdentifierInput { - readonly system: AbsoluteUri - readonly value: string -} +const fault = (code: SchemaIssueCode, path: Path, message: string): Issue => ({ + severity: 'error', + code, + path, + message, +}) -interface ProviderRetractionTargetCommon { - readonly nativeIdentifier?: ProviderRetractionTargetNativeIdentifierInput -} +const isNonBlank = (value: unknown): value is string => + typeof value === 'string' && + value.trim() !== '' && + !containsIsolatedSurrogate(value) -export type ProviderRetractionTargetInput = - | (ProviderRetractionTargetCommon & { - readonly role: 'primary-output' - readonly resourceType: 'Observation' - /** Exact closed output role used when the active Observation was emitted. */ - readonly outputRole: string - /** Exact closed discriminator paired with the output role in the catalog. */ - readonly outputDiscriminator: string - }) - | (ProviderRetractionTargetCommon & { - readonly role: 'source-artifact' - readonly resourceType: 'DocumentReference' - readonly formatCode: 'provider-recording' - readonly partIndex: '0' - }) - | (ProviderRetractionTargetCommon & { - readonly role: 'device-snapshot' - readonly resourceType: 'Device' - readonly priorEventSequence: string - readonly deviceRole: DeviceSnapshotRole - readonly sourceDeviceToken: string - }) +// The systems this scope mints for one identifier role, across the kinds that carry it. +const systemsForRole = ( + scope: OpaqueIdentityScope, + role: string, +): ReadonlySet => + new Set( + groveExchangeProtocol.opaqueIdentity.identityKinds + .filter(({ identifierRole }) => identifierRole === role) + .map(({ kind }) => scope.systems.opaque[kind]), + ) -export interface ProviderRetractionInput< - Provider extends ConnectedProvider = ConnectedProvider, -> { - readonly source: { - readonly provider: Provider - readonly providerScopeIdentifier: ProviderScopeIdentifierInput - readonly sourceType: string - readonly sourceNativeId: string - } - readonly targets: readonly [ - ProviderRetractionTargetInput, - ...ProviderRetractionTargetInput[], - ] - readonly application: ApplicationDeviceInput - readonly eventSequence: string - readonly deploymentIdentity: DeploymentIdentityInput - /** Time the producer learned that the source record was no longer exposed. */ - readonly occurred: FhirInstant - /** Time the retraction Provenance assertion was recorded. */ - readonly recorded: FhirInstant - /** Time the immutable retraction Bundle was assembled. */ - readonly assembled: FhirInstant - readonly repositoryIds?: { - readonly bundle?: FhirId - readonly provenance?: FhirId - } -} +const groveSystems = (scope: OpaqueIdentityScope): ReadonlySet => + new Set([ + SYSTEMS.groveIdentifierRole, + scope.systems.event, + scope.systems.entryNode, + ...Object.values(scope.systems.opaque), + ]) -const providerCodes = new Set( - providerAdapterCatalog.providers.map(({ id }) => id as string), -) -const providerSourceTypes = new Map( - providerAdapterCatalog.providers.map(({ id, sourceTypes }) => [ - id as string, - new Set(sourceTypes.map(({ token }) => token as string)), - ]), -) -const POSITIVE_DECIMAL = /^[1-9]\d*$/u +const isRecord = (value: unknown): value is Record => + typeof value === 'object' && value !== null -const comparePositiveDecimals = (left: string, right: string): -1 | 0 | 1 => { - if (left.length !== right.length) return left.length < right.length ? -1 : 1 - if (left === right) return 0 - return left < right ? -1 : 1 +const opaqueIdentifierIssues = ( + value: unknown, + role: string, + scope: OpaqueIdentityScope, + path: Path, +): readonly Issue[] => { + if ( + !isRecord(value) || + value.role !== role || + typeof value.system !== 'string' || + !systemsForRole(scope, role).has(value.system) || + !isOpaqueIdentityValue(value.value) + ) { + return [ + fault( + 'invalid-identifier', + path, + `Expected the exact ${role} identity this scope minted for the retracted node.`, + ), + ] + } + return [] } -const nativeIdentifierSchema = z - .strictObject({ - system: absoluteUriSchema, - value: nonBlankStringSchema, - }) - .optional() - -const retractionTargetSchema = z.discriminatedUnion('role', [ - z.strictObject({ - role: z.literal('primary-output'), - resourceType: z.literal('Observation'), - outputRole: nonBlankStringSchema, - outputDiscriminator: nonBlankStringSchema, - nativeIdentifier: nativeIdentifierSchema, - }), - z.strictObject({ - role: z.literal('source-artifact'), - resourceType: z.literal('DocumentReference'), - formatCode: z.literal('provider-recording'), - partIndex: z.literal('0'), - nativeIdentifier: nativeIdentifierSchema, - }), - z.strictObject({ - role: z.literal('device-snapshot'), - resourceType: z.literal('Device'), - priorEventSequence: z.string().regex(POSITIVE_DECIMAL), - deviceRole: z.enum(['application', 'host', 'recording-device']), - sourceDeviceToken: nonBlankStringSchema, - nativeIdentifier: nativeIdentifierSchema, - }), -]) - -const retractionInputSchema = z.strictObject({ - source: z.strictObject({ - provider: nonBlankStringSchema, - providerScopeIdentifier: providerScopeIdentifierSchema, - sourceType: nonBlankStringSchema, - sourceNativeId: nonBlankStringSchema, - }), - targets: z.array(retractionTargetSchema).nonempty(), - application: applicationDeviceSchema, - eventSequence: z.string().regex(POSITIVE_DECIMAL), - deploymentIdentity: deploymentIdentitySchema, - occurred: primitiveInstantSchema, - recorded: primitiveInstantSchema, - assembled: primitiveInstantSchema, - repositoryIds: z - .strictObject({ - bundle: fhirIdSchema.optional(), - provenance: fhirIdSchema.optional(), - }) - .optional(), -}) - -const targetIsAdmitted = ( - input: ProviderRetractionInput, - target: ProviderRetractionTargetInput, -): boolean => { - if (target.role === 'primary-output') { - const mapping = ( - providerScalarOutputRoles as Readonly< - Record< - string, - Readonly> | undefined>> - > - > - )[input.source.provider]?.[input.source.sourceType] - const discriminators = ( - providerScalarOutputDiscriminators as Readonly< - Record< - string, - Readonly> | undefined>> - > - > - )[input.source.provider]?.[input.source.sourceType] - return ( - mapping !== undefined && - discriminators !== undefined && - Object.keys(mapping).some( - (measurementId) => - mapping[measurementId] === target.outputRole && - discriminators[measurementId] === target.outputDiscriminator, - ) - ) +const nativeIdentifierIssues = ( + value: unknown, + scope: OpaqueIdentityScope, + path: Path, +): readonly Issue[] => { + if (value === undefined) return [] + const candidate = value as Partial + if ( + typeof candidate !== 'object' || + !parseAbsoluteUri(candidate.system).ok || + !isNonBlank(candidate.value) || + Object.keys(candidate).length !== 2 + ) { + return [ + fault( + 'invalid-identifier', + path, + 'A native record identifier is one complete absolute-system Identifier.', + ), + ] } - if (target.role === 'source-artifact') { - return ( - ( - providerRawOutputRoles as Readonly< - Record> | undefined> - > - )[input.source.provider]?.[input.source.sourceType] !== undefined - ) + if (groveSystems(scope).has(candidate.system ?? '')) { + return [ + fault( + 'value-mismatch', + [...path, 'system'], + 'A native record identifier lives in the adapter’s own key space, never a Grove one.', + ), + ] } - return ( - comparePositiveDecimals(target.priorEventSequence, input.eventSequence) < 0 - ) + return [] } -const validateInput = (input: ProviderRetractionInput): readonly Issue[] => { +const targetIssues = ( + target: unknown, + scope: OpaqueIdentityScope, + path: Path, +): readonly Issue[] => { + const candidate = isRecord(target) ? target : undefined + const role = candidate?.role + if ( + candidate === undefined || + typeof role !== 'string' || + !Object.hasOwn(TARGET_ROLES, role) + ) { + return [ + fault( + 'invalid-code', + [...path, 'role'], + 'Expected a closed retraction target role.', + ), + ] + } + const rule = TARGET_ROLES[role as RetractionTargetRole] const findings: Issue[] = [] + if ( + !(rule.resourceTypes as readonly string[]).includes( + String(candidate.resourceType), + ) + ) { + findings.push( + fault( + 'invalid-code', + [...path, 'resourceType'], + `${role} targets declare one of ${rule.resourceTypes.join(', ')}.`, + ), + ) + } findings.push( - ...providerScopeIdentifierIssues( - input.source.provider, - input.source.providerScopeIdentifier, + ...opaqueIdentifierIssues( + candidate.identifier, + rule.identifierRole, + scope, + [...path, 'identifier'], ), + ...nativeIdentifierIssues(candidate.nativeIdentifier, scope, [ + ...path, + 'nativeIdentifier', + ]), ) - const deployment = validateDeploymentIdentity(input.deploymentIdentity) - if (!deployment.ok) { + const keys = Object.keys(candidate).filter( + (key) => + !['role', 'resourceType', 'identifier', 'nativeIdentifier'].includes(key), + ) + if (keys.length > 0) { findings.push( - ...deployment.issues.map((issue) => ({ - ...issue, - path: ['deploymentIdentity', ...issue.path], - })), + fault( + 'schema-invalid', + [...path, keys[0] ?? ''], + 'Unknown target field.', + ), ) } - if (!providerCodes.has(input.source.provider)) { - findings.push({ - severity: 'error', - code: 'invalid-code', - path: ['source', 'provider'], - message: 'Retraction source provider must be catalog-owned.', - }) - } else if ( - !providerSourceTypes - .get(input.source.provider) - ?.has(input.source.sourceType) - ) { - findings.push({ - severity: 'error', - code: 'invalid-code', - path: ['source', 'sourceType'], - message: 'Retraction source type must be catalog-owned for its provider.', - }) - } - for (const [index, target] of input.targets.entries()) { - if (!targetIsAdmitted(input, target)) { - findings.push({ - severity: 'error', - code: 'unsupported-measurement', - path: ['targets', index], - message: - 'The Provider facade can retract only a catalog-admitted primary output, source artifact, or device snapshot.', - }) - } - } - if ( - new Set(input.targets.map((target) => JSON.stringify(target))).size !== - input.targets.length - ) { - findings.push({ - severity: 'error', - code: 'duplicate-identifier', - path: ['targets'], - message: 'Retraction targets must be unique.', - }) - } return findings } -/** Strict runtime boundary for one Provider source-record retraction assertion. */ -export const parseProviderRetractionInput = ( - input: unknown, -): Result => { - const snapshot = cloneJsonValue(input) - if (!snapshot.ok) return snapshot - const parsed = retractionInputSchema.safeParse(snapshot.value) - if (!parsed.success) return issues(parsed.error.issues.map(zodIssueToIssue)) - const value = parsed.data as unknown as ProviderRetractionInput - const findings = validateInput(value) - if (findings.length > 0) return issues(findings) - return { ok: true, value: deepFreeze(value) as ProviderRetractionInput } -} - -const sourceComponents = ( - input: ProviderRetractionInput, -): readonly [string, string, string, string, string] => [ - input.source.provider, - input.source.sourceType, - input.source.providerScopeIdentifier.system, - input.source.providerScopeIdentifier.value, - input.source.sourceNativeId, -] +const pairKey = (identifier: BusinessIdentifier): string => + `${identifier.system.length}:${identifier.system}${identifier.value.length}:${identifier.value}` -const deriveTargetIdentifier = ( - input: ProviderRetractionInput, - target: ProviderRetractionTargetInput, -): Result => { - const source = sourceComponents(input) - if (target.role === 'source-artifact') { - return deriveOpaqueIdentifier(input.deploymentIdentity, 'provider-output', [ - ...source, - PROVIDER_RECORDING_OUTPUT_ROLE, - PROVIDER_RECORDING_OUTPUT_DISCRIMINATOR, - ]) +const targetsIssues = ( + targets: unknown, + scope: OpaqueIdentityScope, +): readonly Issue[] => { + if (!Array.isArray(targets) || targets.length === 0) { + return [ + fault( + 'missing-required', + ['targets'], + 'A retraction names at least one target.', + ), + ] } - if (target.role === 'device-snapshot') { - const priorEvent = deriveEventIdentifier( - input.deploymentIdentity, - target.priorEventSequence, - ) - if (!priorEvent.ok) return priorEvent - return deriveOpaqueIdentifier(input.deploymentIdentity, 'device-snapshot', [ - priorEvent.value.system, - priorEvent.value.value, - target.deviceRole, - target.sourceDeviceToken, - ]) + const findings = targets.flatMap((target, index) => + targetIssues(target, scope, ['targets', index]), + ) + const pairs = new Set() + for (const [index, target] of ( + targets as readonly RetractionTarget[] + ).entries()) { + const identity = target.identifier as BusinessIdentifier | undefined + if (findings.length > 0 || identity === undefined) continue + const key = pairKey(identity) + if (pairs.has(key)) { + findings.push( + fault( + 'duplicate-identifier', + ['targets', index], + 'Retraction targets must be unique.', + ), + ) + } + pairs.add(key) } - return deriveOpaqueIdentifier(input.deploymentIdentity, 'provider-output', [ - ...source, - target.outputRole, - target.outputDiscriminator, - ]) -} - -const compareCanonicalText = (left: string, right: string): -1 | 0 | 1 => { - if (left === right) return 0 - return left < right ? -1 : 1 -} - -interface TargetRoleExtension { - readonly url: string - readonly valueCode: RetractionTargetRole -} - -interface TargetNativeIdentifierExtension { - readonly url: string - readonly valueIdentifier: ProviderRetractionTargetNativeIdentifierInput + return findings } interface TargetReference { - readonly extension: - | readonly [TargetRoleExtension] - | readonly [TargetRoleExtension, TargetNativeIdentifierExtension] - readonly identifier: ReturnType + readonly extension: readonly [ + { readonly url: string; readonly valueCode: RetractionTargetRole }, + ...Array<{ readonly url: string; readonly valueIdentifier: Identifier }>, + ] + readonly identifier: Identifier readonly type: string } -const nativeIdentifierOf = ( - target: TargetReference, -): ProviderRetractionTargetNativeIdentifierInput | undefined => - target.extension[1]?.valueIdentifier +const compareText = (left: string, right: string): number => { + if (left === right) return 0 + return left < right ? -1 : 1 +} const compareTargetReferences = ( left: TargetReference, right: TargetReference, -): number => { - const leftNative = nativeIdentifierOf(left) - const rightNative = nativeIdentifierOf(right) - return ( - compareCanonicalText( - left.extension[0].valueCode, - right.extension[0].valueCode, - ) || - compareCanonicalText(left.type, right.type) || - compareCanonicalText( - left.identifier.system ?? '', - right.identifier.system ?? '', - ) || - compareCanonicalText( - left.identifier.value ?? '', - right.identifier.value ?? '', - ) || - compareCanonicalText(leftNative?.system ?? '', rightNative?.system ?? '') || - compareCanonicalText(leftNative?.value ?? '', rightNative?.value ?? '') +): number => + compareText(left.extension[0].valueCode, right.extension[0].valueCode) || + compareText(left.type, right.type) || + compareText(left.identifier.system ?? '', right.identifier.system ?? '') || + compareText(left.identifier.value ?? '', right.identifier.value ?? '') || + compareText( + left.extension[1]?.valueIdentifier.system ?? '', + right.extension[1]?.valueIdentifier.system ?? '', + ) || + compareText( + left.extension[1]?.valueIdentifier.value ?? '', + right.extension[1]?.valueIdentifier.value ?? '', ) -} + +const targetReference = (target: RetractionTarget): TargetReference => ({ + extension: + target.nativeIdentifier === undefined ? + [{ url: EXTENSIONS.retractionTargetRole, valueCode: target.role }] + : [ + { url: EXTENSIONS.retractionTargetRole, valueCode: target.role }, + { + url: EXTENSIONS.retractionTargetNativeIdentifier, + valueIdentifier: identifier(target.nativeIdentifier), + }, + ], + type: target.resourceType, + identifier: identifier(target.identifier), +}) /** - * Builds one append-only source-retraction assertion. + * Builds one append-only retraction event naming prior graph nodes by typed logical identity. * - * It emits logical typed targets only. It never copies, mutates, tombstones, or marks prior - * clinical resources entered-in-error; receiver lifecycle policy remains separate. + * It never copies, mutates, tombstones, or marks prior clinical resources entered-in-error; + * receiver lifecycle policy remains separate. `retractedAt` is Provenance.occurred[x]; the + * conversion instant is its recorded time and the Bundle timestamp. */ -export const buildProviderRetractionBundle = ( - input: ProviderRetractionInput, -): Result => { - const parsed = parseProviderRetractionInput(input) - if (!parsed.ok) return parsed - const validated = parsed.value +export const buildProviderRetractionEvent = ( + targets: readonly [RetractionTarget, ...RetractionTarget[]], + context: ExchangeEventContext, + sourceRecord: RoledIdentifier, + retractedAt: FhirInstant, +): Result => { + const parsedContext = parseExchangeEventContext(context) + if (!parsedContext.ok) return parsedContext + const validated = parsedContext.value + const { identityScope: scope, event, repositoryIds = {} } = validated + const findings = [ + ...targetsIssues(targets, scope), + ...opaqueIdentifierIssues(sourceRecord, 'source-record', scope, [ + 'sourceRecord', + ]), + ...(parseFhirInstant(retractedAt).ok ? + [] + : [ + fault( + 'invalid-date-time', + ['retractedAt'], + 'Expected an RFC 3339 instant with seconds and an explicit UTC offset.', + ), + ]), + ...(validated.studies.length > 0 ? + [ + fault( + 'value-mismatch', + ['studies'], + 'A retraction event carries no outputs, so its context names no studies.', + ), + ] + : []), + ] + if (findings.length > 0) return issues(findings) - const identities = deriveProviderIdentities({ - provider: validated.source.provider, - providerScopeIdentifier: validated.source.providerScopeIdentifier, - sourceType: validated.source.sourceType, - sourceNativeId: validated.source.sourceNativeId, - outputs: [], - eventSequence: validated.eventSequence, - deployment: validated.deploymentIdentity, - provenanceNodeRole: 'retraction-provenance', - }) - if (!identities.ok) return identities - const provenanceIdentity = createEntryIdentity( - identities.value.provenanceNode, - validated.repositoryIds?.provenance, + const provenanceIdentity = deriveEntryNodeEntryIdentity( + scope, + event, + 'retraction-provenance', + 0, + repositoryIds.provenance, ) if (!provenanceIdentity.ok) return provenanceIdentity - const applicationIdentity = deriveApplicationEntryIdentity( - validated.deploymentIdentity, - identities.value.event, + const application = deriveApplicationEntryIdentity( + scope, + event, validated.application, + repositoryIds['application-device'], ) - if (!applicationIdentity.ok) return applicationIdentity - const hostIdentity = - validated.application.host === undefined ? - undefined - : deriveDeviceSnapshotEntryIdentity( - validated.deploymentIdentity, - identities.value.event, - validated.application.host.sourceDeviceToken, - 'host', - validated.application.host.id, - ) - if (hostIdentity !== undefined && !hostIdentity.ok) return hostIdentity - - const targetReferences: TargetReference[] = [] - for (const target of validated.targets) { - const targetIdentifier = deriveTargetIdentifier(validated, target) - if (!targetIdentifier.ok) return targetIdentifier - const role: TargetRoleExtension = { - url: EXTENSIONS.retractionTargetRole, - valueCode: target.role, - } - targetReferences.push({ - extension: - target.nativeIdentifier === undefined ? - [role] - : [ - role, - { - url: EXTENSIONS.retractionTargetNativeIdentifier, - valueIdentifier: target.nativeIdentifier, - }, - ], - type: target.resourceType, - identifier: identifier(targetIdentifier.value), - }) - } - targetReferences.sort(compareTargetReferences) + if (!application.ok) return application + const host = deriveHostEntryIdentity( + scope, + event, + validated.host, + repositoryIds['host-device'], + ) + if (!host.ok) return host const provenance = { resourceType: 'Provenance' as const, ...resourceId(provenanceIdentity.value), meta: { profile: [PROFILES.retractionProvenance] }, - target: targetReferences, - occurredDateTime: validated.occurred, - recorded: validated.recorded, + target: [...targets].map(targetReference).sort(compareTargetReferences), + occurredDateTime: retractedAt, + recorded: validated.conversionInstant, activity: concept( SYSTEMS.groveLifecycleEvent, - groveMobileContract.lifecycle.sourceRecordRetracted, + groveExchangeProtocol.lifecycle.retraction.activityCode, 'Source record retracted', ), agent: [ { type: concept(SYSTEMS.provenanceParticipant, 'assembler', 'Assembler'), - who: { - reference: applicationIdentity.value.fullUrl, - }, + who: { reference: application.value.fullUrl }, }, ], entity: [ { role: 'source' as const, - what: { identifier: identifier(identities.value.sourceRecord) }, + what: { identifier: identifier(sourceRecord) }, }, ], } - const application = makeApplicationDevice({ - ...validated.application, - identity: applicationIdentity.value, - ...(hostIdentity === undefined ? - {} - : { parentReference: hostIdentity.value.fullUrl }), - }) - const host = - validated.application.host === undefined || hostIdentity === undefined ? - undefined - : makeHostDevice({ - ...validated.application.host, - identity: hostIdentity.value, - }) - const entry = deduplicateIdentifiedEntries([ - ...(host === undefined || hostIdentity === undefined ? - [] - : [identifiedEntry(hostIdentity.value, host)]), - identifiedEntry(applicationIdentity.value, application), + const entries = deduplicateIdentifiedEntries([ + identifiedEntry( + host.value, + makeHostDevice({ ...validated.host, identity: host.value }), + ), + identifiedEntry( + application.value, + makeApplicationDevice({ + ...validated.application, + identity: application.value, + parentReference: host.value.fullUrl, + }), + ), identifiedEntry(provenanceIdentity.value, provenance), ]) - if (!entry.ok) return entry - - return parseGroveMobileRetractionBundle( - deepFreeze({ - resourceType: 'Bundle' as const, - ...(validated.repositoryIds?.bundle === undefined ? - {} - : { id: validated.repositoryIds.bundle }), - meta: { profile: [PROFILES.retractionBundle] }, - identifier: identifier(identities.value.event), - type: 'collection' as const, - timestamp: validated.assembled, - entry: entry.value, - }), - ) + if (!entries.ok) return entries + return parseRetractionEvent({ + resourceType: 'Bundle' as const, + ...(repositoryIds.bundle === undefined ? {} : { id: repositoryIds.bundle }), + meta: { profile: [PROFILES.retractionBundle] }, + identifier: identifier(event), + type: 'collection' as const, + timestamp: validated.conversionInstant, + entry: entries.value, + }) } diff --git a/packages/fhir/src/providers/types.ts b/packages/fhir/src/providers/types.ts index 878d03ca..663e6749 100644 --- a/packages/fhir/src/providers/types.ts +++ b/packages/fhir/src/providers/types.ts @@ -12,45 +12,22 @@ import type { ProviderRawOutputRoles, ProviderScalarOutputRoles, } from '../contract/providers.generated.js' -import type { AbsoluteUri, FhirId, FhirInstant } from '../core/index.js' +import type { Branded, Issue } from '../core/index.js' +import type { BusinessIdentifier } from '../mobile/identity.js' import type { - ApplicationDeviceInput, - CompleteIdentifierInput, - DeploymentIdentityInput, - GatewayApplicationInput, + ApplicationDevice, + ExchangeGraphIdentifiers, + GovernedSourceIdentifierDisclosurePolicy, InstantEffectiveTime, MobileMeasurement, PeriodEffectiveTime, - RecordingDeviceInput, + RecordingDevice, RecordingMethod, } from '../mobile/types.js' +import type { ExchangeGraph } from '../r4/types.js' type ScalarOutputRoles = ProviderScalarOutputRoles -/** Deployment-owned pseudonym for a provider whose native keys are account-scoped. */ -export interface ProviderAccountScopeIdentifierInput extends CompleteIdentifierInput { - readonly assurance: 'deployment-scoped-account-pseudonym' -} - -/** Authoritative global provider key-space asserted for globally unique native keys. */ -export interface ProviderGlobalScopeIdentifierInput extends CompleteIdentifierInput { - readonly assurance: 'documented-global-key-space' -} - -/** Identifier-only Patient reference; the pseudonym is intentionally carried in emitted FHIR. */ -export interface ProviderPatientReferenceInput { - readonly type: 'Patient' - readonly identifier: CompleteIdentifierInput & { - readonly assurance: 'deployment-scoped-pseudonym' - } -} - -/** Identifier-only ResearchStudy reference; no unresolved literal is emitted. */ -export interface ProviderResearchStudyReferenceInput { - readonly type: 'ResearchStudy' - readonly identifier: CompleteIdentifierInput -} - /** Exact closed provider codes defined by the Provider IG. */ export type ConnectedProvider = keyof ScalarOutputRoles @@ -69,35 +46,6 @@ type ProviderContractRow = Extract< { readonly id: Provider } > -type ProviderIdentifierScope = - ProviderContractRow['identifierScope'] - -type ProviderScopeMode = - ProviderContractRow extends ( - { - readonly providerScopeMode: infer Mode extends - 'deployment-scoped-account-pseudonym' | 'documented-global-key-space' - } - ) ? - Mode - : ProviderIdentifierScope extends 'account' ? - 'deployment-scoped-account-pseudonym' - : 'documented-global-key-space' - -/** - * Complete provider-scope pair required by the Provider identity protocol. - * Account-scoped providers use a deployment pseudonym; globally unique providers use one - * documented global key-space pair, so identity does not fragment across accounts. - */ -export type ProviderScopeIdentifierInput< - Provider extends ConnectedProvider = ConnectedProvider, -> = - Provider extends ConnectedProvider ? - ProviderScopeMode extends 'deployment-scoped-account-pseudonym' ? - ProviderAccountScopeIdentifierInput - : ProviderGlobalScopeIdentifierInput - : never - type ProviderMeasurementOwner = ProviderContractRow extends ( { @@ -184,63 +132,35 @@ export interface ProviderAdapter< readonly provider: Provider } -interface NormalizedSourceRecordBase { - readonly recordingMethod?: RecordingMethod - readonly recordingDevice?: RecordingDeviceInput - /** Optional logical identity assigned by the application that wrote the source record. */ - readonly writerRecord?: WriterRecordInput -} - -export interface WriterRecordInput { - readonly applicationIdentifier: CompleteIdentifierInput +/** The logical record identity the application that wrote the source record assigned. */ +export interface WriterRecord { + readonly applicationIdentifier: BusinessIdentifier readonly nativeRecordId: string /** Canonical non-negative writer revision, when the source exposes one. */ - readonly version?: string -} - -/** Optional FHIR type coding for a deliberately disclosed source-native Identifier. */ -export interface GovernedSourceIdentifierTypeCodingInput { - readonly system: AbsoluteUri - readonly code: string - readonly display?: string -} - -/** Narrow CodeableConcept surface for an optional source-native Identifier.type. */ -export interface GovernedSourceIdentifierTypeInput { - readonly coding?: readonly [ - GovernedSourceIdentifierTypeCodingInput, - ...GovernedSourceIdentifierTypeCodingInput[], - ] - readonly text?: string + readonly version?: string | undefined } -/** - * Explicit opt-in to place the exact source-native value under a caller-governed key-space URI. - * This traceability Identifier supplements Grove identities and is never an entry/retraction key. - */ -export interface GovernedSourceIdentifierInput { - readonly system: AbsoluteUri - readonly nativeId: string - readonly type?: GovernedSourceIdentifierTypeInput +interface NormalizedSourceRecordBase { + readonly recordingMethod?: RecordingMethod | undefined + readonly recordingDevice?: RecordingDevice | undefined + readonly writerRecord?: WriterRecord | undefined } /** - * Source identity fields. The native id is HMAC input and omitted by default; - * an eligible graph may disclose it only through the explicit governed - * top-level configuration. The provider scope must match the catalog: a deployment-owned - * account pseudonym for account-scoped keys, or the documented global key-space pair for - * globally unique keys. An email, access credential, or inferred scope is invalid. + * Source identity fields. The native id is HMAC input and omitted by default; an eligible + * graph discloses it only through the conversion options' disclosure policy. The record's + * provider scope is the context's repository scope: a deployment-owned account pseudonym + * for account-scoped keys, or the documented global key-space pair for globally unique keys. */ export interface ProviderSourceRecord< Provider extends ConnectedProvider, SourceType extends ConnectedSourceType, > extends NormalizedSourceRecordBase { readonly adapter: ProviderAdapter - readonly providerScopeIdentifier: ProviderScopeIdentifierInput readonly sourceType: SourceType readonly sourceNativeId: string - /** Application that entered the record into the connected provider. */ - readonly dataOrigin: ApplicationDeviceInput + /** The application that entered the record at the connected provider. */ + readonly writer: ApplicationDevice } export type ConnectedProviderMeasurements< @@ -282,49 +202,37 @@ export type ConnectedRawProvider = keyof RawOutputRoles export type ConnectedRawSourceType = keyof RawOutputRoles[Provider] & string -declare const recordingInputBrand: unique symbol - /** Canonically padded RFC 4648 base64 containing at least one byte. */ -export type CanonicalBase64 = string & { - readonly [recordingInputBrand]: 'CanonicalBase64' -} +export type CanonicalBase64 = Branded<'CanonicalBase64'> /** Base64-encoded 20-byte SHA-1 digest required by FHIR R4 Attachment.hash. */ -export type Sha1Base64 = string & { - readonly [recordingInputBrand]: 'Sha1Base64' -} +export type Sha1Base64 = Branded<'Sha1Base64'> /** A syntactically valid media type without content-transfer parameters. */ -export type MediaType = string & { - readonly [recordingInputBrand]: 'MediaType' -} +export type MediaType = Branded<'MediaType'> /** Caller-asserted immutable, version-specific HTTP(S) recording URL. */ -export type ImmutableRecordingUrl = string & { - readonly [recordingInputBrand]: 'ImmutableRecordingUrl' -} +export type ImmutableRecordingUrl = Branded<'ImmutableRecordingUrl'> export interface ProviderRecordingSourceRecord< Provider extends ConnectedRawProvider, SourceType extends ConnectedRawSourceType, > { readonly adapter: ProviderAdapter - readonly providerScopeIdentifier: ProviderScopeIdentifierInput readonly sourceType: SourceType - /** - * Opaque-identity digest input. Omitted from FHIR by default; governed - * disclosure may place it on the sole source DocumentReference. - */ + /** Opaque-identity digest input, disclosed only through the conversion options. */ readonly sourceNativeId: string - /** Application that entered the already-obtained record at the provider. */ - readonly dataOrigin: ApplicationDeviceInput - readonly writerRecord?: WriterRecordInput + /** The application that entered the already-obtained record at the provider. */ + readonly writer: ApplicationDevice + /** The source activity time the recording covers. */ + readonly effective: InstantEffectiveTime | PeriodEffectiveTime + readonly writerRecord?: WriterRecord | undefined } -interface RecordingAttachmentBaseInput { +interface RecordingAttachmentBase { readonly contentType: MediaType /** Optional human-facing presentation text; it never participates in identity. */ - readonly title?: string + readonly title?: string | undefined /** Registered payload format the recording bytes conform to. */ readonly format: ProviderRecordingFormat /** Required deployment assertion for opaque content Grove cannot inspect. */ @@ -337,13 +245,13 @@ export type ProviderRecordingFormat = export type RawPayloadAdmissionAssertion = (typeof import('../contract/providers.generated.js').providerAdapterCatalog)['rawPayloadAdmission']['allowedAssertions'][number] -export interface EmbeddedRecordingAttachmentInput extends RecordingAttachmentBaseInput { +export interface EmbeddedRecordingAttachment extends RecordingAttachmentBase { readonly kind: 'embedded' /** Exact caller-supplied bytes represented as canonical RFC 4648 base64. */ readonly dataBase64: CanonicalBase64 } -export interface ExternalRecordingAttachmentInput extends RecordingAttachmentBaseInput { +export interface ExternalRecordingAttachment extends RecordingAttachmentBase { readonly kind: 'external' readonly url: ImmutableRecordingUrl readonly size: number @@ -351,8 +259,8 @@ export interface ExternalRecordingAttachmentInput extends RecordingAttachmentBas readonly immutabilityAssurance: 'immutable-version-specific' } -export type ProviderRecordingAttachmentInput = - EmbeddedRecordingAttachmentInput | ExternalRecordingAttachmentInput +export type ProviderRecordingAttachment = + EmbeddedRecordingAttachment | ExternalRecordingAttachment export type ProviderRecordingSource = { readonly [Provider in ConnectedRawProvider]: { @@ -362,40 +270,6 @@ export type ProviderRecordingSource = { }[ConnectedRawSourceType] }[ConnectedRawProvider] -export interface RecordingRepositoryAssignedResourceIds { - readonly bundle?: FhirId - readonly document?: FhirId - readonly provenance?: FhirId -} - -/** Closed input for one mapped-standard Provider native recording. */ -export interface ProviderRecordingBundleInput { - readonly source: ProviderRecordingSource - readonly attachment: ProviderRecordingAttachmentInput - readonly subject: ProviderPatientReferenceInput - readonly application: ApplicationDeviceInput - /** Durable caller-owned sequence for a new conversion/exchange event. */ - readonly eventSequence: string - /** Deployment-owned, key-epoch-specific HMAC identity configuration. */ - readonly deploymentIdentity: DeploymentIdentityInput - readonly nativeIdentifierDisclosure?: GovernedSourceIdentifierInput - readonly documentDate: FhirInstant - /** Source activity time carried on conversion Provenance.occurred[x]. */ - readonly occurred: FhirInstant - readonly recorded: FhirInstant - /** Time the immutable exchange graph was assembled (Bundle.timestamp). */ - readonly assembled: FhirInstant - readonly repositoryIds?: RecordingRepositoryAssignedResourceIds -} - -export interface MeasurementRepositoryAssignedResourceIds { - readonly bundle?: FhirId - readonly observations?: Readonly< - Partial> - > - readonly provenance?: FhirId -} - /** Every shared or provider-owned measurement kind admitted by a connected source row. */ export type ConnectedProviderMeasurementKind = { readonly [Provider in ConnectedProvider]: { @@ -405,23 +279,30 @@ export type ConnectedProviderMeasurementKind = { }[ConnectedSourceType] }[ConnectedProvider] -interface ProviderMeasurementGraphInput { - readonly subject: ProviderPatientReferenceInput - readonly application: ApplicationDeviceInput - readonly gatewayApplication?: GatewayApplicationInput - /** Durable caller-owned sequence for a new conversion/exchange event. */ - readonly eventSequence: string - /** Deployment-owned, key-epoch-specific HMAC identity configuration. */ - readonly deploymentIdentity: DeploymentIdentityInput - readonly nativeIdentifierDisclosure?: GovernedSourceIdentifierInput - /** Source activity time carried on conversion Provenance.occurred[x]. */ - readonly occurred: FhirInstant - readonly recorded: FhirInstant - /** Time the immutable exchange graph was assembled (Bundle.timestamp). */ - readonly assembled: FhirInstant - readonly repositoryIds?: MeasurementRepositoryAssignedResourceIds - readonly researchStudyReferences?: readonly ProviderResearchStudyReferenceInput[] +/** Deployment policy for one conversion; every disclosure defaults to omit. */ +export interface ProviderConversionOptions { + readonly nativeIdentifierDisclosure?: + GovernedSourceIdentifierDisclosurePolicy | undefined } -export type ProviderMeasurementBundleInput = NormalizedProviderRecord & - ProviderMeasurementGraphInput +/** One converted provider record: what it converted, what it minted, and the graph. */ +export interface ProviderConversion { + readonly source: NormalizedSourceRecord + readonly identifiers: ExchangeGraphIdentifiers + readonly graph: ExchangeGraph + /** Registry warnings about what the accepted record lost; empty when nothing was. */ + readonly warnings: readonly Issue[] +} + +export interface ProviderRecordingConversion { + readonly source: ProviderRecordingSource + readonly identifiers: ExchangeGraphIdentifiers + readonly graph: ExchangeGraph + readonly warnings: readonly Issue[] +} + +/** One record a batch refused, with the registry codes that say why. */ +export interface ProviderConversionFailure { + readonly record: NormalizedProviderRecord + readonly issues: readonly Issue[] +} diff --git a/packages/fhir/src/r4/active-graph-semantics.ts b/packages/fhir/src/r4/active-graph-semantics.ts index afb8151f..fb56048a 100644 --- a/packages/fhir/src/r4/active-graph-semantics.ts +++ b/packages/fhir/src/r4/active-graph-semantics.ts @@ -28,20 +28,24 @@ import { locatedReferences, type UnknownRecord, } from './graph-schema-utils.js' +import { validateStudyContext } from './study-context-semantics.js' import type { R4CollectionBundle } from './types.js' import { + groveExchangeProtocol, groveProfileClaims, healthConnectDataOriginApplication, } from '../contract/measurement-catalog.generated.js' import { parseAbsoluteUri } from '../core/index.js' -import { groveMobileContract } from '../mobile/contract.js' import { isOpaqueIdentityValue } from '../mobile/identity.js' export { hasAdmittedMobileObservationProfile } from './active-observation-semantics.js' export { validateExchangeEnvelope, type ValidatedEnvelope } const ACTIVE_ENTRY_POLICY = - groveMobileContract.lifecycle.activeEntryResourcePolicy + groveExchangeProtocol.lifecycle.active.entryResourcePolicy +const LIFECYCLE_EVENT_SYSTEM = groveExchangeProtocol.codeSystems.lifecycleEvent +const RETRACTION_ACTIVITY_CODE = + groveExchangeProtocol.lifecycle.retraction.activityCode const ACTIVE_OUTPUT_TYPES: ReadonlySet = new Set( ACTIVE_ENTRY_POLICY.outputResourceTypes, ) @@ -112,17 +116,12 @@ export const validateAssemblerAgent = ( 1 || (!internalApplication && !logicalApplication) ) { - addIssue( - context, - 'mobile-provenance.assembler', - path, - 'Grove Provenance requires exactly one assembler linked to an application Device snapshot.', - ) + addIssue(context, 'mobile-exchange.provenance-assembler', path) } } const CONVERSION_PROVENANCE_PROFILES: ReadonlySet = new Set([ - groveMobileContract.profiles.conversionProvenance, + groveExchangeProtocol.profiles.conversionProvenance, ...groveProfileClaims.adapterConversionProvenanceClaims.map( ({ profile }) => profile, ), @@ -173,12 +172,12 @@ const validateHealthConnectDataOriginApplication = ( identifier.system !== healthConnectDataOriginApplication.identifierSystem || identifier.value.trim() === '' ) { - addIssue( - context, - 'health-connect.data-origin-application', - [...path, 'entity', 0, 'agent'], - 'Health Connect DataOrigin.packageName must be one enterer agent with a typed identifier-only logical Device Reference in the Android package-name namespace.', - ) + addIssue(context, 'health-connect-provenance.data-origin-agent', [ + ...path, + 'entity', + 0, + 'agent', + ]) } } @@ -223,8 +222,8 @@ const selectConversionProvenance = ( const retractions = provenances.filter((resource) => codingExists( asRecord(resource)?.activity, - groveMobileContract.systems.lifecycleEvent, - groveMobileContract.lifecycle.sourceRecordRetracted, + LIFECYCLE_EVENT_SYSTEM, + RETRACTION_ACTIVITY_CODE, ), ) if ( @@ -232,12 +231,7 @@ const selectConversionProvenance = ( conversions.length !== 1 || retractions.length !== 0 ) { - addIssue( - context, - 'mobile-exchange.transform-provenance', - ['entry'], - 'An active event requires exactly one transform Provenance and no retraction Provenance.', - ) + addIssue(context, 'mobile-exchange.transform-provenance', ['entry']) return undefined } const resource = asRecord(conversions[0]) @@ -263,47 +257,28 @@ const validateConversionProvenanceHeader = ( typeof profiles[0] !== 'string' || !CONVERSION_PROVENANCE_PROFILES.has(profiles[0]) ) { - addIssue( - context, - 'mobile-exchange.provenance-profile', - ['entry'], - 'Conversion Provenance must directly declare exactly its admitted Mobile or adapter conversion profile.', - ) + addIssue(context, 'mobile-exchange.provenance-profile', ['entry']) } if ( codingCount(provenance.activity, ISO_LIFECYCLE_SYSTEM, 'transform') !== 1 || codingCountForSystem(provenance.activity, ISO_LIFECYCLE_SYSTEM) !== 1 || - codingCountForSystem( - provenance.activity, - groveMobileContract.systems.lifecycleEvent, - ) !== 0 + codingCountForSystem(provenance.activity, LIFECYCLE_EVENT_SYSTEM) !== 0 ) { - addIssue( - context, - 'mobile-exchange.lifecycle-coding', - ['entry'], - 'Conversion Provenance requires exactly one transform coding from the ISO lifecycle system and no Grove retraction lifecycle coding.', - ) + addIssue(context, 'mobile-exchange.lifecycle-coding', ['entry']) } validateAssemblerAgent(provenance, envelope, context, ['entry']) if ( provenance.occurredDateTime === undefined && provenance.occurredPeriod === undefined ) { - addIssue( - context, - 'mobile-exchange.occurred-time', - ['entry'], - 'Conversion Provenance requires occurred[x].', - ) + addIssue(context, 'mobile-exchange.event-times', ['entry'], { + location: 'Provenance.occurred[x]', + }) } if (provenance.recorded === undefined) { - addIssue( - context, - 'mobile-exchange.recorded-time', - ['entry'], - 'Conversion Provenance requires recorded.', - ) + addIssue(context, 'mobile-exchange.event-times', ['entry'], { + location: 'Provenance.recorded', + }) } return directConversionProfile(profiles) } @@ -317,12 +292,7 @@ const conversionSourceIdentifier = ( const sourceWhat = asRecord(sourceEntity?.what) const identifier = sourceWhat?.identifier if (entities.length !== 1) { - addIssue( - context, - 'mobile-exchange.single-source-entity', - ['entry'], - 'Conversion Provenance must carry exactly one source entity.', - ) + addIssue(context, 'mobile-exchange.single-source-entity', ['entry']) } if ( sourceEntity?.role !== 'source' || @@ -332,12 +302,7 @@ const conversionSourceIdentifier = ( identifierRole(identifier) !== 'source-record' || !isOpaqueIdentityValue(identifier.value) ) { - addIssue( - context, - 'mobile-exchange.logical-source-entity', - ['entry'], - 'Conversion Provenance source must be a logical typed opaque source-record Identifier with no literal reference.', - ) + addIssue(context, 'mobile-exchange.logical-source-entity', ['entry']) } return identifier } @@ -352,12 +317,7 @@ const collectOutputEntries = ( : [], ) if (outputs.length === 0) { - addIssue( - context, - 'mobile-exchange.output-required', - ['entry'], - 'An active exchange event requires at least one clinical or source-artifact output.', - ) + addIssue(context, 'mobile-exchange.output-required', ['entry']) } return outputs } @@ -375,12 +335,13 @@ const validateOutputAdapterClaim = ( : claims.length === 1 && claims[0]?.profile === provenanceAdapterClaim.profile if (claims.length > 1 || !matches) { - addIssue( - context, - 'mobile-exchange.adapter-provenance-graph', - ['entry', output.index, 'resource', 'meta', 'profile'], - "Every adapter output must claim exactly the adapter profile governed by this event's conversion Provenance, and source-neutral events must not claim adapter outputs.", - ) + addIssue(context, 'mobile-exchange.adapter-provenance-graph', [ + 'entry', + output.index, + 'resource', + 'meta', + 'profile', + ]) } } @@ -401,12 +362,12 @@ const validateOutputIdentities = ( !parseAbsoluteUri(output.system).ok || !isOpaqueIdentityValue(output.value) ) { - addIssue( - context, - 'mobile-output.source-output-required', - ['entry', index, 'resource', 'identifier'], - 'Every active output requires matching source-record and exact typed output identities.', - ) + addIssue(context, 'mobile-output.source-output-required', [ + 'entry', + index, + 'resource', + 'identifier', + ]) } } @@ -473,12 +434,13 @@ const validateHybridCompanions = ( )) { const source = identifierWithRole(output.entry.resource, 'source-record') if (!hasHybridDocumentCompanion(source, outputs)) { - addIssue( - context, - 'mobile-output.hybrid-companion', - ['entry', output.index, 'resource', 'meta', 'profile'], - 'A SensorKit ECG Observation requires one exact native Recording Document companion in the same event with the same source-record Identifier.', - ) + addIssue(context, 'mobile-output.hybrid-companion', [ + 'entry', + output.index, + 'resource', + 'meta', + 'profile', + ]) } } } @@ -505,12 +467,7 @@ const validateProvenanceTargets = ( new Set(actual).size !== actual.length || actual.some((reference) => !expected.has(reference)) ) { - addIssue( - context, - 'mobile-exchange.provenance-targets', - ['entry'], - 'Conversion Provenance must target every and only active clinical output.', - ) + addIssue(context, 'mobile-exchange.provenance-targets', ['entry']) } } @@ -569,12 +526,7 @@ const validateSupportingResourceConnectivity = ( !reachable.has(fullUrl), ) if (disconnected) { - addIssue( - context, - 'mobile-support.connected', - ['entry'], - 'Every supporting resource must be connected by literal Bundle references to an output or the lifecycle Provenance.', - ) + addIssue(context, 'mobile-support.connected', ['entry']) } } @@ -585,7 +537,7 @@ export const refineActiveBundle = ( const envelope = validateExchangeEnvelope( bundle, context, - groveMobileContract.profiles.exchangeBundle, + groveExchangeProtocol.profiles.activeBundle, ) if (envelope === undefined) return const conversion = selectConversionProvenance(envelope, context) @@ -610,4 +562,5 @@ export const refineActiveBundle = ( validateHybridCompanions(outputs, context) validateProvenanceTargets(conversion.resource, outputs, context) validateSupportingResourceConnectivity(envelope, context) + validateStudyContext(envelope, context) } diff --git a/packages/fhir/src/r4/active-observation-semantics.ts b/packages/fhir/src/r4/active-observation-semantics.ts index d843bb1f..502937a1 100644 --- a/packages/fhir/src/r4/active-observation-semantics.ts +++ b/packages/fhir/src/r4/active-observation-semantics.ts @@ -14,10 +14,12 @@ import { codingCountForSystem, type UnknownRecord, } from './graph-schema-utils.js' -import { groveProfileClaims } from '../contract/measurement-catalog.generated.js' +import { + groveMobilePackageMetadata, + groveProfileClaims, +} from '../contract/measurement-catalog.generated.js' import { providerAdapterCatalog } from '../contract/providers.generated.js' import { compareFhirDateTimes } from '../core/primitives.js' -import { groveMobileContract } from '../mobile/contract.js' import { ALL_MEASUREMENT_DEFINITIONS, PROVIDER_ROWS, @@ -200,21 +202,15 @@ const validateObservationCodes = ( !admittedPrimaryCodes.has(String(asRecord(candidate)?.code)), ) ) { - addIssue( - context, - `mobile-${match.id}.code`, - [...path, 'code'], - `A Grove Mobile ${match.id} Observation requires exactly its closed catalog coding set for ${match.code.system}.`, - ) + addIssue(context, 'mobile-output.semantic-profile', [...path, 'code'], { + location: 'Observation.code', + }) } for (const required of requiredCodings) { if (codingCount(observation.code, required.system, required.code) !== 1) { - addIssue( - context, - `mobile-${match.id}.required-coding`, - [...path, 'code'], - `A Grove Mobile ${match.id} Observation is missing or duplicates required coding ${required.system}|${required.code}.`, - ) + addIssue(context, 'mobile-output.semantic-profile', [...path, 'code'], { + location: 'Observation.code', + }) } } } @@ -248,18 +244,17 @@ const validateObservationCategory = ( ) { addIssue( context, - `mobile-${match.id}.category`, + 'mobile-output.semantic-profile', [...path, 'category'], - `A Grove Mobile ${match.id} Observation requires exactly one catalog category coding.`, + { + location: 'Observation.category', + }, ) } } else if (categories.length > 0) { - addIssue( - context, - `mobile-${match.id}.category`, - [...path, 'category'], - `A Grove Mobile ${match.id} Observation must not invent an uncatalogued clinical category.`, - ) + addIssue(context, 'mobile-output.semantic-profile', [...path, 'category'], { + location: 'Observation.category', + }) } } @@ -273,9 +268,11 @@ const validateObservationEffective = ( if (typeof observation.effectiveDateTime !== 'string') { addIssue( context, - `mobile-${match.id}.effective`, + 'mobile-output.semantic-profile', [...path, 'effectiveDateTime'], - `A Grove Mobile ${match.id} Observation requires effectiveDateTime.`, + { + location: 'Observation.effective[x]', + }, ) } } else if (match.effective === 'Period') { @@ -283,9 +280,11 @@ const validateObservationEffective = ( if (typeof period?.start !== 'string' || typeof period.end !== 'string') { addIssue( context, - `mobile-${match.id}.effective`, + 'mobile-output.semantic-profile', [...path, 'effectivePeriod'], - `A Grove Mobile ${match.id} Observation requires a bounded effectivePeriod.`, + { + location: 'Observation.effective[x]', + }, ) } } else { @@ -297,9 +296,11 @@ const validateObservationEffective = ( if (hasDateTime === hasPeriod) { addIssue( context, - `mobile-${match.id}.effective`, + 'mobile-output.semantic-profile', [...path, 'effectiveDateTime'], - `A Grove Mobile ${match.id} Observation requires exactly one admitted effectiveDateTime or bounded effectivePeriod.`, + { + location: 'Observation.effective[x]', + }, ) } } @@ -311,19 +312,16 @@ const validateObservationMethod = ( context: z.core.$RefinementCtx, path: ReadonlyArray, ): void => { - const aggregationSystem = `${groveMobileContract.canonical}/CodeSystem/grove-aggregation-method` + const aggregationSystem = `${groveMobilePackageMetadata.canonical}/CodeSystem/grove-aggregation-method` if (match.method !== undefined) { if ( codingCount(observation.method, aggregationSystem, match.method.code) !== 1 || codingCountForSystem(observation.method, aggregationSystem) !== 1 ) { - addIssue( - context, - `mobile-${match.id}.method`, - [...path, 'method'], - `A Grove Mobile ${match.id} Observation requires its fixed aggregation method.`, - ) + addIssue(context, 'mobile-output.semantic-profile', [...path, 'method'], { + location: 'Observation.method', + }) } } else if (match.methodChoice !== undefined) { const admittedMethods: readonly string[] = match.methodChoice @@ -343,12 +341,9 @@ const validateObservationMethod = ( admitted.length !== 1 || codingCountForSystem(observation.method, aggregationSystem) !== 1 ) { - addIssue( - context, - `mobile-${match.id}.method`, - [...path, 'method'], - `A Grove Mobile ${match.id} Observation requires exactly one admitted aggregation method.`, - ) + addIssue(context, 'mobile-output.semantic-profile', [...path, 'method'], { + location: 'Observation.method', + }) } } } @@ -360,12 +355,11 @@ const validateQuantityValueDomain = ( path: ReadonlyArray, ): void => { if (typeof value !== 'number' || !violatesQuantityDomain(value, match)) return - addIssue( - context, - 'mobile-output.quantity-value-domain', - [...path, 'valueQuantity', 'value'], - `A Grove Mobile ${match.id} result must satisfy its catalog-owned value domain.`, - ) + addIssue(context, 'mobile-output.quantity-value-domain', [ + ...path, + 'valueQuantity', + 'value', + ]) } const POSITIVE_PERIOD_CONSTRAINT = 'grove-step-count-period-1' @@ -386,9 +380,11 @@ const validatePositivePeriod = ( ) { addIssue( context, - `mobile-${match.id}.nonzero-period`, + 'mobile-output.semantic-profile', [...path, 'effectivePeriod'], - `A Grove Mobile ${match.id} period must have nonzero duration.`, + { + location: 'Observation.effectivePeriod', + }, ) } } @@ -409,12 +405,10 @@ const validateQuantityResult = ( valueQuantity.code !== quantity?.code || valueQuantity.unit !== quantity?.unit ) { - addIssue( - context, - 'mobile-output.fixed-quantity-unit', - [...path, 'valueQuantity'], - `A Grove Mobile ${match.id} result requires one finite exact value in fixed UCUM ${String(quantity?.code)}.`, - ) + addIssue(context, 'mobile-output.fixed-quantity-unit', [ + ...path, + 'valueQuantity', + ]) } validateQuantityValueDomain(valueQuantity?.value, match, context, path) validatePositivePeriod(observation, match, context, path) @@ -451,32 +445,34 @@ const validateCodeableConceptResult = ( if (admitted.length !== 1 || invalidSameSystem) { addIssue( context, - `mobile-${match.id}.coded-result`, + 'mobile-output.semantic-profile', [...path, 'valueCodeableConcept'], - `A Grove Mobile ${match.id} result requires exactly one closed shared result coding.`, + { + location: 'Observation.valueCodeableConcept', + }, ) } } const validateDateTimeResult = ( observation: UnknownRecord, - match: MeasurementDefinition, context: z.core.$RefinementCtx, path: ReadonlyArray, ): void => { if (typeof observation.valueDateTime !== 'string') { addIssue( context, - `mobile-${match.id}.date-time-result`, + 'mobile-output.semantic-profile', [...path, 'valueDateTime'], - `A Grove Mobile ${match.id} result requires valueDateTime.`, + { + location: 'Observation.valueDateTime', + }, ) } } const validateGroupingResult = ( observation: UnknownRecord, - match: MeasurementDefinition, context: z.core.$RefinementCtx, path: ReadonlyArray, ): void => { @@ -485,9 +481,11 @@ const validateGroupingResult = ( if (members.length === 0) { addIssue( context, - `mobile-${match.id}.members`, + 'mobile-output.semantic-profile', [...path, 'hasMember'], - `A Grove Mobile ${match.id} grouping requires at least one member Observation.`, + { + location: 'Observation.hasMember', + }, ) } } @@ -520,10 +518,6 @@ const validateComponentResult = ( ) const quantity = asRecord(matching?.valueQuantity) const componentQuantity = definition.quantity - const unitClause = - componentQuantity === undefined ? '' : ( - ` in fixed UCUM ${componentQuantity.code}` - ) const optional = definition.cardinality === '0..1' if ( matchingIndexes.length > 1 || @@ -543,9 +537,11 @@ const validateComponentResult = ( ) { addIssue( context, - `mobile-${match.id}.${definition.id}`, + 'mobile-output.semantic-profile', [...path, 'component'], - `A Grove Mobile ${match.id} panel admits at most one distinct finite ${definition.id} component${unitClause}${optional ? '' : ' and requires it'}.`, + { + location: 'Observation.component', + }, ) } if (matchedIndex !== undefined) matchedComponentIndexes.add(matchedIndex) @@ -566,10 +562,10 @@ const validateObservationResult = ( validateCodeableConceptResult(observation, match, context, path) break case 'dateTime': - validateDateTimeResult(observation, match, context, path) + validateDateTimeResult(observation, context, path) break case 'grouping': - validateGroupingResult(observation, match, context, path) + validateGroupingResult(observation, context, path) break case 'components': validateComponentResult(observation, match, context, path) @@ -595,23 +591,19 @@ export const validateMobileObservationSemantics = ( admission.directProfiles, ) ) { - addIssue( - context, - 'mobile-output.semantic-profile', - [...path, 'meta', 'profile'], - 'Every active Observation must directly claim its exact admitted semantic/adapter profile set; connected-provider outputs require their exact provider-specific envelope and marker.', - ) + addIssue(context, 'mobile-output.semantic-profile', [ + ...path, + 'meta', + 'profile', + ]) return } const { match } = admission if (match === null) return if (observation.status !== 'final') { - addIssue( - context, - `mobile-${match.id}.status`, - [...path, 'status'], - `A Grove Mobile ${match.id} Observation must be final.`, - ) + addIssue(context, 'mobile-output.semantic-profile', [...path, 'status'], { + location: 'Observation.status', + }) } validateObservationCodes(observation, match, context, path) validateObservationCategory(observation, match, context, path) diff --git a/packages/fhir/src/r4/diagnostics.ts b/packages/fhir/src/r4/diagnostics.ts index df6b6867..2728c0be 100644 --- a/packages/fhir/src/r4/diagnostics.ts +++ b/packages/fhir/src/r4/diagnostics.ts @@ -6,23 +6,35 @@ // SPDX-License-Identifier: MIT // -import { groveExchangeRuleDiagnostics } from '../contract/measurement-catalog.generated.js' +import { + groveProducerDiagnostics, + type ProducerDiagnosticCode, +} from '../contract/measurement-catalog.generated.js' import type { Issue } from '../core/index.js' -export type GroveExchangeRuleCode = keyof typeof groveExchangeRuleDiagnostics +export type { ProducerDiagnosticCode } -const entryIndex = ( - path: ReadonlyArray, -): number | undefined => { +type LocationPath = ReadonlyArray + +/** Rules a producer reports about a record it refused or trimmed; they have no FHIR element. */ +export type ClientRecordRule = Extract< + ProducerDiagnosticCode, + `${string}-input.${string}` | `${string}-omission.${string}` +> +type GraphRule = Exclude + +const entryIndex = (path: LocationPath): number | undefined => { const index = path.indexOf('entry') const candidate = index === -1 ? undefined : path[index + 1] return typeof candidate === 'number' ? candidate : undefined } -type LocationPath = ReadonlyArray - const bundleEntry = (path: LocationPath): string => `Bundle.entry[${entryIndex(path) ?? 0}]` +const entryResource = (path: LocationPath): string => + `${bundleEntry(path)}.resource` +const entryKey = (path: LocationPath): string => + `${bundleEntry(path)}.extension.valueIdentifier` const provenanceTarget = ( path: LocationPath, ordinalOffset: number, @@ -33,139 +45,171 @@ const provenanceTarget = ( const fixed = (location: string) => (): string => location /** - * Stable FHIR-facing locations for producer rules. Array ordinals remain dynamic, + * Stable FHIR-facing locations for every graph rule. Array ordinals remain dynamic, * while logical resource paths intentionally avoid leaking parser-internal paths. - * `groveRuleLocation` indexes this table by the registry, so a newly registered rule - * fails to compile until it names its location. A rule this package implements ahead - * of the pinned registry may be named here, so registering it costs no edit. + * The table is closed over the registry, so a newly registered graph rule fails to + * compile until it names its location; a rule about a refused or trimmed record has none. */ const groveRuleLocations = { + 'health-connect-provenance.data-origin-agent': fixed( + 'Provenance.entity[0].agent', + ), 'healthkit-clinical.fhir-representation': (path) => - `${bundleEntry(path)}.resource.content[0].attachment.contentType`, - 'healthkit-ecg.output-graph': (path) => `${bundleEntry(path)}.resource`, + `${entryResource(path)}.content[0].attachment.contentType`, + 'healthkit-device.application-bundle-identifier': fixed('Device.identifier'), + 'healthkit-ecg.output-graph': entryResource, + 'mobile-device.host-device-identity': fixed('Device.identifier'), 'mobile-device.recording-device-dual-identity': fixed('Device.identifier'), + 'mobile-exchange.adapter-provenance-graph': fixed('Provenance.entity'), + 'mobile-exchange.bundle-profile': fixed('Bundle.meta.profile'), 'mobile-exchange.collection-entry-operation': fixed('Bundle.entry'), 'mobile-exchange.contained-resource-prohibited': (path) => - `${bundleEntry(path)}.resource.contained`, + `${entryResource(path)}.contained`, 'mobile-exchange.deterministic-full-url': (path) => `${bundleEntry(path)}.fullUrl`, - 'mobile-exchange.entry-node-digest': (path) => - `${bundleEntry(path)}.extension.valueIdentifier.value`, + 'mobile-exchange.distinct-entry-key': entryKey, + 'mobile-exchange.distinct-resource-identity-role': (path) => + `${entryResource(path)}.identifier`, + 'mobile-exchange.entry-key-selection': entryKey, + 'mobile-exchange.entry-node-digest': (path) => `${entryKey(path)}.value`, 'mobile-exchange.entry-node-key': bundleEntry, - 'mobile-exchange.entry-node-ordinal': (path) => - `${bundleEntry(path)}.extension.valueIdentifier.value`, + 'mobile-exchange.entry-node-ordinal': (path) => `${entryKey(path)}.value`, + 'mobile-exchange.entry-required': fixed('Bundle.entry'), 'mobile-exchange.entry-resource-type': (path) => - `${bundleEntry(path)}.resource.resourceType`, + `${entryResource(path)}.resourceType`, 'mobile-exchange.event-identity': fixed('Bundle.identifier.value'), + 'mobile-exchange.event-times': fixed('Bundle.timestamp'), + 'mobile-exchange.identifier-role': fixed('Bundle.identifier'), 'mobile-exchange.identity-system-role': fixed('Bundle'), 'mobile-exchange.lifecycle-coding': fixed('Provenance.activity.coding'), 'mobile-exchange.logical-patient-reference': fixed('Observation.subject'), 'mobile-exchange.logical-source-entity': fixed('Provenance.entity[0].what'), + 'mobile-exchange.opaque-resource-identity': (path) => + `${entryResource(path)}.identifier`, + 'mobile-exchange.output-required': fixed('Bundle.entry'), + 'mobile-exchange.provenance-assembler': fixed('Provenance.agent'), 'mobile-exchange.provenance-profile': fixed('Provenance.meta.profile'), + 'mobile-exchange.provenance-targets': fixed('Provenance.target'), 'mobile-exchange.reference-declared-type': fixed('Observation.subject.type'), 'mobile-exchange.reference-shape': fixed('Observation.subject'), 'mobile-exchange.reference-target-type': fixed( 'Observation.subject.reference', ), 'mobile-exchange.resolved-reference': (path) => - `${bundleEntry(path)}.resource.subject.reference`, + `${entryResource(path)}.subject.reference`, 'mobile-exchange.single-source-entity': fixed('Provenance.entity'), 'mobile-exchange.transform-provenance': fixed('Bundle.entry'), 'mobile-exchange.unclassified': fixed('Bundle'), 'mobile-output.adapter-only-profile': fixed('Specimen.meta.profile'), + 'mobile-output.adapter-source-marker': (path) => + `${entryResource(path)}.extension`, 'mobile-output.document-profile': fixed('DocumentReference.meta.profile'), 'mobile-output.fixed-quantity-unit': (path) => - `${bundleEntry(path)}.resource.valueQuantity.code`, + `${entryResource(path)}.valueQuantity.code`, 'mobile-output.hybrid-companion': (path) => - `${bundleEntry(path)}.resource.meta.profile`, + `${entryResource(path)}.meta.profile`, 'mobile-output.quantity-value-domain': (path) => - `${bundleEntry(path)}.resource.valueQuantity.value`, + `${entryResource(path)}.valueQuantity.value`, 'mobile-output.semantic-profile': fixed('Observation.meta.profile'), 'mobile-output.source-output-required': (path) => - `${bundleEntry(path)}.resource.identifier`, + `${entryResource(path)}.identifier`, + 'mobile-retraction.distinct-target': (path) => + `${provenanceTarget(path, -2)}.identifier`, 'mobile-retraction.logical-target': (path) => provenanceTarget(path, -1), 'mobile-retraction.native-record-identifier': (path) => `${provenanceTarget(path, -2)}.extension.valueIdentifier.type`, - 'mobile-retraction.no-clinical-copy': (path) => - `${bundleEntry(path)}.resource`, + 'mobile-retraction.no-clinical-copy': entryResource, 'mobile-retraction.opaque-target': (path) => `${provenanceTarget(path, -2)}.identifier.value`, + 'mobile-retraction.provenance': fixed('Bundle.entry'), 'mobile-retraction.role-target-type': (path) => `${provenanceTarget(path, -2)}.type`, + 'mobile-retraction.target-required': fixed('Provenance.target'), 'mobile-retraction.target-role': (path) => `${provenanceTarget(path, -2)}.extension`, + 'mobile-support.study-context': entryResource, 'mobile-support.connected': fixed('Bundle.entry'), 'mobile-support.device-profile': fixed('Device.meta.profile'), 'mobile-support.questionnaire-response-profile': fixed( 'QuestionnaireResponse.meta.profile', ), + 'sensor-recording-document.embedded-integrity': fixed( + 'DocumentReference.content[0].attachment.hash', + ), + 'sensor-recording-document.format': fixed( + 'DocumentReference.content[0].format.code', + ), 'sensor-recording-document.identity-and-content': fixed( 'DocumentReference.identifier', ), -} satisfies Readonly string>> +} satisfies Readonly string>> -const groveRuleLocation = ( - code: GroveExchangeRuleCode, - path: LocationPath, -): string => groveRuleLocations[code](path) +const isGraphRule = (code: ProducerDiagnosticCode): code is GraphRule => + Object.hasOwn(groveRuleLocations, code) -const isGroveExchangeRuleCode = (code: string): code is GroveExchangeRuleCode => - Object.hasOwn(groveExchangeRuleDiagnostics, code) +export const isProducerDiagnosticCode = ( + code: unknown, +): code is ProducerDiagnosticCode => + typeof code === 'string' && Object.hasOwn(groveProducerDiagnostics, code) -// Producer rules are namespaced; anything else is a base schema failure, not a rule. -const isProducerRuleCode = (code: string): code is `${string}.${string}` => - code.includes('.') +/** The stable FHIR-facing location of a graph rule; a client record rule has none. */ +export const groveRuleLocation = ( + code: ProducerDiagnosticCode, + path: LocationPath, +): string | undefined => + isGraphRule(code) ? groveRuleLocations[code](path) : undefined -/** Registered normative reason, or undefined for a rule this package names locally. */ -export const groveRuleReason = (code: string): string | undefined => - isGroveExchangeRuleCode(code) ? - groveExchangeRuleDiagnostics[code].reason - : undefined +export interface GroveRuleIssueOptions { + /** Overrides the table location when the rule fires at another element. */ + readonly location?: string | undefined + /** The typed detail of a refusal; the registry reason stands when absent. */ + readonly message?: string | undefined +} + +export const groveRuleIssue = ( + code: ProducerDiagnosticCode, + path: LocationPath, + options: GroveRuleIssueOptions = {}, +): Issue => { + const row = groveProducerDiagnostics[code] + const location = options.location ?? groveRuleLocation(code, path) + return { + severity: row.severity, + code, + path, + message: options.message ?? row.reason, + reason: row.reason, + ...(location === undefined ? {} : { location }), + } +} /** Structured producer-rule payload a Grove refinement attaches to its zod issue. */ export const groveRuleParameters = ( - code: string, - path: ReadonlyArray, - location?: string, -): Readonly> => - isGroveExchangeRuleCode(code) ? - { - groveRuleCode: code, - groveRuleLocation: location ?? groveRuleLocation(code, path), - } - : { groveRuleCode: code } - -export const groveRuleIssue = ( - code: GroveExchangeRuleCode, - path: ReadonlyArray, -): Issue => ({ - severity: groveExchangeRuleDiagnostics[code].severity, - code, - path, - message: groveExchangeRuleDiagnostics[code].reason, - reason: groveExchangeRuleDiagnostics[code].reason, - location: groveRuleLocation(code, path), -}) + code: ProducerDiagnosticCode, + path: LocationPath, + options: GroveRuleIssueOptions = {}, +): Readonly> => { + const location = options.location ?? groveRuleLocation(code, path) + return { + groveRuleCode: code, + ...(location === undefined ? {} : { groveRuleLocation: location }), + ...(options.message === undefined ? + {} + : { groveRuleMessage: options.message }), + } +} /** Rebuilds the producer-rule Issue a refinement recorded on its zod issue params. */ export const groveRuleIssueFromParameters = ( parameters: Readonly> | undefined, - path: ReadonlyArray, - message: string, + path: LocationPath, ): Issue | undefined => { const code = parameters?.groveRuleCode - if (typeof code !== 'string' || !isProducerRuleCode(code)) return undefined - if (!isGroveExchangeRuleCode(code)) { - return { severity: 'error', code, path, message } - } + if (!isProducerDiagnosticCode(code)) return undefined const location = parameters?.groveRuleLocation - return { - severity: groveExchangeRuleDiagnostics[code].severity, - code, - path, - message: groveExchangeRuleDiagnostics[code].reason, - reason: groveExchangeRuleDiagnostics[code].reason, - location: - typeof location === 'string' ? location : groveRuleLocation(code, path), - } + const message = parameters?.groveRuleMessage + return groveRuleIssue(code, path, { + location: typeof location === 'string' ? location : undefined, + message: typeof message === 'string' ? message : undefined, + }) } diff --git a/packages/fhir/src/r4/exchange-envelope.ts b/packages/fhir/src/r4/exchange-envelope.ts index 0ed0dc85..492348cc 100644 --- a/packages/fhir/src/r4/exchange-envelope.ts +++ b/packages/fhir/src/r4/exchange-envelope.ts @@ -28,14 +28,19 @@ import { } from './graph-schema-utils.js' import { validateProfiledResource } from './profile-semantics.js' import type { R4CollectionBundle } from './types.js' -import { parseAbsoluteUri, type AbsoluteUri } from '../core/index.js' -import { groveMobileContract } from '../mobile/contract.js' +import { groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' +import { + parseAbsoluteUri, + type AbsoluteUri, + type EntryNodeOrdinal, +} from '../core/index.js' import { deriveEntryFullUrl, deriveEntryNodeValue, isEntryNodeIdentityValue, isEventIdentityValue, isOpaqueIdentityValue, + type ExchangeEventIdentifier, } from '../mobile/identity.js' type CompleteIdentifier = Readonly<{ system: string; value: string }> @@ -62,7 +67,17 @@ interface ValidatedEntryKey { } const ACTIVE_ENTRY_POLICY = - groveMobileContract.lifecycle.activeEntryResourcePolicy + groveExchangeProtocol.lifecycle.active.entryResourcePolicy +const EXCHANGE_BUNDLE_PROFILE = groveExchangeProtocol.profiles.activeBundle +const RETRACTION_BUNDLE_PROFILE = + groveExchangeProtocol.profiles.retractionBundle +// Resources a bundled study context keys by these catalog roles, and the lifecycle Provenance. +const ENTRY_NODE_ROLE_BY_RESOURCE_TYPE: ReadonlyMap = new Map([ + ['Patient', 'patient'], + ['ResearchStudy', 'research-study'], + ['ResearchSubject', 'research-subject'], + ['PlanDefinition', 'plan-definition'], +]) const ACTIVE_ENTRY_TYPES: ReadonlySet = new Set([ ...ACTIVE_ENTRY_POLICY.outputResourceTypes, ...ACTIVE_ENTRY_POLICY.supportingResourceTypes, @@ -77,21 +92,16 @@ const validateEnvelopeProfile = ( requiredProfile: string, context: z.core.$RefinementCtx, ): void => { - const exchangeProfiles = new Set([ - groveMobileContract.profiles.exchangeBundle, - groveMobileContract.profiles.retractionBundle, + const exchangeProfiles = new Set([ + EXCHANGE_BUNDLE_PROFILE, + RETRACTION_BUNDLE_PROFILE, ]) const claimed = (bundle.meta?.profile ?? []).filter( (profile): profile is string => typeof profile === 'string' && exchangeProfiles.has(profile), ) if (claimed.length !== 1 || claimed[0] !== requiredProfile) { - addIssue( - context, - 'mobile-exchange.profile', - ['meta', 'profile'], - `Bundle must declare ${requiredProfile}.`, - ) + addIssue(context, 'mobile-exchange.bundle-profile', ['meta', 'profile']) } } @@ -108,12 +118,7 @@ const validateIdentitySystemRoles = ( (priorSystem !== undefined && priorSystem !== located.system) || (priorRole !== undefined && priorRole !== located.role) ) { - addIssue( - context, - 'mobile-exchange.identity-system-role', - located.path, - 'Within one event, each Grove Identifier role must use one dedicated Identifier.system and a system must not serve multiple Grove roles.', - ) + addIssue(context, 'mobile-exchange.identity-system-role', located.path) continue } systemsByRole.set(located.role, located.system) @@ -132,29 +137,25 @@ const validateEntryTransport = ( entry.request !== undefined || entry.response !== undefined ) { - addIssue( - context, - 'mobile-exchange.collection-entry', - ['entry', index], - 'Exchange collection entries prohibit search, request, and response metadata.', - ) + addIssue(context, 'mobile-exchange.collection-entry-operation', [ + 'entry', + index, + ]) } if (typeof entry.fullUrl !== 'string' || !isLowercaseUuidV5(entry.fullUrl)) { - addIssue( - context, - 'mobile-exchange.deterministic-full-url', - ['entry', index, 'fullUrl'], - 'entry.fullUrl must be a lowercase RFC 4122 UUID-v5 URN.', - ) + addIssue(context, 'mobile-exchange.deterministic-full-url', [ + 'entry', + index, + 'fullUrl', + ]) return undefined } if (state.fullUrls.has(entry.fullUrl)) { - addIssue( - context, - 'mobile-exchange.distinct-full-url', - ['entry', index, 'fullUrl'], - 'Bundle entry fullUrls must be unique.', - ) + addIssue(context, 'mobile-exchange.deterministic-full-url', [ + 'entry', + index, + 'fullUrl', + ]) } state.fullUrls.add(entry.fullUrl) return entry.fullUrl @@ -191,12 +192,11 @@ const validateEntryNodeOrdinal = ( if (written === undefined || expected === undefined || written === expected) { return } - addIssue( - context, - 'mobile-exchange.entry-node-ordinal', - ['entry', index, 'extension'], - `This entry is number ${expected} among the entries sharing its node role, so its entry-node ordinal cannot be ${written}.`, - ) + addIssue(context, 'mobile-exchange.entry-node-ordinal', [ + 'entry', + index, + 'extension', + ]) } const validateEntryNodeDigest = ( @@ -208,24 +208,22 @@ const validateEntryNodeDigest = ( const parts = parseEntryNodeParts(key.value) const expected = parts === null ? undefined : ( - deriveEntryNodeValue( - event.system, - event.value, - parts[1] ?? '', - parts[2] ?? '', - ) - ) - if ( - !isEntryNodeIdentityValue(key.value) || - !expected?.ok || - expected.value !== key.value - ) { - addIssue( - context, - 'mobile-exchange.entry-node-digest', - ['entry', index, 'extension'], - 'An entry-node digest must be derived from this event, role, and ordinal.', + deriveEntryNodeValue({ + event: { + system: event.system, + value: event.value, + role: 'event', + } as ExchangeEventIdentifier, + role: parts[1] ?? '', + ordinal: (parts[2] ?? '') as EntryNodeOrdinal, + }) ) + if (!expected?.ok || expected.value !== key.value) { + addIssue(context, 'mobile-exchange.entry-node-digest', [ + 'entry', + index, + 'extension', + ]) } } @@ -237,14 +235,16 @@ const validateEntryKeyValue = ( state: EnvelopeState, context: z.core.$RefinementCtx, ): void => { - if (role === undefined) { - addIssue( - context, - 'mobile-exchange.entry-key-role', - ['entry', index, 'extension'], - 'The entry key requires one Grove Identifier role.', - ) - } else if (role === 'entry-node') { + if (role === 'entry-node') { + // A key typed entry-node that is not in the n0 form selects nothing. + if (!isEntryNodeIdentityValue(key.value)) { + addIssue(context, 'mobile-exchange.entry-key-selection', [ + 'entry', + index, + 'extension', + ]) + return + } validateEntryNodeDigest(key, event, index, context) validateEntryNodeOrdinal( key, @@ -253,15 +253,15 @@ const validateEntryKeyValue = ( context, ) } else if ( + role === undefined || !OPAQUE_IDENTIFIER_ROLES.has(role) || !isOpaqueIdentityValue(key.value) ) { - addIssue( - context, - 'mobile-exchange.opaque-entry-key', - ['entry', index, 'extension'], - 'Business entry keys require a closed role and canonical Grove HMAC value.', - ) + addIssue(context, 'mobile-exchange.entry-node-key', [ + 'entry', + index, + 'extension', + ]) } } @@ -274,22 +274,20 @@ const validateEntryKey = ( ): ValidatedEntryKey | undefined => { const key = entryKey(entry) if (!completeIdentifier(key)) { - addIssue( - context, - 'mobile-exchange.entry-node-key', - ['entry', index, 'extension'], - 'Every entry requires exactly one complete Grove entry-node-key Identifier.', - ) + addIssue(context, 'mobile-exchange.entry-node-key', [ + 'entry', + index, + 'extension', + ]) return undefined } const pair = identifierPairKey(key) if (state.keyPairs.has(pair)) { - addIssue( - context, - 'mobile-exchange.distinct-entry-key', - ['entry', index, 'extension'], - 'Entry node-key Identifier pairs must be distinct.', - ) + addIssue(context, 'mobile-exchange.distinct-entry-key', [ + 'entry', + index, + 'extension', + ]) } state.keyPairs.add(pair) const role = identifierRole(key) @@ -309,20 +307,20 @@ const validateResourceShape = ( active && (typeof resourceType !== 'string' || !ACTIVE_ENTRY_TYPES.has(resourceType)) ) { - addIssue( - context, - 'mobile-exchange.entry-resource-type', - ['entry', index, 'resource', 'resourceType'], - 'An active event admits only its catalog-closed output, supporting, and lifecycle resource types.', - ) + addIssue(context, 'mobile-exchange.entry-resource-type', [ + 'entry', + index, + 'resource', + 'resourceType', + ]) } if ('contained' in resource) { - addIssue( - context, - 'mobile-exchange.contained-resource-prohibited', - ['entry', index, 'resource', 'contained'], - 'Mobile exchange event graphs prohibit contained resources.', - ) + addIssue(context, 'mobile-exchange.contained-resource-prohibited', [ + 'entry', + index, + 'resource', + 'contained', + ]) } if (typeof resourceType === 'string' && typeof resource.id === 'string') { state.internalLogicalReferences.add(`${resourceType}/${resource.id}`) @@ -335,41 +333,41 @@ const resourceIdentityRoleCounts = ( context: z.core.$RefinementCtx, ): ReadonlyMap => { const roleCounts = new Map() - for (const candidate of identifiersOf(resource)) { + const declaredType = asRecord(resource)?.resourceType + const resourceType = + typeof declaredType === 'string' ? declaredType : 'Resource' + for (const [position, candidate] of identifiersOf(resource).entries()) { const groveRoles = groveIdentifierRoles(candidate) - const role = identifierRole(candidate) if (groveRoles.length === 0) continue + const path = ['entry', index, 'resource', 'identifier', position] + const location = `${resourceType}.identifier[${String(position)}]` + const role = identifierRole(candidate) if (role === undefined || !OPAQUE_IDENTIFIER_ROLES.has(role)) { + addIssue(context, 'mobile-exchange.identifier-role', path, { location }) + continue + } + const count = (roleCounts.get(role) ?? 0) + 1 + roleCounts.set(role, count) + if (count > 1) { addIssue( context, - 'mobile-exchange.identifier-role', - ['entry', index, 'resource', 'identifier'], - 'A Grove-typed resource Identifier requires exactly one closed opaque identity role.', + 'mobile-exchange.distinct-resource-identity-role', + path, + { + location, + }, ) - continue } - roleCounts.set(role, (roleCounts.get(role) ?? 0) + 1) if ( !completeIdentifier(candidate) || !parseAbsoluteUri(candidate.system).ok || !isOpaqueIdentityValue(candidate.value) ) { - addIssue( - context, - 'mobile-exchange.opaque-resource-identity', - ['entry', index, 'resource', 'identifier'], - 'A typed resource identity must be one complete canonical Grove HMAC Identifier.', - ) + addIssue(context, 'mobile-exchange.opaque-resource-identity', path, { + location, + }) } } - if ([...roleCounts.values()].some((count) => count > 1)) { - addIssue( - context, - 'mobile-exchange.distinct-resource-identity-role', - ['entry', index, 'resource', 'identifier'], - 'A resource may carry at most one Identifier for each Grove identity role.', - ) - } return roleCounts } @@ -385,12 +383,11 @@ const validateSelectedEntryKey = ( (selected === undefined && role !== 'entry-node') || (selected !== undefined && !identifierPairEqual(selected, key)) ) { - addIssue( - context, - 'mobile-exchange.entry-key-selection', - ['entry', index, 'extension'], - 'The entry key must be the highest-priority typed business Identifier, or entry-node when none exists.', - ) + addIssue(context, 'mobile-exchange.entry-key-selection', [ + 'entry', + index, + 'extension', + ]) } } @@ -405,19 +402,45 @@ const validateProvenanceEntryNode = ( if (resource.resourceType !== 'Provenance' || role !== 'entry-node') return const parts = parseEntryNodeParts(key.value) const expectedRole = - requiredProfile === groveMobileContract.profiles.retractionBundle ? + requiredProfile === RETRACTION_BUNDLE_PROFILE ? 'retraction-provenance' : 'conversion-provenance' if (parts?.[1] !== expectedRole || parts[2] !== '0') { - addIssue( - context, - 'mobile-exchange.provenance-node-key', - ['entry', index, 'extension'], - `Provenance requires the ${expectedRole} entry-node at ordinal zero.`, - ) + addIssue(context, 'mobile-exchange.entry-node-ordinal', [ + 'entry', + index, + 'extension', + ]) } } +// A study context keys its supporting entries by the catalog's closed node roles, so a +// receiver reads which resource an entry-node key stands for from the key alone. +const validateStudyContextEntryNode = ( + resource: UnknownRecord, + key: CompleteIdentifier, + role: string | undefined, + index: number, + context: z.core.$RefinementCtx, +): void => { + const expectedRole = ENTRY_NODE_ROLE_BY_RESOURCE_TYPE.get( + String(resource.resourceType), + ) + if ( + expectedRole === undefined || + role !== 'entry-node' || + parseEntryNodeParts(key.value)?.[1] === expectedRole + ) { + return + } + addIssue( + context, + 'mobile-support.study-context', + ['entry', index, 'extension'], + { location: `Bundle.entry[${String(index)}].extension.valueIdentifier` }, + ) +} + const validateEntryResource = ( entry: UnknownRecord, fullUrl: string, @@ -429,12 +452,11 @@ const validateEntryResource = ( context: z.core.$RefinementCtx, ): void => { if (entry.resource === undefined) { - addIssue( - context, - 'mobile-exchange.resource-required', - ['entry', index, 'resource'], - 'Every exchange entry requires a resource.', - ) + addIssue(context, 'mobile-exchange.entry-resource-type', [ + 'entry', + index, + 'resource', + ]) return } const resource = asRecord(entry.resource) ?? {} @@ -461,6 +483,13 @@ const validateEntryResource = ( index, context, ) + validateStudyContextEntryNode( + resource, + key.identifier, + key.role, + index, + context, + ) } const validateDerivedFullUrl = ( @@ -474,12 +503,11 @@ const validateDerivedFullUrl = ( value: key.value, }) if (!derived.ok || derived.value !== fullUrl) { - addIssue( - context, - 'mobile-exchange.deterministic-full-url', - ['entry', index, 'fullUrl'], - 'entry.fullUrl must be UUID-v5 over the length-framed entry key pair.', - ) + addIssue(context, 'mobile-exchange.deterministic-full-url', [ + 'entry', + index, + 'fullUrl', + ]) } } @@ -533,8 +561,7 @@ const validateLocatedReference = ( context, 'mobile-exchange.resolved-reference', ['entry', entryIndex, 'resource', ...located.path], - `Literal reference ${located.reference} does not resolve to one Bundle entry fullUrl.`, - `${bundleLocation}.reference`, + { location: `${bundleLocation}.reference` }, ) } else { const targetType = state.resourcesByFullUrl.get( @@ -548,17 +575,16 @@ const validateLocatedReference = ( context, 'mobile-exchange.reference-declared-type', ['entry', entryIndex, 'resource', ...located.path, 'type'], - 'Reference.type must agree with the resolved internal resource type.', - `${resourceLocation}.type`, + { location: `${resourceLocation}.type` }, ) } } if (state.internalLogicalReferences.has(located.reference)) { addIssue( context, - 'mobile-exchange.internal-reference-full-url', + 'mobile-exchange.reference-shape', ['entry', entryIndex, 'resource', ...located.path], - `Reference ${located.reference} targets a Bundle entry and must use that entry's UUID fullUrl.`, + { location: bundleLocation }, ) } } @@ -590,35 +616,23 @@ export const validateExchangeEnvelope = ( requiredProfile: string, ): ValidatedEnvelope | undefined => { validateEnvelopeProfile(bundle, requiredProfile, context) + if (groveIdentifierRoles(bundle.identifier).length !== 1) { + addIssue(context, 'mobile-exchange.identifier-role', ['identifier']) + } if ( !completeIdentifier(bundle.identifier) || !parseAbsoluteUri(bundle.identifier.system).ok || identifierRole(bundle.identifier) !== 'event' || !isEventIdentityValue(bundle.identifier.value) ) { - addIssue( - context, - 'mobile-exchange.event-identity', - ['identifier'], - 'Bundle.identifier must be one complete typed canonical e0 event Identifier.', - ) + addIssue(context, 'mobile-exchange.event-identity', ['identifier']) return undefined } if (bundle.timestamp === undefined) { - addIssue( - context, - 'mobile-exchange.assembled-time', - ['timestamp'], - 'Bundle.timestamp is the mandatory graph assembly time.', - ) + addIssue(context, 'mobile-exchange.event-times', ['timestamp']) } if (bundle.entry === undefined || bundle.entry.length === 0) { - addIssue( - context, - 'mobile-exchange.entry-required', - ['entry'], - 'An exchange event requires at least one entry.', - ) + addIssue(context, 'mobile-exchange.entry-required', ['entry']) return undefined } @@ -631,7 +645,7 @@ export const validateExchangeEnvelope = ( resourcesByFullUrl: new Map(), entryNodeOrdinals: entryNodeOrdinals(entries), } - const active = requiredProfile === groveMobileContract.profiles.exchangeBundle + const active = requiredProfile === EXCHANGE_BUNDLE_PROFILE for (const [index, entry] of entries.entries()) { validateEntry( entry, diff --git a/packages/fhir/src/r4/graph-schema-utils.ts b/packages/fhir/src/r4/graph-schema-utils.ts index efb59d99..498b8b8f 100644 --- a/packages/fhir/src/r4/graph-schema-utils.ts +++ b/packages/fhir/src/r4/graph-schema-utils.ts @@ -9,10 +9,19 @@ /* eslint-disable sonarjs/no-clear-text-protocols -- FHIR R4 fixes lifecycle and participant canonicals to HTTP. */ import type { z } from 'zod' -import { groveRuleParameters, groveRuleReason } from './diagnostics.js' +import { + groveRuleParameters, + type GroveRuleIssueOptions, + type ProducerDiagnosticCode, +} from './diagnostics.js' +import { + groveExchangeProtocol, + groveMobileProfileCanonicals, + groveProducerDiagnostics, +} from '../contract/measurement-catalog.generated.js' import { healthKitApplicationDeviceIdentity } from '../contract/providers.generated.js' import { parseAbsoluteUri } from '../core/index.js' -import { groveMobileContract } from '../mobile/contract.js' +import { identityValuePrefixes } from '../mobile/identity.js' export type UnknownRecord = Readonly> @@ -36,7 +45,7 @@ export const completeIdentifier = ( const LOWERCASE_UUID_V5 = /^urn:uuid:[\da-f]{8}-[\da-f]{4}-5[\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}$/u const ENTRY_NODE_PARTS = new RegExp( - `^${groveMobileContract.identity.valuePrefixes.entryNode}([a-z][a-z\\d-]*):(0|[1-9]\\d*):[A-Za-z\\d_-]{43}$`, + `^${identityValuePrefixes.entryNode}([a-z][a-z\\d-]*):(0|[1-9]\\d*):[A-Za-z\\d_-]{43}$`, 'u', ) @@ -51,17 +60,21 @@ export const PROVENANCE_PARTICIPANT_SYSTEM = 'http://terminology.hl7.org/CodeSystem/provenance-participant-type' export const APPLICATION_DEVICE_PROFILES: ReadonlySet = new Set([ - groveMobileContract.profiles.applicationDevice, + groveMobileProfileCanonicals['grove-application-device'], healthKitApplicationDeviceIdentity.profile, ]) const ENTRY_KEY_PRIORITY: readonly string[] = - groveMobileContract.identity.resourceIdentifierPriority + groveExchangeProtocol.entryIdentity.resourceIdentifierPriority export const OPAQUE_IDENTIFIER_ROLES: ReadonlySet = new Set( - groveMobileContract.identity.opaqueIdentifierRoles, + groveExchangeProtocol.opaqueIdentity.identityKinds.map( + ({ identifierRole }) => identifierRole, + ), ) +const IDENTIFIER_ROLE_SYSTEM = groveExchangeProtocol.codeSystems.identifierRole + export const groveIdentifierRoles = (value: unknown): readonly string[] => { const identifier = asRecord(value) const type = asRecord(identifier?.type) @@ -69,8 +82,7 @@ export const groveIdentifierRoles = (value: unknown): readonly string[] => { return coding.flatMap((candidate) => { const item = asRecord(candidate) return ( - item?.system === groveMobileContract.systems.identifierRole && - typeof item.code === 'string' + item?.system === IDENTIFIER_ROLE_SYSTEM && typeof item.code === 'string' ) ? [item.code] : [] @@ -112,7 +124,8 @@ export const entryKey = (entry: unknown): unknown => { if (!Array.isArray(extension)) return undefined const matches = extension.filter( (candidate) => - asRecord(candidate)?.url === groveMobileContract.extensions.entryNodeKey, + asRecord(candidate)?.url === + groveExchangeProtocol.extensions.entryNodeKey, ) return matches.length === 1 ? asRecord(matches[0])?.valueIdentifier @@ -235,13 +248,9 @@ const validateInternalTarget = ( typeof target.resourceType !== 'string' || !allowedTypes.has(target.resourceType) ) { - addIssue( - context, - 'mobile-exchange.reference-target-type', - path, - `Internal reference must resolve to ${[...allowedTypes].join(' or ')}.`, - `${location}.reference`, - ) + addIssue(context, 'mobile-exchange.reference-target-type', path, { + location: `${location}.reference`, + }) } } @@ -259,7 +268,7 @@ const validateGovernedReferenceShape = ( const identifier = asRecord(value?.identifier) const patientOnly = allowedTypes.size === 1 && allowedTypes.has('Patient') const reservedPatientSystems: ReadonlySet = new Set( - groveMobileContract.referencePolicy.identifierOnlyPatient.reservedSystems, + groveExchangeProtocol.referencePolicy.identifierOnlyPatient.reservedSystems, ) const declaredType = value?.type if ( @@ -273,8 +282,7 @@ const validateGovernedReferenceShape = ( context, 'mobile-exchange.logical-patient-reference', [...path, 'identifier', 'system'], - 'An identifier-only Patient Reference cannot use a protocol-reserved system.', - `${location}.identifier.system`, + { location: `${location}.identifier.system` }, ) return } @@ -288,8 +296,7 @@ const validateGovernedReferenceShape = ( context, 'mobile-exchange.logical-patient-reference', [...path, 'identifier', 'type'], - 'An identifier-only Patient Reference cannot carry a Grove identifier role.', - `${location}.identifier.type`, + { location: `${location}.identifier.type` }, ) return } @@ -300,13 +307,7 @@ const validateGovernedReferenceShape = ( typeof declaredType === 'string' && allowedTypes.has(declaredType) if (hasLiteral && hasIdentifier) { - addIssue( - context, - 'mobile-exchange.reference-shape', - path, - 'A governed Reference must be exclusively a resolving literal or a typed identifier-only logical reference with one complete absolute-system Identifier.', - location, - ) + addIssue(context, 'mobile-exchange.reference-shape', path, { location }) } else if (!hasLiteral && hasIdentifier && !validLogical) { addIssue( context, @@ -314,29 +315,26 @@ const validateGovernedReferenceShape = ( 'mobile-exchange.logical-patient-reference' : 'mobile-exchange.reference-shape', path, - 'An identifier-only logical Reference requires its exact admitted type and one complete absolute-system Identifier.', - location, + { location }, ) } else if (!hasLiteral && !validLogical) { - addIssue( - context, - 'mobile-exchange.reference-shape', - path, - 'A governed Reference requires exactly one resolving literal or identifier-only logical shape.', - location, - ) + addIssue(context, 'mobile-exchange.reference-shape', path, { location }) } } // The reference-policy catalog states admitted targets, not cardinality. Every Grove -// profile binding one of these paths to Patient also binds it 1..1, so an absent -// subject is a contract violation the governed-reference check must report. +// profile binding one of these paths to Patient also binds it 1..1, and a bundled study +// context names its protocol, study and participant, so an absent reference is a +// contract violation the governed-reference check must report. const MANDATORY_REFERENCE_PATHS: ReadonlySet = new Set([ 'DocumentReference.subject', 'MedicationAdministration.subject', 'MedicationStatement.subject', 'Observation.subject', 'QuestionnaireResponse.subject', + 'ResearchStudy.protocol', + 'ResearchSubject.individual', + 'ResearchSubject.study', 'Specimen.subject', 'VisionPrescription.patient', ]) @@ -347,7 +345,7 @@ const validateProfilePathReferenceTargets = ( context: z.core.$RefinementCtx, path: ReadonlyArray, ): void => { - for (const rule of groveMobileContract.referencePolicy.paths) { + for (const rule of groveExchangeProtocol.referencePolicy.paths) { if (rule.resourceType !== resource.resourceType) continue const references = resource[rule.path] const values = Array.isArray(references) ? references : [references] @@ -360,8 +358,9 @@ const validateProfilePathReferenceTargets = ( context, 'mobile-exchange.reference-shape', [...path, rule.path], - `${ruleLocation} is mandatory and requires one governed Reference.`, - ruleLocation, + { + location: ruleLocation, + }, ) continue } @@ -404,7 +403,7 @@ const validateProfileExtensionReferenceTargets = ( const extensions = Array.isArray(resource.extension) ? resource.extension : [] for (const [index, extension] of extensions.entries()) { const item = asRecord(extension) - const rule = groveMobileContract.referencePolicy.extensionTargets.find( + const rule = groveExchangeProtocol.referencePolicy.extensionTargets.find( ({ url }) => url === item?.url, ) if (rule !== undefined && item !== undefined) { @@ -481,17 +480,17 @@ export const codingCountForSystem = ( : 0 } +/** Records one registered producer rule; its reason is the registry's, never a local string. */ export const addIssue = ( context: z.core.$RefinementCtx, - code: string, + code: ProducerDiagnosticCode, path: ReadonlyArray, - message: string, - location?: string, + options: GroveRuleIssueOptions = {}, ): void => { context.addIssue({ code: 'custom', path: [...path], - message: groveRuleReason(code) ?? message, - params: groveRuleParameters(code, path, location), + message: groveProducerDiagnostics[code].reason, + params: groveRuleParameters(code, path, options), }) } diff --git a/packages/fhir/src/r4/index.ts b/packages/fhir/src/r4/index.ts index 98c4c76c..deab2e52 100644 --- a/packages/fhir/src/r4/index.ts +++ b/packages/fhir/src/r4/index.ts @@ -7,8 +7,8 @@ // export { - parseGroveMobileExchangeBundle, - parseGroveMobileRetractionBundle, + parseExchangeGraph, + parseRetractionEvent, parseR4CollectionBundle, parseDevice, parseDocumentReference, @@ -23,11 +23,10 @@ export { codingSchema, deviceSchema, documentReferenceSchema, + exchangeGraphSchema, expressionSchema, extensionSchema, graphResourceSchema, - groveMobileExchangeBundleSchema, - groveMobileRetractionBundleSchema, identifierSchema, metaSchema, observationComponentSchema, @@ -37,30 +36,42 @@ export { provenanceSchema, quantitySchema, resolvableReferenceSchema, + retractionEventSchema, r4CollectionBundleSchema, sampledDataSchema, specimenSchema, supportedR4ResourceSchema, } from './schemas.js' +export { isSemanticallyEqual } from './semantic-equality.js' +export { + retractionTargets, + type RetractionTarget, + type RetractionTargetResourceType, + type RetractionTargetRole, +} from './retraction-targets.js' export type { Attachment, CodeableConcept, Coding, Device, DocumentReference, + ExchangeGraph, Extension, GraphResource, - GroveMobileExchangeBundle, - GroveMobileRetractionBundle, Identifier, Observation, + Patient, Period, + PlanDefinition, Provenance, R4CollectionBundle, Questionnaire, QuestionnaireResponse, Quantity, Reference, + ResearchStudy, + ResearchSubject, + RetractionEvent, SampledData, Specimen, SupportedR4Resource, diff --git a/packages/fhir/src/r4/parse.ts b/packages/fhir/src/r4/parse.ts index ed3f7ffe..392cbafe 100644 --- a/packages/fhir/src/r4/parse.ts +++ b/packages/fhir/src/r4/parse.ts @@ -6,16 +6,18 @@ // SPDX-License-Identifier: MIT // -import { type z } from 'zod' +import { z } from 'zod' +import { refineActiveBundle } from './active-graph-semantics.js' import { groveRuleIssue, groveRuleIssueFromParameters, - type GroveExchangeRuleCode, + isProducerDiagnosticCode, + type ProducerDiagnosticCode, } from './diagnostics.js' import { asRecord } from './graph-schema-utils.js' +import { refineRetractionBundle } from './retraction-graph-semantics.js' import { - groveMobileExchangeBundleSchema, - groveMobileRetractionBundleSchema, + exchangeGraphSchema, hasAdmittedActiveDeviceProfile, hasAdmittedActiveDocumentReferenceProfile, hasAdmittedActiveProvenanceProfile, @@ -28,18 +30,19 @@ import { documentReferenceSchema, observationSchema, provenanceSchema, + retractionEventSchema, specimenSchema, supportedR4ResourceSchema, } from './schemas.js' import type { Device, DocumentReference, + ExchangeGraph, Observation, Provenance, + RetractionEvent, Specimen, SupportedR4Resource, - GroveMobileExchangeBundle, - GroveMobileRetractionBundle, R4CollectionBundle, } from './types.js' import { @@ -57,7 +60,6 @@ const normalizeIssue = (entry: z.core.$ZodIssue): Issue => groveRuleIssueFromParameters( entry.code === 'custom' ? entry.params : undefined, zodIssuePath(entry), - entry.message, ) ?? zodIssueToIssue(entry) // The schema decides the parsed type; a caller-chosen T would make the cast below a lie. @@ -104,7 +106,7 @@ const ofType = asRecord(resource)?.resourceType === resourceType && rejects(resource) interface EntryRulePrecheck { - readonly code: GroveExchangeRuleCode + readonly code: ProducerDiagnosticCode readonly path: readonly string[] readonly rejects: (resource: unknown) => boolean } @@ -193,33 +195,68 @@ const entryRuleIssue = ( return undefined } +type GraphRefinement = ( + bundle: R4CollectionBundle, + context: z.core.$RefinementCtx, +) => void + +// The base R4 schema rejects a Bundle before its graph refinements run, yet the graph +// rule is the more specific diagnostic. The refinements read defensively, so they are +// tried on the snapshot itself; anything they cannot read falls back to the schema issues. +const graphRuleIssues = ( + snapshot: unknown, + refine: GraphRefinement, +): readonly Issue[] | undefined => { + try { + const result = z + .unknown() + .superRefine((value, context) => { + refine(value as R4CollectionBundle, context) + }) + .safeParse(snapshot) + if (result.success) return undefined + const rules = result.error.issues + .map(normalizeIssue) + .filter((issue) => isProducerDiagnosticCode(issue.code)) + return rules.length === 0 ? undefined : rules + } catch { + return undefined + } +} + const parseGraphBundle = ( schema: z.ZodType, + refine: GraphRefinement, prechecks: readonly EntryRulePrecheck[], input: unknown, ): Result => { const snapshot = cloneJsonValue(input) if (!snapshot.ok) return snapshot const early = entryRuleIssue(snapshot.value, prechecks) - return early === undefined ? - parseSnapshotWith(schema, snapshot.value) - : issues([early]) + if (early !== undefined) return issues([early]) + const result = schema.safeParse(snapshot.value) + if (result.success) return ok(deepFreeze(result.data) as T) + const normalized = result.error.issues.map(normalizeIssue) + if (normalized.some((issue) => isProducerDiagnosticCode(issue.code))) { + return issues(normalized) + } + return issues(graphRuleIssues(snapshot.value, refine) ?? normalized) } -export const parseGroveMobileExchangeBundle = ( - input: unknown, -): Result => +/** Parses and validates one active exchange event; it never throws for any input. */ +export const parseExchangeGraph = (input: unknown): Result => parseGraphBundle( - groveMobileExchangeBundleSchema, + exchangeGraphSchema, + refineActiveBundle, ACTIVE_ENTRY_PRECHECKS, input, ) -export const parseGroveMobileRetractionBundle = ( - input: unknown, -): Result => +/** Parses and validates one retraction assertion; it never throws for any input. */ +export const parseRetractionEvent = (input: unknown): Result => parseGraphBundle( - groveMobileRetractionBundleSchema, + retractionEventSchema, + refineRetractionBundle, RETRACTION_ENTRY_PRECHECKS, input, ) diff --git a/packages/fhir/src/r4/profile-semantics.ts b/packages/fhir/src/r4/profile-semantics.ts index c489b243..60bcb409 100644 --- a/packages/fhir/src/r4/profile-semantics.ts +++ b/packages/fhir/src/r4/profile-semantics.ts @@ -18,14 +18,15 @@ import { } from './graph-schema-utils.js' import { adapterSourceMarkerClaims, + groveMobileProfileCanonicals, groveProfileClaims, + groveRecordingFormatRegistry, } from '../contract/measurement-catalog.generated.js' import { healthKitApplicationDeviceIdentity, healthKitClinicalRecordAdmission, } from '../contract/providers.generated.js' import { decodeCanonicalBase64 } from '../core/index.js' -import { groveMobileContract } from '../mobile/contract.js' const SHA1_BASE64 = /^[A-Za-z\d+/]{26}[AEIMQUYcgkosw048]=$/u @@ -40,17 +41,17 @@ const bytesEqual = (left: Uint8Array, right: Uint8Array): boolean => left.length === right.length && left.every((value, index) => value === right[index]) -type RecordingFormats = typeof groveMobileContract.recordingFormats.formats +type RecordingFormats = typeof groveRecordingFormatRegistry.formats const isRecordingFormatCode = (code: unknown): code is keyof RecordingFormats => typeof code === 'string' && - Object.hasOwn(groveMobileContract.recordingFormats.formats, code) + Object.hasOwn(groveRecordingFormatRegistry.formats, code) const recordingFormat = ( code: unknown, ): RecordingFormats[keyof RecordingFormats] | undefined => isRecordingFormatCode(code) ? - groveMobileContract.recordingFormats.formats[code] + groveRecordingFormatRegistry.formats[code] : undefined interface ExactProfileClaim { @@ -161,12 +162,11 @@ const validateProfiledDevice = ( groveProfileClaims.activeDeviceClaims, ) if (claim === undefined) { - addIssue( - context, - 'mobile-support.device-profile', - [...path, 'meta', 'profile'], - 'An active Device must declare exactly one catalog-admitted direct profile mode.', - ) + addIssue(context, 'mobile-support.device-profile', [ + ...path, + 'meta', + 'profile', + ]) return } const requiredRoles = new Set(claim.requiredIdentifierRoles ?? []) @@ -174,37 +174,28 @@ const validateProfiledDevice = ( [...requiredRoles].some((role) => roleCounts.get(role) !== 1) || [...roleCounts].some(([role]) => !requiredRoles.has(role)) ) { - addIssue( - context, - 'mobile-support.device-profile', - [...path, 'identifier'], - 'An active Device must carry exactly the typed identities required by its admitted profile mode.', - ) + addIssue(context, 'mobile-support.device-profile', [...path, 'identifier']) return } const profile = claim.profiles[0] if ( - profile === groveMobileContract.profiles.recordingDevice && + profile === groveMobileProfileCanonicals['grove-recording-device'] && (roleCounts.get('recording-device') !== 1 || identifiersOf(resource).length !== 2) ) { - addIssue( - context, - 'mobile-recording-device.identities', - [...path, 'identifier'], - 'A recording Device requires exactly one stable recording-device identity and one event snapshot identity.', - ) + addIssue(context, 'mobile-device.recording-device-dual-identity', [ + ...path, + 'identifier', + ]) } if ( - profile === groveMobileContract.profiles.hostDevice && + profile === groveMobileProfileCanonicals['grove-host-device'] && identifiersOf(resource).length !== 1 ) { - addIssue( - context, - 'mobile-host-device.identity', - [...path, 'identifier'], - 'A host Device carries exactly one event snapshot identity.', - ) + addIssue(context, 'mobile-device.host-device-identity', [ + ...path, + 'identifier', + ]) } if (profile === healthKitApplicationDeviceIdentity.profile) { const definition = healthKitApplicationDeviceIdentity.bundleIdentifier @@ -226,12 +217,10 @@ const validateProfiledDevice = ( typeof bundleIdentifierValue !== 'string' || bundleIdentifierValue.trim() === '' ) { - addIssue( - context, - 'healthkit-application-device.bundle-identifier', - [...path, 'identifier'], - 'A HealthKit application Device requires exactly one typed Apple bundle product identifier.', - ) + addIssue(context, 'healthkit-device.application-bundle-identifier', [ + ...path, + 'identifier', + ]) } } } @@ -244,12 +233,11 @@ const validateRecordingDocument = ( ) => { const claim = exactProfileClaim(resource, DOCUMENT_REFERENCE_PROFILE_CLAIMS) if (claim === undefined) { - addIssue( - context, - 'mobile-output.document-profile', - [...path, 'meta', 'profile'], - 'A source-preservation DocumentReference must declare exactly one catalog-admitted direct profile set.', - ) + addIssue(context, 'mobile-output.document-profile', [ + ...path, + 'meta', + 'profile', + ]) } const requiredRoles: ReadonlySet = new Set( claim?.requiredIdentifierRoles ?? [], @@ -258,12 +246,10 @@ const validateRecordingDocument = ( (role) => roleCounts.get(role) !== 1, ) if (claim !== undefined && missingRoles.length > 0) { - addIssue( - context, - 'mobile-recording-document.identities', - [...path, 'identifier'], - 'A source-preservation DocumentReference requires every identity role named by its exact profile claim.', - ) + addIssue(context, 'sensor-recording-document.identity-and-content', [ + ...path, + 'identifier', + ]) } const admittedRoles = new Set([...requiredRoles, 'writer-record']) if ( @@ -271,24 +257,17 @@ const validateRecordingDocument = ( (claim !== undefined && [...roleCounts].some(([role]) => !admittedRoles.has(role))) ) { - addIssue( - context, - 'mobile-recording-document.identity-roles', - [...path, 'identifier'], - 'A recording DocumentReference admits only its three required identities and at most one writer-record identity.', - ) + addIssue(context, 'sensor-recording-document.identity-and-content', [ + ...path, + 'identifier', + ]) } if ( resource.type === undefined || resource.subject === undefined || typeof resource.date !== 'string' ) { - addIssue( - context, - 'mobile-recording-document.required-metadata', - path, - 'A recording DocumentReference requires type, Patient subject, and document creation date.', - ) + addIssue(context, 'sensor-recording-document.identity-and-content', path) } const content = Array.isArray(resource.content) ? resource.content : [] const item = asRecord(content[0]) @@ -308,12 +287,10 @@ const validateRecordingDocument = ( typeof attachment.hash !== 'string' || !SHA1_BASE64.test(attachment.hash) ) { - addIssue( - context, - 'mobile-recording-document.attachment', - [...path, 'content'], - 'A recording has one attachment with exactly one payload location, media type, R4-sized byte count, and canonical SHA-1 hash.', - ) + addIssue(context, 'sensor-recording-document.identity-and-content', [ + ...path, + 'content', + ]) } if (typeof attachment?.data === 'string') { const payload = decodeFhirBase64(attachment.data) @@ -324,29 +301,29 @@ const validateRecordingDocument = ( payload.length !== attachment.size || !bytesEqual(sha1(payload), digest) ) { - addIssue( - context, - 'mobile-recording-document.embedded-integrity', - [...path, 'content', 0, 'attachment'], - 'Embedded recording size and SHA-1 hash must match the decoded attachment bytes.', - ) + addIssue(context, 'sensor-recording-document.embedded-integrity', [ + ...path, + 'content', + 0, + 'attachment', + ]) } } const definition = recordingFormat(format?.code) const formatContentTypes: readonly string[] = definition?.contentTypes ?? [] if ( - format?.system !== groveMobileContract.recordingFormats.codeSystem || + format?.system !== groveRecordingFormatRegistry.codeSystem || format.version !== undefined || definition?.status !== 'active' || typeof attachment?.contentType !== 'string' || !formatContentTypes.includes(attachment.contentType) ) { - addIssue( - context, - 'mobile-recording-document.format', - [...path, 'content', 0, 'format'], - 'Recording format and content type must match the active Grove registry entry; Coding.version is not permitted.', - ) + addIssue(context, 'sensor-recording-document.format', [ + ...path, + 'content', + 0, + 'format', + ]) } if (claim === groveProfileClaims.healthKitClinicalRecordDocumentClaim) { const representation = healthKitClinicalRecordAdmission.fhirRepresentation @@ -359,12 +336,13 @@ const validateRecordingDocument = ( typeof attachment?.contentType !== 'string' || !admittedContentTypes.includes(attachment.contentType) ) { - addIssue( - context, - 'healthkit-clinical.fhir-representation', - [...path, 'content', 0, 'attachment', 'contentType'], - 'A HealthKit clinical-record document must declare the admitted payload format and the exact media type of its source FHIR release.', - ) + addIssue(context, 'healthkit-clinical.fhir-representation', [ + ...path, + 'content', + 0, + 'attachment', + 'contentType', + ]) } } } @@ -379,12 +357,11 @@ const validateProfiledQuestionnaireResponse = ( groveProfileClaims.activeQuestionnaireResponseClaim, ]) === undefined ) { - addIssue( - context, - 'mobile-support.questionnaire-response-profile', - [...path, 'meta', 'profile'], - 'An active QuestionnaireResponse must directly declare exactly the admitted Grove QuestionnaireResponse profile.', - ) + addIssue(context, 'mobile-support.questionnaire-response-profile', [ + ...path, + 'meta', + 'profile', + ]) } } @@ -439,12 +416,9 @@ const validateAdapterSourceMarkers = ( (claimsAdapterProfile && (candidates.length !== 1 || !complete)) || (!claimsAdapterProfile && candidates.length !== 0) ) { - addIssue( - context, - 'mobile-exchange.adapter-source-marker', - path, - `The ${claim.adapter} source marker must appear exactly once on its catalog-owned adapter output and never on a source-neutral or different-adapter output.`, - ) + addIssue(context, 'mobile-output.adapter-source-marker', path, { + location: `${resource.resourceType}.extension`, + }) } } } @@ -474,12 +448,11 @@ const validateAdapterOnlyOutputProfile = ( resource.resourceType === 'Specimen' && identifiersOf(resource).length !== requiredRoles.size if (!exactProfile || invalidRoles || closedSpecimenIdentifiers) { - addIssue( - context, - 'mobile-output.adapter-only-profile', - [...path, 'meta', 'profile'], - 'This active output resource type is admitted only under its exact one-profile adapter claim and required typed identities.', - ) + addIssue(context, 'mobile-output.adapter-only-profile', [ + ...path, + 'meta', + 'profile', + ]) } } diff --git a/packages/fhir/src/r4/retraction-graph-semantics.ts b/packages/fhir/src/r4/retraction-graph-semantics.ts index efa16d37..b01922c4 100644 --- a/packages/fhir/src/r4/retraction-graph-semantics.ts +++ b/packages/fhir/src/r4/retraction-graph-semantics.ts @@ -24,23 +24,26 @@ import { type UnknownRecord, } from './graph-schema-utils.js' import type { R4CollectionBundle } from './types.js' +import { groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' import { parseAbsoluteUri } from '../core/index.js' -import { groveMobileContract } from '../mobile/contract.js' import { isOpaqueIdentityValue } from '../mobile/identity.js' +const RETRACTION = groveExchangeProtocol.lifecycle.retraction +const LIFECYCLE_EVENT_SYSTEM = groveExchangeProtocol.codeSystems.lifecycleEvent + const retractionRoleFor = (reference: unknown): string | undefined => { const extensions = asRecord(reference)?.extension if (!Array.isArray(extensions)) return undefined const matches = extensions.filter( (extension) => asRecord(extension)?.url === - groveMobileContract.extensions.retractionTargetRole, + groveExchangeProtocol.extensions.retractionTargetRole, ) const code = asRecord(matches[0])?.valueCode return matches.length === 1 && typeof code === 'string' ? code : undefined } -const RETRACTION_TARGET_RULES = groveMobileContract.lifecycle.retractionTargets +const RETRACTION_TARGET_RULES = RETRACTION.targetRoles const RETRACTION_ROLES: ReadonlySet = new Set( Object.keys(RETRACTION_TARGET_RULES), ) @@ -56,12 +59,7 @@ const validateRetractionResourceSet = ( ), ) if (containsClinicalCopy) { - addIssue( - context, - 'mobile-retraction.no-clinical-copy', - ['entry'], - 'Retraction Bundles contain Provenance and optional Device agents only.', - ) + addIssue(context, 'mobile-retraction.no-clinical-copy', ['entry']) } } @@ -75,17 +73,12 @@ const selectRetractionProvenance = ( const retractions = provenances.filter((resource) => codingExists( asRecord(resource)?.activity, - groveMobileContract.systems.lifecycleEvent, - groveMobileContract.lifecycle.sourceRecordRetracted, + LIFECYCLE_EVENT_SYSTEM, + RETRACTION.activityCode, ), ) if (provenances.length !== 1 || retractions.length !== 1) { - addIssue( - context, - 'mobile-retraction.provenance', - ['entry'], - 'A retraction event requires exactly one retraction Provenance and no transform Provenance.', - ) + addIssue(context, 'mobile-retraction.provenance', ['entry']) return undefined } return asRecord(retractions[0]) @@ -100,33 +93,20 @@ const validateRetractionProvenanceHeader = ( if ( !Array.isArray(profiles) || profiles.length !== 1 || - profiles[0] !== groveMobileContract.profiles.retractionProvenance + profiles[0] !== groveExchangeProtocol.profiles.retractionProvenance ) { - addIssue( - context, - 'mobile-retraction.provenance-profile', - ['entry'], - 'Retraction Provenance must declare the Grove Mobile retraction profile.', - ) + addIssue(context, 'mobile-exchange.provenance-profile', ['entry']) } if ( codingCount( provenance.activity, - groveMobileContract.systems.lifecycleEvent, - groveMobileContract.lifecycle.sourceRecordRetracted, - ) !== 1 || - codingCountForSystem( - provenance.activity, - groveMobileContract.systems.lifecycleEvent, + LIFECYCLE_EVENT_SYSTEM, + RETRACTION.activityCode, ) !== 1 || + codingCountForSystem(provenance.activity, LIFECYCLE_EVENT_SYSTEM) !== 1 || codingCountForSystem(provenance.activity, ISO_LIFECYCLE_SYSTEM) !== 0 ) { - addIssue( - context, - 'mobile-exchange.lifecycle-coding', - ['entry'], - 'Retraction Provenance requires exactly one source-record-retracted coding from the Grove lifecycle system and no ISO transform lifecycle coding.', - ) + addIssue(context, 'mobile-exchange.lifecycle-coding', ['entry']) } validateAssemblerAgent(provenance, envelope, context, ['entry']) } @@ -143,12 +123,12 @@ const validateRetractionTargetShape = ( reference.type === '' || !completeIdentifier(targetIdentifier) ) { - addIssue( - context, - 'mobile-retraction.logical-target', - ['entry', 'resource', 'target', index], - 'A retraction target must be a typed logical Reference with no literal reference.', - ) + addIssue(context, 'mobile-retraction.logical-target', [ + 'entry', + 'resource', + 'target', + index, + ]) } } @@ -164,12 +144,13 @@ const validateRetractionTargetIdentity = ( identifierRole(targetIdentifier) !== expectedRole || !isOpaqueIdentityValue(targetIdentifier.value) ) { - addIssue( - context, - 'mobile-retraction.opaque-target', - ['entry', 'resource', 'target', index, 'identifier'], - 'Each target requires the exact typed canonical Grove HMAC Identifier previously emitted.', - ) + addIssue(context, 'mobile-retraction.opaque-target', [ + 'entry', + 'resource', + 'target', + index, + 'identifier', + ]) } } @@ -182,12 +163,13 @@ const recordDistinctTarget = ( if (!completeIdentifier(targetIdentifier)) return const pair = `${targetIdentifier.system.length}:${targetIdentifier.system}${targetIdentifier.value.length}:${targetIdentifier.value}` if (keys.has(pair)) { - addIssue( - context, - 'mobile-retraction.distinct-target', - ['entry', 'resource', 'target', index, 'identifier'], - 'Retraction target Identifier pairs must be unique.', - ) + addIssue(context, 'mobile-retraction.distinct-target', [ + 'entry', + 'resource', + 'target', + index, + 'identifier', + ]) } keys.add(pair) } @@ -204,7 +186,7 @@ const validateRetractionTargetNativeIdentifier = ( const matches = extensions.filter( (extension) => asRecord(extension)?.url === - groveMobileContract.extensions.retractionTargetNativeIdentifier, + groveExchangeProtocol.extensions.retractionTargetNativeIdentifier, ) if (matches.length === 0) return const native = asRecord(matches[0])?.valueIdentifier @@ -214,18 +196,18 @@ const validateRetractionTargetNativeIdentifier = ( !parseAbsoluteUri(native.system).ok || groveIdentifierRoles(native).length > 0 ) { - addIssue( - context, - 'mobile-retraction.native-record-identifier', - ['entry', 'resource', 'target', index, 'extension'], - "A target carries at most one native record Identifier, complete in the adapter's own absolute key space and free of any Grove identifier-role coding.", - ) + addIssue(context, 'mobile-retraction.native-record-identifier', [ + 'entry', + 'resource', + 'target', + index, + 'extension', + ]) } } const validateRetractionTargetType = ( reference: UnknownRecord | undefined, - role: string | undefined, targetRule: | (typeof RETRACTION_TARGET_RULES)[keyof typeof RETRACTION_TARGET_RULES] | undefined, @@ -237,12 +219,13 @@ const validateRetractionTargetType = ( (typeof reference?.type !== 'string' || !(targetRule.resourceTypes as readonly string[]).includes(reference.type)) ) { - addIssue( - context, - 'mobile-retraction.role-target-type', - ['entry', 'resource', 'target', index, 'type'], - `${String(role)} targets must declare one of the admitted Reference.type values: ${(targetRule.resourceTypes as readonly string[]).join(', ')}.`, - ) + addIssue(context, 'mobile-retraction.role-target-type', [ + 'entry', + 'resource', + 'target', + index, + 'type', + ]) } } @@ -261,12 +244,13 @@ const validateRetractionTarget = ( const identifier = reference?.identifier validateRetractionTargetShape(reference, identifier, index, context) if (role === undefined || !RETRACTION_ROLES.has(role)) { - addIssue( - context, - 'mobile-retraction.target-role', - ['entry', 'resource', 'target', index, 'extension'], - 'Each target requires exactly one closed Grove target role.', - ) + addIssue(context, 'mobile-retraction.target-role', [ + 'entry', + 'resource', + 'target', + index, + 'extension', + ]) } validateRetractionTargetIdentity( identifier, @@ -275,7 +259,7 @@ const validateRetractionTarget = ( context, ) recordDistinctTarget(identifier, index, keys, context) - validateRetractionTargetType(reference, role, targetRule, index, context) + validateRetractionTargetType(reference, targetRule, index, context) validateRetractionTargetNativeIdentifier(reference, index, context) } @@ -285,12 +269,7 @@ const validateRetractionTargets = ( ): void => { const targets = Array.isArray(provenance.target) ? provenance.target : [] if (targets.length === 0) { - addIssue( - context, - 'mobile-retraction.target-required', - ['entry'], - 'Retraction Provenance requires at least one exact logical target.', - ) + addIssue(context, 'mobile-retraction.target-required', ['entry']) } const keys = new Set() for (const [index, target] of targets.entries()) { @@ -307,12 +286,7 @@ const validateRetractionSource = ( const sourceWhat = asRecord(sourceEntity?.what) const source = sourceWhat?.identifier if (entities.length !== 1) { - addIssue( - context, - 'mobile-exchange.single-source-entity', - ['entry'], - 'Retraction Provenance must carry exactly one source entity.', - ) + addIssue(context, 'mobile-exchange.single-source-entity', ['entry']) } if ( sourceEntity?.role !== 'source' || @@ -322,12 +296,7 @@ const validateRetractionSource = ( identifierRole(source) !== 'source-record' || !isOpaqueIdentityValue(source.value) ) { - addIssue( - context, - 'mobile-exchange.logical-source-entity', - ['entry'], - 'Retraction Provenance source must be a logical typed opaque source-record Identifier with no literal reference.', - ) + addIssue(context, 'mobile-exchange.logical-source-entity', ['entry']) } } @@ -340,12 +309,9 @@ const validateRetractionTimes = ( provenance.occurredPeriod === undefined) || provenance.recorded === undefined ) { - addIssue( - context, - 'mobile-retraction.times', - ['entry'], - 'Retraction Provenance requires distinct occurred[x] and recorded fields.', - ) + addIssue(context, 'mobile-exchange.event-times', ['entry'], { + location: 'Provenance.occurred[x]', + }) } } @@ -356,7 +322,7 @@ export const refineRetractionBundle = ( const envelope = validateExchangeEnvelope( bundle, context, - groveMobileContract.profiles.retractionBundle, + groveExchangeProtocol.profiles.retractionBundle, ) if (envelope === undefined) return const resources = envelope.entries.map(({ resource }) => resource) diff --git a/packages/fhir/src/r4/retraction-targets.ts b/packages/fhir/src/r4/retraction-targets.ts new file mode 100644 index 00000000..be5db018 --- /dev/null +++ b/packages/fhir/src/r4/retraction-targets.ts @@ -0,0 +1,174 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + asRecord, + completeIdentifier, + groveIdentifierRoles, + identifierRole, + identifiersOf, + type UnknownRecord, +} from './graph-schema-utils.js' +import { parseExchangeGraph } from './parse.js' +import type { ExchangeGraph } from './types.js' +import { groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' +import { + err, + ok, + parseAbsoluteUri, + type AbsoluteUri, + type Result, +} from '../core/index.js' +import { + isGroveIdentifierRole, + type BusinessIdentifier, + type RoledIdentifier, +} from '../mobile/identity.js' + +type TargetRoles = typeof groveExchangeProtocol.lifecycle.retraction.targetRoles + +/** Closed target roles of the retraction lifecycle. */ +export type RetractionTargetRole = keyof TargetRoles + +export type RetractionTargetResourceType = + TargetRoles[Role]['resourceTypes'][number] + +/** + * One prior graph node a retraction names by its typed logical identifier. + * + * `nativeIdentifier` is the adapter's own record key, disclosed under the same policy as + * a governed source identifier; it never addresses the target. + */ +export type RetractionTarget = { + readonly [Role in RetractionTargetRole]: { + readonly role: Role + readonly resourceType: RetractionTargetResourceType + readonly identifier: RoledIdentifier + readonly nativeIdentifier?: BusinessIdentifier | undefined + } +}[RetractionTargetRole] + +const TARGET_ROLES = groveExchangeProtocol.lifecycle.retraction.targetRoles + +const memberReferences = (resources: readonly unknown[]): ReadonlySet => + new Set( + resources.flatMap((resource) => { + const record = asRecord(resource) + const members = + ( + record?.resourceType === 'Observation' && + Array.isArray(record.hasMember) + ) ? + record.hasMember + : [] + return members.flatMap((member) => { + const reference = asRecord(member)?.reference + return typeof reference === 'string' ? [reference] : [] + }) + }), + ) + +const roledIdentifier = ( + resource: unknown, + role: string, +): RoledIdentifier | undefined => { + const candidate = identifiersOf(resource).find( + (identifier) => identifierRole(identifier) === role, + ) + return completeIdentifier(candidate) && isGroveIdentifierRole(role) ? + { system: candidate.system as AbsoluteUri, value: candidate.value, role } + : undefined +} + +// The one clear identifier a governed disclosure placed on the primary output. +const governedIdentifier = ( + resource: unknown, +): BusinessIdentifier | undefined => { + const candidates = identifiersOf(resource).filter( + (identifier) => + completeIdentifier(identifier) && + groveIdentifierRoles(identifier).length === 0 && + parseAbsoluteUri(identifier.system).ok, + ) + const candidate = candidates[0] + return candidates.length === 1 && completeIdentifier(candidate) ? + { system: candidate.system as AbsoluteUri, value: candidate.value } + : undefined +} + +const targetRoleFor = ( + resource: UnknownRecord, + fullUrl: string, + members: ReadonlySet, +): RetractionTargetRole | undefined => { + const resourceType = String(resource.resourceType) + if (resourceType === 'Device') return 'device-snapshot' + if (resourceType === 'DocumentReference') return 'source-artifact' + if (resourceType === 'Specimen') return 'specimen' + if (resourceType === 'Observation' && members.has(fullUrl)) + return 'child-output' + return ( + ( + TARGET_ROLES['primary-output'].resourceTypes as readonly string[] + ).includes(resourceType) + ) ? + 'primary-output' + : undefined +} + +const targetOf = ( + entry: ExchangeGraph['entry'][number], + members: ReadonlySet, +): RetractionTarget | undefined => { + const resource = asRecord(entry.resource) + if (resource === undefined) return undefined + const role = targetRoleFor(resource, entry.fullUrl, members) + if (role === undefined) return undefined + const rule = TARGET_ROLES[role] + const identifier = roledIdentifier(resource, rule.identifierRole) + const resourceType = String(resource.resourceType) + if ( + identifier === undefined || + !(rule.resourceTypes as readonly string[]).includes(resourceType) + ) { + return undefined + } + const nativeIdentifier = + role === 'primary-output' ? governedIdentifier(resource) : undefined + return { + role, + resourceType, + identifier, + ...(nativeIdentifier === undefined ? {} : { nativeIdentifier }), + } as RetractionTarget +} + +/** + * Derives every node of an accepted graph a retraction of its source record must name. + * + * An Observation another Observation lists in `hasMember` is a child output; every other + * output is primary. A governed source identifier on the primary output travels along as + * the target's native identifier. + */ +export const retractionTargets = ( + graph: ExchangeGraph, +): Result => { + const parsed = parseExchangeGraph(graph) + if (!parsed.ok) return parsed + const members = memberReferences( + parsed.value.entry.map(({ resource }) => resource), + ) + const targets = parsed.value.entry.flatMap((entry) => { + const target = targetOf(entry, members) + return target === undefined ? [] : [target] + }) + const [first, ...rest] = targets + return first === undefined ? + err('missing-required', 'The graph names no retractable node.') + : ok([first, ...rest]) +} diff --git a/packages/fhir/src/r4/schemas.ts b/packages/fhir/src/r4/schemas.ts index 98f6c5c6..1d6037ec 100644 --- a/packages/fhir/src/r4/schemas.ts +++ b/packages/fhir/src/r4/schemas.ts @@ -16,10 +16,7 @@ import { r4CollectionBundleSchema, } from './base-schemas.js' import { refineRetractionBundle } from './retraction-graph-semantics.js' -import type { - GroveMobileExchangeBundle, - GroveMobileRetractionBundle, -} from './types.js' +import type { ExchangeGraph, RetractionEvent } from './types.js' export * from './base-schemas.js' export { @@ -59,13 +56,12 @@ const groveGraphBundle = (refine: typeof refineActiveBundle) => .pipe(groveGraphBundleSchema) /** Profile-aware parser for one immutable active Grove exchange event. */ -export const groveMobileExchangeBundleSchema: z.ZodType = - groveGraphBundle( - refineActiveBundle, - ) as unknown as z.ZodType +export const exchangeGraphSchema: z.ZodType = groveGraphBundle( + refineActiveBundle, +) as unknown as z.ZodType /** Profile-aware parser for one append-only Grove source retraction assertion. */ -export const groveMobileRetractionBundleSchema: z.ZodType = +export const retractionEventSchema: z.ZodType = groveGraphBundle( refineRetractionBundle, - ) as unknown as z.ZodType + ) as unknown as z.ZodType diff --git a/packages/fhir/src/r4/semantic-equality.ts b/packages/fhir/src/r4/semantic-equality.ts new file mode 100644 index 00000000..0bc96f78 --- /dev/null +++ b/packages/fhir/src/r4/semantic-equality.ts @@ -0,0 +1,227 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { err, ok, type Result } from '../core/index.js' + +// A lossless reading of JSON text: numbers keep their lexeme, so `72` and `72.0` differ +// while member order, whitespace, and string escaping do not. +type Token = + | { readonly kind: 'null' | 'true' | 'false' } + | { readonly kind: 'number'; readonly lexeme: string } + | { readonly kind: 'string'; readonly value: string } + | { readonly kind: 'array'; readonly items: readonly Token[] } + | { readonly kind: 'object'; readonly members: ReadonlyMap } + +const MAX_DEPTH = 512 +const WHITESPACE = new Set([' ', '\t', '\n', '\r']) +const NUMBER = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/u +const SIMPLE_ESCAPES: ReadonlyMap = new Map([ + ['"', '"'], + ['\\', '\\'], + ['/', '/'], + ['b', '\b'], + ['f', '\f'], + ['n', '\n'], + ['r', '\r'], + ['t', '\t'], +]) + +class Reader { + private readonly text: string + private position = 0 + + constructor(text: string) { + this.text = text + } + + fail(detail: string): never { + throw new SyntaxError(`${detail} at offset ${this.position}.`) + } + + skipWhitespace(): void { + while (WHITESPACE.has(this.text.charAt(this.position))) this.position += 1 + } + + peek(): string { + return this.text.charAt(this.position) + } + + consume(expected: string): void { + if (!this.text.startsWith(expected, this.position)) { + this.fail(`Expected ${expected}`) + } + this.position += expected.length + } + + atEnd(): boolean { + return this.position >= this.text.length + } + + readNumber(): Token { + const match = NUMBER.exec(this.text.slice(this.position)) + if (match === null) this.fail('Expected a number') + this.position += match[0].length + return { kind: 'number', lexeme: match[0] } + } + + readString(): string { + this.consume('"') + let value = '' + for (;;) { + const character = this.text.charAt(this.position) + if (character === '') this.fail('Unterminated string') + this.position += 1 + if (character === '"') return value + if (character < ' ') this.fail('Control character in string') + value += character === '\\' ? this.readEscape() : character + } + } + + private readEscape(): string { + const escape = this.text.charAt(this.position) + this.position += 1 + if (escape === 'u') { + const digits = this.text.slice(this.position, this.position + 4) + if (!/^[\dA-Fa-f]{4}$/u.test(digits)) this.fail('Invalid unicode escape') + this.position += 4 + return String.fromCharCode(Number.parseInt(digits, 16)) + } + const simple = SIMPLE_ESCAPES.get(escape) + if (simple === undefined) this.fail('Invalid escape') + return simple + } + + readValue(depth: number): Token { + if (depth > MAX_DEPTH) this.fail('Nesting too deep') + this.skipWhitespace() + const next = this.peek() + if (next === '{') return this.readObject(depth) + if (next === '[') return this.readArray(depth) + if (next === '"') return { kind: 'string', value: this.readString() } + for (const literal of ['null', 'true', 'false'] as const) { + if (this.text.startsWith(literal, this.position)) { + this.position += literal.length + return { kind: literal } + } + } + return this.readNumber() + } + + private readArray(depth: number): Token { + this.consume('[') + const items: Token[] = [] + this.skipWhitespace() + if (this.peek() === ']') { + this.position += 1 + return { kind: 'array', items } + } + for (;;) { + items.push(this.readValue(depth + 1)) + this.skipWhitespace() + if (this.peek() === ',') { + this.position += 1 + continue + } + this.consume(']') + return { kind: 'array', items } + } + } + + private readObject(depth: number): Token { + this.consume('{') + const members = new Map() + this.skipWhitespace() + if (this.peek() === '}') { + this.position += 1 + return { kind: 'object', members } + } + for (;;) { + this.skipWhitespace() + const key = this.readString() + if (members.has(key)) this.fail(`Duplicate member ${key}`) + this.skipWhitespace() + this.consume(':') + members.set(key, this.readValue(depth + 1)) + this.skipWhitespace() + if (this.peek() === ',') { + this.position += 1 + continue + } + this.consume('}') + return { kind: 'object', members } + } + } +} + +const decode = (input: string | Uint8Array): string => + typeof input === 'string' ? input : ( + new TextDecoder('utf-8', { fatal: true }).decode(input) + ) + +const tokenize = (input: string | Uint8Array): Result => { + try { + const reader = new Reader(decode(input)) + const token = reader.readValue(0) + reader.skipWhitespace() + if (!reader.atEnd()) reader.fail('Unexpected trailing content') + return ok(token) + } catch (error) { + return err( + 'invalid-type', + error instanceof Error ? error.message : 'Expected JSON text.', + ) + } +} + +const tokensEqual = (left: Token, right: Token): boolean => { + if (left.kind !== right.kind) return false + switch (left.kind) { + case 'number': + return left.lexeme === (right as typeof left).lexeme + case 'string': + return left.value === (right as typeof left).value + case 'array': { + const items = (right as typeof left).items + return ( + left.items.length === items.length && + left.items.every((item, index) => { + const other = items[index] + return other !== undefined && tokensEqual(item, other) + }) + ) + } + case 'object': { + const members = (right as typeof left).members + return ( + left.members.size === members.size && + [...left.members].every(([key, value]) => { + const other = members.get(key) + return other !== undefined && tokensEqual(value, other) + }) + ) + } + default: + return true + } +} + +/** + * Decides whether two serialized graphs carry the same content under the protocol's + * equality rule: member order, whitespace and string escaping do not matter, decimal + * lexemes do, and bytes are never compared. Malformed JSON text is an issue, not `false`. + */ +export const isSemanticallyEqual = ( + left: string | Uint8Array, + right: string | Uint8Array, +): Result => { + const leftTokens = tokenize(left) + if (!leftTokens.ok) return leftTokens + const rightTokens = tokenize(right) + if (!rightTokens.ok) return rightTokens + return ok(tokensEqual(leftTokens.value, rightTokens.value)) +} diff --git a/packages/fhir/src/r4/study-context-semantics.ts b/packages/fhir/src/r4/study-context-semantics.ts new file mode 100644 index 00000000..ff5a12b4 --- /dev/null +++ b/packages/fhir/src/r4/study-context-semantics.ts @@ -0,0 +1,165 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import type { z } from 'zod' +import type { ValidatedEnvelope } from './exchange-envelope.js' +import { addIssue, asRecord, type UnknownRecord } from './graph-schema-utils.js' + +interface IndexedResource { + readonly index: number + readonly fullUrl: string + readonly resource: UnknownRecord +} + +const resourcesOfType = ( + envelope: ValidatedEnvelope, + resourceType: string, +): readonly IndexedResource[] => + envelope.entries.flatMap((entry, index) => { + const resource = asRecord(entry.resource) + return resource?.resourceType === resourceType ? + [{ index, fullUrl: String(entry.fullUrl), resource }] + : [] + }) + +const referenceTarget = (value: unknown): string | undefined => { + const reference = asRecord(value)?.reference + return typeof reference === 'string' ? reference : undefined +} + +// A subject reference names the bundled Patient entry or carries a complete logical +// identifier; which participant it names is the receiver's authorization question. +const namesSubject = ( + value: unknown, + patients: ReadonlySet, +): boolean => { + const reference = asRecord(value) + if (reference === undefined) return false + if (typeof reference.reference === 'string') { + return patients.has(reference.reference) + } + const identifier = asRecord(reference.identifier) + return ( + typeof identifier?.system === 'string' && + typeof identifier.value === 'string' + ) +} + +const studyContextIssue = ( + context: z.core.$RefinementCtx, + node: IndexedResource, + element?: string, +): void => { + addIssue( + context, + 'mobile-support.study-context', + [ + 'entry', + node.index, + 'resource', + ...(element === undefined ? [] : [element]), + ], + element === undefined ? + {} + : { location: `${String(node.resource.resourceType)}.${element}` }, + ) +} + +const validateStudies = ( + studies: readonly IndexedResource[], + protocols: ReadonlySet, + enrollments: readonly IndexedResource[], + context: z.core.$RefinementCtx, +): ReadonlyMap => { + const protocolUse = new Map() + for (const study of studies) { + const references = + Array.isArray(study.resource.protocol) ? + study.resource.protocol.map(referenceTarget) + : [] + const [protocol] = references + if ( + references.length !== 1 || + protocol === undefined || + !protocols.has(protocol) + ) { + studyContextIssue(context, study, 'protocol') + } else { + protocolUse.set(protocol, (protocolUse.get(protocol) ?? 0) + 1) + } + const linked = enrollments.filter( + ({ resource }) => referenceTarget(resource.study) === study.fullUrl, + ) + if (linked.length !== 1) studyContextIssue(context, study) + } + return protocolUse +} + +const validateProtocols = ( + protocols: readonly IndexedResource[], + protocolUse: ReadonlyMap, + context: z.core.$RefinementCtx, +): void => { + for (const protocol of protocols) { + for (const element of ['url', 'version']) { + const value = protocol.resource[element] + if (typeof value !== 'string' || value === '') { + studyContextIssue(context, protocol, element) + } + } + if ((protocolUse.get(protocol.fullUrl) ?? 0) !== 1) { + studyContextIssue(context, protocol) + } + } +} + +const validateEnrollments = ( + enrollments: readonly IndexedResource[], + studies: ReadonlySet, + patients: ReadonlySet, + context: z.core.$RefinementCtx, +): void => { + for (const enrollment of enrollments) { + const study = referenceTarget(enrollment.resource.study) + if (study === undefined || !studies.has(study)) { + studyContextIssue(context, enrollment, 'study') + } + if (!namesSubject(enrollment.resource.individual, patients)) { + studyContextIssue(context, enrollment, 'individual') + } + } +} + +/** + * Holds a bundled study context to the registry rule: one ResearchStudy, its exact-revision + * PlanDefinition with canonical url and version, and one ResearchSubject linking a subject + * to that study. The entry-node role of each is checked with the entry keys; whether the + * enrolled participant is the graph's subject stays a receiver decision. + */ +export const validateStudyContext = ( + envelope: ValidatedEnvelope, + context: z.core.$RefinementCtx, +): void => { + const studies = resourcesOfType(envelope, 'ResearchStudy') + const protocols = resourcesOfType(envelope, 'PlanDefinition') + const enrollments = resourcesOfType(envelope, 'ResearchSubject') + if (studies.length + protocols.length + enrollments.length === 0) return + const protocolUse = validateStudies( + studies, + new Set(protocols.map(({ fullUrl }) => fullUrl)), + enrollments, + context, + ) + validateProtocols(protocols, protocolUse, context) + validateEnrollments( + enrollments, + new Set(studies.map(({ fullUrl }) => fullUrl)), + new Set(resourcesOfType(envelope, 'Patient').map(({ fullUrl }) => fullUrl)), + context, + ) +} diff --git a/packages/fhir/src/r4/types.ts b/packages/fhir/src/r4/types.ts index 71fe2ac6..fac6986e 100644 --- a/packages/fhir/src/r4/types.ts +++ b/packages/fhir/src/r4/types.ts @@ -107,10 +107,10 @@ type GraphBundle = ReadonlyDeep< } > -/** Grove-profiled event graph with complete event and entry identity. */ -export type GroveMobileExchangeBundle = GraphBundle +/** The validated collection Bundle of one active exchange event. */ +export type ExchangeGraph = GraphBundle -/** Grove-profiled retraction assertion containing Provenance and optional Device agents only. */ -export type GroveMobileRetractionBundle = GraphBundle +/** The validated retraction assertion: its Provenance and optional Device agents only. */ +export type RetractionEvent = GraphBundle export type SupportedR4Resource = R4CollectionBundle | GraphResource diff --git a/packages/fhir/test/diagnostics.test.ts b/packages/fhir/test/diagnostics.test.ts index ccceb98d..8e4f41f8 100644 --- a/packages/fhir/test/diagnostics.test.ts +++ b/packages/fhir/test/diagnostics.test.ts @@ -6,78 +6,136 @@ // SPDX-License-Identifier: MIT // +import { readdirSync, readFileSync } from 'node:fs' import { groveExchangeProtocol, - groveExchangeRuleDiagnostics, + groveProducerDiagnostics, } from '../src/contract/measurement-catalog.generated.js' import { groveRuleIssue, groveRuleIssueFromParameters, + groveRuleLocation, groveRuleParameters, - groveRuleReason, - type GroveExchangeRuleCode, + isProducerDiagnosticCode, + type ProducerDiagnosticCode, } from '../src/r4/diagnostics.js' const byText = (left: string, right: string): number => left.localeCompare(right) const ruleCodes = Object.keys( - groveExchangeRuleDiagnostics, -) as GroveExchangeRuleCode[] + groveProducerDiagnostics, +) as ProducerDiagnosticCode[] +const isClientRecordRule = (code: string): boolean => + /-(?:input|omission)\./u.test(code) + +// Every rule code a source file spells out, so nothing unregistered can be emitted. +const sourceRuleLiterals = (): ReadonlyMap => { + const root = new URL('../src/', import.meta.url) + const literals = new Map() + for (const name of readdirSync(root, { recursive: true, encoding: 'utf8' })) { + if (!name.endsWith('.ts') || name.endsWith('.generated.ts')) continue + const source = readFileSync(new URL(name, root), 'utf8') + for (const match of source.matchAll( + /'([a-z][a-z0-9]*(?:-[a-z0-9]+)+\.[a-z][a-z0-9-]*)'/gu, + )) { + const code = match[1] ?? '' + literals.set(code, [...(literals.get(code) ?? []), name]) + } + expect([name, source.includes('`mobile-${')]).toEqual([name, false]) + } + return literals +} describe('stable Grove rule diagnostics', () => { - it('registers exactly the protocol producer diagnostics', () => { + it('registers exactly the protocol producer diagnostics with severity and emitter', () => { expect([...ruleCodes].sort(byText)).toEqual( groveExchangeProtocol.producerDiagnostics .map(({ code }) => code) .sort(byText), ) + for (const row of groveExchangeProtocol.producerDiagnostics) { + expect(groveProducerDiagnostics[row.code]).toEqual({ + reason: row.reason, + emittedBy: row.emittedBy, + severity: 'severity' in row ? row.severity : 'error', + }) + } + }) + + it('emits only registered codes from every source file', () => { + const literals = sourceRuleLiterals() + const unregistered = [...literals] + .filter(([code]) => !isProducerDiagnosticCode(code)) + .map(([code, files]) => `${code} in ${files.join(', ')}`) + expect(unregistered).toEqual([]) + expect(literals.size).toBeGreaterThan(40) }) it.each(ruleCodes)( - 'round-trips %s with its public reason and location', + 'round-trips %s with its registry reason and severity', (code) => { const path = ['entry', 3, 'resource', 'target', 2, 'extension', 0] const issue = groveRuleIssue(code, path) + const row = groveProducerDiagnostics[code] expect( - groveRuleIssueFromParameters( - groveRuleParameters(code, path), - path, - 'fallback', - ), + groveRuleIssueFromParameters(groveRuleParameters(code, path), path), ).toEqual(issue) - expect(issue.reason).toBe(groveExchangeRuleDiagnostics[code].reason) - expect(groveRuleReason(code)).toBe( - groveExchangeRuleDiagnostics[code].reason, - ) + expect(issue.reason).toBe(row.reason) + expect(issue.message).toBe(row.reason) + expect(issue.severity).toBe(row.severity) + if (isClientRecordRule(code)) { + expect(groveRuleLocation(code, path)).toBeUndefined() + expect(issue).not.toHaveProperty('location') + } else { + expect(typeof issue.location).toBe('string') + expect(issue.location).not.toBe('') + } }, ) - it('uses the stable first-target location when no target ordinal is available', () => { + it('warns only on registry rows of warning severity, all of them omissions', () => { + for (const code of ruleCodes) { + const warning = groveRuleIssue(code, []).severity === 'warning' + expect(warning).toBe( + groveProducerDiagnostics[code].severity === 'warning', + ) + expect(warning).toBe(code.startsWith('mobile-omission.')) + } expect( - groveRuleIssue('mobile-retraction.logical-target', []).location, - ).toBe('Provenance.target[0]') + ruleCodes.filter((code) => code.startsWith('mobile-omission.')), + ).toHaveLength(3) }) - it('carries a locally named rule without a registered reason or location', () => { - const parameters = groveRuleParameters('mobile-step-count.nonzero-period', [ - 'entry', - 0, - ]) - - expect(groveRuleReason('mobile-step-count.nonzero-period')).toBeUndefined() - expect(parameters).toEqual({ - groveRuleCode: 'mobile-step-count.nonzero-period', - }) - expect( - groveRuleIssueFromParameters(parameters, ['entry', 0], 'local message'), - ).toEqual({ + it('carries the typed detail of a refusal beside the registry reason', () => { + const issue = groveRuleIssue( + 'mobile-input.value-outside-domain', + ['measurements', 0, 'value'], + { message: '101 is above the 100 percent maximum.' }, + ) + expect(issue).toEqual({ severity: 'error', - code: 'mobile-step-count.nonzero-period', - path: ['entry', 0], - message: 'local message', + code: 'mobile-input.value-outside-domain', + path: ['measurements', 0, 'value'], + message: '101 is above the 100 percent maximum.', + reason: + groveProducerDiagnostics['mobile-input.value-outside-domain'].reason, }) + expect( + groveRuleIssueFromParameters( + groveRuleParameters('mobile-input.value-outside-domain', [], { + message: 'detail', + }), + ['x'], + ), + ).toMatchObject({ message: 'detail', path: ['x'] }) + }) + + it('uses the stable first-target location when no target ordinal is available', () => { + expect( + groveRuleIssue('mobile-retraction.logical-target', []).location, + ).toBe('Provenance.target[0]') }) it('ignores issue parameters that name no producer rule', () => { @@ -86,10 +144,9 @@ describe('stable Grove rule diagnostics', () => { {}, { groveRuleCode: 42 }, { groveRuleCode: 'undotted' }, + { groveRuleCode: 'mobile-step-count.nonzero-period' }, ]) { - expect( - groveRuleIssueFromParameters(parameters, [], 'message'), - ).toBeUndefined() + expect(groveRuleIssueFromParameters(parameters, [])).toBeUndefined() } }) }) diff --git a/packages/fhir/test/exchange-corpus.test.ts b/packages/fhir/test/exchange-corpus.test.ts index a0d6e1fc..60cbd33b 100644 --- a/packages/fhir/test/exchange-corpus.test.ts +++ b/packages/fhir/test/exchange-corpus.test.ts @@ -10,10 +10,7 @@ import { readFileSync } from 'node:fs' import { adapterSourceMarkerClaims } from '../src/contract/measurement-catalog.generated.js' -import { - parseGroveMobileExchangeBundle, - parseGroveMobileRetractionBundle, -} from '../src/r4/index.js' +import { parseExchangeGraph, parseRetractionEvent } from '../src/r4/index.js' interface JsonPatchOperation { readonly op: 'add' | 'remove' | 'replace' @@ -68,6 +65,36 @@ const pointerComponents = (pointer: string): string[] => .split('/') .map((component) => component.replaceAll('~1', '/').replaceAll('~0', '~')) +type PatchContainer = Record | unknown[] + +const resolveParent = ( + root: unknown, + components: readonly string[], + pointer: string, +): PatchContainer => { + let parent = root as PatchContainer + for (const component of components) { + const next = + Array.isArray(parent) ? parent[Number(component)] : parent[component] + if (typeof next !== 'object' || next === null) { + throw new Error(`Patch path does not resolve: ${pointer}`) + } + parent = next as PatchContainer + } + return parent +} + +const applyToArray = ( + parent: unknown[], + property: string, + operation: JsonPatchOperation, +): void => { + const index = property === '-' ? parent.length : Number(property) + if (operation.op === 'remove') parent.splice(index, 1) + else if (operation.op === 'add') parent.splice(index, 0, operation.value) + else parent[index] = operation.value +} + const patched = ( base: unknown, operations: readonly JsonPatchOperation[], @@ -78,37 +105,18 @@ const patched = ( const property = components.pop() if (property === undefined) throw new Error('A JSON Patch path is required.') - let parent = result as Record | unknown[] - for (const component of components) { - const next = - Array.isArray(parent) ? parent[Number(component)] : parent[component] - if (typeof next !== 'object' || next === null) { - throw new Error(`Patch path does not resolve: ${operation.path}`) - } - parent = next as Record | unknown[] - } - if (Array.isArray(parent)) { - const index = Number(property) - if (operation.op === 'remove') parent.splice(index, 1) - else if (operation.op === 'add') parent.splice(index, 0, operation.value) - else parent[index] = operation.value - } else if (operation.op === 'remove') { - Reflect.deleteProperty(parent, property) - } else { - parent[property] = operation.value - } + const parent = resolveParent(result, components, operation.path) + if (Array.isArray(parent)) applyToArray(parent, property, operation) + else if (operation.op === 'remove') Reflect.deleteProperty(parent, property) + else parent[property] = operation.value } return result } describe('shared Grove Mobile exchange corpus', () => { it('accepts both normative positive graphs', () => { - expect(parseGroveMobileExchangeBundle(bases['mobile-exchange']).ok).toBe( - true, - ) - expect( - parseGroveMobileRetractionBundle(bases['mobile-retraction']).ok, - ).toBe(true) + expect(parseExchangeGraph(bases['mobile-exchange']).ok).toBe(true) + expect(parseRetractionEvent(bases['mobile-retraction']).ok).toBe(true) }) it('accepts a directly claimed HealthKit application snapshot with its product identifier', () => { @@ -138,7 +146,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, }, ]) - expect(parseGroveMobileExchangeBundle(input).ok).toBe(true) + expect(parseExchangeGraph(input).ok).toBe(true) }) it('does not mistake an Expression.reference URI for a FHIR Reference', () => { @@ -157,7 +165,7 @@ describe('shared Grove Mobile exchange corpus', () => { ], }, ]) - expect(parseGroveMobileExchangeBundle(input).ok).toBe(true) + expect(parseExchangeGraph(input).ok).toBe(true) }) it.each([ @@ -167,7 +175,7 @@ describe('shared Grove Mobile exchange corpus', () => { const input = patched(bases['mobile-exchange'], [ { op: 'remove', path: `/entry/${String(entry)}/resource/subject` }, ]) - const result = parseGroveMobileExchangeBundle(input) + const result = parseExchangeGraph(input) expect(result.ok).toBe(false) if (result.ok) return expect( @@ -196,7 +204,7 @@ describe('shared Grove Mobile exchange corpus', () => { value: 'n0:patient:1:qN9xaTdoz-LfvONC7FgEUNMz06zyWhfzD0khPhtjVig', }, ]) - const result = parseGroveMobileExchangeBundle(misnumbered) + const result = parseExchangeGraph(misnumbered) expect(result.ok).toBe(false) if (result.ok) return const codes = result.issues.map(({ code }) => code) @@ -212,7 +220,7 @@ describe('shared Grove Mobile exchange corpus', () => { value: [{ reference: 'urn:uuid:00000000-0000-5000-8000-000000000000' }], }, ]) - const result = parseGroveMobileExchangeBundle(input) + const result = parseExchangeGraph(input) expect(result.ok).toBe(false) if (result.ok) return expect( @@ -237,7 +245,7 @@ describe('shared Grove Mobile exchange corpus', () => { value: sourceSystem, }, ]) - const result = parseGroveMobileExchangeBundle(input) + const result = parseExchangeGraph(input) expect(result.ok).toBe(false) if (result.ok) return expect(result.issues).toContainEqual( @@ -258,7 +266,7 @@ describe('shared Grove Mobile exchange corpus', () => { ], }, ]) - const result = parseGroveMobileExchangeBundle(input) + const result = parseExchangeGraph(input) expect(result.ok).toBe(false) if (result.ok) return expect(result.issues).toContainEqual( @@ -277,11 +285,11 @@ describe('shared Grove Mobile exchange corpus', () => { }, }, ]) - const result = parseGroveMobileExchangeBundle(input) + const result = parseExchangeGraph(input) expect(result.ok).toBe(false) if (!result.ok) { expect(result.issues.map(({ code }) => code)).toContain( - 'mobile-exchange.adapter-source-marker', + 'mobile-output.adapter-source-marker', ) } }) @@ -301,11 +309,11 @@ describe('shared Grove Mobile exchange corpus', () => { 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-conversion-provenance', }, ]) - const result = parseGroveMobileExchangeBundle(input) + const result = parseExchangeGraph(input) expect(result.ok).toBe(false) if (!result.ok) { expect(result.issues.map(({ code }) => code)).toContain( - 'mobile-exchange.adapter-source-marker', + 'mobile-output.adapter-source-marker', ) } }) @@ -358,7 +366,7 @@ describe('shared Grove Mobile exchange corpus', () => { ], }, ]) - expect(parseGroveMobileExchangeBundle(healthConnectGraph).ok).toBe(true) + expect(parseExchangeGraph(healthConnectGraph).ok).toBe(true) const literalDataOrigin = patched(healthConnectGraph, [ { @@ -367,11 +375,11 @@ describe('shared Grove Mobile exchange corpus', () => { value: 'urn:uuid:8f87a88a-8744-5116-8901-9274f62472ac', }, ]) - const result = parseGroveMobileExchangeBundle(literalDataOrigin) + const result = parseExchangeGraph(literalDataOrigin) expect(result.ok).toBe(false) if (!result.ok) { expect(result.issues.map(({ code }) => code)).toContain( - 'health-connect.data-origin-application', + 'health-connect-provenance.data-origin-agent', ) } }) @@ -382,8 +390,8 @@ describe('shared Grove Mobile exchange corpus', () => { const input = patched(bases[base], patch) const result = base === 'mobile-exchange' ? - parseGroveMobileExchangeBundle(input) - : parseGroveMobileRetractionBundle(input) + parseExchangeGraph(input) + : parseRetractionEvent(input) expect(result.ok).toBe(false) if (result.ok) return const emitted = result.issues.find( @@ -427,7 +435,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'duplicated primary clinical coding', - expectedCode: 'mobile-heart-rate.code', + expectedCode: 'mobile-output.semantic-profile', patch: [ { op: 'add' as const, @@ -438,7 +446,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'duplicated category coding', - expectedCode: 'mobile-heart-rate.category', + expectedCode: 'mobile-output.semantic-profile', patch: [ { op: 'add' as const, @@ -485,7 +493,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'HealthKit application Device without its product identifier', - expectedCode: 'healthkit-application-device.bundle-identifier', + expectedCode: 'healthkit-device.application-bundle-identifier', patch: [ { op: 'replace' as const, @@ -532,7 +540,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'duplicated active exchange profile claim', - expectedCode: 'mobile-exchange.profile', + expectedCode: 'mobile-exchange.bundle-profile', patch: [ { op: 'add' as const, @@ -544,7 +552,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, { name: 'simultaneous active and retraction profile claims', - expectedCode: 'mobile-exchange.profile', + expectedCode: 'mobile-exchange.bundle-profile', patch: [ { op: 'add' as const, @@ -634,9 +642,7 @@ describe('shared Grove Mobile exchange corpus', () => { ], }, ])('rejects adversarial $name input', ({ patch, expectedCode }) => { - const result = parseGroveMobileExchangeBundle( - patched(bases['mobile-exchange'], patch), - ) + const result = parseExchangeGraph(patched(bases['mobile-exchange'], patch)) expect(result.ok).toBe(false) if (result.ok) return expect(result.issues.map(({ code }) => code)).toContain(expectedCode) @@ -661,7 +667,7 @@ describe('shared Grove Mobile exchange corpus', () => { }, }, ])('rejects adversarial retraction $name', ({ value, expectedCode }) => { - const result = parseGroveMobileRetractionBundle( + const result = parseRetractionEvent( patched(bases['mobile-retraction'], [ { op: 'add', path: '/entry/0/resource/activity/coding/1', value }, ]), @@ -672,7 +678,7 @@ describe('shared Grove Mobile exchange corpus', () => { }) it('rejects even an empty contained array in a retraction graph', () => { - const result = parseGroveMobileRetractionBundle( + const result = parseRetractionEvent( patched(bases['mobile-retraction'], [ { op: 'add', diff --git a/packages/fhir/test/exchange-protocol.test.ts b/packages/fhir/test/exchange-protocol.test.ts index 365ff58d..c94f5583 100644 --- a/packages/fhir/test/exchange-protocol.test.ts +++ b/packages/fhir/test/exchange-protocol.test.ts @@ -8,7 +8,12 @@ import { parseAbsoluteUri, + parseEntryNodeOrdinal, + parseEventSequence, + parseKeyEpoch, type AbsoluteUri, + type EntryNodeOrdinal, + type EventSequence, type Result, } from '../src/core/index.js' import { @@ -27,12 +32,18 @@ import { groveExchangeProtocol, isEntryNodeIdentityValue, isEventIdentityValue, + isOpaqueIdentityScope, isOpaqueIdentityValue, - validateDeploymentIdentity, - type DeploymentIdentityInput, - type GroveOpaqueIdentityComponents, - type GroveOpaqueIdentityKind, + validateOpaqueIdentityScope, + type OpaqueIdentityComponents, + type OpaqueIdentityKind, + type OpaqueIdentityScopeInput, + type OpaqueIdentitySystems, } from '../src/mobile/index.js' +import { + deriveProviderOpaqueIdentifier, + providerCoordinateIssue, +} from '../src/providers/identity.js' const unwrap = (result: Result): Value => { if (!result.ok) throw new Error(result.issues[0]?.message) @@ -40,78 +51,51 @@ const unwrap = (result: Result): Value => { } const uri = (value: string): AbsoluteUri => unwrap(parseAbsoluteUri(value)) +const sequence = (value: string): EventSequence => + unwrap(parseEventSequence(value)) +const ordinal = (value: string): EntryNodeOrdinal => + unwrap(parseEntryNodeOrdinal(value)) + +const vectors = groveExchangeProtocol.testVectors +const vectorSystems = Object.fromEntries( + vectors.identitySystems.map(({ identityKind, system }) => [ + identityKind, + uri(system), + ]), +) as OpaqueIdentitySystems -const conformanceDeployment = { - opaqueIdentifierSystems: { - 'source-record': uri( - 'https://study.example.org/fhir/NamingSystem/grove-source-record-v0/test-key/1', - ), - 'source-output': uri( - 'https://study.example.org/fhir/NamingSystem/grove-source-output-v0/test-key/1', - ), - 'writer-record': uri( - 'https://study.example.org/fhir/NamingSystem/grove-writer-record-v0/test-key/1', - ), - 'provider-record': uri( - 'https://study.example.org/fhir/NamingSystem/grove-provider-record-v0/test-key/1', - ), - 'provider-output': uri( - 'https://study.example.org/fhir/NamingSystem/grove-provider-output-v0/test-key/1', - ), - 'source-artifact': uri( - 'https://study.example.org/fhir/NamingSystem/grove-source-artifact-v0/test-key/1', - ), - 'provider-artifact': uri( - 'https://study.example.org/fhir/NamingSystem/grove-provider-artifact-v0/test-key/1', - ), - 'source-context': uri( - 'https://study.example.org/fhir/NamingSystem/grove-source-context-v0/test-key/1', - ), - 'recording-device': uri( - 'https://study.example.org/fhir/NamingSystem/grove-recording-device-v0/test-key/1', - ), - 'device-snapshot': uri( - 'https://study.example.org/fhir/NamingSystem/grove-device-snapshot-v0/test-key/1', - ), +const conformanceScope: OpaqueIdentityScopeInput = { + systems: { + opaque: vectorSystems, + event: uri(vectors.event.system), + entryNode: uri(vectors.entryNode.system), }, - eventIdentifierSystem: uri(groveExchangeProtocol.testVectors.event.system), - entryNodeIdentifierSystem: uri( - groveExchangeProtocol.testVectors.entryNode.system, - ), - keyId: groveExchangeProtocol.testVectors.keyId, - keyEpoch: groveExchangeProtocol.testVectors.epoch, - secretBase64Url: Buffer.from( - groveExchangeProtocol.testVectors.keyHex, - 'hex', - ).toString('base64url'), - producerInstance: groveExchangeProtocol.testVectors.event.producerInstance, -} as const satisfies DeploymentIdentityInput + keyId: vectors.keyId, + keyEpoch: unwrap(parseKeyEpoch(vectors.epoch)), + secretBase64Url: Buffer.from(vectors.keyHex, 'hex').toString('base64url'), + producerInstance: vectors.event.producerInstance, +} -const runtimeDeployment: DeploymentIdentityInput = { - ...conformanceDeployment, +const runtimeInput: OpaqueIdentityScopeInput = { + ...conformanceScope, secretBase64Url: 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', } +const runtimeScope = unwrap(validateOpaqueIdentityScope(runtimeInput)) -const identityVectors = groveExchangeProtocol.testVectors.identities -const invalidIdentityVectors = - groveExchangeProtocol.testVectors.invalidIdentities +const identityVectors = vectors.identities +const invalidIdentityVectors = vectors.invalidIdentities -const deriveVectorIdentity = ( +const deriveVectorIdentity = ( kind: Kind, - components: GroveOpaqueIdentityComponents[Kind], -) => - deriveConformanceVectorOpaqueIdentifier( - conformanceDeployment, - kind, - components, - ) + components: OpaqueIdentityComponents[Kind], +) => deriveConformanceVectorOpaqueIdentifier(conformanceScope, kind, components) describe('Grove exchange protocol identity', () => { it('fails closed without throwing at every untyped identity boundary', () => { const invalidValues = [null, undefined, 42, 'wrong-shape', Symbol('x')] for (const invalid of invalidValues) { const operations = [ - () => validateDeploymentIdentity(invalid), + () => validateOpaqueIdentityScope(invalid as never), () => encodeLengthFramedUtf8(invalid as never), () => deriveEntryFullUrl(invalid as never), () => entryIdentifierName(invalid as never), @@ -126,17 +110,13 @@ describe('Grove exchange protocol identity', () => { ]), () => deriveOpaqueIdentifier( - runtimeDeployment, + runtimeScope, 'source-record', invalid as never, ), - () => - deriveEntryNodeIdentifier( - runtimeDeployment, - invalid as never, - 'conversion-provenance', - '0', - ), + () => deriveEventIdentifier(runtimeScope, invalid as never), + () => deriveEntryNodeIdentifier(runtimeScope, invalid as never), + () => deriveEntryNodeValue(invalid as never), ] for (const operation of operations) { expect(operation).not.toThrow() @@ -146,13 +126,36 @@ describe('Grove exchange protocol identity', () => { const cyclic: Record = {} cyclic.self = cyclic - expect(() => validateDeploymentIdentity(cyclic as never)).not.toThrow() - expect(validateDeploymentIdentity(cyclic as never).ok).toBe(false) + expect(() => validateOpaqueIdentityScope(cyclic as never)).not.toThrow() + expect(validateOpaqueIdentityScope(cyclic as never).ok).toBe(false) + }) + + it('accepts only the handle it validated and keeps its key private', () => { + expect(isOpaqueIdentityScope(runtimeScope)).toBe(true) + expect(Object.isFrozen(runtimeScope)).toBe(true) + expect(runtimeScope).not.toHaveProperty('secretBase64Url') + expect(JSON.stringify(runtimeScope)).not.toContain( + runtimeInput.secretBase64Url, + ) + const forged = { ...runtimeScope } + expect(isOpaqueIdentityScope(forged)).toBe(false) + expect( + deriveOpaqueIdentifier(forged, 'source-record', [ + 'healthkit', + 'HKQuantityTypeIdentifierHeartRate', + 'https://study.example.org/fhir/NamingSystem/participant', + 'participant-1', + 'native-1', + ]), + ).toMatchObject({ + ok: false, + issues: [{ code: 'invalid-identifier', path: ['scope'] }], + }) + expect(deriveEventIdentifier(forged, sequence('1')).ok).toBe(false) }) - it('keeps a reused deployment handle immune to mutation', () => { - const validated = unwrap(validateDeploymentIdentity(runtimeDeployment)) - const components: GroveOpaqueIdentityComponents['source-record'] = [ + it('keeps a reused scope handle immune to mutation', () => { + const components: OpaqueIdentityComponents['source-record'] = [ 'healthkit', 'HKQuantityTypeIdentifierHeartRate', 'https://study.example.org/fhir/NamingSystem/participant', @@ -160,21 +163,13 @@ describe('Grove exchange protocol identity', () => { 'native-1', ] const before = unwrap( - deriveOpaqueIdentifier(validated, 'source-record', components), + deriveOpaqueIdentifier(runtimeScope, 'source-record', components), ) - const event = unwrap(deriveEventIdentifier(validated, '1')) - - const mutable = validated as { - identity: { keyId: string } - secret?: Uint8Array - } - expect(mutable.secret).toBeUndefined() + const event = unwrap(deriveEventIdentifier(runtimeScope, sequence('1'))) + const mutable = runtimeScope as { keyId: string; secret?: Uint8Array } for (const mutate of [ () => { - mutable.identity.keyId = 'TAMPERED' - }, - () => { - mutable.identity = { keyId: 'TAMPERED' } + mutable.keyId = 'TAMPERED' }, () => { mutable.secret = new Uint8Array(32) @@ -186,16 +181,14 @@ describe('Grove exchange protocol identity', () => { // A frozen handle rejects the write; a sloppy-mode caller silently keeps the original. } } - - expect(validated.identity.keyId).toBe(runtimeDeployment.keyId) + expect(runtimeScope.keyId).toBe(runtimeInput.keyId) expect( - deriveOpaqueIdentifier(validated, 'source-record', components), + deriveOpaqueIdentifier(runtimeScope, 'source-record', components), ).toEqual({ ok: true, value: before }) - expect(deriveEventIdentifier(validated, '1')).toEqual({ + expect(deriveEventIdentifier(runtimeScope, sequence('1'))).toEqual({ ok: true, value: event, }) - expect(validateDeploymentIdentity(validated.identity).ok).toBe(true) }) it.each(identityVectors)( @@ -207,45 +200,47 @@ describe('Grove exchange protocol identity', () => { ) expect(derived).toMatchObject({ ok: true, - value: { value: vector.value }, + value: { + value: vector.value, + system: vectorSystems[vector.identityKind], + }, }) }, ) it('matches the shared event, entry-node, and fullUrl vectors', () => { - const vector = groveExchangeProtocol.testVectors const event = unwrap( - deriveEventIdentifier(runtimeDeployment, vector.event.sequence), + deriveEventIdentifier(runtimeScope, sequence(vectors.event.sequence)), ) expect(event).toEqual({ - system: vector.event.system, - value: vector.event.value, + system: vectors.event.system, + value: vectors.event.value, role: 'event', }) const node = unwrap( - deriveEntryNodeIdentifier( - runtimeDeployment, + deriveEntryNodeIdentifier(runtimeScope, { event, - vector.entryNode.role, - vector.entryNode.ordinal, - ), + role: vectors.entryNode.role, + ordinal: ordinal(vectors.entryNode.ordinal), + }), ) - expect(node.value).toBe(vector.entryNode.value) + expect(node).toEqual({ + system: vectors.entryNode.system, + value: vectors.entryNode.value, + role: 'entry-node', + }) expect(deriveEntryFullUrl(node)).toEqual({ ok: true, - value: vector.entryNode.fullUrl, + value: vectors.entryNode.fullUrl, }) - const fullUrlVector = vector.fullUrls[0] + const fullUrlVector = vectors.fullUrls[0] expect( deriveEntryFullUrl({ system: uri(fullUrlVector.system), value: fullUrlVector.value, }), - ).toEqual({ - ok: true, - value: fullUrlVector.fullUrl, - }) + ).toEqual({ ok: true, value: fullUrlVector.fullUrl }) }) it('length-frames tuples without delimiter collisions', () => { @@ -286,78 +281,83 @@ describe('Grove exchange protocol identity', () => { }) it('fails closed for the public test key at public APIs and malformed key spaces', () => { - expect(validateDeploymentIdentity(conformanceDeployment).ok).toBe(false) + expect(validateOpaqueIdentityScope(conformanceScope).ok).toBe(false) expect( - validateDeploymentIdentity({ - ...runtimeDeployment, + validateOpaqueIdentityScope({ + ...runtimeInput, keyUse: 'conformance-testing', - }).ok, + } as never).ok, ).toBe(false) const sourceVector = identityVectors[0] expect( - deriveOpaqueIdentifier( - conformanceDeployment, + deriveConformanceVectorOpaqueIdentifier( + runtimeInput, sourceVector.identityKind, sourceVector.components as never, ).ok, ).toBe(false) expect( - validateDeploymentIdentity({ - ...runtimeDeployment, - opaqueIdentifierSystems: { - ...runtimeDeployment.opaqueIdentifierSystems, - 'source-record': - runtimeDeployment.opaqueIdentifierSystems['source-output'], + validateOpaqueIdentityScope({ + ...runtimeInput, + systems: { + ...runtimeInput.systems, + opaque: { + ...runtimeInput.systems.opaque, + 'source-record': runtimeInput.systems.opaque['source-output'], + }, }, }).ok, ).toBe(false) const withWrongKinds = { - ...runtimeDeployment, - opaqueIdentifierSystems: Object.fromEntries( - Object.entries(runtimeDeployment.opaqueIdentifierSystems).map( - ([kind, system], index) => [ - index === 0 ? 'unknown-kind' : kind, - system, - ], + ...runtimeInput, + systems: { + ...runtimeInput.systems, + opaque: Object.fromEntries( + Object.entries(runtimeInput.systems.opaque).map( + ([kind, system], index) => [ + index === 0 ? 'unknown-kind' : kind, + system, + ], + ), ), - ), - } as unknown as DeploymentIdentityInput - expect(validateDeploymentIdentity(withWrongKinds).ok).toBe(false) + }, + } as unknown as OpaqueIdentityScopeInput + expect(validateOpaqueIdentityScope(withWrongKinds).ok).toBe(false) for (const badSystem of [ 'https://例.example/identity', 'https://example.org/%ZZ', 'https://example.org/identity value', ]) { expect( - validateDeploymentIdentity({ - ...runtimeDeployment, - eventIdentifierSystem: badSystem as AbsoluteUri, + validateOpaqueIdentityScope({ + ...runtimeInput, + systems: { ...runtimeInput.systems, event: badSystem as AbsoluteUri }, }).ok, ).toBe(false) } }) it.each([ - { opaqueIdentifierSystems: null }, - { opaqueIdentifierSystems: [] }, + { systems: null }, + { systems: { ...runtimeInput.systems, opaque: [] } }, { keyId: 42 }, { keyId: 'not a token' }, { keyEpoch: 1 }, { keyEpoch: '0' }, { producerInstance: 42 }, - { producerInstance: runtimeDeployment.producerInstance.toUpperCase() }, + { producerInstance: runtimeInput.producerInstance.toUpperCase() }, { secretBase64Url: 42 }, { secretBase64Url: '*' }, { secretBase64Url: 'A' }, { secretBase64Url: 'AA' }, - { eventIdentifierSystem: '/relative' }, - { entryNodeIdentifierSystem: '/relative' }, - ])('rejects malformed deployment field set %#', (replacement) => { + { systems: { ...runtimeInput.systems, event: '/relative' } }, + { systems: { ...runtimeInput.systems, entryNode: '/relative' } }, + ])('rejects malformed scope field set %#', (replacement) => { expect( - validateDeploymentIdentity({ - ...runtimeDeployment, + validateOpaqueIdentityScope({ + ...runtimeInput, ...replacement, - }).ok, + } as never).ok, ).toBe(false) }) @@ -368,20 +368,20 @@ describe('Grove exchange protocol identity', () => { if (source === undefined) throw new Error('Missing source-output vector.') expect( deriveOpaqueIdentifier( - runtimeDeployment, - 'unknown-kind' as GroveOpaqueIdentityKind, + runtimeScope, + 'unknown-kind' as OpaqueIdentityKind, source.components as never, ).ok, ).toBe(false) expect( deriveOpaqueIdentifier( - runtimeDeployment, + runtimeScope, 'source-output', source.components.slice(0, -1) as never, ).ok, ).toBe(false) expect( - deriveOpaqueIdentifier(runtimeDeployment, 'source-output', [ + deriveOpaqueIdentifier(runtimeScope, 'source-output', [ ...source.components, 'extra', ] as never).ok, @@ -397,7 +397,7 @@ describe('Grove exchange protocol identity', () => { components[1] = 42 expect( deriveOpaqueIdentifier( - runtimeDeployment, + runtimeScope, 'source-output', components as never, ), @@ -413,7 +413,7 @@ describe('Grove exchange protocol identity', () => { const components: string[] = [...vector.components] components[0] = invalid const result = deriveOpaqueIdentifier( - runtimeDeployment, + runtimeScope, vector.identityKind, components as never, ) @@ -464,26 +464,39 @@ describe('Grove exchange protocol identity', () => { ] as const for (const { kind, components } of invalidProviderCoordinates) { - const result = deriveOpaqueIdentifier( - runtimeDeployment, - kind, - components as never, - ) - expect(result).toMatchObject({ + expect( + deriveProviderOpaqueIdentifier(runtimeScope, kind, components as never), + ).toMatchObject({ ok: false, issues: [{ code: 'invalid-code', path: ['components', 0] }], }) } + expect( + providerCoordinateIssue('provider-record', [ + 'not-a-provider', + 'type', + 's', + 'v', + 'id', + ]), + ).toMatchObject({ code: 'invalid-code' }) }) it('rejects every shared invalid opaque-identity vector', () => { expect(invalidIdentityVectors).toHaveLength(4) for (const vector of invalidIdentityVectors) { - const result = deriveOpaqueIdentifier( - runtimeDeployment, - vector.identityKind, - vector.components as never, - ) + const result = + vector.expectedError === 'provider-kind-required' ? + deriveProviderOpaqueIdentifier( + runtimeScope, + vector.identityKind, + vector.components as never, + ) + : deriveOpaqueIdentifier( + runtimeScope, + vector.identityKind, + vector.components as never, + ) expect(result.ok).toBe(false) if (!result.ok) { expect(result.issues[0]?.path).toEqual([ @@ -498,59 +511,56 @@ describe('Grove exchange protocol identity', () => { it('binds entry-node derivation to this producer typed event identity', () => { const event = unwrap( - deriveEventIdentifier( - runtimeDeployment, - groveExchangeProtocol.testVectors.event.sequence, - ), + deriveEventIdentifier(runtimeScope, sequence(vectors.event.sequence)), ) expect( - deriveEntryNodeIdentifier( - runtimeDeployment, - { system: event.system, value: event.value }, - 'conversion-provenance', - '0', - ).ok, + deriveEntryNodeIdentifier(runtimeScope, { + event: { system: event.system, value: event.value } as never, + role: 'conversion-provenance', + ordinal: ordinal('0'), + }).ok, ).toBe(false) expect( - deriveEntryNodeIdentifier( - runtimeDeployment, - { + deriveEntryNodeIdentifier(runtimeScope, { + event: { ...event, value: 'e0:13ed2fea-25b0-4d0d-9a26-c6d2a7f057a2:1', }, - 'conversion-provenance', - '0', - ).ok, + role: 'conversion-provenance', + ordinal: ordinal('0'), + }).ok, ).toBe(false) }) it('validates event and entry-node lexical boundaries independently', () => { const event = unwrap( - deriveEventIdentifier( - runtimeDeployment, - groveExchangeProtocol.testVectors.event.sequence, - ), + deriveEventIdentifier(runtimeScope, sequence(vectors.event.sequence)), ) - expect(deriveEventIdentifier(runtimeDeployment, 1 as never).ok).toBe(false) - expect(deriveEventIdentifier(runtimeDeployment, '0').ok).toBe(false) - expect( - deriveEntryNodeValue('/relative', event.value, 'resource', '0').ok, - ).toBe(false) - expect( - deriveEntryNodeValue(event.system, event.value, 42 as never, '0').ok, - ).toBe(false) - expect( - deriveEntryNodeValue(event.system, event.value, 'Uppercase', '0').ok, - ).toBe(false) + expect(deriveEventIdentifier(runtimeScope, 1 as never).ok).toBe(false) + expect(deriveEventIdentifier(runtimeScope, '0' as never).ok).toBe(false) + const node = ( + key: Partial<{ system: unknown; value: unknown; role: unknown }>, + role: unknown, + nodeOrdinal: unknown, + ) => + deriveEntryNodeValue({ + event: { ...event, ...key }, + role, + ordinal: nodeOrdinal, + } as never) + expect(node({ system: '/relative' }, 'resource', '0').ok).toBe(false) + expect(node({}, 42, '0').ok).toBe(false) + expect(node({}, 'Uppercase', '0').ok).toBe(false) + expect(node({}, 'resource', 0).ok).toBe(false) + expect(node({}, 'resource', '01').ok).toBe(false) + expect(node({ role: 'source-record' }, 'resource', '0').ok).toBe(false) + expect(node({}, 'resource', '0').ok).toBe(true) expect( - deriveEntryNodeValue(event.system, event.value, 'resource', 0 as never) - .ok, - ).toBe(false) - expect( - deriveEntryNodeValue(event.system, event.value, 'resource', '01').ok, - ).toBe(false) - expect( - deriveEntryNodeIdentifier(runtimeDeployment, event, 'Uppercase', '0').ok, + deriveEntryNodeIdentifier(runtimeScope, { + event, + role: 'Uppercase', + ordinal: ordinal('0'), + }).ok, ).toBe(false) expect(isEventIdentityValue(42)).toBe(false) expect(isEventIdentityValue('e0:not-a-uuid:1')).toBe(false) diff --git a/packages/fhir/test/export-surface.json b/packages/fhir/test/export-surface.json index e6bea024..9e39fa46 100644 --- a/packages/fhir/test/export-surface.json +++ b/packages/fhir/test/export-surface.json @@ -42,6 +42,7 @@ "deriveEntryNodeValue", "deriveEventIdentifier", "deriveOpaqueIdentifier", + "deriveOpaqueIdentitySystems", "encodeBase64Binary", "encodeLengthFramedUtf8", "entryIdentifierName", @@ -54,9 +55,9 @@ "formatHumanName", "groveExchangeProtocol", "groveFhirVersion", - "groveMobileContract", "groveMobilePackageMetadata", "groveMobileProfileCanonicals", + "groveProducerDiagnostics", "groveProfileClaims", "groveRecordingFormatRegistry", "humanNameParts", @@ -75,8 +76,10 @@ "immunizationVaccineDisplay", "isEntryNodeIdentityValue", "isEventIdentityValue", + "isOpaqueIdentityScope", "isOpaqueIdentityValue", "isReferenceToType", + "isSemanticallyEqual", "issues", "mapResult", "mobileEffectiveCanonicalization", @@ -100,10 +103,13 @@ "parseCanonical", "parseDevice", "parseDocumentReference", + "parseEntryNodeOrdinal", + "parseEventSequence", + "parseExchangeEventContext", + "parseExchangeGraph", "parseFhirId", "parseFhirInstant", - "parseGroveMobileExchangeBundle", - "parseGroveMobileRetractionBundle", + "parseKeyEpoch", "parseObservation", "parsePatientReference", "parsePositiveInteger", @@ -112,6 +118,7 @@ "parseQuestionnaireResponse", "parseR4CollectionBundle", "parseResearchStudyReference", + "parseRetractionEvent", "parseSemVer", "parseSpecimen", "parseSupportedR4Resource", @@ -129,6 +136,7 @@ "periodOverlaps", "preflightQuestionnairePair", "referenceType", + "retractionTargets", "scheduledCovers", "scheduledDuration", "scheduledEnd", @@ -139,7 +147,7 @@ "slotSpecialtyDisplays", "toFhirReference", "toReference", - "validateDeploymentIdentity", + "validateOpaqueIdentityScope", "valueToFhirQuantity" ], "./r4": [ @@ -148,22 +156,22 @@ "codingSchema", "deviceSchema", "documentReferenceSchema", + "exchangeGraphSchema", "expressionSchema", "extensionSchema", "graphResourceSchema", - "groveMobileExchangeBundleSchema", - "groveMobileRetractionBundleSchema", "identifierSchema", + "isSemanticallyEqual", "metaSchema", "observationComponentSchema", "observationSchema", "parseDevice", "parseDocumentReference", - "parseGroveMobileExchangeBundle", - "parseGroveMobileRetractionBundle", + "parseExchangeGraph", "parseObservation", "parseProvenance", "parseR4CollectionBundle", + "parseRetractionEvent", "parseSpecimen", "parseSupportedR4Resource", "periodSchema", @@ -172,6 +180,8 @@ "quantitySchema", "r4CollectionBundleSchema", "resolvableReferenceSchema", + "retractionEventSchema", + "retractionTargets", "sampledDataSchema", "specimenSchema", "supportedR4ResourceSchema" @@ -184,28 +194,32 @@ "deriveEntryNodeValue", "deriveEventIdentifier", "deriveOpaqueIdentifier", + "deriveOpaqueIdentitySystems", "encodeLengthFramedUtf8", "entryIdentifierName", "groveExchangeProtocol", "groveFhirVersion", - "groveMobileContract", "groveMobilePackageMetadata", "groveMobileProfileCanonicals", + "groveProducerDiagnostics", "groveProfileClaims", "groveRecordingFormatRegistry", "isEntryNodeIdentityValue", "isEventIdentityValue", + "isOpaqueIdentityScope", "isOpaqueIdentityValue", "mobileEffectiveCanonicalization", "mobileEffectiveCanonicalizationVectors", + "parseExchangeEventContext", "sharedMobileMeasurementCatalog", - "validateDeploymentIdentity" + "validateOpaqueIdentityScope" ], "./providers": [ "adapterMeasurementCatalog", - "buildProviderMeasurementBundle", - "buildProviderRecordingBundle", - "buildProviderRetractionBundle", + "buildProviderExchangeGraph", + "buildProviderExchangeGraphs", + "buildProviderRecordingGraph", + "buildProviderRetractionEvent", "encodeRecordingBytes", "groveFhirVersion", "groveProviderPackageMetadata", @@ -216,9 +230,9 @@ "parseImmutableRecordingUrl", "parseMediaType", "parseNormalizedProviderRecord", - "parseProviderMeasurementBundleInput", - "parseProviderRecordingBundleInput", - "parseProviderRetractionInput", + "parseProviderConversionOptions", + "parseProviderRecordingAttachment", + "parseProviderRecordingSource", "parseSha1Base64", "providerAdapterCatalog", "providerOutputCoordinates", diff --git a/packages/fhir/test/exports.test.ts b/packages/fhir/test/exports.test.ts index ced75ad7..01bffc97 100644 --- a/packages/fhir/test/exports.test.ts +++ b/packages/fhir/test/exports.test.ts @@ -15,11 +15,11 @@ import * as questionnaire from '../src/questionnaire/index.js' import * as r4 from '../src/r4/index.js' type HasMeasurementBuilder = - 'buildProviderMeasurementBundle' extends keyof T ? true : false + 'buildProviderExchangeGraph' extends keyof T ? true : false type HasRecordingBuilder = - 'buildProviderRecordingBundle' extends keyof T ? true : false + 'buildProviderRecordingGraph' extends keyof T ? true : false type HasRetractionBuilder = - 'buildProviderRetractionBundle' extends keyof T ? true : false + 'buildProviderRetractionEvent' extends keyof T ? true : false type HasInternalPackageGraph = 'groveFhirPackageGraph' extends keyof T ? true : false type HasContractVersion = @@ -58,9 +58,9 @@ describe('public entry-point boundaries', () => { }) it('exposes the closed provider facade only from Provider', () => { - expect(typeof provider.buildProviderMeasurementBundle).toBe('function') - expect(typeof provider.buildProviderRecordingBundle).toBe('function') - expect(typeof provider.buildProviderRetractionBundle).toBe('function') + expect(typeof provider.buildProviderExchangeGraph).toBe('function') + expect(typeof provider.buildProviderRecordingGraph).toBe('function') + expect(typeof provider.buildProviderRetractionEvent).toBe('function') expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() @@ -73,15 +73,10 @@ describe('public entry-point boundaries', () => { expect(root.groveExchangeProtocol.protocolVersion).toBe(0) expect(root.groveExchangeProtocol).not.toHaveProperty('version') expect(root.groveExchangeProtocol).not.toHaveProperty('releaseVersion') - expect(root.groveMobileContract).not.toHaveProperty('version') expect(root.groveRecordingFormatRegistry).not.toHaveProperty('version') expect(root.groveProfileClaims).not.toHaveProperty('version') expect(provider.providerAdapterCatalog).not.toHaveProperty('version') - expect( - root.groveMobileContract.identity.resourceIdentifierPriority, - ).toEqual( - root.groveExchangeProtocol.entryIdentity.resourceIdentifierPriority, - ) + expect('groveMobileContract' in root).toBe(false) expect(mobile.groveMobilePackageMetadata.packageId).toBe( 'org.grovealliance.fhir.mobile', ) @@ -95,7 +90,7 @@ describe('public entry-point boundaries', () => { expectTypeOf(root.groveFhirVersion).toEqualTypeOf<'4.0.1'>() expectTypeOf>().toEqualTypeOf() expectTypeOf< - HasRuntimeVersion + HasRuntimeVersion >().toEqualTypeOf() }) diff --git a/packages/fhir/test/identity-systems.test.ts b/packages/fhir/test/identity-systems.test.ts new file mode 100644 index 00000000..ea8eefd5 --- /dev/null +++ b/packages/fhir/test/identity-systems.test.ts @@ -0,0 +1,111 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + parseAbsoluteUri, + parseKeyEpoch, + type AbsoluteUri, + type KeyEpoch, + type Result, +} from '../src/core/index.js' +import { + deriveOpaqueIdentitySystems, + groveExchangeProtocol, + validateOpaqueIdentityScope, +} from '../src/mobile/index.js' + +const unwrap = (result: Result): Value => { + if (!result.ok) throw new Error(result.issues[0]?.message) + return result.value +} +const uri = (value: string): AbsoluteUri => unwrap(parseAbsoluteUri(value)) +const epoch = (value: string): KeyEpoch => unwrap(parseKeyEpoch(value)) + +const vectors = groveExchangeProtocol.testVectors +// The normative vectors name their systems under this root; nothing else hard-codes it. +const vectorRoot = vectors.event.system.slice( + 0, + vectors.event.system.indexOf('/NamingSystem/'), +) + +describe('derived deployment identifier systems', () => { + it('reproduces the vectors identitySystems byte for byte from root, key id and epoch', () => { + const systems = unwrap( + deriveOpaqueIdentitySystems( + uri(vectorRoot), + vectors.keyId, + epoch(vectors.epoch), + ), + ) + for (const { identityKind, system } of vectors.identitySystems) { + expect(systems.opaque[identityKind]).toBe(system) + } + expect(Object.keys(systems.opaque)).toHaveLength( + vectors.identitySystems.length, + ) + expect(systems.event).toBe(vectors.event.system) + expect(systems.entryNode).toBe(vectors.entryNode.system) + expect(Object.isFrozen(systems)).toBe(true) + expect(Object.isFrozen(systems.opaque)).toBe(true) + }) + + it('follows the catalog forms rather than a literal', () => { + const systems = unwrap( + deriveOpaqueIdentitySystems( + uri('https://myheartcounts.stanford.edu/fhir'), + 'store', + epoch('1'), + ), + ) + expect(systems.opaque['source-record']).toBe( + groveExchangeProtocol.opaqueIdentity.recommendedSystemForm + .replace('', 'https://myheartcounts.stanford.edu/fhir') + .replace('', 'source-record') + .replace('', 'store') + .replace('', '1'), + ) + expect(systems.event).toBe( + groveExchangeProtocol.event.bundleIdentifier.recommendedSystemForm.replace( + '', + 'https://myheartcounts.stanford.edu/fhir', + ), + ) + }) + + it('mints a scope from the derived systems', () => { + const systems = unwrap( + deriveOpaqueIdentitySystems(uri(vectorRoot), 'store', epoch('7')), + ) + const scope = validateOpaqueIdentityScope({ + systems, + keyId: 'store', + keyEpoch: epoch('7'), + secretBase64Url: 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', + producerInstance: vectors.event.producerInstance, + }) + expect(scope.ok && scope.value.systems).toEqual(systems) + }) + + it.each([ + ['a trailing slash', 'https://study.example.org/fhir/', 'key', '1'], + ['a query', 'https://study.example.org/fhir?x=1', 'key', '1'], + ['a fragment', 'https://study.example.org/fhir#x', 'key', '1'], + ['a relative root', '/fhir', 'key', '1'], + ['a key id with spaces', 'https://study.example.org/fhir', 'my key', '1'], + ['a zero epoch', 'https://study.example.org/fhir', 'key', '0'], + ['a padded epoch', 'https://study.example.org/fhir', 'key', '01'], + ])('rejects %s', (_label, root, keyId, keyEpoch) => { + expect( + deriveOpaqueIdentitySystems( + root as AbsoluteUri, + keyId, + keyEpoch as KeyEpoch, + ).ok, + ).toBe(false) + }) +}) diff --git a/packages/fhir/test/mobile.test.ts b/packages/fhir/test/mobile.test.ts index 45d18ce0..62c1631b 100644 --- a/packages/fhir/test/mobile.test.ts +++ b/packages/fhir/test/mobile.test.ts @@ -6,36 +6,41 @@ // SPDX-License-Identifier: MIT // +import { readdirSync, readFileSync } from 'node:fs' import { expectTypeOf } from 'expect-type' +import { + context, + identityScope, + scopeInput, + study, + subject, + unwrap, + uri, +} from './provider-test-support.js' import * as mobileContract from '../src/contract/measurement-catalog.generated.js' import { - parseAbsoluteUri, parseFhirId, parseFhirInstant, parseSemVer, type FhirInstant, - type Result, } from '../src/core/index.js' import * as mobile from '../src/mobile/index.js' import { - entryIdentifierName, canonicalizeMobileEffectiveInstant, createEntryIdentity, deriveEntryFullUrl, + deriveEventIdentifier, encodeLengthFramedUtf8, + entryIdentifierName, groveFhirVersion, groveMobilePackageMetadata, mobileEffectiveCanonicalizationVectors, + parseExchangeEventContext, sharedMobileMeasurementCatalog, + validateOpaqueIdentityScope, type MobileMeasurement, } from '../src/mobile/index.js' -const unwrap = (result: Result): T => { - if (!result.ok) throw new Error(result.issues[0]?.message) - return result.value -} - -const uri = (value: string) => unwrap(parseAbsoluteUri(value)) const instant = (value: string): FhirInstant => unwrap(parseFhirInstant(value)) describe('source-neutral Mobile contract', () => { @@ -50,20 +55,26 @@ describe('source-neutral Mobile contract', () => { expect(mobile.groveExchangeProtocol.protocolVersion).toBe(0) expect(mobile.groveExchangeProtocol).not.toHaveProperty('version') expect(mobile.groveExchangeProtocol).not.toHaveProperty('releaseVersion') - expect(mobile.groveMobileContract).not.toHaveProperty('version') expect(mobile.groveRecordingFormatRegistry).not.toHaveProperty('version') - expect(Object.isFrozen(mobile.groveMobileContract)).toBe(true) + expect('groveMobileContract' in mobile).toBe(false) expect('groveFhirContractVersion' in mobile).toBe(false) - expect('groveFhirExchangeIdentity' in mobile).toBe(false) - expect('buildProviderMeasurementBundle' in mobile).toBe(false) + expect('buildProviderExchangeGraph' in mobile).toBe(false) expect('providerAdapterCatalog' in mobile).toBe(false) - expect('groveFhirProfileCanonicals' in mobile).toBe(false) expect('PROFILES' in mobile).toBe(false) expect('providerAdapterCatalog' in mobileContract).toBe(false) expect('providerScalarOutputRoles' in mobileContract).toBe(false) expect('groveProviderPackageMetadata' in mobileContract).toBe(false) expect('adapterMeasurementCatalog' in mobileContract).toBe(false) }) + + it('never imports the provider catalog, so a mobile-only consumer does not ship it', () => { + const mobileRoot = new URL('../src/mobile/', import.meta.url) + for (const name of readdirSync(mobileRoot)) { + const source = readFileSync(new URL(name, mobileRoot), 'utf8') + expect(source).not.toMatch(/providers\.generated|\/providers\//u) + } + }) + it('contains only evidenced shared measurements', () => { const entries = Object.values(sharedMobileMeasurementCatalog) expect(entries).toHaveLength(84) @@ -145,23 +156,27 @@ describe('Mobile exchange entry identity', () => { }) }) - it('retains a complete business Identifier and optional repository id', () => { + it('retains a complete roled Identifier and optional repository id', () => { const identifier = { system: uri('https://example.org/identifiers'), value: 'record-1', + role: 'source-output' as const, } const id = unwrap(parseFhirId('repository-id')) const fullUrl = unwrap(deriveEntryFullUrl(identifier)) const result = createEntryIdentity(identifier, id) - expect(result.ok && result.value).toEqual({ - identifier, - id, - fullUrl, - }) + expect(result.ok && result.value).toEqual({ identifier, id, fullUrl }) expect(result.ok && Object.isFrozen(result.value)).toBe(true) expect(Object.isFrozen(identifier)).toBe(false) identifier.value = 'caller-mutated-after-construction' expect(result.ok && result.value.identifier.value).toBe('record-1') + expect( + createEntryIdentity({ + system: uri('https://example.org/identifiers'), + value: 'record-1', + role: 'not-a-role', + } as never).ok, + ).toBe(false) }) it.each([ @@ -185,6 +200,7 @@ describe('Mobile exchange entry identity', () => { const identifier = { system: uri('https://example.org/identifiers'), value: 'valid-😀', + role: 'source-output' as const, } expect(deriveEntryFullUrl(identifier).ok).toBe(true) expect(createEntryIdentity(identifier, 'invalid/id' as never).ok).toBe( @@ -222,3 +238,139 @@ describe('Mobile effective-time canonicalization', () => { expect(canonicalizeMobileEffectiveInstant(value).ok).toBe(false) }) }) + +describe('exchange event context', () => { + it('defaults the conversion instant to now', () => { + const { + subject, + event, + identityScope: scope, + repositoryScope, + application, + host, + } = context() + const before = Date.now() + const result = parseExchangeEventContext({ + subject, + event, + identityScope: scope, + repositoryScope, + application, + host, + }) + expect(result.ok).toBe(true) + if (!result.ok) return + const instant = Date.parse(result.value.conversionInstant) + expect(instant).toBeGreaterThanOrEqual(before - 1) + expect(instant).toBeLessThanOrEqual(Date.now() + 1) + }) + + it('validates a context once and keeps the scope handle by reference', () => { + const result = parseExchangeEventContext(context()) + expect(result.ok).toBe(true) + if (!result.ok) return + expect(Object.isFrozen(result.value)).toBe(true) + expect(result.value.identityScope).toBe(identityScope) + expect(result.value.converterRole).toEqual({ kind: 'assembler' }) + expect(result.value.studies).toEqual([]) + expect(result.value).not.toHaveProperty('repositoryIds') + }) + + it('treats an optional field set to undefined as absent', () => { + const result = parseExchangeEventContext({ + ...context(), + converterRole: undefined, + studies: undefined, + repositoryIds: undefined, + application: { ...context().application, version: undefined }, + }) + expect(result.ok).toBe(true) + if (!result.ok) return + expect(result.value.application).not.toHaveProperty('version') + }) + + it('reports deployment faults under schema codes, never registry codes', () => { + const otherScope = unwrap( + validateOpaqueIdentityScope({ + ...scopeInput, + producerInstance: '9ae7b610-bac2-4f13-97b4-53b84b8a90cf', + }), + ) + const faults: ReadonlyArray<[string, unknown, string]> = [ + [ + 'a forged scope', + { ...context(), identityScope: { ...identityScope } }, + 'identityScope', + ], + [ + 'an event another producer minted', + { + ...context(), + event: unwrap(deriveEventIdentifier(otherScope, '1' as never)), + }, + 'event', + ], + ['an unknown field', { ...context(), extra: true }, 'extra'], + ['a missing host', { ...context(), host: undefined }, 'host'], + [ + 'a blank application name', + { ...context(), application: { sourceDeviceToken: 'a', name: ' ' } }, + 'application', + ], + [ + 'a subject of unknown kind', + { + ...context(), + subject: { kind: 'remote', identifier: subject.identifier }, + }, + 'subject', + ], + [ + 'a bundled Patient without the subject identifier', + { + ...context(), + subject: { + kind: 'bundled', + identifier: subject.identifier, + patient: { resourceType: 'Patient' }, + }, + }, + 'subject', + ], + [ + 'a duplicated study', + { ...context(), studies: [study('a'), study('a')] }, + 'studies', + ], + [ + 'an unknown graph node', + { ...context(), repositoryIds: { patient: 'x' } }, + 'repositoryIds', + ], + [ + 'a converter role without its application', + { ...context(), converterRole: { kind: 'gateway-application' } }, + 'converterRole', + ], + [ + 'an instant without offset', + { ...context(), conversionInstant: '2026-08-20T12:00:00' }, + 'conversionInstant', + ], + ] + for (const [label, candidate, field] of faults) { + const result = parseExchangeEventContext(candidate) + expect([label, result.ok]).toEqual([label, false]) + if (result.ok) continue + expect([label, result.issues.map(({ path }) => path[0])]).toEqual([ + label, + expect.arrayContaining([field]), + ]) + expect( + result.issues + .filter(({ code }) => code.includes('.')) + .map(({ code }) => code), + ).toEqual([]) + } + }) +}) diff --git a/packages/fhir/test/observation-semantics.test.ts b/packages/fhir/test/observation-semantics.test.ts index 17c0b78d..872a9620 100644 --- a/packages/fhir/test/observation-semantics.test.ts +++ b/packages/fhir/test/observation-semantics.test.ts @@ -230,14 +230,14 @@ describe('catalog-driven Observation semantic boundaries', () => { mutate: (observation: Record) => { observation.status = 'preliminary' }, - code: 'mobile-heart-rate.status', + code: 'mobile-output.semantic-profile', }, { name: 'missing coding list', mutate: (observation: Record) => { observation.code = {} }, - code: 'mobile-heart-rate.code', + code: 'mobile-output.semantic-profile', }, { name: 'duplicate primary code', @@ -245,7 +245,7 @@ describe('catalog-driven Observation semantic boundaries', () => { const code = observation.code as { coding: unknown[] } code.coding.push(structuredClone(code.coding[0])) }, - code: 'mobile-heart-rate.code', + code: 'mobile-output.semantic-profile', }, { name: 'wrong primary code', @@ -255,14 +255,14 @@ describe('catalog-driven Observation semantic boundaries', () => { } code.coding[0] = { system: 'http://loinc.org', code: 'not-admitted' } }, - code: 'mobile-heart-rate.code', + code: 'mobile-output.semantic-profile', }, { name: 'missing category', mutate: (observation: Record) => { Reflect.deleteProperty(observation, 'category') }, - code: 'mobile-heart-rate.category', + code: 'mobile-output.semantic-profile', }, { name: 'additional same-system category', @@ -275,14 +275,14 @@ describe('catalog-driven Observation semantic boundaries', () => { code: 'laboratory', }) }, - code: 'mobile-heart-rate.category', + code: 'mobile-output.semantic-profile', }, { name: 'neither flexible effective choice', mutate: (observation: Record) => { Reflect.deleteProperty(observation, 'effectiveDateTime') }, - code: 'mobile-heart-rate.effective', + code: 'mobile-output.semantic-profile', }, { name: 'both flexible effective choices', @@ -292,7 +292,7 @@ describe('catalog-driven Observation semantic boundaries', () => { end: '2026-08-20T01:00:00Z', } }, - code: 'mobile-heart-rate.effective', + code: 'mobile-output.semantic-profile', }, { name: 'missing quantity value', @@ -329,45 +329,45 @@ describe('catalog-driven Observation semantic boundaries', () => { expect(semanticMessages(resting)).toEqual([]) const restingCode = resting.code as { coding: unknown[] } restingCode.coding.pop() - expectRule(resting, 'mobile-resting-heart-rate.required-coding') + expectRule(resting, 'mobile-output.semantic-profile') const sleep = observationFor(definition('sleep-duration')) sleep.category = [ { coding: [{ system: 'http://loinc.org', code: 'sleep' }] }, ] - expectRule(sleep, 'mobile-sleep-duration.category') + expectRule(sleep, 'mobile-output.semantic-profile') }) it('enforces fixed and choice aggregation methods', () => { const fixed = observationFor(definition('deep-sleep-duration')) fixed.method = undefined - expectRule(fixed, 'mobile-deep-sleep-duration.method') + expectRule(fixed, 'mobile-output.semantic-profile') const choice = observationFor(definition('respiratory-rate-average')) choice.method = {} - expectRule(choice, 'mobile-respiratory-rate-average.method') + expectRule(choice, 'mobile-output.semantic-profile') choice.method = { coding: [ { system: aggregationSystem, code: 'not-admitted' }, { system: aggregationSystem, code: 'also-not-admitted' }, ], } - expectRule(choice, 'mobile-respiratory-rate-average.method') + expectRule(choice, 'mobile-output.semantic-profile') }) it('enforces bounded effective periods and quantity value domains', () => { const weight = observationFor(definition('body-weight')) Reflect.deleteProperty(weight, 'effectiveDateTime') - expectRule(weight, 'mobile-body-weight.effective') + expectRule(weight, 'mobile-output.semantic-profile') const steps = observationFor(definition('step-count')) steps.effectivePeriod = { start: '2026-08-20T00:00:00Z' } - expectRule(steps, 'mobile-step-count.effective') + expectRule(steps, 'mobile-output.semantic-profile') steps.effectivePeriod = { start: '2026-08-20T00:00:00Z', end: '2026-08-20T00:00:00Z', } - expectRule(steps, 'mobile-step-count.nonzero-period') + expectRule(steps, 'mobile-output.semantic-profile') ;(steps.valueQuantity as Record).value = -1 expectRule(steps, 'mobile-output.quantity-value-domain') ;(steps.valueQuantity as Record).value = 0.5 @@ -384,7 +384,7 @@ describe('catalog-driven Observation semantic boundaries', () => { const coded = observationFor(definition('intermenstrual-bleeding')) expect(semanticMessages(coded)).toEqual([]) coded.valueCodeableConcept = {} - expectRule(coded, 'mobile-intermenstrual-bleeding.coded-result') + expectRule(coded, 'mobile-output.semantic-profile') coded.valueCodeableConcept = { coding: [ { system: 'https://example.org/other', code: 'present' }, @@ -395,31 +395,31 @@ describe('catalog-driven Observation semantic boundaries', () => { }, ], } - expectRule(coded, 'mobile-intermenstrual-bleeding.coded-result') + expectRule(coded, 'mobile-output.semantic-profile') const dateOfBirth = observationFor(definition('date-of-birth', 'healthkit')) expect(semanticMessages(dateOfBirth)).toEqual([]) Reflect.deleteProperty(dateOfBirth, 'valueDateTime') - expectRule(dateOfBirth, 'mobile-date-of-birth.date-time-result') + expectRule(dateOfBirth, 'mobile-output.semantic-profile') const food = observationFor(definition('food-correlation', 'healthkit')) expect(semanticMessages(food)).toEqual([]) food.hasMember = 'not-a-reference-list' - expectRule(food, 'mobile-food-correlation.members') + expectRule(food, 'mobile-output.semantic-profile') food.hasMember = [] - expectRule(food, 'mobile-food-correlation.members') + expectRule(food, 'mobile-output.semantic-profile') }) it('enforces distinct required blood-pressure components and their units', () => { const row = definition('blood-pressure') const missing = observationFor(row) missing.component = [] - expectRule(missing, 'mobile-blood-pressure.systolic') + expectRule(missing, 'mobile-output.semantic-profile') const duplicate = observationFor(row) const components = duplicate.component as unknown[] components.push(structuredClone(components[0])) - expectRule(duplicate, 'mobile-blood-pressure.systolic') + expectRule(duplicate, 'mobile-output.semantic-profile') const shared = observationFor(row) const sharedComponents = shared.component as Array> @@ -434,7 +434,7 @@ describe('catalog-driven Observation semantic boundaries', () => { ], } shared.component = [first] - expectRule(shared, 'mobile-blood-pressure.diastolic') + expectRule(shared, 'mobile-output.semantic-profile') const malformed = observationFor(row) const malformedComponent = ( @@ -453,7 +453,7 @@ describe('catalog-driven Observation semantic boundaries', () => { quantity.system = 'https://example.org/not-ucum' quantity.code = 'not-admitted' quantity.unit = 'not-admitted' - expectRule(malformed, 'mobile-blood-pressure.systolic') + expectRule(malformed, 'mobile-output.semantic-profile') const audiogram = observationFor(definition('audiogram-panel', 'healthkit')) const optionalComponent = definition('audiogram-panel', 'healthkit') @@ -470,6 +470,6 @@ describe('catalog-driven Observation semantic boundaries', () => { valueQuantity: {}, }, ] - expectRule(audiogram, `mobile-audiogram-panel.${optionalComponent.id}`) + expectRule(audiogram, 'mobile-output.semantic-profile') }) }) diff --git a/packages/fhir/test/profile-semantics-boundaries.test.ts b/packages/fhir/test/profile-semantics-boundaries.test.ts index 006c6a02..95ffb69e 100644 --- a/packages/fhir/test/profile-semantics-boundaries.test.ts +++ b/packages/fhir/test/profile-semantics-boundaries.test.ts @@ -13,13 +13,13 @@ import { } from './rule-test-support.js' import { adapterSourceMarkerClaims, + groveRecordingFormatRegistry, groveProfileClaims, } from '../src/contract/measurement-catalog.generated.js' import { healthKitApplicationDeviceIdentity, healthKitClinicalRecordAdmission, } from '../src/contract/providers.generated.js' -import { groveMobileContract } from '../src/mobile/contract.js' import { hasAdmittedActiveDeviceProfile, hasAdmittedActiveDocumentReferenceProfile, @@ -115,7 +115,7 @@ describe('profile-specific resource semantic boundaries', () => { ) expectRule( recording, - 'mobile-recording-device.identities', + 'mobile-device.recording-device-dual-identity', roles('recording-device', 'device-snapshot'), ) @@ -125,7 +125,7 @@ describe('profile-specific resource semantic boundaries', () => { meta: { profile: [hostProfile] }, identifier: [], }, - 'mobile-host-device.identity', + 'mobile-device.host-device-identity', roles('device-snapshot'), ) }) @@ -184,7 +184,7 @@ describe('profile-specific resource semantic boundaries', () => { mutate(candidate) expectRule( candidate, - 'healthkit-application-device.bundle-identifier', + 'healthkit-device.application-bundle-identifier', roles('device-snapshot'), ) } @@ -206,15 +206,15 @@ describe('profile-specific resource semantic boundaries', () => { { ...document, meta: { profile: ['https://example.org/unknown'] } }, 'mobile-output.document-profile', ) - expectRule(document, 'mobile-recording-document.identities') + expectRule(document, 'sensor-recording-document.identity-and-content') expectRule( document, - 'mobile-recording-document.identity-roles', + 'sensor-recording-document.identity-and-content', roles('source-record', 'source-output', 'source-artifact', 'unexpected'), ) expectRule( document, - 'mobile-recording-document.identity-roles', + 'sensor-recording-document.identity-and-content', new Map([ ['source-record', 1], ['source-output', 1], @@ -224,11 +224,15 @@ describe('profile-specific resource semantic boundaries', () => { ) expectRule( document, - 'mobile-recording-document.required-metadata', + 'sensor-recording-document.identity-and-content', requiredRoles, ) - expectRule(document, 'mobile-recording-document.attachment', requiredRoles) - expectRule(document, 'mobile-recording-document.format', requiredRoles) + expectRule( + document, + 'sensor-recording-document.identity-and-content', + requiredRoles, + ) + expectRule(document, 'sensor-recording-document.format', requiredRoles) }) it('fails closed for malformed embedded recording data and registry metadata', () => { @@ -267,7 +271,7 @@ describe('profile-specific resource semantic boundaries', () => { content.attachment.data = data expectRule( candidate, - 'mobile-recording-document.embedded-integrity', + 'sensor-recording-document.embedded-integrity', requiredRoles, ) } @@ -279,7 +283,7 @@ describe('profile-specific resource semantic boundaries', () => { system: 'https://example.org/wrong', code: 'unknown', } - expectRule(invalidFormat, 'mobile-recording-document.format', requiredRoles) + expectRule(invalidFormat, 'sensor-recording-document.format', requiredRoles) }) it('requires the exact QuestionnaireResponse profile', () => { @@ -343,7 +347,7 @@ describe('profile-specific resource semantic boundaries', () => { } expectRule( healthKitResource, - 'mobile-exchange.adapter-source-marker', + 'mobile-output.adapter-source-marker', roles(...healthKitClaim.requiredIdentifierRoles), ) @@ -352,7 +356,7 @@ describe('profile-specific resource semantic boundaries', () => { meta: { profile: ['https://example.org/neutral'] }, extension: [{ url: marker.url, valueCode: 'owned-marker' }], } - expectRule(neutral, 'mobile-exchange.adapter-source-marker') + expectRule(neutral, 'mobile-output.adapter-source-marker') }) it('admits only the release-versioned media types on clinical documents', () => { @@ -365,7 +369,7 @@ describe('profile-specific resource semantic boundaries', () => { { attachment: { contentType }, format: { - system: groveMobileContract.recordingFormats.codeSystem, + system: groveRecordingFormatRegistry.codeSystem, code: healthKitClinicalRecordAdmission.payloadFormat, }, }, diff --git a/packages/fhir/test/provider-contract.test.ts b/packages/fhir/test/provider-contract.test.ts index a2b047b0..c0df1cb7 100644 --- a/packages/fhir/test/provider-contract.test.ts +++ b/packages/fhir/test/provider-contract.test.ts @@ -9,17 +9,16 @@ import { readFileSync } from 'node:fs' import { assert, double, oneof, property } from 'fast-check' import { - baseInput, + context, dateTime, - end, heartRateMeasurement, - resources, - resourceIdentity, + observationOf, + record, start, unwrap, uri, } from './provider-test-support.js' -import { parseGroveMobileExchangeBundle } from '../src/index.js' +import { parseExchangeGraph } from '../src/index.js' import { createEntryIdentity, deriveEntryFullUrl, @@ -27,27 +26,33 @@ import { groveExchangeProtocol, } from '../src/mobile/index.js' import { - buildProviderMeasurementBundle, + buildProviderExchangeGraph, + buildProviderExchangeGraphs, parseNormalizedProviderRecord, - parseProviderMeasurementBundleInput, providerAdapterCatalog, providerRawOutputDiscriminators, providerRawOutputRoles, providerScalarOutputDiscriminators, providerScalarOutputRoles, type ConnectedProviderRecord, - type ProviderMeasurementBundleInput, + type NormalizedProviderRecord, } from '../src/providers/index.js' +const normalized = (name: string): NormalizedProviderRecord => + JSON.parse( + readFileSync( + new URL(`../fixtures/normalized/${name}`, import.meta.url), + 'utf8', + ), + ) as NormalizedProviderRecord + describe('Exchange entry identity', () => { it.each(groveExchangeProtocol.testVectors.fullUrls)( 'matches the protocol $id vector', (vector) => { - const derived = deriveEntryFullUrl({ - system: uri(vector.system), - value: vector.value, - }) - expect(derived).toEqual({ ok: true, value: vector.fullUrl }) + expect( + deriveEntryFullUrl({ system: uri(vector.system), value: vector.value }), + ).toEqual({ ok: true, value: vector.fullUrl }) }, ) @@ -62,21 +67,17 @@ describe('Exchange entry identity', () => { it('rejects emitted quantities outside their catalog-owned domains', () => { const valid = unwrap( - buildProviderMeasurementBundle( - baseInput('withings', 'getmeas:6', { + buildProviderExchangeGraph( + record('withings', 'getmeas:6', { kind: 'body-fat-percentage', value: 0, effective: { kind: 'date-time', value: dateTime }, }), + context(), ), - ) + ).graph const tampered = structuredClone(valid) - const observation = tampered.entry.find( - ({ resource }) => resource.resourceType === 'Observation', - )?.resource - if (observation?.resourceType !== 'Observation') { - throw new Error('Expected a body-fat Observation.') - } + const observation = observationOf(tampered) if (observation.valueQuantity === undefined) { throw new Error('Expected a body-fat Quantity result.') } @@ -88,16 +89,13 @@ describe('Exchange entry identity', () => { ...entry, resource: { ...observation, - valueQuantity: { - ...observation.valueQuantity, - value: 100.1, - }, + valueQuantity: { ...observation.valueQuantity, value: 100.1 }, }, } : entry, ), } - const parsed = parseGroveMobileExchangeBundle(invalidBundle) + const parsed = parseExchangeGraph(invalidBundle) expect(parsed.ok).toBe(false) if (!parsed.ok) { expect(parsed.issues.map(({ code }) => code)).toContain( @@ -107,32 +105,20 @@ describe('Exchange entry identity', () => { }) it('rejects isolated UTF-16 surrogates before UUID derivation', () => { - expect( - deriveEntryFullUrl({ - system: uri('https://example.org/identifiers'), - value: 'invalid-\ud800', - }).ok, - ).toBe(false) - expect( - deriveEntryFullUrl({ - system: uri('https://example.org/identifiers'), - value: 'invalid-\udc00', - }).ok, - ).toBe(false) - expect( - deriveEntryFullUrl({ - system: uri('https://example.org/identifiers'), - value: 'valid-😀', - }).ok, - ).toBe(true) + const system = uri('https://example.org/identifiers') + expect(deriveEntryFullUrl({ system, value: 'invalid-\ud800' }).ok).toBe( + false, + ) + expect(deriveEntryFullUrl({ system, value: 'invalid-\udc00' }).ok).toBe( + false, + ) + expect(deriveEntryFullUrl({ system, value: 'valid-😀' }).ok).toBe(true) }) it('rejects incomplete entry identifiers and invalid repository ids', () => { expect( - entryIdentifierName({ - system: '/relative' as never, - value: 'record-1', - }).ok, + entryIdentifierName({ system: '/relative' as never, value: 'record-1' }) + .ok, ).toBe(false) expect( entryIdentifierName({ @@ -151,6 +137,7 @@ describe('Exchange entry identity', () => { { system: uri('https://example.org/identifiers'), value: 'record-1', + role: 'source-output', }, 'invalid/id' as never, ).ok, @@ -165,24 +152,18 @@ describe('Provider-neutral normalization contract', () => { 'oura-sleep-duration.json', 'withings-body-weight.json', ])('accepts the normalized %s fixture', (name) => { - const fixture: unknown = JSON.parse( - readFileSync(new URL(`../fixtures/normalized/${name}`, import.meta.url), { - encoding: 'utf8', - }), - ) + const fixture = normalized(name) expect(parseNormalizedProviderRecord(fixture).ok).toBe(true) + expect( + buildProviderExchangeGraph( + fixture, + context(fixture.source.adapter.provider), + ).ok, + ).toBe(true) }) it('normalizes multi-output serialization order without mutating caller data', () => { - const fixture = JSON.parse( - readFileSync( - new URL( - '../fixtures/normalized/oura-daily-activity.json', - import.meta.url, - ), - { encoding: 'utf8' }, - ), - ) as { measurements: Array<{ kind: string }> } + const fixture = normalized('oura-daily-activity.json') const callerOrder = fixture.measurements.map(({ kind }) => kind) const result = parseNormalizedProviderRecord(fixture) expect(result.ok).toBe(true) @@ -196,179 +177,78 @@ describe('Provider-neutral normalization contract', () => { expect(Object.isFrozen(result.value.measurements)).toBe(true) }) - it('rejects raw response fields rather than silently stripping them', () => { - const fixture = JSON.parse( - readFileSync( - new URL( - '../fixtures/normalized/oura-sleep-duration.json', - import.meta.url, - ), - { encoding: 'utf8' }, - ), - ) as Record + it('refuses raw response fields rather than silently stripping them', () => { expect( parseNormalizedProviderRecord({ - ...fixture, + ...normalized('oura-sleep-duration.json'), rawVendorResponse: { readiness: 97 }, - }).ok, - ).toBe(false) - }) - - it('requires the provider-dependent scope assurance declared by the catalog', () => { - const fixture = JSON.parse( - readFileSync( - new URL( - '../fixtures/normalized/oura-sleep-duration.json', - import.meta.url, - ), - { encoding: 'utf8' }, - ), - ) as { - source: { - providerScopeIdentifier: Record - } - } - const { system, value } = fixture.source.providerScopeIdentifier - expect( - parseNormalizedProviderRecord({ - ...fixture, - source: { - ...fixture.source, - providerScopeIdentifier: { system, value }, - }, - }).ok, - ).toBe(false) - - expect( - parseNormalizedProviderRecord({ - ...fixture, - source: { - ...fixture.source, - providerScopeIdentifier: { - ...fixture.source.providerScopeIdentifier, - assurance: 'deployment-scoped-account-pseudonym', - }, - }, - }).ok, - ).toBe(false) - - const accountScoped = baseInput( - 'withings', - 'getmeas:11', - heartRateMeasurement, - ) - expect( - parseNormalizedProviderRecord({ - source: { - ...accountScoped.source, - providerScopeIdentifier: { - system: 'https://example.org/provider-key-spaces', - value: 'withings-global', - assurance: 'documented-global-key-space', - }, + }), + ).toMatchObject({ + ok: false, + issues: [ + { + code: 'mobile-input.value-shape-invalid', + path: ['rawVendorResponse'], }, - measurements: accountScoped.measurements, - }).ok, - ).toBe(false) + ], + }) }) - it('strictly parses the complete graph input and preserves identity strings verbatim', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const result = parseProviderMeasurementBundleInput({ - ...input, + it('preserves identity strings verbatim and refuses a mismatched source token', () => { + const spaced = record('withings', 'getmeas:11', heartRateMeasurement) + const result = parseNormalizedProviderRecord({ + ...spaced, source: { - ...input.source, - providerScopeIdentifier: { - ...input.source.providerScopeIdentifier, - value: ' pseudonym-with-significant-spaces ', - }, + ...spaced.source, + sourceNativeId: ' record-with-significant-spaces ', }, }) - expect(result.ok).toBe(true) - if (!result.ok) return - expect(result.value.source.providerScopeIdentifier.value).toBe( - ' pseudonym-with-significant-spaces ', + expect(result.ok && result.value.source.sourceNativeId).toBe( + ' record-with-significant-spaces ', ) expect( - buildProviderMeasurementBundle({ - ...input, - rawVendorResponse: { heart_rate: 64 }, - } as ProviderMeasurementBundleInput & { - rawVendorResponse: unknown - }).ok, - ).toBe(false) - expect( - buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, - rawVendorResponse: { heart_rate: 64 }, + parseNormalizedProviderRecord( + record('oura', 'daily_readiness', heartRateMeasurement), + ), + ).toMatchObject({ + ok: false, + issues: [ + { + code: 'mobile-input.unsupported-source-value', + path: ['measurements', 0, 'kind'], }, - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) - }) - - it('rejects an arbitrary adapter and a mismatched source token', () => { - const dataOrigin = { - sourceDeviceToken: 'oura', - name: 'Oura', - } + ], + }) expect( parseNormalizedProviderRecord({ + ...spaced, source: { - adapter: { kind: 'mobile', provider: 'oura' }, - providerScopeIdentifier: { - system: 'https://example.org/provider-key-spaces', - value: 'oura-document-uuid-global', - assurance: 'documented-global-key-space', - }, - sourceType: 'sleep', - sourceNativeId: 'sleep-1', - dataOrigin, + ...spaced.source, + adapter: { kind: 'mobile', provider: 'withings' }, }, - measurements: [ - { - kind: 'sleep-duration', - value: 7.4, - effective: { kind: 'period', start, end }, - }, - ], - }).ok, - ).toBe(false) - + }), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-shape-invalid' }], + }) expect( parseNormalizedProviderRecord({ - source: { - adapter: { kind: 'providers', provider: 'oura' }, - providerScopeIdentifier: { - system: 'https://example.org/provider-key-spaces', - value: 'oura-document-uuid-global', - assurance: 'documented-global-key-space', - }, - sourceType: 'daily_readiness', - sourceNativeId: 'readiness-1', - dataOrigin, - }, - measurements: [heartRateMeasurement], - }).ok, - ).toBe(false) + ...spaced, + source: { ...spaced.source, sourceType: 'invented' }, + }), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.unsupported-source-type' }], + }) }) it('correlates provider, source token, and measurement at compile time', () => { const withingsSource = { adapter: { kind: 'providers', provider: 'withings' }, - providerScopeIdentifier: { - system: uri('https://example.org/deployments/provider-accounts'), - value: 'pseudonym-withings-001', - assurance: 'deployment-scoped-account-pseudonym', - }, sourceType: 'getmeas:11', sourceNativeId: 'heart-rate-1', - dataOrigin: { - sourceDeviceToken: 'withings', - name: 'Withings', - }, + writer: { sourceDeviceToken: 'withings', name: 'Withings' }, } as const const valid: ConnectedProviderRecord<'withings', 'getmeas:11'> = { source: withingsSource, @@ -386,9 +266,6 @@ describe('Provider-neutral normalization contract', () => { kind: 'blood-glucose', value: 95, effective: { kind: 'date-time', value: dateTime }, - specimen: { - identity: resourceIdentity('https://example.org/specimens', 'blood'), - }, } // @ts-expect-error Oura sleep admits sleep-duration, never glucose. const invalid: OuraSleep = { source, measurements: [invalidMeasurement] } @@ -421,39 +298,69 @@ describe('Provider-neutral normalization contract', () => { 'single', ) }) + + it('converts a batch under per-record contexts and keeps refusals beside conversions', () => { + const records = [ + record('withings', 'getmeas:11', heartRateMeasurement), + record('withings', 'getmeas:54', { + kind: 'oxygen-saturation', + value: 101, + effective: { kind: 'date-time', value: dateTime }, + }), + record('google-health-api', 'steps', { + kind: 'step-count', + value: 12, + effective: { kind: 'period', start, end: dateTime }, + }), + ] + let sequence = 0 + const batch = buildProviderExchangeGraphs(records, (candidate) => { + sequence += 1 + return context(candidate.source.adapter.provider, String(sequence)) + }) + expect(batch.conversions).toHaveLength(2) + expect(batch.failures).toHaveLength(1) + expect(batch.failures[0]?.record).toBe(records[1]) + expect(batch.failures[0]?.issues.map(({ code }) => code)).toEqual([ + 'mobile-input.value-outside-domain', + ]) + expect( + new Set(batch.conversions.map(({ graph }) => graph.identifier.value)) + .size, + ).toBe(2) + expect(() => + buildProviderExchangeGraphs(records, () => { + throw new Error('reservation failed') + }), + ).toThrow('reservation failed') + }) }) describe('Provider builder properties', () => { it('preserves every finite positive heart-rate value', () => { assert( property( - double({ - min: 0.01, - max: 400, - noNaN: true, - noDefaultInfinity: true, - }), + double({ min: 0.01, max: 400, noNaN: true, noDefaultInfinity: true }), (value) => { - const result = buildProviderMeasurementBundle( - baseInput('withings', 'getmeas:11', { + const result = buildProviderExchangeGraph( + record('withings', 'getmeas:11', { kind: 'heart-rate', value, effective: { kind: 'date-time', value: dateTime }, }), + context(), ) expect(result.ok).toBe(true) if (!result.ok) return - expect( - resources(result).find( - (resource) => resource.resourceType === 'Observation', - )?.valueQuantity?.value, - ).toBe(value) + expect(observationOf(result.value.graph).valueQuantity?.value).toBe( + value, + ) }, ), ) }) - it('rejects every oxygen saturation outside the percentage range', () => { + it('refuses every oxygen saturation outside the percentage range', () => { assert( property( oneof( @@ -462,14 +369,18 @@ describe('Provider builder properties', () => { ), (value) => { expect( - buildProviderMeasurementBundle( - baseInput('withings', 'getmeas:54', { + buildProviderExchangeGraph( + record('withings', 'getmeas:54', { kind: 'oxygen-saturation', value, effective: { kind: 'date-time', value: dateTime }, }), - ).ok, - ).toBe(false) + context(), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-outside-domain' }], + }) }, ), ) diff --git a/packages/fhir/test/provider-identity.test.ts b/packages/fhir/test/provider-identity.test.ts index 96da4ccf..27b01a42 100644 --- a/packages/fhir/test/provider-identity.test.ts +++ b/packages/fhir/test/provider-identity.test.ts @@ -8,97 +8,92 @@ import { application, - baseInput, bloodPressureMeasurement, + context, dailyEnd, - dateTime, - deploymentIdentity, - end, heartRateMeasurement, + identityScope, + observationOf, + record, resources, start, study, + unwrap, uri, } from './provider-test-support.js' import { parseUrnUuid } from '../src/core/index.js' import { - groveMobileContract, + groveMobileProfileCanonicals, sharedMobileMeasurementCatalog, } from '../src/mobile/index.js' import { - buildProviderMeasurementBundle, - parseProviderMeasurementBundleInput, - type ProviderMeasurementBundleInput, + buildProviderExchangeGraph, + parseNormalizedProviderRecord, + type NormalizedProviderRecord, + type ProviderConversionOptions, } from '../src/providers/index.js' +const heartRate = record('withings', 'getmeas:11', heartRateMeasurement) +const withSource = ( + base: NormalizedProviderRecord, + source: Record, +): NormalizedProviderRecord => + ({ + ...base, + source: { ...base.source, ...source }, + }) as NormalizedProviderRecord + describe('Provider R4 graph builder', () => { it('does not infer identity disclosure from unrelated substring collisions', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) expect( - buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, - sourceNativeId: '1', - }, - } as unknown as ProviderMeasurementBundleInput).ok, + buildProviderExchangeGraph( + withSource(heartRate, { sourceNativeId: '1' }), + context(), + ).ok, ).toBe(true) }) it('derives exact source, conversion and exchange identifiers in their proper namespaces', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const result = buildProviderMeasurementBundle(input) + const result = buildProviderExchangeGraph(heartRate, context()) expect(result.ok).toBe(true) if (!result.ok) return - - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', - ) - if (observation?.resourceType !== 'Observation') { - throw new Error('The graph did not contain its Observation.') - } + const observation = observationOf(result.value.graph) expect(observation.identifier?.map(({ system }) => system)).toEqual([ - deploymentIdentity.opaqueIdentifierSystems['provider-record'], - ( - deploymentIdentity.opaqueIdentifierSystems as Readonly< - Record - > - )['provider-output'], + identityScope.systems.opaque['provider-record'], + identityScope.systems.opaque['provider-output'], ]) for (const identity of observation.identifier ?? []) { expect(identity.value).toMatch(/^v0:test-key:1:[A-Za-z0-9_-]{43}$/u) } - expect(result.value.identifier.system).toBe( - deploymentIdentity.eventIdentifierSystem, + expect(result.value.graph.identifier.system).toBe( + identityScope.systems.event, ) - const provenanceEntry = result.value.entry.find( - (entry) => entry.resource.resourceType === 'Provenance', + expect(result.value.identifiers.event).toEqual(context().event) + expect(result.value.identifiers.provenance.system).toBe( + identityScope.systems.entryNode, ) - expect(provenanceEntry?.extension?.[0]?.valueIdentifier?.system).toBe( - deploymentIdentity.entryNodeIdentifierSystem, - ) - const serialized = JSON.stringify(result.value) - expect(serialized).not.toContain(input.source.sourceNativeId) - expect(serialized).not.toContain(input.source.providerScopeIdentifier.value) + expect(result.value.identifiers.outputs).toHaveLength(1) + expect(result.value.identifiers.writerSnapshot.role).toBe('device-snapshot') + expect(result.value.source).toEqual(heartRate.source) + const serialized = JSON.stringify(result.value.graph) + expect(serialized).not.toContain(heartRate.source.sourceNativeId) + expect(serialized).not.toContain(context().repositoryScope.value) }) it('places an explicitly governed native Identifier on exactly the designated primary Observation', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) const nativeSystem = uri( 'https://example.org/repositories/withings-account-7/heart-rate-records', ) - const result = buildProviderMeasurementBundle({ - ...input, + const result = buildProviderExchangeGraph(heartRate, context(), { nativeIdentifierDisclosure: { + kind: 'authorized', system: nativeSystem, - nativeId: input.source.sourceNativeId, type: { text: 'Withings account-scoped heart-rate id' }, }, }) expect(result.ok).toBe(true) if (!result.ok) return - - const carryingResources = result.value.entry.filter(({ resource }) => + const carrying = result.value.graph.entry.filter(({ resource }) => ( resource as { readonly identifier?: ReadonlyArray<{ @@ -108,13 +103,11 @@ describe('Provider R4 graph builder', () => { } ).identifier?.some( ({ system, value }) => - system === nativeSystem && value === input.source.sourceNativeId, + system === nativeSystem && value === heartRate.source.sourceNativeId, ), ) - expect(carryingResources).toHaveLength(1) - const primary = carryingResources[0]?.resource - expect(primary?.resourceType).toBe('Observation') - if (primary?.resourceType !== 'Observation') return + expect(carrying).toHaveLength(1) + const primary = observationOf(result.value.graph) expect(primary.code.coding?.[0]?.code).toBe( sharedMobileMeasurementCatalog['heart-rate'].code.code, ) @@ -122,13 +115,12 @@ describe('Provider R4 graph builder', () => { primary.identifier?.find(({ system }) => system === nativeSystem), ).toEqual({ system: nativeSystem, - value: input.source.sourceNativeId, + value: heartRate.source.sourceNativeId, type: { text: 'Withings account-scoped heart-rate id' }, }) }) it('emits a writer version only with its writer identity while allowing an unversioned identity', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) const writerRecord = { applicationIdentifier: { system: uri('https://example.org/applications'), @@ -136,121 +128,91 @@ describe('Provider R4 graph builder', () => { }, nativeRecordId: 'writer-record-1', } as const - const unversioned = buildProviderMeasurementBundle({ - ...input, - source: { ...input.source, writerRecord }, - } as ProviderMeasurementBundleInput) - const versioned = buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, + const unversioned = buildProviderExchangeGraph( + withSource(heartRate, { writerRecord }), + context(), + ) + const versioned = buildProviderExchangeGraph( + withSource(heartRate, { writerRecord: { ...writerRecord, version: '0' }, - }, - eventSequence: '2', - } as ProviderMeasurementBundleInput) + }), + context('withings', '2'), + ) expect(unversioned.ok && versioned.ok).toBe(true) if (!unversioned.ok || !versioned.ok) return - - const observation = (result: typeof unversioned) => - resources(result).find( - (resource) => resource.resourceType === 'Observation', - ) const writerVersionUrl = 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version' - const hasWriterIdentity = (resource: ReturnType) => - resource?.identifier?.some((candidate) => + const hasWriterIdentity = (graph: typeof unversioned.value.graph) => + observationOf(graph).identifier?.some((candidate) => candidate.type?.coding?.some(({ code }) => code === 'writer-record'), ) - - expect(hasWriterIdentity(observation(unversioned))).toBe(true) + expect(hasWriterIdentity(unversioned.value.graph)).toBe(true) + expect(unversioned.value.identifiers.writerRecord?.role).toBe( + 'writer-record', + ) expect( - observation(unversioned)?.extension?.some( + observationOf(unversioned.value.graph).extension?.some( ({ url }) => url === writerVersionUrl, ), ).not.toBe(true) - expect(hasWriterIdentity(observation(versioned))).toBe(true) - expect(observation(versioned)?.extension).toEqual( + expect(hasWriterIdentity(versioned.value.graph)).toBe(true) + expect(observationOf(versioned.value.graph).extension).toEqual( expect.arrayContaining([{ url: writerVersionUrl, valueString: '0' }]), ) expect( - parseProviderMeasurementBundleInput({ - ...input, - source: { ...input.source, writerRecordVersion: '1' }, - }).ok, - ).toBe(false) + parseNormalizedProviderRecord( + withSource(heartRate, { writerRecordVersion: '1' }), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-shape-invalid' }], + }) }) it('treats one supported grouped catalog mapping as one designated Provider output', () => { - const input = baseInput( - 'withings', - 'getmeas:9+10', - bloodPressureMeasurement, - ) const nativeSystem = uri( 'https://example.org/repositories/withings-account-7/blood-pressure-records', ) - const result = buildProviderMeasurementBundle({ - ...input, - nativeIdentifierDisclosure: { - system: nativeSystem, - nativeId: input.source.sourceNativeId, + const result = buildProviderExchangeGraph( + record('withings', 'getmeas:9+10', bloodPressureMeasurement), + context(), + { + nativeIdentifierDisclosure: { + kind: 'authorized', + system: nativeSystem, + }, }, - }) + ) expect(result.ok).toBe(true) if (!result.ok) return - const carrying = result.value.entry.filter(({ resource }) => { - const identifiers = ( - resource as { - readonly identifier?: ReadonlyArray<{ - readonly system?: string - readonly value?: string - }> - } - ).identifier - return identifiers?.some( - ({ system, value }) => - system === nativeSystem && value === input.source.sourceNativeId, - ) - }) - expect(carrying).toHaveLength(1) - expect(carrying[0]?.resource.resourceType).toBe('Observation') + expect( + observationOf(result.value.graph).identifier?.some( + ({ system }) => system === nativeSystem, + ), + ).toBe(true) }) it.each([ - { - name: 'a value different from the reconciliation source id', - disclosure: (input: ProviderMeasurementBundleInput) => ({ - system: uri('https://example.org/repositories/provider-records'), - nativeId: `${input.source.sourceNativeId}-wrong`, - }), - }, - { - name: 'a relative key-space system', - disclosure: (input: ProviderMeasurementBundleInput) => ({ - system: 'provider-records', - nativeId: input.source.sourceNativeId, - }), - }, - { - name: 'the Grove event identity system', - disclosure: (input: ProviderMeasurementBundleInput) => ({ - system: input.deploymentIdentity.eventIdentifierSystem, - nativeId: input.source.sourceNativeId, - }), - }, - { - name: 'a Grove opaque identity system', - disclosure: (input: ProviderMeasurementBundleInput) => ({ - system: - input.deploymentIdentity.opaqueIdentifierSystems['source-record'], - nativeId: input.source.sourceNativeId, - }), - }, - { - name: 'a Grove graph-role Identifier.type coding', - disclosure: (input: ProviderMeasurementBundleInput) => ({ - system: uri('https://example.org/repositories/provider-records'), - nativeId: input.source.sourceNativeId, + [ + 'a relative key-space system', + { kind: 'authorized', system: 'provider-records' }, + ], + [ + 'the Grove event identity system', + { kind: 'authorized', system: identityScope.systems.event }, + ], + [ + 'a Grove opaque identity system', + { + kind: 'authorized', + system: identityScope.systems.opaque['source-record'], + }, + ], + [ + 'a Grove graph-role Identifier.type coding', + { + kind: 'authorized', + system: 'https://example.org/repositories/provider-records', type: { coding: [ { @@ -260,196 +222,138 @@ describe('Provider R4 graph builder', () => { }, ], }, - }), - }, - ])('rejects governed source disclosure using $name', ({ disclosure }) => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const candidate = { - ...input, - nativeIdentifierDisclosure: disclosure(input), - } as unknown as ProviderMeasurementBundleInput - expect(parseProviderMeasurementBundleInput(candidate).ok).toBe(false) - expect(buildProviderMeasurementBundle(candidate).ok).toBe(false) - }) - - it('rejects native disclosure for a catalog-ambiguous multi-output Provider row', () => { - const input = baseInput('oura', 'daily_activity', { - kind: 'distance', - value: 6_123, - effective: { kind: 'period', start, end: dailyEnd }, - }) - const candidate = { - ...input, - nativeIdentifierDisclosure: { - system: uri( - 'https://example.org/repositories/oura-account-7/daily-activity-records', - ), - nativeId: input.source.sourceNativeId, }, - } as ProviderMeasurementBundleInput - const parsed = parseProviderMeasurementBundleInput(candidate) - expect(parsed.ok).toBe(false) - if (!parsed.ok) { - expect(parsed.issues).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - code: 'value-mismatch', - path: ['nativeIdentifierDisclosure'], - }), - ]), - ) - } - expect(buildProviderMeasurementBundle(candidate).ok).toBe(false) + ], + [ + 'an unknown policy kind', + { kind: 'disclose', system: 'https://example.org/x' }, + ], + ])('faults governed source disclosure using %s', (_name, disclosure) => { + const result = buildProviderExchangeGraph(heartRate, context(), { + nativeIdentifierDisclosure: disclosure, + } as unknown as ProviderConversionOptions) + expect(result.ok).toBe(false) + if (result.ok) return + for (const issue of result.issues) expect(issue.code).not.toMatch(/\./u) }) - it.each([ - ' leading', - 'trailing ', - 'two spaces', - 'tab\tcode', - 'control\u0001code', - ])('rejects non-lexical FHIR code %p in native Identifier.type', (code) => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - expect( - parseProviderMeasurementBundleInput({ - ...input, + it('faults native disclosure for a catalog-ambiguous multi-output Provider row', () => { + const result = buildProviderExchangeGraph( + record('oura', 'daily_activity', { + kind: 'distance', + value: 6_123, + effective: { kind: 'period', start, end: dailyEnd }, + }), + context('oura'), + { nativeIdentifierDisclosure: { - system: 'https://example.org/repositories/provider-records', - nativeId: input.source.sourceNativeId, - type: { - coding: [ - { - system: 'https://example.org/fhir/CodeSystem/identifier-type', - code, - }, - ], - }, + kind: 'authorized', + system: uri( + 'https://example.org/repositories/oura-account-7/daily-activity-records', + ), }, - }).ok, - ).toBe(false) - }) - - it('matches the frozen Provider composed source and output vectors', () => { - const input = baseInput('google-health-api', 'steps', { - kind: 'step-count', - value: 123, - effective: { kind: 'period', start, end }, - }) - const result = buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, - providerScopeIdentifier: { - system: uri('https://provider.example.org/accounts'), - value: 'account-001', - assurance: 'deployment-scoped-account-pseudonym', - }, - sourceNativeId: 'steps-2026-08-20T16:00:00Z', }, - } as ProviderMeasurementBundleInput) - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', - ) - expect(observation?.identifier).toHaveLength(2) - expect(observation?.identifier?.map((entry) => entry.value)).toEqual( - expect.arrayContaining([ - expect.stringMatching(/^v0:test-key:1:[A-Za-z0-9_-]{43}$/u), - ]), ) - - const unicodeInput = baseInput('oura', 'sleep', { - kind: 'sleep-duration', - value: 7, - effective: { kind: 'period', start, end }, + expect(result).toMatchObject({ + ok: false, + issues: [ + { code: 'value-mismatch', path: ['nativeIdentifierDisclosure'] }, + ], }) - const unicodeResult = buildProviderMeasurementBundle({ - ...unicodeInput, - source: { - ...unicodeInput.source, - providerScopeIdentifier: { - system: uri('https://example.org/provider-key-spaces'), - value: 'oura-document-uuid-global', - assurance: 'documented-global-key-space', - }, - sourceNativeId: 'résumé-\u0001', - }, - } as ProviderMeasurementBundleInput) - const unicodeObservation = resources(unicodeResult).find( - (resource) => resource.resourceType === 'Observation', - ) - expect(unicodeObservation?.identifier?.[0]?.value).toMatch( - /^v0:test-key:1:[A-Za-z0-9_-]{43}$/u, - ) - expect(JSON.stringify(unicodeResult)).not.toContain('résumé-\u0001') }) + it.each([' leading', 'trailing ', 'two spaces', 'tab\tcode', 'controlcode'])( + 'rejects non-lexical FHIR code %p in native Identifier.type', + (code) => { + expect( + buildProviderExchangeGraph(heartRate, context(), { + nativeIdentifierDisclosure: { + kind: 'authorized', + system: uri('https://example.org/repositories/provider-records'), + type: { + coding: [ + { + system: uri( + 'https://example.org/fhir/CodeSystem/identifier-type', + ), + code, + }, + ], + }, + }, + }).ok, + ).toBe(false) + }, + ) + it('changes only event identities when the durable event sequence changes', () => { - const first = buildProviderMeasurementBundle( - baseInput('withings', 'getmeas:11', heartRateMeasurement), + const first = buildProviderExchangeGraph(heartRate, context()) + const second = buildProviderExchangeGraph( + heartRate, + context('withings', '2'), ) - const second = buildProviderMeasurementBundle({ - ...baseInput('withings', 'getmeas:11', heartRateMeasurement), - eventSequence: '2', - }) expect(first.ok && second.ok).toBe(true) if (!first.ok || !second.ok) return - - const firstObservation = resources(first).find( - (resource) => resource.resourceType === 'Observation', + expect(observationOf(first.value.graph).identifier).toEqual( + observationOf(second.value.graph).identifier, + ) + expect(first.value.graph.identifier).not.toEqual( + second.value.graph.identifier, ) - const secondObservation = resources(second).find( - (resource) => resource.resourceType === 'Observation', + expect(first.value.identifiers.applicationSnapshot).not.toEqual( + second.value.identifiers.applicationSnapshot, ) - expect(firstObservation?.identifier).toEqual(secondObservation?.identifier) - expect(first.value.identifier).not.toEqual(second.value.identifier) }) - it('adds a gateway reference only with explicit mediation evidence', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const converterGateway = buildProviderMeasurementBundle({ - ...input, - gatewayApplication: { - kind: 'converter-application', - roleAssurance: 'mediated-or-routed-measurement', - }, - }) - expect(converterGateway.ok).toBe(true) - if (!converterGateway.ok) return - expect(converterGateway.value.entry).toHaveLength(4) - const converterObservation = resources(converterGateway).find( - (resource) => resource.resourceType === 'Observation', + it('adds a gateway reference only for an explicit converter role', () => { + const assembler = unwrap(buildProviderExchangeGraph(heartRate, context())) + expect( + observationOf(assembler.graph).extension?.some(({ url }) => + url?.endsWith('observation-gatewayDevice'), + ), + ).toBe(false) + expect(assembler.graph.entry).toHaveLength(5) + + const gateway = unwrap( + buildProviderExchangeGraph( + heartRate, + context('withings', '1', { converterRole: { kind: 'gateway' } }), + ), ) - const converterApplicationEntry = converterGateway.value.entry.find( - (entry) => - entry.resource.resourceType === 'Device' && - entry.resource.deviceName?.[0]?.name === application.name, + expect(gateway.graph.entry).toHaveLength(5) + const converterEntry = gateway.graph.entry.find( + ({ resource }) => + resource.resourceType === 'Device' && + resource.deviceName?.[0]?.name === application.name, ) expect( - converterObservation?.extension?.find( - ({ url }) => - url === - 'http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice', + observationOf(gateway.graph).extension?.find(({ url }) => + url?.endsWith('observation-gatewayDevice'), )?.valueReference?.reference, - ).toBe(converterApplicationEntry?.fullUrl) + ).toBe(converterEntry?.fullUrl) expect( - buildProviderMeasurementBundle({ - ...input, - gatewayApplication: { - kind: 'converter-application', - roleAssurance: 'conversion-only', - }, - } as unknown as ProviderMeasurementBundleInput).ok, + buildProviderExchangeGraph( + heartRate, + context('withings', '1', { + converterRole: { kind: 'gateway', roleAssurance: 'x' } as never, + }), + ).ok, ).toBe(false) }) - it('builds a fully attributed graph with distinct gateway and authorized hardware identity', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const result = buildProviderMeasurementBundle({ - ...input, - application: { - ...input.application, - manufacturer: 'Grove Alliance', + it('builds a fully attributed graph with a distinct gateway, hardware and a study', () => { + const result = buildProviderExchangeGraph( + withSource(heartRate, { + writer: { ...heartRate.source.writer, version: '3.0.0' }, + recordingDevice: { + stableUnitToken: 'hardware-1', + name: 'Chest strap', + manufacturer: 'Recorder vendor', + modelNumber: 'Model 1', + }, + }), + context('withings', '1', { host: { sourceDeviceToken: 'converter-host', name: 'Converter phone', @@ -457,115 +361,95 @@ describe('Provider R4 graph builder', () => { modelNumber: 'Phone 1', operatingSystemVersion: '20.0', }, - }, - gatewayApplication: { - kind: 'distinct-application', - roleAssurance: 'mediated-or-routed-measurement', - application: { - sourceDeviceToken: 'gateway-app', - name: 'Gateway app', - version: '2.1.0', - manufacturer: 'Gateway vendor', - host: { - sourceDeviceToken: 'gateway-host', - name: 'Gateway server', - operatingSystemVersion: 'Linux 7', - }, - }, - }, - source: { - ...input.source, - dataOrigin: { - ...input.source.dataOrigin, - version: '3.0.0', - manufacturer: 'Source vendor', - host: { - sourceDeviceToken: 'source-host', - name: 'Source host', - operatingSystemVersion: 'Provider runtime 3', - }, - }, - recordingDevice: { - stableUnitToken: 'hardware-1', - subjectIdentifier: { - system: uri('https://example.org/participants'), - value: 'participant-pseudonym-001', + converterRole: { + kind: 'gateway-application', + application: { + sourceDeviceToken: 'gateway-app', + name: 'Gateway app', + version: '2.1.0', }, - identityScope: 'authorized-hardware', - disclosureAuthorization: 'authorized-for-exchange', - name: 'Chest strap', - manufacturer: 'Recorder vendor', - modelNumber: 'Model 1', }, - }, - researchStudyReferences: [study('study-1')], - } as ProviderMeasurementBundleInput) + studies: [study('study-1')], + }), + ) expect(result.ok).toBe(true) if (!result.ok) return - expect(result.value.entry).toHaveLength(9) - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', + expect( + resources(result.value.graph).map(({ resourceType }) => resourceType), + ).toEqual([ + 'Observation', + 'Device', + 'ResearchStudy', + 'PlanDefinition', + 'ResearchSubject', + 'Device', + 'Device', + 'Device', + 'Device', + 'Provenance', + ]) + const observation = observationOf(result.value.graph) + expect(parseUrnUuid(observation.device?.reference).ok).toBe(true) + expect(observation.device?.reference).toBe( + result.value.graph.entry.find( + ({ resource }) => + resource.resourceType === 'Device' && + resource.meta?.profile?.[0] === + groveMobileProfileCanonicals['grove-recording-device'], + )?.fullUrl, + ) + const gatewayEntry = result.value.graph.entry.find( + ({ resource }) => + resource.resourceType === 'Device' && + resource.deviceName?.[0]?.name === 'Gateway app', ) - expect(parseUrnUuid(observation?.device?.reference).ok).toBe(true) expect( - observation?.extension?.some( - ({ url }) => - url === - 'http://hl7.org/fhir/StructureDefinition/workflow-researchStudy', - ), - ).toBe(true) - const hostEntries = result.value.entry.filter( + observation.extension?.find(({ url }) => + url?.endsWith('observation-gatewayDevice'), + )?.valueReference?.reference, + ).toBe(gatewayEntry?.fullUrl) + expect(result.value.identifiers.gatewayApplicationSnapshot?.value).toBe( + gatewayEntry?.extension?.[0]?.valueIdentifier?.value, + ) + const hostEntries = result.value.graph.entry.filter( ({ resource }) => resource.resourceType === 'Device' && resource.meta?.profile?.some( - (profile) => profile === groveMobileContract.profiles.hostDevice, - ), - ) - expect(hostEntries).toHaveLength(3) - for (const applicationDevice of resources(result)) { - if ( - applicationDevice.resourceType !== 'Device' || - !applicationDevice.meta?.profile?.some( (profile) => - profile === groveMobileContract.profiles.applicationDevice, - ) - ) { - continue - } - expect(parseUrnUuid(applicationDevice.parent?.reference).ok).toBe(true) - expect( - hostEntries.some( - ({ fullUrl }) => fullUrl === applicationDevice.parent?.reference, + profile === groveMobileProfileCanonicals['grove-host-device'], ), - ).toBe(true) - } + ) + expect(hostEntries).toHaveLength(1) + const converter = result.value.graph.entry.find( + ({ resource }) => + resource.resourceType === 'Device' && + resource.deviceName?.[0]?.name === application.name, + )?.resource + expect( + converter?.resourceType === 'Device' && converter.parent?.reference, + ).toBe(hostEntries[0]?.fullUrl) + expect(result.value.identifiers.hostSnapshot.value).toBe( + hostEntries[0]?.extension?.[0]?.valueIdentifier?.value, + ) }) - it('requires an explicit privacy scope for recording-device identity and never accepts a serial number', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const result = buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, + it('requires a stable per-unit token for recording-device identity and never accepts a serial number', () => { + const result = buildProviderExchangeGraph( + withSource(heartRate, { recordingDevice: { stableUnitToken: 'device-pseudonym-1', - subjectIdentifier: { - system: uri('https://example.org/participants'), - value: 'participant-pseudonym-001', - }, - identityScope: 'deployment-scoped', name: 'Connected scale', }, - }, - } as ProviderMeasurementBundleInput) + }), + context(), + ) expect(result.ok).toBe(true) if (!result.ok) return - const recordingDevice = resources(result).find( + const recordingDevice = resources(result.value.graph).find( (resource) => resource.resourceType === 'Device' && resource.deviceName?.[0]?.name === 'Connected scale', ) - expect(recordingDevice).toBeDefined() if (recordingDevice?.resourceType !== 'Device') { throw new Error('The graph did not contain its recording Device.') } @@ -573,100 +457,87 @@ describe('Provider R4 graph builder', () => { expect( recordingDevice.identifier?.map(({ type }) => type?.coding?.[0]?.code), ).toEqual(['recording-device', 'device-snapshot']) - const recordingEntry = result.value.entry.find( - ({ resource }) => resource === recordingDevice, + expect(result.value.identifiers.recordingDevice?.role).toBe( + 'recording-device', + ) + expect(result.value.identifiers.recordingDeviceSnapshot?.value).toBe( + recordingDevice.identifier?.[1]?.value, ) expect( - recordingEntry?.extension?.[0]?.valueIdentifier?.type?.coding?.[0]?.code, - ).toBe('device-snapshot') - - expect( - buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, + parseNormalizedProviderRecord( + withSource(heartRate, { recordingDevice: { stableUnitToken: 'global-hardware-id', - subjectIdentifier: { - system: uri('https://example.org/participants'), - value: 'example', - }, - identityScope: 'authorized-hardware', serialNumber: 'SERIAL-123', }, - }, - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) + }), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-shape-invalid' }], + }) + expect( + parseNormalizedProviderRecord( + withSource(heartRate, { recordingDevice: { name: 'No token' } }), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.required-metadata-missing' }], + }) }) - it('rejects arbitrary source codings instead of treating lineage as a clinical code', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + it('refuses arbitrary source codings instead of treating lineage as a clinical code', () => { expect( - buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, + parseNormalizedProviderRecord( + withSource(heartRate, { sourceTypeCoding: { system: 'https://provider.example/source-types', code: 'broad-summary-record', }, - }, - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) + }), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-shape-invalid' }], + }) }) - it('accepts only unique, unambiguous typed ResearchStudy references', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const local = study('local-1') - const absolute = study('remote-1') + it('accepts only unique studies with absolute protocol urls', () => { expect( - buildProviderMeasurementBundle({ - ...input, - researchStudyReferences: [local, absolute], - }).ok, + buildProviderExchangeGraph( + heartRate, + context('withings', '1', { + studies: [study('local-1'), study('remote-1')], + }), + ).ok, ).toBe(true) - - const duplicate = parseProviderMeasurementBundleInput({ - ...input, - researchStudyReferences: [local, local], - }) - expect(duplicate.ok).toBe(false) - if (!duplicate.ok) { - expect(duplicate.issues).toEqual( - expect.arrayContaining([ - expect.objectContaining({ code: 'duplicate-identifier' }), - ]), + const duplicated = buildProviderExchangeGraph( + heartRate, + context('withings', '1', { + studies: [study('local-1'), study('local-1')], + }), + ) + expect(duplicated.ok).toBe(false) + if (!duplicated.ok) { + expect(duplicated.issues.map(({ code }) => code)).toContain( + 'duplicate-identifier', ) } expect( - parseProviderMeasurementBundleInput({ - ...input, - researchStudyReferences: [ - { - type: 'ResearchStudy', - identifier: { - system: 'https://research.example/invalid system', - value: 'remote-1', - }, - }, - ], - }).ok, - ).toBe(false) - expect( - parseProviderMeasurementBundleInput({ - ...baseInput('withings', 'getmeas:54', { - kind: 'oxygen-saturation', - value: 98, - effective: { kind: 'date-time', value: dateTime }, + buildProviderExchangeGraph( + heartRate, + context('withings', '1', { + studies: [ + { + ...study('remote-1'), + protocol: { + url: 'https://research.example/invalid protocol', + version: '1', + }, + } as never, + ], }), - measurements: [ - { - kind: 'oxygen-saturation', - value: 0, - effective: { kind: 'date-time', value: dateTime }, - }, - ], - }).ok, - ).toBe(true) + ).ok, + ).toBe(false) }) }) diff --git a/packages/fhir/test/provider-mapping.test.ts b/packages/fhir/test/provider-mapping.test.ts index 59678b7b..f683e5cf 100644 --- a/packages/fhir/test/provider-mapping.test.ts +++ b/packages/fhir/test/provider-mapping.test.ts @@ -7,32 +7,38 @@ // import { - baseInput, bloodPressureMeasurement, + context, dailyEnd, dateTime, end, instant, mutableRecord, + observationOf, + record, resources, scalarCases, start, unwrap, } from './provider-test-support.js' -import { parseFhirId, parseGroveMobileExchangeBundle } from '../src/index.js' +import { + parseFhirId, + parseExchangeGraph, + type ExchangeGraph, +} from '../src/index.js' import { sharedMobileMeasurementCatalog, type MobileMeasurement, } from '../src/mobile/index.js' import { adapterMeasurementCatalog, - buildProviderMeasurementBundle, + buildProviderExchangeGraph, providerAdapterCatalog, providerRecordEffectiveRules, providerScalarOutputDiscriminators, providerScalarOutputRoles, type ConnectedProvider, - type ProviderMeasurementBundleInput, + type NormalizedProviderRecord, } from '../src/providers/index.js' interface ProviderContractRow { @@ -50,29 +56,43 @@ const providerContractRow = (provider: ConnectedProvider) => { return row } +const observationMutator = + (built: ExchangeGraph) => + (mutate: (observation: Record) => void) => { + const bundle = structuredClone(built) + mutate(observationOf(bundle)) + return parseExchangeGraph(bundle) + } + +const withMeasurements = ( + base: NormalizedProviderRecord, + measurements: readonly unknown[], +): NormalizedProviderRecord => + ({ ...base, measurements }) as NormalizedProviderRecord + describe('Provider R4 graph builder', () => { it.each(scalarCases)( 'builds the admitted $provider/$sourceType $measurement.kind graph', ({ provider, sourceType, measurement }) => { - const result = buildProviderMeasurementBundle( - baseInput(provider, sourceType, measurement), + const result = buildProviderExchangeGraph( + record(provider, sourceType, measurement), + context(provider), ) expect(result.ok).toBe(true) if (!result.ok) return - expect(result.value.type).toBe('collection') - expect(result.value.entry).toHaveLength(4) - expect('total' in result.value).toBe(false) - expect(Object.isFrozen(result.value)).toBe(true) + expect(result.value.graph.type).toBe('collection') + expect(result.value.graph.entry).toHaveLength(5) + expect('total' in result.value.graph).toBe(false) + expect(Object.isFrozen(result.value.graph)).toBe(true) + expect(result.value.warnings).toEqual([]) - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', - ) - expect(observation?.meta?.profile).toEqual([ + const observation = observationOf(result.value.graph) + expect(observation.meta?.profile).toEqual([ `https://grovealliance.org/fhir/mobile/StructureDefinition/${sharedMobileMeasurementCatalog[measurement.kind].profile}`, providerContractRow(provider).observationProfile, ]) - expect(observation?.extension).toEqual( + expect(observation.extension).toEqual( expect.arrayContaining([ { url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider', @@ -85,14 +105,16 @@ describe('Provider R4 graph builder', () => { ]), ) expect( - observation?.extension?.some( - ({ url }) => - url === - 'http://hl7.org/fhir/StructureDefinition/observation-gatewayDevice', + observation.extension?.some(({ url }) => + url?.endsWith('observation-gatewayDevice'), ), ).toBe(false) - expect(observation?.code.coding).toHaveLength(1) - expect(observation?.id).toBeUndefined() + expect(observation.code.coding).toHaveLength(1) + expect(observation.id).toBeUndefined() + expect(observation.subject).toEqual({ + type: 'Patient', + identifier: context().subject.identifier, + }) }, ) @@ -124,26 +146,25 @@ describe('Provider R4 graph builder', () => { ] as const)( 'emits $provider platform-exclusive age under its exact semantic and vendor profiles', ({ provider, sourceType, measurement, semanticProfile, code }) => { - const result = buildProviderMeasurementBundle( - baseInput( + const result = buildProviderExchangeGraph( + record( provider, sourceType, measurement as unknown as MobileMeasurement, ), + context(provider), ) expect(result.ok).toBe(true) if (!result.ok) return - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', - ) - expect(observation?.meta?.profile).toEqual([ + const observation = observationOf(result.value.graph) + expect(observation.meta?.profile).toEqual([ semanticProfile, providerContractRow(provider).observationProfile, ]) - expect(observation?.code.coding).toEqual([ + expect(observation.code.coding).toEqual([ expect.objectContaining({ code }), ]) - expect(observation?.valueQuantity).toEqual( + expect(observation.valueQuantity).toEqual( expect.objectContaining({ system: 'http://unitsofmeasure.org', code: 'a', @@ -153,30 +174,17 @@ describe('Provider R4 graph builder', () => { ) it('rejects generic-parent, cross-vendor, cross-marker, and cross-owner Provider claims', () => { - const built = buildProviderMeasurementBundle( - baseInput('withings', 'getmeas:11', { - kind: 'heart-rate', - value: 64, - effective: { kind: 'date-time', value: dateTime }, - }), - ) - expect(built.ok).toBe(true) - if (!built.ok) return - - const mutateObservation = ( - mutate: (observation: Record) => void, - ) => { - const bundle = structuredClone(built.value) - const observation = bundle.entry.find( - ({ resource }) => resource.resourceType === 'Observation', - )?.resource - if (observation?.resourceType !== 'Observation') { - throw new Error('Expected a Provider Observation.') - } - mutate(observation) - return parseGroveMobileExchangeBundle(bundle) - } - + const built = unwrap( + buildProviderExchangeGraph( + record('withings', 'getmeas:11', { + kind: 'heart-rate', + value: 64, + effective: { kind: 'date-time', value: dateTime }, + }), + context(), + ), + ).graph + const mutateObservation = observationMutator(built) expect( mutateObservation((observation) => { mutableRecord(observation.meta, 'Observation.meta').profile = [ @@ -205,24 +213,19 @@ describe('Provider R4 graph builder', () => { mutableRecord(marker, 'Provider marker').valueCode = 'oura' }).ok, ).toBe(false) - - const crossOwner = baseInput('oura', 'daily_cardiovascular_age', { - kind: 'oura-cardiovascular-age', - value: 38, - effective: { kind: 'period', start, end: dailyEnd }, - } as unknown as MobileMeasurement) expect( - buildProviderMeasurementBundle({ - ...crossOwner, - measurements: [ - { - kind: 'withings-vascular-age', - value: 45, - effective: { kind: 'date-time', value: dateTime }, - }, - ], - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) + buildProviderExchangeGraph( + record('oura', 'daily_cardiovascular_age', { + kind: 'withings-vascular-age', + value: 45, + effective: { kind: 'date-time', value: dateTime }, + } as unknown as MobileMeasurement), + context('oura'), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.unsupported-source-value' }], + }) }) it('builds every exact provider/source/measurement mapping in the generated catalog', () => { @@ -283,7 +286,6 @@ describe('Provider R4 graph builder', () => { for (const [sourceType, mappings] of Object.entries(sourceMappings)) { const discriminatorMappings = exhaustiveDiscriminators[provider as ConnectedProvider][sourceType] - expect(discriminatorMappings).toBeDefined() if (discriminatorMappings === undefined) { throw new Error( `Missing output discriminators for ${provider}/${sourceType}.`, @@ -310,12 +312,13 @@ describe('Provider R4 graph builder', () => { } ) expect( - buildProviderMeasurementBundle( - baseInput( + buildProviderExchangeGraph( + record( provider as ConnectedProvider, sourceType, sourceMeasurement as MobileMeasurement, ), + context(provider as ConnectedProvider), ).ok, ).toBe(true) checked += 1 @@ -327,51 +330,40 @@ describe('Provider R4 graph builder', () => { it('does not invent uncatalogued physiologic or duration lower bounds', () => { expect( - buildProviderMeasurementBundle( - baseInput('google-health-api', 'active-energy-burned', { + buildProviderExchangeGraph( + record('google-health-api', 'active-energy-burned', { kind: 'active-energy', value: 0, effective: { kind: 'period', start, end: start }, }), + context('google-health-api'), ).ok, ).toBe(true) expect( - buildProviderMeasurementBundle( - baseInput('withings', 'getmeas:9+10', { + buildProviderExchangeGraph( + record('withings', 'getmeas:9+10', { kind: 'blood-pressure', systolic: 0, diastolic: 0, effective: { kind: 'date-time', value: dateTime }, }), + context(), ).ok, ).toBe(true) }) it('rejects negative and zero-duration step-count resources at the profiled graph boundary', () => { - const built = buildProviderMeasurementBundle( - baseInput('google-health-api', 'steps', { - kind: 'step-count', - value: 42, - effective: { kind: 'period', start, end }, - }), - ) - expect(built.ok).toBe(true) - if (!built.ok) return - - const mutateObservation = ( - mutate: (observation: Record) => void, - ) => { - const bundle = structuredClone(built.value) - const observation = bundle.entry.find( - ({ resource }) => resource.resourceType === 'Observation', - )?.resource - if (observation?.resourceType !== 'Observation') { - throw new Error('Expected a step-count Observation.') - } - mutate(observation) - return parseGroveMobileExchangeBundle(bundle) - } - + const built = unwrap( + buildProviderExchangeGraph( + record('google-health-api', 'steps', { + kind: 'step-count', + value: 42, + effective: { kind: 'period', start, end }, + }), + context('google-health-api'), + ), + ).graph + const mutateObservation = observationMutator(built) expect( mutateObservation((observation) => { mutableRecord( @@ -392,14 +384,13 @@ describe('Provider R4 graph builder', () => { }) it('atomically builds every present Oura daily-activity output with one complete Provenance target set', () => { - const dailyActivity = baseInput('oura', 'daily_activity', { + const base = record('oura', 'daily_activity', { kind: 'distance', value: 6_123, effective: { kind: 'period', start, end: dailyEnd }, }) - const result = buildProviderMeasurementBundle({ - ...dailyActivity, - measurements: [ + const result = buildProviderExchangeGraph( + withMeasurements(base, [ { kind: 'distance', value: 6_123, @@ -415,12 +406,12 @@ describe('Provider R4 graph builder', () => { value: 8_234, effective: { kind: 'period', start, end: dailyEnd }, }, - ], - } as ProviderMeasurementBundleInput) + ]), + context('oura'), + ) expect(result.ok).toBe(true) if (!result.ok) return - - const observations = result.value.entry.filter( + const observations = result.value.graph.entry.filter( ({ resource }) => resource.resourceType === 'Observation', ) expect(observations).toHaveLength(3) @@ -431,15 +422,21 @@ describe('Provider R4 graph builder', () => { 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-active-energy', 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-distance', ]) - const provenance = result.value.entry.find( - ({ resource }) => resource.resourceType === 'Provenance', - )?.resource - expect(provenance?.resourceType).toBe('Provenance') - if (provenance?.resourceType !== 'Provenance') return + expect(result.value.identifiers.outputs).toHaveLength(3) + const provenance = resources(result.value.graph).find( + (resource) => resource.resourceType === 'Provenance', + ) + if (provenance?.resourceType !== 'Provenance') + throw new Error('No Provenance.') expect(provenance.target).toHaveLength(observations.length) expect( new Set(provenance.target.map(({ reference }) => reference)), ).toEqual(new Set(observations.map(({ fullUrl }) => fullUrl))) + expect(provenance.occurredPeriod).toEqual( + observations[0]?.resource.resourceType === 'Observation' ? + observations[0].resource.effectivePeriod + : undefined, + ) }) it.each([ @@ -460,15 +457,17 @@ describe('Provider R4 graph builder', () => { effective: { kind: 'period', start, end: dailyEnd }, }, } as const - const input = baseInput('oura', 'daily_activity', candidates[kinds[0]]) - const result = buildProviderMeasurementBundle({ - ...input, - measurements: kinds.map((kind) => candidates[kind]), - } as unknown as ProviderMeasurementBundleInput) + const result = buildProviderExchangeGraph( + withMeasurements( + record('oura', 'daily_activity', candidates[kinds[0]]), + kinds.map((kind) => candidates[kind]), + ), + context('oura'), + ) expect(result.ok).toBe(true) if (!result.ok) return expect( - result.value.entry.filter( + result.value.graph.entry.filter( ({ resource }) => resource.resourceType === 'Observation', ), ).toHaveLength(kinds.length) @@ -476,28 +475,30 @@ describe('Provider R4 graph builder', () => { ) it('requires every catalogued daily source to use one shared complete civil-day Period', () => { - const input = baseInput('oura', 'daily_activity', { + const base = record('oura', 'daily_activity', { kind: 'step-count', value: 8_234, effective: { kind: 'period', start, end: dailyEnd }, }) expect( - buildProviderMeasurementBundle({ - ...input, - measurements: [ + buildProviderExchangeGraph( + withMeasurements(base, [ { kind: 'step-count', value: 8_234, effective: { kind: 'period', start, end }, }, - ], - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) + ]), + context('oura'), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.effective-period-invalid' }], + }) expect( - buildProviderMeasurementBundle({ - ...input, - measurements: [ - input.measurements[0], + buildProviderExchangeGraph( + withMeasurements(base, [ + base.measurements[0], { kind: 'distance', value: 6_123, @@ -507,87 +508,110 @@ describe('Provider R4 graph builder', () => { end: instant('2026-08-22T00:00:00Z'), }, }, - ], - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) + ]), + context('oura'), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.effective-period-invalid' }], + }) expect( - buildProviderMeasurementBundle( - baseInput('withings', 'getactivity:steps', { + buildProviderExchangeGraph( + record('withings', 'getactivity:steps', { kind: 'step-count', value: 8_234, effective: { kind: 'period', start, end }, }), + context(), ).ok, ).toBe(false) - const daylightSavingStart = instant('2026-11-01T00:00:00-07:00') - const daylightSavingEnd = instant('2026-11-02T00:00:00-08:00') expect( - buildProviderMeasurementBundle( - baseInput('withings', 'getactivity:steps', { + buildProviderExchangeGraph( + record('withings', 'getactivity:steps', { kind: 'step-count', value: 8_234, effective: { kind: 'period', - start: daylightSavingStart, - end: daylightSavingEnd, + start: instant('2026-11-01T00:00:00-07:00'), + end: instant('2026-11-02T00:00:00-08:00'), }, }), + context(), ).ok, ).toBe(true) }) - it('rejects duplicate, unsupported, invalid, and non-emitted output inputs before constructing a partial graph', () => { - const input = baseInput('oura', 'daily_activity', { + it('refuses duplicate, unsupported, invalid, and misplaced repository ids before constructing a partial graph', () => { + const base = record('oura', 'daily_activity', { kind: 'step-count', value: 8_234, effective: { kind: 'period', start, end: dailyEnd }, }) expect( - buildProviderMeasurementBundle({ - ...input, - measurements: [], - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) + buildProviderExchangeGraph(withMeasurements(base, []), context('oura')), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-shape-invalid' }], + }) expect( - buildProviderMeasurementBundle({ - ...input, - measurements: [input.measurements[0], input.measurements[0]], - } as ProviderMeasurementBundleInput).ok, - ).toBe(false) + buildProviderExchangeGraph( + withMeasurements(base, [base.measurements[0], base.measurements[0]]), + context('oura'), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-shape-invalid' }], + }) expect( - buildProviderMeasurementBundle({ - ...input, - measurements: [ - input.measurements[0], + buildProviderExchangeGraph( + withMeasurements(base, [ + base.measurements[0], { kind: 'body-weight', value: 72, effective: { kind: 'date-time', value: dateTime }, }, - ], - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) + ]), + context('oura'), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.unsupported-source-value' }], + }) expect( - buildProviderMeasurementBundle({ - ...input, - measurements: [ + buildProviderExchangeGraph( + withMeasurements(base, [ { kind: 'step-count', value: -1, effective: { kind: 'period', start, end: dailyEnd }, }, - ], - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) + ]), + context('oura'), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-outside-domain' }], + }) expect( - buildProviderMeasurementBundle({ - ...input, - repositoryIds: { - observations: { - distance: unwrap(parseFhirId('not-emitted')), + buildProviderExchangeGraph( + withMeasurements(base, [ + base.measurements[0], + { + kind: 'distance', + value: 6_123, + effective: { kind: 'period', start, end: dailyEnd }, }, - }, - }).ok, - ).toBe(false) + ]), + context('oura', '1', { + repositoryIds: { 'primary-output': unwrap(parseFhirId('not-sole')) }, + }), + ), + ).toMatchObject({ + ok: false, + issues: [ + { code: 'value-mismatch', path: ['repositoryIds', 'primary-output'] }, + ], + }) }) }) diff --git a/packages/fhir/test/provider-semantics.test.ts b/packages/fhir/test/provider-semantics.test.ts index d1bfd770..4a4a5025 100644 --- a/packages/fhir/test/provider-semantics.test.ts +++ b/packages/fhir/test/provider-semantics.test.ts @@ -8,14 +8,14 @@ import { application, - baseInput, bloodPressureMeasurement, + context, dateTime, - deploymentIdentity, - end, heartRateMeasurement, + identityScope, instant, - resourceIdentity, + observationOf, + record, resources, start, unwrap, @@ -31,11 +31,10 @@ import { } from '../src/mobile/index.js' import { deriveProviderIdentities } from '../src/providers/identity.js' import { - buildProviderMeasurementBundle, + buildProviderExchangeGraph, parseNormalizedProviderRecord, - parseProviderMeasurementBundleInput, providerOutputCoordinates, - type ProviderMeasurementBundleInput, + type NormalizedProviderRecord, } from '../src/providers/index.js' import { providerMeasurementDefinition, @@ -43,21 +42,42 @@ import { providerObservationProfile, } from '../src/providers/measurement-definition.js' +const heartRate = record('withings', 'getmeas:11', heartRateMeasurement) +const withSource = ( + base: NormalizedProviderRecord, + source: Record, +): NormalizedProviderRecord => + ({ + ...base, + source: { ...base.source, ...source }, + }) as NormalizedProviderRecord +const withMeasurement = ( + base: NormalizedProviderRecord, + measurement: unknown, +): NormalizedProviderRecord => + ({ ...base, measurements: [measurement] }) as NormalizedProviderRecord + describe('Provider R4 graph builder', () => { it('rounds Mobile effective instants before enforcing ordering and catalog-owned duration rules', () => { - const collapsed = baseInput('oura', 'sleep', { - kind: 'sleep-duration', - value: 0.1, - effective: { - kind: 'period', - start: instant('2026-08-20T12:00:00.0001Z'), - end: instant('2026-08-20T12:00:00.0002Z'), - }, - }) - expect(buildProviderMeasurementBundle(collapsed).ok).toBe(true) + const sleep = (startValue: string, endValue: string) => + record('oura', 'sleep', { + kind: 'sleep-duration', + value: 0.1, + effective: { + kind: 'period', + start: instant(startValue), + end: instant(endValue), + }, + }) expect( - buildProviderMeasurementBundle( - baseInput('google-health-api', 'steps', { + buildProviderExchangeGraph( + sleep('2026-08-20T12:00:00.0001Z', '2026-08-20T12:00:00.0002Z'), + context('oura'), + ).ok, + ).toBe(true) + expect( + buildProviderExchangeGraph( + record('google-health-api', 'steps', { kind: 'step-count', value: 1, effective: { @@ -66,70 +86,51 @@ describe('Provider R4 graph builder', () => { end: instant('2026-08-20T12:00:00.0002Z'), }, }), - ).ok, - ).toBe(false) - - const input = baseInput('oura', 'sleep', { - kind: 'sleep-duration', - value: 0.1, - effective: { - kind: 'period', - start: instant('2026-08-20T12:00:00.0004Z'), - end: instant('2026-08-20T12:00:00.0006Z'), - }, + context('google-health-api'), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.effective-period-invalid' }], }) - const result = buildProviderMeasurementBundle(input) - expect(result.ok).toBe(true) - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', + + const result = buildProviderExchangeGraph( + sleep('2026-08-20T12:00:00.0004Z', '2026-08-20T12:00:00.0006Z'), + context('oura'), ) - expect(observation?.effectivePeriod).toEqual({ + expect(result.ok).toBe(true) + if (!result.ok) return + expect(observationOf(result.value.graph).effectivePeriod).toEqual({ start: '2026-08-20T12:00:00.000Z', end: '2026-08-20T12:00:00.001Z', }) expect( - buildProviderMeasurementBundle({ - ...input, - measurements: [ - { - ...input.measurements[0], - effective: { - kind: 'period', - start: instant('2026-08-20T12:00:00.0006Z'), - end: instant('2026-08-20T12:00:00.0004Z'), - }, - }, - ], - } as ProviderMeasurementBundleInput).ok, - ).toBe(false) + buildProviderExchangeGraph( + sleep('2026-08-20T12:00:00.0006Z', '2026-08-20T12:00:00.0004Z'), + context('oura'), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.effective-period-invalid' }], + }) }) - it('requires identifier-only typed logical subjects', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - expect( - parseProviderMeasurementBundleInput({ - ...input, - subject: 'Patient/example', - }).ok, - ).toBe(false) - expect( - parseProviderMeasurementBundleInput({ - ...input, - subject: { ...input.subject, reference: 'Patient/example' }, - }).ok, - ).toBe(false) - expect( - parseProviderMeasurementBundleInput({ - ...input, - subject: { - type: 'Patient', - identifier: { - system: 'https://example.org/patient-pseudonyms', - value: 'patient-example', - }, - }, - }).ok, - ).toBe(false) + it('faults a subject that is neither logical nor bundled', () => { + for (const subject of [ + 'Patient/example', + { + kind: 'logical', + identifier: context().subject.identifier, + reference: 'Patient/x', + }, + { type: 'Patient', identifier: context().subject.identifier }, + ]) { + const result = buildProviderExchangeGraph( + heartRate, + context('withings', '1', { subject: subject as never }), + ) + expect(result.ok).toBe(false) + if (!result.ok) expect(result.issues[0]?.path[0]).toBe('subject') + } }) it.each(mobileEffectiveCanonicalizationVectors)( @@ -142,48 +143,40 @@ describe('Provider R4 graph builder', () => { }, ) - it('fails closed for invalid input and a rounding carry outside FHIR four-digit years', () => { - expect(canonicalizeMobileEffectiveInstant('not-an-instant').ok).toBe(false) - expect( - canonicalizeMobileEffectiveInstant('9999-12-31T23:59:59.9996Z').ok, - ).toBe(false) - }) - it('serializes a canonical effectiveDateTime while preserving its source offset', () => { - const input = baseInput('withings', 'getmeas:11', { + const input = withMeasurement(heartRate, { ...heartRateMeasurement, effective: { kind: 'date-time', value: instant('2026-08-20T08:30:00.251500001-07:00'), }, }) - const parsed = parseProviderMeasurementBundleInput(input) + const parsed = parseNormalizedProviderRecord(input) expect(parsed.ok).toBe(true) if (!parsed.ok) return expect(parsed.value.measurements[0].effective).toEqual({ kind: 'date-time', value: '2026-08-20T08:30:00.252-07:00', }) - - const result = buildProviderMeasurementBundle(input) - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', + const result = unwrap(buildProviderExchangeGraph(input, context())) + expect(observationOf(result.graph).effectiveDateTime).toBe( + '2026-08-20T08:30:00.252-07:00', + ) + const provenance = resources(result.graph).find( + (r) => r.resourceType === 'Provenance', ) - expect(observation?.effectiveDateTime).toBe('2026-08-20T08:30:00.252-07:00') + if (provenance?.resourceType !== 'Provenance') + throw new Error('No Provenance.') + expect(provenance.occurredDateTime).toBe('2026-08-20T08:30:00.252-07:00') + expect(provenance.recorded).toBe(context().conversionInstant) + expect(result.graph.timestamp).toBe(context().conversionInstant) }) it('derives every urn:uuid edge from a complete business identifier', () => { - const result = buildProviderMeasurementBundle( - baseInput('withings', 'getmeas:11', heartRateMeasurement), - ) - expect(result.ok).toBe(true) - if (!result.ok) return - - for (const entry of result.value.entry) { + const result = unwrap(buildProviderExchangeGraph(heartRate, context())) + for (const entry of result.graph.entry) { expect(entry.fullUrl).toMatch(/^urn:uuid:/u) const businessIdentifier = entry.extension?.[0]?.valueIdentifier - expect(businessIdentifier?.system).toBeDefined() - expect(businessIdentifier?.value).toBeDefined() expect( deriveEntryFullUrl({ system: uri(businessIdentifier?.system ?? ''), @@ -194,70 +187,62 @@ describe('Provider R4 graph builder', () => { }) it('constructs one composite Withings blood-pressure panel', () => { - const result = buildProviderMeasurementBundle( - baseInput('withings', 'getmeas:9+10', bloodPressureMeasurement), - ) - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', + const result = unwrap( + buildProviderExchangeGraph( + record('withings', 'getmeas:9+10', bloodPressureMeasurement), + context(), + ), ) - expect(observation?.component).toHaveLength(2) - expect(observation?.valueQuantity).toBeUndefined() + const observation = observationOf(result.graph) + expect(observation.component).toHaveLength(2) + expect(observation.valueQuantity).toBeUndefined() }) - it('uses optional Resource.id values only when supplied by a repository', () => { - const result = buildProviderMeasurementBundle({ - ...baseInput('withings', 'getmeas:11', heartRateMeasurement), - repositoryIds: { - bundle: unwrap(parseFhirId('bundle-42')), - observations: { - 'heart-rate': unwrap(parseFhirId('observation-42')), + it('uses optional Resource.id values only when the context supplies them', () => { + const result = buildProviderExchangeGraph( + heartRate, + context('withings', '1', { + repositoryIds: { + bundle: unwrap(parseFhirId('bundle-42')), + 'primary-output': unwrap(parseFhirId('observation-42')), + provenance: unwrap(parseFhirId('provenance-42')), + 'application-device': unwrap(parseFhirId('application-42')), + 'recording-device': unwrap(parseFhirId('recording-device-42')), }, - provenance: unwrap(parseFhirId('provenance-42')), - }, - }) + }), + ) expect(result.ok).toBe(true) if (!result.ok) return - expect(result.value.id).toBe('bundle-42') + expect(result.value.graph.id).toBe('bundle-42') + expect(observationOf(result.value.graph).id).toBe('observation-42') + expect( + resources(result.value.graph).find((r) => r.resourceType === 'Provenance') + ?.id, + ).toBe('provenance-42') expect( - resources(result).find( - (resource) => resource.resourceType === 'Observation', + resources(result.value.graph).find( + (r) => + r.resourceType === 'Device' && + r.deviceName?.[0]?.name === application.name, )?.id, - ).toBe('observation-42') + ).toBe('application-42') }) it('omits optional attribution and device fields while preserving repository device ids', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const result = buildProviderMeasurementBundle({ - ...input, - application: { - ...application, - id: unwrap(parseFhirId('application-42')), - name: application.name, - }, - source: { - adapter: input.source.adapter, - providerScopeIdentifier: input.source.providerScopeIdentifier, - sourceType: input.source.sourceType, - sourceNativeId: input.source.sourceNativeId, - dataOrigin: input.source.dataOrigin, - recordingDevice: { - stableUnitToken: 'minimal-device', - subjectIdentifier: { - system: uri('https://example.org/participants'), - value: 'participant-pseudonym-001', - }, - id: unwrap(parseFhirId('recording-device-42')), - identityScope: 'deployment-scoped', + const result = buildProviderExchangeGraph( + withSource(heartRate, { + recordingMethod: undefined, + recordingDevice: { stableUnitToken: 'minimal-device' }, + }), + context('withings', '1', { + repositoryIds: { + 'recording-device': unwrap(parseFhirId('recording-device-42')), }, - }, - } as ProviderMeasurementBundleInput) + }), + ) expect(result.ok).toBe(true) if (!result.ok) return - - const observation = resources(result).find( - (resource) => resource.resourceType === 'Observation', - ) - expect(observation?.extension).toEqual( + expect(observationOf(result.value.graph).extension).toEqual( expect.arrayContaining([ { url: 'https://grovealliance.org/fhir/providers/StructureDefinition/provider-source-type', @@ -265,135 +250,149 @@ describe('Provider R4 graph builder', () => { }, ]), ) - const minimalDevice = resources(result).find( + const minimalDevice = resources(result.value.graph).find( (resource) => resource.id === 'recording-device-42', ) + expect(minimalDevice).toBeDefined() expect(minimalDevice).not.toHaveProperty('deviceName') expect(minimalDevice).not.toHaveProperty('manufacturer') expect(minimalDevice).not.toHaveProperty('modelNumber') }) - it('enforces only catalog-owned scalar value domains at the runtime boundary', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + it('refuses only catalog-owned scalar value domains at the record boundary', () => { expect( - parseProviderMeasurementBundleInput({ - ...input, - measurements: [ - { - ...heartRateMeasurement, - effective: { kind: 'date-time', value: 'not-an-instant' }, - }, - ], - }).ok, - ).toBe(false) + parseNormalizedProviderRecord( + withMeasurement(heartRate, { + ...heartRateMeasurement, + effective: { kind: 'date-time', value: 'not-an-instant' }, + }), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.effective-period-invalid' }], + }) expect( - parseProviderMeasurementBundleInput({ - ...input, - measurements: [{ ...heartRateMeasurement, value: 0 }], - }).ok, + parseNormalizedProviderRecord( + withMeasurement(heartRate, { ...heartRateMeasurement, value: 0 }), + ).ok, ).toBe(true) expect( - parseProviderMeasurementBundleInput({ - ...baseInput('google-health-api', 'steps', { - kind: 'step-count', - value: 1, - effective: { kind: 'period', start, end }, - }), - measurements: [ + parseNormalizedProviderRecord( + withMeasurement( + record('google-health-api', 'steps', heartRateMeasurement), { kind: 'step-count', value: 1.5, - effective: { kind: 'period', start, end }, + effective: { kind: 'period', start, end: dateTime }, }, - ], - }).ok, - ).toBe(false) + ), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-outside-domain' }], + }) expect( - parseProviderMeasurementBundleInput({ - ...baseInput('withings', 'getmeas:6', { + parseNormalizedProviderRecord( + withMeasurement(record('withings', 'getmeas:6', heartRateMeasurement), { kind: 'body-fat-percentage', - value: 0, + value: 100.1, effective: { kind: 'date-time', value: dateTime }, }), - measurements: [ - { - kind: 'body-fat-percentage', - value: 100.1, - effective: { kind: 'date-time', value: dateTime }, - }, - ], - }).ok, - ).toBe(false) + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-outside-domain' }], + }) expect( - parseProviderMeasurementBundleInput( - baseInput('withings', 'getmeas:6', { - kind: 'body-fat-percentage', - value: 0, - effective: { kind: 'date-time', value: dateTime }, + parseNormalizedProviderRecord( + withMeasurement(heartRate, { + ...heartRateMeasurement, + value: Number.POSITIVE_INFINITY, }), - ).ok, - ).toBe(true) + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-outside-domain' }], + }) expect( - parseProviderMeasurementBundleInput({ - ...baseInput('withings', 'getmeas:54', { - kind: 'oxygen-saturation', - value: 98, - effective: { kind: 'date-time', value: dateTime }, - }), - measurements: [ - { - kind: 'oxygen-saturation', - value: 101, - effective: { kind: 'date-time', value: dateTime }, - }, - ], - }).ok, - ).toBe(false) + parseNormalizedProviderRecord( + withMeasurement(heartRate, { ...heartRateMeasurement, value: '64' }), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-shape-invalid' }], + }) + expect( + parseNormalizedProviderRecord( + withSource(heartRate, { writer: { name: 'x' } }), + ), + ).toMatchObject({ + ok: false, + issues: [ + { + code: 'mobile-input.required-metadata-missing', + path: ['source', 'writer', 'sourceDeviceToken'], + }, + ], + }) + expect( + parseNormalizedProviderRecord( + withSource(heartRate, { recordingMethod: 'guessed' }), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.unsupported-source-value' }], + }) }) - it('rejects a source-native identity containing an isolated surrogate', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const invalid = { - ...input, - source: { ...input.source, sourceNativeId: 'invalid-\ud800' }, - } as ProviderMeasurementBundleInput - expect(parseProviderMeasurementBundleInput(invalid).ok).toBe(false) - expect(buildProviderMeasurementBundle(invalid).ok).toBe(false) + it('refuses a source-native identity containing an isolated surrogate', () => { + expect( + parseNormalizedProviderRecord( + withSource(heartRate, { sourceNativeId: 'invalid-\ud800' }), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.text-not-unicode-scalar' }], + }) }) - it('rejects Unicode IRIs wherever provider inputs require an absolute URI', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - expect( - parseProviderMeasurementBundleInput({ - ...input, - source: { - ...input.source, - providerScopeIdentifier: { - ...input.source.providerScopeIdentifier, - system: 'https://例え.example/provider-accounts', + it('rejects Unicode IRIs wherever inputs require an absolute URI', () => { + expect( + parseNormalizedProviderRecord( + withSource(heartRate, { + writerRecord: { + applicationIdentifier: { + system: 'https://例え.example/apps', + value: 'a', + }, + nativeRecordId: 'r', }, - }, - }).ok, - ).toBe(false) + }), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-shape-invalid' }], + }) expect( - parseProviderMeasurementBundleInput({ - ...input, - deploymentIdentity: { - ...input.deploymentIdentity, - eventIdentifierSystem: 'https://例え.example/events', - }, - }).ok, - ).toBe(false) + buildProviderExchangeGraph( + heartRate, + context('withings', '1', { + repositoryScope: { + system: 'https://例え.example/accounts', + value: 'a', + } as never, + }), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'invalid-uri', path: ['repositoryScope', 'system'] }], + }) }) it('fails closed at every direct Provider identity primitive boundary', () => { const valid = { provider: 'withings', - providerScopeIdentifier: { - system: uri('https://example.org/provider-accounts'), - value: 'account-pseudonym', - assurance: 'deployment-scoped-account-pseudonym', - }, + repositoryScope: context().repositoryScope, sourceType: 'getmeas:11', sourceNativeId: 'source-record-1', outputs: [ @@ -403,14 +402,15 @@ describe('Provider R4 graph builder', () => { outputDiscriminator: 'single', }, ], - eventSequence: '1', - deployment: deploymentIdentity, + event: context().event, + scope: identityScope, } as const + expect(deriveProviderIdentities(valid).ok).toBe(true) expect( deriveProviderIdentities({ ...valid, - providerScopeIdentifier: { - ...valid.providerScopeIdentifier, + repositoryScope: { + ...valid.repositoryScope, system: '/relative' as never, }, }).ok, @@ -433,52 +433,30 @@ describe('Provider R4 graph builder', () => { outputs: [...valid.outputs, ...valid.outputs], }).ok, ).toBe(false) + expect( + deriveProviderIdentities({ ...valid, scope: { ...identityScope } }).ok, + ).toBe(false) expect( deriveProviderIdentities({ ...valid, - eventSequence: '0', + event: { ...valid.event, value: 'e0:x:1' }, }).ok, ).toBe(false) - - const malformed = [ + for (const candidate of [ { ...valid, provider: 42 }, { ...valid, sourceType: 42 }, { ...valid, outputs: 42 }, { ...valid, outputs: [] }, { ...valid, outputs: [null] }, - { - ...valid, - outputs: [{ ...valid.outputs[0], unexpected: true }], - }, - { - ...valid, - providerScopeIdentifier: { - ...valid.providerScopeIdentifier, - value: 42, - }, - }, - { - ...valid, - providerScopeIdentifier: { - ...valid.providerScopeIdentifier, - value: ' ', - }, - }, - { - ...valid, - providerScopeIdentifier: { - ...valid.providerScopeIdentifier, - assurance: 'documented-global-key-space', - }, - }, + { ...valid, outputs: [{ ...valid.outputs[0], unexpected: true }] }, + { ...valid, repositoryScope: { ...valid.repositoryScope, value: 42 } }, + { ...valid, repositoryScope: { ...valid.repositoryScope, value: ' ' } }, { ...valid, sourceNativeId: 42 }, { ...valid, sourceNativeId: ' ' }, { ...valid, provenanceNodeRole: 'unknown' }, - ] - for (const candidate of malformed) { + ]) { expect(deriveProviderIdentities(candidate as never).ok).toBe(false) } - expect( deriveProviderIdentities({ ...valid, @@ -486,15 +464,10 @@ describe('Provider R4 graph builder', () => { provenanceNodeRole: 'retraction-provenance', }).ok, ).toBe(true) - const raw = { ...valid, provider: 'oura', sourceType: 'heartrate', - providerScopeIdentifier: { - ...valid.providerScopeIdentifier, - assurance: 'documented-global-key-space', - }, outputs: [ { kind: 'provider-artifact', @@ -551,9 +524,20 @@ describe('Provider R4 graph builder', () => { for (const invalid of [null, undefined, 42, 'invalid', cyclic]) { const operations = [ () => parseNormalizedProviderRecord(invalid), - () => parseProviderMeasurementBundleInput(invalid), - () => buildProviderMeasurementBundle(invalid as never), + () => buildProviderExchangeGraph(invalid as never, context()), + () => buildProviderExchangeGraph(heartRate, invalid as never), () => deriveProviderIdentities(invalid as never), + // Absent options are the defaults; anything else present must be an options object. + ...(invalid === undefined ? + [] + : [ + () => + buildProviderExchangeGraph( + heartRate, + context(), + invalid as never, + ), + ]), ] for (const operation of operations) { expect(operation).not.toThrow() @@ -565,11 +549,7 @@ describe('Provider R4 graph builder', () => { it('does not mint identities for selectors outside the pinned Provider catalog', () => { const valid = { provider: 'withings', - providerScopeIdentifier: { - system: uri('https://example.org/provider-accounts'), - value: 'account-pseudonym', - assurance: 'deployment-scoped-account-pseudonym', - }, + repositoryScope: context().repositoryScope, sourceType: 'getmeas:11', sourceNativeId: 'source-record-1', outputs: [ @@ -579,19 +559,25 @@ describe('Provider R4 graph builder', () => { outputDiscriminator: 'single', }, ], - eventSequence: '1', - deployment: deploymentIdentity, + event: context().event, + scope: identityScope, } as const expect(deriveProviderIdentities(valid).ok).toBe(true) expect( deriveProviderIdentities({ ...valid, provider: 'invented-provider', - } as never).ok, - ).toBe(false) + } as never), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.unsupported-source-type' }], + }) expect( - deriveProviderIdentities({ ...valid, sourceType: 'invented-source' }).ok, - ).toBe(false) + deriveProviderIdentities({ ...valid, sourceType: 'invented-source' }), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.unsupported-source-type' }], + }) expect( deriveProviderIdentities({ ...valid, @@ -602,8 +588,11 @@ describe('Provider R4 graph builder', () => { outputDiscriminator: 'single', }, ], - }).ok, - ).toBe(false) + }), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.unsupported-source-value' }], + }) expect( deriveProviderIdentities({ ...valid, @@ -611,7 +600,7 @@ describe('Provider R4 graph builder', () => { { kind: 'provider-output', outputRole: 'heart-rate', - outputDiscriminator: 'invented-discriminator', + outputDiscriminator: 'invented', }, ], }).ok, @@ -619,11 +608,7 @@ describe('Provider R4 graph builder', () => { }) it('uses both exact grouped Withings output coordinates in the HMAC preimage', () => { - const input = baseInput( - 'withings', - 'getmeas:9+10', - bloodPressureMeasurement, - ) + const input = record('withings', 'getmeas:9+10', bloodPressureMeasurement) const coordinates = providerOutputCoordinates( 'withings', 'getmeas:9+10', @@ -634,149 +619,91 @@ describe('Provider R4 graph builder', () => { outputDiscriminator: 'single', }) if (coordinates === undefined) return - const expected = deriveProviderIdentities({ provider: 'withings', - providerScopeIdentifier: input.source.providerScopeIdentifier, + repositoryScope: context().repositoryScope, sourceType: input.source.sourceType, sourceNativeId: input.source.sourceNativeId, outputs: [{ kind: 'provider-output', ...coordinates }], - eventSequence: input.eventSequence, - deployment: input.deploymentIdentity, + event: context().event, + scope: identityScope, }) - const bundle = buildProviderMeasurementBundle(input) - expect(expected.ok && bundle.ok).toBe(true) - if (!expected.ok || !bundle.ok) return - const observation = resources(bundle).find( - ({ resourceType }) => resourceType === 'Observation', - ) - if (observation?.resourceType !== 'Observation') { - throw new Error('The grouped output did not emit an Observation.') - } - const sourceOutput = observation.identifier?.find((candidate) => - candidate.type?.coding?.some(({ code }) => code === 'source-output'), + const built = buildProviderExchangeGraph(input, context()) + expect(expected.ok && built.ok).toBe(true) + if (!expected.ok || !built.ok) return + const sourceOutput = observationOf(built.value.graph).identifier?.find( + (candidate) => + candidate.type?.coding?.some(({ code }) => code === 'source-output'), ) - const expectedOutput = expected.value.outputs[0] - expect(expectedOutput).toBeDefined() expect(sourceOutput).toMatchObject({ - system: expectedOutput?.system, - value: expectedOutput?.value, + system: expected.value.outputs[0]?.system, + value: expected.value.outputs[0]?.value, }) + expect(built.value.identifiers.outputs[0]).toEqual( + expected.value.outputs[0], + ) }) it.each([ - [ - 'provider account value', - (input: ProviderMeasurementBundleInput) => ({ - ...input, - source: { - ...input.source, - providerScopeIdentifier: { - ...input.source.providerScopeIdentifier, - value: ' \t ', - }, - }, - }), - ], [ 'source native id', - (input: ProviderMeasurementBundleInput) => ({ - ...input, - source: { ...input.source, sourceNativeId: '\n ' }, - }), - ], - [ - 'source type', - (input: ProviderMeasurementBundleInput) => ({ - ...input, - source: { ...input.source, sourceType: ' ' }, - }), - ], - [ - 'converter source-device token', - (input: ProviderMeasurementBundleInput) => ({ - ...input, - application: { - ...input.application, - sourceDeviceToken: '\t', - }, - }), + { sourceNativeId: '\n ' }, + 'mobile-input.native-identifier-invalid', ], + ['source type', { sourceType: ' ' }, 'mobile-input.value-shape-invalid'], [ - 'data-origin source-device token', - (input: ProviderMeasurementBundleInput) => ({ - ...input, - source: { - ...input.source, - dataOrigin: { - ...input.source.dataOrigin, - sourceDeviceToken: ' ', - }, - }, - }), + 'writer token', + { writer: { sourceDeviceToken: ' ', name: 'x' } }, + 'mobile-input.value-shape-invalid', ], - ] as const)('rejects a whitespace-only %s', (_name, mutate) => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - expect(parseProviderMeasurementBundleInput(mutate(input)).ok).toBe(false) + ] as const)('refuses a whitespace-only %s', (_name, change, code) => { + expect( + parseNormalizedProviderRecord(withSource(heartRate, change)), + ).toMatchObject({ + ok: false, + issues: [{ code }], + }) }) - it.each(['converter', 'gateway', 'data-origin', 'recording-device'] as const)( - 'rejects an invalid Unicode %s business identity', - (role) => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) - const invalidToken = 'invalid-\ud800' - let candidate: ProviderMeasurementBundleInput - if (role === 'converter') { - candidate = { - ...input, - application: { - ...input.application, - sourceDeviceToken: invalidToken, - }, - } - } else if (role === 'gateway') { - candidate = { - ...input, - gatewayApplication: { - kind: 'distinct-application', - roleAssurance: 'mediated-or-routed-measurement', - application: { - sourceDeviceToken: invalidToken, - name: 'Invalid gateway identity', - }, - }, - } - } else if (role === 'data-origin') { - candidate = { - ...input, - source: { - ...input.source, - dataOrigin: { - ...input.source.dataOrigin, - sourceDeviceToken: invalidToken, - }, - }, - } as ProviderMeasurementBundleInput - } else { - candidate = { - ...input, - source: { - ...input.source, - recordingDevice: { - stableUnitToken: invalidToken, - subjectIdentifier: { - system: uri('https://example.org/participants'), - value: 'example', - }, - identityScope: 'deployment-scoped', - }, - }, - } as ProviderMeasurementBundleInput - } - expect(parseProviderMeasurementBundleInput(candidate).ok).toBe(false) - expect(buildProviderMeasurementBundle(candidate).ok).toBe(false) - }, - ) + it('refuses invalid Unicode in the record and faults it in the context', () => { + const invalidToken = 'invalid-\ud800' + expect( + parseNormalizedProviderRecord( + withSource(heartRate, { + writer: { sourceDeviceToken: invalidToken, name: 'x' }, + }), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.text-not-unicode-scalar' }], + }) + expect( + parseNormalizedProviderRecord( + withSource(heartRate, { + recordingDevice: { stableUnitToken: invalidToken }, + }), + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.text-not-unicode-scalar' }], + }) + for (const overrides of [ + { application: { ...application, sourceDeviceToken: invalidToken } }, + { + converterRole: { + kind: 'gateway-application', + application: { sourceDeviceToken: invalidToken, name: 'Gateway' }, + }, + }, + ] as const) { + const result = buildProviderExchangeGraph( + heartRate, + context('withings', '1', overrides), + ) + expect(result.ok).toBe(false) + if (!result.ok) expect(result.issues[0]?.code).toBe('invalid-identifier') + } + }) it.each([ ['google-health-api', 'heart-rate', heartRateMeasurement], @@ -787,12 +714,6 @@ describe('Provider R4 graph builder', () => { kind: 'blood-glucose', value: 94, effective: { kind: 'date-time', value: dateTime }, - specimen: { - identity: resourceIdentity( - 'https://example.org/specimens', - 'whole-blood', - ), - }, }, ], [ @@ -801,51 +722,42 @@ describe('Provider R4 graph builder', () => { { kind: 'sleep-stage', stage: 'deep', - effective: { kind: 'period', start, end }, + effective: { kind: 'period', start, end: dateTime }, }, ], ] as const)( 'fails closed for non-scalar or unsupported %s/%s data', (provider, sourceType, measurement) => { - const result = buildProviderMeasurementBundle( - baseInput( + const result = buildProviderExchangeGraph( + record( provider, sourceType, measurement as unknown as MobileMeasurement, ), + context(provider), ) expect(result.ok).toBe(false) + if (!result.ok) expect(result.issues[0]?.code).toMatch(/^mobile-input\./u) }, ) - it('rejects non-canonical events and conflicting facts for one device snapshot', () => { - const input = baseInput('withings', 'getmeas:11', heartRateMeasurement) + it('rejects conflicting facts for one device snapshot and deduplicates identical ones', () => { expect( - buildProviderMeasurementBundle({ - ...input, - eventSequence: '0', - }).ok, - ).toBe(false) - expect( - buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, - dataOrigin: input.application, - }, - } as unknown as ProviderMeasurementBundleInput).ok, + buildProviderExchangeGraph( + withSource(heartRate, { writer: application }), + context(), + ).ok, ).toBe(true) expect( - buildProviderMeasurementBundle({ - ...input, - source: { - ...input.source, - dataOrigin: { - ...input.source.dataOrigin, - sourceDeviceToken: input.application.sourceDeviceToken, + buildProviderExchangeGraph( + withSource(heartRate, { + writer: { + ...heartRate.source.writer, + sourceDeviceToken: application.sourceDeviceToken, }, - }, - } as unknown as ProviderMeasurementBundleInput).ok, - ).toBe(false) + }), + context(), + ), + ).toMatchObject({ ok: false, issues: [{ code: 'duplicate-identifier' }] }) }) }) diff --git a/packages/fhir/test/provider-test-support.ts b/packages/fhir/test/provider-test-support.ts index d1564350..be152d75 100644 --- a/packages/fhir/test/provider-test-support.ts +++ b/packages/fhir/test/provider-test-support.ts @@ -6,26 +6,35 @@ // SPDX-License-Identifier: MIT // -import type { AbsoluteUri, FhirInstant } from '../src/core/index.js' import { parseAbsoluteUri, + parseEventSequence, parseFhirInstant, + parseKeyEpoch, + type AbsoluteUri, + type FhirInstant, type Result, -} from '../src/index.js' -import type { - ApplicationDeviceInput, - DeploymentIdentityInput, - MobileMeasurement, - ResourceIdentityInput, +} from '../src/core/index.js' +import { + deriveEventIdentifier, + deriveOpaqueIdentitySystems, + validateOpaqueIdentityScope, + type ApplicationDevice, + type BusinessIdentifier, + type DeploymentIdentifierSystems, + type ExchangeEventContext, + type HostDevice, + type MobileMeasurement, + type OpaqueIdentityScope, + type OpaqueIdentityScopeInput, + type StudyEnrollment, + type Subject, } from '../src/mobile/index.js' import type { - buildProviderMeasurementBundle, ConnectedProvider, - ProviderPatientReferenceInput, - ProviderMeasurementBundleInput, - ProviderResearchStudyReferenceInput, + NormalizedProviderRecord, } from '../src/providers/index.js' -import type { GroveMobileExchangeBundle } from '../src/r4/index.js' +import type { ExchangeGraph, Observation } from '../src/r4/index.js' export const unwrap = (result: Result): T => { if (!result.ok) { @@ -38,67 +47,115 @@ export const uri = (value: string): AbsoluteUri => unwrap(parseAbsoluteUri(value)) export const instant = (value: string): FhirInstant => unwrap(parseFhirInstant(value)) -export const patient: ProviderPatientReferenceInput = { - type: 'Patient', + +export const subject: Subject = { + kind: 'logical', identifier: { system: uri('https://example.org/deployments/patient-pseudonyms'), value: 'patient-example', - assurance: 'deployment-scoped-pseudonym', }, } -export const study = (value: string): ProviderResearchStudyReferenceInput => ({ - type: 'ResearchStudy', - identifier: { + +export const study = (value: string): StudyEnrollment => ({ + study: { system: uri('https://example.org/deployments/research-studies'), value, }, -}) -export const resourceIdentity = ( - system: string, - value: string, -): ResourceIdentityInput => ({ - identifier: { system: uri(system), value }, + protocol: { + url: uri(`https://example.org/PlanDefinition/${value}`), + version: '1', + }, + enrollment: { + system: uri('https://example.org/deployments/enrollments'), + value: `${value}-enrollment`, + }, }) -export const application: ApplicationDeviceInput = { +export const application: ApplicationDevice = { sourceDeviceToken: 'converter-app', name: 'Example converter', version: '0.0.0', } -export const deploymentIdentity: DeploymentIdentityInput = { - opaqueIdentifierSystems: { - 'source-record': uri('https://example.org/identity/source-record/test/1'), - 'source-output': uri('https://example.org/identity/source-output/test/1'), - 'writer-record': uri('https://example.org/identity/writer-record/test/1'), - 'provider-record': uri( - 'https://example.org/identity/provider-record/test/1', - ), - 'provider-output': uri( - 'https://example.org/identity/provider-output/test/1', - ), - 'source-artifact': uri( - 'https://example.org/identity/source-artifact/test/1', - ), - 'provider-artifact': uri( - 'https://example.org/identity/provider-artifact/test/1', - ), - 'source-context': uri('https://example.org/identity/source-context/test/1'), - 'recording-device': uri( - 'https://example.org/identity/recording-device/test/1', - ), - 'device-snapshot': uri( - 'https://example.org/identity/device-snapshot/test/1', - ), - }, - eventIdentifierSystem: uri('https://example.org/identity/event'), - entryNodeIdentifierSystem: uri('https://example.org/identity/entry-node'), +export const host: HostDevice = { + sourceDeviceToken: 'converter-host', + operatingSystemVersion: '20.0', +} + +export const identitySystems: DeploymentIdentifierSystems = unwrap( + deriveOpaqueIdentitySystems( + uri('https://example.org/identity'), + 'test-key', + unwrap(parseKeyEpoch('1')), + ), +) + +export const scopeInput: OpaqueIdentityScopeInput = { + systems: identitySystems, keyId: 'test-key', - keyEpoch: '1', + keyEpoch: unwrap(parseKeyEpoch('1')), secretBase64Url: 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', producerInstance: '1f5c58aa-6ec6-4e79-a682-829a9debd3f5', } +export const identityScope: OpaqueIdentityScope = unwrap( + validateOpaqueIdentityScope(scopeInput), +) + +export const accountScope: BusinessIdentifier = { + system: uri('https://example.org/deployments/provider-accounts'), + value: 'pseudonym-account-001', +} + +export const globalScope: BusinessIdentifier = { + system: uri('https://example.org/provider-key-spaces'), + value: 'oura-document-uuid-global', +} + +export const repositoryScope = ( + provider: ConnectedProvider, +): BusinessIdentifier => (provider === 'oura' ? globalScope : accountScope) + +export const conversionInstant: FhirInstant = instant('2026-08-20T12:03:00Z') + +export const context = ( + provider: ConnectedProvider = 'withings', + sequence = '1', + overrides: Partial = {}, +): ExchangeEventContext => ({ + subject, + event: unwrap( + deriveEventIdentifier(identityScope, unwrap(parseEventSequence(sequence))), + ), + identityScope, + repositoryScope: repositoryScope(provider), + application, + host, + conversionInstant, + ...overrides, +}) + +export const writer = (provider: string): ApplicationDevice => ({ + sourceDeviceToken: `writer-${provider}`, + name: provider, +}) + +export const record = ( + provider: ConnectedProvider, + sourceType: string, + measurement: MobileMeasurement, +): NormalizedProviderRecord => + ({ + source: { + adapter: { kind: 'providers', provider }, + sourceType, + sourceNativeId: `native-${provider}-${sourceType}`, + recordingMethod: 'automatically-recorded', + writer: writer(provider), + }, + measurements: [measurement], + }) as NormalizedProviderRecord + export const dateTime: FhirInstant = instant('2026-08-20T12:00:00Z') export const start: FhirInstant = instant('2026-08-20T00:00:00Z') export const end: FhirInstant = instant('2026-08-20T12:00:00Z') @@ -123,44 +180,6 @@ export const bloodPressureMeasurement: Extract< effective: { kind: 'date-time', value: dateTime }, } -export const baseInput = ( - provider: ConnectedProvider, - sourceType: string, - measurement: MobileMeasurement, -): ProviderMeasurementBundleInput => - ({ - subject: patient, - measurements: [measurement], - source: { - adapter: { kind: 'providers', provider }, - providerScopeIdentifier: - provider === 'oura' ? - { - system: uri('https://example.org/provider-key-spaces'), - value: 'oura-document-uuid-global', - assurance: 'documented-global-key-space', - } - : { - system: uri('https://example.org/deployments/provider-accounts'), - value: `pseudonym-${provider}-001`, - assurance: 'deployment-scoped-account-pseudonym', - }, - sourceType, - sourceNativeId: `native-${provider}-${sourceType}`, - recordingMethod: 'automatically-recorded', - dataOrigin: { - sourceDeviceToken: `data-origin-${provider}`, - name: provider, - }, - }, - application, - eventSequence: '1', - deploymentIdentity, - occurred: instant('2026-08-20T12:00:00Z'), - recorded: instant('2026-08-20T12:02:00Z'), - assembled: instant('2026-08-20T12:03:00Z'), - }) as ProviderMeasurementBundleInput - export const scalarCases: ReadonlyArray<{ readonly provider: ConnectedProvider readonly sourceType: string @@ -251,10 +270,18 @@ export const scalarCases: ReadonlyArray<{ ] export const resources = ( - result: ReturnType, -): ReadonlyArray => { - if (!result.ok) throw new Error(JSON.stringify(result.issues)) - return result.value.entry.map((entry) => entry.resource) + graph: ExchangeGraph, +): ReadonlyArray => + graph.entry.map((entry) => entry.resource) + +export const observationOf = (graph: ExchangeGraph): Observation => { + const observation = resources(graph).find( + (resource) => resource.resourceType === 'Observation', + ) + if (observation?.resourceType !== 'Observation') { + throw new Error('The graph did not contain its Observation.') + } + return observation } export const mutableRecord = ( diff --git a/packages/fhir/test/readme-walkthrough.test.ts b/packages/fhir/test/readme-walkthrough.test.ts new file mode 100644 index 00000000..ada76ae6 --- /dev/null +++ b/packages/fhir/test/readme-walkthrough.test.ts @@ -0,0 +1,470 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { readFileSync } from 'node:fs' +import { hostname, release, type } from 'node:os' +import typeSurface from './type-surface.json' with { type: 'json' } +import * as root from '../src/index.js' +import { + codeableConceptDisplay, + deriveEventIdentifier, + deriveOpaqueIdentitySystems, + fhirDateTimeToDate, + fhirQuantityToValue, + isSemanticallyEqual, + observationEffectiveDate, + observationNumericValue, + parseAbsoluteUri, + parseEventSequence, + parseExchangeEventContext, + parseExchangeGraph, + parseFhirId, + parseFhirInstant, + parseKeyEpoch, + parseObservation, + retractionTargets, + validateOpaqueIdentityScope, + type ApplicationDevice, + type ExchangeEventContext, + type ExchangeGraph, + type Result, +} from '../src/index.js' +import * as mobile from '../src/mobile/index.js' +import * as providers from '../src/providers/index.js' +import { + buildProviderExchangeGraph, + buildProviderExchangeGraphs, + buildProviderRetractionEvent, + type NormalizedProviderRecord, +} from '../src/providers/index.js' +import * as questionnaire from '../src/questionnaire/index.js' +import * as r4 from '../src/r4/index.js' +import * as zodR4 from '../src/zod/r4/index.js' +import * as zodR4b from '../src/zod/r4b/index.js' + +// The README walks through this file. Every TypeScript block there, minus its imports, is a +// region below, so the walkthrough compiles and runs against the API it documents. + +const seed = (name: string, value: string): void => { + process.env[name] ??= value +} +seed( + 'GROVE_IDENTITY_SECRET_BASE64URL', + 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', +) +seed('GROVE_PRODUCER_INSTANCE', '1f5c58aa-6ec6-4e79-a682-829a9debd3f5') + +// readme:begin setup +// Every parser returns a Result; report the issues where a real application handles them. +const unwrap = (result: Result): Value => { + if (!result.ok) { + throw new Error(result.issues.map(({ message }) => message).join('\n')) + } + return result.value +} +const uri = (value: string) => unwrap(parseAbsoluteUri(value)) + +// From your secret store: the HMAC key as base64url, and the UUID generated for this installation. +const identitySecret = process.env.GROVE_IDENTITY_SECRET_BASE64URL +const producerInstance = process.env.GROVE_PRODUCER_INSTANCE +if (identitySecret === undefined || producerInstance === undefined) { + throw new Error('The identity key and the producer instance id are missing.') +} + +const keyEpoch = unwrap(parseKeyEpoch('1')) +const identityScope = unwrap( + validateOpaqueIdentityScope({ + systems: unwrap( + deriveOpaqueIdentitySystems( + uri('https://mystudy.example.org/fhir'), + 'study-key', + keyEpoch, + ), + ), + keyId: 'study-key', + keyEpoch, + secretBase64Url: identitySecret, + producerInstance, + }), +) + +const application: ApplicationDevice = { + sourceDeviceToken: 'org.example.mystudy-server/2.1.0', + name: 'MyStudy server', + version: '2.1.0', +} +// readme:end setup + +let lastSequence = 1041 +const reserveNextEventSequence = (): string => { + lastSequence += 1 + return String(lastSequence) +} +const participant = { + pseudonym: 'participant-7f3a', + withingsAccountPseudonym: 'withings-account-7f3a', +} + +// readme:begin export +// Reserve the sequence in the same transaction that records this export; it is never reused. +const sequence = unwrap(parseEventSequence(reserveNextEventSequence())) + +const context: ExchangeEventContext = { + subject: { + kind: 'logical', + identifier: { + system: uri('https://mystudy.example.org/fhir/identifiers/participants'), + value: participant.pseudonym, + }, + }, + event: unwrap(deriveEventIdentifier(identityScope, sequence)), + identityScope, + repositoryScope: { + system: uri( + 'https://mystudy.example.org/fhir/identifiers/withings-accounts', + ), + value: participant.withingsAccountPseudonym, + }, + application, + host: { + sourceDeviceToken: hostname(), + operatingSystemVersion: `${type()} ${release()}`, + }, +} +// readme:end export + +const measure = { grpid: '1234567890', value: 64, date: '2026-08-19T17:30:00Z' } +const uploaded: ExchangeGraph[] = [] +const upload = (graph: ExchangeGraph): void => { + uploaded.push(graph) +} + +// readme:begin convert +// One row of the Withings measure response, already fetched and decoded by your adapter. +const record: NormalizedProviderRecord = { + source: { + adapter: { kind: 'providers', provider: 'withings' }, + sourceType: 'getmeas:11', + sourceNativeId: measure.grpid, + writer: { sourceDeviceToken: 'withings-api', name: 'Withings API' }, + }, + measurements: [ + { + kind: 'heart-rate', + value: measure.value, + effective: { + kind: 'date-time', + value: unwrap(parseFhirInstant(measure.date)), + }, + }, + ], +} + +const result = buildProviderExchangeGraph(record, context) +if (result.ok) { + upload(result.value.graph) +} else { + for (const issue of result.issues) { + console.error(issue.code, issue.path.join('.'), issue.message) + } +} +// readme:end convert + +// readme:begin study +const enrolled: ExchangeEventContext = { + ...context, + subject: { + kind: 'bundled', + identifier: context.subject.identifier, + patient: { + resourceType: 'Patient', + identifier: [context.subject.identifier], + }, + }, + studies: [ + { + study: { + system: uri('https://mystudy.example.org/fhir/identifiers/studies'), + value: 'heart-2026', + }, + protocol: { + url: uri('https://mystudy.example.org/fhir/PlanDefinition/heart-2026'), + version: '3', + }, + enrollment: { + system: uri('https://mystudy.example.org/fhir/identifiers/enrollments'), + value: 'enrollment-7f3a', + }, + }, + ], +} +// readme:end study + +// readme:begin disclosure +const disclosed = buildProviderExchangeGraph(record, context, { + nativeIdentifierDisclosure: { + kind: 'authorized', + system: uri( + 'https://mystudy.example.org/fhir/identifiers/withings-measure-groups', + ), + type: { text: 'Withings measure group id' }, + }, +}) +// readme:end disclosure + +// readme:begin repository-ids +const stored: ExchangeEventContext = { + ...context, + repositoryIds: { + bundle: unwrap(parseFhirId('bundle-1042')), + 'primary-output': unwrap(parseFhirId('observation-1042')), + }, +} +// readme:end repository-ids + +// readme:begin gateway +const relayed: ExchangeEventContext = { + ...context, + converterRole: { + kind: 'gateway-application', + application: { + sourceDeviceToken: 'org.example.mystudy-app/4.2.0', + name: 'MyStudy app', + version: '4.2.0', + }, + }, +} +// readme:end gateway + +// readme:begin warnings +if (result.ok) { + for (const warning of result.value.warnings) { + console.warn(warning.code, warning.reason) + } +} +// readme:end warnings + +const records = [record] + +// readme:begin batch +const batch = buildProviderExchangeGraphs(records, () => ({ + ...context, + event: unwrap( + deriveEventIdentifier( + identityScope, + unwrap(parseEventSequence(reserveNextEventSequence())), + ), + ), +})) +for (const conversion of batch.conversions) upload(conversion.graph) +for (const failure of batch.failures) { + console.error( + failure.record.source.sourceNativeId, + failure.issues.map(({ code }) => code), + ) +} +// readme:end batch + +const conversion = unwrap(buildProviderExchangeGraph(record, context)) +const graph = conversion.graph +const storedGraphText = JSON.stringify(graph, null, 2) + +// readme:begin retry +const sameEvent = unwrap( + isSemanticallyEqual(storedGraphText, JSON.stringify(graph)), +) +// readme:end retry + +// readme:begin retraction +const retraction = buildProviderRetractionEvent( + unwrap(retractionTargets(graph)), + { + ...context, + event: unwrap( + deriveEventIdentifier( + identityScope, + unwrap(parseEventSequence(reserveNextEventSequence())), + ), + ), + }, + conversion.identifiers.sourceRecord, + unwrap(parseFhirInstant('2026-09-01T08:00:00Z')), +) +// readme:end retraction + +const observation = unwrap(parseObservation(graph.entry[0].resource)) +const input: unknown = graph.entry[0].resource + +// readme:begin parse-observation +const parsed = parseObservation(input) + +if (!parsed.ok) { + for (const issue of parsed.issues) { + console.error(issue.path, issue.code, issue.message) + } +} +// readme:end parse-observation + +// readme:begin read-values +const recorded = fhirDateTimeToDate(observation.effectiveDateTime) +const measured = fhirQuantityToValue(observation.valueQuantity) +// readme:end read-values + +// readme:begin extract-values +const when = observationEffectiveDate(observation) +const value = observationNumericValue(observation) +const label = codeableConceptDisplay(observation.code) +// readme:end extract-values + +const readme = readFileSync(new URL('../README.md', import.meta.url), 'utf8') +const source = readFileSync(new URL(import.meta.url), 'utf8') +interface EntryPoint { + readonly runtime: Readonly> + readonly declared: Readonly> +} +const declaredSurface = typeSurface as Readonly< + Record>> +> +const entryPoints: Readonly> = { + '@schmiedmayerlab/grove-fhir': { + runtime: root, + declared: declaredSurface['.'] ?? {}, + }, + '@schmiedmayerlab/grove-fhir/mobile': { + runtime: mobile, + declared: declaredSurface['./mobile'] ?? {}, + }, + '@schmiedmayerlab/grove-fhir/providers': { + runtime: providers, + declared: declaredSurface['./providers'] ?? {}, + }, + '@schmiedmayerlab/grove-fhir/r4': { + runtime: r4, + declared: declaredSurface['./r4'] ?? {}, + }, + '@schmiedmayerlab/grove-fhir/questionnaire': { + runtime: questionnaire, + declared: declaredSurface['./questionnaire'] ?? {}, + }, + '@schmiedmayerlab/grove-fhir/zod/r4': { runtime: zodR4, declared: {} }, + '@schmiedmayerlab/grove-fhir/zod/r4b': { runtime: zodR4b, declared: {} }, +} + +interface ReadmeBlock { + readonly imports: readonly string[] + readonly body: string +} + +// Fenced TypeScript blocks, split into their leading import statements and the code. +const readmeBlocks = (): readonly ReadmeBlock[] => { + const blocks: ReadmeBlock[] = [] + let lines: string[] | undefined + for (const line of readme.split('\n')) { + if (lines === undefined) { + if (line === '```typescript') lines = [] + continue + } + if (line !== '```') { + lines.push(line) + continue + } + const imports: string[] = [] + let statement: string[] = [] + let consumed = 0 + for (const [index, candidate] of lines.entries()) { + if (statement.length === 0 && !candidate.startsWith('import ')) break + statement.push(candidate) + if (candidate.includes(" from '")) { + imports.push(`${statement.join('\n')}\n`) + statement = [] + consumed = index + 1 + } + } + blocks.push({ imports, body: lines.slice(consumed).join('\n').trim() }) + lines = undefined + } + return blocks +} + +const importedNames = ( + statement: string, +): { readonly module: string; readonly names: readonly string[] } => { + const module = /from '([^']+)'/u.exec(statement)?.[1] ?? '' + const open = statement.indexOf('{') + const close = statement.lastIndexOf('}') + const names = ( + open === -1 || close === -1 ? + '' + : statement.slice(open + 1, close)) + .split(',') + .map((name) => name.trim().replace(/ as .*$/u, '')) + .filter((name) => name !== '') + return { module, names } +} + +describe('README walkthrough', () => { + it('converts the record under a context that uses every default', () => { + expect(result.ok).toBe(true) + expect(uploaded).toHaveLength(2) + const resolved = parseExchangeEventContext(context) + expect(resolved.ok).toBe(true) + if (!resolved.ok) return + expect(resolved.value.converterRole).toEqual({ kind: 'assembler' }) + expect(resolved.value.studies).toEqual([]) + expect( + Date.now() - Date.parse(resolved.value.conversionInstant), + ).toBeLessThan(60_000) + }) + + it('reads the emitted Observation back out', () => { + expect(parsed.ok).toBe(true) + expect(recorded.ok && recorded.value.toISOString()).toBe( + '2026-08-19T17:30:00.000Z', + ) + expect(measured.ok && measured.value.value).toBe(64) + expect(when?.toISOString()).toBe('2026-08-19T17:30:00.000Z') + expect(value).toBe(64) + expect(label).toBeUndefined() + }) + + it('accepts every fragment beyond the minimum', () => { + expect(buildProviderExchangeGraph(record, enrolled).ok).toBe(true) + expect(disclosed.ok).toBe(true) + expect(buildProviderExchangeGraph(record, stored).ok).toBe(true) + expect(buildProviderExchangeGraph(record, relayed).ok).toBe(true) + expect(batch.conversions).toHaveLength(1) + expect(batch.failures).toHaveLength(0) + expect(sameEvent).toBe(true) + expect(retraction.ok).toBe(true) + expect(parseExchangeGraph(graph).ok).toBe(true) + }) + + it('keeps every README code block in this file', () => { + const blocks = readmeBlocks() + expect(blocks.length).toBeGreaterThan(8) + for (const block of blocks) { + expect(source).toContain(block.body) + for (const statement of block.imports) { + const { module, names } = importedNames(statement) + const entryPoint = entryPoints[module] + if (entryPoint === undefined) { + expect(source).toContain(statement) + continue + } + for (const name of names) { + const bare = name.replace(/^type /u, '') + const surface = + name.startsWith('type ') ? + Object.keys(entryPoint.declared) + : Object.keys(entryPoint.runtime) + expect(surface).toContain(bare) + } + } + } + }) +}) diff --git a/packages/fhir/test/receiver-lifecycle.test.ts b/packages/fhir/test/receiver-lifecycle.test.ts new file mode 100644 index 00000000..8a193284 --- /dev/null +++ b/packages/fhir/test/receiver-lifecycle.test.ts @@ -0,0 +1,219 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { createHash } from 'node:crypto' +import { readFileSync } from 'node:fs' +import { groveExchangeProtocol } from '../src/contract/measurement-catalog.generated.js' +import { + isSemanticallyEqual, + parseExchangeGraph, + parseRetractionEvent, + retractionTargets, +} from '../src/r4/index.js' + +interface CorpusEvent { + readonly path: string + readonly sha256: string + readonly eventIdentity: { readonly system: string; readonly value: string } +} + +interface CorpusSequence { + readonly id: string + readonly steps: ReadonlyArray<{ readonly event: string }> +} + +interface CorpusSequences { + readonly sequences: readonly CorpusSequence[] +} + +const corpusRoot = new URL( + '../fixtures/conformance/receiver-lifecycle/', + import.meta.url, +) +const readCorpus = (name: string): unknown => + JSON.parse(readFileSync(new URL(name, corpusRoot), 'utf8')) + +const { events } = readCorpus('events.json') as { + readonly events: Record +} +const sequences = [ + ...(readCorpus('sequences.json') as CorpusSequences).sequences, + ...(readCorpus('revision-sequences.json') as CorpusSequences).sequences, +] +const bytesOf = (alias: string): Uint8Array => { + const event = events[alias] + if (event === undefined) throw new Error(`Unknown corpus event ${alias}.`) + return new Uint8Array(readFileSync(new URL(event.path, corpusRoot))) +} +const identityOf = (alias: string): string => { + const event = events[alias] + if (event === undefined) throw new Error(`Unknown corpus event ${alias}.`) + return `${event.eventIdentity.system}|${event.eventIdentity.value}` +} +// Equality is decided over tokens: the reformatted replay is the exact retry, every +// other replay under a reused identifier is different content. +const expectedEqual = (left: string, right: string): boolean => + left === right || + (identityOf(left) === identityOf(right) && + [left, right].every((alias) => + ['original', 'reformatted-retry'].includes(alias), + )) + +const unwrap = ( + result: { ok: true; value: Value } | { ok: false }, +): Value => { + if (!result.ok) throw new Error('Expected an accepted value.') + return result.value +} + +describe('receiver-lifecycle corpus', () => { + it('carries the exact pinned fixture bytes', () => { + for (const [alias, event] of Object.entries(events)) { + expect([ + alias, + createHash('sha256').update(bytesOf(alias)).digest('hex'), + ]).toEqual([alias, event.sha256]) + } + expect( + Buffer.compare(bytesOf('original'), bytesOf('reformatted-retry')), + ).not.toBe(0) + }) + + it('parses every event as the graph kind it claims', () => { + for (const alias of Object.keys(events)) { + const text = Buffer.from(bytesOf(alias)).toString('utf8') + const parsed = + alias === 'retraction' ? + parseRetractionEvent(JSON.parse(text)) + : parseExchangeGraph(JSON.parse(text)) + expect([alias, parsed.ok]).toEqual([alias, true]) + } + }) + + it('decides the exact retry over lossless tokens, never bytes', () => { + expect( + isSemanticallyEqual(bytesOf('original'), bytesOf('reformatted-retry')), + ).toEqual({ + ok: true, + value: true, + }) + expect( + isSemanticallyEqual(bytesOf('original'), bytesOf('lexeme-retry')), + ).toEqual({ + ok: true, + value: false, + }) + expect( + isSemanticallyEqual(bytesOf('original'), bytesOf('altered-retry')), + ).toEqual({ + ok: true, + value: false, + }) + expect(groveExchangeProtocol.payload.equality.vectors).toContain( + 'reformatted-retry', + ) + }) + + it.each(sequences.map((sequence) => [sequence.id, sequence] as const))( + 'agrees with every equality decision in %s', + (_id, sequence) => { + const delivered: string[] = [] + for (const step of sequence.steps) { + for (const prior of delivered) { + expect({ + pair: `${prior} vs ${step.event}`, + equality: isSemanticallyEqual(bytesOf(prior), bytesOf(step.event)), + }).toEqual({ + pair: `${prior} vs ${step.event}`, + equality: { ok: true, value: expectedEqual(prior, step.event) }, + }) + } + delivered.push(step.event) + } + }, + ) + + it('compares decimal lexemes as text and member order as irrelevant', () => { + expect(isSemanticallyEqual('{"a":72}', '{"a":72.0}')).toEqual({ + ok: true, + value: false, + }) + expect(isSemanticallyEqual('{"a":1e2}', '{"a":100}')).toEqual({ + ok: true, + value: false, + }) + expect( + isSemanticallyEqual('{"a":1,"b":[1,2]}', ' {"b" : [1, 2], "a":1}'), + ).toEqual({ + ok: true, + value: true, + }) + expect(isSemanticallyEqual('{"a":"\\u00e9"}', '{"a":"é"}')).toEqual({ + ok: true, + value: true, + }) + expect(isSemanticallyEqual('[1,2]', '[2,1]')).toEqual({ + ok: true, + value: false, + }) + expect(isSemanticallyEqual('{"a":null}', '{"a":false}')).toEqual({ + ok: true, + value: false, + }) + expect(isSemanticallyEqual('{"a":1,"a":2}', '{"a":2}').ok).toBe(false) + expect(isSemanticallyEqual('{"a":1} x', '{"a":1}').ok).toBe(false) + expect(isSemanticallyEqual('{"a":01}', '{"a":1}').ok).toBe(false) + expect(isSemanticallyEqual(new Uint8Array([0xff]), '1').ok).toBe(false) + expect( + isSemanticallyEqual('['.repeat(600) + ']'.repeat(600), '[]').ok, + ).toBe(false) + }) + + it('types the retraction targets the retraction fixture names', () => { + const original = unwrap( + parseExchangeGraph( + JSON.parse(Buffer.from(bytesOf('original')).toString('utf8')), + ), + ) + const retraction = unwrap( + parseRetractionEvent( + JSON.parse(Buffer.from(bytesOf('retraction')).toString('utf8')), + ), + ) + const targets = unwrap(retractionTargets(original)) + const provenance = retraction.entry.find( + ({ resource }) => resource.resourceType === 'Provenance', + )?.resource + if (provenance?.resourceType !== 'Provenance') + throw new Error('No Provenance.') + for (const named of provenance.target) { + const matching = targets.find( + ({ identifier }) => + identifier.system === named.identifier?.system && + identifier.value === named.identifier.value, + ) + expect(matching).toBeDefined() + expect(matching?.resourceType).toBe(named.type) + expect(matching?.role).toBe( + named.extension?.find(({ url }) => + url?.endsWith('grove-retraction-target-role'), + )?.valueCode, + ) + } + const roles = Object.keys( + groveExchangeProtocol.lifecycle.retraction.targetRoles, + ) + for (const target of targets) { + expect(roles).toContain(target.role) + expect( + groveExchangeProtocol.lifecycle.retraction.targetRoles[target.role] + .resourceTypes, + ).toContain(target.resourceType) + } + }) +}) diff --git a/packages/fhir/test/recording.test.ts b/packages/fhir/test/recording.test.ts index 9bfddddb..364a0792 100644 --- a/packages/fhir/test/recording.test.ts +++ b/packages/fhir/test/recording.test.ts @@ -10,89 +10,82 @@ import { readFileSync } from 'node:fs' import { expectTypeOf } from 'expect-type' import { assert, property, uint8Array } from 'fast-check' import { - deploymentIdentity, + application, + context, + conversionInstant, + identityScope, instant, - patient, unwrap, uri, } from './provider-test-support.js' import { adapterSourceMarkerClaims } from '../src/contract/measurement-catalog.generated.js' import { parseFhirId, - parseGroveMobileExchangeBundle, + parseExchangeGraph, parsePositiveInteger, type Result, } from '../src/index.js' import { - buildProviderRecordingBundle, + buildProviderRecordingGraph, groveRecordingFormatRegistry, healthKitClinicalRecordAdmission, providerRawOutputRoles, encodeRecordingBytes, parseCanonicalBase64, - parseProviderRecordingBundleInput, + parseProviderRecordingAttachment, + parseProviderRecordingSource, parseImmutableRecordingUrl, parseMediaType, parseSha1Base64, - type ApplicationDeviceInput, - type ProviderRecordingBundleInput, type ConnectedRawProvider, + type ProviderRecordingAttachment, + type ProviderRecordingConversion, + type ProviderRecordingSource, } from '../src/providers/index.js' -const converter: ApplicationDeviceInput = { - sourceDeviceToken: 'converter-instance-7f3a', - name: 'Grove converter', - version: '0.0.0', -} - -const rawInput = ( +const rawSource = ( provider: ConnectedRawProvider, sourceType: string, -): ProviderRecordingBundleInput => +): ProviderRecordingSource => ({ - source: { - adapter: { kind: 'providers', provider }, - providerScopeIdentifier: - provider === 'oura' ? - { - system: uri('https://example.org/provider-key-spaces'), - value: 'oura-document-uuid-global', - assurance: 'documented-global-key-space', - } - : { - system: uri('https://provider.example.org/accounts'), - value: 'account-pseudonym-001', - assurance: 'deployment-scoped-account-pseudonym', - }, - sourceType, - sourceNativeId: `native-record-${provider}-${sourceType}`, - dataOrigin: { - sourceDeviceToken: `origin-${provider}`, - name: `${provider} source`, - }, + adapter: { kind: 'providers', provider }, + sourceType, + sourceNativeId: `native-record-${provider}-${sourceType}`, + writer: { + sourceDeviceToken: `origin-${provider}`, + name: `${provider} source`, }, - attachment: { - kind: 'embedded', - contentType: unwrap( - parseMediaType( - groveRecordingFormatRegistry.formats['provider-recording'] - .contentTypes[0], - ), - ), - title: 'Authorized minimized provider recording', - format: 'provider-recording', - payloadAssertion: 'caller-authorized-opaque-payload', - dataBase64: unwrap(encodeRecordingBytes(Uint8Array.of(1, 2, 3))), + effective: { + kind: 'period', + start: instant('2026-08-20T00:00:00Z'), + end: instant('2026-08-20T12:00:00Z'), }, - subject: patient, - application: converter, - eventSequence: '1', - deploymentIdentity, - documentDate: instant('2026-08-20T17:00:01Z'), - occurred: instant('2026-08-20T17:00:00Z'), - recorded: instant('2026-08-20T17:00:02Z'), - assembled: instant('2026-08-20T17:00:03Z'), - }) as ProviderRecordingBundleInput + }) as ProviderRecordingSource + +const embedded: ProviderRecordingAttachment = { + kind: 'embedded', + contentType: unwrap( + parseMediaType( + groveRecordingFormatRegistry.formats['provider-recording'] + .contentTypes[0], + ), + ), + title: 'Authorized minimized provider recording', + format: 'provider-recording', + payloadAssertion: 'caller-authorized-opaque-payload', + dataBase64: unwrap(encodeRecordingBytes(Uint8Array.of(1, 2, 3))), +} + +const build = ( + source: ProviderRecordingSource, + attachment: ProviderRecordingAttachment = embedded, + overrides = {}, +): Result => + buildProviderRecordingGraph( + source, + attachment, + context(source.adapter.provider, '1', overrides), + ) const recordingSources = Object.entries(providerRawOutputRoles).flatMap( ([provider, sources]) => @@ -112,11 +105,9 @@ const mutableRecordingGraph = (): { readonly bundle: unknown readonly document: MutableJsonObject } => { - const built = buildProviderRecordingBundle( - rawInput('google-health-api', 'heart-rate'), + const bundle = structuredClone( + unwrap(build(rawSource('google-health-api', 'heart-rate'))).graph, ) - if (!built.ok) throw new Error('Expected a valid recording graph fixture.') - const bundle = structuredClone(built.value) const entries = mutableObject(bundle, 'Bundle').entry if (!Array.isArray(entries)) throw new Error('Expected Bundle.entry.') const documentEntry: unknown = entries.find( @@ -136,39 +127,17 @@ const mutableRecordingGraph = (): { } } -describe('Provider native recording graph', () => { - it('enforces the catalog provider scope for native recordings', () => { - const oura = rawInput('oura', 'heartrate') - expect( - parseProviderRecordingBundleInput({ - ...oura, - source: { - ...oura.source, - providerScopeIdentifier: { - system: uri('https://example.org/provider-accounts'), - value: 'account-pseudonym', - assurance: 'deployment-scoped-account-pseudonym', - }, - }, - }).ok, - ).toBe(false) - - const withings = rawInput('withings', 'activityIntraday') - expect( - parseProviderRecordingBundleInput({ - ...withings, - source: { - ...withings.source, - providerScopeIdentifier: { - system: uri('https://example.org/provider-key-spaces'), - value: 'withings-global', - assurance: 'documented-global-key-space', - }, - }, - }).ok, - ).toBe(false) - }) +const documentOf = (conversion: ProviderRecordingConversion) => { + const document = conversion.graph.entry.find( + ({ resource }) => resource.resourceType === 'DocumentReference', + )?.resource + if (document?.resourceType !== 'DocumentReference') { + throw new Error('Missing recording document.') + } + return document +} +describe('Provider native recording graph', () => { it.each([ 'google-health-heart-rate-recording.json', 'oura-heart-rate-recording.json', @@ -179,32 +148,28 @@ describe('Provider native recording graph', () => { readFileSync(new URL(`../fixtures/normalized/${name}`, import.meta.url), { encoding: 'utf8', }), - ) as Pick - const graph = rawInput('google-health-api', 'heart-rate') - expect( - buildProviderRecordingBundle({ - ...graph, - ...fixture, - }).ok, - ).toBe(true) + ) as { + source: ProviderRecordingSource + attachment: ProviderRecordingAttachment + } + expect(parseProviderRecordingSource(fixture.source).ok).toBe(true) + expect(parseProviderRecordingAttachment(fixture.attachment).ok).toBe(true) + expect(build(fixture.source, fixture.attachment).ok).toBe(true) }) it.each(recordingSources)( 'admits the exact catalogued %s/%s raw source', (provider, sourceType) => { - const result = buildProviderRecordingBundle( - rawInput(provider as ConnectedRawProvider, sourceType), + const result = build( + rawSource(provider as ConnectedRawProvider, sourceType), ) expect(result.ok).toBe(true) if (!result.ok) return - expect(result.value.entry).toHaveLength(4) - expect(Object.isFrozen(result.value)).toBe(true) - const document = result.value.entry.find( - ({ resource }) => resource.resourceType === 'DocumentReference', - )?.resource - expect(document?.resourceType).toBe('DocumentReference') - if (document?.resourceType !== 'DocumentReference') return + expect(result.value.graph.entry).toHaveLength(5) + expect(Object.isFrozen(result.value.graph)).toBe(true) + expect(result.value.warnings).toEqual([]) + const document = documentOf(result.value) expect(document.meta?.profile).toEqual([ 'https://grovealliance.org/fhir/sensor/StructureDefinition/grove-sensor-recording-document', 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-recording-document', @@ -220,40 +185,33 @@ describe('Provider native recording graph', () => { }, ]) expect(document.identifier?.map(({ system }) => system)).toEqual([ - deploymentIdentity.opaqueIdentifierSystems['provider-record'], - ( - deploymentIdentity.opaqueIdentifierSystems as Readonly< - Record - > - )['provider-output'], - ( - deploymentIdentity.opaqueIdentifierSystems as Readonly< - Record - > - )['provider-artifact'], + identityScope.systems.opaque['provider-record'], + identityScope.systems.opaque['provider-output'], + identityScope.systems.opaque['provider-artifact'], ]) expect(document.id).toBeUndefined() + expect(document.date).toBe(conversionInstant) + expect(result.value.identifiers.sourceArtifact?.role).toBe( + 'source-artifact', + ) + expect(result.value.identifiers.outputs).toHaveLength(1) }, ) it('keeps optional Attachment.title presentation text optional end to end', () => { - const input = rawInput('oura', 'heartrate') - const attachment = structuredClone(input.attachment) + const attachment = structuredClone(embedded) Reflect.deleteProperty(attachment, 'title') - const result = buildProviderRecordingBundle({ ...input, attachment }) + const result = build(rawSource('oura', 'heartrate'), attachment) expect(result.ok).toBe(true) if (!result.ok) return - const document = result.value.entry.find( - ({ resource }) => resource.resourceType === 'DocumentReference', - )?.resource - expect(document?.resourceType).toBe('DocumentReference') - if (document?.resourceType !== 'DocumentReference') return - expect(document.content[0]?.attachment.title).toBeUndefined() - expect(parseGroveMobileExchangeBundle(result.value).ok).toBe(true) + expect( + documentOf(result.value).content[0]?.attachment.title, + ).toBeUndefined() + expect(parseExchangeGraph(result.value.graph).ok).toBe(true) }) it('keeps an unversioned writer identity and never emits a version without it', () => { - const input = rawInput('withings', 'activityIntraday') + const input = rawSource('withings', 'activityIntraday') const writerRecord = { applicationIdentifier: { system: uri('https://example.org/applications'), @@ -261,48 +219,38 @@ describe('Provider native recording graph', () => { }, nativeRecordId: 'writer-record-1', } as const - const unversioned = buildProviderRecordingBundle({ - ...input, - source: { ...input.source, writerRecord }, - }) - const versioned = buildProviderRecordingBundle({ + const unversioned = build({ ...input, writerRecord }) + const versioned = build({ ...input, - source: { - ...input.source, - writerRecord: { ...writerRecord, version: '7' }, - }, - eventSequence: '2', + writerRecord: { ...writerRecord, version: '7' }, }) expect(unversioned.ok && versioned.ok).toBe(true) if (!unversioned.ok || !versioned.ok) return - const document = (result: typeof unversioned) => - result.value.entry.find( - ({ resource }) => resource.resourceType === 'DocumentReference', - )?.resource const writerVersionUrl = 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-writer-record-version' - const hasWriterIdentity = (resource: ReturnType) => - resource?.resourceType === 'DocumentReference' && - resource.identifier?.some((candidate) => + const hasWriterIdentity = (conversion: ProviderRecordingConversion) => + documentOf(conversion).identifier?.some((candidate) => candidate.type?.coding?.some(({ code }) => code === 'writer-record'), ) - - expect(hasWriterIdentity(document(unversioned))).toBe(true) + expect(hasWriterIdentity(unversioned.value)).toBe(true) + expect(unversioned.value.identifiers.writerRecord?.role).toBe( + 'writer-record', + ) expect( - document(unversioned)?.extension?.some( + documentOf(unversioned.value).extension?.some( ({ url }) => url === writerVersionUrl, ), ).not.toBe(true) - expect(hasWriterIdentity(document(versioned))).toBe(true) - expect(document(versioned)?.extension).toEqual( + expect(hasWriterIdentity(versioned.value)).toBe(true) + expect(documentOf(versioned.value).extension).toEqual( expect.arrayContaining([{ url: writerVersionUrl, valueString: '7' }]), ) expect( - parseProviderRecordingBundleInput({ - ...input, - source: { ...input.source, writerRecordVersion: '1' }, - }).ok, - ).toBe(false) + parseProviderRecordingSource({ ...input, writerRecordVersion: '1' }), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-shape-invalid' }], + }) }) it('accepts the catalog-owned direct-only HealthKit clinical document claim', () => { @@ -314,9 +262,7 @@ describe('Provider native recording graph', () => { if (healthKitMarker === undefined) { throw new Error('Expected the generated HealthKit source marker.') } - document.meta = { - profile: [healthKitClinicalRecordAdmission.profile], - } + document.meta = { profile: [healthKitClinicalRecordAdmission.profile] } document.extension = [ { url: healthKitMarker.url, @@ -355,33 +301,24 @@ describe('Provider native recording graph', () => { mutableObject(mutableObject(entry, 'entry').resource, 'resource') .resourceType === 'Provenance', ) - if (provenanceEntry === undefined) { - throw new Error('Expected conversion Provenance.') - } - const provenance = mutableObject( + mutableObject( mutableObject(provenanceEntry, 'Provenance entry').resource, 'Provenance', - ) - provenance.meta = { + ).meta = { profile: [ 'https://grovealliance.org/fhir/healthkit/StructureDefinition/healthkit-conversion-provenance', ], } + expect(parseExchangeGraph(bundle).ok).toBe(true) const wrongDirectProfiles = structuredClone(bundle) - const wrongEntries = mutableObject( - wrongDirectProfiles, - 'wrong-profile Bundle', - ).entry + const wrongEntries = mutableObject(wrongDirectProfiles, 'Bundle').entry if (!Array.isArray(wrongEntries)) throw new Error('Expected Bundle.entry.') const wrongDocumentEntry: unknown = wrongEntries.find( (entry: unknown) => mutableObject(mutableObject(entry, 'entry').resource, 'resource') .resourceType === 'DocumentReference', ) - if (wrongDocumentEntry === undefined) { - throw new Error('Expected DocumentReference.') - } mutableObject( mutableObject(wrongDocumentEntry, 'DocumentReference entry').resource, 'DocumentReference', @@ -391,24 +328,19 @@ describe('Provider native recording graph', () => { healthKitClinicalRecordAdmission.profile, ], } - expect(parseGroveMobileExchangeBundle(bundle).ok).toBe(true) - expect(parseGroveMobileExchangeBundle(wrongDirectProfiles).ok).toBe(false) + expect(parseExchangeGraph(wrongDirectProfiles).ok).toBe(false) const clinicalDocumentIn = ( candidate: unknown, ): Record => { const candidateEntries = mutableObject(candidate, 'clinical Bundle').entry - if (!Array.isArray(candidateEntries)) { + if (!Array.isArray(candidateEntries)) throw new Error('Expected Bundle.entry.') - } const candidateDocumentEntry: unknown = candidateEntries.find( (entry: unknown) => mutableObject(mutableObject(entry, 'entry').resource, 'resource') .resourceType === representation.resourceType, ) - if (candidateDocumentEntry === undefined) { - throw new Error(`Expected ${representation.resourceType}.`) - } return mutableObject( mutableObject(candidateDocumentEntry, 'clinical entry').resource, 'clinical DocumentReference', @@ -419,7 +351,7 @@ describe('Provider native recording graph', () => { Array.isArray(candidate.content) ? candidate.content[0] : undefined, 'clinical content', ) - const invalidRepresentations = [ + for (const mutate of [ (candidate: Record) => { mutableObject( contentOf(candidate).attachment, @@ -430,52 +362,43 @@ describe('Provider native recording graph', () => { mutableObject(contentOf(candidate).format, 'format').code = 'fhir-collection-bundle' }, - ] - for (const mutate of invalidRepresentations) { + ]) { const candidate = structuredClone(bundle) mutate(clinicalDocumentIn(candidate)) - const result = parseGroveMobileExchangeBundle(candidate) + const result = parseExchangeGraph(candidate) expect(result.ok).toBe(false) if (result.ok) continue expect(result.issues.map(({ code }) => code)).toContain( 'healthkit-clinical.fhir-representation', ) } - for (const release of healthKitClinicalRecordAdmission.admittedFHIRReleases) { const candidate = structuredClone(bundle) mutableObject( contentOf(clinicalDocumentIn(candidate)).attachment, 'attachment', ).contentType = representation.contentTypeByRelease[release] - expect(parseGroveMobileExchangeBundle(candidate).ok).toBe(true) + expect(parseExchangeGraph(candidate).ok).toBe(true) } }) it('emits exact embedded bytes, computed SHA-1 integrity, and a complete audit graph', () => { - const input = rawInput('google-health-api', 'heart-rate') - const result = buildProviderRecordingBundle(input) + const source = rawSource('google-health-api', 'heart-rate') + const result = build(source) expect(result.ok).toBe(true) if (!result.ok) return - - const documentEntry = result.value.entry.find( - ({ resource }) => resource.resourceType === 'DocumentReference', - ) - const provenance = result.value.entry.find( + const document = documentOf(result.value) + const provenance = result.value.graph.entry.find( ({ resource }) => resource.resourceType === 'Provenance', )?.resource - expect(documentEntry?.resource.resourceType).toBe('DocumentReference') - if (documentEntry?.resource.resourceType !== 'DocumentReference') return - expect(documentEntry.resource.content).toEqual([ + expect(document.content).toEqual([ { attachment: { - contentType: - groveRecordingFormatRegistry.formats['provider-recording'] - .contentTypes[0], + contentType: embedded.contentType, data: 'AQID', size: 3, hash: 'cDeAcZjCKn0rCAc3HXY3eahP388=', - title: 'Authorized minimized provider recording', + title: embedded.title, }, format: { system: @@ -485,23 +408,29 @@ describe('Provider native recording graph', () => { }, }, ]) - expect(documentEntry.resource.status).toBe('current') - expect(documentEntry.resource.date).toBe(input.documentDate) - expect(documentEntry.resource.author).toHaveLength(1) - expect(provenance?.resourceType).toBe('Provenance') - if (provenance?.resourceType !== 'Provenance') return + expect(document.status).toBe('current') + expect(document.author).toHaveLength(1) + if (provenance?.resourceType !== 'Provenance') + throw new Error('No Provenance.') expect(provenance.meta?.profile).toEqual([ 'https://grovealliance.org/fhir/providers/StructureDefinition/providers-conversion-provenance', ]) - expect(provenance.target).toEqual([{ reference: documentEntry.fullUrl }]) - expect(provenance.entity).toHaveLength(1) + expect(provenance.target).toEqual([ + { reference: result.value.graph.entry[0].fullUrl }, + ]) + expect(provenance.occurredPeriod).toEqual({ + start: '2026-08-20T00:00:00.000Z', + end: '2026-08-20T12:00:00.000Z', + }) + expect(provenance.recorded).toBe(conversionInstant) expect(provenance.entity?.[0]?.what.identifier?.system).toBe( - deploymentIdentity.opaqueIdentifierSystems['provider-record'], + identityScope.systems.opaque['provider-record'], + ) + const serialized = JSON.stringify(result.value.graph) + expect(serialized).not.toContain(source.sourceNativeId) + expect(serialized).not.toContain( + context('google-health-api').repositoryScope.value, ) - const serialized = JSON.stringify(result.value) - expect(serialized).not.toContain(input.source.sourceNativeId) - expect(serialized).not.toContain(input.source.providerScopeIdentifier.value) - expect(serialized).not.toContain(input.attachment.payloadAssertion) expect(serialized).not.toContain('payloadAssertion') }) @@ -513,9 +442,10 @@ describe('Provider native recording graph', () => { if (!Array.isArray(identifiers)) throw new Error('Expected identifiers.') document.identifier = identifiers.filter((candidate) => { - const identifier = mutableObject(candidate, 'Identifier') - const type = mutableObject(identifier.type, 'Identifier.type') - const codings = type.coding + const codings = mutableObject( + mutableObject(candidate, 'Identifier').type, + 'Identifier.type', + ).coding return !( Array.isArray(codings) && codings.some( @@ -533,12 +463,10 @@ describe('Provider native recording graph', () => { const identifiers = document.identifier if (!Array.isArray(identifiers)) throw new Error('Expected identifiers.') - const sourceArtifact = mutableObject( - identifiers[2], - 'source-artifact Identifier', - ) - const type = mutableObject(sourceArtifact.type, 'Identifier.type') - const codings = type.coding + const codings = mutableObject( + mutableObject(identifiers[2], 'source-artifact Identifier').type, + 'Identifier.type', + ).coding if (!Array.isArray(codings)) throw new Error('Expected type coding.') mutableObject(codings[0], 'Identifier.type.coding').code = 'source-context' @@ -549,11 +477,10 @@ describe('Provider native recording graph', () => { mutate: (document: MutableJsonObject) => { const content = document.content if (!Array.isArray(content)) throw new Error('Expected content.') - const format = mutableObject( + mutableObject( mutableObject(content[0], 'content').format, - 'content.format', - ) - format.version = '1.2.3' + 'format', + ).version = '1.2.3' }, }, { @@ -561,11 +488,10 @@ describe('Provider native recording graph', () => { mutate: (document: MutableJsonObject) => { const content = document.content if (!Array.isArray(content)) throw new Error('Expected content.') - const attachment = mutableObject( + mutableObject( mutableObject(content[0], 'content').attachment, - 'content.attachment', - ) - attachment.contentType = 'application/octet-stream' + 'attachment', + ).contentType = 'application/octet-stream' }, }, { @@ -581,11 +507,10 @@ describe('Provider native recording graph', () => { mutate: (document: MutableJsonObject) => { const content = document.content if (!Array.isArray(content)) throw new Error('Expected content.') - const attachment = mutableObject( + mutableObject( mutableObject(content[0], 'content').attachment, - 'content.attachment', - ) - attachment.size = 4 + 'attachment', + ).size = 4 }, }, { @@ -593,11 +518,10 @@ describe('Provider native recording graph', () => { mutate: (document: MutableJsonObject) => { const content = document.content if (!Array.isArray(content)) throw new Error('Expected content.') - const attachment = mutableObject( + mutableObject( mutableObject(content[0], 'content').attachment, - 'content.attachment', - ) - attachment.hash = 'AAAAAAAAAAAAAAAAAAAAAAAAAAA=' + 'attachment', + ).hash = 'AAAAAAAAAAAAAAAAAAAAAAAAAAA=' }, }, ...(['type', 'subject', 'date'] as const).map((property) => ({ @@ -609,31 +533,17 @@ describe('Provider native recording graph', () => { ])('rejects a recording document with $name', ({ mutate }) => { const { bundle, document } = mutableRecordingGraph() mutate(document) - expect(parseGroveMobileExchangeBundle(bundle).ok).toBe(false) + expect(parseExchangeGraph(bundle).ok).toBe(false) }) it('matches the frozen raw source/output identity vectors', () => { - const input = rawInput('google-health-api', 'heart-rate') - const result = buildProviderRecordingBundle({ - ...input, - source: { - ...input.source, - providerScopeIdentifier: { - system: uri('https://provider.example.org/accounts'), - value: 'account-001', - assurance: 'deployment-scoped-account-pseudonym', - }, - sourceNativeId: 'heart-rate-2026-08-20', - }, - } as ProviderRecordingBundleInput) + const result = build({ + ...rawSource('google-health-api', 'heart-rate'), + sourceNativeId: 'heart-rate-2026-08-20', + }) expect(result.ok).toBe(true) if (!result.ok) return - const document = result.value.entry.find( - ({ resource }) => resource.resourceType === 'DocumentReference', - )?.resource - if (document?.resourceType !== 'DocumentReference') { - throw new Error('Missing recording document') - } + const document = documentOf(result.value) expect(document.identifier).toHaveLength(3) for (const entry of document.identifier ?? []) { expect(entry.value).toMatch(/^v0:test-key:1:[A-Za-z0-9_-]{43}$/u) @@ -641,30 +551,33 @@ describe('Provider native recording graph', () => { }) it('places an explicitly governed native Identifier only on the sole recording DocumentReference', () => { - const input = rawInput('google-health-api', 'heart-rate') + const source = rawSource('google-health-api', 'heart-rate') const nativeSystem = uri( 'https://example.org/repositories/google-account-4/recordings', ) - const result = buildProviderRecordingBundle({ - ...input, - nativeIdentifierDisclosure: { - system: nativeSystem, - nativeId: input.source.sourceNativeId, - type: { - coding: [ - { - system: uri('https://example.org/identifier-types'), - code: 'provider-record-id', - display: 'Provider record id', - }, - ], + const result = buildProviderRecordingGraph( + source, + embedded, + context('google-health-api'), + { + nativeIdentifierDisclosure: { + kind: 'authorized', + system: nativeSystem, + type: { + coding: [ + { + system: uri('https://example.org/identifier-types'), + code: 'provider-record-id', + display: 'Provider record id', + }, + ], + }, }, }, - }) + ) expect(result.ok).toBe(true) if (!result.ok) return - - const carryingResources = result.value.entry.filter(({ resource }) => + const carrying = result.value.graph.entry.filter(({ resource }) => ( resource as { readonly identifier?: ReadonlyArray<{ @@ -674,18 +587,18 @@ describe('Provider native recording graph', () => { } ).identifier?.some( ({ system, value }) => - system === nativeSystem && value === input.source.sourceNativeId, + system === nativeSystem && value === source.sourceNativeId, ), ) - expect(carryingResources).toHaveLength(1) - const document = carryingResources[0]?.resource - expect(document?.resourceType).toBe('DocumentReference') - if (document?.resourceType !== 'DocumentReference') return + expect(carrying).toHaveLength(1) + expect(carrying[0]?.resource.resourceType).toBe('DocumentReference') expect( - document.identifier?.find(({ system }) => system === nativeSystem), + documentOf(result.value).identifier?.find( + ({ system }) => system === nativeSystem, + ), ).toEqual({ system: nativeSystem, - value: input.source.sourceNativeId, + value: source.sourceNativeId, type: { coding: [ { @@ -699,32 +612,19 @@ describe('Provider native recording graph', () => { }) it.each([ - { - name: 'a mismatched native value', - disclosure: (input: ProviderRecordingBundleInput) => ({ - system: uri('https://example.org/repositories/recordings'), - nativeId: `${input.source.sourceNativeId}-wrong`, - }), - }, - { - name: 'a relative key-space system', - disclosure: (input: ProviderRecordingBundleInput) => ({ - system: 'recordings', - nativeId: input.source.sourceNativeId, - }), - }, - { - name: 'the Grove entry identity system', - disclosure: (input: ProviderRecordingBundleInput) => ({ - system: input.deploymentIdentity.entryNodeIdentifierSystem, - nativeId: input.source.sourceNativeId, - }), - }, - { - name: 'a Grove graph-role type coding', - disclosure: (input: ProviderRecordingBundleInput) => ({ - system: uri('https://example.org/repositories/recordings'), - nativeId: input.source.sourceNativeId, + [ + 'a relative key-space system', + { kind: 'authorized', system: 'recordings' }, + ], + [ + 'the Grove entry identity system', + { kind: 'authorized', system: identityScope.systems.entryNode }, + ], + [ + 'a Grove graph-role type coding', + { + kind: 'authorized', + system: 'https://example.org/repositories/recordings', type: { coding: [ { @@ -734,78 +634,60 @@ describe('Provider native recording graph', () => { }, ], }, - }), - }, - ])('rejects recording source disclosure using $name', ({ disclosure }) => { - const input = rawInput('oura', 'heartrate') - const candidate = { - ...input, - nativeIdentifierDisclosure: disclosure(input), - } as unknown as ProviderRecordingBundleInput - expect(parseProviderRecordingBundleInput(candidate).ok).toBe(false) - expect(buildProviderRecordingBundle(candidate).ok).toBe(false) + }, + ], + ])('rejects recording source disclosure using %s', (_name, disclosure) => { + const result = buildProviderRecordingGraph( + rawSource('oura', 'heartrate'), + embedded, + context('oura'), + { nativeIdentifierDisclosure: disclosure as never }, + ) + expect(result.ok).toBe(false) + if (result.ok) return + for (const issue of result.issues) expect(issue.code).not.toMatch(/\./u) }) it('supports an immutable external attachment without copying or fetching it', () => { - const input = rawInput('oura', 'heartrate') - const result = buildProviderRecordingBundle({ - ...input, - attachment: { - kind: 'external', - contentType: unwrap( - parseMediaType( - groveRecordingFormatRegistry.formats['provider-recording'] - .contentTypes[0], - ), - ), - title: 'Authorized minimized Oura recording', - format: 'provider-recording', - payloadAssertion: 'verified-sanitized-input', - url: unwrap( - parseImmutableRecordingUrl( - 'https://objects.example.org/recordings/version-42', - ), + const result = build(rawSource('oura', 'heartrate'), { + kind: 'external', + contentType: embedded.contentType, + title: 'Authorized minimized Oura recording', + format: 'provider-recording', + payloadAssertion: 'verified-sanitized-input', + url: unwrap( + parseImmutableRecordingUrl( + 'https://objects.example.org/recordings/version-42', ), - size: unwrap(parsePositiveInteger(3)), - hash: unwrap(parseSha1Base64('cDeAcZjCKn0rCAc3HXY3eahP388=')), - immutabilityAssurance: 'immutable-version-specific', - }, + ), + size: unwrap(parsePositiveInteger(3)), + hash: unwrap(parseSha1Base64('cDeAcZjCKn0rCAc3HXY3eahP388=')), + immutabilityAssurance: 'immutable-version-specific', }) expect(result.ok).toBe(true) if (!result.ok) return - const document = result.value.entry.find( - ({ resource }) => resource.resourceType === 'DocumentReference', - )?.resource - if (document?.resourceType !== 'DocumentReference') return - expect(document.content[0]?.attachment).toMatchObject({ + expect(documentOf(result.value).content[0]?.attachment).toMatchObject({ url: 'https://objects.example.org/recordings/version-42', size: 3, hash: 'cDeAcZjCKn0rCAc3HXY3eahP388=', }) - expect(document.content[0]?.attachment.data).toBeUndefined() - const serialized = JSON.stringify(result.value) + expect(documentOf(result.value).content[0]?.attachment.data).toBeUndefined() + const serialized = JSON.stringify(result.value.graph) expect(serialized).not.toContain('verified-sanitized-input') - expect(serialized).not.toContain('payloadAssertion') }) it('retains repository-assigned ids while keeping digest identities out of Resource.id', () => { - const input = rawInput('withings', 'activityIntraday') - const result = buildProviderRecordingBundle({ - ...input, + const result = build(rawSource('withings', 'activityIntraday'), embedded, { repositoryIds: { bundle: unwrap(parseFhirId('bundle-42')), - document: unwrap(parseFhirId('document-42')), + 'primary-output': unwrap(parseFhirId('document-42')), provenance: unwrap(parseFhirId('provenance-42')), }, }) expect(result.ok).toBe(true) if (!result.ok) return - expect(result.value.id).toBe('bundle-42') - expect( - result.value.entry.find( - ({ resource }) => resource.resourceType === 'DocumentReference', - )?.resource.id, - ).toBe('document-42') + expect(result.value.graph.id).toBe('bundle-42') + expect(documentOf(result.value).id).toBe('document-42') }) it.each([ @@ -814,37 +696,37 @@ describe('Provider native recording graph', () => { ['oura', 'sleep'], ['withings', 'getmeas:11'], ] as const)('fails closed for non-raw %s/%s sources', (provider, source) => { - expect(buildProviderRecordingBundle(rawInput(provider, source)).ok).toBe( - false, - ) + expect(build(rawSource(provider, source))).toMatchObject({ + ok: false, + issues: [ + { code: 'mobile-input.unsupported-source-type', path: ['sourceType'] }, + ], + }) }) - it('rejects unknown provider fields rather than silently stripping them', () => { - const input = rawInput('google-health-api', 'heart-rate') + it('refuses unknown provider fields rather than silently stripping them', () => { + const source = rawSource('google-health-api', 'heart-rate') expect( - parseProviderRecordingBundleInput({ - ...input, - source: { ...input.source, vendorPayload: { bpm: 64 } }, - }).ok, - ).toBe(false) + parseProviderRecordingSource({ ...source, vendorPayload: { bpm: 64 } }), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-shape-invalid' }], + }) expect( - parseProviderRecordingBundleInput({ - ...input, - attachment: { ...input.attachment, bearerToken: 'secret' }, - }).ok, - ).toBe(false) + parseProviderRecordingAttachment({ ...embedded, bearerToken: 'secret' }), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.value-shape-invalid' }], + }) }) it('fails closed for absent, ambiguous, or unsupported raw payload assertions', () => { - const input = rawInput('google-health-api', 'heart-rate') const attachmentWithoutAssertion = { kind: 'embedded', - contentType: input.attachment.contentType, - title: input.attachment.title, - dataBase64: - input.attachment.kind === 'embedded' ? - input.attachment.dataBase64 - : 'AQID', + contentType: embedded.contentType, + title: embedded.title, + dataBase64: 'AQID', + format: 'provider-recording', } for (const attachment of [ attachmentWithoutAssertion, @@ -857,36 +739,40 @@ describe('Provider native recording graph', () => { }, { ...attachmentWithoutAssertion, payloadAssertion: 'unreviewed' }, ]) { - expect( - parseProviderRecordingBundleInput({ - ...input, - attachment, - }).ok, - ).toBe(false) + expect(parseProviderRecordingAttachment(attachment).ok).toBe(false) } }) it.each([ - ['empty data', { dataBase64: '' }], - ['noncanonical base64', { dataBase64: 'AQI' }], - ['invalid media type', { contentType: 'application/json; charset=utf-8' }], - ])('rejects %s', (_name, change) => { - const input = rawInput('google-health-api', 'heart-rate') + ['empty data', { dataBase64: '' }, 'mobile-input.empty-recording-series'], + [ + 'noncanonical base64', + { dataBase64: 'AQI' }, + 'mobile-input.empty-recording-series', + ], + [ + 'invalid media type', + { contentType: 'application/json; charset=utf-8' }, + 'mobile-input.value-shape-invalid', + ], + [ + 'an unregistered media type', + { contentType: 'text/plain' }, + 'mobile-input.unsupported-source-value', + ], + ])('refuses %s', (_name, change, code) => { expect( - buildProviderRecordingBundle({ - ...input, - attachment: { ...input.attachment, ...change }, - } as ProviderRecordingBundleInput).ok, - ).toBe(false) + parseProviderRecordingAttachment({ ...embedded, ...change }), + ).toMatchObject({ + ok: false, + issues: [{ code }], + }) }) - it('rejects invalid external integrity and URL metadata', () => { - const input = rawInput('oura', 'heartrate') + it('refuses invalid external integrity and URL metadata', () => { const external = { kind: 'external', - contentType: - groveRecordingFormatRegistry.formats['provider-recording'] - .contentTypes[0], + contentType: embedded.contentType, title: 'Authorized recording', format: 'provider-recording', payloadAssertion: 'caller-authorized-opaque-payload', @@ -895,24 +781,18 @@ describe('Provider native recording graph', () => { hash: 'AQID', immutabilityAssurance: 'immutable-version-specific', } as const + expect(parseProviderRecordingAttachment(external).ok).toBe(false) expect( - buildProviderRecordingBundle({ - ...input, - attachment: external, - } as unknown as ProviderRecordingBundleInput).ok, - ).toBe(false) - - expect( - buildProviderRecordingBundle({ - ...input, - attachment: { - ...external, - url: 'https://objects.example.org/recording/version-42', - size: 2_147_483_648, - hash: 'cDeAcZjCKn0rCAc3HXY3eahP388=', - }, - } as unknown as ProviderRecordingBundleInput).ok, - ).toBe(false) + parseProviderRecordingAttachment({ + ...external, + url: 'https://objects.example.org/recording/version-42', + size: 2_147_483_648, + hash: 'cDeAcZjCKn0rCAc3HXY3eahP388=', + }), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.recording-payload-too-large' }], + }) }) it('rejects a 32-byte SHA-256 digest where R4 requires a 20-byte SHA-1 hash', () => { @@ -949,122 +829,86 @@ describe('Provider native recording graph', () => { const cyclic: Record = {} cyclic.self = cyclic for (const invalid of [null, undefined, 42, 'invalid', cyclic]) { - expect(() => parseProviderRecordingBundleInput(invalid)).not.toThrow() - expect(parseProviderRecordingBundleInput(invalid).ok).toBe(false) - expect(() => buildProviderRecordingBundle(invalid as never)).not.toThrow() - expect(buildProviderRecordingBundle(invalid as never).ok).toBe(false) + expect(() => parseProviderRecordingSource(invalid)).not.toThrow() + expect(parseProviderRecordingSource(invalid).ok).toBe(false) + expect(() => parseProviderRecordingAttachment(invalid)).not.toThrow() + expect(parseProviderRecordingAttachment(invalid).ok).toBe(false) + expect(() => + buildProviderRecordingGraph( + invalid as never, + invalid as never, + invalid as never, + ), + ).not.toThrow() + expect( + buildProviderRecordingGraph( + invalid as never, + invalid as never, + invalid as never, + ).ok, + ).toBe(false) } }) it('deduplicates one application snapshot across participation roles', () => { - const input = rawInput('withings', 'sleepIntraday') - const result = buildProviderRecordingBundle({ - ...input, - source: { ...input.source, dataOrigin: input.application }, + const result = build({ + ...rawSource('withings', 'sleepIntraday'), + writer: application, }) expect(result.ok).toBe(true) if (!result.ok) return expect( - result.value.entry.filter( + result.value.graph.entry.filter( ({ resource }) => resource.resourceType === 'Device', ), - ).toHaveLength(1) - expect(result.value.entry).toHaveLength(3) + ).toHaveLength(2) + expect(result.value.graph.entry).toHaveLength(4) }) - it.each(['source', 'application', 'data-origin'] as const)( - 'rejects invalid Unicode identity input in the %s identity role', - (role) => { - const input = rawInput('google-health-api', 'heart-rate') - const invalid = '\ud800' - let candidate: ProviderRecordingBundleInput - if (role === 'source') { - candidate = { - ...input, - source: { ...input.source, sourceNativeId: invalid }, - } - } else if (role === 'application') { - candidate = { - ...input, - application: { - ...input.application, - sourceDeviceToken: invalid, - }, - } - } else { - candidate = { - ...input, - source: { - ...input.source, - dataOrigin: { - ...input.source.dataOrigin, - sourceDeviceToken: invalid, - }, - }, - } - } - expect(parseProviderRecordingBundleInput(candidate).ok).toBe(false) - expect(buildProviderRecordingBundle(candidate).ok).toBe(false) - }, - ) + it('refuses invalid Unicode in the source and faults it in the context', () => { + const invalid = '\ud800' + const source = rawSource('google-health-api', 'heart-rate') + expect( + parseProviderRecordingSource({ ...source, sourceNativeId: invalid }), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.text-not-unicode-scalar' }], + }) + expect( + parseProviderRecordingSource({ + ...source, + writer: { ...source.writer, sourceDeviceToken: invalid }, + }), + ).toMatchObject({ + ok: false, + issues: [{ code: 'mobile-input.text-not-unicode-scalar' }], + }) + const faulted = build(source, embedded, { + application: { ...application, sourceDeviceToken: invalid }, + }) + expect(faulted.ok).toBe(false) + if (!faulted.ok) expect(faulted.issues[0]?.code).toBe('invalid-identifier') + }) it.each([ - [ - 'provider account value', - (input: ProviderRecordingBundleInput) => ({ - ...input, - source: { - ...input.source, - providerScopeIdentifier: { - ...input.source.providerScopeIdentifier, - value: ' ', - }, - }, - }), - ], [ 'source native id', - (input: ProviderRecordingBundleInput) => ({ - ...input, - source: { ...input.source, sourceNativeId: '\t' }, - }), - ], - [ - 'source type', - (input: ProviderRecordingBundleInput) => ({ - ...input, - source: { ...input.source, sourceType: '\n ' }, - }), + { sourceNativeId: '\t' }, + 'mobile-input.native-identifier-invalid', ], + ['source type', { sourceType: '\n ' }, 'mobile-input.value-shape-invalid'], [ - 'converter source-device token', - (input: ProviderRecordingBundleInput) => ({ - ...input, - application: { - ...input.application, - sourceDeviceToken: ' ', - }, - }), + 'writer token', + { writer: { sourceDeviceToken: ' \t ', name: 'x' } }, + 'mobile-input.value-shape-invalid', ], - [ - 'data-origin source-device token', - (input: ProviderRecordingBundleInput) => ({ - ...input, - source: { - ...input.source, - dataOrigin: { - ...input.source.dataOrigin, - sourceDeviceToken: ' \t ', - }, - }, - }), - ], - ] as const)('rejects a whitespace-only %s', (_name, mutate) => { + ] as const)('refuses a whitespace-only %s', (_name, change, code) => { expect( - parseProviderRecordingBundleInput( - mutate(rawInput('google-health-api', 'heart-rate')), - ).ok, - ).toBe(false) + parseProviderRecordingSource({ + ...rawSource('google-health-api', 'heart-rate'), + ...change, + }), + ).toMatchObject({ ok: false, issues: [{ code }] }) }) it('round-trips arbitrary non-empty byte arrays to canonical base64', () => { @@ -1074,12 +918,9 @@ describe('Provider native recording graph', () => { expect(encoded.ok).toBe(true) if (!encoded.ok) return expect( - parseProviderRecordingBundleInput({ - ...rawInput('google-health-api', 'heart-rate'), - attachment: { - ...rawInput('google-health-api', 'heart-rate').attachment, - dataBase64: encoded.value, - }, + parseProviderRecordingAttachment({ + ...embedded, + dataBase64: encoded.value, }).ok, ).toBe(true) }), @@ -1087,11 +928,7 @@ describe('Provider native recording graph', () => { }) it('exposes a result-typed, closed recording facade', () => { - const result = buildProviderRecordingBundle( - rawInput('google-health-api', 'heart-rate'), - ) - expectTypeOf(result).toExtend< - Result - >() + const result = build(rawSource('google-health-api', 'heart-rate')) + expectTypeOf(result).toExtend>() }) }) diff --git a/packages/fhir/test/retraction.test.ts b/packages/fhir/test/retraction.test.ts index 50f75eb0..19894605 100644 --- a/packages/fhir/test/retraction.test.ts +++ b/packages/fhir/test/retraction.test.ts @@ -7,443 +7,261 @@ // import { - parseAbsoluteUri, - parseFhirInstant, - type FhirInstant, - type Result, -} from '../src/core/index.js' + context, + conversionInstant, + heartRateMeasurement, + identityScope, + instant, + record, + scopeInput, + study, + unwrap, + uri, +} from './provider-test-support.js' +import { groveExchangeProtocol } from '../src/contract/measurement-catalog.generated.js' import { - groveMobileContract, - type DeploymentIdentityInput, + validateOpaqueIdentityScope, + type RoledIdentifier, } from '../src/mobile/index.js' import { - buildProviderMeasurementBundle, - buildProviderRecordingBundle, - buildProviderRetractionBundle, + buildProviderExchangeGraph, + buildProviderRecordingGraph, + buildProviderRetractionEvent, encodeRecordingBytes, groveRecordingFormatRegistry, parseMediaType, - parseProviderRetractionInput, - providerOutputCoordinates, - type ProviderMeasurementBundleInput, - type ProviderRecordingBundleInput, - type ProviderRetractionInput, + type ProviderRecordingSource, + type NormalizedProviderRecord, } from '../src/providers/index.js' -import { parseGroveMobileRetractionBundle } from '../src/r4/index.js' - -const unwrap = (result: Result): Value => { - if (!result.ok) { - throw new Error(result.issues.map(({ message }) => message).join('\n')) - } - return result.value -} - -const uri = (value: string) => unwrap(parseAbsoluteUri(value)) -const instant = (value: string): FhirInstant => unwrap(parseFhirInstant(value)) -const patient = { - type: 'Patient', - identifier: { - system: uri('https://example.org/deployments/patient-pseudonyms'), - value: 'patient-example', - assurance: 'deployment-scoped-pseudonym', - }, -} as const - -const deploymentIdentity = { - opaqueIdentifierSystems: { - 'source-record': uri('https://example.org/identity/source-record/key/1'), - 'source-output': uri('https://example.org/identity/source-output/key/1'), - 'writer-record': uri('https://example.org/identity/writer-record/key/1'), - 'provider-record': uri( - 'https://example.org/identity/provider-record/key/1', - ), - 'provider-output': uri( - 'https://example.org/identity/provider-output/key/1', - ), - 'source-artifact': uri( - 'https://example.org/identity/source-artifact/key/1', - ), - 'provider-artifact': uri( - 'https://example.org/identity/provider-artifact/key/1', - ), - 'source-context': uri('https://example.org/identity/source-context/key/1'), - 'recording-device': uri( - 'https://example.org/identity/recording-device/key/1', - ), - 'device-snapshot': uri( - 'https://example.org/identity/device-snapshot/key/1', - ), - }, - eventIdentifierSystem: uri('https://example.org/identity/event'), - entryNodeIdentifierSystem: uri('https://example.org/identity/entry-node'), - keyId: 'unit-key', - keyEpoch: '1', - secretBase64Url: 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY', - producerInstance: '9ae7b610-bac2-4f13-97b4-53b84b8a90cf', -} as const satisfies DeploymentIdentityInput - -const source = { - adapter: { kind: 'providers', provider: 'withings' }, - providerScopeIdentifier: { - system: uri('https://example.org/provider-accounts'), - value: 'participant-pseudonym', - assurance: 'deployment-scoped-account-pseudonym', - }, - sourceType: 'getmeas:11', - sourceNativeId: 'provider-record-17348211', - recordingMethod: 'automatically-recorded', - dataOrigin: { - sourceDeviceToken: 'withings-cloud', - name: 'Withings', - }, -} as const +import { + parseRetractionEvent, + retractionTargets, + type Provenance, + type RetractionEvent, + type RetractionTarget, +} from '../src/r4/index.js' -const activeInput = { - subject: patient, - measurements: [ - { - kind: 'heart-rate', - value: 64, - effective: { - kind: 'date-time', - value: instant('2026-08-20T12:00:00Z'), - }, - }, - ], - source, - application: { - sourceDeviceToken: 'converter-build-42', - name: 'Grove converter', - version: '1.2.3', - build: '42', - }, - eventSequence: '100', - deploymentIdentity, - occurred: instant('2026-08-20T12:00:00Z'), - recorded: instant('2026-08-20T12:02:00Z'), - assembled: instant('2026-08-20T12:03:00Z'), -} as const satisfies ProviderMeasurementBundleInput +const retractedAt = instant('2026-08-21T10:00:00Z') -const outputCoordinates = providerOutputCoordinates( - 'withings', - 'getmeas:11', - 'heart-rate', +const active = unwrap( + buildProviderExchangeGraph( + record('withings', 'getmeas:11', heartRateMeasurement), + context('withings', '100'), + ), ) -if (outputCoordinates === undefined) { - throw new Error( - 'The test requires the catalog-owned heart-rate output coordinates.', - ) +const targets = unwrap(retractionTargets(active.graph)) +const primary = targets.find(({ role }) => role === 'primary-output') +if (primary === undefined) throw new Error('No primary output target.') + +const provenanceOf = (event: RetractionEvent): Provenance => { + const provenance = event.entry.find( + ({ resource }) => resource.resourceType === 'Provenance', + )?.resource + if (provenance?.resourceType !== 'Provenance') { + throw new Error('The retraction graph did not contain Provenance.') + } + return provenance } -const retractionInput = { - source: { - provider: source.adapter.provider, - providerScopeIdentifier: source.providerScopeIdentifier, - sourceType: source.sourceType, - sourceNativeId: source.sourceNativeId, - }, - targets: [ - { - role: 'primary-output', - resourceType: 'Observation', - ...outputCoordinates, - }, - ], - application: activeInput.application, - eventSequence: '101', - deploymentIdentity, - occurred: instant('2026-08-21T10:00:00Z'), - recorded: instant('2026-08-21T10:01:00Z'), - assembled: instant('2026-08-21T10:02:00Z'), -} as const satisfies ProviderRetractionInput +const roleExtension = groveExchangeProtocol.extensions.retractionTargetRole +const nativeExtension = + groveExchangeProtocol.extensions.retractionTargetNativeIdentifier -describe('Provider source-record retraction', () => { - it('rejects a provider-scope assurance that contradicts the catalog', () => { - expect( - parseProviderRetractionInput({ - ...retractionInput, - source: { - ...retractionInput.source, - providerScopeIdentifier: { - system: uri('https://example.org/provider-key-spaces'), - value: 'withings-global', - assurance: 'documented-global-key-space', - }, - }, - }).ok, - ).toBe(false) - }) +const byText = (left: string, right: string): number => + left.localeCompare(right) - it('reports malformed retraction inputs without throwing', () => { - const cyclic: Record = {} - cyclic.self = cyclic - for (const invalid of [null, undefined, 42, 'invalid', cyclic]) { - expect(() => parseProviderRetractionInput(invalid)).not.toThrow() - expect(parseProviderRetractionInput(invalid).ok).toBe(false) - expect(() => - buildProviderRetractionBundle(invalid as never), - ).not.toThrow() - expect(buildProviderRetractionBundle(invalid as never).ok).toBe(false) - } - }) - it('targets the exact prior output identity in a separate append-only event', () => { - const active = unwrap(buildProviderMeasurementBundle(activeInput)) - const priorObservation = active.entry.find( +describe('Provider source-record retraction', () => { + it('derives every retractable node of an accepted graph', () => { + expect(targets.map(({ role }) => role).sort(byText)).toEqual([ + 'device-snapshot', + 'device-snapshot', + 'device-snapshot', + 'primary-output', + ]) + const observation = active.graph.entry.find( ({ resource }) => resource.resourceType === 'Observation', )?.resource - if (priorObservation?.resourceType !== 'Observation') { - throw new Error('The active graph did not contain its Observation.') - } - const priorOutput = priorObservation.identifier?.find((candidate) => + if (observation?.resourceType !== 'Observation') + throw new Error('No Observation.') + const sourceOutput = observation.identifier?.find((candidate) => candidate.type?.coding?.some(({ code }) => code === 'source-output'), ) + expect(primary).toEqual({ + role: 'primary-output', + resourceType: 'Observation', + identifier: { + system: sourceOutput?.system, + value: sourceOutput?.value, + role: 'source-output', + }, + }) + expect(retractionTargets({} as never).ok).toBe(false) + }) - const retraction = unwrap(buildProviderRetractionBundle(retractionInput)) + it('targets the exact prior output identity in a separate append-only event', () => { + const retraction = unwrap( + buildProviderRetractionEvent( + [primary], + context('withings', '101'), + active.identifiers.sourceRecord, + retractedAt, + ), + ) expect(retraction.meta?.profile).toEqual([ - 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-mobile-retraction-bundle', + groveExchangeProtocol.profiles.retractionBundle, ]) - expect(retraction.entry).toHaveLength(2) - const provenance = retraction.entry.find( - ({ resource }) => resource.resourceType === 'Provenance', - )?.resource expect( - retraction.entry.some( - ({ resource }) => resource.resourceType === 'Device', - ), - ).toBe(true) - if (provenance === undefined) { - throw new Error('The retraction graph did not contain Provenance.') - } - expect(provenance.resourceType).toBe('Provenance') - if (provenance.resourceType !== 'Provenance') return + retraction.entry.map(({ resource }) => resource.resourceType), + ).toEqual(['Device', 'Device', 'Provenance']) + const provenance = provenanceOf(retraction) expect(provenance.target).toEqual([ { - extension: [ - { - url: 'https://grovealliance.org/fhir/mobile/StructureDefinition/grove-retraction-target-role', - valueCode: 'primary-output', - }, - ], + extension: [{ url: roleExtension, valueCode: 'primary-output' }], type: 'Observation', - identifier: priorOutput, + identifier: { + system: primary.identifier.system, + value: primary.identifier.value, + type: { + coding: [ + { + system: groveExchangeProtocol.codeSystems.identifierRole, + code: 'source-output', + }, + ], + }, + }, }, ]) expect(provenance.target[0]).not.toHaveProperty('reference') expect(provenance.activity?.coding).toContainEqual( expect.objectContaining({ - system: - 'https://grovealliance.org/fhir/mobile/CodeSystem/grove-lifecycle-event', - code: 'source-record-retracted', + system: groveExchangeProtocol.codeSystems.lifecycleEvent, + code: groveExchangeProtocol.lifecycle.retraction.activityCode, }), ) - expect(provenance.occurredDateTime).toBe(retractionInput.occurred) - expect(provenance.recorded).toBe(retractionInput.recorded) - expect(retraction.timestamp).toBe(retractionInput.assembled) + expect(provenance.occurredDateTime).toBe(retractedAt) + expect(provenance.recorded).toBe(conversionInstant) + expect(retraction.timestamp).toBe(conversionInstant) + expect(provenance.entity?.[0]?.what.identifier?.value).toBe( + active.identifiers.sourceRecord.value, + ) expect(JSON.stringify(retraction)).not.toMatch(/entered-in-error/u) + expect(parseRetractionEvent(retraction).ok).toBe(true) }) it('rejects additional direct profiles on retraction Provenance', () => { const retraction = structuredClone( - unwrap(buildProviderRetractionBundle(retractionInput)), + unwrap( + buildProviderRetractionEvent( + [primary], + context('withings', '101'), + active.identifiers.sourceRecord, + retractedAt, + ), + ), ) - const provenance = retraction.entry.find( - ({ resource }) => resource.resourceType === 'Provenance', - )?.resource - if (provenance?.resourceType !== 'Provenance') { - throw new Error('The retraction graph did not contain Provenance.') - } - const profiles = provenance.meta?.profile as + const profiles = provenanceOf(retraction).meta?.profile as Array | undefined profiles?.push( 'https://example.org/fhir/StructureDefinition/unrelated-provenance', ) - - const parsed = parseGroveMobileRetractionBundle(retraction) + const parsed = parseRetractionEvent(retraction) expect(parsed.ok).toBe(false) if (parsed.ok) return expect(parsed.issues).toContainEqual( - expect.objectContaining({ - code: 'mobile-retraction.provenance-profile', - }), + expect.objectContaining({ code: 'mobile-exchange.provenance-profile' }), ) }) it('uses the selected source-output identity when retracting a source artifact', () => { - const recordingInput = { - source: { - adapter: { kind: 'providers', provider: 'withings' }, - providerScopeIdentifier: source.providerScopeIdentifier, - sourceType: 'activityIntraday', - sourceNativeId: 'provider-record-activity-1', - dataOrigin: source.dataOrigin, - }, - attachment: { - kind: 'embedded', - contentType: unwrap( - parseMediaType( - groveRecordingFormatRegistry.formats['provider-recording'] - .contentTypes[0], + const source: ProviderRecordingSource = { + adapter: { kind: 'providers', provider: 'withings' }, + sourceType: 'activityIntraday', + sourceNativeId: 'provider-record-activity-1', + writer: { sourceDeviceToken: 'withings-cloud', name: 'Withings' }, + effective: { kind: 'date-time', value: instant('2026-08-20T12:00:00Z') }, + } + const recording = unwrap( + buildProviderRecordingGraph( + source, + { + kind: 'embedded', + contentType: unwrap( + parseMediaType( + groveRecordingFormatRegistry.formats['provider-recording'] + .contentTypes[0], + ), ), - ), - title: 'Authorized minimized provider recording', - format: 'provider-recording', - payloadAssertion: 'verified-sanitized-input', - dataBase64: unwrap(encodeRecordingBytes(Uint8Array.of(1, 2, 3))), - }, - subject: activeInput.subject, - application: activeInput.application, - eventSequence: '200', - deploymentIdentity, - documentDate: instant('2026-08-20T12:01:00Z'), - occurred: instant('2026-08-20T12:00:00Z'), - recorded: instant('2026-08-20T12:02:00Z'), - assembled: instant('2026-08-20T12:03:00Z'), - } as const satisfies ProviderRecordingBundleInput - const active = unwrap(buildProviderRecordingBundle(recordingInput)) - const document = active.entry.find( + format: 'provider-recording', + payloadAssertion: 'verified-sanitized-input', + dataBase64: unwrap(encodeRecordingBytes(Uint8Array.of(1, 2, 3))), + }, + context('withings', '200'), + ), + ) + const document = recording.graph.entry.find( ({ resource }) => resource.resourceType === 'DocumentReference', )?.resource - if (document?.resourceType !== 'DocumentReference') { - throw new Error( - 'The active recording graph did not contain its document.', - ) - } + if (document?.resourceType !== 'DocumentReference') + throw new Error('No document.') const sourceOutput = document.identifier?.find((candidate) => candidate.type?.coding?.some(({ code }) => code === 'source-output'), ) - const sourceArtifact = document.identifier?.find((candidate) => - candidate.type?.coding?.some(({ code }) => code === 'source-artifact'), + const artifact = unwrap(retractionTargets(recording.graph)).find( + ({ role }) => role === 'source-artifact', ) - expect(sourceOutput).toBeDefined() - expect(sourceArtifact).toBeDefined() - expect(sourceOutput).not.toEqual(sourceArtifact) - + expect(artifact).toMatchObject({ + resourceType: 'DocumentReference', + identifier: { system: sourceOutput?.system, value: sourceOutput?.value }, + }) + if (artifact === undefined) return const retraction = unwrap( - buildProviderRetractionBundle({ - source: { - provider: 'withings', - providerScopeIdentifier: source.providerScopeIdentifier, - sourceType: 'activityIntraday', - sourceNativeId: 'provider-record-activity-1', - }, - targets: [ - { - role: 'source-artifact', - resourceType: 'DocumentReference', - formatCode: 'provider-recording', - partIndex: '0', - }, - ], - application: activeInput.application, - eventSequence: '201', - deploymentIdentity, - occurred: instant('2026-08-21T10:00:00Z'), - recorded: instant('2026-08-21T10:01:00Z'), - assembled: instant('2026-08-21T10:02:00Z'), - }), + buildProviderRetractionEvent( + [artifact], + context('withings', '201'), + recording.identifiers.sourceRecord, + retractedAt, + ), ) - const provenance = retraction.entry.find( - ({ resource }) => resource.resourceType === 'Provenance', - )?.resource - if (provenance?.resourceType !== 'Provenance') { - throw new Error('The retraction graph did not contain Provenance.') - } - expect(provenance.target[0]?.identifier).toEqual(sourceOutput) + expect(provenanceOf(retraction).target[0]?.identifier).toEqual(sourceOutput) }) it('targets the exact prior recording-device snapshot', () => { - const stableUnitToken = 'recording-unit-1' - const active = unwrap( - buildProviderMeasurementBundle({ - ...activeInput, - source: { - ...activeInput.source, - recordingDevice: { - stableUnitToken, - subjectIdentifier: { - system: uri('https://example.org/participants'), - value: 'participant-pseudonym-001', + const conversion = unwrap( + buildProviderExchangeGraph( + { + ...record('withings', 'getmeas:11', heartRateMeasurement), + source: { + ...record('withings', 'getmeas:11', heartRateMeasurement).source, + recordingDevice: { + stableUnitToken: 'recording-unit-1', + name: 'Recorder', }, - identityScope: 'deployment-scoped', - name: 'Recorder', }, - }, - }), - ) - const device = active.entry.find( - ({ resource }) => - resource.resourceType === 'Device' && - resource.meta?.profile?.some( - (profile) => profile === groveMobileContract.profiles.recordingDevice, - ), - )?.resource - if (device?.resourceType !== 'Device') { - throw new Error('The active graph did not contain its recording Device.') - } - const snapshot = device.identifier?.find((candidate) => - candidate.type?.coding?.some(({ code }) => code === 'device-snapshot'), + } as NormalizedProviderRecord, + context('withings', '100'), + ), ) - - const retraction = unwrap( - buildProviderRetractionBundle({ - ...retractionInput, - eventSequence: '102', - targets: [ - { - role: 'device-snapshot', - resourceType: 'Device', - priorEventSequence: activeInput.eventSequence, - deviceRole: 'recording-device', - sourceDeviceToken: stableUnitToken, - }, - ], - }), + const snapshot = conversion.identifiers.recordingDeviceSnapshot + const target = unwrap(retractionTargets(conversion.graph)).find( + ({ identifier }) => identifier.value === snapshot?.value, ) - const provenance = retraction.entry.find( - ({ resource }) => resource.resourceType === 'Provenance', - )?.resource - if (provenance?.resourceType !== 'Provenance') { - throw new Error('The retraction graph did not contain Provenance.') - } - expect(provenance.target[0]?.identifier).toEqual(snapshot) - }) - - it('requires a device snapshot event to precede the retraction event', () => { - const target = { + expect(target).toMatchObject({ role: 'device-snapshot', resourceType: 'Device', - priorEventSequence: '9999999999999999999999999999999999999999', - deviceRole: 'application', - sourceDeviceToken: activeInput.application.sourceDeviceToken, - } as const - const eventSequence = '10000000000000000000000000000000000000000' - - expect( - parseProviderRetractionInput({ - ...retractionInput, - eventSequence, - targets: [target], - }).ok, - ).toBe(true) - expect( - parseProviderRetractionInput({ - ...retractionInput, - eventSequence, - targets: [{ ...target, priorEventSequence: eventSequence }], - }).ok, - ).toBe(false) - expect( - parseProviderRetractionInput({ - ...retractionInput, - eventSequence, - targets: [ - { - ...target, - priorEventSequence: '10000000000000000000000000000000000000001', - }, - ], - }).ok, - ).toBe(false) + }) + if (target === undefined) return + const retraction = unwrap( + buildProviderRetractionEvent( + [target], + context('withings', '102'), + conversion.identifiers.sourceRecord, + retractedAt, + ), + ) + expect(provenanceOf(retraction).target[0]?.identifier?.value).toBe( + snapshot?.value, + ) }) it('carries the adapter native record identifier on its target', () => { @@ -452,155 +270,195 @@ describe('Provider source-record retraction', () => { value: 'record-heart-001', } const retraction = unwrap( - buildProviderRetractionBundle({ - ...retractionInput, - targets: [{ ...retractionInput.targets[0], nativeIdentifier }], - }), + buildProviderRetractionEvent( + [{ ...primary, nativeIdentifier }], + context('withings', '101'), + active.identifiers.sourceRecord, + retractedAt, + ), ) - const provenance = retraction.entry.find( - ({ resource }) => resource.resourceType === 'Provenance', - )?.resource - if (provenance?.resourceType !== 'Provenance') { - throw new Error('The retraction graph did not contain Provenance.') - } - expect(provenance.target[0]?.extension).toEqual([ - { - url: groveMobileContract.extensions.retractionTargetRole, - valueCode: 'primary-output', - }, - { - url: groveMobileContract.extensions.retractionTargetNativeIdentifier, - valueIdentifier: nativeIdentifier, - }, + expect(provenanceOf(retraction).target[0]?.extension).toEqual([ + { url: roleExtension, valueCode: 'primary-output' }, + { url: nativeExtension, valueIdentifier: nativeIdentifier }, ]) - - const groveRoleCoding = { - ...retraction, - entry: retraction.entry.map((entry) => - entry.resource.resourceType === 'Provenance' ? + expect( + buildProviderRetractionEvent( + [ { - ...entry, - resource: { - ...entry.resource, - target: [ - { - ...entry.resource.target[0], - extension: [ - entry.resource.target[0]?.extension?.[0], - { - url: groveMobileContract.extensions - .retractionTargetNativeIdentifier, - valueIdentifier: { - ...nativeIdentifier, - type: { - coding: [ - { - system: - groveMobileContract.systems.identifierRole, - code: 'source-record', - }, - ], - }, - }, - }, - ], - }, - ], - }, - } - : entry, + ...primary, + nativeIdentifier: { system: primary.identifier.system, value: 'x' }, + }, + ], + context('withings', '101'), + active.identifiers.sourceRecord, + retractedAt, + ), + ).toMatchObject({ + ok: false, + issues: [ + { + code: 'value-mismatch', + path: ['targets', 0, 'nativeIdentifier', 'system'], + }, + ], + }) + }) + + it('carries a governed source identifier from the primary output into its target', () => { + const disclosed = unwrap( + buildProviderExchangeGraph( + record('withings', 'getmeas:11', heartRateMeasurement), + context('withings', '100'), + { + nativeIdentifierDisclosure: { + kind: 'authorized', + system: uri( + 'https://example.org/repositories/withings-account-7/heart-rate-records', + ), + }, + }, ), - } - const result = parseGroveMobileRetractionBundle(groveRoleCoding) - expect(result.ok).toBe(false) - if (result.ok) return - expect(result.issues.map(({ code }) => code)).toContain( - 'mobile-retraction.native-record-identifier', ) + const target = unwrap(retractionTargets(disclosed.graph)).find( + ({ role }) => role === 'primary-output', + ) + expect(target?.nativeIdentifier).toEqual({ + system: + 'https://example.org/repositories/withings-account-7/heart-rate-records', + value: 'native-withings-getmeas:11', + }) }) it('emits retraction targets in a deterministic canonical order', () => { - const deviceTarget = { - role: 'device-snapshot', - resourceType: 'Device', - priorEventSequence: activeInput.eventSequence, - deviceRole: 'application', - sourceDeviceToken: activeInput.application.sourceDeviceToken, - } as const - const primaryTarget = retractionInput.targets[0] + const devices = targets.filter(({ role }) => role === 'device-snapshot') const forward = unwrap( - buildProviderRetractionBundle({ - ...retractionInput, - targets: [primaryTarget, deviceTarget], - }), + buildProviderRetractionEvent( + [primary, ...devices], + context('withings', '101'), + active.identifiers.sourceRecord, + retractedAt, + ), ) const reversed = unwrap( - buildProviderRetractionBundle({ - ...retractionInput, - targets: [deviceTarget, primaryTarget], - }), + buildProviderRetractionEvent( + [...devices].reverse().concat(primary) as [ + RetractionTarget, + ...RetractionTarget[], + ], + context('withings', '101'), + active.identifiers.sourceRecord, + retractedAt, + ), ) - expect(reversed).toEqual(forward) }) - it('returns structured issues for malformed JavaScript input', () => { - expect(() => - buildProviderRetractionBundle( - undefined as unknown as ProviderRetractionInput, - ), - ).not.toThrow() - expect( - parseProviderRetractionInput({ - ...retractionInput, - unexpected: true, - }).ok, - ).toBe(false) - expect( - parseProviderRetractionInput({ - ...retractionInput, - deploymentIdentity: { - ...deploymentIdentity, - secretBase64Url: 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8', - }, - }).ok, - ).toBe(false) + it('reports malformed retraction inputs without throwing', () => { + const cyclic: Record = {} + cyclic.self = cyclic + for (const invalid of [null, undefined, 42, 'invalid', cyclic]) { + expect(() => + buildProviderRetractionEvent( + invalid as never, + invalid as never, + invalid as never, + invalid as never, + ), + ).not.toThrow() + expect( + buildProviderRetractionEvent( + [primary], + invalid as never, + active.identifiers.sourceRecord, + retractedAt, + ).ok, + ).toBe(false) + } }) - it('rejects a duplicated logical target and role/type mismatch', () => { - const duplicated = buildProviderRetractionBundle({ - ...retractionInput, - targets: [retractionInput.targets[0], retractionInput.targets[0]], + it('rejects targets and sources the context did not mint', () => { + const other = unwrap( + validateOpaqueIdentityScope({ + ...scopeInput, + systems: { + ...scopeInput.systems, + opaque: { + ...scopeInput.systems.opaque, + 'provider-output': uri('https://other.example.org/provider-output'), + }, + }, + }), + ) + const build = ( + candidates: readonly [RetractionTarget, ...RetractionTarget[]], + sourceRecord: RoledIdentifier = active.identifiers.sourceRecord, + overrides = {}, + ) => + buildProviderRetractionEvent( + candidates, + context('withings', '101', overrides), + sourceRecord, + retractedAt, + ) + expect(build([primary, primary])).toMatchObject({ + ok: false, + issues: [{ code: 'duplicate-identifier', path: ['targets', 1] }], }) - expect(duplicated.ok).toBe(false) - - const mismatched = buildProviderRetractionBundle({ - ...retractionInput, - targets: [ + expect( + build([{ ...primary, resourceType: 'DocumentReference' }] as never), + ).toMatchObject({ ok: false, issues: [{ code: 'invalid-code' }] }) + expect(build([{ ...primary, role: 'specimen' }] as never)).toMatchObject({ + ok: false, + issues: [{ code: 'invalid-code' }], + }) + expect( + build([ { - role: 'source-artifact', - resourceType: 'Observation', - formatCode: 'provider-recording', - partIndex: '0', + ...primary, + identifier: { + ...primary.identifier, + system: other.systems.opaque['provider-output'], + }, }, - ], - } as unknown as ProviderRetractionInput) - expect(mismatched.ok).toBe(false) - }) - - it('rejects a source type outside the provider catalog', () => { - const result = parseProviderRetractionInput({ - ...retractionInput, - source: { - ...retractionInput.source, - sourceType: 'unregistered-source-type', - }, + ]), + ).toMatchObject({ ok: false, issues: [{ code: 'invalid-identifier' }] }) + expect( + build([ + { + ...primary, + identifier: { ...primary.identifier, value: 'clear-record' }, + }, + ]), + ).toMatchObject({ ok: false, issues: [{ code: 'invalid-identifier' }] }) + expect(build([] as never)).toMatchObject({ + ok: false, + issues: [{ code: 'missing-required', path: ['targets'] }], }) - expect(result.ok).toBe(false) - if (!result.ok) { - expect(result.issues).toContainEqual( - expect.objectContaining({ path: ['source', 'sourceType'] }), - ) - } + expect(build([primary], { ...primary.identifier })).toMatchObject({ + ok: false, + issues: [{ code: 'invalid-identifier', path: ['sourceRecord'] }], + }) + expect( + build([primary], undefined, { studies: [study('a')] }), + ).toMatchObject({ + ok: false, + issues: [{ code: 'value-mismatch', path: ['studies'] }], + }) + expect( + buildProviderRetractionEvent( + [primary], + context('withings', '101'), + active.identifiers.sourceRecord, + '2026-08-21' as never, + ), + ).toMatchObject({ ok: false, issues: [{ code: 'invalid-date-time' }] }) + expect( + buildProviderRetractionEvent( + [primary], + { ...context('withings', '101'), identityScope: { ...identityScope } }, + active.identifiers.sourceRecord, + retractedAt, + ).ok, + ).toBe(false) }) }) diff --git a/packages/fhir/test/rule-test-support.ts b/packages/fhir/test/rule-test-support.ts index 7e9bd2d3..13fd5efb 100644 --- a/packages/fhir/test/rule-test-support.ts +++ b/packages/fhir/test/rule-test-support.ts @@ -24,7 +24,6 @@ export const refinementIssues = ( const rule = groveRuleIssueFromParameters( issue.code === 'custom' ? issue.params : undefined, path, - issue.message, ) return rule === undefined ? [ @@ -43,8 +42,11 @@ export const expectRule = ( refine: Refinement, resource: unknown, code: string, + location?: string, ): void => { - expect( - refinementIssues(refine, resource).map(({ code: actual }) => actual), - ).toContain(code) + const issues = refinementIssues(refine, resource) + expect(issues.map(({ code: actual }) => actual)).toContain(code) + if (location !== undefined) { + expect(issues.find((issue) => issue.code === code)?.location).toBe(location) + } } diff --git a/packages/fhir/test/study-context.test.ts b/packages/fhir/test/study-context.test.ts new file mode 100644 index 00000000..12c270c7 --- /dev/null +++ b/packages/fhir/test/study-context.test.ts @@ -0,0 +1,380 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { readFileSync } from 'node:fs' +import { + context, + heartRateMeasurement, + record, + resources, + study, + subject, + unwrap, +} from './provider-test-support.js' +import { groveExchangeProtocol } from '../src/contract/measurement-catalog.generated.js' +import type { Subject } from '../src/mobile/index.js' +import { + buildProviderExchangeGraph, + buildProviderRecordingGraph, + parseProviderRecordingSource, +} from '../src/providers/index.js' +import { parseExchangeGraph, type ExchangeGraph } from '../src/r4/index.js' + +const bundled: Subject = { + kind: 'bundled', + identifier: subject.identifier, + patient: { + resourceType: 'Patient', + identifier: [ + { system: subject.identifier.system, value: subject.identifier.value }, + ], + }, +} + +const entryKeyOf = ( + graph: ExchangeGraph, + resourceType: string, + ordinal = 0, +): { readonly value: string; readonly fullUrl: string } => { + const entries = graph.entry.filter( + ({ resource }) => resource.resourceType === resourceType, + ) + const entry = entries[ordinal] + const value = entry?.extension?.[0]?.valueIdentifier?.value + if (entry === undefined || value === undefined) { + throw new Error(`No ${resourceType} entry at ordinal ${String(ordinal)}.`) + } + return { value, fullUrl: entry.fullUrl } +} + +const corpusRoot = new URL( + '../fixtures/conformance/study-attribution/', + import.meta.url, +) +const corpus = (name: string): unknown => + JSON.parse(readFileSync(new URL(name, corpusRoot), 'utf8')) + +interface MutableGraph { + entry: Array<{ + fullUrl: string + extension?: Array<{ valueIdentifier?: { value: string } }> + resource: Record & { resourceType: string } + }> +} + +const mutableGraph = (): MutableGraph => + structuredClone( + unwrap( + buildProviderExchangeGraph( + record('withings', 'getmeas:11', heartRateMeasurement), + context('withings', '1', { studies: [study('a')] }), + ), + ).graph, + ) as unknown as MutableGraph + +const entryOf = ( + graph: MutableGraph, + resourceType: string, +): MutableGraph['entry'][number] => { + const entry = graph.entry.find( + ({ resource }) => resource.resourceType === resourceType, + ) + if (entry === undefined) throw new Error(`No ${resourceType} entry.`) + return entry +} + +const expectStudyContextRule = (graph: MutableGraph, location: string) => { + const result = parseExchangeGraph(graph) + expect(result.ok).toBe(false) + if (result.ok) return + expect( + result.issues + .filter(({ code }) => code === 'mobile-support.study-context') + .map((issue) => issue.location), + ).toContain(location) +} + +describe('study context', () => { + const roles = + groveExchangeProtocol.lifecycle.active.studyContext.entryNodeRoles + + it('bundles the enrollment as the catalog-keyed entries the IG recommends', () => { + const graph = unwrap( + buildProviderExchangeGraph( + record('withings', 'getmeas:11', heartRateMeasurement), + context('withings', '1', { subject: bundled, studies: [study('a')] }), + ), + ).graph + expect(resources(graph).map(({ resourceType }) => resourceType)).toEqual([ + 'Patient', + 'Observation', + 'ResearchStudy', + 'PlanDefinition', + 'ResearchSubject', + 'Device', + 'Device', + 'Device', + 'Provenance', + ]) + const patient = entryKeyOf(graph, 'Patient') + const researchStudy = entryKeyOf(graph, 'ResearchStudy') + const plan = entryKeyOf(graph, 'PlanDefinition') + const enrollment = entryKeyOf(graph, 'ResearchSubject') + expect(patient.value).toMatch(/^n0:patient:0:/u) + expect(researchStudy.value).toMatch(/^n0:research-study:0:/u) + expect(plan.value).toMatch(/^n0:plan-definition:0:/u) + expect(enrollment.value).toMatch(/^n0:research-subject:0:/u) + expect(roles).toEqual([ + 'patient', + 'research-study', + 'research-subject', + 'plan-definition', + ]) + + const observation = resources(graph).find( + (r) => r.resourceType === 'Observation', + ) + if (observation?.resourceType !== 'Observation') + throw new Error('No Observation.') + expect(observation.subject).toEqual({ reference: patient.fullUrl }) + expect(observation.extension).toContainEqual({ + url: 'http://hl7.org/fhir/StructureDefinition/workflow-researchStudy', + valueReference: { reference: researchStudy.fullUrl }, + }) + expect(JSON.stringify(graph)).not.toContain( + 'workflow-instantiatesCanonical', + ) + + const studyResource = resources(graph).find( + (r) => r.resourceType === 'ResearchStudy', + ) + if (studyResource?.resourceType !== 'ResearchStudy') + throw new Error('No study.') + expect(studyResource.identifier).toEqual([study('a').study]) + expect(studyResource.protocol).toEqual([{ reference: plan.fullUrl }]) + const planResource = resources(graph).find( + (r) => r.resourceType === 'PlanDefinition', + ) + if (planResource?.resourceType !== 'PlanDefinition') + throw new Error('No plan.') + expect(planResource.url).toBe(study('a').protocol.url) + expect(planResource.version).toBe('1') + const subjectResource = resources(graph).find( + (r) => r.resourceType === 'ResearchSubject', + ) + if (subjectResource?.resourceType !== 'ResearchSubject') + throw new Error('No subject.') + expect(subjectResource.identifier).toEqual([study('a').enrollment]) + expect(subjectResource.study).toEqual({ reference: researchStudy.fullUrl }) + expect(subjectResource.individual).toEqual({ reference: patient.fullUrl }) + expect(parseExchangeGraph(graph).ok).toBe(true) + }) + + it('numbers each role independently across two enrollments', () => { + const graph = unwrap( + buildProviderExchangeGraph( + record('withings', 'getmeas:11', heartRateMeasurement), + context('withings', '1', { studies: [study('a'), study('b')] }), + ), + ).graph + for (const [resourceType, role] of [ + ['ResearchStudy', 'research-study'], + ['PlanDefinition', 'plan-definition'], + ['ResearchSubject', 'research-subject'], + ] as const) { + expect(entryKeyOf(graph, resourceType, 0).value).toMatch( + new RegExp(`^n0:${role}:0:`, 'u'), + ) + expect(entryKeyOf(graph, resourceType, 1).value).toMatch( + new RegExp(`^n0:${role}:1:`, 'u'), + ) + } + const observation = resources(graph).find( + (r) => r.resourceType === 'Observation', + ) + if (observation?.resourceType !== 'Observation') + throw new Error('No Observation.') + expect( + observation.extension?.filter(({ url }) => + url?.endsWith('workflow-researchStudy'), + ), + ).toHaveLength(2) + }) + + it('keeps the logical subject by default and references it from the enrollment', () => { + const graph = unwrap( + buildProviderExchangeGraph( + record('withings', 'getmeas:11', heartRateMeasurement), + context('withings', '1', { studies: [study('a')] }), + ), + ).graph + expect(resources(graph).some((r) => r.resourceType === 'Patient')).toBe( + false, + ) + const subjectResource = resources(graph).find( + (r) => r.resourceType === 'ResearchSubject', + ) + if (subjectResource?.resourceType !== 'ResearchSubject') + throw new Error('No subject.') + expect(subjectResource.individual).toEqual({ + type: 'Patient', + identifier: { + system: subject.identifier.system, + value: subject.identifier.value, + }, + }) + }) + + it('rejects a study context on a graph whose output cannot carry it', () => { + const source = unwrap( + parseProviderRecordingSource({ + adapter: { kind: 'providers', provider: 'oura' }, + sourceType: 'heartrate', + sourceNativeId: 'series-1', + writer: { sourceDeviceToken: 'oura-app', name: 'Oura' }, + effective: { kind: 'date-time', value: '2026-08-20T12:00:00Z' }, + }), + ) + const result = buildProviderRecordingGraph( + source, + { + kind: 'embedded', + contentType: 'application/json' as never, + format: 'provider-recording', + payloadAssertion: 'caller-authorized-opaque-payload', + dataBase64: 'AQID' as never, + }, + context('oura', '1', { studies: [study('a')] }), + ) + expect(result).toMatchObject({ + ok: false, + issues: [{ code: 'value-mismatch', path: ['studies'] }], + }) + }) + + it('accepts both study-attribution corpus events and refuses the canonical protocol', () => { + expect(parseExchangeGraph(corpus('source-event.json')).ok).toBe(true) + expect(parseExchangeGraph(corpus('context-two-studies.json')).ok).toBe(true) + const negatives = corpus('negative-cases.json') as { + readonly cases: ReadonlyArray<{ + readonly id: string + readonly base: string + readonly patch: ReadonlyArray<{ + readonly op: string + readonly path: string + readonly value?: unknown + }> + readonly expectedRule: { readonly code: string } + }> + } + const protocolCase = negatives.cases.find( + ({ id }) => id === 'protocol-is-not-a-reference', + ) + const subjectCase = negatives.cases.find( + ({ id }) => id === 'subject-mismatch', + ) + if (protocolCase === undefined || subjectCase === undefined) + throw new Error('Corpus changed.') + const apply = ( + base: unknown, + patch: typeof protocolCase.patch, + ): unknown => { + const copy = structuredClone(base) as Record + for (const operation of patch) { + const parts = operation.path.slice(1).split('/') + const last = parts.pop() ?? '' + let parent: unknown = copy + for (const part of parts) { + parent = (parent as Record)[part] + } + ;(parent as Record)[last] = operation.value + } + return copy + } + const protocol = parseExchangeGraph( + apply(corpus('source-event.json'), protocolCase.patch), + ) + expect(protocol.ok).toBe(false) + if (!protocol.ok) { + expect(protocol.issues.map(({ code }) => code)).toContain( + protocolCase.expectedRule.code, + ) + } + // A foreign enrollment subject is a receiver decision, not a graph rule. + expect( + parseExchangeGraph(apply(corpus('source-event.json'), subjectCase.patch)) + .ok, + ).toBe(true) + }) + + it('reports a study entry keyed by a role outside the catalog', () => { + const tampered = mutableGraph() + const studyEntry = entryOf(tampered, 'ResearchStudy') + const key = studyEntry.extension?.[0]?.valueIdentifier + if (key === undefined) throw new Error('No study key.') + key.value = key.value.replace('research-study', 'study') + expectStudyContextRule( + tampered, + `Bundle.entry[${String(tampered.entry.indexOf(studyEntry))}].extension.valueIdentifier`, + ) + }) + + it.each([ + [ + 'a protocol without its exact revision', + (graph: MutableGraph) => { + delete entryOf(graph, 'PlanDefinition').resource.version + }, + 'PlanDefinition.version', + ], + [ + 'a study that names no protocol', + (graph: MutableGraph) => { + entryOf(graph, 'ResearchStudy').resource.protocol = [] + }, + 'ResearchStudy.protocol', + ], + [ + 'an enrollment linked to the protocol instead of the study', + (graph: MutableGraph) => { + entryOf(graph, 'ResearchSubject').resource.study = { + reference: entryOf(graph, 'PlanDefinition').fullUrl, + } + }, + 'ResearchSubject.study', + ], + [ + 'an enrollment that names no subject', + (graph: MutableGraph) => { + entryOf(graph, 'ResearchSubject').resource.individual = { + display: 'the participant', + } + }, + 'ResearchSubject.individual', + ], + ])('reports %s', (_name, mutate, location) => { + const tampered = mutableGraph() + mutate(tampered) + expectStudyContextRule(tampered, location) + }) + + it('reports a study without its enrollment', () => { + const tampered = mutableGraph() + const studyIndex = tampered.entry.indexOf( + entryOf(tampered, 'ResearchStudy'), + ) + tampered.entry.splice( + tampered.entry.indexOf(entryOf(tampered, 'ResearchSubject')), + 1, + ) + expectStudyContextRule( + tampered, + `Bundle.entry[${String(studyIndex)}].resource`, + ) + }) +}) diff --git a/packages/fhir/test/type-surface.json b/packages/fhir/test/type-surface.json new file mode 100644 index 00000000..38d1a67c --- /dev/null +++ b/packages/fhir/test/type-surface.json @@ -0,0 +1,513 @@ +{ + ".": { + "AbsoluteUri": "type AbsoluteUri = Branded<'AbsoluteUri'>;", + "annotationTexts": "const annotationTexts: (value: unknown) => readonly string[]", + "ApplicationDevice": "interface ApplicationDevice {\n readonly sourceDeviceToken: string;\n readonly name: string;\n readonly version?: string | undefined;\n readonly build?: string | undefined;\n}", + "appointmentServiceTypeDisplay": "const appointmentServiceTypeDisplay: (appointment: unknown) => string | undefined", + "BloodPressureMeasurement": "interface BloodPressureMeasurement {\n readonly kind: 'blood-pressure';\n readonly systolic: number;\n readonly diastolic: number;\n readonly effective: InstantEffectiveTime;\n}", + "boundsToFhirPeriod": "const boundsToFhirPeriod: (bounds: PeriodBounds) => {\n readonly start?: string;\n readonly end?: string;\n}", + "buildQuestionnaire": "const buildQuestionnaire: (input: QuestionnaireInput) => Result", + "buildQuestionnaireResponse": "const buildQuestionnaireResponse: (input: QuestionnaireResponseInput) => Result", + "BusinessIdentifier": "interface BusinessIdentifier {\n readonly system: AbsoluteUri;\n readonly value: string;\n}", + "Canonical": "type Canonical = Branded<'Canonical'>;", + "canonicalizeMobileEffectiveInstant": "const canonicalizeMobileEffectiveInstant: (value: unknown) => Result", + "carePlanCategoryDisplays": "const carePlanCategoryDisplays: (carePlan: unknown) => readonly string[]", + "carePlanCreatedDate": "const carePlanCreatedDate: (carePlan: unknown) => Date | undefined", + "carePlanNoteTexts": "const carePlanNoteTexts: (carePlan: unknown) => readonly string[]", + "carePlanPeriodEnd": "const carePlanPeriodEnd: (carePlan: unknown) => Date | undefined", + "carePlanPeriodIsActive": "const carePlanPeriodIsActive: (carePlan: unknown, asOf?: Date) => boolean", + "carePlanPeriodStart": "const carePlanPeriodStart: (carePlan: unknown) => Date | undefined", + "ChoiceQuantityMeasurement": "type ChoiceQuantityMeasurement = Extract;", + "ChoiceQuantityMeasurementKind": "type ChoiceQuantityMeasurementKind = MeasurementKindsWhere<'quantity', 'dateTime-or-Period'>;", + "codeableConceptDisplay": "const codeableConceptDisplay: (concept: unknown) => string | undefined", + "codeableConceptDisplays": "const codeableConceptDisplays: (concept: unknown) => readonly string[]", + "codesBySystem": "const codesBySystem: (concept: unknown, system: string) => readonly string[]", + "CodingParts": "interface CodingParts {\n readonly system?: string;\n readonly code?: string;\n readonly display?: string;\n}", + "codingToCodeableConcept": "const codingToCodeableConcept: (coding: CodingParts) => {\n readonly coding: readonly CodingParts[];\n readonly text?: string;\n}", + "collectResults": "const collectResults: (results: ReadonlyArray>) => Result", + "conditionAbatementDate": "const conditionAbatementDate: (condition: unknown) => Date | undefined", + "conditionCategoryDisplays": "const conditionCategoryDisplays: (condition: unknown) => readonly string[]", + "conditionClinicalStatus": "const conditionClinicalStatus: (condition: unknown) => string | undefined", + "conditionCodeDisplay": "const conditionCodeDisplay: (condition: unknown) => string | undefined", + "conditionDuration": "const conditionDuration: (condition: unknown) => number | undefined", + "conditionIsActive": "const conditionIsActive: (condition: unknown) => boolean", + "conditionIsConfirmed": "const conditionIsConfirmed: (condition: unknown) => boolean", + "conditionOnsetDate": "const conditionOnsetDate: (condition: unknown) => Date | undefined", + "conditionRecordedDate": "const conditionRecordedDate: (condition: unknown) => Date | undefined", + "contactPointBySystem": "const contactPointBySystem: (resource: unknown, system: string, use?: string) => string | undefined", + "contactPointsBySystem": "const contactPointsBySystem: (resource: unknown, system: string) => readonly string[]", + "containedResource": "const containedResource: (resource: unknown, reference: string) => unknown", + "containedResourcesByType": "const containedResourcesByType: (resource: unknown, resourceType: string) => readonly unknown[]", + "containsCoding": "const containsCoding: (concept: unknown, system: string, code: string) => boolean", + "ConversionBatch": "interface ConversionBatch {\n readonly conversions: readonly Conversion[];\n readonly failures: readonly Failure[];\n}", + "ConverterRole": "type ConverterRole = {\n readonly kind: 'assembler';\n} | {\n readonly kind: 'gateway';\n} | {\n readonly kind: 'gateway-application';\n readonly application: ApplicationDevice;\n};", + "createEntryIdentity": "const createEntryIdentity: (identifier: RoledIdentifier, id?: FhirId) => Result", + "dateToFhirDate": "const dateToFhirDate: (value: Date) => string", + "dateToFhirDateTime": "const dateToFhirDateTime: (value: Date) => string", + "dateToFhirInstant": "const dateToFhirInstant: (value: Date) => string", + "decodeBase64Binary": "const decodeBase64Binary: (value: unknown) => Result", + "deepFreeze": "const deepFreeze: (value: T) => ReadonlyDeep", + "DeploymentIdentifierSystems": "interface DeploymentIdentifierSystems {\n readonly opaque: OpaqueIdentitySystems;\n readonly event: AbsoluteUri;\n readonly entryNode: AbsoluteUri;\n}", + "deriveEntryFullUrl": "const deriveEntryFullUrl: (input: BusinessIdentifier) => Result", + "deriveEntryNodeIdentifier": "const deriveEntryNodeIdentifier: (scope: OpaqueIdentityScope, key: EntryNodeKey) => Result", + "deriveEntryNodeValue": "const deriveEntryNodeValue: (key: EntryNodeKey) => Result", + "deriveEventIdentifier": "const deriveEventIdentifier: (scope: OpaqueIdentityScope, sequence: EventSequence) => Result", + "deriveOpaqueIdentifier": "const deriveOpaqueIdentifier: (scope: OpaqueIdentityScope, identityKind: Kind, components: OpaqueIdentityComponents[Kind]) => Result", + "deriveOpaqueIdentitySystems": "const deriveOpaqueIdentitySystems: (root: AbsoluteUri, keyId: string, epoch: KeyEpoch) => Result", + "Device": "type Device = ReadonlyDeep>;", + "DocumentReference": "type DocumentReference = ReadonlyDeep>;", + "encodeBase64Binary": "const encodeBase64Binary: (bytes: Uint8Array) => string", + "encodeLengthFramedUtf8": "const encodeLengthFramedUtf8: (components: readonly string[]) => Result", + "entryIdentifierName": "const entryIdentifierName: (input: BusinessIdentifier) => Result", + "EntryIdentity": "interface EntryIdentity {\n readonly identifier: RoledIdentifier;\n readonly fullUrl: UrnUuid;\n readonly id?: FhirId;\n}", + "EntryNodeIdentifier": "type EntryNodeIdentifier = RoledIdentifier & {\n readonly role: 'entry-node';\n};", + "EntryNodeKey": "interface EntryNodeKey {\n readonly event: ExchangeEventIdentifier;\n readonly role: string;\n readonly ordinal: EntryNodeOrdinal;\n}", + "EntryNodeOrdinal": "type EntryNodeOrdinal = Branded<'EntryNodeOrdinal'>;", + "err": "const err: (code: IssueCode, message: string, path?: ReadonlyArray) => Result", + "EventSequence": "type EventSequence = Branded<'EventSequence'>;", + "ExchangeEventContext": "interface ExchangeEventContext {\n readonly subject: Subject;\n readonly event: ExchangeEventIdentifier;\n readonly identityScope: OpaqueIdentityScope;\n readonly repositoryScope: BusinessIdentifier;\n readonly application: ApplicationDevice;\n /** Required here: a server states its own facts, where a phone reads them from the OS. */\n readonly host: HostDevice;\n /** Defaults to the instant the builder runs. */\n readonly conversionInstant?: FhirInstant | undefined;\n readonly converterRole?: ConverterRole | undefined;\n readonly studies?: readonly StudyEnrollment[] | undefined;\n readonly repositoryIds?: Readonly>> | undefined;\n}", + "ExchangeEventIdentifier": "type ExchangeEventIdentifier = RoledIdentifier & {\n readonly role: 'event';\n};", + "ExchangeGraph": "type ExchangeGraph = GraphBundle;", + "ExchangeGraphIdentifiers": "interface ExchangeGraphIdentifiers {\n readonly event: ExchangeEventIdentifier;\n readonly sourceRecord: RoledIdentifier;\n /** Every output identity in Bundle order; a provider record has no child outputs. */\n readonly outputs: readonly [RoledIdentifier, ...RoledIdentifier[]];\n readonly provenance: RoledIdentifier;\n readonly applicationSnapshot: RoledIdentifier;\n readonly hostSnapshot: RoledIdentifier;\n readonly writerSnapshot: RoledIdentifier;\n readonly sourceArtifact?: RoledIdentifier;\n readonly recordingDevice?: RoledIdentifier;\n readonly recordingDeviceSnapshot?: RoledIdentifier;\n readonly gatewayApplicationSnapshot?: RoledIdentifier;\n readonly writerRecord?: RoledIdentifier;\n}", + "ExchangeGraphNode": "type ExchangeGraphNode = 'bundle' | 'primary-output' | 'source-artifact' | 'recording-device' | 'application-device' | 'host-device' | 'writer' | 'provenance';", + "extensionByUrl": "const extensionByUrl: (value: unknown, url: string) => unknown", + "extensionsByUrl": "const extensionsByUrl: (value: unknown, url: string) => readonly unknown[]", + "fhirDateTimeToDate": "const fhirDateTimeToDate: (value: unknown) => Result", + "FhirId": "type FhirId = Branded<'FhirId'>;", + "FhirInstant": "type FhirInstant = Branded<'FhirInstant'>;", + "FhirJson": "type FhirJson = Value extends JsonPrimitive | null | undefined ? Value : Value extends object ? FhirJsonObject : Value;", + "fhirPeriodToBounds": "const fhirPeriodToBounds: (period: unknown) => PeriodBounds", + "fhirQuantityToValue": "const fhirQuantityToValue: (value: unknown) => Result", + "formatHumanName": "const formatHumanName: (name: unknown, options?: HumanNameOptions) => string | undefined", + "GovernedSourceIdentifierDisclosurePolicy": "type GovernedSourceIdentifierDisclosurePolicy = {\n readonly kind: 'omit';\n} | {\n readonly kind: 'authorized';\n readonly system: AbsoluteUri;\n readonly type?: GovernedSourceIdentifierType | undefined;\n};", + "GovernedSourceIdentifierType": "interface GovernedSourceIdentifierType {\n readonly coding?: readonly [\n GovernedSourceIdentifierTypeCoding,\n ...GovernedSourceIdentifierTypeCoding[]\n ] | undefined;\n readonly text?: string | undefined;\n}", + "GovernedSourceIdentifierTypeCoding": "interface GovernedSourceIdentifierTypeCoding {\n readonly system: AbsoluteUri;\n readonly code: string;\n readonly display?: string | undefined;\n}", + "groveExchangeProtocol": "const groveExchangeProtocol: typeof groveExchangeProtocolValue", + "groveFhirVersion": "const groveFhirVersion: \"4.0.1\"", + "GroveIdentifierRole": "type GroveIdentifierRole = OpaqueIdentityDefinition['identifierRole'] | 'entry-node' | 'event';", + "groveMobilePackageMetadata": "const groveMobilePackageMetadata: typeof groveMobilePackageMetadataValue", + "groveMobileProfileCanonicals": "const groveMobileProfileCanonicals: typeof groveMobileProfileCanonicalsValue", + "groveProducerDiagnostics": "const groveProducerDiagnostics: typeof groveProducerDiagnosticsValue", + "groveProfileClaims": "const groveProfileClaims: typeof groveProfileClaimsValue", + "GroveQuestionnaire": "type GroveQuestionnaire = Readonly & {\n readonly item: NonNullable;\n}>;", + "GroveQuestionnaireResponse": "type GroveQuestionnaireResponse = Readonly & {\n readonly identifier: Identifier;\n readonly questionnaire: string;\n readonly authored: string;\n readonly item?: QuestionnaireResponse['item'];\n}>;", + "GroveRecordingFormat": "type GroveRecordingFormat = keyof typeof groveRecordingFormatRegistry.formats;", + "groveRecordingFormatRegistry": "const groveRecordingFormatRegistry: typeof groveRecordingFormatRegistryValue", + "HostDevice": "interface HostDevice {\n readonly sourceDeviceToken: string;\n readonly operatingSystemVersion: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", + "HumanNameOptions": "interface HumanNameOptions {\n readonly includePrefix?: boolean;\n readonly includeSuffix?: boolean;\n}", + "humanNameParts": "const humanNameParts: (name: unknown, options?: HumanNameOptions) => readonly string[]", + "identifierBySystem": "const identifierBySystem: (resource: unknown, system: string) => string | undefined", + "identifierByType": "const identifierByType: (resource: unknown, system: string, code: string) => string | undefined", + "IdentifierParts": "interface IdentifierParts {\n readonly system?: string;\n readonly value?: string;\n readonly use?: string;\n readonly type?: unknown;\n}", + "identifiersBySystem": "const identifiersBySystem: (resource: unknown, system: string) => readonly string[]", + "identifiersByType": "const identifiersByType: (resource: unknown, system: string, code: string) => readonly string[]", + "immunizationExpirationDate": "const immunizationExpirationDate: (immunization: unknown) => Date | undefined", + "immunizationIsExpired": "const immunizationIsExpired: (immunization: unknown, asOf?: Date) => boolean | undefined", + "immunizationNoteTexts": "const immunizationNoteTexts: (immunization: unknown) => readonly string[]", + "immunizationOccurrenceDate": "const immunizationOccurrenceDate: (immunization: unknown) => Date | undefined", + "immunizationReactionDisplays": "const immunizationReactionDisplays: (immunization: unknown) => readonly string[]", + "immunizationRecordedDate": "const immunizationRecordedDate: (immunization: unknown) => Date | undefined", + "immunizationRouteDisplay": "const immunizationRouteDisplay: (immunization: unknown) => string | undefined", + "immunizationSiteDisplay": "const immunizationSiteDisplay: (immunization: unknown) => string | undefined", + "immunizationVaccineDisplay": "const immunizationVaccineDisplay: (immunization: unknown) => string | undefined", + "InstantCodedMeasurement": "type InstantCodedMeasurement = {\n readonly [Kind in InstantCodedMeasurementKind]: {\n readonly kind: Kind;\n /** Shared code drawn from the catalog's closed allowed-value set. */\n readonly value: SharedCatalog[Kind]['allowedValues'][number];\n readonly effective: InstantEffectiveTime;\n };\n}[InstantCodedMeasurementKind];", + "InstantCodedMeasurementKind": "type InstantCodedMeasurementKind = MeasurementKindsWhere<'codeableConcept', 'dateTime'>;", + "InstantEffectiveTime": "interface InstantEffectiveTime {\n readonly kind: 'date-time';\n readonly value: FhirInstant;\n}", + "InstantQuantityMeasurement": "type InstantQuantityMeasurement = {\n readonly [Kind in InstantQuantityMeasurementKind]: {\n readonly kind: Kind;\n /** Value expressed in the catalog's canonical UCUM unit. */\n readonly value: number;\n readonly effective: InstantEffectiveTime;\n };\n}[InstantQuantityMeasurementKind];", + "InstantQuantityMeasurementKind": "type InstantQuantityMeasurementKind = MeasurementKindsWhere<'quantity', 'dateTime' | 'dateTime-or-Period'>;", + "isEntryNodeIdentityValue": "const isEntryNodeIdentityValue: (value: unknown) => boolean", + "isEventIdentityValue": "const isEventIdentityValue: (value: unknown) => boolean", + "isOpaqueIdentityScope": "const isOpaqueIdentityScope: (value: unknown) => value is OpaqueIdentityScope", + "isOpaqueIdentityValue": "const isOpaqueIdentityValue: (value: unknown) => boolean", + "isReferenceToType": "const isReferenceToType: (value: unknown, resourceType: string) => boolean", + "isSemanticallyEqual": "const isSemanticallyEqual: (left: string | Uint8Array, right: string | Uint8Array) => Result", + "Issue": "interface Issue {\n readonly severity: IssueSeverity;\n readonly code: IssueCode;\n readonly path: ReadonlyArray;\n readonly message: string;\n /** Exact normative producer-rule reason when this is a Grove protocol diagnostic. */\n readonly reason?: string;\n /** Stable FHIR-facing producer-rule location, independent of parser internals. */\n readonly location?: string;\n}", + "IssueCode": "type IssueCode = SchemaIssueCode | ProducerDiagnosticCode;", + "issues": "const issues: (entries: readonly Issue[]) => Result", + "IssueSeverity": "type IssueSeverity = 'error' | 'warning';", + "KeyEpoch": "type KeyEpoch = Branded<'KeyEpoch'>;", + "mapResult": "const mapResult: (result: Result, transform: (value: T) => U) => Result", + "mobileEffectiveCanonicalization": "const mobileEffectiveCanonicalization: typeof mobileEffectiveCanonicalizationValue", + "mobileEffectiveCanonicalizationVectors": "const mobileEffectiveCanonicalizationVectors: typeof mobileEffectiveCanonicalizationVectorsValue", + "MobileMeasurement": "type MobileMeasurement = BloodPressureMeasurement | InstantCodedMeasurement | InstantQuantityMeasurement | PeriodCodedMeasurement | PeriodQuantityMeasurement | SleepStageMeasurement;", + "Observation": "type Observation = ReadonlyDeep>;", + "observationBooleanValue": "const observationBooleanValue: (observation: unknown) => boolean | undefined", + "observationCodeDisplay": "const observationCodeDisplay: (observation: unknown) => string | undefined", + "observationEffectiveDate": "const observationEffectiveDate: (observation: unknown) => Date | undefined", + "observationEffectiveEnd": "const observationEffectiveEnd: (observation: unknown) => Date | undefined", + "observationEffectiveOverlaps": "const observationEffectiveOverlaps: (observation: unknown, rangeStart: Date, rangeEnd: Date) => boolean", + "observationHasCategory": "const observationHasCategory: (observation: unknown, system: string, code: string) => boolean", + "observationInterpretationDisplays": "const observationInterpretationDisplays: (observation: unknown) => readonly string[]", + "observationIssuedDate": "const observationIssuedDate: (observation: unknown) => Date | undefined", + "observationNoteTexts": "const observationNoteTexts: (observation: unknown) => readonly string[]", + "observationNumericValue": "const observationNumericValue: (observation: unknown) => number | undefined", + "observationQuantity": "const observationQuantity: (observation: unknown) => QuantityValue | undefined", + "observationStringValue": "const observationStringValue: (observation: unknown) => string | undefined", + "observationUnit": "const observationUnit: (observation: unknown) => string | undefined", + "observationValueDisplay": "const observationValueDisplay: (observation: unknown) => string | undefined", + "ok": "const ok: (value: T, warnings?: readonly Issue[]) => Result", + "OpaqueIdentityComponents": "type OpaqueIdentityComponents = Readonly<{\n [Kind in OpaqueIdentityKind]: StringComponentTuple['components']>;\n}>;", + "OpaqueIdentityKind": "type OpaqueIdentityKind = OpaqueIdentityDefinition['kind'];", + "OpaqueIdentityScope": "interface OpaqueIdentityScope {\n readonly systems: DeploymentIdentifierSystems;\n readonly keyId: string;\n readonly keyEpoch: KeyEpoch;\n readonly producerInstance: string;\n}", + "OpaqueIdentityScopeInput": "interface OpaqueIdentityScopeInput {\n readonly systems: DeploymentIdentifierSystems;\n readonly keyId: string;\n readonly keyEpoch: KeyEpoch;\n /** Canonical unpadded base64url key material containing at least 32 bytes. */\n readonly secretBase64Url: string;\n /** Canonical lowercase RFC 4122 UUID (versions 1 through 5). */\n readonly producerInstance: string;\n}", + "OpaqueIdentitySystems": "type OpaqueIdentitySystems = Readonly>;", + "parseAbsoluteUri": "const parseAbsoluteUri: (value: unknown) => Result", + "parseCanonical": "const parseCanonical: (value: unknown) => Result", + "parseDevice": "const parseDevice: (input: unknown) => Result", + "parseDocumentReference": "const parseDocumentReference: (input: unknown) => Result", + "parseEntryNodeOrdinal": "const parseEntryNodeOrdinal: (value: unknown) => Result", + "parseEventSequence": "const parseEventSequence: (value: unknown) => Result", + "parseExchangeEventContext": "const parseExchangeEventContext: (input: unknown) => Result", + "parseExchangeGraph": "const parseExchangeGraph: (input: unknown) => Result", + "parseFhirId": "const parseFhirId: (value: unknown) => Result", + "parseFhirInstant": "const parseFhirInstant: (value: unknown) => Result", + "parseKeyEpoch": "const parseKeyEpoch: (value: unknown) => Result", + "parseObservation": "const parseObservation: (input: unknown) => Result", + "parsePatientReference": "const parsePatientReference: (value: unknown) => Result", + "parsePositiveInteger": "const parsePositiveInteger: (value: unknown) => Result", + "parseProvenance": "const parseProvenance: (input: unknown) => Result", + "parseQuestionnaire": "const parseQuestionnaire: (input: unknown) => Result", + "parseQuestionnaireResponse": "const parseQuestionnaireResponse: (input: unknown) => Result", + "parseR4CollectionBundle": "const parseR4CollectionBundle: (input: unknown) => Result", + "parseResearchStudyReference": "const parseResearchStudyReference: (value: unknown) => Result", + "parseRetractionEvent": "const parseRetractionEvent: (input: unknown) => Result", + "parseSemVer": "const parseSemVer: (value: unknown) => Result", + "parseSpecimen": "const parseSpecimen: (input: unknown) => Result", + "parseSupportedR4Resource": "const parseSupportedR4Resource: (input: unknown) => Result", + "parseUrnUuid": "const parseUrnUuid: (value: unknown) => Result", + "Patient": "type Patient = ReadonlyDeep>;", + "patientAgeInYears": "const patientAgeInYears: (patient: unknown, asOf?: Date) => number | undefined", + "patientBirthDate": "const patientBirthDate: (patient: unknown) => Date | undefined", + "patientDeceasedDate": "const patientDeceasedDate: (patient: unknown) => Date | undefined", + "patientEmailAddresses": "const patientEmailAddresses: (patient: unknown) => readonly string[]", + "patientIsDeceased": "const patientIsDeceased: (patient: unknown) => boolean", + "patientMaritalStatusDisplay": "const patientMaritalStatusDisplay: (patient: unknown) => string | undefined", + "patientName": "const patientName: (patient: unknown, options?: {\n readonly includePrefix?: boolean;\n readonly includeSuffix?: boolean;\n}) => string | undefined", + "patientPhoneNumbers": "const patientPhoneNumbers: (patient: unknown) => readonly string[]", + "PatientReference": "type PatientReference = Branded<'PatientReference'>;", + "PeriodBounds": "interface PeriodBounds {\n readonly start?: Date;\n readonly end?: Date;\n}", + "PeriodCodedMeasurement": "type PeriodCodedMeasurement = {\n readonly [Kind in PeriodCodedMeasurementKind]: {\n readonly kind: Kind;\n /** Shared code drawn from the catalog's closed allowed-value set. */\n readonly value: SharedCatalog[Kind]['allowedValues'][number];\n readonly effective: PeriodEffectiveTime;\n };\n}[PeriodCodedMeasurementKind];", + "PeriodCodedMeasurementKind": "type PeriodCodedMeasurementKind = Exclude, 'sleep-stage'>;", + "periodDuration": "const periodDuration: (period: unknown) => number | undefined", + "PeriodEffectiveTime": "interface PeriodEffectiveTime {\n readonly kind: 'period';\n readonly start: FhirInstant;\n readonly end: FhirInstant;\n}", + "periodIsActive": "const periodIsActive: (period: unknown, asOf?: Date) => boolean", + "periodOverlaps": "const periodOverlaps: (period: unknown, rangeStart: Date, rangeEnd: Date) => boolean", + "PeriodQuantityMeasurement": "type PeriodQuantityMeasurement = {\n readonly [Kind in PeriodQuantityMeasurementKind]: {\n readonly kind: Kind;\n /** Value expressed in the catalog's canonical UCUM unit. */\n readonly value: number;\n readonly effective: PeriodEffectiveTime;\n };\n}[PeriodQuantityMeasurementKind];", + "PeriodQuantityMeasurementKind": "type PeriodQuantityMeasurementKind = MeasurementKindsWhere<'quantity', 'Period' | 'dateTime-or-Period'>;", + "PlanDefinition": "type PlanDefinition = ReadonlyDeep>;", + "PositiveInteger": "type PositiveInteger = number & {\n readonly [numericBrand]: 'PositiveInteger';\n};", + "preflightQuestionnairePair": "const preflightQuestionnairePair: (questionnaireInput: unknown, responseInput: unknown, options?: QuestionnairePreflightOptions) => Result", + "ProducerDiagnosticCode": "type ProducerDiagnosticCode = keyof typeof groveProducerDiagnostics;", + "Provenance": "type Provenance = ReadonlyDeep>;", + "QuantityComparator": "type QuantityComparator = '<' | '<=' | '>=' | '>';", + "QuantityValue": "interface QuantityValue {\n readonly value: number;\n /** The unit's code when the Quantity states one, and its display unit otherwise. */\n readonly unit: string;\n /** The system the unit code belongs to, so a UCUM unit is distinguishable from a local one. */\n readonly system?: string;\n /** Stated only when the Quantity names a bound; dropping it would misreport the measurement. */\n readonly comparator?: QuantityComparator;\n}", + "QuestionnaireInput": "interface QuestionnaireInput {\n readonly id?: FhirId;\n readonly url: AbsoluteUri;\n readonly version: SemVer;\n readonly name?: string;\n readonly title?: string;\n readonly status: Questionnaire['status'];\n /** Fixed by the Grove Questionnaire profile to exactly `Patient`. */\n readonly subjectTypes: readonly ['Patient'];\n readonly date?: FhirInstant;\n readonly description?: string;\n readonly purpose?: string;\n readonly extensions?: readonly Extension[];\n readonly items: readonly [QuestionnaireItemInput, ...QuestionnaireItemInput[]];\n}", + "QuestionnaireItemInput": "interface QuestionnaireItemInput extends BackboneInput {\n readonly linkId: string;\n readonly definition?: string;\n readonly code?: readonly Coding[];\n readonly prefix?: string;\n readonly text?: string;\n readonly type: QuestionnaireItemType;\n readonly enableWhen?: readonly QuestionnaireEnableWhenInput[];\n readonly enableBehavior?: 'all' | 'any';\n readonly required?: boolean;\n readonly repeats?: boolean;\n readonly readOnly?: boolean;\n readonly maxLength?: number;\n readonly answerValueSet?: string;\n readonly answerOption?: readonly QuestionnaireAnswerOptionInput[];\n readonly initial?: readonly QuestionnaireInitialInput[];\n readonly item?: readonly QuestionnaireItemInput[];\n}", + "QuestionnairePair": "interface QuestionnairePair {\n readonly questionnaire: GroveQuestionnaire;\n readonly response: GroveQuestionnaireResponse;\n}", + "QuestionnairePreflightOptions": "interface QuestionnairePreflightOptions {\n readonly valueSets?: readonly ResolvedQuestionnaireValueSetInput[];\n}", + "QuestionnaireResponseAnswerInput": "type QuestionnaireResponseAnswerInput = WithBackbone;", + "QuestionnaireResponseAuthorInput": "type QuestionnaireResponseAuthorInput = TypedQuestionnaireResponseReference<'Device' | 'Organization' | 'Patient' | 'Practitioner' | 'PractitionerRole' | 'RelatedPerson'>;", + "QuestionnaireResponseInput": "interface QuestionnaireResponseInput {\n readonly id?: FhirId;\n /** Exact `Questionnaire.url|Questionnaire.version` canonical. */\n readonly questionnaire: Canonical;\n readonly identifier: QuestionnaireResponseIdentifierInput;\n readonly status: QuestionnaireResponse['status'];\n readonly subject: QuestionnaireResponseSubjectInput;\n readonly authored: FhirInstant;\n readonly author?: QuestionnaireResponseAuthorInput;\n readonly source?: QuestionnaireResponseSourceInput;\n readonly extensions?: readonly Extension[];\n readonly items?: readonly QuestionnaireResponseItemInput[];\n}", + "QuestionnaireResponseItemInput": "interface QuestionnaireResponseItemInput extends BackboneInput {\n readonly linkId: string;\n readonly definition?: string;\n readonly text?: string;\n readonly answer?: readonly QuestionnaireResponseAnswerInput[];\n readonly item?: readonly QuestionnaireResponseItemInput[];\n}", + "QuestionnaireResponseSourceInput": "type QuestionnaireResponseSourceInput = TypedQuestionnaireResponseReference<'Patient' | 'Practitioner' | 'PractitionerRole' | 'RelatedPerson'>;", + "QuestionnaireResponseSubjectInput": "type QuestionnaireResponseSubjectInput = TypedQuestionnaireResponseReference<'Patient'>;", + "R4CollectionBundle": "type R4CollectionBundle = ReadonlyDeep> & {\n readonly type: 'collection';\n}>;", + "ReadonlyDeep": "type ReadonlyDeep = T extends string | number | bigint | boolean | symbol | null | undefined ? T : T extends (...arguments_: never[]) => unknown ? T : T extends readonly unknown[] ? Readonly<{\n [Index in keyof T]: ReadonlyDeep;\n}> : T extends object ? {\n readonly [Key in keyof T]: ReadonlyDeep;\n} : T;", + "RecordingDevice": "interface RecordingDevice {\n readonly stableUnitToken: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", + "RecordingMethod": "type RecordingMethod = 'actively-recorded' | 'automatically-recorded' | 'manual-entry';", + "referenceType": "const referenceType: (value: unknown) => string | undefined", + "ResearchStudy": "type ResearchStudy = ReadonlyDeep>;", + "ResearchStudyReference": "type ResearchStudyReference = Branded<'ResearchStudyReference'>;", + "ResearchSubject": "type ResearchSubject = ReadonlyDeep>;", + "ResolvedExchangeEventContext": "interface ResolvedExchangeEventContext extends ExchangeEventContext {\n readonly conversionInstant: FhirInstant;\n readonly converterRole: ConverterRole;\n readonly studies: readonly StudyEnrollment[];\n}", + "ResolvedQuestionnaireValueSetInput": "interface ResolvedQuestionnaireValueSetInput {\n readonly canonical: Canonical;\n readonly concepts: readonly QuestionnaireValueSetConceptInput[];\n}", + "Result": "type Result = {\n readonly ok: true;\n readonly value: T;\n /** Non-blocking findings that the caller must still surface or record. */\n readonly warnings?: readonly Issue[];\n} | {\n readonly ok: false;\n readonly issues: readonly Issue[];\n};", + "RetractionEvent": "type RetractionEvent = GraphBundle;", + "RetractionTarget": "type RetractionTarget = {\n readonly [Role in RetractionTargetRole]: {\n readonly role: Role;\n readonly resourceType: RetractionTargetResourceType;\n readonly identifier: RoledIdentifier;\n readonly nativeIdentifier?: BusinessIdentifier | undefined;\n };\n}[RetractionTargetRole];", + "RetractionTargetResourceType": "type RetractionTargetResourceType = TargetRoles[Role]['resourceTypes'][number];", + "RetractionTargetRole": "type RetractionTargetRole = keyof TargetRoles;", + "retractionTargets": "const retractionTargets: (graph: ExchangeGraph) => Result", + "RoledIdentifier": "interface RoledIdentifier extends BusinessIdentifier {\n readonly role: GroveIdentifierRole;\n}", + "scheduledCovers": "const scheduledCovers: (resource: unknown, asOf?: Date) => boolean", + "scheduledDuration": "const scheduledDuration: (resource: unknown) => number | undefined", + "scheduledEnd": "const scheduledEnd: (resource: unknown) => Date | undefined", + "scheduledIsPast": "const scheduledIsPast: (resource: unknown, asOf?: Date) => boolean | undefined", + "scheduledIsUpcoming": "const scheduledIsUpcoming: (resource: unknown, asOf?: Date) => boolean | undefined", + "scheduledStart": "const scheduledStart: (resource: unknown) => Date | undefined", + "SchemaIssueCode": "type SchemaIssueCode = 'duplicate-identifier' | 'external-validation-required' | 'invalid-choice' | 'invalid-code' | 'invalid-date-time' | 'invalid-identifier' | 'invalid-reference' | 'invalid-type' | 'invalid-uri' | 'missing-required' | 'out-of-range' | 'schema-invalid' | 'value-mismatch';", + "SemVer": "type SemVer = Branded<'SemVer'>;", + "sharedMobileMeasurementCatalog": "const sharedMobileMeasurementCatalog: typeof sharedMobileMeasurementCatalogValue", + "SharedMobileMeasurementKind": "type SharedMobileMeasurementKind = keyof typeof sharedMobileMeasurementCatalog;", + "SleepStage": "type SleepStage = SharedCatalog['sleep-stage']['allowedValues'][number];", + "SleepStageMeasurement": "interface SleepStageMeasurement {\n readonly kind: 'sleep-stage';\n readonly stage: SleepStage;\n /** Source-native stage retained when it is more precise than the shared stage. */\n readonly sourceStageCoding?: SleepStageSourceCodingInput | undefined;\n readonly effective: PeriodEffectiveTime;\n}", + "SleepStageSourceCodingInput": "interface SleepStageSourceCodingInput {\n readonly system: AbsoluteUri;\n readonly code: string;\n readonly display?: string | undefined;\n}", + "slotSpecialtyDisplays": "const slotSpecialtyDisplays: (slot: unknown) => readonly string[]", + "Specimen": "type Specimen = ReadonlyDeep>;", + "StudyEnrollment": "interface StudyEnrollment {\n readonly study: BusinessIdentifier;\n readonly protocol: {\n readonly url: AbsoluteUri;\n readonly version: string;\n };\n readonly enrollment: BusinessIdentifier;\n}", + "Subject": "type Subject = {\n readonly kind: 'logical';\n readonly identifier: BusinessIdentifier;\n} | {\n readonly kind: 'bundled';\n readonly identifier: BusinessIdentifier;\n readonly patient: Patient;\n};", + "SupportedR4Resource": "type SupportedR4Resource = R4CollectionBundle | GraphResource;", + "toFhirReference": "const toFhirReference: (resourceType: string, id: string) => {\n readonly reference: string;\n readonly type: string;\n}", + "toReference": "const toReference: (resource: unknown) => Result", + "UrnUuid": "type UrnUuid = Branded<'UrnUuid'>;", + "validateOpaqueIdentityScope": "const validateOpaqueIdentityScope: (input: OpaqueIdentityScopeInput) => Result", + "valueToFhirQuantity": "const valueToFhirQuantity: (value: QuantityValue) => Result<{\n readonly value: number;\n readonly unit: string;\n readonly code: string;\n readonly system?: string;\n readonly comparator?: string;\n}>" + }, + "./r4": { + "Attachment": "type Attachment = ReadonlyDeep>;", + "attachmentSchema": "const attachmentSchema: z.ZodType>", + "CodeableConcept": "type CodeableConcept = ReadonlyDeep>;", + "codeableConceptSchema": "const codeableConceptSchema: z.ZodType>", + "Coding": "type Coding = ReadonlyDeep>;", + "codingSchema": "const codingSchema: z.ZodType>", + "Device": "type Device = ReadonlyDeep>;", + "deviceSchema": "const deviceSchema: z.ZodType>", + "DocumentReference": "type DocumentReference = ReadonlyDeep>;", + "documentReferenceSchema": "const documentReferenceSchema: z.ZodType>", + "ExchangeGraph": "type ExchangeGraph = GraphBundle;", + "exchangeGraphSchema": "const exchangeGraphSchema: z.ZodType", + "expressionSchema": "const expressionSchema: z.ZodType>", + "Extension": "type Extension = ReadonlyDeep>;", + "extensionSchema": "const extensionSchema: z.ZodType>", + "FhirJson": "type FhirJson = Value extends JsonPrimitive | null | undefined ? Value : Value extends object ? FhirJsonObject : Value;", + "GraphResource": "type GraphResource = Device | DocumentReference | MedicationAdministration | MedicationStatement | Observation | Patient | PlanDefinition | Provenance | QuestionnaireResponse | ResearchStudy | ResearchSubject | Specimen | VisionPrescription;", + "graphResourceSchema": "const graphResourceSchema: z.ZodType", + "Identifier": "type Identifier = ReadonlyDeep>;", + "identifierSchema": "const identifierSchema: z.ZodType>", + "isSemanticallyEqual": "const isSemanticallyEqual: (left: string | Uint8Array, right: string | Uint8Array) => Result", + "metaSchema": "const metaSchema: z.ZodType>", + "Observation": "type Observation = ReadonlyDeep>;", + "observationComponentSchema": "const observationComponentSchema: z.ZodType>", + "observationSchema": "const observationSchema: z.ZodType>", + "parseDevice": "const parseDevice: (input: unknown) => Result", + "parseDocumentReference": "const parseDocumentReference: (input: unknown) => Result", + "parseExchangeGraph": "const parseExchangeGraph: (input: unknown) => Result", + "parseObservation": "const parseObservation: (input: unknown) => Result", + "parseProvenance": "const parseProvenance: (input: unknown) => Result", + "parseR4CollectionBundle": "const parseR4CollectionBundle: (input: unknown) => Result", + "parseRetractionEvent": "const parseRetractionEvent: (input: unknown) => Result", + "parseSpecimen": "const parseSpecimen: (input: unknown) => Result", + "parseSupportedR4Resource": "const parseSupportedR4Resource: (input: unknown) => Result", + "Patient": "type Patient = ReadonlyDeep>;", + "Period": "type Period = ReadonlyDeep>;", + "periodSchema": "const periodSchema: z.ZodType>", + "PlanDefinition": "type PlanDefinition = ReadonlyDeep>;", + "primitiveElementSchema": "const primitiveElementSchema: typeof elementSchema", + "Provenance": "type Provenance = ReadonlyDeep>;", + "provenanceSchema": "const provenanceSchema: z.ZodType>", + "Quantity": "type Quantity = ReadonlyDeep>;", + "quantitySchema": "const quantitySchema: z.ZodType>", + "Questionnaire": "type Questionnaire = ReadonlyDeep>;", + "QuestionnaireResponse": "type QuestionnaireResponse = ReadonlyDeep>;", + "R4CollectionBundle": "type R4CollectionBundle = ReadonlyDeep> & {\n readonly type: 'collection';\n}>;", + "r4CollectionBundleSchema": "const r4CollectionBundleSchema: z.ZodType", + "Reference": "type Reference = ReadonlyDeep>;", + "ResearchStudy": "type ResearchStudy = ReadonlyDeep>;", + "ResearchSubject": "type ResearchSubject = ReadonlyDeep>;", + "resolvableReferenceSchema": "const resolvableReferenceSchema: z.ZodType", + "RetractionEvent": "type RetractionEvent = GraphBundle;", + "retractionEventSchema": "const retractionEventSchema: z.ZodType", + "RetractionTarget": "type RetractionTarget = {\n readonly [Role in RetractionTargetRole]: {\n readonly role: Role;\n readonly resourceType: RetractionTargetResourceType;\n readonly identifier: RoledIdentifier;\n readonly nativeIdentifier?: BusinessIdentifier | undefined;\n };\n}[RetractionTargetRole];", + "RetractionTargetResourceType": "type RetractionTargetResourceType = TargetRoles[Role]['resourceTypes'][number];", + "RetractionTargetRole": "type RetractionTargetRole = keyof TargetRoles;", + "retractionTargets": "const retractionTargets: (graph: ExchangeGraph) => Result", + "SampledData": "type SampledData = ReadonlyDeep>;", + "sampledDataSchema": "const sampledDataSchema: z.ZodType>", + "Specimen": "type Specimen = ReadonlyDeep>;", + "specimenSchema": "const specimenSchema: z.ZodType>", + "SupportedR4Resource": "type SupportedR4Resource = R4CollectionBundle | GraphResource;", + "supportedR4ResourceSchema": "const supportedR4ResourceSchema: z.ZodType" + }, + "./mobile": { + "ApplicationDevice": "interface ApplicationDevice {\n readonly sourceDeviceToken: string;\n readonly name: string;\n readonly version?: string | undefined;\n readonly build?: string | undefined;\n}", + "BloodPressureMeasurement": "interface BloodPressureMeasurement {\n readonly kind: 'blood-pressure';\n readonly systolic: number;\n readonly diastolic: number;\n readonly effective: InstantEffectiveTime;\n}", + "BusinessIdentifier": "interface BusinessIdentifier {\n readonly system: AbsoluteUri;\n readonly value: string;\n}", + "canonicalizeMobileEffectiveInstant": "const canonicalizeMobileEffectiveInstant: (value: unknown) => Result", + "ChoiceQuantityMeasurement": "type ChoiceQuantityMeasurement = Extract;", + "ChoiceQuantityMeasurementKind": "type ChoiceQuantityMeasurementKind = MeasurementKindsWhere<'quantity', 'dateTime-or-Period'>;", + "ConversionBatch": "interface ConversionBatch {\n readonly conversions: readonly Conversion[];\n readonly failures: readonly Failure[];\n}", + "ConverterRole": "type ConverterRole = {\n readonly kind: 'assembler';\n} | {\n readonly kind: 'gateway';\n} | {\n readonly kind: 'gateway-application';\n readonly application: ApplicationDevice;\n};", + "createEntryIdentity": "const createEntryIdentity: (identifier: RoledIdentifier, id?: FhirId) => Result", + "DeploymentIdentifierSystems": "interface DeploymentIdentifierSystems {\n readonly opaque: OpaqueIdentitySystems;\n readonly event: AbsoluteUri;\n readonly entryNode: AbsoluteUri;\n}", + "deriveEntryFullUrl": "const deriveEntryFullUrl: (input: BusinessIdentifier) => Result", + "deriveEntryNodeIdentifier": "const deriveEntryNodeIdentifier: (scope: OpaqueIdentityScope, key: EntryNodeKey) => Result", + "deriveEntryNodeValue": "const deriveEntryNodeValue: (key: EntryNodeKey) => Result", + "deriveEventIdentifier": "const deriveEventIdentifier: (scope: OpaqueIdentityScope, sequence: EventSequence) => Result", + "deriveOpaqueIdentifier": "const deriveOpaqueIdentifier: (scope: OpaqueIdentityScope, identityKind: Kind, components: OpaqueIdentityComponents[Kind]) => Result", + "deriveOpaqueIdentitySystems": "const deriveOpaqueIdentitySystems: (root: AbsoluteUri, keyId: string, epoch: KeyEpoch) => Result", + "encodeLengthFramedUtf8": "const encodeLengthFramedUtf8: (components: readonly string[]) => Result", + "entryIdentifierName": "const entryIdentifierName: (input: BusinessIdentifier) => Result", + "EntryIdentity": "interface EntryIdentity {\n readonly identifier: RoledIdentifier;\n readonly fullUrl: UrnUuid;\n readonly id?: FhirId;\n}", + "EntryNodeIdentifier": "type EntryNodeIdentifier = RoledIdentifier & {\n readonly role: 'entry-node';\n};", + "EntryNodeKey": "interface EntryNodeKey {\n readonly event: ExchangeEventIdentifier;\n readonly role: string;\n readonly ordinal: EntryNodeOrdinal;\n}", + "ExchangeEventContext": "interface ExchangeEventContext {\n readonly subject: Subject;\n readonly event: ExchangeEventIdentifier;\n readonly identityScope: OpaqueIdentityScope;\n readonly repositoryScope: BusinessIdentifier;\n readonly application: ApplicationDevice;\n /** Required here: a server states its own facts, where a phone reads them from the OS. */\n readonly host: HostDevice;\n /** Defaults to the instant the builder runs. */\n readonly conversionInstant?: FhirInstant | undefined;\n readonly converterRole?: ConverterRole | undefined;\n readonly studies?: readonly StudyEnrollment[] | undefined;\n readonly repositoryIds?: Readonly>> | undefined;\n}", + "ExchangeEventIdentifier": "type ExchangeEventIdentifier = RoledIdentifier & {\n readonly role: 'event';\n};", + "ExchangeGraphIdentifiers": "interface ExchangeGraphIdentifiers {\n readonly event: ExchangeEventIdentifier;\n readonly sourceRecord: RoledIdentifier;\n /** Every output identity in Bundle order; a provider record has no child outputs. */\n readonly outputs: readonly [RoledIdentifier, ...RoledIdentifier[]];\n readonly provenance: RoledIdentifier;\n readonly applicationSnapshot: RoledIdentifier;\n readonly hostSnapshot: RoledIdentifier;\n readonly writerSnapshot: RoledIdentifier;\n readonly sourceArtifact?: RoledIdentifier;\n readonly recordingDevice?: RoledIdentifier;\n readonly recordingDeviceSnapshot?: RoledIdentifier;\n readonly gatewayApplicationSnapshot?: RoledIdentifier;\n readonly writerRecord?: RoledIdentifier;\n}", + "ExchangeGraphNode": "type ExchangeGraphNode = 'bundle' | 'primary-output' | 'source-artifact' | 'recording-device' | 'application-device' | 'host-device' | 'writer' | 'provenance';", + "GovernedSourceIdentifierDisclosurePolicy": "type GovernedSourceIdentifierDisclosurePolicy = {\n readonly kind: 'omit';\n} | {\n readonly kind: 'authorized';\n readonly system: AbsoluteUri;\n readonly type?: GovernedSourceIdentifierType | undefined;\n};", + "GovernedSourceIdentifierType": "interface GovernedSourceIdentifierType {\n readonly coding?: readonly [\n GovernedSourceIdentifierTypeCoding,\n ...GovernedSourceIdentifierTypeCoding[]\n ] | undefined;\n readonly text?: string | undefined;\n}", + "GovernedSourceIdentifierTypeCoding": "interface GovernedSourceIdentifierTypeCoding {\n readonly system: AbsoluteUri;\n readonly code: string;\n readonly display?: string | undefined;\n}", + "groveExchangeProtocol": "const groveExchangeProtocol: typeof groveExchangeProtocolValue", + "groveFhirVersion": "const groveFhirVersion: \"4.0.1\"", + "GroveIdentifierRole": "type GroveIdentifierRole = OpaqueIdentityDefinition['identifierRole'] | 'entry-node' | 'event';", + "groveMobilePackageMetadata": "const groveMobilePackageMetadata: typeof groveMobilePackageMetadataValue", + "groveMobileProfileCanonicals": "const groveMobileProfileCanonicals: typeof groveMobileProfileCanonicalsValue", + "groveProducerDiagnostics": "const groveProducerDiagnostics: typeof groveProducerDiagnosticsValue", + "groveProfileClaims": "const groveProfileClaims: typeof groveProfileClaimsValue", + "GroveRecordingFormat": "type GroveRecordingFormat = keyof typeof groveRecordingFormatRegistry.formats;", + "groveRecordingFormatRegistry": "const groveRecordingFormatRegistry: typeof groveRecordingFormatRegistryValue", + "HostDevice": "interface HostDevice {\n readonly sourceDeviceToken: string;\n readonly operatingSystemVersion: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", + "InstantCodedMeasurement": "type InstantCodedMeasurement = {\n readonly [Kind in InstantCodedMeasurementKind]: {\n readonly kind: Kind;\n /** Shared code drawn from the catalog's closed allowed-value set. */\n readonly value: SharedCatalog[Kind]['allowedValues'][number];\n readonly effective: InstantEffectiveTime;\n };\n}[InstantCodedMeasurementKind];", + "InstantCodedMeasurementKind": "type InstantCodedMeasurementKind = MeasurementKindsWhere<'codeableConcept', 'dateTime'>;", + "InstantEffectiveTime": "interface InstantEffectiveTime {\n readonly kind: 'date-time';\n readonly value: FhirInstant;\n}", + "InstantQuantityMeasurement": "type InstantQuantityMeasurement = {\n readonly [Kind in InstantQuantityMeasurementKind]: {\n readonly kind: Kind;\n /** Value expressed in the catalog's canonical UCUM unit. */\n readonly value: number;\n readonly effective: InstantEffectiveTime;\n };\n}[InstantQuantityMeasurementKind];", + "InstantQuantityMeasurementKind": "type InstantQuantityMeasurementKind = MeasurementKindsWhere<'quantity', 'dateTime' | 'dateTime-or-Period'>;", + "isEntryNodeIdentityValue": "const isEntryNodeIdentityValue: (value: unknown) => boolean", + "isEventIdentityValue": "const isEventIdentityValue: (value: unknown) => boolean", + "isOpaqueIdentityScope": "const isOpaqueIdentityScope: (value: unknown) => value is OpaqueIdentityScope", + "isOpaqueIdentityValue": "const isOpaqueIdentityValue: (value: unknown) => boolean", + "mobileEffectiveCanonicalization": "const mobileEffectiveCanonicalization: typeof mobileEffectiveCanonicalizationValue", + "mobileEffectiveCanonicalizationVectors": "const mobileEffectiveCanonicalizationVectors: typeof mobileEffectiveCanonicalizationVectorsValue", + "MobileMeasurement": "type MobileMeasurement = BloodPressureMeasurement | InstantCodedMeasurement | InstantQuantityMeasurement | PeriodCodedMeasurement | PeriodQuantityMeasurement | SleepStageMeasurement;", + "OpaqueIdentityComponents": "type OpaqueIdentityComponents = Readonly<{\n [Kind in OpaqueIdentityKind]: StringComponentTuple['components']>;\n}>;", + "OpaqueIdentityKind": "type OpaqueIdentityKind = OpaqueIdentityDefinition['kind'];", + "OpaqueIdentityScope": "interface OpaqueIdentityScope {\n readonly systems: DeploymentIdentifierSystems;\n readonly keyId: string;\n readonly keyEpoch: KeyEpoch;\n readonly producerInstance: string;\n}", + "OpaqueIdentityScopeInput": "interface OpaqueIdentityScopeInput {\n readonly systems: DeploymentIdentifierSystems;\n readonly keyId: string;\n readonly keyEpoch: KeyEpoch;\n /** Canonical unpadded base64url key material containing at least 32 bytes. */\n readonly secretBase64Url: string;\n /** Canonical lowercase RFC 4122 UUID (versions 1 through 5). */\n readonly producerInstance: string;\n}", + "OpaqueIdentitySystems": "type OpaqueIdentitySystems = Readonly>;", + "parseExchangeEventContext": "const parseExchangeEventContext: (input: unknown) => Result", + "PeriodCodedMeasurement": "type PeriodCodedMeasurement = {\n readonly [Kind in PeriodCodedMeasurementKind]: {\n readonly kind: Kind;\n /** Shared code drawn from the catalog's closed allowed-value set. */\n readonly value: SharedCatalog[Kind]['allowedValues'][number];\n readonly effective: PeriodEffectiveTime;\n };\n}[PeriodCodedMeasurementKind];", + "PeriodCodedMeasurementKind": "type PeriodCodedMeasurementKind = Exclude, 'sleep-stage'>;", + "PeriodEffectiveTime": "interface PeriodEffectiveTime {\n readonly kind: 'period';\n readonly start: FhirInstant;\n readonly end: FhirInstant;\n}", + "PeriodQuantityMeasurement": "type PeriodQuantityMeasurement = {\n readonly [Kind in PeriodQuantityMeasurementKind]: {\n readonly kind: Kind;\n /** Value expressed in the catalog's canonical UCUM unit. */\n readonly value: number;\n readonly effective: PeriodEffectiveTime;\n };\n}[PeriodQuantityMeasurementKind];", + "PeriodQuantityMeasurementKind": "type PeriodQuantityMeasurementKind = MeasurementKindsWhere<'quantity', 'Period' | 'dateTime-or-Period'>;", + "ProducerDiagnosticCode": "type ProducerDiagnosticCode = keyof typeof groveProducerDiagnostics;", + "ProviderRecordingFormat": "type ProviderRecordingFormat = Extract;", + "RecordingDevice": "interface RecordingDevice {\n readonly stableUnitToken: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", + "RecordingMethod": "type RecordingMethod = 'actively-recorded' | 'automatically-recorded' | 'manual-entry';", + "ResolvedExchangeEventContext": "interface ResolvedExchangeEventContext extends ExchangeEventContext {\n readonly conversionInstant: FhirInstant;\n readonly converterRole: ConverterRole;\n readonly studies: readonly StudyEnrollment[];\n}", + "RoledIdentifier": "interface RoledIdentifier extends BusinessIdentifier {\n readonly role: GroveIdentifierRole;\n}", + "sharedMobileMeasurementCatalog": "const sharedMobileMeasurementCatalog: typeof sharedMobileMeasurementCatalogValue", + "SharedMobileMeasurementKind": "type SharedMobileMeasurementKind = keyof typeof sharedMobileMeasurementCatalog;", + "SleepStage": "type SleepStage = SharedCatalog['sleep-stage']['allowedValues'][number];", + "SleepStageMeasurement": "interface SleepStageMeasurement {\n readonly kind: 'sleep-stage';\n readonly stage: SleepStage;\n /** Source-native stage retained when it is more precise than the shared stage. */\n readonly sourceStageCoding?: SleepStageSourceCodingInput | undefined;\n readonly effective: PeriodEffectiveTime;\n}", + "SleepStageSourceCodingInput": "interface SleepStageSourceCodingInput {\n readonly system: AbsoluteUri;\n readonly code: string;\n readonly display?: string | undefined;\n}", + "StudyEnrollment": "interface StudyEnrollment {\n readonly study: BusinessIdentifier;\n readonly protocol: {\n readonly url: AbsoluteUri;\n readonly version: string;\n };\n readonly enrollment: BusinessIdentifier;\n}", + "Subject": "type Subject = {\n readonly kind: 'logical';\n readonly identifier: BusinessIdentifier;\n} | {\n readonly kind: 'bundled';\n readonly identifier: BusinessIdentifier;\n readonly patient: Patient;\n};", + "validateOpaqueIdentityScope": "const validateOpaqueIdentityScope: (input: OpaqueIdentityScopeInput) => Result" + }, + "./providers": { + "adapterMeasurementCatalog": "const adapterMeasurementCatalog: typeof adapterMeasurementCatalogValue", + "AdapterMeasurementCatalog": "type AdapterMeasurementCatalog = typeof adapterMeasurementCatalog;", + "ApplicationDevice": "interface ApplicationDevice {\n readonly sourceDeviceToken: string;\n readonly name: string;\n readonly version?: string | undefined;\n readonly build?: string | undefined;\n}", + "buildProviderExchangeGraph": "const buildProviderExchangeGraph: (record: NormalizedProviderRecord, context: ExchangeEventContext, options?: ProviderConversionOptions) => Result", + "buildProviderExchangeGraphs": "const buildProviderExchangeGraphs: (records: readonly NormalizedProviderRecord[], context: (record: NormalizedProviderRecord) => ExchangeEventContext, options?: ProviderConversionOptions) => ConversionBatch", + "buildProviderRecordingGraph": "const buildProviderRecordingGraph: (source: ProviderRecordingSource, attachment: ProviderRecordingAttachment, context: ExchangeEventContext, options?: ProviderConversionOptions) => Result", + "buildProviderRetractionEvent": "const buildProviderRetractionEvent: (targets: readonly [RetractionTarget, ...RetractionTarget[]], context: ExchangeEventContext, sourceRecord: RoledIdentifier, retractedAt: FhirInstant) => Result", + "CanonicalBase64": "type CanonicalBase64 = Branded<'CanonicalBase64'>;", + "ConnectedProvider": "type ConnectedProvider = keyof ScalarOutputRoles;", + "ConnectedProviderMeasurement": "type ConnectedProviderMeasurement> = {\n readonly [Kind in SupportedConnectedProviderMeasurementKind]: ProviderMeasurementForKind;\n}[SupportedConnectedProviderMeasurementKind];", + "ConnectedProviderMeasurementKind": "type ConnectedProviderMeasurementKind = {\n readonly [Provider in ConnectedProvider]: {\n readonly [SourceType in ConnectedSourceType]: SupportedConnectedProviderMeasurementKind;\n }[ConnectedSourceType];\n}[ConnectedProvider];", + "ConnectedProviderMeasurements": "type ConnectedProviderMeasurements> = readonly [\n ConnectedProviderMeasurement,\n ...Array>\n];", + "ConnectedProviderRecord": "interface ConnectedProviderRecord> {\n /** Non-empty unique subset of catalog-admitted outputs for one source record. */\n readonly measurements: ConnectedProviderMeasurements;\n readonly source: ProviderSourceRecord;\n}", + "ConnectedRawProvider": "type ConnectedRawProvider = keyof RawOutputRoles;", + "ConnectedRawSourceType": "type ConnectedRawSourceType = keyof RawOutputRoles[Provider] & string;", + "ConnectedSourceType": "type ConnectedSourceType = keyof ScalarOutputRoles[Provider] & string;", + "ConversionBatch": "interface ConversionBatch {\n readonly conversions: readonly Conversion[];\n readonly failures: readonly Failure[];\n}", + "ConverterRole": "type ConverterRole = {\n readonly kind: 'assembler';\n} | {\n readonly kind: 'gateway';\n} | {\n readonly kind: 'gateway-application';\n readonly application: ApplicationDevice;\n};", + "DeviceSnapshotRole": "type DeviceSnapshotRole = 'application' | 'host' | 'recording-device';", + "EmbeddedRecordingAttachment": "interface EmbeddedRecordingAttachment extends RecordingAttachmentBase {\n readonly kind: 'embedded';\n /** Exact caller-supplied bytes represented as canonical RFC 4648 base64. */\n readonly dataBase64: CanonicalBase64;\n}", + "encodeRecordingBytes": "const encodeRecordingBytes: (bytes: Uint8Array) => Result", + "ExchangeEventContext": "interface ExchangeEventContext {\n readonly subject: Subject;\n readonly event: ExchangeEventIdentifier;\n readonly identityScope: OpaqueIdentityScope;\n readonly repositoryScope: BusinessIdentifier;\n readonly application: ApplicationDevice;\n /** Required here: a server states its own facts, where a phone reads them from the OS. */\n readonly host: HostDevice;\n /** Defaults to the instant the builder runs. */\n readonly conversionInstant?: FhirInstant | undefined;\n readonly converterRole?: ConverterRole | undefined;\n readonly studies?: readonly StudyEnrollment[] | undefined;\n readonly repositoryIds?: Readonly>> | undefined;\n}", + "ExchangeGraphIdentifiers": "interface ExchangeGraphIdentifiers {\n readonly event: ExchangeEventIdentifier;\n readonly sourceRecord: RoledIdentifier;\n /** Every output identity in Bundle order; a provider record has no child outputs. */\n readonly outputs: readonly [RoledIdentifier, ...RoledIdentifier[]];\n readonly provenance: RoledIdentifier;\n readonly applicationSnapshot: RoledIdentifier;\n readonly hostSnapshot: RoledIdentifier;\n readonly writerSnapshot: RoledIdentifier;\n readonly sourceArtifact?: RoledIdentifier;\n readonly recordingDevice?: RoledIdentifier;\n readonly recordingDeviceSnapshot?: RoledIdentifier;\n readonly gatewayApplicationSnapshot?: RoledIdentifier;\n readonly writerRecord?: RoledIdentifier;\n}", + "ExternalRecordingAttachment": "interface ExternalRecordingAttachment extends RecordingAttachmentBase {\n readonly kind: 'external';\n readonly url: ImmutableRecordingUrl;\n readonly size: number;\n readonly hash: Sha1Base64;\n readonly immutabilityAssurance: 'immutable-version-specific';\n}", + "GovernedSourceIdentifierDisclosurePolicy": "type GovernedSourceIdentifierDisclosurePolicy = {\n readonly kind: 'omit';\n} | {\n readonly kind: 'authorized';\n readonly system: AbsoluteUri;\n readonly type?: GovernedSourceIdentifierType | undefined;\n};", + "groveFhirVersion": "const groveFhirVersion: \"4.0.1\"", + "groveProviderPackageMetadata": "const groveProviderPackageMetadata: typeof groveProviderPackageMetadataValue", + "groveRecordingFormatRegistry": "const groveRecordingFormatRegistry: typeof groveRecordingFormatRegistryValue", + "healthKitApplicationDeviceIdentity": "const healthKitApplicationDeviceIdentity: typeof healthKitApplicationDeviceIdentityValue", + "HealthKitApplicationDeviceIdentity": "type HealthKitApplicationDeviceIdentity = typeof healthKitApplicationDeviceIdentity;", + "healthKitClinicalRecordAdmission": "const healthKitClinicalRecordAdmission: typeof healthKitClinicalRecordAdmissionValue", + "HealthKitClinicalRecordAdmission": "type HealthKitClinicalRecordAdmission = typeof healthKitClinicalRecordAdmission;", + "HostDevice": "interface HostDevice {\n readonly sourceDeviceToken: string;\n readonly operatingSystemVersion: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", + "ImmutableRecordingUrl": "type ImmutableRecordingUrl = Branded<'ImmutableRecordingUrl'>;", + "MediaType": "type MediaType = Branded<'MediaType'>;", + "MobileMeasurement": "type MobileMeasurement = BloodPressureMeasurement | InstantCodedMeasurement | InstantQuantityMeasurement | PeriodCodedMeasurement | PeriodQuantityMeasurement | SleepStageMeasurement;", + "NormalizedProviderRecord": "type NormalizedProviderRecord = {\n readonly [Provider in ConnectedProvider]: ConnectedProviderRecordFor;\n}[ConnectedProvider];", + "NormalizedSourceRecord": "type NormalizedSourceRecord = NormalizedProviderRecord['source'];", + "parseCanonicalBase64": "const parseCanonicalBase64: (value: unknown) => Result", + "parseImmutableRecordingUrl": "const parseImmutableRecordingUrl: (value: unknown) => Result", + "parseMediaType": "const parseMediaType: (value: unknown) => Result", + "parseNormalizedProviderRecord": "const parseNormalizedProviderRecord: (input: unknown) => Result", + "parseProviderConversionOptions": "const parseProviderConversionOptions: (input: unknown) => Result", + "parseProviderRecordingAttachment": "const parseProviderRecordingAttachment: (input: unknown) => Result", + "parseProviderRecordingSource": "const parseProviderRecordingSource: (input: unknown) => Result", + "parseSha1Base64": "const parseSha1Base64: (value: unknown) => Result", + "ProviderAdapter": "interface ProviderAdapter {\n readonly kind: 'providers';\n readonly provider: Provider;\n}", + "providerAdapterCatalog": "const providerAdapterCatalog: typeof providerAdapterCatalogValue", + "ProviderConversion": "interface ProviderConversion {\n readonly source: NormalizedSourceRecord;\n readonly identifiers: ExchangeGraphIdentifiers;\n readonly graph: ExchangeGraph;\n /** Registry warnings about what the accepted record lost; empty when nothing was. */\n readonly warnings: readonly Issue[];\n}", + "ProviderConversionFailure": "interface ProviderConversionFailure {\n readonly record: NormalizedProviderRecord;\n readonly issues: readonly Issue[];\n}", + "ProviderConversionOptions": "interface ProviderConversionOptions {\n readonly nativeIdentifierDisclosure?: GovernedSourceIdentifierDisclosurePolicy | undefined;\n}", + "ProviderExclusiveMeasurement": "type ProviderExclusiveMeasurement = ProviderExclusiveMeasurementKind> = Kind extends ProviderExclusiveMeasurementKind ? {\n readonly kind: Kind;\n readonly effective: ExclusiveEffective>;\n} & ExclusiveResult> : never;", + "ProviderExclusiveMeasurementKind": "type ProviderExclusiveMeasurementKind = keyof ExclusiveCatalogFor & string;", + "providerOutputCoordinates": "const providerOutputCoordinates: (provider: ConnectedProvider, sourceType: string, kind: string) => ProviderOutputCoordinates | undefined", + "ProviderOutputCoordinates": "interface ProviderOutputCoordinates {\n readonly outputRole: string;\n readonly outputDiscriminator: string;\n}", + "providerOutputRole": "const providerOutputRole: (provider: ConnectedProvider, sourceType: string, kind: string) => string | undefined", + "providerRawOutputDiscriminators": "const providerRawOutputDiscriminators: typeof providerRawOutputDiscriminatorsValue", + "ProviderRawOutputDiscriminators": "type ProviderRawOutputDiscriminators = typeof providerRawOutputDiscriminators;", + "providerRawOutputRoles": "const providerRawOutputRoles: typeof providerRawOutputRolesValue", + "ProviderRawOutputRoles": "type ProviderRawOutputRoles = typeof providerRawOutputRoles;", + "providerRecordEffectiveRules": "const providerRecordEffectiveRules: typeof providerRecordEffectiveRulesValue", + "ProviderRecordEffectiveRules": "type ProviderRecordEffectiveRules = typeof providerRecordEffectiveRules;", + "ProviderRecordingAttachment": "type ProviderRecordingAttachment = EmbeddedRecordingAttachment | ExternalRecordingAttachment;", + "ProviderRecordingConversion": "interface ProviderRecordingConversion {\n readonly source: ProviderRecordingSource;\n readonly identifiers: ExchangeGraphIdentifiers;\n readonly graph: ExchangeGraph;\n readonly warnings: readonly Issue[];\n}", + "ProviderRecordingFormat": "type ProviderRecordingFormat = import('../contract/providers.generated.js').ProviderRecordingFormat;", + "ProviderRecordingSource": "type ProviderRecordingSource = {\n readonly [Provider in ConnectedRawProvider]: {\n readonly [SourceType in ConnectedRawSourceType]: ProviderRecordingSourceRecord;\n }[ConnectedRawSourceType];\n}[ConnectedRawProvider];", + "ProviderRecordingSourceRecord": "interface ProviderRecordingSourceRecord> {\n readonly adapter: ProviderAdapter;\n readonly sourceType: SourceType;\n /** Opaque-identity digest input, disclosed only through the conversion options. */\n readonly sourceNativeId: string;\n /** The application that entered the already-obtained record at the provider. */\n readonly writer: ApplicationDevice;\n /** The source activity time the recording covers. */\n readonly effective: InstantEffectiveTime | PeriodEffectiveTime;\n readonly writerRecord?: WriterRecord | undefined;\n}", + "providerScalarOutputDiscriminators": "const providerScalarOutputDiscriminators: typeof providerScalarOutputDiscriminatorsValue", + "ProviderScalarOutputDiscriminators": "type ProviderScalarOutputDiscriminators = typeof providerScalarOutputDiscriminators;", + "providerScalarOutputRoles": "const providerScalarOutputRoles: typeof providerScalarOutputRolesValue", + "ProviderScalarOutputRoles": "type ProviderScalarOutputRoles = typeof providerScalarOutputRoles;", + "ProviderSourceRecord": "interface ProviderSourceRecord> extends NormalizedSourceRecordBase {\n readonly adapter: ProviderAdapter;\n readonly sourceType: SourceType;\n readonly sourceNativeId: string;\n /** The application that entered the record at the connected provider. */\n readonly writer: ApplicationDevice;\n}", + "RawPayloadAdmissionAssertion": "type RawPayloadAdmissionAssertion = (typeof import('../contract/providers.generated.js').providerAdapterCatalog)['rawPayloadAdmission']['allowedAssertions'][number];", + "RecordingDevice": "interface RecordingDevice {\n readonly stableUnitToken: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", + "RecordingMethod": "type RecordingMethod = 'actively-recorded' | 'automatically-recorded' | 'manual-entry';", + "Sha1Base64": "type Sha1Base64 = Branded<'Sha1Base64'>;", + "StudyEnrollment": "interface StudyEnrollment {\n readonly study: BusinessIdentifier;\n readonly protocol: {\n readonly url: AbsoluteUri;\n readonly version: string;\n };\n readonly enrollment: BusinessIdentifier;\n}", + "Subject": "type Subject = {\n readonly kind: 'logical';\n readonly identifier: BusinessIdentifier;\n} | {\n readonly kind: 'bundled';\n readonly identifier: BusinessIdentifier;\n readonly patient: Patient;\n};", + "SupportedConnectedProviderMeasurementKind": "type SupportedConnectedProviderMeasurementKind = ConnectedSourceType> = keyof ScalarOutputRoles[Provider][SourceType] & string;", + "WriterRecord": "interface WriterRecord {\n readonly applicationIdentifier: BusinessIdentifier;\n readonly nativeRecordId: string;\n /** Canonical non-negative writer revision, when the source exposes one. */\n readonly version?: string | undefined;\n}" + }, + "./questionnaire": { + "buildQuestionnaire": "const buildQuestionnaire: (input: QuestionnaireInput) => Result", + "buildQuestionnaireResponse": "const buildQuestionnaireResponse: (input: QuestionnaireResponseInput) => Result", + "groveFhirVersion": "const groveFhirVersion: \"4.0.1\"", + "GroveQuestionnaire": "type GroveQuestionnaire = Readonly & {\n readonly item: NonNullable;\n}>;", + "groveQuestionnairePackageMetadata": "const groveQuestionnairePackageMetadata: typeof groveQuestionnairePackageMetadataValue", + "GroveQuestionnaireResponse": "type GroveQuestionnaireResponse = Readonly & {\n readonly identifier: Identifier;\n readonly questionnaire: string;\n readonly authored: string;\n readonly item?: QuestionnaireResponse['item'];\n}>;", + "parseQuestionnaire": "const parseQuestionnaire: (input: unknown) => Result", + "parseQuestionnaireResponse": "const parseQuestionnaireResponse: (input: unknown) => Result", + "preflightQuestionnairePair": "const preflightQuestionnairePair: (questionnaireInput: unknown, responseInput: unknown, options?: QuestionnairePreflightOptions) => Result", + "QuestionnaireAnswerOptionInput": "type QuestionnaireAnswerOptionInput = WithBackbone, {\n readonly initialSelected?: boolean;\n}>;", + "QuestionnaireAnswerValueInput": "type QuestionnaireAnswerValueInput = ExactlyOne;", + "QuestionnaireEnableWhenInput": "type QuestionnaireEnableWhenInput = WithBackbone, {\n readonly question: string;\n readonly operator: '!=' | '<' | '<=' | '=' | '>' | '>=' | 'exists';\n}>;", + "QuestionnaireInitialInput": "type QuestionnaireInitialInput = WithBackbone;", + "QuestionnaireInput": "interface QuestionnaireInput {\n readonly id?: FhirId;\n readonly url: AbsoluteUri;\n readonly version: SemVer;\n readonly name?: string;\n readonly title?: string;\n readonly status: Questionnaire['status'];\n /** Fixed by the Grove Questionnaire profile to exactly `Patient`. */\n readonly subjectTypes: readonly ['Patient'];\n readonly date?: FhirInstant;\n readonly description?: string;\n readonly purpose?: string;\n readonly extensions?: readonly Extension[];\n readonly items: readonly [QuestionnaireItemInput, ...QuestionnaireItemInput[]];\n}", + "QuestionnaireItemInput": "interface QuestionnaireItemInput extends BackboneInput {\n readonly linkId: string;\n readonly definition?: string;\n readonly code?: readonly Coding[];\n readonly prefix?: string;\n readonly text?: string;\n readonly type: QuestionnaireItemType;\n readonly enableWhen?: readonly QuestionnaireEnableWhenInput[];\n readonly enableBehavior?: 'all' | 'any';\n readonly required?: boolean;\n readonly repeats?: boolean;\n readonly readOnly?: boolean;\n readonly maxLength?: number;\n readonly answerValueSet?: string;\n readonly answerOption?: readonly QuestionnaireAnswerOptionInput[];\n readonly initial?: readonly QuestionnaireInitialInput[];\n readonly item?: readonly QuestionnaireItemInput[];\n}", + "questionnaireItemSchema": "const questionnaireItemSchema: z.ZodType>", + "QuestionnaireItemType": "type QuestionnaireItemType = 'attachment' | 'boolean' | 'choice' | 'date' | 'dateTime' | 'decimal' | 'display' | 'group' | 'integer' | 'open-choice' | 'quantity' | 'string' | 'text' | 'time' | 'url';", + "QuestionnairePair": "interface QuestionnairePair {\n readonly questionnaire: GroveQuestionnaire;\n readonly response: GroveQuestionnaireResponse;\n}", + "QuestionnairePreflightOptions": "interface QuestionnairePreflightOptions {\n readonly valueSets?: readonly ResolvedQuestionnaireValueSetInput[];\n}", + "QuestionnaireResponseAnswerInput": "type QuestionnaireResponseAnswerInput = WithBackbone;", + "questionnaireResponseAnswerSchema": "const questionnaireResponseAnswerSchema: z.ZodType>", + "QuestionnaireResponseAuthorInput": "type QuestionnaireResponseAuthorInput = TypedQuestionnaireResponseReference<'Device' | 'Organization' | 'Patient' | 'Practitioner' | 'PractitionerRole' | 'RelatedPerson'>;", + "QuestionnaireResponseIdentifierInput": "interface QuestionnaireResponseIdentifierInput {\n readonly system: AbsoluteUri;\n readonly value: string;\n}", + "QuestionnaireResponseInput": "interface QuestionnaireResponseInput {\n readonly id?: FhirId;\n /** Exact `Questionnaire.url|Questionnaire.version` canonical. */\n readonly questionnaire: Canonical;\n readonly identifier: QuestionnaireResponseIdentifierInput;\n readonly status: QuestionnaireResponse['status'];\n readonly subject: QuestionnaireResponseSubjectInput;\n readonly authored: FhirInstant;\n readonly author?: QuestionnaireResponseAuthorInput;\n readonly source?: QuestionnaireResponseSourceInput;\n readonly extensions?: readonly Extension[];\n readonly items?: readonly QuestionnaireResponseItemInput[];\n}", + "QuestionnaireResponseItemInput": "interface QuestionnaireResponseItemInput extends BackboneInput {\n readonly linkId: string;\n readonly definition?: string;\n readonly text?: string;\n readonly answer?: readonly QuestionnaireResponseAnswerInput[];\n readonly item?: readonly QuestionnaireResponseItemInput[];\n}", + "questionnaireResponseItemSchema": "const questionnaireResponseItemSchema: z.ZodType>", + "questionnaireResponseSchema": "const questionnaireResponseSchema: z.ZodType", + "QuestionnaireResponseSourceInput": "type QuestionnaireResponseSourceInput = TypedQuestionnaireResponseReference<'Patient' | 'Practitioner' | 'PractitionerRole' | 'RelatedPerson'>;", + "QuestionnaireResponseSubjectInput": "type QuestionnaireResponseSubjectInput = TypedQuestionnaireResponseReference<'Patient'>;", + "questionnaireSchema": "const questionnaireSchema: z.ZodType", + "QuestionnaireValueSetConceptInput": "interface QuestionnaireValueSetConceptInput {\n readonly system: AbsoluteUri;\n readonly code: string;\n readonly version?: string;\n}", + "ResolvedQuestionnaireValueSetInput": "interface ResolvedQuestionnaireValueSetInput {\n readonly canonical: Canonical;\n readonly concepts: readonly QuestionnaireValueSetConceptInput[];\n}" + } +} diff --git a/packages/fhir/test/type-surface.json.license b/packages/fhir/test/type-surface.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/test/type-surface.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT From 48a13d14835ea089f4b442fb2acdce4909647d12 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Tue, 22 Sep 2026 23:05:53 -0700 Subject: [PATCH 24/31] Resolve the Open Dependency Advisories --- package-lock.json | 581 +++++++++--------- packages/design-system/docs/package-lock.json | 62 +- packages/design-system/package.json | 4 +- .../firebase-cloud-messaging/package.json | 4 +- packages/firebase-utils/package.json | 4 +- scripts/audit-docs.mjs | 11 +- scripts/audit-docs.test.mjs | 24 +- 7 files changed, 347 insertions(+), 343 deletions(-) diff --git a/package-lock.json b/package-lock.json index d8653e03..eb9d8b5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2588,9 +2588,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", - "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz", + "integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==", "cpu": [ "arm64" ], @@ -2606,13 +2606,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.3.2" + "@img/sharp-libvips-darwin-arm64": "1.3.3" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", - "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz", + "integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==", "cpu": [ "x64" ], @@ -2628,20 +2628,20 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.3.2" + "@img/sharp-libvips-darwin-x64": "1.3.3" } }, "node_modules/@img/sharp-freebsd-wasm32": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", - "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz", + "integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==", "license": "Apache-2.0", "optional": true, "os": [ "freebsd" ], "dependencies": { - "@img/sharp-wasm32": "0.35.3" + "@img/sharp-wasm32": "0.35.4" }, "engines": { "node": ">=20.9.0" @@ -2651,9 +2651,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", - "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz", + "integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==", "cpu": [ "arm64" ], @@ -2667,9 +2667,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", - "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz", + "integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==", "cpu": [ "x64" ], @@ -2683,9 +2683,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", - "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz", + "integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==", "cpu": [ "arm" ], @@ -2702,9 +2702,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", - "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz", + "integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==", "cpu": [ "arm64" ], @@ -2721,9 +2721,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", - "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz", + "integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==", "cpu": [ "ppc64" ], @@ -2740,9 +2740,9 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", - "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz", + "integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==", "cpu": [ "riscv64" ], @@ -2759,9 +2759,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", - "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz", + "integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==", "cpu": [ "s390x" ], @@ -2778,9 +2778,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", - "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz", + "integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==", "cpu": [ "x64" ], @@ -2797,9 +2797,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", - "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz", + "integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==", "cpu": [ "arm64" ], @@ -2816,9 +2816,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", - "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz", + "integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==", "cpu": [ "x64" ], @@ -2835,9 +2835,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", - "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz", + "integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==", "cpu": [ "arm" ], @@ -2856,13 +2856,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.3.2" + "@img/sharp-libvips-linux-arm": "1.3.3" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", - "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz", + "integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==", "cpu": [ "arm64" ], @@ -2881,13 +2881,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.3.2" + "@img/sharp-libvips-linux-arm64": "1.3.3" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", - "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz", + "integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==", "cpu": [ "ppc64" ], @@ -2906,13 +2906,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.3.2" + "@img/sharp-libvips-linux-ppc64": "1.3.3" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", - "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz", + "integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==", "cpu": [ "riscv64" ], @@ -2931,13 +2931,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.3.2" + "@img/sharp-libvips-linux-riscv64": "1.3.3" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", - "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz", + "integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==", "cpu": [ "s390x" ], @@ -2956,13 +2956,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.3.2" + "@img/sharp-libvips-linux-s390x": "1.3.3" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", - "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz", + "integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==", "cpu": [ "x64" ], @@ -2981,13 +2981,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.3.2" + "@img/sharp-libvips-linux-x64": "1.3.3" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", - "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz", + "integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==", "cpu": [ "arm64" ], @@ -3006,13 +3006,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", - "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz", + "integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==", "cpu": [ "x64" ], @@ -3031,17 +3031,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.3.2" + "@img/sharp-libvips-linuxmusl-x64": "1.3.3" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", - "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz", + "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==", "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.11.1" + "@emnapi/runtime": "^1.11.3" }, "engines": { "node": ">=20.9.0" @@ -3050,17 +3050,27 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-wasm32/node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@img/sharp-webcontainers-wasm32": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", - "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz", + "integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==", "cpu": [ "wasm32" ], "license": "Apache-2.0", "optional": true, "dependencies": { - "@img/sharp-wasm32": "0.35.3" + "@img/sharp-wasm32": "0.35.4" }, "engines": { "node": ">=20.9.0" @@ -3070,9 +3080,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", - "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz", + "integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==", "cpu": [ "arm64" ], @@ -3089,9 +3099,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", - "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz", + "integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==", "cpu": [ "ia32" ], @@ -3108,9 +3118,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", - "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz", + "integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==", "cpu": [ "x64" ], @@ -3953,15 +3963,15 @@ } }, "node_modules/@next/env": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.0.tgz", - "integrity": "sha512-o9r1S0BNiNreHP9Vs+Qnqd9kviDkJh8xIACY7UFZSmiGbbQRzPBBosvHzAU4TULHOIuOj/18RSsyz2qrREmIFw==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.5.tgz", + "integrity": "sha512-NWEXVDMqoEo0ktmU6u0sE2Vg0LOcsD7NnOTJNo3/fEaTfsg+F1bMIxuDmQbda4e3yTIQwVdUREF2yIuMOusKtg==", "license": "MIT" }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.3.0.tgz", - "integrity": "sha512-55hpqq18bEVAlxedlTt3tFqZmKg2nUXT1kn1G/BGEy0R13h3LwtwHPVzzjG6P4LLeOHE32PFDQUVaJEWvBEZBw==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.3.5.tgz", + "integrity": "sha512-pMmGgETfKvElucLHtVaeiMRbp2zUbvKx7b1yGko0liBz3cw1mKSggWN/Rp/wPz8z+E1O82u3r4L1Co+ZS5hokQ==", "cpu": [ "arm64" ], @@ -3975,9 +3985,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.3.0.tgz", - "integrity": "sha512-SOi96kSaF5T+0wW4koiM1bWzSPwjzTesC1p3df+FjdOi5LIQkBK/blxh7HdoKnNuI4PURF1OO7TZqtfnbWDSgw==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.3.5.tgz", + "integrity": "sha512-76VaGYvf6HPa5/w12yLkE3dXTn9AfdEviI79oEL3aZoAmRLc9rWitjWqyjViVysK/ht/y9YKzFkBrUdi/wGkow==", "cpu": [ "x64" ], @@ -3991,9 +4001,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.3.0.tgz", - "integrity": "sha512-P0gZAoPMF4dyTRzhmkV4PrqVzSOB6t4mC1oI3c4dqijJ+OVEVx5clIXAKR4/uQpsqw2KKM/0D5tVumcR2r5blg==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.3.5.tgz", + "integrity": "sha512-zKDELJ5jSQMHeO/hmXUQsAzagX4bQD4OiMi3pQ5FbUj+yK506oLVHnKA2YXMlbg1EHHqJYtyePOgByIDXD1lqw==", "cpu": [ "arm64" ], @@ -4010,9 +4020,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.3.0.tgz", - "integrity": "sha512-tXXGKJw0m37O0eKJARVTX/TheKPhz0QFVtVVZXmOig+9YKLQOSP6hvf2pxv5DO7CLEJyTHx3Pg043CDQkv1G4Q==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.3.5.tgz", + "integrity": "sha512-7Vql0pgzCoHagv6+FNOZoqmJqA52c6zeVbhtS/47qFozO1MSx4ms7x7GHiciY8R5CDsSMKMQjJEryoJLcsBIbA==", "cpu": [ "arm64" ], @@ -4029,9 +4039,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.0.tgz", - "integrity": "sha512-pjGxK5EY7yWml78ALejFkWmgHsU7wbFQrISiugpH6FbUJhgEvw3xFZ/EBAtLl7QtL0WdQKiG9eWJ3mOKGTukHw==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.5.tgz", + "integrity": "sha512-NH/xzehyHEFWE2nlcZon7TB/0+H4shfWCi7S1zka815XCOhJDYZhoeJtOYy0dh0WVRWACVXSyGNFFytoMxUhRg==", "cpu": [ "x64" ], @@ -4048,9 +4058,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.3.0.tgz", - "integrity": "sha512-sjo++Xx+lomlPs3HRsHWhVDyGG6ms1kGW5EtHLERdII8AyG1i+f6aq68xHREO6AEMlhjTNEWBSmfJfqm9orf7g==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.3.5.tgz", + "integrity": "sha512-lV4+EhWMfS8jcC+EH2nn/Cm5cn6XsgbE07bU9tMH8fCo0tNAqhyzi1b5wQ/Tn6NGFTvKDY65w3ZH95EjwBRAnQ==", "cpu": [ "x64" ], @@ -4067,9 +4077,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.3.0.tgz", - "integrity": "sha512-C5JSgiO54wURdaxdEUIXqkz04uMqC9UmPX1gtDrV/5Tf1UowdWYI8uA5hfFbPolTlp0q4KZ60xlHePNibf0VIw==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.3.5.tgz", + "integrity": "sha512-/wKzAREX2RF++MhicjDbg8tGn2AiBIM0+EFeTFKoUEUbW5D6amCJehd5Z5G1H5/gxNdgnwoXMcHz24H/c2tGkQ==", "cpu": [ "arm64" ], @@ -4083,9 +4093,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.3.0.tgz", - "integrity": "sha512-fDOggsweNb5SSw0ZKVk6U+gxSyGFFlIBY/LBc1r8GUj4u/6t6oArL+Pmkg0MBnsgR+KkdsURilVH4F3GXUGepA==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.3.5.tgz", + "integrity": "sha512-LNdCHzgLFc+UeqMS84LzXPaeBRKyqDN9OMyFAr1OrB0XrNw78IRrEVtZvvA7245W/HsaoeVOQX9jPjPk8jojwA==", "cpu": [ "x64" ], @@ -5129,16 +5139,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/watcher/node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -9149,14 +9149,14 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz", - "integrity": "sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.11.tgz", + "integrity": "sha512-8MVGEFnJIcdGjcbfKmeq8z0pZHH0JlVtoVZH9Q/qwUp6wyFnEJUBMrw9DCaj+ra3vShGmhavjalMIhPNxZAUcw==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.10", + "@vitest/utils": "4.1.11", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", @@ -9170,8 +9170,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.1.10", - "vitest": "4.1.10" + "@vitest/browser": "4.1.11", + "vitest": "4.1.11" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -9235,13 +9235,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", - "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.10", + "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -9262,9 +9262,9 @@ } }, "node_modules/@vitest/mocker/node_modules/@vitest/spy": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", - "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", "dev": true, "license": "MIT", "funding": { @@ -9272,9 +9272,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", - "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "license": "MIT", "dependencies": { @@ -9285,13 +9285,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", - "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.10", + "@vitest/utils": "4.1.11", "pathe": "^2.0.3" }, "funding": { @@ -9299,14 +9299,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", - "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.10", - "@vitest/utils": "4.1.10", + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -9328,13 +9328,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", - "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.10", + "@vitest/pretty-format": "4.1.11", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -9903,11 +9903,14 @@ } }, "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "license": "Python-2.0" + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } }, "node_modules/aria-hidden": { "version": "1.2.6", @@ -10961,6 +10964,15 @@ "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -11191,9 +11203,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", - "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", "dev": true, "license": "MIT" }, @@ -14594,23 +14606,14 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", - "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", + "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -15159,16 +15162,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/lightningcss/node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, "node_modules/limiter": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", @@ -15682,13 +15675,13 @@ "license": "MIT" }, "node_modules/next": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/next/-/next-16.3.0.tgz", - "integrity": "sha512-NEdGOzH+08eTXMUp9UYkA99Nhi5N6Thrhc1jgFOQgfgnGK/dA2hRwBpXep+exdFQrnwlRf/3Wixyp8lLBUpE2A==", + "version": "16.3.5", + "resolved": "https://registry.npmjs.org/next/-/next-16.3.5.tgz", + "integrity": "sha512-MdtsTgzyfCPRLC6uJ1mN8ao7lyJ4BB0U6Inhnx3gta1UcCIdHK3yxLG0E8OWQteWD8/Q0qb8A5o7wJaL8M9y2w==", "license": "MIT", "dependencies": { - "@next/env": "16.3.0", - "@swc/helpers": "0.5.15", + "@next/env": "16.3.5", + "@swc/helpers": "0.5.23", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.5.23", @@ -15701,15 +15694,15 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.3.0", - "@next/swc-darwin-x64": "16.3.0", - "@next/swc-linux-arm64-gnu": "16.3.0", - "@next/swc-linux-arm64-musl": "16.3.0", - "@next/swc-linux-x64-gnu": "16.3.0", - "@next/swc-linux-x64-musl": "16.3.0", - "@next/swc-win32-arm64-msvc": "16.3.0", - "@next/swc-win32-x64-msvc": "16.3.0", - "sharp": "^0.35.3" + "@next/swc-darwin-arm64": "16.3.5", + "@next/swc-darwin-x64": "16.3.5", + "@next/swc-linux-arm64-gnu": "16.3.5", + "@next/swc-linux-arm64-musl": "16.3.5", + "@next/swc-linux-x64-gnu": "16.3.5", + "@next/swc-linux-x64-musl": "16.3.5", + "@next/swc-win32-arm64-msvc": "16.3.5", + "@next/swc-win32-x64-msvc": "16.3.5", + "sharp": "^0.35.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -15790,13 +15783,32 @@ "@formatjs/fast-memoize": "3.1.7" } }, - "node_modules/next/node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", + "node_modules/next/node_modules/postcss": { + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "tslib": "^2.8.0" + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" } }, "node_modules/node-addon-api": { @@ -16499,6 +16511,7 @@ "version": "8.5.26", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -17532,9 +17545,9 @@ } }, "node_modules/sharp": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", - "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz", + "integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==", "license": "Apache-2.0", "optional": true, "dependencies": { @@ -17549,31 +17562,31 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.35.3", - "@img/sharp-darwin-x64": "0.35.3", - "@img/sharp-freebsd-wasm32": "0.35.3", - "@img/sharp-libvips-darwin-arm64": "1.3.2", - "@img/sharp-libvips-darwin-x64": "1.3.2", - "@img/sharp-libvips-linux-arm": "1.3.2", - "@img/sharp-libvips-linux-arm64": "1.3.2", - "@img/sharp-libvips-linux-ppc64": "1.3.2", - "@img/sharp-libvips-linux-riscv64": "1.3.2", - "@img/sharp-libvips-linux-s390x": "1.3.2", - "@img/sharp-libvips-linux-x64": "1.3.2", - "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", - "@img/sharp-libvips-linuxmusl-x64": "1.3.2", - "@img/sharp-linux-arm": "0.35.3", - "@img/sharp-linux-arm64": "0.35.3", - "@img/sharp-linux-ppc64": "0.35.3", - "@img/sharp-linux-riscv64": "0.35.3", - "@img/sharp-linux-s390x": "0.35.3", - "@img/sharp-linux-x64": "0.35.3", - "@img/sharp-linuxmusl-arm64": "0.35.3", - "@img/sharp-linuxmusl-x64": "0.35.3", - "@img/sharp-webcontainers-wasm32": "0.35.3", - "@img/sharp-win32-arm64": "0.35.3", - "@img/sharp-win32-ia32": "0.35.3", - "@img/sharp-win32-x64": "0.35.3" + "@img/sharp-darwin-arm64": "0.35.4", + "@img/sharp-darwin-x64": "0.35.4", + "@img/sharp-freebsd-wasm32": "0.35.4", + "@img/sharp-libvips-darwin-arm64": "1.3.3", + "@img/sharp-libvips-darwin-x64": "1.3.3", + "@img/sharp-libvips-linux-arm": "1.3.3", + "@img/sharp-libvips-linux-arm64": "1.3.3", + "@img/sharp-libvips-linux-ppc64": "1.3.3", + "@img/sharp-libvips-linux-riscv64": "1.3.3", + "@img/sharp-libvips-linux-s390x": "1.3.3", + "@img/sharp-libvips-linux-x64": "1.3.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3", + "@img/sharp-libvips-linuxmusl-x64": "1.3.3", + "@img/sharp-linux-arm": "0.35.4", + "@img/sharp-linux-arm64": "0.35.4", + "@img/sharp-linux-ppc64": "0.35.4", + "@img/sharp-linux-riscv64": "0.35.4", + "@img/sharp-linux-s390x": "0.35.4", + "@img/sharp-linux-x64": "0.35.4", + "@img/sharp-linuxmusl-arm64": "0.35.4", + "@img/sharp-linuxmusl-x64": "0.35.4", + "@img/sharp-webcontainers-wasm32": "0.35.4", + "@img/sharp-win32-arm64": "0.35.4", + "@img/sharp-win32-ia32": "0.35.4", + "@img/sharp-win32-x64": "0.35.4" }, "peerDependenciesMeta": { "@types/node": { @@ -17581,16 +17594,6 @@ } } }, - "node_modules/sharp/node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=8" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -17733,6 +17736,13 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/stable-hash-x": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz", @@ -19711,16 +19721,6 @@ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/vite/node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, "node_modules/vite/node_modules/lightningcss": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", @@ -19995,19 +19995,19 @@ } }, "node_modules/vitest": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", - "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.10", - "@vitest/mocker": "4.1.10", - "@vitest/pretty-format": "4.1.10", - "@vitest/runner": "4.1.10", - "@vitest/snapshot": "4.1.10", - "@vitest/spy": "4.1.10", - "@vitest/utils": "4.1.10", + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -20035,12 +20035,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.10", - "@vitest/browser-preview": "4.1.10", - "@vitest/browser-webdriverio": "4.1.10", - "@vitest/coverage-istanbul": "4.1.10", - "@vitest/coverage-v8": "4.1.10", - "@vitest/ui": "4.1.10", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -20085,16 +20085,16 @@ } }, "node_modules/vitest/node_modules/@vitest/expect": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", - "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.10", - "@vitest/utils": "4.1.10", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -20103,9 +20103,9 @@ } }, "node_modules/vitest/node_modules/@vitest/spy": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", - "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", "dev": true, "license": "MIT", "funding": { @@ -20684,7 +20684,7 @@ "@types/react": "^19.2.18", "@types/react-dom": "^19.2.4", "@vitejs/plugin-react": "^6.0.5", - "@vitest/coverage-v8": "4.1.10", + "@vitest/coverage-v8": "4.1.11", "eslint": "^10", "eslint-plugin-jsdoc": "^63.3.2", "jsdom": "^30.0.1", @@ -20698,7 +20698,7 @@ "vite": "^8.2.0", "vite-plugin-dts": "^5.0.0", "vite-plugin-static-copy": "^4.0.0", - "vitest": "4.1.10" + "vitest": "4.1.11" }, "engines": { "node": ">=24" @@ -20844,8 +20844,7 @@ "dependencies": { "@noble/hashes": "2.3.0", "@types/fhir": "^0.0.44", - "uuid": "^14.0.2", - "zod": "^4.4.3" + "uuid": "^14.0.2" }, "devDependencies": { "@schmiedmayerlab/grove-configurations": "0.0.0", @@ -20860,10 +20859,14 @@ "prettier": "^3.6.2", "rimraf": "^6.1.2", "ts-jest": "^29.4.12", - "typescript": "^6.0.3" + "typescript": "^6.0.3", + "zod": "^4.4.3" }, "engines": { "node": ">=24" + }, + "peerDependencies": { + "zod": "^4.4.3" } }, "packages/fhir/node_modules/@types/node": { @@ -20894,7 +20897,7 @@ "devDependencies": { "@schmiedmayerlab/grove-configurations": "0.0.0", "@types/node": "^24.10.0", - "@vitest/coverage-v8": "4.1.10", + "@vitest/coverage-v8": "4.1.11", "eslint": "^10.8.0", "eslint-import-resolver-typescript": "^4.4.5", "firebase-admin": "^14.2.0", @@ -20902,7 +20905,7 @@ "rimraf": "^6.1.2", "typescript": "^6.0.3", "typescript-eslint": "^8.65.0", - "vitest": "4.1.10" + "vitest": "4.1.11" }, "engines": { "node": ">=24" @@ -20938,14 +20941,14 @@ "devDependencies": { "@schmiedmayerlab/grove-configurations": "0.0.0", "@types/node": "^24.10.0", - "@vitest/coverage-v8": "4.1.10", + "@vitest/coverage-v8": "4.1.11", "eslint": "^10.8.0", "eslint-import-resolver-typescript": "^4.4.5", "prettier": "^3.6.2", "rimraf": "^6.1.2", "typescript": "^6.0.3", "typescript-eslint": "^8.65.0", - "vitest": "4.1.10" + "vitest": "4.1.11" }, "engines": { "node": ">=24" diff --git a/packages/design-system/docs/package-lock.json b/packages/design-system/docs/package-lock.json index 700d07e1..b38c7c3b 100644 --- a/packages/design-system/docs/package-lock.json +++ b/packages/design-system/docs/package-lock.json @@ -6868,9 +6868,9 @@ } }, "node_modules/body-parser": { - "version": "1.20.6", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", - "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", + "version": "1.20.8", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.8.tgz", + "integrity": "sha512-JNcyFQ64OiijEkPzUBTCe+hyPXUD/3LEldGQ6iF5LR1w00mx9o7xtDWHXBY2iItjdCFGoilOLNQbH943ut7pHA==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -6881,7 +6881,7 @@ "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", - "qs": "~6.15.1", + "qs": "~6.16.0", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" @@ -7503,9 +7503,9 @@ "license": "MIT" }, "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.10.0.tgz", + "integrity": "sha512-AidJptpBJmjTclAp9BkLwJi0T93fo5epJnbaZslpg6QVzpHjAiveF55mE9AcUJiGMqRHgMDY8soMsQtuNYMHfw==", "license": "MIT" }, "node_modules/colorette": { @@ -9117,9 +9117,9 @@ } }, "node_modules/express": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", - "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "version": "4.22.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.3.tgz", + "integrity": "sha512-Bdcs4+3qlpVlx2NRn6fgX2Ue2/gGRaPeawebgclM0ERSCqDpA+owF1fdPwjJUTAJWMTuAaxjDf+hzb0/4eKvvw==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", @@ -9141,9 +9141,9 @@ "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.12", + "path-to-regexp": "~0.1.13", "proxy-addr": "~2.0.7", - "qs": "~6.15.1", + "qs": "~6.16.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", @@ -9251,9 +9251,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", - "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "funding": [ { "type": "github", @@ -10374,15 +10374,15 @@ } }, "node_modules/image-size": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz", - "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.4.tgz", + "integrity": "sha512-QRUkFFsRV/6fuESxb9Vkq+a0LkSrgKXuc2NEqfikiXxxN/G3tjWt5EVUlMaImRBZRZK/jRBEbYvpPYZL8t08Zw==", "license": "MIT", "bin": { "image-size": "bin/image-size.js" }, "engines": { - "node": ">=16.x" + "node": ">=18" } }, "node_modules/import-fresh": { @@ -10869,9 +10869,9 @@ } }, "node_modules/joi": { - "version": "17.13.4", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.4.tgz", - "integrity": "sha512-1RuuER6kmt8K8I3nIWvPZKi5RQCb568ZPyY4Pwjlua+yo+63ZTmIwxLZH0heBmiKN4uxjvCiarDrjaeH84xicQ==", + "version": "17.13.8", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.8.tgz", + "integrity": "sha512-iPKOGmiRw1jxf/JOPwxmCcUQAOdF359mdzYiP2DJ+TMX0YK2zjK3D+zYOaGjpumWxOFF/l2xVWjRVK5bGSLdEw==", "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.3.0", @@ -10888,9 +10888,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", - "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "funding": [ { "type": "github", @@ -16198,9 +16198,9 @@ } }, "node_modules/qs": { - "version": "6.15.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", - "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", @@ -17930,9 +17930,9 @@ "license": "MIT" }, "node_modules/svgo": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.4.tgz", - "integrity": "sha512-GsNRis4e8jxn2Y9ENz/8lbJ93CstG8svtMnuRaHbiF2LTJ5tK0/q3t/URPq9Zc7zVWBJnNnJMIp6bevK7bSmNg==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.5.tgz", + "integrity": "sha512-8SQMzdrvWaD8deUmrnYB+ASyxBVgWUOilg+A75nE/76WdLpj6LopCwiAVvkzkcqy/9b7t2Mg7faFLjg0ZRcZ3w==", "license": "MIT", "dependencies": { "commander": "^7.2.0", diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 5b418c4c..fb195e28 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -400,7 +400,7 @@ "@types/react": "^19.2.18", "@types/react-dom": "^19.2.4", "@vitejs/plugin-react": "^6.0.5", - "@vitest/coverage-v8": "4.1.10", + "@vitest/coverage-v8": "4.1.11", "eslint": "^10", "eslint-plugin-jsdoc": "^63.3.2", "jsdom": "^30.0.1", @@ -414,7 +414,7 @@ "vite": "^8.2.0", "vite-plugin-dts": "^5.0.0", "vite-plugin-static-copy": "^4.0.0", - "vitest": "4.1.10" + "vitest": "4.1.11" }, "peerDependencies": { "@tanstack/react-query": "^5", diff --git a/packages/firebase-cloud-messaging/package.json b/packages/firebase-cloud-messaging/package.json index 99eeaede..cd6fba90 100644 --- a/packages/firebase-cloud-messaging/package.json +++ b/packages/firebase-cloud-messaging/package.json @@ -59,7 +59,7 @@ "devDependencies": { "@schmiedmayerlab/grove-configurations": "0.0.0", "@types/node": "^24.10.0", - "@vitest/coverage-v8": "4.1.10", + "@vitest/coverage-v8": "4.1.11", "eslint": "^10.8.0", "eslint-import-resolver-typescript": "^4.4.5", "firebase-admin": "^14.2.0", @@ -67,7 +67,7 @@ "rimraf": "^6.1.2", "typescript": "^6.0.3", "typescript-eslint": "^8.65.0", - "vitest": "4.1.10" + "vitest": "4.1.11" }, "peerDependencies": { "firebase-admin": "^14.2.0" diff --git a/packages/firebase-utils/package.json b/packages/firebase-utils/package.json index a5d11092..82fadde7 100644 --- a/packages/firebase-utils/package.json +++ b/packages/firebase-utils/package.json @@ -58,14 +58,14 @@ "devDependencies": { "@schmiedmayerlab/grove-configurations": "0.0.0", "@types/node": "^24.10.0", - "@vitest/coverage-v8": "4.1.10", + "@vitest/coverage-v8": "4.1.11", "eslint": "^10.8.0", "eslint-import-resolver-typescript": "^4.4.5", "prettier": "^3.6.2", "rimraf": "^6.1.2", "typescript": "^6.0.3", "typescript-eslint": "^8.65.0", - "vitest": "4.1.10" + "vitest": "4.1.11" }, "engines": { "node": ">=24" diff --git a/scripts/audit-docs.mjs b/scripts/audit-docs.mjs index 81649215..0000a097 100644 --- a/scripts/audit-docs.mjs +++ b/scripts/audit-docs.mjs @@ -14,16 +14,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url' const executeFile = promisify(execFile) const repositoryRoot = resolve(fileURLToPath(new URL('..', import.meta.url))) -export const auditExceptions = new Map([ - [ - 'GHSA-5p2g-fcmc-qvqq', - 'image-size is used only while building version-controlled documentation; no patched release exists.', - ], - [ - 'GHSA-w3rx-r6r6-pgpr', - 'image-size is used only while building version-controlled documentation; no patched release exists.', - ], -]) +export const auditExceptions = new Map() const advisoryId = (advisory) => advisory.url?.match(/\/advisories\/(GHSA-[A-Za-z0-9-]+)$/)?.[1] diff --git a/scripts/audit-docs.test.mjs b/scripts/audit-docs.test.mjs index 11f02721..c0d1c69c 100644 --- a/scripts/audit-docs.test.mjs +++ b/scripts/audit-docs.test.mjs @@ -8,7 +8,12 @@ import assert from 'node:assert/strict' import { describe, it } from 'node:test' -import { auditExceptions, validateAuditReport } from './audit-docs.mjs' +import { validateAuditReport } from './audit-docs.mjs' + +const exceptions = new Map([ + ['GHSA-5p2g-fcmc-qvqq', 'Build-time only; no patched release exists.'], + ['GHSA-w3rx-r6r6-pgpr', 'Build-time only; no patched release exists.'], +]) const advisory = (id) => ({ title: id, @@ -48,8 +53,12 @@ const report = ({ }) describe('documentation audit exceptions', () => { + it('accepts a clean report under the standing exceptions', () => { + assert.deepEqual(validateAuditReport({ vulnerabilities: {} }), []) + }) + it('accepts only the documented, unfixable advisories', () => { - assert.deepEqual(validateAuditReport(report()), [ + assert.deepEqual(validateAuditReport(report(), exceptions), [ 'GHSA-5p2g-fcmc-qvqq', 'GHSA-w3rx-r6r6-pgpr', ]) @@ -57,7 +66,8 @@ describe('documentation audit exceptions', () => { it('rejects unexpected advisories', () => { assert.throws( - () => validateAuditReport(report({ includeUnexpected: true })), + () => + validateAuditReport(report({ includeUnexpected: true }), exceptions), /Unexpected npm audit findings: GHSA-xxxx-yyyy-zzzz/, ) }) @@ -74,7 +84,7 @@ describe('documentation audit exceptions', () => { it('rejects stale exceptions and advisories with fixes', () => { const oneException = new Map([ - ...auditExceptions, + ...exceptions, ['GHSA-resolved-advisory', 'Resolved.'], ]) assert.throws( @@ -82,18 +92,18 @@ describe('documentation audit exceptions', () => { /Remove resolved npm audit exceptions/, ) assert.throws( - () => validateAuditReport(report({ fixAvailable: true })), + () => validateAuditReport(report({ fixAvailable: true }), exceptions), /Fixes are now available/, ) assert.throws( - () => validateAuditReport(report({ rootFixAvailable: true })), + () => validateAuditReport(report({ rootFixAvailable: true }), exceptions), /Fixes are now available/, ) }) it('ignores fixAvailable on transitive, non-direct dependencies', () => { assert.deepEqual( - validateAuditReport(report({ transitiveFixAvailable: true })), + validateAuditReport(report({ transitiveFixAvailable: true }), exceptions), ['GHSA-5p2g-fcmc-qvqq', 'GHSA-w3rx-r6r6-pgpr'], ) }) From c4fc2af0e4f3c1ee969e058b19719c02c2871da1 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Tue, 22 Sep 2026 23:05:53 -0700 Subject: [PATCH 25/31] Adopt Record-First Identities, Branded Identifier Systems and Producer Diagnostics --- packages/fhir/README.md | 102 ++-- packages/fhir/scripts/browser-smoke.mjs | 28 +- .../scripts/generate-conformance-fixtures.mjs | 28 +- packages/fhir/scripts/package-smoke.mjs | 2 + packages/fhir/src/core/index.ts | 6 + packages/fhir/src/core/primitives.ts | 26 + packages/fhir/src/core/result.ts | 32 + packages/fhir/src/index.ts | 12 + packages/fhir/src/mobile/context.ts | 32 +- packages/fhir/src/mobile/identity.ts | 328 +++++++++-- packages/fhir/src/mobile/index.ts | 6 + packages/fhir/src/mobile/types.ts | 35 +- packages/fhir/src/providers/builder.ts | 8 +- packages/fhir/src/providers/context-graph.ts | 13 +- packages/fhir/src/providers/graph.ts | 7 +- packages/fhir/src/providers/identity.ts | 190 +++--- packages/fhir/src/providers/index.ts | 13 +- .../src/providers/provider-input-schemas.ts | 33 +- packages/fhir/src/providers/provider.ts | 20 +- packages/fhir/src/providers/recording.ts | 14 +- packages/fhir/src/providers/retraction.ts | 21 +- packages/fhir/src/providers/types.ts | 17 +- packages/fhir/src/questionnaire/builder.ts | 4 +- packages/fhir/src/questionnaire/parse.ts | 3 +- packages/fhir/src/questionnaire/preflight.ts | 4 +- packages/fhir/src/questionnaire/references.ts | 4 +- packages/fhir/src/questionnaire/types.ts | 3 +- .../fhir/src/r4/active-graph-semantics.ts | 10 +- packages/fhir/src/r4/diagnostics.ts | 5 +- packages/fhir/src/r4/exchange-envelope.ts | 19 +- packages/fhir/src/r4/graph-schema-utils.ts | 4 +- .../fhir/src/r4/retraction-graph-semantics.ts | 8 +- packages/fhir/src/r4/retraction-targets.ts | 49 +- packages/fhir/test/diagnostics.test.ts | 60 ++ packages/fhir/test/exchange-protocol.test.ts | 545 +++++++++++++----- packages/fhir/test/export-surface.json | 6 + packages/fhir/test/foundation.test.ts | 19 + packages/fhir/test/mobile.test.ts | 76 ++- packages/fhir/test/provider-contract.test.ts | 17 +- packages/fhir/test/provider-identity.test.ts | 18 +- packages/fhir/test/provider-semantics.test.ts | 74 +-- packages/fhir/test/provider-test-support.ts | 27 +- .../test/questionnaire-input-surface.test.ts | 8 +- .../fhir/test/questionnaire-test-support.ts | 3 +- packages/fhir/test/readme-walkthrough.test.ts | 34 +- packages/fhir/test/recording.test.ts | 33 +- packages/fhir/test/retraction.test.ts | 40 +- packages/fhir/test/study-context.test.ts | 2 +- packages/fhir/test/type-surface.json | 77 ++- 49 files changed, 1525 insertions(+), 600 deletions(-) diff --git a/packages/fhir/README.md b/packages/fhir/README.md index 360df32a..765a1606 100644 --- a/packages/fhir/README.md +++ b/packages/fhir/README.md @@ -23,7 +23,7 @@ Any receiver can deduplicate, correct and retract that graph without knowing whi Grove adds what plain FHIR lacks: stable identities that never leak the provider's own record ids, provenance that says which application assembled the graph on which host, and optional study context. The receiver gets a record it can store, match to a retry, replace with a later revision and retract, all through identifiers it can verify and none it can reverse. -If FHIR is new to you, the [New to FHIR](https://grovealliance.org/fhir/mobile/fhir-basics.html) page of the Grove Mobile guide explains the resources this package emits in about ten minutes. +If FHIR is new to you, the [New to FHIR](https://schmiedmayerlab.github.io/grove-fhir/fhir-basics.html) page of the Grove Mobile guide explains the resources this package emits in about ten minutes. ## What you need and why @@ -60,11 +60,11 @@ Persist the account pseudonym with the linked account. ### The application -The application is the software assembling the graph: a stable token for this build, a name and a version, as an `ApplicationDevice`. +The application is the software assembling the graph: a name, a version and a token for this build of the form `|`, as an `ApplicationDevice`. Provenance names the assembler, so a receiver knows which code produced a graph and can act on a defect in one version. It is a constant of your build; nothing to persist beyond your release history. -> **Note:** `host` is required on TypeScript, unlike the Swift and Kotlin producers: a phone reads its own facts from the operating system, a server states them, so you pass a stable token for the machine and its operating system version. +> **Note:** `host` is required on TypeScript, unlike the Swift and Kotlin producers: a phone reads its own facts from the operating system, a server states them, so you pass its operating system version and a token of the form `|`. > The conversion instant defaults to the moment the builder runs; pass `conversionInstant` when you replay a stored export or when one batch must share one instant. > `converterRole` defaults to the assembler, `studies` to none and `repositoryIds` to none. @@ -76,6 +76,7 @@ Once per installation: derive the identifier systems, validate the scope and des import { deriveOpaqueIdentitySystems, parseAbsoluteUri, + parseIdentifierSystem, parseKeyEpoch, validateOpaqueIdentityScope, type ApplicationDevice, @@ -90,6 +91,7 @@ const unwrap = (result: Result): Value => { return result.value } const uri = (value: string) => unwrap(parseAbsoluteUri(value)) +const identifierSystem = (value: string) => unwrap(parseIdentifierSystem(value)) // From your secret store: the HMAC key as base64url, and the UUID generated for this installation. const identitySecret = process.env.GROVE_IDENTITY_SECRET_BASE64URL @@ -116,7 +118,7 @@ const identityScope = unwrap( ) const application: ApplicationDevice = { - sourceDeviceToken: 'org.example.mystudy-server/2.1.0', + sourceDeviceToken: 'org.example.mystudy-server|2.1.0', name: 'MyStudy server', version: '2.1.0', } @@ -125,7 +127,7 @@ const application: ApplicationDevice = { Per export: reserve the next event sequence and create the context, using every default. ```typescript -import { hostname, release, type } from 'node:os' +import { machine, release, type } from 'node:os' import { deriveEventIdentifier, parseEventSequence, @@ -139,21 +141,23 @@ const context: ExchangeEventContext = { subject: { kind: 'logical', identifier: { - system: uri('https://mystudy.example.org/fhir/identifiers/participants'), + system: identifierSystem( + 'https://mystudy.example.org/fhir/identifiers/participants', + ), value: participant.pseudonym, }, }, event: unwrap(deriveEventIdentifier(identityScope, sequence)), identityScope, repositoryScope: { - system: uri( + system: identifierSystem( 'https://mystudy.example.org/fhir/identifiers/withings-accounts', ), value: participant.withingsAccountPseudonym, }, application, host: { - sourceDeviceToken: hostname(), + sourceDeviceToken: `${machine()}|${type()} ${release()}`, operatingSystemVersion: `${type()} ${release()}`, }, } @@ -218,7 +222,7 @@ What to persist, and why: When you know the participant's enrollment, name the study, its exact protocol revision and the enrollment; the graph bundles them as ResearchStudy, PlanDefinition and ResearchSubject entries and references the study from every output. A bundled subject adds your own Patient entry. -Reference: [The exchange event context](#the-exchange-event-context) and the guide's [Study context](https://grovealliance.org/fhir/mobile/study.html). +Reference: [The exchange event context](#the-exchange-event-context) and the guide's [Study context](https://schmiedmayerlab.github.io/grove-fhir/study.html). ```typescript const enrolled: ExchangeEventContext = { @@ -234,15 +238,19 @@ const enrolled: ExchangeEventContext = { studies: [ { study: { - system: uri('https://mystudy.example.org/fhir/identifiers/studies'), + system: identifierSystem( + 'https://mystudy.example.org/fhir/identifiers/studies', + ), value: 'heart-2026', }, - protocol: { - url: uri('https://mystudy.example.org/fhir/PlanDefinition/heart-2026'), - version: '3', - }, + protocolUrl: uri( + 'https://mystudy.example.org/fhir/PlanDefinition/heart-2026', + ), + protocolVersion: '3', enrollment: { - system: uri('https://mystudy.example.org/fhir/identifiers/enrollments'), + system: identifierSystem( + 'https://mystudy.example.org/fhir/identifiers/enrollments', + ), value: 'enrollment-7f3a', }, }, @@ -260,7 +268,7 @@ Reference: [Governed source identifiers](#governed-source-identifiers). const disclosed = buildProviderExchangeGraph(record, context, { nativeIdentifierDisclosure: { kind: 'authorized', - system: uri( + system: identifierSystem( 'https://mystudy.example.org/fhir/identifiers/withings-measure-groups', ), type: { text: 'Withings measure group id' }, @@ -296,7 +304,7 @@ const relayed: ExchangeEventContext = { converterRole: { kind: 'gateway-application', application: { - sourceDeviceToken: 'org.example.mystudy-app/4.2.0', + sourceDeviceToken: 'org.example.mystudy-app|4.2.0', name: 'MyStudy app', version: '4.2.0', }, @@ -307,6 +315,7 @@ const relayed: ExchangeEventContext = { ### Warnings An accepted record can still lose something, and `warnings` says what, with a registered `mobile-omission.*` code: `recording-device` when the source names a device without a stable per-unit token, so no Device is emitted; `source-offset` when an instant came without a UTC offset, so it is serialized in UTC; `unmodeled-metadata` when the source carried fields outside the adapter's allowlist, so they were withheld. +A `source-offset` warning names the element that lost its offset in `location`: `Observation.effectiveDateTime`, `Observation.effectivePeriod.start` or `Observation.effectivePeriod.end`. Log them with the event so a receiver's question has an answer. Reference: [Diagnostics](#diagnostics). @@ -402,21 +411,21 @@ npm run conformance -- --ig /path/to/grove-fhir The guide's vocabulary and the TypeScript type that carries it. -| Term in the guide | Here | -| ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | -| Exchange event | `ExchangeEventIdentifier`, minted by `deriveEventIdentifier` | -| Exchange graph | `ExchangeGraph`, built by `buildProviderExchangeGraph` and checked by `parseExchangeGraph` | -| Business identifier | `BusinessIdentifier` | -| Identifier role | `GroveIdentifierRole`, carried by a `RoledIdentifier` | -| Opaque identity | minted by an `OpaqueIdentityScope` under `DeploymentIdentifierSystems` | -| Entry-node key | `EntryNodeKey`, rendered as an `EntryNodeIdentifier` | -| Subject | `Subject` | -| Study enrollment | `StudyEnrollment` | -| Application device, host device, recording device | `ApplicationDevice`, `HostDevice`, `RecordingDevice` | -| Writer | `writer` on the source record, an `ApplicationDevice`; `WriterRecord` when the platform assigns a record identity | -| Retraction event and retraction target | `RetractionEvent` and `RetractionTarget`, derived by `retractionTargets` | -| Governed source identifier | `GovernedSourceIdentifierDisclosurePolicy` | -| Producer diagnostic | `ProducerDiagnosticCode`, described by `groveProducerDiagnostics`, reported as an `Issue` | +| Term in the guide | Here | +| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| Exchange event | `ExchangeEventIdentifier`, minted by `deriveEventIdentifier` | +| Exchange graph | `ExchangeGraph`, built by `buildProviderExchangeGraph` and checked by `parseExchangeGraph` | +| Business identifier | `BusinessIdentifier`, whose `system` is an `IdentifierSystem` | +| Identifier role | `GroveIdentifierRole`, carried by a `RoledIdentifier` | +| Opaque identity | minted by an `OpaqueIdentityScope` under `DeploymentIdentifierSystems`; a record's as a `SourceRecordIdentity` or `ProviderRecordIdentity` | +| Entry-node key | `EntryNodeKey`, rendered as an `EntryNodeIdentifier` | +| Subject | `Subject` | +| Study enrollment | `StudyEnrollment` | +| Application device, host device, recording device | `ApplicationDevice`, `HostDevice`, `RecordingDevice` | +| Writer | `Writer`, the source record's `writer`; `WriterRecord` when the platform assigns a record identity | +| Retraction event and retraction target | `RetractionEvent` and `RetractionTarget`, derived by `retractionTargets` | +| Governed source identifier | `GovernedSourceIdentifierDisclosurePolicy` | +| Producer diagnostic | `ProducerDiagnostic`, an `Issue` narrowed by `isProducerDiagnostic`; its `ProducerDiagnosticCode` is described by `groveProducerDiagnostics` | ## Reference @@ -450,18 +459,19 @@ The published JavaScript uses portable ES2022 and Web Platform APIs. - Runtime schemas are strict and reject unknown properties instead of silently removing them. - The supported R4 closure is intentionally bounded to the resources Grove constructs and validates. - Expected input failures use a discriminated `Result` with stable issue codes and paths; successful results may carry non-blocking warnings. -- Validated strings use branded types for instants, canonical URLs, FHIR ids, Patient references, `urn:uuid` full URLs, key epochs, event sequences, and entry-node ordinals. +- Validated strings use branded types for instants, canonical URLs, identifier systems, FHIR ids, Patient references, `urn:uuid` full URLs, key epochs, event sequences, entry-node ordinals, and part indexes. +- `IdentifierSystem` and `AbsoluteUri` validate alike but are distinct brands: an identifier system names the key space of an `Identifier.value`, while an absolute URI is anything else, such as a deployment root, a code system, or a canonical url; a value crosses from one to the other only through its parser. - The identity scope, event identifier, repository scope, device snapshots, conversion instant, and studies are caller-owned context; the source record is the adapter's normalized handoff. - Source/output/artifact/writer/device identifiers use HMAC-SHA-256 over unsigned-32-bit length-framed UTF-8 fields with an explicit protocol domain, key id, and positive key epoch. - `Bundle.identifier` is the sole business identifier for one immutable source-record revision event; Provenance uses a typed event-scoped node key because R4 Provenance has no business-identifier element. - Every Bundle `fullUrl` is lowercase UUIDv5 over the length-framed selected Identifier `(system, value)` pair. UUIDv5 formats an internal link; it is not a privacy mechanism. - Every emitted output carries matching typed `source-record` and `source-output` identifiers. Source-preservation documents additionally carry one typed `source-artifact` identifier. - `Resource.id` is omitted unless the context supplies a repository-assigned id for that graph node. -- Every public concept is named after the Mobile guide's vocabulary: exchange graph, business identifier, identifier role, opaque identity, entry-node key, subject, study enrollment, application device, host device, recording device, retraction event, retraction target, governed source identifier, and producer diagnostic. +- Every public concept is named after the Mobile guide's vocabulary: exchange graph, business identifier, identifier role, opaque identity, entry-node key, subject, study enrollment, application device, host device, recording device, writer, retraction event, retraction target, governed source identifier, and producer diagnostic. ### Deployment identity -`deriveOpaqueIdentitySystems(root, keyId, epoch)` names all twelve deployment-owned identifier systems by the catalog's recommended form: `/NamingSystem/grove--v0//` for the ten opaque identity kinds, `/NamingSystem/grove-event-v0` for events, and `/NamingSystem/grove-entry-node-v0` for entry-node keys. +`deriveOpaqueIdentitySystems(root, keyId, epoch)` takes the root as an `AbsoluteUri` and names all twelve deployment-owned `IdentifierSystem` values by the catalog's recommended form: `/NamingSystem/grove--v0//` for the ten opaque identity kinds, `/NamingSystem/grove-event-v0` for events, and `/NamingSystem/grove-entry-node-v0` for entry-node keys. The normative test vectors are derived from `https://study.example.org/fhir`, so a deployment that adopts the form names its identities exactly as every other Grove producer does. A deployment that already governs its own namespaces supplies them explicitly instead. @@ -469,7 +479,11 @@ A deployment that already governs its own namespaces supplies them explicitly in The handle prints its systems, key id, epoch, and producer instance; the decoded key stays private to the module, and every minting function accepts only a handle it validated. A rotated key uses a new epoch and therefore new systems, and a deployment retains old epochs while identifiers minted under them can still be replayed or retracted. -`deriveOpaqueIdentifier(scope, kind, components)`, `deriveEventIdentifier(scope, sequence)`, and `deriveEntryNodeIdentifier(scope, key)` mint the three identifier families from a scope; `createEntryIdentity` and `deriveEntryFullUrl` derive the deterministic Bundle `fullUrl` for any complete identifier pair. +`deriveSourceRecordIdentity(scope, record)` derives the `SourceRecordIdentity` of one record from its `adapterId`, `sourceType`, `repositoryScope`, and `nativeRecordId`. +Its `identifier` is the typed `source-record` identifier, `output({ role, discriminator })` mints the `source-output` identifier of one output the record yields, and `artifact({ formatCode, partIndex })` mints the `source-artifact` identifier of one part of a recording it carries. +`deriveProviderRecordIdentity(scope, record)` on the providers entry point does the same for a connected provider's record, named by its catalog `providerCode` and `providerScope`, and returns a `ProviderRecordIdentity`. +Output and artifact identities extend their record's components, so they are minted only through it; the record identity keeps the scope and its coordinates private and serializes as its identifier alone. +`deriveOpaqueIdentifier(scope, kind, components)` mints the other kinds, `deriveEventIdentifier(scope, sequence)` and `deriveEntryNodeIdentifier(scope, key)` the event and entry-node identifiers, and `createEntryIdentity` and `deriveEntryFullUrl` derive the deterministic Bundle `fullUrl` for any complete identifier pair. ### The exchange event context @@ -483,7 +497,7 @@ A bundled study context is held to `mobile-support.study-context` by `parseExcha The subject is logical by default: the identifier-only deployment pseudonym reference the guide admits, with no fabricated Patient entry. A bundled subject adds the deployment's own Patient resource as an entry keyed by the `patient` node role, and every output references that entry. -A `StudyEnrollment` names one ResearchStudy by business identifier, the exact protocol revision as a PlanDefinition `url` and `version`, and one ResearchSubject enrollment identifier. +A `StudyEnrollment` names one ResearchStudy by business identifier, the exact protocol revision by its PlanDefinition `protocolUrl` and `protocolVersion`, and one ResearchSubject enrollment identifier. For each enrollment the graph bundles those three entries under the catalog's `research-study`, `plan-definition`, and `research-subject` node roles, references the study from every output through `workflow-researchStudy`, and references the protocol from `ResearchStudy.protocol`. `workflow-instantiatesCanonical` is not an admitted extension and is never emitted. A native recording document cannot carry the study extension, so a context that names studies is a fault for the recording builder rather than a silent omission. @@ -572,7 +586,8 @@ Omission remains conformant. #### Devices and writers -The converter application and its host always appear as separate Device snapshots linked through `Device.parent`, and the writer appears as its own application snapshot. +The converter application and its host always appear as separate Device snapshots linked through `Device.parent`. +The writer appears as its own application snapshot under the `writer` graph node; a provider reports no host for it and rarely its version, so a `Writer` has no host and an optional `version`, where an `ApplicationDevice` requires one. A single snapshot can fill multiple Provenance or gateway roles and is emitted only once; two different devices supplying the same source device token are an identity collision and refuse the conversion. Recording-device identity requires a governed stable per-unit token on the record's `recordingDevice` and emits both a stable `recording-device` identity, derived with the subject, and an event-scoped `device-snapshot` identity; without that evidence the record names no device. @@ -591,10 +606,14 @@ The synchronized normative catalog and its resolved development reference live u ### Diagnostics -Every issue code is closed over the exchange protocol's producer-diagnostic registry plus the package's own schema codes; `groveProducerDiagnostics` publishes each registered code with its normative `reason`, its `severity`, and the side that emits it. +Every `Issue` is either a schema issue or a producer diagnostic, reported through the one `Result` channel. +A schema issue is this package's own input contract: its `SchemaIssueCode` is an undotted code such as `invalid-uri`, and grove-fhir does not define it. +A producer diagnostic is a grove-fhir registry rule: its `ProducerDiagnosticCode` is registered in the exchange protocol, and `groveProducerDiagnostics` publishes each code with its normative `reason`, its `severity`, and the side that emits it. +`isProducerDiagnostic(issue)` narrows an `Issue` to a `ProducerDiagnostic`, whose registered `code` and `reason` are always present. A refused source record carries exactly one `mobile-input.*` code per countable reason: an unsupported source type, an unsupported source value, a value outside its catalog domain, a malformed value shape, missing required metadata, an invalid effective period, text with an unpaired surrogate, an invalid native identifier, an oversized recording payload, or an empty recording series. `Issue.reason` is the registry text and `Issue.message` the typed detail of that particular refusal. -A graph rule reported by `parseExchangeGraph` or `parseRetractionEvent` additionally carries the stable FHIR-facing `Issue.location` the shared corpus pins; a rule about a refused or trimmed record has no location, because no FHIR element exists yet. +A graph rule reported by `parseExchangeGraph` or `parseRetractionEvent` additionally carries the stable FHIR-facing `Issue.location` the shared corpus pins; a rule about a refused or trimmed record has no location, because no FHIR element exists yet, except a `source-offset` warning, which names the effective element that lost its offset. +A producer diagnostic's `location` is therefore present for a graph rule and absent for a refusal or an omission, the `source-offset` warning aside. Deployment faults, such as an invalid identity scope, an event another producer minted, or a disclosure policy naming a Grove system, use the package's undotted schema codes, because no record can cause them. `Result.warnings` and `ProviderConversion.warnings` carry the non-blocking findings of an accepted record under the registry's `mobile-omission.*` rows, each of severity `warning`; the graph stays valid, and an omission a disclosure policy chose is never reported. @@ -606,10 +625,11 @@ A source removal is a new append-only Provenance event, never a mutation of the `retractionTargets(graph)` derives every node of an accepted graph a retraction must name, typed by the catalog's closed target roles: `primary-output`, `child-output`, `source-artifact`, `specimen`, and `device-snapshot`, each with its admitted resource type and the exact opaque identity previously emitted. An Observation another Observation lists in `hasMember` is a child output; every other output is primary. -A governed source identifier disclosed on the primary output travels along as the target's `nativeIdentifier`, and an application may also supply the adapter's own record key there itself. +A governed source identifier disclosed on the primary output travels along as the target's `nativeRecordIdentifier`, and an application may also supply the adapter's own record key there itself. It is emitted verbatim as a target-carried native record Identifier, must use an absolute system outside every Grove namespace, and never carries a Grove identifier-role coding. `buildProviderRetractionEvent(targets, context, sourceRecord, retractedAt)` accepts only targets and a source record the context's own scope minted. +Without the earlier conversion at hand, `deriveProviderRecordIdentity(scope, record).identifier` derives the same source record again from its coordinates. Each target has a closed role and a typed Identifier; it never carries a literal reference or a copied prior resource. `retractedAt` is the time the producer learned the source no longer exposes the record and becomes Provenance.occurred[x]; the conversion instant becomes its recorded time and the Bundle timestamp. The resulting event contains one retraction Provenance plus its immutable assembler application and host snapshots. diff --git a/packages/fhir/scripts/browser-smoke.mjs b/packages/fhir/scripts/browser-smoke.mjs index ae916536..29776414 100644 --- a/packages/fhir/scripts/browser-smoke.mjs +++ b/packages/fhir/scripts/browser-smoke.mjs @@ -137,6 +137,7 @@ try { application: { sourceDeviceToken: 'browser-converter', name: 'Browser converter', + version: '1.0.0', }, host: { sourceDeviceToken: 'browser-host', @@ -169,12 +170,29 @@ try { }, context('1', accountScope), ) + const heartRateRecord = unwrap( + provider.deriveProviderRecordIdentity(identityScope, { + providerCode: 'withings', + sourceType: 'getmeas:11', + providerScope: accountScope, + nativeRecordId: 'browser-heart-rate', + }), + ) + const heartRateOutput = unwrap( + heartRateRecord.output( + provider.providerOutputCoordinates( + 'withings', + 'getmeas:11', + 'heart-rate', + ), + ), + ) const retraction = measurementGraph.ok ? provider.buildProviderRetractionEvent( unwrap(grove.retractionTargets(measurementGraph.value.graph)), context('3', accountScope), - measurementGraph.value.identifiers.sourceRecord, + heartRateRecord.identifier, '2026-08-21T12:00:00Z', ) : { ok: false } @@ -259,6 +277,13 @@ try { measurementCount: Object.keys(mobile.sharedMobileMeasurementCatalog) .length, measurementGraph: measurementGraph.ok, + recordIdentity: + measurementGraph.ok && + JSON.stringify([heartRateRecord.identifier, heartRateOutput]) === + JSON.stringify([ + measurementGraph.value.identifiers.sourceRecord, + measurementGraph.value.identifiers.outputs[0], + ]), recordingGraph: recording.ok, retractionGraph: retraction.ok && @@ -298,6 +323,7 @@ try { result.hasNodeProcess || result.measurementCount !== 84 || result.measurementGraph !== true || + result.recordIdentity !== true || result.recordingGraph !== true || result.retractionGraph !== true || result.replayed !== true || diff --git a/packages/fhir/scripts/generate-conformance-fixtures.mjs b/packages/fhir/scripts/generate-conformance-fixtures.mjs index e04bb4a6..d5caf433 100644 --- a/packages/fhir/scripts/generate-conformance-fixtures.mjs +++ b/packages/fhir/scripts/generate-conformance-fixtures.mjs @@ -29,6 +29,7 @@ const { parseCanonical, parseEventSequence, parseFhirInstant, + parseIdentifierSystem, parseKeyEpoch, parseSemVer, retractionTargets, @@ -172,12 +173,15 @@ const unwrap = (result) => { } const uri = (value) => unwrap(parseAbsoluteUri(value)) +const identifierSystem = (value) => unwrap(parseIdentifierSystem(value)) const instant = (value) => unwrap(parseFhirInstant(value)) const sequence = (value) => unwrap(parseEventSequence(value)) const subject = { kind: 'logical', identifier: { - system: uri('https://grovealliance.org/fhir/testing/patient-pseudonyms'), + system: identifierSystem( + 'https://grovealliance.org/fhir/testing/patient-pseudonyms', + ), value: 'patient-example', }, } @@ -201,7 +205,7 @@ const repositoryScope = (provider) => { )?.identifierScope if (scope === 'account') { return { - system: uri( + system: identifierSystem( 'https://example.org/deployments/provider-account-pseudonyms', ), value: `account-${provider}`, @@ -209,7 +213,7 @@ const repositoryScope = (provider) => { } if (scope === 'global' || scope === 'none') { return { - system: uri('https://example.org/provider-key-spaces'), + system: identifierSystem('https://example.org/provider-key-spaces'), value: `${provider}-document-id-global`, } } @@ -631,7 +635,7 @@ const questionnaireResponse = unwrap( parseCanonical(`${questionnaireUrl}|${questionnaireVersion}`), ), identifier: { - system: uri( + system: identifierSystem( 'https://grovealliance.org/fhir/testing/questionnaire-responses', ), value: 'grove-ts-conformance-response', @@ -762,17 +766,17 @@ resources.set( studies: [ { study: { - system: uri('https://grovealliance.org/fhir/testing/studies'), - value: 'grove-ts-conformance-study', - }, - protocol: { - url: uri( - 'https://grovealliance.org/fhir/testing/PlanDefinition/grove-ts-conformance', + system: identifierSystem( + 'https://grovealliance.org/fhir/testing/studies', ), - version: '1', + value: 'grove-ts-conformance-study', }, + protocolUrl: uri( + 'https://grovealliance.org/fhir/testing/PlanDefinition/grove-ts-conformance', + ), + protocolVersion: '1', enrollment: { - system: uri( + system: identifierSystem( 'https://grovealliance.org/fhir/testing/enrollments', ), value: 'grove-ts-conformance-enrollment', diff --git a/packages/fhir/scripts/package-smoke.mjs b/packages/fhir/scripts/package-smoke.mjs index a2297afe..883eb87c 100644 --- a/packages/fhir/scripts/package-smoke.mjs +++ b/packages/fhir/scripts/package-smoke.mjs @@ -80,12 +80,14 @@ assert.equal( 'org.grovealliance.fhir.questionnaire', ) assert.equal(typeof root.parseFhirInstant, 'function') +assert.equal(typeof mobile.deriveSourceRecordIdentity, 'function') assert.equal(typeof r4.parseR4CollectionBundle, 'function') assert.equal(typeof r4.parseExchangeGraph, 'function') assert.equal(typeof r4.parseRetractionEvent, 'function') assert.equal(typeof provider.buildProviderRetractionEvent, 'function') assert.equal(typeof provider.buildProviderExchangeGraphs, 'function') assert.equal(typeof provider.providerOutputCoordinates, 'function') +assert.equal(typeof provider.deriveProviderRecordIdentity, 'function') assert.equal(typeof provider.providerOutputRole, 'function') assert.equal(typeof questionnaire.buildQuestionnaire, 'function') diff --git a/packages/fhir/src/core/index.ts b/packages/fhir/src/core/index.ts index dfbf866a..23e0fdd1 100644 --- a/packages/fhir/src/core/index.ts +++ b/packages/fhir/src/core/index.ts @@ -22,7 +22,9 @@ export { parseEventSequence, parseFhirId, parseFhirInstant, + parseIdentifierSystem, parseKeyEpoch, + parsePartIndex, parsePatientReference, parsePositiveInteger, parseResearchStudyReference, @@ -35,7 +37,9 @@ export { type EventSequence, type FhirId, type FhirInstant, + type IdentifierSystem, type KeyEpoch, + type PartIndex, type PatientReference, type PositiveInteger, type ResearchStudyReference, @@ -50,6 +54,7 @@ export { export { collectResults, err, + isProducerDiagnostic, issue, issues, mapResult, @@ -59,6 +64,7 @@ export { type Issue, type IssueCode, type IssueSeverity, + type ProducerDiagnostic, type Result, type SchemaIssueCode, } from './result.js' diff --git a/packages/fhir/src/core/primitives.ts b/packages/fhir/src/core/primitives.ts index 9834887b..922063a9 100644 --- a/packages/fhir/src/core/primitives.ts +++ b/packages/fhir/src/core/primitives.ts @@ -16,6 +16,8 @@ export type Branded = string & { } export type AbsoluteUri = Branded<'AbsoluteUri'> +/** The absolute URI naming the namespace an Identifier.value is unique within. */ +export type IdentifierSystem = Branded<'IdentifierSystem'> export type Canonical = Branded<'Canonical'> /** Canonical positive decimal naming one key epoch of a deployment's identity scope. */ export type KeyEpoch = Branded<'KeyEpoch'> @@ -23,6 +25,8 @@ export type KeyEpoch = Branded<'KeyEpoch'> export type EventSequence = Branded<'EventSequence'> /** Canonical unsigned decimal position of an entry among those sharing its node role. */ export type EntryNodeOrdinal = Branded<'EntryNodeOrdinal'> +/** Canonical unsigned decimal zero-based position of one part of a source artifact. */ +export type PartIndex = Branded<'PartIndex'> export type FhirId = Branded<'FhirId'> export type FhirInstant = Branded<'FhirInstant'> export type PatientReference = Branded<'PatientReference'> @@ -274,6 +278,18 @@ export const parseAbsoluteUri = (value: unknown): Result => { return ok(value as AbsoluteUri) } +export const parseIdentifierSystem = ( + value: unknown, +): Result => { + if (typeof value !== 'string' || !isAbsoluteUri(value)) { + return err( + 'invalid-uri', + 'Expected an identifier system: an absolute ASCII RFC 3986 URI with valid percent encoding.', + ) + } + return ok(value as IdentifierSystem) +} + export const parseCanonical = (value: unknown): Result => { if (typeof value !== 'string') { return err('invalid-uri', 'Expected a canonical URI string.') @@ -408,6 +424,16 @@ export const parseEntryNodeOrdinal = ( return ok(value as EntryNodeOrdinal) } +export const parsePartIndex = (value: unknown): Result => { + if (typeof value !== 'string' || !UNSIGNED_DECIMAL.test(value)) { + return err( + 'invalid-identifier', + 'Expected a canonical unsigned decimal part index.', + ) + } + return ok(value as PartIndex) +} + export const parseUrnUuid = (value: unknown): Result => { if (typeof value !== 'string' || !URN_UUID.test(value)) { return err('invalid-uri', 'Expected a lowercase RFC 4122 urn:uuid fullUrl.') diff --git a/packages/fhir/src/core/result.ts b/packages/fhir/src/core/result.ts index 72445177..85de09a4 100644 --- a/packages/fhir/src/core/result.ts +++ b/packages/fhir/src/core/result.ts @@ -40,6 +40,38 @@ export interface Issue { readonly location?: string } +/** + * An issue under a grove-fhir registry rule, as opposed to a schema issue of this package's + * own input contract: its code is registered and carries the registry's reason. + */ +export interface ProducerDiagnostic extends Issue { + readonly code: ProducerDiagnosticCode + readonly reason: string +} + +// IssueCode is closed over both halves, so a code outside the schema half is registered. +const schemaIssueCodes: Readonly> = { + 'duplicate-identifier': true, + 'external-validation-required': true, + 'invalid-choice': true, + 'invalid-code': true, + 'invalid-date-time': true, + 'invalid-identifier': true, + 'invalid-reference': true, + 'invalid-type': true, + 'invalid-uri': true, + 'missing-required': true, + 'out-of-range': true, + 'schema-invalid': true, + 'value-mismatch': true, +} + +export const isProducerDiagnostic = ( + issue: Issue, +): issue is ProducerDiagnostic => + !Object.hasOwn(schemaIssueCodes, issue.code) && + typeof issue.reason === 'string' + export type Result = | { readonly ok: true diff --git a/packages/fhir/src/index.ts b/packages/fhir/src/index.ts index 7bd1f5b4..da76c550 100644 --- a/packages/fhir/src/index.ts +++ b/packages/fhir/src/index.ts @@ -12,6 +12,7 @@ export { err, fhirDateTimeToDate, fhirQuantityToValue, + isProducerDiagnostic, issues, mapResult, ok, @@ -21,7 +22,9 @@ export { parseEventSequence, parseFhirId, parseFhirInstant, + parseIdentifierSystem, parseKeyEpoch, + parsePartIndex, parsePatientReference, parsePositiveInteger, parseResearchStudyReference, @@ -33,12 +36,15 @@ export { type EventSequence, type FhirId, type FhirInstant, + type IdentifierSystem, type Issue, type IssueCode, type IssueSeverity, type KeyEpoch, + type PartIndex, type PatientReference, type PositiveInteger, + type ProducerDiagnostic, type QuantityComparator, type QuantityValue, type ResearchStudyReference, @@ -183,6 +189,7 @@ export { deriveEventIdentifier, deriveOpaqueIdentifier, deriveOpaqueIdentitySystems, + deriveSourceRecordIdentity, encodeLengthFramedUtf8, entryIdentifierName, groveExchangeProtocol, @@ -202,6 +209,7 @@ export { sharedMobileMeasurementCatalog, validateOpaqueIdentityScope, type ApplicationDevice, + type ArtifactCoordinates, type BloodPressureMeasurement, type BusinessIdentifier, type ChoiceQuantityMeasurement, @@ -234,12 +242,14 @@ export { type OpaqueIdentityScope, type OpaqueIdentityScopeInput, type OpaqueIdentitySystems, + type OutputCoordinates, type PeriodEffectiveTime, type PeriodCodedMeasurement, type PeriodCodedMeasurementKind, type PeriodQuantityMeasurement, type PeriodQuantityMeasurementKind, type ProducerDiagnosticCode, + type RecordIdentityKind, type RecordingDevice, type RecordingMethod, type RoledIdentifier, @@ -247,6 +257,8 @@ export { type SleepStage, type SleepStageMeasurement, type SleepStageSourceCodingInput, + type SourceRecordCoordinates, + type SourceRecordIdentity, type StudyEnrollment, type Subject, } from './mobile/index.js' diff --git a/packages/fhir/src/mobile/context.ts b/packages/fhir/src/mobile/context.ts index e9c4b057..271a5e3e 100644 --- a/packages/fhir/src/mobile/context.ts +++ b/packages/fhir/src/mobile/context.ts @@ -31,6 +31,7 @@ import { parseAbsoluteUri, parseFhirId, parseFhirInstant, + parseIdentifierSystem, type FhirId, type FhirInstant, type Issue, @@ -104,7 +105,7 @@ const identifierIssues = (value: unknown, path: Path): readonly Issue[] => { } return [ ...shapeIssues(record, ['system', 'value'], [], path), - ...(parseAbsoluteUri(record.system).ok ? + ...(parseIdentifierSystem(record.system).ok ? [] : [ fault( @@ -212,7 +213,7 @@ const deviceIssues = ( } const applicationIssues = (value: unknown, path: Path): readonly Issue[] => - deviceIssues(value, ['sourceDeviceToken', 'name'], ['version', 'build'], path) + deviceIssues(value, ['sourceDeviceToken', 'name', 'version'], ['build'], path) const hostIssues = (value: unknown, path: Path): readonly Issue[] => deviceIssues( @@ -268,33 +269,34 @@ const studyIssues = (value: unknown, path: Path): readonly Issue[] => { findings.push( ...shapeIssues( record, - ['study', 'protocol', 'enrollment'], + ['study', 'protocolUrl', 'protocolVersion', 'enrollment'], [], entryPath, ), ...identifierIssues(record.study, [...entryPath, 'study']), ...identifierIssues(record.enrollment, [...entryPath, 'enrollment']), ) - const protocol = asRecord(record.protocol) if ( - protocol === undefined || - !parseAbsoluteUri(protocol.url).ok || - !isNonBlank(protocol.version) + record.protocolUrl !== undefined && + !parseAbsoluteUri(record.protocolUrl).ok ) { findings.push( fault( 'invalid-uri', - [...entryPath, 'protocol'], - 'A protocol names its PlanDefinition by absolute url and non-blank version.', + [...entryPath, 'protocolUrl'], + 'A protocol names its PlanDefinition by absolute canonical url.', ), ) - } else { + } + if ( + record.protocolVersion !== undefined && + !isNonBlank(record.protocolVersion) + ) { findings.push( - ...shapeIssues( - protocol, - ['url', 'version'], - [], - [...entryPath, 'protocol'], + fault( + 'invalid-identifier', + [...entryPath, 'protocolVersion'], + 'A protocol version is a non-blank Unicode-scalar string.', ), ) } diff --git a/packages/fhir/src/mobile/identity.ts b/packages/fhir/src/mobile/identity.ts index 415c0979..235dda0d 100644 --- a/packages/fhir/src/mobile/identity.ts +++ b/packages/fhir/src/mobile/identity.ts @@ -21,14 +21,18 @@ import { parseEntryNodeOrdinal, parseEventSequence, parseFhirId, + parseIdentifierSystem, parseKeyEpoch, + parsePartIndex, parseUrnUuid, type AbsoluteUri, type EntryNodeOrdinal, type EventSequence, type FhirId, + type IdentifierSystem, type JsonValue, type KeyEpoch, + type PartIndex, type Result, type UrnUuid, } from '../core/index.js' @@ -45,7 +49,7 @@ export type GroveIdentifierRole = /** A complete Identifier.system and Identifier.value pair; never a repository id. */ export interface BusinessIdentifier { - readonly system: AbsoluteUri + readonly system: IdentifierSystem readonly value: string } @@ -71,14 +75,14 @@ export interface EntryNodeKey { /** One deployment-owned Identifier.system for each opaque identity kind. */ export type OpaqueIdentitySystems = Readonly< - Record + Record > /** All twelve deployment-owned identifier systems of one key id and epoch. */ export interface DeploymentIdentifierSystems { readonly opaque: OpaqueIdentitySystems - readonly event: AbsoluteUri - readonly entryNode: AbsoluteUri + readonly event: IdentifierSystem + readonly entryNode: IdentifierSystem } /** @@ -123,6 +127,78 @@ export type OpaqueIdentityComponents = Readonly<{ > }> +/** The coordinates of one record in an adapter's repository. */ +export interface SourceRecordCoordinates { + readonly adapterId: string + readonly sourceType: string + readonly repositoryScope: BusinessIdentifier + readonly nativeRecordId: string +} + +/** The coordinates that set one output apart from the others its record yields. */ +export interface OutputCoordinates { + readonly role: string + readonly discriminator: string +} + +/** The coordinates of one part of a recording its record carries. */ +export interface ArtifactCoordinates { + readonly formatCode: string + readonly partIndex: PartIndex +} + +/** + * The opaque identity of one source record, which its output and artifact identities extend. + * + * The scope and the record's coordinates stay private to it; only the identifier serializes. + */ +export interface SourceRecordIdentity { + /** The typed `source-record` identifier. */ + readonly identifier: RoledIdentifier + /** Mints the `source-output` identifier of one output this record yields. */ + readonly output: (coordinates: OutputCoordinates) => Result + /** Mints the `source-artifact` identifier of one part of a recording this record carries. */ + readonly artifact: ( + coordinates: ArtifactCoordinates, + ) => Result +} + +/** The opaque identity kinds only a source- or provider-record identity mints. */ +export type RecordIdentityKind = + | 'provider-artifact' + | 'provider-output' + | 'provider-record' + | 'source-artifact' + | 'source-output' + | 'source-record' + +type RecordKind = 'provider-record' | 'source-record' +type RecordComponents = OpaqueIdentityComponents[RecordKind] + +interface RecordFamily { + readonly adapter: string + readonly scope: string + readonly output: RecordIdentityKind + readonly artifact: RecordIdentityKind +} + +// Each family names its adapter and scope coordinates its own way; its output and artifact kinds +// extend the record kind's components with two of their own. +const RECORD_IDENTITIES: Readonly> = { + 'source-record': { + adapter: 'adapterId', + scope: 'repositoryScope', + output: 'source-output', + artifact: 'source-artifact', + }, + 'provider-record': { + adapter: 'providerCode', + scope: 'providerScope', + output: 'provider-output', + artifact: 'provider-artifact', + }, +} + const ASCII_TOKEN = /^[A-Za-z\d._-]+$/u const LOWERCASE_ROLE = /^[a-z][a-z\d-]*$/u const CANONICAL_PRODUCER_UUID = @@ -169,11 +245,15 @@ const identityKinds = groveExchangeProtocol.opaqueIdentity.identityKinds.map( ({ kind }) => kind, ) as readonly OpaqueIdentityKind[] -const identityComponentCounts = Object.fromEntries( +const identityComponentNames = Object.fromEntries( groveExchangeProtocol.opaqueIdentity.identityKinds.map( - ({ kind, components }) => [kind, components.length], + ({ kind, components }): [string, readonly string[]] => [kind, components], ), -) as Readonly> +) as Readonly> + +const UNSIGNED_DECIMAL_COMPONENTS: ReadonlySet = new Set( + groveExchangeProtocol.opaqueIdentity.componentRequirements.unsignedDecimal, +) const identifierRoleByKind = Object.fromEntries( groveExchangeProtocol.opaqueIdentity.identityKinds.map( @@ -189,6 +269,14 @@ const IDENTIFIER_ROLES: ReadonlySet = new Set([ 'event', ]) +const RECORD_IDENTITY_KINDS: ReadonlySet = new Set( + Object.entries(RECORD_IDENTITIES).flatMap(([kind, { output, artifact }]) => [ + kind, + output, + artifact, + ]), +) + export const isGroveIdentifierRole = ( value: unknown, ): value is GroveIdentifierRole => @@ -330,9 +418,9 @@ export const deriveOpaqueIdentitySystems = ( const parsedEpoch = parseKeyEpoch(epoch) if (!parsedEpoch.ok) return parsedEpoch - const opaque: Partial> = {} + const opaque: Partial> = {} for (const kind of identityKinds) { - const system = parseAbsoluteUri( + const system = parseIdentifierSystem( renderSystemForm( groveExchangeProtocol.opaqueIdentity.recommendedSystemForm, { @@ -346,14 +434,14 @@ export const deriveOpaqueIdentitySystems = ( if (!system.ok) return system opaque[kind] = system.value } - const event = parseAbsoluteUri( + const event = parseIdentifierSystem( renderSystemForm( groveExchangeProtocol.event.bundleIdentifier.recommendedSystemForm, { 'deployment-root': root }, ), ) if (!event.ok) return event - const entryNode = parseAbsoluteUri( + const entryNode = parseIdentifierSystem( renderSystemForm( groveExchangeProtocol.entryIdentity.entryNode.recommendedSystemForm, { 'deployment-root': root }, @@ -431,9 +519,9 @@ const validateSystems = ( const opaqueEntries = Object.entries(opaque) if ( !hasExactKeys(opaque, identityKinds) || - opaqueEntries.some(([, system]) => !parseAbsoluteUri(system).ok) || - !parseAbsoluteUri(systems.event).ok || - !parseAbsoluteUri(systems.entryNode).ok + opaqueEntries.some(([, system]) => !parseIdentifierSystem(system).ok) || + !parseIdentifierSystem(systems.event).ok || + !parseIdentifierSystem(systems.entryNode).ok ) { return err( 'invalid-uri', @@ -568,7 +656,7 @@ const deriveOpaqueIdentifierWith = ( ): Result => { if ( typeof identityKind !== 'string' || - !Object.hasOwn(identityComponentCounts, identityKind) + !Object.hasOwn(identityComponentNames, identityKind) ) { return err( 'invalid-code', @@ -578,13 +666,14 @@ const deriveOpaqueIdentifierWith = ( } const componentSnapshot = cloneJsonValue(components) if (!componentSnapshot.ok) return componentSnapshot + const componentNames = identityComponentNames[identityKind] if ( !Array.isArray(componentSnapshot.value) || - componentSnapshot.value.length !== identityComponentCounts[identityKind] + componentSnapshot.value.length !== componentNames.length ) { return err( 'value-mismatch', - `${identityKind} requires exactly ${identityComponentCounts[identityKind]} ordered components.`, + `${identityKind} requires exactly ${componentNames.length} ordered components.`, ['components'], ) } @@ -610,6 +699,18 @@ const deriveOpaqueIdentifierWith = ( ['components', invalidComponentIndex], ) } + const nonCanonicalIndex = componentSnapshot.value.findIndex( + (component, index) => + UNSIGNED_DECIMAL_COMPONENTS.has(componentNames[index] ?? '') && + !parsePartIndex(component).ok, + ) + if (nonCanonicalIndex >= 0) { + return err( + 'invalid-identifier', + `${identityKind}.${componentNames[nonCanonicalIndex]} must be a canonical unsigned decimal.`, + ['components', nonCanonicalIndex], + ) + } const preimage = encodeLengthFramedUtf8([ groveExchangeProtocol.opaqueIdentity.domain, identityKind, @@ -627,29 +728,163 @@ const deriveOpaqueIdentifierWith = ( ) } -/** Mints one deployment-owned, role-typed opaque identifier. */ -export const deriveOpaqueIdentifier = ( +/** Mints one deployment-owned, role-typed opaque identifier of a kind no record identity mints. */ +export const deriveOpaqueIdentifier = < + Kind extends Exclude, +>( scope: OpaqueIdentityScope, identityKind: Kind, components: OpaqueIdentityComponents[Kind], ): Result => { const resolved = resolveScope(scope) if (!resolved.ok) return resolved + if (RECORD_IDENTITY_KINDS.has(identityKind)) { + return err( + 'invalid-code', + `${identityKind} is minted only through its record identity.`, + ['identityKind'], + ) + } return deriveOpaqueIdentifierWith(resolved.value, identityKind, components) } -/** - * Internal test seam for the exact published normative vector key. - * - * Deliberately omitted from every package entry point; application code cannot opt into it. - */ -export const deriveConformanceVectorOpaqueIdentifier = < - Kind extends OpaqueIdentityKind, ->( +const isComponent = (value: JsonValue | undefined): value is string => + typeof value === 'string' && value !== '' && !containsIsolatedSurrogate(value) + +const COMPONENT_MESSAGE = + 'Opaque identity components must be nonempty Unicode-scalar strings.' + +// A snapshot of exactly the named fields, so a later edit of the caller's object never reaches +// an identity derived from it. +const closedCoordinates = ( + input: unknown, + fields: readonly string[], +): Result>> => { + const snapshot = cloneJsonValue(input) + if (!snapshot.ok) return snapshot + const candidate = asJsonObject(snapshot.value) + if (candidate === undefined || !hasExactKeys(candidate, fields)) { + return err( + 'schema-invalid', + `Identity coordinates name exactly ${fields.join(', ')}.`, + ) + } + return ok(candidate) +} + +/** Internal: the ordered components of one record's coordinates under its family's names. */ +export const parseRecordCoordinates = ( + kind: RecordKind, + record: unknown, +): Result => { + const { adapter, scope } = RECORD_IDENTITIES[kind] + const candidate = closedCoordinates(record, [ + adapter, + 'sourceType', + scope, + 'nativeRecordId', + ]) + if (!candidate.ok) return candidate + const recordScope = asJsonObject(candidate.value[scope]) + const components = [ + [candidate.value[adapter], [adapter]], + [candidate.value.sourceType, ['sourceType']], + [recordScope?.system, [scope, 'system']], + [recordScope?.value, [scope, 'value']], + [candidate.value.nativeRecordId, ['nativeRecordId']], + ] as const + const invalid = components.find(([value]) => !isComponent(value)) + if (invalid !== undefined) { + return err('invalid-identifier', COMPONENT_MESSAGE, invalid[1]) + } + if (!parseIdentifierSystem(recordScope?.system).ok) { + return err('invalid-uri', `${scope}.system must be an absolute URI.`, [ + scope, + 'system', + ]) + } + return ok(components.map(([value]) => value) as unknown as RecordComponents) +} + +const recordIdentityWith = ( + resolved: ResolvedScope, + kind: RecordKind, + components: RecordComponents, +): Result => { + const identifier = deriveOpaqueIdentifierWith(resolved, kind, components) + if (!identifier.ok) return identifier + const { output, artifact } = RECORD_IDENTITIES[kind] + return ok( + deepFreeze({ + identifier: identifier.value, + output: (coordinates: OutputCoordinates) => { + const fields = closedCoordinates(coordinates, ['role', 'discriminator']) + if (!fields.ok) return fields + const { role, discriminator } = fields.value + if (!isComponent(role)) { + return err('invalid-identifier', COMPONENT_MESSAGE, ['role']) + } + if (!isComponent(discriminator)) { + return err('invalid-identifier', COMPONENT_MESSAGE, ['discriminator']) + } + return deriveOpaqueIdentifierWith(resolved, output, [ + ...components, + role, + discriminator, + ]) + }, + artifact: (coordinates: ArtifactCoordinates) => { + const fields = closedCoordinates(coordinates, [ + 'formatCode', + 'partIndex', + ]) + if (!fields.ok) return fields + const { formatCode } = fields.value + if (!isComponent(formatCode)) { + return err('invalid-identifier', COMPONENT_MESSAGE, ['formatCode']) + } + const partIndex = parsePartIndex(fields.value.partIndex) + if (!partIndex.ok) { + return err( + 'invalid-identifier', + 'A part index is a canonical unsigned decimal.', + ['partIndex'], + ) + } + return deriveOpaqueIdentifierWith(resolved, artifact, [ + ...components, + formatCode, + partIndex.value, + ]) + }, + }), + ) +} + +/** Internal: the identity of one record whose coordinates `parseRecordCoordinates` validated. */ +export const deriveRecordIdentity = ( + scope: OpaqueIdentityScope, + kind: RecordKind, + components: RecordComponents, +): Result => { + const resolved = resolveScope(scope) + if (!resolved.ok) return resolved + return recordIdentityWith(resolved.value, kind, components) +} + +/** Derives the identity of one source record; its outputs and artifacts mint through it. */ +export const deriveSourceRecordIdentity = ( + scope: OpaqueIdentityScope, + record: SourceRecordCoordinates, +): Result => { + const components = parseRecordCoordinates('source-record', record) + if (!components.ok) return components + return deriveRecordIdentity(scope, 'source-record', components.value) +} + +const resolveConformanceVectorScope = ( input: OpaqueIdentityScopeInput, - identityKind: Kind, - components: OpaqueIdentityComponents[Kind], -): Result => { +): Result => { const secret = decodeBase64UrlWithoutPadding(input.secretBase64Url) if ( secret === undefined || @@ -663,11 +898,38 @@ export const deriveConformanceVectorOpaqueIdentifier = < ['secretBase64Url'], ) } - const resolved = validateScopeInternal(input, true) + return validateScopeInternal(input, true) +} + +/** + * Internal test seams for the exact published normative vector key. + * + * Deliberately omitted from every package entry point; application code cannot opt into them. + */ +export const deriveConformanceVectorOpaqueIdentifier = < + Kind extends Exclude, +>( + input: OpaqueIdentityScopeInput, + identityKind: Kind, + components: OpaqueIdentityComponents[Kind], +): Result => { + const resolved = resolveConformanceVectorScope(input) if (!resolved.ok) return resolved return deriveOpaqueIdentifierWith(resolved.value, identityKind, components) } +export const deriveConformanceVectorRecordIdentity = ( + input: OpaqueIdentityScopeInput, + kind: RecordKind, + record: unknown, +): Result => { + const resolved = resolveConformanceVectorScope(input) + if (!resolved.ok) return resolved + const components = parseRecordCoordinates(kind, record) + if (!components.ok) return components + return recordIdentityWith(resolved.value, kind, components.value) +} + /** Mints the sole event business identifier for one immutable exchange assertion. */ export const deriveEventIdentifier = ( scope: OpaqueIdentityScope, @@ -694,7 +956,7 @@ const validateEntryNodeKey = (key: unknown): Result => { if ( candidate === undefined || event?.role !== 'event' || - !parseAbsoluteUri(event.system).ok || + !parseIdentifierSystem(event.system).ok || typeof event.value !== 'string' || !EVENT_VALUE.test(event.value) ) { @@ -796,7 +1058,7 @@ export const entryIdentifierName = ( if (identifier === undefined) { return err('invalid-type', 'Expected one complete Identifier.') } - if (!parseAbsoluteUri(identifier.system).ok) { + if (!parseIdentifierSystem(identifier.system).ok) { return err('invalid-uri', 'Identifier.system must be an absolute URI.', [ 'system', ]) diff --git a/packages/fhir/src/mobile/index.ts b/packages/fhir/src/mobile/index.ts index a7777d13..e1034096 100644 --- a/packages/fhir/src/mobile/index.ts +++ b/packages/fhir/src/mobile/index.ts @@ -14,6 +14,7 @@ export { deriveEventIdentifier, deriveOpaqueIdentifier, deriveOpaqueIdentitySystems, + deriveSourceRecordIdentity, encodeLengthFramedUtf8, entryIdentifierName, isEntryNodeIdentityValue, @@ -21,6 +22,7 @@ export { isOpaqueIdentityScope, isOpaqueIdentityValue, validateOpaqueIdentityScope, + type ArtifactCoordinates, type BusinessIdentifier, type DeploymentIdentifierSystems, type EntryIdentity, @@ -33,7 +35,11 @@ export { type OpaqueIdentityScope, type OpaqueIdentityScopeInput, type OpaqueIdentitySystems, + type OutputCoordinates, + type RecordIdentityKind, type RoledIdentifier, + type SourceRecordCoordinates, + type SourceRecordIdentity, } from './identity.js' export { parseExchangeEventContext } from './context.js' export { diff --git a/packages/fhir/src/mobile/types.ts b/packages/fhir/src/mobile/types.ts index 79a50103..42594328 100644 --- a/packages/fhir/src/mobile/types.ts +++ b/packages/fhir/src/mobile/types.ts @@ -13,7 +13,12 @@ import type { RoledIdentifier, } from './identity.js' import type { SharedMobileMeasurementKind } from '../contract/measurement-catalog.generated.js' -import type { AbsoluteUri, FhirId, FhirInstant } from '../core/index.js' +import type { + AbsoluteUri, + FhirId, + FhirInstant, + IdentifierSystem, +} from '../core/index.js' import type { Patient } from '../r4/types.js' type SharedCatalog = @@ -163,23 +168,30 @@ export type Subject = /** One ResearchStudy, its exact-revision PlanDefinition and one ResearchSubject. */ export interface StudyEnrollment { readonly study: BusinessIdentifier - readonly protocol: { - readonly url: AbsoluteUri - readonly version: string - } + /** The PlanDefinition's canonical url; with `protocolVersion` it names one exact revision. */ + readonly protocolUrl: AbsoluteUri + readonly protocolVersion: string readonly enrollment: BusinessIdentifier } -/** The immutable snapshot of one application build; the token never appears in clear. */ +/** The immutable snapshot of one application build. */ export interface ApplicationDevice { + /** + * Recommended as `|[|]`, such as `com.example.app|1.2.3`; + * never emitted in clear. + */ readonly sourceDeviceToken: string readonly name: string - readonly version?: string | undefined + readonly version: string readonly build?: string | undefined } /** The immutable snapshot of the hardware and OS hosting the converter. */ export interface HostDevice { + /** + * Recommended as `|`, such as `iPhone17,1|26.0`; + * never emitted in clear. + */ readonly sourceDeviceToken: string readonly operatingSystemVersion: string readonly name?: string | undefined @@ -209,7 +221,12 @@ export type ConverterRole = readonly application: ApplicationDevice } -/** The graph nodes a repository may have assigned a Resource.id to. */ +/** + * The graph nodes a repository may have assigned a Resource.id to. + * + * `writer` is the snapshot of the application that wrote the source record; providers report + * no host for it, so there is no `writer-host` node. + */ export type ExchangeGraphNode = | 'bundle' | 'primary-output' @@ -275,7 +292,7 @@ export type GovernedSourceIdentifierDisclosurePolicy = | { readonly kind: 'omit' } | { readonly kind: 'authorized' - readonly system: AbsoluteUri + readonly system: IdentifierSystem readonly type?: GovernedSourceIdentifierType | undefined } diff --git a/packages/fhir/src/providers/builder.ts b/packages/fhir/src/providers/builder.ts index 14f50e48..065f48e7 100644 --- a/packages/fhir/src/providers/builder.ts +++ b/packages/fhir/src/providers/builder.ts @@ -43,7 +43,6 @@ import { parseProviderConversionOptions, providerOutputCoordinates, providerSourceKinds, - type ProviderOutputCoordinates, } from './provider.js' import type { NormalizedProviderRecord, @@ -56,6 +55,7 @@ import { collectResults, issues, ok, type Result } from '../core/index.js' import { createEntryIdentity, type EntryIdentity, + type OutputCoordinates, type RoledIdentifier, } from '../mobile/identity.js' import type { @@ -345,9 +345,7 @@ const makeObservation = (input: ObservationInput): Observation => { const outputCoordinates = ( record: NormalizedProviderRecord, -): Result< - readonly [ProviderOutputCoordinates, ...ProviderOutputCoordinates[]] -> => { +): Result => { const coordinates = record.measurements.map(({ kind }) => providerOutputCoordinates( record.source.adapter.provider, @@ -359,7 +357,7 @@ const outputCoordinates = ( if (first === undefined || coordinates.some((entry) => entry === undefined)) { throw new Error('Validated Provider measurements name no catalog output.') } - return ok([first, ...(rest as ProviderOutputCoordinates[])]) + return ok([first, ...(rest as OutputCoordinates[])]) } // A disclosure is admitted only where the catalog row designates one one-to-one output. diff --git a/packages/fhir/src/providers/context-graph.ts b/packages/fhir/src/providers/context-graph.ts index 8f9b44c8..d89a5348 100644 --- a/packages/fhir/src/providers/context-graph.ts +++ b/packages/fhir/src/providers/context-graph.ts @@ -27,6 +27,7 @@ import { deriveEntryNodeEntryIdentity, deriveHostEntryIdentity, } from './identity.js' +import type { Writer } from './types.js' import { err, issues, @@ -44,7 +45,6 @@ import type { RoledIdentifier, } from '../mobile/identity.js' import type { - ApplicationDevice, ExchangeEventContext, InstantEffectiveTime, PeriodEffectiveTime, @@ -128,10 +128,7 @@ const studyEntries = ( return ok(entries) } -const sameApplication = ( - left: ApplicationDevice, - right: ApplicationDevice, -): boolean => +const sameApplication = (left: Writer, right: Writer): boolean => left.sourceDeviceToken === right.sourceDeviceToken && left.name === right.name && left.version === right.version && @@ -140,9 +137,9 @@ const sameApplication = ( // The writer may be the converter itself; the same token then names one snapshot, // and differing facts under one token are a collision, not a second Device. const writerEntries = ( - writer: ApplicationDevice, + writer: Writer, author: EntryIdentity, - converters: ReadonlyArray, + converters: ReadonlyArray, ): Result => { const shared = converters.find( ([, identity]) => identity.fullUrl === author.fullUrl, @@ -202,7 +199,7 @@ const gatewayIdentity = ( */ export const resolveContextGraph = ( context: ExchangeEventContext, - writer: ApplicationDevice, + writer: Writer, admitsStudies: boolean, ): Result => { const parsed = parseExchangeEventContext(context) diff --git a/packages/fhir/src/providers/graph.ts b/packages/fhir/src/providers/graph.ts index 2a58b65a..b34b47f0 100644 --- a/packages/fhir/src/providers/graph.ts +++ b/packages/fhir/src/providers/graph.ts @@ -7,6 +7,7 @@ // import { EXTENSIONS, PROFILES, SYSTEMS } from './profiles.js' +import type { Writer } from './types.js' import { err, ok, type Result } from '../core/index.js' import type { BusinessIdentifier, @@ -119,7 +120,7 @@ export const subjectReference = ( : { reference: patient.fullUrl } export const makeApplicationDevice = ( - input: ApplicationDevice & { + input: (ApplicationDevice | Writer) & { readonly identity: EntryIdentity readonly parentReference?: string }, @@ -243,8 +244,8 @@ export const makePlanDefinition = ( ): PlanDefinition => ({ resourceType: 'PlanDefinition' as const, ...resourceId(identity), - url: enrollment.protocol.url, - version: enrollment.protocol.version, + url: enrollment.protocolUrl, + version: enrollment.protocolVersion, status: 'active' as const, }) diff --git a/packages/fhir/src/providers/identity.ts b/packages/fhir/src/providers/identity.ts index bf1902f9..164bb9a4 100644 --- a/packages/fhir/src/providers/identity.ts +++ b/packages/fhir/src/providers/identity.ts @@ -6,11 +6,8 @@ // SPDX-License-Identifier: MIT // -import type { ConnectedProvider, WriterRecord } from './types.js' -import { - groveExchangeProtocol, - groveProfileClaims, -} from '../contract/measurement-catalog.generated.js' +import type { ConnectedProvider, Writer, WriterRecord } from './types.js' +import { groveProfileClaims } from '../contract/measurement-catalog.generated.js' import { providerAdapterCatalog, providerRawOutputDiscriminators, @@ -19,11 +16,12 @@ import { providerScalarOutputRoles, } from '../contract/providers.generated.js' import { + collectResults, deepFreeze, err, issues, ok, - parseAbsoluteUri, + parseIdentifierSystem, type EntryNodeOrdinal, type FhirId, type Issue, @@ -34,15 +32,17 @@ import { createEntryIdentity, deriveEntryNodeIdentifier, deriveOpaqueIdentifier, + deriveRecordIdentity, isEventOfScope, isOpaqueIdentityScope, + parseRecordCoordinates, + type ArtifactCoordinates, type BusinessIdentifier, type EntryIdentity, type EntryNodeIdentifier, type ExchangeEventIdentifier, - type OpaqueIdentityComponents, - type OpaqueIdentityKind, type OpaqueIdentityScope, + type OutputCoordinates, type RoledIdentifier, } from '../mobile/identity.js' import type { @@ -58,22 +58,6 @@ export type DeviceSnapshotRole = 'application' | 'host' | 'recording-device' const PROVIDER_CODES: ReadonlySet = new Set( providerAdapterCatalog.providers.map(({ id }) => id), ) -// A kind's coordinate family is written in its components: the first names the adapter -// space, and only source-record coordinates carry a native record id. -const identityKindsCoordinatedBy = ( - adapterComponent: string, -): ReadonlySet => - new Set( - groveExchangeProtocol.opaqueIdentity.identityKinds - .filter( - ({ components }) => - components[0] === adapterComponent && - (components as readonly string[]).includes('native-record-id'), - ) - .map(({ kind }) => kind), - ) -const PROVIDER_IDENTITY_KINDS = identityKindsCoordinatedBy('provider-code') -const GENERIC_SOURCE_IDENTITY_KINDS = identityKindsCoordinatedBy('adapter-id') const recordingDeviceAdapters: ReadonlySet = new Set([ ...groveProfileClaims.adapterConversionProvenanceClaims.map( @@ -91,54 +75,66 @@ const ordinal = (index: number): EntryNodeOrdinal => String(index) as EntryNodeOrdinal /** - * The provider coordinate guard: a provider identity kind opens with one catalog provider - * code, and a provider code never enters a generic source identity kind. + * The provider coordinate guard: a provider record opens with one catalog provider code, and a + * provider code never opens a generic source record. */ export const providerCoordinateIssue = ( - identityKind: OpaqueIdentityKind, - components: readonly string[], + kind: 'provider-record' | 'source-record', + adapter: string, ): Issue | undefined => { - const first = components[0] - if ( - PROVIDER_IDENTITY_KINDS.has(identityKind) && - !PROVIDER_CODES.has(first ?? '') - ) { + if (kind === 'provider-record' && !PROVIDER_CODES.has(adapter)) { return { severity: 'error', code: 'invalid-code', - path: ['components', 0], - message: - 'A Provider identity kind requires one exact catalog provider code as its first component.', + path: ['providerCode'], + message: 'A provider record requires one exact catalog provider code.', } } - if ( - GENERIC_SOURCE_IDENTITY_KINDS.has(identityKind) && - PROVIDER_CODES.has(first ?? '') - ) { + if (kind === 'source-record' && PROVIDER_CODES.has(adapter)) { return { severity: 'error', code: 'invalid-code', - path: ['components', 0], - message: - 'Provider coordinates require the matching provider-record, provider-output, or provider-artifact identity kind.', + path: ['adapterId'], + message: 'Provider coordinates require a provider record.', } } return undefined } -/** Mints one opaque identifier after the provider coordinate guard admits its components. */ -export const deriveProviderOpaqueIdentifier = ( +/** The coordinates of one record at a connected provider. */ +export interface ProviderRecordCoordinates { + readonly providerCode: ConnectedProvider + readonly sourceType: string + readonly providerScope: BusinessIdentifier + readonly nativeRecordId: string +} + +/** + * The opaque identity of one provider record, which its output and artifact identities extend. + * + * The scope and the record's coordinates stay private to it; only the identifier serializes. + */ +export interface ProviderRecordIdentity { + /** The `provider-record` identifier, typed in the `source-record` role. */ + readonly identifier: RoledIdentifier + /** Mints the `provider-output` identifier of one output this record yields. */ + readonly output: (coordinates: OutputCoordinates) => Result + /** Mints the `provider-artifact` identifier of one part of a recording this record carries. */ + readonly artifact: ( + coordinates: ArtifactCoordinates, + ) => Result +} + +/** Derives the identity of one provider record; its outputs and artifacts mint through it. */ +export const deriveProviderRecordIdentity = ( scope: OpaqueIdentityScope, - identityKind: Kind, - components: OpaqueIdentityComponents[Kind], -): Result => { - const guard = - Array.isArray(components) ? - providerCoordinateIssue(identityKind, components as readonly string[]) - : undefined - return guard === undefined ? - deriveOpaqueIdentifier(scope, identityKind, components) - : issues([guard]) + record: ProviderRecordCoordinates, +): Result => { + const components = parseRecordCoordinates('provider-record', record) + if (!components.ok) return components + const guard = providerCoordinateIssue('provider-record', components.value[0]) + if (guard !== undefined) return issues([guard]) + return deriveRecordIdentity(scope, 'provider-record', components.value) } /** Derives an immutable event-scoped Device snapshot identity. */ @@ -174,7 +170,7 @@ const deriveDeviceSnapshotEntryIdentity = ( export const deriveApplicationEntryIdentity = ( scope: OpaqueIdentityScope, event: ExchangeEventIdentifier, - application: ApplicationDevice, + application: ApplicationDevice | Writer, id?: FhirId, ): Result => deriveDeviceSnapshotEntryIdentity( @@ -217,7 +213,7 @@ export const deriveRecordingDeviceEntryIdentity = ( if ( !recordingDeviceAdapters.has(adapterId) || !isNonBlank(device.stableUnitToken) || - !parseAbsoluteUri(subject.system).ok || + !parseIdentifierSystem(subject.system).ok || !isNonBlank(subject.value) ) { return err( @@ -258,16 +254,8 @@ export const deriveWriterRecordIdentifier = ( ]) export type ProviderOutputIdentityInput = - | { - readonly kind: 'provider-artifact' - readonly formatCode: string - readonly partIndex: string - } - | { - readonly kind: 'provider-output' - readonly outputRole: string - readonly outputDiscriminator: string - } + | (ArtifactCoordinates & { readonly kind: 'provider-artifact' }) + | (OutputCoordinates & { readonly kind: 'provider-output' }) export interface ProviderIdentityInput< Provider extends ConnectedProvider = ConnectedProvider, @@ -309,7 +297,7 @@ const isRecord = (value: unknown): value is Record => const OUTPUT_SELECTOR_KEYS: Readonly> = { 'provider-artifact': ['kind', 'formatCode', 'partIndex'], - 'provider-output': ['kind', 'outputRole', 'outputDiscriminator'], + 'provider-output': ['kind', 'role', 'discriminator'], } const isClosedSelector = (candidate: Record): boolean => { @@ -336,10 +324,8 @@ const admittedProviderOutput = ( output.partIndex === '0' ) } - if (rawRole === output.outputRole) { - return ( - rawDiscriminators[provider]?.[sourceType] === output.outputDiscriminator - ) + if (rawRole === output.role) { + return rawDiscriminators[provider]?.[sourceType] === output.discriminator } const mapping = scalarRoles[provider]?.[sourceType] const discriminators = scalarDiscriminators[provider]?.[sourceType] @@ -348,8 +334,8 @@ const admittedProviderOutput = ( discriminators !== undefined && Object.keys(mapping).some( (measurementId) => - mapping[measurementId] === output.outputRole && - discriminators[measurementId] === output.outputDiscriminator, + mapping[measurementId] === output.role && + discriminators[measurementId] === output.discriminator, ) ) } @@ -458,7 +444,7 @@ export const deriveProviderIdentities = ( const findings = selectorIssues(input) if (findings.length > 0) return issues(findings) if ( - !parseAbsoluteUri(input.repositoryScope.system).ok || + !parseIdentifierSystem(input.repositoryScope.system).ok || !isNonBlank(input.repositoryScope.value) ) { return err( @@ -477,37 +463,27 @@ export const deriveProviderIdentities = ( ['event'], ) } - const source = [ - input.provider, - input.sourceType, - input.repositoryScope.system, - input.repositoryScope.value, - input.sourceNativeId, - ] as const - const sourceRecord = deriveProviderOpaqueIdentifier( - input.scope, - 'provider-record', - source, - ) - if (!sourceRecord.ok) return sourceRecord - - const outputs: RoledIdentifier[] = [] - for (const output of input.outputs) { - const derived = + const record = deriveProviderRecordIdentity(input.scope, { + providerCode: input.provider, + sourceType: input.sourceType, + providerScope: input.repositoryScope, + nativeRecordId: input.sourceNativeId, + }) + if (!record.ok) return record + const outputs = collectResults( + input.outputs.map((output) => output.kind === 'provider-output' ? - deriveProviderOpaqueIdentifier(input.scope, 'provider-output', [ - ...source, - output.outputRole, - output.outputDiscriminator, - ]) - : deriveProviderOpaqueIdentifier(input.scope, 'provider-artifact', [ - ...source, - output.formatCode, - output.partIndex, - ]) - if (!derived.ok) return derived - outputs.push(derived.value) - } + record.value.output({ + role: output.role, + discriminator: output.discriminator, + }) + : record.value.artifact({ + formatCode: output.formatCode, + partIndex: output.partIndex, + }), + ), + ) + if (!outputs.ok) return outputs const provenanceNode = deriveEntryNodeIdentifier(input.scope, { event: input.event, role: input.provenanceNodeRole ?? 'conversion-provenance', @@ -516,8 +492,8 @@ export const deriveProviderIdentities = ( if (!provenanceNode.ok) return provenanceNode return ok( deepFreeze({ - sourceRecord: sourceRecord.value, - outputs, + sourceRecord: record.value.identifier, + outputs: outputs.value, event: input.event, provenanceNode: provenanceNode.value, }), diff --git a/packages/fhir/src/providers/index.ts b/packages/fhir/src/providers/index.ts index e4299470..de417ffb 100644 --- a/packages/fhir/src/providers/index.ts +++ b/packages/fhir/src/providers/index.ts @@ -37,7 +37,6 @@ export { parseProviderConversionOptions, providerOutputCoordinates, providerOutputRole, - type ProviderOutputCoordinates, } from './provider.js' export { buildProviderRecordingGraph, @@ -50,7 +49,12 @@ export { parseSha1Base64, } from './recording.js' export { buildProviderRetractionEvent } from './retraction.js' -export type { DeviceSnapshotRole } from './identity.js' +export { + deriveProviderRecordIdentity, + type DeviceSnapshotRole, + type ProviderRecordCoordinates, + type ProviderRecordIdentity, +} from './identity.js' export type { CanonicalBase64, ConnectedProvider, @@ -82,8 +86,13 @@ export type { RawPayloadAdmissionAssertion, Sha1Base64, SupportedConnectedProviderMeasurementKind, + Writer, WriterRecord, } from './types.js' +export type { + ArtifactCoordinates, + OutputCoordinates, +} from '../mobile/identity.js' export type { ApplicationDevice, ConversionBatch, diff --git a/packages/fhir/src/providers/provider-input-schemas.ts b/packages/fhir/src/providers/provider-input-schemas.ts index aa2fc2e1..52893605 100644 --- a/packages/fhir/src/providers/provider-input-schemas.ts +++ b/packages/fhir/src/providers/provider-input-schemas.ts @@ -8,11 +8,17 @@ import { z } from 'zod' import { SYSTEMS } from './profiles.js' -import type { ProviderConversionOptions, WriterRecord } from './types.js' +import type { + ProviderConversionOptions, + Writer, + WriterRecord, +} from './types.js' import { parseAbsoluteUri, parseFhirInstant, + parseIdentifierSystem, type AbsoluteUri, + type IdentifierSystem, type Issue, } from '../core/index.js' import { @@ -21,7 +27,6 @@ import { } from '../mobile/identity.js' import { canonicalizeMobileEffectiveInstant } from '../mobile/time.js' import type { - ApplicationDevice, GovernedSourceIdentifierDisclosurePolicy, InstantEffectiveTime, PeriodEffectiveTime, @@ -79,14 +84,29 @@ export const sourceAbsoluteUri: z.ZodType = z.custom( ), ) +/** An identifier system inside a source record; a malformed one is a refused record. */ +const sourceIdentifierSystem = z.custom( + (value) => parseIdentifierSystem(value).ok, + refusal( + 'mobile-input.value-shape-invalid', + 'Expected an identifier system: an absolute ASCII RFC 3986 URI.', + ), +) + /** An absolute URI inside deployment configuration; a malformed one is a fault. */ const absoluteUriSchema = z.custom( (value) => parseAbsoluteUri(value).ok, { message: 'Expected an absolute ASCII RFC 3986 URI.' }, ) +/** An identifier system inside deployment configuration; a malformed one is a fault. */ +const identifierSystemSchema = z.custom( + (value) => parseIdentifierSystem(value).ok, + { message: 'Expected an identifier system: an absolute ASCII RFC 3986 URI.' }, +) + const sourceIdentifierSchema = z.strictObject({ - system: sourceAbsoluteUri, + system: sourceIdentifierSystem, value: unicodeScalarText.refine( (value) => value !== '', refusal( @@ -145,14 +165,13 @@ export const effectiveTimeSchema: z.ZodType< periodEffectiveSchemaValue, ]) -const applicationDeviceSchemaValue = z.strictObject({ +const writerSchemaValue = z.strictObject({ sourceDeviceToken: nonBlankText, name: nonBlankText, version: nonBlankText.optional(), build: nonBlankText.optional(), }) -export const applicationDeviceSchema: z.ZodType = - applicationDeviceSchemaValue +export const writerSchema: z.ZodType = writerSchemaValue const recordingDeviceSchemaValue = z.strictObject({ stableUnitToken: nonBlankText, @@ -204,7 +223,7 @@ const disclosurePolicySchema = z.discriminatedUnion('kind', [ z.strictObject({ kind: z.literal('omit') }), z.strictObject({ kind: z.literal('authorized'), - system: absoluteUriSchema, + system: identifierSystemSchema, type: z .strictObject({ coding: z diff --git a/packages/fhir/src/providers/provider.ts b/packages/fhir/src/providers/provider.ts index 405ea896..281552eb 100644 --- a/packages/fhir/src/providers/provider.ts +++ b/packages/fhir/src/providers/provider.ts @@ -14,7 +14,6 @@ import { type MeasurementDefinition, } from './measurement-definition.js' import { - applicationDeviceSchema, conversionOptionsSchema, effectiveTimeSchema, instantEffectiveSchema, @@ -25,6 +24,7 @@ import { refusal, sourceAbsoluteUri, writerRecordSchema, + writerSchema, } from './provider-input-schemas.js' import type { ConnectedProvider, @@ -51,6 +51,7 @@ import { type Issue, type Result, } from '../core/index.js' +import type { OutputCoordinates } from '../mobile/identity.js' import type { MobileMeasurement } from '../mobile/types.js' import { groveRuleIssue, @@ -150,7 +151,7 @@ const sourceSchema = z.strictObject({ }), sourceType: nonBlankText, sourceNativeId: nativeIdentifierText, - writer: applicationDeviceSchema, + writer: writerSchema, recordingMethod: z .enum(['actively-recorded', 'automatically-recorded', 'manual-entry']) .optional(), @@ -376,11 +377,6 @@ const providerSourceDiscriminatorMapping = ( return providerMappings[sourceType] } -export interface ProviderOutputCoordinates { - readonly outputRole: string - readonly outputDiscriminator: string -} - interface RecordEffectiveRule { readonly kind: 'complete-civil-day-period' readonly measurementIds: readonly string[] @@ -475,15 +471,15 @@ export const providerOutputCoordinates = ( provider: ConnectedProvider, sourceType: string, kind: string, -): ProviderOutputCoordinates | undefined => { - const outputRole = providerSourceMapping(provider, sourceType)?.[kind] - const outputDiscriminator = providerSourceDiscriminatorMapping( +): OutputCoordinates | undefined => { + const role = providerSourceMapping(provider, sourceType)?.[kind] + const discriminator = providerSourceDiscriminatorMapping( provider, sourceType, )?.[kind] - return outputRole === undefined || outputDiscriminator === undefined ? + return role === undefined || discriminator === undefined ? undefined - : { outputRole, outputDiscriminator } + : { role, discriminator } } const recordMappingIssues = (record: ParsedRecord): readonly Issue[] => { diff --git a/packages/fhir/src/providers/recording.ts b/packages/fhir/src/providers/recording.ts index d1511eda..33729ec3 100644 --- a/packages/fhir/src/providers/recording.ts +++ b/packages/fhir/src/providers/recording.ts @@ -34,13 +34,13 @@ import { PROVIDER_RECORDING_OUTPUT_ROLE, } from './profiles.js' import { - applicationDeviceSchema, governedSourceIdentifierIssues, effectiveTimeSchema, nativeIdentifierText, nonBlankText, refusal, writerRecordSchema, + writerSchema, } from './provider-input-schemas.js' import { parseProviderConversionOptions, refusalIssues } from './provider.js' import type { @@ -69,6 +69,7 @@ import { decodeCanonicalBase64, encodeBase64, type Issue, + type PartIndex, type Result, } from '../core/index.js' import { @@ -194,7 +195,7 @@ const recordingSourceSchema = z.strictObject({ }), sourceType: nonBlankText, sourceNativeId: nativeIdentifierText, - writer: applicationDeviceSchema, + writer: writerSchema, effective: effectiveTimeSchema, writerRecord: writerRecordSchema.optional(), }) @@ -334,6 +335,9 @@ export const parseProviderRecordingAttachment = ( return ok(deepFreeze(parsed.data) as unknown as ProviderRecordingAttachment) } +// A provider recording is the sole part of its source artifact. +const RECORDING_PART_INDEX = '0' as PartIndex + interface RecordingGraphIdentities { readonly connected: ProviderIdentities readonly document: EntryIdentity @@ -357,13 +361,13 @@ const resolveRecordingIdentities = ( outputs: [ { kind: 'provider-output', - outputRole: PROVIDER_RECORDING_OUTPUT_ROLE, - outputDiscriminator: PROVIDER_RECORDING_OUTPUT_DISCRIMINATOR, + role: PROVIDER_RECORDING_OUTPUT_ROLE, + discriminator: PROVIDER_RECORDING_OUTPUT_DISCRIMINATOR, }, { kind: 'provider-artifact', formatCode: attachment.format, - partIndex: '0', + partIndex: RECORDING_PART_INDEX, }, ], event: context.event, diff --git a/packages/fhir/src/providers/retraction.ts b/packages/fhir/src/providers/retraction.ts index 1d90b838..b97e206f 100644 --- a/packages/fhir/src/providers/retraction.ts +++ b/packages/fhir/src/providers/retraction.ts @@ -24,8 +24,8 @@ import { EXTENSIONS, PROFILES, SYSTEMS } from './profiles.js' import { groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' import { issues, - parseAbsoluteUri, parseFhirInstant, + parseIdentifierSystem, type FhirInstant, type Issue, type Result, @@ -109,7 +109,7 @@ const opaqueIdentifierIssues = ( return [] } -const nativeIdentifierIssues = ( +const nativeRecordIdentifierIssues = ( value: unknown, scope: OpaqueIdentityScope, path: Path, @@ -118,7 +118,7 @@ const nativeIdentifierIssues = ( const candidate = value as Partial if ( typeof candidate !== 'object' || - !parseAbsoluteUri(candidate.system).ok || + !parseIdentifierSystem(candidate.system).ok || !isNonBlank(candidate.value) || Object.keys(candidate).length !== 2 ) { @@ -184,14 +184,19 @@ const targetIssues = ( scope, [...path, 'identifier'], ), - ...nativeIdentifierIssues(candidate.nativeIdentifier, scope, [ + ...nativeRecordIdentifierIssues(candidate.nativeRecordIdentifier, scope, [ ...path, - 'nativeIdentifier', + 'nativeRecordIdentifier', ]), ) const keys = Object.keys(candidate).filter( (key) => - !['role', 'resourceType', 'identifier', 'nativeIdentifier'].includes(key), + ![ + 'role', + 'resourceType', + 'identifier', + 'nativeRecordIdentifier', + ].includes(key), ) if (keys.length > 0) { findings.push( @@ -278,13 +283,13 @@ const compareTargetReferences = ( const targetReference = (target: RetractionTarget): TargetReference => ({ extension: - target.nativeIdentifier === undefined ? + target.nativeRecordIdentifier === undefined ? [{ url: EXTENSIONS.retractionTargetRole, valueCode: target.role }] : [ { url: EXTENSIONS.retractionTargetRole, valueCode: target.role }, { url: EXTENSIONS.retractionTargetNativeIdentifier, - valueIdentifier: identifier(target.nativeIdentifier), + valueIdentifier: identifier(target.nativeRecordIdentifier), }, ], type: target.resourceType, diff --git a/packages/fhir/src/providers/types.ts b/packages/fhir/src/providers/types.ts index 663e6749..cf52f3fc 100644 --- a/packages/fhir/src/providers/types.ts +++ b/packages/fhir/src/providers/types.ts @@ -15,7 +15,6 @@ import type { import type { Branded, Issue } from '../core/index.js' import type { BusinessIdentifier } from '../mobile/identity.js' import type { - ApplicationDevice, ExchangeGraphIdentifiers, GovernedSourceIdentifierDisclosurePolicy, InstantEffectiveTime, @@ -132,6 +131,18 @@ export interface ProviderAdapter< readonly provider: Provider } +/** + * The application that wrote the source record at the provider, emitted as its own + * application snapshot; unlike the converter, a provider rarely reports its version. + */ +export interface Writer { + /** A stable token naming the writer; never emitted in clear. */ + readonly sourceDeviceToken: string + readonly name: string + readonly version?: string | undefined + readonly build?: string | undefined +} + /** The logical record identity the application that wrote the source record assigned. */ export interface WriterRecord { readonly applicationIdentifier: BusinessIdentifier @@ -160,7 +171,7 @@ export interface ProviderSourceRecord< readonly sourceType: SourceType readonly sourceNativeId: string /** The application that entered the record at the connected provider. */ - readonly writer: ApplicationDevice + readonly writer: Writer } export type ConnectedProviderMeasurements< @@ -223,7 +234,7 @@ export interface ProviderRecordingSourceRecord< /** Opaque-identity digest input, disclosed only through the conversion options. */ readonly sourceNativeId: string /** The application that entered the already-obtained record at the provider. */ - readonly writer: ApplicationDevice + readonly writer: Writer /** The source activity time the recording covers. */ readonly effective: InstantEffectiveTime | PeriodEffectiveTime readonly writerRecord?: WriterRecord | undefined diff --git a/packages/fhir/src/questionnaire/builder.ts b/packages/fhir/src/questionnaire/builder.ts index a99b804f..950faea7 100644 --- a/packages/fhir/src/questionnaire/builder.ts +++ b/packages/fhir/src/questionnaire/builder.ts @@ -38,10 +38,10 @@ import { cloneJsonValue, issue, issues, - parseAbsoluteUri, parseCanonical, parseFhirId, parseFhirInstant, + parseIdentifierSystem, parseSemVer, zodIssueToIssue, type Issue, @@ -184,7 +184,7 @@ const validateResponseInput = ( ) } if ( - !parseAbsoluteUri(input.identifier.system).ok || + !parseIdentifierSystem(input.identifier.system).ok || input.identifier.value.trim() === '' ) { failures.push( diff --git a/packages/fhir/src/questionnaire/parse.ts b/packages/fhir/src/questionnaire/parse.ts index 050e8152..cb211141 100644 --- a/packages/fhir/src/questionnaire/parse.ts +++ b/packages/fhir/src/questionnaire/parse.ts @@ -40,6 +40,7 @@ import { ok, parseAbsoluteUri, parseCanonical, + parseIdentifierSystem, parseSemVer, zodIssueToIssue, type Issue, @@ -262,7 +263,7 @@ const responseContractIssues = ( const identifierSystem = objectPart(response.identifier, 'system') const identifierValue = objectPart(response.identifier, 'value') if ( - !parseAbsoluteUri(identifierSystem).ok || + !parseIdentifierSystem(identifierSystem).ok || typeof identifierValue !== 'string' || identifierValue.trim() === '' ) { diff --git a/packages/fhir/src/questionnaire/preflight.ts b/packages/fhir/src/questionnaire/preflight.ts index 0a2b3698..9a153db3 100644 --- a/packages/fhir/src/questionnaire/preflight.ts +++ b/packages/fhir/src/questionnaire/preflight.ts @@ -41,7 +41,7 @@ import type { import { issues, ok, - parseAbsoluteUri, + parseIdentifierSystem, parseSemVer, type Issue, type Result, @@ -107,7 +107,7 @@ const validateQuestionnaireResponseEnvelope = ( const identifierSystem = codingPart(response.identifier, 'system') const identifierValue = codingPart(response.identifier, 'value') if ( - !parseAbsoluteUri(identifierSystem).ok || + !parseIdentifierSystem(identifierSystem).ok || typeof identifierValue !== 'string' || identifierValue.trim() === '' ) { diff --git a/packages/fhir/src/questionnaire/references.ts b/packages/fhir/src/questionnaire/references.ts index 196e3b3e..48d6e92b 100644 --- a/packages/fhir/src/questionnaire/references.ts +++ b/packages/fhir/src/questionnaire/references.ts @@ -7,7 +7,7 @@ // import { isR4ResourceType } from './r4-resource-types.js' -import { parseAbsoluteUri } from '../core/index.js' +import { parseAbsoluteUri, parseIdentifierSystem } from '../core/index.js' type UnknownRecord = Readonly> @@ -79,7 +79,7 @@ const literalTargetType = ( const completeLogicalIdentifier = (value: unknown): boolean => { const identifier = asRecord(value) return ( - parseAbsoluteUri(identifier?.system).ok && + parseIdentifierSystem(identifier?.system).ok && typeof identifier?.value === 'string' && identifier.value.trim() !== '' ) diff --git a/packages/fhir/src/questionnaire/types.ts b/packages/fhir/src/questionnaire/types.ts index 5db5afab..3286d9af 100644 --- a/packages/fhir/src/questionnaire/types.ts +++ b/packages/fhir/src/questionnaire/types.ts @@ -12,6 +12,7 @@ import type { Canonical, FhirId, FhirInstant, + IdentifierSystem, SemVer, } from '../core/index.js' import type { @@ -163,7 +164,7 @@ export interface QuestionnaireInput { } export interface QuestionnaireResponseIdentifierInput { - readonly system: AbsoluteUri + readonly system: IdentifierSystem readonly value: string } diff --git a/packages/fhir/src/r4/active-graph-semantics.ts b/packages/fhir/src/r4/active-graph-semantics.ts index fb56048a..5363d5fa 100644 --- a/packages/fhir/src/r4/active-graph-semantics.ts +++ b/packages/fhir/src/r4/active-graph-semantics.ts @@ -35,7 +35,7 @@ import { groveProfileClaims, healthConnectDataOriginApplication, } from '../contract/measurement-catalog.generated.js' -import { parseAbsoluteUri } from '../core/index.js' +import { parseIdentifierSystem } from '../core/index.js' import { isOpaqueIdentityValue } from '../mobile/identity.js' export { hasAdmittedMobileObservationProfile } from './active-observation-semantics.js' @@ -105,7 +105,7 @@ export const validateAssemblerAgent = ( const logicalApplication = who?.type === 'Device' && completeIdentifier(logicalIdentifier) && - parseAbsoluteUri(logicalIdentifier.system).ok && + parseIdentifierSystem(logicalIdentifier.system).ok && identifierRole(logicalIdentifier) === 'device-snapshot' && isOpaqueIdentityValue(logicalIdentifier.value) if ( @@ -298,7 +298,7 @@ const conversionSourceIdentifier = ( sourceEntity?.role !== 'source' || sourceWhat?.reference !== undefined || !completeIdentifier(identifier) || - !parseAbsoluteUri(identifier.system).ok || + !parseIdentifierSystem(identifier.system).ok || identifierRole(identifier) !== 'source-record' || !isOpaqueIdentityValue(identifier.value) ) { @@ -355,11 +355,11 @@ const validateOutputIdentities = ( const output = identifierWithRole(entry.resource, 'source-output') if ( !completeIdentifier(source) || - !parseAbsoluteUri(source.system).ok || + !parseIdentifierSystem(source.system).ok || !isOpaqueIdentityValue(source.value) || !identifierPairEqual(source, sourceIdentifier) || !completeIdentifier(output) || - !parseAbsoluteUri(output.system).ok || + !parseIdentifierSystem(output.system).ok || !isOpaqueIdentityValue(output.value) ) { addIssue(context, 'mobile-output.source-output-required', [ diff --git a/packages/fhir/src/r4/diagnostics.ts b/packages/fhir/src/r4/diagnostics.ts index 2728c0be..41b3f7b8 100644 --- a/packages/fhir/src/r4/diagnostics.ts +++ b/packages/fhir/src/r4/diagnostics.ts @@ -16,7 +16,10 @@ export type { ProducerDiagnosticCode } type LocationPath = ReadonlyArray -/** Rules a producer reports about a record it refused or trimmed; they have no FHIR element. */ +/** + * Rules a producer reports about a record it refused or trimmed; they have no FHIR element, + * except a source-offset warning, whose emitter passes the effective element as its location. + */ export type ClientRecordRule = Extract< ProducerDiagnosticCode, `${string}-input.${string}` | `${string}-omission.${string}` diff --git a/packages/fhir/src/r4/exchange-envelope.ts b/packages/fhir/src/r4/exchange-envelope.ts index 492348cc..b119a724 100644 --- a/packages/fhir/src/r4/exchange-envelope.ts +++ b/packages/fhir/src/r4/exchange-envelope.ts @@ -29,11 +29,7 @@ import { import { validateProfiledResource } from './profile-semantics.js' import type { R4CollectionBundle } from './types.js' import { groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' -import { - parseAbsoluteUri, - type AbsoluteUri, - type EntryNodeOrdinal, -} from '../core/index.js' +import { parseIdentifierSystem, type EntryNodeOrdinal } from '../core/index.js' import { deriveEntryFullUrl, deriveEntryNodeValue, @@ -360,7 +356,7 @@ const resourceIdentityRoleCounts = ( } if ( !completeIdentifier(candidate) || - !parseAbsoluteUri(candidate.system).ok || + !parseIdentifierSystem(candidate.system).ok || !isOpaqueIdentityValue(candidate.value) ) { addIssue(context, 'mobile-exchange.opaque-resource-identity', path, { @@ -498,10 +494,11 @@ const validateDerivedFullUrl = ( index: number, context: z.core.$RefinementCtx, ): void => { - const derived = deriveEntryFullUrl({ - system: key.system as AbsoluteUri, - value: key.value, - }) + const system = parseIdentifierSystem(key.system) + const derived = + system.ok ? + deriveEntryFullUrl({ system: system.value, value: key.value }) + : system if (!derived.ok || derived.value !== fullUrl) { addIssue(context, 'mobile-exchange.deterministic-full-url', [ 'entry', @@ -621,7 +618,7 @@ export const validateExchangeEnvelope = ( } if ( !completeIdentifier(bundle.identifier) || - !parseAbsoluteUri(bundle.identifier.system).ok || + !parseIdentifierSystem(bundle.identifier.system).ok || identifierRole(bundle.identifier) !== 'event' || !isEventIdentityValue(bundle.identifier.value) ) { diff --git a/packages/fhir/src/r4/graph-schema-utils.ts b/packages/fhir/src/r4/graph-schema-utils.ts index 498b8b8f..5c172b92 100644 --- a/packages/fhir/src/r4/graph-schema-utils.ts +++ b/packages/fhir/src/r4/graph-schema-utils.ts @@ -20,7 +20,7 @@ import { groveProducerDiagnostics, } from '../contract/measurement-catalog.generated.js' import { healthKitApplicationDeviceIdentity } from '../contract/providers.generated.js' -import { parseAbsoluteUri } from '../core/index.js' +import { parseIdentifierSystem } from '../core/index.js' import { identityValuePrefixes } from '../mobile/identity.js' export type UnknownRecord = Readonly> @@ -303,7 +303,7 @@ const validateGovernedReferenceShape = ( const validLogical = !hasLiteral && completeIdentifier(identifier) && - parseAbsoluteUri(identifier.system).ok && + parseIdentifierSystem(identifier.system).ok && typeof declaredType === 'string' && allowedTypes.has(declaredType) if (hasLiteral && hasIdentifier) { diff --git a/packages/fhir/src/r4/retraction-graph-semantics.ts b/packages/fhir/src/r4/retraction-graph-semantics.ts index b01922c4..de8935d1 100644 --- a/packages/fhir/src/r4/retraction-graph-semantics.ts +++ b/packages/fhir/src/r4/retraction-graph-semantics.ts @@ -25,7 +25,7 @@ import { } from './graph-schema-utils.js' import type { R4CollectionBundle } from './types.js' import { groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' -import { parseAbsoluteUri } from '../core/index.js' +import { parseIdentifierSystem } from '../core/index.js' import { isOpaqueIdentityValue } from '../mobile/identity.js' const RETRACTION = groveExchangeProtocol.lifecycle.retraction @@ -140,7 +140,7 @@ const validateRetractionTargetIdentity = ( ): void => { if ( !completeIdentifier(targetIdentifier) || - !parseAbsoluteUri(targetIdentifier.system).ok || + !parseIdentifierSystem(targetIdentifier.system).ok || identifierRole(targetIdentifier) !== expectedRole || !isOpaqueIdentityValue(targetIdentifier.value) ) { @@ -193,7 +193,7 @@ const validateRetractionTargetNativeIdentifier = ( if ( matches.length !== 1 || !completeIdentifier(native) || - !parseAbsoluteUri(native.system).ok || + !parseIdentifierSystem(native.system).ok || groveIdentifierRoles(native).length > 0 ) { addIssue(context, 'mobile-retraction.native-record-identifier', [ @@ -292,7 +292,7 @@ const validateRetractionSource = ( sourceEntity?.role !== 'source' || sourceWhat?.reference !== undefined || !completeIdentifier(source) || - !parseAbsoluteUri(source.system).ok || + !parseIdentifierSystem(source.system).ok || identifierRole(source) !== 'source-record' || !isOpaqueIdentityValue(source.value) ) { diff --git a/packages/fhir/src/r4/retraction-targets.ts b/packages/fhir/src/r4/retraction-targets.ts index be5db018..c962d4f3 100644 --- a/packages/fhir/src/r4/retraction-targets.ts +++ b/packages/fhir/src/r4/retraction-targets.ts @@ -17,13 +17,7 @@ import { import { parseExchangeGraph } from './parse.js' import type { ExchangeGraph } from './types.js' import { groveExchangeProtocol } from '../contract/measurement-catalog.generated.js' -import { - err, - ok, - parseAbsoluteUri, - type AbsoluteUri, - type Result, -} from '../core/index.js' +import { err, ok, parseIdentifierSystem, type Result } from '../core/index.js' import { isGroveIdentifierRole, type BusinessIdentifier, @@ -41,15 +35,15 @@ export type RetractionTargetResourceType = /** * One prior graph node a retraction names by its typed logical identifier. * - * `nativeIdentifier` is the adapter's own record key, disclosed under the same policy as - * a governed source identifier; it never addresses the target. + * `nativeRecordIdentifier` is the adapter's own record key, disclosed under the same + * policy as a governed source identifier; it never addresses the target. */ export type RetractionTarget = { readonly [Role in RetractionTargetRole]: { readonly role: Role readonly resourceType: RetractionTargetResourceType readonly identifier: RoledIdentifier - readonly nativeIdentifier?: BusinessIdentifier | undefined + readonly nativeRecordIdentifier?: BusinessIdentifier | undefined } }[RetractionTargetRole] @@ -80,8 +74,12 @@ const roledIdentifier = ( const candidate = identifiersOf(resource).find( (identifier) => identifierRole(identifier) === role, ) - return completeIdentifier(candidate) && isGroveIdentifierRole(role) ? - { system: candidate.system as AbsoluteUri, value: candidate.value, role } + if (!completeIdentifier(candidate) || !isGroveIdentifierRole(role)) { + return undefined + } + const system = parseIdentifierSystem(candidate.system) + return system.ok ? + { system: system.value, value: candidate.value, role } : undefined } @@ -89,16 +87,17 @@ const roledIdentifier = ( const governedIdentifier = ( resource: unknown, ): BusinessIdentifier | undefined => { - const candidates = identifiersOf(resource).filter( - (identifier) => - completeIdentifier(identifier) && - groveIdentifierRoles(identifier).length === 0 && - parseAbsoluteUri(identifier.system).ok, - ) - const candidate = candidates[0] - return candidates.length === 1 && completeIdentifier(candidate) ? - { system: candidate.system as AbsoluteUri, value: candidate.value } - : undefined + const candidates = identifiersOf(resource).flatMap((identifier) => { + if ( + !completeIdentifier(identifier) || + groveIdentifierRoles(identifier).length > 0 + ) { + return [] + } + const system = parseIdentifierSystem(identifier.system) + return system.ok ? [{ system: system.value, value: identifier.value }] : [] + }) + return candidates.length === 1 ? candidates[0] : undefined } const targetRoleFor = ( @@ -138,13 +137,13 @@ const targetOf = ( ) { return undefined } - const nativeIdentifier = + const nativeRecordIdentifier = role === 'primary-output' ? governedIdentifier(resource) : undefined return { role, resourceType, identifier, - ...(nativeIdentifier === undefined ? {} : { nativeIdentifier }), + ...(nativeRecordIdentifier === undefined ? {} : { nativeRecordIdentifier }), } as RetractionTarget } @@ -153,7 +152,7 @@ const targetOf = ( * * An Observation another Observation lists in `hasMember` is a child output; every other * output is primary. A governed source identifier on the primary output travels along as - * the target's native identifier. + * the target's native record identifier. */ export const retractionTargets = ( graph: ExchangeGraph, diff --git a/packages/fhir/test/diagnostics.test.ts b/packages/fhir/test/diagnostics.test.ts index 8e4f41f8..ec1a2516 100644 --- a/packages/fhir/test/diagnostics.test.ts +++ b/packages/fhir/test/diagnostics.test.ts @@ -7,10 +7,17 @@ // import { readdirSync, readFileSync } from 'node:fs' +import { expectTypeOf } from 'expect-type' import { groveExchangeProtocol, groveProducerDiagnostics, } from '../src/contract/measurement-catalog.generated.js' +import { issue as schemaIssue } from '../src/core/index.js' +import { + isProducerDiagnostic, + type Issue, + type ProducerDiagnostic, +} from '../src/index.js' import { groveRuleIssue, groveRuleIssueFromParameters, @@ -150,3 +157,56 @@ describe('stable Grove rule diagnostics', () => { } }) }) + +describe('producer diagnostics', () => { + it('narrows a graph rule with its reason and location', () => { + const issue: Issue = groveRuleIssue('mobile-exchange.entry-node-key', [ + 'entry', + 1, + ]) + expect(isProducerDiagnostic(issue)).toBe(true) + if (!isProducerDiagnostic(issue)) return + expectTypeOf(issue).toEqualTypeOf() + expect(issue.reason).toBe( + groveProducerDiagnostics['mobile-exchange.entry-node-key'].reason, + ) + expect(issue.location).toBe('Bundle.entry[1]') + for (const code of ruleCodes) { + expect(isProducerDiagnostic(groveRuleIssue(code, []))).toBe(true) + } + }) + + it('narrows a refusal and an omission, which carry no location', () => { + for (const code of [ + 'mobile-input.value-outside-domain', + 'mobile-omission.recording-device', + ] as const) { + const issue = groveRuleIssue(code, []) + expect(issue).not.toHaveProperty('location') + expect(isProducerDiagnostic(issue)).toBe(true) + } + }) + + it('never narrows a schema issue, even one carrying a reason and location', () => { + const issue = schemaIssue('invalid-uri', ['system'], 'Not absolute.') + expect(isProducerDiagnostic(issue)).toBe(false) + expect( + isProducerDiagnostic({ + ...issue, + reason: 'Not absolute.', + location: 'Identifier.system', + }), + ).toBe(false) + }) + + it('never narrows a registry issue missing its reason', () => { + for (const code of [ + 'mobile-exchange.entry-node-key', + 'mobile-input.value-outside-domain', + ] as const) { + const { reason, ...unreasoned } = groveRuleIssue(code, []) + expect(reason).toBeDefined() + expect(isProducerDiagnostic(unreasoned)).toBe(false) + } + }) +}) diff --git a/packages/fhir/test/exchange-protocol.test.ts b/packages/fhir/test/exchange-protocol.test.ts index c94f5583..b733e494 100644 --- a/packages/fhir/test/exchange-protocol.test.ts +++ b/packages/fhir/test/exchange-protocol.test.ts @@ -6,19 +6,24 @@ // SPDX-License-Identifier: MIT // +import { inspect } from 'node:util' import { - parseAbsoluteUri, + ok, parseEntryNodeOrdinal, parseEventSequence, + parseIdentifierSystem, parseKeyEpoch, - type AbsoluteUri, + parsePartIndex, type EntryNodeOrdinal, type EventSequence, + type IdentifierSystem, + type PartIndex, type Result, } from '../src/core/index.js' import { containsIsolatedSurrogate, deriveConformanceVectorOpaqueIdentifier, + deriveConformanceVectorRecordIdentity, } from '../src/mobile/identity.js' import { createEntryIdentity, @@ -27,6 +32,7 @@ import { deriveEntryNodeValue, deriveEventIdentifier, deriveOpaqueIdentifier, + deriveSourceRecordIdentity, entryIdentifierName, encodeLengthFramedUtf8, groveExchangeProtocol, @@ -35,22 +41,26 @@ import { isOpaqueIdentityScope, isOpaqueIdentityValue, validateOpaqueIdentityScope, - type OpaqueIdentityComponents, type OpaqueIdentityKind, type OpaqueIdentityScopeInput, type OpaqueIdentitySystems, + type RoledIdentifier, + type SourceRecordCoordinates, + type SourceRecordIdentity, } from '../src/mobile/index.js' +import { providerCoordinateIssue } from '../src/providers/identity.js' import { - deriveProviderOpaqueIdentifier, - providerCoordinateIssue, -} from '../src/providers/identity.js' + deriveProviderRecordIdentity, + type ProviderRecordCoordinates, +} from '../src/providers/index.js' const unwrap = (result: Result): Value => { if (!result.ok) throw new Error(result.issues[0]?.message) return result.value } -const uri = (value: string): AbsoluteUri => unwrap(parseAbsoluteUri(value)) +const identifierSystem = (value: string): IdentifierSystem => + unwrap(parseIdentifierSystem(value)) const sequence = (value: string): EventSequence => unwrap(parseEventSequence(value)) const ordinal = (value: string): EntryNodeOrdinal => @@ -60,15 +70,15 @@ const vectors = groveExchangeProtocol.testVectors const vectorSystems = Object.fromEntries( vectors.identitySystems.map(({ identityKind, system }) => [ identityKind, - uri(system), + identifierSystem(system), ]), ) as OpaqueIdentitySystems const conformanceScope: OpaqueIdentityScopeInput = { systems: { opaque: vectorSystems, - event: uri(vectors.event.system), - entryNode: uri(vectors.entryNode.system), + event: identifierSystem(vectors.event.system), + entryNode: identifierSystem(vectors.entryNode.system), }, keyId: vectors.keyId, keyEpoch: unwrap(parseKeyEpoch(vectors.epoch)), @@ -85,13 +95,153 @@ const runtimeScope = unwrap(validateOpaqueIdentityScope(runtimeInput)) const identityVectors = vectors.identities const invalidIdentityVectors = vectors.invalidIdentities -const deriveVectorIdentity = ( - kind: Kind, - components: OpaqueIdentityComponents[Kind], -) => deriveConformanceVectorOpaqueIdentifier(conformanceScope, kind, components) +type RecordKind = 'provider-record' | 'source-record' +type RecordExtension = 'artifact' | 'output' + +// A record-family vector lists its record's five components, then its output's or artifact's two. +const recordFamilies: Partial< + Record +> = { + 'source-record': ['source-record'], + 'source-output': ['source-record', 'output'], + 'source-artifact': ['source-record', 'artifact'], + 'provider-record': ['provider-record'], + 'provider-output': ['provider-record', 'output'], + 'provider-artifact': ['provider-record', 'artifact'], +} + +const recordCoordinates = ( + kind: RecordKind, + [ + adapter = '', + sourceType = '', + system = '', + value = '', + nativeRecordId = '', + ]: readonly string[], +) => + kind === 'source-record' ? + { + adapterId: adapter, + sourceType, + repositoryScope: { system: system as IdentifierSystem, value }, + nativeRecordId, + } + : { + providerCode: adapter, + sourceType, + providerScope: { system: system as IdentifierSystem, value }, + nativeRecordId, + } + +const extendRecord = ( + record: SourceRecordIdentity, + extension: RecordExtension | undefined, + [first = '', second = '']: readonly string[], +): Result => { + if (extension === undefined) return ok(record.identifier) + return extension === 'output' ? + record.output({ role: first, discriminator: second }) + : record.artifact({ formatCode: first, partIndex: second as PartIndex }) +} + +const deriveVectorIdentity = ( + kind: OpaqueIdentityKind, + components: readonly string[], +): Result => { + const family = recordFamilies[kind] + if (family === undefined) { + return deriveConformanceVectorOpaqueIdentifier( + conformanceScope, + kind as never, + components as never, + ) + } + const [recordKind, extension] = family + const record = deriveConformanceVectorRecordIdentity( + conformanceScope, + recordKind, + recordCoordinates(recordKind, components), + ) + return record.ok ? + extendRecord(record.value, extension, components.slice(5)) + : record +} + +const deriveRuntimeIdentity = ( + kind: OpaqueIdentityKind, + components: readonly string[], +): Result => { + const family = recordFamilies[kind] + if (family === undefined) { + return deriveOpaqueIdentifier( + runtimeScope, + kind as never, + components as never, + ) + } + const [recordKind, extension] = family + const coordinates = recordCoordinates(recordKind, components) + const record = + recordKind === 'source-record' ? + deriveSourceRecordIdentity( + runtimeScope, + coordinates as SourceRecordCoordinates, + ) + : deriveProviderRecordIdentity( + runtimeScope, + coordinates as ProviderRecordCoordinates, + ) + return record.ok ? + extendRecord(record.value, extension, components.slice(5)) + : record +} + +const vectorOf = (kind: OpaqueIdentityKind) => { + const vector = identityVectors.find( + ({ identityKind }) => identityKind === kind, + ) + if (vector === undefined) throw new Error(`Missing ${kind} vector.`) + return vector +} + +const part = (value: string): PartIndex => unwrap(parsePartIndex(value)) + +const sourceRecordPaths = [ + ['adapterId'], + ['sourceType'], + ['repositoryScope', 'system'], + ['repositoryScope', 'value'], + ['nativeRecordId'], +] + +const heartRateRecord: SourceRecordCoordinates = { + adapterId: 'healthkit', + sourceType: 'HKQuantityTypeIdentifierHeartRate', + repositoryScope: { + system: identifierSystem( + 'https://study.example.org/fhir/NamingSystem/participant', + ), + value: 'participant-1', + }, + nativeRecordId: 'native-1', +} + +const withingsRecord: ProviderRecordCoordinates = { + providerCode: 'withings', + sourceType: 'getmeas:11', + providerScope: { + system: identifierSystem('https://accounts.example.org'), + value: 'patient-001', + }, + nativeRecordId: '17348211', +} describe('Grove exchange protocol identity', () => { it('fails closed without throwing at every untyped identity boundary', () => { + const record = unwrap( + deriveSourceRecordIdentity(runtimeScope, heartRateRecord), + ) const invalidValues = [null, undefined, 42, 'wrong-shape', Symbol('x')] for (const invalid of invalidValues) { const operations = [ @@ -100,18 +250,15 @@ describe('Grove exchange protocol identity', () => { () => deriveEntryFullUrl(invalid as never), () => entryIdentifierName(invalid as never), () => createEntryIdentity(invalid as never), - () => - deriveOpaqueIdentifier(invalid as never, 'source-record', [ - 'healthkit', - 'type', - 'scope-system', - 'scope-value', - 'native-id', - ]), + () => deriveSourceRecordIdentity(invalid as never, heartRateRecord), + () => deriveSourceRecordIdentity(runtimeScope, invalid as never), + () => deriveProviderRecordIdentity(runtimeScope, invalid as never), + () => record.output(invalid as never), + () => record.artifact(invalid as never), () => deriveOpaqueIdentifier( runtimeScope, - 'source-record', + 'writer-record', invalid as never, ), () => deriveEventIdentifier(runtimeScope, invalid as never), @@ -139,15 +286,7 @@ describe('Grove exchange protocol identity', () => { ) const forged = { ...runtimeScope } expect(isOpaqueIdentityScope(forged)).toBe(false) - expect( - deriveOpaqueIdentifier(forged, 'source-record', [ - 'healthkit', - 'HKQuantityTypeIdentifierHeartRate', - 'https://study.example.org/fhir/NamingSystem/participant', - 'participant-1', - 'native-1', - ]), - ).toMatchObject({ + expect(deriveSourceRecordIdentity(forged, heartRateRecord)).toMatchObject({ ok: false, issues: [{ code: 'invalid-identifier', path: ['scope'] }], }) @@ -155,16 +294,9 @@ describe('Grove exchange protocol identity', () => { }) it('keeps a reused scope handle immune to mutation', () => { - const components: OpaqueIdentityComponents['source-record'] = [ - 'healthkit', - 'HKQuantityTypeIdentifierHeartRate', - 'https://study.example.org/fhir/NamingSystem/participant', - 'participant-1', - 'native-1', - ] const before = unwrap( - deriveOpaqueIdentifier(runtimeScope, 'source-record', components), - ) + deriveSourceRecordIdentity(runtimeScope, heartRateRecord), + ).identifier const event = unwrap(deriveEventIdentifier(runtimeScope, sequence('1'))) const mutable = runtimeScope as { keyId: string; secret?: Uint8Array } for (const mutate of [ @@ -183,8 +315,9 @@ describe('Grove exchange protocol identity', () => { } expect(runtimeScope.keyId).toBe(runtimeInput.keyId) expect( - deriveOpaqueIdentifier(runtimeScope, 'source-record', components), - ).toEqual({ ok: true, value: before }) + unwrap(deriveSourceRecordIdentity(runtimeScope, heartRateRecord)) + .identifier, + ).toEqual(before) expect(deriveEventIdentifier(runtimeScope, sequence('1'))).toEqual({ ok: true, value: event, @@ -237,7 +370,7 @@ describe('Grove exchange protocol identity', () => { const fullUrlVector = vectors.fullUrls[0] expect( deriveEntryFullUrl({ - system: uri(fullUrlVector.system), + system: identifierSystem(fullUrlVector.system), value: fullUrlVector.value, }), ).toEqual({ ok: true, value: fullUrlVector.fullUrl }) @@ -288,12 +421,18 @@ describe('Grove exchange protocol identity', () => { keyUse: 'conformance-testing', } as never).ok, ).toBe(false) - const sourceVector = identityVectors[0] + expect( + deriveConformanceVectorRecordIdentity( + runtimeInput, + 'source-record', + heartRateRecord, + ).ok, + ).toBe(false) expect( deriveConformanceVectorOpaqueIdentifier( runtimeInput, - sourceVector.identityKind, - sourceVector.components as never, + 'writer-record', + vectorOf('writer-record').components as never, ).ok, ).toBe(false) expect( @@ -331,7 +470,10 @@ describe('Grove exchange protocol identity', () => { expect( validateOpaqueIdentityScope({ ...runtimeInput, - systems: { ...runtimeInput.systems, event: badSystem as AbsoluteUri }, + systems: { + ...runtimeInput.systems, + event: badSystem as IdentifierSystem, + }, }).ok, ).toBe(false) } @@ -362,43 +504,52 @@ describe('Grove exchange protocol identity', () => { }) it('rejects unknown identity kinds and every wrong component arity at runtime', () => { - const source = identityVectors.find( - ({ identityKind }) => identityKind === 'source-output', - ) - if (source === undefined) throw new Error('Missing source-output vector.') + const writer = vectorOf('writer-record') expect( deriveOpaqueIdentifier( runtimeScope, - 'unknown-kind' as OpaqueIdentityKind, - source.components as never, + 'unknown-kind' as never, + writer.components as never, ).ok, ).toBe(false) expect( deriveOpaqueIdentifier( runtimeScope, - 'source-output', - source.components.slice(0, -1) as never, + 'writer-record', + writer.components.slice(0, -1) as never, ).ok, ).toBe(false) expect( - deriveOpaqueIdentifier(runtimeScope, 'source-output', [ - ...source.components, + deriveOpaqueIdentifier(runtimeScope, 'writer-record', [ + ...writer.components, 'extra', ] as never).ok, ).toBe(false) }) + it('mints every record kind only through its record identity', () => { + for (const vector of identityVectors) { + if (recordFamilies[vector.identityKind] === undefined) continue + expect( + deriveOpaqueIdentifier( + runtimeScope, + vector.identityKind as never, + vector.components as never, + ), + ).toMatchObject({ + ok: false, + issues: [{ code: 'invalid-code', path: ['identityKind'] }], + }) + } + }) + it('rejects non-string identity components after validating their arity', () => { - const source = identityVectors.find( - ({ identityKind }) => identityKind === 'source-output', - ) - if (source === undefined) throw new Error('Missing source-output vector.') - const components: unknown[] = [...source.components] + const components: unknown[] = [...vectorOf('writer-record').components] components[1] = 42 expect( deriveOpaqueIdentifier( runtimeScope, - 'source-output', + 'writer-record', components as never, ), ).toMatchObject({ @@ -408,103 +559,207 @@ describe('Grove exchange protocol identity', () => { }) it('rejects empty or malformed Unicode only at the typed opaque-identity boundary', () => { - for (const vector of identityVectors) { - for (const invalid of ['', 'prefix\ud800suffix']) { + const record = unwrap( + deriveSourceRecordIdentity(runtimeScope, heartRateRecord), + ) + for (const invalid of ['', 'prefix\ud800suffix']) { + for (const vector of identityVectors) { + if (recordFamilies[vector.identityKind] !== undefined) continue const components: string[] = [...vector.components] components[0] = invalid - const result = deriveOpaqueIdentifier( - runtimeScope, - vector.identityKind, - components as never, - ) - expect(result.ok).toBe(false) - if (!result.ok) { - expect(result.issues[0]?.path).toEqual(['components', 0]) - } + expect( + deriveOpaqueIdentifier( + runtimeScope, + vector.identityKind as never, + components as never, + ), + ).toMatchObject({ ok: false, issues: [{ path: ['components', 0] }] }) } - } - }) - - it('rejects provider coordinates under every generic source identity kind', () => { - const invalidProviderCoordinates = [ - { - kind: 'source-record', - components: [ - 'oura', - 'daily_activity', - 'https://accounts.example.org', - 'patient-001', - 'activity-001', + const refusals = [ + [ + deriveSourceRecordIdentity(runtimeScope, { + ...heartRateRecord, + adapterId: invalid, + }), + ['adapterId'], ], - }, - { - kind: 'source-output', - components: [ - 'withings', - 'getmeas:9+10', - 'https://accounts.example.org', - 'patient-001', - '17348211', - 'blood-pressure-panel', - 'single', + [ + deriveSourceRecordIdentity(runtimeScope, { + ...heartRateRecord, + repositoryScope: { + ...heartRateRecord.repositoryScope, + value: invalid, + }, + }), + ['repositoryScope', 'value'], ], - }, - { - kind: 'source-artifact', - components: [ - 'google-health-api', - 'heart-rate', - 'https://accounts.example.org', - 'patient-001', - 'recording-001', - 'provider-recording', - '0', + [ + deriveProviderRecordIdentity(runtimeScope, { + ...withingsRecord, + nativeRecordId: invalid, + }), + ['nativeRecordId'], ], - }, - ] as const + [record.output({ role: invalid, discriminator: 'single' }), ['role']], + [ + record.output({ role: 'sample', discriminator: invalid }), + ['discriminator'], + ], + [ + record.artifact({ formatCode: invalid, partIndex: part('0') }), + ['formatCode'], + ], + ] as const + for (const [result, path] of refusals) { + expect(result).toMatchObject({ + ok: false, + issues: [{ code: 'invalid-identifier', path }], + }) + } + } + }) - for (const { kind, components } of invalidProviderCoordinates) { + it('admits exactly the named coordinates and a canonical part index', () => { + const record = unwrap( + deriveSourceRecordIdentity(runtimeScope, heartRateRecord), + ) + expect( + deriveSourceRecordIdentity(runtimeScope, { + ...heartRateRecord, + version: '2', + } as never), + ).toMatchObject({ ok: false, issues: [{ code: 'schema-invalid' }] }) + expect( + deriveSourceRecordIdentity(runtimeScope, { + ...heartRateRecord, + repositoryScope: { + ...heartRateRecord.repositoryScope, + system: 'participants' as IdentifierSystem, + }, + }), + ).toMatchObject({ + ok: false, + issues: [{ code: 'invalid-uri', path: ['repositoryScope', 'system'] }], + }) + expect( + record.output({ + role: 'sample', + discriminator: 'single', + index: '0', + } as never), + ).toMatchObject({ ok: false, issues: [{ code: 'schema-invalid' }] }) + expect( + record.artifact({ formatCode: 'native-recording', partIndex: part('7') }) + .ok, + ).toBe(true) + for (const partIndex of ['0', '10']) { + expect(parsePartIndex(partIndex)).toEqual({ ok: true, value: partIndex }) + } + for (const partIndex of ['01', '-1', '1.0', 0]) { + expect(parsePartIndex(partIndex).ok).toBe(false) expect( - deriveProviderOpaqueIdentifier(runtimeScope, kind, components as never), - ).toMatchObject({ - ok: false, - issues: [{ code: 'invalid-code', path: ['components', 0] }], + record.artifact({ + formatCode: 'native-recording', + partIndex: partIndex as never, + }), + ).toMatchObject({ ok: false, issues: [{ path: ['partIndex'] }] }) + } + }) + + it('holds its scope and coordinates privately and serializes only its identifier', () => { + const coordinates = { ...heartRateRecord } + const record = unwrap(deriveSourceRecordIdentity(runtimeScope, coordinates)) + const output = unwrap( + record.output({ role: 'sample', discriminator: 'single' }), + ) + Object.assign(coordinates, { nativeRecordId: 'native-2' }) + expect(Object.isFrozen(record)).toBe(true) + expect(record.output({ role: 'sample', discriminator: 'single' })).toEqual({ + ok: true, + value: output, + }) + expect(JSON.parse(JSON.stringify(record))).toEqual({ + identifier: record.identifier, + }) + for (const printed of [ + JSON.stringify(record), + inspect(record, { depth: null, showHidden: true }), + ]) { + expect(printed).not.toContain(heartRateRecord.nativeRecordId) + expect(printed).not.toContain(heartRateRecord.repositoryScope.value) + expect(printed).not.toContain(runtimeInput.secretBase64Url) + } + }) + + it('keeps provider codes and generic adapters in their own record families', () => { + for (const provider of ['google-health-api', 'oura', 'withings']) { + expect(providerCoordinateIssue('source-record', provider)).toMatchObject({ + code: 'invalid-code', + path: ['adapterId'], }) + expect( + providerCoordinateIssue('provider-record', provider), + ).toBeUndefined() } expect( - providerCoordinateIssue('provider-record', [ - 'not-a-provider', - 'type', - 's', - 'v', - 'id', - ]), - ).toMatchObject({ code: 'invalid-code' }) + providerCoordinateIssue('source-record', 'healthkit'), + ).toBeUndefined() + expect( + deriveProviderRecordIdentity(runtimeScope, { + ...withingsRecord, + providerCode: 'healthkit' as never, + }), + ).toMatchObject({ + ok: false, + issues: [{ code: 'invalid-code', path: ['providerCode'] }], + }) }) it('rejects every shared invalid opaque-identity vector', () => { - expect(invalidIdentityVectors).toHaveLength(4) + expect(invalidIdentityVectors).toHaveLength(6) for (const vector of invalidIdentityVectors) { - const result = - vector.expectedError === 'provider-kind-required' ? - deriveProviderOpaqueIdentifier( - runtimeScope, - vector.identityKind, - vector.components as never, - ) - : deriveOpaqueIdentifier( - runtimeScope, - vector.identityKind, + const [recordKind = 'source-record'] = + recordFamilies[vector.identityKind] ?? [] + if (vector.expectedError === 'provider-kind-required') { + expect( + providerCoordinateIssue(recordKind, vector.components[0]), + ).toMatchObject({ code: 'invalid-code', path: ['adapterId'] }) + } else if (vector.expectedError === 'non-canonical-part-index') { + const names: readonly string[] = + groveExchangeProtocol.opaqueIdentity.identityKinds.find( + ({ kind }) => kind === vector.identityKind, + )?.components ?? [] + const partIndex = names.indexOf('part-index') + expect( + deriveConformanceVectorOpaqueIdentifier( + conformanceScope, + vector.identityKind as never, vector.components as never, - ) - expect(result.ok).toBe(false) - if (!result.ok) { - expect(result.issues[0]?.path).toEqual([ - 'components', - vector.expectedError === 'empty-component' ? - vector.components.indexOf('') - : 0, - ]) + ), + ).toMatchObject({ + ok: false, + issues: [ + { code: 'invalid-identifier', path: ['components', partIndex] }, + ], + }) + expect( + deriveRuntimeIdentity(vector.identityKind, vector.components), + ).toMatchObject({ + ok: false, + issues: [{ code: 'invalid-identifier', path: ['partIndex'] }], + }) + } else { + expect( + deriveRuntimeIdentity(vector.identityKind, vector.components), + ).toMatchObject({ + ok: false, + issues: [ + { + code: 'invalid-identifier', + path: sourceRecordPaths[vector.components.indexOf('')], + }, + ], + }) } } }) diff --git a/packages/fhir/test/export-surface.json b/packages/fhir/test/export-surface.json index 9e39fa46..e266a289 100644 --- a/packages/fhir/test/export-surface.json +++ b/packages/fhir/test/export-surface.json @@ -43,6 +43,7 @@ "deriveEventIdentifier", "deriveOpaqueIdentifier", "deriveOpaqueIdentitySystems", + "deriveSourceRecordIdentity", "encodeBase64Binary", "encodeLengthFramedUtf8", "entryIdentifierName", @@ -78,6 +79,7 @@ "isEventIdentityValue", "isOpaqueIdentityScope", "isOpaqueIdentityValue", + "isProducerDiagnostic", "isReferenceToType", "isSemanticallyEqual", "issues", @@ -109,8 +111,10 @@ "parseExchangeGraph", "parseFhirId", "parseFhirInstant", + "parseIdentifierSystem", "parseKeyEpoch", "parseObservation", + "parsePartIndex", "parsePatientReference", "parsePositiveInteger", "parseProvenance", @@ -195,6 +199,7 @@ "deriveEventIdentifier", "deriveOpaqueIdentifier", "deriveOpaqueIdentitySystems", + "deriveSourceRecordIdentity", "encodeLengthFramedUtf8", "entryIdentifierName", "groveExchangeProtocol", @@ -220,6 +225,7 @@ "buildProviderExchangeGraphs", "buildProviderRecordingGraph", "buildProviderRetractionEvent", + "deriveProviderRecordIdentity", "encodeRecordingBytes", "groveFhirVersion", "groveProviderPackageMetadata", diff --git a/packages/fhir/test/foundation.test.ts b/packages/fhir/test/foundation.test.ts index 2e549af8..1b48c509 100644 --- a/packages/fhir/test/foundation.test.ts +++ b/packages/fhir/test/foundation.test.ts @@ -23,6 +23,7 @@ import { parseDocumentReference, parseFhirId, parseFhirInstant, + parseIdentifierSystem, parseObservation, parsePatientReference, parseProvenance, @@ -30,6 +31,8 @@ import { parseSpecimen, parseSupportedR4Resource, parseUrnUuid, + type AbsoluteUri, + type IdentifierSystem, type R4CollectionBundle, type DocumentReference, type Observation, @@ -405,6 +408,7 @@ describe('validated primitives', () => { [parseCanonical, 'https://example.org/Questionnaire/example|1.2.3'], [parseFhirId, 'repository-assigned.1'], [parseFhirInstant, '2026-08-20T12:00:00-07:00'], + [parseIdentifierSystem, 'https://example.org/identifiers/participants'], [parsePatientReference, 'Patient/example'], [parsePatientReference, 'https://care.example/fhir/Patient/example'], [parseResearchStudyReference, 'ResearchStudy/example'], @@ -419,6 +423,7 @@ describe('validated primitives', () => { it.each([ [parseAbsoluteUri, '/relative'], + [parseIdentifierSystem, '/relative'], [parseCanonical, 42], [parseCanonical, 'https://example.org|'], [parseFhirId, 'invalid/id'], @@ -455,6 +460,20 @@ describe('validated primitives', () => { expect(parser(value).ok).toBe(false) }) + it('brands an identifier system apart from any other absolute URI', () => { + for (const value of [ + 42, + 'https://example.org/identifiers', + 'urn:oid:1.2.3', + 'https://example.org/%ZZ', + 'https://example.org/white space', + ]) { + expect(parseIdentifierSystem(value).ok).toBe(parseAbsoluteUri(value).ok) + } + expectTypeOf().not.toExtend() + expectTypeOf().not.toExtend() + }) + it('rejects an invalid instant comparison before ordering', () => { expect(compareFhirInstants('not-an-instant', 42).ok).toBe(false) }) diff --git a/packages/fhir/test/mobile.test.ts b/packages/fhir/test/mobile.test.ts index 62c1631b..e95a5f14 100644 --- a/packages/fhir/test/mobile.test.ts +++ b/packages/fhir/test/mobile.test.ts @@ -10,12 +10,12 @@ import { readdirSync, readFileSync } from 'node:fs' import { expectTypeOf } from 'expect-type' import { context, + identifierSystem, identityScope, scopeInput, study, subject, unwrap, - uri, } from './provider-test-support.js' import * as mobileContract from '../src/contract/measurement-catalog.generated.js' import { @@ -147,7 +147,7 @@ describe('Mobile exchange entry identity', () => { const vector = mobile.groveExchangeProtocol.testVectors.fullUrls[0] expect( deriveEntryFullUrl({ - system: uri(vector.system), + system: identifierSystem(vector.system), value: vector.value, }), ).toEqual({ @@ -158,7 +158,7 @@ describe('Mobile exchange entry identity', () => { it('retains a complete roled Identifier and optional repository id', () => { const identifier = { - system: uri('https://example.org/identifiers'), + system: identifierSystem('https://example.org/identifiers'), value: 'record-1', role: 'source-output' as const, } @@ -172,7 +172,7 @@ describe('Mobile exchange entry identity', () => { expect(result.ok && result.value.identifier.value).toBe('record-1') expect( createEntryIdentity({ - system: uri('https://example.org/identifiers'), + system: identifierSystem('https://example.org/identifiers'), value: 'record-1', role: 'not-a-role', } as never).ok, @@ -198,7 +198,7 @@ describe('Mobile exchange entry identity', () => { it('rejects an invalid repository id and accepts a valid surrogate pair', () => { const identifier = { - system: uri('https://example.org/identifiers'), + system: identifierSystem('https://example.org/identifiers'), value: 'valid-😀', role: 'source-output' as const, } @@ -211,7 +211,7 @@ describe('Mobile exchange entry identity', () => { it('preserves nonempty whitespace because Identifier.value is lexical data', () => { expect( entryIdentifierName({ - system: uri('https://example.org/identifiers'), + system: identifierSystem('https://example.org/identifiers'), value: ' \t\n ', }).ok, ).toBe(true) @@ -282,11 +282,66 @@ describe('exchange event context', () => { converterRole: undefined, studies: undefined, repositoryIds: undefined, - application: { ...context().application, version: undefined }, + application: { ...context().application, build: undefined }, }) expect(result.ok).toBe(true) if (!result.ok) return - expect(result.value.application).not.toHaveProperty('version') + expect(result.value.application).not.toHaveProperty('build') + }) + + it('requires the application version, a flat protocol revision and a known graph node', () => { + const { protocolUrl, protocolVersion, ...enrollment } = study('a') + const faults: ReadonlyArray< + readonly [unknown, ReadonlyArray] + > = [ + [ + { ...context(), application: { sourceDeviceToken: 'a', name: 'A' } }, + ['application', 'version'], + ], + [ + { + ...context(), + converterRole: { + kind: 'gateway-application', + application: { sourceDeviceToken: 'g', name: 'G' }, + }, + }, + ['converterRole', 'application', 'version'], + ], + [ + { + ...context(), + studies: [ + { + ...enrollment, + protocol: { url: protocolUrl, version: protocolVersion }, + }, + ], + }, + ['studies', 0, 'protocol'], + ], + [ + { ...context(), studies: [{ ...study('a'), protocolVersion: ' ' }] }, + ['studies', 0, 'protocolVersion'], + ], + [ + { ...context(), repositoryIds: { 'writer-host': 'x' } }, + ['repositoryIds', 'writer-host'], + ], + ] + for (const [candidate, path] of faults) { + const result = parseExchangeEventContext(candidate) + expect(result.ok).toBe(false) + if (!result.ok) { + expect(result.issues.map((issue) => issue.path)).toContainEqual(path) + } + } + expect( + parseExchangeEventContext({ + ...context(), + repositoryIds: { writer: 'writer-1' }, + }).ok, + ).toBe(true) }) it('reports deployment faults under schema codes, never registry codes', () => { @@ -314,7 +369,10 @@ describe('exchange event context', () => { ['a missing host', { ...context(), host: undefined }, 'host'], [ 'a blank application name', - { ...context(), application: { sourceDeviceToken: 'a', name: ' ' } }, + { + ...context(), + application: { sourceDeviceToken: 'a', name: ' ', version: '1' }, + }, 'application', ], [ diff --git a/packages/fhir/test/provider-contract.test.ts b/packages/fhir/test/provider-contract.test.ts index c0df1cb7..91eb9212 100644 --- a/packages/fhir/test/provider-contract.test.ts +++ b/packages/fhir/test/provider-contract.test.ts @@ -12,11 +12,11 @@ import { context, dateTime, heartRateMeasurement, + identifierSystem, observationOf, record, start, unwrap, - uri, } from './provider-test-support.js' import { parseExchangeGraph } from '../src/index.js' import { @@ -51,7 +51,10 @@ describe('Exchange entry identity', () => { 'matches the protocol $id vector', (vector) => { expect( - deriveEntryFullUrl({ system: uri(vector.system), value: vector.value }), + deriveEntryFullUrl({ + system: identifierSystem(vector.system), + value: vector.value, + }), ).toEqual({ ok: true, value: vector.fullUrl }) }, ) @@ -59,7 +62,7 @@ describe('Exchange entry identity', () => { it('length framing admits separators without collisions', () => { expect( deriveEntryFullUrl({ - system: uri('https://example.org/a;b'), + system: identifierSystem('https://example.org/a;b'), value: 'x|y', }).ok, ).toBe(true) @@ -105,7 +108,7 @@ describe('Exchange entry identity', () => { }) it('rejects isolated UTF-16 surrogates before UUID derivation', () => { - const system = uri('https://example.org/identifiers') + const system = identifierSystem('https://example.org/identifiers') expect(deriveEntryFullUrl({ system, value: 'invalid-\ud800' }).ok).toBe( false, ) @@ -122,20 +125,20 @@ describe('Exchange entry identity', () => { ).toBe(false) expect( entryIdentifierName({ - system: uri('https://example.org/identifiers'), + system: identifierSystem('https://example.org/identifiers'), value: '', }).ok, ).toBe(false) expect( entryIdentifierName({ - system: uri('https://example.org/identifiers'), + system: identifierSystem('https://example.org/identifiers'), value: ' \t\n ', }).ok, ).toBe(true) expect( createEntryIdentity( { - system: uri('https://example.org/identifiers'), + system: identifierSystem('https://example.org/identifiers'), value: 'record-1', role: 'source-output', }, diff --git a/packages/fhir/test/provider-identity.test.ts b/packages/fhir/test/provider-identity.test.ts index 27b01a42..f434d87e 100644 --- a/packages/fhir/test/provider-identity.test.ts +++ b/packages/fhir/test/provider-identity.test.ts @@ -12,6 +12,7 @@ import { context, dailyEnd, heartRateMeasurement, + identifierSystem, identityScope, observationOf, record, @@ -81,7 +82,7 @@ describe('Provider R4 graph builder', () => { }) it('places an explicitly governed native Identifier on exactly the designated primary Observation', () => { - const nativeSystem = uri( + const nativeSystem = identifierSystem( 'https://example.org/repositories/withings-account-7/heart-rate-records', ) const result = buildProviderExchangeGraph(heartRate, context(), { @@ -123,7 +124,7 @@ describe('Provider R4 graph builder', () => { it('emits a writer version only with its writer identity while allowing an unversioned identity', () => { const writerRecord = { applicationIdentifier: { - system: uri('https://example.org/applications'), + system: identifierSystem('https://example.org/applications'), value: 'writer-app', }, nativeRecordId: 'writer-record-1', @@ -170,7 +171,7 @@ describe('Provider R4 graph builder', () => { }) it('treats one supported grouped catalog mapping as one designated Provider output', () => { - const nativeSystem = uri( + const nativeSystem = identifierSystem( 'https://example.org/repositories/withings-account-7/blood-pressure-records', ) const result = buildProviderExchangeGraph( @@ -248,7 +249,7 @@ describe('Provider R4 graph builder', () => { { nativeIdentifierDisclosure: { kind: 'authorized', - system: uri( + system: identifierSystem( 'https://example.org/repositories/oura-account-7/daily-activity-records', ), }, @@ -269,7 +270,9 @@ describe('Provider R4 graph builder', () => { buildProviderExchangeGraph(heartRate, context(), { nativeIdentifierDisclosure: { kind: 'authorized', - system: uri('https://example.org/repositories/provider-records'), + system: identifierSystem( + 'https://example.org/repositories/provider-records', + ), type: { coding: [ { @@ -530,10 +533,7 @@ describe('Provider R4 graph builder', () => { studies: [ { ...study('remote-1'), - protocol: { - url: 'https://research.example/invalid protocol', - version: '1', - }, + protocolUrl: 'https://research.example/invalid protocol', } as never, ], }), diff --git a/packages/fhir/test/provider-semantics.test.ts b/packages/fhir/test/provider-semantics.test.ts index 4a4a5025..255b5952 100644 --- a/packages/fhir/test/provider-semantics.test.ts +++ b/packages/fhir/test/provider-semantics.test.ts @@ -12,6 +12,7 @@ import { context, dateTime, heartRateMeasurement, + identifierSystem, identityScope, instant, observationOf, @@ -19,9 +20,8 @@ import { resources, start, unwrap, - uri, } from './provider-test-support.js' -import { parseFhirId } from '../src/index.js' +import { parseFhirId, parsePartIndex } from '../src/index.js' import { canonicalizeMobileEffectiveInstant, deriveEntryFullUrl, @@ -32,6 +32,7 @@ import { import { deriveProviderIdentities } from '../src/providers/identity.js' import { buildProviderExchangeGraph, + deriveProviderRecordIdentity, parseNormalizedProviderRecord, providerOutputCoordinates, type NormalizedProviderRecord, @@ -179,7 +180,7 @@ describe('Provider R4 graph builder', () => { const businessIdentifier = entry.extension?.[0]?.valueIdentifier expect( deriveEntryFullUrl({ - system: uri(businessIdentifier?.system ?? ''), + system: identifierSystem(businessIdentifier?.system ?? ''), value: businessIdentifier?.value ?? '', }), ).toEqual({ ok: true, value: entry.fullUrl }) @@ -398,8 +399,8 @@ describe('Provider R4 graph builder', () => { outputs: [ { kind: 'provider-output', - outputRole: 'heart-rate', - outputDiscriminator: 'single', + role: 'heart-rate', + discriminator: 'single', }, ], event: context().event, @@ -421,8 +422,8 @@ describe('Provider R4 graph builder', () => { outputs: [ { kind: 'provider-output', - outputRole: 'heart-rate', - outputDiscriminator: ' ', + role: 'heart-rate', + discriminator: ' ', }, ], }).ok, @@ -472,7 +473,7 @@ describe('Provider R4 graph builder', () => { { kind: 'provider-artifact', formatCode: 'provider-recording', - partIndex: '0', + partIndex: unwrap(parsePartIndex('0')), }, ], } as const @@ -480,7 +481,7 @@ describe('Provider R4 graph builder', () => { expect( deriveProviderIdentities({ ...raw, - outputs: [{ ...raw.outputs[0], partIndex: '01' }], + outputs: [{ ...raw.outputs[0], partIndex: '01' as never }], }).ok, ).toBe(false) expect( @@ -555,8 +556,8 @@ describe('Provider R4 graph builder', () => { outputs: [ { kind: 'provider-output', - outputRole: 'heart-rate', - outputDiscriminator: 'single', + role: 'heart-rate', + discriminator: 'single', }, ], event: context().event, @@ -584,8 +585,8 @@ describe('Provider R4 graph builder', () => { outputs: [ { kind: 'provider-output', - outputRole: 'invented-output', - outputDiscriminator: 'single', + role: 'invented-output', + discriminator: 'single', }, ], }), @@ -599,8 +600,8 @@ describe('Provider R4 graph builder', () => { outputs: [ { kind: 'provider-output', - outputRole: 'heart-rate', - outputDiscriminator: 'invented', + role: 'heart-rate', + discriminator: 'invented', }, ], }).ok, @@ -615,33 +616,30 @@ describe('Provider R4 graph builder', () => { 'blood-pressure', ) expect(coordinates).toEqual({ - outputRole: 'blood-pressure-panel', - outputDiscriminator: 'single', + role: 'blood-pressure-panel', + discriminator: 'single', }) if (coordinates === undefined) return - const expected = deriveProviderIdentities({ - provider: 'withings', - repositoryScope: context().repositoryScope, - sourceType: input.source.sourceType, - sourceNativeId: input.source.sourceNativeId, - outputs: [{ kind: 'provider-output', ...coordinates }], - event: context().event, - scope: identityScope, - }) - const built = buildProviderExchangeGraph(input, context()) - expect(expected.ok && built.ok).toBe(true) - if (!expected.ok || !built.ok) return - const sourceOutput = observationOf(built.value.graph).identifier?.find( + const sourceRecord = unwrap( + deriveProviderRecordIdentity(identityScope, { + providerCode: 'withings', + sourceType: input.source.sourceType, + providerScope: context().repositoryScope, + nativeRecordId: input.source.sourceNativeId, + }), + ) + const expected = unwrap(sourceRecord.output(coordinates)) + const built = unwrap(buildProviderExchangeGraph(input, context())) + const sourceOutput = observationOf(built.graph).identifier?.find( (candidate) => candidate.type?.coding?.some(({ code }) => code === 'source-output'), ) expect(sourceOutput).toMatchObject({ - system: expected.value.outputs[0]?.system, - value: expected.value.outputs[0]?.value, + system: expected.system, + value: expected.value, }) - expect(built.value.identifiers.outputs[0]).toEqual( - expected.value.outputs[0], - ) + expect(built.identifiers.sourceRecord).toEqual(sourceRecord.identifier) + expect(built.identifiers.outputs[0]).toEqual(expected) }) it.each([ @@ -692,7 +690,11 @@ describe('Provider R4 graph builder', () => { { converterRole: { kind: 'gateway-application', - application: { sourceDeviceToken: invalidToken, name: 'Gateway' }, + application: { + sourceDeviceToken: invalidToken, + name: 'Gateway', + version: '1', + }, }, }, ] as const) { diff --git a/packages/fhir/test/provider-test-support.ts b/packages/fhir/test/provider-test-support.ts index be152d75..988ae5db 100644 --- a/packages/fhir/test/provider-test-support.ts +++ b/packages/fhir/test/provider-test-support.ts @@ -10,9 +10,11 @@ import { parseAbsoluteUri, parseEventSequence, parseFhirInstant, + parseIdentifierSystem, parseKeyEpoch, type AbsoluteUri, type FhirInstant, + type IdentifierSystem, type Result, } from '../src/core/index.js' import { @@ -33,6 +35,7 @@ import { import type { ConnectedProvider, NormalizedProviderRecord, + Writer, } from '../src/providers/index.js' import type { ExchangeGraph, Observation } from '../src/r4/index.js' @@ -45,28 +48,32 @@ export const unwrap = (result: Result): T => { export const uri = (value: string): AbsoluteUri => unwrap(parseAbsoluteUri(value)) +export const identifierSystem = (value: string): IdentifierSystem => + unwrap(parseIdentifierSystem(value)) export const instant = (value: string): FhirInstant => unwrap(parseFhirInstant(value)) export const subject: Subject = { kind: 'logical', identifier: { - system: uri('https://example.org/deployments/patient-pseudonyms'), + system: identifierSystem( + 'https://example.org/deployments/patient-pseudonyms', + ), value: 'patient-example', }, } export const study = (value: string): StudyEnrollment => ({ study: { - system: uri('https://example.org/deployments/research-studies'), + system: identifierSystem( + 'https://example.org/deployments/research-studies', + ), value, }, - protocol: { - url: uri(`https://example.org/PlanDefinition/${value}`), - version: '1', - }, + protocolUrl: uri(`https://example.org/PlanDefinition/${value}`), + protocolVersion: '1', enrollment: { - system: uri('https://example.org/deployments/enrollments'), + system: identifierSystem('https://example.org/deployments/enrollments'), value: `${value}-enrollment`, }, }) @@ -103,12 +110,12 @@ export const identityScope: OpaqueIdentityScope = unwrap( ) export const accountScope: BusinessIdentifier = { - system: uri('https://example.org/deployments/provider-accounts'), + system: identifierSystem('https://example.org/deployments/provider-accounts'), value: 'pseudonym-account-001', } export const globalScope: BusinessIdentifier = { - system: uri('https://example.org/provider-key-spaces'), + system: identifierSystem('https://example.org/provider-key-spaces'), value: 'oura-document-uuid-global', } @@ -135,7 +142,7 @@ export const context = ( ...overrides, }) -export const writer = (provider: string): ApplicationDevice => ({ +export const writer = (provider: string): Writer => ({ sourceDeviceToken: `writer-${provider}`, name: provider, }) diff --git a/packages/fhir/test/questionnaire-input-surface.test.ts b/packages/fhir/test/questionnaire-input-surface.test.ts index c08f7b70..c0ca857a 100644 --- a/packages/fhir/test/questionnaire-input-surface.test.ts +++ b/packages/fhir/test/questionnaire-input-surface.test.ts @@ -15,8 +15,8 @@ import { import { buildQuestionnaire, buildQuestionnaireResponse, - parseAbsoluteUri, parseFhirId, + parseIdentifierSystem, parseQuestionnaireResponse, type QuestionnaireInput, type QuestionnaireResponseInput, @@ -134,7 +134,7 @@ describe('Questionnaire R4 builders', () => { type: 'PractitionerRole', identifier: { system: unwrap( - parseAbsoluteUri('https://example.org/practitioner-roles'), + parseIdentifierSystem('https://example.org/practitioner-roles'), ), value: 'role-7', }, @@ -142,7 +142,9 @@ describe('Questionnaire R4 builders', () => { source: { type: 'RelatedPerson', identifier: { - system: unwrap(parseAbsoluteUri('https://example.org/respondents')), + system: unwrap( + parseIdentifierSystem('https://example.org/respondents'), + ), value: 'respondent-9', }, }, diff --git a/packages/fhir/test/questionnaire-test-support.ts b/packages/fhir/test/questionnaire-test-support.ts index d949657a..d90fa979 100644 --- a/packages/fhir/test/questionnaire-test-support.ts +++ b/packages/fhir/test/questionnaire-test-support.ts @@ -12,6 +12,7 @@ import { parseAbsoluteUri, parseCanonical, parseFhirInstant, + parseIdentifierSystem, parsePatientReference, parseSemVer, preflightQuestionnairePair, @@ -177,7 +178,7 @@ export const responseInput = ( ): QuestionnaireResponseInput => ({ questionnaire: questionnaireCanonical, identifier: { - system: unwrap(parseAbsoluteUri('https://example.org/submissions')), + system: unwrap(parseIdentifierSystem('https://example.org/submissions')), value: 'submission-1', }, status: 'completed', diff --git a/packages/fhir/test/readme-walkthrough.test.ts b/packages/fhir/test/readme-walkthrough.test.ts index ada76ae6..9d431e7d 100644 --- a/packages/fhir/test/readme-walkthrough.test.ts +++ b/packages/fhir/test/readme-walkthrough.test.ts @@ -7,7 +7,7 @@ // import { readFileSync } from 'node:fs' -import { hostname, release, type } from 'node:os' +import { machine, release, type } from 'node:os' import typeSurface from './type-surface.json' with { type: 'json' } import * as root from '../src/index.js' import { @@ -25,6 +25,7 @@ import { parseExchangeGraph, parseFhirId, parseFhirInstant, + parseIdentifierSystem, parseKeyEpoch, parseObservation, retractionTargets, @@ -68,6 +69,7 @@ const unwrap = (result: Result): Value => { return result.value } const uri = (value: string) => unwrap(parseAbsoluteUri(value)) +const identifierSystem = (value: string) => unwrap(parseIdentifierSystem(value)) // From your secret store: the HMAC key as base64url, and the UUID generated for this installation. const identitySecret = process.env.GROVE_IDENTITY_SECRET_BASE64URL @@ -94,7 +96,7 @@ const identityScope = unwrap( ) const application: ApplicationDevice = { - sourceDeviceToken: 'org.example.mystudy-server/2.1.0', + sourceDeviceToken: 'org.example.mystudy-server|2.1.0', name: 'MyStudy server', version: '2.1.0', } @@ -118,21 +120,23 @@ const context: ExchangeEventContext = { subject: { kind: 'logical', identifier: { - system: uri('https://mystudy.example.org/fhir/identifiers/participants'), + system: identifierSystem( + 'https://mystudy.example.org/fhir/identifiers/participants', + ), value: participant.pseudonym, }, }, event: unwrap(deriveEventIdentifier(identityScope, sequence)), identityScope, repositoryScope: { - system: uri( + system: identifierSystem( 'https://mystudy.example.org/fhir/identifiers/withings-accounts', ), value: participant.withingsAccountPseudonym, }, application, host: { - sourceDeviceToken: hostname(), + sourceDeviceToken: `${machine()}|${type()} ${release()}`, operatingSystemVersion: `${type()} ${release()}`, }, } @@ -189,15 +193,19 @@ const enrolled: ExchangeEventContext = { studies: [ { study: { - system: uri('https://mystudy.example.org/fhir/identifiers/studies'), + system: identifierSystem( + 'https://mystudy.example.org/fhir/identifiers/studies', + ), value: 'heart-2026', }, - protocol: { - url: uri('https://mystudy.example.org/fhir/PlanDefinition/heart-2026'), - version: '3', - }, + protocolUrl: uri( + 'https://mystudy.example.org/fhir/PlanDefinition/heart-2026', + ), + protocolVersion: '3', enrollment: { - system: uri('https://mystudy.example.org/fhir/identifiers/enrollments'), + system: identifierSystem( + 'https://mystudy.example.org/fhir/identifiers/enrollments', + ), value: 'enrollment-7f3a', }, }, @@ -209,7 +217,7 @@ const enrolled: ExchangeEventContext = { const disclosed = buildProviderExchangeGraph(record, context, { nativeIdentifierDisclosure: { kind: 'authorized', - system: uri( + system: identifierSystem( 'https://mystudy.example.org/fhir/identifiers/withings-measure-groups', ), type: { text: 'Withings measure group id' }, @@ -233,7 +241,7 @@ const relayed: ExchangeEventContext = { converterRole: { kind: 'gateway-application', application: { - sourceDeviceToken: 'org.example.mystudy-app/4.2.0', + sourceDeviceToken: 'org.example.mystudy-app|4.2.0', name: 'MyStudy app', version: '4.2.0', }, diff --git a/packages/fhir/test/recording.test.ts b/packages/fhir/test/recording.test.ts index 364a0792..b86176ce 100644 --- a/packages/fhir/test/recording.test.ts +++ b/packages/fhir/test/recording.test.ts @@ -13,8 +13,10 @@ import { application, context, conversionInstant, + identifierSystem, identityScope, instant, + repositoryScope, unwrap, uri, } from './provider-test-support.js' @@ -22,11 +24,13 @@ import { adapterSourceMarkerClaims } from '../src/contract/measurement-catalog.g import { parseFhirId, parseExchangeGraph, + parsePartIndex, parsePositiveInteger, type Result, } from '../src/index.js' import { buildProviderRecordingGraph, + deriveProviderRecordIdentity, groveRecordingFormatRegistry, healthKitClinicalRecordAdmission, providerRawOutputRoles, @@ -195,6 +199,31 @@ describe('Provider native recording graph', () => { 'source-artifact', ) expect(result.value.identifiers.outputs).toHaveLength(1) + const sourceRecord = unwrap( + deriveProviderRecordIdentity(identityScope, { + providerCode: provider as ConnectedRawProvider, + sourceType, + providerScope: repositoryScope(provider as ConnectedRawProvider), + nativeRecordId: `native-record-${provider}-${sourceType}`, + }), + ) + expect(result.value.identifiers).toMatchObject({ + sourceRecord: sourceRecord.identifier, + outputs: [ + unwrap( + sourceRecord.output({ + role: 'native-recording', + discriminator: 'single', + }), + ), + ], + sourceArtifact: unwrap( + sourceRecord.artifact({ + formatCode: 'provider-recording', + partIndex: unwrap(parsePartIndex('0')), + }), + ), + }) }, ) @@ -214,7 +243,7 @@ describe('Provider native recording graph', () => { const input = rawSource('withings', 'activityIntraday') const writerRecord = { applicationIdentifier: { - system: uri('https://example.org/applications'), + system: identifierSystem('https://example.org/applications'), value: 'writer-app', }, nativeRecordId: 'writer-record-1', @@ -552,7 +581,7 @@ describe('Provider native recording graph', () => { it('places an explicitly governed native Identifier only on the sole recording DocumentReference', () => { const source = rawSource('google-health-api', 'heart-rate') - const nativeSystem = uri( + const nativeSystem = identifierSystem( 'https://example.org/repositories/google-account-4/recordings', ) const result = buildProviderRecordingGraph( diff --git a/packages/fhir/test/retraction.test.ts b/packages/fhir/test/retraction.test.ts index 19894605..8eb3798e 100644 --- a/packages/fhir/test/retraction.test.ts +++ b/packages/fhir/test/retraction.test.ts @@ -10,13 +10,13 @@ import { context, conversionInstant, heartRateMeasurement, + identifierSystem, identityScope, instant, record, scopeInput, study, unwrap, - uri, } from './provider-test-support.js' import { groveExchangeProtocol } from '../src/contract/measurement-catalog.generated.js' import { @@ -265,13 +265,15 @@ describe('Provider source-record retraction', () => { }) it('carries the adapter native record identifier on its target', () => { - const nativeIdentifier = { - system: uri('https://study.example.org/fhir/NamingSystem/native-record'), + const nativeRecordIdentifier = { + system: identifierSystem( + 'https://study.example.org/fhir/NamingSystem/native-record', + ), value: 'record-heart-001', } const retraction = unwrap( buildProviderRetractionEvent( - [{ ...primary, nativeIdentifier }], + [{ ...primary, nativeRecordIdentifier }], context('withings', '101'), active.identifiers.sourceRecord, retractedAt, @@ -279,14 +281,17 @@ describe('Provider source-record retraction', () => { ) expect(provenanceOf(retraction).target[0]?.extension).toEqual([ { url: roleExtension, valueCode: 'primary-output' }, - { url: nativeExtension, valueIdentifier: nativeIdentifier }, + { url: nativeExtension, valueIdentifier: nativeRecordIdentifier }, ]) expect( buildProviderRetractionEvent( [ { ...primary, - nativeIdentifier: { system: primary.identifier.system, value: 'x' }, + nativeRecordIdentifier: { + system: primary.identifier.system, + value: 'x', + }, }, ], context('withings', '101'), @@ -298,10 +303,23 @@ describe('Provider source-record retraction', () => { issues: [ { code: 'value-mismatch', - path: ['targets', 0, 'nativeIdentifier', 'system'], + path: ['targets', 0, 'nativeRecordIdentifier', 'system'], }, ], }) + expect( + buildProviderRetractionEvent( + [{ ...primary, nativeIdentifier: nativeRecordIdentifier } as never], + context('withings', '101'), + active.identifiers.sourceRecord, + retractedAt, + ), + ).toMatchObject({ + ok: false, + issues: [ + { code: 'schema-invalid', path: ['targets', 0, 'nativeIdentifier'] }, + ], + }) }) it('carries a governed source identifier from the primary output into its target', () => { @@ -312,7 +330,7 @@ describe('Provider source-record retraction', () => { { nativeIdentifierDisclosure: { kind: 'authorized', - system: uri( + system: identifierSystem( 'https://example.org/repositories/withings-account-7/heart-rate-records', ), }, @@ -322,7 +340,7 @@ describe('Provider source-record retraction', () => { const target = unwrap(retractionTargets(disclosed.graph)).find( ({ role }) => role === 'primary-output', ) - expect(target?.nativeIdentifier).toEqual({ + expect(target?.nativeRecordIdentifier).toEqual({ system: 'https://example.org/repositories/withings-account-7/heart-rate-records', value: 'native-withings-getmeas:11', @@ -384,7 +402,9 @@ describe('Provider source-record retraction', () => { ...scopeInput.systems, opaque: { ...scopeInput.systems.opaque, - 'provider-output': uri('https://other.example.org/provider-output'), + 'provider-output': identifierSystem( + 'https://other.example.org/provider-output', + ), }, }, }), diff --git a/packages/fhir/test/study-context.test.ts b/packages/fhir/test/study-context.test.ts index 12c270c7..27b44880 100644 --- a/packages/fhir/test/study-context.test.ts +++ b/packages/fhir/test/study-context.test.ts @@ -162,7 +162,7 @@ describe('study context', () => { ) if (planResource?.resourceType !== 'PlanDefinition') throw new Error('No plan.') - expect(planResource.url).toBe(study('a').protocol.url) + expect(planResource.url).toBe(study('a').protocolUrl) expect(planResource.version).toBe('1') const subjectResource = resources(graph).find( (r) => r.resourceType === 'ResearchSubject', diff --git a/packages/fhir/test/type-surface.json b/packages/fhir/test/type-surface.json index 38d1a67c..af93409d 100644 --- a/packages/fhir/test/type-surface.json +++ b/packages/fhir/test/type-surface.json @@ -2,13 +2,14 @@ ".": { "AbsoluteUri": "type AbsoluteUri = Branded<'AbsoluteUri'>;", "annotationTexts": "const annotationTexts: (value: unknown) => readonly string[]", - "ApplicationDevice": "interface ApplicationDevice {\n readonly sourceDeviceToken: string;\n readonly name: string;\n readonly version?: string | undefined;\n readonly build?: string | undefined;\n}", + "ApplicationDevice": "interface ApplicationDevice {\n /**\n * Recommended as `|[|]`, such as `com.example.app|1.2.3`;\n * never emitted in clear.\n */\n readonly sourceDeviceToken: string;\n readonly name: string;\n readonly version: string;\n readonly build?: string | undefined;\n}", "appointmentServiceTypeDisplay": "const appointmentServiceTypeDisplay: (appointment: unknown) => string | undefined", + "ArtifactCoordinates": "interface ArtifactCoordinates {\n readonly formatCode: string;\n readonly partIndex: PartIndex;\n}", "BloodPressureMeasurement": "interface BloodPressureMeasurement {\n readonly kind: 'blood-pressure';\n readonly systolic: number;\n readonly diastolic: number;\n readonly effective: InstantEffectiveTime;\n}", "boundsToFhirPeriod": "const boundsToFhirPeriod: (bounds: PeriodBounds) => {\n readonly start?: string;\n readonly end?: string;\n}", "buildQuestionnaire": "const buildQuestionnaire: (input: QuestionnaireInput) => Result", "buildQuestionnaireResponse": "const buildQuestionnaireResponse: (input: QuestionnaireResponseInput) => Result", - "BusinessIdentifier": "interface BusinessIdentifier {\n readonly system: AbsoluteUri;\n readonly value: string;\n}", + "BusinessIdentifier": "interface BusinessIdentifier {\n readonly system: IdentifierSystem;\n readonly value: string;\n}", "Canonical": "type Canonical = Branded<'Canonical'>;", "canonicalizeMobileEffectiveInstant": "const canonicalizeMobileEffectiveInstant: (value: unknown) => Result", "carePlanCategoryDisplays": "const carePlanCategoryDisplays: (carePlan: unknown) => readonly string[]", @@ -47,13 +48,14 @@ "dateToFhirInstant": "const dateToFhirInstant: (value: Date) => string", "decodeBase64Binary": "const decodeBase64Binary: (value: unknown) => Result", "deepFreeze": "const deepFreeze: (value: T) => ReadonlyDeep", - "DeploymentIdentifierSystems": "interface DeploymentIdentifierSystems {\n readonly opaque: OpaqueIdentitySystems;\n readonly event: AbsoluteUri;\n readonly entryNode: AbsoluteUri;\n}", + "DeploymentIdentifierSystems": "interface DeploymentIdentifierSystems {\n readonly opaque: OpaqueIdentitySystems;\n readonly event: IdentifierSystem;\n readonly entryNode: IdentifierSystem;\n}", "deriveEntryFullUrl": "const deriveEntryFullUrl: (input: BusinessIdentifier) => Result", "deriveEntryNodeIdentifier": "const deriveEntryNodeIdentifier: (scope: OpaqueIdentityScope, key: EntryNodeKey) => Result", "deriveEntryNodeValue": "const deriveEntryNodeValue: (key: EntryNodeKey) => Result", "deriveEventIdentifier": "const deriveEventIdentifier: (scope: OpaqueIdentityScope, sequence: EventSequence) => Result", - "deriveOpaqueIdentifier": "const deriveOpaqueIdentifier: (scope: OpaqueIdentityScope, identityKind: Kind, components: OpaqueIdentityComponents[Kind]) => Result", + "deriveOpaqueIdentifier": "const deriveOpaqueIdentifier: >(scope: OpaqueIdentityScope, identityKind: Kind, components: OpaqueIdentityComponents[Kind]) => Result", "deriveOpaqueIdentitySystems": "const deriveOpaqueIdentitySystems: (root: AbsoluteUri, keyId: string, epoch: KeyEpoch) => Result", + "deriveSourceRecordIdentity": "const deriveSourceRecordIdentity: (scope: OpaqueIdentityScope, record: SourceRecordCoordinates) => Result", "Device": "type Device = ReadonlyDeep>;", "DocumentReference": "type DocumentReference = ReadonlyDeep>;", "encodeBase64Binary": "const encodeBase64Binary: (bytes: Uint8Array) => string", @@ -79,7 +81,7 @@ "fhirPeriodToBounds": "const fhirPeriodToBounds: (period: unknown) => PeriodBounds", "fhirQuantityToValue": "const fhirQuantityToValue: (value: unknown) => Result", "formatHumanName": "const formatHumanName: (name: unknown, options?: HumanNameOptions) => string | undefined", - "GovernedSourceIdentifierDisclosurePolicy": "type GovernedSourceIdentifierDisclosurePolicy = {\n readonly kind: 'omit';\n} | {\n readonly kind: 'authorized';\n readonly system: AbsoluteUri;\n readonly type?: GovernedSourceIdentifierType | undefined;\n};", + "GovernedSourceIdentifierDisclosurePolicy": "type GovernedSourceIdentifierDisclosurePolicy = {\n readonly kind: 'omit';\n} | {\n readonly kind: 'authorized';\n readonly system: IdentifierSystem;\n readonly type?: GovernedSourceIdentifierType | undefined;\n};", "GovernedSourceIdentifierType": "interface GovernedSourceIdentifierType {\n readonly coding?: readonly [\n GovernedSourceIdentifierTypeCoding,\n ...GovernedSourceIdentifierTypeCoding[]\n ] | undefined;\n readonly text?: string | undefined;\n}", "GovernedSourceIdentifierTypeCoding": "interface GovernedSourceIdentifierTypeCoding {\n readonly system: AbsoluteUri;\n readonly code: string;\n readonly display?: string | undefined;\n}", "groveExchangeProtocol": "const groveExchangeProtocol: typeof groveExchangeProtocolValue", @@ -93,7 +95,7 @@ "GroveQuestionnaireResponse": "type GroveQuestionnaireResponse = Readonly & {\n readonly identifier: Identifier;\n readonly questionnaire: string;\n readonly authored: string;\n readonly item?: QuestionnaireResponse['item'];\n}>;", "GroveRecordingFormat": "type GroveRecordingFormat = keyof typeof groveRecordingFormatRegistry.formats;", "groveRecordingFormatRegistry": "const groveRecordingFormatRegistry: typeof groveRecordingFormatRegistryValue", - "HostDevice": "interface HostDevice {\n readonly sourceDeviceToken: string;\n readonly operatingSystemVersion: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", + "HostDevice": "interface HostDevice {\n /**\n * Recommended as `|`, such as `iPhone17,1|26.0`;\n * never emitted in clear.\n */\n readonly sourceDeviceToken: string;\n readonly operatingSystemVersion: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", "HumanNameOptions": "interface HumanNameOptions {\n readonly includePrefix?: boolean;\n readonly includeSuffix?: boolean;\n}", "humanNameParts": "const humanNameParts: (name: unknown, options?: HumanNameOptions) => readonly string[]", "identifierBySystem": "const identifierBySystem: (resource: unknown, system: string) => string | undefined", @@ -101,6 +103,7 @@ "IdentifierParts": "interface IdentifierParts {\n readonly system?: string;\n readonly value?: string;\n readonly use?: string;\n readonly type?: unknown;\n}", "identifiersBySystem": "const identifiersBySystem: (resource: unknown, system: string) => readonly string[]", "identifiersByType": "const identifiersByType: (resource: unknown, system: string, code: string) => readonly string[]", + "IdentifierSystem": "type IdentifierSystem = Branded<'IdentifierSystem'>;", "immunizationExpirationDate": "const immunizationExpirationDate: (immunization: unknown) => Date | undefined", "immunizationIsExpired": "const immunizationIsExpired: (immunization: unknown, asOf?: Date) => boolean | undefined", "immunizationNoteTexts": "const immunizationNoteTexts: (immunization: unknown) => readonly string[]", @@ -119,6 +122,7 @@ "isEventIdentityValue": "const isEventIdentityValue: (value: unknown) => boolean", "isOpaqueIdentityScope": "const isOpaqueIdentityScope: (value: unknown) => value is OpaqueIdentityScope", "isOpaqueIdentityValue": "const isOpaqueIdentityValue: (value: unknown) => boolean", + "isProducerDiagnostic": "const isProducerDiagnostic: (issue: Issue) => issue is ProducerDiagnostic", "isReferenceToType": "const isReferenceToType: (value: unknown, resourceType: string) => boolean", "isSemanticallyEqual": "const isSemanticallyEqual: (left: string | Uint8Array, right: string | Uint8Array) => Result", "Issue": "interface Issue {\n readonly severity: IssueSeverity;\n readonly code: IssueCode;\n readonly path: ReadonlyArray;\n readonly message: string;\n /** Exact normative producer-rule reason when this is a Grove protocol diagnostic. */\n readonly reason?: string;\n /** Stable FHIR-facing producer-rule location, independent of parser internals. */\n readonly location?: string;\n}", @@ -150,7 +154,8 @@ "OpaqueIdentityKind": "type OpaqueIdentityKind = OpaqueIdentityDefinition['kind'];", "OpaqueIdentityScope": "interface OpaqueIdentityScope {\n readonly systems: DeploymentIdentifierSystems;\n readonly keyId: string;\n readonly keyEpoch: KeyEpoch;\n readonly producerInstance: string;\n}", "OpaqueIdentityScopeInput": "interface OpaqueIdentityScopeInput {\n readonly systems: DeploymentIdentifierSystems;\n readonly keyId: string;\n readonly keyEpoch: KeyEpoch;\n /** Canonical unpadded base64url key material containing at least 32 bytes. */\n readonly secretBase64Url: string;\n /** Canonical lowercase RFC 4122 UUID (versions 1 through 5). */\n readonly producerInstance: string;\n}", - "OpaqueIdentitySystems": "type OpaqueIdentitySystems = Readonly>;", + "OpaqueIdentitySystems": "type OpaqueIdentitySystems = Readonly>;", + "OutputCoordinates": "interface OutputCoordinates {\n readonly role: string;\n readonly discriminator: string;\n}", "parseAbsoluteUri": "const parseAbsoluteUri: (value: unknown) => Result", "parseCanonical": "const parseCanonical: (value: unknown) => Result", "parseDevice": "const parseDevice: (input: unknown) => Result", @@ -161,8 +166,10 @@ "parseExchangeGraph": "const parseExchangeGraph: (input: unknown) => Result", "parseFhirId": "const parseFhirId: (value: unknown) => Result", "parseFhirInstant": "const parseFhirInstant: (value: unknown) => Result", + "parseIdentifierSystem": "const parseIdentifierSystem: (value: unknown) => Result", "parseKeyEpoch": "const parseKeyEpoch: (value: unknown) => Result", "parseObservation": "const parseObservation: (input: unknown) => Result", + "parsePartIndex": "const parsePartIndex: (value: unknown) => Result", "parsePatientReference": "const parsePatientReference: (value: unknown) => Result", "parsePositiveInteger": "const parsePositiveInteger: (value: unknown) => Result", "parseProvenance": "const parseProvenance: (input: unknown) => Result", @@ -175,6 +182,7 @@ "parseSpecimen": "const parseSpecimen: (input: unknown) => Result", "parseSupportedR4Resource": "const parseSupportedR4Resource: (input: unknown) => Result", "parseUrnUuid": "const parseUrnUuid: (value: unknown) => Result", + "PartIndex": "type PartIndex = Branded<'PartIndex'>;", "Patient": "type Patient = ReadonlyDeep>;", "patientAgeInYears": "const patientAgeInYears: (patient: unknown, asOf?: Date) => number | undefined", "patientBirthDate": "const patientBirthDate: (patient: unknown) => Date | undefined", @@ -197,6 +205,7 @@ "PlanDefinition": "type PlanDefinition = ReadonlyDeep>;", "PositiveInteger": "type PositiveInteger = number & {\n readonly [numericBrand]: 'PositiveInteger';\n};", "preflightQuestionnairePair": "const preflightQuestionnairePair: (questionnaireInput: unknown, responseInput: unknown, options?: QuestionnairePreflightOptions) => Result", + "ProducerDiagnostic": "interface ProducerDiagnostic extends Issue {\n readonly code: ProducerDiagnosticCode;\n readonly reason: string;\n}", "ProducerDiagnosticCode": "type ProducerDiagnosticCode = keyof typeof groveProducerDiagnostics;", "Provenance": "type Provenance = ReadonlyDeep>;", "QuantityComparator": "type QuantityComparator = '<' | '<=' | '>=' | '>';", @@ -213,6 +222,7 @@ "QuestionnaireResponseSubjectInput": "type QuestionnaireResponseSubjectInput = TypedQuestionnaireResponseReference<'Patient'>;", "R4CollectionBundle": "type R4CollectionBundle = ReadonlyDeep> & {\n readonly type: 'collection';\n}>;", "ReadonlyDeep": "type ReadonlyDeep = T extends string | number | bigint | boolean | symbol | null | undefined ? T : T extends (...arguments_: never[]) => unknown ? T : T extends readonly unknown[] ? Readonly<{\n [Index in keyof T]: ReadonlyDeep;\n}> : T extends object ? {\n readonly [Key in keyof T]: ReadonlyDeep;\n} : T;", + "RecordIdentityKind": "type RecordIdentityKind = 'provider-artifact' | 'provider-output' | 'provider-record' | 'source-artifact' | 'source-output' | 'source-record';", "RecordingDevice": "interface RecordingDevice {\n readonly stableUnitToken: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", "RecordingMethod": "type RecordingMethod = 'actively-recorded' | 'automatically-recorded' | 'manual-entry';", "referenceType": "const referenceType: (value: unknown) => string | undefined", @@ -223,7 +233,7 @@ "ResolvedQuestionnaireValueSetInput": "interface ResolvedQuestionnaireValueSetInput {\n readonly canonical: Canonical;\n readonly concepts: readonly QuestionnaireValueSetConceptInput[];\n}", "Result": "type Result = {\n readonly ok: true;\n readonly value: T;\n /** Non-blocking findings that the caller must still surface or record. */\n readonly warnings?: readonly Issue[];\n} | {\n readonly ok: false;\n readonly issues: readonly Issue[];\n};", "RetractionEvent": "type RetractionEvent = GraphBundle;", - "RetractionTarget": "type RetractionTarget = {\n readonly [Role in RetractionTargetRole]: {\n readonly role: Role;\n readonly resourceType: RetractionTargetResourceType;\n readonly identifier: RoledIdentifier;\n readonly nativeIdentifier?: BusinessIdentifier | undefined;\n };\n}[RetractionTargetRole];", + "RetractionTarget": "type RetractionTarget = {\n readonly [Role in RetractionTargetRole]: {\n readonly role: Role;\n readonly resourceType: RetractionTargetResourceType;\n readonly identifier: RoledIdentifier;\n readonly nativeRecordIdentifier?: BusinessIdentifier | undefined;\n };\n}[RetractionTargetRole];", "RetractionTargetResourceType": "type RetractionTargetResourceType = TargetRoles[Role]['resourceTypes'][number];", "RetractionTargetRole": "type RetractionTargetRole = keyof TargetRoles;", "retractionTargets": "const retractionTargets: (graph: ExchangeGraph) => Result", @@ -242,8 +252,10 @@ "SleepStageMeasurement": "interface SleepStageMeasurement {\n readonly kind: 'sleep-stage';\n readonly stage: SleepStage;\n /** Source-native stage retained when it is more precise than the shared stage. */\n readonly sourceStageCoding?: SleepStageSourceCodingInput | undefined;\n readonly effective: PeriodEffectiveTime;\n}", "SleepStageSourceCodingInput": "interface SleepStageSourceCodingInput {\n readonly system: AbsoluteUri;\n readonly code: string;\n readonly display?: string | undefined;\n}", "slotSpecialtyDisplays": "const slotSpecialtyDisplays: (slot: unknown) => readonly string[]", + "SourceRecordCoordinates": "interface SourceRecordCoordinates {\n readonly adapterId: string;\n readonly sourceType: string;\n readonly repositoryScope: BusinessIdentifier;\n readonly nativeRecordId: string;\n}", + "SourceRecordIdentity": "interface SourceRecordIdentity {\n /** The typed `source-record` identifier. */\n readonly identifier: RoledIdentifier;\n /** Mints the `source-output` identifier of one output this record yields. */\n readonly output: (coordinates: OutputCoordinates) => Result;\n /** Mints the `source-artifact` identifier of one part of a recording this record carries. */\n readonly artifact: (coordinates: ArtifactCoordinates) => Result;\n}", "Specimen": "type Specimen = ReadonlyDeep>;", - "StudyEnrollment": "interface StudyEnrollment {\n readonly study: BusinessIdentifier;\n readonly protocol: {\n readonly url: AbsoluteUri;\n readonly version: string;\n };\n readonly enrollment: BusinessIdentifier;\n}", + "StudyEnrollment": "interface StudyEnrollment {\n readonly study: BusinessIdentifier;\n /** The PlanDefinition's canonical url; with `protocolVersion` it names one exact revision. */\n readonly protocolUrl: AbsoluteUri;\n readonly protocolVersion: string;\n readonly enrollment: BusinessIdentifier;\n}", "Subject": "type Subject = {\n readonly kind: 'logical';\n readonly identifier: BusinessIdentifier;\n} | {\n readonly kind: 'bundled';\n readonly identifier: BusinessIdentifier;\n readonly patient: Patient;\n};", "SupportedR4Resource": "type SupportedR4Resource = R4CollectionBundle | GraphResource;", "toFhirReference": "const toFhirReference: (resourceType: string, id: string) => {\n readonly reference: string;\n readonly type: string;\n}", @@ -306,7 +318,7 @@ "resolvableReferenceSchema": "const resolvableReferenceSchema: z.ZodType", "RetractionEvent": "type RetractionEvent = GraphBundle;", "retractionEventSchema": "const retractionEventSchema: z.ZodType", - "RetractionTarget": "type RetractionTarget = {\n readonly [Role in RetractionTargetRole]: {\n readonly role: Role;\n readonly resourceType: RetractionTargetResourceType;\n readonly identifier: RoledIdentifier;\n readonly nativeIdentifier?: BusinessIdentifier | undefined;\n };\n}[RetractionTargetRole];", + "RetractionTarget": "type RetractionTarget = {\n readonly [Role in RetractionTargetRole]: {\n readonly role: Role;\n readonly resourceType: RetractionTargetResourceType;\n readonly identifier: RoledIdentifier;\n readonly nativeRecordIdentifier?: BusinessIdentifier | undefined;\n };\n}[RetractionTargetRole];", "RetractionTargetResourceType": "type RetractionTargetResourceType = TargetRoles[Role]['resourceTypes'][number];", "RetractionTargetRole": "type RetractionTargetRole = keyof TargetRoles;", "retractionTargets": "const retractionTargets: (graph: ExchangeGraph) => Result", @@ -318,22 +330,24 @@ "supportedR4ResourceSchema": "const supportedR4ResourceSchema: z.ZodType" }, "./mobile": { - "ApplicationDevice": "interface ApplicationDevice {\n readonly sourceDeviceToken: string;\n readonly name: string;\n readonly version?: string | undefined;\n readonly build?: string | undefined;\n}", + "ApplicationDevice": "interface ApplicationDevice {\n /**\n * Recommended as `|[|]`, such as `com.example.app|1.2.3`;\n * never emitted in clear.\n */\n readonly sourceDeviceToken: string;\n readonly name: string;\n readonly version: string;\n readonly build?: string | undefined;\n}", + "ArtifactCoordinates": "interface ArtifactCoordinates {\n readonly formatCode: string;\n readonly partIndex: PartIndex;\n}", "BloodPressureMeasurement": "interface BloodPressureMeasurement {\n readonly kind: 'blood-pressure';\n readonly systolic: number;\n readonly diastolic: number;\n readonly effective: InstantEffectiveTime;\n}", - "BusinessIdentifier": "interface BusinessIdentifier {\n readonly system: AbsoluteUri;\n readonly value: string;\n}", + "BusinessIdentifier": "interface BusinessIdentifier {\n readonly system: IdentifierSystem;\n readonly value: string;\n}", "canonicalizeMobileEffectiveInstant": "const canonicalizeMobileEffectiveInstant: (value: unknown) => Result", "ChoiceQuantityMeasurement": "type ChoiceQuantityMeasurement = Extract;", "ChoiceQuantityMeasurementKind": "type ChoiceQuantityMeasurementKind = MeasurementKindsWhere<'quantity', 'dateTime-or-Period'>;", "ConversionBatch": "interface ConversionBatch {\n readonly conversions: readonly Conversion[];\n readonly failures: readonly Failure[];\n}", "ConverterRole": "type ConverterRole = {\n readonly kind: 'assembler';\n} | {\n readonly kind: 'gateway';\n} | {\n readonly kind: 'gateway-application';\n readonly application: ApplicationDevice;\n};", "createEntryIdentity": "const createEntryIdentity: (identifier: RoledIdentifier, id?: FhirId) => Result", - "DeploymentIdentifierSystems": "interface DeploymentIdentifierSystems {\n readonly opaque: OpaqueIdentitySystems;\n readonly event: AbsoluteUri;\n readonly entryNode: AbsoluteUri;\n}", + "DeploymentIdentifierSystems": "interface DeploymentIdentifierSystems {\n readonly opaque: OpaqueIdentitySystems;\n readonly event: IdentifierSystem;\n readonly entryNode: IdentifierSystem;\n}", "deriveEntryFullUrl": "const deriveEntryFullUrl: (input: BusinessIdentifier) => Result", "deriveEntryNodeIdentifier": "const deriveEntryNodeIdentifier: (scope: OpaqueIdentityScope, key: EntryNodeKey) => Result", "deriveEntryNodeValue": "const deriveEntryNodeValue: (key: EntryNodeKey) => Result", "deriveEventIdentifier": "const deriveEventIdentifier: (scope: OpaqueIdentityScope, sequence: EventSequence) => Result", - "deriveOpaqueIdentifier": "const deriveOpaqueIdentifier: (scope: OpaqueIdentityScope, identityKind: Kind, components: OpaqueIdentityComponents[Kind]) => Result", + "deriveOpaqueIdentifier": "const deriveOpaqueIdentifier: >(scope: OpaqueIdentityScope, identityKind: Kind, components: OpaqueIdentityComponents[Kind]) => Result", "deriveOpaqueIdentitySystems": "const deriveOpaqueIdentitySystems: (root: AbsoluteUri, keyId: string, epoch: KeyEpoch) => Result", + "deriveSourceRecordIdentity": "const deriveSourceRecordIdentity: (scope: OpaqueIdentityScope, record: SourceRecordCoordinates) => Result", "encodeLengthFramedUtf8": "const encodeLengthFramedUtf8: (components: readonly string[]) => Result", "entryIdentifierName": "const entryIdentifierName: (input: BusinessIdentifier) => Result", "EntryIdentity": "interface EntryIdentity {\n readonly identifier: RoledIdentifier;\n readonly fullUrl: UrnUuid;\n readonly id?: FhirId;\n}", @@ -343,7 +357,7 @@ "ExchangeEventIdentifier": "type ExchangeEventIdentifier = RoledIdentifier & {\n readonly role: 'event';\n};", "ExchangeGraphIdentifiers": "interface ExchangeGraphIdentifiers {\n readonly event: ExchangeEventIdentifier;\n readonly sourceRecord: RoledIdentifier;\n /** Every output identity in Bundle order; a provider record has no child outputs. */\n readonly outputs: readonly [RoledIdentifier, ...RoledIdentifier[]];\n readonly provenance: RoledIdentifier;\n readonly applicationSnapshot: RoledIdentifier;\n readonly hostSnapshot: RoledIdentifier;\n readonly writerSnapshot: RoledIdentifier;\n readonly sourceArtifact?: RoledIdentifier;\n readonly recordingDevice?: RoledIdentifier;\n readonly recordingDeviceSnapshot?: RoledIdentifier;\n readonly gatewayApplicationSnapshot?: RoledIdentifier;\n readonly writerRecord?: RoledIdentifier;\n}", "ExchangeGraphNode": "type ExchangeGraphNode = 'bundle' | 'primary-output' | 'source-artifact' | 'recording-device' | 'application-device' | 'host-device' | 'writer' | 'provenance';", - "GovernedSourceIdentifierDisclosurePolicy": "type GovernedSourceIdentifierDisclosurePolicy = {\n readonly kind: 'omit';\n} | {\n readonly kind: 'authorized';\n readonly system: AbsoluteUri;\n readonly type?: GovernedSourceIdentifierType | undefined;\n};", + "GovernedSourceIdentifierDisclosurePolicy": "type GovernedSourceIdentifierDisclosurePolicy = {\n readonly kind: 'omit';\n} | {\n readonly kind: 'authorized';\n readonly system: IdentifierSystem;\n readonly type?: GovernedSourceIdentifierType | undefined;\n};", "GovernedSourceIdentifierType": "interface GovernedSourceIdentifierType {\n readonly coding?: readonly [\n GovernedSourceIdentifierTypeCoding,\n ...GovernedSourceIdentifierTypeCoding[]\n ] | undefined;\n readonly text?: string | undefined;\n}", "GovernedSourceIdentifierTypeCoding": "interface GovernedSourceIdentifierTypeCoding {\n readonly system: AbsoluteUri;\n readonly code: string;\n readonly display?: string | undefined;\n}", "groveExchangeProtocol": "const groveExchangeProtocol: typeof groveExchangeProtocolValue", @@ -355,7 +369,7 @@ "groveProfileClaims": "const groveProfileClaims: typeof groveProfileClaimsValue", "GroveRecordingFormat": "type GroveRecordingFormat = keyof typeof groveRecordingFormatRegistry.formats;", "groveRecordingFormatRegistry": "const groveRecordingFormatRegistry: typeof groveRecordingFormatRegistryValue", - "HostDevice": "interface HostDevice {\n readonly sourceDeviceToken: string;\n readonly operatingSystemVersion: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", + "HostDevice": "interface HostDevice {\n /**\n * Recommended as `|`, such as `iPhone17,1|26.0`;\n * never emitted in clear.\n */\n readonly sourceDeviceToken: string;\n readonly operatingSystemVersion: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", "InstantCodedMeasurement": "type InstantCodedMeasurement = {\n readonly [Kind in InstantCodedMeasurementKind]: {\n readonly kind: Kind;\n /** Shared code drawn from the catalog's closed allowed-value set. */\n readonly value: SharedCatalog[Kind]['allowedValues'][number];\n readonly effective: InstantEffectiveTime;\n };\n}[InstantCodedMeasurementKind];", "InstantCodedMeasurementKind": "type InstantCodedMeasurementKind = MeasurementKindsWhere<'codeableConcept', 'dateTime'>;", "InstantEffectiveTime": "interface InstantEffectiveTime {\n readonly kind: 'date-time';\n readonly value: FhirInstant;\n}", @@ -372,7 +386,8 @@ "OpaqueIdentityKind": "type OpaqueIdentityKind = OpaqueIdentityDefinition['kind'];", "OpaqueIdentityScope": "interface OpaqueIdentityScope {\n readonly systems: DeploymentIdentifierSystems;\n readonly keyId: string;\n readonly keyEpoch: KeyEpoch;\n readonly producerInstance: string;\n}", "OpaqueIdentityScopeInput": "interface OpaqueIdentityScopeInput {\n readonly systems: DeploymentIdentifierSystems;\n readonly keyId: string;\n readonly keyEpoch: KeyEpoch;\n /** Canonical unpadded base64url key material containing at least 32 bytes. */\n readonly secretBase64Url: string;\n /** Canonical lowercase RFC 4122 UUID (versions 1 through 5). */\n readonly producerInstance: string;\n}", - "OpaqueIdentitySystems": "type OpaqueIdentitySystems = Readonly>;", + "OpaqueIdentitySystems": "type OpaqueIdentitySystems = Readonly>;", + "OutputCoordinates": "interface OutputCoordinates {\n readonly role: string;\n readonly discriminator: string;\n}", "parseExchangeEventContext": "const parseExchangeEventContext: (input: unknown) => Result", "PeriodCodedMeasurement": "type PeriodCodedMeasurement = {\n readonly [Kind in PeriodCodedMeasurementKind]: {\n readonly kind: Kind;\n /** Shared code drawn from the catalog's closed allowed-value set. */\n readonly value: SharedCatalog[Kind]['allowedValues'][number];\n readonly effective: PeriodEffectiveTime;\n };\n}[PeriodCodedMeasurementKind];", "PeriodCodedMeasurementKind": "type PeriodCodedMeasurementKind = Exclude, 'sleep-stage'>;", @@ -381,6 +396,7 @@ "PeriodQuantityMeasurementKind": "type PeriodQuantityMeasurementKind = MeasurementKindsWhere<'quantity', 'Period' | 'dateTime-or-Period'>;", "ProducerDiagnosticCode": "type ProducerDiagnosticCode = keyof typeof groveProducerDiagnostics;", "ProviderRecordingFormat": "type ProviderRecordingFormat = Extract;", + "RecordIdentityKind": "type RecordIdentityKind = 'provider-artifact' | 'provider-output' | 'provider-record' | 'source-artifact' | 'source-output' | 'source-record';", "RecordingDevice": "interface RecordingDevice {\n readonly stableUnitToken: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", "RecordingMethod": "type RecordingMethod = 'actively-recorded' | 'automatically-recorded' | 'manual-entry';", "ResolvedExchangeEventContext": "interface ResolvedExchangeEventContext extends ExchangeEventContext {\n readonly conversionInstant: FhirInstant;\n readonly converterRole: ConverterRole;\n readonly studies: readonly StudyEnrollment[];\n}", @@ -390,14 +406,17 @@ "SleepStage": "type SleepStage = SharedCatalog['sleep-stage']['allowedValues'][number];", "SleepStageMeasurement": "interface SleepStageMeasurement {\n readonly kind: 'sleep-stage';\n readonly stage: SleepStage;\n /** Source-native stage retained when it is more precise than the shared stage. */\n readonly sourceStageCoding?: SleepStageSourceCodingInput | undefined;\n readonly effective: PeriodEffectiveTime;\n}", "SleepStageSourceCodingInput": "interface SleepStageSourceCodingInput {\n readonly system: AbsoluteUri;\n readonly code: string;\n readonly display?: string | undefined;\n}", - "StudyEnrollment": "interface StudyEnrollment {\n readonly study: BusinessIdentifier;\n readonly protocol: {\n readonly url: AbsoluteUri;\n readonly version: string;\n };\n readonly enrollment: BusinessIdentifier;\n}", + "SourceRecordCoordinates": "interface SourceRecordCoordinates {\n readonly adapterId: string;\n readonly sourceType: string;\n readonly repositoryScope: BusinessIdentifier;\n readonly nativeRecordId: string;\n}", + "SourceRecordIdentity": "interface SourceRecordIdentity {\n /** The typed `source-record` identifier. */\n readonly identifier: RoledIdentifier;\n /** Mints the `source-output` identifier of one output this record yields. */\n readonly output: (coordinates: OutputCoordinates) => Result;\n /** Mints the `source-artifact` identifier of one part of a recording this record carries. */\n readonly artifact: (coordinates: ArtifactCoordinates) => Result;\n}", + "StudyEnrollment": "interface StudyEnrollment {\n readonly study: BusinessIdentifier;\n /** The PlanDefinition's canonical url; with `protocolVersion` it names one exact revision. */\n readonly protocolUrl: AbsoluteUri;\n readonly protocolVersion: string;\n readonly enrollment: BusinessIdentifier;\n}", "Subject": "type Subject = {\n readonly kind: 'logical';\n readonly identifier: BusinessIdentifier;\n} | {\n readonly kind: 'bundled';\n readonly identifier: BusinessIdentifier;\n readonly patient: Patient;\n};", "validateOpaqueIdentityScope": "const validateOpaqueIdentityScope: (input: OpaqueIdentityScopeInput) => Result" }, "./providers": { "adapterMeasurementCatalog": "const adapterMeasurementCatalog: typeof adapterMeasurementCatalogValue", "AdapterMeasurementCatalog": "type AdapterMeasurementCatalog = typeof adapterMeasurementCatalog;", - "ApplicationDevice": "interface ApplicationDevice {\n readonly sourceDeviceToken: string;\n readonly name: string;\n readonly version?: string | undefined;\n readonly build?: string | undefined;\n}", + "ApplicationDevice": "interface ApplicationDevice {\n /**\n * Recommended as `|[|]`, such as `com.example.app|1.2.3`;\n * never emitted in clear.\n */\n readonly sourceDeviceToken: string;\n readonly name: string;\n readonly version: string;\n readonly build?: string | undefined;\n}", + "ArtifactCoordinates": "interface ArtifactCoordinates {\n readonly formatCode: string;\n readonly partIndex: PartIndex;\n}", "buildProviderExchangeGraph": "const buildProviderExchangeGraph: (record: NormalizedProviderRecord, context: ExchangeEventContext, options?: ProviderConversionOptions) => Result", "buildProviderExchangeGraphs": "const buildProviderExchangeGraphs: (records: readonly NormalizedProviderRecord[], context: (record: NormalizedProviderRecord) => ExchangeEventContext, options?: ProviderConversionOptions) => ConversionBatch", "buildProviderRecordingGraph": "const buildProviderRecordingGraph: (source: ProviderRecordingSource, attachment: ProviderRecordingAttachment, context: ExchangeEventContext, options?: ProviderConversionOptions) => Result", @@ -413,13 +432,14 @@ "ConnectedSourceType": "type ConnectedSourceType = keyof ScalarOutputRoles[Provider] & string;", "ConversionBatch": "interface ConversionBatch {\n readonly conversions: readonly Conversion[];\n readonly failures: readonly Failure[];\n}", "ConverterRole": "type ConverterRole = {\n readonly kind: 'assembler';\n} | {\n readonly kind: 'gateway';\n} | {\n readonly kind: 'gateway-application';\n readonly application: ApplicationDevice;\n};", + "deriveProviderRecordIdentity": "const deriveProviderRecordIdentity: (scope: OpaqueIdentityScope, record: ProviderRecordCoordinates) => Result", "DeviceSnapshotRole": "type DeviceSnapshotRole = 'application' | 'host' | 'recording-device';", "EmbeddedRecordingAttachment": "interface EmbeddedRecordingAttachment extends RecordingAttachmentBase {\n readonly kind: 'embedded';\n /** Exact caller-supplied bytes represented as canonical RFC 4648 base64. */\n readonly dataBase64: CanonicalBase64;\n}", "encodeRecordingBytes": "const encodeRecordingBytes: (bytes: Uint8Array) => Result", "ExchangeEventContext": "interface ExchangeEventContext {\n readonly subject: Subject;\n readonly event: ExchangeEventIdentifier;\n readonly identityScope: OpaqueIdentityScope;\n readonly repositoryScope: BusinessIdentifier;\n readonly application: ApplicationDevice;\n /** Required here: a server states its own facts, where a phone reads them from the OS. */\n readonly host: HostDevice;\n /** Defaults to the instant the builder runs. */\n readonly conversionInstant?: FhirInstant | undefined;\n readonly converterRole?: ConverterRole | undefined;\n readonly studies?: readonly StudyEnrollment[] | undefined;\n readonly repositoryIds?: Readonly>> | undefined;\n}", "ExchangeGraphIdentifiers": "interface ExchangeGraphIdentifiers {\n readonly event: ExchangeEventIdentifier;\n readonly sourceRecord: RoledIdentifier;\n /** Every output identity in Bundle order; a provider record has no child outputs. */\n readonly outputs: readonly [RoledIdentifier, ...RoledIdentifier[]];\n readonly provenance: RoledIdentifier;\n readonly applicationSnapshot: RoledIdentifier;\n readonly hostSnapshot: RoledIdentifier;\n readonly writerSnapshot: RoledIdentifier;\n readonly sourceArtifact?: RoledIdentifier;\n readonly recordingDevice?: RoledIdentifier;\n readonly recordingDeviceSnapshot?: RoledIdentifier;\n readonly gatewayApplicationSnapshot?: RoledIdentifier;\n readonly writerRecord?: RoledIdentifier;\n}", "ExternalRecordingAttachment": "interface ExternalRecordingAttachment extends RecordingAttachmentBase {\n readonly kind: 'external';\n readonly url: ImmutableRecordingUrl;\n readonly size: number;\n readonly hash: Sha1Base64;\n readonly immutabilityAssurance: 'immutable-version-specific';\n}", - "GovernedSourceIdentifierDisclosurePolicy": "type GovernedSourceIdentifierDisclosurePolicy = {\n readonly kind: 'omit';\n} | {\n readonly kind: 'authorized';\n readonly system: AbsoluteUri;\n readonly type?: GovernedSourceIdentifierType | undefined;\n};", + "GovernedSourceIdentifierDisclosurePolicy": "type GovernedSourceIdentifierDisclosurePolicy = {\n readonly kind: 'omit';\n} | {\n readonly kind: 'authorized';\n readonly system: IdentifierSystem;\n readonly type?: GovernedSourceIdentifierType | undefined;\n};", "groveFhirVersion": "const groveFhirVersion: \"4.0.1\"", "groveProviderPackageMetadata": "const groveProviderPackageMetadata: typeof groveProviderPackageMetadataValue", "groveRecordingFormatRegistry": "const groveRecordingFormatRegistry: typeof groveRecordingFormatRegistryValue", @@ -427,12 +447,13 @@ "HealthKitApplicationDeviceIdentity": "type HealthKitApplicationDeviceIdentity = typeof healthKitApplicationDeviceIdentity;", "healthKitClinicalRecordAdmission": "const healthKitClinicalRecordAdmission: typeof healthKitClinicalRecordAdmissionValue", "HealthKitClinicalRecordAdmission": "type HealthKitClinicalRecordAdmission = typeof healthKitClinicalRecordAdmission;", - "HostDevice": "interface HostDevice {\n readonly sourceDeviceToken: string;\n readonly operatingSystemVersion: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", + "HostDevice": "interface HostDevice {\n /**\n * Recommended as `|`, such as `iPhone17,1|26.0`;\n * never emitted in clear.\n */\n readonly sourceDeviceToken: string;\n readonly operatingSystemVersion: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", "ImmutableRecordingUrl": "type ImmutableRecordingUrl = Branded<'ImmutableRecordingUrl'>;", "MediaType": "type MediaType = Branded<'MediaType'>;", "MobileMeasurement": "type MobileMeasurement = BloodPressureMeasurement | InstantCodedMeasurement | InstantQuantityMeasurement | PeriodCodedMeasurement | PeriodQuantityMeasurement | SleepStageMeasurement;", "NormalizedProviderRecord": "type NormalizedProviderRecord = {\n readonly [Provider in ConnectedProvider]: ConnectedProviderRecordFor;\n}[ConnectedProvider];", "NormalizedSourceRecord": "type NormalizedSourceRecord = NormalizedProviderRecord['source'];", + "OutputCoordinates": "interface OutputCoordinates {\n readonly role: string;\n readonly discriminator: string;\n}", "parseCanonicalBase64": "const parseCanonicalBase64: (value: unknown) => Result", "parseImmutableRecordingUrl": "const parseImmutableRecordingUrl: (value: unknown) => Result", "parseMediaType": "const parseMediaType: (value: unknown) => Result", @@ -448,32 +469,34 @@ "ProviderConversionOptions": "interface ProviderConversionOptions {\n readonly nativeIdentifierDisclosure?: GovernedSourceIdentifierDisclosurePolicy | undefined;\n}", "ProviderExclusiveMeasurement": "type ProviderExclusiveMeasurement = ProviderExclusiveMeasurementKind> = Kind extends ProviderExclusiveMeasurementKind ? {\n readonly kind: Kind;\n readonly effective: ExclusiveEffective>;\n} & ExclusiveResult> : never;", "ProviderExclusiveMeasurementKind": "type ProviderExclusiveMeasurementKind = keyof ExclusiveCatalogFor & string;", - "providerOutputCoordinates": "const providerOutputCoordinates: (provider: ConnectedProvider, sourceType: string, kind: string) => ProviderOutputCoordinates | undefined", - "ProviderOutputCoordinates": "interface ProviderOutputCoordinates {\n readonly outputRole: string;\n readonly outputDiscriminator: string;\n}", + "providerOutputCoordinates": "const providerOutputCoordinates: (provider: ConnectedProvider, sourceType: string, kind: string) => OutputCoordinates | undefined", "providerOutputRole": "const providerOutputRole: (provider: ConnectedProvider, sourceType: string, kind: string) => string | undefined", "providerRawOutputDiscriminators": "const providerRawOutputDiscriminators: typeof providerRawOutputDiscriminatorsValue", "ProviderRawOutputDiscriminators": "type ProviderRawOutputDiscriminators = typeof providerRawOutputDiscriminators;", "providerRawOutputRoles": "const providerRawOutputRoles: typeof providerRawOutputRolesValue", "ProviderRawOutputRoles": "type ProviderRawOutputRoles = typeof providerRawOutputRoles;", + "ProviderRecordCoordinates": "interface ProviderRecordCoordinates {\n readonly providerCode: ConnectedProvider;\n readonly sourceType: string;\n readonly providerScope: BusinessIdentifier;\n readonly nativeRecordId: string;\n}", "providerRecordEffectiveRules": "const providerRecordEffectiveRules: typeof providerRecordEffectiveRulesValue", "ProviderRecordEffectiveRules": "type ProviderRecordEffectiveRules = typeof providerRecordEffectiveRules;", + "ProviderRecordIdentity": "interface ProviderRecordIdentity {\n /** The `provider-record` identifier, typed in the `source-record` role. */\n readonly identifier: RoledIdentifier;\n /** Mints the `provider-output` identifier of one output this record yields. */\n readonly output: (coordinates: OutputCoordinates) => Result;\n /** Mints the `provider-artifact` identifier of one part of a recording this record carries. */\n readonly artifact: (coordinates: ArtifactCoordinates) => Result;\n}", "ProviderRecordingAttachment": "type ProviderRecordingAttachment = EmbeddedRecordingAttachment | ExternalRecordingAttachment;", "ProviderRecordingConversion": "interface ProviderRecordingConversion {\n readonly source: ProviderRecordingSource;\n readonly identifiers: ExchangeGraphIdentifiers;\n readonly graph: ExchangeGraph;\n readonly warnings: readonly Issue[];\n}", "ProviderRecordingFormat": "type ProviderRecordingFormat = import('../contract/providers.generated.js').ProviderRecordingFormat;", "ProviderRecordingSource": "type ProviderRecordingSource = {\n readonly [Provider in ConnectedRawProvider]: {\n readonly [SourceType in ConnectedRawSourceType]: ProviderRecordingSourceRecord;\n }[ConnectedRawSourceType];\n}[ConnectedRawProvider];", - "ProviderRecordingSourceRecord": "interface ProviderRecordingSourceRecord> {\n readonly adapter: ProviderAdapter;\n readonly sourceType: SourceType;\n /** Opaque-identity digest input, disclosed only through the conversion options. */\n readonly sourceNativeId: string;\n /** The application that entered the already-obtained record at the provider. */\n readonly writer: ApplicationDevice;\n /** The source activity time the recording covers. */\n readonly effective: InstantEffectiveTime | PeriodEffectiveTime;\n readonly writerRecord?: WriterRecord | undefined;\n}", + "ProviderRecordingSourceRecord": "interface ProviderRecordingSourceRecord> {\n readonly adapter: ProviderAdapter;\n readonly sourceType: SourceType;\n /** Opaque-identity digest input, disclosed only through the conversion options. */\n readonly sourceNativeId: string;\n /** The application that entered the already-obtained record at the provider. */\n readonly writer: Writer;\n /** The source activity time the recording covers. */\n readonly effective: InstantEffectiveTime | PeriodEffectiveTime;\n readonly writerRecord?: WriterRecord | undefined;\n}", "providerScalarOutputDiscriminators": "const providerScalarOutputDiscriminators: typeof providerScalarOutputDiscriminatorsValue", "ProviderScalarOutputDiscriminators": "type ProviderScalarOutputDiscriminators = typeof providerScalarOutputDiscriminators;", "providerScalarOutputRoles": "const providerScalarOutputRoles: typeof providerScalarOutputRolesValue", "ProviderScalarOutputRoles": "type ProviderScalarOutputRoles = typeof providerScalarOutputRoles;", - "ProviderSourceRecord": "interface ProviderSourceRecord> extends NormalizedSourceRecordBase {\n readonly adapter: ProviderAdapter;\n readonly sourceType: SourceType;\n readonly sourceNativeId: string;\n /** The application that entered the record at the connected provider. */\n readonly writer: ApplicationDevice;\n}", + "ProviderSourceRecord": "interface ProviderSourceRecord> extends NormalizedSourceRecordBase {\n readonly adapter: ProviderAdapter;\n readonly sourceType: SourceType;\n readonly sourceNativeId: string;\n /** The application that entered the record at the connected provider. */\n readonly writer: Writer;\n}", "RawPayloadAdmissionAssertion": "type RawPayloadAdmissionAssertion = (typeof import('../contract/providers.generated.js').providerAdapterCatalog)['rawPayloadAdmission']['allowedAssertions'][number];", "RecordingDevice": "interface RecordingDevice {\n readonly stableUnitToken: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", "RecordingMethod": "type RecordingMethod = 'actively-recorded' | 'automatically-recorded' | 'manual-entry';", "Sha1Base64": "type Sha1Base64 = Branded<'Sha1Base64'>;", - "StudyEnrollment": "interface StudyEnrollment {\n readonly study: BusinessIdentifier;\n readonly protocol: {\n readonly url: AbsoluteUri;\n readonly version: string;\n };\n readonly enrollment: BusinessIdentifier;\n}", + "StudyEnrollment": "interface StudyEnrollment {\n readonly study: BusinessIdentifier;\n /** The PlanDefinition's canonical url; with `protocolVersion` it names one exact revision. */\n readonly protocolUrl: AbsoluteUri;\n readonly protocolVersion: string;\n readonly enrollment: BusinessIdentifier;\n}", "Subject": "type Subject = {\n readonly kind: 'logical';\n readonly identifier: BusinessIdentifier;\n} | {\n readonly kind: 'bundled';\n readonly identifier: BusinessIdentifier;\n readonly patient: Patient;\n};", "SupportedConnectedProviderMeasurementKind": "type SupportedConnectedProviderMeasurementKind = ConnectedSourceType> = keyof ScalarOutputRoles[Provider][SourceType] & string;", + "Writer": "interface Writer {\n /** A stable token naming the writer; never emitted in clear. */\n readonly sourceDeviceToken: string;\n readonly name: string;\n readonly version?: string | undefined;\n readonly build?: string | undefined;\n}", "WriterRecord": "interface WriterRecord {\n readonly applicationIdentifier: BusinessIdentifier;\n readonly nativeRecordId: string;\n /** Canonical non-negative writer revision, when the source exposes one. */\n readonly version?: string | undefined;\n}" }, "./questionnaire": { @@ -499,7 +522,7 @@ "QuestionnaireResponseAnswerInput": "type QuestionnaireResponseAnswerInput = WithBackbone;", "questionnaireResponseAnswerSchema": "const questionnaireResponseAnswerSchema: z.ZodType>", "QuestionnaireResponseAuthorInput": "type QuestionnaireResponseAuthorInput = TypedQuestionnaireResponseReference<'Device' | 'Organization' | 'Patient' | 'Practitioner' | 'PractitionerRole' | 'RelatedPerson'>;", - "QuestionnaireResponseIdentifierInput": "interface QuestionnaireResponseIdentifierInput {\n readonly system: AbsoluteUri;\n readonly value: string;\n}", + "QuestionnaireResponseIdentifierInput": "interface QuestionnaireResponseIdentifierInput {\n readonly system: IdentifierSystem;\n readonly value: string;\n}", "QuestionnaireResponseInput": "interface QuestionnaireResponseInput {\n readonly id?: FhirId;\n /** Exact `Questionnaire.url|Questionnaire.version` canonical. */\n readonly questionnaire: Canonical;\n readonly identifier: QuestionnaireResponseIdentifierInput;\n readonly status: QuestionnaireResponse['status'];\n readonly subject: QuestionnaireResponseSubjectInput;\n readonly authored: FhirInstant;\n readonly author?: QuestionnaireResponseAuthorInput;\n readonly source?: QuestionnaireResponseSourceInput;\n readonly extensions?: readonly Extension[];\n readonly items?: readonly QuestionnaireResponseItemInput[];\n}", "QuestionnaireResponseItemInput": "interface QuestionnaireResponseItemInput extends BackboneInput {\n readonly linkId: string;\n readonly definition?: string;\n readonly text?: string;\n readonly answer?: readonly QuestionnaireResponseAnswerInput[];\n readonly item?: readonly QuestionnaireResponseItemInput[];\n}", "questionnaireResponseItemSchema": "const questionnaireResponseItemSchema: z.ZodType>", From 02e937f904ab499c5ef0d7e93a07601d2cdf7c47 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Tue, 22 Sep 2026 23:05:58 -0700 Subject: [PATCH 26/31] Re-Pin Grove FHIR to the Canonical Part-Index Revision --- .../receiver-lifecycle/events.json | 2 +- packages/fhir/grove-fhir.json | 4 +-- .../scripts/generate-measurement-catalog.mjs | 24 ++++++++++++-- .../contract/measurement-catalog.generated.ts | 31 +++++++++++++++++++ 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/events.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/events.json index 732bab21..e539f2b7 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/events.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/events.json @@ -5,7 +5,7 @@ "baseExchangeProtocolSHA256": "bbc9e980c2c674d5ef6f9900958d29c5602031ead8a3b6325cff508799106bb5", "releaseVersion": "0.6.0", "fhirVersion": "4.0.1", - "exchangeProtocolSHA256": "04ad91c9ed49804f89bbf9a86486b001ed67ed5d98d2754fc48c6f4007481b69" + "exchangeProtocolSHA256": "fe6ab1a88f429b89116627044530764e460b97680430110f6f35846610a3eb70" }, "events": { "original": { diff --git a/packages/fhir/grove-fhir.json b/packages/fhir/grove-fhir.json index 2c65d127..405225cb 100644 --- a/packages/fhir/grove-fhir.json +++ b/packages/fhir/grove-fhir.json @@ -1,5 +1,5 @@ { "repository": "https://github.com/SchmiedmayerLab/grove-fhir", - "ref": "459e016610a94de776953601cc7b65cc445eb3c6", - "archiveSha256": "04c5f42ae083d8efa9b3cc651f8b1f170b948ef411684632d697a0b6a93b3cac" + "ref": "96de131f036fa4116345864e098e8789204ff67f", + "archiveSha256": "58bd923c222cdfaf77753712a33777f80dcbea4396efff6c2418d4c75c91c688" } diff --git a/packages/fhir/scripts/generate-measurement-catalog.mjs b/packages/fhir/scripts/generate-measurement-catalog.mjs index 501586b5..43331ec5 100644 --- a/packages/fhir/scripts/generate-measurement-catalog.mjs +++ b/packages/fhir/scripts/generate-measurement-catalog.mjs @@ -1290,6 +1290,15 @@ const identityKindsByName = new Map( const protocolIdentifierRoles = new Set( (protocolIdentityKinds ?? []).map(({ identifierRole }) => identifierRole), ) +const unsignedDecimalComponents = + exchangeProtocol.opaqueIdentity?.componentRequirements?.unsignedDecimal +const canonicalUnsignedDecimal = /^(?:0|[1-9][0-9]*)$/u +const nonCanonicalComponentCount = (definition, components) => + definition.components.filter( + (name, index) => + unsignedDecimalComponents?.includes(name) === true && + !canonicalUnsignedDecimal.test(components[index]), + ).length const identityVectors = exchangeProtocol.testVectors?.identities const invalidIdentityVectors = exchangeProtocol.testVectors?.invalidIdentities const providerCodes = new Set(providerAdapter.providers.map(({ id }) => id)) @@ -1633,6 +1642,13 @@ if ( ) || !Array.isArray(protocolIdentityKinds) || protocolIdentityKinds.length === 0 || + !uniqueNonemptyStrings(unsignedDecimalComponents) || + unsignedDecimalComponents.some( + (name) => + !protocolIdentityKinds.some(({ components }) => + components?.includes(name), + ), + ) || identityKindsByName.size !== protocolIdentityKinds.length || protocolIdentityKinds?.some( ({ kind, identifierRole, components }) => @@ -1661,11 +1677,12 @@ if ( !providerCodes.has(vector.components[0])) || (genericSourceIdentityKinds.has(vector.identityKind) && providerCodes.has(vector.components[0])) || + nonCanonicalComponentCount(definition, vector.components) !== 0 || !opaqueIdentityValue.test(vector.value) ) }) || !Array.isArray(invalidIdentityVectors) || - invalidIdentityVectors.length !== 4 || + invalidIdentityVectors.length !== 6 || new Set(invalidIdentityVectors.map(({ id }) => id)).size !== invalidIdentityVectors.length || invalidIdentityVectors.some((vector) => { @@ -1688,7 +1705,10 @@ if ( !( (vector.expectedError === 'empty-component' && emptyComponentCount === 1) || - providerKindRequired + providerKindRequired || + (vector.expectedError === 'non-canonical-part-index' && + emptyComponentCount === 0 && + nonCanonicalComponentCount(definition, vector.components) === 1) ) ) }) || diff --git a/packages/fhir/src/contract/measurement-catalog.generated.ts b/packages/fhir/src/contract/measurement-catalog.generated.ts index 57b66cbd..1e20695d 100644 --- a/packages/fhir/src/contract/measurement-catalog.generated.ts +++ b/packages/fhir/src/contract/measurement-catalog.generated.ts @@ -664,6 +664,9 @@ const groveExchangeProtocolValue = { valueType: 'unicode-scalar-string', nonEmpty: true, arity: 'exactly-kind-components', + unsignedDecimal: ['part-index'], + unsignedDecimalForm: + 'A canonical unsigned decimal: 0, or a nonzero digit followed by digits, with no sign, whitespace, or leading zero.', }, privacy: 'HMAC reduces disclosure of native identifiers. It does not de-identify subjects, times, clinical data, graph relationships, or attachments.', @@ -1970,6 +1973,34 @@ const groveExchangeProtocolValue = { ], expectedError: 'provider-kind-required', }, + { + id: 'negative-part-index', + identityKind: 'source-artifact', + components: [ + 'health-connect', + 'HeartRateRecord', + 'urn:uuid:1f5c58aa-6ec6-4e79-a682-829a9debd3f5', + 'default', + 'record-heart-001', + 'heart-rate-samples', + '-1', + ], + expectedError: 'non-canonical-part-index', + }, + { + id: 'leading-zero-part-index', + identityKind: 'provider-artifact', + components: [ + 'google-health-api', + 'heart-rate', + 'https://accounts.example.org', + 'patient|α', + 'recording-001', + 'provider-recording', + '01', + ], + expectedError: 'non-canonical-part-index', + }, ], event: { system: 'https://study.example.org/fhir/NamingSystem/grove-event-v0', From aebf54f6d7722301604d33c7258b5c9eba226929 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Wed, 23 Sep 2026 09:44:31 -0700 Subject: [PATCH 27/31] Localize Questionnaires Through One Base Language and Standard Translations --- packages/fhir/README.md | 68 +++- .../resources/questionnaire-response.json | 1 + .../conformance/resources/questionnaire.json | 1 + packages/fhir/scripts/browser-smoke.mjs | 37 +-- .../scripts/generate-conformance-fixtures.mjs | 62 ++-- packages/fhir/src/questionnaire/builder.ts | 131 ++++++-- packages/fhir/src/questionnaire/contract.ts | 9 - .../fhir/src/questionnaire/localization.ts | 111 +++++++ packages/fhir/src/questionnaire/parse.ts | 18 ++ packages/fhir/src/questionnaire/preflight.ts | 42 ++- .../questionnaire/questionnaire-extensions.ts | 1 + packages/fhir/src/questionnaire/schemas.ts | 37 ++- packages/fhir/src/questionnaire/types.ts | 32 +- .../fhir/test/questionnaire-builder.test.ts | 45 +-- .../questionnaire-extension-contract.test.ts | 163 +++++----- .../test/questionnaire-input-surface.test.ts | 148 +++++---- .../test/questionnaire-localization.test.ts | 297 ++++++++++++++++++ ...uestionnaire-preflight-expressions.test.ts | 2 +- .../fhir/test/questionnaire-preflight.test.ts | 22 +- .../fhir/test/questionnaire-test-support.ts | 23 +- packages/fhir/test/readme-walkthrough.test.ts | 62 ++++ packages/fhir/test/type-surface.json | 30 +- 22 files changed, 1017 insertions(+), 325 deletions(-) create mode 100644 packages/fhir/src/questionnaire/localization.ts create mode 100644 packages/fhir/test/questionnaire-localization.test.ts diff --git a/packages/fhir/README.md b/packages/fhir/README.md index 765a1606..3d86980a 100644 --- a/packages/fhir/README.md +++ b/packages/fhir/README.md @@ -656,11 +656,59 @@ The `questionnaire` entry point builds strict, versioned R4 submission: ```typescript +import { parseFhirInstant, parseSemVer } from '@schmiedmayerlab/grove-fhir' import { buildQuestionnaire, buildQuestionnaireResponse, preflightQuestionnairePair, } from '@schmiedmayerlab/grove-fhir/questionnaire' + +const translation = (lang: string, content: string) => ({ + url: 'http://hl7.org/fhir/StructureDefinition/translation', + extension: [ + { url: 'lang', valueCode: lang }, + { url: 'content', valueString: content }, + ], +}) + +// One Questionnaire carries every language: base strings plus their translations. +const instrument = unwrap( + buildQuestionnaire({ + url: uri('https://mystudy.example.org/fhir/Questionnaire/wellbeing'), + version: unwrap(parseSemVer('1.0.0')), + language: 'en-US', + status: 'active', + subjectTypes: ['Patient'], + items: [ + { + linkId: 'feeling', + text: 'How are you feeling today?', + _text: { extension: [translation('es', '¿Cómo se siente hoy?')] }, + type: 'string', + required: true, + }, + ], + }), +) + +// The participant read Spanish, so the response names it and repeats no base text. +const answers = unwrap( + buildQuestionnaireResponse( + { + language: 'es', + identifier: { + system: identifierSystem('https://mystudy.example.org/fhir/responses'), + value: 'wellbeing-1', + }, + status: 'completed', + subject: { type: 'Patient', reference: 'Patient/participant-1' }, + authored: unwrap(parseFhirInstant('2026-08-19T17:30:00Z')), + items: [{ linkId: 'feeling', answer: [{ valueString: 'Bien' }] }], + }, + instrument, + ), +) +const pair = preflightQuestionnairePair(instrument, answers) ``` The builders own the exact Grove profile declarations, Semantic Versioning @@ -668,12 +716,20 @@ algorithm extension, and electronic completion-mode extension. Answer values are an exactly-one `value[x]` union in TypeScript and in the runtime schema. The Grove profiles fix both subjects to `Patient`, so `subjectTypes` is required and is exactly `['Patient']`, and a response requires one `Patient`-typed `subject`. Both builders and both parsers enforce this, as does the exchange-graph validator for every mandatory governed subject. -The pair preflight checks the exact `url|version`, global link identity, -response nesting, answer types, inline options, repeats, response text, -conditional enablement, and required enabled items for completed or amended -responses. It also enforces text length and decimal-place limits, scalar and -quantity bounds, inline or ValueSet-backed quantity units, attachment MIME and -size limits, repeated-answer occurrence bounds, and exclusive options. + +One Questionnaire holds every language of an instrument under its one `url|version`. +`language` is required and names the BCP 47 language of every base string. +Each other language is a standard `translation` extension on the string's primitive element: `_title`, `_description`, an item's `_text` or `_prefix`, a coding's `_display`, or an extension's string value. +Each translation needs one BCP 47 `lang` and one non-empty `content`, and one element translates into each language at most once. +A `valueString` answer option is data, not display: a translation may sit on its `_valueString`, but a response always stores the base value. +Changing a translation changes the content, so it needs a new `version`. + +`buildQuestionnaireResponse` takes the Questionnaire it answers and derives `questionnaire` from its `url|version`. +The response `language` is required and must be the base language or a translation language the Questionnaire offers. +The builder writes each item's `text` from the base Questionnaire only when the response uses the base language; a response to a translation omits it. + +The pair preflight checks the exact `url|version`, the offered response language, global link identity, response nesting, answer types, inline options, repeats, response text (omitted, or exactly the base text), conditional enablement, and required enabled items for completed or amended responses. +It also enforces text length and decimal-place limits, scalar and quantity bounds, inline or ValueSet-backed quantity units, attachment MIME and size limits, repeated-answer occurrence bounds, and exclusive options. Terminology is never fetched. For a coded item backed by `answerValueSet`, the caller supplies a normalized resolved concept list through diff --git a/packages/fhir/fixtures/conformance/resources/questionnaire-response.json b/packages/fhir/fixtures/conformance/resources/questionnaire-response.json index 4ac2d32c..3a1379a2 100644 --- a/packages/fhir/fixtures/conformance/resources/questionnaire-response.json +++ b/packages/fhir/fixtures/conformance/resources/questionnaire-response.json @@ -5,6 +5,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/resources/questionnaire.json b/packages/fhir/fixtures/conformance/resources/questionnaire.json index cc194cd8..3338ef7e 100644 --- a/packages/fhir/fixtures/conformance/resources/questionnaire.json +++ b/packages/fhir/fixtures/conformance/resources/questionnaire.json @@ -5,6 +5,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm", diff --git a/packages/fhir/scripts/browser-smoke.mjs b/packages/fhir/scripts/browser-smoke.mjs index 29776414..8f126187 100644 --- a/packages/fhir/scripts/browser-smoke.mjs +++ b/packages/fhir/scripts/browser-smoke.mjs @@ -236,6 +236,7 @@ try { const instrument = questionnaire.buildQuestionnaire({ url: 'https://example.org/Questionnaire/browser', version: '1.0.0', + language: 'en-US', status: 'active', subjectTypes: ['Patient'], items: [ @@ -247,25 +248,25 @@ try { }, ], }) - const response = questionnaire.buildQuestionnaireResponse({ - questionnaire: 'https://example.org/Questionnaire/browser|1.0.0', - identifier: { - system: 'https://example.org/responses', - value: 'browser-1', - }, - status: 'completed', - subject: { type: 'Patient', reference: 'Patient/browser' }, - authored: '2026-08-20T12:00:00Z', - items: [ - { - linkId: 'ready', - text: 'Are you ready?', - answer: [{ valueBoolean: true }], - }, - ], - }) + const response = + instrument.ok ? + questionnaire.buildQuestionnaireResponse( + { + language: 'en-US', + identifier: { + system: 'https://example.org/responses', + value: 'browser-1', + }, + status: 'completed', + subject: { type: 'Patient', reference: 'Patient/browser' }, + authored: '2026-08-20T12:00:00Z', + items: [{ linkId: 'ready', answer: [{ valueBoolean: true }] }], + }, + instrument.value, + ) + : undefined const pair = - instrument.ok && response.ok ? + response?.ok === true ? questionnaire.preflightQuestionnairePair( instrument.value, response.value, diff --git a/packages/fhir/scripts/generate-conformance-fixtures.mjs b/packages/fhir/scripts/generate-conformance-fixtures.mjs index d5caf433..f7a11fe9 100644 --- a/packages/fhir/scripts/generate-conformance-fixtures.mjs +++ b/packages/fhir/scripts/generate-conformance-fixtures.mjs @@ -26,7 +26,6 @@ const { groveMobilePackageMetadata, groveRecordingFormatRegistry, parseAbsoluteUri, - parseCanonical, parseEventSequence, parseFhirInstant, parseIdentifierSystem, @@ -595,6 +594,7 @@ const questionnaire = unwrap( buildQuestionnaire({ url: questionnaireUrl, version: questionnaireVersion, + language: 'en-US', name: 'GroveTsConformance', title: 'Grove TypeScript conformance instrument', status: 'active', @@ -630,37 +630,37 @@ const questionnaire = unwrap( }), ) const questionnaireResponse = unwrap( - buildQuestionnaireResponse({ - questionnaire: unwrap( - parseCanonical(`${questionnaireUrl}|${questionnaireVersion}`), - ), - identifier: { - system: identifierSystem( - 'https://grovealliance.org/fhir/testing/questionnaire-responses', - ), - value: 'grove-ts-conformance-response', - }, - status: 'completed', - subject: { type: 'Patient', reference: 'Patient/example' }, - authored: instant('2026-08-20T12:00:00Z'), - items: [ - { - linkId: 'wellbeing', - text: 'How are you feeling?', - answer: [ - { - valueCoding: { - system: uri( - 'https://grovealliance.org/fhir/testing/CodeSystem/wellbeing', - ), - code: 'well', - display: 'Well', - }, - }, - ], + buildQuestionnaireResponse( + { + language: 'en-US', + identifier: { + system: identifierSystem( + 'https://grovealliance.org/fhir/testing/questionnaire-responses', + ), + value: 'grove-ts-conformance-response', }, - ], - }), + status: 'completed', + subject: { type: 'Patient', reference: 'Patient/example' }, + authored: instant('2026-08-20T12:00:00Z'), + items: [ + { + linkId: 'wellbeing', + answer: [ + { + valueCoding: { + system: uri( + 'https://grovealliance.org/fhir/testing/CodeSystem/wellbeing', + ), + code: 'well', + display: 'Well', + }, + }, + ], + }, + ], + }, + questionnaire, + ), ) const resources = new Map( diff --git a/packages/fhir/src/questionnaire/builder.ts b/packages/fhir/src/questionnaire/builder.ts index 950faea7..6f784075 100644 --- a/packages/fhir/src/questionnaire/builder.ts +++ b/packages/fhir/src/questionnaire/builder.ts @@ -11,7 +11,9 @@ import { validateQuestionnaireContract, validateQuestionnaireResponseItemContract, } from './contract.js' +import { isLanguageTag, offeredLanguages } from './localization.js' import { parseQuestionnaire, parseQuestionnaireResponse } from './parse.js' +import { prefixed } from './preflight-diagnostics.js' import { QUESTIONNAIRE_EXTENSIONS, QUESTIONNAIRE_SYSTEMS, @@ -32,13 +34,13 @@ import type { GroveQuestionnaireResponse, QuestionnaireInput, QuestionnaireResponseInput, + QuestionnaireResponseItemInput, } from './types.js' import { groveQuestionnaireProfileCanonicals } from '../contract/questionnaire.generated.js' import { cloneJsonValue, issue, issues, - parseCanonical, parseFhirId, parseFhirInstant, parseIdentifierSystem, @@ -88,6 +90,15 @@ const validateQuestionnaireInput = ( ), ) } + if (!isLanguageTag(input.language)) { + failures.push( + issue( + 'invalid-code', + ['language'], + 'Questionnaire.language must be a BCP 47 language tag.', + ), + ) + } if (input.id !== undefined && !parseFhirId(input.id).ok) { failures.push( issue('invalid-identifier', ['id'], 'Questionnaire.id is invalid.'), @@ -130,6 +141,7 @@ export const buildQuestionnaire = ( meta: { profile: [groveQuestionnaireProfileCanonicals['grove-questionnaire']], }, + language: validatedInput.language, extension: [ ...(validatedInput.extensions ?? []), { @@ -146,12 +158,18 @@ export const buildQuestionnaire = ( ...(validatedInput.title === undefined ? {} : { title: validatedInput.title }), + ...(validatedInput._title === undefined ? + {} + : { _title: validatedInput._title }), status: validatedInput.status, subjectType: validatedInput.subjectTypes, ...(validatedInput.date === undefined ? {} : { date: validatedInput.date }), ...(validatedInput.description === undefined ? {} : { description: validatedInput.description }), + ...(validatedInput._description === undefined ? + {} + : { _description: validatedInput._description }), ...(validatedInput.purpose === undefined ? {} : { purpose: validatedInput.purpose }), @@ -161,25 +179,15 @@ export const buildQuestionnaire = ( const validateResponseInput = ( input: QuestionnaireResponseInput, + questionnaire: GroveQuestionnaire, ): readonly Issue[] => { const failures: Issue[] = [] - const canonical = parseCanonical(input.questionnaire) - if (!canonical.ok || !/^[^|#]+\|[^|#]+$/u.test(input.questionnaire)) { + if (!offeredLanguages(questionnaire).has(input.language)) { failures.push( issue( - 'invalid-uri', - ['questionnaire'], - 'Response.questionnaire must be an exact url|version canonical.', - ), - ) - } - const version = input.questionnaire.split('|')[1] - if (!parseSemVer(version).ok) { - failures.push( - issue( - 'invalid-code', - ['questionnaire'], - 'Response.questionnaire version must be SemVer.', + 'value-mismatch', + ['language'], + 'Response.language must be the Questionnaire base language or one of its translation languages.', ), ) } @@ -238,17 +246,97 @@ const validateResponseInput = ( return failures } -/** Builds a Grove R4 QuestionnaireResponse for one exact instrument version. */ +type QuestionnaireItem = GroveQuestionnaire['item'][number] + +// Response text repeats the base Questionnaire text, so only a base-language response carries it. +const itemsWithText = ( + items: readonly QuestionnaireResponseItemInput[], + definitions: readonly QuestionnaireItem[], + writeText: boolean, + path: ReadonlyArray, + failures: Issue[], +): readonly object[] => + items.map((item, index) => { + const itemPath = [...path, index] + const definition = definitions.find( + (candidate) => candidate.linkId === item.linkId, + ) + if (definition === undefined) { + failures.push( + issue( + 'invalid-reference', + [...itemPath, 'linkId'], + `Response linkId ${item.linkId} is not valid at this nesting level.`, + ), + ) + return item + } + const children = definition.item ?? [] + const { answer, item: nested, ...element } = item + return { + ...element, + ...(writeText && definition.text !== undefined ? + { text: definition.text } + : {}), + ...(answer === undefined ? + {} + : { + answer: answer.map((entry, answerIndex) => + entry.item === undefined ? + entry + : { + ...entry, + item: itemsWithText( + entry.item, + children, + writeText, + [...itemPath, 'answer', answerIndex, 'item'], + failures, + ), + }, + ), + }), + ...(nested === undefined ? + {} + : { + item: itemsWithText( + nested, + children, + writeText, + [...itemPath, 'item'], + failures, + ), + }), + } + }) + +/** Builds a Grove R4 QuestionnaireResponse to one exact Questionnaire in one of its languages. */ export const buildQuestionnaireResponse = ( input: QuestionnaireResponseInput, + questionnaire: GroveQuestionnaire, ): Result => { + const parsedQuestionnaire = parseQuestionnaire(questionnaire) + if (!parsedQuestionnaire.ok) { + return issues(prefixed(parsedQuestionnaire.issues, 'questionnaire')) + } + const instrument = parsedQuestionnaire.value const parsedInput = parseBuilderInput( questionnaireResponseBuilderInputSchema, input, ) if (!parsedInput.ok) return parsedInput const validatedInput = parsedInput.value - const failures = validateResponseInput(validatedInput) + const failures = [...validateResponseInput(validatedInput, instrument)] + const items = + validatedInput.items === undefined ? + undefined + : itemsWithText( + validatedInput.items, + instrument.item, + validatedInput.language === instrument.language, + ['items'], + failures, + ) if (failures.length > 0) return issues(failures) return parseQuestionnaireResponse({ @@ -259,6 +347,7 @@ export const buildQuestionnaireResponse = ( groveQuestionnaireProfileCanonicals['grove-questionnaire-response'], ], }, + language: validatedInput.language, extension: [ ...(validatedInput.extensions ?? []), { @@ -269,7 +358,7 @@ export const buildQuestionnaireResponse = ( }, ], identifier: validatedInput.identifier, - questionnaire: validatedInput.questionnaire, + questionnaire: `${instrument.url}|${instrument.version}`, status: validatedInput.status, subject: validatedInput.subject, authored: validatedInput.authored, @@ -279,8 +368,6 @@ export const buildQuestionnaireResponse = ( ...(validatedInput.source === undefined ? {} : { source: validatedInput.source }), - ...(validatedInput.items === undefined ? - {} - : { item: validatedInput.items }), + ...(items === undefined ? {} : { item: items }), }) } diff --git a/packages/fhir/src/questionnaire/contract.ts b/packages/fhir/src/questionnaire/contract.ts index edf5b367..811c7e13 100644 --- a/packages/fhir/src/questionnaire/contract.ts +++ b/packages/fhir/src/questionnaire/contract.ts @@ -834,15 +834,6 @@ export const validateQuestionnaireResponseItemContract = ( for (const [itemIndex, item] of items.entries()) { const itemPath = [...path, itemIndex] const answered = (item.answer?.length ?? 0) > 0 - if (answered && (item.text?.trim() ?? '') === '') { - failures.push( - issue( - 'missing-required', - [...itemPath, 'text'], - 'Every answered response item must repeat the question text.', - ), - ) - } if (answered && answeredLinkIds.has(item.linkId)) { failures.push( issue( diff --git a/packages/fhir/src/questionnaire/localization.ts b/packages/fhir/src/questionnaire/localization.ts new file mode 100644 index 00000000..00b48203 --- /dev/null +++ b/packages/fhir/src/questionnaire/localization.ts @@ -0,0 +1,111 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + QUESTIONNAIRE_EXTENSIONS, + extensionValue, + extensionsFor, + issue, + type ExtensionElement, +} from './questionnaire-extensions.js' +import type { Issue } from '../core/index.js' + +type ExtensionCandidate = ExtensionElement & { readonly url?: unknown } + +// The well-formed BCP 47 shape: a primary language subtag, then hyphenated subtags. +const languageTagPattern = /^[A-Za-z]{2,8}(?:-[A-Za-z\d]{1,8})*$/u + +export const isLanguageTag = (value: unknown): value is string => + typeof value === 'string' && languageTagPattern.test(value) + +const singleValue = (translation: ExtensionElement, url: string) => { + const parts = extensionsFor(translation, url) + return parts.length === 1 ? extensionValue(parts[0]) : undefined +} + +const translationLanguage = ( + translation: ExtensionElement, +): string | undefined => { + const language = singleValue(translation, 'lang') + const content = singleValue(translation, 'content') + return ( + language?.key === 'valueCode' && + isLanguageTag(language.value) && + (content?.key === 'valueString' || content?.key === 'valueMarkdown') && + typeof content.value === 'string' && + content.value !== '' + ) ? + language.value + : undefined +} + +interface QuestionnaireTranslations { + /** Every language a well-formed `translation` extension offers. */ + readonly languages: ReadonlySet + readonly failures: readonly Issue[] +} + +/** Collects and checks the `translation` extensions on every element of a resource. */ +export const questionnaireTranslations = ( + resource: unknown, +): QuestionnaireTranslations => { + const languages = new Set() + const failures: Issue[] = [] + const visit = (value: unknown, path: ReadonlyArray) => { + if (typeof value !== 'object' || value === null) return + if (Array.isArray(value)) { + for (const [index, entry] of value.entries()) { + visit(entry, [...path, index]) + } + return + } + const candidates: unknown = Reflect.get(value, 'extension') + const extensions = + Array.isArray(candidates) ? (candidates as ExtensionCandidate[]) : [] + const elementLanguages = new Set() + for (const [index, extension] of extensions.entries()) { + if (extension.url !== QUESTIONNAIRE_EXTENSIONS.translation) continue + const translationPath = [...path, 'extension', index] + const language = translationLanguage(extension) + if (language === undefined) { + failures.push( + issue( + 'invalid-type', + translationPath, + 'A translation requires one BCP 47 lang valueCode and one non-empty content valueString or valueMarkdown.', + ), + ) + } else if (elementLanguages.has(language)) { + failures.push( + issue( + 'duplicate-identifier', + translationPath, + `Translation ${language} occurs more than once on one element.`, + ), + ) + } else { + elementLanguages.add(language) + languages.add(language) + } + } + for (const [key, child] of Object.entries(value)) { + visit(child, [...path, key]) + } + } + visit(resource, []) + return { languages, failures } +} + +/** The base language and every translation language a Questionnaire offers. */ +export const offeredLanguages = (questionnaire: { + readonly language: string +}): ReadonlySet => + new Set([ + questionnaire.language, + ...questionnaireTranslations(questionnaire).languages, + ]) diff --git a/packages/fhir/src/questionnaire/parse.ts b/packages/fhir/src/questionnaire/parse.ts index cb211141..472e1ad1 100644 --- a/packages/fhir/src/questionnaire/parse.ts +++ b/packages/fhir/src/questionnaire/parse.ts @@ -13,6 +13,7 @@ import { validateQuestionnaireContract, validateQuestionnaireResponseItemContract, } from './contract.js' +import { isLanguageTag, questionnaireTranslations } from './localization.js' import { QUESTIONNAIRE_EXTENSIONS, QUESTIONNAIRE_SYSTEMS, @@ -175,6 +176,20 @@ const validateElementExtensions = ( return failures } +const languageIssues = ( + resource: { readonly language: string }, + resourceType: string, +): readonly Issue[] => + isLanguageTag(resource.language) ? + [] + : [ + issue( + 'invalid-code', + ['language'], + `${resourceType}.language must be a BCP 47 language tag.`, + ), + ] + const questionnaireContractIssues = ( questionnaire: GroveQuestionnaire, ): readonly Issue[] => { @@ -184,6 +199,8 @@ const questionnaireContractIssues = ( groveQuestionnaireProfileCanonicals['grove-questionnaire'], ), ...validateElementExtensions(questionnaire), + ...languageIssues(questionnaire, 'Questionnaire'), + ...questionnaireTranslations(questionnaire).failures, ] if (!isExactQuestionnaireUrl(questionnaire.url)) { failures.push( @@ -250,6 +267,7 @@ const responseContractIssues = ( groveQuestionnaireProfileCanonicals['grove-questionnaire-response'], ), ...validateElementExtensions(response), + ...languageIssues(response, 'QuestionnaireResponse'), ] if (!isExactQuestionnaireCanonical(response.questionnaire)) { failures.push( diff --git a/packages/fhir/src/questionnaire/preflight.ts b/packages/fhir/src/questionnaire/preflight.ts index 9a153db3..d4fee93b 100644 --- a/packages/fhir/src/questionnaire/preflight.ts +++ b/packages/fhir/src/questionnaire/preflight.ts @@ -14,6 +14,7 @@ import { QUESTIONNAIRE_SYSTEMS, validateQuestionnaireContract, } from './contract.js' +import { offeredLanguages } from './localization.js' import { isExactQuestionnaireCanonical, parseQuestionnaire, @@ -47,6 +48,9 @@ import { type Result, } from '../core/index.js' +// Parsed response items keep the optional text the builder input omits. +type ResponseItem = QuestionnaireResponseItemInput & { readonly text?: string } + const completedStatuses = new Set(['amended', 'completed']) const VERSION_ALGORITHM = QUESTIONNAIRE_EXTENSIONS.versionAlgorithm const VERSION_ALGORITHM_SYSTEM = QUESTIONNAIRE_SYSTEMS.versionAlgorithm @@ -151,7 +155,7 @@ const validateQuestionnaireResponseEnvelope = ( const validateOmittedGroupChildren = ( item: QuestionnaireItemInput, - responseItem: QuestionnaireResponseItemInput | undefined, + responseItem: ResponseItem | undefined, enabled: boolean, context: PreflightContext, path: ReadonlyArray, @@ -171,7 +175,7 @@ const validateOmittedGroupChildren = ( const validateRequiredItems = ( questionnaireItems: readonly QuestionnaireItemInput[], - responseByLinkId: ReadonlyMap, + responseByLinkId: ReadonlyMap, context: PreflightContext, path: ReadonlyArray, ): readonly Issue[] => { @@ -229,18 +233,21 @@ const validateRequiredItems = ( const validateMatchedResponseItem = ( questionnaireItem: QuestionnaireItemInput, - responseItem: QuestionnaireResponseItemInput, + responseItem: ResponseItem, context: PreflightContext, path: ReadonlyArray, ): readonly Issue[] => { const failures: Issue[] = [] const answers = responseItem.answer ?? [] - if (responseItem.text !== questionnaireItem.text) { + if ( + responseItem.text !== undefined && + responseItem.text !== questionnaireItem.text + ) { failures.push( issue( 'value-mismatch', [...path, 'text'], - 'Response item text must exactly match Questionnaire.item.text.', + 'Response item text, when present, must exactly match the base Questionnaire.item.text.', ), ) } @@ -341,7 +348,7 @@ const validateMatchedResponseItem = ( const validateResponseItems = ( questionnaireItems: readonly QuestionnaireItemInput[], - responseItems: readonly QuestionnaireResponseItemInput[], + responseItems: readonly ResponseItem[], context: PreflightContext, path: ReadonlyArray = ['response', 'item'], ): readonly Issue[] => { @@ -349,7 +356,7 @@ const validateResponseItems = ( const questionnaireByLinkId = new Map( questionnaireItems.map((item) => [item.linkId, item]), ) - const responseByLinkId = new Map() + const responseByLinkId = new Map() for (const [responseIndex, responseItem] of responseItems.entries()) { const itemPath = [...path, responseIndex] @@ -400,11 +407,11 @@ const validateResponseItems = ( /** * Strictly parses and cross-checks one Questionnaire/QuestionnaireResponse pair. - * It validates exact canonical version identity, nesting, answer types, options, - * portable answer constraints, status-aware required items, expressions, and - * response text fidelity. Warning-severity target constraints are returned in - * the successful Result's `warnings`; unexecuted behavior that can change - * completion acceptance fails closed. + * It validates exact canonical version identity, the offered response language, + * nesting, answer types, options, portable answer constraints, status-aware + * required items, expressions, and response text fidelity. Warning-severity + * target constraints are returned in the successful Result's `warnings`; + * unexecuted behavior that can change completion acceptance fails closed. */ export const preflightQuestionnairePair = ( questionnaireInput: unknown, @@ -441,6 +448,15 @@ export const preflightQuestionnairePair = ( ), ) } + if (!offeredLanguages(questionnaire.value).has(response.value.language)) { + failures.push( + issue( + 'value-mismatch', + ['response', 'language'], + 'QuestionnaireResponse.language must be the Questionnaire base language or one of its translation languages.', + ), + ) + } const admittedSubjectTypes = questionnaire.value.subjectType ?? [] const responseSubjectType = questionnaireResponseReferenceType( response.value.subject, @@ -500,7 +516,7 @@ export const preflightQuestionnairePair = ( const questionnaireItems = questionnaire.value .item as unknown as readonly QuestionnaireItemInput[] const responseItems = (response.value.item ?? - []) as unknown as readonly QuestionnaireResponseItemInput[] + []) as unknown as readonly ResponseItem[] failures.push( ...prefixed( validateQuestionnaireContract( diff --git a/packages/fhir/src/questionnaire/questionnaire-extensions.ts b/packages/fhir/src/questionnaire/questionnaire-extensions.ts index 4926c965..2d98f9d5 100644 --- a/packages/fhir/src/questionnaire/questionnaire-extensions.ts +++ b/packages/fhir/src/questionnaire/questionnaire-extensions.ts @@ -49,6 +49,7 @@ export const QUESTIONNAIRE_EXTENSIONS = { styleSensitive: 'http://hl7.org/fhir/StructureDefinition/rendering-styleSensitive', targetConstraint: 'http://hl7.org/fhir/StructureDefinition/targetConstraint', + translation: 'http://hl7.org/fhir/StructureDefinition/translation', unitOption: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption', unitValueSet: diff --git a/packages/fhir/src/questionnaire/schemas.ts b/packages/fhir/src/questionnaire/schemas.ts index e4a80f7b..b8da7724 100644 --- a/packages/fhir/src/questionnaire/schemas.ts +++ b/packages/fhir/src/questionnaire/schemas.ts @@ -149,6 +149,23 @@ const answerOptionFields = { const answerOptionKeys = Object.keys(answerOptionFields) +const answerOptionChoice = ( + value: { + readonly valueString?: string | undefined + readonly _valueString?: object | undefined + }, + context: z.RefinementCtx, +) => { + exactlyOneAnswerChoice(value, answerOptionKeys, context) + if (value._valueString !== undefined && value.valueString === undefined) { + context.addIssue({ + code: 'custom', + path: ['_valueString'], + message: 'answerOption._valueString requires valueString.', + }) + } +} + const populatedCount = (value: object, keys: readonly string[]): number => keys.filter((key) => Reflect.get(value, key) !== undefined).length @@ -211,12 +228,11 @@ const answerOptionSchema = z extension: z.array(extensionSchema).optional(), modifierExtension: z.array(extensionSchema).optional(), ...answerOptionFields, + _valueString: primitiveMetadata, initialSelected: z.boolean().optional(), _initialSelected: primitiveMetadata, }) - .superRefine((value, context) => - exactlyOneAnswerChoice(value, answerOptionKeys, context), - ) + .superRefine(answerOptionChoice) const initialSchema = z .strictObject({ @@ -325,6 +341,7 @@ export const questionnaireItemSchema: z.ZodType> = const questionnaireSchemaValue = z.strictObject({ resourceType: z.literal('Questionnaire'), ...resourceFields, + language: z.string().min(1), url: z.url(), _url: primitiveMetadata, version: z.string().min(1), @@ -410,6 +427,7 @@ export const questionnaireResponseItemSchema: z.ZodType< const questionnaireResponseSchemaValue = z.strictObject({ resourceType: z.literal('QuestionnaireResponse'), ...resourceFields, + language: z.string().min(1), identifier: identifierSchema, basedOn: z.array(resolvableReferenceSchema).optional(), partOf: z.array(resolvableReferenceSchema).optional(), @@ -480,11 +498,10 @@ const builderAnswerOptionSchema = z .strictObject({ ...builderBackboneFields, ...answerOptionFields, + _valueString: primitiveMetadata, initialSelected: z.boolean().optional(), }) - .superRefine((value, context) => - exactlyOneAnswerChoice(value, answerOptionKeys, context), - ) + .superRefine(answerOptionChoice) const builderInitialSchema = z .strictObject({ @@ -502,7 +519,9 @@ const questionnaireBuilderItemSchema: z.ZodType = z.lazy(() => definition: z.string().optional(), code: z.array(codingSchema).optional(), prefix: z.string().optional(), + _prefix: primitiveMetadata, text: z.string().optional(), + _text: primitiveMetadata, type: z.enum([ 'group', 'display', @@ -538,12 +557,15 @@ export const questionnaireBuilderInputSchema: z.ZodType = z.strictObject({ id: z.string().optional(), url: z.string(), version: z.string(), + language: z.string(), name: z.string().optional(), title: z.string().optional(), + _title: primitiveMetadata, status: z.enum(['draft', 'active', 'retired', 'unknown']), subjectTypes: z.tuple([z.literal('Patient')]), date: z.string().optional(), description: z.string().optional(), + _description: primitiveMetadata, purpose: z.string().optional(), extensions: z.array(extensionSchema).optional(), items: z.array(questionnaireBuilderItemSchema).min(1), @@ -554,7 +576,6 @@ const questionnaireResponseBuilderItemSchema: z.ZodType = z.lazy(() => ...builderBackboneFields, linkId: z.string(), definition: z.string().optional(), - text: z.string().optional(), answer: z .array( z @@ -601,7 +622,7 @@ const actorReferenceInputSchema = builderReferenceSchema( export const questionnaireResponseBuilderInputSchema: z.ZodType = z.strictObject({ id: z.string().optional(), - questionnaire: z.string(), + language: z.string(), identifier: questionnaireResponseIdentifierInputSchema, status: z.enum([ 'in-progress', diff --git a/packages/fhir/src/questionnaire/types.ts b/packages/fhir/src/questionnaire/types.ts index 3286d9af..c721e316 100644 --- a/packages/fhir/src/questionnaire/types.ts +++ b/packages/fhir/src/questionnaire/types.ts @@ -44,6 +44,12 @@ interface BackboneInput { readonly modifierExtension?: readonly Extension[] } +/** The element beside a primitive, which carries its `translation` extensions. */ +interface PrimitiveElementInput { + readonly id?: string + readonly extension?: readonly Extension[] +} + interface AnswerValues { readonly valueBoolean: boolean readonly valueDecimal: number @@ -105,9 +111,13 @@ export type QuestionnaireEnableWhenInput = WithBackbone< } > +/** A `valueString` option is data: a response stores its base value, whatever language rendered it. */ export type QuestionnaireAnswerOptionInput = WithBackbone< ExactlyOne, - { readonly initialSelected?: boolean } + { + readonly _valueString?: PrimitiveElementInput + readonly initialSelected?: boolean + } > export type QuestionnaireInitialInput = @@ -119,7 +129,10 @@ export interface QuestionnaireItemInput extends BackboneInput { readonly definition?: string readonly code?: readonly Coding[] readonly prefix?: string + readonly _prefix?: PrimitiveElementInput + /** Base-language text; other languages are `translation` extensions on `_text`. */ readonly text?: string + readonly _text?: PrimitiveElementInput readonly type: QuestionnaireItemType readonly enableWhen?: readonly QuestionnaireEnableWhenInput[] readonly enableBehavior?: 'all' | 'any' @@ -138,11 +151,10 @@ export type QuestionnaireResponseAnswerInput = WithBackbone< { readonly item?: readonly QuestionnaireResponseItemInput[] } > -/** Bounded R4 QuestionnaireResponse item surface supported by the Grove profile. */ +/** Bounded R4 QuestionnaireResponse item surface; the builder writes `text` from the Questionnaire. */ export interface QuestionnaireResponseItemInput extends BackboneInput { readonly linkId: string readonly definition?: string - readonly text?: string readonly answer?: readonly QuestionnaireResponseAnswerInput[] readonly item?: readonly QuestionnaireResponseItemInput[] } @@ -151,13 +163,17 @@ export interface QuestionnaireInput { readonly id?: FhirId readonly url: AbsoluteUri readonly version: SemVer + /** BCP 47 tag of every base string; other languages are `translation` extensions. */ + readonly language: string readonly name?: string readonly title?: string + readonly _title?: PrimitiveElementInput readonly status: Questionnaire['status'] /** Fixed by the Grove Questionnaire profile to exactly `Patient`. */ readonly subjectTypes: readonly ['Patient'] readonly date?: FhirInstant readonly description?: string + readonly _description?: PrimitiveElementInput readonly purpose?: string readonly extensions?: readonly Extension[] readonly items: readonly [QuestionnaireItemInput, ...QuestionnaireItemInput[]] @@ -201,8 +217,8 @@ export type QuestionnaireResponseSourceInput = export interface QuestionnaireResponseInput { readonly id?: FhirId - /** Exact `Questionnaire.url|Questionnaire.version` canonical. */ - readonly questionnaire: Canonical + /** BCP 47 tag the participant saw: the Questionnaire's base language or one of its translations. */ + readonly language: string readonly identifier: QuestionnaireResponseIdentifierInput readonly status: QuestionnaireResponse['status'] readonly subject: QuestionnaireResponseSubjectInput @@ -214,7 +230,8 @@ export interface QuestionnaireResponseInput { } export type GroveQuestionnaire = Readonly< - Omit & { + Omit & { + readonly language: string readonly item: NonNullable } > @@ -222,8 +239,9 @@ export type GroveQuestionnaire = Readonly< export type GroveQuestionnaireResponse = Readonly< Omit< QuestionnaireResponse, - 'authored' | 'identifier' | 'item' | 'questionnaire' + 'authored' | 'identifier' | 'item' | 'language' | 'questionnaire' > & { + readonly language: string readonly identifier: Identifier readonly questionnaire: string readonly authored: string diff --git a/packages/fhir/test/questionnaire-builder.test.ts b/packages/fhir/test/questionnaire-builder.test.ts index 14997481..dcb37161 100644 --- a/packages/fhir/test/questionnaire-builder.test.ts +++ b/packages/fhir/test/questionnaire-builder.test.ts @@ -11,6 +11,7 @@ import { codingSystem, preflightItems, questionnaire, + questionnaireDeclaring, questionnaireInput, response, responseInput, @@ -185,7 +186,6 @@ describe('Questionnaire R4 builders', () => { items: [ { linkId: 'strict-item', - text: 'Strict item', answer: [{ valueString: 'answer', unknownAnswerField: true }], }, ], @@ -193,7 +193,10 @@ describe('Questionnaire R4 builders', () => { ] for (const candidate of malformedResponses) { const invoke = () => - buildQuestionnaireResponse(candidate as QuestionnaireResponseInput) + buildQuestionnaireResponse( + candidate as QuestionnaireResponseInput, + questionnaireDeclaring('strict-item'), + ) expect(invoke).not.toThrow() expect(invoke().ok).toBe(false) } @@ -279,18 +282,17 @@ describe('Questionnaire R4 builders', () => { const mixedItem = { linkId: 'mixed', - text: 'Mixed', answer: [{ valueString: 'answer' }], - item: [{ linkId: 'child', text: 'Child' }], + item: [{ linkId: 'child' }], } expect( parseQuestionnaireResponse({ ...response, item: [mixedItem] }).ok, ).toBe(false) expect( - buildQuestionnaireResponse({ - ...responseInput(), - items: [mixedItem], - }).ok, + buildQuestionnaireResponse( + { ...responseInput(), items: [mixedItem] }, + questionnaireDeclaring('mixed'), + ).ok, ).toBe(false) }) @@ -576,12 +578,15 @@ describe('Questionnaire R4 builders', () => { }).ok, ).toBe(false) expect( - buildQuestionnaireResponse({ - ...responseInput(), - extensions: [ - { url: 'https://example.org/fhir/StructureDefinition/empty' }, - ], - }).ok, + buildQuestionnaireResponse( + { + ...responseInput(), + extensions: [ + { url: 'https://example.org/fhir/StructureDefinition/empty' }, + ], + }, + questionnaire, + ).ok, ).toBe(false) }) @@ -609,13 +614,17 @@ describe('Questionnaire R4 builders', () => { expect( buildQuestionnaireResponse( withoutSubject(responseInput()) as QuestionnaireResponseInput, + questionnaire, ).ok, ).toBe(false) expect( - buildQuestionnaireResponse({ - ...responseInput(), - subject: { type: 'Group', reference: 'Group/example' }, - } as unknown as QuestionnaireResponseInput).ok, + buildQuestionnaireResponse( + { + ...responseInput(), + subject: { type: 'Group', reference: 'Group/example' }, + } as unknown as QuestionnaireResponseInput, + questionnaire, + ).ok, ).toBe(false) expect(parseQuestionnaireResponse(withoutSubject(response)).ok).toBe(false) }) diff --git a/packages/fhir/test/questionnaire-extension-contract.test.ts b/packages/fhir/test/questionnaire-extension-contract.test.ts index b4dfc912..451db886 100644 --- a/packages/fhir/test/questionnaire-extension-contract.test.ts +++ b/packages/fhir/test/questionnaire-extension-contract.test.ts @@ -10,6 +10,7 @@ import { codingSystem, preflightItems, questionnaire, + questionnaireDeclaring, questionnaireExtensions, questionnaireInput, response, @@ -65,55 +66,59 @@ describe('Questionnaire R4 builders', () => { ).toBe(false) expect( - buildQuestionnaireResponse({ - ...responseInput(), - items: [ - { - linkId: 'answer-weight', - text: 'Answer weight', - answer: [ - { - extension: [ - { - url: questionnaireExtensions.itemWeight, - valueInteger: 1, - }, - ], - valueCoding: { system: codingSystem, code: 'answer-weight' }, - }, - ], - }, - ], - }).ok, + buildQuestionnaireResponse( + { + ...responseInput(), + items: [ + { + linkId: 'answer-weight', + answer: [ + { + extension: [ + { + url: questionnaireExtensions.itemWeight, + valueInteger: 1, + }, + ], + valueCoding: { system: codingSystem, code: 'answer-weight' }, + }, + ], + }, + ], + }, + questionnaireDeclaring('answer-weight'), + ).ok, ).toBe(false) expect( - buildQuestionnaireResponse({ - ...responseInput(), - items: [ - { - linkId: 'duplicate-answer-weight', - text: 'Duplicate answer weight', - answer: [ - { - extension: [ - { - url: questionnaireExtensions.itemWeight, - valueDecimal: 1, - }, - { - url: questionnaireExtensions.itemWeight, - valueDecimal: 2, + buildQuestionnaireResponse( + { + ...responseInput(), + items: [ + { + linkId: 'duplicate-answer-weight', + answer: [ + { + extension: [ + { + url: questionnaireExtensions.itemWeight, + valueDecimal: 1, + }, + { + url: questionnaireExtensions.itemWeight, + valueDecimal: 2, + }, + ], + valueCoding: { + system: codingSystem, + code: 'duplicate-answer-weight', }, - ], - valueCoding: { - system: codingSystem, - code: 'duplicate-answer-weight', }, - }, - ], - }, - ], - }).ok, + ], + }, + ], + }, + questionnaireDeclaring('duplicate-answer-weight'), + ).ok, ).toBe(false) const weightedCoding = { @@ -143,28 +148,32 @@ describe('Questionnaire R4 builders', () => { }).ok, ).toBe(false) expect( - buildQuestionnaireResponse({ - ...responseInput(), - items: [ - { - linkId: 'weighted', - text: 'Weighted', - answer: [{ valueCoding: weightedCoding }], - }, - ], - }).ok, + buildQuestionnaireResponse( + { + ...responseInput(), + items: [ + { + linkId: 'weighted', + answer: [{ valueCoding: weightedCoding }], + }, + ], + }, + questionnaireDeclaring('weighted'), + ).ok, ).toBe(false) expect( - buildQuestionnaireResponse({ - ...responseInput(), - items: [ - { - linkId: 'duplicate-weight', - text: 'Duplicate weight', - answer: [{ valueCoding: duplicateWeightedCoding }], - }, - ], - }).ok, + buildQuestionnaireResponse( + { + ...responseInput(), + items: [ + { + linkId: 'duplicate-weight', + answer: [{ valueCoding: duplicateWeightedCoding }], + }, + ], + }, + questionnaireDeclaring('duplicate-weight'), + ).ok, ).toBe(false) }) @@ -185,24 +194,16 @@ describe('Questionnaire R4 builders', () => { } }) - it('enforces answered text and qrs-2 outside pair preflight', () => { + it('enforces qrs-2 and admits omitted text outside pair preflight', () => { const duplicateAnswers = [ - { - linkId: 'duplicate', - text: 'Duplicate', - answer: [{ valueString: 'one' }], - }, - { - linkId: 'duplicate', - text: 'Duplicate', - answer: [{ valueString: 'two' }], - }, + { linkId: 'duplicate', answer: [{ valueString: 'one' }] }, + { linkId: 'duplicate', answer: [{ valueString: 'two' }] }, ] expect( - buildQuestionnaireResponse({ - ...responseInput(), - items: duplicateAnswers, - }).ok, + buildQuestionnaireResponse( + { ...responseInput(), items: duplicateAnswers }, + questionnaireDeclaring('duplicate'), + ).ok, ).toBe(false) expect( parseQuestionnaireResponse({ @@ -221,7 +222,7 @@ describe('Questionnaire R4 builders', () => { }, ], }).ok, - ).toBe(false) + ).toBe(true) }) it('validates authored and answer temporal values as R4 primitives', () => { diff --git a/packages/fhir/test/questionnaire-input-surface.test.ts b/packages/fhir/test/questionnaire-input-surface.test.ts index c0ca857a..6ad07b52 100644 --- a/packages/fhir/test/questionnaire-input-surface.test.ts +++ b/packages/fhir/test/questionnaire-input-surface.test.ts @@ -8,6 +8,7 @@ import { authored, + questionnaire, questionnaireInput, responseInput, unwrap, @@ -28,6 +29,7 @@ describe('Questionnaire R4 builders', () => { ...questionnaireInput, url: '/relative', version: '01.0.0', + language: 'not a tag', id: 'invalid/id', date: 'not-an-instant', extensions: [ @@ -109,18 +111,21 @@ describe('Questionnaire R4 builders', () => { 'A complete test instrument.', ) - const fullResponse = buildQuestionnaireResponse({ - ...responseInput(), - id: unwrap(parseFhirId('response-1')), - author: { type: 'Practitioner', reference: 'Practitioner/author' }, - source: { type: 'Patient', reference: 'Patient/example' }, - extensions: [ - { - url: 'https://example.org/fhir/StructureDefinition/response-note', - valueString: 'note', - }, - ], - }) + const fullResponse = buildQuestionnaireResponse( + { + ...responseInput(), + id: unwrap(parseFhirId('response-1')), + author: { type: 'Practitioner', reference: 'Practitioner/author' }, + source: { type: 'Patient', reference: 'Patient/example' }, + extensions: [ + { + url: 'https://example.org/fhir/StructureDefinition/response-note', + valueString: 'note', + }, + ], + }, + questionnaire, + ) expect(fullResponse.ok).toBe(true) if (!fullResponse.ok) return expect(fullResponse.value.author?.reference).toBe('Practitioner/author') @@ -128,27 +133,30 @@ describe('Questionnaire R4 builders', () => { }) it('supports identifier-only typed response actors and rejects disallowed source targets', () => { - const logical = buildQuestionnaireResponse({ - ...responseInput(), - author: { - type: 'PractitionerRole', - identifier: { - system: unwrap( - parseIdentifierSystem('https://example.org/practitioner-roles'), - ), - value: 'role-7', + const logical = buildQuestionnaireResponse( + { + ...responseInput(), + author: { + type: 'PractitionerRole', + identifier: { + system: unwrap( + parseIdentifierSystem('https://example.org/practitioner-roles'), + ), + value: 'role-7', + }, }, - }, - source: { - type: 'RelatedPerson', - identifier: { - system: unwrap( - parseIdentifierSystem('https://example.org/respondents'), - ), - value: 'respondent-9', + source: { + type: 'RelatedPerson', + identifier: { + system: unwrap( + parseIdentifierSystem('https://example.org/respondents'), + ), + value: 'respondent-9', + }, }, }, - }) + questionnaire, + ) expect(logical.ok).toBe(true) if (!logical.ok) return expect(logical.value.author?.type).toBe('PractitionerRole') @@ -163,7 +171,7 @@ describe('Questionnaire R4 builders', () => { }) it('parses literal, historical, absolute, and contained response references without redundant type', () => { - const built = buildQuestionnaireResponse(responseInput()) + const built = buildQuestionnaireResponse(responseInput(), questionnaire) expect(built.ok).toBe(true) if (!built.ok) return @@ -187,7 +195,7 @@ describe('Questionnaire R4 builders', () => { }) it('rejects conflicting, unresolved, and untyped logical response references', () => { - const built = buildQuestionnaireResponse(responseInput()) + const built = buildQuestionnaireResponse(responseInput(), questionnaire) expect(built.ok).toBe(true) if (!built.ok) return const invalid = [ @@ -229,58 +237,48 @@ describe('Questionnaire R4 builders', () => { }) it('reports the complete bounded QuestionnaireResponse input validation surface', () => { - const result = buildQuestionnaireResponse({ - questionnaire: 'https://example.org/Questionnaire/pain', - identifier: { system: '/relative', value: ' ' }, - status: 'completed', - id: 'invalid/id', - subject: { - type: 'Patient', - reference: 'Patient/example/_history/2', - }, - authored: 'not-an-instant', - author: { type: 'Device', reference: ' ' }, - source: { type: 'Organization', reference: '' }, - extensions: [ - { - url: 'http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode', - valueCodeableConcept: { text: 'duplicate' }, - }, - ], - items: [ - { - linkId: 'parent', - item: [ - { - linkId: 'direct-child', - answer: [{ valueString: 'missing repeated text' }], - }, - ], - answer: [ - { - valueString: 'parent answer', - item: [ - { - linkId: 'answer-child', - answer: [{ valueString: 'also missing repeated text' }], - }, - ], - }, - ], + const result = buildQuestionnaireResponse( + { + language: 'fr-FR', + identifier: { system: '/relative', value: ' ' }, + status: 'completed', + id: 'invalid/id', + subject: { + type: 'Patient', + reference: 'Patient/example/_history/2', }, - ], - } as unknown as QuestionnaireResponseInput) + authored: 'not-an-instant', + author: { type: 'Device', reference: ' ' }, + source: { type: 'Organization', reference: '' }, + extensions: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode', + valueCodeableConcept: { text: 'duplicate' }, + }, + ], + items: [ + { + linkId: 'health', + item: [ + { + linkId: 'undeclared-child', + answer: [{ valueString: 'undeclared answer' }], + }, + ], + }, + ], + } as unknown as QuestionnaireResponseInput, + questionnaire, + ) expect(result.ok).toBe(false) if (result.ok) return expect(new Set(result.issues.map(({ code }) => code))).toEqual( new Set([ - 'invalid-uri', - 'invalid-code', + 'value-mismatch', 'invalid-identifier', 'invalid-date-time', 'invalid-reference', 'duplicate-identifier', - 'missing-required', ]), ) }) diff --git a/packages/fhir/test/questionnaire-localization.test.ts b/packages/fhir/test/questionnaire-localization.test.ts new file mode 100644 index 00000000..512aa661 --- /dev/null +++ b/packages/fhir/test/questionnaire-localization.test.ts @@ -0,0 +1,297 @@ +// +// This source file is part of the Grove open-source project +// +// SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// + +import { + codingSystem, + questionnaire, + questionnaireInput, + response, + responseInput, + unwrap, +} from './questionnaire-test-support.js' +import { + buildQuestionnaire, + buildQuestionnaireResponse, + parseQuestionnaire, + parseQuestionnaireResponse, + preflightQuestionnairePair, + type Issue, + type Result, +} from '../src/index.js' + +const translation = (lang: string, content: string) => ({ + url: 'http://hl7.org/fhir/StructureDefinition/translation', + extension: [ + { url: 'lang', valueCode: lang }, + { url: 'content', valueString: content }, + ], +}) + +const inSpanish = (content: string) => ({ + extension: [translation('es', content)], +}) + +const multilingual = unwrap( + buildQuestionnaire({ + ...questionnaireInput, + _title: inSpanish('Cuestionario de dolor'), + description: 'How pain affects you.', + _description: inSpanish('Cómo le afecta el dolor.'), + items: [ + { + linkId: 'has-pain', + prefix: '1.', + _prefix: inSpanish('1.ª'), + text: 'Are you in pain?', + _text: inSpanish('¿Tiene dolor?'), + type: 'boolean', + }, + { + linkId: 'location', + text: 'Where?', + _text: inSpanish('¿Dónde?'), + type: 'string', + answerOption: [ + { valueString: 'Head', _valueString: inSpanish('Cabeza') }, + { valueString: 'Back', _valueString: inSpanish('Espalda') }, + ], + }, + { + linkId: 'severity', + text: 'How severe?', + type: 'choice', + answerOption: [ + { + valueCoding: { + system: codingSystem, + code: 'mild', + display: 'Mild', + _display: inSpanish('Leve'), + }, + }, + ], + }, + ], + }), +) + +const answers = { + ...responseInput(), + items: [ + { linkId: 'has-pain', answer: [{ valueBoolean: true }] }, + { linkId: 'location', answer: [{ valueString: 'Head' }] }, + ], +} + +const withoutLanguage = (value: object): unknown => { + const copy = { ...value } + Reflect.deleteProperty(copy, 'language') + return copy +} + +const codes = (result: Result): ReadonlySet => + new Set(result.ok ? [] : result.issues.map(({ code }) => code)) + +describe('Questionnaire localization', () => { + it('keeps the base language and every translation in one Questionnaire', () => { + expect(multilingual.language).toBe('en-US') + expect(multilingual._title).toEqual(inSpanish('Cuestionario de dolor')) + expect(multilingual._description).toEqual( + inSpanish('Cómo le afecta el dolor.'), + ) + expect(multilingual.item[0]?._text).toEqual(inSpanish('¿Tiene dolor?')) + expect(multilingual.item[0]?._prefix).toEqual(inSpanish('1.ª')) + expect(multilingual.item[1]?.answerOption?.[0]).toEqual({ + valueString: 'Head', + _valueString: inSpanish('Cabeza'), + }) + expect( + multilingual.item[2]?.answerOption?.[0]?.valueCoding?._display, + ).toEqual(inSpanish('Leve')) + expect(parseQuestionnaire(multilingual)).toEqual({ + ok: true, + value: multilingual, + }) + }) + + it('requires a BCP 47 base language on both resources', () => { + expect(codes(parseQuestionnaire(withoutLanguage(questionnaire)))).toEqual( + new Set(['schema-invalid']), + ) + expect( + codes(parseQuestionnaireResponse(withoutLanguage(response))), + ).toEqual(new Set(['schema-invalid'])) + expect( + codes(parseQuestionnaire({ ...questionnaire, language: 'en_US' })), + ).toEqual(new Set(['invalid-code'])) + expect( + codes(parseQuestionnaireResponse({ ...response, language: 'en US' })), + ).toEqual(new Set(['invalid-code'])) + expect( + codes(buildQuestionnaire({ ...questionnaireInput, language: '' })), + ).toEqual(new Set(['invalid-code'])) + }) + + it('rejects malformed and ambiguous translations', () => { + const withTitleExtensions = (extension: readonly object[]) => + parseQuestionnaire({ ...questionnaire, _title: { extension } }) + expect( + codes( + withTitleExtensions([ + { + url: 'http://hl7.org/fhir/StructureDefinition/translation', + extension: [{ url: 'lang', valueCode: 'es' }], + }, + ]), + ), + ).toEqual(new Set(['invalid-type'])) + expect( + codes(withTitleExtensions([translation('not a tag', 'Dolor')])), + ).toEqual(new Set(['invalid-type'])) + const duplicated = withTitleExtensions([ + translation('es', 'Dolor'), + translation('es', 'Dolores'), + ]) + expect(duplicated.ok).toBe(false) + if (duplicated.ok) return + expect(duplicated.issues).toEqual([ + expect.objectContaining({ + code: 'duplicate-identifier', + path: ['_title', 'extension', 1], + }), + ]) + }) + + it('admits _valueString only beside a valueString option', () => { + expect( + buildQuestionnaire({ + ...questionnaireInput, + items: [ + { + linkId: 'severity', + text: 'How severe?', + type: 'choice', + answerOption: [ + { + valueCoding: { system: codingSystem, code: 'mild' }, + _valueString: inSpanish('Leve'), + }, + ], + }, + ], + }).ok, + ).toBe(false) + }) + + it('writes response text only for the base language', () => { + const base = unwrap(buildQuestionnaireResponse(answers, multilingual)) + expect(base.language).toBe('en-US') + expect(base.questionnaire).toBe( + `${multilingual.url}|${multilingual.version}`, + ) + expect(base.item?.map(({ text }) => text)).toEqual([ + 'Are you in pain?', + 'Where?', + ]) + + const translated = unwrap( + buildQuestionnaireResponse({ ...answers, language: 'es' }, multilingual), + ) + expect(translated.language).toBe('es') + expect(translated.item?.some((item) => 'text' in item)).toBe(false) + expect(translated.item?.[1]?.answer).toEqual([{ valueString: 'Head' }]) + + for (const pair of [base, translated]) { + expect(preflightQuestionnairePair(multilingual, pair).ok).toBe(true) + } + }) + + it('writes nested group and answer-child text from the base Questionnaire', () => { + expect(response.item?.[0]?.text).toBe('Health') + expect(response.item?.[0]?.item?.[0]?.text).toBe('Are you in pain?') + expect(response.item?.[0]?.item?.[0]?.answer?.[0]?.item?.[0]?.text).toBe( + 'How severe is the pain?', + ) + }) + + it('rejects a response language or linkId the Questionnaire does not offer', () => { + const unoffered = buildQuestionnaireResponse( + { ...answers, language: 'es-MX' }, + multilingual, + ) + expect(unoffered.ok).toBe(false) + if (unoffered.ok) return + expect(unoffered.issues).toEqual([ + expect.objectContaining({ code: 'value-mismatch', path: ['language'] }), + ]) + + const undeclared = buildQuestionnaireResponse( + { + ...answers, + items: [ + { + linkId: 'has-pain', + answer: [ + { + valueBoolean: true, + item: [{ linkId: 'undeclared', answer: [{ valueInteger: 1 }] }], + }, + ], + }, + ], + }, + multilingual, + ) + expect(undeclared.ok).toBe(false) + if (undeclared.ok) return + expect(undeclared.issues).toEqual([ + expect.objectContaining({ + code: 'invalid-reference', + path: ['items', 0, 'answer', 0, 'item', 0, 'linkId'], + }), + ]) + + expect( + codes( + buildQuestionnaireResponse( + answers, + withoutLanguage(multilingual) as typeof multilingual, + ), + ), + ).toEqual(new Set(['schema-invalid'])) + }) + + it('reports translated response text and an unoffered language as pair errors', () => { + const translated = unwrap( + buildQuestionnaireResponse({ ...answers, language: 'es' }, multilingual), + ) + const result = preflightQuestionnairePair(multilingual, { + ...translated, + language: 'fr', + item: [ + { + linkId: 'has-pain', + text: '¿Tiene dolor?', + answer: [{ valueBoolean: true }], + }, + { linkId: 'location', answer: [{ valueString: 'Cabeza' }] }, + ], + }) + expect(result.ok).toBe(false) + if (result.ok) return + expect( + result.issues.map(({ code, path }) => [code, path.join('.')]), + ).toEqual( + expect.arrayContaining([ + ['value-mismatch', 'response.language'], + ['value-mismatch', 'response.item.0.text'], + ['value-mismatch', 'response.item.1.answer.0'], + ]), + ) + }) +}) diff --git a/packages/fhir/test/questionnaire-preflight-expressions.test.ts b/packages/fhir/test/questionnaire-preflight-expressions.test.ts index 9ca4e270..94e9d096 100644 --- a/packages/fhir/test/questionnaire-preflight-expressions.test.ts +++ b/packages/fhir/test/questionnaire-preflight-expressions.test.ts @@ -505,7 +505,7 @@ describe('Questionnaire pair preflight', () => { assert( property(string({ minLength: 1 }), (value) => { const candidate = unwrap( - buildQuestionnaireResponse(responseInput(value)), + buildQuestionnaireResponse(responseInput(value), questionnaire), ) const result = preflightQuestionnairePair(questionnaire, candidate) return result.ok diff --git a/packages/fhir/test/questionnaire-preflight.test.ts b/packages/fhir/test/questionnaire-preflight.test.ts index e277880e..dac47ade 100644 --- a/packages/fhir/test/questionnaire-preflight.test.ts +++ b/packages/fhir/test/questionnaire-preflight.test.ts @@ -316,16 +316,18 @@ describe('Questionnaire pair preflight', () => { }), ) const valueSetResponse = unwrap( - buildQuestionnaireResponse({ - ...responseInput(), - items: [ - { - linkId: 'mood', - text: 'Mood', - answer: [{ valueCoding: { system: moodSystem, code: 'calm' } }], - }, - ], - }), + buildQuestionnaireResponse( + { + ...responseInput(), + items: [ + { + linkId: 'mood', + answer: [{ valueCoding: { system: moodSystem, code: 'calm' } }], + }, + ], + }, + valueSetQuestionnaire, + ), ) expect( preflightQuestionnairePair(valueSetQuestionnaire, valueSetResponse).ok, diff --git a/packages/fhir/test/questionnaire-test-support.ts b/packages/fhir/test/questionnaire-test-support.ts index d90fa979..cfa02e9a 100644 --- a/packages/fhir/test/questionnaire-test-support.ts +++ b/packages/fhir/test/questionnaire-test-support.ts @@ -10,14 +10,12 @@ import { buildQuestionnaire, buildQuestionnaireResponse, parseAbsoluteUri, - parseCanonical, parseFhirInstant, parseIdentifierSystem, parsePatientReference, parseSemVer, preflightQuestionnairePair, type AbsoluteUri, - type Canonical, type FhirInstant, type GroveQuestionnaire, type GroveQuestionnaireResponse, @@ -41,9 +39,6 @@ export const url: AbsoluteUri = unwrap( parseAbsoluteUri('https://example.org/fhir/Questionnaire/pain'), ) export const version: SemVer = unwrap(parseSemVer('2.0.0')) -export const questionnaireCanonical: Canonical = unwrap( - parseCanonical(`${url}|${version}`), -) export const authored: FhirInstant = unwrap( parseFhirInstant('2026-08-20T12:00:00-07:00'), ) @@ -127,6 +122,7 @@ export const targetConstraint = ( export const questionnaireInput: QuestionnaireInput = { url, version, + language: 'en-US', name: 'PainQuestionnaire', title: 'Pain Questionnaire', status: 'active', @@ -176,7 +172,7 @@ export const questionnaireInput: QuestionnaireInput = { export const responseInput = ( note = 'Rest helps.', ): QuestionnaireResponseInput => ({ - questionnaire: questionnaireCanonical, + language: 'en-US', identifier: { system: unwrap(parseIdentifierSystem('https://example.org/submissions')), value: 'submission-1', @@ -187,18 +183,15 @@ export const responseInput = ( items: [ { linkId: 'health', - text: 'Health', item: [ { linkId: 'has-pain', - text: 'Are you in pain?', answer: [ { valueBoolean: true, item: [ { linkId: 'pain-severity', - text: 'How severe is the pain?', answer: [ { valueCoding: { system: codingSystem, code: 'mild' }, @@ -213,7 +206,6 @@ export const responseInput = ( }, { linkId: 'notes', - text: 'Additional notes', answer: [{ valueString: note }], }, ], @@ -223,9 +215,18 @@ export const questionnaire: GroveQuestionnaire = unwrap( buildQuestionnaire(questionnaireInput), ) export const response: GroveQuestionnaireResponse = unwrap( - buildQuestionnaireResponse(responseInput()), + buildQuestionnaireResponse(responseInput(), questionnaire), ) +/** Declares one string item, so a response builder test reaches the rule it names. */ +export const questionnaireDeclaring = (linkId: string): GroveQuestionnaire => + unwrap( + buildQuestionnaire({ + ...questionnaireInput, + items: [{ linkId, text: linkId, type: 'string' }], + }), + ) + export const preflightItems = ( questionnaireItems: ReadonlyArray>>, responseItems: ReadonlyArray>>, diff --git a/packages/fhir/test/readme-walkthrough.test.ts b/packages/fhir/test/readme-walkthrough.test.ts index 9d431e7d..259f4b4d 100644 --- a/packages/fhir/test/readme-walkthrough.test.ts +++ b/packages/fhir/test/readme-walkthrough.test.ts @@ -28,6 +28,7 @@ import { parseIdentifierSystem, parseKeyEpoch, parseObservation, + parseSemVer, retractionTargets, validateOpaqueIdentityScope, type ApplicationDevice, @@ -44,6 +45,11 @@ import { type NormalizedProviderRecord, } from '../src/providers/index.js' import * as questionnaire from '../src/questionnaire/index.js' +import { + buildQuestionnaire, + buildQuestionnaireResponse, + preflightQuestionnairePair, +} from '../src/questionnaire/index.js' import * as r4 from '../src/r4/index.js' import * as zodR4 from '../src/zod/r4/index.js' import * as zodR4b from '../src/zod/r4b/index.js' @@ -329,6 +335,55 @@ const value = observationNumericValue(observation) const label = codeableConceptDisplay(observation.code) // readme:end extract-values +// readme:begin questionnaire +const translation = (lang: string, content: string) => ({ + url: 'http://hl7.org/fhir/StructureDefinition/translation', + extension: [ + { url: 'lang', valueCode: lang }, + { url: 'content', valueString: content }, + ], +}) + +// One Questionnaire carries every language: base strings plus their translations. +const instrument = unwrap( + buildQuestionnaire({ + url: uri('https://mystudy.example.org/fhir/Questionnaire/wellbeing'), + version: unwrap(parseSemVer('1.0.0')), + language: 'en-US', + status: 'active', + subjectTypes: ['Patient'], + items: [ + { + linkId: 'feeling', + text: 'How are you feeling today?', + _text: { extension: [translation('es', '¿Cómo se siente hoy?')] }, + type: 'string', + required: true, + }, + ], + }), +) + +// The participant read Spanish, so the response names it and repeats no base text. +const answers = unwrap( + buildQuestionnaireResponse( + { + language: 'es', + identifier: { + system: identifierSystem('https://mystudy.example.org/fhir/responses'), + value: 'wellbeing-1', + }, + status: 'completed', + subject: { type: 'Patient', reference: 'Patient/participant-1' }, + authored: unwrap(parseFhirInstant('2026-08-19T17:30:00Z')), + items: [{ linkId: 'feeling', answer: [{ valueString: 'Bien' }] }], + }, + instrument, + ), +) +const pair = preflightQuestionnairePair(instrument, answers) +// readme:end questionnaire + const readme = readFileSync(new URL('../README.md', import.meta.url), 'utf8') const source = readFileSync(new URL(import.meta.url), 'utf8') interface EntryPoint { @@ -452,6 +507,13 @@ describe('README walkthrough', () => { expect(parseExchangeGraph(graph).ok).toBe(true) }) + it('answers a multilingual Questionnaire in a translation', () => { + expect(instrument.item[0]?._text?.extension).toHaveLength(1) + expect(answers.language).toBe('es') + expect(answers.item?.[0]?.text).toBeUndefined() + expect(pair.ok).toBe(true) + }) + it('keeps every README code block in this file', () => { const blocks = readmeBlocks() expect(blocks.length).toBeGreaterThan(8) diff --git a/packages/fhir/test/type-surface.json b/packages/fhir/test/type-surface.json index af93409d..42983daf 100644 --- a/packages/fhir/test/type-surface.json +++ b/packages/fhir/test/type-surface.json @@ -8,7 +8,7 @@ "BloodPressureMeasurement": "interface BloodPressureMeasurement {\n readonly kind: 'blood-pressure';\n readonly systolic: number;\n readonly diastolic: number;\n readonly effective: InstantEffectiveTime;\n}", "boundsToFhirPeriod": "const boundsToFhirPeriod: (bounds: PeriodBounds) => {\n readonly start?: string;\n readonly end?: string;\n}", "buildQuestionnaire": "const buildQuestionnaire: (input: QuestionnaireInput) => Result", - "buildQuestionnaireResponse": "const buildQuestionnaireResponse: (input: QuestionnaireResponseInput) => Result", + "buildQuestionnaireResponse": "const buildQuestionnaireResponse: (input: QuestionnaireResponseInput, questionnaire: GroveQuestionnaire) => Result", "BusinessIdentifier": "interface BusinessIdentifier {\n readonly system: IdentifierSystem;\n readonly value: string;\n}", "Canonical": "type Canonical = Branded<'Canonical'>;", "canonicalizeMobileEffectiveInstant": "const canonicalizeMobileEffectiveInstant: (value: unknown) => Result", @@ -91,8 +91,8 @@ "groveMobileProfileCanonicals": "const groveMobileProfileCanonicals: typeof groveMobileProfileCanonicalsValue", "groveProducerDiagnostics": "const groveProducerDiagnostics: typeof groveProducerDiagnosticsValue", "groveProfileClaims": "const groveProfileClaims: typeof groveProfileClaimsValue", - "GroveQuestionnaire": "type GroveQuestionnaire = Readonly & {\n readonly item: NonNullable;\n}>;", - "GroveQuestionnaireResponse": "type GroveQuestionnaireResponse = Readonly & {\n readonly identifier: Identifier;\n readonly questionnaire: string;\n readonly authored: string;\n readonly item?: QuestionnaireResponse['item'];\n}>;", + "GroveQuestionnaire": "type GroveQuestionnaire = Readonly & {\n readonly language: string;\n readonly item: NonNullable;\n}>;", + "GroveQuestionnaireResponse": "type GroveQuestionnaireResponse = Readonly & {\n readonly language: string;\n readonly identifier: Identifier;\n readonly questionnaire: string;\n readonly authored: string;\n readonly item?: QuestionnaireResponse['item'];\n}>;", "GroveRecordingFormat": "type GroveRecordingFormat = keyof typeof groveRecordingFormatRegistry.formats;", "groveRecordingFormatRegistry": "const groveRecordingFormatRegistry: typeof groveRecordingFormatRegistryValue", "HostDevice": "interface HostDevice {\n /**\n * Recommended as `|`, such as `iPhone17,1|26.0`;\n * never emitted in clear.\n */\n readonly sourceDeviceToken: string;\n readonly operatingSystemVersion: string;\n readonly name?: string | undefined;\n readonly manufacturer?: string | undefined;\n readonly modelNumber?: string | undefined;\n}", @@ -210,14 +210,14 @@ "Provenance": "type Provenance = ReadonlyDeep>;", "QuantityComparator": "type QuantityComparator = '<' | '<=' | '>=' | '>';", "QuantityValue": "interface QuantityValue {\n readonly value: number;\n /** The unit's code when the Quantity states one, and its display unit otherwise. */\n readonly unit: string;\n /** The system the unit code belongs to, so a UCUM unit is distinguishable from a local one. */\n readonly system?: string;\n /** Stated only when the Quantity names a bound; dropping it would misreport the measurement. */\n readonly comparator?: QuantityComparator;\n}", - "QuestionnaireInput": "interface QuestionnaireInput {\n readonly id?: FhirId;\n readonly url: AbsoluteUri;\n readonly version: SemVer;\n readonly name?: string;\n readonly title?: string;\n readonly status: Questionnaire['status'];\n /** Fixed by the Grove Questionnaire profile to exactly `Patient`. */\n readonly subjectTypes: readonly ['Patient'];\n readonly date?: FhirInstant;\n readonly description?: string;\n readonly purpose?: string;\n readonly extensions?: readonly Extension[];\n readonly items: readonly [QuestionnaireItemInput, ...QuestionnaireItemInput[]];\n}", - "QuestionnaireItemInput": "interface QuestionnaireItemInput extends BackboneInput {\n readonly linkId: string;\n readonly definition?: string;\n readonly code?: readonly Coding[];\n readonly prefix?: string;\n readonly text?: string;\n readonly type: QuestionnaireItemType;\n readonly enableWhen?: readonly QuestionnaireEnableWhenInput[];\n readonly enableBehavior?: 'all' | 'any';\n readonly required?: boolean;\n readonly repeats?: boolean;\n readonly readOnly?: boolean;\n readonly maxLength?: number;\n readonly answerValueSet?: string;\n readonly answerOption?: readonly QuestionnaireAnswerOptionInput[];\n readonly initial?: readonly QuestionnaireInitialInput[];\n readonly item?: readonly QuestionnaireItemInput[];\n}", + "QuestionnaireInput": "interface QuestionnaireInput {\n readonly id?: FhirId;\n readonly url: AbsoluteUri;\n readonly version: SemVer;\n /** BCP 47 tag of every base string; other languages are `translation` extensions. */\n readonly language: string;\n readonly name?: string;\n readonly title?: string;\n readonly _title?: PrimitiveElementInput;\n readonly status: Questionnaire['status'];\n /** Fixed by the Grove Questionnaire profile to exactly `Patient`. */\n readonly subjectTypes: readonly ['Patient'];\n readonly date?: FhirInstant;\n readonly description?: string;\n readonly _description?: PrimitiveElementInput;\n readonly purpose?: string;\n readonly extensions?: readonly Extension[];\n readonly items: readonly [QuestionnaireItemInput, ...QuestionnaireItemInput[]];\n}", + "QuestionnaireItemInput": "interface QuestionnaireItemInput extends BackboneInput {\n readonly linkId: string;\n readonly definition?: string;\n readonly code?: readonly Coding[];\n readonly prefix?: string;\n readonly _prefix?: PrimitiveElementInput;\n /** Base-language text; other languages are `translation` extensions on `_text`. */\n readonly text?: string;\n readonly _text?: PrimitiveElementInput;\n readonly type: QuestionnaireItemType;\n readonly enableWhen?: readonly QuestionnaireEnableWhenInput[];\n readonly enableBehavior?: 'all' | 'any';\n readonly required?: boolean;\n readonly repeats?: boolean;\n readonly readOnly?: boolean;\n readonly maxLength?: number;\n readonly answerValueSet?: string;\n readonly answerOption?: readonly QuestionnaireAnswerOptionInput[];\n readonly initial?: readonly QuestionnaireInitialInput[];\n readonly item?: readonly QuestionnaireItemInput[];\n}", "QuestionnairePair": "interface QuestionnairePair {\n readonly questionnaire: GroveQuestionnaire;\n readonly response: GroveQuestionnaireResponse;\n}", "QuestionnairePreflightOptions": "interface QuestionnairePreflightOptions {\n readonly valueSets?: readonly ResolvedQuestionnaireValueSetInput[];\n}", "QuestionnaireResponseAnswerInput": "type QuestionnaireResponseAnswerInput = WithBackbone;", "QuestionnaireResponseAuthorInput": "type QuestionnaireResponseAuthorInput = TypedQuestionnaireResponseReference<'Device' | 'Organization' | 'Patient' | 'Practitioner' | 'PractitionerRole' | 'RelatedPerson'>;", - "QuestionnaireResponseInput": "interface QuestionnaireResponseInput {\n readonly id?: FhirId;\n /** Exact `Questionnaire.url|Questionnaire.version` canonical. */\n readonly questionnaire: Canonical;\n readonly identifier: QuestionnaireResponseIdentifierInput;\n readonly status: QuestionnaireResponse['status'];\n readonly subject: QuestionnaireResponseSubjectInput;\n readonly authored: FhirInstant;\n readonly author?: QuestionnaireResponseAuthorInput;\n readonly source?: QuestionnaireResponseSourceInput;\n readonly extensions?: readonly Extension[];\n readonly items?: readonly QuestionnaireResponseItemInput[];\n}", - "QuestionnaireResponseItemInput": "interface QuestionnaireResponseItemInput extends BackboneInput {\n readonly linkId: string;\n readonly definition?: string;\n readonly text?: string;\n readonly answer?: readonly QuestionnaireResponseAnswerInput[];\n readonly item?: readonly QuestionnaireResponseItemInput[];\n}", + "QuestionnaireResponseInput": "interface QuestionnaireResponseInput {\n readonly id?: FhirId;\n /** BCP 47 tag the participant saw: the Questionnaire's base language or one of its translations. */\n readonly language: string;\n readonly identifier: QuestionnaireResponseIdentifierInput;\n readonly status: QuestionnaireResponse['status'];\n readonly subject: QuestionnaireResponseSubjectInput;\n readonly authored: FhirInstant;\n readonly author?: QuestionnaireResponseAuthorInput;\n readonly source?: QuestionnaireResponseSourceInput;\n readonly extensions?: readonly Extension[];\n readonly items?: readonly QuestionnaireResponseItemInput[];\n}", + "QuestionnaireResponseItemInput": "interface QuestionnaireResponseItemInput extends BackboneInput {\n readonly linkId: string;\n readonly definition?: string;\n readonly answer?: readonly QuestionnaireResponseAnswerInput[];\n readonly item?: readonly QuestionnaireResponseItemInput[];\n}", "QuestionnaireResponseSourceInput": "type QuestionnaireResponseSourceInput = TypedQuestionnaireResponseReference<'Patient' | 'Practitioner' | 'PractitionerRole' | 'RelatedPerson'>;", "QuestionnaireResponseSubjectInput": "type QuestionnaireResponseSubjectInput = TypedQuestionnaireResponseReference<'Patient'>;", "R4CollectionBundle": "type R4CollectionBundle = ReadonlyDeep> & {\n readonly type: 'collection';\n}>;", @@ -501,20 +501,20 @@ }, "./questionnaire": { "buildQuestionnaire": "const buildQuestionnaire: (input: QuestionnaireInput) => Result", - "buildQuestionnaireResponse": "const buildQuestionnaireResponse: (input: QuestionnaireResponseInput) => Result", + "buildQuestionnaireResponse": "const buildQuestionnaireResponse: (input: QuestionnaireResponseInput, questionnaire: GroveQuestionnaire) => Result", "groveFhirVersion": "const groveFhirVersion: \"4.0.1\"", - "GroveQuestionnaire": "type GroveQuestionnaire = Readonly & {\n readonly item: NonNullable;\n}>;", + "GroveQuestionnaire": "type GroveQuestionnaire = Readonly & {\n readonly language: string;\n readonly item: NonNullable;\n}>;", "groveQuestionnairePackageMetadata": "const groveQuestionnairePackageMetadata: typeof groveQuestionnairePackageMetadataValue", - "GroveQuestionnaireResponse": "type GroveQuestionnaireResponse = Readonly & {\n readonly identifier: Identifier;\n readonly questionnaire: string;\n readonly authored: string;\n readonly item?: QuestionnaireResponse['item'];\n}>;", + "GroveQuestionnaireResponse": "type GroveQuestionnaireResponse = Readonly & {\n readonly language: string;\n readonly identifier: Identifier;\n readonly questionnaire: string;\n readonly authored: string;\n readonly item?: QuestionnaireResponse['item'];\n}>;", "parseQuestionnaire": "const parseQuestionnaire: (input: unknown) => Result", "parseQuestionnaireResponse": "const parseQuestionnaireResponse: (input: unknown) => Result", "preflightQuestionnairePair": "const preflightQuestionnairePair: (questionnaireInput: unknown, responseInput: unknown, options?: QuestionnairePreflightOptions) => Result", - "QuestionnaireAnswerOptionInput": "type QuestionnaireAnswerOptionInput = WithBackbone, {\n readonly initialSelected?: boolean;\n}>;", + "QuestionnaireAnswerOptionInput": "type QuestionnaireAnswerOptionInput = WithBackbone, {\n readonly _valueString?: PrimitiveElementInput;\n readonly initialSelected?: boolean;\n}>;", "QuestionnaireAnswerValueInput": "type QuestionnaireAnswerValueInput = ExactlyOne;", "QuestionnaireEnableWhenInput": "type QuestionnaireEnableWhenInput = WithBackbone, {\n readonly question: string;\n readonly operator: '!=' | '<' | '<=' | '=' | '>' | '>=' | 'exists';\n}>;", "QuestionnaireInitialInput": "type QuestionnaireInitialInput = WithBackbone;", - "QuestionnaireInput": "interface QuestionnaireInput {\n readonly id?: FhirId;\n readonly url: AbsoluteUri;\n readonly version: SemVer;\n readonly name?: string;\n readonly title?: string;\n readonly status: Questionnaire['status'];\n /** Fixed by the Grove Questionnaire profile to exactly `Patient`. */\n readonly subjectTypes: readonly ['Patient'];\n readonly date?: FhirInstant;\n readonly description?: string;\n readonly purpose?: string;\n readonly extensions?: readonly Extension[];\n readonly items: readonly [QuestionnaireItemInput, ...QuestionnaireItemInput[]];\n}", - "QuestionnaireItemInput": "interface QuestionnaireItemInput extends BackboneInput {\n readonly linkId: string;\n readonly definition?: string;\n readonly code?: readonly Coding[];\n readonly prefix?: string;\n readonly text?: string;\n readonly type: QuestionnaireItemType;\n readonly enableWhen?: readonly QuestionnaireEnableWhenInput[];\n readonly enableBehavior?: 'all' | 'any';\n readonly required?: boolean;\n readonly repeats?: boolean;\n readonly readOnly?: boolean;\n readonly maxLength?: number;\n readonly answerValueSet?: string;\n readonly answerOption?: readonly QuestionnaireAnswerOptionInput[];\n readonly initial?: readonly QuestionnaireInitialInput[];\n readonly item?: readonly QuestionnaireItemInput[];\n}", + "QuestionnaireInput": "interface QuestionnaireInput {\n readonly id?: FhirId;\n readonly url: AbsoluteUri;\n readonly version: SemVer;\n /** BCP 47 tag of every base string; other languages are `translation` extensions. */\n readonly language: string;\n readonly name?: string;\n readonly title?: string;\n readonly _title?: PrimitiveElementInput;\n readonly status: Questionnaire['status'];\n /** Fixed by the Grove Questionnaire profile to exactly `Patient`. */\n readonly subjectTypes: readonly ['Patient'];\n readonly date?: FhirInstant;\n readonly description?: string;\n readonly _description?: PrimitiveElementInput;\n readonly purpose?: string;\n readonly extensions?: readonly Extension[];\n readonly items: readonly [QuestionnaireItemInput, ...QuestionnaireItemInput[]];\n}", + "QuestionnaireItemInput": "interface QuestionnaireItemInput extends BackboneInput {\n readonly linkId: string;\n readonly definition?: string;\n readonly code?: readonly Coding[];\n readonly prefix?: string;\n readonly _prefix?: PrimitiveElementInput;\n /** Base-language text; other languages are `translation` extensions on `_text`. */\n readonly text?: string;\n readonly _text?: PrimitiveElementInput;\n readonly type: QuestionnaireItemType;\n readonly enableWhen?: readonly QuestionnaireEnableWhenInput[];\n readonly enableBehavior?: 'all' | 'any';\n readonly required?: boolean;\n readonly repeats?: boolean;\n readonly readOnly?: boolean;\n readonly maxLength?: number;\n readonly answerValueSet?: string;\n readonly answerOption?: readonly QuestionnaireAnswerOptionInput[];\n readonly initial?: readonly QuestionnaireInitialInput[];\n readonly item?: readonly QuestionnaireItemInput[];\n}", "questionnaireItemSchema": "const questionnaireItemSchema: z.ZodType>", "QuestionnaireItemType": "type QuestionnaireItemType = 'attachment' | 'boolean' | 'choice' | 'date' | 'dateTime' | 'decimal' | 'display' | 'group' | 'integer' | 'open-choice' | 'quantity' | 'string' | 'text' | 'time' | 'url';", "QuestionnairePair": "interface QuestionnairePair {\n readonly questionnaire: GroveQuestionnaire;\n readonly response: GroveQuestionnaireResponse;\n}", @@ -523,8 +523,8 @@ "questionnaireResponseAnswerSchema": "const questionnaireResponseAnswerSchema: z.ZodType>", "QuestionnaireResponseAuthorInput": "type QuestionnaireResponseAuthorInput = TypedQuestionnaireResponseReference<'Device' | 'Organization' | 'Patient' | 'Practitioner' | 'PractitionerRole' | 'RelatedPerson'>;", "QuestionnaireResponseIdentifierInput": "interface QuestionnaireResponseIdentifierInput {\n readonly system: IdentifierSystem;\n readonly value: string;\n}", - "QuestionnaireResponseInput": "interface QuestionnaireResponseInput {\n readonly id?: FhirId;\n /** Exact `Questionnaire.url|Questionnaire.version` canonical. */\n readonly questionnaire: Canonical;\n readonly identifier: QuestionnaireResponseIdentifierInput;\n readonly status: QuestionnaireResponse['status'];\n readonly subject: QuestionnaireResponseSubjectInput;\n readonly authored: FhirInstant;\n readonly author?: QuestionnaireResponseAuthorInput;\n readonly source?: QuestionnaireResponseSourceInput;\n readonly extensions?: readonly Extension[];\n readonly items?: readonly QuestionnaireResponseItemInput[];\n}", - "QuestionnaireResponseItemInput": "interface QuestionnaireResponseItemInput extends BackboneInput {\n readonly linkId: string;\n readonly definition?: string;\n readonly text?: string;\n readonly answer?: readonly QuestionnaireResponseAnswerInput[];\n readonly item?: readonly QuestionnaireResponseItemInput[];\n}", + "QuestionnaireResponseInput": "interface QuestionnaireResponseInput {\n readonly id?: FhirId;\n /** BCP 47 tag the participant saw: the Questionnaire's base language or one of its translations. */\n readonly language: string;\n readonly identifier: QuestionnaireResponseIdentifierInput;\n readonly status: QuestionnaireResponse['status'];\n readonly subject: QuestionnaireResponseSubjectInput;\n readonly authored: FhirInstant;\n readonly author?: QuestionnaireResponseAuthorInput;\n readonly source?: QuestionnaireResponseSourceInput;\n readonly extensions?: readonly Extension[];\n readonly items?: readonly QuestionnaireResponseItemInput[];\n}", + "QuestionnaireResponseItemInput": "interface QuestionnaireResponseItemInput extends BackboneInput {\n readonly linkId: string;\n readonly definition?: string;\n readonly answer?: readonly QuestionnaireResponseAnswerInput[];\n readonly item?: readonly QuestionnaireResponseItemInput[];\n}", "questionnaireResponseItemSchema": "const questionnaireResponseItemSchema: z.ZodType>", "questionnaireResponseSchema": "const questionnaireResponseSchema: z.ZodType", "QuestionnaireResponseSourceInput": "type QuestionnaireResponseSourceInput = TypedQuestionnaireResponseReference<'Patient' | 'Practitioner' | 'PractitionerRole' | 'RelatedPerson'>;", From 3e0d6fba041c6b7e160d913762cc99a00bcb800a Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Wed, 23 Sep 2026 09:45:08 -0700 Subject: [PATCH 28/31] Validate a Translated Questionnaire Response in the Conformance Fixtures --- packages/fhir/README.md | 2 +- .../fhir/fixtures/conformance/manifest.json | 6 + .../questionnaire-response-translated.json | 44 ++++++++ ...tionnaire-response-translated.json.license | 3 + .../conformance/resources/questionnaire.json | 51 ++++++++- .../scripts/generate-conformance-fixtures.mjs | 103 +++++++++++------- .../fhir/scripts/validate-conformance.mjs | 32 +++--- 7 files changed, 187 insertions(+), 54 deletions(-) create mode 100644 packages/fhir/fixtures/conformance/resources/questionnaire-response-translated.json create mode 100644 packages/fhir/fixtures/conformance/resources/questionnaire-response-translated.json.license diff --git a/packages/fhir/README.md b/packages/fhir/README.md index 3d86980a..b43b1180 100644 --- a/packages/fhir/README.md +++ b/packages/fhir/README.md @@ -971,7 +971,7 @@ npm run conformance:structural -- --ig /path/to/grove-fhir ``` `npm run conformance` additionally requires the checksum-pinned official FHIR Validator and the Mobile, Sensor, Provider, Questionnaire, Google Health, Oura, and Withings packages built in that checkout. -It validates the generated Mobile measurement graphs, the study-context graph, every admitted native-recording graph, both retraction events, the shared active/retraction exchange corpus, and the Questionnaire/Response pair. +It validates the generated Mobile measurement graphs, the study-context graph, every admitted native-recording graph, both retraction events, the shared active/retraction exchange corpus, and a multilingual Questionnaire with one base-language and one translated response. Every admitted scalar fixture is generated from the IG's versioned Mobile semantic corpus and bound through an exact RFC 6901 resource pointer, so the generic producer kit compares the emitted profile, clinical code, effective time, and result projection, not only the resource shape. The unit tests bind the same contract from the committed fixtures: the protocol vectors and the derived identifier systems, every negative case of the mobile-exchange corpus with its exact code and location, every equality decision of the receiver-lifecycle sequences, the study-attribution events, and the registry's severities. diff --git a/packages/fhir/fixtures/conformance/manifest.json b/packages/fhir/fixtures/conformance/manifest.json index c3655aa0..ab7c9e56 100644 --- a/packages/fhir/fixtures/conformance/manifest.json +++ b/packages/fhir/fixtures/conformance/manifest.json @@ -393,6 +393,12 @@ "requiredProfiles": [ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] + }, + { + "path": "resources/questionnaire-response-translated.json", + "requiredProfiles": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] } ], "semanticVectors": [ diff --git a/packages/fhir/fixtures/conformance/resources/questionnaire-response-translated.json b/packages/fhir/fixtures/conformance/resources/questionnaire-response-translated.json new file mode 100644 index 00000000..45429db5 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/questionnaire-response-translated.json @@ -0,0 +1,44 @@ +{ + "resourceType": "QuestionnaireResponse", + "meta": { + "profile": [ + "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" + ] + }, + "language": "es", + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationMode", + "code": "ELECTRONIC" + } + ] + } + } + ], + "identifier": { + "system": "https://grovealliance.org/fhir/testing/questionnaire-responses", + "value": "grove-ts-conformance-response-es" + }, + "questionnaire": "https://grovealliance.org/fhir/testing/Questionnaire/grove-ts-conformance|1.0.0", + "status": "completed", + "subject": { "reference": "Patient/example", "type": "Patient" }, + "authored": "2026-08-20T12:00:00Z", + "item": [ + { + "linkId": "wellbeing", + "answer": [ + { + "valueCoding": { + "system": "https://grovealliance.org/fhir/testing/CodeSystem/wellbeing", + "code": "well", + "display": "Well" + } + } + ] + } + ] +} diff --git a/packages/fhir/fixtures/conformance/resources/questionnaire-response-translated.json.license b/packages/fhir/fixtures/conformance/resources/questionnaire-response-translated.json.license new file mode 100644 index 00000000..fa4e8d92 --- /dev/null +++ b/packages/fhir/fixtures/conformance/resources/questionnaire-response-translated.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md) + +SPDX-License-Identifier: MIT diff --git a/packages/fhir/fixtures/conformance/resources/questionnaire.json b/packages/fhir/fixtures/conformance/resources/questionnaire.json index 3338ef7e..3b960ddd 100644 --- a/packages/fhir/fixtures/conformance/resources/questionnaire.json +++ b/packages/fhir/fixtures/conformance/resources/questionnaire.json @@ -19,12 +19,37 @@ "version": "1.0.0", "name": "GroveTsConformance", "title": "Grove TypeScript conformance instrument", + "_title": { + "extension": [ + { + "extension": [ + { "url": "lang", "valueCode": "es" }, + { + "url": "content", + "valueString": "Instrumento de conformidad de Grove TypeScript" + } + ], + "url": "http://hl7.org/fhir/StructureDefinition/translation" + } + ] + }, "status": "active", "subjectType": ["Patient"], "item": [ { "linkId": "wellbeing", "text": "How are you feeling?", + "_text": { + "extension": [ + { + "extension": [ + { "url": "lang", "valueCode": "es" }, + { "url": "content", "valueString": "¿Cómo se siente?" } + ], + "url": "http://hl7.org/fhir/StructureDefinition/translation" + } + ] + }, "type": "choice", "required": true, "answerOption": [ @@ -32,14 +57,36 @@ "valueCoding": { "system": "https://grovealliance.org/fhir/testing/CodeSystem/wellbeing", "code": "well", - "display": "Well" + "display": "Well", + "_display": { + "extension": [ + { + "extension": [ + { "url": "lang", "valueCode": "es" }, + { "url": "content", "valueString": "Bien" } + ], + "url": "http://hl7.org/fhir/StructureDefinition/translation" + } + ] + } } }, { "valueCoding": { "system": "https://grovealliance.org/fhir/testing/CodeSystem/wellbeing", "code": "unwell", - "display": "Unwell" + "display": "Unwell", + "_display": { + "extension": [ + { + "extension": [ + { "url": "lang", "valueCode": "es" }, + { "url": "content", "valueString": "Mal" } + ], + "url": "http://hl7.org/fhir/StructureDefinition/translation" + } + ] + } } } ] diff --git a/packages/fhir/scripts/generate-conformance-fixtures.mjs b/packages/fhir/scripts/generate-conformance-fixtures.mjs index f7a11fe9..42b45ebe 100644 --- a/packages/fhir/scripts/generate-conformance-fixtures.mjs +++ b/packages/fhir/scripts/generate-conformance-fixtures.mjs @@ -590,6 +590,20 @@ const questionnaireUrl = uri( 'https://grovealliance.org/fhir/testing/Questionnaire/grove-ts-conformance', ) const questionnaireVersion = unwrap(parseSemVer('1.0.0')) +const wellbeingSystem = uri( + 'https://grovealliance.org/fhir/testing/CodeSystem/wellbeing', +) +const inSpanish = (content) => ({ + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/translation', + extension: [ + { url: 'lang', valueCode: 'es' }, + { url: 'content', valueString: content }, + ], + }, + ], +}) const questionnaire = unwrap( buildQuestionnaire({ url: questionnaireUrl, @@ -597,31 +611,31 @@ const questionnaire = unwrap( language: 'en-US', name: 'GroveTsConformance', title: 'Grove TypeScript conformance instrument', + _title: inSpanish('Instrumento de conformidad de Grove TypeScript'), status: 'active', subjectTypes: ['Patient'], items: [ { linkId: 'wellbeing', text: 'How are you feeling?', + _text: inSpanish('¿Cómo se siente?'), type: 'choice', required: true, answerOption: [ { valueCoding: { - system: uri( - 'https://grovealliance.org/fhir/testing/CodeSystem/wellbeing', - ), + system: wellbeingSystem, code: 'well', display: 'Well', + _display: inSpanish('Bien'), }, }, { valueCoding: { - system: uri( - 'https://grovealliance.org/fhir/testing/CodeSystem/wellbeing', - ), + system: wellbeingSystem, code: 'unwell', display: 'Unwell', + _display: inSpanish('Mal'), }, }, ], @@ -629,38 +643,45 @@ const questionnaire = unwrap( ], }), ) -const questionnaireResponse = unwrap( - buildQuestionnaireResponse( - { - language: 'en-US', - identifier: { - system: identifierSystem( - 'https://grovealliance.org/fhir/testing/questionnaire-responses', - ), - value: 'grove-ts-conformance-response', - }, - status: 'completed', - subject: { type: 'Patient', reference: 'Patient/example' }, - authored: instant('2026-08-20T12:00:00Z'), - items: [ - { - linkId: 'wellbeing', - answer: [ - { - valueCoding: { - system: uri( - 'https://grovealliance.org/fhir/testing/CodeSystem/wellbeing', - ), - code: 'well', - display: 'Well', - }, - }, - ], +const questionnaireResponseIn = (language, value) => + unwrap( + buildQuestionnaireResponse( + { + language, + identifier: { + system: identifierSystem( + 'https://grovealliance.org/fhir/testing/questionnaire-responses', + ), + value, }, - ], - }, - questionnaire, - ), + status: 'completed', + subject: { type: 'Patient', reference: 'Patient/example' }, + authored: instant('2026-08-20T12:00:00Z'), + items: [ + { + linkId: 'wellbeing', + answer: [ + { + valueCoding: { + system: wellbeingSystem, + code: 'well', + display: 'Well', + }, + }, + ], + }, + ], + }, + questionnaire, + ), + ) +const questionnaireResponse = questionnaireResponseIn( + 'en-US', + 'grove-ts-conformance-response', +) +const translatedQuestionnaireResponse = questionnaireResponseIn( + 'es', + 'grove-ts-conformance-response-es', ) const resources = new Map( @@ -790,6 +811,10 @@ resources.set( ) resources.set('resources/questionnaire.json', questionnaire) resources.set('resources/questionnaire-response.json', questionnaireResponse) +resources.set( + 'resources/questionnaire-response-translated.json', + translatedQuestionnaireResponse, +) for (const [path, text] of sharedCorpora) resources.set(path, JSON.parse(text)) const abstractProviderObservationProfile = providerAdapterCatalog.adapterProfile @@ -887,6 +912,10 @@ const manifest = { path: 'resources/questionnaire-response.json', requiredProfiles: [questionnaireResponseProfile], }, + { + path: 'resources/questionnaire-response-translated.json', + requiredProfiles: [questionnaireResponseProfile], + }, ], semanticVectors: semanticVectors.map(({ id }) => ({ id, diff --git a/packages/fhir/scripts/validate-conformance.mjs b/packages/fhir/scripts/validate-conformance.mjs index 6b110b93..e55e5f23 100644 --- a/packages/fhir/scripts/validate-conformance.mjs +++ b/packages/fhir/scripts/validate-conformance.mjs @@ -190,18 +190,23 @@ if (structuralOnly) { run('python3', producerArguments, igRoot) const questionnaire = resolve(fixtureRoot, 'resources/questionnaire.json') -const response = resolve(fixtureRoot, 'resources/questionnaire-response.json') -run( - 'python3', - [ - resolve(igRoot, 'Scripts/validate-questionnaire.py'), - '--questionnaire', - questionnaire, - '--response', - response, - ], - igRoot, -) +const responses = [ + 'resources/questionnaire-response.json', + 'resources/questionnaire-response-translated.json', +].map((path) => resolve(fixtureRoot, path)) +for (const response of responses) { + run( + 'python3', + [ + resolve(igRoot, 'Scripts/validate-questionnaire.py'), + '--questionnaire', + questionnaire, + '--response', + response, + ], + igRoot, + ) +} if (!structuralOnly) { run( @@ -214,8 +219,7 @@ if (!structuralOnly) { resolve(igRoot, 'questionnaire/output/package.tgz'), '--resource', questionnaire, - '--resource', - response, + ...responses.flatMap((response) => ['--resource', response]), '--allow-example-urls', ], igRoot, From a2b913a9f85353280a34ae1a05f36e27c1cec36e Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Wed, 23 Sep 2026 09:51:51 -0700 Subject: [PATCH 29/31] Compare Language Tags Case-Insensitively and Reject Translations Into the Base Language --- packages/fhir/README.md | 2 +- packages/fhir/src/questionnaire/builder.ts | 7 ++-- .../fhir/src/questionnaire/localization.ts | 35 ++++++++++--------- packages/fhir/src/questionnaire/preflight.ts | 4 +-- .../test/questionnaire-localization.test.ts | 22 +++++++++++- 5 files changed, 46 insertions(+), 24 deletions(-) diff --git a/packages/fhir/README.md b/packages/fhir/README.md index b43b1180..4392ba69 100644 --- a/packages/fhir/README.md +++ b/packages/fhir/README.md @@ -720,7 +720,7 @@ Both builders and both parsers enforce this, as does the exchange-graph validato One Questionnaire holds every language of an instrument under its one `url|version`. `language` is required and names the BCP 47 language of every base string. Each other language is a standard `translation` extension on the string's primitive element: `_title`, `_description`, an item's `_text` or `_prefix`, a coding's `_display`, or an extension's string value. -Each translation needs one BCP 47 `lang` and one non-empty `content`, and one element translates into each language at most once. +Each translation needs one BCP 47 `lang` other than the base language and one non-empty `content`, and one string translates into each language at most once; language tags compare case-insensitively. A `valueString` answer option is data, not display: a translation may sit on its `_valueString`, but a response always stores the base value. Changing a translation changes the content, so it needs a new `version`. diff --git a/packages/fhir/src/questionnaire/builder.ts b/packages/fhir/src/questionnaire/builder.ts index 6f784075..8a164b4a 100644 --- a/packages/fhir/src/questionnaire/builder.ts +++ b/packages/fhir/src/questionnaire/builder.ts @@ -11,7 +11,7 @@ import { validateQuestionnaireContract, validateQuestionnaireResponseItemContract, } from './contract.js' -import { isLanguageTag, offeredLanguages } from './localization.js' +import { isLanguageTag, offersLanguage } from './localization.js' import { parseQuestionnaire, parseQuestionnaireResponse } from './parse.js' import { prefixed } from './preflight-diagnostics.js' import { @@ -182,7 +182,7 @@ const validateResponseInput = ( questionnaire: GroveQuestionnaire, ): readonly Issue[] => { const failures: Issue[] = [] - if (!offeredLanguages(questionnaire).has(input.language)) { + if (!offersLanguage(questionnaire, input.language)) { failures.push( issue( 'value-mismatch', @@ -333,7 +333,8 @@ export const buildQuestionnaireResponse = ( : itemsWithText( validatedInput.items, instrument.item, - validatedInput.language === instrument.language, + validatedInput.language.toLowerCase() === + instrument.language.toLowerCase(), ['items'], failures, ) diff --git a/packages/fhir/src/questionnaire/localization.ts b/packages/fhir/src/questionnaire/localization.ts index 00b48203..ba1b1204 100644 --- a/packages/fhir/src/questionnaire/localization.ts +++ b/packages/fhir/src/questionnaire/localization.ts @@ -45,15 +45,16 @@ const translationLanguage = ( } interface QuestionnaireTranslations { - /** Every language a well-formed `translation` extension offers. */ + /** Every language a well-formed `translation` extension offers, lowercased. */ readonly languages: ReadonlySet readonly failures: readonly Issue[] } -/** Collects and checks the `translation` extensions on every element of a resource. */ -export const questionnaireTranslations = ( - resource: unknown, -): QuestionnaireTranslations => { +/** Collects and checks the `translation` extensions on every element of a Questionnaire. */ +export const questionnaireTranslations = (questionnaire: { + readonly language: string +}): QuestionnaireTranslations => { + const base = questionnaire.language.toLowerCase() const languages = new Set() const failures: Issue[] = [] const visit = (value: unknown, path: ReadonlyArray) => { @@ -67,11 +68,12 @@ export const questionnaireTranslations = ( const candidates: unknown = Reflect.get(value, 'extension') const extensions = Array.isArray(candidates) ? (candidates as ExtensionCandidate[]) : [] - const elementLanguages = new Set() + // BCP 47 tags compare case-insensitively. + const elementLanguages = new Set([base]) for (const [index, extension] of extensions.entries()) { if (extension.url !== QUESTIONNAIRE_EXTENSIONS.translation) continue const translationPath = [...path, 'extension', index] - const language = translationLanguage(extension) + const language = translationLanguage(extension)?.toLowerCase() if (language === undefined) { failures.push( issue( @@ -85,7 +87,7 @@ export const questionnaireTranslations = ( issue( 'duplicate-identifier', translationPath, - `Translation ${language} occurs more than once on one element.`, + `Translation ${language} repeats the base language or another translation of the same string.`, ), ) } else { @@ -97,15 +99,14 @@ export const questionnaireTranslations = ( visit(child, [...path, key]) } } - visit(resource, []) + visit(questionnaire, []) return { languages, failures } } -/** The base language and every translation language a Questionnaire offers. */ -export const offeredLanguages = (questionnaire: { - readonly language: string -}): ReadonlySet => - new Set([ - questionnaire.language, - ...questionnaireTranslations(questionnaire).languages, - ]) +/** Whether a language is the Questionnaire's base language or one of its translations. */ +export const offersLanguage = ( + questionnaire: { readonly language: string }, + language: string, +): boolean => + language.toLowerCase() === questionnaire.language.toLowerCase() || + questionnaireTranslations(questionnaire).languages.has(language.toLowerCase()) diff --git a/packages/fhir/src/questionnaire/preflight.ts b/packages/fhir/src/questionnaire/preflight.ts index d4fee93b..684a5a42 100644 --- a/packages/fhir/src/questionnaire/preflight.ts +++ b/packages/fhir/src/questionnaire/preflight.ts @@ -14,7 +14,7 @@ import { QUESTIONNAIRE_SYSTEMS, validateQuestionnaireContract, } from './contract.js' -import { offeredLanguages } from './localization.js' +import { offersLanguage } from './localization.js' import { isExactQuestionnaireCanonical, parseQuestionnaire, @@ -448,7 +448,7 @@ export const preflightQuestionnairePair = ( ), ) } - if (!offeredLanguages(questionnaire.value).has(response.value.language)) { + if (!offersLanguage(questionnaire.value, response.value.language)) { failures.push( issue( 'value-mismatch', diff --git a/packages/fhir/test/questionnaire-localization.test.ts b/packages/fhir/test/questionnaire-localization.test.ts index 512aa661..b8eecb3a 100644 --- a/packages/fhir/test/questionnaire-localization.test.ts +++ b/packages/fhir/test/questionnaire-localization.test.ts @@ -155,7 +155,8 @@ describe('Questionnaire localization', () => { ).toEqual(new Set(['invalid-type'])) const duplicated = withTitleExtensions([ translation('es', 'Dolor'), - translation('es', 'Dolores'), + translation('ES', 'Dolores'), + translation('en-us', 'Pain'), ]) expect(duplicated.ok).toBe(false) if (duplicated.ok) return @@ -164,6 +165,10 @@ describe('Questionnaire localization', () => { code: 'duplicate-identifier', path: ['_title', 'extension', 1], }), + expect.objectContaining({ + code: 'duplicate-identifier', + path: ['_title', 'extension', 2], + }), ]) }) @@ -209,6 +214,21 @@ describe('Questionnaire localization', () => { for (const pair of [base, translated]) { expect(preflightQuestionnairePair(multilingual, pair).ok).toBe(true) } + + // BCP 47 tags compare case-insensitively. + const recased = unwrap( + buildQuestionnaireResponse( + { ...answers, language: 'EN-us' }, + multilingual, + ), + ) + expect(recased.item?.[0]?.text).toBe('Are you in pain?') + expect( + preflightQuestionnairePair(multilingual, { + ...translated, + language: 'ES', + }).ok, + ).toBe(true) }) it('writes nested group and answer-child text from the base Questionnaire', () => { From 68b090dea89fdc4b09f8ee3bb4f8b93ddd92f400 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Wed, 23 Sep 2026 10:30:44 -0700 Subject: [PATCH 30/31] Report the Contract's Questionnaire Rule Codes and Omit Translated Answer Displays --- packages/fhir/README.md | 5 +- .../questionnaire-response-translated.json | 3 +- packages/fhir/src/core/index.ts | 1 + packages/fhir/src/core/result.ts | 24 +++- packages/fhir/src/index.ts | 1 + packages/fhir/src/questionnaire/builder.ts | 113 +++++++++++++----- .../fhir/src/questionnaire/localization.ts | 2 +- packages/fhir/src/questionnaire/parse.ts | 34 +++++- packages/fhir/src/questionnaire/preflight.ts | 4 +- packages/fhir/src/questionnaire/schemas.ts | 4 +- packages/fhir/test/diagnostics.test.ts | 5 + .../test/questionnaire-input-surface.test.ts | 2 +- .../test/questionnaire-localization.test.ts | 55 +++++++-- ...uestionnaire-preflight-expressions.test.ts | 2 +- packages/fhir/test/type-surface.json | 3 +- 15 files changed, 201 insertions(+), 57 deletions(-) diff --git a/packages/fhir/README.md b/packages/fhir/README.md index 4392ba69..8829ca51 100644 --- a/packages/fhir/README.md +++ b/packages/fhir/README.md @@ -608,6 +608,7 @@ The synchronized normative catalog and its resolved development reference live u Every `Issue` is either a schema issue or a producer diagnostic, reported through the one `Result` channel. A schema issue is this package's own input contract: its `SchemaIssueCode` is an undotted code such as `invalid-uri`, and grove-fhir does not define it. +A Questionnaire rule issue carries the `QuestionnaireRuleCode` grove-fhir's Questionnaire validator reports for the same rule, such as `pair-item-text`. A producer diagnostic is a grove-fhir registry rule: its `ProducerDiagnosticCode` is registered in the exchange protocol, and `groveProducerDiagnostics` publishes each code with its normative `reason`, its `severity`, and the side that emits it. `isProducerDiagnostic(issue)` narrows an `Issue` to a `ProducerDiagnostic`, whose registered `code` and `reason` are always present. A refused source record carries exactly one `mobile-input.*` code per countable reason: an unsupported source type, an unsupported source value, a value outside its catalog domain, a malformed value shape, missing required metadata, an invalid effective period, text with an unpaired surrogate, an invalid native identifier, an oversized recording payload, or an empty recording series. @@ -727,8 +728,10 @@ Changing a translation changes the content, so it needs a new `version`. `buildQuestionnaireResponse` takes the Questionnaire it answers and derives `questionnaire` from its `url|version`. The response `language` is required and must be the base language or a translation language the Questionnaire offers. The builder writes each item's `text` from the base Questionnaire only when the response uses the base language; a response to a translation omits it. +Likewise, a coded answer to an inline option carries the option's base `display` in the base language, and a response to a translation omits `display`, so `system` and `code` identify the answer. +A missing language is reported as `qg-language-required` or `gqr-language-required`, a repeated or base-language translation as `qg-translation-1`, and a language the Questionnaire does not offer as `pair-response-language`. -The pair preflight checks the exact `url|version`, the offered response language, global link identity, response nesting, answer types, inline options, repeats, response text (omitted, or exactly the base text), conditional enablement, and required enabled items for completed or amended responses. +The pair preflight checks the exact `url|version`, the offered response language, global link identity, response nesting, answer types, inline options, repeats, response text (omitted, or exactly the base text, else `pair-item-text`), conditional enablement, and required enabled items for completed or amended responses. It also enforces text length and decimal-place limits, scalar and quantity bounds, inline or ValueSet-backed quantity units, attachment MIME and size limits, repeated-answer occurrence bounds, and exclusive options. Terminology is never fetched. For a coded item backed by `answerValueSet`, the diff --git a/packages/fhir/fixtures/conformance/resources/questionnaire-response-translated.json b/packages/fhir/fixtures/conformance/resources/questionnaire-response-translated.json index 45429db5..45802935 100644 --- a/packages/fhir/fixtures/conformance/resources/questionnaire-response-translated.json +++ b/packages/fhir/fixtures/conformance/resources/questionnaire-response-translated.json @@ -34,8 +34,7 @@ { "valueCoding": { "system": "https://grovealliance.org/fhir/testing/CodeSystem/wellbeing", - "code": "well", - "display": "Well" + "code": "well" } } ] diff --git a/packages/fhir/src/core/index.ts b/packages/fhir/src/core/index.ts index 23e0fdd1..35364208 100644 --- a/packages/fhir/src/core/index.ts +++ b/packages/fhir/src/core/index.ts @@ -65,6 +65,7 @@ export { type IssueCode, type IssueSeverity, type ProducerDiagnostic, + type QuestionnaireRuleCode, type Result, type SchemaIssueCode, } from './result.js' diff --git a/packages/fhir/src/core/result.ts b/packages/fhir/src/core/result.ts index 85de09a4..6ffd410f 100644 --- a/packages/fhir/src/core/result.ts +++ b/packages/fhir/src/core/result.ts @@ -27,7 +27,16 @@ export type SchemaIssueCode = | 'schema-invalid' | 'value-mismatch' -export type IssueCode = SchemaIssueCode | ProducerDiagnosticCode +/** A Questionnaire rule, under the id grove-fhir's Questionnaire validator reports it with. */ +export type QuestionnaireRuleCode = + | 'gqr-language-required' + | 'pair-item-text' + | 'pair-response-language' + | 'qg-language-required' + | 'qg-translation-1' + +export type IssueCode = + SchemaIssueCode | QuestionnaireRuleCode | ProducerDiagnosticCode export interface Issue { readonly severity: IssueSeverity @@ -49,8 +58,10 @@ export interface ProducerDiagnostic extends Issue { readonly reason: string } -// IssueCode is closed over both halves, so a code outside the schema half is registered. -const schemaIssueCodes: Readonly> = { +// IssueCode is closed, so a code outside the schema and Questionnaire rule codes is registered. +const unregisteredIssueCodes: Readonly< + Record +> = { 'duplicate-identifier': true, 'external-validation-required': true, 'invalid-choice': true, @@ -64,12 +75,17 @@ const schemaIssueCodes: Readonly> = { 'out-of-range': true, 'schema-invalid': true, 'value-mismatch': true, + 'gqr-language-required': true, + 'pair-item-text': true, + 'pair-response-language': true, + 'qg-language-required': true, + 'qg-translation-1': true, } export const isProducerDiagnostic = ( issue: Issue, ): issue is ProducerDiagnostic => - !Object.hasOwn(schemaIssueCodes, issue.code) && + !Object.hasOwn(unregisteredIssueCodes, issue.code) && typeof issue.reason === 'string' export type Result = diff --git a/packages/fhir/src/index.ts b/packages/fhir/src/index.ts index da76c550..ef78b2c3 100644 --- a/packages/fhir/src/index.ts +++ b/packages/fhir/src/index.ts @@ -47,6 +47,7 @@ export { type ProducerDiagnostic, type QuantityComparator, type QuantityValue, + type QuestionnaireRuleCode, type ResearchStudyReference, type ReadonlyDeep, type Result, diff --git a/packages/fhir/src/questionnaire/builder.ts b/packages/fhir/src/questionnaire/builder.ts index 8a164b4a..0bbe3c2d 100644 --- a/packages/fhir/src/questionnaire/builder.ts +++ b/packages/fhir/src/questionnaire/builder.ts @@ -12,7 +12,13 @@ import { validateQuestionnaireResponseItemContract, } from './contract.js' import { isLanguageTag, offersLanguage } from './localization.js' -import { parseQuestionnaire, parseQuestionnaireResponse } from './parse.js' +import { + parseQuestionnaire, + parseQuestionnaireResponse, + questionnaireLanguageRequired, + responseLanguageRequired, + withLanguageRule, +} from './parse.js' import { prefixed } from './preflight-diagnostics.js' import { QUESTIONNAIRE_EXTENSIONS, @@ -126,9 +132,13 @@ const validateQuestionnaireInput = ( export const buildQuestionnaire = ( input: QuestionnaireInput, ): Result => { - const parsedInput = parseBuilderInput( - questionnaireBuilderInputSchema, - input, + const parsedInput = withLanguageRule( + parseBuilderInput( + questionnaireBuilderInputSchema, + input, + ), + 'qg-language-required', + questionnaireLanguageRequired, ) if (!parsedInput.ok) return parsedInput const validatedInput = parsedInput.value @@ -185,7 +195,7 @@ const validateResponseInput = ( if (!offersLanguage(questionnaire, input.language)) { failures.push( issue( - 'value-mismatch', + 'pair-response-language', ['language'], 'Response.language must be the Questionnaire base language or one of its translation languages.', ), @@ -247,12 +257,41 @@ const validateResponseInput = ( } type QuestionnaireItem = GroveQuestionnaire['item'][number] +type ResponseAnswer = NonNullable< + QuestionnaireResponseItemInput['answer'] +>[number] + +const withoutDisplay = (coding: object): object => + Object.fromEntries( + Object.entries(coding).filter( + ([key]) => key !== 'display' && key !== '_display', + ), + ) + +// A translated response names a coded answer by system and code alone; a base-language one +// repeats the inline option's base display. +const localizedCoding = ( + coding: NonNullable, + definition: QuestionnaireItem, + baseLanguage: boolean, +): object => { + if (!baseLanguage) return withoutDisplay(coding) + const option = definition.answerOption?.find( + ({ valueCoding }) => + valueCoding?.system === coding.system && + valueCoding?.code === coding.code, + )?.valueCoding + if (option === undefined) return coding + return option.display === undefined ? + withoutDisplay(coding) + : { ...coding, display: option.display } +} // Response text repeats the base Questionnaire text, so only a base-language response carries it. -const itemsWithText = ( +const localizedItems = ( items: readonly QuestionnaireResponseItemInput[], definitions: readonly QuestionnaireItem[], - writeText: boolean, + baseLanguage: boolean, path: ReadonlyArray, failures: Issue[], ): readonly object[] => @@ -272,37 +311,43 @@ const itemsWithText = ( return item } const children = definition.item ?? [] + const localizedAnswer = (answer: ResponseAnswer, answerIndex: number) => ({ + ...answer, + ...(answer.valueCoding === undefined ? + {} + : { + valueCoding: localizedCoding( + answer.valueCoding, + definition, + baseLanguage, + ), + }), + ...(answer.item === undefined ? + {} + : { + item: localizedItems( + answer.item, + children, + baseLanguage, + [...itemPath, 'answer', answerIndex, 'item'], + failures, + ), + }), + }) const { answer, item: nested, ...element } = item return { ...element, - ...(writeText && definition.text !== undefined ? + ...(baseLanguage && definition.text !== undefined ? { text: definition.text } : {}), - ...(answer === undefined ? - {} - : { - answer: answer.map((entry, answerIndex) => - entry.item === undefined ? - entry - : { - ...entry, - item: itemsWithText( - entry.item, - children, - writeText, - [...itemPath, 'answer', answerIndex, 'item'], - failures, - ), - }, - ), - }), + ...(answer === undefined ? {} : { answer: answer.map(localizedAnswer) }), ...(nested === undefined ? {} : { - item: itemsWithText( + item: localizedItems( nested, children, - writeText, + baseLanguage, [...itemPath, 'item'], failures, ), @@ -320,9 +365,13 @@ export const buildQuestionnaireResponse = ( return issues(prefixed(parsedQuestionnaire.issues, 'questionnaire')) } const instrument = parsedQuestionnaire.value - const parsedInput = parseBuilderInput( - questionnaireResponseBuilderInputSchema, - input, + const parsedInput = withLanguageRule( + parseBuilderInput( + questionnaireResponseBuilderInputSchema, + input, + ), + 'gqr-language-required', + responseLanguageRequired, ) if (!parsedInput.ok) return parsedInput const validatedInput = parsedInput.value @@ -330,7 +379,7 @@ export const buildQuestionnaireResponse = ( const items = validatedInput.items === undefined ? undefined - : itemsWithText( + : localizedItems( validatedInput.items, instrument.item, validatedInput.language.toLowerCase() === diff --git a/packages/fhir/src/questionnaire/localization.ts b/packages/fhir/src/questionnaire/localization.ts index ba1b1204..a0be37a1 100644 --- a/packages/fhir/src/questionnaire/localization.ts +++ b/packages/fhir/src/questionnaire/localization.ts @@ -85,7 +85,7 @@ export const questionnaireTranslations = (questionnaire: { } else if (elementLanguages.has(language)) { failures.push( issue( - 'duplicate-identifier', + 'qg-translation-1', translationPath, `Translation ${language} repeats the base language or another translation of the same string.`, ), diff --git a/packages/fhir/src/questionnaire/parse.ts b/packages/fhir/src/questionnaire/parse.ts index 472e1ad1..54a192c0 100644 --- a/packages/fhir/src/questionnaire/parse.ts +++ b/packages/fhir/src/questionnaire/parse.ts @@ -45,6 +45,7 @@ import { parseSemVer, zodIssueToIssue, type Issue, + type QuestionnaireRuleCode, type Result, } from '../core/index.js' import type { Extension } from '../r4/index.js' @@ -366,10 +367,35 @@ const parseWith = (schema: z.ZodType, input: unknown): Result => { return ok(deepFreeze(result.data) as T) } +// A missing or blank language fails the schema, but it is the contract's named rule. +export const withLanguageRule = ( + result: Result, + code: QuestionnaireRuleCode, + message: string, +): Result => + result.ok ? result : ( + issues( + result.issues.map((entry) => + entry.path.length === 1 && entry.path[0] === 'language' ? + { ...entry, code, message } + : entry, + ), + ) + ) + +export const questionnaireLanguageRequired = + 'Questionnaire.language names the base language of every string and is required.' +export const responseLanguageRequired = + 'QuestionnaireResponse.language names the language the participant saw and is required.' + export const parseQuestionnaire = ( input: unknown, ): Result => { - const parsed = parseWith(questionnaireSchema, input) + const parsed = withLanguageRule( + parseWith(questionnaireSchema, input), + 'qg-language-required', + questionnaireLanguageRequired, + ) if (!parsed.ok) return parsed const failures = questionnaireContractIssues(parsed.value) return failures.length === 0 ? parsed : issues(failures) @@ -378,7 +404,11 @@ export const parseQuestionnaire = ( export const parseQuestionnaireResponse = ( input: unknown, ): Result => { - const parsed = parseWith(questionnaireResponseSchema, input) + const parsed = withLanguageRule( + parseWith(questionnaireResponseSchema, input), + 'gqr-language-required', + responseLanguageRequired, + ) if (!parsed.ok) return parsed const failures = responseContractIssues(parsed.value) return failures.length === 0 ? parsed : issues(failures) diff --git a/packages/fhir/src/questionnaire/preflight.ts b/packages/fhir/src/questionnaire/preflight.ts index 684a5a42..d89f0f6d 100644 --- a/packages/fhir/src/questionnaire/preflight.ts +++ b/packages/fhir/src/questionnaire/preflight.ts @@ -245,7 +245,7 @@ const validateMatchedResponseItem = ( ) { failures.push( issue( - 'value-mismatch', + 'pair-item-text', [...path, 'text'], 'Response item text, when present, must exactly match the base Questionnaire.item.text.', ), @@ -451,7 +451,7 @@ export const preflightQuestionnairePair = ( if (!offersLanguage(questionnaire.value, response.value.language)) { failures.push( issue( - 'value-mismatch', + 'pair-response-language', ['response', 'language'], 'QuestionnaireResponse.language must be the Questionnaire base language or one of its translation languages.', ), diff --git a/packages/fhir/src/questionnaire/schemas.ts b/packages/fhir/src/questionnaire/schemas.ts index b8da7724..0d032f84 100644 --- a/packages/fhir/src/questionnaire/schemas.ts +++ b/packages/fhir/src/questionnaire/schemas.ts @@ -557,7 +557,7 @@ export const questionnaireBuilderInputSchema: z.ZodType = z.strictObject({ id: z.string().optional(), url: z.string(), version: z.string(), - language: z.string(), + language: z.string().min(1), name: z.string().optional(), title: z.string().optional(), _title: primitiveMetadata, @@ -622,7 +622,7 @@ const actorReferenceInputSchema = builderReferenceSchema( export const questionnaireResponseBuilderInputSchema: z.ZodType = z.strictObject({ id: z.string().optional(), - language: z.string(), + language: z.string().min(1), identifier: questionnaireResponseIdentifierInputSchema, status: z.enum([ 'in-progress', diff --git a/packages/fhir/test/diagnostics.test.ts b/packages/fhir/test/diagnostics.test.ts index ec1a2516..756c38e1 100644 --- a/packages/fhir/test/diagnostics.test.ts +++ b/packages/fhir/test/diagnostics.test.ts @@ -199,6 +199,11 @@ describe('producer diagnostics', () => { ).toBe(false) }) + it('never narrows a Questionnaire rule issue carrying a reason', () => { + const issue = schemaIssue('pair-item-text', ['item', 0, 'text'], 'Differs.') + expect(isProducerDiagnostic({ ...issue, reason: 'Differs.' })).toBe(false) + }) + it('never narrows a registry issue missing its reason', () => { for (const code of [ 'mobile-exchange.entry-node-key', diff --git a/packages/fhir/test/questionnaire-input-surface.test.ts b/packages/fhir/test/questionnaire-input-surface.test.ts index 6ad07b52..b90bf94e 100644 --- a/packages/fhir/test/questionnaire-input-surface.test.ts +++ b/packages/fhir/test/questionnaire-input-surface.test.ts @@ -274,7 +274,7 @@ describe('Questionnaire R4 builders', () => { if (result.ok) return expect(new Set(result.issues.map(({ code }) => code))).toEqual( new Set([ - 'value-mismatch', + 'pair-response-language', 'invalid-identifier', 'invalid-date-time', 'invalid-reference', diff --git a/packages/fhir/test/questionnaire-localization.test.ts b/packages/fhir/test/questionnaire-localization.test.ts index b8eecb3a..69bf91b8 100644 --- a/packages/fhir/test/questionnaire-localization.test.ts +++ b/packages/fhir/test/questionnaire-localization.test.ts @@ -85,6 +85,18 @@ const answers = { items: [ { linkId: 'has-pain', answer: [{ valueBoolean: true }] }, { linkId: 'location', answer: [{ valueString: 'Head' }] }, + { + linkId: 'severity', + answer: [ + { + valueCoding: { + system: codingSystem, + code: 'mild', + display: 'Slight', + }, + }, + ], + }, ], } @@ -121,11 +133,14 @@ describe('Questionnaire localization', () => { it('requires a BCP 47 base language on both resources', () => { expect(codes(parseQuestionnaire(withoutLanguage(questionnaire)))).toEqual( - new Set(['schema-invalid']), + new Set(['qg-language-required']), ) expect( codes(parseQuestionnaireResponse(withoutLanguage(response))), - ).toEqual(new Set(['schema-invalid'])) + ).toEqual(new Set(['gqr-language-required'])) + expect( + codes(parseQuestionnaireResponse({ ...response, language: '' })), + ).toEqual(new Set(['gqr-language-required'])) expect( codes(parseQuestionnaire({ ...questionnaire, language: 'en_US' })), ).toEqual(new Set(['invalid-code'])) @@ -134,7 +149,18 @@ describe('Questionnaire localization', () => { ).toEqual(new Set(['invalid-code'])) expect( codes(buildQuestionnaire({ ...questionnaireInput, language: '' })), + ).toEqual(new Set(['qg-language-required'])) + expect( + codes(buildQuestionnaire({ ...questionnaireInput, language: 'en_US' })), ).toEqual(new Set(['invalid-code'])) + expect( + codes( + buildQuestionnaireResponse( + withoutLanguage(responseInput()) as typeof answers, + questionnaire, + ), + ), + ).toEqual(new Set(['gqr-language-required'])) }) it('rejects malformed and ambiguous translations', () => { @@ -162,11 +188,11 @@ describe('Questionnaire localization', () => { if (duplicated.ok) return expect(duplicated.issues).toEqual([ expect.objectContaining({ - code: 'duplicate-identifier', + code: 'qg-translation-1', path: ['_title', 'extension', 1], }), expect.objectContaining({ - code: 'duplicate-identifier', + code: 'qg-translation-1', path: ['_title', 'extension', 2], }), ]) @@ -202,7 +228,13 @@ describe('Questionnaire localization', () => { expect(base.item?.map(({ text }) => text)).toEqual([ 'Are you in pain?', 'Where?', + 'How severe?', ]) + expect(base.item?.[2]?.answer?.[0]?.valueCoding).toEqual({ + system: codingSystem, + code: 'mild', + display: 'Mild', + }) const translated = unwrap( buildQuestionnaireResponse({ ...answers, language: 'es' }, multilingual), @@ -210,6 +242,10 @@ describe('Questionnaire localization', () => { expect(translated.language).toBe('es') expect(translated.item?.some((item) => 'text' in item)).toBe(false) expect(translated.item?.[1]?.answer).toEqual([{ valueString: 'Head' }]) + expect(translated.item?.[2]?.answer?.[0]?.valueCoding).toEqual({ + system: codingSystem, + code: 'mild', + }) for (const pair of [base, translated]) { expect(preflightQuestionnairePair(multilingual, pair).ok).toBe(true) @@ -247,7 +283,10 @@ describe('Questionnaire localization', () => { expect(unoffered.ok).toBe(false) if (unoffered.ok) return expect(unoffered.issues).toEqual([ - expect.objectContaining({ code: 'value-mismatch', path: ['language'] }), + expect.objectContaining({ + code: 'pair-response-language', + path: ['language'], + }), ]) const undeclared = buildQuestionnaireResponse( @@ -283,7 +322,7 @@ describe('Questionnaire localization', () => { withoutLanguage(multilingual) as typeof multilingual, ), ), - ).toEqual(new Set(['schema-invalid'])) + ).toEqual(new Set(['qg-language-required'])) }) it('reports translated response text and an unoffered language as pair errors', () => { @@ -308,8 +347,8 @@ describe('Questionnaire localization', () => { result.issues.map(({ code, path }) => [code, path.join('.')]), ).toEqual( expect.arrayContaining([ - ['value-mismatch', 'response.language'], - ['value-mismatch', 'response.item.0.text'], + ['pair-response-language', 'response.language'], + ['pair-item-text', 'response.item.0.text'], ['value-mismatch', 'response.item.1.answer.0'], ]), ) diff --git a/packages/fhir/test/questionnaire-preflight-expressions.test.ts b/packages/fhir/test/questionnaire-preflight-expressions.test.ts index 94e9d096..b2c11420 100644 --- a/packages/fhir/test/questionnaire-preflight-expressions.test.ts +++ b/packages/fhir/test/questionnaire-preflight-expressions.test.ts @@ -451,7 +451,7 @@ describe('Questionnaire pair preflight', () => { 'invalid-reference', 'invalid-type', 'out-of-range', - 'value-mismatch', + 'pair-item-text', ]), ) }) diff --git a/packages/fhir/test/type-surface.json b/packages/fhir/test/type-surface.json index 42983daf..09b8cf1d 100644 --- a/packages/fhir/test/type-surface.json +++ b/packages/fhir/test/type-surface.json @@ -126,7 +126,7 @@ "isReferenceToType": "const isReferenceToType: (value: unknown, resourceType: string) => boolean", "isSemanticallyEqual": "const isSemanticallyEqual: (left: string | Uint8Array, right: string | Uint8Array) => Result", "Issue": "interface Issue {\n readonly severity: IssueSeverity;\n readonly code: IssueCode;\n readonly path: ReadonlyArray;\n readonly message: string;\n /** Exact normative producer-rule reason when this is a Grove protocol diagnostic. */\n readonly reason?: string;\n /** Stable FHIR-facing producer-rule location, independent of parser internals. */\n readonly location?: string;\n}", - "IssueCode": "type IssueCode = SchemaIssueCode | ProducerDiagnosticCode;", + "IssueCode": "type IssueCode = SchemaIssueCode | QuestionnaireRuleCode | ProducerDiagnosticCode;", "issues": "const issues: (entries: readonly Issue[]) => Result", "IssueSeverity": "type IssueSeverity = 'error' | 'warning';", "KeyEpoch": "type KeyEpoch = Branded<'KeyEpoch'>;", @@ -220,6 +220,7 @@ "QuestionnaireResponseItemInput": "interface QuestionnaireResponseItemInput extends BackboneInput {\n readonly linkId: string;\n readonly definition?: string;\n readonly answer?: readonly QuestionnaireResponseAnswerInput[];\n readonly item?: readonly QuestionnaireResponseItemInput[];\n}", "QuestionnaireResponseSourceInput": "type QuestionnaireResponseSourceInput = TypedQuestionnaireResponseReference<'Patient' | 'Practitioner' | 'PractitionerRole' | 'RelatedPerson'>;", "QuestionnaireResponseSubjectInput": "type QuestionnaireResponseSubjectInput = TypedQuestionnaireResponseReference<'Patient'>;", + "QuestionnaireRuleCode": "type QuestionnaireRuleCode = 'gqr-language-required' | 'pair-item-text' | 'pair-response-language' | 'qg-language-required' | 'qg-translation-1';", "R4CollectionBundle": "type R4CollectionBundle = ReadonlyDeep> & {\n readonly type: 'collection';\n}>;", "ReadonlyDeep": "type ReadonlyDeep = T extends string | number | bigint | boolean | symbol | null | undefined ? T : T extends (...arguments_: never[]) => unknown ? T : T extends readonly unknown[] ? Readonly<{\n [Index in keyof T]: ReadonlyDeep;\n}> : T extends object ? {\n readonly [Key in keyof T]: ReadonlyDeep;\n} : T;", "RecordIdentityKind": "type RecordIdentityKind = 'provider-artifact' | 'provider-output' | 'provider-record' | 'source-artifact' | 'source-output' | 'source-record';", From 2034f2c7e825047117055b5b540a10ece2ed8ce7 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Wed, 23 Sep 2026 11:08:13 -0700 Subject: [PATCH 31/31] Re-Pin Grove FHIR to the Questionnaire Localization Revision --- .../mobile-exchange/exchange-bundle.json | 1 + .../receiver-lifecycle/events.json | 34 +++++++++---------- .../resources/altered-retry.json | 1 + .../resources/changed-source-record.json | 1 + .../resources/corrected.json | 1 + .../resources/equal-version-conflict.json | 1 + .../resources/equal-version-reassembly.json | 1 + .../resources/lexeme-retry.json | 1 + .../resources/missing-order.json | 1 + .../resources/multi-v1.json | 1 + .../resources/multi-v2.json | 1 + .../resources/multi-v3.json | 1 + .../resources/original.json | 1 + .../resources/other-writer.json | 1 + .../resources/reformatted-retry.json | 1 + .../receiver-lifecycle/resources/target.json | 1 + .../resources/unordered-a.json | 1 + .../resources/unordered-b.json | 1 + .../context-two-studies.json | 1 + .../study-attribution/source-event.json | 1 + packages/fhir/grove-fhir.json | 4 +-- 21 files changed, 38 insertions(+), 19 deletions(-) diff --git a/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json b/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json index 81468d06..fc405ef0 100644 --- a/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json +++ b/packages/fhir/fixtures/conformance/mobile-exchange/exchange-bundle.json @@ -319,6 +319,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/events.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/events.json index e539f2b7..2b3ce2ef 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/events.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/events.json @@ -1,7 +1,7 @@ { "schemaVersion": 0, "contractLock": { - "baseSourceRevision": "444e06fb25680c3af29e5d3ccbebeb0249be7318", + "baseSourceRevision": "928db16ef8bfd86d2a171727d2d1cb9f8979a442", "baseExchangeProtocolSHA256": "bbc9e980c2c674d5ef6f9900958d29c5602031ead8a3b6325cff508799106bb5", "releaseVersion": "0.6.0", "fhirVersion": "4.0.1", @@ -10,7 +10,7 @@ "events": { "original": { "path": "resources/original.json", - "sha256": "ff6c49864a4cf56068022ea019de6d6efb424f0c01eb51278578a98a33f47e6d", + "sha256": "236e65092ac9eba15d22d10c2db48d267cba65bfa9dd86b9790f055c737c434e", "eventIdentity": { "type": { "coding": [ @@ -27,7 +27,7 @@ }, "corrected": { "path": "resources/corrected.json", - "sha256": "dc4684173f3173538c61b164fa5654a5c8692d173b7d95f3b4109141f5ca584f", + "sha256": "ce8474fd4463aaec04ead2630aab375b7666800aa87273beb0c09779341aa77f", "eventIdentity": { "type": { "coding": [ @@ -44,7 +44,7 @@ }, "altered-retry": { "path": "resources/altered-retry.json", - "sha256": "9a57d2b9697b6250966de2e6e134fe3f5855bfc8a4830386ac2ff698654cebd7", + "sha256": "8178abe80fbff11b03edde194ba5ce8b14d35368f0f1e49ef6c248b24b61f248", "eventIdentity": { "type": { "coding": [ @@ -61,7 +61,7 @@ }, "reformatted-retry": { "path": "resources/reformatted-retry.json", - "sha256": "093a6dfd0cebc17b78073f1a64b4aae349fa37724903cb4e5b0c74da77368c9c", + "sha256": "0593cfb669f79ded7b7b410f65d9f311f304b2cbc319f4e003032c2af2a9a347", "eventIdentity": { "type": { "coding": [ @@ -78,7 +78,7 @@ }, "lexeme-retry": { "path": "resources/lexeme-retry.json", - "sha256": "7518697d093c4646c0a2b48aac74306f5c9ed195bb85617130379f1334c1803d", + "sha256": "b9421e514143f623840b52225d3eb6c4e786ede89f1f65998f4d1a4f763e69fa", "eventIdentity": { "type": { "coding": [ @@ -95,7 +95,7 @@ }, "unordered-a": { "path": "resources/unordered-a.json", - "sha256": "77edbb69c72b65cc9e0f4fec4f09e482cc6cd961f507c6f352da6ee13fdbd420", + "sha256": "2e237307e5ed64cf900dbde4b9df1920b56eb04d05528dd9e7c8de3f6b090b33", "eventIdentity": { "type": { "coding": [ @@ -112,7 +112,7 @@ }, "unordered-b": { "path": "resources/unordered-b.json", - "sha256": "f0d1de544dae87d08909d0738bdb6acc979c8db023c5409e86f3d042a572eafc", + "sha256": "7d2c95e1a4ac739da11b7917081dd4b1eb7fe38c0565d718ec2784dea582be2a", "eventIdentity": { "type": { "coding": [ @@ -146,7 +146,7 @@ }, "target": { "path": "resources/target.json", - "sha256": "d47ca9fe090282e1c84924f29088db4ffaa8ca3813f56a936c1bf02e96a8b5e2", + "sha256": "246b88c3019e47f1a31f32a205f5836187637e08e6e99fbbf4fc7dc65f7cceda", "eventIdentity": { "type": { "coding": [ @@ -180,7 +180,7 @@ }, "multi-v1": { "path": "resources/multi-v1.json", - "sha256": "6460bf578d4239d06efbbbe6aefe2ef1fa4d4e8a343a9d1f704867e504fd7524", + "sha256": "f4f479e7ef026fc175589ad94798afd9db29c257d3842c025d70faae06be69f4", "eventIdentity": { "type": { "coding": [ @@ -197,7 +197,7 @@ }, "multi-v2": { "path": "resources/multi-v2.json", - "sha256": "93f39fce303e95ab51253822b2192b67614a834977c7ad3b627c8f4245f7992e", + "sha256": "43743519b578ce0301a90243ac6a25daa4f7172668fad0c899221a03dd0af714", "eventIdentity": { "type": { "coding": [ @@ -214,7 +214,7 @@ }, "multi-v3": { "path": "resources/multi-v3.json", - "sha256": "0655373c22f80324ae0406cb26bc791f53d3e183102976a8b9652cd347ba1035", + "sha256": "a6fb56da2fad85bc3490e452452451f5205ac66b187d57af74c205629d0f7710", "eventIdentity": { "type": { "coding": [ @@ -231,7 +231,7 @@ }, "equal-version-conflict": { "path": "resources/equal-version-conflict.json", - "sha256": "18432910374b9739e05eb3119d911625e3c59a56b8a8b9ac8319d805342078b3", + "sha256": "056ac29b11d4b46b1d12fa623bc574f22cc79f163cf9f6546de0d6eb7eae7b27", "eventIdentity": { "type": { "coding": [ @@ -248,7 +248,7 @@ }, "equal-version-reassembly": { "path": "resources/equal-version-reassembly.json", - "sha256": "8d430701506d6e790f648c023b676a8c6dbaa1f65cd88e0819c13d76cf76e5c7", + "sha256": "82420e39528325d0ad3ede6250cce10f0664a54948cd25656f0c7065bcf5ba8b", "eventIdentity": { "type": { "coding": [ @@ -265,7 +265,7 @@ }, "other-writer": { "path": "resources/other-writer.json", - "sha256": "ddcd18be3734b9d7b40620f985fca2f88135ca93f14c2fe2f2d17d244e44ebf1", + "sha256": "340c045d124591681d466c60727e9f7f3307647641115e09a024b19db5169518", "eventIdentity": { "type": { "coding": [ @@ -282,7 +282,7 @@ }, "missing-order": { "path": "resources/missing-order.json", - "sha256": "f1b91c21429b9c0a900c35b92b0f6e05e10390ce735fe8d31676372b4d9e03f6", + "sha256": "58c555a64c92b29d15508c456506f22344f76150393988f4c153835708ea92ca", "eventIdentity": { "type": { "coding": [ @@ -299,7 +299,7 @@ }, "changed-source-record": { "path": "resources/changed-source-record.json", - "sha256": "a67904123787cc274e68b6fc6b23a927e0260e7deb4b4ade232be3b508c6c003", + "sha256": "5d212a563849efd76d01116a54f76d8694a09bd8c45b59a7b8e68aa33bff5958", "eventIdentity": { "type": { "coding": [ diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/altered-retry.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/altered-retry.json index 78a6c8d5..3edad2da 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/altered-retry.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/altered-retry.json @@ -340,6 +340,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/changed-source-record.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/changed-source-record.json index 9dcf66f7..77159ce7 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/changed-source-record.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/changed-source-record.json @@ -334,6 +334,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/corrected.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/corrected.json index 5e00a8d9..81c09d70 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/corrected.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/corrected.json @@ -340,6 +340,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-conflict.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-conflict.json index 3c5556fb..6acb0e94 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-conflict.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-conflict.json @@ -340,6 +340,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-reassembly.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-reassembly.json index a8b26d87..8c01b540 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-reassembly.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/equal-version-reassembly.json @@ -340,6 +340,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/lexeme-retry.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/lexeme-retry.json index cf88c043..c6c8d390 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/lexeme-retry.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/lexeme-retry.json @@ -340,6 +340,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/missing-order.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/missing-order.json index f3339a6a..1665579f 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/missing-order.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/missing-order.json @@ -324,6 +324,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v1.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v1.json index 3f21c9af..7e363812 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v1.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v1.json @@ -343,6 +343,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v2.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v2.json index 01e249d1..1e4d1f40 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v2.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v2.json @@ -340,6 +340,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v3.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v3.json index c826ab45..a667bba0 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v3.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/multi-v3.json @@ -343,6 +343,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/original.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/original.json index 25fdc9f5..7ea80090 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/original.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/original.json @@ -340,6 +340,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/other-writer.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/other-writer.json index 6f2bc5c2..71f980f5 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/other-writer.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/other-writer.json @@ -340,6 +340,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/reformatted-retry.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/reformatted-retry.json index 2afc41d8..8a56938a 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/reformatted-retry.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/reformatted-retry.json @@ -320,6 +320,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/target.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/target.json index aba18920..677849c3 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/target.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/target.json @@ -318,6 +318,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-a.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-a.json index e7c22000..2cd3200b 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-a.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-a.json @@ -324,6 +324,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-b.json b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-b.json index a77465cd..7c9c984c 100644 --- a/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-b.json +++ b/packages/fhir/fixtures/conformance/receiver-lifecycle/resources/unordered-b.json @@ -324,6 +324,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/study-attribution/context-two-studies.json b/packages/fhir/fixtures/conformance/study-attribution/context-two-studies.json index d60e642e..58742db6 100644 --- a/packages/fhir/fixtures/conformance/study-attribution/context-two-studies.json +++ b/packages/fhir/fixtures/conformance/study-attribution/context-two-studies.json @@ -332,6 +332,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/fixtures/conformance/study-attribution/source-event.json b/packages/fhir/fixtures/conformance/study-attribution/source-event.json index 6dc89372..8446dab3 100644 --- a/packages/fhir/fixtures/conformance/study-attribution/source-event.json +++ b/packages/fhir/fixtures/conformance/study-attribution/source-event.json @@ -326,6 +326,7 @@ "https://grovealliance.org/fhir/questionnaire/StructureDefinition/grove-questionnaire-response" ] }, + "language": "en-US", "extension": [ { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-completionMode", diff --git a/packages/fhir/grove-fhir.json b/packages/fhir/grove-fhir.json index 405225cb..66cc326e 100644 --- a/packages/fhir/grove-fhir.json +++ b/packages/fhir/grove-fhir.json @@ -1,5 +1,5 @@ { "repository": "https://github.com/SchmiedmayerLab/grove-fhir", - "ref": "96de131f036fa4116345864e098e8789204ff67f", - "archiveSha256": "58bd923c222cdfaf77753712a33777f80dcbea4396efff6c2418d4c75c91c688" + "ref": "9c5eff45622af55217c489cc985b162e6d9b8c75", + "archiveSha256": "2c9d316a2f874e7f3e75c6de0eaa4336287b5a3885f5032ff8b66b5104e45289" }